[jalview] 01/03: New upstream version 2.10.1.dfsg

Ximin Luo infinity0 at debian.org
Sat Dec 10 15:13:25 UTC 2016


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository jalview.

commit 7eb4c6f9eac0c765349c6e1fdd249e1ff1055fa5
Author: Ximin Luo <infinity0 at debian.org>
Date:   Sat Dec 10 16:11:25 2016 +0100

    New upstream version 2.10.1.dfsg
---
 build.xml                                          |  446 ++--
 doc/AddingGroovySupport.html                       |   24 +-
 doc/AnnotationPostAnalysis.txt                     |    4 +-
 doc/JalviewRNASupport.html                         |    4 +-
 doc/UnitTesting.html                               |   13 +-
 doc/biojsmsa-templates.html                        |    4 +-
 doc/building.html                                  |   46 +-
 doc/developing.html                                |   24 +-
 doc/i18n.html                                      |    4 +-
 doc/index.html                                     |   24 +-
 doc/newdmobj.html                                  |    4 +-
 help/help.hs                                       |    4 +-
 help/help.jhm                                      |   12 +-
 help/helpTOC.html                                  |   17 +-
 help/helpTOC.xml                                   |   24 +-
 help/html/calculations/consensus.html              |   13 +-
 help/html/calculations/conservation.html           |   21 +-
 help/html/calculations/pairwise.html               |    4 +-
 help/html/calculations/pca.html                    |   24 +-
 help/html/calculations/quality.html                |    6 +-
 help/html/calculations/recoverInputdata.html       |    4 +-
 help/html/calculations/redundancy.html             |   12 +-
 help/html/calculations/referenceseq.html           |   51 +-
 help/html/calculations/scorematrices.html          |   18 +-
 help/html/calculations/sorting.html                |   30 +-
 help/html/calculations/structureconsensus.html     |   53 +-
 help/html/calculations/tree.html                   |   34 +-
 help/html/calculations/treeviewer.html             |   41 +-
 help/html/colourSchemes/abovePID.html              |   14 +-
 help/html/colourSchemes/annotationColouring.html   |   29 +-
 help/html/colourSchemes/blosum.html                |    4 +-
 help/html/colourSchemes/buried.html                |    4 +-
 help/html/colourSchemes/clustal.html               |    4 +-
 help/html/colourSchemes/conservation.html          |   19 +-
 help/html/colourSchemes/helix.html                 |    4 +-
 help/html/colourSchemes/hydrophobic.html           |    4 +-
 help/html/colourSchemes/index.html                 |    7 +-
 help/html/colourSchemes/nucleotide.html            |    4 +-
 help/html/colourSchemes/pid.html                   |    4 +-
 help/html/colourSchemes/purinepyrimidine.html      |    4 +-
 help/html/colourSchemes/rnahelicesColouring.html   |   12 +-
 help/html/colourSchemes/strand.html                |    4 +-
 help/html/colourSchemes/taylor.html                |    4 +-
 help/html/colourSchemes/textcolour.html            |    4 +-
 help/html/colourSchemes/turn.html                  |    4 +-
 help/html/colourSchemes/user.html                  |    4 +-
 help/html/colourSchemes/zappo.html                 |    4 +-
 help/html/editing/index.html                       |   20 +-
 help/html/features/annotation.html                 |  316 ++-
 help/html/features/annotationsFormat.html          |   38 +-
 help/html/features/bioJsonFormat.html              |    9 +-
 help/html/features/biojsmsa.html                   |    4 +-
 help/html/features/chimera.html                    |   68 +-
 help/html/features/clarguments.html                |   27 +-
 help/html/features/codingfeatures.html             |    4 +-
 help/html/features/columnFilterByAnnotation.html   |   26 +-
 help/html/features/commandline.html                |    8 +-
 help/html/features/creatinFeatures.html            |   16 +-
 help/html/features/cursorMode.html                 |    4 +-
 help/html/features/dasfeatures.html                |   33 +-
 help/html/features/dassettings.html                |   19 +-
 help/html/features/editingFeatures.html            |   11 +-
 help/html/features/ensemblsequencefetcher.html     |   96 +
 help/html/features/featuresFormat.html             |   47 +-
 help/html/features/featureschemes.html             |    4 +-
 help/html/features/featuresettings.html            |   53 +-
 help/html/features/groovy.html                     |  101 +-
 help/html/features/hiddenRegions.html              |   10 +-
 help/html/features/jalarchive.html                 |    4 +-
 help/html/features/jmol.html                       |   56 +-
 help/html/features/mmcif.html                      |   55 +
 help/html/features/multipleViews.html              |    7 +-
 help/html/features/newkeystrokes.html              |    4 +-
 help/html/features/overview.html                   |    4 +-
 help/html/features/pdbseqfetcher.png               |  Bin 97462 -> 198431 bytes
 help/html/features/pdbsequencefetcher.html         |   13 +-
 help/html/features/pdbviewer.html                  |   17 +-
 help/html/features/preferences.html                |   38 +-
 help/html/features/search.html                     |   19 +-
 help/html/features/selectfetchdb.gif               |  Bin 41486 -> 19522 bytes
 help/html/features/seqfeatures.html                |   39 +-
 help/html/features/seqfetch.html                   |  102 +-
 help/html/features/seqmappings.html                |   10 +-
 help/html/features/sifts_mapping_output.png        |  Bin 0 -> 123157 bytes
 help/html/features/siftsmapping.html               |   90 +
 help/html/features/splitView.html                  |   27 +-
 help/html/features/structurechooser.html           |  151 +-
 help/html/features/uniprotqueryfields.html         |  391 +++
 help/html/features/uniprotseqfetcher.png           |  Bin 0 -> 215964 bytes
 help/html/features/uniprotsequencefetcher.html     |  169 ++
 help/html/features/varna.html                      |   14 +-
 help/html/features/viewingpdbs.html                |  130 +-
 help/html/features/wrap.html                       |    4 +-
 help/html/features/xsspannotation.html             |   26 +-
 help/html/groovy/featureCounter.html               |  269 ++
 help/html/index.html                               |   14 +-
 help/html/io/export.html                           |   14 +-
 help/html/io/exportseqreport.html                  |   15 +-
 help/html/io/fileformats.html                      |   10 +-
 help/html/io/index.html                            |   15 +-
 help/html/io/modellerpir.html                      |   25 +-
 help/html/io/tcoffeescores.html                    |    8 +-
 help/html/keys.html                                |   44 +-
 help/html/memory.html                              |   13 +-
 help/html/menus/alignmentMenu.html                 |  116 +-
 help/html/menus/alwannotation.html                 |   11 +-
 help/html/menus/alwannotationpanel.html            |   12 +-
 help/html/menus/alwcalculate.html                  |   43 +-
 help/html/menus/alwcolour.html                     |    4 +-
 help/html/menus/alwedit.html                       |   12 +-
 help/html/menus/alwfile.html                       |   22 +-
 help/html/menus/alwformat.html                     |   14 +-
 help/html/menus/alwselect.html                     |   18 +-
 help/html/menus/alwview.html                       |    4 +-
 help/html/menus/desktopMenu.html                   |   21 +-
 help/html/menus/index.html                         |    9 +-
 help/html/menus/popupMenu.html                     |   71 +-
 help/html/menus/wsmenu.html                        |   21 +-
 help/html/misc/aaproperties.html                   |   48 +-
 help/html/misc/aminoAcids.html                     |    4 +-
 help/html/misc/geneticCode.html                    |    4 +-
 help/html/na/index.html                            |   23 +-
 help/html/privacy.html                             |   21 +-
 help/html/releases.html                            |  853 +++++-
 help/html/vamsas/index.html                        |   13 +-
 help/html/webServices/AACon.html                   |   33 +-
 help/html/webServices/JABAWS.html                  |   25 +-
 help/html/webServices/RNAalifold.html              |   13 +-
 help/html/webServices/dbreffetcher.html            |   30 +-
 help/html/webServices/index.html                   |   37 +-
 help/html/webServices/jnet.html                    |   11 +-
 help/html/webServices/msaclient.html               |   25 +-
 help/html/webServices/newsreader.html              |   43 +-
 help/html/webServices/proteinDisorder.html         |   87 +-
 help/html/webServices/shmr.html                    |   22 +-
 help/html/webServices/urllinks.html                |   51 +-
 help/html/webServices/webServicesParams.html       |   22 +-
 help/html/webServices/webServicesPrefs.html        |   12 +-
 help/html/whatsNew.html                            |  101 +-
 jalview-jalopy.xml                                 |    4 +-
 nbbuild.xml                                        |    4 +-
 nbproject/project.properties                       |    8 +-
 nbproject/project.xml                              |    4 +-
 resources/authors.props                            |    6 +-
 resources/embl_mapping.xml                         |  115 +-
 resources/fts/pdb_data_columns.txt                 |  130 +
 resources/fts/uniprot_data_columns.txt             |  175 ++
 resources/images/blank_16x16_placeholder.png       |  Bin 0 -> 2873 bytes
 resources/lang/Messages.properties                 |  173 +-
 resources/lang/Messages_es.properties              |  332 ++-
 resources/so-xp-simple.obo.zip                     |  Bin 0 -> 144326 bytes
 resources/uniprot_mapping.xml                      |    6 +-
 schemas/JalviewWsParamSet.xsd                      |    4 +-
 schemas/castor-mapping.xsd                         |    4 +-
 schemas/jalview.nodesc.properties                  |    4 +-
 schemas/jalview.properties                         |    4 +-
 schemas/jalview.xsd                                |    5 +-
 schemas/jalviewJvV1.xsd                            |    4 +-
 schemas/sifts/alignment.xsd                        |  155 ++
 schemas/sifts/dataTypes.xsd                        |  154 ++
 schemas/sifts/eFamily.xsd                          |  154 ++
 schemas/vamsas.xsd                                 |    4 +-
 schemas/vamsasJvV1.xsd                             |    4 +-
 src/MCview/AppletPDBCanvas.java                    |   83 +-
 src/MCview/AppletPDBViewer.java                    |    4 +-
 src/MCview/Atom.java                               |   24 +-
 src/MCview/Bond.java                               |    4 +-
 src/MCview/MCMatrix.java                           |    4 +-
 src/MCview/PDBCanvas.java                          |   66 +-
 src/MCview/PDBChain.java                           |  141 +-
 src/MCview/PDBViewer.java                          |  124 +-
 src/MCview/PDBfile.java                            |  368 +--
 src/MCview/Residue.java                            |    9 +-
 src/MCview/Zsort.java                              |    4 +-
 src/com/stevesoft/pat/RegexWriter.java             |   48 +
 src/ext/edu/ucsf/rbvi/strucviz2/ChimUtils.java     |  106 +-
 .../edu/ucsf/rbvi/strucviz2/ChimeraManager.java    |  120 +-
 src/ext/edu/ucsf/rbvi/strucviz2/ChimeraModel.java  |   66 +
 .../edu/ucsf/rbvi/strucviz2/StructureManager.java  |   32 +
 .../edu/ucsf/rbvi/strucviz2/StructureSettings.java |   32 +
 .../ucsf/rbvi/strucviz2/port/ListenerThreads.java  |   32 +
 src/ext/vamsas/IRegistry.java                      |    4 +-
 src/ext/vamsas/IRegistryService.java               |    4 +-
 src/ext/vamsas/IRegistryServiceLocator.java        |    4 +-
 src/ext/vamsas/Jpred.java                          |    4 +-
 src/ext/vamsas/JpredService.java                   |    4 +-
 src/ext/vamsas/JpredServiceLocator.java            |    4 +-
 src/ext/vamsas/JpredSoapBindingStub.java           |    4 +-
 src/ext/vamsas/MuscleWS.java                       |    4 +-
 src/ext/vamsas/MuscleWSService.java                |    4 +-
 src/ext/vamsas/MuscleWSServiceLocator.java         |    4 +-
 src/ext/vamsas/MuscleWSSoapBindingStub.java        |    4 +-
 src/ext/vamsas/RegistryServiceSoapBindingStub.java |    4 +-
 src/ext/vamsas/SeqSearchI.java                     |    4 +-
 src/ext/vamsas/SeqSearchServiceLocator.java        |    4 +-
 src/ext/vamsas/SeqSearchServiceService.java        |    4 +-
 .../vamsas/SeqSearchServiceSoapBindingStub.java    |    4 +-
 src/ext/vamsas/ServiceHandle.java                  |    4 +-
 src/ext/vamsas/ServiceHandles.java                 |    4 +-
 src/jalview/analysis/AAFrequency.java              |  512 ++--
 src/jalview/analysis/AlignSeq.java                 |   17 +-
 src/jalview/analysis/AlignmentAnnotationUtils.java |    4 +-
 src/jalview/analysis/AlignmentSorter.java          |  106 +-
 src/jalview/analysis/AlignmentUtils.java           | 2204 ++++++++++++---
 src/jalview/analysis/AnnotationSorter.java         |    4 +-
 src/jalview/analysis/CodingUtils.java              |    4 +-
 src/jalview/analysis/CodonComparator.java          |    4 +-
 src/jalview/analysis/Conservation.java             |  569 ++--
 src/jalview/analysis/CrossRef.java                 | 1248 ++++++---
 src/jalview/analysis/Dna.java                      |  172 +-
 src/jalview/analysis/Finder.java                   |   78 +-
 src/jalview/analysis/Grouping.java                 |   11 +-
 src/jalview/analysis/NJTree.java                   |  296 +-
 src/jalview/analysis/PCA.java                      |    4 +-
 src/jalview/analysis/ParseProperties.java          |    4 +-
 src/jalview/analysis/Rna.java                      |  345 ++-
 src/jalview/analysis/SecStrConsensus.java          |    4 +-
 src/jalview/analysis/SeqsetUtils.java              |   29 +-
 src/jalview/analysis/SequenceIdMatcher.java        |   89 +-
 src/jalview/analysis/StructureFrequency.java       |  199 +-
 src/jalview/analysis/WUSSParseException.java       |    4 +-
 .../analysis/scoremodels/FeatureScoreModel.java    |  109 +-
 .../analysis/scoremodels/PIDScoreModel.java        |    4 +-
 .../scoremodels/PairwiseSeqScoreModel.java         |    4 +-
 src/jalview/analysis/scoremodels/SWScoreModel.java |    4 +-
 src/jalview/api/AlignCalcManagerI.java             |   57 +-
 src/jalview/api/AlignCalcWorkerI.java              |   37 +-
 src/jalview/api/AlignExportSettingI.java           |    4 +-
 src/jalview/api/AlignViewControllerGuiI.java       |    4 +-
 src/jalview/api/AlignViewControllerI.java          |   24 +-
 src/jalview/api/AlignViewportI.java                |   80 +-
 src/jalview/api/AlignmentViewPanel.java            |    4 +-
 src/jalview/api/BuildDetailsI.java                 |    4 +-
 src/jalview/api/ComplexAlignFile.java              |    7 +-
 src/jalview/api/DBRefEntryI.java                   |  114 +
 src/jalview/api/FeatureColourI.java                |  181 ++
 src/jalview/api/FeatureRenderer.java               |   33 +-
 src/jalview/api/FeatureSettingsControllerI.java    |    4 +-
 src/jalview/api/FeatureSettingsModelI.java         |   75 +-
 src/jalview/api/FeaturesDisplayedI.java            |    6 +-
 ...ureSettingsModelI.java => FeaturesSourceI.java} |   10 +-
 src/jalview/api/OOMHandlerI.java                   |    4 +-
 src/jalview/api/RotatableCanvasI.java              |    4 +-
 src/jalview/api/SequenceRenderer.java              |    4 +-
 src/jalview/api/SequenceStructureBinding.java      |    4 +-
 src/jalview/api/SiftsClientI.java                  |  132 +
 src/jalview/api/SplitContainerI.java               |    4 +-
 .../api/StructureSelectionManagerProvider.java     |    4 +-
 src/jalview/api/ViewStyleI.java                    |    4 +-
 src/jalview/api/analysis/ScoreModelI.java          |    4 +-
 src/jalview/api/analysis/ViewBasedAnalysisI.java   |    4 +-
 .../api/structures/JalviewStructureDisplayI.java   |    4 +-
 src/jalview/appletgui/APopupMenu.java              |  246 +-
 src/jalview/appletgui/AlignFrame.java              |  161 +-
 src/jalview/appletgui/AlignViewport.java           |   68 +-
 src/jalview/appletgui/AlignmentPanel.java          |   48 +-
 src/jalview/appletgui/AnnotationColourChooser.java |   34 +-
 src/jalview/appletgui/AnnotationColumnChooser.java |   25 +-
 src/jalview/appletgui/AnnotationLabels.java        |   68 +-
 src/jalview/appletgui/AnnotationPanel.java         |  106 +-
 src/jalview/appletgui/AnnotationRowFilter.java     |   49 +-
 src/jalview/appletgui/AppletJmol.java              |   29 +-
 src/jalview/appletgui/AppletJmolBinding.java       |   15 +-
 src/jalview/appletgui/CutAndPasteTransfer.java     |   35 +-
 src/jalview/appletgui/EditNameDialog.java          |    4 +-
 src/jalview/appletgui/EmbmenuFrame.java            |    4 +-
 src/jalview/appletgui/ExtJmol.java                 |   11 +-
 src/jalview/appletgui/FeatureColourChooser.java    |   67 +-
 src/jalview/appletgui/FeatureRenderer.java         |  113 +-
 src/jalview/appletgui/FeatureSettings.java         |  263 +-
 src/jalview/appletgui/Finder.java                  |   24 +-
 src/jalview/appletgui/FontChooser.java             |    4 +-
 src/jalview/appletgui/IdCanvas.java                |    8 +-
 src/jalview/appletgui/IdPanel.java                 |   37 +-
 src/jalview/appletgui/IdwidthAdjuster.java         |    4 +-
 src/jalview/appletgui/JVDialog.java                |   31 +-
 src/jalview/appletgui/OverviewPanel.java           |   15 +-
 src/jalview/appletgui/PCAPanel.java                |    4 +-
 src/jalview/appletgui/PaintRefresher.java          |    4 +-
 src/jalview/appletgui/PairwiseAlignPanel.java      |    4 +-
 src/jalview/appletgui/RedundancyPanel.java         |    4 +-
 src/jalview/appletgui/RotatableCanvas.java         |    4 +-
 src/jalview/appletgui/ScalePanel.java              |  307 ++-
 src/jalview/appletgui/SeqCanvas.java               |   61 +-
 src/jalview/appletgui/SeqPanel.java                |   72 +-
 src/jalview/appletgui/SequenceRenderer.java        |   52 +-
 src/jalview/appletgui/SliderPanel.java             |    9 +-
 src/jalview/appletgui/SplitFrame.java              |   32 +-
 src/jalview/appletgui/TitledPanel.java             |    4 +-
 src/jalview/appletgui/Tooltip.java                 |    4 +-
 src/jalview/appletgui/TreeCanvas.java              |   29 +-
 src/jalview/appletgui/TreePanel.java               |    4 +-
 src/jalview/appletgui/UserDefinedColours.java      |   50 +-
 src/jalview/bin/ArgsParser.java                    |  117 +
 src/jalview/bin/BuildDetails.java                  |    4 +-
 src/jalview/bin/Cache.java                         |   97 +-
 src/jalview/bin/Jalview.java                       |  512 ++--
 src/jalview/bin/JalviewLite.java                   |  387 +--
 src/jalview/bin/JalviewLiteURLRetrieve.java        |    6 +-
 src/jalview/commands/ChangeCaseCommand.java        |    4 +-
 src/jalview/commands/CommandI.java                 |    4 +-
 src/jalview/commands/EditCommand.java              |   24 +-
 src/jalview/commands/OrderCommand.java             |    4 +-
 src/jalview/commands/RemoveGapColCommand.java      |    4 +-
 src/jalview/commands/RemoveGapsCommand.java        |    4 +-
 src/jalview/commands/SlideSequencesCommand.java    |    4 +-
 src/jalview/commands/TrimRegionCommand.java        |  139 +-
 src/jalview/controller/AlignViewController.java    |  326 ++-
 .../controller/FeatureSettingsController.java      |    4 +-
 .../controller/FeatureSettingsControllerGuiI.java  |    4 +-
 src/jalview/datamodel/ASequence.java               |    4 +-
 src/jalview/datamodel/ASequenceI.java              |    4 +-
 src/jalview/datamodel/AlignedCodon.java            |   18 +-
 src/jalview/datamodel/AlignedCodonFrame.java       |  637 +++--
 src/jalview/datamodel/Alignment.java               |  412 ++-
 src/jalview/datamodel/AlignmentAnnotation.java     |  127 +-
 src/jalview/datamodel/AlignmentExportData.java     |    4 +-
 src/jalview/datamodel/AlignmentI.java              |   56 +-
 src/jalview/datamodel/AlignmentOrder.java          |    4 +-
 src/jalview/datamodel/AlignmentView.java           |   66 +-
 src/jalview/datamodel/AnnotatedCollectionI.java    |    4 +-
 src/jalview/datamodel/Annotation.java              |    4 +-
 src/jalview/datamodel/BinaryNode.java              |    4 +-
 src/jalview/datamodel/BinarySequence.java          |    4 +-
 src/jalview/datamodel/CigarArray.java              |    4 +-
 src/jalview/datamodel/CigarBase.java               |    5 +-
 src/jalview/datamodel/CigarCigar.java              |    4 +-
 src/jalview/datamodel/CigarSimple.java             |    4 +-
 src/jalview/datamodel/ColumnSelection.java         |  756 ++++--
 src/jalview/datamodel/DBRefEntry.java              |  196 +-
 src/jalview/datamodel/DBRefSource.java             |  110 +-
 src/jalview/datamodel/FeatureProperties.java       |    7 +-
 src/jalview/datamodel/GraphLine.java               |    4 +-
 src/jalview/datamodel/HiddenSequences.java         |  108 +-
 .../datamodel/IncompleteCodonException.java        |    4 +-
 src/jalview/datamodel/Mapping.java                 |  107 +-
 src/jalview/datamodel/MappingType.java             |   83 +
 src/jalview/datamodel/NodeTransformI.java          |    4 +-
 src/jalview/datamodel/PDBEntry.java                |  341 ++-
 src/jalview/datamodel/Profile.java                 |  163 ++
 src/jalview/datamodel/ProfileI.java                |   87 +
 .../{xdb/embl/BasePosition.java => Profiles.java}  |   51 +-
 .../{NodeTransformI.java => ProfilesI.java}        |   18 +-
 src/jalview/datamodel/Provenance.java              |    4 +-
 src/jalview/datamodel/ProvenanceEntry.java         |    4 +-
 src/jalview/datamodel/ResidueCount.java            |  641 +++++
 src/jalview/datamodel/RnaViewerModel.java          |    4 +-
 .../SearchResultMatchI.java}                       |   30 +-
 src/jalview/datamodel/SearchResults.java           |  240 +-
 src/jalview/datamodel/SearchResultsI.java          |  104 +
 .../datamodel/SecondaryStructureAnnotation.java    |    4 +-
 src/jalview/datamodel/SeqCigar.java                |   43 +-
 src/jalview/datamodel/Sequence.java                |  414 ++-
 src/jalview/datamodel/SequenceCollectionI.java     |    4 +-
 src/jalview/datamodel/SequenceDummy.java           |   16 +-
 src/jalview/datamodel/SequenceFeature.java         |  313 ++-
 src/jalview/datamodel/SequenceGroup.java           |   64 +-
 src/jalview/datamodel/SequenceI.java               |   71 +-
 src/jalview/datamodel/SequenceNode.java            |    4 +-
 src/jalview/datamodel/SequencePoint.java           |    4 +-
 src/jalview/datamodel/StructureViewerModel.java    |    4 +-
 src/jalview/datamodel/UniprotEntry.java            |    4 +-
 src/jalview/datamodel/UniprotFile.java             |    4 +-
 src/jalview/datamodel/UniprotProteinName.java      |    4 +-
 src/jalview/datamodel/UniprotSequence.java         |    4 +-
 src/jalview/datamodel/xdb/embl/EmblEntry.java      | 1083 ++++----
 src/jalview/datamodel/xdb/embl/EmblError.java      |    4 +-
 src/jalview/datamodel/xdb/embl/EmblFeature.java    |   19 +-
 .../datamodel/xdb/embl/EmblFeatureLocElement.java  |  125 -
 .../datamodel/xdb/embl/EmblFeatureLocations.java   |  175 --
 src/jalview/datamodel/xdb/embl/EmblFile.java       |   66 +-
 src/jalview/datamodel/xdb/embl/EmblSequence.java   |   45 +-
 src/jalview/datamodel/xdb/embl/Qualifier.java      |    4 +-
 src/jalview/exceptions/JalviewException.java       |    4 +-
 .../exceptions/NoFileSelectedException.java        |    4 +-
 src/jalview/ext/android/ContainerHelpers.java      |  108 +
 src/jalview/ext/android/SparseIntArray.java        |  432 +++
 src/jalview/ext/android/SparseShortArray.java      |  442 +++
 src/jalview/ext/ensembl/EnsemblCdna.java           |  131 +
 src/jalview/ext/ensembl/EnsemblCds.java            |  138 +
 src/jalview/ext/ensembl/EnsemblFeatures.java       |  160 ++
 src/jalview/ext/ensembl/EnsemblGene.java           |  615 +++++
 src/jalview/ext/ensembl/EnsemblGenome.java         |  115 +
 .../ensembl/EnsemblGenomes.java}                   |   46 +-
 src/jalview/ext/ensembl/EnsemblInfo.java           |   91 +
 src/jalview/ext/ensembl/EnsemblLookup.java         |  180 ++
 src/jalview/ext/ensembl/EnsemblProtein.java        |  147 +
 src/jalview/ext/ensembl/EnsemblRestClient.java     |  567 ++++
 src/jalview/ext/ensembl/EnsemblSeqProxy.java       |  955 +++++++
 .../ext/ensembl/EnsemblSequenceFetcher.java        |  133 +
 src/jalview/ext/ensembl/EnsemblSymbol.java         |  159 ++
 src/jalview/ext/ensembl/EnsemblXref.java           |  224 ++
 src/jalview/ext/ensembl/Species.java               |   52 +
 src/jalview/ext/htsjdk/HtsContigDb.java            |  232 ++
 src/jalview/ext/jmol/JalviewJmolBinding.java       |  116 +-
 src/jalview/ext/jmol/JmolCommands.java             |   12 +-
 src/jalview/ext/jmol/JmolParser.java               |  658 +++++
 src/jalview/ext/jmol/PDBFileWithJmol.java          |  465 ----
 src/jalview/ext/paradise/Annotate3D.java           |   19 +-
 src/jalview/ext/rbvi/chimera/ChimeraCommands.java  |    6 +-
 src/jalview/ext/rbvi/chimera/ChimeraListener.java  |    4 +-
 .../ext/rbvi/chimera/JalviewChimeraBinding.java    |  203 +-
 src/jalview/ext/so/SequenceOntology.java           |  474 ++++
 src/jalview/ext/varna/JalviewVarnaBinding.java     |    4 +-
 src/jalview/ext/varna/RnaModel.java                |    4 +-
 src/jalview/ext/varna/VarnaCommands.java           |    4 +-
 .../params/ArgumentI.java => fts/api/FTSData.java} |   32 +-
 src/jalview/fts/api/FTSDataColumnI.java            |  161 ++
 src/jalview/fts/api/FTSRestClientI.java            |  139 +
 src/jalview/fts/api/GFTSPanelI.java                |  140 +
 .../fts/core/DecimalFormatTableCellRenderer.java   |   80 +
 src/jalview/fts/core/FTSDataColumnPreferences.java |  338 +++
 src/jalview/fts/core/FTSRestClient.java            |  506 ++++
 .../core/FTSRestRequest.java}                      |   82 +-
 src/jalview/fts/core/FTSRestResponse.java          |  174 ++
 src/jalview/fts/core/GFTSPanel.java                |  992 +++++++
 src/jalview/fts/service/pdb/PDBFTSPanel.java       |  279 ++
 src/jalview/fts/service/pdb/PDBFTSRestClient.java  |  426 +++
 .../fts/service/uniprot/UniProtFTSRestClient.java  |  323 +++
 .../fts/service/uniprot/UniprotFTSPanel.java       |  238 ++
 src/jalview/gui/AlignExportSettings.java           |    4 +-
 src/jalview/gui/AlignFrame.java                    |  837 +++---
 src/jalview/gui/AlignViewport.java                 |  236 +-
 src/jalview/gui/AlignmentPanel.java                |  352 ++-
 src/jalview/gui/AnnotationChooser.java             |    4 +-
 src/jalview/gui/AnnotationColourChooser.java       |   30 +-
 src/jalview/gui/AnnotationColumnChooser.java       |   18 +-
 src/jalview/gui/AnnotationExporter.java            |   31 +-
 src/jalview/gui/AnnotationLabels.java              |  850 +++---
 src/jalview/gui/AnnotationPanel.java               |  393 ++-
 src/jalview/gui/AnnotationRowFilter.java           |   64 +-
 src/jalview/gui/AppJmol.java                       |  380 +--
 src/jalview/gui/AppJmolBinding.java                |   12 +-
 src/jalview/gui/AppVarna.java                      |    4 +-
 src/jalview/gui/AppVarnaBinding.java               |    4 +-
 src/jalview/gui/AssociatePdbFileWithSeq.java       |   11 +-
 src/jalview/gui/BlogReader.java                    |   40 +-
 src/jalview/gui/ChimeraViewFrame.java              |  164 +-
 src/jalview/gui/Console.java                       |    4 +-
 src/jalview/gui/CrossRefAction.java                |  312 +++
 src/jalview/gui/CutAndPasteHtmlTransfer.java       |   14 +-
 src/jalview/gui/CutAndPasteTransfer.java           |   56 +-
 src/jalview/gui/DasSourceBrowser.java              |   30 +-
 src/jalview/gui/Desktop.java                       |  624 +++--
 src/jalview/gui/EPSOptions.java                    |    4 +-
 src/jalview/gui/EditNameDialog.java                |    4 +-
 src/jalview/gui/FeatureColourChooser.java          |  153 +-
 src/jalview/gui/FeatureRenderer.java               |   68 +-
 src/jalview/gui/FeatureSettings.java               |  336 ++-
 src/jalview/gui/Finder.java                        |   25 +-
 src/jalview/gui/FontChooser.java                   |   19 +-
 src/jalview/gui/HTMLOptions.java                   |    4 +-
 src/jalview/gui/Help.java                          |    8 +-
 src/jalview/gui/IProgressIndicator.java            |    4 +-
 src/jalview/gui/IProgressIndicatorHandler.java     |    4 +-
 src/jalview/gui/IdCanvas.java                      |    7 +-
 src/jalview/gui/IdPanel.java                       |  105 +-
 src/jalview/gui/IdwidthAdjuster.java               |    4 +-
 src/jalview/gui/JDatabaseTree.java                 |   51 +-
 src/jalview/gui/Jalview2XML.java                   | 1050 +++++---
 src/jalview/gui/Jalview2XML_V1.java                |   42 +-
 src/jalview/gui/JalviewAppender.java               |    4 +-
 src/jalview/gui/JalviewChangeSupport.java          |    4 +-
 src/jalview/gui/JalviewChimeraBindingModel.java    |   14 +-
 src/jalview/gui/JalviewDialog.java                 |    7 +-
 src/jalview/gui/JvSwingUtils.java                  |    4 +-
 src/jalview/gui/MenuChooser.java                   |    4 +-
 src/jalview/gui/OOMWarning.java                    |    4 +-
 src/jalview/gui/OptsAndParamsPage.java             |   48 +-
 src/jalview/gui/OptsParametersContainerI.java      |    4 +-
 src/jalview/gui/OverviewPanel.java                 |   58 +-
 src/jalview/gui/PCAPanel.java                      |   37 +-
 src/jalview/gui/PDBSearchPanel.java                |  317 ---
 src/jalview/gui/PaintRefresher.java                |    4 +-
 src/jalview/gui/PairwiseAlignPanel.java            |    4 +-
 src/jalview/gui/PopupMenu.java                     |  355 +--
 src/jalview/gui/Preferences.java                   |  176 +-
 src/jalview/gui/ProgressBar.java                   |    4 +-
 src/jalview/gui/PromptUserConfig.java              |    4 +-
 src/jalview/gui/RedundancyPanel.java               |    6 +-
 src/jalview/gui/RestInputParamEditDialog.java      |    4 +-
 src/jalview/gui/RestServiceEditorPane.java         |    4 +-
 src/jalview/gui/RotatableCanvas.java               |    4 +-
 src/jalview/gui/SVGOptions.java                    |    4 +-
 src/jalview/gui/ScalePanel.java                    |  351 ++-
 src/jalview/gui/SeqCanvas.java                     |   58 +-
 src/jalview/gui/SeqPanel.java                      |  175 +-
 src/jalview/gui/SequenceFetcher.java               |  549 ++--
 src/jalview/gui/SequenceRenderer.java              |   22 +-
 src/jalview/gui/SliderPanel.java                   |    9 +-
 src/jalview/gui/SplashScreen.java                  |    4 +-
 src/jalview/gui/SplitFrame.java                    |  109 +-
 src/jalview/gui/StructureChooser.java              |  614 +++--
 src/jalview/gui/StructureViewer.java               |   40 +-
 src/jalview/gui/StructureViewerBase.java           |   64 +-
 src/jalview/gui/TextColourChooser.java             |   13 +-
 src/jalview/gui/TreeCanvas.java                    |  207 +-
 src/jalview/gui/TreePanel.java                     |   27 +-
 src/jalview/gui/UserDefinedColours.java            |    4 +-
 src/jalview/gui/UserQuestionnaireCheck.java        |    4 +-
 src/jalview/gui/VamsasApplication.java             |   14 +-
 src/jalview/gui/ViewSelectionMenu.java             |    4 +-
 src/jalview/gui/WebserviceInfo.java                |    4 +-
 src/jalview/gui/WsJobParameters.java               |    4 +-
 src/jalview/gui/WsParamSetManager.java             |    4 +-
 src/jalview/gui/WsPreferences.java                 |    4 +-
 src/jalview/httpserver/AbstractRequestHandler.java |    4 +-
 src/jalview/httpserver/HttpServer.java             |    4 +-
 src/jalview/io/AMSAFile.java                       |    4 +-
 src/jalview/io/AlignFile.java                      |   35 +-
 src/jalview/io/AlignmentProperties.java            |    4 +-
 src/jalview/io/AnnotationFile.java                 |   12 +-
 src/jalview/io/AppletFormatAdapter.java            |   84 +-
 src/jalview/io/BLCFile.java                        |    4 +-
 src/jalview/io/BioJsHTMLOutput.java                |  221 +-
 src/jalview/io/ClansFile.java                      |    4 +-
 src/jalview/io/ClustalFile.java                    |    9 +-
 src/jalview/io/DBRefFile.java                      |    4 +-
 src/jalview/io/FastaFile.java                      |   12 +-
 src/jalview/io/FeaturesFile.java                   | 1791 +++++--------
 src/jalview/io/FileLoader.java                     |   51 +-
 src/jalview/io/FileParse.java                      |   52 +-
 src/jalview/io/FormatAdapter.java                  |   23 +-
 src/jalview/io/Gff3File.java                       |  174 --
 src/jalview/io/HTMLOutput.java                     |  450 ++--
 src/jalview/io/HtmlFile.java                       |   37 +-
 src/jalview/io/HtmlSvgOutput.java                  |  657 +----
 src/jalview/io/IdentifyFile.java                   |  144 +-
 src/jalview/io/InputStreamParser.java              |    4 +-
 src/jalview/io/JPredFile.java                      |    4 +-
 src/jalview/io/JSONFile.java                       |  218 +-
 src/jalview/io/JalviewFileChooser.java             |    4 +-
 src/jalview/io/JalviewFileFilter.java              |    4 +-
 src/jalview/io/JalviewFileView.java                |    4 +-
 src/jalview/io/JnetAnnotationMaker.java            |    8 +-
 src/jalview/io/MSFfile.java                        |   92 +-
 src/jalview/io/MatrixFile.java                     |    4 +-
 src/jalview/io/ModellerDescription.java            |    8 +-
 src/jalview/io/NewickFile.java                     |    4 +-
 src/jalview/io/PDBFeatureSettings.java             |   85 +
 src/jalview/io/PIRFile.java                        |    9 +-
 src/jalview/io/PfamFile.java                       |  106 +-
 src/jalview/io/PhylipFile.java                     |    4 +-
 src/jalview/io/PileUpfile.java                     |   13 +-
 src/jalview/io/RnamlFile.java                      |   38 +-
 src/jalview/io/SequenceAnnotationReport.java       |  577 ++--
 src/jalview/io/SimpleBlastFile.java                |    4 +-
 src/jalview/io/StockholmFile.java                  |   63 +-
 src/jalview/io/StructureFile.java                  |  508 ++++
 src/jalview/io/TCoffeeScoreFile.java               |    4 +-
 src/jalview/io/VamsasAppDatastore.java             |    9 +-
 src/jalview/io/WSWUBlastClient.java                |    6 +-
 src/jalview/io/gff/ExonerateHelper.java            |  365 +++
 src/jalview/io/gff/Gff2Helper.java                 |   71 +
 src/jalview/io/gff/Gff3Helper.java                 |  425 +++
 .../EmblError.java => io/gff/GffConstants.java}    |   33 +-
 src/jalview/io/gff/GffHelperBase.java              |  426 +++
 src/jalview/io/gff/GffHelperFactory.java           |   89 +
 src/jalview/io/gff/GffHelperI.java                 |   63 +
 src/jalview/io/gff/InterProScanHelper.java         |  138 +
 .../gff/SequenceOntologyFactory.java}              |   27 +-
 src/jalview/io/gff/SequenceOntologyI.java          |   80 +
 src/jalview/io/gff/SequenceOntologyLite.java       |  240 ++
 src/jalview/io/packed/DataProvider.java            |    4 +-
 src/jalview/io/packed/JalviewDataset.java          |   25 +-
 src/jalview/io/packed/ParsePackedSet.java          |   11 +-
 src/jalview/io/packed/SimpleDataProvider.java      |    4 +-
 src/jalview/io/vamsas/Datasetsequence.java         |    8 +-
 src/jalview/io/vamsas/DatastoreItem.java           |    4 +-
 src/jalview/io/vamsas/DatastoreRegistry.java       |    8 +-
 src/jalview/io/vamsas/Dbref.java                   |    4 +-
 src/jalview/io/vamsas/LocalDocSyncObject.java      |    4 +-
 src/jalview/io/vamsas/Rangetype.java               |    4 +-
 src/jalview/io/vamsas/Sequencefeature.java         |   16 +-
 src/jalview/io/vamsas/Sequencemapping.java         |   14 +-
 src/jalview/io/vamsas/Tree.java                    |   18 +-
 src/jalview/javascript/JSFunctionExec.java         |   12 +-
 src/jalview/javascript/JalviewLiteJsApi.java       |    4 +-
 src/jalview/javascript/JsCallBack.java             |    4 +-
 src/jalview/javascript/JsSelectionSender.java      |   12 +-
 src/jalview/javascript/MouseOverListener.java      |    4 +-
 .../javascript/MouseOverStructureListener.java     |   77 +-
 src/jalview/jbgui/GAlignExportSettings.java        |    4 +-
 src/jalview/jbgui/GAlignFrame.java                 |  222 +-
 src/jalview/jbgui/GAlignmentPanel.java             |    4 +-
 src/jalview/jbgui/GCutAndPasteHtmlTransfer.java    |   17 +-
 src/jalview/jbgui/GCutAndPasteTransfer.java        |   19 +-
 src/jalview/jbgui/GDasSourceBrowser.java           |    4 +-
 src/jalview/jbgui/GDesktop.java                    |    4 +-
 src/jalview/jbgui/GFinder.java                     |    4 +-
 src/jalview/jbgui/GFontChooser.java                |    4 +-
 src/jalview/jbgui/GPCAPanel.java                   |    4 +-
 src/jalview/jbgui/GPDBSearchPanel.java             |  396 ---
 src/jalview/jbgui/GPairwiseAlignPanel.java         |    4 +-
 src/jalview/jbgui/GPreferences.java                |   69 +-
 src/jalview/jbgui/GRestInputParamEditDialog.java   |    4 +-
 src/jalview/jbgui/GRestServiceEditorPane.java      |    4 +-
 src/jalview/jbgui/GRnaStructureViewer.java         |    4 +-
 src/jalview/jbgui/GSequenceLink.java               |   40 +-
 src/jalview/jbgui/GSliderPanel.java                |    4 +-
 src/jalview/jbgui/GSplitFrame.java                 |   93 +-
 src/jalview/jbgui/GStructureChooser.java           |  318 ++-
 src/jalview/jbgui/GStructureViewer.java            |   29 +-
 src/jalview/jbgui/GTreePanel.java                  |    4 +-
 src/jalview/jbgui/GUserDefinedColours.java         |    4 +-
 src/jalview/jbgui/GWebserviceInfo.java             |    4 +-
 src/jalview/jbgui/GWsPreferences.java              |    4 +-
 src/jalview/jbgui/PDBDocFieldPreferences.java      |  267 --
 .../json/binding/biojs/BioJSReleasePojo.java       |    4 +-
 .../json/binding/biojs/BioJSRepositoryPojo.java    |    4 +-
 .../biojson/v1/AlignmentAnnotationPojo.java        |   83 +-
 .../json/binding/biojson/v1/AlignmentPojo.java     |    4 +-
 .../biojson/v1/AnnotationDisplaySettingPojo.java   |  118 +
 .../json/binding/biojson/v1/AnnotationPojo.java    |   19 +-
 .../binding/biojson/v1/ColourSchemeMapper.java     |  111 +
 .../biojson/v1/JalviewBioJsColorSchemeMapper.java  |   94 -
 .../binding/biojson/v1/SequenceFeaturesPojo.java   |    4 +-
 .../json/binding/biojson/v1/SequenceGrpPojo.java   |    4 +-
 .../json/binding/biojson/v1/SequencePojo.java      |    6 +-
 src/jalview/math/AlignmentDimension.java           |    4 +-
 src/jalview/math/Matrix.java                       |    4 +-
 src/jalview/math/RotatableMatrix.java              |    4 +-
 src/jalview/renderer/AnnotationRenderer.java       |  218 +-
 src/jalview/renderer/AwtRenderPanelI.java          |    4 +-
 src/jalview/renderer/ScaleRenderer.java            |  132 +
 .../renderer/seqfeatures/FeatureRenderer.java      |  144 +-
 src/jalview/rest/RestHandler.java                  |    4 +-
 src/jalview/schemabinding/version2/.castor.cdr     |   50 -
 src/jalview/schemabinding/version2/JSeq.java       |   59 +
 .../descriptors/AnnotationColoursDescriptor.java   |    1 +
 .../version2/descriptors/FeaturesDescriptor.java   |    1 +
 .../version2/descriptors/JSeqDescriptor.java       |   55 +
 .../descriptors/JalviewUserColoursDescriptor.java  |   18 +
 .../descriptors/UserColourSchemeDescriptor.java    |    1 +
 .../descriptors/VamsasModelDescriptor.java         |    1 +
 src/jalview/schemes/AnnotationColourGradient.java  |    4 +-
 src/jalview/schemes/Blosum62ColourScheme.java      |   24 +-
 src/jalview/schemes/BuriedColourScheme.java        |    4 +-
 src/jalview/schemes/ClustalxColourScheme.java      |    4 +-
 src/jalview/schemes/ColourSchemeI.java             |    7 +-
 src/jalview/schemes/ColourSchemeProperty.java      |    4 +-
 src/jalview/schemes/Consensus.java                 |    4 +-
 src/jalview/schemes/CovariationColourScheme.java   |    4 +-
 src/jalview/schemes/FeatureColour.java             |  696 +++++
 .../FeatureSettingsAdapter.java}                   |   49 +-
 src/jalview/schemes/FollowerColourScheme.java      |    9 +-
 src/jalview/schemes/GraduatedColor.java            |  298 ---
 src/jalview/schemes/HelixColourScheme.java         |    4 +-
 src/jalview/schemes/HydrophobicColourScheme.java   |    4 +-
 src/jalview/schemes/NucleotideColourScheme.java    |    4 +-
 src/jalview/schemes/PIDColourScheme.java           |   28 +-
 .../schemes/PurinePyrimidineColourScheme.java      |    4 +-
 src/jalview/schemes/RNAHelicesColour.java          |    8 +-
 src/jalview/schemes/RNAHelicesColourChooser.java   |   25 +-
 .../schemes/RNAInteractionColourScheme.java        |    4 +-
 src/jalview/schemes/ResidueColourScheme.java       |  192 +-
 src/jalview/schemes/ResidueProperties.java         | 2264 +++++++++++-----
 src/jalview/schemes/ScoreColourScheme.java         |    4 +-
 src/jalview/schemes/ScoreMatrix.java               |    4 +-
 src/jalview/schemes/StrandColourScheme.java        |    4 +-
 src/jalview/schemes/TCoffeeColourScheme.java       |    8 +-
 src/jalview/schemes/TaylorColourScheme.java        |    4 +-
 src/jalview/schemes/TurnColourScheme.java          |    4 +-
 src/jalview/schemes/UserColourScheme.java          |   14 +-
 src/jalview/schemes/ZappoColourScheme.java         |    4 +-
 .../structure/AlignmentViewPanelListener.java      |    4 +-
 src/jalview/structure/AtomSpec.java                |    4 +-
 src/jalview/structure/CommandListener.java         |    4 +-
 .../structure/SecondaryStructureListener.java      |    4 +-
 src/jalview/structure/SelectionListener.java       |    4 +-
 src/jalview/structure/SelectionSource.java         |    4 +-
 src/jalview/structure/SequenceListener.java        |    7 +-
 src/jalview/structure/StructureImportSettings.java |  153 ++
 src/jalview/structure/StructureListener.java       |    4 +-
 src/jalview/structure/StructureMapping.java        |   52 +-
 .../structure/StructureMappingcommandSet.java      |    4 +-
 .../structure/StructureSelectionManager.java       |  451 +++-
 src/jalview/structure/VamsasListener.java          |    4 +-
 src/jalview/structure/VamsasSource.java            |    4 +-
 .../structures/models/AAStructureBindingModel.java |   70 +-
 .../models/SequenceStructureBindingModel.java      |    4 +-
 src/jalview/util/AWTConsole.java                   |    4 +-
 .../Residue.java => jalview/util/ArrayUtils.java}  |   45 +-
 src/jalview/util/BrowserLauncher.java              |    4 +-
 src/jalview/util/CaseInsensitiveString.java        |   77 +
 src/jalview/util/ColorUtils.java                   |   92 +-
 src/jalview/util/Comparison.java                   |  133 +-
 src/jalview/util/DBRefUtils.java                   |  415 ++-
 src/jalview/util/DnaUtils.java                     |  153 ++
 src/jalview/util/Format.java                       |   71 +-
 src/jalview/util/GroupUrlLink.java                 |    4 +-
 .../UniprotSequence.java => util/HttpUtils.java}   |   57 +-
 src/jalview/util/ImageMaker.java                   |   57 +-
 src/jalview/util/LinkedIdentityHashSet.java        |  143 +
 src/jalview/util/MapList.java                      |  308 ++-
 src/jalview/util/MappingUtils.java                 |  437 ++-
 src/jalview/util/MessageManager.java               |    4 +-
 src/jalview/util/ParseHtmlBodyAndLinks.java        |   50 +-
 src/jalview/util/Platform.java                     |   65 +-
 src/jalview/util/QuickSort.java                    |  342 ++-
 src/jalview/util/ReverseListIterator.java          |    4 +-
 src/jalview/util/ShiftList.java                    |    4 +-
 src/jalview/util/SparseCount.java                  |  174 ++
 src/jalview/util/StringUtils.java                  |  159 +-
 src/jalview/util/TableSorter.java                  |    4 +-
 src/jalview/util/UrlConstants.java                 |   55 +
 src/jalview/util/UrlLink.java                      |  420 ++-
 src/jalview/util/jarInputStreamProvider.java       |    4 +-
 src/jalview/viewmodel/AlignmentViewport.java       |  405 ++-
 src/jalview/viewmodel/PCAModel.java                |    4 +-
 .../AnnotationFilterParameter.java                 |    4 +-
 .../seqfeatures/FeatureRendererModel.java          |  265 +-
 .../seqfeatures/FeatureRendererSettings.java       |   59 +-
 .../seqfeatures/FeatureSettingsModel.java          |    4 +-
 .../viewmodel/seqfeatures/FeaturesDisplayed.java   |   10 +-
 src/jalview/viewmodel/styles/ViewStyle.java        |    4 +-
 src/jalview/workers/AlignCalcManager.java          |  212 +-
 src/jalview/workers/AlignCalcWorker.java           |   51 +-
 .../workers/AlignmentAnnotationFactory.java        |  148 +
 .../AnnotationProviderI.java}                      |   24 +-
 src/jalview/workers/AnnotationWorker.java          |  144 +
 src/jalview/workers/ColumnCounterWorker.java       |  244 ++
 src/jalview/workers/ComplementConsensusThread.java |   17 +-
 src/jalview/workers/ConsensusThread.java           |   36 +-
 src/jalview/workers/ConservationThread.java        |   11 +-
 src/jalview/workers/FeatureCounterI.java           |   83 +
 src/jalview/workers/StrucConsensusThread.java      |   21 +-
 src/jalview/ws/AWSThread.java                      |   15 +-
 src/jalview/ws/AWsJob.java                         |    4 +-
 src/jalview/ws/DBRefFetcher.java                   |  728 ++---
 src/jalview/ws/DasSequenceFeatureFetcher.java      |   31 +-
 src/jalview/ws/HttpClientUtils.java                |    4 +-
 src/jalview/ws/JobStateSummary.java                |    4 +-
 src/jalview/ws/SequenceFetcher.java                |  323 +--
 .../SequenceFetcherFactory.java}                   |   40 +-
 src/jalview/ws/WSClient.java                       |    4 +-
 src/jalview/ws/WSClientI.java                      |    4 +-
 src/jalview/ws/WSMenuEntryProviderI.java           |    4 +-
 .../ws/dbsources/EbiFileRetrievedProxy.java        |    4 +-
 .../{EmblCdsSouce.java => EmblCdsSource.java}      |   20 +-
 src/jalview/ws/dbsources/EmblSource.java           |   20 +-
 src/jalview/ws/dbsources/EmblXmlSource.java        |   87 +-
 src/jalview/ws/dbsources/GeneDbSource.java         |   18 +-
 src/jalview/ws/dbsources/PDBRestClient.java        |  422 ---
 src/jalview/ws/dbsources/Pdb.java                  |  100 +-
 src/jalview/ws/dbsources/Pfam.java                 |   30 +-
 src/jalview/ws/dbsources/PfamFull.java             |   15 +-
 src/jalview/ws/dbsources/PfamSeed.java             |   14 +-
 src/jalview/ws/dbsources/Rfam.java                 |   17 +-
 src/jalview/ws/dbsources/RfamFull.java             |   16 +-
 src/jalview/ws/dbsources/RfamSeed.java             |   21 +-
 src/jalview/ws/dbsources/Uniprot.java              |  237 +-
 .../{UnprotName.java => UniprotName.java}          |   12 +-
 src/jalview/ws/dbsources/Xfam.java                 |   32 +-
 .../ws/dbsources/das/api/DasSourceRegistryI.java   |    4 +-
 .../ws/dbsources/das/api/jalviewSourceI.java       |    4 +-
 .../dbsources/das/datamodel/DasSequenceSource.java |    4 +-
 .../dbsources/das/datamodel/DasSourceRegistry.java |    8 +-
 .../ws/dbsources/das/datamodel/JalviewSource.java  |    4 +-
 src/jalview/ws/ebi/EBIFetchClient.java             |  176 +-
 src/jalview/ws/io/mime/HttpContentHandler.java     |    4 +-
 .../ws/io/mime/JalviewMimeContentHandler.java      |    4 +-
 src/jalview/ws/io/mime/MimeTypes.java              |    4 +-
 src/jalview/ws/jws1/Annotate3D.java                |    4 +-
 src/jalview/ws/jws1/Discoverer.java                |    4 +-
 src/jalview/ws/jws1/JPredClient.java               |    4 +-
 src/jalview/ws/jws1/JPredThread.java               |    6 +-
 src/jalview/ws/jws1/JWS1Thread.java                |    4 +-
 src/jalview/ws/jws1/MsaWSClient.java               |   16 +-
 src/jalview/ws/jws1/MsaWSThread.java               |   23 +-
 src/jalview/ws/jws1/SeqSearchWSClient.java         |   17 +-
 src/jalview/ws/jws1/SeqSearchWSThread.java         |   27 +-
 src/jalview/ws/jws1/WS1Client.java                 |    4 +-
 src/jalview/ws/jws1/WSJob.java                     |    4 +-
 src/jalview/ws/jws2/AAConClient.java               |    4 +-
 src/jalview/ws/jws2/AADisorderClient.java          |   22 +-
 src/jalview/ws/jws2/AWS2Thread.java                |    4 +-
 src/jalview/ws/jws2/AbstractJabaCalcWorker.java    |   12 +-
 src/jalview/ws/jws2/JPred301Client.java            |    9 +-
 src/jalview/ws/jws2/JWs2Job.java                   |    4 +-
 src/jalview/ws/jws2/JabaParamStore.java            |    4 +-
 src/jalview/ws/jws2/JabaPreset.java                |    4 +-
 src/jalview/ws/jws2/JabaWsServerQuery.java         |    4 +-
 src/jalview/ws/jws2/JabawsCalcWorker.java          |    4 +-
 .../ws/jws2/JabawsMsaInterfaceAlignCalcWorker.java |    4 +-
 src/jalview/ws/jws2/Jws2Client.java                |   11 +-
 src/jalview/ws/jws2/Jws2Discoverer.java            |    4 +-
 src/jalview/ws/jws2/MsaWSClient.java               |   38 +-
 src/jalview/ws/jws2/MsaWSThread.java               |   22 +-
 src/jalview/ws/jws2/ParameterUtils.java            |    4 +-
 src/jalview/ws/jws2/RNAalifoldClient.java          |    9 +-
 .../ws/jws2/SequenceAnnotationWSClient.java        |    6 +-
 src/jalview/ws/jws2/dm/AAConSettings.java          |    4 +-
 src/jalview/ws/jws2/dm/JabaOption.java             |    4 +-
 src/jalview/ws/jws2/dm/JabaParameter.java          |    4 +-
 src/jalview/ws/jws2/dm/JabaValueConstrain.java     |    4 +-
 src/jalview/ws/jws2/dm/JabaWsParamSet.java         |    4 +-
 src/jalview/ws/jws2/jabaws2/Jws2Instance.java      |    4 +-
 .../ws/jws2/jabaws2/Jws2InstanceFactory.java       |    4 +-
 src/jalview/ws/params/ArgumentI.java               |    4 +-
 src/jalview/ws/params/AutoCalcSetting.java         |    4 +-
 .../ws/params/InvalidArgumentException.java        |    4 +-
 src/jalview/ws/params/OptionI.java                 |    4 +-
 src/jalview/ws/params/ParamDatastoreI.java         |    4 +-
 src/jalview/ws/params/ParamManager.java            |    4 +-
 src/jalview/ws/params/ParameterI.java              |    4 +-
 src/jalview/ws/params/ValueConstrainI.java         |    4 +-
 src/jalview/ws/params/WsParamSetI.java             |    4 +-
 src/jalview/ws/params/simple/BooleanOption.java    |    4 +-
 src/jalview/ws/params/simple/IntegerParameter.java |    4 +-
 src/jalview/ws/params/simple/Option.java           |    4 +-
 src/jalview/ws/params/simple/Parameter.java        |    4 +-
 .../ws/params/simple/StringChoiceParameter.java    |    4 +-
 src/jalview/ws/rest/AlignmentProcessor.java        |    4 +-
 src/jalview/ws/rest/HttpResultSet.java             |    4 +-
 src/jalview/ws/rest/InputType.java                 |    4 +-
 src/jalview/ws/rest/NoValidInputDataException.java |    4 +-
 src/jalview/ws/rest/RestClient.java                |    4 +-
 src/jalview/ws/rest/RestJob.java                   |    4 +-
 src/jalview/ws/rest/RestJobThread.java             |    4 +-
 src/jalview/ws/rest/RestServiceDescription.java    |    4 +-
 src/jalview/ws/rest/params/Alignment.java          |    4 +-
 src/jalview/ws/rest/params/AnnotationFile.java     |    4 +-
 src/jalview/ws/rest/params/JobConstant.java        |    4 +-
 .../ws/rest/params/SeqGroupIndexVector.java        |    4 +-
 src/jalview/ws/rest/params/SeqIdVector.java        |    4 +-
 src/jalview/ws/rest/params/SeqVector.java          |    4 +-
 src/jalview/ws/rest/params/Tree.java               |    4 +-
 src/jalview/ws/seqfetcher/ASequenceFetcher.java    |  265 +-
 src/jalview/ws/seqfetcher/DbSourceProxy.java       |  114 +-
 src/jalview/ws/seqfetcher/DbSourceProxyImpl.java   |  100 +-
 src/jalview/ws/sifts/MappingOutputPojo.java        |  137 +
 src/jalview/ws/sifts/SiftsClient.java              | 1094 ++++++++
 .../ws/sifts/SiftsException.java}                  |   13 +-
 src/jalview/ws/sifts/SiftsSettings.java            |   78 +
 src/jalview/ws/uimodel/AlignAnalysisUIText.java    |    5 +-
 src/jalview/ws/uimodel/PDBRestResponse.java        |  223 --
 src/jalview/xml/binding/sifts/Alignment.java       | 2310 ++++++++++++++++
 src/jalview/xml/binding/sifts/EntityType.java      |   62 +
 src/jalview/xml/binding/sifts/Entry.java           | 2818 ++++++++++++++++++++
 src/jalview/xml/binding/sifts/ObjectFactory.java   |  319 +++
 src/jalview/xml/binding/sifts/package-info.java    |    9 +
 src/org/jibble/epsgraphics/EpsDocument.java        |    4 +-
 src/org/jibble/epsgraphics/EpsException.java       |    4 +-
 src/org/jibble/epsgraphics/EpsGraphics2D.java      |    4 +-
 src/uk/ac/ebi/picr/model/CrossReference.java       |    4 +-
 .../ac/ebi/picr/model/CrossReference_Helper.java   |    4 +-
 src/uk/ac/ebi/picr/model/UPEntry.java              |    4 +-
 src/uk/ac/ebi/picr/model/UPEntry_Helper.java       |    4 +-
 src/uk/ac/ebi/www/Data.java                        |    4 +-
 src/uk/ac/ebi/www/InputParams.java                 |    4 +-
 src/uk/ac/ebi/www/WSFile.java                      |    4 +-
 src/uk/ac/ebi/www/WSWUBlast.java                   |    4 +-
 src/uk/ac/ebi/www/WSWUBlastService.java            |    4 +-
 src/uk/ac/ebi/www/WSWUBlastServiceLocator.java     |    4 +-
 src/uk/ac/ebi/www/WSWUBlastSoapBindingStub.java    |    4 +-
 .../AccessionMapperBindingStub.java                |    4 +-
 .../AccessionMapperInterface.java                  |    4 +-
 .../AccessionMapperService.java                    |    4 +-
 .../AccessionMapperServiceLocator.java             |    4 +-
 src/vamsas/IMsaWS.java                             |    4 +-
 src/vamsas/objects/simple/Alignment.java           |    4 +-
 src/vamsas/objects/simple/Alignment_Helper.java    |    4 +-
 src/vamsas/objects/simple/JpredResult.java         |    4 +-
 src/vamsas/objects/simple/JpredResult_Helper.java  |    4 +-
 src/vamsas/objects/simple/MsaResult.java           |    4 +-
 src/vamsas/objects/simple/MsaResult_Helper.java    |    4 +-
 src/vamsas/objects/simple/Msfalignment.java        |    4 +-
 src/vamsas/objects/simple/Msfalignment_Helper.java |    4 +-
 src/vamsas/objects/simple/Object.java              |    4 +-
 src/vamsas/objects/simple/Object_Helper.java       |    4 +-
 src/vamsas/objects/simple/Result.java              |    4 +-
 src/vamsas/objects/simple/Result_Helper.java       |    4 +-
 src/vamsas/objects/simple/Secstructpred.java       |    4 +-
 .../objects/simple/Secstructpred_Helper.java       |    4 +-
 src/vamsas/objects/simple/SeqSearchResult.java     |    4 +-
 .../objects/simple/SeqSearchResult_Helper.java     |    4 +-
 src/vamsas/objects/simple/Sequence.java            |    4 +-
 src/vamsas/objects/simple/SequenceSet.java         |    4 +-
 src/vamsas/objects/simple/SequenceSet_Helper.java  |    4 +-
 src/vamsas/objects/simple/Sequence_Helper.java     |    4 +-
 src/vamsas/objects/simple/WsJobId.java             |    4 +-
 src/vamsas/objects/simple/WsJobId_Helper.java      |    4 +-
 883 files changed, 55780 insertions(+), 21237 deletions(-)

diff --git a/build.xml b/build.xml
index de0d7a8..923a915 100644
--- a/build.xml
+++ b/build.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -19,7 +19,7 @@
 -->
 <project name="jalviewX" default="usage" basedir=".">
   <target name="help" depends="usage" />
-  <target name="usage">
+  <target name="usage" depends="init">
     <echo message="~~~Jalview Ant build.xml Usage~~~~" />
     <echo message="Targets include:" />
     <echo message="usage - default target, displays this message" />
@@ -27,9 +27,14 @@
     <echo message="build - compiles all necessary files for Application" />
     <echo message="makedist - compiles and places all necessary jar files into directory dist" />
     <echo message="makefulldist - signs all jar files and builds jnlp file for full distribution" />
-    <echo message="              this needs a keystore and key. See docs/building.html for more information." />
+    <echo message="              this needs a keystore and key."/>
+    <echo message="              Add -Dtimestamp to timestamp signed jars"/>
+    <echo message="              -Djalview.keyalg and -Djalview.keydig are SHA1/SHA1withRSA"/>
+    <echo message="              See docs/building.html for more information." />
     <echo message="compileApplet - compiles all necessary files for Applet" />
     <echo message="makeApplet - compiles, then packages and obfuscates the Applet" />
+    <echo message="testng - run jalview's tests via testNG. Default group is '${testng-groups}'" />
+    <echo message="      you can specify particular test groups as a list via -Dtestng-groups=" />
     <echo message="See docs/building.html and the comments in build file for other targets." />
     <echo message="note: compile and makeApplet require the property java118.home to be set to point to a java 1.1.8 jdk." />
     <echo message="Useful -D flags: -Ddonotobfuscate will prevent applet obfuscation" />
@@ -62,12 +67,12 @@
         <include name="*.jar"/>
       </fileset> -->
       </path>
-  	
+
     <!-- Jalview Version String displayed by application on startup and used to check for updates -->
     <property name="JALVIEW_VERSION" value="DEVELOPMENT" />
-  	
+
     <property name="INSTALLATION" value="Source" />
-  	
+
     <!-- 2.4 (VAMSAS)" -->
     <!-- Include debugging information in javac true or false -->
     <property name="javac.debug" value="true" />
@@ -80,19 +85,30 @@
     <property name="jalview.key" value="jalview" />
     <!-- Key Password -->
     <property name="jalview.key.pass" value="alignmentisfun" />
+    <!-- time stamp server URL -->
+    <property name="jalview.tsaurl" value="" />
+    <!-- locally valid proxy for signing with external time server -->
+    <property name="proxyPort" value="80"/>
+    <property name="proxyHost" value="sqid"/>
+    <!-- key sign/digest algorithms -->
+    <property name="jalview.keyalg" value="SHA1withRSA" description="key algorithm for signing"/>
+    <property name="jalview.keydig" value="SHA1" description="algorithm for jar digest"/>
+
+    <!-- default TestNG groups to run -->
+    <property name="testng-groups" value="Functional" />
 
     <!-- Don't change anything below here unless you know what you are doing! -->
     <!-- Url path for WebStart in JNLP file -->
     <property name="WebStartLocation" value="http://www.jalview.org/webstart" />
     <!-- Webstart Image - looked for in resources/images -->
-    <property name="WebStartImage" value="JalviewLogo_big.png"/>
+    <property name="WebStartImage" value="JalviewLogo_big.png" />
     <!-- J2SE version needed for webstart launch -->
     <!-- Anne's version needs 1.7 - should rebuild VARNA to java 1.6 for release -->
-    <property name="j2sev" value="1.7+"/>
-	<!-- Java Compilation settings - source and target javac version -->
-	<property name="javac.source" value="1.7"/>
-  	<property name="javac.target" value="1.7"/>
-  	  	
+    <property name="j2sev" value="1.7+" />
+    <!-- Java Compilation settings - source and target javac version -->
+    <property name="javac.source" value="1.7" />
+    <property name="javac.target" value="1.7" />
+
     <!-- Permissions for running Java applets and applications. -->
     <!-- Defaults are those suitable for deploying jalview webstart www.jalview.org -->
     <property name="application.codebase" value="*.jalview.org" />
@@ -112,15 +128,15 @@
     <property name="packageDir" value="dist" />
     <property name="outputJar" value="jalview.jar" />
     <!-- Jalview Applet JMol Jar Dependency -->
-    <property name="jmolJar" value="JmolApplet-14.2.14_2015.06.11.jar" />
+    <property name="jmolJar" value="JmolApplet-14.6.4_2016.10.26.jar" />
     <property name="varnaJar" value="VARNAv3-93.jar" />
     <property name="jsoup" value="jsoup-1.8.1.jar" />
     <property name="jsonSimple" value="json_simple-1.1.jar" />
     <property name="javaJson" value="java-json.jar" />
     <property name="jalviewLiteJar" value="jalviewApplet.jar" />
-	<property name="reportDir" value="test-reports" />
-	<property name="testDir" value="test" />
-	<property name="testOutputDir" value="tests" />
+    <property name="reportDir" value="test-reports" />
+    <property name="testDir" value="test" />
+    <property name="testOutputDir" value="tests" />
     <!-- switch to indicate if we should obfuscate jalviewLite -->
     <!-- <property name="donotobfuscate" value="true"/> -->
     <!-- switch to exclude associations from generated jnlp files -->
@@ -145,7 +161,7 @@
         <include name="**/*.jar" />
       </fileset>
       <fileset dir="${java.home}/lib">
-        <include name="plugin.jar"/>
+        <include name="plugin.jar" />
       </fileset>
       <fileset dir="appletlib">
         <!-- the JmolApplet includes the JmolApplet console and the application javac seems to always try and build all packages 
@@ -153,7 +169,10 @@
         <include name="${jmolJar}" />
         <include name="${varnaJar}" />
       </fileset>
-
+    </path>
+    <path id="test.classpath">
+      <pathelement path="${outputDir}" />
+      <path refid="build.classpath" />
     </path>
     <property name="source.dist.name" value="${basedir}/jalview-src.tar.gz" />
     <!-- The Location of the java 1.1.8 jdk -->
@@ -162,7 +181,7 @@
     <!-- <property name="applet.jre.tools" value="${java118.home}/lib/classes.zip" />
 		-->
     <!-- jre for 1.4 version -->
-    <property name="applet.jre.tools" value="${java.home}/lib/rt.jar"/>
+    <property name="applet.jre.tools" value="${java.home}/lib/rt.jar" />
 
     <!-- the classpath for building the 1.1 applet -->
     <path id="jalviewlite.deps">
@@ -170,17 +189,17 @@
         <include name="lib/classes.zip" />
       </fileset>
       <fileset dir="${java.home}/lib">
-        <include name="plugin.jar"/>
+        <include name="plugin.jar" />
       </fileset>
       <pathelement location="appletlib/${jmolJar}" />
       <pathelement location="lib/${varnaJar}" />
-      <pathelement location="lib/${jsoup}" />
-      <pathelement location="lib/${jsonSimple}" />
-      <pathelement location="lib/${javaJson}" />
-      
+      <pathelement location="appletlib/${jsoup}" />
+      <pathelement location="appletlib/${jsonSimple}" />
+      <pathelement location="appletlib/${javaJson}" />
+
     </path>
     <!-- default location for outputting javadoc -->
-    <property name="javadocDir" value="${packageDir}/javadoc"/>
+    <property name="javadocDir" value="${packageDir}/javadoc" />
   </target>
 
 
@@ -189,20 +208,20 @@
     <tstamp prefix="build">
       <format property="date" pattern="dd MMMM yyyy" />
     </tstamp>
-    <exec executable="/usr/bin/git" outputproperty="git.commit"  failifexecutionfails="false">
-      <arg value="rev-parse"/>
-      <arg value="--short"/>
-      <arg value="HEAD"/>
+    <exec executable="/usr/bin/git" outputproperty="git.commit" failifexecutionfails="false">
+      <arg value="rev-parse" />
+      <arg value="--short" />
+      <arg value="HEAD" />
     </exec>
     <exec executable="/usr/bin/git" outputproperty="git.branch" failifexecutionfails="false">
-      <arg value="rev-parse"/>
-      <arg value="--abbrev-ref"/>
-      <arg value="HEAD"/>
+      <arg value="rev-parse" />
+      <arg value="--abbrev-ref" />
+      <arg value="HEAD" />
     </exec>
     <properties file="${outputDir}/.build_properties">
       <header>
           ---Jalview Build Details---
-        </header>    	
+        </header>
       <property name="VERSION" value="${JALVIEW_VERSION}" />
       <property name="INSTALLATION" value="${INSTALLATION} git-commit:${git.commit} [${git.branch}]" />
       <property name="BUILD_DATE" value="${build.date}" />
@@ -212,7 +231,7 @@
 
   <target name="clean" depends="init">
     <!-- not efficient yet. -->
-    <delete dir="${outputDir}" includes="*,**/*"/>
+    <delete dir="${outputDir}" includes="*,**/*" />
   </target>
 
   <target name="distclean" depends="init, clean">
@@ -255,57 +274,44 @@
       <exclude name="com/stevesoft/**" />
     </javac>
   </target>
-  
-  
+
+
   <target name="testclean" depends="init">
-    <delete dir="${testOutputDir}" includes="*,**/*"/>
+    <delete dir="${testOutputDir}" includes="*,**/*" />
+  </target>
+
+  <target name="prepareTests" depends="init,testclean">
+    <mkdir dir="${testOutputDir}" />
   </target>
-  
-  <target name="prepareTests" depends="init">
-	<mkdir dir="${testOutputDir}" />
-	<copy todir="${testOutputDir}">
-		<fileset dir=".">
-			<include name="${docDir}/**/*.*" />
-			<include name="${helpDir}/**/*.*" />
-			<include name="${libDir}/*.jar" />
-		</fileset>
-		<fileset dir="${resourceDir}">
-			<include name="**/*.*" />
-		</fileset>
-	</copy>
-  </target>  
-  
-  <target name="buildTests" depends="prepareTests">
-	<javac source="${javac.source}" target="${javac.target}" srcdir="${sourceDir}" destdir="${testOutputDir}"
-		debug="${javac.debug}" classpathref="build.classpath" includeantruntime="false" >		
-	</javac>
-	<javac source="${javac.source}" target="${javac.target}" srcdir="${testDir}" destdir="${testOutputDir}"
-		debug="${javac.debug}" classpathref="build.classpath" includeantruntime="false" >
-	</javac>
+
+  <target name="buildTests" depends="build,buildindices,prepareTests">
+    <javac source="${javac.source}" target="${javac.target}" srcdir="${testDir}" destdir="${testOutputDir}" debug="${javac.debug}" classpathref="test.classpath" includeantruntime="false">
+    </javac>
   </target>
-  
-  <taskdef name="testng" classname="org.testng.TestNGAntTask" >	    
-	<classpath location="utils/testnglibs/testng.jar" />
-  </taskdef>
-  
+
+  <taskdef resource="testngtasks" classpath="utils/testnglibs/testng.jar" />
+
   <target name="testng" depends="buildTests">
-	<testng classpathref="build.classpath" outputDir="${reportDir}"
-		haltOnFailure="false">
-		<classpath location="${testOutputDir}" />
-		<xmlfileset dir="utils" includes="jalview_testng.xml" />
-	</testng>
+    <testng outputDir="${reportDir}" haltOnFailure="false" groups="${testng-groups}" mode="testng"
+      verbose="2">
+      <classpath>
+        <pathelement location="${testOutputDir}" />
+        <path refid="test.classpath" />
+      </classpath>
+      <classfileset dir="${testOutputDir}" includes="**/*.class" />
+    </testng>
   </target>
-  
+
   <target name="buildindices" depends="init, prepare" unless="help.uptodate">
     <java classname="com.sun.java.help.search.Indexer" classpathref="build.classpath" fork="true" dir="${outputDir}/${helpDir}">
       <arg line="html" />
     </java>
   </target>
 
-  <target name="makefulldist" depends="makedist">
+  <target name="preparejnlp" depends="makedist">
     <copy todir="${packageDir}">
       <fileset dir="${resourceDir}/images">
-        <include name="${WebStartImage}"/>
+        <include name="${WebStartImage}" />
       </fileset>
     </copy>
 
@@ -318,35 +324,35 @@
       </fileset>
     </jar>
 
-    <mkdir dir="${packageDir}/JNLP-INF"/>
+    <mkdir dir="${packageDir}/JNLP-INF" />
     <antcall target="writejnlpf">
-      <param name="jnlpFile" value="${packageDir}/JNLP-INF/APPLICATION-TEMPLATE.JNLP"/>
+      <param name="jnlpFile" value="${packageDir}/JNLP-INF/APPLICATION-TEMPLATE.JNLP" />
       <param name="inih" value="*" />
-      <param name="maxh" value="*"/>
+      <param name="maxh" value="*" />
     </antcall>
 
     <jar destfile="${packageDir}/jalview_jnlp_vm.jar" index="true">
       <fileset dir="${packageDir}">
-        <include name="JNLP-INF"/>
+        <include name="JNLP-INF/APPLICATION-TEMPLATE.JNLP" />
       </fileset>
     </jar>
 
     <antcall target="writejnlpf">
-      <param name="jnlpFile" value="${packageDir}/jalview.jnlp"/>
+      <param name="jnlpFile" value="${packageDir}/jalview.jnlp" />
       <param name="inih" value="10M" />
-      <param name="maxh" value="256M"/>
+      <param name="maxh" value="256M" />
     </antcall>
 
     <antcall target="writejnlpf">
-      <param name="jnlpFile" value="${packageDir}/jalview_1G.jnlp"/>
+      <param name="jnlpFile" value="${packageDir}/jalview_1G.jnlp" />
       <param name="inih" value="128M" />
-      <param name="maxh" value="512M"/>
+      <param name="maxh" value="512M" />
     </antcall>
 
     <antcall target="writejnlpf">
-      <param name="jnlpFile" value="${packageDir}/jalview_2G.jnlp"/>
+      <param name="jnlpFile" value="${packageDir}/jalview_2G.jnlp" />
       <param name="inih" value="256M" />
-      <param name="maxh" value="1024M"/>
+      <param name="maxh" value="1024M" />
     </antcall>
 
     <!-- finally, need to postprocess to add in associations at end of 'information' element 
@@ -370,15 +376,29 @@
         <association mime-type="application-x/ext-file" extensions="amsa"/>
         <association mime-type="application-x/ext-file" extensions="stk"/>
         <association mime-type="application-x/ext-file" extensions="jar"/>-->
-    <!-- and sign the jars -->
-    <!-- the default keystore details might need to be edited here -->
-    <signjar storepass="${jalview.keystore.pass}" keypass="${jalview.key.pass}" keystore="${jalview.keystore}" alias="${jalview.key}" lazy="false" verbose="false" sigalg="SHA1withRSA">
+  </target>
+
+  <target name="-jarsignwithtsa" depends="makedist,preparejnlp" if="timestamp">
+    <signjar storepass="${jalview.keystore.pass}" keypass="${jalview.key.pass}" keystore="${jalview.keystore}" alias="${jalview.key}" lazy="false" verbose="false" sigalg="${jalview.keyalg}" digestalg="${jalview.keydig}"
+      tsaproxyhost="${proxyHost}" tsaproxyport="${proxyPort}" tsaurl="${jalview.tsaurl}">
+      <fileset dir="${packageDir}">
+        <include name="*.jar" />
+      </fileset>
+    </signjar>
+  </target>
+  <target name="-jarsignnotsa" depends="makedist,preparejnlp" unless="timestamp">
+    <signjar storepass="${jalview.keystore.pass}" keypass="${jalview.key.pass}" keystore="${jalview.keystore}" alias="${jalview.key}" lazy="false" verbose="false" sigalg="${jalview.keyalg}" digestalg="${jalview.keydig}">
       <fileset dir="${packageDir}">
         <include name="*.jar" />
       </fileset>
     </signjar>
   </target>
 
+  <target name="makefulldist" depends="makedist,preparejnlp,-jarsignwithtsa,-jarsignnotsa">
+    <!-- and sign the jars -->
+    <!-- the default keystore details might need to be edited here -->
+  </target>
+
   <target name="runenv" depends="init">
     <path id="run.classpath">
       <pathelement location="${outputDir}" />
@@ -406,23 +426,21 @@
         </information>
         <resources>
           <j2se version="${j2sev}" initial_heap_size="${inih}" max_heap_size="${maxh}" />
+          <jar main="true" href="jalview.jar"/>
           <fileset dir="${packageDir}">
-            <include name="jalview.jar" />
-          </fileset>
-          <fileset dir="${packageDir}">
+            <exclude name="jalview.jar" />
             <include name="*.jar" />
             <include name="*_*.jar" />
-            <exclude name="jalview.jar" />
-          	<exclude name="*jnilib.jar"/>
+            <exclude name="*jnilib.jar" />
           </fileset>
-      	</resources>
-        <resources os="Mac OS X">
           <property name="jalview.version" value="${JALVIEW_VERSION}" />
-        	<fileset dir="${packageDir}">
-        		<include name="*quaqua*.jnilib.jar"/>
-        	</fileset>
         </resources>
-      	
+        <resources os="Mac OS X">
+          <fileset dir="${packageDir}">
+            <include name="*quaqua*.jnilib.jar" />
+          </fileset>
+        </resources>
+
         <application_desc main_class="jalview.bin.Jalview">
         </application_desc>
         <security>
@@ -431,12 +449,12 @@
       </jnlp>
     </presetdef>
 
-    <jnlpf toFile="${jnlpFile}"/>
+    <jnlpf toFile="${jnlpFile}" />
 
   </target>
 
   <target name="-dofakejnlpfileassoc" depends="-generatejnlpf" if="nojnlpfileassocs">
-    <echo message="Not adding JNLP File Associations"/>
+    <echo message="Not adding JNLP File Associations" />
   </target>
 
   <target name="-dojnlpfileassoc" depends="-generatejnlpf" unless="nojnlpfileassocs">
@@ -459,7 +477,7 @@
         <association mime-type="application-x/ext-file" extensions="jvp"/>
       </information>]]></replacevalue>
   </replace>
-  <echo message="Added file associations to JNLP file"/>
+  <echo message="Added file associations to JNLP file" />
 </target>
 <target name="writejnlpf" depends="-dojnlpfileassoc,-dofakejnlpfileassoc">
 </target>
@@ -503,13 +521,13 @@
   </axis-wsdl2java>
 </target>
 
-<target name="makedist" depends="build, buildPropertiesFile, buildindices">
+<target name="makedist" depends="build, buildPropertiesFile, linkcheck, buildindices">
   <!-- make the package jar if not already existing -->
   <mkdir dir="${packageDir}" />
   <!-- clean dir if it already existed -->
   <delete>
     <fileset dir="${packageDir}">
-      <include name="*.jar"/>
+      <include name="*.jar" />
     </fileset>
   </delete>
   <jar destfile="${packageDir}/${outputJar}" index="true">
@@ -551,13 +569,11 @@
 
 
 <!-- Compile, package and obfuscate Jalview Applet -->
-<target name="makeApplet" depends="obfuscate" description="assemble the final jalviewLite applet jar with or without obfuscation"/>
+<target name="makeApplet" depends="obfuscate" description="assemble the final jalviewLite applet jar with or without obfuscation" />
 
 <target name="compileApplet" depends="init,clean">
   <mkdir dir="${outputDir}" />
-  <javac source="${javac.source}" target="${javac.target}" srcdir="${sourceDir}" destdir="${outputDir}" debug="${javac.debug}" 
-			classpathref="jalviewlite.deps" includes="jalview/appletgui/**"
-			excludes="ext/**,gui/**,jbgui/**,MCview/**,org/**,vamsas/**,jalview/ext/rbvi/**,jalview/ext/paradise/**" />
+  <javac source="${javac.source}" target="${javac.target}" srcdir="${sourceDir}" destdir="${outputDir}" debug="${javac.debug}" classpathref="jalviewlite.deps" includes="jalview/appletgui/**" excludes="ext/**,gui/**,jbgui/**,MCview/**,org/**,vamsas/**,jalview/ext/rbvi/**,jalview/ext/paradise/**,jalview/ext/ensembl/**,jalview/ext/so/**" />
 </target>
 
 <target name="packageApplet" depends="compileApplet, buildPropertiesFile">
@@ -565,7 +581,7 @@
   <copy file="${resourceDir}/images/link.gif" toFile="${outputDir}/images/link.gif" />
   <copy todir="${outputDir}/lang">
     <fileset dir="${resourceDir}/lang">
-      <include name="**.*"/>
+      <include name="**.*" />
     </fileset>
   </copy>
   <jar destfile="in.jar" index="true">
@@ -596,16 +612,21 @@
   <path id="obfuscateDeps.path">
     <pathelement location="${applet.jre.tools}" />
     <pathelement location="appletlib/${jmolJar}" />
+    <pathelement location="lib/${varnaJar}" />
+    <pathelement location="appletlib/${jsoup}" />
     <pathelement location="appletlib/${jsonSimple}" />
     <pathelement location="appletlib/${javaJson}" />
+    <fileset dir="${java.home}/lib">
+      <include name="plugin.jar" />
+    </fileset>
   </path>
   <taskdef resource="proguard/ant/task.properties" classpath="utils/proguard.jar" />
 
-  <proguard>
+  <proguard verbose="true" >
     <injar file="in.jar" />
     <outjar file="${jalviewLiteJar}" />
     <libraryjar refid="obfuscateDeps.path" />
-    <dontwarn/>
+    <dontwarn />
     <keep access="public" type="class" name="jalview.bin.JalviewLite">
       <field access="public" />
       <method access="public" />
@@ -616,6 +637,31 @@
       <method access="public" />
       <constructor access="public" />
     </keep>
+
+    <keep name="jalview.json.binding**">
+      <constructor/>
+      <method name="*"/>
+    </keep>
+
+    <keep access="public" type="class" name="MCview.PDBfile">
+      <field access="public" />
+      <method access="public" />
+      <constructor access="public" />
+    </keep>
+
+    <keep access="public" type="class" name="jalview.ws.jws1.Annotate3D">
+      <field access="public" />
+      <method access="public" />
+      <constructor access="public" />
+    </keep>
+
+    <keep access="public" type="class" name="jalview.ext.jmol.JmolParser">
+      <field access="public" />
+      <method access="public" />
+      <constructor access="public" />
+    </keep>
+
+
     <!--      -libraryjars "${obfuscateDeps}"
       -injars      in.jar
       -outjars     jalviewApplet.jar
@@ -651,18 +697,23 @@
 </target>
 <target name="sourcedist" description="create jalview source distribution" depends="init">
   <delete file="${source.dist.name}" />
-  <tar destfile="${source.dist.name}" compression="gzip">
-    <tarfileset dir="./" prefix="jalview" preserveLeadingSlashes="true">
+  <!-- temporary copy of source to update timestamps -->
+  <copy todir="_sourcedist">
+    <fileset dir=".">
+      <exclude name=".*" />
+      <exclude name="**/.*" />
+      <exclude name="*.class" />
+      <exclude name="**/*.class" />
       <include name="LICENSE" />
       <include name="README" />
       <include name="build.xml" />
       <include name="jalview-jalopy.xml" />
       <include name="JalviewApplet.jpx" />
       <include name="JalviewX.jpx" />
-      <include name="nbbuild.xml"/>
-      <include name="nbproject/genfiles.properties"/>
-      <include name="nbproject/project.properties"/>
-      <include name="nbproject/project.xml"/>
+      <include name="nbbuild.xml" />
+      <include name="nbproject/genfiles.properties" />
+      <include name="nbproject/project.properties" />
+      <include name="nbproject/project.xml" />
       <include name="${sourceDir}/*.java" />
       <include name="${sourceDir}/**/*.java" />
       <include name="${sourceDir}/**/*.cdr" />
@@ -670,51 +721,76 @@
       <include name="${resourceDir}/**/*" />
       <include name="${helpDir}/**/*" />
       <include name="appletlib/${jmolJar}" />
+      <include name="appletlib/${jsonSimple}" />
+      <include name="appletlib/${javaJson}" />
       <exclude name="**/*locales" />
       <exclude name="*locales/**" />
       <exclude name="utils/InstallAnywhere/**Build.iap_xml" />
       <exclude name="utils/InstallAnywhere/**Build*/**" />
       <exclude name="utils/InstallAnywhere/**Build*/**" />
+      <exclude name="utils/InstallAnywhere/.build*.*/**" />
       <exclude name="utils/InstallAnywhere/**locale*" />
       <exclude name="utils/InstallAnywhere/**locale*/**" />
+      <exclude name="utils/InstallAnywhere/**locale*/**" />
       <include name="${schemaDir}/**/*" />
       <include name="utils/**/*" />
       <include name="${docDir}/**/*" />
       <include name="examples/**/*" />
+    </fileset>
+  </copy>
+
+  <tstamp prefix="build">
+    <format property="year" pattern="yyyy" />
+  </tstamp>
+  <!-- each replacetoken CDATA body must be on one line - 
+       otherwise the pattern doesn't match -->
+  <replace value="${JALVIEW_VERSION}">
+    <replacetoken><![CDATA[2.10.1]]></replacetoken>
+    <fileset dir="_sourcedist">
+      <include name="**/*" />
+    </fileset>
+  </replace>
+  <replace dir="_sourcedist" value="${build.year}">
+    <replacetoken><![CDATA[2016]]></replacetoken>
+    <fileset dir="_sourcedist">
+      <include name="**/*" />
+    </fileset>
+  </replace>
+
+  <tar destfile="${source.dist.name}" compression="gzip">
+    <tarfileset dir="_sourcedist/" prefix="jalview" preserveLeadingSlashes="true">
     </tarfileset>
   </tar>
+
+  <delete dir="_sourcedist" />
 </target>
-<target name="pubapplet" description="installs the jalviewLite applet and dependent jars into an applet examples directory built under ${outputDir}" depends="makeApplet">
+<target name="prepubapplet_1" depends="makeApplet">
   <copy todir="${packageDir}/examples">
     <fileset dir="examples">
-      <include name="**/*"/>
-      <include name="javascript/*"/>
-      <include name="jmol/*"/>
+      <include name="**/*" />
+      <include name="javascript/*" />
+      <include name="jmol/*" />
     </fileset>
     <fileset dir=".">
       <include name="${jalviewLiteJar}" />
     </fileset>
     <fileset dir="appletlib">
-      <include name="**/*"/>
+      <include name="**/*" />
     </fileset>
   </copy>
-  <jar update="true" index="true" jarfile="${packageDir}/examples/${jalviewLiteJar}"/>
-  <jar update="true" index="true" jarfile="${packageDir}/examples/${javaJson}"/>
-  <jar update="true" index="true" jarfile="${packageDir}/examples/${jsonSimple}"/>
+  <jar update="true" index="true" jarfile="${packageDir}/examples/${jalviewLiteJar}" />
+  <jar update="true" index="true" jarfile="${packageDir}/examples/${javaJson}" />
+  <jar update="true" index="true" jarfile="${packageDir}/examples/${jsonSimple}" />
   <jar update="true" index="true" jarfile="${packageDir}/examples/${jmolJar}">
     <manifest>
-      <attribute name="Application-Name" value="Jmol (bundled with JalviewLite)"/>
+      <attribute name="Application-Name" value="Jmol (bundled with JalviewLite)" />
       <!--          <attribute name="Permissions" value="sandbox" /> -->
       <!--<attribute name="Trusted-Lib" value="true" /> -->
-      <attribute name="Codebase" value="${applet.codebase}"/>
-      <attribute name="Caller-Allowable-Codebase" value="${applet.caller-codebase}"/>
+      <attribute name="Codebase" value="${applet.codebase}" />
+      <attribute name="Caller-Allowable-Codebase" value="${applet.caller-codebase}" />
     </manifest>
   </jar>
-  <signjar sigalg="SHA1WithRSA" storepass="${jalview.keystore.pass}" keypass="${jalview.key.pass}" keystore="${jalview.keystore}" alias="${jalview.key}" lazy="false" verbose="false">
-    <fileset dir="${packageDir}/examples">
-      <include name="*.jar" />
-    </fileset>
-  </signjar>
+
   <presetdef name="ap_applet.jar">
     <!-- build a signed applet with 'all-permissions' - 
 			  Needs 'param name="permissions' value="all-permissions"' in applet tag
@@ -780,13 +856,13 @@
   </presetdef>
   <!-- create differently privileged artefacts -->
   <copy file="${packageDir}/examples/${jalviewLiteJar}" tofile="${packageDir}/examples/u_${jalviewLiteJar}" />
-  <copy file="${packageDir}/examples/${jmolJar}" tofile="${packageDir}/examples/u_${jmolJar}" overwrite="true"/>
-  <copy file="${packageDir}/examples/${javaJson}" tofile="${packageDir}/examples/u_${javaJson}" overwrite="true"/>
-  <copy file="${packageDir}/examples/${jsonSimple}" tofile="${packageDir}/examples/u_${jsonSimple}" overwrite="true"/>
+  <copy file="${packageDir}/examples/${jmolJar}" tofile="${packageDir}/examples/u_${jmolJar}" overwrite="true" />
+  <copy file="${packageDir}/examples/${javaJson}" tofile="${packageDir}/examples/u_${javaJson}" overwrite="true" />
+  <copy file="${packageDir}/examples/${jsonSimple}" tofile="${packageDir}/examples/u_${jsonSimple}" overwrite="true" />
   <copy file="${packageDir}/examples/${jalviewLiteJar}" tofile="${packageDir}/examples/ap_${jalviewLiteJar}" />
-  <copy file="${packageDir}/examples/${jmolJar}" tofile="${packageDir}/examples/ap_${jmolJar}"/>
-  <copy file="${packageDir}/examples/${javaJson}" tofile="${packageDir}/examples/ap_${javaJson}"/>
-  <copy file="${packageDir}/examples/${jsonSimple}" tofile="${packageDir}/examples/ap_${jsonSimple}"/>
+  <copy file="${packageDir}/examples/${jmolJar}" tofile="${packageDir}/examples/ap_${jmolJar}" />
+  <copy file="${packageDir}/examples/${javaJson}" tofile="${packageDir}/examples/ap_${javaJson}" />
+  <copy file="${packageDir}/examples/${jsonSimple}" tofile="${packageDir}/examples/ap_${jsonSimple}" />
   <ap_applet.jar jarfile="${packageDir}/examples/ap_${jalviewLiteJar}" />
   <ap_applet.jar jarfile="${packageDir}/examples/ap_${jmolJar}" />
   <ap_applet.jar jarfile="${packageDir}/examples/ap_${javaJson}" />
@@ -813,44 +889,67 @@
   <applet.jar jarfile="${packageDir}/examples/${javaJson}" />
   <applet.jar jarfile="${packageDir}/examples/${jsonSimple}" />
 
-  <!-- todo - write examples/downloads for alternate versions of the applet -->
+  <!-- todo - write examples/downloads for alternate versions of the applet 
+    probably don't need to do this now since we don't have alternate versions anymore !-->
+</target>
+<target name="-signapplet" depends="prepubapplet_1">
+  <fileset id="signappletjarset" dir="${packageDir}/examples">
+    <exclude name="u_*.jar" />
+    <include name="${jalviewLiteJar}" />
+    <include name="${jmolJar}" />
+    <include name="${javaJson}" />
+    <include name="${jsonSimple}" />
+    <include name="to_${jalviewLiteJar}" />
+    <include name="to_${jmolJar}" />
+    <include name="to_${javaJson}" />
+    <include name="to_${jsonSimple}" />
+    <include name="tl_${jalviewLiteJar}" />
+    <include name="tl_${jmolJar}" />
+    <include name="tl_${javaJson}" />
+    <include name="tl_${jsonSimple}" />
+    <include name="ap_${jalviewLiteJar}" />
+    <include name="ap_${jmolJar}" />
+    <include name="ap_${javaJson}" />
+    <include name="ap_${jsonSimple}" />
+  </fileset>
+</target>
+<target name="-signappletnotsa" unless="timestamp" depends="-signapplet">
   <signjar storepass="${jalview.keystore.pass}" keypass="${jalview.key.pass}" keystore="${jalview.keystore}" alias="${jalview.key}" lazy="false" verbose="false">
+    <fileset refid="signappletjarset" />
+  </signjar>
+</target>
 
-    <fileset dir="${packageDir}/examples">
-      <exclude name="u_*.jar"/>
-      <include name="${jalviewLiteJar}" />
-      <include name="${jmolJar}" />
-      <include name="${javaJson}" />
-      <include name="${jsonSimple}" />
-      <include name="to_${jalviewLiteJar}" />
-      <include name="to_${jmolJar}" />
-      <include name="to_${javaJson}" />
-      <include name="to_${jsonSimple}" />
-      <include name="tl_${jalviewLiteJar}" />
-      <include name="tl_${jmolJar}" />
-      <include name="tl_${javaJson}" />
-      <include name="tl_${jsonSimple}" />
-      <include name="ap_${jalviewLiteJar}" />
-      <include name="ap_${jmolJar}" />
-      <include name="ap_${javaJson}" />
-      <include name="ap_${jsonSimple}" />
-    </fileset>
+<target name="-signapplettsa" if="timestamp" depends="-signapplet">
+  <signjar storepass="${jalview.keystore.pass}" keypass="${jalview.key.pass}" keystore="${jalview.keystore}" alias="${jalview.key}" lazy="false" verbose="false" tsaproxyhost="${proxyHost}" tsaproxyport="${proxyPort}" tsaurl="${jalview.tsaurl}">
+    <fileset refid="signappletjarset" />
   </signjar>
+</target>
+
+<target name="signApplet" description="internal target to sign applet" depends="-signappletnotsa,-signapplettsa" />
+
+<target name="pubapplet" description="installs the jalviewLite applet and dependent jars into an applet examples directory built under ${outputDir}" depends="makeApplet, signApplet">
+
   <!-- bizarre bug causes JmolApplet to always get signed, even if excluded from above. so copy explicitly -->
   <copy file="appletlib/${jmolJar}" tofile="${packageDir}/examples/u_${jmolJar}" overwrite="true" />
-	<!-- finally, replace any launchApp servlet tags with a version specification -->
-	<replace value="http://www.jalview.org/services/launchApp?version=${JALVIEW_VERSION}"">
-		<replacetoken><![CDATA[http://www.jalview.org/services/launchApp"]]></replacetoken>
-		<fileset dir="${packageDir}/examples">
-			<include name="**/*.html"/>
-		</fileset>
-	</replace>
-	<replace value="http://www.jalview.org/services/launchApp?version=${JALVIEW_VERSION}'">
-		<replacetoken><![CDATA[http://www.jalview.org/services/launchApp']]></replacetoken>
-		<fileset dir="${packageDir}/examples">
-			<include name="**/*.html"/>
-		</fileset>
-	</replace>
+  <copy file="appletlib/${jsonSimple}" tofile="${packageDir}/examples/u_${jsonSimple}" overwrite="true" />
+  <copy file="appletlib/${javaJson}" tofile="${packageDir}/examples/u_${javaJson}" overwrite="true" />
+  <!-- finally, replace any launchApp servlet tags with a version specification -->
+  <replace value="http://www.jalview.org/services/launchApp?version=${JALVIEW_VERSION}"">
+    <replacetoken>
+      <![CDATA[http://www.jalview.org/services/launchApp"]]>
+    </replacetoken>
+    <fileset dir="${packageDir}/examples">
+      <include name="**/*.html" />
+    </fileset>
+  </replace>
+  <replace value="http://www.jalview.org/services/launchApp?version=${JALVIEW_VERSION}'">
+    <replacetoken>
+      <![CDATA[http://www.jalview.org/services/launchApp']]>
+    </replacetoken>
+    <fileset dir="${packageDir}/examples">
+      <include name="**/*.html" />
+    </fileset>
+  </replace>
 
 </target>
 <target name="sourcedoc" description="Create jalview source documentation pages" depends="init">
@@ -859,4 +958,11 @@
     </packageset>
   </javadoc>
 </target>
+<target name="linkcheck" depends="init,prepare">
+  <javac srcdir="utils" destdir="utils" includes="HelpLinksChecker.java"/>
+  <java fork="true" dir="${helpDir}" classpath="utils" classname="HelpLinksChecker" failonerror="true">
+    <arg file="${helpDir}"/>
+    <arg value="-nointernet"/>
+  </java>
+</target>
 </project>
diff --git a/doc/AddingGroovySupport.html b/doc/AddingGroovySupport.html
index e71c53b..3e10fd8 100644
--- a/doc/AddingGroovySupport.html
+++ b/doc/AddingGroovySupport.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,35 +21,21 @@
 </title>
 <body>
 <h1>
-Adding Groovy Support to Jalview
+Groovy Support in Jalview
 </h1>
 <p>
-There is currently no scripting language 
-extension within Jalview, in part because a 
-scripting API has not yet been developed.
-</p>
-<p>It is, however, really easy to embed scripting
-engines like groovy. If groovy is detected on the 
-classpath, a new menu entry on the Desktop's Tools 
-menu will open the GroovyShell.
+  <a href="http://www.groovy-lang.org">Groovy</a> has been bundled with the Jalview desktop since circa 2012. The program supports interactive execution of groovy scripts via the Groovy Console, and command line execution via the '-groovy' option. The main source for documentation about Groovy in Jalview is the <a href="http://www.jalview.org/help/html/features/groovy.html">online help pages</a>.
 </p>
 <p>Here are some scripts to get you started:</p>
 <ul><li>Getting the title, alignment and first sequence from the current alignFrame<br>
 <pre>
-def alf = Jalview.getAlignframes();
+def alf = Jalview.getAlignFrames();
 print alf[0].getTitle();
 def alignment = alf[0].viewport.alignment;
 def seq = alignment.getSequenceAt(0);
 </pre>
 </li>
 </ul>
-<h1>Getting Groovy...</h1>
-<p>
-You need the core groovy jars which include the GroovyShell. The easiest way of doing
-this is to add the groovy-all-*.jar to the lib directory whose path is given in the java.ext.dirs property.</p>
-<p>The is obtained from the <em>embedded</em> directory within the <a 
-href="http://dist.codehaus.org/groovy/distributions"/>groovy distribution</a>).
-</p>
 <h2>TODO</h2>
 <p>
 Using Java class methods from Groovy is straightforward, but currently, there isn't a set of easy to use methods for the jalview objects. A Jalview Scripting API needs to be developed to make this easier.</p>
diff --git a/doc/AnnotationPostAnalysis.txt b/doc/AnnotationPostAnalysis.txt
index 014cb73..2fd0997 100644
--- a/doc/AnnotationPostAnalysis.txt
+++ b/doc/AnnotationPostAnalysis.txt
@@ -1,6 +1,6 @@
 ##
-# Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
-# Copyright (C) 2015 The Jalview Authors
+# Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+# Copyright (C) 2016 The Jalview Authors
 # 
 # This file is part of Jalview.
 # 
diff --git a/doc/JalviewRNASupport.html b/doc/JalviewRNASupport.html
index 2e610d9..51102c7 100644
--- a/doc/JalviewRNASupport.html
+++ b/doc/JalviewRNASupport.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/doc/UnitTesting.html b/doc/UnitTesting.html
index 3f1eb5a..c16bb42 100644
--- a/doc/UnitTesting.html
+++ b/doc/UnitTesting.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -24,7 +24,7 @@
 <h1> Unit testing in Jalview </h1>
 
 <p>
-In June 2015, the Jalview team adopted <a href=http://testng.org/doc/index.html>TestNG</a> as the favourite unit testing framework. Consequently all existing JUnit tests were ported to TestNG.
+In June 2015, the Jalview team adopted <a href=http://testng.org/doc/index.html>TestNG</a> for handling unit tests, and all existing JUnit tests were ported to TestNG.
 
 
 <h2>Test Groups</h2>
@@ -83,18 +83,17 @@ The TestNG tests for Jalview can be executed in any of the following ways:
 	<ul>
             <li>Ensure that you have TestNG plugin correctly install for eclipse</li>
             <li>Ensure that your test classes are error free and properly annotated</li>
-            <li>Create a lunch configuration "Select the Run / Run... (or Run / Debug...) menu and create a new TestNG configuration"</li>
+            <li>Create a launch configuration "Select the Run / Run... (or Run / Debug...) menu and create a new TestNG configuration"</li>
 	    <li>Run the tests by executing the configuration target created above</li>
         </ul> 
 	A more detailed guide for installing and executing TestNG in eclipse is available at <a href=http://testng.org/doc/eclipse.html>testng.org/doc/eclipse.html</a> <br> 
   </li>
   <li><b>From Ant:</b> 
-        </br> To execute Jalview unit test from ant please take the following steps:
+        </br>The ant task 'testng' will run Jalview's tests. You should:
 	<ul>
             <li>Ensure that you have ant installed</li>
             <li>Ensure that your test classes are error free and properly annotated</li>
-            <li>Ensure that the test class or group is available in the TestNG config file. For Jalview this is located in jalview/utils/jalview_testng.xml</li>
-	    <li>Add a TestNG run target to your main ant build file, and execute the target.</li>
+            <li>Specify the desired group of tests by passing a comma separated list of one or more groups via -Dtestng-groups=""</li>
         </ul> 
         A more detailed guide for executing TestNG from ant is available at <a href=http://testng.org/doc/ant.html>http://testng.org/doc/ant.html</a>
   </li>
diff --git a/doc/biojsmsa-templates.html b/doc/biojsmsa-templates.html
index 50da663..f675352 100644
--- a/doc/biojsmsa-templates.html
+++ b/doc/biojsmsa-templates.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- *Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- *Copyright (C) 2015 The Jalview Authors
+ *Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ *Copyright (C) 2016 The Jalview Authors
  *
  *This file is part of Jalview.
  *
diff --git a/doc/building.html b/doc/building.html
index 5b9158a..6f789e3 100644
--- a/doc/building.html
+++ b/doc/building.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,13 +26,12 @@
 <p>
 You will need the following (hopefully):<br>
 <ul>
-<li>Java development kit (JDK1.6 is the recommended platform for developing with Jalview, although JDK1.7 seems to work too!).</li>
-<li>Ant (we think 1.5.4 is quite sufficient to use the simple build
-file supplied, and it seems to work with later versions e.g. 1.7).</li>
+<li>Java development kit (JDK1.8 is now the recommended platform for developing with Jalview.</li>
+<li>Ant (1.7 or later will be needed for some of the jarsigning tools).</li>
 </ul>
 With any luck, after setting your paths and JAVA_HOME correctly, you
 just need to change to the Jalview directory and run ant (this works
-from JBuilder and eclipse too, but NetBeans is a bit trickier).
+from eclipse too, but NetBeans is a bit trickier).
 <pre>
    ant
 </pre>
@@ -46,23 +45,24 @@ build target in ant to make the signed jar files in a directory called
 dist. But first you need to make your own key:
 <p><strong>Making your own key</strong></p>
 
-<p>The ant 'makefulldist' target assumes that a keystore exists in a
-directory 'keys'. To make a key accessible using the default settings
-in the build.xml file then make the keys directory and add the
-jarsigner key with the following :
-</p>
-<pre>
-mkdir keys
-keytool -genkey -keystore keys/.keystore -keypass alignmentisfun
--storepass alignmentisfun -alias jalview
- (you will have to answer some personal questions here)
-ant makedist
- (should eventually generate a Jalview.jnlp file
-  in ./dist along with a set of signed jars using the jalview
-  key)
-</pre>
-
-	<p>
+  <p>The ant 'makefulldist' target assumes that a keystore exists in
+    a directory 'keys'. To make a key accessible using the default
+    settings in the build.xml file then make the keys directory and add
+    the jarsigner key with the following :</p>
+  <pre>mkdir keys</pre>
+  <pre>keytool -genkey -keystore keys/.keystore -keypass alignmentisfun
+  -storepass alignmentisfun -sigalg SHA1withRSA -keyalg RSA -alias jalview</pre>
+  <em>(you will have to answer some personal questions here)</em>
+  <pre>ant makedist -DWebStartLocation="file://.pathtojalviewsource./dist" -Dapplication.codebase="*"</pre>
+  <p>This should eventually generate a jalview.jnlp file in ./dist
+    along with a set of signed jars using the jalview key). In order to
+    test locally via webstart you'll now need to add 'file:/' to your
+    java webstart security exception list. Then:</p>
+  <pre>javaws file://.pathtojalviewsource./dist/jalview.jnlp</pre>
+  <p>Please remember to remove that entry afterwards, since it will leave
+  your system vulnerable to malicious code.
+  </p>
+  <p>
 		<strong>Building the JalviewLite applet<br>
 		</strong> The JalviewLite applet is compiled using a subset of the packages in
 		the src directory (specifically: MCView, and jalview.{datamodel,
diff --git a/doc/developing.html b/doc/developing.html
index fd6ba1f..46de0b2 100644
--- a/doc/developing.html
+++ b/doc/developing.html
@@ -1,7 +1,27 @@
+<!--
+ *Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ *Copyright (C) 2016 The Jalview Authors
+ *
+ *This file is part of Jalview.
+ *
+ *Jalview is free software: you can redistribute it and/or
+ *modify it under the terms of the GNU General Public License 
+ *as published by the Free Software Foundation, either version 3
+ *of the License, or (at your option) any later version.
+ * 
+ *Jalview is distributed in the hope that it will be useful, but 
+ *WITHOUT ANY WARRANTY; without even the implied warranty 
+ *of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ *PURPOSE.  See the GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ *The Jalview Authors are detailed in the 'AUTHORS' file.
+-->
 <!DOCTYPE html SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/doc/i18n.html b/doc/i18n.html
index a985524..91d4cfb 100644
--- a/doc/i18n.html
+++ b/doc/i18n.html
@@ -1,6 +1,6 @@
 <!--
- *Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- *Copyright (C) 2015 The Jalview Authors
+ *Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ *Copyright (C) 2016 The Jalview Authors
  *
  *This file is part of Jalview.
  *
diff --git a/doc/index.html b/doc/index.html
index 1f24939..4b01bbe 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -1,7 +1,27 @@
+<!--
+ *Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ *Copyright (C) 2016 The Jalview Authors
+ *
+ *This file is part of Jalview.
+ *
+ *Jalview is free software: you can redistribute it and/or
+ *modify it under the terms of the GNU General Public License 
+ *as published by the Free Software Foundation, either version 3
+ *of the License, or (at your option) any later version.
+ * 
+ *Jalview is distributed in the hope that it will be useful, but 
+ *WITHOUT ANY WARRANTY; without even the implied warranty 
+ *of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ *PURPOSE.  See the GNU General Public License for more details.
+ *
+ *You should have received a copy of the GNU General Public License
+ *along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ *The Jalview Authors are detailed in the 'AUTHORS' file.
+-->
 <!DOCTYPE html SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/doc/newdmobj.html b/doc/newdmobj.html
index f10c9d3..d4d5f8a 100644
--- a/doc/newdmobj.html
+++ b/doc/newdmobj.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/help.hs b/help/help.hs
index 6115dea..620fd68 100644
--- a/help/help.hs
+++ b/help/help.hs
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="ISO-8859-1" ?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/help.jhm b/help/help.jhm
index 357844b..d7f7d73 100644
--- a/help/help.jhm
+++ b/help/help.jhm
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="ISO-8859-1" ?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,7 +22,7 @@
    <mapID target="home" url="html/index.html" />
    
    <mapID target="new" url="html/whatsNew.html"/>
-   <mapID target="release" url="html/releases.html#Jalview.2.9"/>
+   <mapID target="release" url="html/releases.html#Jalview.2.10.1"/>
    <mapID target="alannotation" url="html/features/annotation.html"/>
    <mapID target="keys" url="html/keys.html"/>
    <mapID target="newkeys" url="html/features/newkeystrokes.html"/>
@@ -130,6 +130,7 @@
    
    <mapID target="memory" url="html/memory.html" />
    <mapID target="groovy" url="html/features/groovy.html" />
+   <mapID target="groovy.featurecounter" url="html/groovy/featureCounter.html" />
    <mapID target="privacy" url="html/privacy.html" />
    <mapID target="vamsas" url="html/vamsas/index.html"/>
    <mapID target="aminoAcids" url="html/misc/aminoAcids.html" />
@@ -140,10 +141,15 @@
    
    <mapID target="biojson" url="html/features/bioJsonFormat.html" />
    <mapID target="pdbfetcher" url="html/features/pdbsequencefetcher.html" />
+   <mapID target="siftsmapping" url="html/features/siftsmapping.html" />
    <mapID target="pdbchooser" url="html/features/structurechooser.html" />
    <mapID target="selectcolbyannot" url="html/features/columnFilterByAnnotation.html" />
    <mapID target="biojsmsa" url="html/features/biojsmsa.html" />
    
+   <mapID target="ensemblfetch" url="html/features/ensemblsequencefetcher.html" />
+   
+   <mapID target="uniprotfetcher" url="html/features/uniprotsequencefetcher.html" />
+   
    <mapID target="backIcon" url="icons/back.png" />
    <mapID target="forwardIcon" url="icons/forward.png" />
    <mapID target="homeIcon" url="icons/Home.png" />
diff --git a/help/helpTOC.html b/help/helpTOC.html
index ccc4406..a17b559 100644
--- a/help/helpTOC.html
+++ b/help/helpTOC.html
@@ -4,12 +4,7 @@
 
 <br><a href="html/index.html" target=bodyframe>Jalview Documentation</a>
 <br>   <a href="html/whatsNew.html" target=bodyframe>What's new</a>
-<br>      <a href="html/features/splitView.html" target=bodyframe>Split Frame View</a>
-<br>      <a href="html/features/pdbsequencefetcher.html" target=bodyframe>PDB Sequence Fetcher</a>
-<br>      <a href="html/features/structurechooser.html" target=bodyframe>PDB Structure Chooser</a>
-<br>      <a href="html/features/chimera.html" target=bodyframe>Chimera Viewer</a>
-<br>      <a href="html/features/columnFilterByAnnotation.html" target=bodyframe>Select columns by annotation</a>
-<br>      <a href="html/releases.html#Jalview.2.9" target=bodyframe>Latest Release Notes</a>
+<br>      <a href="html/releases.html#Jalview.2.10.1" target=bodyframe>Latest Release Notes</a>
 <br>   <a href="html/editing/index.html" target=bodyframe>Editing Alignments</a>
 <br>   <a href="html/features/cursorMode.html" target=bodyframe>Cursor Mode</a>
 <br>   <a href="html/keys.html" target=bodyframe>Key Strokes</a>
@@ -22,7 +17,7 @@
 <br>   <a href="html/features/splitView.html" target=bodyframe>Split Frame View</a>
 <br>   <a href="html/calculations/treeviewer.html" target=bodyframe>Viewing Trees</a>
 <br>   <a href="html/features/seqfetch.html" target=bodyframe>Fetching Sequences</a>
-<br>   <a href="html/features/columnFilterByAnnotation.html" target=bodyframe>Select columns by annotation</a>
+<br>   <a href="html/features/columnFilterByAnnotation.html" target=bodyframe>Select Columns by Annotation</a>
 <br>   <a href="html/na/index.html" target=bodyframe>Nucleic Acid Support</a>
 <br>      <a href="html/features/varna.html" target=bodyframe>Viewing RNA structure</a>
 <br>      <a href="html/calculations/structureconsensus.html" target=bodyframe>RNA Structure Consensus</a>
@@ -86,13 +81,12 @@
 <br>      <a href="html/calculations/consensus.html" target=bodyframe>Consensus</a>
 <br>      <a href="html/calculations/structureconsensus.html" target=bodyframe>RNA Structure Consensus</a>
 <br>      <a href="html/features/annotationsFormat.html" target=bodyframe>Annotations File Format</a>
-<br>      Select Column by Annotation
+<br>      <a href="html/features/columnFilterByAnnotation.html" target=bodyframe>Select Columns by Annotation</a>
 <br>   <a href="html/features/viewingpdbs.html" target=bodyframe>3D Structure Data</a>
 <br>      <a href="html/features/pdbsequencefetcher.html" target=bodyframe>PDB Sequence Fetcher</a>
 <br>      <a href="html/features/structurechooser.html" target=bodyframe>PDB Structure Chooser</a>
 <br>      <a href="html/features/jmol.html" target=bodyframe>Jmol Viewer</a>
 <br>      <a href="html/features/chimera.html" target=bodyframe>Chimera Viewer</a>
-<br>      <a href="html/features/pdbviewer.html" target=bodyframe>Simple PDB Viewer</a>
 <br>   <a href="html/features/varna.html" target=bodyframe>Viewing RNA structures</a>
 <br>   <a href="html/vamsas/index.html" target=bodyframe>VAMSAS Data Exchange</a>
 <br>   <a href="html/menus/index.html" target=bodyframe>Window Menus</a>
@@ -105,15 +99,16 @@
 <br>         <a href="html/menus/alwannotation.html" target=bodyframe>Annotations Menu</a>
 <br>         <a href="html/menus/alwformat.html" target=bodyframe>Format Menu</a>
 <br>         <a href="html/menus/alwcolour.html" target=bodyframe>Colour Menu</a>
-<br>         <a href="html/menus/alwcalculate.html" target=bodyframe>Calculation Menu</a>
+<br>         <a href="html/menus/alwcalculate.html" target=bodyframe>Calculate Menu</a>
 <br>         <a href="html/menus/wsmenu.html" target=bodyframe>Web Service Menu</a>
 <br>         <a href="html/menus/alwannotationpanel.html" target=bodyframe>Annotation Panel Menu</a>
 <br>         <a href="html/menus/popupMenu.html" target=bodyframe>Popup Menu</a>
 <br>   <a href="html/features/preferences.html" target=bodyframe>Preferences</a>
 <br>   <a href="html/memory.html" target=bodyframe>Memory Settings</a>
+<br>   <a href="html/features/groovy.html" target=bodyframe>Scripting with Groovy</a>
+<br>      <a href="html/groovy/featureCounter.html" target=bodyframe>Groovy Feature Counter example</a>
 <br>   <a href="html/features/commandline.html" target=bodyframe>Command Line</a>
 <br>      <a href="html/features/clarguments.html" target=bodyframe>Command Line Arguments</a>
-<br>      <a href="html/features/groovy.html" target=bodyframe>Groovy Shell</a>
 <br>   <a href="html/privacy.html" target=bodyframe>Privacy</a>
 <br>Useful information
 <br>   <a href="html/misc/aminoAcids.html" target=bodyframe>Amino Acid Table</a>
diff --git a/help/helpTOC.xml b/help/helpTOC.xml
index 73147d2..d8a57d1 100644
--- a/help/helpTOC.xml
+++ b/help/helpTOC.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="ISO-8859-1"  ?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -23,11 +23,6 @@
 <!-- DO NOT WRAP THESE LINES - help2Website relies on each item being on one line! -->
 	<tocitem text="Jalview Documentation" target="home" expand="true">
 			<tocitem text="What's new" target="new" expand="true">
-				<tocitem text="Split Frame View" target="splitframe" />
-				<tocitem text="PDB Sequence Fetcher" target="pdbfetcher" />
-				<tocitem text="PDB Structure Chooser" target="pdbchooser" />
-				<tocitem text="Chimera Viewer" target="chimera" />
-				<tocitem text="Select columns by annotation" target="selectcolbyannot" />
 				<tocitem text="Latest Release Notes" target="release"/>
 		</tocitem>
 		
@@ -49,7 +44,7 @@
 		<tocitem text="Viewing Trees" target="treeviewer" expand="false" />
 		<tocitem text="Fetching Sequences" target="seqfetch" />		
 		
-		<tocitem text="Select columns by annotation" target="selectcolbyannot" />
+		<tocitem text="Select Columns by Annotation" target="selectcolbyannot" />
 		
 		<tocitem text="Nucleic Acid Support" target="nucleicAcids" expand="false">
 			<tocitem text="Viewing RNA structure" target="varna" />
@@ -107,7 +102,7 @@
 			<tocitem text="By RNA Helices" target="colours.rnahelices" />
 		</tocitem>
 		
-		<tocitem text="Calculations" target="calculations" expand="false">
+		<tocitem text="Calculations" expand="false">
 			<tocitem text="Sorting alignments" target="sorting" />
 			<tocitem text="Calculating trees" target="trees" />
 			<tocitem text="Principal Component Analysis" target="pca" />
@@ -127,14 +122,13 @@
 			<tocitem text="Consensus" target="calcs.consensus" />
 			<tocitem text="RNA Structure Consensus" target="calcs.alstrconsensus" />
 			<tocitem text="Annotations File Format" target="annotations.fileformat" />
-			<tocitem text="Select Column by Annotation" target="calcs.annotation" />
+			<tocitem text="Select Columns by Annotation" target="selectcolbyannot" />
 		</tocitem>
 		<tocitem text="3D Structure Data" target="viewingpdbs" expand="false">
 			<tocitem text="PDB Sequence Fetcher" target="pdbfetcher" />
 			<tocitem text="PDB Structure Chooser" target="pdbchooser" />
 			<tocitem text="Jmol Viewer" target="pdbjmol" />
-			<tocitem text="Chimera Viewer" target="chimera" />
-			<tocitem text="Simple PDB Viewer" target="pdbmcviewer" />
+			<tocitem text="Chimera Viewer" target="chimera" />			
 		</tocitem>
 		<tocitem text="Viewing RNA structures" target="varna" expand="false"/>
 		<tocitem text="VAMSAS Data Exchange" target="vamsas">
@@ -151,7 +145,7 @@
 				<tocitem text="Annotations Menu" target="alwAnnotations" />
 				<tocitem text="Format Menu" target="alwFormat" />
 				<tocitem text="Colour Menu" target="alwColour" />
-				<tocitem text="Calculation Menu" target="alwCalc" />
+				<tocitem text="Calculate Menu" target="alwCalc" />
 				<tocitem text="Web Service Menu" target="wsMenu" />
 				<tocitem text="Annotation Panel Menu" target="annotPanelMenu" />
 				<tocitem text="Popup Menu" target="popMenu" />
@@ -159,9 +153,11 @@
 		</tocitem>
 		<tocitem text="Preferences" target="preferences" />
 		<tocitem text="Memory Settings" target="memory" expand="false"/>
+		<tocitem text="Scripting with Groovy" target="groovy">
+			<tocitem text="Groovy Feature Counter example" target="groovy.featurecounter"/>
+		</tocitem>
 		<tocitem text="Command Line" target="commandline" expand="false">
 			<tocitem text="Command Line Arguments" target="clarguments" />
-			<tocitem text="Groovy Shell" target="groovy" />
 		</tocitem>
 		<tocitem text="Privacy" target="privacy" />
 	</tocitem>
diff --git a/help/html/calculations/consensus.html b/help/html/calculations/consensus.html
index 0476f05..b7c3340 100644
--- a/help/html/calculations/consensus.html
+++ b/help/html/calculations/consensus.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -43,7 +43,7 @@
     entry from the consensus annotation label to copy the alignment's
     consensus sequence to the clipboard.
   <p>
-    <strong>Sequence logo</strong>
+    <a name="logo"><strong>Sequence logo</strong></a>
   </p>
   By clicking on the label you can also activate the sequence logo. It
   indicates the relative amount of residues per column which can be
@@ -56,6 +56,13 @@
   logo to the same height.
 
   <p>
+    <strong>Group Consensus</strong><br> If sequence groups have
+    been defined, then selecting option 'Group Consensus' in the <a
+      href="../menus/alwannotation.html">Annotations menu</a> will
+    result in Consensus being calculated for each group, as well as the
+    alignment as a whole.
+  </p>
+  <p>
     <strong>cDNA Consensus</strong>
   </p>
   A
diff --git a/help/html/calculations/conservation.html b/help/html/calculations/conservation.html
index da48455..c980ab1 100644
--- a/help/html/calculations/conservation.html
+++ b/help/html/calculations/conservation.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -40,8 +40,8 @@
     <b>9</b> No. 6 (745-756)).
   </ul>
   <em><a
-    href="http://www.compbio.dundee.ac.uk/papers/amas/amas3d.html"
-  >View an HTML version of the paper</a></em>
+    href="http://www.compbio.dundee.ac.uk/papers/amas/amas3d.html">View
+      an HTML version of the paper</a></em>
   </p>
   <p>
     Conservation is measured as a numerical index reflecting the
@@ -65,11 +65,18 @@
     (e.g. !proline).
   </p>
   <p>
-    <em>Colouring an alignment by conservation</em><br>
+    <strong>Colouring an alignment by conservation</strong><br>
     Conservation scores can be used to colour an alignment. This is
     explained further in the help page for <a
-      href="../colourSchemes/conservation.html"
-    >conservation colouring</a>.
+      href="../colourSchemes/conservation.html">conservation
+      colouring</a>.
+  </p>
+  <p>
+    <strong>Group conservation</strong><br> If sequence groups have
+    been defined, then selecting option 'Group Conservation' in the <a
+      href="../menus/alwannotation.html">Annotations menu</a> will
+    result in Conservation being calculated for each group, as well as
+    the alignment as a whole.
   </p>
 </body>
 </html>
diff --git a/help/html/calculations/pairwise.html b/help/html/calculations/pairwise.html
index 4b9cf68..782fbb0 100644
--- a/help/html/calculations/pairwise.html
+++ b/help/html/calculations/pairwise.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/calculations/pca.html b/help/html/calculations/pca.html
index d1841a1..315a50c 100644
--- a/help/html/calculations/pca.html
+++ b/help/html/calculations/pca.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -58,15 +58,15 @@
     pair of sequences - computed with one of the available score
     matrices, such as <a href="scorematrices.html#blosum62">BLOSUM62</a>,
     <a href="scorematrices.html#pam250">PAM250</a>, or the <a
-      href="scorematrices.html#simplenucleotide"
-    >simple single nucleotide substitution matrix</a>. The options
-    available for calculation are given in the <strong><em>Change
+      href="scorematrices.html#simplenucleotide">simple single
+      nucleotide substitution matrix</a>. The options available for
+    calculation are given in the <strong><em>Change
         Parameters</em></strong> menu.
   </p>
   <p>
-    <em>PCA Calculation modes</em><br /> The default Jalview calculation
-    mode (indicated when <em><strong>Jalview PCA
-        Calculation</strong></em> is ticked in the <strong><em>Change
+    <em>PCA Calculation modes</em><br /> The default Jalview
+    calculation mode (indicated when <em><strong>Jalview
+        PCA Calculation</strong></em> is ticked in the <strong><em>Change
         Parameters</em></strong> menu) is to perform a PCA on a matrix where elements
     in the upper diagonal give the sum of scores for mutating in one
     direction, and the lower diagonal is the sum of scores for mutating
@@ -74,10 +74,10 @@
     gives an asymmetric matrix, and a different PCA to a matrix produced
     with the method described in the paper by G. Casari, C. Sander and
     A. Valencia. Structural Biology volume 2, no. 2, February 1995 (<a
-      href="http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=pubmed&dopt=Abstract&list_uids=7749921"
-    >pubmed</a>) and implemented at the SeqSpace server at the EBI. This
-    method preconditions the matrix by multiplying it with its
-    transpose, and can be employed in the PCA viewer by unchecking the <strong><em>Jalview
+      href="http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=pubmed&dopt=Abstract&list_uids=7749921">pubmed</a>)
+    and implemented at the SeqSpace server at the EBI. This method
+    preconditions the matrix by multiplying it with its transpose, and
+    can be employed in the PCA viewer by unchecking the <strong><em>Jalview
         PCA Calculation</em></strong> option in the <strong><em>Change
         Parameters</em></strong> menu.
   </p>
diff --git a/help/html/calculations/quality.html b/help/html/calculations/quality.html
index ff084cf..de79d56 100644
--- a/help/html/calculations/quality.html
+++ b/help/html/calculations/quality.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -44,7 +44,7 @@
     conserved BLOSUM62 score (which is higher). This value is normalised
     for each column, and then plotted on a scale from 0 to 1.
   </p>
-  <p>Multiple alignment algorithms using the BLOSUM 62 substition
+  <p>Multiple alignment algorithms using the BLOSUM 62 substitution
     matrices should, in theory, maximise alignment quality for an
     un-gapped alignment, and locally maximise quality for gapped
     alignments.</p>
diff --git a/help/html/calculations/recoverInputdata.html b/help/html/calculations/recoverInputdata.html
index cd6a808..b2e0e40 100644
--- a/help/html/calculations/recoverInputdata.html
+++ b/help/html/calculations/recoverInputdata.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/calculations/redundancy.html b/help/html/calculations/redundancy.html
index 2946ab7..57fead8 100644
--- a/help/html/calculations/redundancy.html
+++ b/help/html/calculations/redundancy.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -31,10 +31,10 @@
     menu or pressing <strong>'CONTROL+D'</strong> brings up a dialog box
     asking you to select a threshold. If the percentage identity between
     the aligned positions of any two sequences in the visible alignment
-    exceeds this value, the shorter sequence is discarded.<br>
-    <em>Note:</em> The redundancy calculation is done when the dialog
-    box is opened. For large numbers of sequences this can take a long
-    time as all pairs have to be compared.
+    exceeds this value, the shorter sequence is discarded.<br> <em>Note:</em>
+    The redundancy calculation is done when the dialog box is opened.
+    For large numbers of sequences this can take a long time as all
+    pairs have to be compared.
   </p>
 </body>
 </html>
diff --git a/help/html/calculations/referenceseq.html b/help/html/calculations/referenceseq.html
index d0fbb20..45f3a55 100644
--- a/help/html/calculations/referenceseq.html
+++ b/help/html/calculations/referenceseq.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,25 +26,46 @@
   <p>
     <strong>Reference Sequence Alignment Views</strong>
   </p>
+  <p>Many alignment analysis tasks concern a query, or reference
+    sequence. For instance, when searching for sequences from other
+    organisms that are similar to a newly sequenced gene, or when
+    searching for structurally similar sequences for use in homology
+    modelling.</p>
   <p>
-    The reference sequence for an alignment is indicated by its ID being
-    shown in bold. When a reference sequence has been defined, the <strong>Format→Show
-      unconserved</strong> option highlights mutations with respect to the
-    reference sequence, rather than the alignment's consensus sequence.
+    <strong>What happens when a reference sequence is defined ?</strong>
   </p>
+  <p>The reference sequence for an alignment is indicated by its ID
+    being shown in bold. In addition:</p>
+  <ul>
+    <li><strong>Reference sequence numbering</strong>. Instead of
+      column numbers, the alignment ruler shows the reference sequence
+      positions at each column. At each tick mark, either the reference
+      sequence symbol and position is given, or the column number when a
+      gap is present at that position in the reference sequence.</li>
+    <li><strong>Format→Show unconserved</strong> highlights
+      mutations with respect to the reference sequence, rather than the
+      alignment's consensus sequence.</li>
+  </ul>
   <p>
+    <strong>Defining the reference sequence</strong>
+  </p>
+  <p>Each alignment view can have its own reference sequence.</p>
   <ul>
-    <li>Jalview automatically assigns a reference sequence when <a
-      href="../webServices/jnet.html"
-    >JPred4</a> predictions are imported.
-    </li>
-    <li><strong>Assigning a reference sequence</strong><br /> A
-      sequence can be marked as the reference sequence by right-clicking
-      on it's ID to open the popup menu, and selecting the "<strong>(Sequence
-        ID)→Mark as Reference</strong>" entry."</li>
+    <li><strong>Manually assigning a reference sequence</strong><br />
+      A sequence can be marked as the reference sequence by
+      right-clicking on its ID to open the popup menu, and selecting the
+      "<strong>(Sequence ID)→Mark as Reference</strong>" entry.</li>
+    <li><strong>Defining a reference when importing
+        annotation</strong><br />Jalview automatically assigns a reference
+      sequence when importing analysis results, such as those returned
+      from <a href="../webServices/jnet.html">JPred4</a> . A reference
+      sequence can also be assigned via the <a
+      href="../features/annotationsFormat.html#refsandviews">SET_REF</a>
+      command in an alignment annotation file.</li>
   </ul>
   <p>
     <em>Reference sequence based alignment visualisation was
-      introduced in Jalview 2.9.</em>
+      introduced in Jalview 2.9, and support for storage and retrieval
+      of reference sequence views in 2.10.</em>
   </p>
 </html>
diff --git a/help/html/calculations/scorematrices.html b/help/html/calculations/scorematrices.html
index 8a1181d..67fae63 100644
--- a/help/html/calculations/scorematrices.html
+++ b/help/html/calculations/scorematrices.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -33,7 +33,8 @@
       matrix, and (since 2.8.1) is available for Tree and PCA
       calculations.</li>
     <li><a href="#simplenucleotide">Simple Nucleotide
-        Substition</a> is a (fairly) arbitrary DNA/RNA substitution matrix.</li>
+        Substitution</a> is a (fairly) arbitrary DNA/RNA substitution
+      matrix.</li>
     <!--  <li><a href="#conservation">Conservation Matrices</a> A range of matrices for distinguishing amino-acids by physicochemical property conservation.
 </li> -->
   </ul>
@@ -721,9 +722,10 @@
     <strong><a name="pam250">PAM250</a></strong><br /> <em><strong>P</strong>ercentage
       <strong>A</strong>ccepted <strong>M</strong>utation matrix. PAM250
       estimates substitutions after 250% of sites have changed (each
-      site can be mutated multple times).<br /> Jalview 2.8.1 introduced
-      support for PAM250 based <a href="../calculations/pca.html">PCA</a>
-      and <a href="../calculations/tree.html">tree</a> calculations.</em>
+      site can be mutated multple times).<br /> Jalview 2.8.1
+      introduced support for PAM250 based <a
+      href="../calculations/pca.html">PCA</a> and <a
+      href="../calculations/tree.html">tree</a> calculations.</em>
   <table border="1">
     <tr>
       <td></td>
@@ -1559,8 +1561,8 @@
   </table>
   <strong><em>This nucleotide matrix was introduced in
       Jalview 2.8. If you'd like to improve it - please take a look at <a
-      href="http://issues.jalview.org/browse/JAL-1027"
-    >Issue JAL-1027 - introduce a nucleotide substitution matrix that
+      href="http://issues.jalview.org/browse/JAL-1027">Issue
+        JAL-1027 - introduce a nucleotide substitution matrix that
         supports RNA/DNA and ambiguity codes</a>
   </em></strong>
   </p>
diff --git a/help/html/calculations/sorting.html b/help/html/calculations/sorting.html
index 1478d59..2476c98 100644
--- a/help/html/calculations/sorting.html
+++ b/help/html/calculations/sorting.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -75,23 +75,31 @@
       </p>
       <p>
         This menu appears if the alignment contains any <a
-          href="../features/annotationsFormat.html"
-        >sequence associated alignment annotation</a> with associated
-        score values. Each entry is the label for a distinct group of
-        sequence associated annotation scores which can be used for
-        sorting.
+          href="../features/annotationsFormat.html">sequence
+          associated alignment annotation</a> with associated score values.
+        Each entry is the label for a distinct group of sequence
+        associated annotation scores which can be used for sorting.
       </p>
   </ul>
   <p>
+    <strong>Sorting according to sequence features</strong><br />
+    Additional sort operations for alignments containing sequence
+    features are provided in the <strong><a
+      href="../features/featuresettings.html#sortbyfeature">Feature
+        Settings</a></strong> dialog, opened via <strong>View→Feature
+      Settings...</strong>
+  <p>
     <strong>Reversing the Order</strong>
   </p>
   <p>Selecting any item from the Sort menu will sort sequences in an
     ascending order according to the property defining the sort. If the
     same sort is re-applied, the sequences will be sorted in the inverse
-    order. In the case of trees and alignment orderings, Jalview will
-    remember your last choice for sorting the alignment and only apply
-    the inverse ordering if you select the same tree or alignment
-    ordering item again.</p>
+    order. In both cases, for sequences which are equivalent under the
+    sort operation, their order will be preserved (since version 2.10).
+    In the case of trees and alignment orderings, Jalview will remember
+    your last choice for sorting the alignment and only apply the
+    inverse ordering if you select the same tree or alignment ordering
+    item again.</p>
 
 </body>
 </html>
diff --git a/help/html/calculations/structureconsensus.html b/help/html/calculations/structureconsensus.html
index 7b0ce98..b35c57b 100644
--- a/help/html/calculations/structureconsensus.html
+++ b/help/html/calculations/structureconsensus.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -27,28 +27,37 @@
     <strong>Alignment RNA Structure Consensus Annotation</strong>
   </p>
 
+  <p>The RNA structure consensus displayed below the alignment gives
+    the percentage of valid base pairs per column for the first
+    secondary structure annotation shown on the annotation panel. These
+    values are shown as a histogram labeled "StrucConsensus",
+    where a symbol below each bar indicates whether the majority of base
+    pairs are:
+  <ul>
+    <li>'(' - Watson-Crick (C:G, A:U/T)</li>
+    <li>'[' - Non-canonical (a.ka. wobble) (G:U/T)</li>
+    <li>'{' - Invalid (a.k.a. tertiary) (the rest)</li>
+  </ul>
+  <p>Mousing over the column gives the fraction of pairs classified
+    as Watson-Crick, Canonical or Invalid.</p>
+
   <p>
-    The RNA structure consensus displayed below the alignment is the
-    percentage of valid base pairs per column. It is calculated in
-    relation to a secondary structure and just paired columns are
-    calculated. The canonical Watson-Crick base pairings (A-T/U, G-C)
-    and the wobble base pair (G-T/U) are regarded as valid pairings.<br>
-    The amount of valid base pairs is indicated by the profile in the
-    Alignment Annotation row.<br> By default this calculation
-    includes gaps in columns. You can choose to ignore gaps in the
-    calculation by right clicking on the label "StrConsensus"
-    to the left of the structure consensus bar chart.<br>
+    By default this calculation includes gaps in columns. You can choose
+    to ignore gaps in the calculation by right clicking on the label
+    "StrucConsensus" to the left of the structure consensus
+    bar chart.<br>
   <p>
-    <strong>Structure logo</strong>
-  </p>
-  By clicking on the label you can also activate the structure logo. It
-  is very similar to a sequence logo but counts the numbers of base
-  pairs. There are two residues per column, the actual column and the
-  interacting base. The opening bracket is always the one on the left
-  side.
-  <br> Like sequence logos the relative amount of a specific base
-  pair can be estimated by its size in the logo. The tool tip of a
-  column gives the exact numbers for all occurring valid base pairs.
+    <strong>RNA Structure logo</strong><br /> Right-clicking on the
+    label allows you to enable the structure logo. It is very similar to
+    a sequence logo but instead shows the distribution of base pairs.
+    There are two residues per column, the actual column and the
+    interacting base. The opening bracket is always the one on the left
+    side. <br> Like <a href="consensus.html#logo">sequence
+      logos</a>, the relative amount of a specific base pair can be
+    estimated by its size in the logo, and this can be made more obvious
+    by <em>normalising</em> the logo (enabled via the popup menu). When
+    the logo is displayed, the tool tip for a column gives the exact
+    percentages for all base pairs at that position.
   </p>
 </body>
 </html>
diff --git a/help/html/calculations/tree.html b/help/html/calculations/tree.html
index f95476f..12e35d4 100644
--- a/help/html/calculations/tree.html
+++ b/help/html/calculations/tree.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -30,10 +30,10 @@
     Trees are calculated on either the complete alignment, or just the
     currently selected group of sequences, using the functions in the <strong>Calculate→Calculate
       tree</strong> submenu. Once calculated, trees are displayed in a new <a
-      href="../calculations/treeviewer.html"
-    >tree viewing window</a>. There are four different calculations, using
-    one of two distance measures and constructing the tree from one of
-    two algorithms :
+      href="../calculations/treeviewer.html">tree viewing
+      window</a>. There are four different calculations, using one of two
+    distance measures and constructing the tree from one of two
+    algorithms :
   </p>
   <p>
     <strong>Distance Measures</strong>
@@ -56,8 +56,8 @@
       scores for the residue pairs at each aligned position.
       <ul>
         <li>For details about each model, see the <a
-          href="scorematrices.html"
-        >list of built-in score matrices</a>.
+          href="scorematrices.html">list of built-in score
+            matrices</a>.
         </li>
       </ul></li>
     <li><strong>Sequence Feature Similarity</strong><br>Trees
@@ -102,12 +102,12 @@
   </ul>
   <p>
     A newly calculated tree will be displayed in a new <a
-      href="../calculations/treeviewer.html"
-    >tree viewing window</a>. In addition, a new entry with the same tree
-    viewer window name will be added in the Sort menu so that the
-    alignment can be reordered to reflect the ordering of the leafs of
-    the tree. If the tree was calculated on a selected region of the
-    alignment, then the title of the tree view will reflect this.
+      href="../calculations/treeviewer.html">tree viewing
+      window</a>. In addition, a new entry with the same tree viewer window
+    name will be added in the Sort menu so that the alignment can be
+    reordered to reflect the ordering of the leafs of the tree. If the
+    tree was calculated on a selected region of the alignment, then the
+    title of the tree view will reflect this.
   </p>
 
   <p>
@@ -118,9 +118,9 @@
     phylogenetic trees, which can cope with large numbers of sequences,
     use better distance methods and can perform bootstrapping. Jalview
     can read <a
-      href="http://evolution.genetics.washington.edu/phylip/newick_doc.html"
-    >Newick</a> format tree files using the 'Load Associated Tree' entry
-    of the alignment's File menu. Sequences in the alignment will be
+      href="http://evolution.genetics.washington.edu/phylip/newick_doc.html">Newick</a>
+    format tree files using the 'Load Associated Tree' entry of the
+    alignment's File menu. Sequences in the alignment will be
     automatically associated to nodes in the tree, by matching Sequence
     IDs to the tree's leaf names.
   </p>
diff --git a/help/html/calculations/treeviewer.html b/help/html/calculations/treeviewer.html
index cbe4f87..db3bc55 100644
--- a/help/html/calculations/treeviewer.html
+++ b/help/html/calculations/treeviewer.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,12 +28,11 @@
   </p>
   <p>
     The tree viewing window is opened when a tree has been <a
-      href="tree.html"
-    >calculated from an alignment</a>, or imported via a file or web
-    service. It includes <a href="#menus">menus</a> for controlling
-    layout and file and figure creation, and enables various selection
-    and colouring operations on the associated sequences in the
-    alignment.
+      href="tree.html">calculated from an alignment</a>, or
+    imported via a file or web service. It includes <a href="#menus">menus</a>
+    for controlling layout and file and figure creation, and enables
+    various selection and colouring operations on the associated
+    sequences in the alignment.
   </p>
   <p>
     <strong><em>Selecting Sequence Leaf Nodes</em></strong><br>
@@ -95,23 +94,24 @@
     tree is rendered and labeled:
   <ul>
     <li><strong>Fit to Window</strong>
-    <p>The tree layout will be scaled to fit in the display window.
-        You may need to reduce the font size to minimise the leaf label
-        overlap when this option is selected.</p></li>
+      <p>The tree layout will be scaled to fit in the display
+        window. You may need to reduce the font size to minimise the
+        leaf label overlap when this option is selected.</p></li>
     <li><strong>Font Size ...</strong><em>n</em>
-    <p>
+      <p>
         Brings up a dialog box to set the font size for the leaf names.
         <em>n</em> is the current font size.
       </p></li>
     <li><strong>Show Distances</strong>
-    <p>Labels each branch or leaf with its associated branch length.</p></li>
+      <p>Labels each branch or leaf with its associated branch
+        length.</p></li>
     <li><strong>Show Bootstrap values</strong>
-    <p>Labels each branch or leaf with its associated bootstrap
+      <p>Labels each branch or leaf with its associated bootstrap
         value.</p></li>
     <li><strong>Mark unlinked leaves</strong>
-    <p>Toggles the display of a '*' at the beginning of a leaf label
-        to indicate that there is no sequence corresponding to that leaf
-        in the associated alignment.</p></li>
+      <p>Toggles the display of a '*' at the beginning of a leaf
+        label to indicate that there is no sequence corresponding to
+        that leaf in the associated alignment.</p></li>
     <li><strong>Sort Alignment By Tree</strong>
       <p>
         Sorts any associated alignment views using the current tree. (<em>Only
@@ -120,10 +120,9 @@
     <li><strong>Associate Leaves with ...</strong>
       <p>
         Only visible when there are <a
-          href="../features/multipleviews.html"
-        >multiple views</a> of the same alignment to show and edit which
-        alignment views are associated with the leaves of the displayed
-        tree.
+          href="../features/multipleViews.html">multiple
+          views</a> of the same alignment to show and edit which alignment
+        views are associated with the leaves of the displayed tree.
       </p>
   </ul>
   </p>
diff --git a/help/html/colourSchemes/abovePID.html b/help/html/colourSchemes/abovePID.html
index 80d45f4..7198da3 100644
--- a/help/html/colourSchemes/abovePID.html
+++ b/help/html/colourSchemes/abovePID.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -35,10 +35,10 @@ td {
     <strong>Colouring above a percentage identity threshold</strong>
   </p>
   <p>Selecting this option causes the colour scheme to be applied to
-    only those residues that occur in that column more than a certain
-    percentage of the time. For instance selecting the threshold to be
-    100 will only colour those columns with 100 % identity. This
-    threshold option can be applied to the Zappo, Taylor, Hydrophobicity
-    and User colour schemes.</p>
+    only those residues that occur in that column at least a certain
+    percentage of the time. For instance, selecting the threshold to be
+    100 will only colour those columns with 100% identity, and selecting 50 will shade residues appearing in least 50% of the rows (or sequences) in each column.</p>
+   <p>The percentage calculation may include or exclude gaps in the column, depending on the option selected for the <a href="../calculations/consensus.html">consensus calculation</a>.</p>
+   <p>With a threshold of 0, colouring is unchanged.</p>
 </body>
 </html>
diff --git a/help/html/colourSchemes/annotationColouring.html b/help/html/colourSchemes/annotationColouring.html
index 55e3a85..c7ddf68 100644
--- a/help/html/colourSchemes/annotationColouring.html
+++ b/help/html/colourSchemes/annotationColouring.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -39,14 +39,17 @@
   </div>
   <ul>
     <li>Select which annotation to base the colouring scheme on
-      using the top left selection box.<br />If the <strong>Per-sequence
-        only</strong> tick box is not greyed out, then ticking it will limit the
-      available annotation rows to just those that are sequence
-      associated (e.g. T-COFFEE scores and <a
-      href="../webServices/proteinDisorder.html"
-    >protein disorder predictions</a>), which will colour each sequence
-      according to its own per-residue scores.<br /> <em>Per-sequence
-        associated annotation colouring was introduced in Jalview 2.8</em>
+      using the top left selection box. Sequence associated alignment
+      annotation are shown with the seuqence's name appended.<br />If
+      the <strong>Per-sequence only</strong> tick box is not greyed out,
+      then ticking it will limit the list of available annotation rows
+      to just the labels for those that are sequence associated.
+      Annotation rows on each sequence with the same label (e.g.
+      T-COFFEE scores and <a href="../webServices/proteinDisorder.html">protein
+        disorder predictions</a>) will then be used to colour its
+      corresponding positions in the alignment.<br /> <em>Per-sequence
+        associated annotation colouring is currently only available in
+        the Desktop.</em>
     </li>
     <li>If the "Use Original Colours" box is selected,
       the colouring scheme will use the colouring scheme present on the
@@ -74,9 +77,9 @@
         <li>Press the "Defaults" button to reset the
           minimum and maximum colours to their default settings (these
           are configured in the applet's parameters or the <a
-          href="../features/preferences.html"
-        >application's user preferences</a>.).<br /> <em>Default min
-            and max colours were introduced in Jalview 2.7</em>
+          href="../features/preferences.html">application's
+            user preferences</a>.).<br /> <em>Default min and max
+            colours were introduced in Jalview 2.7</em>
       </ul>
     </li>
 
diff --git a/help/html/colourSchemes/blosum.html b/help/html/colourSchemes/blosum.html
index e327d56..e871299 100644
--- a/help/html/colourSchemes/blosum.html
+++ b/help/html/colourSchemes/blosum.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/buried.html b/help/html/colourSchemes/buried.html
index d6d0eec..7e79246 100644
--- a/help/html/colourSchemes/buried.html
+++ b/help/html/colourSchemes/buried.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/clustal.html b/help/html/colourSchemes/clustal.html
index 0c39a15..c8d2417 100644
--- a/help/html/colourSchemes/clustal.html
+++ b/help/html/colourSchemes/clustal.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/conservation.html b/help/html/colourSchemes/conservation.html
index ccd3615..1651f97 100644
--- a/help/html/colourSchemes/conservation.html
+++ b/help/html/colourSchemes/conservation.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -33,9 +33,9 @@
     alignment analysis (Livingstone C.D. and Barton G.J. (1993), Protein
     Sequence Alignments: A Strategy for the Hierarchical Analysis of
     Residue Conservation.CABIOS Vol. 9 No. 6 (745-756)). See the <a
-      href="../calculations/conservation.html"
-    >conservation calculation</a> help page for a more thorough
-    explanation of the calculation.
+      href="../calculations/conservation.html">conservation
+      calculation</a> help page for a more thorough explanation of the
+    calculation.
   </p>
   <p>For an already coloured alignment, the conservation index at
     each alignment position is used to modify the shading intensity of
@@ -46,11 +46,10 @@
   <p>
     Conservation can be calculated over all sequences in an alignment,
     or just within specific groups (such as those defined by <a
-      href="../calculations/tree.html"
-    >phylogenetic tree partitioning</a>). The option 'apply to all groups'
-    controls whether the contrast slider value will be applied to the
-    indices for the currently selected group, or all groups defined over
-    the alignment.
+      href="../calculations/tree.html">phylogenetic tree
+      partitioning</a>). The option 'apply to all groups' controls whether
+    the contrast slider value will be applied to the indices for the
+    currently selected group, or all groups defined over the alignment.
   </p>
 </body>
 </html>
diff --git a/help/html/colourSchemes/helix.html b/help/html/colourSchemes/helix.html
index f570ce2..835a1c7 100644
--- a/help/html/colourSchemes/helix.html
+++ b/help/html/colourSchemes/helix.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/hydrophobic.html b/help/html/colourSchemes/hydrophobic.html
index dda803d..873c011 100644
--- a/help/html/colourSchemes/hydrophobic.html
+++ b/help/html/colourSchemes/hydrophobic.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/index.html b/help/html/colourSchemes/index.html
index 01319b7..2511fe7 100644
--- a/help/html/colourSchemes/index.html
+++ b/help/html/colourSchemes/index.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -49,8 +49,7 @@ td {
     the background colour.</p>
   <p>
     The <strong>"Colour→<a
-      href="../colourSchemes/textcolour.html"
-    >Colour Text...</a>"
+      href="../colourSchemes/textcolour.html">Colour Text...</a>"
     </strong> entry opens a dialog box to set a different text colour for light
     and dark background, and the intensity threshold for transition
     between them.
diff --git a/help/html/colourSchemes/nucleotide.html b/help/html/colourSchemes/nucleotide.html
index 88a894b..b6bc884 100644
--- a/help/html/colourSchemes/nucleotide.html
+++ b/help/html/colourSchemes/nucleotide.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/pid.html b/help/html/colourSchemes/pid.html
index 20e8330..83d3bf9 100644
--- a/help/html/colourSchemes/pid.html
+++ b/help/html/colourSchemes/pid.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/purinepyrimidine.html b/help/html/colourSchemes/purinepyrimidine.html
index e24b082..cb4c61c 100644
--- a/help/html/colourSchemes/purinepyrimidine.html
+++ b/help/html/colourSchemes/purinepyrimidine.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/rnahelicesColouring.html b/help/html/colourSchemes/rnahelicesColouring.html
index a49a9e5..dbbc9a0 100644
--- a/help/html/colourSchemes/rnahelicesColouring.html
+++ b/help/html/colourSchemes/rnahelicesColouring.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -32,10 +32,10 @@
     on its helices. The helices are determined from the secondary
     structure line in the Stockholm file (#GC SS_cons) written in WUSS
     notation that specifies base pairing. See <a
-      href="http://en.wikipedia.org/wiki/Stockholm_format"
-    > Wikipedia</a> or <a
-      href="http://jalview-rnasupport.blogspot.com/2010/06/parsing-wuss-notation-of-rna-secondary.html"
-    > Jalview RNA Support Blog</a> for more information about Stockholm
+      href="http://en.wikipedia.org/wiki/Stockholm_format">
+      Wikipedia</a> or <a
+      href="http://jalview-rnasupport.blogspot.com/2010/06/parsing-wuss-notation-of-rna-secondary.html">
+      Jalview RNA Support Blog</a> for more information about Stockholm
     files and WUSS notation.
   </p>
   Select "Colour"
diff --git a/help/html/colourSchemes/strand.html b/help/html/colourSchemes/strand.html
index 93c6fed..1cffe3f 100644
--- a/help/html/colourSchemes/strand.html
+++ b/help/html/colourSchemes/strand.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/taylor.html b/help/html/colourSchemes/taylor.html
index cf19eb8..375a980 100644
--- a/help/html/colourSchemes/taylor.html
+++ b/help/html/colourSchemes/taylor.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/textcolour.html b/help/html/colourSchemes/textcolour.html
index 979e950..b380f75 100644
--- a/help/html/colourSchemes/textcolour.html
+++ b/help/html/colourSchemes/textcolour.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/turn.html b/help/html/colourSchemes/turn.html
index fa83414..a4c1a3c 100644
--- a/help/html/colourSchemes/turn.html
+++ b/help/html/colourSchemes/turn.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/user.html b/help/html/colourSchemes/user.html
index 100e8ee..d226f92 100644
--- a/help/html/colourSchemes/user.html
+++ b/help/html/colourSchemes/user.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/colourSchemes/zappo.html b/help/html/colourSchemes/zappo.html
index eca1df9..67d184c 100644
--- a/help/html/colourSchemes/zappo.html
+++ b/help/html/colourSchemes/zappo.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/editing/index.html b/help/html/editing/index.html
index 2dfbde6..ab0c29d 100644
--- a/help/html/editing/index.html
+++ b/help/html/editing/index.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -32,12 +32,11 @@
     clicking the left mouse button and pressing a combination of either
     shift and control (or the alt, option or apple key on Macs) and
     dragging the mouse. Pressing <em>F2</em> toggles the alternative <a
-      href="../features/cursorMode.html"
-    >'Cursor mode'</a> keyboard editing facility, where the space bar and
-    delete keys add and remove gaps at the current editing position. The
-    key strokes for both these modes are summarised in the <a
-      href="../keys.html"
-    >keystrokes table</a>.
+      href="../features/cursorMode.html">'Cursor mode'</a> keyboard
+    editing facility, where the space bar and delete keys add and remove
+    gaps at the current editing position. The key strokes for both these
+    modes are summarised in the <a href="../keys.html">keystrokes
+      table</a>.
   </p>
   <p>
     <strong>Tip:</strong> For large alignments, deselect "Calculate
@@ -50,9 +49,8 @@
     right to insert gaps and remove gaps.<br> If the current
     selection is a group over all sequences in the alignment, or a group
     over some sequences or all columns in the alignment, then hold down
-    either "Control" key (or the "Alt;" on OSX if
-    "Control" does not work) and drag the residue left or
-    right to edit all sequences in the defined group at once.
+    "Control" key ("Cmd" key on OSX) and drag the residue 
+    left or right to edit all sequences in the defined group at once.
   </p>
   <p>
     <em>Copy/paste/cut/delete</em> - any sequences which are in the
diff --git a/help/html/features/annotation.html b/help/html/features/annotation.html
index 078dc8b..92777fe 100644
--- a/help/html/features/annotation.html
+++ b/help/html/features/annotation.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -19,123 +19,203 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  -->
-<head><title>Alignment Annotation</title></head>
+<head>
+<title>Alignment Annotation</title>
+</head>
 <body>
-<p><strong>Alignment Annotation</strong></p>
+  <p>
+    <strong>Alignment Annotation</strong>
+  </p>
 
-<p>In addition to the definition of groups and sequence features,
-  Jalview can display symbols and graphs under the columns of an
-  alignment, and allow you to mark particular columns of an alignment and add symbols and text 
-  in the annotation area shown below the alignment (which may be hidden if <strong>View→Show 
-  Annotation</strong> is not ticked). Any displayed annotation row can be hidden (using the pop-up 
-  menu obtained by right-clicking the label), or re-ordered by dragging the label to a new 
-  position with the left mouse button.</p>
-<p>
-Web services can also add annotation to an alignment (see the <a
-href="../webServices/jnet.html">JNet</a> and <a
-href="../webServices/proteinDisorder.html">Disorder</a> protein
-structure prediction services), and as of Jalview 2.08 quantitative
-and symbolic annotations can be added to an alignment via an <a
-href="annotationsFormat.html">Annotations File</a> dragged into the
-alignment window or loaded from the alignment's file menu.
-</p>
-<p><a name="seqannots"/><strong>Sequence Reference Annotation</strong>
-</p>
-<p>
-		Sequence reference annotation is created from 3D structure
-		data, and from the results of sequence based prediction of
-		<a href="../webServices/jnet.html">secondary structure</a> and <a
-			href="../webServices/proteinDisorder.html">disordered region</a>
-		prediction methods. 
-</p>
-<p><strong>Interactive Alignment Annotation</strong></p>
-<p>
-Annotation rows are added using the <strong>Annotation Label</strong>
-menu, which is obtained by clicking anywhere on the annotation row labels
-area (below the sequence ID area).
-</p>
-<ul>
-  <li><strong>Add New Row</strong><br>
-    <em>Adds a new, named annotation row (a dialog box will pop up for you to 
-    enter the label for the new row). </em> </li>
-  <li><strong>Edit Label/Description</strong><br>
-    <em>This opens a dialog where you can change the name (displayed label), or the description
-    (as shown on the label tooltip) of the clicked annotation. </em> </li>
-  <li><strong>Hide This Row</strong><br>
-    <em>Hides the annotation row whose label was clicked in order to bring up 
-    the menu.</em> </li>
-  <li><strong>Hide All <em><label></em></strong><br>
-    <em>Hides all annotation rows whose label matches the one clicked. 
-    (This option is only shown for annotations that relate to individual sequences, 
-    not for whole alignment annotations. Since Jalview 2.8.2.)</em> </li>
-  <li><strong>Delete This Row</strong><br>
-    <em>Deletes the annotation row whose label was clicked in order to bring up 
-    the menu.</em> </li>
-  <li><strong>Show All Hidden Rows</strong><br>
-    <em>Shows all hidden annotation rows.</em> </li>
-          <li><strong>Export Annotation</strong> <em>(Application only)</em><br>
-       <em>Annotations can be saved to file or output to a text window in either the 
-        Jalview annotations format or as a spreadsheet style set of comma separated values (CSV). </em> </li>
-      <li><strong>Show Values in Text Box</strong> <em>(applet only)</em><br>
-        <em>Opens a text box with a list of comma-separated values corresponding 
-        to the annotation (numerical or otherwise) at each position in the row. 
-        This is useful to export alignment quality measurements for further analysis.</em> 
-      </li>
-      <li><strong>Scale Label To Column</strong><em>(introduced in 2.5)</em><br>
-      <em>Selecting this toggles whether column labels will be shrunk to fit within each column, or displayed using the view's standard font size.</em></li>
-</ul>
-<p>
-<strong>Editing Label and secondary structure Annotation</strong></p>
-<p>
-Use the <strong>left mouse button</strong> to select a position along the row that are to
-be annotated - these regions will be coloured red. <strong>Control</strong> and <strong>shift</strong> in combination
-with the left-click will select more than one position, or a range of
-positions on the alignment.
-</p>
-<p>Once the desired position has been selected, use the <strong>right mouse
-button</strong> to open the <strong>annotation menu</strong>:</p>
-<ul>
-<li>Helix<br><em>Mark selected positions with a helix glyph (a red
-oval), and optional text label (see below). A
-dialog box will open for you to enter the text. Consecutive ovals
-will be rendered as an unbroken red line.</em>
-</li>
-<li>Sheet<br><em>Mark selected positions with a sheet glyph (a green
-arrow oriented from left to right), and optional text label (see
-below). A dialog box will open for you to enter the text. Consecutive
-arrows will be joined together to form a single green arrow.</em>
-</li>
-<li><a name="rna">RNA Helix</a> (only shown when working with nucleotide sequences)<br>
-<em>Mark selected positions as participating in a base pair
-either upstream or downstream. When the dialog box opens, enter a
-'(' to indicate these bases pair with columns upstream (to right),
-and ')' to indicate this region pairs with bases to the left of the
-highlighted columns.<br />If any brackets do not match up, then an
-orange square will highlight the first position where a bracket was
-found not to match.
-</em>
-</li>
-<li>Label<br><em>Set the text label at the selected positions. A
-dialog box will open for you to enter the text.  If
-more that one consecutive position is marked with the same label, only
-the first position's label will be rendered.</em>
-</li>
-<li>Colour<br><em>Changes the colour of the annotation text label.</em>
-</li>
-<li>Remove Annotation<br><em>Blanks any annotation at the selected positions on
-the row. Note: <strong>This cannot be undone</strong></em>
-</li>
-</ul>
-<p>
-User defined annotation is stored and retrieved using <a
-href="../features/jalarchive.html">Jalview Archives</a>.
-</p>
-<p><em>Current Limitations</em></p>
-<p>As of version 2.5, the Jalview user interface does not support the 
-creation and editing of quantitative annotation (histograms and line graphs), or 
-to create annotation associated with a specific sequence. It is also incapable of
-annotation grouping or changing the style of existing annotation (to change between line or bar charts, or to make multiple line graphs). These annotation capabilities are only possible by the import of an 
-<a href="annotationsFormat.html">Annotation file</a>.<br>
-</p>
+  <p>
+    In addition to the definition of groups and sequence features,
+    Jalview can display symbols and graphs under the columns of an
+    alignment. These annotation tracks are displayed in the annotation
+    area below the alignment. The annotation area's visibility is
+    controlled with the <strong>View→Show Annotation</strong>
+    option.
+  </p>
+  <p>
+    <strong>Types of annotation</strong>
+  <ul>
+    <li><a name="seqannots"><strong>Sequence
+          associated annotation.</strong></a><br />Data displayed on sequence
+      annotation rows are associated with the positions of a sequence.
+      Often this is 'Reference annotation' such as secondary structure
+      information derived from 3D structure data, or from the results of
+      sequence based prediction of <a href="../webServices/jnet.html">secondary
+        structure</a> and <a href="../webServices/proteinDisorder.html">disorder</a>.
+      If reference annotation is available for a the currently selected
+      sequences, it can be shown by selecting the <strong>Add
+        Reference Annotation</strong> option in the sequence or selection popup
+      menu.</li>
+    <li><strong>Group associated annotation.</strong><br />Data can
+      be associated with groups defined on the alignment. If sequence
+      groups are defined, <a href="../calculations/conservation.html">Conservation</a>
+      and <a href="../calculations/consensus.html">Consensus</a>
+      annotation can be enabled for each group from the <a
+      href="../menus/alwannotation.html">Annotations menu</a>, or can be
+      imported from a Jalview <a href="annotationsFormat.html">Annotations
+        file</a>.</li>
+    <li><strong>Alignment associated annotation.</strong><br />Annotation
+      rows associated with columns on the alignment are simply
+      'alignment annotation'. Controls allow you to <a href="#iaannot">interactively
+        create alignment annotation</a> to add labels and symbols to
+      alignment columns. Jalview's consensus, conservation and quality
+      calculations also create histogram and sequence logo annotations
+      on the alignment.</li>
+  </ul>
+  <p>
+    <strong>Importing and exporting annotation</strong><br />
+    Annotations on an alignment view are saved in Jalview project files.
+    You can also load <a href="annotationsFormat.html">Annotations
+      Files</a> in order to add any kind of quantitative and symbolic
+    annotations to an alignment. To see an example, use the <strong>Export
+      Features/Annotation</strong> option from an alignment window's File menu.
+  </p>
+  <p>
+    <strong>Layout and display controls</strong><br /> Individual and
+    groups of annotation rows can be shown or hidden using the pop-up
+    menu obtained by right-clicking the label. You can also reorder them
+    by dragging the label to a new position with the left mouse button.
+    The <strong>Annotations</strong> menu provides settings controlling
+    the ordering and display of sequence, group and alignment associated
+    annotation. The <strong>Colour by annotation</strong> option in the
+    colour menu allows annotation to be used to <a
+      href="../colourSchemes/annotationColouring.html">shade the
+      alignment</a>. Annotations can also be used to <a
+      href="../features/columnFilterByAnnotation.html">select or
+      hide columns</a> via the dialog opened from the <strong>Selection</strong>
+    menu.
+  </p>
+  <p>
+    <strong>Sequence Highlighting and Selection from Annotation</strong>
+  </p>
+  <p>
+    A <strong>single click</strong> on the label of an annotation row
+    associated with sequences and sequence groups will cause the
+    associated sequences to be highlighted in the alignment view. <strong>Double
+      clicking</strong> the label will select the associated sequences, replacing
+    any existing selection. Like with other kinds of selection, <strong>shift
+      double-click</strong> will add associated sequences, and <strong>Ctrl
+      (Mac CMD) double-click</strong> will toggle inclusion of associated
+    sequences in the selection.
+  <p>
+    <strong>Interactive Alignment Annotation</strong>
+  </p>
+  <p>
+    <a name="iaannot"> Annotation rows</a> are added using the <strong>Annotation
+      Label</strong> menu, which is obtained by clicking anywhere on the
+    annotation row labels area (below the sequence ID area).
+  </p>
+  <ul>
+    <li><strong>Add New Row</strong><br> <em>Adds a new,
+        named annotation row (a dialog box will pop up for you to enter
+        the label for the new row). </em></li>
+    <li><strong>Edit Label/Description</strong><br> <em>This
+        opens a dialog where you can change the name (displayed label),
+        or the description (as shown on the label tooltip) of the
+        clicked annotation. </em></li>
+    <li><strong>Hide This Row</strong><br> <em>Hides the
+        annotation row whose label was clicked in order to bring up the
+        menu.</em></li>
+    <li><strong>Hide All <em><label></em></strong><br> <em>Hides
+        all annotation rows whose label matches the one clicked. (This
+        option is only shown for annotations that relate to individual
+        sequences, not for whole alignment annotations. Since Jalview
+        2.8.2.)</em></li>
+    <li><strong>Delete This Row</strong><br> <em>Deletes
+        the annotation row whose label was clicked in order to bring up
+        the menu.</em></li>
+    <li><strong>Show All Hidden Rows</strong><br> <em>Shows
+        all hidden annotation rows.</em></li>
+    <li><strong>Export Annotation</strong> <em>(Application
+        only)</em><br> <em>Annotations can be saved to file or
+        output to a text window in either the Jalview annotations format
+        or as a spreadsheet style set of comma separated values (CSV). </em>
+    </li>
+    <li><strong>Show Values in Text Box</strong> <em>(applet
+        only)</em><br> <em>Opens a text box with a list of
+        comma-separated values corresponding to the annotation
+        (numerical or otherwise) at each position in the row. This is
+        useful to export alignment quality measurements for further
+        analysis.</em></li>
+    <li><strong>Scale Label To Column</strong><em>(introduced
+        in 2.5)</em><br> <em>Selecting this toggles whether column
+        labels will be shrunk to fit within each column, or displayed
+        using the view's standard font size.</em></li>
+  </ul>
+  <p>
+    <strong>Editing labels and secondary structure annotation
+      rows</strong>
+  </p>
+  <p>
+    Use the <strong>left mouse button</strong> to select a position
+    along the row that are to be annotated - these regions will be
+    coloured red. Press <strong>Control</strong> or <strong>shift</strong>
+    in combination with the left-click to either select an additional
+    position, or a range of positions on the alignment.
+  </p>
+  <p>
+    Once positions have been selected, use the <strong>right
+      mouse button</strong> and select one of the following from the <strong>annotation
+      menu</strong>:
+  </p>
+  <ul>
+    <li>Helix<br> <em>Marks selected positions with a
+        helix glyph (a red oval), and optional text label (see below). A
+        dialog box will open for you to enter the text. Consecutive
+        ovals will be rendered as an unbroken red line.</em>
+    </li>
+    <li>Sheet<br> <em>Marks selected positions with a
+        sheet glyph (a green arrow oriented from left to right), and
+        optional text label (see below). A dialog box will open for you
+        to enter the text. Consecutive arrows will be joined together to
+        form a single green arrow.</em>
+    </li>
+    <li><a name="rna">RNA Helix</a> (only shown when working with
+      nucleotide sequences)<br> <em>Marks selected positions
+        as participating in a base pair either upstream or downstream.
+        When the dialog box opens, enter a '(' to indicate these bases
+        pair with columns upstream (to right), and ')' to indicate this
+        region pairs with bases to the left of the highlighted columns.
+        Other kinds of base-pair annotation are also supported (e.g. 'A'
+        and 'a', or '<' and '>'), and Jalview will suggest an
+        appropriate symbol based on the closest unmatched parenthesis to
+        the left.<br />If any brackets do not match up, then an orange
+        square will highlight the first position where a bracket was
+        found not to match.
+    </em></li>
+    <li>Label<br> <em>Set the text label at the selected
+        positions. A dialog box will open for you to enter the text. If
+        more than one consecutive position is marked with the same
+        label, only the first position's label will be rendered.</em>
+    </li>
+    <li>Colour<br> <em>Changes the colour of the
+        annotation text label.</em>
+    </li>
+    <li>Remove Annotation<br> <em>Blanks any annotation
+        at the selected positions on the row. Note: <strong>This
+          cannot be undone</strong>
+    </em>
+    </li>
+  </ul>
+  <p>
+    User defined annotation is stored and retrieved using <a
+      href="../features/jalarchive.html">Jalview Archives</a>.
+  </p>
+  <p>
+    <em>Current Limitations</em>
+  </p>
+  <p>
+    The Jalview user interface does not support interactive creation and
+    editing of quantitative annotation (histograms and line graphs), or
+    to create annotation associated with a specific sequence or group.
+    It is also incapable of annotation grouping or changing the style of
+    existing annotation (e.g. to change between line or bar charts, or
+    to make multiple line graphs). These annotation capabilities are
+    only possible by the import of an <a href="annotationsFormat.html">Annotation
+      file</a>.<br>
+  </p>
 </body>
 </html>
diff --git a/help/html/features/annotationsFormat.html b/help/html/features/annotationsFormat.html
index afd3586..c4e54df 100644
--- a/help/html/features/annotationsFormat.html
+++ b/help/html/features/annotationsFormat.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -31,9 +31,8 @@
     Alignment annotations can be imported onto an alignment since
     version 2.08 of Jalview, via an annotations file. It is a simple
     ASCII text file consisting of tab delimited records similar to the <a
-      href="featuresFormat.html"
-    >Sequence Features File</a>, and introduced primarily for use with the
-    Jalview applet.
+      href="featuresFormat.html">Sequence Features File</a>, and
+    introduced primarily for use with the Jalview applet.
   </p>
 
   <p>
@@ -55,8 +54,8 @@
     alignment window.
   </p>
   <p>
-    <strong>THE ANNOTATION FILE FORMAT</strong> <br />An annotation file
-    consists of lines containing an instruction followed by tab
+    <strong>THE ANNOTATION FILE FORMAT</strong> <br />An annotation
+    file consists of lines containing an instruction followed by tab
     delimited fields. Any lines starting with "#" are
     considered comments, and ignored. The sections below describe the
     structure of an annotation file.
@@ -83,11 +82,10 @@
   </ul>
   <p>
     At the end of this document, you can also find notes on <a
-      href="#compatibility"
-    >compatibility</a> of annotation files across different versions of
-    Jalview. An <a href="#exampleann">example annotation file</a> is
-    also provided along with instructions on how to import it to
-    Jalview.
+      href="#compatibility">compatibility</a> of annotation files
+    across different versions of Jalview. An <a href="#exampleann">example
+      annotation file</a> is also provided along with instructions on how to
+    import it to Jalview.
   </p>
   <hr />
   <p>
@@ -110,8 +108,9 @@
     followed by a <em>description</em> for the row, which is shown in a
     tooltip when the user mouses over the annotation row's label. Since
     Jalview 2.7, the description field may also contain HTML tags (in
-    the same way as a <a href="featuresFile.html">sequence feature's</a>
-    label), providing the text is enclosed in an <html/> tag.
+    the same way as a <a href="featuresFormat.html">sequence
+      feature's</a> label), providing the text is enclosed in an
+    <html/> tag.
   <ul>
     <em>Please note: URL links embedded in HTML descriptions are
       not yet supported.</em>
@@ -124,6 +123,7 @@
     <em>GRAPH_TYPE</em>. The allowed values of <em>GRAPH_TYPE</em> and
     corresponding interpretation of each <em>Value</em> are shown below:
 
+
   
   <ul>
     <li><strong>BAR_GRAPH</strong><br> Plots a histogram with
@@ -190,9 +190,9 @@ GRAPHLINE	<em>graph_name</em>	<em>value</em>	<em>label</em>	<em>colo
   </ul>
   </p>
   <p>
-    <strong><a name="groupdefs">SEQUENCE_GROUP</a></strong><br /> Groups
-    of sequences and column ranges can be defined using a tab delimited
-    statement like:
+    <strong><a name="groupdefs">SEQUENCE_GROUP</a></strong><br />
+    Groups of sequences and column ranges can be defined using a tab
+    delimited statement like:
   </p>
   <pre>SEQUENCE_GROUP	Group_Name	Group_Start	Group_End	<em>Sequences</em>
   </pre>
@@ -296,8 +296,8 @@ GRAPHLINE	<em>graph_name</em>	<em>value</em>	<em>label</em>	<em>colo
       <tr>
         <td width="50%">hide</td>
         <td>Boolean (default false) indicating whether the rows in
-          this group should be marked as hidden.<br />
-        <em>Note:</em> if the group is sequence associated (specified by
+          this group should be marked as hidden.<br /> <em>Note:</em>
+          if the group is sequence associated (specified by
           SEQUENCE_REF), then all members will be hidden and marked as
           represented by the reference sequence.
         </td>
diff --git a/help/html/features/bioJsonFormat.html b/help/html/features/bioJsonFormat.html
index f1a5b4e..d9aaee6 100644
--- a/help/html/features/bioJsonFormat.html
+++ b/help/html/features/bioJsonFormat.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,7 @@
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  -->
 <head>
-<title>BioJSON in Jalviwe</title>
+<title>BioJSON in Jalview</title>
 </head>
 <body>
   <p>
@@ -40,8 +40,7 @@
   </p>
   <p>
     The BioJSON specification is published at <a
-      href="http://jalview.github.io/biojson/"
-    >http://jalview.github.io/biojson/</a>.
+      href="http://jalview.github.io/biojson/">http://jalview.github.io/biojson/</a>.
   </p>
   <p>
     <em>Import of BioJSON data from HTML pages</em>
diff --git a/help/html/features/biojsmsa.html b/help/html/features/biojsmsa.html
index 89180d5..32ef11d 100644
--- a/help/html/features/biojsmsa.html
+++ b/help/html/features/biojsmsa.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- *Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- *Copyright (C) 2015 The Jalview Authors
+ *Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ *Copyright (C) 2016 The Jalview Authors
  *
  *This file is part of Jalview.
  *
diff --git a/help/html/features/chimera.html b/help/html/features/chimera.html
index e80d0e2..78be146 100644
--- a/help/html/features/chimera.html
+++ b/help/html/features/chimera.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,43 +28,23 @@
   </p>
   <p>
     Since Jalview 2.8.2, <a href="http://www.cgl.ucsf.edu/chimera/">Chimera</a>
-    (http://www.cgl.ucsf.edu/chimera/) has been integrated into Jalview
-    for interactively viewing structures opened by entries in the <strong>"Structure"</strong>
-    submenu in the <a href="../menus/popupMenu.html">sequence id
-      pop-up menu</a> (if you can't see this, then you need to <a
-      href="viewingpdbs.html"
-    >associate a PDB structure</a> with the sequence). Chimera is
-    available from the Jalview desktop, provided Chimera has been
-    separately installed.
+    (http://www.cgl.ucsf.edu/chimera/) can be used for viewing
+    structures opened via the <a href="structurechooser.html"><strong>"View
+        Structure Data.."</strong> dialog</a>.
   </p>
   <p>
     You can set a default choice of Jmol or Chimera structure viewer in
     <a href="preferences.html#structure"> Preferences</a>. You can also
     optionally specify the path to the Chimera program here (if it
-    differs from the standard paths searched by Jalview).
+    differs from the standard paths searched by Jalview).<br /> <strong>Please
+      make sure your version of Chimera is up to date. Jalview requires
+      at least Chimera version 1.11.1</strong>
+  </p>
   <p>
     If you save your Jalview session as a project file, the state of any
     open Chimera windows will also be saved, and can be reopened by
     loading the project file on any machine with Chimera installed. <em>Since
       Jalview 2.9.</em>
-    <!-- <p>The following menu entries are provided for viewing structure data<br>
-  <ul>
-    <li>The <strong>"Structure→View
-        Structure→</strong> submenu allows a single PDB structure to be chosen
-      for display from the available structures for a sequence.
-    </li>
-    <li>The <strong>"Structure→View all <em>N</em>
-        structures
-    </strong> option will open a new window containing all structures associated
-      with the current selection.
-    </li>
-    <li>The <strong>"Structure→View all <em>N</em>
-        representative structures
-    </strong> option will open a new window containing exactly one structure per
-      currently selected sequence.<br /></li>
-  </ul>
-  <br> 
-</p> -->
   <p>
     <a name="align"><strong>Superposing structures based on
         their aligned sequences</strong></a><br> If several structures are
@@ -85,13 +65,23 @@
     number and chain code ([RES]Num:Chain). Moving the mouse over an
     associated residue in an alignment window highlights the associated
     atoms in the displayed structures. When residues are selected in the
-    Chimera window, they are highlighted on the alignment. For
-    comprehensive details of Chimera's commands, refer to the tool's
-    Help menu.
+    Chimera window, they are highlighted on the alignment.
+  <p>For comprehensive details of Chimera's commands, refer to the
+    tool's Help menu.</p>
+  <p>
+    <strong>Selecting residues in Jalview from Chimera</strong><br />
+    When a selection is highlighted in a Jalview window, use the
+    <em>Select→Select Highlighted Region</em> or press <em>B</em>
+    to add the mapped positions to the alignment window's column
+    selection.<br /> <em>Hint: Use your machine's 'switch
+      application' key combination (Alt-Tab on Windows and Linux,
+      Cmd-Tab on OSX) to quickly switch between UCSF Chimera and Jalview
+      before pressing 'B' to select highlighted regions.</em>
+  </p>
   <p>
     Basic screen operations (see <a
-      href="http://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/mouse.html"
-    >Chimera help</a> at
+      href="http://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/mouse.html">Chimera
+      help</a> at
     http://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/mouse.html
     for full details).
   <table border="1">
@@ -181,7 +171,7 @@
         </strong><em> Colours each residue in the structure with the colour
             of its corresponding residue in the associated sequence as
             rendered in the associated alignment views, including any
-            Uniprot sequence features or region colourings.<br />Pick
+            UniProt sequence features or region colourings.<br />Pick
             which of the associated alignment views are used to colour
             the structures using the <strong>View→Colour
               by ..</strong> sub menu.
@@ -205,8 +195,8 @@
             colourschemes.<br>
         </strong><em>The remaining entries apply the colourschemes available
             from the standard and user defined <a
-            href="../colourSchemes/index.html"
-          >amino acid colours</a>.
+            href="../colourSchemes/index.html">amino acid
+              colours</a>.
         </em></li>
       </ul></li>
     <li><strong>Chimera<br>
@@ -237,8 +227,8 @@
   </p>
   Jalview and Chimera communicate using Chimera's
   <a
-    href="http://www.cgl.ucsf.edu/chimera/current/docs/ContributedSoftware/restserver/restserver.html"
-  >REST service</a>
+    href="http://www.cgl.ucsf.edu/chimera/current/docs/ContributedSoftware/restserver/restserver.html">REST
+    service</a>
   (http://www.cgl.ucsf.edu/chimera/current/docs/ContributedSoftware/restserver/restserver.html).
   <br> Technically this requires both Chimera and Jalview to open
   ports on the local network, and this may be blocked by Windows
diff --git a/help/html/features/clarguments.html b/help/html/features/clarguments.html
index b514e65..b02b9cf 100644
--- a/help/html/features/clarguments.html
+++ b/help/html/features/clarguments.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -67,8 +67,8 @@
         <div align="center">-annotations FILE/URL</div>
       </td>
       <td>Add precalculated annotations to the alignment. See <a
-        href="annotationsFormat.html" target="NEW"
-      >Annotation File</a> description.
+        href="annotationsFormat.html" target="NEW">Annotation
+          File</a> description.
       </td>
     </tr>
     <tr>
@@ -96,6 +96,17 @@
     </tr>
     <tr>
       <td>
+        <div align="center">-nonews</div>
+      <td>
+        <div align="left">
+          Disable check for <a href="../webServices/newsreader.html">Jalview
+            news</a> on startup (not recommended other than for classroom /
+          demo usage)
+        </div>
+      </td>
+    </tr>
+    <tr>
+      <td>
         <div align="center">-nousagestats</div>
       <td>
         <div align="left">Turn off google analytics usage tracking</div>
@@ -247,6 +258,14 @@
       <td><div align="left">Create Scalable Vector Graphics
           file FILE from alignment.</div></td>
     </tr>
+    <tr>
+      <td><div align="center">-biojsMSA FILE</div></td>
+      <td><div align="left">Write an HTML page to display
+          the alignment with the <a href="biojsmsa.html">
+          BioJS MSAviewer MSA</a>
+          </div>
+      </td>
+    </tr>
   </table>
 </body>
 </html>
diff --git a/help/html/features/codingfeatures.html b/help/html/features/codingfeatures.html
index e4b8950..ca68d66 100644
--- a/help/html/features/codingfeatures.html
+++ b/help/html/features/codingfeatures.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/features/columnFilterByAnnotation.html b/help/html/features/columnFilterByAnnotation.html
index 128b478..b5781a5 100644
--- a/help/html/features/columnFilterByAnnotation.html
+++ b/help/html/features/columnFilterByAnnotation.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -31,22 +31,30 @@
     The 'Select/Hide by Annotation' window allows columns to be selected
     or hidden according to annotation rows on the alignment. The dialog
     box is opened <em>via</em> "<strong>Select→Select/Hide
-      Columns by Annotation...</strong>", and different filters are
-    presented dependent upon the data shown in the selected annotation
+      Columns by Annotation...</strong>", and different filters are then
+    presented for filtering data according to the selected annotation
     row.
   </p>
   <table>
     <tr>
-      <td><img src="annotationColumnSelectionWithSM.gif"></td>
-      <td><img src="annotationColumnSelectionWithoutSM.gif"></td>
+      <td><img src="AnnotationColumnSelectionWithSM.gif"></td>
+      <td><img src="AnnotationColumnSelectionWithoutSM.gif"></td>
     </tr>
   </table>
-
-  <p>If an annotation with numeric values is selected, the threshold
+  <p>The drop down menu lists the annotation available on the
+    alignment. Sequence associated annotation rows will be shown with
+    the sequence ID appended to the annotation label. It is only
+    possible to select one row at a time.</p>
+  <p>
+    If an annotation with numeric values is selected, the threshold
     filter option is activated. For other types of annotation, use the
     text box and secondary structure check boxes (right). The radio
     buttons at the bottom of the dialog specify the action applied to
-    columns matching the query.</p>
+    columns matching the query.<br /> <em>Note: annotation
+      containing only numeric labels (e.g. T-COFFEE column confidence
+      scores) will not be treated as quantitative data. You will need to
+      enter search expressions to select columns in this case.</em>
+  </p>
   <ul>
     <li><strong>Search Filter</strong>
       <ul>
diff --git a/help/html/features/commandline.html b/help/html/features/commandline.html
index 7b58a34..de49ba2 100644
--- a/help/html/features/commandline.html
+++ b/help/html/features/commandline.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -56,8 +56,8 @@
   <pre>java -Djava.ext.dirs=$INSTALL_DIR$/lib -cp $INSTALL_DIR$/jalview.jar jalview.bin.Jalview -open [FILE] </pre>
   <p>
     Use '-help' to get more information on the <a
-      href="clarguments.html"
-    >command line arguments</a> that Jalview accepts.
+      href="clarguments.html">command line arguments</a> that
+    Jalview accepts.
   </p>
   <p> </p>
   <p> </p>
diff --git a/help/html/features/creatinFeatures.html b/help/html/features/creatinFeatures.html
index 643c8a3..35749c8 100644
--- a/help/html/features/creatinFeatures.html
+++ b/help/html/features/creatinFeatures.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,12 +26,12 @@
   <strong>Creating Sequence Features</strong>
   <p>
     Jalview can create sequence features from the matches of a <a
-      href="search.html"
-    >regular expression search</a>, or from the currently selected area
-    via the <strong>"selection→Create sequence
-      feature"</strong> entry in the <a href="../menus/popupMenu.html">selection
-      area popup menu</a>. In both cases, the <strong>Create
-      Features</strong> dialog box will then be opened:
+      href="search.html">regular expression search</a>, or from the
+    currently selected area via the <strong>"selection→Create
+      sequence feature"</strong> entry in the <a
+      href="../menus/popupMenu.html">selection area popup menu</a>. In
+    both cases, the <strong>Create Features</strong> dialog box will
+    then be opened:
   </p>
   <p>
     <img src="crnewfeature.gif">
diff --git a/help/html/features/cursorMode.html b/help/html/features/cursorMode.html
index d257f1b..251af13 100644
--- a/help/html/features/cursorMode.html
+++ b/help/html/features/cursorMode.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/features/dasfeatures.html b/help/html/features/dasfeatures.html
index 5b88ca9..533ee22 100644
--- a/help/html/features/dasfeatures.html
+++ b/help/html/features/dasfeatures.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -27,11 +27,8 @@
   <p>
     <strong>DAS Sequence Feature Retrieval</strong>
   </p>
-  <p>
-    Jalview includes a client for retrieving sequences and their
-    features via the <a href="http://www.biodas.org">Distributed
-      Annotation System</a>.
-  </p>
+  <p>Jalview includes a client for retrieving sequences and their
+    features via the Distributed Annotation System.</p>
   <ol>
     <li>Open the Feature Settings panel by selecting "View
       -> Feature Settings..."</li>
@@ -51,15 +48,15 @@
     </li>
   </ol>
   <p>
-    If your DAS source selection contains sources which use Uniprot
-    accession ids, you will be asked whether Jalview should find Uniprot
+    If your DAS source selection contains sources which use UniProt
+    accession ids, you will be asked whether Jalview should find UniProt
     Accession ids for the given sequence names. It is important to
-    realise that many DAS sources only use Uniprot accession ids, rather
-    than Swissprot/Uniprot sequence names.<br> The <a
-      href="../webServices/dbreffetcher.html"
-    >database reference fetcher</a> documentation describes how Jalview
-    discovers what database references are appropriate for the sequences
-    in the alignment.
+    realise that many DAS sources only use UniProt accession ids, rather
+    than Swissprot/UniProt sequence names.<br> The <a
+      href="../webServices/dbreffetcher.html">database
+      reference fetcher</a> documentation describes how Jalview discovers
+    what database references are appropriate for the sequences in the
+    alignment.
   <ul>
     <li><em>Note</em><br> Please remember to save your
       alignment if either the start/end numbering, or the sequence IDs
@@ -69,6 +66,12 @@
   <p>
     <em>DAS support was introduced in Jalview Version 2.1.</em>
   </p>
+  <br />
+  <p>
+    <em>The DAS registry at http://www.dasregistry.org was
+      decommissioned early in 2015. An unmaintained mirror is currently
+      hosted at http://www.ebi.ac.uk/das-srv/registry/.</em>
+  </p>
   <p> 
 </body>
 </html>
diff --git a/help/html/features/dassettings.html b/help/html/features/dassettings.html
index 1a0c863..e52d56a 100644
--- a/help/html/features/dassettings.html
+++ b/help/html/features/dassettings.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,14 +29,13 @@
   </p>
   <p>
     Jalview can retrieve sequences and features from many <a
-      href="http://biodas.org/"
-    >DAS</a> sources. The DAS sources that it uses are discovered and
-    selected <em>via</em> the DAS settings panel, opened either from the
-    <a href="featuresettings.html">View→Feature Settings</a>
-    dialog box from the alignment window's menu bar, or the <a
-      href="featuresettings.html"
-    >Tools→Preferences</a> dialog box opened from the Desktop menu
-    bar.
+      href="http://biodas.org/">DAS</a> sources. The DAS sources
+    that it uses are discovered and selected <em>via</em> the DAS
+    settings panel, opened either from the <a
+      href="featuresettings.html">View→Feature Settings</a> dialog
+    box from the alignment window's menu bar, or the <a
+      href="featuresettings.html">Tools→Preferences</a>
+    dialog box opened from the Desktop menu bar.
   </p>
   <p>
     <img src="das.gif">
diff --git a/help/html/features/editingFeatures.html b/help/html/features/editingFeatures.html
index 5fc36c6..0577174 100644
--- a/help/html/features/editingFeatures.html
+++ b/help/html/features/editingFeatures.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -41,10 +41,9 @@
     <!-- and <strong>Feature Group</strong>  -->
     pull down menu. In addition to the Name, group, colour and
     description attributes described for the <a
-      href="creatinFeatures.html"
-    >new feature dialog box</a>, a feature's start and end position can be
-    changed either by entering a new position directly or by using the
-    adjacent up and down buttons.
+      href="creatinFeatures.html">new feature dialog box</a>, a
+    feature's start and end position can be changed either by entering a
+    new position directly or by using the adjacent up and down buttons.
   </p>
   <p>
     Select <strong>Amend</strong> to update the feature, <strong>Delete</strong>
diff --git a/help/html/features/ensemblsequencefetcher.html b/help/html/features/ensemblsequencefetcher.html
new file mode 100644
index 0000000..cc61bf4
--- /dev/null
+++ b/help/html/features/ensemblsequencefetcher.html
@@ -0,0 +1,96 @@
+<html>
+<!--
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ -->
+<head>
+<title>Fetching ENSEMBL Data in Jalview</title>
+</head>
+<body>
+
+  <strong>Fetching ENSEMBL Data in Jalview</strong>
+  <br /> Jalview Version 2.10 (October 2016) introduced support to
+  retrieve annotated transcripts, peptides and genomic contigs from
+  <a href="http://www.ensembl.org">ENSEMBL</a>.
+  <br />
+  <img src="selectfetchdb.gif" align="right" width="480" height="204"
+    alt="Database selection dialog with Ensembl sequence source tooltip">
+
+  <p>Two types of ENSEMBL source are provided. ENSEMBL queries the
+    main ENSEMBL warehouse containing data for higher eukaryotes, and
+    EnsemblGenomes, which queries Ensembl Pathogens, and other
+    warehouses.</p>
+  <p>
+    <strong>General Use</strong><br /> If you have a set of Ensembl
+    gene or transcript IDs, then you can retrieve them <em>via</em> the
+    sequence fetcher dialog opened after selecting the most appropriate
+    source (either 'ENSEMBL', or Ensembl Genomes). However, Jalview's
+    Ensembl client has a couple of additional capabilities:
+  </p>
+  <p>
+    <strong>Retrieving aligned transcripts for a genomic ID</strong>
+  </p>
+  <p>If a single genomic identifier is entered in the Ensembl
+    fetcher, Jalview will return all transcripts and products for the
+    locus, and display them in a split view - complete with sequence
+    variant annotation.</p>
+  <p>
+    <strong>Retrieving orthologs for a gene ID</strong>
+  </p>
+  <p>
+    If a gene ID is entered (e.g. fox1), Jalview will resolve Ensembl
+    genomic identifiers for a predefined set of taxa (Mouse, Rat, Human,
+    Yeast in Jalview 2.10).<br />
+  </p>
+  <p>
+    <strong><a name="ensemblannotation">Ensembl Sequence
+        Features</a></strong><br /> Jalview 2.10 includes support for the
+    visualisation and transfer genomic and transcriptomic sequence
+    features onto protein product sequences. Retrieval of a genomic
+    locus results in a set of transcripts that are annotated with
+    nucleotide variant information and exonic regions. By default,
+    intronic regions will be hidden.
+  </p>
+  <p>
+    <strong><a name="variantvis">Variant information on
+        Protein Products</a></strong><br />Jalview can translate genomic variant
+    annotation into protein sequence variant codes for variants
+    intersecting coding regions of a gene. To see this in action, use
+    the <strong>Calculate→Show cross-references</strong> menu to
+    view protein product sequences for the currently displayed (or
+    selected) sequences. The same menu allows you to recover Ensembl
+    exon, transcript and variant information when viewing UniProt
+    sequences.
+  </p>
+  <p>
+    <strong>Viewing more information about variant annotation</strong><br />
+    Variants are highlighted as red sequence features on the protein
+    sequence, with each one reporting all protein sequence variants
+    observed at that position as a result of the genomic variants.
+    Right-clicking a variant allows you to open the Ensembl Variants web
+    page for each variant, via the <strong>Link</strong> submenu.
+  </p>
+  <p>
+    <strong>Work in Progress !</strong><br />In the next few releases,
+    we hope to improve and extend Jalview's support for working with
+    Ensembl. If you have any problems, questions or suggestions then
+    please get in contact with us via the Jalview discussion list.
+  </p>
+</body>
+</html>
\ No newline at end of file
diff --git a/help/html/features/featuresFormat.html b/help/html/features/featuresFormat.html
index 9d354dc..377b508 100644
--- a/help/html/features/featuresFormat.html
+++ b/help/html/features/featuresFormat.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -83,7 +83,7 @@
         <li><em>label</em><br> Indicate that the feature
           description should be used to create a colour for features of
           this type.<br> <em>Note: if no threshold value is
-            needed then the final '|' may be ommitted.<br> This
+            needed then the final '|' may be omitted.<br> This
             keyword was added in Jalview 2.6
         </em></li>
 
@@ -121,11 +121,12 @@
     a colour the feature).</p>
 
   <p>
-    If your sequence annotation is already available in GFF Format (see
-    <a href="http://www.sanger.ac.uk/resources/software/gff/spec.html">http://www.sanger.ac.uk/resources/software/gff/spec.html</a>),
-    then you can leave it as is, after first adding a line containing
-    only 'GFF' after any Jalview feature colour definitions (<em>this
-      mixed format capability was added in Jalview 2.6</em>). Alternately,
+    If your sequence annotation is already available in <a href="http://gmod.org/wiki/GFF2">GFF2</a> (http://gmod.org/wiki/GFF2) or
+    <a href="https://github.com/The-Sequence-Ontology/Specifications/blob/master/gff3.md">GFF3</a> 
+    (http://github.com/The-Sequence-Ontology/Specifications/blob/master/gff3.md) format, 
+    then you can leave it as is, after first adding a line containing only
+    'GFF' after any Jalview feature colour definitions (<em>this
+    mixed format capability was added in Jalview 2.6</em>). Alternately,
     you can use Jalview's own sequence feature annotation format, which
     additionally allows HTML and URLs to be directly attached to each
     piece of annotation.
@@ -141,13 +142,13 @@
 </pre>
 
   This format allows two alternate ways of referring to a sequence,
-  either by its text ID, or its index in an associated alignment.
-  Normally, sequence features are associated with sequences rather than
-  alignments, and the sequenceIndex field is given as "-1". In
-  order to specify a sequence by its index in a particular alignment,
-  the sequenceId should be given as "ID_NOT_SPECIFIED",
-  otherwise the sequenceId field will be used in preference to the
-  sequenceIndex field.
+  either by its text ID, or its index (base 0) in an associated
+  alignment. Normally, sequence features are associated with sequences
+  rather than alignments, and the sequenceIndex field is given as
+  "-1". In order to specify a sequence by its index in a
+  particular alignment, the sequenceId should be given as
+  "ID_NOT_SPECIFIED", otherwise the sequenceId field will be
+  used in preference to the sequenceIndex field.
   </p>
 
 
@@ -161,14 +162,14 @@
     description line will be translated into URL links. A link symbol
     will be displayed adjacent to any feature which includes links, and
     these are made available from the <a
-      href="../menus/popupMenu.html#sqid.popup"
-    >links submenu</a> of the popup menu which is obtained by
-    right-clicking when a link symbol is displayed in the tooltip.<br>
-    <em>Non-positional features</em><br> Specify the <em>start</em>
-    and <em>end</em> for a feature to be <strong>0</strong> in order to
-    attach it to the whole sequence. Non-positional features are shown
-    in a tooltip when the mouse hovers over the sequence ID panel, and
-    any embedded links can be accessed from the popup menu.<br /> <em>Scores</em><br>
+      href="../menus/popupMenu.html#sqid.popup">links submenu</a>
+    of the popup menu which is obtained by right-clicking when a link
+    symbol is displayed in the tooltip.<br> <em>Non-positional
+      features</em><br> Specify the <em>start</em> and <em>end</em> for
+    a feature to be <strong>0</strong> in order to attach it to the
+    whole sequence. Non-positional features are shown in a tooltip when
+    the mouse hovers over the sequence ID panel, and any embedded links
+    can be accessed from the popup menu.<br /> <em>Scores</em><br>
     Scores can be associated with sequence features, and used to sort
     sequences or shade the alignment (this was added in Jalview 2.5).
     The score field is optional, and malformed scores will be ignored.
diff --git a/help/html/features/featureschemes.html b/help/html/features/featureschemes.html
index fa91c59..9f15be7 100644
--- a/help/html/features/featureschemes.html
+++ b/help/html/features/featureschemes.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/features/featuresettings.html b/help/html/features/featuresettings.html
index 54c6dcb..68fab14 100644
--- a/help/html/features/featuresettings.html
+++ b/help/html/features/featuresettings.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -72,23 +72,29 @@
   </p>
   <p>
     <strong><em>Feature settings pop-up menu</em></strong><br> <strong>Right-click</strong>
-    on a feature to open a pop-up menu that allows you to sort the
-    alignment or current selection using that feature type (see below),
-    or toggle the type of colouring used for the feature. Features may
-    be highlighted with either a single colour or a <a
-      href="featureschemes.html"
-    >feature colourscheme</a> based on either the scores associated with
-    that feature or from the feature's description (e.g. to distinguish
-    different names associated with a DOMAIN feature).
+    on a feature to open a pop-up menu that allows you to
+  <ul>
+    <li>Hide, show and select columns containing that feature</li>
+    <li>Sort the alignment or current selection using that feature
+      type (see below)</li>
+    <li>Toggle the type of colouring used for the feature</li>
+  </ul>
+  <p>
+    Features may be highlighted with either a single colour or a <a
+      href="featureschemes.html">feature colourscheme</a> based on
+    either the scores associated with that feature or from the feature's
+    description (e.g. to distinguish different names associated with a
+    DOMAIN feature).
   </p>
   <p>
-    <strong>Ordering alignment by features</strong><br> The 'Seq
-    Sort by Score' and 'Seq Sort by Density' buttons will sort the
-    alignment based on the average score or total number of currently
-    active features and groups on each sequence. To order the alignment
-    using a specific feature type, use the <em>sort by ..</em> entries
-    in the pop-up menu for that type.<br> <em>Feature sorting
-      and graduated feature colouring were introduced in Jalview 2.5</em>
+    <strong><a name="sortbyfeature">Ordering alignment by
+        features</a></strong><br> The 'Seq Sort by Score' and 'Seq Sort by
+    Density' buttons will sort the alignment based on the average score
+    or total number of currently active features and groups on each
+    sequence. To order the alignment using a specific feature type, use
+    the <em>sort by ..</em> entries in the pop-up menu for that type.<br>
+    <em>Feature sorting and graduated feature colouring were
+      introduced in Jalview 2.5</em>
   </p>
 
   <p>
@@ -104,7 +110,7 @@
     the bottom of the list is rendered <em>below</em> a feature higher
     up in the list.<br> <em><strong>You can change
         the order of a feature by dragging it up and down the list with
-        the mouse</strong></em>.
+        the mouse (not applet)</strong></em>.
   </p>
   <p>
     The <strong><em>Optimise order</em></strong> button (currently only
@@ -112,12 +118,11 @@
     ordering based on the average length of each feature type.
   </p>
   <p>
-    The <strong><em>transparency slider setting</em></strong> (currently
-    only available in the application version) controls the visibility
-    of features rendered below other features. Reducing the transparency
-    will mean that features at the top of the list can obscure features
-    lower down, and increasing it allows the user to 'see through' the
-    upper layers of a set of features.
+    The <strong><em>transparency slider setting</em></strong> controls
+    the visibility of features rendered below other features. Reducing
+    the transparency will mean that features at the top of the list can
+    obscure features lower down, and increasing it allows the user to
+    'see through' the upper layers of a set of features.
   </p>
   <p>
     <strong><em>You can save all features, with their
diff --git a/help/html/features/groovy.html b/help/html/features/groovy.html
index bdaeab6..ed7a8b2 100644
--- a/help/html/features/groovy.html
+++ b/help/html/features/groovy.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -27,34 +27,23 @@
     <strong>The Groovy Shell</strong>
   </p>
   <p>
-    <a href="http://groovy.codehaus.org/">Groovy</a> is an "<em>agile
-      and dynamic language for the Java platform</em>". The groovy
-    scripting language makes it extremely easy to programmatically
-    interact with Java programs, in much the same way that Javascript is
-    used to generate and interact with applets and other objects on the
-    page.
+    Groovy (<a href="http://www.groovy-lang.org/">www.groovy-lang.org</a>)
+    is an "<em>agile and dynamic language for the Java
+      platform</em>". The groovy scripting language makes it extremely
+    easy to programmatically interact with Java programs, in much the
+    same way that Javascript is used to generate and interact with
+    applets and other objects on the page.
   </p>
   <p>
-    <strong><em>Getting Groovy...</em> </strong><br> Jalview Groovy
-    support is only possible if the core groovy jars which include the
-    GroovyShell are present on the CLASSPATH when Jalview is started.
-  </p>
-  <p>
-    The jars are obtained from the <em>embedded</em> directory within
-    the <a href="http://dist.codehaus.org/groovy/distributions">groovy
-      distribution</a>. The easiest way of adding them to the Jalview
-    classpath is to download and build Jalview from its source
-    distribution, and then add the groovy-all-*.jar to the lib directory
-    whose path is given in the java.ext.dirs property.
-  </p>
-  <p>
-    <strong>Opening Jalview's Groovy Console</strong><br>If groovy
-    is available, then the <strong>Tools→Groovy
-      Console...</strong> menu entry will be available from the Jalview Desktop's
-    drop-down menu. Selecting this will open the <a
-      href="http://groovy.codehaus.org/Groovy+Console"
-    >Groovy Console</a> which allows you to interactively execute Groovy
-    scripts within the Jalview run-time environment.
+    <em>Getting Groovy...</em><br> Jalview comes with an embedded
+    installation of Groovy. Just select <strong>Tools→Groovy
+      Console...</strong> from the Jalview Desktop's drop-down menu. After a
+    short pause, you should then see the <a
+      href="http://groovy-lang.org/groovyconsole.html">Groovy
+      Console</a> appear. This allows you to interactively execute Groovy
+    scripts whilst Jalview is running. We've also provided a <strong>Calculations→Execute
+      Groovy Script</strong> button so you can execute the currently loaded
+    groovy script whilst viewing an alignment.
   </p>
   <p>
     <strong>Executing groovy scripts on Jalview startup</strong><br>
@@ -70,14 +59,29 @@
   </p>
   <p>
     <strong>Access to Jalview's functions from Groovy Scripts</strong><br>
-    There is as yet no properly defined scripting interface to Jalview,
-    but all the public methods of the jalview class hierarchy can be
-    called from Groovy scripts. The access point for this is the <strong>Jalview</strong>
-    object defined in the groovy environent which corresponds to the
-  <pre>jalview.gui.Desktop</pre>
-  object which manages all the Jalview windows. Here's an example to get
-  you started:
-  <br>
+    The scripting interface to Jalview is still a work in progress, so
+    we recommend you also take a look at Jalview's source, since all the
+    public methods of the jalview class hierarchy can be called from
+    Groovy scripts. In addition, the following objects are also defined:
+
+
+  
+  <ul>
+    <li><strong>Jalview</strong> - this is bound to <code>jalview.bin.Jalview</code>.<br />Useful
+      methods include:
+      <ul>
+        <li>Jalview.getAlignFrames() - returns a list of
+          jalview.gui.AlignFrame objects</li>
+        <li>Jalview.getCurrentAlignFrame() - returns the alignment
+          window which is currently being looked at by the user</li>
+      </ul></li>
+    <li><strong>currentAlFrame</strong> - this is only defined when
+      running a Groovy script via the -groovy command line argument. It
+      returns the first alignment window created after acting on the
+      other arguments passed on the command line.</li>
+  </ul>
+  <p>
+    <em>A simple script</em><br />
   <ul>
     <li>Getting the title, alignment and first sequence from the
       current alignFrame<br> <pre>
@@ -87,13 +91,26 @@ def alignment = alf[0].viewport.alignment;
 def seq = alignment.getSequenceAt(0);
 </pre>
     </li>
-    <li>When running a groovy script from the command line, the
-      alignment that was just loaded can be referred to like so:<br>
-    <pre>
+    <li>If you wanted to do the same thing from the command line,
+      you can refer to alignment that was just loaded with
+      currentAlFrame:<br> <pre>
 print currentAlFrame.getTitle();</pre>
   </ul>
-  If you have downloaded the InstallAnywhere version of Jalview, you can
-  find additional groovy scripts in the examples/groovy subfolder of the
-  installation directory.
+  <p>
+    <em>Example scripts</em><br />If you have downloaded the
+    InstallAnywhere version of Jalview, you can find additional groovy
+    scripts in the examples/groovy subfolder of the installation
+    directory. The examples are also available at <a
+      href="http://www.jalview.org/examples/groovy">http://www.jalview.org/examples/groovy</a>.
+  </p>
+  <p>
+    <em>Using Groovy to add new Alignment Calculations</em><br />We've
+    simplified the alignment analysis programming interface in Jalview
+    2.10 to make it easy for you to add your own dynamic annotation
+    tracks with Groovy. Have a look at the <a
+      href="../groovy/featureCounter.html">featureCounter.groovy</a>
+    example for more information.
+  </p>
+
 </body>
 </html>
diff --git a/help/html/features/hiddenRegions.html b/help/html/features/hiddenRegions.html
index eab0df4..d4cd62b 100644
--- a/help/html/features/hiddenRegions.html
+++ b/help/html/features/hiddenRegions.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -86,9 +86,9 @@
     <li><strong><a href="../calculations/tree.html">Tree</a></strong>,
       <strong><a href="../calculations/pairwise.html">pairwise
           alignment</a></strong> and <strong><a
-        href="../calculations/pca.html"
-      >PCA</a></strong> calculations will only be performed using the <em>visible</em>
-      parts of the alignment.</li>
+        href="../calculations/pca.html">PCA</a></strong> calculations will
+      only be performed using the <em>visible</em> parts of the
+      alignment.</li>
     <li><a href="../webServices/msaclient.html">Multiple
         Sequence Alignments</a> are performed locally on on each visible
       chunk of the input, and concatenated with the hidden regions to
diff --git a/help/html/features/jalarchive.html b/help/html/features/jalarchive.html
index 8e81024..ac4cd48 100644
--- a/help/html/features/jalarchive.html
+++ b/help/html/features/jalarchive.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/features/jmol.html b/help/html/features/jmol.html
index b9e7e84..8dbb120 100644
--- a/help/html/features/jmol.html
+++ b/help/html/features/jmol.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -32,12 +32,12 @@
     structures opened by entries in the <strong>"Structure"</strong>
     submenu in the <a href="../menus/popupMenu.html">sequence id
       pop-up menu</a> (if you can't see this, then you need to <a
-      href="viewingpdbs.html"
-    >associate a PDB structure</a> with the sequence). Jmol is available
-    from the Jalview desktop and should also run in the JalviewLite
-    applet, providing the browser supports Java 1.5. If Jmol is not
-    available, then the original <a href="pdbviewer.html">internal
-      pdb viewer</a> will be used as a fallback.
+      href="viewingpdbs.html">associate a PDB structure</a> with
+    the sequence). Jmol is available from the Jalview desktop and should
+    also run in the JalviewLite applet, providing the browser supports
+    Java 1.5. If Jmol is not available, then the original <a
+      href="pdbviewer.html">internal pdb viewer</a> will be used as a
+    fallback.
   </p>
   <!-- <p>The following menu entries are provided for viewing structure data<br>
   <ul>
@@ -74,15 +74,18 @@
       based structure superposition was added in Jalview 2.6</em>
   </p>
   <p>
-    <strong>Controls</strong><br> The structure is by default
-    rendered as a ribbon diagram. Moving the mouse over the structure
-    brings up tooltips giving the residue name, PDB residue number and
-    chain code, atom name and number
-    ([RES]Num:Chain.AtomName#AtomNumber). If a mapping exists to a
-    residue in any associated sequences, then this will be highlighted
-    in each one's alignment window. The converse also occurs - moving
-    the mouse over an associated residue in an alignment window
-    highlights the associated atoms in the displayed structures.
+    <strong>Controls</strong><br> The structure is by default rendered
+    as a ribbon diagram. Moving the mouse over the structure brings up
+    tooltips giving the residue name, PDB residue number and chain code,
+    atom name and number ([RES]Num:Chain.AtomName#AtomNumber). If a
+    mapping exists to a residue in any associated sequences, then this
+    will be highlighted in each one's alignment window. The converse
+    also occurs - moving the mouse over an associated residue in an
+    alignment window highlights the associated atoms in the displayed
+    structures. Press B or use
+    <em>Select→Select Highlighted columns</em> from any linked
+    alignment window to mark the columns highlighted after mousing over
+    the structure.
   </p>
   <p>Selecting a residue highlights its associated sequence residue
     and alpha carbon location. Double clicking an atom allows distances
@@ -195,7 +198,7 @@
         </strong><em> Colours each residue in the structure with the colour
             of its corresponding residue in the associated sequence as
             rendered in the associated alignment views, including any
-            Uniprot sequence features or region colourings.<br />Pick
+            UniProt sequence features or region colourings.<br />Pick
             which of the associated alignment views are used to colour
             the structures using the <strong>View→Colour
               by ..</strong> sub menu.
@@ -218,8 +221,8 @@
             colourschemes.<br>
         </strong><em>The remaining entries apply the colourschemes available
             from the standard and user defined <a
-            href="../colourSchemes/index.html"
-          >amino acid colours</a>.
+            href="../colourSchemes/index.html">amino acid
+              colours</a>.
         </em></li>
       </ul></li>
     <li><strong>Jmol<br>
@@ -259,11 +262,10 @@
     Jmol scripting console.</p>
   <p>
     The state of each Jmol display is stored within <a
-      href="jalarchive.html"
-    >Jalview archives</a> as a Jmol state recovery script file. This means
-    that any Jmol visualization effects that you add beyond those
-    provided by Jalview will be able to be stored and recovered along
-    with the displayed alignments in Jalview.
+      href="jalarchive.html">Jalview archives</a> as a Jmol state
+    recovery script file. This means that any Jmol visualization effects
+    that you add beyond those provided by Jalview will be able to be
+    stored and recovered along with the displayed alignments in Jalview.
   </p>
   <p>
     <strong>More Information</strong>
@@ -272,8 +274,8 @@
     Jmol is a sophisticated program in its own right, with its own
     command console and scripting language. Only the essentials have
     been described here - the interested reader is referred to <a
-      href="http://jmol.sourceforge.net/docs/"
-    >Jmol's own comprehensive online documentation</a>.
+      href="http://jmol.sourceforge.net/docs/">Jmol's own
+      comprehensive online documentation</a>.
   </p>
   </p>
 </body>
diff --git a/help/html/features/mmcif.html b/help/html/features/mmcif.html
new file mode 100644
index 0000000..323078a
--- /dev/null
+++ b/help/html/features/mmcif.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!--
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ -->
+<html>
+<head>
+<meta charset="UTF-8">
+<title>mmCIF File Format</title>
+</head>
+<body>
+  <strong>What is mmCIF ?</strong>
+  <br />mmCIF stands for 'macro-molecular Crystallographic Information
+  File'. This format was developed by the PDB consortium and the
+  International Union of Crystallography (IUCr), based on
+  Crystallographic Information File (CIF), a format used for describing
+  the structures of small molecules.
+  <br />mmCIF became the recommended format for the exchange of
+  biomacromolecular structures in 2014.
+  <p>
+    <strong>mmCIF and Jalview</strong> <br />Since Jalview 2.10, mmCIF
+    is used for structures downloaded from the PDB. This means:
+  </p>
+  <ol>
+    <li>Jalview can use the mmCIF to read structures that are too
+      large to be represented by a single PDB file. (ie, with more than
+      >62 chains and/or 99999 ATOM records).</li>
+    <li>Jalview users will have access to the richer annotation
+      provided by PDBx/mmCIF files.</li>
+    <li>There may be slight differences between sequences
+      containing modified residues for structures downloaded in mmCIF
+      format. This is because mmCIF differs from the PDB format in the
+      way it describes non-standard sequence data.</li>
+  </ol>
+
+  <em>Support for importing 3D structure data from flat file and
+    EMBL-PDBe as mmCIF was added in Jalview 2.10</em>
+</body>
+</html>
diff --git a/help/html/features/multipleViews.html b/help/html/features/multipleViews.html
index 65aeb80..ff32263 100644
--- a/help/html/features/multipleViews.html
+++ b/help/html/features/multipleViews.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -74,8 +74,7 @@
   <p>
     A tree calculated on a particular view, or loaded onto it, is by
     default associated with just that view. However, the <a
-      href="../calculations/treeviewer.html"
-    >Tree Viewer's</a> <strong>"View→Associate
+      href="../calculations/treeviewer.html">Tree Viewer's</a> <strong>"View→Associate
       leaves"</strong> submenu allows a tree's view association to be
     changed to to any or all other views.
   </p>
diff --git a/help/html/features/newkeystrokes.html b/help/html/features/newkeystrokes.html
index 78d18a6..2b789ba 100644
--- a/help/html/features/newkeystrokes.html
+++ b/help/html/features/newkeystrokes.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/features/overview.html b/help/html/features/overview.html
index a002c92..550a81a 100644
--- a/help/html/features/overview.html
+++ b/help/html/features/overview.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/features/pdbseqfetcher.png b/help/html/features/pdbseqfetcher.png
index b243168..97a779a 100644
Binary files a/help/html/features/pdbseqfetcher.png and b/help/html/features/pdbseqfetcher.png differ
diff --git a/help/html/features/pdbsequencefetcher.html b/help/html/features/pdbsequencefetcher.html
index c26575e..b176c0a 100644
--- a/help/html/features/pdbsequencefetcher.html
+++ b/help/html/features/pdbsequencefetcher.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -40,8 +40,7 @@
     <strong>"File →Fetch Sequences"</strong>).
   </p>
   <img src="pdbseqfetcher.png" align="left"
-    alt="PDB sequence fetcher (introduced in Jalview 2.9)"
-  />
+    alt="PDB sequence fetcher (introduced in Jalview 2.9)" />
 
   <p>
     <strong>Searching the PDB Database</strong>
@@ -56,9 +55,9 @@
   </p>
   <p>
   <ul>
-    <li><strong>Searching a specific PDB field</strong><br />If you
-      want to find structures based on a specific PDB metadata field,
-      you can select it from the drop-down menu.</li>
+    <li><strong>Searching a specific PDB field</strong><br />If
+      you want to find structures based on a specific PDB metadata
+      field, you can select it from the drop-down menu.</li>
     <li><strong>Retrieving a unique chain for a PDB entry</strong><br>To
       retrieve a specific chain for a PDB entry, append the PDB ID with
       a colon followed by the chain code in the search box.<br /> e.g
diff --git a/help/html/features/pdbviewer.html b/help/html/features/pdbviewer.html
index 0d7a51b..a6405c6 100644
--- a/help/html/features/pdbviewer.html
+++ b/help/html/features/pdbviewer.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -40,10 +40,9 @@
       pop-up menu</a>. The internal PDB viewer is not able to show
     superpositions, so no other options are provided. Structures can
     only be viewed for sequences which have an <a
-      href="viewingpdbs.html"
-    >associated PDB structure</a>, and the PDB Viewer will only be
-    associated with the particular alignment view from which it was
-    opened.
+      href="viewingpdbs.html">associated PDB structure</a>, and the
+    PDB Viewer will only be associated with the particular alignment
+    view from which it was opened.
   </p>
   <p>
     <strong>Controls</strong>
@@ -123,7 +122,7 @@
         </strong><em> Colours each residue in the structure with the colour
             of its corresponding residue in the associated sequence as
             rendered in the associated alignment view, including any
-            Uniprot sequence features or region colourings.<br>
+            UniProt sequence features or region colourings.<br>
             Residues which only exist in the PDB structure are coloured
             white if they are insertions (relative to the associated
             sequence in the alignment) and grey if they are N or C
@@ -140,8 +139,8 @@
               Jalview colourschemes.<br>
           </em></strong> The remaining entries apply the colourschemes available from
           the standard and user defined <a
-          href="../colourSchemes/index.html"
-        >amino acid colours</a>.</em></li>
+          href="../colourSchemes/index.html">amino acid
+            colours</a>.</em></li>
       </ul></li>
     <li><strong>View<br>
     </strong><em> These options can be turned off to improve performance
diff --git a/help/html/features/preferences.html b/help/html/features/preferences.html
index 235ce3f..baf9538 100644
--- a/help/html/features/preferences.html
+++ b/help/html/features/preferences.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -63,8 +63,8 @@
     </li>
     <li>The <a href="../webServices/webServicesPrefs.html"><strong>"Web
           Service"</strong> Preferences</a> tab allows you to configure the <a
-      href="http://www.compbio.dundee.ac.uk/jabaws"
-    >JABAWS</a> servers that Jalview uses, and change the layout of the
+      href="http://www.compbio.dundee.ac.uk/jabaws">JABAWS</a>
+      servers that Jalview uses, and change the layout of the
       alignment's Web Services menu.
     </li>
   </ul>
@@ -78,9 +78,8 @@
   </p>
   <p>
     <em>Open Overview Window</em> - When this is selected, the <a
-      href="overview.html"
-    >alignment overview</a> panel is opened by default for a new alignment
-    window.
+      href="overview.html">alignment overview</a> panel is opened
+    by default for a new alignment window.
   </p>
   <p>
     <em>Show Annotations</em> - If this is selected the new window will
@@ -155,9 +154,9 @@
     <em>Open file</em> - If this is selected then the default alignment
     file will be opened when Jalview is started. You can change the
     default file by clicking on file name and either typing in the file
-    path or selecting it from the file chooser window.<br />
-    <em>Note: The default example alignment is updated periodically
-      to demonstrate new features in Jalview.</em>
+    path or selecting it from the file chooser window.<br /> <em>Note:
+      The default example alignment is updated periodically to
+      demonstrate new features in Jalview.</em>
   </p>
   <p>
     <a name="colours"><strong>"Colours"
@@ -172,9 +171,9 @@
   <p>
     <em>Annotation Shading Default</em> - set the default minimum and
     maximum colours used when <a
-      href="../colourSchemes/annotationColouring.html"
-    >Colour by Annotation...</a> is selected from the alignment window's
-    colours menu.
+      href="../colourSchemes/annotationColouring.html">Colour
+      by Annotation...</a> is selected from the alignment window's colours
+    menu.
   </p>
   <p>
     <a name="structure"><strong>"Structure"
@@ -216,8 +215,8 @@
     <em>URL Link From Sequence ID</em><br> These definitions are
     used to generate URLs from a sequence's ID or database cross
     references. Read more about <a
-      href="../webServices/urllinks.html#urllinks"
-    >configuring URL links here</a>.
+      href="../webServices/urllinks.html#urllinks">configuring
+      URL links here</a>.
   </p>
   <p>
     <em>Default Browser (Unix)</em><br> Its difficult in Java to
@@ -292,11 +291,10 @@
   </p>
   <p>
     When this option is enabled, Jalview embeds <a
-      href="bioJsonFormat.html"
-    >BioJSON</a> data within HTML files exported from Jalview at
-    generation time. This enables the exported HTML files to be
-    extracted and imported back into the Jalview desktop application at
-    a later time.
+      href="bioJsonFormat.html">BioJSON</a> data within HTML files
+    exported from Jalview at generation time. This enables the exported
+    HTML files to be extracted and imported back into the Jalview
+    desktop application at a later time.
   <p>
     <em>Use Modeller Output</em>
   </p>
diff --git a/help/html/features/search.html b/help/html/features/search.html
index 2c1ed0f..2317485 100644
--- a/help/html/features/search.html
+++ b/help/html/features/search.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -65,6 +65,19 @@ td {
     Settings" under the "View" menu to change the
     visibility and colour of the new sequence feature.</p>
   <p>
+  <p>
+    <strong>Selecting regions from Search Results</strong>
+  </p>
+  <p>
+    Press 'B' or use the <em>Select Highlighted Columns</em> option from
+    the alignment window's select menu to add columns containing
+    highlighted search results to the alignment window's column
+    selection. Alt-'B' will add all but the highlighted columns, and
+    Ctrl (or Cmd) -B will toggle the column selection for the
+    highlighted region.
+  </p>
+  <p>
+  
     <strong>A quick Regular Expression Guide</strong>
   </p>
   <p>A regular expression is not just a simple text query - although
@@ -73,7 +86,7 @@ td {
     the match. For example, a simple query like "ACDED" would
     match all occurences of that string, but "ACD+ED" matches
     both 'ACDDED' and 'ACDDDDDDDDED'. More usefully, the query
-    "[ILGVMA]{;5,}" would find stretches of small, hydrophobic
+    "[GVATC]{;5,}" would find stretches of small, hydrophobic
     amino acids of at least five residues in length.</p>
   <p>
     The table below describes some of the regular expression syntax:<br>
diff --git a/help/html/features/selectfetchdb.gif b/help/html/features/selectfetchdb.gif
index 6e1ce10..de0a7e6 100644
Binary files a/help/html/features/selectfetchdb.gif and b/help/html/features/selectfetchdb.gif differ
diff --git a/help/html/features/seqfeatures.html b/help/html/features/seqfeatures.html
index 99b008e..feb1265 100644
--- a/help/html/features/seqfeatures.html
+++ b/help/html/features/seqfeatures.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,7 +29,7 @@
   <p>
     Jalview can colour parts of a sequence based on the presence of
     sequence features - which may be retrieved from database records
-    (such as Uniprot), the result of <a href="search.html">sequence
+    (such as UniProt), the result of <a href="search.html">sequence
       motif searches</a> or simply read from a <a href="featuresFormat.html">sequence
       features file</a>. You can also <a href="creatinFeatures.html">create
       features</a> from the results of searches or the current selection,
@@ -42,11 +42,11 @@
   <p>
     By default, Jalview will assign a color to each feature based on its
     type. These colours can be changed from the <a
-      href="featuresettings.html"
-    >feature settings</a> and <a href="editingFeatures.html">amend
-      features</a> dialog boxes. Since Jalview 2.5, it is also possible to
-    define <a href="featureschemes.html">feature colourschemes</a> to
-    shade features based on their associated scores or text labels.
+      href="featuresettings.html">feature settings</a> and <a
+      href="editingFeatures.html">amend features</a> dialog boxes. Since
+    Jalview 2.5, it is also possible to define <a
+      href="featureschemes.html">feature colourschemes</a> to shade
+    features based on their associated scores or text labels.
   </p>
   <p>
     <strong>Sequence Feature Groups</strong>
@@ -54,10 +54,9 @@
   <p>
     Since Jalview 2.08, sequence features assigned to a sequence can be
     organised into groups, which may indicate that the features were all
-    retrieved from the same database (such as Uniprot features), or
+    retrieved from the same database (such as UniProt features), or
     generated by the same analysis process (as might be specified in a <a
-      href="featuresFormat.html"
-    >sequence features file</a>).
+      href="featuresFormat.html">sequence features file</a>).
   </p>
   <p>
     <strong>Sequence Feature Inheritance</strong>
@@ -85,13 +84,13 @@
   <p>
     Once sequence features have been loaded, their display can be fully
     controlled using the alignment window's <a
-      href="featuresettings.html"
-    >Sequence Feature Settings</a> dialog box. Feature colour schemes and
-    display parameters are unique to a particular alignment, so it is
-    possible to colour the same sequence features differently in
-    different alignment views.<br> Since Jalview 2.1, it is
-    possible to add <a href="dassettings.html">DAS features</a> to an
-    alignment via the DAS tabbed pane of the feature settings window.
+      href="featuresettings.html">Sequence Feature Settings</a>
+    dialog box. Feature colour schemes and display parameters are unique
+    to a particular alignment, so it is possible to colour the same
+    sequence features differently in different alignment views.<br>
+    Since Jalview 2.1, it is possible to add <a href="dassettings.html">DAS
+      features</a> to an alignment via the DAS tabbed pane of the feature
+    settings window.
   </p>
   <p>
     <strong>View→Sequence ID Tooltip→Show
@@ -105,8 +104,8 @@
     Precalculated Sequence Features may be added to an alignment from
     the command line, drag and drop, or from the "File→Load
     Features / Annotations" menu item. See the <a
-      href="featuresFormat.html"
-    >Features File Format</a> for more details.
+      href="featuresFormat.html">Features File Format</a> for more
+    details.
   </p>
 </body>
 </html>
diff --git a/help/html/features/seqfetch.html b/help/html/features/seqfetch.html
index 8870550..ec16e8e 100644
--- a/help/html/features/seqfetch.html
+++ b/help/html/features/seqfetch.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -32,62 +32,70 @@
     Institute, or, since Jalview 2.4, DAS servers capable of the <em>sequence</em>
     command (configured in <a href="dassettings.html">DAS settings</a>).
   </p>
-  <img src="seqfetcher.gif" align="center"
-    alt="The Jalview Sequence Fetcher Dialog Box"
-  >
-  <p>The Sequence Fetcher dialog box can be opened via the
-    "File" menu on the main desktop in order to retrieve
-    sequences as a new alignment, or opened via the "File"
-    menu of an existing alignment to import additional sequences. There
-    may be a short delay when the sequence fetcher is first opened,
-    whilst Jalview compiles the list of available sequence datasources
-    from the currently defined DAS server registry.</p>
+  <p>The Sequence Fetcher can be opened via the "File"
+    menu on the main desktop in order to retrieve sequences as a new
+    alignment, or opened via the "File" menu of an existing
+    alignment to import additional sequences. There may be a short delay
+    when the sequence fetcher is first opened, whilst Jalview compiles
+    the list of available sequence datasources from the currently
+    defined DAS server registry.</p>
   <p>
-    First, <strong>select the database you want to retrieve
-      sequences from</strong> by clicking the button labeled 'Select database
-    retrieval source'. If a database source is already selected, then
-    the button's label will change to show the currently selected
-    database.
+    Every time a new fetcher is opened, you will need to <strong>select
+      the database you want to retrieve sequences</strong> from the database
+    chooser.
   </p>
-  <img src="selectfetchdb.gif" align="left"
-    alt="Database selection dialog for fetching sequences (introduced in Jalview 2.8)"
-  >
-  <p>Since Jalview 2.8, the available databases are shown as a tree
-    in a popup dialog box. The databases are ordered alphabetically, and
-    if there are many sources for the same type of sequence identifier,
-    they will be grouped together in a sub-branch branch labeled with
-    the identifier.</p>
+  <img src="selectfetchdb.gif" align="left" width="480" height="204"
+    alt="Database selection dialog for fetching sequences (introduced in Jalview 2.8)">
   <p>
-    Once you have selected the sequence database using the popup dialog
-    box, <strong>enter one or more accession ids</strong> (as a
-    semi-colon separated list), or press the "Example" button
-    to paste the example accession for the currently selected database
-    into the retrieval box. Finally, press "OK" to initiate
-    the retrieval.
-  </p>
-  <p>
-    <strong>Fetching from The PDB with the EMBL-EBI PDBe Search
-      Interface</strong>
-  </p>
-  <p>
-    Since Jalview 2.9, selecting PDB as the sequence database will open
-    the <a href="pdbsequencefetcher.html">PDB Sequence Fetcher</a> for
-    discovering and retrieving structures.
+    The databases are shown as a tree, and ordered alphabetically;
+    tooltips are shown if you mouse over some sources, explaining what
+    the database will retrieve. You can select one by using the up/down
+    arrow keys and hitting return, or by double clicking with the mouse.
+    <br />
+    <em>If you have DAS sources enabled, then you may have several
+      sources for the same type of sequence identifier, and these will
+      be grouped together in a sub-branch branch labeled with the
+      identifier.</em>
   </p>
+  <p>Once you have selected a sequence database, its fetcher dialog
+    will open. Jalview provides two types of dialog:</p>
+  <ol>
+    <li><strong>The Free-text Search Interface</strong> <br />Free-text
+      search clients are provided for PDB (Since 2.9), and UniProt
+      (Since 2.10). They provide access to each database's own query
+      system, enabling you to retrieve data by accession, free text
+      description, or any other type of supported field. For full
+      details, see each client's help page:
+      <ul>
+        <li><a href="pdbsequencefetcher.html">PDB Sequence
+            Fetcher</a></li>
+        <li><a href="uniprotsequencefetcher.html">UniProt
+            Sequence Fetcher</a></li>
+      </ul></li>
+    <li><strong>Accession based sequence retrieval</strong> <br />
+
+      <img src="seqfetcher.gif" align="center"
+      alt="The Jalview Sequence Fetcher Dialog Box"><br /> To
+      retrieve sequences, simply <strong>enter one or more
+        accession ids</strong> (as a semi-colon separated list), or press the
+      "Example" button to paste the example accession for the
+      currently selected database into the retrieval box. Finally, press
+      "OK" to initiate the retrieval.</li>
+  </ol>
   <p>
     <strong>Only retrieving part of a sequence</strong>
   </p>
   <p>
-    DAS sources (indicated by a "<em>(DAS)</em>") allow a
-    range to be specified in addition to a sequence ID. To retrieve 50
-    residues starting at position 35 in UNIPROT sequence P73137 using
-    the UNIPROT DAS server, you would enter "'P73137:35,84'.<br />
-    <em>Full support for DAS range queries was introduced in
-      Jalview 2.8</em>
+    When using DAS sources (indicated by a "<em>(DAS)</em>"),
+    you can append a range in addition to a sequence ID. For example, to
+    retrieve 50 residues starting at position 35 in UNIPROT sequence
+    P73137 using the UNIPROT DAS server, you would enter
+    "'P73137:35,84'.<br /> <em>Full support for DAS range
+      queries was introduced in Jalview 2.8</em>
   </p>
 
   <p>If you use the WSDBFetch sequence fetcher services (EMBL,
-    Uniprot, PFAM, and RFAM) in work for publication, please cite:</p>
+    UniProt, PFAM, and RFAM) in work for publication, please cite:</p>
   <p>
     Pillai S., Silventoinen V., Kallio K., Senger M., Sobhany S., Tate
     J., Velankar S., Golovin A., Henrick K., Rice P., Stoehr P., Lopez
diff --git a/help/html/features/seqmappings.html b/help/html/features/seqmappings.html
index 1b078d0..22d760a 100644
--- a/help/html/features/seqmappings.html
+++ b/help/html/features/seqmappings.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,6 +26,7 @@
   <p>
     <strong>Mapping Between Different Sequences</strong>
   </p>
+  <!--  TODO: review and check if this page is really needed -->
   <p>A new feature in Jalview 2.3 is the ability to map between
     sequences in different domains, based on alignment, or by the use of
     explicit mappings provided by databases.</p>
@@ -40,7 +41,10 @@
     correspondence between DNA and protein sequences. This mapping can
     be imported directly from EMBL and EMBLCDS database records
     retrieved by the <a href="seqfetch.html">Sequence Fetcher</a>, and
-    allows sequence features to be mapped directly from Uniprot das
+    allows sequence features to be mapped directly from UniProt das
     sources to their coding region on EMBL sequence records.
+  </p>
+  <em><a href="siftsmapping.html">SIFTS Mapping</a> between PDB and
+    UniProt data was introduced in Jalview 2.10</em>
 </body>
 </html>
diff --git a/help/html/features/sifts_mapping_output.png b/help/html/features/sifts_mapping_output.png
new file mode 100644
index 0000000..ddc8a1f
Binary files /dev/null and b/help/html/features/sifts_mapping_output.png differ
diff --git a/help/html/features/siftsmapping.html b/help/html/features/siftsmapping.html
new file mode 100644
index 0000000..a7b6f1f
--- /dev/null
+++ b/help/html/features/siftsmapping.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<!--
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ -->
+<html>
+<head>
+<meta charset="UTF-8">
+<title>SIFTS Mapping from UniProt for PDB Structures</title>
+</head>
+<body>
+
+  <p>
+    <strong>SIFTS Mapping for UniProt sequences and PDB
+      Structures</strong><br /> SIFTS (Structure Integration with Function,
+    Taxonomy and Sequences) is a database of residue-level mappings
+    between UniProt protein sequences, and protein structures found in
+    the PDB. The database is updated for each PDB release, and is
+    provided by the <a href="https://www.ebi.ac.uk/pdbe/docs/sifts/">PDBe
+      at EMBL-EBI</a>.
+  </p>
+  <p>When Jalview imports PDB data for a protein sequence found in
+    UniProt, either via the 'View 3D Structure...' option, or the 'Fetch
+    DB Refs' web services menu, Jalview will also download its SIFTS
+    record and use that information to construct a mapping between the
+    sequence and downloaded structure.</p>
+  <p>If, for some reason, no SIFTS mapping data exists, then Jalview
+    will generate a mapping using the built-in Needleman and Wunsch
+    global alignment algorithm. This is how sequence-structure mappings
+    were created before version 2.10.</p>
+  <p>
+    <strong>Controlling and troubleshooting SIFTS mappings</strong> <br />
+    Configuration options controlling whether SIFTS mappings are used
+    can be found in the <strong>Tools → Preferences →
+      Structure tab</strong>, under 'Sequence ↔ Structure method'.<br /> <em>Note:</em>
+    Changing the configuration will only affect how new mappings are
+    created. In order to recompute mappings for structures already
+    loaded, please reload the sequence & structural data.
+  </p>
+
+  <p>
+    <strong>Multi-Chain Mappings</strong> <br />SIFTS gives Jalview the
+    ability to display multi-chain mappings between UniProt sequences
+    and PDB structure data. This is important when working with
+    multimeric proteins, when the biological assembly can contain
+    several structures for the same protein sequence. Multi-chain
+    mapping allows all residues in a structure to be located in the
+    alignment, and also, when shading the structure by sequence colours,
+    enables conservation patterns between oligomer interfaces to be
+    explored.
+  </p>
+  <p>To see this in action, Retrieve the UniProt sequence
+    FER1_MAIZE, and then view one of its structures: 3B2F. Mousing over
+    the sequence results to two positions being highlighted in the
+    structure, and colouring the alignment transfers the color to all
+    the mapped chains in the structure.</p>
+
+  <p>
+    <Strong>Viewing Mapping Output</Strong> <br /> The mapping provided
+    by the SIFTS record is accessible via <strong>File →
+      View mapping</strong> menu of the structure viewers. The screenshot below
+    shows the mapping created between UniProt sequence FER1_MAIZE and
+    proteins in PDB 3B2F, which reports mappings for two chains. The
+    mapping method is also reported (highlighted with red border).
+  <p>
+
+     <img src="sifts_mapping_output.png" align="left"
+      alt="SIFTS mapping output" /> <br />
+  <p>
+    <em>SIFTS Mapping integration was added in Jalview 2.10</em>
+  </p>
+
+</body>
+</html>
diff --git a/help/html/features/splitView.html b/help/html/features/splitView.html
index f91b8e5..91636a8 100644
--- a/help/html/features/splitView.html
+++ b/help/html/features/splitView.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -47,21 +47,21 @@
   <ul>
     <li>Mouseover or scrolling of either alignment is followed by
       the other (unless you turn off <strong><a
-        href="../menus/alwview.html"
-      >"View→Automatic Scrolling"</a></strong>)
+        href="../menus/alwview.html">"View→Automatic
+          Scrolling"</a></strong>)
     </li>
     <li>On selecting rows, columns or regions in one alignment, the
       corresponding selection is made in the other</li>
     <li>Sequence ordering in one alignment (using the cursor, or <strong><a
-        href="../calculate/sorting.html"
-      >"Calculate→Sort")</a></strong> is also applied to the other
+        href="../calculations/sorting.html">"Calculate→Sort")</a></strong>
+      is also applied to the other
     </li>
     <li>Editing (gap insertion / deletion) in the protein alignment
       is reflected in the cDNA (but not vice versa)</li>
     <li>Any trees imported or created with <strong><a
-        href="../calculations/tree.html"
-      >"Calculate Tree"</a></strong> on one of the views allow both cDNA and
-      Protein views to be grouped, coloured or sorted.
+        href="../calculations/tree.html">"Calculate Tree"</a></strong> on
+      one of the views allow both cDNA and Protein views to be grouped,
+      coloured or sorted.
     </li>
     <li>To allow for the different widths in cDNA and Protein
       alignments, the <strong><a href="../menus/alwformat.html">"Format→Font"</a></strong>
@@ -75,16 +75,15 @@
       panels.</li>
     <li>Panel heights are adjusted dragging the divider between
       them using the mouse</li>
-    <li><a href="menus/alwview.html"><strong>"View→New
+    <li><a href="../menus/alwview.html"><strong>"View→New
           View / Expand Views / Gather Views"</strong></a> behave as for a normal
       alignment window, but always create new views as Split Frames</li>
   </ul>
   <p>
     An alignment annotation row on the protein alignment shows the <strong><a
-      href="../calculations/consensus.html"
-    >cDNA consensus</a></strong> for each peptide column.<br /> This consensus may
-    reveal variation in nucleotides coding for conserved protein
-    residues.
+      href="../calculations/consensus.html">cDNA consensus</a></strong> for
+    each peptide column.<br /> This consensus may reveal variation in
+    nucleotides coding for conserved protein residues.
   </p>
 
   <a name="opensplit" />
diff --git a/help/html/features/structurechooser.html b/help/html/features/structurechooser.html
index 30825c5..5a2de1e 100644
--- a/help/html/features/structurechooser.html
+++ b/help/html/features/structurechooser.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,57 +28,65 @@
     <strong>Structure Chooser</strong>
   </p>
 
-  The Structure Chooser interface provides a smart technique for
-  selecting PDB structures to view in Jalview by querying readily
-  available meta-data of structures. The Interface can be invoked by
-  selecting the
-  <strong>"3D Structure Data.."</strong> option from a sequence's
-  <a href="../menus/popupMenu.html">pop-up menu</a>. Some of the main
-  features it provides are listed below:
+  <p>
+    The Structure Chooser interface allows you to interactively select
+    which PDB structures to view for the currently selected set of
+    sequences. It is opened by selecting the <strong>"3D
+      Structure Data.."</strong> option from the Sequence ID panel's <a
+      href="../menus/popupMenu.html">pop-up menu</a>. The dialog
+    provides:
+  </p>
   <ul>
     <li>Automatic discovery, retrieval and association of PDB
-      entries for an alignment's sequences</li>
-    <li>Visualisation of discovered structures' meta-data</li>
-    <li>Ability to configure the meta-data entries to visualise</li>
-    <li>Auto-selection of the best structure via filtering by the
-      available metric parameters in the meta-data (i.e. resolution,
-      quality etc).</li>
-    <li>Selection of multiple structures in a single operation</li>
-  </ul>
-  Additionally, the Structure Chooser retains the following contemporary
-  features of Jalview:
-  <ul>
-    <li>Manual association of PDB entries via entering the PDB Id
-      or From File</li>
-    <li>Ability to view cached PDB entries</li>
+      entries for sequences</li>
+    <li>Exploration of meta-data for available 3D structures</li>
+    <li>Automatic selection of the 'best structure' to display for
+      each sequence</li>
+    <li>Manual association of PDB entries by entering a PDB Id</li>
+    <li>Association of structure data from a local file (in mmCIF
+      or PDB format)</li>
   </ul>
-
-
-  <strong>Associating PDB files with Sequences</strong>
-  <br> Discovery/Association of PDB entries to a sequence now
-  happens automatically during the initialisation of the Structure
-  Chooser Interface. Jalview uses the sequence's ID to query the PDB
-  Rest API provided by the EBI to discover PDB Ids associated with the
-  sequence.
-
-  <br>
-  <br>
-  <strong>Configuring displayed meta-data for Structures</strong>
-  <br> To configure the visible meta-data displayed for the
-  discovered structures, click the 'Configure Displayed Columns' tab,
-  then tick the options which you intend to make visible.
-
-  <br>
-  <br>
-  <strong>Auto-selection of best Structures</strong>
-  <br> Jalview can automatically filter and select the best
-  structures using various metric categories avaialble from the
-  meta-data of the structures. To perform this simply select any of the
-  following options from the drop-down menu in the Structure Chooser
-  interface: Best Uniprot coverage, Higest Resolution, Best Quality,
-  Highest Protein Chain etc. When the 'Invert' option is selected,
-  Jalview returns an inverse result for the current selected option in
-  the drop-down menu.
+  <p>
+    <strong>Selecting and Viewing Structures</strong>
+  </p>
+  <p>
+    Once one or more structures have been selected, pressing the <strong>View</strong>
+    button will import them into <a
+      href="viewingpdbs.html#afterviewbutton">a new or existing
+      structure view</a>.
+  </p>
+  <p>
+    <strong>Automated discovery of structure data</strong>
+  </p>
+  <p>
+    After selecting "3D Structure Data ..", Jalview queries the PDB via
+    the PDBe SOLR Rest API provided by EMBL-EBI to discover PDB IDs
+    associated with the sequence. It does this based on the sequence's
+    ID string, and any other associated database IDs. <br />
+    <br />
+  <p>
+    <strong><a name="cachedstructview">Viewing existing
+        structures for your sequences</a></strong>
+  </p>
+  <p>
+    If you have already loaded 3D structure data for the selected
+    sequences, the structure chooser will first open with the <strong>Cached
+      Structures View</strong>. This view shows associations between each
+    sequence, and chains for 3D structure files already in memory. If
+    you want to download additional structures, select one of the other
+    options from the drop down menu.
+  </p>
+  <p>
+    <strong>Selection of the best structure for each sequence</strong>
+  </p>
+  <p>Jalview can automatically select the best structures according
+    to meta-data provided by the PDB. For alignments with no existing
+    structure data, the 'Best Quality' structure for each sequence will
+    by default be selected, but clicking on the drop down menu allows
+    other criteria to be chosen, including Resolution (only defined for
+    X-Ray structures), Highest Protein Chain etc. When 'Invert' is
+    selected, structures are selected in reverse order for the current
+    criteria (e.g. worst quality rather than best).</p>
   <p>
 
     <img src="schooser_main.png" style="width: 464px; height: 369px;">
@@ -89,34 +97,33 @@
 	<p><img src="schooser_cached.png"> -->
     <br>The screenshot above shows the Structure Chooser interface
     along with the meta-data of auto-discovered structures for the
-    sample alignment. Note however that if no structures were
-    auto-discovered, a different interface for manual association will
-    be invoked as seen in the screenshot below.
+    sample alignment. If no structures were
+    auto-discovered, options for manually associating PDB records will be shown (see below).
   <p>
-    <img src="schooser_enter-id.png"
-      style="width: 464px; height: 369px;"
-    >
+    <strong>Exploration of meta-data for available structures</strong>
+  </p>
+  <p>Information on each structure available is displayed in columns
+    in the dialog box. By default, only the title, resolution and PDB
+    identifier are shown, but many more are provided by the PDBe. To
+    configure which ones are displayed, select the 'Configure Displayed
+    Columns' tab and tick the columns which you want to see.</p>
   <p>
+    <img src="schooser_enter-id.png"
+      style="width: 464px; height: 369px;">
+      <br/>
     <strong>Manual selection/association of PDB files with
       Sequences</strong>
   </p>
-  <p>To manually associate PDB files with a sequence, select any of
-    the follwing options listed below from the drop-down menu in the
-    interface:
+  <p>To manually associate PDB files with a sequence, select 'From
+    File', or 'Enter PDB Id' from the drop-down menu:
   <ul>
-    <li><strong>From File</strong> - You can load a PDB file from
-      the local machine or network and associate it with the selected
-      sequence. PDB files associated in this way will also be saved in
-      the <a href="jalarchive.html">Jalview Archive file</a>.<br></li>
-    <li><strong>Enter PDB Id</strong> - Jalview will use the PDB
-      Rest API, provided by the EBI, to fetch the PDB file with the
-      entered Id.<br></li>
-    <li><strong>Cached PDB Entries</strong> - You can view PDB
-      structures which have previously been downloaded/viewed using this
-      option. Jalview caches previously downloaded PDB entries in the
-      computer memory. However, if the project is saved before exiting
-      Jalview, Jalview will serialize the cached entries to the file
-      system.</li>
+    <li><strong>From File</strong> - allows you to load a PDB file
+      from the local machine or network and associate it with the
+      selected sequence. PDB files associated in this way will also be
+      saved in the <a href="jalarchive.html">Jalview Archive file</a>.<br></li>
+    <li><strong>Enter PDB Id</strong> - allows you specify a PDB ID
+      for your sequence. The PDB Rest API, provided by EMBL-EBI, is used
+      to validate and fetch structure data.<br></li>
   </ul>
 
   <p>
diff --git a/help/html/features/uniprotqueryfields.html b/help/html/features/uniprotqueryfields.html
new file mode 100644
index 0000000..e8e75cd
--- /dev/null
+++ b/help/html/features/uniprotqueryfields.html
@@ -0,0 +1,391 @@
+<html>
+<!--
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ -->
+<head>
+<title>UniProtKB query fields</title>
+</head>
+
+<body>
+  <p>
+    <strong>UniProtKB query fields</strong>
+  </p>
+  <p>
+    Supported query fields for searching specific data in UniProtKB (see
+    also <a href="uniprotsequencefetcher.html#text-search">query
+      syntax</a>).
+  </p>
+
+  <table border="1" width="95%">
+    <tr>
+      <th>Field</th>
+      <th>Example</th>
+      <th>Description</th>
+    </tr>
+    <tr>
+      <td>accession</td>
+      <td><code>accession:P62988</code></td>
+      <td>Lists all entries with the primary or secondary accession
+        number P62988.</td>
+    </tr>
+    <tr>
+      <td>active</td>
+      <td><code>active:no </code></td>
+      <td>Lists all obsolete entries.</td>
+    </tr>
+    <tr>
+      <td>annotation</td>
+      <td><code>
+          annotation:(type:non-positional) <br />
+          annotation:(type:positional) <br /> annotation:(type:mod_res
+          "Pyrrolidone carboxylic acid" evidence:experimental)
+        </code></td>
+      <td>Lists all entries with:
+        <ul>
+          <li>any general annotation (comments [CC])</li>
+          <li>any sequence annotation (features [FT])</li>
+          <li>at least one amino acid modified with a Pyrrolidone
+            carboxylic acid group</li>
+        </ul>
+      </td>
+    </tr>
+    <tr>
+      <td>author</td>
+      <td><code> author:ashburner </code></td>
+      <td>Lists all entries with at least one reference co-authored
+        by Michael Ashburner.</td>
+    </tr>
+    <tr>
+      <td>cdantigen</td>
+      <td><code> cdantigen:CD233 </code></td>
+      <td>Lists all entries whose cluster of differentiation number
+        is CD233.</td>
+    </tr>
+    <tr>
+      <td>citation</td>
+      <td><code>
+          citation:("intracellular structural proteins") <br />
+          citation:(author:ashburner journal:nature) citation:9169874
+        </code></td>
+      <td>Lists all entries with a literature citation:
+        <ul>
+          <li>containing the phrase "intracellular structural
+            proteins" in either title or abstract</li>
+          <li>co-authored by Michael Ashburner and published in
+            Nature</li>
+          <li>with the PubMed identifier 9169874</li>
+        </ul>
+      </td>
+    </tr>
+    <tr>
+      <td>cluster</td>
+      <td><code> cluster:UniRef90_A5YMT3 </code></td>
+      <td>Lists all entries in the UniRef 90% identity cluster
+        whose representative sequence is UniProtKB entry A5YMT3.</td>
+    </tr>
+    <tr>
+      <td>count</td>
+      <td><code>
+          annotation:(type:transmem count:5)<br />
+          annotation:(type:transmem count:[5 TO *])<br />
+          annotation:(type:cofactor count:[3 TO *])
+        </code></td>
+      <td>Lists all entries with:
+        <ul>
+          <li>exactly 5 transmembrane regions</li>
+          <li>5 or more transmembrane regions</li>
+          <li>3 or more Cofactor comments</li>
+        </ul>
+      </td>
+    </tr>
+    <tr>
+      <td>created</td>
+      <td><code>
+          created:[20121001 TO *]<br /> reviewed:yes AND
+          created:[current TO *]
+        </code></td>
+      <td>Lists all entries created since October 1st 2012.<br />
+        Lists all new UniProtKB/Swiss-Prot entries in the last release.
+      </td>
+    </tr>
+    <tr>
+      <td>database</td>
+      <td><code>
+          database:(type:pfam) <br /> database:(type:pdb 1aut)
+        </code></td>
+      <td>Lists all entries with:
+        <ul>
+          <li>a cross-reference to the Pfam database</li>
+          <li>a cross-reference to the PDB database entry 1aut</li>
+        </ul>
+
+      </td>
+    </tr>
+    <tr>
+      <td>domain</td>
+      <td><code> domain:VWFA </code></td>
+      <td>Lists all entries with a Von Willebrand factor type A
+        domain described in the 'Family and Domains' section.</td>
+    </tr>
+    <tr>
+      <td>ec</td>
+      <td><code> ec:3.2.1.23 </code></td>
+      <td>Lists all beta-galactosidases.</td>
+    </tr>
+    <tr>
+      <td>evidence</td>
+      <td><code>
+          annotation:(type:signal evidence:ECO_0000269)<br />
+          (type:mod_res phosphoserine evidence:ECO_0000269)<br />
+          annotation:(type:function AND evidence:ECO_0000255)
+        </code></td>
+      <td>Lists all entries with:
+        <ul>
+          <li>a signal sequence whose positions have been
+            experimentally proven</li>
+          <li>experimentally proven phosphoserine sites</li>
+          <li>a function manually asserted according to rules</li>
+        </ul>
+      </td>
+    </tr>
+    <tr>
+      <td>family</td>
+      <td><code> family:serpin </code></td>
+      <td>Lists all entries belonging to the Serpin family of
+        proteins.</td>
+    </tr>
+    <tr>
+      <td>fragment</td>
+      <td><code> fragment:yes </code></td>
+      <td>Lists all entries with an incomplete sequence.</td>
+    </tr>
+
+    <tr>
+      <td>gene</td>
+      <td><code> gene:HSPC233 </code></td>
+      <td>Lists all entries for proteins encoded by gene HSPC233.</td>
+    </tr>
+    <tr>
+      <td>go</td>
+      <td><code>
+          go:cytoskeleton <br /> go:0015629
+        </code></td>
+      <td>Lists all entries associated with:
+        <ul>
+          <li>a GO term containing the word "cytoskeleton"</li>
+          <li>the GO term Actin cytoskeleton and any subclasses</li>
+        </ul>
+      </td>
+    </tr>
+    <tr>
+      <td>host</td>
+      <td><code>
+          host:mouse <br /> host:10090 <br /> host:40674
+        </code></td>
+      <td>Lists all entries for viruses infecting:
+        <ul>
+          <li>organisms with a name containing the word "mouse"</li>
+          <li>Mus musculus (Mouse)</li>
+          <li>all mammals (all taxa classified under the taxonomy
+            node for Mammalia)</li>
+        </ul>
+      </td>
+    </tr>
+    <tr>
+      <td>id</td>
+      <td><code>id:P00750</code></td>
+      <td>Returns the entry with the primary accession number
+        P00750.</td>
+    </tr>
+    <tr>
+      <td>inn</td>
+      <td><code> inn:Anakinra </code></td>
+      <td>Lists all entries whose "International Nonproprietary
+        Name" is Anakinra.</td>
+    </tr>
+    <tr>
+      <td>interactor</td>
+      <td><code> interactor:P00520 </code></td>
+      <td>Lists all entries describing interactions with the
+        protein described by entry P00520.</td>
+    </tr>
+    <tr>
+      <td>keyword</td>
+      <td><code> keyword:toxin </code></td>
+      <td>Lists all entries associated with the keyword Toxin.</td>
+    </tr>
+    <tr>
+      <td>length</td>
+      <td><code> length:[500 TO 700] </code></td>
+      <td>Lists all entries describing sequences of length between
+        500 and 700 residues.</td>
+    </tr>
+    <tr>
+      <td>lineage</td>
+      <td />
+      <td>This field is a synonym for the field <code>taxonomy</code>.
+      </td>
+    </tr>
+    <tr>
+      <td>mass</td>
+      <td><code> mass:[500000 TO *] </code></td>
+      <td>Lists all entries describing sequences with a mass of at
+        least 500,000 Da.</td>
+    </tr>
+    <tr>
+      <td>method</td>
+      <td><code>
+          method:maldi <br /> method:xray
+        </code></td>
+      <td>Lists all entries for proteins identified by:
+        matrix-assisted laser desorption/ionization (MALDI),
+        crystallography (X-Ray). The <code>method</code> field searches
+        names of physico-chemical identification methods in the
+        'Biophysicochemical properties' subsection of the 'Function'
+        section, the 'Publications' and 'Cross-references' sections.
+      </td>
+    </tr>
+    <tr>
+      <td>mnemonic</td>
+      <td><code> mnemonic:ATP6_HUMAN </code></td>
+      <td>Lists all entries with entry name (ID) ATP6_HUMAN.
+        Searches also obsolete entry names.</td>
+    </tr>
+    <tr>
+      <td>modified</td>
+      <td><code>
+          modified:[20120101 TO 20120301]<br /> reviewed:yes AND
+          modified:[current TO *]
+        </code></td>
+      <td>Lists all entries that were last modified between January
+        and March 2012.<br /> Lists all UniProtKB/Swiss-Prot entries
+        modified in the last release.
+      </td>
+    </tr>
+    <tr>
+      <td>name</td>
+      <td><code> name:"prion protein" </code></td>
+      <td>Lists all entries for prion proteins.</td>
+    </tr>
+    <tr>
+      <td>organelle</td>
+      <td><code> organelle:Mitochondrion </code></td>
+      <td>Lists all entries for proteins encoded by a gene of the
+        mitochondrial chromosome.</td>
+    </tr>
+    <tr>
+      <td>organism</td>
+      <td><code>
+          organism:"Ovis aries" <br /> organism:9940 <br />
+          organism:sheep <br />
+        </code></td>
+      <td>Lists all entries for proteins expressed in sheep (first
+        2 examples) and organisms whose name contains the term "sheep".
+      </td>
+    </tr>
+
+    <tr>
+      <td>plasmid</td>
+      <td><code> plasmid:ColE1 </code></td>
+      <td>Lists all entries for proteins encoded by a gene of
+        plasmid ColE1.</td>
+    </tr>
+    <tr>
+      <td>proteome</td>
+      <td><code> proteome:UP000005640 </code></td>
+      <td>Lists all entries from the human proteome.</td>
+    </tr>
+    <tr>
+      <td>proteomecomponent</td>
+      <td><code> proteomecomponent:"chromosome 1" and
+          organism:9606 </code></td>
+      <td>Lists all entries from the human chromosome 1.</td>
+    </tr>
+    <tr>
+      <td>replaces</td>
+      <td><code> replaces:P02023 </code></td>
+      <td>Lists all entries that were created from a merge with
+        entry P02023.</td>
+    </tr>
+    <tr>
+      <td>reviewed</td>
+      <td><code> reviewed:yes </code></td>
+      <td>Lists all UniProtKB/Swiss-Prot entries.</td>
+    </tr>
+    <tr>
+      <td>scope</td>
+      <td><code> scope:mutagenesis </code></td>
+      <td>Lists all entries containing a reference that was used to
+        gather information about mutagenesis.</td>
+    </tr>
+    <tr>
+      <td>sequence</td>
+      <td><code> sequence:P05067-9 </code></td>
+      <td>Lists all entries containing a link to isoform 9 of the
+        sequence described in entry P05067. Allows searching by specific
+        sequence identifier.</td>
+    </tr>
+    <tr>
+      <td>sequence_modified</td>
+      <td><code>
+          sequence_modified:[20120101 TO 20120301]<br /> reviewed:yes
+          AND sequence_modified:[current TO *]
+        </code></td>
+      <td>Lists all entries whose sequences were last modified
+        between January and March 2012.<br /> Lists all
+        UniProtKB/Swiss-Prot entries whose sequences were modified in
+        the last release.
+      </td>
+    </tr>
+    <tr>
+      <td>source</td>
+      <td><code> source:intact </code></td>
+      <td>Lists all entries containing a GO term whose annotation
+        source is the IntAct database.</td>
+    </tr>
+    <tr>
+      <td>strain</td>
+      <td><code> strain:wistar </code></td>
+      <td>Lists all entries containing a reference relevant to
+        strain wistar.</td>
+    </tr>
+    <tr>
+      <td>taxonomy</td>
+      <td><code> taxonomy:40674 </code></td>
+      <td>Lists all entries for proteins expressed in Mammals. This
+        field is used to retrieve entries for all organisms classified
+        below a given taxonomic node taxonomy classification).</td>
+    </tr>
+    <tr>
+      <td>tissue</td>
+      <td><code> tissue:liver </code></td>
+      <td>Lists all entries containing a reference describing the
+        protein sequence obtained from a clone isolated from liver.</td>
+    </tr>
+    <tr>
+      <td>web</td>
+      <td><code> web:wikipedia </code></td>
+      <td>Lists all entries for proteins that are described in
+        Wikipedia.</td>
+    </tr>
+  </table>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/help/html/features/uniprotseqfetcher.png b/help/html/features/uniprotseqfetcher.png
new file mode 100644
index 0000000..a592e8e
Binary files /dev/null and b/help/html/features/uniprotseqfetcher.png differ
diff --git a/help/html/features/uniprotsequencefetcher.html b/help/html/features/uniprotsequencefetcher.html
new file mode 100644
index 0000000..fd3bacc
--- /dev/null
+++ b/help/html/features/uniprotsequencefetcher.html
@@ -0,0 +1,169 @@
+<html>
+<!--
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ -->
+<head>
+<title>The UniProt Free Text Search Interface</title>
+</head>
+<body>
+
+  <strong>The UniProt Free Text Search Interface</strong>
+  <br /> Since version 2.10 (October 2016), the Jalview Desktop
+  provides a search interface for interactive discovery and retrieval of
+  sequence data from UniProt. This dialog enables UniProt sequence
+  metadata to be searched with free text and structured queries, which
+  allows sequences to be located via gene name, keywords, or even
+  <em>via</em> manual cross-referencing from UniProt or other
+  bioinformatics websites.
+  <p>
+    To open the UniProt Sequence Fetcher, select UniProt as the database
+    from any <a href="seqfetch.html">Sequence Fetcher</a> dialog (opened
+    <em>via</em> <strong>"File →Fetch
+      Sequences"</strong>).
+  </p>
+  <p>
+    <img src="uniprotseqfetcher.png" align="left"
+      alt="UniProt sequence fetcher (introduced in Jalview 2.10)" />
+  </p>
+
+  <p>
+    <strong>Searching the UniProt Database</strong>
+  </p>
+  <p>
+    To search UniProt, simply begin typing in the text box. After a
+    short delay (about 1.5 seconds), results will be shown in the table
+    below. You can sort results by clicking on the displayed columns,
+    and select entries with the mouse or keyboard. Once you have
+    selected one or more entries, hit the <strong>OK</strong> button to
+    retrieve the sequences.
+  </p>
+  <ul>
+    <li><strong>Searching a specific UniProt field </strong> To
+      find sequences with particular UniProt metadata, you can select a
+      field to search from the drop-down menu.</li>
+
+
+    <li><strong>Bulk UniProt record retrieval</strong><br> To
+      retrieve several uniprot accessions at once, first select <strong>UniProt
+        ID</strong> from the dropdown menu, then paste in the accession IDs as a
+      semi-colon separated list. (e.g. fila_human; mnt_human;
+      mnt_mouse).<br />Hitting Return or OK will automatically fetch
+      those IDs, like the default Sequence Fetcher interface.</li>
+
+    <li><strong><a name="text-search">Complex queries
+          with the UniProt query Syntax</a></strong> The text box also allows complex
+      queries to be entered. The table below provides a brief overview
+      of the supported syntax (see <a href="uniprotqueryfields.html">query
+        fields for UniProtKB</a>):
+      <table border="1" width="95%">
+        <tr>
+          <td><code>human antigen</code></td>
+          <td rowspan="3">All entries containing both terms.</td>
+        </tr>
+        <tr>
+          <td><code>human AND antigen</code></td>
+        </tr>
+        <tr>
+          <td><code>human && antigen</code></td>
+        </tr>
+        <tr>
+          <td><code>"human antigen"</code></td>
+          <td>All entries containing both terms in the exact order.</td>
+        </tr>
+        <tr>
+          <td><code>human -antigen</code></td>
+          <td rowspan="3">All entries containing the term <code>human</code>
+            but not <code>antigen</code>.
+          </td>
+        </tr>
+        <tr>
+          <td><code>human NOT antigen</code></td>
+        </tr>
+        <tr>
+          <td><code>human ! antigen</code></td>
+        </tr>
+        <tr>
+          <td><code>human OR mouse</code></td>
+          <td rowspan="2">All entries containing either term.</td>
+        </tr>
+        <tr>
+          <td><code>human || mouse</code></td>
+        </tr>
+        <tr>
+          <td><code>antigen AND (human OR mouse)</code></td>
+          <td>Using parentheses to override boolean precedence
+            rules.</td>
+        </tr>
+        <tr>
+          <td><code>anti*</code></td>
+          <td>All entries containing terms starting with <code>anti</code>.
+            Asterisks can also be used at the beginning and within
+            terms. <strong>Note:</strong> Terms starting with an
+            asterisk or a single letter followed by an asterisk can slow
+            down queries considerably.
+          </td>
+        </tr>
+        <tr>
+          <td><code> author:Tiger*</code></td>
+          <td>Citations that have an author whose name starts with
+            <code>Tiger</code>. To search in a specific field of a
+            dataset, you must prefix your search term with the field
+            name and a colon. To discover what fields can be queried
+            explicitly, observe the query hints that are shown after
+            submitting a query or use the query builder (see below).
+          </td>
+        </tr>
+        <tr>
+          <td><code>length:[100 TO *]</code></td>
+          <td>All entries with a sequence of at least 100 amino
+            acids.</td>
+        </tr>
+        <tr>
+          <td><code>citation:(author:Arai author:Chung)</code></td>
+          <td>All entries with a publication that was coauthored by
+            two specific authors.</td>
+        </tr>
+      </table></li>
+  </ul>
+  <p>
+    <strong>Result pagination</strong>
+  </p>
+  The query results returned from the UniProt server are paginated for
+  performance optimisation. The button labelled
+  <strong>' << '</strong> and
+  <strong>' >> '</strong> can be used to navigate to the
+  next or previous result page respectively. The page range is shown on
+  the title bar of the Free Text Search interface. Jalview's pagination
+  implementation supports multiple selection of entries across multiple
+  pages.
+
+
+  <p>
+    <strong>Customising The UniProt Sequence Fetcher</strong>
+  </p>
+  <p>To change the displayed meta-data in the search result, click
+    the 'Customise Displayed Options' tab, and select the fields you'd
+    like to be displayed or removed.</p>
+  <p>
+    <em>The UniProt Free Test Search Interface was introduced in
+      Jalview 2.10.0</em>
+  </p>
+</body>
+</html>
\ No newline at end of file
diff --git a/help/html/features/varna.html b/help/html/features/varna.html
index 0ce3139..e446991 100644
--- a/help/html/features/varna.html
+++ b/help/html/features/varna.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -27,9 +27,9 @@
     <strong>The VARNA RNA Viewer</strong>
   </p>
   <p>
-    <a href="http://varna.lri.fr/index.html">VARNA</a> was integrated
-    into Jalview 2.8 to allow interactive viewing of RNA secondary
-    structure annotation. It is opened by selecting the <strong>"Structure→View
+    <a href="http://varna.lri.fr">VARNA</a> was integrated into Jalview
+    2.8 to allow interactive viewing of RNA secondary structure
+    annotation. It is opened by selecting the <strong>"Structure→View
       Structure:"</strong> option in the <a href="../menus/popupMenu.html">sequence
       id pop-up menu</a> (if you can't see this, then no RNA structure is
     associated with your sequence or alignment). In the pop-up menu all
@@ -86,8 +86,8 @@
   <p>
     VARNA is a very powerful RNA viewer on its own. Only the essentials
     have been described here - the interested reader is referred to <a
-      href="http://varna.lri.fr/index.php?page=manual&css=varna"
-    >VARNA's own comprehensive online documentation</a>.
+      href="http://varna.lri.fr/index.php?page=manual&css=varna">VARNA's
+      own comprehensive online documentation</a>.
   </p>
 </body>
 </html>
diff --git a/help/html/features/viewingpdbs.html b/help/html/features/viewingpdbs.html
index f993204..3844f35 100644
--- a/help/html/features/viewingpdbs.html
+++ b/help/html/features/viewingpdbs.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -24,10 +24,10 @@
 </head>
 <body>
   <p>
-    <strong>Viewing PDB Structures</strong>
+    <strong>Discovering and Viewing PDB Structures</strong>
   </p>
-  Jalview can be used to view protein structures by following the steps
-  below:
+  Jalview can be used to explore the 3D structures of sequences in an
+  alignment by following the steps below:
   <ol>
     <li>Select the <strong>"3D Structure Data..."</strong> option
       from a sequence's <a href="../menus/popupMenu.html">pop-up
@@ -40,30 +40,43 @@
           pane.
         </li>
         <li>However, if no structure was found, the <a
-          href="structurechooser.html"
-        >Structure Chooser</a> interface will present options for manual
-          association of PDB structures.
+          href="structurechooser.html">Structure Chooser</a> interface
+          will present options for manual association of PDB structures.
         </li>
       </ul>
     </li>
-    <li><strong>Selecting Structures</strong><br /> If structures
-      have been discovered, then some will already be selected according
-      to predefined selection criteria, such as structures with the
-      highest resolution. Use the drop down menu to select structures
-      according to different criteria, or, alternatively, choose
-      structures manually by selecting with the keyboard and mouse.
+    <li><strong>Selecting Structures</strong><br />You can select
+      the structures that you want to open and view by selecting them
+      with the mouse and keyboard.<br />By default, if structures were
+      discovered, then some will already be selected according to the
+      criteria shown in the drop-down menu. The default criteria is
+      'highest resolution', simply choose another to pick structures in
+      a different way.<br />
       <ul>
-        <li><strong>Viewing Cached Structures</strong><br />If you
-          have previously downloaded structures for your sequences, they
-          can be viewed by selecting the <strong>Cached PDB
-            Entries</strong> option from the drop down menu at the top of the
-          dialog box.</li>
+        <li><strong>Viewing Cached Structures</strong><br />If
+          previously downloaded structures are available for your
+          sequences, the structure chooser will automatically offer them
+          via the <strong>Cached PDB Entries</strong> view. If you wish
+          to download new structures, select one of the PDBe selection
+          criteria from the drop-down menu.</li>
+      </ul></li>
+    <li><strong>To view selected structures, click the <strong>"View"</strong>
+        button.
+    </strong><br />
+      <ul>
+        <li>Additional structure data will be downloaded with the
+          EMBL-EBI's dbfetch service</li>
+        <li><a href="siftsmapping.html">SIFTS</a> records will also
+          be downloaded for mapping UniProt protein sequence data to PDB
+          coordinates.</li>
+        <li>A new structure viewer will open, or you will be
+          prompted to add structures to existing viewers (see <a
+          href="#afterviewbutton">below</a> for details).
+        </li>
       </ul></li>
-    <li>To view selected structures, click the <strong>"View"</strong>
-      button.
-    </li>
   </ol>
-
+  <p>
+  <strong>Structure Viewers in the Jalview Desktop</strong><br/>
   The
   <a href="jmol.html">Jmol viewer</a> has been included since Jalview
   2.3. Jalview 2.8.2 included support for
@@ -77,10 +90,19 @@
     the <a href="xsspannotation.html">Annotation from Structure</a> page
     for more information.
   </p>
-
   <p>
-    If a <strong>single</strong> PDB structure is selected, one of the
-    following will happen:
+    <strong><a name="afterviewbutton">After pressing the
+        'View' button in the Structure Chooser</a></strong><br /> The behaviour of
+    the 'View' button depends on the number of structures selected, and
+    whether structure views already exist for the selected structures or
+    aligned sequences.
+  </p>
+  <p>
+    If multiple structures are selected, then Jalview will always create
+    a new structure view. The selected structures will be imported into
+    this view, and superposed with the matched positions from the
+    aligned sequences.<br /> If a <strong>single</strong> PDB structure
+    is selected, one of the following will happen:
   </p>
 
   <ul>
@@ -89,10 +111,10 @@
 
     <li>If another structure is already shown for the current
       alignment, then you will be asked if you want to add and <a
-      href="jmol.html#align"
-    >align this structure</a> to the structure in the existing view. (<em>new
-        feature in Jalview 2.6</em>).
-    </li>
+      href="jmol.html#align"></a> to
+    the structure in the existing view. (<em>new feature in Jalview
+      2.6</em>).
+  </li>
 
     <li>If the structure is already shown, then you will be
       prompted to associate the sequence with an existing view of the
@@ -107,14 +129,15 @@
 
 
   <p>
-    <strong>Importing PDB Entries or files in PDB format</strong><br>
-    You can retrieve sequences from the PDB using the <a
-      href="pdbsequencefetcher.html"
-    >Sequence Fetcher</a>. Any sequences retrieved with this service are
-    automatically associated with their source database entry. For PDB
-    sequences, simply select PDB as the database and enter your known
-    PDB id (appended with ':' and a chain code, if desired).<br>
-    Jalview will also read PDB files directly. Simply load in the file
+    <strong>Retrieving sequences from the PDB</strong><br>You can
+    retrieve sequences from the PDB using the <a
+      href="pdbsequencefetcher.html">Sequence Fetcher</a>. The sequences
+    retrieved with this service are derived directly from the PDB 3D
+    structure data, which can be viewed in the same way above. Secondary
+    structure and temperature factor annotation can also be added. <br />
+
+    <br>Jalview will also read PDB files directly - either in PDB
+    format, or <a href="mmcif.html">mmCIF</a>. Simply load in the file
     as you would an alignment file. The sequences of any protein or
     nucleotide chains will be extracted from the file and viewed in the
     alignment window.
@@ -129,8 +152,8 @@
     desktop, Jalview will give you the option of associating PDB files
     with sequences that have the same filename. This means, for example,
     you can automatically associate PDB files with names like '1gaq.pdb'
-    with sequences that have an ID like '1gaq'. <br />
-    <em>Note: This feature was added in Jalview 2.7</em>
+    with sequences that have an ID like '1gaq'. <br /> <em>Note:
+      This feature was added in Jalview 2.7</em>
   </p>
   <p>
     <em>Note for Jalview applet users:<br> Due to the applet
@@ -150,16 +173,31 @@
       Features"</strong> menu item and the <a href="featuresettings.html">Feature
       Settings dialog box</a>.
   </p>
+  <br />
+  <hr>
+  <p>
+    <strong>Switching between mmCIF and PDB format for
+      downloading files from the PDB</strong><br /> Jalview now employs the <a
+      href="mmcif.html">mmCIF format</a> for importing 3D structure data
+    from flat file and EMBL-PDBe web-service, as recommended by the
+    wwwPDB. If you prefer (for any reason) to download data as PDB files
+    instead, then first close Jalview, and add the following line to
+    your .jalview_properties file:<br />
+    <code> PDB_DOWNLOAD_FORMAT=PDB </code>
+    <br /> When this setting is configured, Jalview will only request
+    PDB format files from EMBL-EBI's PDBe.<br /> <em>mmCIF format
+      file support was added in Jalview 2.10.</em>
+  </p>
 
   <p>
     <em><strong>Outstanding problem with cut'n'pasted
-        files in Jalview 2.6 and Jalview 2.7</strong><br> Structures
-      imported via the cut'n'paste dialog box will not be correctly
-      highlighted or coloured when they are displayed in structure
-      views, especially if they contain more than one PDB structure. See
-      the bug report at http://issues.jalview.org/browse/JAL-623 for
-      news on this problem.</em>
+        files in Jalview 2.6 and Jalview 2.7</strong><br>Structures imported
+      via the cut'n'paste dialog box will not be correctly highlighted
+      or coloured when they are displayed in structure views, especially
+      if they contain more than one PDB structure. See the bug report at
+      http://issues.jalview.org/browse/JAL-623 for news on this problem.</em>
   </p>
+
 </body>
 </html>
 
diff --git a/help/html/features/wrap.html b/help/html/features/wrap.html
index 8f562eb..f00fa1c 100644
--- a/help/html/features/wrap.html
+++ b/help/html/features/wrap.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/features/xsspannotation.html b/help/html/features/xsspannotation.html
index fcc2b21..51a3946 100644
--- a/help/html/features/xsspannotation.html
+++ b/help/html/features/xsspannotation.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -74,22 +74,20 @@
     tab in the <strong>Tools→Preferences</strong> dialog allow the
     processing of structure data to be disabled, or selectively enabled.
     For more information, take a look at the <a
-      href="preferences.html#structure"
-    >documentation for the structure panel</a>.
+      href="preferences.html#structure">documentation for the
+      structure panel</a>.
   </p>
   <p>
     <em>The display of secondary structure data was introduced in
       Jalview 2.8.2, and is made possible by Jalview's use of <a
-      href="jmol.html"
-    >Jmol's DSSP implementation</a>, based on the original <a
-      href="http://www.ncbi.nlm.nih.gov/pubmed/6667333"
-    >Kabsch and Sander algorithm</a> ported by <a
-      href="http://swift.cmbi.ru.nl/gv/dssp/"
-    >Robbie P. Joosten and colleagues</a>, and a client for <a
-      href="https://github.com/fjossinet/PyRNA"
-    >Fabrice Jossinet's pyRNA services</a> that was developed by Anne
-      Menard, Jim Procter and Yann Ponty as part of the Jalview Summer
-      of Code 2012.
+      href="jmol.html">Jmol's DSSP implementation</a>, based on the
+      original <a href="http://www.ncbi.nlm.nih.gov/pubmed/6667333">Kabsch
+        and Sander algorithm</a> ported by <a
+      href="http://swift.cmbi.ru.nl/gv/dssp/">Robbie P. Joosten
+        and colleagues</a>, and a client for <a
+      href="https://github.com/fjossinet/PyRNA">Fabrice
+        Jossinet's pyRNA services</a> that was developed by Anne Menard, Jim
+      Procter and Yann Ponty as part of the Jalview Summer of Code 2012.
     </em>
   </p>
 </body>
diff --git a/help/html/groovy/featureCounter.html b/help/html/groovy/featureCounter.html
new file mode 100644
index 0000000..e208c85
--- /dev/null
+++ b/help/html/groovy/featureCounter.html
@@ -0,0 +1,269 @@
+<html>
+<!--
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ -->
+<head>
+<title>Extending Jalview with Groovy - Feature Counter Example</title>
+</head>
+<body>
+  <p>
+    <strong>Extending Jalview with Groovy - A customisable
+      feature counter</strong><br /> <br />The groovy script below shows how to
+    add a new calculation track to a Jalview alignment window.
+  </p>
+  <p>As currently written, it will add two tracks to a protein
+    alignment view which count Pfam features in each column, and ones
+    where a charge residue also occur.</p>
+  <p>To try it for yourself:</p>
+  <ol>
+    <li>Copy and paste it into the groovy script console</li>
+    <li>Load the example Feredoxin project (the one that opens by
+      default when you first launched Jalview)</li>
+    <li>Select <strong>Calculations→Execute Groovy
+        Script</strong> from the alignment window's menu bar to run the script on
+      the current view.
+    </li>
+  </ol>
+  <em><a
+    href="http://www.jalview.org/examples/groovy/featureCounter.groovy">http://www.jalview.org/examples/groovy/featureCounter.groovy</a>
+    - rendered with <a href="http://hilite.me">hilite.me</a></em>
+  <!-- HTML generated using hilite.me -->
+  <div
+    style="background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;">
+    <pre style="margin: 0; line-height: 125%">
+<span style="color: #888888">/*</span>
+<span style="color: #888888"> * Jalview - A Sequence Alignment Editor and Viewer (Version 2.10)</span>
+<span style="color: #888888"> * Copyright (C) 2016 The Jalview Authors</span>
+<span style="color: #888888"> * </span>
+<span style="color: #888888"> * This file is part of Jalview.</span>
+<span style="color: #888888"> * </span>
+<span style="color: #888888"> * Jalview is free software: you can redistribute it and/or</span>
+<span style="color: #888888"> * modify it under the terms of the GNU General Public License </span>
+<span style="color: #888888"> * as published by the Free Software Foundation, either version 3</span>
+<span style="color: #888888"> * of the License, or (at your option) any later version.</span>
+<span style="color: #888888"> *  </span>
+<span style="color: #888888"> * Jalview is distributed in the hope that it will be useful, but </span>
+<span style="color: #888888"> * WITHOUT ANY WARRANTY; without even the implied warranty </span>
+<span style="color: #888888"> * of MERCHANTABILITY or FITNESS FOR A PARTICULAR </span>
+<span style="color: #888888"> * PURPOSE.  See the GNU General Public License for more details.</span>
+<span style="color: #888888"> * </span>
+<span style="color: #888888"> * You should have received a copy of the GNU General Public License</span>
+<span style="color: #888888"> * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.</span>
+<span style="color: #888888"> * The Jalview Authors are detailed in the 'AUTHORS' file.</span>
+<span style="color: #888888"> */</span>
+ 
+<span style="color: #008800; font-weight: bold">import</span> <span
+        style="color: #0e84b5; font-weight: bold">jalview.workers.FeatureCounterI</span><span
+        style="color: #333333">;</span>
+<span style="color: #008800; font-weight: bold">import</span> <span
+        style="color: #0e84b5; font-weight: bold">jalview.workers.AlignmentAnnotationFactory</span><span
+        style="color: #333333">;</span>
+
+<span style="color: #888888">/*</span>
+<span style="color: #888888"> * Example script that registers two alignment annotation calculators</span>
+<span style="color: #888888"> * - one that counts residues in a column with Pfam annotation</span>
+<span style="color: #888888"> * - one that counts only charged residues with Pfam annotation</span>
+<span style="color: #888888"> *</span>
+<span style="color: #888888"> * To try:</span>
+<span style="color: #888888"> * 1. load uniref50.fa from the examples folder</span>
+<span style="color: #888888"> * 2. load features onto it from from examples/exampleFeatures.txt</span>
+<span style="color: #888888"> * 3. Open this script in the Groovy console.</span>
+<span style="color: #888888"> * 4. Either execute this script from the console, or via Calculate->Run Groovy Script</span>
+<span style="color: #888888"> </span>
+<span style="color: #888888"> * To explore further, try changing this script to count other kinds of occurrences of </span>
+<span style="color: #888888"> * residue and sequence features at columns in an alignment.</span>
+<span style="color: #888888"> */</span>
+
+<span style="color: #888888">/*</span>
+<span style="color: #888888"> * A closure that returns true for any Charged residue</span>
+<span style="color: #888888"> */</span>
+<span style="color: #333399; font-weight: bold">def</span> isCharged <span
+        style="color: #333333">=</span> <span style="color: #333333">{</span> residue <span
+        style="color: #333333">-></span>
+    <span style="color: #008800; font-weight: bold">switch</span><span
+        style="color: #333333">(</span>residue<span
+        style="color: #333333">)</span> <span style="color: #333333">{</span>
+        <span style="color: #008800; font-weight: bold">case</span> <span
+        style="color: #333333">[</span><span
+        style="background-color: #fff0f0">'D'</span><span
+        style="color: #333333">,</span> <span
+        style="background-color: #fff0f0">'d'</span><span
+        style="color: #333333">,</span> <span
+        style="background-color: #fff0f0">'E'</span><span
+        style="color: #333333">,</span> <span
+        style="background-color: #fff0f0">'e'</span><span
+        style="color: #333333">,</span> <span
+        style="background-color: #fff0f0">'H'</span><span
+        style="color: #333333">,</span> <span
+        style="background-color: #fff0f0">'h'</span><span
+        style="color: #333333">,</span> <span
+        style="background-color: #fff0f0">'K'</span><span
+        style="color: #333333">,</span> <span
+        style="background-color: #fff0f0">'k'</span><span
+        style="color: #333333">,</span> <span
+        style="background-color: #fff0f0">'R'</span><span
+        style="color: #333333">,</span> <span
+        style="background-color: #fff0f0">'r'</span><span
+        style="color: #333333">]:</span>
+            <span style="color: #008800; font-weight: bold">return</span> <span
+        style="color: #008800; font-weight: bold">true</span>
+    <span style="color: #333333">}</span>
+    <span style="color: #008800; font-weight: bold">false</span>
+<span style="color: #333333">}</span> 
+
+<span style="color: #888888">/*</span>
+<span style="color: #888888"> * A closure that returns 1 if sequence features include type 'Pfam', else 0</span>
+<span style="color: #888888"> * Argument should be a list of SequenceFeature </span>
+<span style="color: #888888"> */</span>
+<span style="color: #333399; font-weight: bold">def</span> hasPfam <span
+        style="color: #333333">=</span> <span style="color: #333333">{</span> features <span
+        style="color: #333333">-></span> 
+    <span style="color: #008800; font-weight: bold">for</span> <span
+        style="color: #333333">(</span>sf <span
+        style="color: #008800; font-weight: bold">in</span> features<span
+        style="color: #333333">)</span>
+    <span style="color: #333333">{</span>
+        <span style="color: #888888">/*</span>
+<span style="color: #888888">         * Here we inspect the type of the sequence feature.</span>
+<span style="color: #888888">         * You can also test sf.description, sf.score, sf.featureGroup,</span>
+<span style="color: #888888">         * sf.strand, sf.phase, sf.begin, sf.end</span>
+<span style="color: #888888">         * or sf.getValue(attributeName) for GFF 'column 9' properties</span>
+<span style="color: #888888">         */</span>
+        <span style="color: #008800; font-weight: bold">if</span> <span
+        style="color: #333333">(</span><span
+        style="background-color: #fff0f0">"Pfam"</span><span
+        style="color: #333333">.</span><span style="color: #0000CC">equals</span><span
+        style="color: #333333">(</span>sf<span style="color: #333333">.</span><span
+        style="color: #0000CC">type</span><span style="color: #333333">))</span>
+        <span style="color: #333333">{</span>
+            <span style="color: #008800; font-weight: bold">return</span> <span
+        style="color: #008800; font-weight: bold">true</span>
+        <span style="color: #333333">}</span>
+    <span style="color: #333333">}</span>
+    <span style="color: #008800; font-weight: bold">false</span>
+<span style="color: #333333">}</span>
+
+<span style="color: #888888">/*</span>
+<span style="color: #888888"> * Closure that computes an annotation based on </span>
+<span style="color: #888888"> * presence of particular residues and features</span>
+<span style="color: #888888"> * Parameters are</span>
+<span style="color: #888888"> * - the name (label) for the alignment annotation</span>
+<span style="color: #888888"> * - the description (tooltip) for the annotation</span>
+<span style="color: #888888"> * - a closure (groovy function) that tests whether to include a residue</span>
+<span style="color: #888888"> * - a closure that tests whether to increment count based on sequence features  </span>
+<span style="color: #888888"> */</span>
+<span style="color: #333399; font-weight: bold">def</span> getColumnCounter <span
+        style="color: #333333">=</span> <span style="color: #333333">{</span> name<span
+        style="color: #333333">,</span> desc<span style="color: #333333">,</span> acceptResidue<span
+        style="color: #333333">,</span> acceptFeatures <span
+        style="color: #333333">-></span>
+    <span style="color: #333333">[</span>
+     <span style="color: #997700; font-weight: bold">getName:</span> <span
+        style="color: #333333">{</span> name <span
+        style="color: #333333">},</span> 
+     <span style="color: #997700; font-weight: bold">getDescription:</span> <span
+        style="color: #333333">{</span> desc <span
+        style="color: #333333">},</span>
+     <span style="color: #997700; font-weight: bold">getMinColour:</span> <span
+        style="color: #333333">{</span> <span style="color: #333333">[</span><span
+        style="color: #0000DD; font-weight: bold">0</span><span
+        style="color: #333333">,</span> <span
+        style="color: #0000DD; font-weight: bold">255</span><span
+        style="color: #333333">,</span> <span
+        style="color: #0000DD; font-weight: bold">255</span><span
+        style="color: #333333">]</span> <span style="color: #333333">},</span> <span
+        style="color: #888888">// cyan</span>
+     <span style="color: #997700; font-weight: bold">getMaxColour:</span> <span
+        style="color: #333333">{</span> <span style="color: #333333">[</span><span
+        style="color: #0000DD; font-weight: bold">0</span><span
+        style="color: #333333">,</span> <span
+        style="color: #0000DD; font-weight: bold">0</span><span
+        style="color: #333333">,</span> <span
+        style="color: #0000DD; font-weight: bold">255</span><span
+        style="color: #333333">]</span> <span style="color: #333333">},</span> <span
+        style="color: #888888">// blue</span>
+     <span style="color: #997700; font-weight: bold">count:</span> 
+         <span style="color: #333333">{</span> res<span
+        style="color: #333333">,</span> feats <span
+        style="color: #333333">-></span> 
+            <span style="color: #333399; font-weight: bold">def</span> c <span
+        style="color: #333333">=</span> <span
+        style="color: #0000DD; font-weight: bold">0</span>
+            <span style="color: #008800; font-weight: bold">if</span> <span
+        style="color: #333333">(</span>acceptResidue<span
+        style="color: #333333">.</span><span style="color: #0000CC">call</span><span
+        style="color: #333333">(</span>res<span style="color: #333333">))</span>
+            <span style="color: #333333">{</span>
+                <span style="color: #008800; font-weight: bold">if</span> <span
+        style="color: #333333">(</span>acceptFeatures<span
+        style="color: #333333">.</span><span style="color: #0000CC">call</span><span
+        style="color: #333333">(</span>feats<span style="color: #333333">))</span>
+                <span style="color: #333333">{</span>
+                    c<span style="color: #333333">++</span>
+                <span style="color: #333333">}</span>
+            <span style="color: #333333">}</span>
+            c
+         <span style="color: #333333">}</span>
+     <span style="color: #333333">]</span> <span
+        style="color: #008800; font-weight: bold">as</span> FeatureCounterI
+<span style="color: #333333">}</span>
+
+<span style="color: #888888">/*</span>
+<span style="color: #888888"> * Define an annotation row that counts any residue with Pfam domain annotation</span>
+<span style="color: #888888"> */</span>
+<span style="color: #333399; font-weight: bold">def</span> pfamAnnotation <span
+        style="color: #333333">=</span> getColumnCounter<span
+        style="color: #333333">(</span><span
+        style="background-color: #fff0f0">"Pfam"</span><span
+        style="color: #333333">,</span> <span
+        style="background-color: #fff0f0">"Count of residues with Pfam domain annotation"</span><span
+        style="color: #333333">,</span> <span style="color: #333333">{</span><span
+        style="color: #008800; font-weight: bold">true</span><span
+        style="color: #333333">},</span> hasPfam<span
+        style="color: #333333">)</span>
+
+<span style="color: #888888">/*</span>
+<span style="color: #888888"> * Define an annotation row that counts charged residues with Pfam domain annotation</span>
+<span style="color: #888888"> */</span>
+<span style="color: #333399; font-weight: bold">def</span> chargedPfamAnnotation <span
+        style="color: #333333">=</span> getColumnCounter<span
+        style="color: #333333">(</span><span
+        style="background-color: #fff0f0">"Pfam charged"</span><span
+        style="color: #333333">,</span> <span
+        style="background-color: #fff0f0">"Count of charged residues with Pfam domain annotation"</span><span
+        style="color: #333333">,</span> isCharged<span
+        style="color: #333333">,</span> hasPfam<span
+        style="color: #333333">)</span>
+
+<span style="color: #888888">/*</span>
+<span style="color: #888888"> * Register the annotations</span>
+<span style="color: #888888"> */</span>
+AlignmentAnnotationFactory<span style="color: #333333">.</span><span
+        style="color: #0000CC">newCalculator</span><span
+        style="color: #333333">(</span>pfamAnnotation<span
+        style="color: #333333">)</span> 
+AlignmentAnnotationFactory<span style="color: #333333">.</span><span
+        style="color: #0000CC">newCalculator</span><span
+        style="color: #333333">(</span>chargedPfamAnnotation<span
+        style="color: #333333">)</span>
+</pre>
+  </div>
+</body>
+</html>
diff --git a/help/html/index.html b/help/html/index.html
index c1a4b60..062500d 100644
--- a/help/html/index.html
+++ b/help/html/index.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -45,8 +45,7 @@
   <p>
     For more information, you might also want to take a look at the
     documentation section of the Jalview website (<a
-      href="http://www-test.jalview.org/about/documentation"
-    >http://www.jalview.org/about/documentation</a>).
+      href="http://www-test.jalview.org/about/documentation">http://www.jalview.org/about/documentation</a>).
   </p>
   <p>
     If you are using the Jalview Desktop application and are looking for
@@ -55,8 +54,7 @@
     google the online version of these pages. If you don't find what you
     are looking for, or want to report a bug or make a feature request,
     then get in contact over at <a
-      href="http://www.jalview.org/community"
-    >http://www.jalview.org/community</a>
+      href="http://www.jalview.org/community">http://www.jalview.org/community</a>
   </p>
 
   <p>
@@ -70,8 +68,8 @@
     <strong>25</strong> (9) 1189-1191 doi: 10.1093/bioinformatics/btp033
   </p>
   <p>
-    <strong>The Jalview Authors</strong><br /> The following people have
-    contributed to Jalview's development:
+    <strong>The Jalview Authors</strong><br /> The following people
+    have contributed to Jalview's development:
   <ul>
     <li>Jalview 1
       <ul>
diff --git a/help/html/io/export.html b/help/html/io/export.html
index 709d297..d248291 100644
--- a/help/html/io/export.html
+++ b/help/html/io/export.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -25,8 +25,7 @@
 <body>
   <p>
     <strong>Exporting alignments as graphics and lineart<a
-      name="export"
-    ></a></strong>
+      name="export"></a></strong>
   </p>
   <p>
     The alignment view can be printed using <strong>File→Print</strong>,
@@ -46,8 +45,7 @@
     <li>BioJS MSA - A JavaScript based multiple sequence alignment
       viewer. <br> <em>For interactive alignment data
         visualisation in a web browser.<br />Get more info about the
-        BioJS MSA viewer at a
-        href="http://msa.biojs.net/">http://msa.biojs.net/</a>
+        BioJS MSA viewer at <a href="http://msa.biojs.net/">http://msa.biojs.net/</a>
     </em>
     </li>
 
@@ -58,12 +56,12 @@
   <p>
     In Jalview 2.9, new HTML exporting options were introduced. The
     standard HTML export option displays alignments as SVG documents
-    embedded as scollable panes. Exported pages can optionally also
+    embedded as scrollable panes. Exported pages can optionally also
     include <a href="../features/bioJsonFormat.html">BioJSON</a> data,
     which allows Jalview to extract the original data used to create the
     page. Jalview can also generate HTML pages which embed BioJSON data
     and the BioJS MSA viewer, a pure javascript alignment viewer that
-    provides a range of interactive analysis capabiltiies.
+    provides a range of interactive analysis capabilities.
   </p>
   <p>
   <p>
diff --git a/help/html/io/exportseqreport.html b/help/html/io/exportseqreport.html
index 8e99d0e..9ce4dad 100644
--- a/help/html/io/exportseqreport.html
+++ b/help/html/io/exportseqreport.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,23 +28,22 @@
     Much of the information retrieved by Jalview about a sequence is
     visualized on the alignment. Often, however, there are a huge number
     of ontology terms, cross-references, links to publications and other
-    kinds of data shown in the sequence ID tooltip that cannot be
-    examined. In this case, you can view and export the information
-    shown in a sequence's ID tooltip by right-clicking and selecting the
+    kinds of data associated with a sequence, and only some of these are shown in 
+    sequence ID tooltip. To show the full set of annotation and database links for
+    a sequence, right-click and select the
     <strong>"<em>(sequence's name)</em></em>→Sequence
       Details ..."
     </strong> entry from the <a href="../menus/popupMenu.html">pop-up menu</a>.
   </p>
   <p>
     <strong>Annotation Reports for a range of sequences</strong><br />
-    If you would like to view the tooltips for a number of sequences,
+    If you would like to view database and metadata for a number of sequences,
     simply select them all and then use the <strong>Selection→Sequence
       Details ...</strong> entry in the <a href="../menus/popupMenu.html">pop-up
       menu</a>.
   </p>
   <img src="seqreport.gif"
-    alt="Sequence Annotation is displayed as HTML in a report window"
-  />
+    alt="Sequence Annotation is displayed as HTML in a report window" />
   <p>
     <strong>Copying and pasting annotation to other programs</strong><br>
     The <strong>File→Save</strong> option in the sequence
diff --git a/help/html/io/fileformats.html b/help/html/io/fileformats.html
index 8f88c26..f4ae819 100644
--- a/help/html/io/fileformats.html
+++ b/help/html/io/fileformats.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -95,9 +95,9 @@ td {
     <tr>
       <td width="17%">JSON</td>
       <td width="60%">Data starts with '{' <br>Data ends with
-        '}' <br>
-      <br>See <a href="../features/bioJsonFormat.html">BioJSON</a>
-        for more infomation about the Jalview JSON format <br></td>
+        '}' <br> <br>See <a
+        href="../features/bioJsonFormat.html">BioJSON</a> for more
+        infomation about the Jalview JSON format <br></td>
       <td width="23%">.json</td>
     </tr>
 
diff --git a/help/html/io/index.html b/help/html/io/index.html
index 57f0416..e938945 100644
--- a/help/html/io/index.html
+++ b/help/html/io/index.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -33,10 +33,6 @@
       NBRF/PIR (including MODELLER variant), Pfam/Stockholm</em>
   </p>
   <p>
-    The EBI has <a href="http://www.ebi.ac.uk/help/formats.html">examples</a>
-    of these file formats.
-  </p>
-  <p>
     Additionally, whole sets of coloured and annotated alignments and
     trees can be read from a <a href="../features/jalarchive.html">Jalview
       (jar) format</a> file using <strong>Desktop→Load
@@ -63,10 +59,9 @@
   </p>
   <p>
     Jalview can also read Jalview specific files for <a
-      href="../features/featuresFormat.html"
-    >sequence features</a> and <a
-      href="../features/annotationsFormat.html"
-    >alignment annotation</a>.
+      href="../features/featuresFormat.html">sequence features</a>
+    and <a href="../features/annotationsFormat.html">alignment
+      annotation</a>.
   </p>
   <p>
     <strong>Output</strong>
diff --git a/help/html/io/modellerpir.html b/help/html/io/modellerpir.html
index 0290721..7386fa6 100644
--- a/help/html/io/modellerpir.html
+++ b/help/html/io/modellerpir.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,11 +28,10 @@
   </p>
   <p>
     The homology modelling program, <a
-      href="http://salilab.org/modeller/"
-    >Modeller</a> uses a special form of the PIR format where information
-    about sequence numbering and chain codes are written into the
-    'description' line between the PIR protein tag and the protein
-    alignment entry:
+      href="http://salilab.org/modeller/">Modeller</a> uses a
+    special form of the PIR format where information about sequence
+    numbering and chain codes are written into the 'description' line
+    between the PIR protein tag and the protein alignment entry:
   </p>
   <pre>>P1;Q93Z60_ARATH
 sequence:Q93Z60_ARATH:1:.:118:.:.
@@ -52,12 +51,12 @@ KDPLPDAEDWDGVKGKLQHLE*
     no information is lost if this parsing process fails.</p>
   <p>
     The 'Modeller Output' flag in the 'Output' tab of the Jalview <a
-      href="../features/preferences.html"
-    >Preferences dialog box</a> controls whether Jalview will also output
-    MODELLER style PIR files. In this case, any existing 'non-modeller
-    PIR' header information in the description string of an alignment is
-    appended to an automatically generated modeller description line for
-    that sequence.
+      href="../features/preferences.html">Preferences dialog
+      box</a> controls whether Jalview will also output MODELLER style PIR
+    files. In this case, any existing 'non-modeller PIR' header
+    information in the description string of an alignment is appended to
+    an automatically generated modeller description line for that
+    sequence.
   </p>
   <p>The general format used for generating the Modeller/PIR
     sequence description line is shown below :
diff --git a/help/html/io/tcoffeescores.html b/help/html/io/tcoffeescores.html
index 5bc6f7d..5eb149b 100644
--- a/help/html/io/tcoffeescores.html
+++ b/help/html/io/tcoffeescores.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -30,8 +30,8 @@
     T-COFFEE score files like the <a href="#tcoffeeeg">one below</a> can
     be displayed on the alignment using the <strong><em>Colours→T-COFFEE
         Scores</em></strong> or <strong><em>Colour → <a
-        href="../colourSchemes/annotationColouring.html"
-      >Colour by Annotation</a></em></strong> options.
+        href="../colourSchemes/annotationColouring.html">Colour
+          by Annotation</a></em></strong> options.
   </p>
   <img src="../colourSchemes/colbytcoffee.png" />
   <p>
diff --git a/help/html/keys.html b/help/html/keys.html
index 6a88c5c..a1292b0 100644
--- a/help/html/keys.html
+++ b/help/html/keys.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -30,10 +30,10 @@
     Jalview has two distinct modes of keyboard operation - in 'Normal'
     mode, single keystrokes (including those shown next to menu items)
     provide short cuts to common commands. In <a
-      href="features/cursorMode.html"
-    >'Cursor'</a> mode (enabled by <em>F2</em>), some of these are
-    disabled and more complex 'Compound Keystrokes' can be entered to
-    perform precise navigation, selection and editing operations.
+      href="features/cursorMode.html">'Cursor'</a> mode (enabled by
+    <em>F2</em>), some of these are disabled and more complex 'Compound
+    Keystrokes' can be entered to perform precise navigation, selection
+    and editing operations.
   </p>
   <table border="1">
     <tr>
@@ -159,7 +159,7 @@
       <td>Both</td>
       <td>Cuts the (fully) selected sequences from the alignment. <!-- not yet in this version 
 This will not happen if only some
-columns are selected, you should use the <a href="features/regionHiding.html">Hide Regions feature</a> instead.-->
+columns are selected, you should use the <a href="features/hiddenRegions.html">Hide Regions feature</a> instead.-->
       </td>
     </tr>
     <tr>
@@ -167,6 +167,19 @@ columns are selected, you should use the <a href="features/regionHiding.html">Hi
       <td>Both</td>
       <td>Launches the search window</td>
     </tr>
+    <tr><td><strong>B</strong></td>
+      <td>Both</td>
+      <td>Add highlighted columns to current column selection</td>
+    </tr>
+    <tr><td><strong>Alt 'B'</strong></td>
+      <td>Both</td>
+      <td>Add all but the currently highlighted columns to current selection</td>
+    </tr>
+    <tr><td><strong>Control 'B'</strong></td>
+      <td>Both</td>
+      <td>Toggle the column selection marks for the currently highlighted 
+          columns (or all others if Alt is also pressed)</td>
+    </tr>
     <tr>
       <td><strong>H</strong></td>
       <td>Both</td>
@@ -279,7 +292,7 @@ columns are selected, you should use the <a href="features/regionHiding.html">Hi
       <td>Cursor</td>
       <td>Move cursor to a particular column (<strong><em>p1</em></strong>)
         and row (<strong><em>p2</em></strong>) in the alignment.<br>
-      <em>e.g. '5,6<Return>' moves the cursor to the 5th
+        <em>e.g. '5,6<Return>' moves the cursor to the 5th
           column in the 6th sequence.</em></td>
     </tr>
     <tr>
@@ -317,25 +330,22 @@ columns are selected, you should use the <a href="features/regionHiding.html">Hi
       <td><strong><em>[p]</em><br>Space</strong></td>
       <td>Cursor</td>
       <td>Inserts one (or optionally <strong><em>p</em></strong>)
-        gaps at the current position.<br>
-      <em>Hold down Control or Shift to insert gaps over a sequence
-          group</em></td>
+        gaps at the current position.<br> <em>Hold down
+          Control or Shift to insert gaps over a sequence group</em></td>
     </tr>
     <tr>
       <td><strong><em>[p]</em><br>Delete<br></strong></td>
       <td>Cursor</td>
       <td>Removes one (or optionally <strong><em>p</em></strong>)
-        gaps at the cursor position.<br>
-      <em>Hold down Control or Shift to insert gaps over a sequence
-          group</em></td>
+        gaps at the cursor position.<br> <em>Hold down Control
+          or Shift to insert gaps over a sequence group</em></td>
     </tr>
     <tr>
       <td><strong><em>[p]</em><br>Backspace<br></strong></td>
       <td>Cursor</td>
       <td>Removes one (or optionally <strong><em>p</em></strong>)
-        gaps at the cursor position.<br>
-      <em>Hold down Control or Shift to insert gaps over a sequence
-          group</em></td>
+        gaps at the cursor position.<br> <em>Hold down Control
+          or Shift to insert gaps over a sequence group</em></td>
     </tr>
   </table>
   <p> </p>
diff --git a/help/html/memory.html b/help/html/memory.html
index edc277c..0bb59bf 100644
--- a/help/html/memory.html
+++ b/help/html/memory.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -63,15 +63,13 @@
         file. You can obtain a JNLP file with modified memory settings
         from our service with the following link (replace 2G with
         desired memory in G or M):<br /> <a
-          href="http://www.jalview.org/services/launchApp?jvm-max-heap=2G"
-        >http://www.jalview.org/services/launchApp?jvm-max-heap=2G</a>
+          href="http://www.jalview.org/services/launchApp?jvm-max-heap=2G">http://www.jalview.org/services/launchApp?jvm-max-heap=2G</a>
       </p>
       <p>
         Alternatively, if you want to create your own JNLP file then
         please download the latest JNLP file from <a
-          href="http://www.jalview.org/webstart/jalview.jnlp"
-        >http://www.jalview.org/webstart/jalview.jnlp</a> and modify the
-        max-heap-size parameter for the j2se tag in the
+          href="http://www.jalview.org/webstart/jalview.jnlp">http://www.jalview.org/webstart/jalview.jnlp</a>
+        and modify the max-heap-size parameter for the j2se tag in the
         <resources> element. e.g.
       <pre>
 <j2se version="1.7+" initial-heap-size="500M" max-heap-size="1000M"/>
@@ -109,6 +107,7 @@ lax.nl.java.option.java.heap.size.initial=500m
             The lines you need to change are in the <em>Info.plist</em>
             file inside the <em>Jalview.app/Contents</em> directory
             (which is where the installAnywhere installation was made) :
+
           
           <pre>
 <key&ht;VMOptions</key&ht;
diff --git a/help/html/menus/alignmentMenu.html b/help/html/menus/alignmentMenu.html
index 5f60ba5..d945307 100644
--- a/help/html/menus/alignmentMenu.html
+++ b/help/html/menus/alignmentMenu.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -33,7 +33,7 @@
       <ul>
         <li><strong>Fetch Sequence</strong><br> <em>Shows
             a dialog window in which you can retrieve known ids from
-            Uniprot, EMBL, EMBLCDS, PFAM, Rfam, or PDB database using
+            UniProt, EMBL, EMBLCDS, PFAM, Rfam, or PDB database using
             Web Services provided by the European Bioinformatics
             Institute. See <a href="../features/seqfetch.html">Sequence
               Fetcher</a>
@@ -133,10 +133,9 @@
         </em></li>
         <li><strong>Load Features / Annotations<br>
         </strong><em>Load files describing precalculated <a
-            href="../features/featuresFormat.html"
-          >sequence features</a> or <a
-            href="../features/annotationsFormat.html"
-          >alignment annotations</a>.
+            href="../features/featuresFormat.html">sequence
+              features</a> or <a href="../features/annotationsFormat.html">alignment
+              annotations</a>.
         </em></li>
         <li><strong>Close (Control W)</strong><br> <em>Close
             the alignment window. Make sure you have saved your
@@ -196,7 +195,7 @@
             "Deselect All" to deselect all columns.</em></li>
         <li><strong>Remove Right (Control R)<br>
         </strong><em>If the alignment has marked columns, the alignment will
-            be trimmed to the left of the leftmost marked column. To
+            be trimmed to the right of the rightmost marked column. To
             mark a column, mouse click the scale bar above the
             alignment. Click again to unmark a column, or select
             "Deselect All" to deselect all columns.</em></li>
@@ -204,16 +203,14 @@
         </strong><em>All columns which only contain gap characters
             ("-", ".") will be deleted.<br> You
             may set the default gap character in <a
-            href="../features/preferences.html"
-          >preferences</a>.
+            href="../features/preferences.html">preferences</a>.
         </em></li>
         <li><strong>Remove All Gaps (Control Shift E)</strong><br>
           <em>Gap characters ("-", ".") will be
             deleted from the selected area of the alignment. If no
             selection is made, ALL the gaps in the alignment will be
             removed.<br> You may set the default gap character in <a
-            href="../features/preferences.html"
-          >preferences</a>.
+            href="../features/preferences.html">preferences</a>.
         </em></li>
         <li><strong>Remove Redundancy (Control D)<br>
         </strong><em>Selecting this option brings up a window asking you to
@@ -233,8 +230,7 @@
             with alignment analysis programs which require 'properly
             aligned sequences' to be all the same length.<br> You
             may set the default for <strong>Pad Gaps</strong> in the <a
-            href="../features/preferences.html"
-          >preferences</a>.
+            href="../features/preferences.html">preferences</a>.
         </em></li>
       </ul></li>
     <li><strong>Select</strong>
@@ -278,11 +274,16 @@
             <strong>WARNING</strong>: This cannot be undone.
         </em></li>
         <li><strong><a
-            href="../features/columnFilterByAnnotation.html"
-          >Select/Hide Columns by Annotation</a></strong> <br /> <em>Select
-            or Hide columns in the alignment according to secondary
-            structure, labels and values shown in alignment annotation
-            rows. </em></li>
+            href="../features/columnFilterByAnnotation.html">Select/Hide
+              Columns by Annotation</a></strong> <br /> <em>Select or Hide
+            columns in the alignment according to secondary structure,
+            labels and values shown in alignment annotation rows. </em></li>
+        <li><strong>Select Highlighted Columns</strong> <br /> <em>Selects
+        the columns currently highlighted as a result of a find, mouse
+        over, or selection event from a linked structure viewer or other
+        application. Modifiers will work on some platforms: ALT will add
+        all but the highlighted set to the column selection, and CTRL
+        (or META) will toggle the selection. </em></li>
       </ul></li>
     <li><strong>View</strong>
       <ul>
@@ -310,11 +311,11 @@
         <li><strong>Show Sequence Features</strong><br> <em>Show
             or hide sequence features on this alignment.</em></li>
         <li><strong><a
-            href="../features/featuresettings.html"
-          >Sequence Feature Settings...</a> </strong><em><br> <em>Opens
-              the Sequence Feature Settings dialog box to control the
-              colour and display of sequence features on the alignment,
-              and configure and retrieve features from DAS annotation
+            href="../features/featuresettings.html">Sequence
+              Feature Settings...</a> </strong><em><br> <em>Opens the
+              Sequence Feature Settings dialog box to control the colour
+              and display of sequence features on the alignment, and
+              configure and retrieve features from DAS annotation
               servers.</em></li>
         <li><strong>Sequence ID Tooltip</strong><em>
             (application only) <br>This submenu's options allow the
@@ -403,22 +404,21 @@
             rendering. </em></li>
         <li><strong>Wrap<br>
         </strong><em>When ticked, the alignment display is "<a
-            href="../features/wrap.html"
-          >wrapped</a>" to the width of the alignment window. This is
-            useful if your alignment has only a few sequences to view
-            its full width at once.
+            href="../features/wrap.html">wrapped</a>" to
+            the width of the alignment window. This is useful if your
+            alignment has only a few sequences to view its full width at
+            once.
         </em><br> Additional options for display of sequence numbering
           and scales are also visible in wrapped layout mode:<br>
           <ul>
-            <li><strong>Scale Above</strong><br>
-            <em> Show the alignment column position scale.</em></li>
-            <li><strong>Scale Left</strong><br>
-            <em> Show the sequence position for the first aligned
-                residue in each row in the left column of the alignment.</em></li>
-            <li><strong>Scale Right</strong><br>
-            <em> Show the sequence position for the last aligned
-                residue in each row in the right-most column of the
-                alignment.</em></li>
+            <li><strong>Scale Above</strong><br> <em>
+                Show the alignment column position scale.</em></li>
+            <li><strong>Scale Left</strong><br> <em> Show
+                the sequence position for the first aligned residue in
+                each row in the left column of the alignment.</em></li>
+            <li><strong>Scale Right</strong><br> <em>
+                Show the sequence position for the last aligned residue
+                in each row in the right-most column of the alignment.</em></li>
             <li><strong>Show Sequence Limits<br>
             </strong><em>If this box is selected the sequence name will have
                 the start and end position of the sequence appended to
@@ -474,11 +474,10 @@
           colour will be applied to all currently defined groups.<br>
       </em></li>
       <li><strong><a
-          href="../colourSchemes/textcolour.html"
-        >Colour Text...</a> </strong><em><br> Opens the Colour Text
-          dialog box to set a different text colour for light and dark
-          background, and the intensity threshold for transition between
-          them. </em></li>
+          href="../colourSchemes/textcolour.html">Colour
+            Text...</a> </strong><em><br> Opens the Colour Text dialog box
+          to set a different text colour for light and dark background,
+          and the intensity threshold for transition between them. </em></li>
       <li>Colour Scheme options: <strong>None, ClustalX,
           Blosum62 Score, Percentage Identity, Zappo, Taylor,
           Hydrophobicity, Helix Propensity, Strand Propensity, Turn
@@ -507,8 +506,8 @@
       <li><strong>By Annotation</strong><br> <em>Colours
           the alignment on a per-column value from a specified
           annotation. See <a
-          href="../colourSchemes/annotationColouring.html"
-        >Annotation Colouring</a>.
+          href="../colourSchemes/annotationColouring.html">Annotation
+            Colouring</a>.
       </em><br></li>
       <li><strong>By RNA Helices</strong><br> <em>Colours
           the helices of an RNA alignment loaded from a Stockholm file.
@@ -563,14 +562,14 @@
           provided in this menu.</strong></li>
       <li><strong>Pairwise Alignments</strong><br> <em>Applies
           Smith and Waterman algorithm to selected sequences. See <a
-          href="../calculations/pairwise.html"
-        >pairwise alignments</a>.
+          href="../calculations/pairwise.html">pairwise
+            alignments</a>.
       </em><br></li>
       <li><strong>Principal Component Analysis</strong><br> <em>Shows
           a spatial clustering of the sequences based on similarity
           scores calculated with the alignment. See <a
-          href="../calculations/pca.html"
-        >Principal Component Analysis</a>.
+          href="../calculations/pca.html">Principal
+            Component Analysis</a>.
       </em> <br></li>
       <li><strong>Extract Scores ... (optional)</strong><br> <em>This
           option is only visible if Jalview detects one or more
@@ -626,15 +625,14 @@
       or elsewhere. You need a continuous network connection in order to
       use these services through Jalview.</p>
     <ul>
-      <li><strong>Alignment</strong><br />
-      <em> Align the currently selected sequences or all sequences
-          in the alignment, or re-align unaligned sequences to the
-          aligned sequences. Entries in this menu provide access to the
-          various alignment programs supported by <a
-          href="../webServices/JABAWS.html"
-        >JABAWS</a>. See the <a href="../webServices/msaclient.html">Multiple
-            Sequence Alignment webservice client</a> entry for more
-          information.
+      <li><strong>Alignment</strong><br /> <em> Align the
+          currently selected sequences or all sequences in the
+          alignment, or re-align unaligned sequences to the aligned
+          sequences. Entries in this menu provide access to the various
+          alignment programs supported by <a
+          href="../webServices/JABAWS.html">JABAWS</a>. See the
+          <a href="../webServices/msaclient.html">Multiple Sequence
+            Alignment webservice client</a> entry for more information.
       </em></li>
       <li><strong>Secondary Structure Prediction</strong>
         <ul>
@@ -667,8 +665,8 @@
           <li><strong>Multi-Harmony</strong><br> <em>Performs
               functional residue analysis on a protein family alignment
               with sub-families defined on it. See the <a
-              href="../webServices/shmr.html"
-            >Multi-Harmony service</a> entry for more information.
+              href="../webServices/shmr.html">Multi-Harmony
+                service</a> entry for more information.
           </em></li>
         </ul></li>
     </ul></li>
diff --git a/help/html/menus/alwannotation.html b/help/html/menus/alwannotation.html
index 843486d..c930291 100644
--- a/help/html/menus/alwannotation.html
+++ b/help/html/menus/alwannotation.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -25,8 +25,8 @@
 
 <body>
   <p>
-    <strong>Alignment Window Annotations Menu</strong> (Since Jalview
-    2.8.2)
+    <strong>Alignment Window Annotations Menu</strong> <em>Since
+      Jalview 2.8.2</em>
   </p>
   <ul>
     <li><strong>Show Alignment Related</strong><em><br>
@@ -44,8 +44,7 @@
         example, Consensus).</em></li>
     <li><em>You can also selectively show or hide annotations
         from the <a href="./popupMenu.html">Popup</a> or <a
-        href="../features/annotation.html"
-      >Annotation</a> menus.
+        href="../features/annotation.html">Annotation</a> menus.
     </em></li>
     <li><strong>Sort by Sequence</strong><em><br>Sort
         sequence-specific annotations by sequence order in the alignment
diff --git a/help/html/menus/alwannotationpanel.html b/help/html/menus/alwannotationpanel.html
index 8d49744..42f52cd 100644
--- a/help/html/menus/alwannotationpanel.html
+++ b/help/html/menus/alwannotationpanel.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,7 +29,11 @@
   <ul>
     <li><strong>Annotation Label Popup Menu</strong><br> <em>This
         menu is opened by clicking anywhere on the annotation row labels
-        area (below the sequence ID area).</em>
+        area (below the sequence ID area).</em> <br />
+    <em><strong>Mac Users:</strong> pressing CTRL whilst clicking
+        the mouse/track pad is the same as a right-click. See your
+        system's settings to configure your track-pad's corners to
+        generate right-clicks.</em>
       <ul>
         <li><strong>Add New Row</strong><br> <em>Adds a
             new, named annotation row (a dialog box will pop up for you
@@ -102,7 +106,7 @@
             Consecutive arrows will be joined together to form a single
             green arrow.</em></li>
         <li><strong>Label</strong><br> <em>Sets the text
-            label at the selected positions. If more that one
+            label at the selected positions. If more than one
             consecutive position is marked with the same label, only the
             first position's label will be rendered.</em></li>
         <li><strong>Colour</strong><br> <em>Changes the
diff --git a/help/html/menus/alwcalculate.html b/help/html/menus/alwcalculate.html
index d39afa0..d2258f5 100644
--- a/help/html/menus/alwcalculate.html
+++ b/help/html/menus/alwcalculate.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -75,14 +75,14 @@
       </ul></li>
     <li><strong>Pairwise Alignments</strong><br> <em>Applies
         Smith and Waterman algorithm to selected sequences. See <a
-        href="../calculations/pairwise.html"
-      >pairwise alignments</a>.
+        href="../calculations/pairwise.html">pairwise
+          alignments</a>.
     </em><br></li>
     <li><strong>Principal Component Analysis</strong><br> <em>Shows
         a spatial clustering of the sequences based on similarity scores
         calculated over the alignment.. See <a
-        href="../calculations/pca.html"
-      >Principal Component Analysis</a>.
+        href="../calculations/pca.html">Principal Component
+          Analysis</a>.
     </em> <br></li>
     <li><strong>Extract Scores ... (optional)</strong><br> <em>This
         option is only visible if Jalview detects one or more
@@ -91,19 +91,28 @@
         parsed into sequence associated annotation which can then be
         used to sort the alignment via the Sort by→Score menu.
     </em> <br></li>
-    <li><strong>Translate as cDNA</strong> (not applet)<br>
-    <em>This option is visible for nucleotide alignments. Selecting
-        this option shows the DNA's calculated protein product in a new
-        <a href="../features/splitView.html">split frame</a> window.
-        Note that the translation is not frame- or intron-aware; it
-        simply translates all codons in each sequence, using the
-        standard <a href="../misc/geneticCode.html">genetic code</a>
-        (any incomplete final codon is discarded). You can perform this
-        action on the whole alignment, or selected rows, columns, or
-        regions.
+    <li><strong>Translate as cDNA</strong> (not applet)<br> <em>This
+        option is visible for nucleotide alignments. Selecting this
+        option shows the DNA's calculated protein product in a new <a
+        href="../features/splitView.html">split frame</a> window. Note
+        that the translation is not frame- or intron-aware; it simply
+        translates all codons in each sequence, using the standard <a
+        href="../misc/geneticCode.html">genetic code</a> (any incomplete
+        final codon is discarded). You can perform this action on the
+        whole alignment, or selected rows, columns, or regions.
+    </em> <br></li>
+    <li><strong>Reverse, Reverse Complement</strong> (not applet)<br>
+      <em>These options are visible for nucleotide alignments.
+        Selecting them adds the reverse (or reverse complement) of the
+        sequences (or selected region) as new sequences in the
+        alignment. To try this out, add this sequence and perform
+        'Reverse Complement' followed by 'Translate as cDNA': <br>
+      <small> Seq
+          GTCATTTGCGCGTGTTGATTATTCGGACCGCTCCACTTCCCTTTACTCGTGCGTTCAATTGATTTAATCCTC
+          TGGGGGGGCTCTGGTTTACATAGCTTAAATCTATTCCATTCAAGGAAGCTCATG</small>
     </em> <br></li>
     <li><strong>Get Cross-References</strong> (not applet)<br>
-    <em>This option is visible where sequences have
+      <em>This option is visible where sequences have
         cross-references to other standard databases; for example, an
         EMBL entry may have cross-references to one or more UNIPROT
         entries. Select the database to view all cross-referenced
diff --git a/help/html/menus/alwcolour.html b/help/html/menus/alwcolour.html
index 72df8f7..19f4eda 100644
--- a/help/html/menus/alwcolour.html
+++ b/help/html/menus/alwcolour.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/menus/alwedit.html b/help/html/menus/alwedit.html
index 1ead68a..da07408 100644
--- a/help/html/menus/alwedit.html
+++ b/help/html/menus/alwedit.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -75,7 +75,7 @@
         deselect all columns.</em></li>
     <li><strong>Remove Right (Control R)<br>
     </strong><em>If the alignment has marked columns, the alignment will be
-        trimmed to the left of the leftmost marked column. To mark a
+        trimmed to the right of the rightmost marked column. To mark a
         column, mouse click the scale bar above the alignment. Click
         again to unmark a column, or select "Deselect All" to
         deselect all columns.</em></li>
@@ -83,16 +83,14 @@
     </strong><em>All columns which only contain gap characters
         ("-", ".") will be deleted.<br> You may
         set the default gap character in <a
-        href="../features/preferences.html"
-      >preferences</a>.
+        href="../features/preferences.html">preferences</a>.
     </em></li>
     <li><strong>Remove All Gaps (Control Shift E)</strong><br>
       <em>Gap characters ("-", ".") will be
         deleted from the selected area of the alignment. If no selection
         is made, ALL the gaps in the alignment will be removed.<br>
         You may set the default gap character in <a
-        href="../features/preferences.html"
-      >preferences</a>.
+        href="../features/preferences.html">preferences</a>.
     </em></li>
     <li><strong>Remove Redundancy (Control D)<br>
     </strong><em>Selecting this option brings up a window asking you to
diff --git a/help/html/menus/alwfile.html b/help/html/menus/alwfile.html
index 3e4cf68..d04b401 100644
--- a/help/html/menus/alwfile.html
+++ b/help/html/menus/alwfile.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,11 +29,10 @@
   </p>
   <ul>
     <li><strong>Fetch Sequence</strong><br> <em>Shows a
-        dialog window in which you can select known ids from Uniprot,
+        dialog window in which you can select known ids from UniProt,
         EMBL, EMBLCDS, PDB, PFAM, or RFAM databases using Web Services
         provided by the European Bioinformatics Institute. See <a
-        href="../features/seqfetch.html"
-      >Sequence Fetcher</a>
+        href="../features/seqfetch.html">Sequence Fetcher</a>
     </em>.</li>
     <li><strong>Add Sequences</strong><em><br> Add
         sequences to the visible alignment from file, URL, or cut &
@@ -86,9 +85,9 @@
     <li><strong>Export Image</strong> <em><br> Creates an
         alignment graphic with the current view's annotation, alignment
         background colours and group colours. If the alignment is <a
-        href="../features/wrap.html"
-      >wrapped</a>, the output will also be wrapped and will have the same
-        visible residue width as the open alignment. </em>
+        href="../features/wrap.html">wrapped</a>, the output will
+        also be wrapped and will have the same visible residue width as
+        the open alignment. </em>
       <ul>
         <li><strong>HTML<br>
         </strong><em>Create a <a href="../io/export.html">web page</a> from
@@ -126,10 +125,9 @@
     </em></li>
     <li><strong>Load Features / Annotations<br>
     </strong><em>Load files describing precalculated <a
-        href="../features/featuresFormat.html"
-      >sequence features</a> or <a
-        href="../features/annotationsFormat.html"
-      >alignment annotations</a>.
+        href="../features/featuresFormat.html">sequence
+          features</a> or <a href="../features/annotationsFormat.html">alignment
+          annotations</a>.
     </em></li>
     <li><strong>Close (Control W)</strong><br> <em>Close
         the alignment window. Make sure you have saved your alignment
diff --git a/help/html/menus/alwformat.html b/help/html/menus/alwformat.html
index 2c6b4f7..88e9488 100644
--- a/help/html/menus/alwformat.html
+++ b/help/html/menus/alwformat.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -30,11 +30,11 @@
       for faster alignment rendering. </em></em></li>
   <li><strong>Wrap<br>
   </strong><em>When ticked, the alignment display is "<a
-      href="../features/wrap.html"
-    >wrapped</a>" to the width of the alignment window. This is
-      useful if your alignment has only a few sequences to view its full
-      width at once.<br> Additional options for display of sequence
-      numbering and scales are also visible in wrapped layout mode:
+      href="../features/wrap.html">wrapped</a>" to the width
+      of the alignment window. This is useful if your alignment has only
+      a few sequences to view its full width at once.<br>
+      Additional options for display of sequence numbering and scales
+      are also visible in wrapped layout mode:
   </em>
     <ul>
       <li><strong>Scale Left</strong><br> <em>Show the
diff --git a/help/html/menus/alwselect.html b/help/html/menus/alwselect.html
index 013ae4b..10453b9 100644
--- a/help/html/menus/alwselect.html
+++ b/help/html/menus/alwselect.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -63,10 +63,16 @@
         <strong>WARNING</strong>: This cannot be undone.
     </em></li>
     <li><strong><a
-        href="../features/columnFilterByAnnotation.html"
-      >Select/Hide Columns by Annotation</a></strong> <br /> <em>Select or
-        Hide columns in the alignment according to secondary structure,
-        labels and values shown in alignment annotation rows. </em></li>
+        href="../features/columnFilterByAnnotation.html">Select/Hide
+          Columns by Annotation</a></strong> <br /> <em>Select or Hide columns
+        in the alignment according to secondary structure, labels and
+        values shown in alignment annotation rows. </em></li>
+    <li><strong>Select Highlighted Columns</strong> <br /> <em>Selects
+        the columns currently highlighted as a result of a find, mouse
+        over, or selection event from a linked structure viewer or other
+        application. Modifiers will work on some platforms: ALT will add
+        all but the highlighted set to the column selection, and CTRL
+        (or META) will toggle the selection. </em></li>
   </ul>
 </body>
 </html>
diff --git a/help/html/menus/alwview.html b/help/html/menus/alwview.html
index d18b6f0..f6d0d1c 100644
--- a/help/html/menus/alwview.html
+++ b/help/html/menus/alwview.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/menus/desktopMenu.html b/help/html/menus/desktopMenu.html
index 9c41b7e..ddf68c2 100644
--- a/help/html/menus/desktopMenu.html
+++ b/help/html/menus/desktopMenu.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -43,13 +43,13 @@
           </ul></li>
         <li><strong>Fetch Sequence<br>
         </strong><em>Shows a dialog window in which you can select known ids
-            from Uniprot, EMBL, EMBLCDS or PDB database using Web
+            from UniProt, EMBL, EMBLCDS or PDB database using Web
             Services provided by the European Bioinformatics Institute.</em></li>
         <li><strong>Save Project</strong><br> <em>Saves
             all currently open alignment windows with their current view
             settings and any associated trees, as a <a
-            href="../features/jalarchive.html"
-          >Jalview Archive</a> (which has a .jar extension).
+            href="../features/jalarchive.html">Jalview
+              Archive</a> (which has a .jar extension).
         </em></li>
         <li><strong>Load Project</strong><br> <em>Loads
             Jalview archives <strong>only</strong>.
@@ -82,9 +82,8 @@
             <a href="../webServices/newsreader.html">Jalview News</a>
             dialog box.
         </em></li>
-        <li><strong>Groovy Console...<em> (only
-              available if groovy is on the classpath)</em><br></strong> <em>Open's
-            the <a href="../groovy.html">Groovy Console</a> for
+        <li><strong>Groovy Console...<br></strong> <em>Opens
+            the <a href="../features/groovy.html">Groovy Console</a> for
             interactive scripting.
         </em><strong><br></strong></li>
 
@@ -130,9 +129,9 @@
         window to the top of the pile when it is selected.
         <ul>
           <li><strong>Close All</strong><br> Close all
-            alignment and analysis windows.<br>
-          <strong>Note: This will erase all alignments from
-              memory, and cannot be undone!</strong></li>
+            alignment and analysis windows.<br> <strong>Note:
+              This will erase all alignments from memory, and cannot be
+              undone!</strong></li>
           <li><strong>Raise Associated Windows</strong><br>
             Bring all windows associated with the current alignment to
             the top of the pile.</li>
diff --git a/help/html/menus/index.html b/help/html/menus/index.html
index 4f40e96..e53c7a3 100644
--- a/help/html/menus/index.html
+++ b/help/html/menus/index.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -41,7 +41,10 @@
   <p>
     The <a href="popupMenu.html">Popup Menus</a> are opened by clicking
     with the right mouse button in the alignment display area or on a
-    sequence label in the alignment window.
+    sequence label in the alignment window.<br /> <em><strong>Mac
+        Users:</strong> pressing CTRL whilst clicking the mouse/track pad is the
+      same as a right-click. See your system's settings to configure
+      your track-pad's corners to generate right-clicks.</em>
   </p>
   <p>
     The <a href="alwannotationpanel.html">Annotations Menu</a> is opened
diff --git a/help/html/menus/popupMenu.html b/help/html/menus/popupMenu.html
index 20169c4..ae50b59 100644
--- a/help/html/menus/popupMenu.html
+++ b/help/html/menus/popupMenu.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,7 +28,10 @@
     <strong>Popup Menu</strong><br> <em>This menu is visible
       when right clicking either within a selected region on the
       alignment or on a selected sequence name. It may not be accessible
-      when in 'Cursor Mode' (toggled with the F2 key).</em>
+      when in 'Cursor Mode' (toggled with the F2 key).</em><br /> <em><strong>Mac
+        Users:</strong> pressing CTRL whilst clicking the mouse/track pad is the
+      same as a right-click. See your system's settings to configure
+      your track-pad's corners to generate right-clicks.</em>
   </p>
   <ul>
     <li><strong>Selection</strong>
@@ -36,9 +39,9 @@
         <li><a name="sqreport"><strong>Sequence
               Details...<br>
           </strong></a><em>(Since Jalview 2.8)<br>Open an <a
-            href="../io/exportseqreport.html"
-          >HTML report containing the annotation and database cross
-              references</a> normally shown in the sequence's tooltip.
+            href="../io/exportseqreport.html">HTML report
+              containing the annotation and database cross references</a> normally
+            shown in the sequence's tooltip.
         </em></li>
         <li><strong>Show Annotations...<br>
         </strong><em>Choose to show (unhide) either All or a selected type
@@ -78,10 +81,10 @@
         </strong><em>The selection area will be output to a a text window in
             the selected alignment format. </em></li>
         <li><strong><a
-            href="../features/creatinFeatures.html"
-          >Create Sequence Feature...</a></strong><br> <em>Opens the
-            dialog box for creating sequence features over the currently
-            selected region on each selected sequence.</em></li>
+            href="../features/creatinFeatures.html">Create
+              Sequence Feature...</a></strong><br> <em>Opens the dialog box
+            for creating sequence features over the currently selected
+            region on each selected sequence.</em></li>
         <li><strong>Create Group<br>
         </strong><em>This will define a new group from the current
             selection.</em><strong> </strong></li>
@@ -134,9 +137,9 @@
         <li><a name="sqreport"><strong>Sequence
               Details ...<br>
           </strong></a><em>(Since Jalview 2.8)<br>Open an <a
-            href="../io/exportseqreport.html"
-          >HTML report containing the annotation and database cross
-              references</a> normally shown in the sequence's tooltip.
+            href="../io/exportseqreport.html">HTML report
+              containing the annotation and database cross references</a>
+            normally shown in the sequence's tooltip.
         </em></li>
         <li><strong>Edit Name/Description<br>
         </strong><em>You may edit the name and description of each sequence.
@@ -144,13 +147,14 @@
             and sequence description to be entered. Press OK to accept
             your edit. To save sequence descriptions, you must save in
             Fasta, PIR or Jalview File format.</em></li>
-        <li><a href="sqaddrefannot"><strong>Add
-              Reference Annotations<br>
-          </strong><em>When enabled, copies any available alignment
-              annotation for this sequence to the current view.</em></li>
+        <li><strong>Add <a
+            href="../features/annotation.html#seqannots">Reference
+              Annotations</a></strong><br> <em>When enabled, copies any
+            available alignment annotation for this sequence to the
+            current view.</em></li>
         <li><strong>Set as Reference</strong> or <strong>Unmark
-            as Reference</strong><br /> Sets or unsets the reference sequence for
-          the the alignment.</li>
+            as Reference</strong><br /> Sets or unsets the reference sequence
+          for the the alignment.</li>
 
         <li><strong>Represent Group With (Sequence Id)</strong><br>
           <em>All sequences in the current selection group will be
@@ -163,29 +167,26 @@
               Connections tab.<br> Since Jalview 2.4, links will
               also be made for database cross references (where the
               database name exactly matches the link name set up in <a
-              href="../features/preferences.html"
-            >Preferences</a>). <br>Since Jalview 2.5, links are also
-              shown for non-positional sequence features attached to the
-              sequence, and any regular-expression based URL links that
-              matched the description line.
+              href="../features/preferences.html">Preferences</a>).
+              <br>Since Jalview 2.5, links are also shown for
+              non-positional sequence features attached to the sequence,
+              and any regular-expression based URL links that matched
+              the description line.
           </em><strong><br> </strong></li>
       </ul></li>
     <li><strong>3D Structure Data...</strong> </strong><em>This menu is
         visible when you right-click on a sequence name. When this
-        option is clicked, Jalview will open a <a
-        href="../features/structurechooser.html"
-      >'Structure Chooser' </a> dialogue with options to select the
-        structure which will eventually be opened in a 3D interactive
-        view.<br> These entries will only be present if the
-        sequence has <a href="../features/viewingpdbs.html">associated
-          PDB structures</a>.
+        option is clicked, Jalview will open the <a
+        href="../features/structurechooser.html">'Structure Chooser'
+      </a>, which allows you to discover and view 3D structures for the
+        current selection. For more info, see <a
+        href="../features/viewingpdbs.html">viewing PDB structures</a>.
     </em></li>
-    <li><strong>VARNA 2D Structure</strong><br />
-    <em> If the sequence or alignment has RNA structure, then <strong>VARNA
+    <li><strong>VARNA 2D Structure</strong><br /> <em> If the
+        sequence or alignment has RNA structure, then <strong>VARNA
           2D Structure</strong> entries will also be present enabling you to open
         a linked view of the RNA structure in <a
-        href="../features/varna.html"
-      >VARNA</a>.
+        href="../features/varna.html">VARNA</a>.
     </em></li>
     <li><a name="hideinserts"><strong>Hide Insertions</strong></a><br />
       <em>Hides columns containing gaps in the current sequence or
diff --git a/help/html/menus/wsmenu.html b/help/html/menus/wsmenu.html
index 753f4f6..618720b 100644
--- a/help/html/menus/wsmenu.html
+++ b/help/html/menus/wsmenu.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -55,13 +55,12 @@
     elsewhere. You need a continuous network connection in order to use
     these services through Jalview.</p>
   <ul>
-    <li><strong>Alignment</strong><br />
-    <em> Align the currently selected sequences or all sequences in
-        the alignment, or re-align unaligned sequences to the aligned
-        sequences. Entries in this menu provide access to the various
-        alignment programs supported by <a
-        href="../webServices/JABAWS.html"
-      >JABAWS</a>. See the <a href="../webServices/msaclient.html">Multiple
+    <li><strong>Alignment</strong><br /> <em> Align the
+        currently selected sequences or all sequences in the alignment,
+        or re-align unaligned sequences to the aligned sequences.
+        Entries in this menu provide access to the various alignment
+        programs supported by <a href="../webServices/JABAWS.html">JABAWS</a>.
+        See the <a href="../webServices/msaclient.html">Multiple
           Sequence Alignment webservice client</a> entry for more
         information.
     </em></li>
@@ -96,8 +95,8 @@
         <li><strong>Multi-Harmony</strong><br> <em>Performs
             functional residue analysis on a protein family alignment
             with sub-families defined on it. See the <a
-            href="../webServices/shmr.html"
-          >Multi-Harmony service</a> entry for more information.
+            href="../webServices/shmr.html">Multi-Harmony
+              service</a> entry for more information.
         </em></li>
       </ul></li>
   </ul>
diff --git a/help/html/misc/aaproperties.html b/help/html/misc/aaproperties.html
index 88b85b9..a49296f 100644
--- a/help/html/misc/aaproperties.html
+++ b/help/html/misc/aaproperties.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,29 +20,39 @@
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  -->
 <head>
-<title>Amino Acid Properties</title>
+<style>
+body {
+    font-size: 100%;
+}
+table {
+    border: solid;
+    border-collapse: separate;
+}
+th, td {
+    border-style:none;
+    font-family: "Courier New", Courier, mono;
+    font-size: large;
+}
+</style><title>Amino Acid Properties</title>
 </head>
 <body>
   <div align="center">
     <h1>Amino Acid Properties</h1>
     <img src="properties.gif"> <br>
-    <table width="295" border="1" cellspacing="0" cellpadding="0">
-      <tr>
-        <td width="291"><pre>
-            <font size="4" face="Courier New, Courier, mono"><strong>ILVCAGMFYWHKREQDNSTPBZX-</strong>
-XXXXXXXXXXX·······X···XX Hydrophobic
-········XXXXXXXXXX·XXXXX Polar
-··XXXX·········XXXXX··XX Small
-···················X··XX Proline
-····XX···········X····XX Tiny
-XXX···················XX Aliphatic
-·······XXXX···········XX Aromatic
-··········XXX·········XX Positive
-·············X·X······XX Negative
-··········XXXX·X······XX Charged</font>
-          </pre></td>
-      </tr>
+    <table>
+    <tr><th>ILVCA</th><th>GMFYW</th><th>HKREQ</th><th>DNSTP</th><th>BZX-</th><th></th></tr>
+      <tr><td>XXXXX</td><td>XXXXX</td><td>XX···</td><td>···X·</td><td>··XX</td><td>Hydrophobic</td></tr>
+<tr><td>·····</td><td>···XX</td><td>XXXXX</td><td>XXXX·</td><td>XXXX</td><td>Polar</td></tr>
+<tr><td>··XXX</td><td>X····</td><td>·····</td><td>XXXXX</td><td>··XX</td><td>Small</td></tr>
+<tr><td>·····</td><td>·····</td><td>·····</td><td>····X</td><td>··XX</td><td>Proline</td></tr>
+<tr><td>····X</td><td>X····</td><td>·····</td><td>··X··</td><td>··XX</td><td>Tiny</td></tr>
+<tr><td>XXX··</td><td>·····</td><td>·····</td><td>·····</td><td>··XX</td><td>Aliphatic</td></tr>
+<tr><td>·····</td><td>··XXX</td><td>X····</td><td>·····</td><td>··XX</td><td>Aromatic</td></tr>
+<tr><td>·····</td><td>·····</td><td>XXX··</td><td>·····</td><td>··XX</td><td>Positive</td></tr>
+<tr><td>·····</td><td>·····</td><td>···X·</td><td>X····</td><td>··XX</td><td>Negative</td></tr>
+<tr><td>·····</td><td>·····</td><td>XXXX·</td><td>X····</td><td>··XX</td><td>Charged</td></tr>
     </table>
+    </font>
   </div>
   <p>
     <br> From Livingstone, C. D. and Barton, G. J. (1993), <br>
diff --git a/help/html/misc/aminoAcids.html b/help/html/misc/aminoAcids.html
index c692586..4b688e5 100644
--- a/help/html/misc/aminoAcids.html
+++ b/help/html/misc/aminoAcids.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/misc/geneticCode.html b/help/html/misc/geneticCode.html
index fdcb06e..3fe7d85 100644
--- a/help/html/misc/geneticCode.html
+++ b/help/html/misc/geneticCode.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/help/html/na/index.html b/help/html/na/index.html
index 956307b..e762c98 100644
--- a/help/html/na/index.html
+++ b/help/html/na/index.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -57,17 +57,16 @@ td {
   way:
   <ul>
     <li><em>RFAM</em> - Sequences can be <a
-      href="../features/seqfetch.html"
-    >fetched</a> from the RFAM database by accession number or ID.</li>
+      href="../features/seqfetch.html">fetched</a> from the RFAM
+      database by accession number or ID.</li>
     <li><em>Stockholm files</em> - WUSS (or VIENNA) dot-bracket
       notation found in the secondary structure annotation line will be
       imported as sequence or alignment associated secondary structure
       annotation.</li>
     <li><em>Clustal files</em> - certain RNA alignment programs,
-      such as <a
-      href="http://rna.informatik.uni-freiburg.de:8080/LocARNA.jsp"
-    >LocaRNA</a> output consensus RNA secondary structure lines in the
-      line normally reserved for the Clustal consensus line in a clustal
+      such as <a href="http://rna.informatik.uni-freiburg.de/LocARNA">LocaRNA</a>
+      output consensus RNA secondary structure lines in the line
+      normally reserved for the Clustal consensus line in a clustal
       file.</li>
     <li><em>RNAML</em> Jalview can import RNAML files containing
       sequences and extended secondary structure annotation derived from
@@ -79,7 +78,7 @@ td {
     the alignment will have a secondary structure line shown below it,
     and a number of additional options become available:
   <ul>
-    <li><a href="../colourschemes/rnaHelicesColouring.html">RNA
+    <li><a href="../colourSchemes/rnahelicesColouring.html">RNA
         Helix colouring</a> - highlights columns of alignment involved in
       particular RNA helices, Uses the first displayed secondary
       structure annotation.</li>
@@ -103,9 +102,9 @@ td {
       per-sequence secondary structure is available).</li>
   </ul>
   <p>
-    <strong>Pseudo-knots</strong><br /> Jalview 2.8.2 introduced limited
-    support for working with structures including pseudoknots. Where
-    possible, extended WUSS symbols (e.g. different types of
+    <strong>Pseudo-knots</strong><br /> Jalview 2.8.2 introduced
+    limited support for working with structures including pseudoknots.
+    Where possible, extended WUSS symbols (e.g. different types of
     parentheses, or upper and lower case letters) are preserved when
     parsing RNA structure annotation and will be shaded differently when
     displayed in the structure.<br /> Extended WUSS annotation is also
diff --git a/help/html/privacy.html b/help/html/privacy.html
index b6bd728..1a80dd2 100644
--- a/help/html/privacy.html
+++ b/help/html/privacy.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -38,8 +38,7 @@
     <li><em>HTTP logs on the Jalview website</em><br> We
       record IP addresses of machines which access the web site, either
       via the browser when downloading the application, or when the
-      Jalview Desktop user interface is launched.<br>
-    <br>
+      Jalview Desktop user interface is launched.<br> <br>
       <ul>
         <li><i>The JNLP file at
             www.jalview.org/webstart/jalview.jnlp is retrieved to
@@ -53,8 +52,7 @@
             interactions with the public Jalview web services are
             logged, but we delete all job data (input data and results)
             after about two weeks.</i></li>
-      </ul>
-      <br></li>
+      </ul> <br></li>
     <li><em>Google Analytics</em><br> Since Jalview 2.4.0b2,
       the Jalview Desktop records usage data with Google Analytics via
       the <a href="http://code.google.com/p/jgoogleanalytics/">JGoogleAnalytics</a>
@@ -70,12 +68,11 @@
     run Jalview in 'headless mode' via the command line, then the
     program shouldn't try to contact any of the web servers mentioned
     above (if it does, then it's a bug!). You can also specify some <a
-      href="features/commandline.html"
-    >command line options</a> to disable the questionnaire and usage
-    statistics check. Finally, the <a
-      href="features/preferences.html#connections"
-    >Connections Tab</a> of the Jalview preferences contains options for
-    controlling the submission of usage statistics.
+      href="features/commandline.html">command line options</a> to
+    disable the questionnaire and usage statistics check. Finally, the <a
+      href="features/preferences.html#connections">Connections
+      Tab</a> of the Jalview preferences contains options for controlling
+    the submission of usage statistics.
   <p>
     <strong>Other Web Clients in Jalview</strong><br> The Jalview
     desktop is intended to make it easier to interact with web-based
diff --git a/help/html/releases.html b/help/html/releases.html
index 7fc0939..c90f21f 100644
--- a/help/html/releases.html
+++ b/help/html/releases.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -45,6 +45,792 @@
       </td>
     </tr>
     <tr>
+      <td width="60" nowrap>
+        <div align="center">
+          <strong><a name="Jalview.2.10.1">2.10.1</a><br />
+            <em>29/11/2016</em></strong>
+        </div>
+      </td>
+      <td><div align="left">
+          <em>General</em>
+          <ul>
+            <li>
+              <!-- JAL-98 -->Improved memory usage: sparse arrays used
+              for all consensus calculations
+            </li>
+            <li>
+              <!-- JAL-2177 -->Jmol updated to version 14.6.4 (released 3rd Oct 2016)
+            </li>
+            <li>Updated Jalview's Certum code signing certificate
+              for 2016-2017</li>
+          </ul>
+          <em>Application</em>
+          <ul>
+            <li>
+              <!-- JAL-1723 -->Sequence ID tool tip presents abridged
+              set of database cross-references, sorted alphabetically
+            </li>
+            <li>
+              <!-- JAL-2282-->New replacement token for creating URLs <em>just</em>
+              from database cross references. Users with custom links
+              will receive a <a href="webServices/urllinks.html#warning">warning
+                dialog</a> asking them to update their preferences.
+            </li>
+            <li>
+              <!-- JAL-2287-->Cancel button and escape listener on
+              dialog warning user about disconnecting Jalview from a
+              Chimera session
+            </li>
+            <li>
+              <!-- JAL-2320-->Jalview's Chimera control window closes if
+              the Chimera it is connected to is shut down
+            </li>
+            <li>
+              <!-- JAL-1738-->New keystroke (B) and Select highlighted
+              columns menu item to mark columns containing
+              highlighted regions (e.g. from structure selections or results
+              of a Find operation)
+            </li>
+            <li>
+              <!-- JAL-2284-->Command line option for batch-generation
+              of HTML pages rendering alignment data with the BioJS
+              MSAviewer
+            </li>
+          </ul>
+        </div></td>
+      <td>
+        <div align="left">
+          <em>General</em>
+          <ul>
+            <li>
+              <!-- JAL-2286 -->Columns with more than one modal residue
+              are not coloured or thresholded according to percent
+              identity (first observed in Jalview 2.8.2)
+            </li>
+            <li>
+              <!-- JAL-2301 -->Threonine incorrectly reported as not
+              hydrophobic
+            </li>
+            <li>
+              <!-- JAL-2318 -->Updates to documentation pages (above PID
+              threshold, amino acid properties)
+            </li>
+            <li>
+              <!-- JAL-2292 -->Lower case residues in sequences are not
+              reported as mapped to residues in a structure file in the
+              View Mapping report
+            </li>
+            <li>
+              <!--JAL-2324 -->Identical features with non-numeric scores
+              could be added multiple times to a sequence
+            </li>
+            <li>
+              <!--JAL-2323, JAL-2333,JAL-2335,JAL-2327 -->Disulphide
+              bond features shown as two highlighted residues rather
+              than a range in linked structure views, and treated
+              correctly when selecting and computing trees from features
+            </li>
+            <li>
+              <!-- JAL-2281-->Custom URL links for database
+              cross-references are matched to database name regardless
+              of case
+            </li>
+
+          </ul>
+          <em>Application</em>
+          <ul>
+            <li>
+              <!-- JAL-2282-->Custom URL links for specific database
+              names without regular expressions also offer links from
+              Sequence ID
+            </li>
+            <li>
+              <!-- JAL-2315-->Removing a single configured link in the
+              URL links pane in Connections preferences doesn't actually
+              update Jalview configuration
+            </li>
+            <li>
+              <!-- JAL-2272-->CTRL-Click on a selected region to open
+              the alignment area popup menu doesn't work on El-Capitan
+            </li>
+            <li>
+              <!-- JAL-2280 -->Jalview doesn't offer to associate mmCIF
+              files with similarly named sequences if dropped onto the
+              alignment
+            </li>
+            <li>
+              <!-- JAL-2312 -->Additional mappings are shown for PDB
+              entries where more chains exist in the PDB accession than
+              are reported in the SIFTS file
+            </li>
+            <li>
+              <!-- JAL-2317-->Certain structures do not get mapped to
+              the structure view when displayed with Chimera
+            </li>
+            <li>
+              <!-- JAL-2317-->No chains shown in the Chimera view
+              panel's View->Show Chains submenu
+            </li>
+            <li>
+              <!--JAL-2277 -->Export as HTML with embedded SVG doesn't
+              work for wrapped alignment views
+            </li>
+            <li>
+              <!--JAL-2197 -->Rename UI components for running JPred
+              predictions from 'JNet' to 'JPred'
+            </li>
+            <li>
+              <!-- JAL-2337,JAL-2277 -->Export as PNG or SVG is
+              corrupted when annotation panel vertical scroll is not at
+              first annotation row
+            </li>
+            <li>
+              <!--JAL-2332 -->Attempting to view structure for Hen
+              lysozyme results in a PDB Client error dialog box
+            </li>
+          </ul>
+<!--           <em>New Known Issues</em>
+          <ul>
+            <li></li>
+          </ul> -->
+        </div>
+      </td>
+    </tr>
+      <td width="60" nowrap>
+        <div align="center">
+          <strong><a name="Jalview.2.10.0b1">2.10.0b1</a><br />
+            <em>25/10/2016</em></strong>
+        </div>
+      </td>
+      <td><em>Application</em>
+        <ul>
+          <li>3D Structure chooser opens with 'Cached structures'
+            view if structures already loaded</li>
+          <li>Progress bar reports models as they are loaded to
+            structure views</li>
+        </ul></td>
+      <td>
+        <div align="left">
+          <em>General</em>
+          <ul>
+            <li>Colour by conservation always enabled and no tick
+              shown in menu when BLOSUM or PID shading applied</li>
+            <li>FER1_ARATH and FER2_ARATH labels were switched in
+              example sequences/projects/trees</li>
+          </ul>
+          <em>Application</em>
+          <ul>
+            <li>Jalview projects with views of local PDB structure
+              files saved on Windows cannot be opened on OSX</li>
+            <li>Multiple structure views can be opened and
+              superposed without timeout for structures with multiple
+              models or multiple sequences in alignment</li>
+            <li>Cannot import or associated local PDB files without
+              a PDB ID HEADER line</li>
+            <li>RMSD is not output in Jmol console when
+              superposition is performed</li>
+            <li>Drag and drop of URL from Browser fails for Linux
+              and OSX versions earlier than El Capitan</li>
+            <li>ENA client ignores invalid content from ENA server</li>
+            <li>Exceptions are not raised in console when ENA
+              client attempts to fetch non-existent IDs via Fetch DB
+              Refs UI option</li>
+            <li>Exceptions are not raised in console when a new
+              view is created on the alignment</li>
+            <li>OSX right-click fixed for group selections:
+              CMD-click to insert/remove gaps in groups and CTRL-click
+              to open group pop-up menu</li>
+          </ul>
+          <em>Build and deployment</em>
+          <ul>
+            <li>URL link checker now copes with multi-line anchor
+              tags</li>
+          </ul>
+          <em>New Known Issues</em>
+          <ul>
+            <li>Drag and drop from URL links in browsers do not
+              work on Windows</li>
+          </ul>
+        </div>
+      </td>
+    </tr>
+    <tr>
+      <td width="60" nowrap>
+        <div align="center">
+          <strong><a name="Jalview.2.10.0">2.10.0</a><br /> <em>06/10/2016</em></strong>
+        </div>
+      </td>
+      <td><em>General</em>
+        <ul>
+          <li>
+          <!-- JAL-2124 -->Updated Spanish translations.
+          </li> 
+          <li>
+            <!-- JAL-2164,JAL-1919,JAL-2148 -->Jmol now primary parser
+            for importing structure data to Jalview. Enables mmCIF and
+            better PDB parsing.
+          </li>
+          <li>
+            <!-- JAL-192 --->Alignment ruler shows positions relative to
+            reference sequence
+          </li>
+          <li>
+            <!-- JAL-2202 -->Position/residue shown in status bar when
+            mousing over sequence associated annotation
+          </li>
+          <li>
+            <!-- JAL-2171 -->Default RNA SS symbol to 'matching bracket'
+            for manual entry
+          </li>
+          <li>
+            <!-- JAL-2214 -->RNA Structure consensus indicates wc-only
+            '()', canonical '[]' and invalid '{}' base pair populations
+            for each column
+          </li>
+          <li>
+            <!-- JAL-2092 -->Feature settings popup menu options for
+            showing or hiding columns containing a feature
+          </li>
+          <li>
+            <!-- JAL-1557 -->Edit selected group by double clicking on
+            group and sequence associated annotation labels
+          </li>
+          <li>
+            <!-- JAL-2236 -->Sequence name added to annotation label in
+            select/hide columns by annotation and colour by annotation
+            dialogs
+          </li>
+
+        </ul> <em>Application</em>
+        <ul>
+          <li>
+            <!-- JAL-2050-->Automatically hide introns when opening a
+            gene/transcript view
+          </li>
+          <li>
+            <!-- JAL-1563 -->Uniprot Sequence fetcher Free Text Search
+            dialog
+          </li>
+          <li>
+            <!--  JAL-1957, JAL-1479 JAL-1491 -->UniProt - PDB protein
+            structure mappings with the EMBL-EBI PDBe SIFTS database
+          </li>
+          <li>
+            <!-- JAL-2079 -->Updated download sites used for Rfam and
+            Pfam sources to xfam.org
+          </li>
+          <li>
+            <!-- JAL-2084 -->Disabled Rfam(Full) in the sequence fetcher
+          </li>
+          <li>
+            <!-- JAL-2123 -->Show residue labels in Chimera when mousing
+            over sequences in Jalview
+          </li>
+          <li>
+            <!-- JAL-2027-->Support for reverse-complement coding
+            regions in ENA and EMBL
+          </li>
+          <li>
+            <!-- JAL-1855, JAL-2113, JAL-2114-->Upgrade to EMBL XML 1.2
+            for record retrieval via ENA rest API
+          </li>
+          <li>
+            <!-- JAL-2027 -->Support for ENA CDS records with reverse
+            complement operator
+          </li>
+          <li>
+            <!--  JAL-1812 -->Update to groovy-2.4.6-indy - for faster
+            groovy script execution
+          </li>
+          <li>
+            <!--  JAL-1812 -->New 'execute Groovy script' option in an
+            alignment window's Calculate menu
+          </li>
+          <li>
+            <!--  JAL-1812 -->Allow groovy scripts that call
+            Jalview.getAlignFrames() to run in headless mode
+          </li>
+          <li>
+            <!--  JAL-2068 -->Support for creating new alignment
+            calculation workers from groovy scripts
+          </li>
+          <li>
+            <!-- JAL-1369 --->Store/restore reference sequence in
+            Jalview projects
+          </li>
+          <li>
+            <!-- JAL-1803 -->Chain codes for a sequence's PDB
+            associations are now saved/restored from project
+          </li>
+          <li>
+            <!-- JAL-1993 -->Database selection dialog always shown
+            before sequence fetcher is opened
+          </li>
+          <li>
+            <!-- JAL-2183 -->Double click on an entry in Jalview's
+            database chooser opens a sequence fetcher
+          </li>
+          <li>
+            <!-- JAL-1563 -->Free-text search client for UniProt using
+            the UniProt REST API
+          </li>
+          <li>
+            <!-- JAL-2168 -->-nonews command line parameter to prevent
+            the news reader opening
+          </li>
+          <li>
+            <!-- JAL-2028 -->Displayed columns for PDBe and Uniprot
+            querying stored in preferences
+          </li>
+          <li>
+            <!-- JAL-2091 -->Pagination for displaying PDBe and Uniprot
+            search results
+          </li>
+          <li>
+            <!-- JAL-1977-->Tooltips shown on database chooser
+          </li>
+          <li>
+            <!--  JAL-391 -->Reverse complement function in calculate
+            menu for nucleotide sequences
+          </li>
+          <li>
+            <!-- JAL-2005, JAL-599 -->Alignment sort by feature scores
+            and feature counts preserves alignment ordering (and
+            debugged for complex feature sets).
+          </li>
+          <li>
+            <!-- JAL-2152-->Chimera 1.11.1 minimum requirement for
+            viewing structures with Jalview 2.10
+          </li>
+          <li>
+            <!-- JAL-1705, JAL-1975, JAL-2050,JAL-2041,JAL-2105 -->Retrieve
+            genome, transcript CCDS and gene ids via the Ensembl and
+            Ensembl Genomes REST API
+          </li>
+          <li>
+            <!-- JAL-2049 -->Protein sequence variant annotation
+            computed for 'sequence_variant' annotation on CDS regions
+            (Ensembl)
+          </li>
+          <li>
+            <!-- JAL-2232 -->ENA CDS 'show cross references' for Uniprot
+            sequences
+          </li>
+          <li>
+            <!-- JAL-2213,JAL-1856 -->Improved warning messages when DB
+            Ref Fetcher fails to match, or otherwise updates sequence
+            data from external database records.
+          </li>
+          <li>
+            <!-- JAL-2154 -->Revised Jalview Project format for
+            efficient recovery of sequence coding and alignment
+            annotation relationships.
+          </li>
+        </ul> <!-- <em>Applet</em>
+        <ul>
+          <li>
+            -- JAL---
+          </li>
+        </ul> --></td>
+      <td>
+        <div align="left">
+          <em>General</em>
+          <ul>
+            <li>
+              <!-- JAL-2077 -->reinstate CTRL-click for opening pop-up
+              menu on OSX
+            </li>
+            <li>
+              <!-- JAL-2018-->Export features in Jalview format (again)
+              includes graduated colourschemes
+            </li>
+            <li>
+              <!-- JAL-2172,JAL-1722, JAL-2001-->More responsive when
+              working with big alignments and lots of hidden columns
+            </li>
+            <li>
+              <!-- JAL-2053-->Hidden column markers not always rendered
+              at right of alignment window
+            </li>
+            <li>
+              <!-- JAL-2067 -->Tidied up links in help file table of
+              contents
+            </li>
+            <li>
+              <!-- JAL-2072  -->Feature based tree calculation not shown
+              for DNA alignments
+            </li>
+            <li>
+              <!-- JAL-2075  -->Hidden columns ignored during feature
+              based tree calculation
+            </li>
+            <li>
+              <!-- JAL-2065  -->Alignment view stops updating when show
+              unconserved enabled for group on alignment
+            </li>
+            <li>
+              <!--  JAL-2086  -->Cannot insert gaps into sequence when
+              set as reference
+            </li>
+            <li>
+              <!-- JAL-2146 -->Alignment column in status incorrectly
+              shown as "Sequence position" when mousing over
+              annotation
+            </li>
+            <li>
+              <!--  JAL-2099 -->Incorrect column numbers in ruler when
+              hidden columns present
+            </li>
+            <li>
+              <!--  JAL-1577 -->Colour by RNA Helices not enabled when
+              user created annotation added to alignment
+            </li>
+            <li>
+              <!-- JAL-1841 -->RNA Structure consensus only computed for
+              '()' base pair annotation
+            </li>
+            <li>
+              <!-- JAL-2215, JAL-1841 -->Enabling 'Ignore Gaps' results
+              in zero scores for all base pairs in RNA Structure
+              Consensus
+            </li>
+            <li>
+              <!-- JAL-2174-->Extend selection with columns containing
+              feature not working
+            </li>
+            <li>
+              <!-- JAL-2275 -->Pfam format writer puts extra space at
+              beginning of sequence
+            </li>
+            <li>
+              <!-- JAL-1827 -->Incomplete sequence extracted from pdb
+              entry 3a6s
+            </li>
+            <li>
+              <!-- JAL-2238 -->Cannot create groups on an alignment from
+              from a tree when t-coffee scores are shown
+            </li>
+            <li>
+              <!-- JAL-1836,1967 -->Cannot import and view PDB
+              structures with chains containing negative resnums (4q4h)
+            </li>
+            <li>
+              <!--  JAL-1998 -->ArithmeticExceptions raised when parsing
+              some structures
+            </li>
+            <li>
+              <!--  JAL-1991, JAl-1952 -->'Empty' alignment blocks added
+              to Clustal, PIR and PileUp output
+            </li>
+            <li>
+              <!--  JAL-2008 -->Reordering sequence features that are
+              not visible causes alignment window to repaint
+            </li>
+            <li>
+              <!--  JAL-2006 -->Threshold sliders don't work in
+              graduated colour and colour by annotation row for e-value
+              scores associated with features and annotation rows
+            </li>
+            <li>
+              <!-- JAL-1797 -->amino acid physicochemical conservation
+              calculation should be case independent
+            </li>
+            <li>
+              <!-- JAL-2173 -->Remove annotation also updates hidden
+              columns
+            </li>
+            <li>
+              <!-- JAL-2234 -->FER1_ARATH and FER2_ARATH mislabelled in
+              example file (uniref50.fa, feredoxin.fa, unaligned.fa,
+              exampleFile_2_7.jar, exampleFile.jar, exampleFile_2_3.jar)
+            </li>
+            <li>
+              <!-- JAL-2065 -->Null pointer exceptions and redraw
+              problems when reference sequence defined and 'show
+              non-conserved' enabled
+            </li>
+            <li>
+              <!-- JAL-1306 -->Quality and Conservation are now shown on
+              load even when Consensus calculation is disabled
+            </li>
+            <li>
+              <!-- JAL-1932 -->Remove right on penultimate column of 
+              alignment does nothing
+            </li>
+          </ul>
+          <em>Application</em>
+          <ul>
+            <li>
+              <!-- JAL-1552-->URLs and links can't be imported by
+              drag'n'drop on OSX when launched via webstart (note - not
+              yet fixed for El Capitan)
+            </li>
+            <li>
+              <!-- JAL-1911-->Corrupt preferences for SVG, EPS & HTML
+              output when running on non-gb/us i18n platforms
+            </li>
+            <li>
+              <!-- JAL-1944 -->Error thrown when exporting a view with
+              hidden sequences as flat-file alignment
+            </li>
+            <li>
+              <!-- JAL-2030-->InstallAnywhere distribution fails when
+              launching Chimera
+            </li>
+            <li>
+              <!-- JAL-2080-->Jalview very slow to launch via webstart
+              (also hotfix for 2.9.0b2)
+            </li>
+            <li>
+              <!--  JAL-2085  -->Cannot save project when view has a
+              reference sequence defined
+            </li>
+            <li>
+              <!--  JAL-1011  -->Columns are suddenly selected in other
+              alignments and views when revealing hidden columns
+            </li>
+            <li>
+              <!--  JAL-1989  -->Hide columns not mirrored in complement
+              view in a cDNA/Protein splitframe
+            </li>
+            <li>
+              <!--  JAL-1369 -->Cannot save/restore representative
+              sequence from project when only one sequence is
+              represented
+            </li>
+            <li>
+              <!-- JAL-2002 -->Disabled 'Best Uniprot Coverage' option
+              in Structure Chooser
+            </li>
+            <li>
+              <!-- JAL-2215 -->Modifying 'Ignore Gaps' on consensus or
+              structure consensus didn't refresh annotation panel
+            </li>
+            <li>
+              <!-- JAL-1962 -->View mapping in structure view shows
+              mappings between sequence and all chains in a PDB file
+            </li>
+            <li>
+              <!-- JAL-2102, JAL-2101, JAL-2102, -->PDB and Uniprot FTS
+              dialogs format columns correctly, don't display array
+              data, sort columns according to type
+            </li>
+            <li>
+              <!-- JAL-1975 -->Export complete shown after destination
+              file chooser is cancelled during an image export
+            </li>
+            <li>
+              <!-- JAL-2025 -->Error when querying PDB Service with
+              sequence name containing special characters
+            </li>
+            <li>
+              <!-- JAL-2024 -->Manual PDB structure querying should be
+              case insensitive
+            </li>
+            <li>
+              <!-- JAL-2104 -->Large tooltips with broken HTML
+              formatting don't wrap
+            </li>
+            <li>
+              <!-- JAL-1128 -->Figures exported from wrapped view are
+              truncated so L looks like I in consensus annotation
+            </li>
+            <li>
+              <!-- JAL-2003 -->Export features should only export the
+              currently displayed features for the current selection or
+              view
+            </li>
+            <li>
+              <!-- JAL-2036 -->Enable 'Get Cross-References' in menu
+              after fetching cross-references, and restoring from project
+            </li>
+            <li>
+              <!-- JAL-2032 -->Mouseover of a copy of a sequence is not
+              followed in the structure viewer
+            </li>
+            <li>
+              <!-- JAL-2163 -->Titles for individual alignments in
+              splitframe not restored from project
+            </li>
+            <li>
+              <!-- JAL-2145 -->missing autocalculated annotation at
+              trailing end of protein alignment in transcript/product
+              splitview when pad-gaps not enabled by default
+            </li>
+            <li>
+              <!-- JAL-1797 -->amino acid physicochemical conservation
+              is case dependent
+            </li>
+            <li>
+              <!-- JAL-1448 -->RSS reader doesn't stay hidden after last
+              article has been read (reopened issue due to
+              internationalisation problems)
+            </li>
+            <li>
+              <!-- JAL-1960 -->Only offer PDB structures in structure
+              viewer based on sequence name, PDB and UniProt
+              cross-references
+            </li>
+
+            <li>
+              <!-- JAL-1976 -->No progress bar shown during export of
+              alignment as HTML
+            </li>
+            <li>
+              <!-- JAL-2213 -->Structures not always superimposed after
+              multiple structures are shown for one or more sequences.
+            </li>
+            <li>
+              <!-- JAL-1370 -->Reference sequence characters should not
+              be replaced with '.' when 'Show unconserved' format option
+              is enabled.
+            </li>
+            <li>
+              <!-- JAL-1823 -->Cannot specify chain code when entering
+              specific PDB id for sequence
+            </li>
+            <li>
+              <!-- JAL-1944 -->File->Export->.. as doesn't work when
+              'Export hidden sequences' is enabled, but 'export hidden
+              columns' is disabled.
+            </li>
+            <li>
+              <!--JAL-2026-->Best Quality option in structure chooser
+              selects lowest rather than highest resolution structures
+              for each sequence
+            </li>
+            <li>
+              <!-- JAL-1887 -->Incorrect start and end reported for PDB
+              to sequence mapping in 'View Mappings' report
+            </li>
+            <li>
+              <!-- JAL-2284 -->Unable to read old Jalview projects that
+              contain non-XML data added after Jalvew wrote project.
+            </li>
+            <li><!-- JAL-2118 -->Newly created annotation row reorders
+              after clicking on it to create new annotation for a
+              column.
+            </li>
+            <!--  may exclude, this is an external service stability issue  JAL-1941 
+            -- > RNA 3D structure not added via DSSR service</li> -->
+          </ul>
+          <em>Applet</em>
+          <ul>
+            <li>
+              <!-- JAL-2151 -->Incorrect columns are selected when
+              hidden columns present before start of sequence
+            </li>
+            <li>
+              <!-- JAL-1986 -->Missing dependencies on applet pages
+              (JSON jars)
+            </li>
+            <li>
+              <!-- JAL-1947 -->Overview pixel size changes when
+              sequences are hidden in applet
+            </li>
+            <li>
+              <!-- JAL-1996 -->Updated instructions for applet
+              deployment on examples pages.
+            </li>
+          </ul>
+        </div>
+      </td>
+    </tr>
+    <tr>
+      <td width="60" nowrap>
+        <div align="center">
+          <strong><a name="Jalview.2.9.0b2">2.9.0b2</a><br />
+            <em>16/10/2015</em></strong>
+        </div>
+      </td>
+      <td><em>General</em>
+        <ul>
+          <li>Time stamps for signed Jalview application and applet
+            jars</li>
+        </ul></td>
+      <td>
+        <div align="left">
+          <em>Application</em>
+          <ul>
+            <li>Duplicate group consensus and conservation rows
+              shown when tree is partitioned</li>
+            <li>Erratic behaviour when tree partitions made with
+              multiple cDNA/Protein split views</li>
+          </ul>
+        </div>
+      </td>
+    </tr>
+    <tr>
+      <td width="60" nowrap>
+        <div align="center">
+          <strong><a name="Jalview.2.9.0b1">2.9.0b1</a><br />
+            <em>8/10/2015</em></strong>
+        </div>
+      </td>
+      <td><em>General</em>
+        <ul>
+          <li>Updated Spanish translations of localized text for
+            2.9</li>
+        </ul> <em>Application</em>
+        <ul>
+          <!-- <li>cDNA/Protein splitframe window geometry preserved in Jalview projects</li>-->
+          <li>Signed OSX InstallAnywhere installer<br></li>
+          <li>Support for per-sequence based annotations in BioJSON</li>
+        </ul> <em>Applet</em>
+        <ul>
+          <li>Split frame example added to applet examples page</li>
+        </ul><em>Build and Deployment</em>
+        <ul>
+          <li><!--  JAL-1888 -->New ant target for running Jalview's test suite</li>
+        </ul></td>
+      <td>
+        <div align="left">
+          <em>General</em>
+          <ul>
+            <li>Mapping of cDNA to protein in split frames
+              incorrect when sequence start > 1</li>
+            <li>Broken images in filter column by annotation dialog
+              documentation</li>
+            <li>Feature colours not parsed from features file</li>
+            <li>Exceptions and incomplete link URLs recovered when
+              loading a features file containing HTML tags in feature
+              description</li>
+
+          </ul>
+          <em>Application</em>
+          <ul>
+            <li>Annotations corrupted after BioJS export and
+              reimport</li>
+            <li>Incorrect sequence limits after Fetch DB References
+              with 'trim retrieved sequences'</li>
+            <li>Incorrect warning about deleting all data when
+              deleting selected columns</li>
+            <li>Patch to build system for shipping properly signed
+              JNLP templates for webstart launch</li>
+            <li>EMBL-PDBe fetcher/viewer dialogs do not offer
+              unreleased structures for download or viewing</li>
+            <li>Tab/space/return keystroke operation of EMBL-PDBe
+              fetcher/viewer dialogs works correctly</li>
+            <li>Disabled 'minimise' button on Jalview windows
+              running on OSX to workaround redraw hang bug</li>
+            <li>Split cDNA/Protein view position and geometry not
+              recovered from jalview project</li>
+            <li>Initial enabled/disabled state of annotation menu
+              sorter 'show autocalculated first/last' corresponds to
+              alignment view</li>
+            <li>Restoring of Clustal, RNA Helices and T-Coffee
+              color schemes from BioJSON</li>
+          </ul>
+          <em>Applet</em>
+          <ul>
+            <li>Reorder sequences mirrored in cDNA/Protein split
+              frame</li>
+            <li>Applet with Jmol examples not loading correctly</li>
+          </ul>
+        </div>
+      </td>
+    </tr>
+    <tr>
       <td><div align="center">
           <strong><a name="Jalview.2.9">2.9</a><br /> <em>10/9/2015</em></strong>
         </div></td>
@@ -109,7 +895,7 @@
                 region export in flat file generation</li>
 
               <li>Export alignment views for display with the <a
-                href="http://biojs.io/d/msa">BioJS MSAViewer</a></li>
+                href="http://msa.biojs.net/">BioJS MSAViewer</a></li>
 
               <li>Export scrollable SVG in HTML page</li>
               <li>Optional embedding of BioJSON data when exporting
@@ -280,27 +1066,6 @@
       </td>
     </tr>
     <tr>
-      <td width="60" nowrap>
-        <div align="center">
-          <strong><a name="Jalview.2.8.2b1">2.8.2b1</a><br />
-            <em>15/12/2014</em></strong>
-        </div>
-      </td>
-      <td>
-        <div align="center"></div>
-      </td>
-      <td>
-        <div align="center">
-          <ul>
-            <li>Reinstated the display of default example file on
-              startup</li>
-            <li>All pairs shown in Jalview window when viewing
-              result of pairwise alignment</li>
-          </ul>
-        </div>
-      </td>
-    </tr>
-    <tr>
       <td><div align="center">
           <strong><a name="Jalview.2.8.2">2.8.2</a><br /> <em>3/12/2014</em></strong>
         </div></td>
@@ -606,7 +1371,7 @@
     <tr>
       <td><div align="center">
           <strong><a name="Jalview.2.8.0b1">2.8.0b1</a><br />
-          <em>30/1/2014</em></strong>
+            <em>30/1/2014</em></strong>
         </div></td>
       <td>
         <ul>
@@ -615,7 +1380,7 @@
             <a href="https://www.certum.eu">Certum</a> to the Jalview
             open source project).
           </li>
-          <li>Jalview SRS links replaced by Uniprot and EBI-search
+          <li>Jalview SRS links replaced by UniProt and EBI-search
           </li>
           <li>Output in Stockholm format</li>
           <li>Allow import of data from gzipped files</li>
@@ -970,8 +1735,8 @@
             current built in colourscheme is saved as new scheme</li>
           <li>AlignFrame->Save in application pops up save
             dialog for valid filename/format</li>
-          <li>Cannot view associated structure for Uniprot sequence</li>
-          <li>PDB file association breaks for Uniprot sequence
+          <li>Cannot view associated structure for UniProt sequence</li>
+          <li>PDB file association breaks for UniProt sequence
             P37173</li>
           <li>Associate PDB from file dialog does not tell you
             which sequence is to be associated with the file</li>
@@ -1245,6 +2010,11 @@
         <ul>
           <li>URL links generated from description line for
             regular-expression based URL links (applet and application)
+
+
+
+
+
           
           <li>Non-positional feature URL links are shown in link
             menu</li>
@@ -1288,10 +2058,8 @@
             between different screens.</li>
           <li>New preference items for sequence ID tooltip and
             consensus annotation</li>
-          <li>Client to submit sequences and IDs to <a
-            href="webServices/index.html#envision2">Envision2</a>
-            Workflows
-          </li>
+          <li>Client to submit sequences and IDs to Envision2
+            Workflows</li>
           <li><em>Vamsas Capabilities</em>
             <ul>
               <li>Improved VAMSAS synchronization (Jalview archive
@@ -1548,7 +2316,7 @@
           <li>Save works when Jalview project is default format</li>
           <li>Save as dialog opened if current alignment format is
             not a valid output format</li>
-          <li>Uniprot canonical names introduced for both das and
+          <li>UniProt canonical names introduced for both das and
             vamsas</li>
           <li>Histidine should be midblue (not pink!) in Zappo</li>
           <li>error messages passed up and output when data read
@@ -1577,7 +2345,7 @@
             due to null pointer exceptions</li>
           <li>Secondary structure lines are drawn starting from
             first column of alignment</li>
-          <li>Uniprot XML import updated for new schema release in
+          <li>UniProt XML import updated for new schema release in
             July 2008</li>
           <li>Sequence feature to sequence ID match for Features
             file is case-insensitive</li>
@@ -1699,6 +2467,11 @@
           <li>Cancel button for DAS Feature Fetching
           <li>PCA and PDB Viewers zoom via mouse roller
           <li>User-defined sub-tree colours and sub-tree selection
+
+
+
+
+
           
           <li>'New Window' button on the 'Output to Text box'
         </ul>
@@ -1713,17 +2486,27 @@
           <li>Fixed Remove Empty Columns Bug (empty columns at end
             of alignment)
           <li>Slowed DAS Feature Fetching for increased robustness.
+
+
+
+
+
           
           <li>Made angle brackets in ASCII feature descriptions
             display correctly
           <li>Re-instated Zoom function for PCA
           <li>Sequence descriptions conserved in web service
             analysis results
-          <li>Uniprot ID discoverer uses any word separated by
+          <li>UniProt ID discoverer uses any word separated by
             ∣
           <li>WsDbFetch query/result association resolved
           <li>Tree leaf to sequence mapping improved
           <li>Smooth fonts switch moved to FontChooser dialog box.
+
+
+
+
+
           
         </ul>
       </td>
diff --git a/help/html/vamsas/index.html b/help/html/vamsas/index.html
index 87b6102..7945910 100644
--- a/help/html/vamsas/index.html
+++ b/help/html/vamsas/index.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -33,15 +33,14 @@
     and <strong>A</strong>nalysis of <strong>Molecular</strong> <strong>S</strong>equences,
     <strong>Alignements</strong> and <strong>S</strong>tructures).
     Currently, the only other VAMSAS enabled application is <a
-      href="http://www.topali.org"
-    >TOPALi</a> - a user friendly program for phylogenetics and
-    evolutionary analysis.
+      href="http://www.topali.org">TOPALi</a> - a user friendly
+    program for phylogenetics and evolutionary analysis.
   <p>
     VAMSAS enabled applications access a shared bioinformatics dataset
     containing sequences, alignments, annotation and trees, which can be
     represented by an XML document analogous to a <a
-      href="../features/jalarchive.html"
-    >Jalview Project Archive</a>.
+      href="../features/jalarchive.html">Jalview Project
+      Archive</a>.
   </p>
   <br>
   <strong>Connecting to a VAMSAS session</strong>
diff --git a/help/html/webServices/AACon.html b/help/html/webServices/AACon.html
index 07473f2..9f69caa 100644
--- a/help/html/webServices/AACon.html
+++ b/help/html/webServices/AACon.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -30,13 +30,13 @@
     The majority of these scores were described by Valdar in 2002
     (Scoring residue conservation. <em>Proteins: Structure,
       Function, and Genetics</em> 43(2): 227-241. <a
-      href="http://www.ncbi.nlm.nih.gov/pubmed/12112692"
-    >PubMed</a> or available on the <a
-      href="http://www.well.ox.ac.uk/~valdar/publications.html"
-    >Valdar Group publications page</a>), but the SMERFs score was
-    developed later and described by Manning et al. in 2008 (<a
-      href="http://www.biomedcentral.com/1471-2105/9/51"
-    >BMC Bioinformatics 2008, 9:51 doi:10.1186/1471-2105-9-51</a>).
+      href="http://www.ncbi.nlm.nih.gov/pubmed/12112692">PubMed</a>
+    or available on the <a
+      href="http://valdarlab.unc.edu/publications.html">Valdar
+      Group publications page</a>), but the SMERFs score was developed later
+    and described by Manning et al. in 2008 (<a
+      href="http://www.biomedcentral.com/1471-2105/9/51">BMC
+      Bioinformatics 2008, 9:51 doi:10.1186/1471-2105-9-51</a>).
   </p>
   <p>
     <strong>Enabling and disabling AACon calculations</strong><br />
@@ -49,14 +49,13 @@
     <strong>Configuring which AACon calculations are performed</strong><br />
     The <strong>Web Services→Conservation→Change
       AACon Settings ...</strong> menu entry will open a <a
-      href="webServicesParams.html"
-    >web services parameter dialog</a> for the currently configured AACon
-    server. Standard presets are provided for quick and more expensive
-    conservation calculations, and parameters are also provided to
-    change the way that SMERFS calculations are performed.<br /> <em>AACon
-      settings for an alignment are saved in <a
-      href="../features/jalarchive.html"
-    >Jalview projects</a> along with the latest calculation results.
+      href="webServicesParams.html">web services parameter
+      dialog</a> for the currently configured AACon server. Standard presets
+    are provided for quick and more expensive conservation calculations,
+    and parameters are also provided to change the way that SMERFS
+    calculations are performed.<br /> <em>AACon settings for an
+      alignment are saved in <a href="../features/jalarchive.html">Jalview
+        projects</a> along with the latest calculation results.
     </em>
   </p>
   <p>
diff --git a/help/html/webServices/JABAWS.html b/help/html/webServices/JABAWS.html
index f12b081..bec41cc 100644
--- a/help/html/webServices/JABAWS.html
+++ b/help/html/webServices/JABAWS.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -42,8 +42,7 @@
     the <a href="webServicesPrefs.html">Web Services Preferences
       Panel</a>, and detailed information about a particular service is
     available from the help text and web pages accessible from its <a
-      href="webServicesParams.html"
-    >job parameters dialog box</a>.
+      href="webServicesParams.html">job parameters dialog box</a>.
   </p>
   <p>
     <strong>Obtaining JABAWS</strong><br> One of the aims of JABAWS
@@ -53,21 +52,21 @@
     stand-alone execution of analysis programs, or as a job submission
     engine - enabling larger numbers of jobs to be handled. If you would
     like to download and install JABAWS for your own use, please go to <a
-      href="http://www.compbio.dundee.ac.uk/jabaws"
-    >http://www.compbio.dundee.ac.uk/jabaws</a> for more information.
+      href="http://www.compbio.dundee.ac.uk/jabaws">http://www.compbio.dundee.ac.uk/jabaws</a>
+    for more information.
   </p>
   <p>
     <strong>Configuring your own JABAWS services for use by
       Jalview</strong><br> Once you have downloaded and installed JABAWS,
     and verified it is working, all that is needed is to add the URL for
     your JABAWS server(s) to the list in the <a
-      href="webServicesPrefs.html"
-    >Web Services Preferences Panel</a>. After adding your service and
-    saving your preferences or hitting the 'refresh web services'
-    button, you should be able to submit jobs to the server via the
-    alignment window's web services menu. Your JABAWS servers list is
-    stored in your Jalview preferences, so you will only have to
-    configure Jalview once for each new server.
+      href="webServicesPrefs.html">Web Services Preferences
+      Panel</a>. After adding your service and saving your preferences or
+    hitting the 'refresh web services' button, you should be able to
+    submit jobs to the server via the alignment window's web services
+    menu. Your JABAWS servers list is stored in your Jalview
+    preferences, so you will only have to configure Jalview once for
+    each new server.
   </p>
   <p>
     <em>Support for accessing JABAWS servers was introduced in
diff --git a/help/html/webServices/RNAalifold.html b/help/html/webServices/RNAalifold.html
index 60e27cf..a2b9108 100644
--- a/help/html/webServices/RNAalifold.html
+++ b/help/html/webServices/RNAalifold.html
@@ -1,6 +1,6 @@
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -36,8 +36,7 @@
     Gruber, and Peter F. Stadler, <em>RNAalifold: Improved
       consensus structure prediction for RNA alignments</em> (BMC
     Bioinformatics, 9:474, 2008). Download the paper at <a
-      href="http://www.biomedcentral.com/1471-2105/9/474"
-    >http://www.biomedcentral.com/1471-2105/9/474</a>.
+      href="http://www.biomedcentral.com/1471-2105/9/474">http://www.biomedcentral.com/1471-2105/9/474</a>.
   </p>
   <p>
     <strong>Running RNAalifold from Jalview</strong><br />
@@ -54,8 +53,7 @@
     <Strong>RNAalifold prediction parameters</Strong> <br /> JABAWS and
     Jalview only provide access to a selection of the RNAalifold
     arguments. For a full description, see the documentation at <a
-      href="http://www.tbi.univie.ac.at/RNA/RNAalifold.html"
-    >http://www.tbi.univie.ac.at/RNA/RNAalifold.html</a>.
+      href="http://www.tbi.univie.ac.at/RNA/RNAalifold.html">http://www.tbi.univie.ac.at/RNA/RNAalifold.html</a>.
   </p>
   <p>
     <strong>Supported Arguments which give alternate structures</strong>
@@ -75,8 +73,7 @@
     Calculate an MEA structure where the expected Accuracy is computed
     from the base pair probabilities. A more detailed description can be
     found in the <strong>RNAfold</strong> program documentation at <a
-      href="http://www.tbi.univie.ac.at/RNA/RNAfold.html"
-    >http://www.tbi.univie.ac.at/RNA/RNAfold.html</a>.
+      href="http://www.tbi.univie.ac.at/RNA/RNAfold.html">http://www.tbi.univie.ac.at/RNA/RNAfold.html</a>.
   </p>
   <p>
     <strong>Example RNAalifold Structure Annotation rows</strong>
diff --git a/help/html/webServices/dbreffetcher.html b/help/html/webServices/dbreffetcher.html
index fdf48a1..2a957cc 100644
--- a/help/html/webServices/dbreffetcher.html
+++ b/help/html/webServices/dbreffetcher.html
@@ -1,6 +1,6 @@
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -25,13 +25,15 @@
 <p>
 <p>
   <strong>Discovering Database References for Sequences</strong><br>
-  Database references are associated with a sequence are displayed as a
-  list in the tooltip shown when mousing over its sequence ID. Jalview
-  uses references for the retrieval of <a
-    href="../features/viewingpdbs.html"
-  >PDB structures</a> and <a href="../features/dasfeatures.html">DAS
-    features</a>, and for retrieving sequence cross-references such as the
-  protein products of a DNA sequence.
+  Database references associated with a sequence are displayed as an
+  abbreviated list in the tooltip shown when mousing over its sequence
+  ID, and can be viewed in full via the
+  <a href="../io/exportseqreport.html">Sequence Details</a> window. .
+  Jalview also uses references for the retrieval of
+  <a href="../features/viewingpdbs.html">PDB structures</a> and <a
+    href="../features/dasfeatures.html">DAS features</a>, and for
+  retrieving sequence cross-references such as the protein products of a
+  DNA sequence.
 </p>
 <p>
   <strong>Initiating reference retrieval</strong><br> The
@@ -64,13 +66,13 @@
 <p>
   <strong>The Sequence Identification Process</strong><br> The
   method of accession id discovery is derived from the method which
-  earlier Jalview versions used for Uniprot sequence feature retrieval,
-  and was originally restricted to the identification of valid Uniprot
+  earlier Jalview versions used for UniProt sequence feature retrieval,
+  and was originally restricted to the identification of valid UniProt
   accessions.<br> Essentially, Jalview will try to retrieve records
   from a subset of the databases accessible by the <a
-    href="../features/seqfetch.html"
-  >sequence fetcher</a> using each sequence's ID string (or each string in
-  the ID separated by the '∣' symbol).
+    href="../features/seqfetch.html">sequence fetcher</a> using each
+  sequence's ID string (or each string in the ID separated by the
+  '∣' symbol).
 </p>
 <p>If a record (or set of records) is retrieved by any query derived
   from the ID string of a sequence, then the sequence is aligned to the
diff --git a/help/html/webServices/index.html b/help/html/webServices/index.html
index fd2584a..beecc6e 100644
--- a/help/html/webServices/index.html
+++ b/help/html/webServices/index.html
@@ -1,6 +1,6 @@
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -49,15 +49,14 @@
       <ul>
         <li>Programs for <a href="msaclient.html">multiple
             sequence alignment</a>, made available <em>via</em> <a
-          href="JABAWS.html"
-        >Java Bioinformatic Analysis Web Service (JABAWS)</a> servers.
+          href="JABAWS.html">Java Bioinformatic Analysis
+            Web Service (JABAWS)</a> servers.
         </li>
         <li>Jalview SOAP Web Services for <a href="jnet.html">secondary
             structure prediction</a> based at the University of Dundee.
         </li>
         <li>Services for alignment analysis, such as <a
-          href="shmr.html"
-        >Multi-Harmony</a>.
+          href="shmr.html">Multi-Harmony</a>.
       </ul>
       <p>
         <strong>Web Service Dialog Box</strong>
@@ -67,10 +66,9 @@
         citation information, and monitors the progress of the
         calculation. The cancel button will permanently cancel the job,
         but this is only possible for some services.</p> The <a
-      href="webServicesPrefs.html"
-    >Web Services Preference panel</a> controls the display and appearance
-      of the submission and analysis services in the <strong>Web
-        Services</strong> menu.
+      href="webServicesPrefs.html">Web Services Preference
+        panel</a> controls the display and appearance of the submission and
+      analysis services in the <strong>Web Services</strong> menu.
     </li>
     <li>If Jalview encounters problems accessing any services, it
       may display a <a href="webServicesPrefs.html#wswarnings">warning
@@ -82,17 +80,14 @@
   <p>
     <strong>More about Jalview's Web Services</strong> <br>
     Jalview's distributed computations utilise <a
-      href="http://en.wikipedia.org/wiki/SOAP"
-    >SOAP</a> and <a
-      href="http://en.wikipedia.org/wiki/Representational_State_Transfer"
-    >REST</a> web services exposing sequence alignment, analysis, and
-    secondary structure prediction programs. Originally, Jalview 2's
-    services were maintained by the Barton group at the University of
-    Dundee, and ran programs on the Life Sciences High-performace
-    Computing Cluster. With the advent of <a
-      href="http://www.compbio.dundee.ac.uk/JABAWS"
-    >JABAWS</a>, however, it is possible for anyone to host Jalview web
-    services.
+      href="http://en.wikipedia.org/wiki/SOAP">SOAP</a> and <a
+      href="http://en.wikipedia.org/wiki/Representational_State_Transfer">REST</a>
+    web services exposing sequence alignment, analysis, and secondary
+    structure prediction programs. Originally, Jalview 2's services were
+    maintained by the Barton group at the University of Dundee, and ran
+    programs on the Life Sciences High-performance Computing Cluster.
+    With the advent of <a href="http://www.compbio.dundee.ac.uk/jabaws">JABAWS</a>,
+    however, it is possible for anyone to host Jalview web services.
   </p>
 </body>
 </html>
diff --git a/help/html/webServices/jnet.html b/help/html/webServices/jnet.html
index a2fdeb1..cb50156 100644
--- a/help/html/webServices/jnet.html
+++ b/help/html/webServices/jnet.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -38,8 +38,7 @@
       JPred4: a protein secondary structure prediction server<br /> <em>Nucleic
         Acids Research</em>, <strong>Web Server issue</strong> (first
       published 15th April 2015)<br /> <a
-      href="http://dx.doi.org/10.1093/nar/gkv332"
-    >http://dx.doi.org/10.1093/nar/gkv332</a>
+      href="http://dx.doi.org/10.1093/nar/gkv332">http://dx.doi.org/10.1093/nar/gkv332</a>
     </li>
     <li>Cole C., Barber J.D. and Barton G.J. (2008) The Jpred 3
       secondary structure prediction server <em>Nucleic Acids
@@ -127,8 +126,8 @@
   </p>
   <em>JNet annotation created in Jalview 2.8.2 and later versions
     can be displayed on other alignments via the <a
-    href="../features/annotation.html#seqannots"
-  >Add reference annotation</a> Sequence ID popup menu option.
+    href="../features/annotation.html#seqannots">Add reference
+      annotation</a> Sequence ID popup menu option.
   </em>
   <em>As of Jalview 2.6, the Jnet service accessed accessed via the
     'Secondary structure prediction' submenu should be considered a
diff --git a/help/html/webServices/msaclient.html b/help/html/webServices/msaclient.html
index 24851d9..c105dd1 100644
--- a/help/html/webServices/msaclient.html
+++ b/help/html/webServices/msaclient.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -39,13 +39,13 @@
       from the input</li>
     <li><em>realignment</em> - where any aligned sequences will be
       used by the service to construct a profile based alignment of the
-      remaining unaligned sequences.</li>
+      remaining unaligned sequences</li>
   </ul>
   <strong>JABAWS Alignment services</strong>
   <br> Most alignment services are provided by the
   <a href="JABAWS.html">JABAWS framework</a>, which allows you to
   customise the precise parameters used when running each alignment
-  prgoram. In addition to the 'Default settings', you may choose from a
+  program. In addition to the 'Default settings', you may choose from a
   range of alignment preset settings, or create your own using the
   <a href="webServicesParams.html">'Edit Settings And Run ..' dialog
     box</a>.
@@ -58,13 +58,13 @@
   <ul>
     <li><a href="http://www.clustal.org/">Clustal Omega and
         Clustal W</a> (version 2.0.12)</li>
-    <li><a href="http://align.bmr.kyushu-u.ac.jp/mafft/software/">Mafft</a>
+    <li><a href="http://mafft.cbrc.jp/alignment/software/">Mafft</a>
       (version 6.8.57b)</li>
     <li><a href="http://www.drive5.com/muscle">Muscle</a> (version
       3.8.31)</li>
     <li><a
-      href="http://www.tcoffee.org/Projects_home_page/t_coffee_home_page.html"
-    >Tcoffee</a> (version 8.99)</li>
+      href="http://www.tcoffee.org/Projects_home_page/t_coffee_home_page.html">Tcoffee</a>
+      (version 8.99)</li>
     <li><a href="http://probcons.stanford.edu/">Probcons</a>
       (version 1.12)</li>
   </ul>
@@ -74,12 +74,11 @@
     <strong>Multiple Alignments of Sequences with hidden
       columns</strong><br> Multiple alignment services are 'column
     separable' analysis operations. If the input contains <a
-      href="../features/hiddenRegions.html"
-    >hidden columns</a> then each visible segment of the input sequence
-    set will be submitted for alignment separately, and the results
-    concatenated (with the hidden regions preserved) once all alignment
-    functions have completed. Each sub-job's state is reported in its
-    own tab:
+      href="../features/hiddenRegions.html">hidden columns</a> then
+    each visible segment of the input sequence set will be submitted for
+    alignment separately, and the results concatenated (with the hidden
+    regions preserved) once all alignment functions have completed. Each
+    sub-job's state is reported in its own tab:
   <p>
   <center>
     <strong>Multiple Multiple Sequence Alignment sub jobs
diff --git a/help/html/webServices/newsreader.html b/help/html/webServices/newsreader.html
index 44bddea..ee68aac 100644
--- a/help/html/webServices/newsreader.html
+++ b/help/html/webServices/newsreader.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -19,32 +19,43 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  -->
-<head>Jalview Desktop RSS News Reader
+<head>
 </head>
 <body>
   <p>
     <strong>The Jalview Desktop RSS News Reader</strong><br /> The
     Jalview Desktop includes a built in news reader for the <a
-      href="http://www.jalview.org/feeds/desktop/rss"
-    >Jalview Desktop News Channel</a>.
+      href="http://www.jalview.org/feeds/desktop/rss">Jalview
+      Desktop News Channel</a>.
   </p>
 
   <p>We will use the desktop news channel to keep you informed of
     important updates relevant to users of the Jalview desktop, such as
     web service outages and user community events.</p>
-  <p>The news reader will be launched automatically when you start
-    the Desktop if new items are available. Should you want to browse
-    older items, however, you can open it manually from the 'Jalview
-    news reader' option in the Desktop's 'Tools' menu.</p>
-  <img src="jalviewrssreader.gif" align="center" width="513"
-    height="337" alt="Snapshot of the Jalview Desktop's RSS reader"
-  />
+  <p>
+    The news reader will be launched automatically when you start the
+    Desktop if new items are available. Should you want to browse older
+    items, however, you can open it manually from the 'Jalview news
+    reader' option in the Desktop's <a href="../menus/desktopMenu.html">'Tools'
+      menu</a>.
+  </p>
+  <br />
+  <div style="text-align: center;">
+    <img src="jalviewrssreader.gif" width="513" height="337"
+      alt="Snapshot of the Jalview Desktop's RSS reader" />
+  </div>
+
+  <br />
   <p>
     The <em>Jalview news reader</em> was introduced in <a
-      href="http://www.jalview.org/releaseHistory.html#Jalview2.7"
-    >Jalview version 2.7</a>. Its implementation is based on <a
-      href="http://jswingreader.sourceforge.net/"
-    >JSwingReader</a>.
+      href="http://www.jalview.org/releaseHistory.html#Jalview2.7">Jalview
+      version 2.7</a>. Its implementation is based on <a
+      href="http://jswingreader.sourceforge.net/">JSwingReader</a>.
   </p>
+  <br />
+  <em>If you need to prevent the news-reader opening, then add the
+    <a href="../features/clarguments.html">command-line parameter</a>
+    '-nonews' to Jalview's command-line launch.
+  </em>
 </body>
 </html>
diff --git a/help/html/webServices/proteinDisorder.html b/help/html/webServices/proteinDisorder.html
index 8841b5e..4c685ae 100644
--- a/help/html/webServices/proteinDisorder.html
+++ b/help/html/webServices/proteinDisorder.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,26 +28,25 @@
     The <strong>Web Services→Disorder</strong> menu in the
     alignment window allows access to protein disorder prediction
     services provided by the configured <a
-      href="http://www.compbio.dundee.ac.uk/jabaws"
-    >JABAWS servers</a>. Each service operates on sequences in the
-    alignment or currently selected region (<em>since Jalview
-      2.8.0b1</em>) to identify regions likely to be unstructured or
-    flexible, or alternately, fold to form globular domains.
+      href="http://www.compbio.dundee.ac.uk/jabaws">JABAWS
+      servers</a>. Each service operates on sequences in the alignment or
+    currently selected region (<em>since Jalview 2.8.0b1</em>) to
+    identify regions likely to be unstructured or flexible, or
+    alternately, fold to form globular domains.
   </p>
   <p>
     Predictor results include both <a
-      href="../features/seqfeatures.html"
-    >sequence features</a> and sequence associated <a
-      href="../features/annotation.html"
-    >alignment annotation</a> rows. Features display is controlled from
-    the <a href="../features/featureSettings.html">Feature Settings</a>
+      href="../features/seqfeatures.html">sequence features</a> and
+    sequence associated <a href="../features/annotation.html">alignment
+      annotation</a> rows. Features display is controlled from the <a
+      href="../features/featuresettings.html">Feature Settings</a>
     dialog box. Clicking on the ID for a disorder prediction annotation
     row will highlight or select (if double clicked) the associated
     sequence for that row. You can also use the <em>Sequence
       Associated</em> option in the <a
-      href="../colourSchemes/annotationColouring.html"
-    >Colour By Annotation</a> dialog box to colour sequences according to
-    the results of predictors shown as annotation rows.
+      href="../colourSchemes/annotationColouring.html">Colour
+      By Annotation</a> dialog box to colour sequences according to the
+    results of predictors shown as annotation rows.
   </p>
   <p>JABAWS 2.0 provides four disorder predictors which are
     described below:</p>
@@ -74,10 +73,10 @@
       <td>Sequence Feature &<br />Annotation Row
       </td>
       <td>Predicts loops/coils according to DSSP definition<a
-        href="#dsspstates"
-      >[1]</a>.<br />Features mark range(s) of residues predicted as
-        loops/coils, and annotation row gives raw value for each
-        residue. Value over 0.516 indicates loop/coil.
+        href="#dsspstates">[1]</a>.<br />Features mark range(s)
+        of residues predicted as loops/coils, and annotation row gives
+        raw value for each residue. Value over 0.516 indicates
+        loop/coil.
       </td>
     </tr>
     <tr>
@@ -117,13 +116,13 @@
 
   <p>
     <strong><a name="ronn"></a><a
-      href="http://www.strubi.ox.ac.uk/RONN"
-    >RONN</a></strong> <em>a.k.a.</em> Regional Order Neural Network<br />This
-    predictor employs an approach known as the 'bio-basis' method to
-    predict regions of disorder in sequences based on their local
-    similarity with a gold-standard set of disordered protein sequences.
-    It yields a set of disorder prediction scores, which are shown as
-    sequence annotation below the alignment.
+      href="http://www.strubi.ox.ac.uk/RONN">RONN</a></strong> <em>a.k.a.</em>
+    Regional Order Neural Network<br />This predictor employs an
+    approach known as the 'bio-basis' method to predict regions of
+    disorder in sequences based on their local similarity with a
+    gold-standard set of disordered protein sequences. It yields a set
+    of disorder prediction scores, which are shown as sequence
+    annotation below the alignment.
   </p>
   <table border="1">
     <tr>
@@ -147,14 +146,13 @@
   </p>
   <p>
     <strong><a name="iupred"></a><a
-      href="http://iupred.enzim.hu/Help.php"
-    >IUPred</a></strong><br /> IUPred employs an empirical model to estimate
-    likely regions of disorder. There are three different prediction
-    types offered, each using different parameters optimized for
-    slightly different applications. It provides raw scores based on two
-    models for predicting regions of 'long disorder' and 'short
-    disorder'. A third predictor identifies regions likely to form
-    structured domains.
+      href="http://iupred.enzim.hu/Help.php">IUPred</a></strong><br />
+    IUPred employs an empirical model to estimate likely regions of
+    disorder. There are three different prediction types offered, each
+    using different parameters optimized for slightly different
+    applications. It provides raw scores based on two models for
+    predicting regions of 'long disorder' and 'short disorder'. A third
+    predictor identifies regions likely to form structured domains.
   </p>
   <table border="1">
     <tr>
@@ -196,17 +194,16 @@
   </table>
   <p>
     <strong><a name="globplot"></a><a
-      href="http://globplot.embl.de/"
-    >GLOBPLOT</a></strong><br /> Defines regions of globularity or natively
-    unstructured regions based on a running sum of the propensity of
-    residues to be structured or unstructured. The propensity is
-    calculated based on the probability of each amino acid being
-    observed within well defined regions of secondary structure or
-    within regions of random coil. The initial signal is smoothed with a
-    Savitzky-Golay filter, and its first order derivative computed.
-    Residues for which the first order derivative is positive are
-    designated as natively unstructured, whereas those with negative
-    values are structured.<br />
+      href="http://globplot.embl.de/">GLOBPLOT</a></strong><br /> Defines
+    regions of globularity or natively unstructured regions based on a
+    running sum of the propensity of residues to be structured or
+    unstructured. The propensity is calculated based on the probability
+    of each amino acid being observed within well defined regions of
+    secondary structure or within regions of random coil. The initial
+    signal is smoothed with a Savitzky-Golay filter, and its first order
+    derivative computed. Residues for which the first order derivative
+    is positive are designated as natively unstructured, whereas those
+    with negative values are structured.<br />
   <table border="1">
     <tr>
       <td><strong>Name</strong></td>
diff --git a/help/html/webServices/shmr.html b/help/html/webServices/shmr.html
index a19d78e..533b342 100644
--- a/help/html/webServices/shmr.html
+++ b/help/html/webServices/shmr.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -38,31 +38,29 @@
     a protein sequence multiple alignment that has been sub-divided into
     groups containing at least two non-identical protein sequences. An
     easy way to create groups is to use the built-in <a
-      href="../calculations/tree.html"
-    >neighbour-joining or UPGMA tree</a> routines to calculate a tree for
-    the alignment, and then click on the tree to subdivide the
-    alignment.
+      href="../calculations/tree.html">neighbour-joining or
+      UPGMA tree</a> routines to calculate a tree for the alignment, and
+    then click on the tree to subdivide the alignment.
   </p>
   <p>
     The SHMR service operates on the currently selected visible
     region(s) of the alignment. Once submitted, a job progress window
     will display status information about your job, including a URL
     which allows you to visit the status page on the <a
-      href="http://zeus.few.vu.nl/programs/shmrwww/"
-    >IBIVU SHMR server</a>.
+      href="http://zeus.few.vu.nl/programs/shmrwww/">IBIVU SHMR
+      server</a>.
   </p>
   <p>When the job is complete, Jalview will automatically open a new
     window containing the alignment and groups that were submitted for
     analysis, with additional histograms added portraying the SHMR
     scores for each column of the sub-grouped alignment.</p>
   <p>
-    If you use this service in your work, please cite :<br />
+    If you use this service in your work, please cite :<br /> 
     <a name="shmrref" /> Brandt, B.W.*, Feenstra, K.A*. and Heringa, J.
     (2010) Multi-Harmony: detecting functional specificity from sequence
     alignment. <a
-      href="http://nar.oxfordjournals.org/cgi/content/abstract/gkq415"
-    >Nucleic Acids Res. 38: W35-W40.</a> (<em>* joint first authors</em>)
-  
+      href="http://nar.oxfordjournals.org/cgi/content/abstract/gkq415">Nucleic
+      Acids Res. 38: W35-W40.</a> (<em>* joint first authors</em>)
   <p>
     <strong><em>Note:</em></strong> The Multi-Harmony service is
     implemented with a prototype of Jalview's RESTful web service client
diff --git a/help/html/webServices/urllinks.html b/help/html/webServices/urllinks.html
index 3dac542..ece0889 100644
--- a/help/html/webServices/urllinks.html
+++ b/help/html/webServices/urllinks.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,9 +28,8 @@
     and the desktop application are able to open URLs as 'popups' in
     your web browser. <br> Double-clicking on the ID of a sequence
     will open the first URL that can be generated from its sequence ID.
-    This is often the SRS site, but you can easily configure your own <a
-      href="#urllinks"
-    >sequence URL links</a>.
+    This is by default the EMBL-EBI site, but you can easily configure your own <a
+      href="#urllinks">sequence URL links</a>.
   </p>
   <p>
     Other links for a sequence either derived from any other configured
@@ -41,20 +40,20 @@
   <p>
     <strong><a name="urllinks">Configuring URL Links</a></strong> <br>URL
     links are defined in the "Connections" tab of the <a
-      href="../features/preferences.html"
-    >Jalview desktop preferences</a>, or specified as <a
-      href="http://www.jalview.org/examples/appletParameters.html#parameters"
-    >applet parameters</a>. <br> By default the item "SRS"
-    is added to this link menu. This link will show a web page in your
-    default browser with the selected sequence id as part of the URL.<br>
+    href="../features/preferences.html">Jalview desktop
+    preferences</a>, or specified as <a
+    href="http://www.jalview.org/examples/appletParameters.html#parameters">applet
+    parameters</a>. <br> By default the item "EMBL-EBI Search" is added
+    to this link menu. This link will show a web page in your default
+    browser with the selected sequence id as part of the URL.<br>
     In the preferences dialog box, click <strong>new</strong> to add a
     new link, and <strong>edit</strong> to modify an existing link, or <strong>delete</strong>
     to remove it.<br> You can name the link, this will be displayed
     on a new menu item under the "Link" menu when you right
     click on a sequence id. <br> The URL string must contain a
-    token that can be replaced with a sequence ID. The simplest token is
+    token that can be replaced with a sequence ID or DB accession ID. The simplest token is
     "$SEQUENCE_ID$", which will be replaced by the chosen
-    sequence id when you click on it.
+    sequence id when you click on it. 
   </p>
   <p>
     eg.<br> UniRef100 =
@@ -62,15 +61,31 @@
     Swissprot = http://www.expasy.org/uniprot/$SEQUENCE_ID$ <br> <br>
     Links will also be made for any database cross references associated
     with the sequence where the database name exactly matches a URL link
-    name. In this case, the $SEQUENCE_ID$ string will be replaced with
+    name. In this case, the $DB_ACCESSION$ string will be replaced with
     the accession string for the database cross-reference, rather than
-    the sequence ID for the sequence (<em>since Jalview 2.4</em>).
+    the sequence ID for the sequence (<em>since Jalview 2.10.1</em>).
+  </p>
+  <p>
+    <strong><a name="warning">Warning dialog about updating
+        your configured URL links</a></strong><br /> In the desktop
+    prior to Jalview 2.10.1, the only way to configure custom links for
+    a particular database cross-reference for a sequence was to give it
+    a name that
+    <em>exactly</em> matched the database source, and a regular
+    expression for filtering out any spurious matches generated when the
+    custom linked was tested against the Sequence's ID string. Since the
+    introduction of the $DB_ACCESSION$ token, however, $SEQUENCE_ID$
+    will not be used for database cross-reference accession strings, and
+    if you have custom links configured, Jalview will raise a warning
+    message so let you know that you may need to update your links to
+    use $DB_ACCESSION$.
   </p>
   <p>
     <strong>Regular Expression Substitution</strong><br> A url may
     contain a string of the form $SEQUENCE_ID=/<em>regular
-      expression</em>/=$. In this case, the regular expression will be
-    applied to the full sequence ID string and the resulting match will
+    expression</em>/=$ or $DB_ACCESSION=/<em>regular expression</em>/=$. 
+    In this case, the regular expression will be
+    applied to the full sequence ID or DB accession ID string and the resulting match will
     be inserted into the URL. Groups of parentheses can be used to
     specify which regions of the regular expression will be used to
     generate the URL:
@@ -83,7 +98,7 @@
     <em>Please Note:
       <ul>
         <li>The regular expressions supported by Jalview are those
-          provided by the <a href="www.javaregex.com">Stevesoft
+          provided by the <a href="http://www.javaregex.com">Stevesoft
             javaregex package</a>.
         </li>
         <li>Some characters must be escaped when specifying them as
diff --git a/help/html/webServices/webServicesParams.html b/help/html/webServices/webServicesParams.html
index 63d5468..8ffd0eb 100644
--- a/help/html/webServices/webServicesParams.html
+++ b/help/html/webServices/webServicesParams.html
@@ -1,6 +1,6 @@
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,13 +29,12 @@
 
   <p>
     Some Jalview services, including those provided by <a
-      href="JABAWS.html"
-    >JABAWS</a>, support a range of parameters and options, enabling you
-    to employ the most appropriate settings for the input data. In
-    addition to any preset combinations provided by services themselves,
-    the Web services parameters dialog box also allows you to create and
-    store your own parameter sets, so they can be accessed quickly from
-    the presets menu.
+      href="JABAWS.html">JABAWS</a>, support a range of parameters
+    and options, enabling you to employ the most appropriate settings
+    for the input data. In addition to any preset combinations provided
+    by services themselves, the Web services parameters dialog box also
+    allows you to create and store your own parameter sets, so they can
+    be accessed quickly from the presets menu.
   </p>
   <p>
     <Strong>Accessing the parameter dialog box</Strong><br> The
@@ -61,8 +60,8 @@
   <p>
   <center>
     <img src="wsparams.gif" align="center" width="480" height="499"
-      alt="Analysis Parameters Dialog Box for JABAWS Services"
-    > <br> Parameter settings dialog box for JABAWS MAFFT Service
+      alt="Analysis Parameters Dialog Box for JABAWS Services">
+    <br> Parameter settings dialog box for JABAWS MAFFT Service
   </center>
   </p>
   <p>The menu and text box at the top of the dialog box displays the
@@ -73,6 +72,7 @@
     this), allowing you to provide notes to accompany the parameter set.
     The modification of these or any of the option or parameter settings
     will enable one or more of the following buttons, that allow you to:
+
   
   <ul>
     <li><em>Revert</em> the changes you have made. This will undo
diff --git a/help/html/webServices/webServicesPrefs.html b/help/html/webServices/webServicesPrefs.html
index ac25ed6..4f27956 100644
--- a/help/html/webServices/webServicesPrefs.html
+++ b/help/html/webServices/webServicesPrefs.html
@@ -1,6 +1,6 @@
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -38,8 +38,8 @@
   <p>
   <center>
     <img src="wsprefs.gif" align="center"
-      alt="Web Services Preferences Panel" width="571" height="461"
-    ><br> Web Services Preference Panel
+      alt="Web Services Preferences Panel" width="571" height="461"><br>
+    Web Services Preference Panel
   </center>
   </p>
   <p>
@@ -85,8 +85,8 @@
   <center>
     <img src="invalidurldialog.gif" align="center"
       alt="Web Services Invalid URL Warning dialog box" width="389"
-      height="258"
-    ><br> Web Services Invalid URL Warning dialog box
+      height="258"><br> Web Services Invalid URL Warning
+    dialog box
   </center>
   <br>
   <strong><em>Note:</em></strong> this warning will be shown if you are
diff --git a/help/html/whatsNew.html b/help/html/whatsNew.html
index 736e786..02a7fe7 100644
--- a/help/html/whatsNew.html
+++ b/help/html/whatsNew.html
@@ -1,7 +1,7 @@
 <html>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -24,64 +24,55 @@
 </head>
 <body>
   <p>
-    <strong>What's new ?</strong>
+    <strong>What's new in Jalview 2.10.1 ?</strong>
   </p>
   <p>
-    Jalview 2.9 has been in development since December 2014. In addition
-    to a multitude of bug fixes and minor improvements (both small, and
-    rather big!), it also brings major new capabilities for codon-level
-    analysis of protein alignments and the retrieval and manipulation of
-    structural data.</p><p>For the full list of changes, see the
-    <a href="releases.html#Jalview.2.9">Jalview 2.9 Release Notes</a>.
+    Jalview 2.10.1 was released on 29th November 2016. Full details are
+    in the <a href="releases.html#Jalview.2.10.1">Jalview 2.10.1
+      Release Notes</a>, but the highlights are below. This is also the
+    first release to include contributions from Kira Mourão, who
+    joined Jalview's core development team in October 2016.
   </p>
-  <p>
-    <strong>Highlights in Jalview 2.9</strong>
   <ul>
-    <li><strong>Visualisation, editing and analysis of
-        cDNA and Protein alignments</strong><br />A new <a
-      href="features/splitView.html">Split View</a> window allows linked
-      protein and nucleotide sequence alignments to be viewed, edited,
-      and analysed as one. <br />cDNA alignments can also be
-      reconstructed from protein alignments calculated by Jalview's web
-      services, and update in response to edits in the amino acid view.<br />To
-      start experimenting with cDNA/Protein analysis, jut drop a file
-      containing cDNA sequences which code for proteins in an existing
-      alignment, and Jalview will do the rest.</li>
-    <li><strong>Enhanced Integration of UCSF Chimera</strong> <br>Jalview
-      2.9 provides full support for the use of Chimera to view 3D
-      structures linked to alignment views in the Jalview Desktop. We've
-      also included support for saving Chimera sessions in Jalview
-      project files.<br />Jalview and Chimera communicate using local
-      web server connections, which may cause firewall alerts on some
-      systems, but has the advantage of allowing bidirectional
-      communication. Communication between Jalview and Chimera is now
-      much more responsive, and selected regions in Chimera are now
-      shown as highlighted regions in the Jalview desktop.</li>
-    <li><strong>Interactive querying of the PDBe</strong><br />Jalview
-      users can now <a href="features/pdbsequencefetcher.html">browse</a> and <a href="features/viewingpdbs.html">retrieve 3D structure</a> data from the PDB
-      via the <a href="http://www.ebi.ac.uk/pdbe/api/doc/search.html">PDBe
-        Search API</a> (<a href="http://dx.doi.org/10.1093%2Fnar%2Fgkt1180">Gutmanas
-        et al 2014</a>). Developed in collaboration with the PDBe group at
-      EMBL-EBI, the interface allows both structured and free-text
-      queries to be performed, and allows automatic selection of the
-      most relevant structures for an alignment acording to a variety of
-      criteria.</li>
-    <li><strong>Improved support for RNA visualisation</strong><br />Jalview
-      2.9 integrates the latest version of the <a
-      href="features/varna.html">VARNA RNA Viewer</a>, and VARNA views
-      can also now be stored in Jalview projects. We've also dealt with
-      a number of lingering bugs in the VARNA/Jalview interface,
-      including the loss of pseudoknots when RNA secondary structure is
-      shown VARNA.</li>
-    <li><strong>Protein Secondary Structure predictions
-        with JPred4</strong><br />Jalview includes a number of new features for
-      working with secondary structure predictions from the JPred4
-      server. These include new <a href="menus/popupMenu.html#hideinserts">popup menu actions</a> to automatically hide insertions and highlight
-      mutations in an alignment with respect to a <a href="calculations/referenceseq.html">Reference
-        Sequence</a>. Jalview 2.9's new <a href="io/export.html#htmlexport">scrollable
-        SVG HTML export</a> was also developed specifically for the JPred4
-      server.</li>
+    <li><strong>More memory efficient</strong><br />We've slimmed
+      down the consensus analysis data structures used by Jalview so
+      even wider alignments can be worked with.</li>
+    <li><strong>Select highlighted region</strong><br />Press 'B'
+      or use the new menu option in the alignment window's Select menu
+      to mark columns containing highlighted regions generated from
+      structure selections, mouse-overs, or resulting from a Find
+      operation.</li>
+    <li><strong>New custom link mechanism for opening URLs
+        for database cross references.</strong><br /> If you have customised URL
+      links in your Jalview preferences, then you may already have seen
+      the <a href="#warning"> warning dialog (see below).</a></li>
+    <li><strong>New command line export option for BioJS
+        MSAviewer</strong><br />A number of small bugs with the HTML export
+      functions from the Jalview desktop were also fixed.</li>
+    <li><strong>Small but significant changes to the
+        physicochemical properties and consensus calculations</strong><br />Threonine
+      is no longer considered a non-hydrophobic residue in the protein
+      conservation calculation, and minor bugs addressed in PID and
+      consensus colouring.</li>
+    <li><strong>Correct display of disulphide bond
+        features</strong><br /> In linked structure views, Jalview would
+      highlight all residues between in addition to the two linked
+      cysteines. The 'select columns by feature' function in the feature
+      settings would also select all intermediate columns.
   </ul>
 
+  <p>
+    <strong><a name="warning">Warning dialog about updating
+        your configured URL links</a></strong><br /> In the desktop prior to Jalview
+    2.10.1, the only way to configure custom links for a particular
+    database cross-reference for a sequence was to give it a name that <em>exactly</em>
+    matched the database source, and a regular expression for filtering
+    out any spurious matches generated when the custom linked was tested
+    against the Sequence's ID string. Since the introduction of the
+    $DB_ACCESSION$ token, however, $SEQUENCE_ID$ will not be used for
+    database cross-reference accession strings, and if you have custom
+    links configured, Jalview will raise a warning message so let you
+    know that you may need to update your links to use $DB_ACCESSION$.
+  </p>
 </body>
 </html>
diff --git a/jalview-jalopy.xml b/jalview-jalopy.xml
index c1329cb..4f8871d 100644
--- a/jalview-jalopy.xml
+++ b/jalview-jalopy.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/nbbuild.xml b/nbbuild.xml
index 79ee7eb..2d57bb1 100644
--- a/nbbuild.xml
+++ b/nbbuild.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/nbproject/project.properties b/nbproject/project.properties
index ac1a2e3..b569f55 100644
--- a/nbproject/project.properties
+++ b/nbproject/project.properties
@@ -59,8 +59,8 @@ file.reference.jalview-src=src
 file.reference.jaxrpc.jar=lib/jaxrpc.jar
 file.reference.JGoogleAnalytics_0.3.jar=lib/JGoogleAnalytics_0.3.jar
 file.reference.jhall.jar=lib/jhall.jar
-file.reference.Jmol-14.2.14_2015.06.11.jar=lib/Jmol-14.2.14_2015.06.11.jar
-file.reference.JmolApplet-14.2.14_2015.06.11.jar=appletlib/JmolApplet-14.2.14_2015.06.11.jar
+file.reference.Jmol-14.6.4_2016.10.26.jar=lib/Jmol-14.6.4_2016.10.26.jar
+file.reference.JmolApplet-14.6.4_2016.10.26.jar=appletlib/JmolApplet-14.6.4_2016.10.26.jar
 file.reference.log4j-1.2.8.jar=lib/log4j-1.2.8.jar
 file.reference.mail.jar=lib/mail.jar
 file.reference.min-jaba-client.jar=lib/min-jaba-client-2.0.jar
@@ -92,7 +92,7 @@ javac.classpath=\
     ${file.reference.jaxrpc.jar}:\
     ${file.reference.JGoogleAnalytics_0.3.jar}:\
     ${file.reference.jhall.jar}:\
-    ${file.reference.Jmol-14.2.14_2015.06.11.jar}:\
+    ${file.reference.Jmol-14.6.4_2016.10.26.jar}:\
     ${file.reference.miglayout-4.0-swing.jar}:\
     ${file.reference.log4j-1.2.8.jar}:\
     ${file.reference.mail.jar}:\
@@ -101,7 +101,7 @@ javac.classpath=\
     ${file.reference.xml-apis.jar}:\
     ${file.reference.xercesImpl.jar}:\
     ${file.reference.wsdl4j.jar}:\
-    ${file.reference.JmolApplet-14.2.14_2015.06.11.jar} \
+    ${file.reference.JmolApplet-14.6.4_2016.10.26.jar} \
     ${file.reference.varna-3.9-dev.jar}
 # Space-separated list of extra javac options
 javac.compilerargs=
diff --git a/nbproject/project.xml b/nbproject/project.xml
index efa19b2..0054e5f 100644
--- a/nbproject/project.xml
+++ b/nbproject/project.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/resources/authors.props b/resources/authors.props
index d41c2ca..3488ac6 100644
--- a/resources/authors.props
+++ b/resources/authors.props
@@ -1,4 +1,4 @@
-YEAR=2014
-AUTHORS=J Procter, AM Waterhouse, M Carstairs, TC Ofoegbu, J Engelhardt, LM Lui, A Menard, D Barton, N Sherstnev, D Roldan-Martinez, M Clamp, S Searle, G Barton
-AUTHORFNAMES=Jim Procter, Andrew Waterhouse, Mungo Carstairs, Tochukwu 'Charles' Ofoegbu, Jan Engelhardt, Lauren Lui, Anne Menard, Daniel Barton, Natasha Sherstnev, David Roldan-Martinez, Michele Clamp, James Cuff, Steve Searle, David Martin & Geoff Barton
+YEAR=2016
+AUTHORS=J Procter, M Carstairs, TC Ofoegbu, K Mourao, AM Waterhouse, J Engelhardt, LM Lui, A Menard, D Barton, N Sherstnev, D Roldan-Martinez, M Clamp, S Searle, G Barton
+AUTHORFNAMES=Jim Procter, Mungo Carstairs, Tochukwu 'Charles' Ofoegbu, Kira Mourao, Andrew Waterhouse, Jan Engelhardt, Lauren Lui, Anne Menard, Daniel Barton, Natasha Sherstnev, David Roldan-Martinez, Michele Clamp, James Cuff, Steve Searle, David Martin & Geoff Barton
  
\ No newline at end of file
diff --git a/resources/embl_mapping.xml b/resources/embl_mapping.xml
index 957d113..68e3fdb 100644
--- a/resources/embl_mapping.xml
+++ b/resources/embl_mapping.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,35 +22,64 @@
 <!--
 	History: Originally created from EMBL_common_V1.0
 	Updated on 24th April 2007 for WsDBFetch Service move to EMBL_Services_V1.1.xsd
+	Updated May 2016 for EMBL XML 1.2 JAL-2113 JAL-2114
+	  see ftp://ftp.sra.ebi.ac.uk/meta/xsd/sra_1_5/ENA.embl.xsd
+	  see http://www.ebi.ac.uk/ena/submit/data-formats
 	-->
 	<class name="jalview.datamodel.xdb.embl.EmblFile">
-		<map-to xml="EMBL_Services"/>
+		<map-to xml="ROOT"/>
+		<field name="text" type="string">
+			<bind-xml node="text"/>
+		</field>
 		<field name="entries" type="jalview.datamodel.xdb.embl.EmblEntry" collection="vector">
 			<bind-xml name="entry"/>
 		</field>
-		
 		<field name="errors" type="jalview.datamodel.xdb.embl.EmblError" collection="vector">
 			<bind-xml name="Error"/>
 		</field>
 	</class>
 	<class name="jalview.datamodel.xdb.embl.EmblEntry">
 		<field name="accession" type="string">
-			<bind-xml location="accession" node="attribute"/>
+			<bind-xml name="accession" node="attribute"/>
 		</field>
-		<!--  May 2015 changed from last-updated to match xml -->
-		<field name="lastUpdated" type="string">
-			<bind-xml location="lastUpdated" node="attribute"/>
+		<!-- 
+		    in EMBL XML 1.2 sequence/@version became entry/version 
+		    entry/@version became entry/@entryVersion
+		-->
+		<field name="sequenceVersion" type="string">
+			<bind-xml name="version" node="attribute"/>
 		</field>
-		<field name="version" type="string">
-			<bind-xml location="version" node="attribute"/>
+		<field name="entryVersion" type="string">
+			<bind-xml name="entryVersion" node="attribute"/>
+		</field>
+		<field name="dataClass" type="string">
+			<bind-xml name="dataClass" node="attribute"/>
+		</field>
+		<field name="taxonomicDivision" type="string">
+			<bind-xml name="taxonomicDivision" node="attribute"/>
+		</field>
+		<field name="moleculeType" type="string">
+			<bind-xml name="moleculeType" node="attribute"/>
+		</field>
+		<field name="sequenceLength" type="string">
+			<bind-xml name="sequenceLength" node="attribute"/>
+		</field>
+		<field name="topology" type="string">
+			<bind-xml name="topology" node="attribute" location="type"/>
 		</field>
-		<field name="rCreated" type="string">
-			<bind-xml location="releaseCreated" node="attribute"/>
+		<field name="firstPublicDate" type="string">
+			<bind-xml name="firstPublic" node="attribute"/>
 		</field>
-		<field name="rLastUpdated" type="string">
-			<bind-xml location="releaseLastUpdated" node="attribute"/>
+		<field name="firstPublicRelease" type="string">
+			<bind-xml name="firstPublicRelease" node="attribute"/>
 		</field>
-		<field name="desc" type="string">
+		<field name="lastUpdatedDate" type="string">
+			<bind-xml name="lastUpdated" node="attribute"/>
+		</field>
+		<field name="lastUpdatedRelease" type="string">
+			<bind-xml name="lastUpdatedRelease" node="attribute"/>
+		</field>
+		<field name="description" type="string">
 			<bind-xml name="description" node="element"/>
 		</field>
 		<field name="keywords" type="string" collection="vector">
@@ -60,19 +89,13 @@
 			<bind-xml name="feature"/>
 		</field>
 		<field name="dbRefs" type="jalview.datamodel.DBRefEntry" collection="vector">
-			<bind-xml name="dbreference" />
+			<bind-xml name="xref" />
 		</field>
 		<field name="sequence" type="jalview.datamodel.xdb.embl.EmblSequence">
 			<bind-xml name="sequence"/>
 		</field>
 	</class>
 	<class name="jalview.datamodel.xdb.embl.EmblSequence">
-		<field name="type" type="string">
-			<bind-xml name="type" node="attribute" location="type"/>
-		</field>
-		<field name="version" type="string">
-			<bind-xml name="version" node="attribute" location="version"/>
-		</field>
 		<field name="sequence" type="string">
 			<bind-xml node="text"/>
 		</field>
@@ -81,25 +104,25 @@
 		<field name="name" type="string">
 			<bind-xml name="name" node="attribute"/>
 		</field>
+		<field name="location" type="string">
+			<bind-xml name="location" node="attribute"/>
+		</field>
 		<field name="dbRefs" type="jalview.datamodel.DBRefEntry" collection="vector">
-			<bind-xml name="dbreference" node="element"/>
+			<bind-xml name="xref" node="element"/>
 		</field>
 		<field name="qualifiers" type="jalview.datamodel.xdb.embl.Qualifier" collection="vector">
 			<bind-xml name="qualifier"/>
 		</field>					
-		<field name="locations" type="jalview.datamodel.xdb.embl.EmblFeatureLocations" collection="vector">
-			<bind-xml name="location"/>
-		</field>
 	</class>
 	<class name="jalview.datamodel.DBRefEntry" verify-constructable="false">
 		<field name="accessionId" type="java.lang.String">
-			<bind-xml name="primary" node="attribute"/>
+			<bind-xml name="id" node="attribute"/>
 		</field>
 		<field name="source" type="java.lang.String"> 
 			<bind-xml name="db" node="attribute"/>
 		</field>
 		<field name="version" type="string">
-			<bind-xml name="secondary" node="attribute"/>
+			<bind-xml name="secondaryId" node="attribute"/>
 		</field>
 	</class>
 	<class  name="jalview.datamodel.xdb.embl.Qualifier" verify-constructable="false">
@@ -113,40 +136,4 @@
 			<bind-xml name="value" node="element"/>
 		</field>
 	</class>
-	<class name="jalview.datamodel.xdb.embl.EmblFeatureLocations">
-		<field name="locationType" type="string">
-			<bind-xml name="type" node="attribute"/>
-		</field>
-		<field name="locationComplement" type="boolean">
-			<bind-xml name="complement" node="attribute"/>
-		</field>
-		<field name="locElements" type="jalview.datamodel.xdb.embl.EmblFeatureLocElement" collection="vector">
-			<bind-xml name="locationElement"/>
-		</field>
-	</class>
-	<class name="jalview.datamodel.xdb.embl.EmblFeatureLocElement">
-		<field name="type" type="string">
-			<bind-xml name="type" node="attribute"/>
-		</field>
-		<field name="accession" type="string">
-			<bind-xml name="accession" node="attribute"/>			
-		</field>
-		<field name="version" type="string">
-			<bind-xml name="version" node="attribute"/>
-		</field>
-		<field name="complement" type="boolean">
-			<bind-xml name="complement"/>
-		</field>
-		<field name="basePositions" type="jalview.datamodel.xdb.embl.BasePosition" collection="array">
-			<bind-xml name="basePosition" node="element"/>
-		</field>
-	</class>
-	<class name="jalview.datamodel.xdb.embl.BasePosition">
-		<field name="type">
-			<bind-xml name="type" node="attribute"/>
-		</field>
-		<field name="pos">
-			<bind-xml node="text"/>
-		</field>
-	</class>
 </mapping>
diff --git a/resources/fts/pdb_data_columns.txt b/resources/fts/pdb_data_columns.txt
new file mode 100644
index 0000000..c249cf6
--- /dev/null
+++ b/resources/fts/pdb_data_columns.txt
@@ -0,0 +1,130 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+pdb_data_columns
+#
+_group.id
+_group.name
+_group.sort_order
+g1;Quality Measures;1
+g2;Cross References;2
+g3;Names & Taxonomy;3
+g4;Procedures & Software;4
+g5;Date Of;5
+g6;Miscellaneous;6
+#
+_data_column.primary_key;pdb_id
+_data_column.default_response_page_size;100
+#
+_data_column.name
+_data_column.code
+_data_column.group_id
+_data_column.data_type | _data_column.isFormated | _data_column.significantDigit
+_data_column.min_col_width
+_data_column.max_col_width
+_data_column.preferred_col_width
+_data_column.is_shown_by_default
+_data_column.is_searchable
+PDB Id;pdb_id;String;g2;40;60;45;true;true
+Title;title;String;g6;50;1500;400;true;false
+Molecule;molecule_name;String;g3;50;400;95;false;true
+Molecule Type;molecule_type;String;g3;50;400;95;false;true
+Sequence;molecule_sequence;String;g6;50;400;95;false;false
+PFAM Accession;pfam_accession;String;g2;50;400;95;false;true
+PFAM Name;pfam_name;String;g3;50;400;95;false;true
+InterPro Name;interpro_name;String;g3;50;400;95;false;false
+InterPro Accession;interpro_accession;String;g2;50;400;95;false;false
+UniProt Id;uniprot_id;String;g2;50;400;95;false;true
+UniProt Accession;uniprot_accession;String;g2;50;400;95;false;false
+UniProt Coverage;uniprot_coverage;String;g6;50;400;95;false;false
+Uniprot Features;uniprot_features;String;g6;50;400;95;false;false
+R Factor;r_factor;Double|T|3;g1;50;150;85;false;false
+Experimental Method;experimental_method;String;g4;50;400;105;true;false
+Resolution;resolution;Double|T|3;g1;50;150;85;true;false
+Data Quality;data_quality;Double|T|2;g1;50;150;85;false;false
+Overall Quality;overall_quality;Double|T|1;g1;50;150;85;false;false
+Number of Polymers;number_of_polymers;int;g6;50;400;95;false;false
+Number of Protein Chains;number_of_protein_chains;int;g6;50;400;95;false;false
+Number of Bound Molecule;number_of_bound_molecules;int;g6;50;400;95;false;false
+Number of Polymer Residue;number_of_polymer_residues;int;g6;50;400;95;false;false
+GENUS;genus;String;g3;50;400;95;false;true
+Gene Name;gene_name;String;g3;50;400;95;false;true
+GO Id;go_id;String;g2;50;400;95;false;false
+Assembly Id;assembly_id;String;g2;50;400;95;false;false
+Assembly Form;assembly_form;String;g6;50;400;95;false;false
+Assembly Type;assembly_type;String;g6;50;400;95;false;false
+Space Group;spacegroup;String;g6;50;400;95;false;false
+Cath Code;cath_code;String;g2;50;400;95;false;false
+Tax Id;tax_id;String;g2;50;400;95;false;false
+Tax Query;tax_query;String;g2;50;400;95;false;false
+Interacting Entity Id;interacting_entity_id;String;g2;50;400;95;false;false
+Interacting Molecules;interacting_molecules;String;g6;50;400;95;false;false
+Pubmed Id;pubmed_id;int;g2;50;400;95;false;false
+Status;status;String;g6;50;400;95;false;false
+Model Quality;model_quality;Double|T|2;g1;50;150;85;false;false
+Pivot Resolution;pivot_resolution;Double|T|3;g1;50;150;85;false;false
+Data reduction software;data_reduction_software;String;g4;50;400;95;false;false
+Max observed residues;max_observed_residues;Integer|F;g6;50;400;95;false;false
+Organism scientific name;organism_scientific_name;String;g3;50;400;95;false;false
+Super kingdom;superkingdom;String;g3;50;400;95;false;false
+Rank;rank;String;g3;50;400;95;false;false
+Crystallisation Ph;crystallisation_ph;String;g6;50;400;95;false;false
+Biological Function;biological_function;String;g6;50;400;95;false;false
+Biological Process;biological_process;String;g6;50;400;95;false;false
+Biological Cell Component;biological_cell_component;String;g6;50;400;95;false;false
+Compound Name;compound_name;String;g3;50;400;95;false;false
+Compound Id;compound_id;String;g2;50;400;95;false;false
+Compound Weight;compound_weight;String;g6;50;400;95;false;false
+Compound Systematic Name;compound_systematic_name;String;g3;50;400;95;false;false
+Interacting Ligands;interacting_ligands;String;g6;50;400;95;false;false
+Journal;journal;String;g6;50;400;95;false;false
+All Authors;all_authors;String;g6;50;400;95;false;false
+Experiment Data Available;experiment_data_available;String;g6;50;400;95;false;false
+Diffraction Protocol;diffraction_protocol;String;g4;50;400;95;false;false
+Refinement Software;refinement_software;String;g4;50;400;95;false;false
+Structure Determination Method;structure_determination_method;String;g4;50;400;95;false;false
+Synchrotron Site;synchrotron_site;String;g6;50;400;95;false;false
+Sample Preparation Method;sample_preparation_method;String;g4;50;400;95;false;false
+Entry Authors;entry_authors;String;g6;50;400;95;false;false
+Citation Title;citation_title;String;g6;50;400;95;false;false
+Structure Solution Software;structure_solution_software;String;g4;50;400;95;false;false
+Entry Entity;entry_entity;String;g6;50;400;95;false;false
+R Free;r_free;Double|T|3;g1;50;150;85;false;false
+Number of Polymer Entities;number_of_polymer_entities;int;g6;50;400;95;false;false
+Number of Bound Entities;number_of_bound_entities;int;g6;50;400;95;false;false
+Crystallisation Reservoir;crystallisation_reservoir;String;g6;50;400;95;false;false
+Data Scaling Software;data_scaling_software;String;g4;50;400;95;false;false
+Detector;detector;String;g6;50;400;95;false;false
+Detector Type;detector_type;String;g6;50;400;95;false;false
+Modified Residue Flag;modified_residue_flag;String;g6;50;400;95;false;false
+Number of Copies;number_of_copies;int;g6;50;400;95;false;false
+Struc Asym Id;struct_asym_id;String;g2;50;400;95;false;false
+Homologus PDB Entity Id;homologus_pdb_entity_id;String;g2;50;400;95;false;false
+Molecule Synonym;molecule_synonym;String;g6;50;400;95;false;false
+Deposition Site;deposition_site;String;g6;50;400;95;false;false
+Synchrotron Beamline;synchrotron_beamline;String;g6;50;400;95;false;false
+Entity Id; entity_id;String;g2;50;400;95;false;false
+Beam Source Name;beam_source_name;String;g3;50;400;95;false;false
+Processing Site;processing_site;String;g6;50;400;95;false;false
+Entity Weight;entity_weight;Double|T|0;g6;50;400;95;false;false
+Version;_version_;Double|F|0;g6;50;400;95;false;false
+ALL;text;String;g6;50;400;95;false;true
+#
diff --git a/resources/fts/uniprot_data_columns.txt b/resources/fts/uniprot_data_columns.txt
new file mode 100644
index 0000000..bb339d4
--- /dev/null
+++ b/resources/fts/uniprot_data_columns.txt
@@ -0,0 +1,175 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+uniprot_data_columns
+#
+_group.id
+_group.name
+_group.sort_order
+g1;Quality Measures;3
+g2;Cross References;2
+g3;Names & Taxonomy;1
+g4;Procedures & Softwares;4
+g5;Date Of;5
+g6;Miscellaneous;6
+g7;Sequences;7
+g8;Function;8
+g9;Interaction;9
+g10;Expression;10
+g11;Gene Ontology;11
+g12;Pathology & Biotech;12
+g13;Subcellular location;13
+g14;PTM / Processing;14
+g15;Structure;15
+g16;Publications;16
+g17;Date of;17
+g18;Family & Domain;18
+#
+_data_column.primary_key;id
+_data_column.default_response_page_size;500
+#
+_data_column.name
+_data_column.code|_data_column.alt_code (optional: used for specifying search code when different from original code)
+_data_column.group_id
+_data_column.data_type
+_data_column.min_col_width
+_data_column.max_col_width
+_data_column.preferred_col_width
+_data_column.is_shown_by_default
+_data_column.is_searchable
+Uniprot Id;id;String;g3;80;150;85;true;true
+Entry Name;entry name|mnemonic;String;g3;100;150;105;true;true
+Protein names;protein names|name;String;g3;300;1500;500;true;true
+Gene Names;genes|gene;String;g3;100;1000;145;true;true
+Organism;organism;String;g3;100;1000;200;true;true
+Organism ID;organism-id;int;g3;60;100;80;false;false
+Proteomes;proteome;String;g3;50;1000;95;false;false
+Taxonomic lineage (ALL);lineage(ALL)|taxonomy;String;g3;50;400;95;false;false
+Virus hosts;virus hosts|host;String;g3;50;1000;95;false;true
+Fragment;fragment;String;g7;50;1000;95;false;false
+Gene encoded by;encodedon;String;g7;50;1000;95;false;false
+Alternative products (isoforms);comment(ALTERNATIVE PRODUCTS);String;g7;50;1000;95;false;false
+Erroneous gene model prediction;comment(ERRONEOUS GENE MODEL PREDICTION);String;g7;50;1000;95;false;false
+Erroneous initiation;comment(ERRONEOUS INITIATION);String;g7;50;1000;95;false;false
+Erroneous translation;comment(ERRONEOUS TRANSLATION);String;g7;50;1000;95;false;false
+Frameshift;comment(FRAMESHIFT);String;g7;50;1000;95;false;false
+Mass spectrometry;comment(MASS SPECTROMETRY);String;g7;50;1000;95;false;false
+Polymorphism;comment(POLYMORPHISM);String;g7;50;1000;95;false;false
+RNA editing;comment(RNA EDITING);String;g7;50;1000;95;false;false
+Sequence caution;comment(SEQUENCE CAUTION);String;g7;50;1000;95;false;false
+Status;reviewed;String;g6;50;100;95;true;true
+Length;length;int|T|0;g7;50;100;65;true;true
+Mass;mass;int|T|0;g7;50;100;80;false;true
+Sequence;sequence;String;g7;50;1000;95;false;false
+Alternative sequence;feature(ALTERNATIVE SEQUENCE);String;g7;50;1000;95;false;false
+Natural variant;feature(NATURAL VARIANT);String;g7;50;1000;95;false;false
+Non-adjacent residues;feature(NON ADJACENT RESIDUES);String;g7;50;1000;95;false;false
+Non-standard residue;feature(NON STANDARD RESIDUE);String;g7;50;1000;95;false;false
+Non-terminal residue;feature(NON TERMINAL RESIDUE);String;g7;50;1000;95;false;false
+Sequence conflict;feature(SEQUENCE CONFLICT);String;g7;50;1000;95;false;false
+Sequence uncertainty;feature(SEQUENCE UNCERTAINTY);String;g7;50;1000;95;false;false
+Version (Sequence);version(sequence);String;g7;50;1000;95;false;false
+EC number;ec;String;g8;50;1000;95;false;true
+Absorption;comment(ABSORPTION);String;g8;50;1000;95;false;false
+Catalytic activity;comment(CATALYTIC ACTIVITY);String;g8;50;1000;95;false;false
+Cofactor;comment(COFACTOR);String;g8;50;1000;95;false;false
+Enzyme regulation;comment(ENZYME REGULATION);String;g8;50;1000;95;false;false
+Function [CC];comment(FUNCTION);String;g8;50;1000;95;false;false
+Kinetics;comment(KINETICS);String;g8;50;1000;95;false;false
+Pathway;comment(PATHWAY);String;g8;50;1000;95;false;false
+Redox potential;comment(REDOX POTENTIAL);String;g8;50;1000;95;false;false
+Temperature dependence;comment(TEMPERATURE DEPENDENCE);String;g8;50;1000;95;false;false
+pH dependence;comment(PH DEPENDENCE);String;g8;50;1000;95;false;false
+Active site;feature(ACTIVE SITE);String;g8;50;1000;95;false;false
+Binding site;feature(BINDING SITE);String;g8;50;1000;95;false;false
+DNA binding;feature(DNA BINDING);String;g8;50;1000;95;false;false
+Metal binding;feature(METAL BINDING);String;g8;50;1000;95;false;false
+Nucleotide binding;feature(NP BIND);String;g8;50;1000;95;false;false
+Site;feature(SITE);String;g8;50;1000;95;false;false
+Annotation;annotation score;String;g6;50;1000;95;false;false
+Features;features;String;g6;50;1000;95;false;false
+Caution;comment(CAUTION);String;g6;50;1000;95;false;false
+Miscellaneous [CC];comment(GENERAL);String;g6;50;1000;95;false;false
+Keywords;keywords|keyword;String;g6;50;1000;95;false;true
+Protein existence;existence;String;g6;50;1000;95;false;true
+ALL;Search All;String;g7;50;1000;95;false;true
+Subunit structure [CC];comment(SUBUNIT);String;g9;50;1000;95;false;false
+Interacts with;interactor;String;g9;50;1000;95;false;false
+Developmental stage;comment(DEVELOPMENTAL STAGE);String;g10;50;1000;95;false;false
+Induction;comment(INDUCTION);String;g10;50;1000;95;false;false
+Tissue specificity;comment(TISSUE SPECIFICITY);String;g10;50;1000;95;false;false
+Gene ontology (GO);go;String;g11;50;1000;95;false;true
+Gene ontology (biological process);go(biological process);String;g11;50;1000;95;false;false
+Gene ontology (molecular function);go(molecular function);String;g11;50;1000;95;false;false
+Gene ontology (cellular component);go(cellular component);String;g11;50;1000;95;false;false
+Gene ontology IDs;go-id;String;g11;50;1000;95;false;true
+Allergenic properties;comment(ALLERGEN);String;g12;50;1000;95;false;false
+Biotechnological use;comment(BIOTECHNOLOGY);String;g12;50;1000;95;false;false
+Disruption phenotype;comment(DISRUPTION PHENOTYPE);String;g12;50;1000;95;false;false
+Involvement in disease;comment(DISEASE);String;g12;50;1000;95;false;false
+Pharmaceutical use;comment(PHARMACEUTICAL);String;g12;50;1000;95;false;false
+Toxic dose;comment(TOXIC DOSE);String;g12;50;1000;95;false;false
+Subcellular location [CC];comment(SUBCELLULAR LOCATION);String;g13;50;1000;95;false;false
+Intramembrane;feature(INTRAMEMBRANE);String;g13;50;1000;95;false;false
+Topological domain;feature(TOPOLOGICAL DOMAIN);String;g13;50;1000;95;false;false
+Transmembrane;feature(TRANSMEMBRANE);String;g13;50;1000;95;false;false
+Post-translational modification;comment(PTM);String;g14;50;1000;95;false;false
+Chain;feature(CHAIN);String;g14;50;1000;95;false;false
+Cross-link;feature(CROSS LINK);String;g14;50;1000;95;false;false
+Disulfide bond;feature(DISULFIDE BOND);String;g14;50;1000;95;false;false
+Glycosylation;feature(GLYCOSYLATION);String;g14;50;1000;95;false;false
+Initiator methionine;feature(INITIATOR METHIONINE);String;g14;50;1000;95;false;false
+Lipidation;feature(LIPIDATION);String;g14;50;1000;95;false;false
+Modified residue;feature(MODIFIED RESIDUE);String;g14;50;1000;95;false;false
+Peptide;feature(PEPTIDE);String;g14;50;1000;95;false;false
+Propeptide;feature(PROPEPTIDE);String;g14;50;1000;95;false;false
+Signal peptide;feature(SIGNAL);String;g14;50;1000;95;false;false
+Transit peptide;feature(TRANSIT);String;g14;50;1000;95;false;false
+3D;3d;String;g15;50;1000;95;false;false
+Beta strand;feature(BETA STRAND);String;g15;50;1000;95;false;false
+Helix;feature(HELIX);String;g15;50;1000;95;false;false
+Turn;feature(TURN);String;g15;50;1000;95;false;false
+PubMed ID;citation;String;g16;50;1000;95;false;true
+Date of creation;created;String;g17;80;150;100;false;true
+Date of last modification;last-modified;String;g17;80;150;100;false;true
+Date of last sequence modification;sequence-modified;String;g17;80;150;100;false;true
+Version (entry);version(entry);int;g17;80;100;80;false;false
+Domain [cc];comment(DOMAIN)|domain;String;g18;80;1000;95;false;true
+Sequence similarities;comment(SIMILARITY);String;g18;50;1000;95;false;false
+Protein families;families|family;String;g18;50;1000;95;false;true
+Coiled coil;feature(COILED COIL);String;g18;50;1000;95;false;false
+Compositional bias;feature(COMPOSITIONAL BIAS);String;g18;50;1000;95;false;false
+Domain [FT];feature(DOMAIN EXTENT);String;g18;50;1000;95;false;false
+Motif;feature(MOTIF);String;g18;50;1000;95;false;false
+Region;feature(REGION);String;g18;50;1000;95;false;false
+Repeat;feature(REPEAT);String;g18;50;1000;95;false;false
+Zinc finger;feature(ZINC FINGER);String;g18;50;1000;95;false;false
+Cross-reference (EMBL);database(EMBL);String;g2;50;1000;95;false;false
+Cross-reference (PDB);database(PDB);String;g2;50;1000;95;false;false
+Cross-reference (ENSEMBL);database(ENSEMBL);String;g2;50;1000;95;false;false
+Cross-reference (PFAM);database(PFAM);String;g2;50;1000;95;false;false
+Cross-reference (RFAM);database(RFAM);String;g2;50;1000;95;false;false
+Cross-reference (CATH);database(CATH);String;g2;50;1000;95;false;false
+Cross-reference (SCOPE);database(SCOPE);String;g2;50;1000;95;false;false
+Cross-reference (GO);database(GO);String;g2;50;1000;95;false;false
+Cross-reference (INTERPRO);database(INTERPRO);String;g2;50;1000;95;false;false
+Mapped PubMed ID;citationmapping;String;g16;50;1000;95;false;true
+#
diff --git a/resources/images/blank_16x16_placeholder.png b/resources/images/blank_16x16_placeholder.png
new file mode 100644
index 0000000..885ad87
Binary files /dev/null and b/resources/images/blank_16x16_placeholder.png differ
diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties
index 05d6427..6360dc7 100644
--- a/resources/lang/Messages.properties
+++ b/resources/lang/Messages.properties
@@ -38,7 +38,6 @@ action.cancel = Cancel
 action.create = Create
 action.update = Update
 action.delete = Delete
-action.snapshot = Snapshot
 action.clear = Clear
 action.accept = Accept
 action.select_ddbb = --- Select Database ---
@@ -121,12 +120,13 @@ action.save_as_default = Save as default
 action.save_as = Save as...
 action.save = Save
 action.cancel_fetch = Cancel Fetch
-action.save_omit_hidden_columns = Save / Omit Hidden Regions
 action.change_font = Change Font
 action.change_font_tree_panel = Change Font (Tree Panel)
 action.colour = Colour
 action.calculate = Calculate
 action.select_all = Select all
+action.select_highlighted_columns = Select Highlighted Columns
+tooltip.select_highlighted_columns = Press B to mark highlighted columns, Ctrl-(or Cmd)-B to toggle, and Alt-B to mark all but highlighted columns 
 action.deselect_all = Deselect all
 action.invert_selection = Invert selection
 action.using_jmol = Using Jmol
@@ -137,21 +137,22 @@ action.show_group = Show Group
 action.fetch_db_references = Fetch DB References
 action.view_flanking_regions = Show flanking regions
 label.view_flanking_regions = Show sequence data either side of the subsequences involved in this alignment
-label.str = Str:
-label.seq = Seq:
 label.structures_manager = Structures Manager
 label.nickname = Nickname:
 label.url = URL:
 label.input_file_url = Enter URL or Input File
-label.select_feature = Select feature:
+label.select_feature = Select feature
 label.name = Name
+label.name\: = Name:
 label.name_param = Name: {0}
 label.group = Group
+label.group\: = Group:
 label.group_name = Group Name
 label.group_description = Group Description
 label.edit_group_name_description = Edit Group Name/Description
 label.colour = Colour:
-label.description = Description:
+label.description = Description
+label.description\: = Description:
 label.start = Start:
 label.end = End:
 label.current_parameter_set_name = Current parameter set name:
@@ -217,11 +218,13 @@ label.above_identity_threshold = Above Identity Threshold
 label.show_sequence_features = Show Sequence Features
 label.nucleotide = Nucleotide
 label.protein = Protein
+label.nucleotides = Nucleotides
+label.proteins = Proteins
 label.to_new_alignment = To New Alignment
 label.to_this_alignment = Add To This Alignment
 label.apply_colour_to_all_groups = Apply Colour To All Groups
-label.modify_identity_thereshold = Modify Identity Threshold...
-label.modify_conservation_thereshold = Modify Conservation Threshold...
+label.modify_identity_threshold = Modify Identity Threshold...
+label.modify_conservation_threshold = Modify Conservation Threshold...
 label.input_from_textbox = Input from textbox
 label.centre_column_labels = Centre column labels
 label.automatic_scrolling = Automatic Scrolling
@@ -238,7 +241,6 @@ label.except_selected_sequences = All except selected sequences
 label.all_but_selected_region = All but Selected Region (Shift+Ctrl+H)
 label.selected_region = Selected Region
 label.all_sequences_columns = All Sequences and Columns
-label.hide_insertions = Hide columns gapped for selection
 label.hide_selected_annotations = Hide selected annotations
 label.show_selected_annotations = Show selected annotations
 label.group_consensus = Group Consensus
@@ -319,7 +321,6 @@ label.found_match_for = Found match for {0}
 label.font = Font:
 label.size = Size:
 label.style = Style:
-label.enter_redundancy_threshold = Enter the redundancy threshold
 label.calculating = Calculating....
 label.modify_conservation_visibility = Modify conservation visibility
 label.colour_residues_above_occurence = Colour residues above % occurence
@@ -363,7 +364,6 @@ label.example = Example
 label.example_param = Example: {0}
 label.select_file_format_before_saving = You must select a file format before saving!
 label.file_format_not_specified = File format not specified
-label.alignment_contains_hidden_columns = The Alignment contains hidden regions (hidden sequences/columns).\nDo you want to save only the visible alignment?
 label.couldnt_save_file = Couldn't save file: {0}
 label.error_saving_file = Error Saving File
 label.remove_from_default_list = Remove from default list?
@@ -384,14 +384,14 @@ label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation =
 label.translation_failed = Translation Failed
 label.error_when_translating_sequences_submit_bug_report = Unfortunately, something went wrong when translating your sequences.\nPlease take a look in the Jalview java console\nand submit a bug report including the stacktrace.
 label.implementation_error  = Implementation error:
-label.automatically_associate_pdb_files_with_sequences_same_name = Do you want to automatically associate the {0} PDB files with sequences in the alignment that have the same name?
-label.automatically_associate_pdb_files_by_name = Automatically Associate PDB files by name
+label.automatically_associate_structure_files_with_sequences_same_name = Do you want to automatically associate the {0} structure file(s) with sequences in the alignment that have the same name?
+label.automatically_associate_structure_files_by_name = Automatically Associate Structure files by name
 label.ignore_unmatched_dropped_files_info = <html>Do you want to <em>ignore</em> the {0} files whose names did not match any sequence IDs ?</html>
 label.ignore_unmatched_dropped_files = Ignore unmatched dropped files?
 label.view_name_original = Original
 label.enter_view_name = Enter View Name
 label.enter_label = Enter label
-label.enter_label_for_the_structure = Enter a label for the structure?
+label.enter_label_for_the_structure = Enter a label for the structure
 label.pdb_entry_is_already_displayed = {0} is already displayed.\nDo you want to re-use this viewer ?
 label.map_sequences_to_visible_window = Map Sequences to Visible Window: {0}
 label.add_pdbentry_to_view = Do you want to add {0} to the view called\n{1}\n
@@ -465,7 +465,6 @@ label.no_features_added_to_this_alignment = No Features added to this alignment!
 label.features_can_be_added_from_searches_1 = (Features can be added from searches or
 label.features_can_be_added_from_searches_2 = from Jalview / GFF features files)
 label.calculating_pca= Calculating PCA
-label.reveal_columns = Reveal Columns
 label.jalview_cannot_open_file = Jalview can't open file
 label.jalview_applet = Jalview applet
 label.loading_data = Loading data
@@ -473,7 +472,6 @@ label.memory_stats = Total Free Memory: {0} MB; Max Memory: {1} MB; {2} %
 label.calculating_tree = Calculating tree
 label.state_queueing = queuing
 label.state_running = running
-label.state_complete = complete
 label.state_completed = finished
 label.state_job_cancelled = job cancelled!!
 label.state_job_error = job error!
@@ -499,7 +497,6 @@ label.jmol_help = Jmol Help
 label.chimera_help = Chimera Help
 label.close_viewer = Close Viewer
 label.confirm_close_chimera = This will close Jalview''s connection to {0}.<br>Do you want to close the Chimera window as well?
-label.chimera_help = Chimera Help
 label.all = All
 label.sort_by = Sort alignment by
 label.sort_by_score = Sort by Score
@@ -519,15 +516,14 @@ label.reset_min_max_colours_to_defaults = Reset min and max colours to defaults
 label.align_structures_using_linked_alignment_views = Align structures using {0} linked alignment views
 label.connect_to_session = Connect to session {0}
 label.threshold_feature_display_by_score = Threshold the feature display by score.
-label.threshold_feature_no_thereshold = No Threshold
-label.threshold_feature_above_thereshold = Above Threshold
-label.threshold_feature_below_thereshold = Below Threshold
-label.adjust_thereshold = Adjust threshold
+label.threshold_feature_no_threshold = No Threshold
+label.threshold_feature_above_threshold = Above Threshold
+label.threshold_feature_below_threshold = Below Threshold
+label.adjust_threshold = Adjust threshold
 label.toggle_absolute_relative_display_threshold = Toggle between absolute and relative display threshold.
 label.display_features_same_type_different_label_using_different_colour = Display features of the same type with a different label using a different colour. (e.g. domain features)
 label.select_colour_minimum_value = Select Colour for Minimum Value
 label.select_colour_maximum_value = Select Colour for Maximum Value
-label.open_new_jmol_view_with_all_structures_associated_current_selection_superimpose_using_alignment = Open a new structure viewer with all structures associated with the current selection and superimpose them using the alignment.
 label.open_url_param = Open URL {0}
 label.open_url_seqs_param = Open URL ({0}..) ({1} seqs)
 label.load_pdb_file_associate_with_sequence = Load a PDB file and associate it with sequence {0}
@@ -582,8 +578,8 @@ label.histogram = Histogram
 label.logo = Logo
 label.non_positional_features = List Non-positional Features
 label.database_references = List Database References
-label.share_selection_across_views = Share selection across views
-label.scroll_highlighted_regions = Scroll to highlighted regions
+#label.share_selection_across_views = Share selection across views
+#label.scroll_highlighted_regions = Scroll to highlighted regions
 label.gap_symbol = Gap Symbol
 label.prot_alignment_colour = Protein Alignment Colour
 label.nuc_alignment_colour = Nucleotide Alignment Colour
@@ -666,20 +662,12 @@ label.cut_paste = Cut'n'Paste
 label.adjusting_parameters_for_calculation = Adjusting parameters for existing Calculation
 label.2d_rna_structure_line = 2D RNA {0} (alignment)
 label.2d_rna_sequence_name = 2D RNA - {0}
-label.edit_name_and_description_current_group = Edit name and description of current group.
-label.view_structure_for = View structure for {0}
-label.view_all_structures = View all {0} structures.
-label.view_all_representative_structures = View all {0} representative structures.
-label.open_new_jmol_view_with_all_representative_structures_associated_current_selection_superimpose_using_alignment = Opens a new structure viewer with all representative structures\nassociated with the current selection\nsuperimposed with the current alignment.
-label.associate_structure_with_sequence = Associate Structure with Sequence
+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
-label.discover_pdb_ids = Discover PDB IDs
+label.enter_pdb_id = Enter PDB Id (or pdbid:chaincode)
 label.text_colour = Text Colour
 action.set_text_colour = Text Colour...
 label.structure = Structure
-label.view_structure = View Structure
-label.view_protein_structure = View Protein Structure
 label.show_pdbstruct_dialog = 3D Structure Data...
 label.view_rna_structure = VARNA 2D Structure
 label.clustalx_colours = Clustalx colours
@@ -704,8 +692,9 @@ label.load_tree_for_sequence_set = Load a tree for this sequence set
 label.export_image = Export Image
 label.vamsas_store = VAMSAS store
 label.translate_cDNA = Translate as cDNA
-label.linked_view_title = Linked cDNA and protein view
-label.align = Align
+label.reverse = Reverse
+label.reverse_complement = Reverse Complement
+label.linked_view_title = Linked CDS and protein view
 label.extract_scores = Extract Scores
 label.get_cross_refs = Get Cross-References
 label.sort_alignment_new_tree = Sort Alignment With New Tree
@@ -717,7 +706,6 @@ label.use_registry = Use Registry
 label.add_local_source = Add Local Source
 label.set_as_default = Set as Default
 label.show_labels = Show labels
-label.background_colour = Background Colour
 action.background_colour = Background Colour...
 label.associate_nodes_with = Associate Nodes With
 label.jalview_pca_calculation = Jalview PCA Calculation
@@ -743,8 +731,8 @@ label.move_url_down = Move URL Down
 label.add_sbrs_definition = Add a SBRS Definition
 label.edit_sbrs_definition = Edit SBRS Definition
 label.delete_sbrs_definition = Delete SBRS Definition
-label.your_sequences_have_been_verified = Your sequences have been verified against known sequence databases. Some of the ids have been\n altered, most likely the start/end residue will have been updated.\n Save your alignment to maintain the updated id.\n\n
-label.sequence_names_updated = Sequence names updated
+label.your_sequences_have_been_verified = Your sequences have been verified against known sequence databases.\n(Use Calculate | Show flanking regions to show enclosing sequence.)\nTo preserve data changes, save your alignment.\n\n
+label.sequences_updated = Sequences updated
 label.dbref_search_completed = DBRef search completed
 label.show_all_chains = Show all chains
 label.fetch_all_param = Fetch all {0}
@@ -784,9 +772,9 @@ label.transformed_points_for_params = Transformed points for {0}
 label.graduated_color_for_params = Graduated Feature Colour for {0}
 label.select_backgroud_colour = Select Background Colour
 label.invalid_font = Invalid Font
-label.separate_multiple_accession_ids = Enter one or more PDB accession IDs separated by a semi-colon ";"
+label.separate_multiple_accession_ids = Enter one or more accession IDs separated by a semi-colon ";"
 label.separate_multiple_query_values = Enter one or more {0}s separated by a semi-colon ";"
-label.search_all = Enter one or more search values separated by a semi-colon ";" (Note: This Searches the entire PDB database)
+label.search_all = Enter one or more search values separated by a semi-colon ";" (Note: This searches the entire database)
 label.replace_commas_semicolons = Replace commas with semi-colons
 label.parsing_failed_syntax_errors_shown_below_param = Parsing failed. Syntax errors shown below {0}
 label.parsing_failed_unrecoverable_exception_thrown_param = \nParsing failed. An unrecoverable exception was thrown\:\n {0}
@@ -797,10 +785,14 @@ label.wswublast_client_credits = To display sequence features an exact Uniprot i
 label.blasting_for_unidentified_sequence = BLASTing for unidentified sequences
 label.select_columns_containing = Select columns containing
 label.select_columns_not_containing = Select columns that do not contain
+label.hide_columns_containing = Hide columns containing
+label.hide_columns_not_containing = Hide columns that do not contain
 option.trim_retrieved_seqs = Trim retrieved sequences
 label.trim_retrieved_sequences = When the reference sequence is longer than the sequence that you are working with, only keep the relevant subsequences.
-label.use_sequence_id_1 = Use $SEQUENCE_ID$ or $SEQUENCE_ID=/<regex>/=$
-label.use_sequence_id_2 = \nto embed sequence id in URL
+label.use_sequence_id_1 = Use $DB_ACCESSION$ or $DB_ACCESSION=/<regex>/=$
+label.use_sequence_id_2 = to embed accession id in URL
+label.use_sequence_id_3 = Use $SEQUENCE_ID$ similarly to embed sequence id
+label.use_sequence_id_4 = 
 label.ws_parameters_for = Parameters for {0}
 label.switch_server = Switch server
 label.choose_jabaws_server = Choose a server for running this service
@@ -808,7 +800,6 @@ label.services_at = Services at {0}
 label.rest_client_submit = {0} using {1}
 label.fetch_retrieve_from =Retrieve from {0}</html>
 label.fetch_retrieve_from_all_sources = Retrieve from all {0} sources in {1}<br>First is :{2}<html> 
-#label.feature_settings_click_drag = <html>Click/drag feature types up or down to change render order.<br/>Double click to select columns containing feature in alignment/current selection<br/>Pressing Alt will select columns outside features rather than inside<br/>Pressing Shift to modify current selection (rather than clear current selection)<br/>Press CTRL or Command/Meta to toggle columns in/outside features<br/></html>
 label.feature_settings_click_drag = Drag up or down to change render order.<br/>Double click to select columns containing feature.
 label.transparency_tip = Adjust transparency to 'see through' feature colours.
 label.opt_and_params_further_details = see further details by right-clicking
@@ -824,17 +815,10 @@ label.user_preset = User Preset
 label.service_preset = Service Preset
 label.run_with_preset = Run {0} with preset
 label.view_service_doc_url = <html>View <a href="{0}">{1}</a></html>
-label.submit_sequence = <html>Submit {0} {1} {2} {3} to<br/>{4}</html>
 action.by_title_param = By {0}
-label.alignment = Alignment
-label.secondary_structure_prediction = Secondary Structure Prediction
-label.sequence_database_search = Sequence Database Search
-label.analysis = Analysis
-label.protein_disorder = Protein Disorder 
 label.source_from_db_source = Sources from {0}
 label.from_msname = from {0}
 label.superpose_with = Superpose with
-action.do = Do
 label.scale_label_to_column = Scale Label to Column
 label.add_new_row = Add New Row
 label.edit_label_description = Edit Label/Description
@@ -878,7 +862,7 @@ label.service_url = Service URL
 label.copied_sequences = Copied sequences
 label.cut_sequences = Cut Sequences
 label.conservation_colour_increment = Conservation Colour Increment ({0})
-label.percentage_identity_thereshold = Percentage Identity Threshold ({0})
+label.percentage_identity_threshold = Percentage Identity Threshold ({0})
 label.error_unsupported_owwner_user_colour_scheme = Unsupported owner for User Colour scheme dialog
 label.save_alignment_to_file = Save Alignment to file
 label.save_features_to_file = Save Features to File
@@ -894,7 +878,6 @@ label.save_vamsas_document_archive = Save Vamsas Document Archive
 label.saving_vamsas_doc = Saving VAMSAS Document to {0}
 label.load_feature_colours = Load Feature Colours
 label.save_feature_colours = Save Feature Colour Scheme
-label.dataset_for = {0} Dataset for {1}
 label.select_startup_file = Select startup file
 label.select_default_browser = Select default web browser
 label.save_tree_as_newick = Save tree as newick file
@@ -922,15 +905,9 @@ error.null_from_clone1 = Null from clone1!
 error.implementation_error_sortbyfeature = Implementation Error - sortByFeature method must be one of FEATURE_SCORE, FEATURE_LABEL or FEATURE_DENSITY.
 error.not_yet_implemented = Not yet implemented
 error.unknown_type_dna_or_pep = Unknown Type {0} - dna or pep are the only allowed values.
-error.implementation_error_dont_know_thereshold_annotationcolourgradient = Implementation error: don't know about threshold setting for current AnnotationColourGradient.
-error.implementation_error_embeddedpopup_not_null = Implementation error - embeddedPopup must be non-null
-error.invalid_colour_for_mycheckbox = Invalid color for MyCheckBox
-error.implementation_error_unrecognised_render_object_for_features_type = Implementation Error: Unrecognised render object {0} for features of type {1}
-error.implementation_error_unsupported_feature_colour_object = Implementation error: Unsupported feature colour object.
+error.implementation_error_dont_know_threshold_annotationcolourgradient = Implementation error: don't know about threshold setting for current AnnotationColourGradient.
 error.invalid_separator_parameter = Invalid separator parameter - must be non-zero length
 error.alignment_cigararray_not_implemented = Alignment(CigarArray) not yet implemented
-error.weak_sequencei_equivalence_not_yet_implemented = Weak sequenceI equivalence not yet implemented.
-error.implementation_error_can_only_make_alignmnet_from_cigararray = Implementation Error - can only make an alignment view from a CigarArray of sequences.
 error.empty_view_cannot_be_updated = empty view cannot be updated.
 error.mismatch_between_number_of_sequences_in_block = Mismatch between number of sequences in block {0} ({1}) and the original view ({2})
 error.padding_not_yet_implemented = Padding not yet implemented
@@ -952,21 +929,18 @@ error.not_yet_implemented_cigar_object_from_cigar_string = NOT YET Implemented:
 error.implementation_bug_cigar_operation = Implementation Bug. Cigar Operation {0} {1} not one of {2}, {3}, or {4}.
 error.implementation_error_for_new_cigar = Implementation error for new Cigar(SequenceI)
 error.implementation_error_cigar_seq_no_operations = Implementation error: {0}th sequence Cigar has no operations.
-error.implementation_error_jmol_getting_data = Implementation error - Jmol seems to be still working on getting its data - report at http://issues.jalview.org/browse/JAL-1016
 error.implementation_error_no_pdbentry_from_index = Implementation error - no corresponding pdbentry (for index {0}) to add sequences mappings to
 error.jmol_version_not_compatible_with_jalview_version = Jmol version {0} is not compatible with this version of Jalview. Report this problem at issues.jalview.org
 error.not_implemented_remove = Remove: Not implemented
 error.not_implemented_clone = Clone: Not implemented
-error.implementation_error_chimera_getting_data = Implementation error - Chimera seems to be still working on getting its data - report at http://issues.jalview.org/browse/JAL-1016
 error.call_setprogressbar_before_registering_handler = call setProgressBar before registering the progress bar's handler.
 label.cancelled_params = Cancelled {0}
 error.implementation_error_cannot_show_view_alignment_frame = Implementation error: cannot show a view from another alignment in an AlignFrame.
-error.implementation_error_dont_know_about_thereshold_setting = Implementation error: don't know about threshold setting for current AnnotationColourGradient.
+error.implementation_error_dont_know_about_threshold_setting = Implementation error: don't know about threshold setting for current AnnotationColourGradient.
 error.eps_generation_not_implemented = EPS Generation not yet implemented
 error.png_generation_not_implemented = PNG Generation not yet implemented
 error.try_join_vamsas_session_another = Trying to join a vamsas session when another is already connected
 error.invalid_vamsas_session_id = Invalid vamsas session id
-error.implementation_error_cannot_create_groovyshell = Implementation Error. Cannot create groovyShell without Groovy on the classpath!
 label.groovy_support_failed = Jalview Groovy Support Failed
 label.couldnt_create_groovy_shell = Couldn't create the groovy Shell. Check the error log for the details of what went wrong.
 error.unsupported_version_calcIdparam = Unsupported Version for calcIdparam {0}
@@ -981,7 +955,6 @@ error.setstatus_called_non_existent_job_pane = setStatus called for non-existent
 error.implementation_error_cannot_find_marshaller_for_param_set =Implementation error: Can't find a marshaller for the parameter set
 error.implementation_error_old_jalview_object_not_bound =IMPLEMENTATION ERROR: old jalview object is not bound ! ({0})
 error.implementation_error_vamsas_doc_class_should_bind_to_type = Implementation Error: Vamsas Document Class {0} should bind to a {1} (found a {2})
-error.implementation_error_jalview_class_should_bind_to_type = Implementation Error: Jalview Class {0} should bind to a {1} (found a {2})
 error.invalid_vamsas_rangetype_cannot_resolve_lists = Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!
 error.implementation_error_maplist_is_null = Implementation error. MapList is null for initMapType.
 error.implementation_error_cannot_have_null_alignment = Implementation error: Cannot have null alignment property key
@@ -1023,10 +996,11 @@ error.implementation_error_need_to_have_httpresponse = Implementation Error: nee
 error.dbrefsource_implementation_exception =DBRefSource Implementation Exception
 error.implementation_error_dbinstance_must_implement_interface = Implmentation Error - getDbInstances must be given a class that implements jalview.ws.seqfetcher.DbSourceProxy (was given{0})
 error.implementation_error_must_init_dbsources =Implementation error. Must initialise dbSources
-label.view_controller_toggled_marked = {0} {1} columns {2} containing features of type {3}  across {4} sequence(s)
+label.view_controller_toggled_marked = {0} {1} columns {2} features of type {3}  across {4} sequence(s)
 label.toggled = Toggled
 label.marked = Marked
-label.not = not
+label.containing = containing
+label.not_containing = not containing
 label.no_feature_of_type_found = No features of type {0} found.
 label.submission_params = Submission {0}
 label.empty_alignment_job = Empty Alignment Job
@@ -1036,7 +1010,7 @@ label.pca_recalculating = Recalculating PCA
 label.pca_calculating = Calculating PCA
 label.select_foreground_colour = Choose foreground colour
 label.select_colour_for_text = Select Colour for Text
-label.adjunst_foreground_text_colour_thereshold = Adjust Foreground Text Colour Threshold
+label.adjunst_foreground_text_colour_threshold = Adjust Foreground Text Colour Threshold
 label.select_subtree_colour = Select Sub-Tree Colour
 label.create_new_sequence_features = Create New Sequence Feature(s)
 label.amend_delete_features = Amend/Delete Features for {0}
@@ -1056,7 +1030,6 @@ exception.mismatched_unseen_closing_char = Mismatched (unseen) closing character
 exception.mismatched_closing_char = Mismatched closing character {0}
 exception.mismatched_opening_char = Mismatched opening character {0} at {1}
 exception.invalid_datasource_couldnt_obtain_reader = Invalid datasource. Could not obtain Reader
-exception.index_value_not_in_range = {0}: Index value {1} not in range [0..{2}]
 exception.unterminated_cigar_string = Unterminated cigar string
 exception.unexpected_operation_cigar_string_pos = Unexpected operation {0} in cigar string (position {1} in {2}
 exception.couldnt_parse_responde_from_annotated3d_server = Couldn't parse response from Annotate3d server
@@ -1084,7 +1057,6 @@ exception.ranml_problem_parsing_data = Problem parsing data as RNAML ({0})
 exception.pfam_no_sequences_found = No sequences found (PFAM input)
 exception.stockholm_invalid_format = This file is not in valid STOCKHOLM format: First line does not contain '# STOCKHOLM'
 exception.couldnt_parse_sequence_line = Could not parse sequence line: {0}
-exception.error_parsing_line = Error parsing {0}
 exception.unknown_annotation_detected = Unknown annotation detected: {0} {1}
 exception.couldnt_store_sequence_mappings = Couldn't store sequence mappings for {0}
 exception.matrix_too_many_iteration = Too many iterations in {0} (max is {1})
@@ -1092,7 +1064,6 @@ exception.browser_not_found = Exception in finding browser: {0}
 exception.browser_unable_to_locate = Unable to locate browser: {0}
 exception.invocation_target_exception_creating_aedesc = InvocationTargetException while creating AEDesc: {0}
 exception.illegal_access_building_apple_evt= IllegalAccessException while building AppleEvent: {0}
-exception.instantiation_creating_aedesc = InstantiationException while creating AEDesc: {0}
 exception.unable_to_launch_url = Unable to launch URL: {0}
 exception.unable_to_create_internet_config = Unable to create an Internet Config instance: {0}
 exception.invocation_target_calling_url = InvocationTargetException while calling openURL: {0}
@@ -1101,8 +1072,6 @@ exception.interrupted_launching_browser = InterruptedException while launching b
 exception.das_source_doesnt_support_sequence_command = Source {0} does not support the sequence command.
 exception.invalid_das_source = Invalid das source: {0}
 exception.ebiembl_retrieval_failed_on = EBI EMBL XML retrieval failed on {0}:{1}
-label.no_embl_record_found = # No EMBL record retrieved for {0}:{1}
-label.embl_successfully_parsed = # Successfully parsed the {0} queries into an Alignment
 exception.no_pdb_records_for_chain = No PDB Records for {0} chain {1}
 exception.unexpected_handling_rnaml_translation_for_pdb = Unexpected exception when handling RNAML translation of PDB data
 exception.couldnt_recover_sequence_properties_for_alignment = Couldn't recover sequence properties for alignment
@@ -1143,7 +1112,7 @@ status.finished_searching_for_sequences_from = Finished searching for sequences
 label.eps_file = EPS file
 label.png_image = PNG image
 status.saving_file = Saving {0}
-status.export_complete = Export complete.
+status.export_complete = {0} Export completed.
 status.fetching_pdb = Fetching PDB {0}
 status.refreshing_news = Refreshing news
 status.importing_vamsas_session_from = Importing VAMSAS session from {0}
@@ -1163,7 +1132,7 @@ status.das_feature_fetching_complete = DAS Feature Fetching Complete
 status.fetching_db_refs = Fetching db refs
 status.loading_cached_pdb_entries = Loading Cached PDB Entries
 status.searching_for_pdb_structures = Searching for PDB Structures
-status.opening_file = opening file
+status.opening_file_for = opening file for
 status.colouring_chimera = Colouring Chimera
 label.font_doesnt_have_letters_defined = Font doesn't have letters defined\nso cannot be used\nwith alignment data
 label.font_too_small = Font size is too small
@@ -1177,7 +1146,7 @@ warn.user_defined_width_requirements = The user defined width for the\nannotatio
 label.couldnt_create_sequence_fetcher = Couldn't create SequenceFetcher
 warn.couldnt_create_sequence_fetcher_client = Could not create the sequence fetcher client. Check error logs for details.
 warn.server_didnt_pass_validation = Service did not pass validation.\nCheck the Jalview Console for more details.
-warn.url_must_contain = Sequence URL must contain $SEQUENCE_ID$ or a regex $SEQUENCE_ID=/<regex>/=$
+warn.url_must_contain = Sequence URL must contain $SEQUENCE_ID$, $DB_ACCESSION$, or a regex
 warn.urls_not_contacted = URLs that could not be contacted
 warn.urls_no_jaba = URLs without any JABA Services
 info.validate_jabaws_server = Validate JabaWS Server ?\n(Look in console output for results)
@@ -1193,8 +1162,8 @@ label.edit_jabaws_url = Edit JABAWS URL
 label.add_jabaws_url = Add new JABAWS URL
 label.news_from_jalview = News from http://www.jalview.org
 label.cut_paste_alignmen_file = Cut & Paste Alignment File
-label.enter_redundancy_thereshold = Enter the redundancy threshold
-label.select_dark_light_set_thereshold = <html><i>Select a dark and light text colour, then set the threshold to<br>switch between colours, based on background colour</i></html>
+label.enter_redundancy_threshold = Enter the redundancy threshold
+label.select_dark_light_set_threshold = <html><i>Select a dark and light text colour, then set the threshold to<br>switch between colours, based on background colour</i></html>
 label.select_feature_colour = Select Feature Colour
 label.delete_all = Delete all sequences
 warn.delete_all = <html>Deleting all sequences will close the alignment window.<br>Confirm deletion or Cancel.
@@ -1215,30 +1184,24 @@ label.no_colour_selection_in_scheme = Please make a colour selection before appl
 label.no_colour_selection_warn = Error saving colour scheme
 label.open_split_window? = Would you like to open as a split window, with cDNA and protein linked?
 label.open_split_window = Open split window
-label.no_mappings = No mappings found
-label.mapping_failed = No sequence mapping could be made between the alignments.<br>A mapping requires sequence names to match, and equivalent sequence lengths.
 action.no = No
 action.yes = Yes
 label.for = for
 label.select_by_annotation = Select/Hide Columns by Annotation
 action.select_by_annotation = Select/Hide Columns by Annotation...
 label.threshold_filter =  Threshold Filter
-action.hide = Hide
-action.select = Select
 label.alpha_helix = Alpha Helix
 label.beta_strand = Beta Strand
 label.turn = Turn
 label.select_all = Select All
 label.structures_filter = Structures Filter
 label.search_filter = Search Filter
-label.description = Description
 label.include_description= Include Description
 action.back = Back
 label.hide_insertions = Hide Insertions
 label.mark_as_representative = Mark as representative
 label.open_jabaws_web_page = Open JABAWS web page
-label.opens_the_jabaws_server_homepage = Opens the JABAWS server's homepage in web browser
-label.pdb_sequence_getcher = PDB Sequence Fetcher
+label.pdb_sequence_fetcher = PDB Sequence Fetcher
 label.result = result
 label.results = results
 label.structure_chooser = Structure Chooser
@@ -1249,7 +1212,7 @@ info.select_filter_option = Select Filter Option/Manual Entry
 info.associate_wit_sequence = Associate with Sequence
 label.search_result = Search Result
 label.found_structures_summary = Found Structures Summary
-label.configure_displayed_columns = Configure Displayed Columns
+label.configure_displayed_columns = Customise Displayed Options
 label.start_jalview = Start Jalview
 label.biojs_html_export = BioJS
 label.scale_as_cdna = Scale protein residues to codons
@@ -1276,7 +1239,39 @@ label.structure_chooser_filter_time = Structure Chooser - Filter time ({0})
 label.structure_chooser_no_of_structures = Structure Chooser - {0} Found ({1})
 info.no_pdb_entry_found_for = No PDB entry found for {0}
 exception.unable_to_detect_internet_connection = Jalview is unable to detect an internet connection
-exception.pdb_rest_service_no_longer_available = PDB rest services no longer available!
+exception.fts_rest_service_no_longer_available = {0} rest services no longer available!
 exception.resource_not_be_found = The requested resource could not be found
-exception.pdb_server_error = There seems to be an error from the PDB server
-exception.pdb_server_unreachable = Jalview is unable to reach the PDBe Solr server. \nPlease ensure that you are connected to the internet and try again.
+exception.fts_server_error = There seems to be an error from the {0} server
+exception.fts_server_unreachable = Jalview is unable to reach the {0} server. \nPlease ensure that you are connected to the internet and try again.
+label.nw_mapping = Needleman & Wunsch Alignment
+label.sifts_mapping = SIFTs Mapping
+label.mapping_method = Sequence \u27f7 Structure mapping method
+status.waiting_for_user_to_select_output_file = Waiting for user to select {0} file
+status.cancelled_image_export_operation = Cancelled {0} export operation
+info.error_creating_file = Error creating {0} file
+exception.outofmemory_loading_mmcif_file = Out of memory loading mmCIF File
+label.run_groovy = Run Groovy console script
+label.run_groovy_tip = Run the script in the Groovy console over this alignment
+label.couldnt_run_groovy_script = Failed to run Groovy script
+label.uniprot_sequence_fetcher = UniProt Sequence Fetcher
+action.next_page= >> 
+action.prev_page= << 
+label.next_page_tooltip=Next Page
+label.prev_page_tooltip=Previous Page
+exception.bad_request=Bad request. There is a problem with your input.
+exception.service_not_available=Service not available. The server is being updated, try again later.
+status.launching_3d_structure_viewer = Launching 3D Structure viewer...
+status.fetching_3d_structures_for_selected_entries = Fetching 3D Structures for selected entries...
+status.fetching_dbrefs_for_sequences_without_valid_refs = Fetching db refs for {0} sequence(s) without valid db ref required for SIFTS mapping
+status.fetching_3d_structures_for = Fetching 3D Structure for {0}
+status.obtaining_mapping_with_sifts = Obtaining mapping with SIFTS
+status.obtaining_mapping_with_nw_alignment = Obtaining mapping with NW alignment
+status.exporting_alignment_as_x_file = Exporting alignment as {0} file
+label.column = Column
+label.cant_map_cds = Unable to map CDS to protein\nCDS missing or incomplete
+label.operation_failed = Operation failed
+label.SEQUENCE_ID_no_longer_used = $SEQUENCE_ID$ is no longer used for DB accessions
+label.SEQUENCE_ID_for_DB_ACCESSION1 = Please review your URL links in the 'Connections' tab of the Preferences window:
+label.SEQUENCE_ID_for_DB_ACCESSION2 = URL links using '$SEQUENCE_ID$' for DB accessions now use '$DB_ACCESSION$'.
+label.do_not_display_again = Do not display this message again
+label.output_seq_details = Output Sequence Details to list all database references
diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties
index 6db40cf..e5b5e27 100644
--- a/resources/lang/Messages_es.properties
+++ b/resources/lang/Messages_es.properties
@@ -38,7 +38,6 @@ action.cancel = Cancelar
 action.create = Crear
 action.update = Actualizar
 action.delete = Borrar
-action.snapshot = Imagen
 action.clear = Limpiar
 action.accept = Aceptar
 action.select_ddbb = --- Seleccionar base de datos ---
@@ -118,12 +117,13 @@ action.save_as_default = Guardar como por defecto
 action.save_as = Guardar como
 action.save = Guardar
 action.cancel_fetch = Cancelar b�squeda
-action.save_omit_hidden_columns = Guardar / Omitir las columnas ocultas
 action.change_font = Cambiar Fuente
 action.change_font_tree_panel = Cambiar fuente (panel del �rbol)
 action.colour = Color
 action.calculate = Calcular
 action.select_all = Seleccionar Todo
+action.select_highlighted_columns = Seleccionar columnas resaltadas
+tooltip.select_highlighted_columns = Presione B para marcar las columnas resaltadas, Ctrl (o Cmd)-B para cambiarlas, y Alt-B para marcar todas menos las columnas resaltadas
 action.deselect_all = Deseleccionar Todo
 action.invert_selection = Invertir selecci�n
 action.using_jmol = Usar Jmol
@@ -134,21 +134,22 @@ action.show_group = Mostrar grupo
 action.fetch_db_references = Recuperar referencias a base de datos
 action.view_flanking_regions = Mostrar flancos
 label.view_flanking_regions = Mostrar los datos de la secuencia a ambos lados de las subsecuencias implicadas en este alineamiento
-label.str = Str: 
-label.seq = Seq: 
 label.structures_manager = Administrar estructuras
 label.nickname = Sobrenombre:
 label.url = URL: 
 label.input_file_url = Introducir URL en el fichero de entrada
-label.select_feature = Seleccionar funci�n:
-label.name = Nombre:
+label.select_feature = Seleccionar caracter�stica
+label.name = Nombre
+label.name\: = Nombre:
 label.name_param = Nombre: {0}
-label.group = Grupo:
+label.group = Grupo
+label.group\: = Grupo:
 label.group_name = Nombre del grupo
 label.group_description = Descripci�n del grupo
 label.edit_group_name_description = Editar nombre/descripci�n del grupo
 label.colour = Color:
-label.description = Descripci�n:
+label.description = Descripci�n
+label.description\: = Descripci�n:
 label.start = Comenzar:
 label.end = Terminar:
 label.current_parameter_set_name = Nombre actual del conjunto de par�metros:
@@ -208,8 +209,8 @@ label.nucleotide = Nucle
 label.to_new_alignment = A nuevo alineamiento
 label.to_this_alignment = A�adir a este alineamiento
 label.apply_colour_to_all_groups = Aplicar color a todos los grupos
-label.modify_identity_thereshold = Modificar el umbral de identidad...
-label.modify_conservation_thereshold = Modificar el umbral de conservaci�n...
+label.modify_identity_threshold = Modificar el umbral de identidad...
+label.modify_conservation_threshold = Modificar el umbral de conservaci�n...
 label.input_from_textbox = Introducir desde el cuadro de texto
 label.centre_column_labels = Centrar las etiquetas de las columnas
 label.automatic_scrolling = Desplazamiento autom�tico
@@ -217,7 +218,6 @@ label.documentation = Documentaci
 label.about = Acerca de...
 label.show_sequence_limits = Mostrar los l�mites de la secuencia
 label.feature_settings = Ajustar funciones...
-label.sequence_features = Funciones de la secuencia
 label.all_columns = Todas las columnas
 label.all_sequences = Todas las secuencias
 label.selected_columns = Columnas seleccionadas
@@ -277,7 +277,7 @@ label.order_by_params = Ordenar por {0}
 label.html_content = <html>{0}</html>
 label.paste_pdb_file= Pegar tu fichero PDB aqu�.
 label.paste_pdb_file_for_sequence = Pegar fichero PDB para la secuencia {0}
-label.could_not_parse_newick_file  = No se pudo analizar el fichero Newick\\\!\\n {0}
+label.could_not_parse_newick_file  = No se pudo analizar el fichero Newick\!\n {0}
 label.successfully_pasted_tcoffee_scores_to_alignment= Pegada exitosamente la puntuaci�n T-Coffee al alineamiento.
 label.failed_add_tcoffee_scores = Fallo al a�adir las puntuaciones T-Coffee: 
 label.successfully_pasted_annotation_to_alignment = Anotaci�n pegada exitosamente al alineamiento.
@@ -291,7 +291,6 @@ label.found_match_for = Buscar coincidencia para {0}
 label.font = Fuente:
 label.size = Talla:
 label.style = Estilo:
-label.enter_redundancy_threshold = Introducir el umbral de redundancia
 label.calculating = Calculando....
 label.modify_conservation_visibility = Modificar la visibilidad de conservaci�n
 label.colour_residues_above_occurence = Residuos de color por encima del % de aparici�n 
@@ -315,12 +314,11 @@ label.blog_item_published_on_date = {0} {1}
 label.select_das_service_from_table = Seleccionar servicio DAS de la tabla para leer una descripci�n completa aqu�.
 label.session_update = Actualizar sesi�n
 label.new_vamsas_session = Nueva sesi�n Vamsas
-label.load_vamsas_session = Cargar sesi�n Vamsas
-label.save_vamsas_session = Guardar sesi�n Vamsas
+action.save_vamsas_session = Guardar Sesi�n Vamsas
 label.select_vamsas_session_opened_as_new_vamsas_session= Selecciones una sesi�n vamsas para abrirla como una nueva sesi�n.
 label.open_saved_vamsas_session = Abrir una sesi�n VAMSAS guardada
 label.groovy_console = Consola Groovy 
-label.lineart = lineart
+label.lineart = Lineart
 label.dont_ask_me_again = No volver a preguntar
 label.select_eps_character_rendering_style = Seleccionar el car�cter EPS como estilo de visualizaci�n 
 label.invert_selection = Invertir selecci�n
@@ -335,39 +333,38 @@ label.example = Ejemplo
 label.example_param = Ejemplo: {0}
 label.select_file_format_before_saving = Debe seleccionar un formato de fichero antes de guardar!
 label.file_format_not_specified = Formato de fichero no especificado
-label.alignment_contains_hidden_columns = El alineamiento contiene columnas ocultas.\\nQuieres guardar s\u00F3lo el alineamiento visible?
 label.couldnt_save_file = No se pudo guardar el fichero: {0}
 label.error_saving_file = Error guardando el fichero
 label.remove_from_default_list = eliminar de la lista de defectuosos?
 label.remove_user_defined_colour = Eliminar el color definido por el usuario
 label.you_must_select_least_two_sequences = Debes seleccionar al menos 2 secuencias.
 label.invalid_selection = Selecci�n inv�lida
-label.principal_component_analysis_must_take_least_four_input_sequences = El an\u00E1lisis de la componente principal debe tomar\\nal menos 4 secuencias de entrada.
+label.principal_component_analysis_must_take_least_four_input_sequences = El an\u00E1lisis de la componente principal debe tomar\nal menos 4 secuencias de entrada.
 label.sequence_selection_insufficient = Selecci�n de secuencias insuficiente
 label.you_need_more_two_sequences_selected_build_tree = necesitas seleccionar m�s de dos secuencias para construir un �rbol!
 label.not_enough_sequences = No suficientes secuencias
-label.selected_region_to_tree_may_only_contain_residues_or_gaps = La regi\u00F3n seleccionada para construir un \u00E1rbol puede\\ncontener s\u00F3lo residuos o espacios.\\nPrueba usando la funci\u00F3n Pad en el men\u00FA de edici\u00F3n,\\n o uno de los m\u00FAltiples servicios web de alineamiento de secuencias.
+label.selected_region_to_tree_may_only_contain_residues_or_gaps = La regi\u00F3n seleccionada para construir un \u00E1rbol puede\ncontener s\u00F3lo residuos o espacios.\nPrueba usando la funci\u00F3n Pad en el men\u00FA de edici\u00F3n,\n o uno de los m\u00FAltiples servicios web de alineamiento de secuencias.
 label.sequences_selection_not_aligned = Las secuencias seleccionadas no est�n alineadas
-label.sequences_must_be_aligned_before_creating_tree = Las secuencias deben estar alineadas antes de crear el \u00E1rbol.\\nPrueba usando la funci\u00F3n Pad en el men\u00FA de editar,\\n o uno de los m\u00FAltiples servicios web de alineamiento de secuencias.
+label.sequences_must_be_aligned_before_creating_tree = Las secuencias deben estar alineadas antes de crear el \u00E1rbol.\nPrueba usando la funci\u00F3n Pad en el men\u00FA de editar,\n o uno de los m\u00FAltiples servicios web de alineamiento de secuencias.
 label.sequences_not_aligned = Secuencias no alineadas
 label.problem_reading_tree_file =  Problema al leer el fichero del �rbol
 label.possible_problem_with_tree_file = Posible problema con el fichero del �rbol
 label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation = Por favor seleccionar al menos tres bases de al menos una secuencia para poder realizar la traducci�n de cDNA.
 label.translation_failed = Translation Failed
-label.error_when_translating_sequences_submit_bug_report = Desafortunadamente, algo fue mal a la hora de traducir tus secuencias.\\nPor favor, revisa la consola Jalview java \\ny presenta un informe de error que incluya el seguimiento.
+label.error_when_translating_sequences_submit_bug_report = Desafortunadamente, algo fue mal a la hora de traducir tus secuencias.\nPor favor, revisa la consola Jalview java \ny presenta un informe de error que incluya el seguimiento.
 label.implementation_error  = Error de implementaci�n:
-label.automatically_associate_pdb_files_with_sequences_same_name = Quieres asociar autom�ticamente los {0} ficheros PDB con las secuencias del alineamiento que tengan el mismo nombre?
-label.automatically_associate_pdb_files_by_name = Asociar los ficheros PDB por nombre autom�ticamente
+label.automatically_associate_structure_files_with_sequences_same_name = Quieres asociar autom�ticamente los {0} ficheros estructura con las secuencias del alineamiento que tengan el mismo nombre?
+label.automatically_associate_structure_files_by_name = Asociar los ficheros estructura por nombre autom�ticamente
 label.ignore_unmatched_dropped_files_info = Quieres <em>ignorar</em> los {0} ficheros cuyos nombres no coincidan con ning�n IDs de las secuencias ?
 label.ignore_unmatched_dropped_files = Ignorar los ficheros sin coincidencias?
-label.enter_view_name = Introducir nombre visible (�?)
+label.enter_view_name = Introduzca un nombre para la vista
 label.enter_label = Introducir etiqueta
-label.enter_label_for_the_structure = Introducir una etiqueta para la estructura?
-label.pdb_entry_is_already_displayed = {0} Ya est\u00E1 mostrado.\\nQuieres volver a usar este visor?
+label.enter_label_for_the_structure = Introducir una etiqueta para la estructura
+label.pdb_entry_is_already_displayed = {0} Ya est\u00E1 mostrado.\nQuieres volver a usar este visor?
 label.map_sequences_to_visible_window = Mapa de secuencias en ventana visible: {0}
-label.add_pdbentry_to_view = Quieres a\u00F1adir {0} a la vista llamada\\n{1}\\n
+label.add_pdbentry_to_view = Quieres a\u00F1adir {0} a la vista llamada\n{1}\n
 label.align_to_existing_structure_view = Alinear a una estructura ya existente
-label.pdb_entries_couldnt_be_retrieved = Las siguientes entradas pdb no pueden ser extra\u00EDdas del PDB\\\:\\n{0}\\nPor favor, prueba descarg\u00E1ndolas manualmente.
+label.pdb_entries_couldnt_be_retrieved = Las siguientes entradas pdb no pueden ser extra\u00EDdas del PDB\:\n{0}\nPor favor, prueba descarg\u00E1ndolas manualmente.
 label.couldnt_load_file = No se pudo cargar el fichero
 label.couldnt_find_pdb_id_in_file = No se pudo encontrar un Id PDB en el fichero suministrado. Por favor, introduzca un Id para identificar esta estructura.
 label.no_pdb_id_in_file = No hay un Id PDB en el fichero
@@ -393,11 +390,11 @@ label.invalid_url = URL Invalido!
 label.error_loading_file = Error al cargar el fichero
 label.problems_opening_file = Encontrados problemas al abrir el fichero {0}!!
 label.file_open_error = Error al abrir el fichero
-label.no_das_sources_selected_warn = No han sido seleccionadas fuentes DAS.\\nPor favor, seleccione algunas fuentes y\\npruebe de nuevo.
+label.no_das_sources_selected_warn = No han sido seleccionadas fuentes DAS.\nPor favor, seleccione algunas fuentes y\npruebe de nuevo.
 label.no_das_sources_selected_title = No han sido seleccionadas fuentes DAS
-label.colour_scheme_exists_overwrite = El esquema de colores {0} ya existe.\\nContinuar guardando el esquema de colores como {1}?
+label.colour_scheme_exists_overwrite = El esquema de colores {0} ya existe.\nContinuar guardando el esquema de colores como {1}?
 label.duplicate_scheme_name = Duplicar nombre de esquema
-label.jalview_new_questionnaire = Hay un nuevo cuestionario disponible. Querr\u00EDa completarlo ahora ?\\n
+label.jalview_new_questionnaire = Hay un nuevo cuestionario disponible. Querr\u00EDa completarlo ahora ?\n
 label.jalview_user_survey = Encuesta de usuario Jalview 
 label.alignment_properties = Propiedades del alineamiento: {0}
 label.alignment_props = Propiedades del alineamiento
@@ -435,7 +432,6 @@ label.no_features_added_to_this_alignment = No hay funciones asociadas a este al
 label.features_can_be_added_from_searches_1 = (Las funciones pueden ser a�adidas de b�squedas o
 label.features_can_be_added_from_searches_2 = de ficheros de funciones Jalview / GFF)
 label.calculating_pca= Calculando PCA
-label.reveal_columns = Mostrar Columnas
 label.jalview_cannot_open_file = Jalview no puede abrir el fichero
 label.jalview_applet = Aplicaci�n Jalview  
 label.loading_data = Cargando datos
@@ -443,7 +439,6 @@ label.memory_stats = Memoria libre total: {0} MB; Memoria m
 label.calculating_tree = Calculando �rbol
 label.state_queueing = En cola 
 label.state_running = Procesando
-label.state_complete = Completar
 label.state_completed = Finalizado
 label.state_job_cancelled = �Trabajo cancelado!
 label.state_job_error = Error del trabajo!
@@ -457,8 +452,6 @@ label.load_associated_tree = Cargar 
 label.load_features_annotations = Cargar caracter�sticas/anotaciones ...
 label.export_features = Exportar caracter�sticas...
 label.export_annotations = Exportar anotaciones ...
-label.jalview_copy = Copiar (s�lo Jalview)
-label.jalview_cut = Cortar (s�lo Jalview)
 label.to_upper_case = Pasar a may�sculas
 label.to_lower_case = Pasar a min�sculas
 label.toggle_case = Alternar may�sculas y min�sculas
@@ -467,8 +460,9 @@ label.create_sequence_feature = Crear funci
 label.edit_sequence = Editar secuencia
 label.edit_sequences = Editar secuencias
 label.sequence_details = Detalles de la secuencia
-label.jmol_help = Ayuda de Jmol 
-label.all = Todo
+label.jmol_help = Ayuda de Jmol
+# Todos/Todas is gender-sensitive, but currently only used for feminine (cadena / anotaci�n)! 
+label.all = Todas
 label.sort_by = Ordenar por
 label.sort_by_score = Ordenar por puntuaci�n
 label.sort_by_density = Ordenar por densidad
@@ -484,15 +478,14 @@ label.reset_min_max_colours_to_defaults = Reiniciar los colores min y max colour
 label.align_structures_using_linked_alignment_views = Alinear las estructuras utlizando las {0} vistas de alineamiento enlazadas
 label.connect_to_session = Conectar a la sesi�n {0}
 label.threshold_feature_display_by_score = Filtrar la caracter�stica mostrada por puntuaci�n.
-label.threshold_feature_no_thereshold = Sin umbral
-label.threshold_feature_above_thereshold = Por encima del umbral
-label.threshold_feature_below_thereshold = Por debajo del umbral
-label.adjust_thereshold = Ajustar umbral
+label.threshold_feature_no_threshold = Sin umbral
+label.threshold_feature_above_threshold = Por encima del umbral
+label.threshold_feature_below_threshold = Por debajo del umbral
+label.adjust_threshold = Ajustar umbral
 label.toggle_absolute_relative_display_threshold = Cambiar entre mostrar el umbral absoluto y el relativo.
 label.display_features_same_type_different_label_using_different_colour = Mostrar las caracter�sticas del mismo tipo con una etiqueta diferente y empleando un color distinto (p.e. caracter�sticas del dominio)
 label.select_colour_minimum_value = Seleccionar el color para el valor m�nimo
 label.select_colour_maximum_value = Seleccionar el color para el valor m�ximo
-label.open_new_jmol_view_with_all_structures_associated_current_selection_superimpose_using_alignment = Abrir una nueva vista Jmol con todas las estructuras asociadas con la selecci�n acxtual y superponer las utilizando el alineamiento.
 label.open_url_param = Abrir URL {0}
 label.open_url_seqs_param = Abrir URL ({0}..) ({1} secuencias)
 label.load_pdb_file_associate_with_sequence = Cargar un fichero PDB y asociarlo con la secuencia {0}
@@ -543,10 +536,9 @@ label.histogram = Histograma
 label.logo = Logo
 label.non_positional_features = Caracter�sticas no posicionales
 label.database_references = Referencias a base de datos
-label.share_selection_across_views = Compartir la selecci�n en todas las vistas
-label.scroll_highlighted_regions = Desplazarse hasta las regiones resaltadas
+#label.share_selection_across_views = Compartir la selecci�n en todas las vistas
+#label.scroll_highlighted_regions = Desplazarse hasta las regiones resaltadas
 label.gap_symbol = S�mbolo del hueco
-label.alignment_colour = Color del alineamiento
 label.address = Direcci�n
 label.port = Puerto
 label.default_browser_unix = Navegador por defecto (Unix)
@@ -625,22 +617,15 @@ label.cut_paste = Cortar y pegar
 label.adjusting_parameters_for_calculation = Ajustar los par�metros para el c�lculo existente
 label.2d_rna_structure_line = 2D RNA {0}
 label.2d_rna_sequence_name = 2D RNA - {0}
-label.edit_name_and_description_current_group = Editar el nombre y la descripci�n del grupo actual.
-label.view_structure_for = Visualizar la estructura para {0}
-label.view_all_structures = Visualizar todas las {0} estructuras.
-label.view_all_representative_structures = Visualizar todas las {0} estructuras representativas.
-label.open_new_jmol_view_with_all_representative_structures_associated_current_selection_superimpose_using_alignment = Abrir una nueva vista de Jmol con todas las estructuras representativas\nasociadas con la selecci\u00F3n actual\nsuperpuesta con el alineamiento actual.
-label.associate_structure_with_sequence = Asociar estructura con la secuencia
+label.edit_name_and_description_current_group = Editar el nombre y la descripci�n del grupo actual
 label.from_file = desde fichero
 label.enter_pdb_id = Introducir PDB Id
-label.discover_pdb_ids = Buscar PDB ids
 label.text_colour = Color del texto
 label.structure = Estructura
-label.view_structure = Visualizar estructura
 label.clustalx_colours = Colores de Clustalx
 label.above_identity_percentage = Sobre % identidad
 label.create_sequence_details_report_annotation_for = Anotaci�n para {0}
-label.sequece_details_for = Detalles de la secuencia para {0}
+label.sequence_details_for = Detalles de la secuencia para {0}
 label.sequence_name = Nombre de la secuencia
 label.sequence_description = Descripci�n de la secuencia
 label.edit_sequence_name_description = Editar el nombre/descripci�n de la secuencia
@@ -669,7 +654,6 @@ label.use_registry = Utilizar el registro
 label.add_local_source = A�adir fuente local
 label.set_as_default = Establecer por defecto
 label.show_labels = Mostrar etiquetas
-label.background_colour = Color de fondo
 label.associate_nodes_with = Asociar nodos con
 label.jalview_pca_calculation = C�lculo del PCA por Jalview
 label.link_name = Nombre del enalce
@@ -692,8 +676,8 @@ label.move_url_down = Mover la URL hacia abajo
 label.add_sbrs_definition = A�adir una definici�n SBRS 
 label.edit_sbrs_definition = Editar una definici�n SBRS 
 label.delete_sbrs_definition = Borrar una definici�n SBRS 
-label.your_sequences_have_been_verified = Sus secuencias has sido verificadas en una base de datos de secuencias conocidas. Algunos de sus ID se han alterado y\n, probablemente, el residuo de inicio/fin se haya actualizado.\nGuarde su alineamiento para mantener el ID actualizado.\n\n 
-label.sequence_names_updated = Nombres de secuencia actualizados
+label.your_sequences_have_been_verified = Sus secuencias has sido verificadas en una base de datos de secuencias conocidas.\n(Usar Calcular | Mostrar flancos para ver ampliaci�n.)\nPara mantener los datos actualizados, guarde su alineamiento.\n\n 
+label.sequences_updated = Secuencias actualizadas
 label.dbref_search_completed = B�squeda de DBRef terminada
 label.show_all_chains = Mostrar todas las cadenas
 label.fetch_all_param = Recuperar todas {0}
@@ -738,8 +722,10 @@ label.select_columns_containing = Seleccione las columnas que contengan
 label.select_columns_not_containing = Seleccione las columnas que no contengan
 option.trim_retrieved_seqs = Ajustar las secuencias recuperadas
 label.trim_retrieved_sequences = Cuando la secuencia de referencia es m�s larga que la secuencia con la que est� trabajando, s�lo se mantienen las subsecuencias relevantes.
-label.use_sequence_id_1 = Utilice $SEQUENCE_ID$ o $SEQUENCE_ID=/<regex>/=$
-label.use_sequence_id_2 = \nto para embeber el id de la secuencia en una URL
+label.use_sequence_id_1 = Utilice $DB_ACCESSION$ o $DB_ACCESSION=/<regex>/=$
+label.use_sequence_id_2 = para embeber el ID de accesi�n en una URL
+label.use_sequence_id_3 = Utilice $SEQUENCE_ID$ de manera similar para embeber
+label.use_sequence_id_4 = el ID de la secuencia
 label.ws_parameters_for = Par�metros para {0}
 label.switch_server = Cambiar servidor
 label.open_jabaws_web_page = Abra el p�gina principal del servidor JABAWS en un navegador web
@@ -762,17 +748,10 @@ label.user_preset = Preselecci
 label.service_preset = Preselecci�n del servicio
 label.run_with_preset = Ejecutar {0} con preselecci�n
 label.view_service_doc_url = Visualizar <a href="{0}">{1}</a></html>
-label.submit_sequence = Enviar {0} {1} {2} {3} a<br/>{4}</html>
 action.by_title_param = por {0}
-label.alignment = Alineamiento
-label.secondary_structure_prediction = Predicci�n de la estructura secundaria
-label.sequence_database_search = B�squeda en base de datos de secuencias
-label.analysis = An�lisis
-label.protein_disorder = Desorden en la prote�na 
 label.source_from_db_source = Fuentes de {0}
 label.from_msname = de {0}
 label.superpose_with = Superponer con...
-action.do = Hacer
 label.scale_label_to_column = Ajustar la etiqueta a la columna
 label.add_new_row = A�adir nuevo fila
 label.edit_label_description = Editar etiqueta/descripci�n
@@ -805,7 +784,7 @@ label.invalid_name = Nombre no v
 label.set_proxy_settings = Por favor, configure su proxy en la pesta�a 'Conexiones' de la ventana de Preferencia
 label.proxy_authorization_failed = Autorizaci�n del proxy fallida
 label.internal_jalview_error = Error interno de Jalview
-label.secondary_structure_prediction_service_couldnt_be_located = No se ha podido encontrar el Servicio de Predicci�nd de la Estructura Secudaria {0} en {1}.
+label.secondary_structure_prediction_service_couldnt_be_located = No se ha podido encontrar el Servicio de Predicci�n de la Estructura Secundaria {0} en {1}.
 label.service_called_is_not_msa_service = El Servicio llamado \n{0}\nno es un \nServicio de Alineamiento M\u00FAltiple de Secuencias\!
 label.msa_service_is_unknown = El Servicio de Alineamiento M�ltiple llamado {0} es desconocido
 label.service_called_is_not_seq_search_service = El Servicio llamando \n{0}\nno es un \nServicio de B\u00FAsqueda de Secuencias\!
@@ -816,7 +795,7 @@ label.service_url = URL del servicio
 label.copied_sequences = Secuencias copiadas
 label.cut_sequences = Cortar secuencias
 label.conservation_colour_increment = Incremento de Conservaci�n del Color ({0})
-label.percentage_identity_thereshold = Umbral del Porcentaje de Identidad ({0})
+label.percentage_identity_threshold = Umbral del Porcentaje de Identidad ({0})
 label.error_unsupported_owwner_user_colour_scheme = Propietario no soportado para el di�logo del Esquema Crom�tico del Usuario
 label.save_alignment_to_file = Guardar Alineamiento en fichero
 label.save_features_to_file = Guardar Caracter�sticas en un fichero
@@ -832,7 +811,6 @@ label.save_vamsas_document_archive = Guardar el archivo de documento Vamsas
 label.saving_vamsas_doc = Guardando el documento VAMSAS en {0}
 label.load_feature_colours = Cargar colores de caracter�sticas
 label.save_feature_colours = Guardar esquema crom�tico de caracter�sticas
-label.dataset_for = {0} conjunto de datos para {1}
 label.select_startup_file = Seleccionar fichero de arranque
 label.select_default_browser = Seleccionar navegador web por defecto
 label.save_tree_as_newick = Guardar �rbol como fichero newick
@@ -860,15 +838,9 @@ error.null_from_clone1 = Nulo de clone1!
 error.implementation_error_sortbyfeature = Error de implementaci�n - sortByFeature debe ser uno de FEATURE_SCORE, FEATURE_LABEL o FEATURE_DENSITY.
 error.not_yet_implemented = No se ha implementado todav�a
 error.unknown_type_dna_or_pep = Tipo desconocido {0} - dna o pep son los �nicos valores permitidos
-error.implementation_error_dont_know_thereshold_annotationcolourgradient = Error de implementaci�n: no se conoce el valor umbral para el AnnotationColourGradient actual.
-error.implementation_error_embeddedpopup_not_null = Error de implementaci�n - embeddedPopup debe ser no nulo.
-error.invalid_colour_for_mycheckbox = Color no v�lido para MyCheckBox
-error.implementation_error_unrecognised_render_object_for_features_type = Error de implementaci�n: no se reconoce el objeto de representaci�n {0} para las caracter�sticas de tipo {1}
-error.implementation_error_unsupported_feature_colour_object = Error de implementaci�n: objeto de color de caracter�sticas no soportado.
+error.implementation_error_dont_know_threshold_annotationcolourgradient = Error de implementaci�n: no se conoce el valor umbral para el AnnotationColourGradient actual.
 error.invalid_separator_parameter = Separador de par�metros no v�lido - debe tener longitud mayor que cero
 error.alignment_cigararray_not_implemented = Alignment(CigarArray) no se ha implementado todav�a
-error.weak_sequencei_equivalence_not_yet_implemented = Equivalencia d�bil sequenceI no se ha implementado todav�a.
-error.implementation_error_can_only_make_alignmnet_from_cigararray = Error de implementaci�n - s�lo se puede construir un vista de alineamiento a partir de una CigarArray de secuencias.
 error.empty_view_cannot_be_updated = una vista vac�a no se puede actualizar.
 error.mismatch_between_number_of_sequences_in_block = No hay coincidencia entre el n�mero de secuencias en el bloque {0} ({1}) y la vista original ({2})
 error.padding_not_yet_implemented = El relleno no se ha implementado todav�a
@@ -890,21 +862,18 @@ error.not_yet_implemented_cigar_object_from_cigar_string = No implementado todav
 error.implementation_bug_cigar_operation = Bug de implementaci�n. La operaci�n Cigar {0} {1} no es ni {2}, ni {3} ni {4}.
 error.implementation_error_for_new_cigar = Error de implementaci�n en new Cigar(SequenceI)
 error.implementation_error_cigar_seq_no_operations = Error de implementaci�n: la {0}a secuencia Cigar no tiene operaciones.
-error.implementation_error_jmol_getting_data = Error de implementaci�n - Jmol parece estar todav�a intentando recuperar sus datos - informe de ello en http://issues.jalview.org/browse/JAL-1016
 error.implementation_error_no_pdbentry_from_index = Error de implementaci�n - no existe la correspondiente entrada pdb (para el �ndice {0}) para a�adir el mapeo de secuencias a
 error.jmol_version_not_compatible_with_jalview_version = La versi�n {0} de Jmol no es compatible con esta versi�n de Jalview. Informe de este problema en http://issues.jalview.org
 error.not_implemented_remove = Borrar: no implementado
 error.not_implemented_clone = Clonar: no implementado
-error.implementation_error_chimera_getting_data = Error de implementaci�n - Chimera parece estar todav�a intentando recuperar sus datos - informe de ello en http://issues.jalview.org/browse/JAL-1016
 error.call_setprogressbar_before_registering_handler = llamada a setProgressBar antes de registrar el manejador de la barra de estado
 label.cancelled_params = {0} cancelado
 error.implementation_error_cannot_show_view_alignment_frame = Error de implementaci�n: no es posible mostrar una vista de otro alineamiento en un AlignFrame.
-error.implementation_error_dont_know_about_thereshold_setting = Error de implementaci�n: no se conoce la configuraci�n del umbral para el AnnotationColourGradient actual.
+error.implementation_error_dont_know_about_threshold_setting = Error de implementaci�n: no se conoce la configuraci�n del umbral para el AnnotationColourGradient actual.
 error.eps_generation_not_implemented = La generaci�n de EPS no se ha implementado todav�a
 error.png_generation_not_implemented = La generaci�n de PNG no se ha implementado todav�a
 error.try_join_vamsas_session_another = Tratando de establecer una sesi�n VAMSAS cuando ya hab�a otra conectada
 error.invalid_vamsas_session_id = Identificador de sesi�n VAMSAS no v�lido
-error.implementation_error_cannot_create_groovyshell = Error de implementaci�n:no se puede crear groovyShell sin Groovy en el classpath
 label.groovy_support_failed = El soporte Groovy de Jalview ha fallado
 label.couldnt_create_groovy_shell = No es posible crear el shell de Groovy. Compruebe el fichero de log para conocer los detalles.
 error.unsupported_version_calcIdparam = Versi�n no soportada de {0}
@@ -919,7 +888,6 @@ error.setstatus_called_non_existent_job_pane = se lllamado a setStatus para el p
 error.implementation_error_cannot_find_marshaller_for_param_set =Error de implementaci�n: no puede encontrar un marshaller para el conjunto de par�metros
 error.implementation_error_old_jalview_object_not_bound =Error de implementaci�n: �el objeto Jalview antiguo no est� enlazado! ({0})
 error.implementation_error_vamsas_doc_class_should_bind_to_type = Error de implementaci�n: la clase de documento VAMSAS {0} debe enlazar a {1} (pero se ha encontrado que lo est� a {2})
-error.implementation_error_jalview_class_should_bind_to_type = Error de implementaci�n: la clase Jalview {0} debe enlazar a {1} (pero se ha encontrado que lo est� a {2})
 error.invalid_vamsas_rangetype_cannot_resolve_lists = RangeType VAMSAS no v�lido - �no es posible resolver ambas listas de Pos y Seg con los valores elegidos!
 error.implementation_error_maplist_is_null = Error de implementaci�n. MapList es nulo en initMapType.
 error.implementation_error_cannot_have_null_alignment = Error de implementaci�n: no es posible tener una clave nula en el alineamiento
@@ -961,10 +929,11 @@ error.implementation_error_need_to_have_httpresponse = Error de implementaci
 error.dbrefsource_implementation_exception = Excepci�n de implementaci�n DBRefSource
 error.implementation_error_dbinstance_must_implement_interface = Error de Implementaci�n- getDbInstances debe recibir una clase que implemente jalview.ws.seqfetcher.DbSourceProxy (recibi� {0})
 error.implementation_error_must_init_dbsources =Error de implementaci�n. Debe inicializar dbSources
-label.view_controller_toggled_marked = {0} {1} columnas {2} conteniendo caracter�sticas del tipo  {3} en {4} secuencia(s)
+label.view_controller_toggled_marked = {0} {1} columnas {2} caracter�sticas del tipo {3} en {4} secuencia(s)
 label.toggled = Invertida
 label.marked = Marcada
-label.not = no
+label.containing = conteniendo
+label.not_containing = no conteniendo
 label.no_feature_of_type_found = No se han encontrado caracter�sticas del tipo {0}.
 label.submission_params = Env�o {0}
 label.empty_alignment_job = Trabajo de alineamiento vac�o
@@ -974,7 +943,7 @@ label.pca_recalculating = Recalculando PCA
 label.pca_calculating = Calculando PCA
 label.select_foreground_colour = Escoger color del primer plano
 label.select_colour_for_text = Seleccione el color del texto
-label.adjunst_foreground_text_colour_thereshold = Ajustar el umbral del color del texto en primer plano
+label.adjunst_foreground_text_colour_threshold = Ajustar el umbral del color del texto en primer plano
 label.select_subtree_colour = Seleccioanr el color del sub-�rbol
 label.create_new_sequence_features = Crear nueva(s) caracter�stica(s) de secuencia
 label.amend_delete_features = Arrelgar/Borrar caracter�sticas de {0}
@@ -994,7 +963,6 @@ exception.mismatched_unseen_closing_char = Discordancia (no vista) en el car
 exception.mismatched_closing_char = Car�cter de cierre discordante {0}
 exception.mismatched_opening_char = Car�cter de apertura discordante {0} en {1}
 exception.invalid_datasource_couldnt_obtain_reader = Fuente de datos no v�lida. No es posible obtener el Reader
-exception.index_value_not_in_range = {0}: el valor del �ndice {1} en se encuentra en el rango [0..{2}]
 exception.unterminated_cigar_string = Cadena cigar sin terminar
 exception.unexpected_operation_cigar_string_pos = Operaci�n no esperada {0} en una cadena cigar (posici�n {1} en {2})
 exception.couldnt_parse_responde_from_annotated3d_server = No es posible parsear la respuesta procedente del servidor Annotate3d 
@@ -1022,7 +990,6 @@ exception.ranml_problem_parsing_data = Problema parseando los datos como RNAML (
 exception.pfam_no_sequences_found = No se han encontrado secuencias (entrada PFAM)
 exception.stockholm_invalid_format = Este fichero no es tiene un formato STOCKHOLM v�lido: la primera l�nea no contiene '# STOCKHOLM'
 exception.couldnt_parse_sequence_line = No es posible parse la l�nea de secuencia: {0}
-exception.error_parsing_line = Error parseando {0}
 exception.unknown_annotation_detected = Anotaci�n desconocida detectada: {0} {1}
 exception.couldnt_store_sequence_mappings = No es posible almacenar los mapeos de secuencia para {0}
 exception.matrix_too_many_iteration = Demasiadas iteraciones en {0} (el m�ximo es {1})
@@ -1030,7 +997,6 @@ exception.browser_not_found = Excepci
 exception.browser_unable_to_locate = Imposible encontrar el navegador: {0}
 exception.invocation_target_exception_creating_aedesc = InvocationTargetException mientras se creaba AEDesc: {0}
 exception.illegal_access_building_apple_evt= IllegalAccessException mientras se constru�a AppleEvent: {0}
-exception.instantiation_creating_aedesc = InstantiationException mientras se creaba AEDesc: {0}
 exception.unable_to_launch_url = Imposible lanzar la URL: {0}
 exception.unable_to_create_internet_config = Imposible crear una instancia de configuraci�n de Internet: {0}
 exception.invocation_target_calling_url = InvocationTargetException mientras se invocaba openURL: {0}
@@ -1039,8 +1005,6 @@ exception.interrupted_launching_browser = InterruptedException mientras se lanza
 exception.das_source_doesnt_support_sequence_command = La fuente {0} no soporta el comando sequence.
 exception.invalid_das_source = Fuente DAS no v�lida: {0}
 exception.ebiembl_retrieval_failed_on = La recuperaci�n de datos EBI EMBL XML ha fallado en {0}:{1}
-label.no_embl_record_found = # No se ha recuperado ning�n registro EMBL de {0}:{1}
-label.embl_successfully_parsed = # Se han parseado con �xito las consultas {0} en un alineamiento
 exception.no_pdb_records_for_chain = No se han encontrado registros {0} para la cadena {1}
 exception.unexpected_handling_rnaml_translation_for_pdb = Excepcion inesperada cuando se traduc�an a RNAML los datos PDB
 exception.couldnt_recover_sequence_properties_for_alignment = No es posible recuperar las propiedades de la secuencia para el alineamiento
@@ -1054,7 +1018,7 @@ error.implementation_error_cannot_find_service_url_in_given_set = Error de imple
 error.implementation_error_cannot_find_service_url_in_given_set_param_store = Error de implementaci�n: la URL del servicio en el conjunto de URL  para este almac�n de par�metros del servicio({0})
 exception.jobsubmission_invalid_params_set = Conjunto de par�metros no v�lido. Comprueba la implementaci�n de Jalview
 exception.notvaliddata_group_contains_less_than_min_seqs = El grupo contiene menos de {0} secuencias.
-exception.outofmemory_loading_pdb_file = Sin menoria al cargar el fichero PDB
+exception.outofmemory_loading_pdb_file = Sin memoria al cargar el fichero PDB
 exception.eps_coudnt_write_output_file = No es posible escribir el fichero de salida: {0}
 exception.eps_method_not_supported = M�todo actualmente no suportado por la versi�n {0} de EpsGraphics2D
 exception.eps_unable_to_get_inverse_matrix = Imposible obtener la inversa de la matrix: {0}
@@ -1083,7 +1047,7 @@ status.fetching_pdb = Recuperando PDB {0}
 status.refreshing_news = Refrescando noticias
 status.importing_vamsas_session_from = Importando sesi�n VAMSAS de {0}
 status.opening_params = Abriendo {0}
-status.waiting_sequence_database_fetchers_init = Esperando la inicializaci�n de los recuperadores de bases de datos de secuencias
+status.waiting_sequence_database_fetchers_init = Esperando inicializaci�n de los recuperadores de bases de datos de secuencias
 status.init_sequence_database_fetchers = Inicializando recuperadores de bases de datos de secuencias
 status.fetching_sequence_queries_from = Recuperando {0} consultas de secuencias de {1}
 status.finshed_querying = Consulta finalizada
@@ -1108,7 +1072,7 @@ warn.user_defined_width_requirements = La anchura definida por el usuario para l
 label.couldnt_create_sequence_fetcher = No es posible crear SequenceFetcher
 warn.couldnt_create_sequence_fetcher_client = No es posible crear el cliente de recuperador de secuencias. Comprueba el fichero de log para m�s detalles.
 warn.server_didnt_pass_validation = El servicio no ha pasado la validaci\u00F3n.\nCompruebe la consola de Jalview para m\u00E1s detalles.
-warn.url_must_contain = La URL de la secuencia debe contener $SEQUENCE_ID$ o un regex $SEQUENCE_ID=/<regex>/=$
+warn.url_must_contain = La URL de la secuencia debe contener $SEQUENCE_ID$, $DB_ACCESSION$ o un regex
 info.validate_jabaws_server = \u00BFValidar el servidor JabaWS?\n(Consulte la consola de salida para obtener los resultados)
 label.test_server = �Probar servidor?
 info.you_want_jalview_to_find_uniprot_accessions = \u00BFDesea que Jalview encuentre\nUniprot Accession ids para los nombres de secuencias dados?
@@ -1122,8 +1086,8 @@ label.edit_jabaws_url = Editar JABAWS URL
 label.add_jabaws_url = A�adir nueva JABAWS URL
 label.news_from_jalview = Noticias de http://www.jalview.org
 label.cut_paste_alignmen_file = Cortar & Pegar fichero de alineamiento
-label.enter_redundancy_thereshold = Introducir el umbral de redundancia
-label.select_dark_light_set_thereshold = <i>Seleccionar un color oscuro y un color claro para el texto y establecer el umbral en que<br>cambiar entre colores, bas�ndose en el color de fondo</i>
+label.enter_redundancy_threshold = Introducir el umbral de redundancia
+label.select_dark_light_set_threshold = <i>Seleccionar un color oscuro y un color claro para el texto y establecer el umbral en que<br>cambiar entre colores, bas�ndose en el color de fondo</i>
 label.select_feature_colour = Seleccionar color de las caracter�sticas
 label.ignore_gaps_consensus = Ignorar huecos en el consenso
 label.show_group_histogram = Mostrar histograma de grupo
@@ -1133,4 +1097,182 @@ label.show_histogram = Mostrar histograma
 label.show_logo = Mostrar logo
 label.normalise_logo = Normalizar logo
 label.no_colour_selection_in_scheme = Por favor, seleccione un color antes de aplicar el esquema crom�tico
-label.no_colour_selection_warn = Error guardando el esquema crom�tico
\ No newline at end of file
+label.no_colour_selection_warn = Error guardando el esquema crom�tico
+label.start_jalview = Arrancar Jalview
+warn.urls_no_jaba=URLs sin servicios JABA
+label.structure_chooser_filter_time=Selector de Estructura - Tiempo de filtro ({0})
+action.back=Atras
+action.choose_annotations=Escoja Anotaciones...
+action.feature_settings=Ajustes de caracter�sticas...
+info.invalid_msa_notenough=No suficientes datos de sequencia para alinear
+label.result=resultado
+label.results=resultados
+label.struct_from_pdb=Procesar la estructura secundaria PDB
+label.hide_selected_annotations=Ocultar anotaciones seleccionados
+info.select_annotation_row=Seleccionar Fila de Anotaciones
+label.delete_all=Borrar todas las secuencias
+label.rnalifold_calculations=Predicci�n RNAAliFold
+label.rnalifold_settings=Cambiar ajustes RNAAliFold...
+label.sort_ann_by=Ordenar anotaciones por
+info.enter_search_text_here=Introducir texto de b�squeda aqu�
+action.load_vamsas_session=Cargar Sesi�n Vamsas...
+label.show_all_al_annotations=Mostrar anotaciones relacionadas con el alineamiento 
+label.hide_all_al_annotations=Ocultar anotaciones relacionadas con el alineamiento
+label.show_all_seq_annotations=Mostrar anotaciones relacionadas con las secuencias
+label.hide_all_seq_annotations=Ocultar anotaciones relacionadas con las secuencias
+label.close_viewer=Cerrar Visualizador
+label.all_views=Todas las Vistas
+label.search_result=Resultado de b�squeda
+action.fetch_sequences=Recuperar Secuencias...
+label.hide_annotations=Ocultar anotaciones
+action.export_groups=Exportar Grupos
+action.no=No
+action.yes=S�
+label.export_settings=Exportar Ajustes
+label.linked_view_title=Vista vinculada de cDNA y prote�na
+label.couldnt_read_data=No se pudo leer los datos
+status.opening_file_for=abriendo fichero para
+label.except_selected_sequences=Todo excepto secuencias seleccionadas
+label.structure_chooser_no_of_structures=Selector de Estructuras - {0} Encontr� ({1})
+label.search_filter=filtro de b�squeda
+label.sort_annotations_by_label=Ordenar por etiqueta
+label.sort_annotations_by_sequence=Ordenar por secuencia
+label.biojs_html_export=BioJS
+info.enter_search_text_to_enable=Introducir Texto de B�squeda para Habilitar
+label.autoadd_secstr=A�adir anotaci�n de estructura secundaria al alineamiento
+action.annotations=Anotaciones
+label.nuc_alignment_colour=Color del Alineamiento Nucleot�dico
+label.copy_format_from=Copiar formato de
+label.chimera=Chimera
+label.open_split_window=Abrir ventana dividida
+label.open_split_window?=�Quieres abrir ventana dividida, con cDNA y prote�na vinculadas?
+status.searching_for_pdb_structures=Buscando Estructuras PDB
+label.scale_as_cdna=Adaptar residuos proteicos a los codones
+action.export_hidden_sequences=Exportar Secuencias Ocultas
+action.export_hidden_columns=Exportar Columnas Ocultas
+label.found_structures_summary=Resumen de Estructuras Encontradas
+info.no_pdb_entry_found_for=No se ha encontrada entrada PDB para {0}
+label.turn=Giro
+label.beta_strand=L�mina Beta
+label.show_first=Mostrar arriba
+label.show_last=Mostrar por debajo
+label.split_window=Ventana Dividida
+label.invalid_search=Texto de b�squeda inv�lido
+action.export_annotations=Exportar Anotaciones
+action.set_as_reference=Marcar como Referencia
+action.unmark_as_reference=Desmarcar como Referencia
+action.set_text_colour=Color de Texto...
+label.chimera_failed=Error al abrir Chimera - est� instalado?\nCompruebe ruta en Preferencias, Estructura
+label.find=Buscar
+label.select_pdb_file=Seleccionar Fichero PDB
+label.structures_filter=Filtro de Estructuras
+label.scale_protein_to_cdna=Adaptar prote�na a cDNA
+status.loading_cached_pdb_entries=Cargando Entradas PDB en Cach�
+label.select=Seleccionar :
+label.select_by_annotation=Seleccionar/Ocultar Columnas por Anotaci�n
+action.select_by_annotation=Seleccionar/Ocultar Columnas por Anotaci�n...
+action.export_features=Exportar Caracter�sticas
+error.invalid_regex=Expresi�n regular inv�lida
+label.autoadd_temp=A�adir anotaci�n factor de temperatura al alineamiento
+label.chimera_path_tip=Jalview intentar� primero las rutas introducidas aqu�, Y si no las rutas usuales de instalaci�n
+label.structure_chooser=Selector de Estructuras
+label.structure_chooser_manual_association=Selector de Estructuras - asociaci�n manual
+label.threshold_filter=Filtro de Umbral
+label.add_reference_annotations=A�adir anotaciones de referencia
+label.hide_insertions=Ocultar Inserciones
+info.change_threshold_mode_to_enable=Cambiar Modo de Umbral para Habilitar
+label.separate_multiple_query_values=Introducir uno o mas {0}s separados por punto y coma ";"
+label.let_chimera_manage_structure_colours=Deja que Chimera maneje colores de estructuras
+label.view_rna_structure=Estructura 2D VARNA
+label.scale_protein_to_cdna_tip=Hacer a los residuos de prote�nas de la misma anchura que los codones en ventanas divididas
+label.colour_with_chimera=Colorear con Chimera
+label.show_pdbstruct_dialog=Datos de Estructura 3D...
+label.hide_all=Ocultar todos
+label.invert=Invertir
+tooltip.aacon_settings=Cambiar ajustes para c�lculos AACon
+label.mark_as_representative=Marcar como representativa
+label.include_description=Incluir Descripci�n
+label.for=para
+label.invalid_chimera_path=Ruta de Chimera no encontrada o no ejecutable
+info.search_in_annotation_label=Buscar en etiqueta de {0}
+info.search_in_annotation_description=Buscar en descripci�n de {0}
+label.select_many_views=Seleccionar m�ltiples vistas
+label.add_annotations_for=A�adir anotaciones para
+action.background_colour=Color de Fondo...
+warn.urls_not_contacted=URLs que no pudieron ser contactados
+label.prot_alignment_colour=Color del Alineamiento Proteico
+info.associate_wit_sequence=Asociar con secuencia
+label.protein=Prote�na
+warn.oneseq_msainput_selection=La selecci�n actual s�lo contiene una �nica secuencia. �Quieres enviar todas las secuencias para la alineaci�n en su lugar?
+label.use_rnaview=Usar RNAView para estructura secondaria
+label.search_all=Introducir uno o m�s valores de b�squeda separados por punto y coma ";" (Nota: buscar� en toda la base de datos PDB)
+label.confirm_close_chimera=Cerrar� la conexi�n de Jalview a {0}.<br>�Quieres cerrar la ventana Chimera tambi�n?
+tooltip.rnalifold_calculations=Se calcular�n predicciones de estructura secondaria de RNA para el alineaminento, y se actualizar�n si se efectuan cambios
+tooltip.rnalifold_settings=Modificar la configuraci�n de la predicci�n RNAAlifold. �selo para ocultar o mostrar resultados del c�lculo de RNA, o cambiar par�metros de el plegado de RNA.
+label.show_selected_annotations=Mostrar anotaciones seleccionadas
+status.colouring_chimera=Coloreando Chimera
+label.configure_displayed_columns=Configurar Columnas Mostradas
+exception.resource_not_be_found=El recurso solicitado no se ha encontrado
+label.aacon_calculations=c�lculos AACon
+label.pdb_web-service_error=Error de servicio web PDB
+exception.unable_to_detect_internet_connection=Jalview no puede detectar una conexi�n a Internet
+label.chimera_path=Ruta de acceso a programa Chimera
+warn.delete_all=<html>Borrar todas las secuencias cerrar� la ventana del alineamiento.<br>Confirmar o Cancelar.
+label.select_all=Seleccionar Todos
+label.alpha_helix=H�lice Alfa
+label.chimera_help=Ayuda para Chimera
+label.find_tip=Buscar alineamiento, selecci�n o IDs de secuencia para una subsecuencia (sin huecos)
+label.structure_viewer=Visualizador de estructura for defecto
+label.embbed_biojson=Incrustar BioJSON al exportar HTML
+label.transparency_tip=Ajustar la transparencia a "ver a trav�s" los colores de las caracter�sticas.
+label.choose_annotations=Escoja anotaciones
+label.structure_options=Opciones Estructura
+label.view_name_original=Original
+label.aacon_settings=Cambiar Ajustes AACon...
+tooltip.aacon_calculations=Actualizar c�lculos AACon autom�ticamente.
+info.select_filter_option=Escoger Opci�n de Filtro / Entrada Manual
+info.invalid_msa_input_mininfo=Necesita por lo menos dos secuencias con al menos 3 residuos cada una, sin regiones ocultas entre ellas.
+label.chimera_missing=Visualizador de estructura Chimera no encontrado.<br/>Por favor, introduzca la ruta de Chimera,<br/>o descargar e instalar la UCSF Chimera.
+label.save_as_biojs_html=Guardar como HTML BioJs
+exception.fts_server_unreachable=Jalview no puede conectar con el servidor {0}. \nPor favor aseg�rese de que est� conectado a Internet y vuelva a intentarlo.
+exception.outofmemory_loading_mmcif_file=Sin memoria al cargar el fichero mmCIF
+label.hide_columns_not_containing=Ocultar las columnas que no contengan
+label.pdb_sequence_fetcher=Recuperador de secuencias PDB
+exception.fts_server_error=Parece que hay un error desde el servidor {0}
+exception.service_not_available=Servicio no disponible. El servidor se est� actualizando, vuelva a intentarlo m�s tarde.
+status.waiting_for_user_to_select_output_file=Esperando que el usuario seleccione el fichero {0}
+action.prev_page=<< 
+status.cancelled_image_export_operation=Operaci�n de exportaci�n {0} cancelada
+label.couldnt_run_groovy_script=No se ha podido ejecutar el script Groovy
+exception.bad_request=Solicitud incorrecta. Hay un problema con su entrada.
+label.run_groovy=Ejecutar script Groovy desde la consola
+action.next_page=>> 
+label.uniprot_sequence_fetcher=Recuperador de secuencias UniProt
+label.prev_page_tooltip=P�gina anterior
+label.reverse=Invertir
+label.hide_columns_containing=Ocultar las columnas que contengan
+label.nucleotides=Nucle�tidos
+label.run_groovy_tip = Ejecutar script Groovy desde la consola sobre este alineamiento
+label.nw_mapping=Alineamiento Needleman y Wunsch
+label.proteins=Prote�na 
+label.reverse_complement=Invertir y complementar
+label.next_page_tooltip=P�gina siguiente
+label.sifts_mapping=Mapeado SIFTs
+label.mapping_method=M�todo de mapeo de secuencia \u27F7 estructura
+info.error_creating_file=Error al crear fichero {0}
+exception.fts_rest_service_no_longer_available= Servicios Rest {0} ya no est�n disponibles! 
+status.launching_3d_structure_viewer=Lanzando visualizador de estructura 3D...
+status.obtaining_mapping_with_sifts=Obteniendo mapeo por SIFTS
+status.fetching_3d_structures_for=Buscando la estructura 3D para {0}
+status.fetching_3d_structures_for_selected_entries=Buscando las estructuras 3D para entradas seleccionadas...
+status.fetching_dbrefs_for_sequences_without_valid_refs=Buscando referencias para {0} secuencia(s) sin referencia v�lida necesaria para mapeado SIFTS
+status.obtaining_mapping_with_nw_alignment=Obteniendo mapeo por alineamiento Needleman y Wunsch
+status.exporting_alignment_as_x_file = Exportando alineamiento como fichero tipo {0}
+label.column = Columna
+label.cant_map_cds = No se pudo mapear CDS a prote�na\nDatos CDS faltantes o incompletos
+label.operation_failed = Operaci�n fallada
+label.SEQUENCE_ID_no_longer_used = $SEQUENCE_ID$ no se utiliza m�s para accesiones DB
+label.SEQUENCE_ID_for_DB_ACCESSION1 = Por favor, revise sus URLs en la pesta�a 'Conexiones' de la ventana de Preferencias:
+label.SEQUENCE_ID_for_DB_ACCESSION2 = URL enlaza usando '$SEQUENCE_ID$' para accesiones DB ahora usar '$DB_ACCESSION$'.
+label.do_not_display_again = No mostrar este mensaje de nuevo
+label.output_seq_details = Seleccionar Detalles de la secuencia para ver todas
diff --git a/resources/so-xp-simple.obo.zip b/resources/so-xp-simple.obo.zip
new file mode 100644
index 0000000..d150da0
Binary files /dev/null and b/resources/so-xp-simple.obo.zip differ
diff --git a/resources/uniprot_mapping.xml b/resources/uniprot_mapping.xml
index 96edb05..8b26766 100644
--- a/resources/uniprot_mapping.xml
+++ b/resources/uniprot_mapping.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -80,7 +80,7 @@
 	 <class name="jalview.datamodel.PDBEntry">
 	 	<field name="type"><bind-xml node="attribute"/></field>
 	 	<field name="id"><bind-xml node="attribute"/></field>
-	 	<field name="property" collection="hashtable">
+	 	<field name="props" collection="hashtable">
 			<bind-xml name="property">
 			   <class name="org.exolab.castor.mapping.MapItem">
 			      <field name="key">
diff --git a/schemas/JalviewWsParamSet.xsd b/schemas/JalviewWsParamSet.xsd
index eec9068..8c84523 100644
--- a/schemas/JalviewWsParamSet.xsd
+++ b/schemas/JalviewWsParamSet.xsd
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/schemas/castor-mapping.xsd b/schemas/castor-mapping.xsd
index 0e83d72..1e74c06 100644
--- a/schemas/castor-mapping.xsd
+++ b/schemas/castor-mapping.xsd
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/schemas/jalview.nodesc.properties b/schemas/jalview.nodesc.properties
index b5af923..3ec053b 100644
--- a/schemas/jalview.nodesc.properties
+++ b/schemas/jalview.nodesc.properties
@@ -1,6 +1,6 @@
 ##
-# Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
-# Copyright (C) 2015 The Jalview Authors
+# Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+# Copyright (C) 2016 The Jalview Authors
 # 
 # This file is part of Jalview.
 # 
diff --git a/schemas/jalview.properties b/schemas/jalview.properties
index 42b5ef7..58a4816 100644
--- a/schemas/jalview.properties
+++ b/schemas/jalview.properties
@@ -1,6 +1,6 @@
 ##
-# Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
-# Copyright (C) 2015 The Jalview Authors
+# Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+# Copyright (C) 2016 The Jalview Authors
 # 
 # This file is part of Jalview.
 # 
diff --git a/schemas/jalview.xsd b/schemas/jalview.xsd
index b002618..0aedce9 100644
--- a/schemas/jalview.xsd
+++ b/schemas/jalview.xsd
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9) 
-	* Copyright (C) 2015 The Jalview Authors * * This file is part of 
+<!-- * Jalview - A Sequence Alignment Editor and Viewer (2.10.1) 
+	* Copyright (C) 2016 The Jalview Authors * * This file is part of 
 	Jalview. * * Jalview is free software: you can redistribute it and/or * modify 
 	it under the terms of the GNU General Public License * as published by the 
 	Free Software Foundation, either version 3 of the License, or (at your option) 
@@ -293,6 +293,7 @@
 						<xs:attribute name="end" type="xs:int" use="required" />
 						<xs:attribute name="id" type="xs:string" use="required" />
 						<xs:attribute name="hidden" type="xs:boolean" />
+						<xs:attribute name="viewreference" type="xs:boolean" use="optional"/>
 					</xs:complexType>
 				</xs:element>
 				<xs:element name="JGroup" minOccurs="0" maxOccurs="unbounded">
diff --git a/schemas/jalviewJvV1.xsd b/schemas/jalviewJvV1.xsd
index 4bce631..9090ea8 100644
--- a/schemas/jalviewJvV1.xsd
+++ b/schemas/jalviewJvV1.xsd
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/schemas/sifts/alignment.xsd b/schemas/sifts/alignment.xsd
new file mode 100644
index 0000000..7731048
--- /dev/null
+++ b/schemas/sifts/alignment.xsd
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema targetNamespace="http://www.ebi.ac.uk/pdbe/docs/sifts/eFamily.xsd"
+	elementFormDefault="qualified" attributeFormDefault="unqualified"
+	xmlns:data="http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd"
+	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+	xmlns="http://www.ebi.ac.uk/pdbe/docs/sifts/alignment.xsd" version="1.1">
+	<xsd:import namespace="http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd"
+		schemaLocation="dataTypes.xsd"/>
+	<xsd:element name="alignment">
+		<xsd:annotation>
+			<xsd:documentation>This section of the schema deals with alignments.  The alignment can be either a sequence alignment or a structural alignment.</xsd:documentation>
+		</xsd:annotation>
+		<xsd:complexType>
+			<xsd:sequence>
+				<xsd:element name="alignObject" maxOccurs="unbounded">
+					<xsd:annotation>
+						<xsd:documentation>description of object. id e.g. 1tim.A,8tim.B, P001228, ...; type: type of object e.g.: protein, dna. version: last time this object has been changed (sometimes not so easy to know ...)</xsd:documentation>
+					</xsd:annotation>
+					<xsd:complexType>
+						<xsd:sequence>
+							<xsd:element name="alignObjectDetail" minOccurs="0"
+								maxOccurs="unbounded">
+								<xsd:complexType mixed="true">
+									<xsd:attributeGroup ref="data:detail"/>
+								</xsd:complexType>
+							</xsd:element>
+							<xsd:element name="sequence" minOccurs="0">
+								<xsd:complexType mixed="true">
+									<xsd:attributeGroup ref="data:region"/>
+								</xsd:complexType>
+							</xsd:element>
+						</xsd:sequence>
+						<xsd:attribute name="objectVersion" type="xsd:string" use="required"/>
+						<xsd:attribute name="intObjectId" type="xsd:string" use="required"/>
+						<xsd:attribute name="type" type="xsd:string" use="optional"/>
+						<xsd:attributeGroup ref="data:dbRef"/>
+					</xsd:complexType>
+				</xsd:element>
+				<xsd:element name="score" minOccurs="0" maxOccurs="unbounded">
+					<xsd:annotation>
+						<xsd:documentation>e.g.: number of identical residues, % id residues, aligmnent score, e-value, p-value, etc.</xsd:documentation>
+					</xsd:annotation>
+					<xsd:complexType>
+						<xsd:attribute name="methodName" type="xsd:string" use="required"/>
+						<xsd:attribute name="scoreValue" type="xsd:string" use="required"/>
+					</xsd:complexType>
+				</xsd:element>
+				<xsd:element name="block" maxOccurs="unbounded">
+					<xsd:complexType>
+						<xsd:sequence>
+							<xsd:element name="segment" maxOccurs="unbounded">
+								<xsd:annotation>
+									<xsd:documentation>the alignment given for a single object</xsd:documentation>
+									<xsd:documentation>the alignment given for a single object</xsd:documentation>
+								</xsd:annotation>
+								<xsd:complexType>
+									<xsd:sequence minOccurs="0">
+										<xsd:element name="cigar" type="data:cigarstring">
+											<xsd:annotation>
+												<xsd:documentation>e.g. 2D23M4I</xsd:documentation>
+											</xsd:annotation>
+										</xsd:element>
+									</xsd:sequence>
+									<xsd:attributeGroup ref="data:region"/>
+									<xsd:attribute name="intObjectId" use="required"/>
+									<xsd:attribute name="strand" use="optional"/>
+								</xsd:complexType>
+							</xsd:element>
+						</xsd:sequence>
+						<xsd:attribute name="blockScore" type="xsd:string" use="optional"/>
+						<xsd:attribute name="blockOrder" type="xsd:integer" use="required"/>
+					</xsd:complexType>
+				</xsd:element>
+				<xsd:element name="geo3d" minOccurs="0" maxOccurs="unbounded">
+					<xsd:annotation>
+						<xsd:documentation>geometrical operation to perform on 3D object</xsd:documentation>
+					</xsd:annotation>
+					<xsd:complexType>
+						<xsd:sequence>
+							<xsd:element name="vector">
+								<xsd:complexType>
+									<xsd:attribute name="x" type="xsd:float" use="required"/>
+									<xsd:attribute name="y" type="xsd:float" use="required"/>
+									<xsd:attribute name="z" type="xsd:float" use="required"/>
+								</xsd:complexType>
+							</xsd:element>
+							<xsd:element name="matrix" maxOccurs="unbounded">
+								<xsd:complexType>
+									<xsd:sequence>
+										<xsd:element name="max11">
+											<xsd:complexType>
+												<xsd:attribute name="coord" type="xsd:float"
+												use="required"/>
+											</xsd:complexType>
+										</xsd:element>
+										<xsd:element name="max12">
+											<xsd:complexType>
+												<xsd:attribute name="coord" type="xsd:float"
+												use="required"/>
+											</xsd:complexType>
+										</xsd:element>
+										<xsd:element name="max13">
+											<xsd:complexType>
+												<xsd:attribute name="coord" type="xsd:float"
+												use="required"/>
+											</xsd:complexType>
+										</xsd:element>
+										<xsd:element name="max21">
+											<xsd:complexType>
+												<xsd:attribute name="coord" type="xsd:float"
+												use="required"/>
+											</xsd:complexType>
+										</xsd:element>
+										<xsd:element name="max22">
+											<xsd:complexType>
+												<xsd:attribute name="coord" type="xsd:float"
+												use="required"/>
+											</xsd:complexType>
+										</xsd:element>
+										<xsd:element name="max23">
+											<xsd:complexType>
+												<xsd:attribute name="coord" type="xsd:float"
+												use="required"/>
+											</xsd:complexType>
+										</xsd:element>
+										<xsd:element name="max31">
+											<xsd:complexType>
+												<xsd:attribute name="coord" type="xsd:float"
+												use="required"/>
+											</xsd:complexType>
+										</xsd:element>
+										<xsd:element name="max32">
+											<xsd:complexType>
+												<xsd:attribute name="coord" type="xsd:float"
+												use="required"/>
+											</xsd:complexType>
+										</xsd:element>
+										<xsd:element name="max33">
+											<xsd:complexType>
+												<xsd:attribute name="coord" type="xsd:float"
+												use="required"/>
+											</xsd:complexType>
+										</xsd:element>
+									</xsd:sequence>
+								</xsd:complexType>
+							</xsd:element>
+						</xsd:sequence>
+						<xsd:attribute name="intObjectId" type="xsd:string" use="required"/>
+					</xsd:complexType>
+				</xsd:element>
+			</xsd:sequence>
+			<xsd:attribute name="alignType" type="xsd:string" use="required"/>
+		</xsd:complexType>
+	</xsd:element>
+</xsd:schema>
\ No newline at end of file
diff --git a/schemas/sifts/dataTypes.xsd b/schemas/sifts/dataTypes.xsd
new file mode 100644
index 0000000..f0947b4
--- /dev/null
+++ b/schemas/sifts/dataTypes.xsd
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema targetNamespace="http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd"
+	elementFormDefault="qualified" attributeFormDefault="unqualified" version="2.0"
+	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+	xmlns="http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd">
+	<xsd:simpleType name="entityType">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="protein"/>
+			<xsd:enumeration value="RNA"/>
+			<xsd:enumeration value="DNA"/>
+			<xsd:enumeration value="domain"/>
+		</xsd:restriction>
+	</xsd:simpleType>
+	<xsd:simpleType name="dbChainId">
+		<xsd:restriction base="xsd:string">
+			<xsd:minLength value="1"/>
+			<xsd:maxLength value="2"/>
+		</xsd:restriction>
+	</xsd:simpleType>
+	<xsd:simpleType name="chainId">
+		<xsd:restriction base="xsd:string">
+			<xsd:minLength value="1"/>
+			<xsd:maxLength value="1"/>
+		</xsd:restriction>
+	</xsd:simpleType>
+	<xsd:simpleType name="cigarstring">
+		<xsd:restriction base="xsd:string">
+			<xsd:minLength value="1"/>
+			<xsd:whiteSpace value="collapse"/>
+			<xsd:pattern value="(\d{0,5}\w{1})*"/>
+		</xsd:restriction>
+	</xsd:simpleType>
+	<xsd:attributeGroup name="dbRef">
+		<xsd:attribute name="dbSource" use="required">
+			<xsd:simpleType>
+				<xsd:restriction base="xsd:string">
+					<xsd:enumeration value="PDBe"/>
+					<xsd:enumeration value="UniProt"/>
+					<xsd:enumeration value="Pfam"/>
+					<xsd:enumeration value="CATH"/>
+					<xsd:enumeration value="SCOP"/>
+					<xsd:enumeration value="InterPro"/>
+					<xsd:enumeration value="PDB"/>
+					<xsd:enumeration value="NCBI"/>
+					<xsd:enumeration value="EC"/>
+					<xsd:enumeration value="GO"/>
+					<xsd:enumeration value="Astral"/>
+				</xsd:restriction>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="dbCoordSys" use="required">
+			<xsd:simpleType>
+				<xsd:restriction base="xsd:string">
+					<xsd:enumeration value="PDBe"/>
+					<xsd:enumeration value="PDBseqres"/>
+					<xsd:enumeration value="PDBresnum"/>
+					<xsd:enumeration value="UniProt"/>
+					<xsd:enumeration value="Astral"/>
+				</xsd:restriction>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="dbAccessionId" type="xsd:string" use="required"/>
+		<xsd:attribute name="dbEvidence" type="xsd:string"/>
+		<xsd:attribute name="dbVersion" type="xsd:string" use="optional"/>
+	</xsd:attributeGroup>
+	<xsd:attributeGroup name="resRef">
+		<xsd:attribute name="dbResNum" use="required">
+			<xsd:simpleType>
+				<xsd:restriction base="xsd:string">
+					<xsd:minLength value="1"/>
+					<xsd:pattern value="-?\d+(\S+)?"/>
+				</xsd:restriction>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="dbResName" use="required">
+			<xsd:simpleType>
+				<xsd:restriction base="xsd:string">
+					<xsd:minLength value="1"/>
+					<xsd:maxLength value="3"/>
+					<xsd:pattern value="\w{1,3}"/>
+				</xsd:restriction>
+			</xsd:simpleType>
+		</xsd:attribute>
+	</xsd:attributeGroup>
+	<xsd:attributeGroup name="detail">
+		<xsd:attribute name="dbSource" use="optional">
+			<xsd:simpleType>
+				<xsd:restriction base="xsd:string">
+					<xsd:enumeration value="PDBe"/>
+					<xsd:enumeration value="UniProt"/>
+					<xsd:enumeration value="Pfam"/>
+					<xsd:enumeration value="CATH"/>
+					<xsd:enumeration value="SCOP"/>
+					<xsd:enumeration value="InterPro"/>
+					<xsd:enumeration value="PDB"/>
+					<xsd:enumeration value="NCBI"/>
+					<xsd:enumeration value="EC"/>
+					<xsd:enumeration value="GO"/>
+					<xsd:enumeration value="Astral"/>
+				</xsd:restriction>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="property" type="xsd:string" use="required"/>
+	</xsd:attributeGroup>
+	<xsd:attributeGroup name="region">
+		<xsd:attribute name="start" use="optional">
+			<xsd:simpleType>
+				<xsd:restriction base="xsd:string">
+					<xsd:minLength value="1"/>
+					<xsd:pattern value="-?\d+(.\S)?"/>
+				</xsd:restriction>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="end" use="optional">
+			<xsd:simpleType>
+				<xsd:restriction base="xsd:string">
+					<xsd:minLength value="1"/>
+					<xsd:pattern value="-?\d+(.\S)?"/>
+				</xsd:restriction>
+			</xsd:simpleType>
+		</xsd:attribute>
+	</xsd:attributeGroup>
+	<xsd:attributeGroup name="listdbRef">
+		<xsd:attribute name="dbVersion" type="xsd:string" use="optional"/>
+		<xsd:attribute name="dbSource" use="required">
+			<xsd:simpleType>
+				<xsd:restriction base="xsd:string">
+					<xsd:enumeration value="PDBe"/>
+					<xsd:enumeration value="UniProt"/>
+					<xsd:enumeration value="Pfam"/>
+					<xsd:enumeration value="CATH"/>
+					<xsd:enumeration value="SCOP"/>
+					<xsd:enumeration value="InterPro"/>
+					<xsd:enumeration value="PDB"/>
+					<xsd:enumeration value="NCBI"/>
+					<xsd:enumeration value="EC"/>
+					<xsd:enumeration value="GO"/>
+					<xsd:enumeration value="Astral"/>
+				</xsd:restriction>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="dbCoordSys" use="required">
+			<xsd:simpleType>
+				<xsd:restriction base="xsd:string">
+					<xsd:enumeration value="PDBe"/>
+					<xsd:enumeration value="PDBseqres"/>
+					<xsd:enumeration value="PDBresnum"/>
+					<xsd:enumeration value="UniProt"/>
+					<xsd:enumeration value="Astral"/>
+				</xsd:restriction>
+			</xsd:simpleType>
+		</xsd:attribute>
+	</xsd:attributeGroup>
+</xsd:schema>
\ No newline at end of file
diff --git a/schemas/sifts/eFamily.xsd b/schemas/sifts/eFamily.xsd
new file mode 100644
index 0000000..250f1f4
--- /dev/null
+++ b/schemas/sifts/eFamily.xsd
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- edited with XMLSpy v2016 (http://www.altova.com) by Charles (student) -->
+<xsd:schema xmlns="http://www.ebi.ac.uk/pdbe/docs/sifts/eFamily.xsd" xmlns:data="http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd" xmlns:align="http://www.ebi.ac.uk/pdbe/docs/sifts/alignment.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" targetNamespace="http://www.ebi.ac.uk/pdbe/docs/sifts/eFamily.xsd" elementFormDefault="qualified" attributeFormDefault="unqualified" version="2.0">
+	<xsd:include schemaLocation="alignment.xsd"/>
+	<xsd:import namespace="http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd" schemaLocation="dataTypes.xsd"/>
+	<xsd:annotation>
+		<xsd:documentation>The eFamily schema is designed to allow the members of the eFamily consortium to exchange domain definitions.  As members of the different databases use different underlying data (languages) so we need a way of getting between the co-ordinates systems. MSD are to provide the mapping between the co-ordinates (translator), hence the reason for the incorporation of the mappings into the core of the schema. 
+		</xsd:documentation>
+	</xsd:annotation>
+	<xsd:element name="entry">
+		<xsd:annotation>
+			<xsd:documentation>The entry represents a database entry.  This schema is currently designed for domain and mapping entires.</xsd:documentation>
+		</xsd:annotation>
+		<xsd:complexType>
+			<xsd:sequence>
+				<xsd:element name="listDB" minOccurs="1" maxOccurs="1">
+					<xsd:complexType mixed="false">
+						<xsd:sequence minOccurs="1" maxOccurs="unbounded">
+							<xsd:element name="db">
+								<xsd:complexType>
+									<xsd:attributeGroup ref="data:listdbRef"/>
+								</xsd:complexType>
+							</xsd:element>
+						</xsd:sequence>
+					</xsd:complexType>
+				</xsd:element>
+				<xsd:element name="entryDetail" minOccurs="0" maxOccurs="unbounded">
+					<xsd:annotation>
+						<xsd:documentation>This is a free text field that allows someone to attach some sort of documentation to the entry</xsd:documentation>
+					</xsd:annotation>
+					<xsd:complexType mixed="true">
+						<xsd:attributeGroup ref="data:detail"/>
+					</xsd:complexType>
+				</xsd:element>
+				<xsd:element name="entity" maxOccurs="unbounded">
+					<xsd:annotation>
+						<xsd:documentation> An entity is a single domain definition.  In the case of SCOP, there is only one entity per entry, but in the case of Pfam, an entry is a collection of domains/entities.</xsd:documentation>
+					</xsd:annotation>
+					<xsd:complexType>
+						<xsd:sequence>
+							<xsd:element name="entityDetail" minOccurs="0" maxOccurs="unbounded">
+								<xsd:annotation>
+									<xsd:documentation>This is a free text field that allows someone to attach some sort of documentation to the entity</xsd:documentation>
+								</xsd:annotation>
+								<xsd:complexType mixed="true">
+									<xsd:attributeGroup ref="data:detail"/>
+								</xsd:complexType>
+							</xsd:element>
+							<xsd:element name="segment" maxOccurs="unbounded">
+								<xsd:annotation>
+									<xsd:documentation>An entity may not comprise of a single continuous region. This may be used to a chimeric structure or a discontinuous domain</xsd:documentation>
+								</xsd:annotation>
+								<xsd:complexType>
+									<xsd:sequence>
+										<xsd:element name="listResidue" minOccurs="0">
+											<xsd:annotation>
+												<xsd:documentation>Contains a set of residues objects</xsd:documentation>
+											</xsd:annotation>
+											<xsd:complexType>
+												<xsd:sequence>
+													<xsd:element name="residue" maxOccurs="unbounded">
+														<xsd:annotation>
+															<xsd:documentation>A single residue object.  This object can contain information on what the residue is,  general annotation, the numbering system and co-ordinate mapping </xsd:documentation>
+														</xsd:annotation>
+														<xsd:complexType>
+															<xsd:sequence>
+																<xsd:element name="crossRefDb" minOccurs="0" maxOccurs="unbounded">
+																	<xsd:annotation>
+																		<xsd:documentation>Allows the linking between different co-ordinate systems</xsd:documentation>
+																	</xsd:annotation>
+																	<xsd:complexType>
+																		<xsd:attributeGroup ref="data:dbRef"/>
+																		<xsd:attributeGroup ref="data:resRef"/>
+																		<xsd:attribute name="dbChainId" type="data:chainId" use="optional"/>
+																	</xsd:complexType>
+																</xsd:element>
+																<xsd:element name="residueDetail" minOccurs="0" maxOccurs="unbounded">
+																	<xsd:annotation>
+																		<xsd:documentation>This allows one to add information to the residues.  For example whether it is observed or whether it is an active site residue</xsd:documentation>
+																	</xsd:annotation>
+																	<xsd:complexType mixed="true">
+																		<xsd:attributeGroup ref="data:detail"/>
+																	</xsd:complexType>
+																</xsd:element>
+															</xsd:sequence>
+															<xsd:attributeGroup ref="data:resRef"/>
+															<xsd:attributeGroup ref="data:listdbRef"/>
+														</xsd:complexType>
+													</xsd:element>
+												</xsd:sequence>
+											</xsd:complexType>
+										</xsd:element>
+										<xsd:element name="listMapRegion" minOccurs="0">
+											<xsd:annotation>
+												<xsd:documentation>Allows cross referencing to another database.  For example, one may wish to include which the taxon that a mapping or sequence corresponds</xsd:documentation>
+											</xsd:annotation>
+											<xsd:complexType>
+												<xsd:sequence>
+													<xsd:element name="mapRegion" maxOccurs="unbounded">
+														<xsd:annotation>
+															<xsd:documentation>Defines the database that is being cross mapped to</xsd:documentation>
+														</xsd:annotation>
+														<xsd:complexType>
+															<xsd:sequence>
+																<xsd:element name="db">
+																	<xsd:annotation>
+																		<xsd:documentation>Contains the mapping coordinates.  The start end tags refer to the master databse co-ordinates.  The tags prefixed with sys refer to the database being mapped to.</xsd:documentation>
+																	</xsd:annotation>
+																	<xsd:complexType>
+																		<xsd:sequence>
+																			<xsd:element name="dbDetail" minOccurs="0" maxOccurs="unbounded">
+																				<xsd:complexType mixed="true">
+																					<xsd:attributeGroup ref="data:detail"/>
+																				</xsd:complexType>
+																			</xsd:element>
+																		</xsd:sequence>
+																		<xsd:attributeGroup ref="data:dbRef"/>
+																		<xsd:attribute name="dbChainId" type="data:dbChainId" use="optional"/>
+																		<xsd:attributeGroup ref="data:region"/>
+																	</xsd:complexType>
+																</xsd:element>
+															</xsd:sequence>
+															<xsd:attributeGroup ref="data:region"/>
+														</xsd:complexType>
+													</xsd:element>
+												</xsd:sequence>
+											</xsd:complexType>
+										</xsd:element>
+										<xsd:element name="segmentDetail" minOccurs="0" maxOccurs="unbounded">
+											<xsd:annotation>
+												<xsd:documentation>This is a free text field that allows someone to attach some sort of documentation to the segment</xsd:documentation>
+											</xsd:annotation>
+											<xsd:complexType mixed="true">
+												<xsd:attributeGroup ref="data:detail"/>
+											</xsd:complexType>
+										</xsd:element>
+									</xsd:sequence>
+									<xsd:attribute name="segId" type="xsd:string" use="required"/>
+									<xsd:attributeGroup ref="data:region"/>
+								</xsd:complexType>
+							</xsd:element>
+						</xsd:sequence>
+						<xsd:attribute name="type" type="data:entityType" use="required"/>
+						<xsd:attribute name="entityId" type="xsd:string" use="required"/>
+					</xsd:complexType>
+				</xsd:element>
+				<xsd:element ref="alignment" minOccurs="0" maxOccurs="unbounded"/>
+			</xsd:sequence>
+			<xsd:attributeGroup ref="data:dbRef"/>
+			<xsd:attribute name="date" type="xsd:date" use="required"/>
+			<xsd:attribute name="dbEntryVersion" type="xsd:date" use="required"/>
+		</xsd:complexType>
+	</xsd:element>
+</xsd:schema>
diff --git a/schemas/vamsas.xsd b/schemas/vamsas.xsd
index 9c95d5f..a93916f 100644
--- a/schemas/vamsas.xsd
+++ b/schemas/vamsas.xsd
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/schemas/vamsasJvV1.xsd b/schemas/vamsasJvV1.xsd
index 10587f5..a5ec98e 100644
--- a/schemas/vamsasJvV1.xsd
+++ b/schemas/vamsasJvV1.xsd
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/MCview/AppletPDBCanvas.java b/src/MCview/AppletPDBCanvas.java
index 9cfb4ea..5e79da2 100644
--- a/src/MCview/AppletPDBCanvas.java
+++ b/src/MCview/AppletPDBCanvas.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,6 +26,7 @@ import jalview.appletgui.FeatureRenderer;
 import jalview.appletgui.SequenceRenderer;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.io.StructureFile;
 import jalview.structure.AtomSpec;
 import jalview.structure.StructureListener;
 import jalview.structure.StructureMapping;
@@ -67,7 +68,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
   int my = 0;
 
-  public PDBfile pdb;
+  public StructureFile pdb;
 
   PDBEntry pdbentry;
 
@@ -160,7 +161,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
       if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
       {
-        pdbentry.setFile("INLINE" + pdb.id);
+        pdbentry.setFile("INLINE" + pdb.getId());
       }
 
     } catch (Exception ex)
@@ -169,7 +170,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
       return;
     }
 
-    pdbentry.setId(pdb.id);
+    pdbentry.setId(pdb.getId());
 
     ssm.addStructureViewerListener(this);
 
@@ -185,30 +186,34 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     // JUST DEAL WITH ONE SEQUENCE FOR NOW
     SequenceI sequence = seq[0];
 
-    for (int i = 0; i < pdb.chains.size(); i++)
+    for (int i = 0; i < pdb.getChains().size(); i++)
     {
 
-      mappingDetails.append("\n\nPDB Sequence is :\nSequence = "
-              + pdb.chains.elementAt(i).sequence.getSequenceAsString());
+      mappingDetails
+              .append("\n\nPDB Sequence is :\nSequence = "
+                      + pdb.getChains().elementAt(i).sequence
+                              .getSequenceAsString());
       mappingDetails.append("\nNo of residues = "
-              + pdb.chains.elementAt(i).residues.size() + "\n\n");
+              + pdb.getChains().elementAt(i).residues.size() + "\n\n");
 
       // Now lets compare the sequences to get
       // the start and end points.
       // Align the sequence to the pdb
       // TODO: DNa/Pep switch
       AlignSeq as = new AlignSeq(sequence,
-              pdb.chains.elementAt(i).sequence,
-              pdb.chains.elementAt(i).isNa ? AlignSeq.DNA : AlignSeq.PEP);
+              pdb.getChains().elementAt(i).sequence, pdb.getChains()
+                      .elementAt(i).isNa ? AlignSeq.DNA : AlignSeq.PEP);
       as.calcScoreMatrix();
       as.traceAlignment();
       PrintStream ps = new PrintStream(System.out)
       {
+        @Override
         public void print(String x)
         {
           mappingDetails.append(x);
         }
 
+        @Override
         public void println()
         {
           mappingDetails.append("\n");
@@ -232,7 +237,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
       mappingDetails.append("\nSEQ start/end " + seqstart + " " + seqend);
     }
 
-    mainchain = pdb.chains.elementAt(maxchain);
+    mainchain = pdb.getChains().elementAt(maxchain);
 
     mainchain.pdbstart = pdbstart;
     mainchain.pdbend = pdbend;
@@ -267,6 +272,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
     addKeyListener(new KeyAdapter()
     {
+      @Override
       public void keyPressed(KeyEvent evt)
       {
         doKeyPressed(evt);
@@ -289,11 +295,11 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     // Sort the bonds by z coord
     visiblebonds = new Vector<Bond>();
 
-    for (int ii = 0; ii < pdb.chains.size(); ii++)
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
     {
-      if (pdb.chains.elementAt(ii).isVisible)
+      if (pdb.getChains().elementAt(ii).isVisible)
       {
-        Vector<Bond> tmp = pdb.chains.elementAt(ii).bonds;
+        Vector<Bond> tmp = pdb.getChains().elementAt(ii).bonds;
 
         for (int i = 0; i < tmp.size(); i++)
         {
@@ -320,11 +326,11 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     min[1] = (float) 1e30;
     min[2] = (float) 1e30;
 
-    for (int ii = 0; ii < pdb.chains.size(); ii++)
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
     {
-      if (pdb.chains.elementAt(ii).isVisible)
+      if (pdb.getChains().elementAt(ii).isVisible)
       {
-        Vector<Bond> bonds = pdb.chains.elementAt(ii).bonds;
+        Vector<Bond> bonds = pdb.getChains().elementAt(ii).bonds;
 
         for (Bond tmp : bonds)
         {
@@ -448,11 +454,11 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     int bsize = 0;
 
     // Find centre coordinate
-    for (int ii = 0; ii < pdb.chains.size(); ii++)
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
     {
-      if (pdb.chains.elementAt(ii).isVisible)
+      if (pdb.getChains().elementAt(ii).isVisible)
       {
-        Vector<Bond> bonds = pdb.chains.elementAt(ii).bonds;
+        Vector<Bond> bonds = pdb.getChains().elementAt(ii).bonds;
 
         bsize += bonds.size();
 
@@ -470,6 +476,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     centre[2] = ztot / (2 * (float) bsize);
   }
 
+  @Override
   public void paint(Graphics g)
   {
 
@@ -572,9 +579,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     PDBChain chain;
     if (bysequence && pdb != null)
     {
-      for (int ii = 0; ii < pdb.chains.size(); ii++)
+      for (int ii = 0; ii < pdb.getChains().size(); ii++)
       {
-        chain = pdb.chains.elementAt(ii);
+        chain = pdb.getChains().elementAt(ii);
 
         for (int i = 0; i < chain.bonds.size(); i++)
         {
@@ -774,6 +781,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     }
   }
 
+  @Override
   public void mousePressed(MouseEvent e)
   {
     pdbAction = true;
@@ -786,7 +794,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
       repaint();
       if (foundchain != -1)
       {
-        PDBChain chain = pdb.chains.elementAt(foundchain);
+        PDBChain chain = pdb.getChains().elementAt(foundchain);
         if (chain == mainchain)
         {
           if (fatom.alignmentMapping != -1)
@@ -818,6 +826,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     dragging = false;
   }
 
+  @Override
   public void mouseMoved(MouseEvent e)
   {
     pdbAction = true;
@@ -834,7 +843,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     PDBChain chain = null;
     if (foundchain != -1)
     {
-      chain = pdb.chains.elementAt(foundchain);
+      chain = pdb.getChains().elementAt(foundchain);
       if (chain == mainchain)
       {
         mouseOverStructure(fatom.resNumber, chain.id);
@@ -860,18 +869,22 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     }
   }
 
+  @Override
   public void mouseClicked(MouseEvent e)
   {
   }
 
+  @Override
   public void mouseEntered(MouseEvent e)
   {
   }
 
+  @Override
   public void mouseExited(MouseEvent e)
   {
   }
 
+  @Override
   public void mouseDragged(MouseEvent evt)
   {
     int x = evt.getX();
@@ -893,7 +906,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     }
 
     // Alter the bonds
-    for (PDBChain chain : pdb.chains)
+    for (PDBChain chain : pdb.getChains())
     {
       for (Bond tmpBond : chain.bonds)
       {
@@ -921,6 +934,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     repaint();
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     dragging = false;
@@ -930,7 +944,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
   void drawLabels(Graphics g)
   {
 
-    for (PDBChain chain : pdb.chains)
+    for (PDBChain chain : pdb.getChains())
     {
       if (chain.isVisible)
       {
@@ -981,15 +995,15 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
     foundchain = -1;
 
-    for (int ii = 0; ii < pdb.chains.size(); ii++)
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
     {
-      PDBChain chain = pdb.chains.elementAt(ii);
+      PDBChain chain = pdb.getChains().elementAt(ii);
       int truex;
       Bond tmpBond = null;
 
       if (chain.isVisible)
       {
-        Vector<Bond> bonds = pdb.chains.elementAt(ii).bonds;
+        Vector<Bond> bonds = pdb.getChains().elementAt(ii).bonds;
 
         for (int i = 0; i < bonds.size(); i++)
         {
@@ -1030,13 +1044,14 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
       if (fatom != null) // )&& chain.ds != null)
       {
-        chain = pdb.chains.elementAt(foundchain);
+        chain = pdb.getChains().elementAt(foundchain);
       }
     }
 
     return fatom;
   }
 
+  @Override
   public void update(Graphics g)
   {
     paint(g);
@@ -1096,9 +1111,9 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
   public void setAllchainsVisible(boolean b)
   {
-    for (int ii = 0; ii < pdb.chains.size(); ii++)
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
     {
-      PDBChain chain = pdb.chains.elementAt(ii);
+      PDBChain chain = pdb.getChains().elementAt(ii);
       chain.isVisible = b;
     }
     mainchain.isVisible = true;
@@ -1108,6 +1123,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
 
   // ////////////////////////////////
   // /StructureListener
+  @Override
   public String[] getPdbFile()
   {
     return new String[] { pdbentry.getFile() };
@@ -1210,6 +1226,7 @@ public class AppletPDBCanvas extends Panel implements MouseListener,
     // return new Color(viewer.getAtomArgb(atomIndex));
   }
 
+  @Override
   public void updateColours(Object source)
   {
     colourBySequence();
diff --git a/src/MCview/AppletPDBViewer.java b/src/MCview/AppletPDBViewer.java
index 620d3b8..f097987 100644
--- a/src/MCview/AppletPDBViewer.java
+++ b/src/MCview/AppletPDBViewer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/MCview/Atom.java b/src/MCview/Atom.java
index b2fb5f5..4f58dbb 100644
--- a/src/MCview/Atom.java
+++ b/src/MCview/Atom.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -81,7 +81,7 @@ public class Atom
     chain = str.substring(21, 22);
 
     resNumber = Integer.parseInt(str.substring(22, 26).trim());
-    resNumIns = str.substring(22, 27);
+    resNumIns = str.substring(22, 27).trim();
     insCode = str.substring(26, 27).charAt(0);
     this.x = (new Float(str.substring(30, 38).trim()).floatValue());
     this.y = (new Float(str.substring(38, 46).trim()).floatValue());
@@ -109,6 +109,24 @@ public class Atom
     }
   }
 
+  @Override
+  public boolean equals(Object that)
+  {
+    if (this == that || that == null)
+    {
+      return true;
+    }
+    if (that instanceof Atom)
+    {
+      Atom other = (Atom) that;
+      return other.resName.equals(this.resName)
+              && other.resNumber == this.resNumber
+              && other.resNumIns.equals(this.resNumIns)
+              && other.chain.equals(this.chain);
+    }
+    return false;
+  }
+
   public Atom(float x, float y, float z)
   {
     this.x = x;
diff --git a/src/MCview/Bond.java b/src/MCview/Bond.java
index 9a8c614..9087f6d 100644
--- a/src/MCview/Bond.java
+++ b/src/MCview/Bond.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/MCview/MCMatrix.java b/src/MCview/MCMatrix.java
index e3ba289..6b03fc3 100644
--- a/src/MCview/MCMatrix.java
+++ b/src/MCview/MCMatrix.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/MCview/PDBCanvas.java b/src/MCview/PDBCanvas.java
index fdd673b..be443fa 100644
--- a/src/MCview/PDBCanvas.java
+++ b/src/MCview/PDBCanvas.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,6 +26,7 @@ import jalview.datamodel.SequenceI;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.FeatureRenderer;
 import jalview.gui.SequenceRenderer;
+import jalview.io.StructureFile;
 import jalview.structure.AtomSpec;
 import jalview.structure.StructureListener;
 import jalview.structure.StructureMapping;
@@ -65,7 +66,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
   int my = 0;
 
-  public PDBfile pdb;
+  public StructureFile pdb;
 
   PDBEntry pdbentry;
 
@@ -154,7 +155,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
       if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
       {
-        pdbentry.setFile("INLINE" + pdb.id);
+        pdbentry.setFile("INLINE" + pdb.getId());
       }
 
     } catch (Exception ex)
@@ -168,7 +169,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
       errorMessage = "Error loading file: " + pdbentry.getId();
       return;
     }
-    pdbentry.setId(pdb.id);
+    pdbentry.setId(pdb.getId());
 
     ssm.addStructureViewerListener(this);
 
@@ -184,28 +185,32 @@ public class PDBCanvas extends JPanel implements MouseListener,
     // JUST DEAL WITH ONE SEQUENCE FOR NOW
     SequenceI sequence = seq[0];
 
-    for (int i = 0; i < pdb.chains.size(); i++)
+    for (int i = 0; i < pdb.getChains().size(); i++)
     {
 
-      mappingDetails.append("\n\nPDB Sequence is :\nSequence = "
-              + pdb.chains.elementAt(i).sequence.getSequenceAsString());
+      mappingDetails
+              .append("\n\nPDB Sequence is :\nSequence = "
+                      + pdb.getChains().elementAt(i).sequence
+                              .getSequenceAsString());
       mappingDetails.append("\nNo of residues = "
-              + pdb.chains.elementAt(i).residues.size() + "\n\n");
+              + pdb.getChains().elementAt(i).residues.size() + "\n\n");
 
       // Now lets compare the sequences to get
       // the start and end points.
       // Align the sequence to the pdb
       AlignSeq as = new AlignSeq(sequence,
-              pdb.chains.elementAt(i).sequence, "pep");
+              pdb.getChains().elementAt(i).sequence, "pep");
       as.calcScoreMatrix();
       as.traceAlignment();
       PrintStream ps = new PrintStream(System.out)
       {
+        @Override
         public void print(String x)
         {
           mappingDetails.append(x);
         }
 
+        @Override
         public void println()
         {
           mappingDetails.append("\n");
@@ -229,7 +234,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
       mappingDetails.append("\nSEQ start/end " + seqstart + " " + seqend);
     }
 
-    mainchain = pdb.chains.elementAt(maxchain);
+    mainchain = pdb.getChains().elementAt(maxchain);
 
     mainchain.pdbstart = pdbstart;
     mainchain.pdbend = pdbend;
@@ -245,6 +250,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
     addKeyListener(new KeyAdapter()
     {
+      @Override
       public void keyPressed(KeyEvent evt)
       {
         keyPressed(evt);
@@ -271,7 +277,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     // Sort the bonds by z coord
     visiblebonds = new Vector<Bond>();
 
-    for (PDBChain chain : pdb.chains)
+    for (PDBChain chain : pdb.getChains())
     {
       if (chain.isVisible)
       {
@@ -301,7 +307,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     min[1] = (float) 1e30;
     min[2] = (float) 1e30;
 
-    for (PDBChain chain : pdb.chains)
+    for (PDBChain chain : pdb.getChains())
     {
       if (chain.isVisible)
       {
@@ -432,7 +438,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     int bsize = 0;
 
     // Find centre coordinate
-    for (PDBChain chain : pdb.chains)
+    for (PDBChain chain : pdb.getChains())
     {
       if (chain.isVisible)
       {
@@ -452,6 +458,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     centre[2] = ztot / (2 * (float) bsize);
   }
 
+  @Override
   public void paintComponent(Graphics g)
   {
     super.paintComponent(g);
@@ -541,9 +548,9 @@ public class PDBCanvas extends JPanel implements MouseListener,
     PDBChain chain;
     if (bysequence && pdb != null)
     {
-      for (int ii = 0; ii < pdb.chains.size(); ii++)
+      for (int ii = 0; ii < pdb.getChains().size(); ii++)
       {
-        chain = pdb.chains.elementAt(ii);
+        chain = pdb.getChains().elementAt(ii);
 
         for (int i = 0; i < chain.bonds.size(); i++)
         {
@@ -745,6 +752,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     }
   }
 
+  @Override
   public void mousePressed(MouseEvent e)
   {
     pdbAction = true;
@@ -757,7 +765,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
       repaint();
       if (foundchain != -1)
       {
-        PDBChain chain = pdb.chains.elementAt(foundchain);
+        PDBChain chain = pdb.getChains().elementAt(foundchain);
         if (chain == mainchain)
         {
           if (fatom.alignmentMapping != -1)
@@ -789,6 +797,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     dragging = false;
   }
 
+  @Override
   public void mouseMoved(MouseEvent e)
   {
     pdbAction = true;
@@ -805,7 +814,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     PDBChain chain = null;
     if (foundchain != -1)
     {
-      chain = pdb.chains.elementAt(foundchain);
+      chain = pdb.getChains().elementAt(foundchain);
       if (chain == mainchain)
       {
         mouseOverStructure(fatom.resNumber, chain.id);
@@ -824,18 +833,22 @@ public class PDBCanvas extends JPanel implements MouseListener,
     }
   }
 
+  @Override
   public void mouseClicked(MouseEvent e)
   {
   }
 
+  @Override
   public void mouseEntered(MouseEvent e)
   {
   }
 
+  @Override
   public void mouseExited(MouseEvent e)
   {
   }
 
+  @Override
   public void mouseDragged(MouseEvent evt)
   {
     int x = evt.getX();
@@ -857,7 +870,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     }
 
     // Alter the bonds
-    for (PDBChain chain : pdb.chains)
+    for (PDBChain chain : pdb.getChains())
     {
       for (Bond tmpBond : chain.bonds)
       {
@@ -885,6 +898,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     repaint();
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     dragging = false;
@@ -894,7 +908,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
   void drawLabels(Graphics g)
   {
 
-    for (PDBChain chain : pdb.chains)
+    for (PDBChain chain : pdb.getChains())
     {
       if (chain.isVisible)
       {
@@ -943,9 +957,9 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
     foundchain = -1;
 
-    for (int ii = 0; ii < pdb.chains.size(); ii++)
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
     {
-      PDBChain chain = pdb.chains.elementAt(ii);
+      PDBChain chain = pdb.getChains().elementAt(ii);
       int truex;
       Bond tmpBond = null;
 
@@ -990,7 +1004,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
       if (fatom != null) // )&& chain.ds != null)
       { // dead code? value of chain is either overwritten or discarded
-        chain = pdb.chains.elementAt(foundchain);
+        chain = pdb.getChains().elementAt(foundchain);
       }
     }
 
@@ -1053,9 +1067,9 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
   public void setAllchainsVisible(boolean b)
   {
-    for (int ii = 0; ii < pdb.chains.size(); ii++)
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
     {
-      PDBChain chain = pdb.chains.elementAt(ii);
+      PDBChain chain = pdb.getChains().elementAt(ii);
       chain.isVisible = b;
     }
     mainchain.isVisible = true;
@@ -1065,6 +1079,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
 
   // ////////////////////////////////
   // /StructureListener
+  @Override
   public String[] getPdbFile()
   {
     return new String[] { pdbentry.getFile() };
@@ -1169,6 +1184,7 @@ public class PDBCanvas extends JPanel implements MouseListener,
     // return new Color(viewer.getAtomArgb(atomIndex));
   }
 
+  @Override
   public void updateColours(Object source)
   {
     colourBySequence();
diff --git a/src/MCview/PDBChain.java b/src/MCview/PDBChain.java
index a6cd575..508e8f8 100644
--- a/src/MCview/PDBChain.java
+++ b/src/MCview/PDBChain.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,7 +29,9 @@ import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ResidueProperties;
+import jalview.structure.StructureImportSettings;
 import jalview.structure.StructureMapping;
+import jalview.util.Comparison;
 
 import java.awt.Color;
 import java.util.List;
@@ -81,7 +83,7 @@ public class PDBChain
 
   public PDBChain(String pdbid, String id)
   {
-    this.pdbid = pdbid.toLowerCase();
+    this.pdbid = pdbid == null ? pdbid : pdbid.toLowerCase();
     this.id = id;
   }
 
@@ -145,8 +147,14 @@ public class PDBChain
         pdbpos++;
       }
 
-      if (as.astr1.charAt(i) == as.astr2.charAt(i))
+      boolean sameResidue = Comparison.isSameResidue(as.astr1.charAt(i),
+              as.astr2.charAt(i), false);
+      if (sameResidue)
       {
+        if (pdbpos >= residues.size())
+        {
+          continue;
+        }
         Residue res = residues.elementAt(pdbpos);
         for (Atom atom : res.atoms)
         {
@@ -188,9 +196,14 @@ public class PDBChain
       status = PDBChain.IEASTATUS;
     }
     SequenceFeature[] features = sequence.getSequenceFeatures();
+    if (features == null)
+    {
+      return null;
+    }
     for (int i = 0; i < features.length; i++)
     {
-      if (features[i].getFeatureGroup().equals(pdbid))
+      if (features[i].getFeatureGroup() != null
+              && features[i].getFeatureGroup().equals(pdbid))
       {
         SequenceFeature tx = new SequenceFeature(features[i]);
         tx.setBegin(1 + residues.elementAt(tx.getBegin() - offset).atoms
@@ -252,7 +265,7 @@ public class PDBChain
      * If > 99% 'P', flag as nucleotide; note the count doesn't include the last
      * residue
      */
-    if (residues.size() > 0 && (numNa / (residues.size() - 1) > 0.99))
+    if (residues.size() > 1 && (numNa / (residues.size() - 1) > 0.99))
     {
       isNa = true;
     }
@@ -295,11 +308,15 @@ public class PDBChain
     Vector<Annotation> resAnnotation = new Vector<Annotation>();
     int i, iSize = atoms.size() - 1;
     int resNumber = -1;
+    char insCode = ' ';
     for (i = 0; i <= iSize; i++)
     {
       Atom tmp = atoms.elementAt(i);
       resNumber = tmp.resNumber;
+      insCode = tmp.insCode;
+
       int res = resNumber;
+      char ins = insCode;
 
       if (i == 0)
       {
@@ -309,7 +326,7 @@ public class PDBChain
       Vector<Atom> resAtoms = new Vector<Atom>();
       // Add atoms to a vector while the residue number
       // remains the same as the first atom's resNumber (res)
-      while ((resNumber == res) && (i < atoms.size()))
+      while ((resNumber == res) && (ins == insCode) && (i < atoms.size()))
       {
         resAtoms.add(atoms.elementAt(i));
         i++;
@@ -317,6 +334,7 @@ public class PDBChain
         if (i < atoms.size())
         {
           resNumber = atoms.elementAt(i).resNumber;
+          insCode = atoms.elementAt(i).insCode;
         }
         else
         {
@@ -327,51 +345,71 @@ public class PDBChain
       // We need this to keep in step with the outer for i = loop
       i--;
 
-      // Make a new Residue object with the new atoms vector
-      residues.addElement(new Residue(resAtoms, resNumber - 1, count));
-
-      Residue tmpres = residues.lastElement();
-      Atom tmpat = tmpres.atoms.get(0);
-      // Make A new SequenceFeature for the current residue numbering
-      SequenceFeature sf = new SequenceFeature("RESNUM", tmpat.resName
-              + ":" + tmpat.resNumIns + " " + pdbid + id, "", offset
-              + count, offset + count, pdbid);
-      // MCview.PDBChain.PDBFILEFEATURE);
-      resFeatures.addElement(sf);
-      resAnnotation.addElement(new Annotation(tmpat.tfactor));
-      // Keep totting up the sequence
-      if ((symbol = ResidueProperties.getAA3Hash().get(tmpat.resName)) == null)
+      // Add inserted residues as features to the base residue
+      Atom currAtom = resAtoms.get(0);
+      if (currAtom.insCode != ' '
+              && !residues.isEmpty()
+              && residues.lastElement().atoms.get(0).resNumber == currAtom.resNumber)
       {
-        String nucname = tmpat.resName.trim();
-        // use the aaIndex rather than call 'toLower' - which would take a bit
-        // more time.
-        deoxyn = nucname.length() == 2
-                && ResidueProperties.aaIndex[nucname.charAt(0)] == ResidueProperties.aaIndex['D'];
-        if (tmpat.name.equalsIgnoreCase("CA")
-                || ResidueProperties.nucleotideIndex[nucname
-                        .charAt((deoxyn ? 1 : 0))] == -1)
-        {
-          seq.append("X");
-          // System.err.println("PDBReader:Null aa3Hash for " +
-          // tmpat.resName);
-        }
-        else
-        {
-          // nucleotide flag
-          nucleotide = true;
-          seq.append(nucname.charAt((deoxyn ? 1 : 0)));
-        }
+        SequenceFeature sf = new SequenceFeature("INSERTION",
+                currAtom.resName + ":" + currAtom.resNumIns + " " + pdbid
+                        + id, "", offset + count - 1, offset + count - 1,
+                "PDB_INS");
+        resFeatures.addElement(sf);
+        residues.lastElement().atoms.addAll(resAtoms);
       }
       else
       {
-        if (nucleotide)
+
+        // Make a new Residue object with the new atoms vector
+        residues.addElement(new Residue(resAtoms, resNumber - 1, count));
+
+        Residue tmpres = residues.lastElement();
+        Atom tmpat = tmpres.atoms.get(0);
+        // Make A new SequenceFeature for the current residue numbering
+        SequenceFeature sf = new SequenceFeature("RESNUM", tmpat.resName
+                + ":" + tmpat.resNumIns + " " + pdbid + id, "", offset
+                + count, offset + count, pdbid);
+        resFeatures.addElement(sf);
+        resAnnotation.addElement(new Annotation(tmpat.tfactor));
+        // Keep totting up the sequence
+
+        if ((symbol = ResidueProperties.getAA3Hash().get(tmpat.resName)) == null)
         {
-          System.err
-                  .println("Warning: mixed nucleotide and amino acid chain.. its gonna do bad things to you!");
+          String nucname = tmpat.resName.trim();
+          // use the aaIndex rather than call 'toLower' - which would take a bit
+          // more time.
+          deoxyn = nucname.length() == 2
+                  && ResidueProperties.aaIndex[nucname.charAt(0)] == ResidueProperties.aaIndex['D'];
+          if (tmpat.name.equalsIgnoreCase("CA")
+                  || ResidueProperties.nucleotideIndex[nucname
+                          .charAt((deoxyn ? 1 : 0))] == -1)
+          {
+            char r = ResidueProperties
+                    .getSingleCharacterCode(ResidueProperties
+                            .getCanonicalAminoAcid(tmpat.resName));
+            seq.append(r == '0' ? 'X' : r);
+            // System.err.println("PDBReader:Null aa3Hash for " +
+            // tmpat.resName);
+          }
+          else
+          {
+            // nucleotide flag
+            nucleotide = true;
+            seq.append(nucname.charAt((deoxyn ? 1 : 0)));
+          }
         }
-        seq.append(ResidueProperties.aa[((Integer) symbol).intValue()]);
+        else
+        {
+          if (nucleotide)
+          {
+            System.err
+                    .println("Warning: mixed nucleotide and amino acid chain.. its gonna do bad things to you!");
+          }
+          seq.append(ResidueProperties.aa[((Integer) symbol).intValue()]);
+        }
+        count++;
       }
-      count++;
     }
 
     if (id.length() < 1)
@@ -388,10 +426,14 @@ public class PDBChain
 
     // System.out.println("PDB Sequence is :\nSequence = " + seq);
     // System.out.println("No of residues = " + residues.size());
-    for (i = 0, iSize = resFeatures.size(); i < iSize; i++)
+
+    if (StructureImportSettings.isShowSeqFeatures())
     {
-      sequence.addSequenceFeature(resFeatures.elementAt(i));
-      resFeatures.setElementAt(null, i);
+      for (i = 0, iSize = resFeatures.size(); i < iSize; i++)
+      {
+        sequence.addSequenceFeature(resFeatures.elementAt(i));
+        resFeatures.setElementAt(null, i);
+      }
     }
     if (visibleChainAnnotation)
     {
@@ -544,12 +586,13 @@ public class PDBChain
         {
           for (AlignmentAnnotation ana : sequence.getAnnotation())
           {
-            List<AlignmentAnnotation> transfer = sq
+            List<AlignmentAnnotation> transfer = dsq
                     .getAlignmentAnnotations(ana.getCalcId(), ana.label);
             if (transfer == null || transfer.size() == 0)
             {
               ana = new AlignmentAnnotation(ana);
               ana.liftOver(dsq, sqmpping);
+              dsq.addAlignmentAnnotation(ana);
               // mapping.transfer(ana);
             }
             else
diff --git a/src/MCview/PDBViewer.java b/src/MCview/PDBViewer.java
index 364a46e..0fa5992 100644
--- a/src/MCview/PDBViewer.java
+++ b/src/MCview/PDBViewer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -62,7 +62,6 @@ import javax.swing.JMenuBar;
 import javax.swing.JMenuItem;
 import javax.swing.JOptionPane;
 import javax.swing.JRadioButtonMenuItem;
-import javax.swing.SwingUtilities;
 
 public class PDBViewer extends JInternalFrame implements Runnable
 {
@@ -129,29 +128,29 @@ public class PDBViewer extends JInternalFrame implements Runnable
       worker.start();
     }
 
-    if (pdbentry.getProperty() != null)
+    String method = (String) pdbentry.getProperty("method");
+    if (method != null)
     {
-      if (pdbentry.getProperty().get("method") != null)
-      {
-        title.append(" Method: ");
-        title.append(pdbentry.getProperty().get("method"));
-      }
-      if (pdbentry.getProperty().get("chains") != null)
-      {
-        title.append(" Chain:");
-        title.append(pdbentry.getProperty().get("chains"));
-      }
+      title.append(" Method: ");
+      title.append(method);
+    }
+    String ch = (String) pdbentry.getProperty("chains");
+    if (ch != null)
+    {
+      title.append(" Chain:");
+      title.append(ch);
     }
     Desktop.addInternalFrame(this, title.toString(), 400, 400);
   }
 
+  @Override
   public void run()
   {
     try
     {
       EBIFetchClient ebi = new EBIFetchClient();
       String query = "pdb:" + pdbentry.getId();
-      pdbentry.setFile(ebi.fetchDataAsFile(query, "default", "raw")
+      pdbentry.setFile(ebi.fetchDataAsFile(query, "default", ".xml")
               .getAbsolutePath());
 
       if (pdbentry.getFile() != null)
@@ -169,6 +168,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
   {
     this.addKeyListener(new KeyAdapter()
     {
+      @Override
       public void keyPressed(KeyEvent evt)
       {
         pdbcanvas.keyPressed(evt);
@@ -183,6 +183,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     png.setText("PNG");
     png.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         png_actionPerformed(e);
@@ -191,6 +192,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     eps.setText("EPS");
     eps.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         eps_actionPerformed(e);
@@ -199,6 +201,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     mapping.setText(MessageManager.getString("label.view_mapping"));
     mapping.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         mapping_actionPerformed(e);
@@ -207,6 +210,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     wire.setText(MessageManager.getString("label.wireframe"));
     wire.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         wire_actionPerformed(e);
@@ -216,6 +220,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     depth.setText(MessageManager.getString("label.depthcue"));
     depth.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         depth_actionPerformed(e);
@@ -225,6 +230,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     zbuffer.setText(MessageManager.getString("label.z_buffering"));
     zbuffer.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         zbuffer_actionPerformed(e);
@@ -233,6 +239,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     charge.setText(MessageManager.getString("label.charge_cysteine"));
     charge.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         charge_actionPerformed(e);
@@ -241,6 +248,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     chain.setText(MessageManager.getString("action.by_chain"));
     chain.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         chain_actionPerformed(e);
@@ -250,6 +258,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     seqButton.setText(MessageManager.getString("action.by_sequence"));
     seqButton.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         seqButton_actionPerformed(e);
@@ -259,6 +268,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     allchains.setText(MessageManager.getString("label.show_all_chains"));
     allchains.addItemListener(new ItemListener()
     {
+      @Override
       public void itemStateChanged(ItemEvent e)
       {
         allchains_itemStateChanged(e);
@@ -267,6 +277,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     zappo.setText(MessageManager.getString("label.zappo"));
     zappo.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         zappo_actionPerformed(e);
@@ -275,6 +286,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     taylor.setText(MessageManager.getString("label.taylor"));
     taylor.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         taylor_actionPerformed(e);
@@ -283,6 +295,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     hydro.setText(MessageManager.getString("label.hydrophobicity"));
     hydro.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         hydro_actionPerformed(e);
@@ -291,6 +304,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     helix.setText(MessageManager.getString("label.helix_propensity"));
     helix.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         helix_actionPerformed(e);
@@ -299,6 +313,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     strand.setText(MessageManager.getString("label.strand_propensity"));
     strand.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         strand_actionPerformed(e);
@@ -307,6 +322,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     turn.setText(MessageManager.getString("label.turn_propensity"));
     turn.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         turn_actionPerformed(e);
@@ -315,6 +331,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     buried.setText(MessageManager.getString("label.buried_index"));
     buried.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         buried_actionPerformed(e);
@@ -323,6 +340,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     user.setText(MessageManager.getString("action.user_defined"));
     user.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         user_actionPerformed(e);
@@ -333,6 +351,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
             .setText(MessageManager.getString("action.background_colour"));
     background.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         background_actionPerformed(e);
@@ -341,6 +360,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
     savePDB.setText(MessageManager.getString("label.pdb_file"));
     savePDB.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         savePDB_actionPerformed(e);
@@ -391,41 +411,59 @@ public class PDBViewer extends JInternalFrame implements Runnable
         radioItem.setName("USER_DEFINED");
         radioItem.addMouseListener(new MouseAdapter()
         {
+          @Override
           public void mousePressed(MouseEvent evt)
           {
-            if (evt.isControlDown()
-                    || SwingUtilities.isRightMouseButton(evt))
+            if (evt.isPopupTrigger()) // Mac
             {
-              radioItem.removeActionListener(radioItem.getActionListeners()[0]);
-
-              int option = JOptionPane.showInternalConfirmDialog(
-                      jalview.gui.Desktop.desktop,
-                      MessageManager
-                              .getString("label.remove_from_default_list"),
-                      MessageManager
-                              .getString("label.remove_user_defined_colour"),
-                      JOptionPane.YES_NO_OPTION);
-              if (option == JOptionPane.YES_OPTION)
-              {
-                jalview.gui.UserDefinedColours
-                        .removeColourFromDefaults(radioItem.getText());
-                coloursMenu.remove(radioItem);
-              }
-              else
+              offerRemoval(radioItem);
+            }
+          }
+
+          @Override
+          public void mouseReleased(MouseEvent evt)
+          {
+            if (evt.isPopupTrigger()) // Windows
+            {
+              offerRemoval(radioItem);
+            }
+          }
+
+          /**
+           * @param radioItem
+           */
+          void offerRemoval(final JRadioButtonMenuItem radioItem)
+          {
+            radioItem.removeActionListener(radioItem.getActionListeners()[0]);
+
+            int option = JOptionPane.showInternalConfirmDialog(
+                    jalview.gui.Desktop.desktop, MessageManager
+                            .getString("label.remove_from_default_list"),
+                    MessageManager
+                            .getString("label.remove_user_defined_colour"),
+                    JOptionPane.YES_NO_OPTION);
+            if (option == JOptionPane.YES_OPTION)
+            {
+              jalview.gui.UserDefinedColours
+                      .removeColourFromDefaults(radioItem.getText());
+              coloursMenu.remove(radioItem);
+            }
+            else
+            {
+              radioItem.addActionListener(new ActionListener()
               {
-                radioItem.addActionListener(new ActionListener()
+                @Override
+                public void actionPerformed(ActionEvent evt)
                 {
-                  public void actionPerformed(ActionEvent evt)
-                  {
-                    user_actionPerformed(evt);
-                  }
-                });
-              }
+                  user_actionPerformed(evt);
+                }
+              });
             }
           }
         });
         radioItem.addActionListener(new ActionListener()
         {
+          @Override
           public void actionPerformed(ActionEvent evt)
           {
             user_actionPerformed(evt);
@@ -525,20 +563,20 @@ public class PDBViewer extends JInternalFrame implements Runnable
     {
       im = new jalview.util.ImageMaker(this,
               jalview.util.ImageMaker.TYPE.PNG, "Make PNG image from view",
-              width, height, null, null);
+              width, height, null, null, null, 0, false);
     }
     else if (type == jalview.util.ImageMaker.TYPE.EPS)
     {
       im = new jalview.util.ImageMaker(this,
               jalview.util.ImageMaker.TYPE.EPS, "Make EPS file from view",
-              width, height, null, this.getTitle());
+              width, height, null, this.getTitle(), null, 0, false);
     }
     else
     {
 
       im = new jalview.util.ImageMaker(this,
               jalview.util.ImageMaker.TYPE.SVG, "Make SVG file from PCA",
-              width, height, null, this.getTitle());
+              width, height, null, this.getTitle(), null, 0, false);
     }
 
     if (im.getGraphics() != null)
diff --git a/src/MCview/PDBfile.java b/src/MCview/PDBfile.java
index 58f8ed5..ab722d1 100644
--- a/src/MCview/PDBfile.java
+++ b/src/MCview/PDBfile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,91 +20,62 @@
  */
 package MCview;
 
-import jalview.analysis.AlignSeq;
-import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.PDBEntry;
+import jalview.datamodel.DBRefSource;
 import jalview.datamodel.SequenceI;
 import jalview.io.FileParse;
+import jalview.io.StructureFile;
 import jalview.util.MessageManager;
 
-import java.awt.Color;
 import java.io.IOException;
-import java.lang.reflect.Constructor;
 import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Vector;
 
-public class PDBfile extends jalview.io.AlignFile
+public class PDBfile extends StructureFile
 {
   private static String CALC_ID_PREFIX = "JalviewPDB";
 
-  public Vector<PDBChain> chains;
-
-  public String id;
-
-  /**
-   * set to true to add derived sequence annotations (temp factor read from
-   * file, or computed secondary structure) to the alignment
-   */
-  private boolean visibleChainAnnotation = false;
-
-  /*
-   * Set true to predict secondary structure (using JMol for protein, Annotate3D
-   * for RNA)
-   */
-  private boolean predictSecondaryStructure = true;
-
-  /*
-   * Set true (with predictSecondaryStructure=true) to predict secondary
-   * structure using an external service (currently Annotate3D for RNA only)
-   */
-  private boolean externalSecondaryStructure = false;
-
   public PDBfile(boolean addAlignmentAnnotations,
           boolean predictSecondaryStructure, boolean externalSecStr)
   {
     super();
-    this.visibleChainAnnotation = addAlignmentAnnotations;
-    this.predictSecondaryStructure = predictSecondaryStructure;
-    this.externalSecondaryStructure = externalSecStr;
+    addSettings(addAlignmentAnnotations, predictSecondaryStructure,
+            externalSecStr);
   }
 
-  public PDBfile(boolean addAlignmentAnnotations,
-          boolean predictSecondaryStructure, boolean externalSecStr,
-          String file, String protocol) throws IOException
+  public PDBfile(boolean addAlignmentAnnotations, boolean predictSecStr,
+          boolean externalSecStr, String dataObject, String protocol)
+          throws IOException
   {
-    super(false, file, protocol);
-    this.visibleChainAnnotation = addAlignmentAnnotations;
-    this.predictSecondaryStructure = predictSecondaryStructure;
-    this.externalSecondaryStructure = externalSecStr;
+    super(false, dataObject, protocol);
+    addSettings(addAlignmentAnnotations, predictSecStr, externalSecStr);
     doParse();
   }
 
-  public PDBfile(boolean addAlignmentAnnotations,
-          boolean predictSecondaryStructure, boolean externalSecStr,
-          FileParse source) throws IOException
+  public PDBfile(boolean addAlignmentAnnotations, boolean predictSecStr,
+          boolean externalSecStr, FileParse source) throws IOException
   {
     super(false, source);
-    this.visibleChainAnnotation = addAlignmentAnnotations;
-    this.predictSecondaryStructure = predictSecondaryStructure;
-    this.externalSecondaryStructure = externalSecStr;
+    addSettings(addAlignmentAnnotations, predictSecStr, externalSecStr);
     doParse();
   }
 
+  @Override
   public String print()
   {
     return null;
   }
 
+  @Override
   public void parse() throws IOException
   {
+    setDbRefType(DBRefSource.PDB);
     // TODO set the filename sensibly - try using data source name.
-    id = safeName(getDataName());
+    setId(safeName(getDataName()));
 
-    chains = new Vector<PDBChain>();
+    setChains(new Vector<PDBChain>());
     List<SequenceI> rna = new ArrayList<SequenceI>();
     List<SequenceI> prot = new ArrayList<SequenceI>();
     PDBChain tmpchain;
@@ -134,7 +105,7 @@ public class PDBfile extends jalview.io.AlignFile
             }
             if (tid.length() > 0)
             {
-              id = tid;
+              setId(tid);
             }
             continue;
           }
@@ -171,20 +142,19 @@ public class PDBfile extends jalview.io.AlignFile
           }
 
           Atom tmpatom = new Atom(line);
-          tmpchain = findChain(tmpatom.chain);
-          if (tmpchain != null)
+          try
           {
+            tmpchain = findChain(tmpatom.chain);
             if (tmpatom.resNumIns.trim().equals(lastID))
             {
               // phosphorylated protein - seen both CA and P..
               continue;
             }
             tmpchain.atoms.addElement(tmpatom);
-          }
-          else
+          } catch (Exception e)
           {
-            tmpchain = new PDBChain(id, tmpatom.chain);
-            chains.addElement(tmpchain);
+            tmpchain = new PDBChain(getId(), tmpatom.chain);
+            getChains().add(tmpchain);
             tmpchain.atoms.addElement(tmpatom);
           }
           lastID = tmpatom.resNumIns.trim();
@@ -195,11 +165,11 @@ public class PDBfile extends jalview.io.AlignFile
       makeResidueList();
       makeCaBondList();
 
-      if (id == null)
+      if (getId() == null)
       {
-        id = inFile.getName();
+        setId(inFile.getName());
       }
-      for (PDBChain chain : chains)
+      for (PDBChain chain : getChains())
       {
         SequenceI chainseq = postProcessChain(chain);
         if (isRNA(chainseq))
@@ -213,7 +183,7 @@ public class PDBfile extends jalview.io.AlignFile
       }
       if (predictSecondaryStructure)
       {
-        predictSecondaryStructure(rna, prot);
+        addSecondaryStructure(rna, prot);
       }
     } catch (OutOfMemoryError er)
     {
@@ -233,95 +203,12 @@ public class PDBfile extends jalview.io.AlignFile
   }
 
   /**
-   * Predict secondary structure for RNA and/or protein sequences and add as
-   * annotations
-   * 
-   * @param rnaSequences
-   * @param proteinSequences
-   */
-  protected void predictSecondaryStructure(List<SequenceI> rnaSequences,
-          List<SequenceI> proteinSequences)
-  {
-    /*
-     * Currently using Annotate3D for RNA, but only if the 'use external
-     * prediction' flag is set
-     */
-    if (externalSecondaryStructure && rnaSequences.size() > 0)
-    {
-      try
-      {
-        processPdbFileWithAnnotate3d(rnaSequences);
-      } catch (Exception x)
-      {
-        System.err.println("Exceptions when dealing with RNA in pdb file");
-        x.printStackTrace();
-
-      }
-    }
-
-    /*
-     * Currently using JMol PDB parser for peptide
-     */
-    if (proteinSequences.size() > 0)
-    {
-      try
-      {
-        processPdbFileWithJmol(proteinSequences);
-      } catch (Exception x)
-      {
-        System.err
-                .println("Exceptions from Jmol when processing data in pdb file");
-        x.printStackTrace();
-      }
-    }
-  }
-
-  /**
    * Process a parsed chain to construct and return a Sequence, and add it to
    * the list of sequences parsed.
    * 
    * @param chain
    * @return
    */
-  protected SequenceI postProcessChain(PDBChain chain)
-  {
-    SequenceI dataset = chain.sequence;
-    dataset.setName(id + "|" + dataset.getName());
-    PDBEntry entry = new PDBEntry();
-    entry.setId(id);
-    entry.setType(PDBEntry.Type.PDB);
-    entry.setProperty(new Hashtable());
-    if (chain.id != null)
-    {
-      // entry.getProperty().put("CHAIN", chains.elementAt(i).id);
-      entry.setChainCode(String.valueOf(chain.id));
-    }
-    if (inFile != null)
-    {
-      entry.setFile(inFile.getAbsolutePath());
-    }
-    else
-    {
-      // TODO: decide if we should dump the datasource to disk
-      entry.setFile(getDataName());
-    }
-    dataset.addPDBId(entry);
-    // PDBChain objects maintain reference to dataset
-    SequenceI chainseq = dataset.deriveSequence();
-    seqs.addElement(chainseq);
-
-    AlignmentAnnotation[] chainannot = chainseq.getAnnotation();
-
-    if (chainannot != null && visibleChainAnnotation)
-    {
-      for (int ai = 0; ai < chainannot.length; ai++)
-      {
-        chainannot[ai].visible = visibleChainAnnotation;
-        annotations.addElement(chainannot[ai]);
-      }
-    }
-    return chainseq;
-  }
 
   public static boolean isCalcIdHandled(String calcId)
   {
@@ -360,204 +247,11 @@ public class PDBfile extends jalview.io.AlignFile
             oldId = "";
           }
           aa.setCalcId(CALC_ID_PREFIX);
-          aa.setProperty("PDBID", id);
+          aa.setProperty("PDBID", getId());
           aa.setProperty("oldCalcId", oldId);
         }
       }
     }
   }
 
-  private void processPdbFileWithJmol(List<SequenceI> prot)
-          throws Exception
-  {
-    try
-    {
-      Class cl = Class.forName("jalview.ext.jmol.PDBFileWithJmol");
-      if (cl != null)
-      {
-        final Constructor constructor = cl
-                .getConstructor(new Class[] { FileParse.class });
-        final Object[] args = new Object[] { new FileParse(getDataName(),
-                type) };
-        Object jmf = constructor.newInstance(args);
-        AlignmentI al = new Alignment((SequenceI[]) cl.getMethod(
-                "getSeqsAsArray", new Class[] {}).invoke(jmf));
-        cl.getMethod("addAnnotations", new Class[] { AlignmentI.class })
-                .invoke(jmf, al);
-        for (SequenceI sq : al.getSequences())
-        {
-          if (sq.getDatasetSequence() != null)
-          {
-            sq.getDatasetSequence().getAllPDBEntries().clear();
-          }
-          else
-          {
-            sq.getAllPDBEntries().clear();
-          }
-        }
-        replaceAndUpdateChains(prot, al, AlignSeq.PEP, false);
-      }
-    } catch (ClassNotFoundException q)
-    {
-    }
-  }
-
-  private void replaceAndUpdateChains(List<SequenceI> prot, AlignmentI al,
-          String pep, boolean b)
-  {
-    List<List<? extends Object>> replaced = AlignSeq
-            .replaceMatchingSeqsWith(seqs, annotations, prot, al, pep,
-                    false);
-    for (PDBChain ch : chains)
-    {
-      int p = 0;
-      for (SequenceI sq : (List<SequenceI>) replaced.get(0))
-      {
-        p++;
-        if (sq == ch.sequence || sq.getDatasetSequence() == ch.sequence)
-        {
-          p = -p;
-          break;
-        }
-      }
-      if (p < 0)
-      {
-        p = -p - 1;
-        // set shadow entry for chains
-        ch.shadow = (SequenceI) replaced.get(1).get(p);
-        ch.shadowMap = ((AlignSeq) replaced.get(2).get(p))
-                .getMappingFromS1(false);
-      }
-    }
-  }
-
-  private void processPdbFileWithAnnotate3d(List<SequenceI> rna)
-          throws Exception
-  {
-    // System.out.println("this is a PDB format and RNA sequence");
-    // note: we use reflection here so that the applet can compile and run
-    // without the HTTPClient bits and pieces needed for accessing Annotate3D
-    // web service
-    try
-    {
-      Class cl = Class.forName("jalview.ws.jws1.Annotate3D");
-      if (cl != null)
-      {
-        // TODO: use the PDB ID of the structure if one is available, to save
-        // bandwidth and avoid uploading the whole structure to the service
-        Object annotate3d = cl.getConstructor(new Class[] {}).newInstance(
-                new Object[] {});
-        AlignmentI al = ((AlignmentI) cl.getMethod("getRNAMLFor",
-                new Class[] { FileParse.class }).invoke(annotate3d,
-                new Object[] { new FileParse(getDataName(), type) }));
-        for (SequenceI sq : al.getSequences())
-        {
-          if (sq.getDatasetSequence() != null)
-          {
-            if (sq.getDatasetSequence().getAllPDBEntries() != null)
-            {
-              sq.getDatasetSequence().getAllPDBEntries().clear();
-            }
-          }
-          else
-          {
-            if (sq.getAllPDBEntries() != null)
-            {
-              sq.getAllPDBEntries().clear();
-            }
-          }
-        }
-        replaceAndUpdateChains(rna, al, AlignSeq.DNA, false);
-      }
-    } catch (ClassNotFoundException x)
-    {
-      // ignore classnotfounds - occurs in applet
-    }
-    ;
-  }
-
-  /**
-   * make a friendly ID string.
-   * 
-   * @param dataName
-   * @return truncated dataName to after last '/'
-   */
-  private String safeName(String dataName)
-  {
-    int p = 0;
-    while ((p = dataName.indexOf("/")) > -1 && p < dataName.length())
-    {
-      dataName = dataName.substring(p + 1);
-    }
-    return dataName;
-  }
-
-  public void makeResidueList()
-  {
-    for (int i = 0; i < chains.size(); i++)
-    {
-      chains.elementAt(i).makeResidueList(visibleChainAnnotation);
-    }
-  }
-
-  public void makeCaBondList()
-  {
-    for (int i = 0; i < chains.size(); i++)
-    {
-      chains.elementAt(i).makeCaBondList();
-    }
-  }
-
-  public PDBChain findChain(String id)
-  {
-    for (int i = 0; i < chains.size(); i++)
-    {
-      if (chains.elementAt(i).id.equals(id))
-      {
-        return chains.elementAt(i);
-      }
-    }
-
-    return null;
-  }
-
-  public void setChargeColours()
-  {
-    for (int i = 0; i < chains.size(); i++)
-    {
-      chains.elementAt(i).setChargeColours();
-    }
-  }
-
-  public void setColours(jalview.schemes.ColourSchemeI cs)
-  {
-    for (int i = 0; i < chains.size(); i++)
-    {
-      chains.elementAt(i).setChainColours(cs);
-    }
-  }
-
-  public void setChainColours()
-  {
-    for (int i = 0; i < chains.size(); i++)
-    {
-      // divide by zero --> infinity --> 255 ;-)
-      chains.elementAt(i).setChainColours(
-              Color.getHSBColor(1.0f / i, .4f, 1.0f));
-    }
-  }
-
-  public static boolean isRNA(SequenceI seq)
-  {
-    for (char c : seq.getSequence())
-    {
-      if ((c != 'A') && (c != 'C') && (c != 'G') && (c != 'U'))
-      {
-        return false;
-      }
-    }
-
-    return true;
-
-  }
 }
diff --git a/src/MCview/Residue.java b/src/MCview/Residue.java
index 2b0159e..542e6d9 100644
--- a/src/MCview/Residue.java
+++ b/src/MCview/Residue.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -49,4 +49,9 @@ public class Residue
 
     return null;
   }
+
+  public Vector<Atom> getAtoms()
+  {
+    return this.atoms;
+  }
 }
diff --git a/src/MCview/Zsort.java b/src/MCview/Zsort.java
index e7121d3..80d01a9 100644
--- a/src/MCview/Zsort.java
+++ b/src/MCview/Zsort.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/com/stevesoft/pat/RegexWriter.java b/src/com/stevesoft/pat/RegexWriter.java
index 61bcdf6..57e2170 100644
--- a/src/com/stevesoft/pat/RegexWriter.java
+++ b/src/com/stevesoft/pat/RegexWriter.java
@@ -8,6 +8,7 @@
 package com.stevesoft.pat;
 
 import java.io.IOException;
+import java.io.StringWriter;
 import java.io.Writer;
 
 import com.stevesoft.pat.wrap.WriterWrap;
@@ -231,4 +232,51 @@ public class RegexWriter extends Writer
   {
     bufferSize = i;
   }
+
+  static void test(String re, String inp, int n) throws Exception
+  {
+    StringWriter sw = new StringWriter();
+    Regex rex = Regex.perlCode(re);
+    String res1 = rex.replaceAll(inp);
+    RegexWriter rw = new RegexWriter(rex, sw);
+    for (int i = 0; i < inp.length(); i++)
+    {
+      rw.write(inp.charAt(i));
+    }
+    rw.close();
+    String res2 = sw.toString();
+    if (!res1.equals(res2))
+    {
+      System.out.println("nmax=" + n);
+      System.out.println("re=" + re);
+      System.out.println("inp=" + inp);
+      System.out.println("res1=" + res1);
+      System.out.println("res2=" + res2);
+      System.exit(255);
+    }
+  }
+
+  public static void main(String[] args) throws Exception
+  {
+    for (int n = 1; n <= 1; n++)
+    {
+      test("s/x/y/", "-----x123456789", n);
+      test("s/x/y/", "x123456789", n);
+      test("s/x/y/", "-----x", n);
+      test("s/x.*?x/y/", ".xx..x..x...x...x....x....x", n);
+      test("s/x.*x/[$&]/", "--x........x--xx", n);
+      test("s/x.*x/[$&]/", "--x........x------", n);
+      test("s/.$/a/m", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbbbbbbbbbbbb", n);
+      test("s/.$/a/", "123", n);
+      test("s/.$/a/", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbb", n);
+      test("s/^./a/", "bb\nbbb\nbbbb\nbbbbb\nbbbbbb\nbb", n);
+      test("s/$/a/", "bbb", n);
+      test("s/^/a/", "bbb", n);
+      test("s/^/a/", "", n);
+      test("s{.*}{N}", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", n);
+      test("s/.{0,7}/y/", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", n);
+      test("s/x/$&/", "xxx", n);
+    }
+    System.out.println("Success!!!");
+  }
 }
diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/ChimUtils.java b/src/ext/edu/ucsf/rbvi/strucviz2/ChimUtils.java
index 03f51f7..1d57a31 100644
--- a/src/ext/edu/ucsf/rbvi/strucviz2/ChimUtils.java
+++ b/src/ext/edu/ucsf/rbvi/strucviz2/ChimUtils.java
@@ -1,3 +1,35 @@
+/* vim: set ts=2: */
+/**
+ * Copyright (c) 2006 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions, and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions, and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *   3. Redistributions must acknowledge that this software was
+ *      originally developed by the UCSF Computer Graphics Laboratory
+ *      under support by the NIH National Center for Research Resources,
+ *      grant P41-RR01081.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
 package ext.edu.ucsf.rbvi.strucviz2;
 
 import java.awt.Color;
@@ -60,10 +92,25 @@ public abstract class ChimUtils
    */
   // invoked by openModel in ChimeraManager
   // line: #1, chain A: hiv-1 protease
+  // line: Model 0 (filename)
   public static int[] parseOpenedModelNumber(String inputLine)
   {
     int hash = inputLine.indexOf('#');
-    int space = inputLine.indexOf(',', hash);
+    int space = -1;
+    if (hash == (-1))
+    {
+      hash = inputLine.indexOf("Model");
+      if (hash >= 0)
+      {
+        hash = hash + 5;
+      }
+      space = inputLine.indexOf(' ', hash + 1);
+    }
+    else
+    {
+      space = inputLine.indexOf(',', hash);
+    }
+
     int decimal = inputLine.substring(hash + 1, space).indexOf('.');
     // model number is between hash+1 and space
     int modelNumber = -1;
@@ -93,7 +140,9 @@ public abstract class ChimUtils
   {
     int start = inputLine.indexOf("name ");
     if (start < 0)
+    {
       return null;
+    }
     // Might get a quoted string (don't understand why, but there you have it)
     if (inputLine.startsWith("\"", start + 5))
     {
@@ -104,7 +153,9 @@ public abstract class ChimUtils
         return inputLine.substring(start, end);
       }
       else
+      {
         return inputLine.substring(start);
+      }
     }
     else
     {
@@ -176,12 +227,18 @@ public abstract class ChimUtils
     {
       String[] subSplit = split[0].substring(1).split("\\.");
       if (subSplit.length > 0)
+      {
         model = Integer.parseInt(subSplit[0]);
+      }
       else
+      {
         model = Integer.parseInt(split[0].substring(1));
+      }
 
       if (subSplit.length > 1)
+      {
         submodel = Integer.parseInt(subSplit[1]);
+      }
     } catch (Exception e)
     {
       // ignore
@@ -258,7 +315,9 @@ public abstract class ChimUtils
   {
     if (atom.equals("C") || atom.equals("CA") || atom.equals("N")
             || atom.equals("O") || atom.equals("H"))
+    {
       return true;
+    }
     return false;
   }
 
@@ -449,7 +508,9 @@ public abstract class ChimUtils
       }
       else
       {
+        // length > 1, so we probably have a file name with "." in it
         logger.info("Could not parse model identifier: " + modelID);
+        resKeyParts[0] = modelID;
       }
     }
   }
@@ -547,8 +608,7 @@ public abstract class ChimUtils
       // }
 
       // System.out.println("model = " + model + " chain = " + chain +
-      // " residue = " +
-      // residue);
+      // " residue = " + residue);
       if (model != null)
       {
         List<ChimeraModel> models = chimeraManager.getChimeraModels(model,
@@ -594,13 +654,19 @@ public abstract class ChimUtils
       }
 
       if (chimeraResidue != null)
+      {
         return chimeraResidue;
+      }
 
       if (chimeraChain != null)
+      {
         return chimeraChain;
+      }
 
       if (chimeraModel != null)
+      {
         return chimeraModel;
+      }
 
     } catch (Exception ex)
     {
@@ -709,13 +775,19 @@ public abstract class ChimUtils
       }
 
       if (chimeraResidue != null)
+      {
         return chimeraResidue;
+      }
 
       if (chimeraChain != null)
+      {
         return chimeraChain;
+      }
 
       if (chimeraModel != null)
+      {
         return chimeraModel;
+      }
 
     } catch (Exception ex)
     {
@@ -736,7 +808,9 @@ public abstract class ChimUtils
   public static String findStructures(String residueList)
   {
     if (residueList == null)
+    {
       return null;
+    }
     String[] residues = residueList.split(",");
     Map<String, String> structureNameMap = new HashMap<String, String>();
     for (int i = 0; i < residues.length; i++)
@@ -748,15 +822,21 @@ public abstract class ChimUtils
       }
     }
     if (structureNameMap.isEmpty())
+    {
       return null;
+    }
 
     String structure = null;
     for (String struct : structureNameMap.keySet())
     {
       if (structure == null)
+      {
         structure = new String();
+      }
       else
+      {
         structure = structure.concat(",");
+      }
       structure = structure.concat(struct);
     }
     return structure;
@@ -798,7 +878,9 @@ public abstract class ChimUtils
         {
           resRange = resRange.concat("-");
           if (chain != null && range[res].indexOf('.') == -1)
+          {
             range[res] = range[res].concat("." + chain);
+          }
         }
 
         if (res == 0 && range.length >= 2 && range[res].indexOf('.') > 0)
@@ -887,8 +969,10 @@ public abstract class ChimUtils
   public static String toFullName(String aaType)
   {
     if (!aaNames.containsKey(aaType))
+    {
       return aaType;
-    String[] ids = ((String) aaNames.get(aaType)).split(" ");
+    }
+    String[] ids = aaNames.get(aaType).split(" ");
     return ids[2].replace('_', ' ');
   }
 
@@ -902,8 +986,10 @@ public abstract class ChimUtils
   public static String toSingleLetter(String aaType)
   {
     if (!aaNames.containsKey(aaType))
+    {
       return aaType;
-    String[] ids = ((String) aaNames.get(aaType)).split(" ");
+    }
+    String[] ids = aaNames.get(aaType).split(" ");
     return ids[0];
   }
 
@@ -917,8 +1003,10 @@ public abstract class ChimUtils
   public static String toThreeLetter(String aaType)
   {
     if (!aaNames.containsKey(aaType))
+    {
       return aaType;
-    String[] ids = ((String) aaNames.get(aaType)).split(" ");
+    }
+    String[] ids = aaNames.get(aaType).split(" ");
     return ids[1];
   }
 
@@ -932,10 +1020,14 @@ public abstract class ChimUtils
   public static String toSMILES(String aaType)
   {
     if (!aaNames.containsKey(aaType))
+    {
       return null;
-    String[] ids = ((String) aaNames.get(aaType)).split(" ");
+    }
+    String[] ids = aaNames.get(aaType).split(" ");
     if (ids.length < 4)
+    {
       return null;
+    }
     return ids[3];
   }
 
diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
index 2de2829..736e459 100644
--- a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
+++ b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
@@ -1,3 +1,35 @@
+/* vim: set ts=2: */
+/**
+ * Copyright (c) 2006 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions, and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions, and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *   3. Redistributions must acknowledge that this software was
+ *      originally developed by the UCSF Computer Graphics Laboratory
+ *      under support by the NIH National Center for Research Resources,
+ *      grant P41-RR01081.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
 package ext.edu.ucsf.rbvi.strucviz2;
 
 import jalview.ws.HttpClientUtils;
@@ -27,6 +59,10 @@ import ext.edu.ucsf.rbvi.strucviz2.port.ListenerThreads;
  */
 public class ChimeraManager
 {
+  private static final int REST_REPLY_TIMEOUT_MS = 15000;
+
+  private static final int CONNECTION_TIMEOUT_MS = 100;
+
   private static final boolean debug = false;
 
   private int chimeraRestPort;
@@ -178,6 +214,7 @@ public class ChimeraManager
   {
     logger.info("chimera open " + modelPath);
     // stopListening();
+    List<ChimeraModel> modelList = getModelList();
     List<String> response = null;
     // TODO: [Optional] Handle modbase models
     if (type == ModelType.MODBASE_MODEL)
@@ -197,76 +234,29 @@ public class ChimeraManager
       logger.warn("Could not open " + modelPath);
       return null;
     }
-    List<ChimeraModel> models = new ArrayList<ChimeraModel>();
-    int[] modelNumbers = null;
-    if (type == ModelType.PDB_MODEL)
-    {
-      for (String line : response)
-      {
-        if (line.startsWith("#"))
-        {
-          modelNumbers = ChimUtils.parseOpenedModelNumber(line);
-          if (modelNumbers != null)
-          {
-            int modelNumber = ChimUtils.makeModelKey(modelNumbers[0],
-                    modelNumbers[1]);
-            if (currentModelsMap.containsKey(modelNumber))
-            {
-              continue;
-            }
-            ChimeraModel newModel = new ChimeraModel(modelName, type,
-                    modelNumbers[0], modelNumbers[1]);
-            currentModelsMap.put(modelNumber, newModel);
-            models.add(newModel);
-
-            //
-            // patch for Jalview - set model name in Chimera
-            // TODO: find a variant that works for sub-models
-            sendChimeraCommand("setattr M name " + modelName + " #"
-                    + modelNumbers[0], false);
-            // end patch for Jalview
-
-            modelNumbers = null;
-          }
-        }
-      }
-    }
-    else
+
+    // patch for Jalview - set model name in Chimera
+    // TODO: find a variant that works for sub-models
+    for (ChimeraModel newModel : getModelList())
     {
-      // TODO: [Optional] Open smiles from file would fail. Do we need it?
-      // If parsing fails, iterate over all open models to get the right one
-      List<ChimeraModel> openModels = getModelList();
-      for (ChimeraModel openModel : openModels)
+      if (!modelList.contains(newModel))
       {
-        String openModelName = openModel.getModelName();
-        if (openModelName.endsWith("..."))
-        {
-          openModelName = openModelName.substring(0,
-                  openModelName.length() - 3);
-        }
-        if (modelPath.startsWith(openModelName))
-        {
-          openModel.setModelName(modelPath);
-          int modelNumber = ChimUtils
-                  .makeModelKey(openModel.getModelNumber(),
-                          openModel.getSubModelNumber());
-          if (!currentModelsMap.containsKey(modelNumber))
-          {
-            currentModelsMap.put(modelNumber, openModel);
-            models.add(openModel);
-          }
-        }
+        newModel.setModelName(modelName);
+        sendChimeraCommand(
+                "setattr M name " + modelName + " #"
+                        + newModel.getModelNumber(), false);
+        modelList.add(newModel);
       }
     }
 
     // assign color and residues to open models
-    for (ChimeraModel newModel : models)
+    for (ChimeraModel chimeraModel : modelList)
     {
       // get model color
-      Color modelColor = getModelColor(newModel);
+      Color modelColor = getModelColor(chimeraModel);
       if (modelColor != null)
       {
-        newModel.setModelColor(modelColor);
+        chimeraModel.setModelColor(modelColor);
       }
 
       // Get our properties (default color scheme, etc.)
@@ -276,13 +266,13 @@ public class ChimeraManager
       // Create the information we need for the navigator
       if (type != ModelType.SMILES)
       {
-        addResidues(newModel);
+        addResidues(chimeraModel);
       }
     }
 
     sendChimeraCommand("focus", false);
     // startListening(); // see ChimeraListener
-    return models;
+    return modelList;
   }
 
   /**
@@ -775,7 +765,7 @@ public class ChimeraManager
    */
   public List<String> sendChimeraCommand(String command, boolean reply)
   {
-    // System.out.println("chimeradebug>> " + command);
+   // System.out.println("chimeradebug>> " + command);
     if (!isChimeraLaunched() || command == null
             || "".equals(command.trim()))
     {
@@ -828,8 +818,8 @@ public class ChimeraManager
     BufferedReader response = null;
     try
     {
-      response = HttpClientUtils
-              .doHttpUrlPost(restUrl, commands, 100, 5000);
+      response = HttpClientUtils.doHttpUrlPost(restUrl, commands, CONNECTION_TIMEOUT_MS,
+              REST_REPLY_TIMEOUT_MS);
       String line = "";
       while ((line = response.readLine()) != null)
       {
diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraModel.java b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraModel.java
index 0700565..d2f4b11 100644
--- a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraModel.java
+++ b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraModel.java
@@ -1,3 +1,35 @@
+/* vim: set ts=2: */
+/**
+ * Copyright (c) 2006 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions, and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions, and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *   3. Redistributions must acknowledge that this software was
+ *      originally developed by the UCSF Computer Graphics Laboratory
+ *      under support by the NIH National Center for Research Resources,
+ *      grant P41-RR01081.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
 package ext.edu.ucsf.rbvi.strucviz2;
 
 import java.awt.Color;
@@ -145,6 +177,7 @@ public class ChimeraModel implements ChimeraStructuralObject
    * 
    * @return ChimeraModel
    */
+  @Override
   public ChimeraModel getChimeraModel()
   {
     return this;
@@ -272,6 +305,7 @@ public class ChimeraModel implements ChimeraStructuralObject
    * 
    * @return user data
    */
+  @Override
   public Object getUserData()
   {
     return userData;
@@ -283,6 +317,7 @@ public class ChimeraModel implements ChimeraStructuralObject
    * @param data
    *          user data to associate with this model
    */
+  @Override
   public void setUserData(Object data)
   {
     this.userData = data;
@@ -293,6 +328,7 @@ public class ChimeraModel implements ChimeraStructuralObject
    * 
    * @return the selected state
    */
+  @Override
   public boolean isSelected()
   {
     return selected;
@@ -304,6 +340,7 @@ public class ChimeraModel implements ChimeraStructuralObject
    * @param selected
    *          a boolean to set the selected state to
    */
+  @Override
   public void setSelected(boolean selected)
   {
     this.selected = selected;
@@ -314,6 +351,7 @@ public class ChimeraModel implements ChimeraStructuralObject
    * 
    * @return the chains in this model as a list
    */
+  @Override
   public List<ChimeraStructuralObject> getChildren()
   {
     return new ArrayList<ChimeraStructuralObject>(chainMap.values());
@@ -414,6 +452,7 @@ public class ChimeraModel implements ChimeraStructuralObject
   /**
    * Checks if this model has selected children.
    */
+  @Override
   public boolean hasSelectedChildren()
   {
     if (selected)
@@ -458,10 +497,13 @@ public class ChimeraModel implements ChimeraStructuralObject
   /**
    * Return the Chimera specification for this model.
    */
+  @Override
   public String toSpec()
   {
     if (subModelNumber == 0)
+    {
       return ("#" + modelNumber);
+    }
     return ("#" + modelNumber + "." + subModelNumber);
   }
 
@@ -469,6 +511,7 @@ public class ChimeraModel implements ChimeraStructuralObject
    * Return a string representation for the model. Shorten if longer than 100
    * characters.
    */
+  @Override
   public String toString()
   {
     String modelName = "";
@@ -554,4 +597,27 @@ public class ChimeraModel implements ChimeraStructuralObject
     }
     return nodeName;
   }
+
+  @Override
+  public boolean equals(Object otherChimeraModel)
+  {
+    if (!(otherChimeraModel instanceof ChimeraModel))
+    {
+      return false;
+    }
+    ChimeraModel otherCM = ((ChimeraModel) otherChimeraModel);
+    return this.name.equals(otherCM.name)
+            && this.modelNumber == otherCM.modelNumber
+            && this.type == otherCM.type;
+  }
+
+  @Override
+  public int hashCode()
+  {
+    int hashCode = 1;
+    hashCode = hashCode * 37 + this.name.hashCode();
+    hashCode = hashCode * 37 + this.type.hashCode();
+    hashCode = (hashCode * 37) + modelNumber;
+    return hashCode;
+  }
 }
diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java b/src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java
index 6fd6340..effe556 100644
--- a/src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java
+++ b/src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java
@@ -1,3 +1,35 @@
+/* vim: set ts=2: */
+/**
+ * Copyright (c) 2006 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions, and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions, and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *   3. Redistributions must acknowledge that this software was
+ *      originally developed by the UCSF Computer Graphics Laboratory
+ *      under support by the NIH National Center for Research Resources,
+ *      grant P41-RR01081.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
 package ext.edu.ucsf.rbvi.strucviz2;
 
 import jalview.bin.Cache;
diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/StructureSettings.java b/src/ext/edu/ucsf/rbvi/strucviz2/StructureSettings.java
index 08a6cb7..77c1883 100644
--- a/src/ext/edu/ucsf/rbvi/strucviz2/StructureSettings.java
+++ b/src/ext/edu/ucsf/rbvi/strucviz2/StructureSettings.java
@@ -1,3 +1,35 @@
+/* vim: set ts=2: */
+/**
+ * Copyright (c) 2006 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions, and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions, and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *   3. Redistributions must acknowledge that this software was
+ *      originally developed by the UCSF Computer Graphics Laboratory
+ *      under support by the NIH National Center for Research Resources,
+ *      grant P41-RR01081.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
 package ext.edu.ucsf.rbvi.strucviz2;
 
 /**
diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/port/ListenerThreads.java b/src/ext/edu/ucsf/rbvi/strucviz2/port/ListenerThreads.java
index 2b2ce48..379097c 100644
--- a/src/ext/edu/ucsf/rbvi/strucviz2/port/ListenerThreads.java
+++ b/src/ext/edu/ucsf/rbvi/strucviz2/port/ListenerThreads.java
@@ -1,3 +1,35 @@
+/* vim: set ts=2: */
+/**
+ * Copyright (c) 2006 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions, and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions, and the following
+ *      disclaimer in the documentation and/or other materials provided
+ *      with the distribution.
+ *   3. Redistributions must acknowledge that this software was
+ *      originally developed by the UCSF Computer Graphics Laboratory
+ *      under support by the NIH National Center for Research Resources,
+ *      grant P41-RR01081.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
 package ext.edu.ucsf.rbvi.strucviz2.port;
 
 import java.io.BufferedReader;
diff --git a/src/ext/vamsas/IRegistry.java b/src/ext/vamsas/IRegistry.java
index cd476f5..1b0000e 100644
--- a/src/ext/vamsas/IRegistry.java
+++ b/src/ext/vamsas/IRegistry.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/IRegistryService.java b/src/ext/vamsas/IRegistryService.java
index d0d9e53..2810448 100644
--- a/src/ext/vamsas/IRegistryService.java
+++ b/src/ext/vamsas/IRegistryService.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/IRegistryServiceLocator.java b/src/ext/vamsas/IRegistryServiceLocator.java
index 0bd1696..3b75f4d 100644
--- a/src/ext/vamsas/IRegistryServiceLocator.java
+++ b/src/ext/vamsas/IRegistryServiceLocator.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/Jpred.java b/src/ext/vamsas/Jpred.java
index 8cd6797..34e90bd 100644
--- a/src/ext/vamsas/Jpred.java
+++ b/src/ext/vamsas/Jpred.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/JpredService.java b/src/ext/vamsas/JpredService.java
index 9d32f29..1b231ac 100644
--- a/src/ext/vamsas/JpredService.java
+++ b/src/ext/vamsas/JpredService.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/JpredServiceLocator.java b/src/ext/vamsas/JpredServiceLocator.java
index 937775a..666175c 100644
--- a/src/ext/vamsas/JpredServiceLocator.java
+++ b/src/ext/vamsas/JpredServiceLocator.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/JpredSoapBindingStub.java b/src/ext/vamsas/JpredSoapBindingStub.java
index 079ce85..8126b9d 100644
--- a/src/ext/vamsas/JpredSoapBindingStub.java
+++ b/src/ext/vamsas/JpredSoapBindingStub.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/MuscleWS.java b/src/ext/vamsas/MuscleWS.java
index 5e1aaba..18a5240 100644
--- a/src/ext/vamsas/MuscleWS.java
+++ b/src/ext/vamsas/MuscleWS.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/MuscleWSService.java b/src/ext/vamsas/MuscleWSService.java
index 18284c4..c6f84a1 100644
--- a/src/ext/vamsas/MuscleWSService.java
+++ b/src/ext/vamsas/MuscleWSService.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/MuscleWSServiceLocator.java b/src/ext/vamsas/MuscleWSServiceLocator.java
index c4f83c9..c02dede 100644
--- a/src/ext/vamsas/MuscleWSServiceLocator.java
+++ b/src/ext/vamsas/MuscleWSServiceLocator.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/MuscleWSSoapBindingStub.java b/src/ext/vamsas/MuscleWSSoapBindingStub.java
index 4ea8a26..57548d6 100644
--- a/src/ext/vamsas/MuscleWSSoapBindingStub.java
+++ b/src/ext/vamsas/MuscleWSSoapBindingStub.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/RegistryServiceSoapBindingStub.java b/src/ext/vamsas/RegistryServiceSoapBindingStub.java
index ffaac05..1e09e93 100644
--- a/src/ext/vamsas/RegistryServiceSoapBindingStub.java
+++ b/src/ext/vamsas/RegistryServiceSoapBindingStub.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/SeqSearchI.java b/src/ext/vamsas/SeqSearchI.java
index acdea32..b090d6e 100644
--- a/src/ext/vamsas/SeqSearchI.java
+++ b/src/ext/vamsas/SeqSearchI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/SeqSearchServiceLocator.java b/src/ext/vamsas/SeqSearchServiceLocator.java
index 86c3043..aae8fa2 100644
--- a/src/ext/vamsas/SeqSearchServiceLocator.java
+++ b/src/ext/vamsas/SeqSearchServiceLocator.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/SeqSearchServiceService.java b/src/ext/vamsas/SeqSearchServiceService.java
index f727ea9..e9a46de 100644
--- a/src/ext/vamsas/SeqSearchServiceService.java
+++ b/src/ext/vamsas/SeqSearchServiceService.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/SeqSearchServiceSoapBindingStub.java b/src/ext/vamsas/SeqSearchServiceSoapBindingStub.java
index 57ce618..0544b08 100644
--- a/src/ext/vamsas/SeqSearchServiceSoapBindingStub.java
+++ b/src/ext/vamsas/SeqSearchServiceSoapBindingStub.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/ServiceHandle.java b/src/ext/vamsas/ServiceHandle.java
index 27405f9..af727c7 100644
--- a/src/ext/vamsas/ServiceHandle.java
+++ b/src/ext/vamsas/ServiceHandle.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/ext/vamsas/ServiceHandles.java b/src/ext/vamsas/ServiceHandles.java
index 7ad7c39..af4ef65 100644
--- a/src/ext/vamsas/ServiceHandles.java
+++ b/src/ext/vamsas/ServiceHandles.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/analysis/AAFrequency.java b/src/jalview/analysis/AAFrequency.java
index 3a79959..72b61d0 100644
--- a/src/jalview/analysis/AAFrequency.java
+++ b/src/jalview/analysis/AAFrequency.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -24,7 +24,15 @@ import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.Profile;
+import jalview.datamodel.ProfileI;
+import jalview.datamodel.Profiles;
+import jalview.datamodel.ProfilesI;
+import jalview.datamodel.ResidueCount;
+import jalview.datamodel.ResidueCount.SymbolCounts;
 import jalview.datamodel.SequenceI;
+import jalview.ext.android.SparseIntArray;
+import jalview.util.Comparison;
 import jalview.util.Format;
 import jalview.util.MappingUtils;
 import jalview.util.QuickSort;
@@ -32,7 +40,6 @@ import jalview.util.QuickSort;
 import java.util.Arrays;
 import java.util.Hashtable;
 import java.util.List;
-import java.util.Set;
 
 /**
  * Takes in a vector or array of sequences and column start and column end and
@@ -45,20 +52,8 @@ import java.util.Set;
  */
 public class AAFrequency
 {
-  private static final int TO_UPPER_CASE = 'A' - 'a'; // -32
-
-  public static final String MAXCOUNT = "C";
-
-  public static final String MAXRESIDUE = "R";
-
-  public static final String PID_GAPS = "G";
-
-  public static final String PID_NOGAPS = "N";
-
   public static final String PROFILE = "P";
 
-  public static final String ENCODED_CHARS = "E";
-
   /*
    * Quick look-up of String value of char 'A' to 'Z'
    */
@@ -72,13 +67,13 @@ public class AAFrequency
     }
   }
 
-  public static final Hashtable[] calculate(List<SequenceI> list,
+  public static final ProfilesI calculate(List<SequenceI> list,
           int start, int end)
   {
     return calculate(list, start, end, false);
   }
 
-  public static final Hashtable[] calculate(List<SequenceI> sequences,
+  public static final ProfilesI calculate(List<SequenceI> sequences,
           int start, int end, boolean profile)
   {
     SequenceI[] seqs = new SequenceI[sequences.size()];
@@ -88,307 +83,262 @@ public class AAFrequency
       for (int i = 0; i < sequences.size(); i++)
       {
         seqs[i] = sequences.get(i);
-        if (seqs[i].getLength() > width)
+        int length = seqs[i].getLength();
+        if (length > width)
         {
-          width = seqs[i].getLength();
+          width = length;
         }
       }
 
-      Hashtable[] reply = new Hashtable[width];
-
       if (end >= width)
       {
         end = width;
       }
 
-      calculate(seqs, start, end, reply, profile);
+      ProfilesI reply = calculate(seqs, width, start, end, profile);
       return reply;
     }
   }
 
-  public static final void calculate(SequenceI[] sequences, int start,
-          int end, Hashtable[] result, boolean profile)
+  /**
+   * Calculate the consensus symbol(s) for each column in the given range.
+   * 
+   * @param sequences
+   * @param width
+   *          the full width of the alignment
+   * @param start
+   *          start column (inclusive, base zero)
+   * @param end
+   *          end column (exclusive)
+   * @param saveFullProfile
+   *          if true, store all symbol counts
+   */
+  public static final ProfilesI calculate(final SequenceI[] sequences,
+          int width, int start, int end, boolean saveFullProfile)
   {
-    Hashtable residueHash;
-    int maxCount, nongap, i, j, v;
-    int jSize = sequences.length;
-    String maxResidue;
-    char c = '-';
-    float percentage;
+    // long now = System.currentTimeMillis();
+    int seqCount = sequences.length;
+    boolean nucleotide = false;
+    int nucleotideCount = 0;
+    int peptideCount = 0;
 
-    int[] values = new int[255];
+    ProfileI[] result = new ProfileI[width];
 
-    char[] seq;
-
-    for (i = start; i < end; i++)
+    for (int column = start; column < end; column++)
     {
-      residueHash = new Hashtable();
-      maxCount = 0;
-      maxResidue = "";
-      nongap = 0;
-      values = new int[255];
+      /*
+       * Apply a heuristic to detect nucleotide data (which can
+       * be counted in more compact arrays); here we test for
+       * more than 90% nucleotide; recheck every 10 columns in case
+       * of misleading data e.g. highly conserved Alanine in peptide!
+       * Mistakenly guessing nucleotide has a small performance cost,
+       * as it will result in counting in sparse arrays.
+       * Mistakenly guessing peptide has a small space cost, 
+       * as it will use a larger than necessary array to hold counts. 
+       */
+      if (nucleotideCount > 100 && column % 10 == 0)
+      {
+        nucleotide = (9 * peptideCount < nucleotideCount);
+      }
+      ResidueCount residueCounts = new ResidueCount(nucleotide);
 
-      for (j = 0; j < jSize; j++)
+      for (int row = 0; row < seqCount; row++)
       {
-        if (sequences[j] == null)
+        if (sequences[row] == null)
         {
           System.err
                   .println("WARNING: Consensus skipping null sequence - possible race condition.");
           continue;
         }
-        seq = sequences[j].getSequence();
-        if (seq.length > i)
+        char[] seq = sequences[row].getSequence();
+        if (seq.length > column)
         {
-          c = seq[i];
-
-          if (c == '.' || c == ' ')
+          char c = seq[column];
+          residueCounts.add(c);
+          if (Comparison.isNucleotide(c))
           {
-            c = '-';
+            nucleotideCount++;
           }
-
-          if (c == '-')
-          {
-            values['-']++;
-            continue;
-          }
-          else if ('a' <= c && c <= 'z')
+          else if (!Comparison.isGap(c))
           {
-            c += TO_UPPER_CASE;
+            peptideCount++;
           }
-
-          nongap++;
-          values[c]++;
-
         }
         else
         {
-          values['-']++;
-        }
-      }
-      if (jSize == 1)
-      {
-        maxResidue = String.valueOf(c);
-        maxCount = 1;
-      }
-      else
-      {
-        for (v = 'A'; v <= 'Z'; v++)
-        {
-          // TODO why ignore values[v] == 1?
-          if (values[v] < 1 /* 2 */|| values[v] < maxCount)
-          {
-            continue;
-          }
-
-          if (values[v] > maxCount)
-          {
-            maxResidue = CHARS[v - 'A'];
-          }
-          else if (values[v] == maxCount)
-          {
-            maxResidue += CHARS[v - 'A'];
-          }
-          maxCount = values[v];
+          /*
+           * count a gap if the sequence doesn't reach this column
+           */
+          residueCounts.addGap();
         }
       }
-      if (maxResidue.length() == 0)
-      {
-        maxResidue = "-";
-      }
-      if (profile)
-      {
-        // TODO use a 1-dimensional array with jSize, nongap in [0] and [1]
-        residueHash.put(PROFILE, new int[][] { values,
-            new int[] { jSize, nongap } });
-      }
-      residueHash.put(MAXCOUNT, new Integer(maxCount));
-      residueHash.put(MAXRESIDUE, maxResidue);
 
-      percentage = ((float) maxCount * 100) / jSize;
-      residueHash.put(PID_GAPS, new Float(percentage));
+      int maxCount = residueCounts.getModalCount();
+      String maxResidue = residueCounts.getResiduesForCount(maxCount);
+      int gapCount = residueCounts.getGapCount();
+      ProfileI profile = new Profile(seqCount, gapCount, maxCount,
+              maxResidue);
 
-      if (nongap > 0)
+      if (saveFullProfile)
       {
-        // calculate for non-gapped too
-        percentage = ((float) maxCount * 100) / nongap;
+        profile.setCounts(residueCounts);
       }
-      residueHash.put(PID_NOGAPS, new Float(percentage));
 
-      result[i] = residueHash;
+      result[column] = profile;
     }
+    return new Profiles(result);
+    // long elapsed = System.currentTimeMillis() - now;
+    // System.out.println(elapsed);
   }
 
   /**
-   * Compute all or part of the annotation row from the given consensus
-   * hashtable
+   * Make an estimate of the profile size we are going to compute i.e. how many
+   * different characters may be present in it. Overestimating has a cost of
+   * using more memory than necessary. Underestimating has a cost of needing to
+   * extend the SparseIntArray holding the profile counts.
    * 
-   * @param consensus
-   *          - pre-allocated annotation row
-   * @param hconsensus
-   * @param iStart
-   * @param width
-   * @param ignoreGapsInConsensusCalculation
-   * @param includeAllConsSymbols
-   * @param nseq
+   * @param profileSizes
+   *          counts of sizes of profiles so far encountered
+   * @return
    */
-  public static void completeConsensus(AlignmentAnnotation consensus,
-          Hashtable[] hconsensus, int iStart, int width,
-          boolean ignoreGapsInConsensusCalculation,
-          boolean includeAllConsSymbols, long nseq)
+  static int estimateProfileSize(SparseIntArray profileSizes)
   {
-    completeConsensus(consensus, hconsensus, iStart, width,
-            ignoreGapsInConsensusCalculation, includeAllConsSymbols, null,
-            nseq);
+    if (profileSizes.size() == 0)
+    {
+      return 4;
+    }
+
+    /*
+     * could do a statistical heuristic here e.g. 75%ile
+     * for now just return the largest value
+     */
+    return profileSizes.keyAt(profileSizes.size() - 1);
   }
 
   /**
    * Derive the consensus annotations to be added to the alignment for display.
    * This does not recompute the raw data, but may be called on a change in
-   * display options, such as 'show logo', which may in turn result in a change
-   * in the derived values.
+   * display options, such as 'ignore gaps', which may in turn result in a
+   * change in the derived values.
    * 
    * @param consensus
    *          the annotation row to add annotations to
-   * @param hconsensus
+   * @param profiles
    *          the source consensus data
-   * @param iStart
-   *          start column
-   * @param width
-   *          end column
-   * @param ignoreGapsInConsensusCalculation
-   *          if true, use the consensus calculated ignoring gaps
-   * @param includeAllConsSymbols
+   * @param startCol
+   *          start column (inclusive)
+   * @param endCol
+   *          end column (exclusive)
+   * @param ignoreGaps
+   *          if true, normalise residue percentages ignoring gaps
+   * @param showSequenceLogo
    *          if true include all consensus symbols, else just show modal
    *          residue
-   * @param alphabet
    * @param nseq
    *          number of sequences
    */
   public static void completeConsensus(AlignmentAnnotation consensus,
-          Hashtable[] hconsensus, int iStart, int width,
-          boolean ignoreGapsInConsensusCalculation,
-          boolean includeAllConsSymbols, char[] alphabet, long nseq)
+          ProfilesI profiles, int startCol, int endCol, boolean ignoreGaps,
+          boolean showSequenceLogo, long nseq)
   {
+    // long now = System.currentTimeMillis();
     if (consensus == null || consensus.annotations == null
-            || consensus.annotations.length < width)
+            || consensus.annotations.length < endCol)
     {
-      // called with a bad alignment annotation row - wait for it to be
-      // initialised properly
+      /*
+       * called with a bad alignment annotation row 
+       * wait for it to be initialised properly
+       */
       return;
     }
 
-    final Format fmt = getPercentageFormat(nseq);
-
-    for (int i = iStart; i < width; i++)
+    for (int i = startCol; i < endCol; i++)
     {
-      Hashtable hci;
-      if (i >= hconsensus.length || ((hci = hconsensus[i]) == null))
-      {
-        // happens if sequences calculated over were shorter than alignment
-        // width
-        consensus.annotations[i] = null;
-        continue;
-      }
-      Float fv = (Float) hci
-              .get(ignoreGapsInConsensusCalculation ? PID_NOGAPS : PID_GAPS);
-      if (fv == null)
+      ProfileI profile = profiles.get(i);
+      if (profile == null)
       {
+        /*
+         * happens if sequences calculated over were 
+         * shorter than alignment width
+         */
         consensus.annotations[i] = null;
-        // data has changed below us .. give up and
-        continue;
+        return;
       }
-      float value = fv.floatValue();
-      String maxRes = hci.get(AAFrequency.MAXRESIDUE).toString();
-      StringBuilder mouseOver = new StringBuilder(64);
-      if (maxRes.length() > 1)
+
+      final int dp = getPercentageDp(nseq);
+
+      float value = profile.getPercentageIdentity(ignoreGaps);
+
+      String description = getTooltip(profile, value, showSequenceLogo,
+              ignoreGaps, dp);
+
+      String modalResidue = profile.getModalResidue();
+      if ("".equals(modalResidue))
       {
-        mouseOver.append("[").append(maxRes).append("] ");
-        maxRes = "+";
+        modalResidue = "-";
       }
-      else
+      else if (modalResidue.length() > 1)
       {
-        mouseOver.append(hci.get(AAFrequency.MAXRESIDUE) + " ");
+        modalResidue = "+";
       }
-      int[][] profile = (int[][]) hci.get(AAFrequency.PROFILE);
-      if (profile != null && includeAllConsSymbols)
-      {
-        int sequenceCount = profile[1][0];
-        int nonGappedCount = profile[1][1];
-        int normalisedBy = ignoreGapsInConsensusCalculation ? nonGappedCount
-                : sequenceCount;
-        mouseOver.setLength(0);
-        if (alphabet != null)
-        {
-          for (int c = 0; c < alphabet.length; c++)
-          {
-            float tval = profile[0][alphabet[c]] * 100f / normalisedBy;
-            mouseOver
-                    .append(((c == 0) ? "" : "; "))
-                    .append(alphabet[c])
-                    .append(" ")
-                    .append(((fmt != null) ? fmt.form(tval) : ((int) tval)))
-                    .append("%");
-          }
-        }
-        else
-        {
-          // TODO do this sort once only in calculate()?
-          // char[][] ca = new char[profile[0].length][];
-          char[] ca = new char[profile[0].length];
-          float[] vl = new float[profile[0].length];
-          for (int c = 0; c < ca.length; c++)
-          {
-            ca[c] = (char) c;
-            // ca[c] = new char[]
-            // { (char) c };
-            vl[c] = profile[0][c];
-          }
-          QuickSort.sort(vl, ca);
-          for (int p = 0, c = ca.length - 1; profile[0][ca[c]] > 0; c--)
-          {
-            final char residue = ca[c];
-            if (residue != '-')
-            {
-              float tval = profile[0][residue] * 100f / normalisedBy;
-              mouseOver
-                      .append((((p == 0) ? "" : "; ")))
-                      .append(residue)
-                      .append(" ")
-                      .append(((fmt != null) ? fmt.form(tval)
-                              : ((int) tval))).append("%");
-              p++;
-            }
-          }
-        }
-      }
-      else
-      {
-        mouseOver.append(
-                (((fmt != null) ? fmt.form(value) : ((int) value))))
-                .append("%");
-      }
-      consensus.annotations[i] = new Annotation(maxRes,
-              mouseOver.toString(), ' ', value);
+      consensus.annotations[i] = new Annotation(modalResidue, description,
+              ' ', value);
     }
+    // long elapsed = System.currentTimeMillis() - now;
+    // System.out.println(-elapsed);
   }
 
   /**
-   * Returns a Format designed to show all significant figures for profile
-   * percentages. For less than 100 sequences, returns null (the integer
-   * percentage value will be displayed). For 100-999 sequences, returns "%3.1f"
+   * Returns a tooltip showing either
+   * <ul>
+   * <li>the full profile (percentages of all residues present), if
+   * showSequenceLogo is true, or</li>
+   * <li>just the modal (most common) residue(s), if showSequenceLogo is false</li>
+   * </ul>
+   * Percentages are as a fraction of all sequence, or only ungapped sequences
+   * if ignoreGaps is true.
    * 
-   * @param nseq
+   * @param profile
+   * @param pid
+   * @param showSequenceLogo
+   * @param ignoreGaps
+   * @param dp
+   *          the number of decimal places to format percentages to
    * @return
    */
-  protected static Format getPercentageFormat(long nseq)
+  static String getTooltip(ProfileI profile, float pid,
+          boolean showSequenceLogo, boolean ignoreGaps, int dp)
   {
-    int scale = 0;
-    while (nseq >= 10)
+    ResidueCount counts = profile.getCounts();
+
+    String description = null;
+    if (counts != null && showSequenceLogo)
     {
-      scale++;
-      nseq /= 10;
+      int normaliseBy = ignoreGaps ? profile.getNonGapped() : profile
+              .getHeight();
+      description = counts.getTooltip(normaliseBy, dp);
+    }
+    else
+    {
+      StringBuilder sb = new StringBuilder(64);
+      String maxRes = profile.getModalResidue();
+      if (maxRes.length() > 1)
+      {
+        sb.append("[").append(maxRes).append("]");
+      }
+      else
+      {
+        sb.append(maxRes);
+      }
+      if (maxRes.length() > 0)
+      {
+        sb.append(" ");
+        Format.appendPercentage(sb, pid, dp);
+        sb.append("%");
+      }
+      description = sb.toString();
     }
-    return scale <= 1 ? null : new Format("%3." + (scale - 1) + "f");
+    return description;
   }
 
   /**
@@ -400,46 +350,46 @@ public class AAFrequency
    * in descending order of percentage value
    * </pre>
    * 
-   * @param hconsensus
-   *          the data table from which to extract and sort values
+   * @param profile
+   *          the data object from which to extract and sort values
    * @param ignoreGaps
    *          if true, only non-gapped values are included in percentage
    *          calculations
    * @return
    */
-  public static int[] extractProfile(Hashtable hconsensus,
+  public static int[] extractProfile(ProfileI profile,
           boolean ignoreGaps)
   {
     int[] rtnval = new int[64];
-    int[][] profile = (int[][]) hconsensus.get(AAFrequency.PROFILE);
-    if (profile == null)
+    ResidueCount counts = profile.getCounts();
+    if (counts == null)
     {
       return null;
     }
-    char[] ca = new char[profile[0].length];
-    float[] vl = new float[profile[0].length];
-    for (int c = 0; c < ca.length; c++)
-    {
-      ca[c] = (char) c;
-      vl[c] = profile[0][c];
-    }
-    QuickSort.sort(vl, ca);
+
+    SymbolCounts symbolCounts = counts.getSymbolCounts();
+    char[] symbols = symbolCounts.symbols;
+    int[] values = symbolCounts.values;
+    QuickSort.sort(values, symbols);
     int nextArrayPos = 2;
     int totalPercentage = 0;
-    int distinctValuesCount = 0;
-    final int divisor = profile[1][ignoreGaps ? 1 : 0];
-    for (int c = ca.length - 1; profile[0][ca[c]] > 0; c--)
+    final int divisor = ignoreGaps ? profile.getNonGapped() : profile
+            .getHeight();
+
+    /*
+     * traverse the arrays in reverse order (highest counts first)
+     */
+    for (int i = symbols.length - 1; i >= 0; i--)
     {
-      if (ca[c] != '-')
-      {
-        rtnval[nextArrayPos++] = ca[c];
-        final int percentage = (int) (profile[0][ca[c]] * 100f / divisor);
-        rtnval[nextArrayPos++] = percentage;
-        totalPercentage += percentage;
-        distinctValuesCount++;
-      }
+      int theChar = symbols[i];
+      int charCount = values[i];
+
+      rtnval[nextArrayPos++] = theChar;
+      final int percentage = (charCount * 100) / divisor;
+      rtnval[nextArrayPos++] = percentage;
+      totalPercentage += percentage;
     }
-    rtnval[0] = distinctValuesCount;
+    rtnval[0] = symbols.length;
     rtnval[1] = totalPercentage;
     int[] result = new int[rtnval.length + 1];
     result[0] = AlignmentAnnotation.SEQUENCE_PROFILE;
@@ -520,7 +470,7 @@ public class AAFrequency
           Hashtable[] hconsensus)
   {
     final char gapCharacter = alignment.getGapCharacter();
-    Set<AlignedCodonFrame> mappings = alignment.getCodonFrames();
+    List<AlignedCodonFrame> mappings = alignment.getCodonFrames();
     if (mappings == null || mappings.isEmpty())
     {
       return;
@@ -541,12 +491,16 @@ public class AAFrequency
         {
           continue;
         }
-        char[] codon = MappingUtils.findCodonFor(seq, col, mappings);
-        int codonEncoded = CodingUtils.encodeCodon(codon);
-        if (codonEncoded >= 0)
+        List<char[]> codons = MappingUtils
+                .findCodonsFor(seq, col, mappings);
+        for (char[] codon : codons)
         {
-          codonCounts[codonEncoded + 2]++;
-          ungappedCount++;
+          int codonEncoded = CodingUtils.encodeCodon(codon);
+          if (codonEncoded >= 0)
+          {
+            codonCounts[codonEncoded + 2]++;
+            ungappedCount++;
+          }
         }
       }
       codonCounts[1] = ungappedCount;
@@ -621,8 +575,11 @@ public class AAFrequency
       String modalCodon = String.valueOf(CodingUtils
               .decodeCodon(modalCodonEncoded));
       if (sortedCodonCounts.length > 1
-              && sortedCodonCounts[codons.length - 2] == modalCodonEncoded)
+              && sortedCodonCounts[codons.length - 2] == sortedCodonCounts[codons.length - 1])
       {
+        /*
+         * two or more codons share the modal count
+         */
         modalCodon = "+";
       }
       float pid = sortedCodonCounts[sortedCodonCounts.length - 1] * 100
@@ -641,7 +598,7 @@ public class AAFrequency
       StringBuilder samePercent = new StringBuilder();
       String percent = null;
       String lastPercent = null;
-      Format fmt = getPercentageFormat(nseqs);
+      int percentDecPl = getPercentageDp(nseqs);
 
       for (int j = codons.length - 1; j >= 0; j--)
       {
@@ -663,7 +620,9 @@ public class AAFrequency
         final int pct = codonCount * 100 / totalCount;
         String codon = String
                 .valueOf(CodingUtils.decodeCodon(codonEncoded));
-        percent = fmt == null ? Integer.toString(pct) : fmt.form(pct);
+        StringBuilder sb = new StringBuilder();
+        Format.appendPercentage(sb, pct, percentDecPl);
+        percent = sb.toString();
         if (showProfileLogo || codonCount == modalCodonCount)
         {
           if (percent.equals(lastPercent) && j > 0)
@@ -689,4 +648,23 @@ public class AAFrequency
               mouseOver.toString(), ' ', pid);
     }
   }
+
+  /**
+   * Returns the number of decimal places to show for profile percentages. For
+   * less than 100 sequences, returns zero (the integer percentage value will be
+   * displayed). For 100-999 sequences, returns 1, for 1000-9999 returns 2, etc.
+   * 
+   * @param nseq
+   * @return
+   */
+  protected static int getPercentageDp(long nseq)
+  {
+    int scale = 0;
+    while (nseq >= 100)
+    {
+      scale++;
+      nseq /= 10;
+    }
+    return scale;
+  }
 }
diff --git a/src/jalview/analysis/AlignSeq.java b/src/jalview/analysis/AlignSeq.java
index 241eeeb..eb2b351 100644
--- a/src/jalview/analysis/AlignSeq.java
+++ b/src/jalview/analysis/AlignSeq.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -577,7 +577,8 @@ public class AlignSeq
       }
     }
     int len = 72 - maxid - 1;
-    int nochunks = ((aseq1.length - count) / len) + 1;
+    int nochunks = ((aseq1.length - count) / len)
+            + ((aseq1.length - count) % len > 0 ? 1 : 0);
     pid = 0;
 
     output.append("Score = ").append(score[maxi][maxj]).append(NEWLINE);
@@ -619,7 +620,10 @@ public class AlignSeq
       {
         if ((i + (j * len)) < astr1.length())
         {
-          if (astr1.charAt(i + (j * len)) == astr2.charAt(i + (j * len))
+          boolean sameChar = Comparison.isSameResidue(
+                  astr1.charAt(i + (j * len)), astr2.charAt(i + (j * len)),
+                  false);
+          if (sameChar
                   && !jalview.util.Comparison.isGap(astr1.charAt(i
                           + (j * len))))
           {
@@ -662,9 +666,7 @@ public class AlignSeq
     }
 
     pid = pid / (aseq1.length - count) * 100;
-    output = output.append(new Format("Percentage ID = %2.2f\n\n")
-            .form(pid));
-
+    output = output.append(new Format("Percentage ID = %2.2f\n").form(pid));
     try
     {
       os.print(output.toString());
@@ -948,6 +950,7 @@ public class AlignSeq
   public static void displayMatrix(Graphics g, int[][] mat, int n, int m,
           int psize)
   {
+    // TODO method dosen't seem to be referenced anywhere delete??
     int max = -1000;
     int min = 1000;
 
diff --git a/src/jalview/analysis/AlignmentAnnotationUtils.java b/src/jalview/analysis/AlignmentAnnotationUtils.java
index ca618e3..54bbb0c 100644
--- a/src/jalview/analysis/AlignmentAnnotationUtils.java
+++ b/src/jalview/analysis/AlignmentAnnotationUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/analysis/AlignmentSorter.java b/src/jalview/analysis/AlignmentSorter.java
index ac861d8..a358f31 100644
--- a/src/jalview/analysis/AlignmentSorter.java
+++ b/src/jalview/analysis/AlignmentSorter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -32,6 +32,7 @@ import jalview.util.MessageManager;
 import jalview.util.QuickSort;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -718,13 +719,16 @@ public class AlignmentSorter
   public static void sortByFeature(String featureLabel, String groupLabel,
           int start, int stop, AlignmentI alignment, String method)
   {
-    sortByFeature(featureLabel == null ? null
-            : new String[] { featureLabel }, groupLabel == null ? null
-            : new String[] { groupLabel }, start, stop, alignment, method);
+    sortByFeature(
+            featureLabel == null ? null
+                    : Arrays.asList(new String[] { featureLabel }),
+            groupLabel == null ? null : Arrays
+                    .asList(new String[] { groupLabel }), start, stop,
+            alignment, method);
   }
 
   private static boolean containsIgnoreCase(final String lab,
-          final String[] labs)
+          final List<String> labs)
   {
     if (labs == null)
     {
@@ -734,9 +738,9 @@ public class AlignmentSorter
     {
       return false;
     }
-    for (int q = 0; q < labs.length; q++)
+    for (String label : labs)
     {
-      if (labs[q] != null && lab.equalsIgnoreCase(labs[q]))
+      if (lab.equalsIgnoreCase(label))
       {
         return true;
       }
@@ -744,9 +748,9 @@ public class AlignmentSorter
     return false;
   }
 
-  public static void sortByFeature(String[] featureLabels,
-          String[] groupLabels, int start, int stop, AlignmentI alignment,
-          String method)
+  public static void sortByFeature(List<String> featureLabels,
+          List<String> groupLabels, int start, int stop,
+          AlignmentI alignment, String method)
   {
     if (method != FEATURE_SCORE && method != FEATURE_LABEL
             && method != FEATURE_DENSITY)
@@ -755,20 +759,41 @@ public class AlignmentSorter
               MessageManager
                       .getString("error.implementation_error_sortbyfeature"));
     }
+
     boolean ignoreScore = method != FEATURE_SCORE;
     StringBuffer scoreLabel = new StringBuffer();
     scoreLabel.append(start + stop + method);
     // This doesn't quite work yet - we'd like to have a canonical ordering that
     // can be preserved from call to call
-    for (int i = 0; featureLabels != null && i < featureLabels.length; i++)
+    if (featureLabels != null)
     {
-      scoreLabel.append(featureLabels[i] == null ? "null"
-              : featureLabels[i]);
+      for (String label : featureLabels)
+      {
+        scoreLabel.append(label);
+      }
     }
-    for (int i = 0; groupLabels != null && i < groupLabels.length; i++)
+    if (groupLabels != null)
     {
-      scoreLabel.append(groupLabels[i] == null ? "null" : groupLabels[i]);
+      for (String label : groupLabels)
+      {
+        scoreLabel.append(label);
+      }
     }
+
+    /*
+     * if resorting the same feature, toggle sort order
+     */
+    if (lastSortByFeatureScore == null
+            || !scoreLabel.toString().equals(lastSortByFeatureScore))
+    {
+      sortByFeatureScoreAscending = true;
+    }
+    else
+    {
+      sortByFeatureScoreAscending = !sortByFeatureScoreAscending;
+    }
+    lastSortByFeatureScore = scoreLabel.toString();
+
     SequenceI[] seqs = alignment.getSequencesArray();
 
     boolean[] hasScore = new boolean[seqs.length]; // per sequence score
@@ -855,7 +880,7 @@ public class AlignmentSorter
             labs[l] = (fs[l].getDescription() != null ? fs[l]
                     .getDescription() : fs[l].getType());
           }
-          jalview.util.QuickSort.sort(labs, ((Object[]) feats[i]));
+          QuickSort.sort(labs, ((Object[]) feats[i]));
         }
       }
       if (hasScore[i])
@@ -898,32 +923,27 @@ public class AlignmentSorter
           }
           else
           {
-            int nf = (feats[i] == null) ? 0
-                    : ((SequenceFeature[]) feats[i]).length;
-            // System.err.println("Sorting on Score: seq "+seqs[i].getName()+
-            // " Feats: "+nf+" Score : "+scores[i]);
+            // int nf = (feats[i] == null) ? 0
+            // : ((SequenceFeature[]) feats[i]).length;
+            // // System.err.println("Sorting on Score: seq " +
+            // seqs[i].getName()
+            // + " Feats: " + nf + " Score : " + scores[i]);
           }
         }
       }
-
-      jalview.util.QuickSort.sort(scores, seqs);
+      QuickSort.sortByDouble(scores, seqs, sortByFeatureScoreAscending);
     }
     else if (method == FEATURE_DENSITY)
     {
-
-      // break ties between equivalent numbers for adjacent sequences by adding
-      // 1/Nseq*i on the original order
-      double fr = 0.9 / (1.0 * seqs.length);
       for (int i = 0; i < seqs.length; i++)
       {
-        double nf;
-        scores[i] = (0.05 + fr * i)
-                + (nf = ((feats[i] == null) ? 0.0
-                        : 1.0 * ((SequenceFeature[]) feats[i]).length));
+        int featureCount = feats[i] == null ? 0
+                : ((SequenceFeature[]) feats[i]).length;
+        scores[i] = featureCount;
         // System.err.println("Sorting on Density: seq "+seqs[i].getName()+
-        // " Feats: "+nf+" Score : "+scores[i]);
+        // " Feats: "+featureCount+" Score : "+scores[i]);
       }
-      jalview.util.QuickSort.sort(scores, seqs);
+      QuickSort.sortByDouble(scores, seqs, sortByFeatureScoreAscending);
     }
     else
     {
@@ -933,24 +953,8 @@ public class AlignmentSorter
                 MessageManager.getString("error.not_yet_implemented"));
       }
     }
-    if (lastSortByFeatureScore == null
-            || !scoreLabel.toString().equals(lastSortByFeatureScore))
-    {
-      sortByFeatureScoreAscending = true;
-    }
-    else
-    {
-      sortByFeatureScoreAscending = !sortByFeatureScoreAscending;
-    }
-    if (sortByFeatureScoreAscending)
-    {
-      setOrder(alignment, seqs);
-    }
-    else
-    {
-      setReverseOrder(alignment, seqs);
-    }
-    lastSortByFeatureScore = scoreLabel.toString();
+
+    setOrder(alignment, seqs);
   }
 
 }
diff --git a/src/jalview/analysis/AlignmentUtils.java b/src/jalview/analysis/AlignmentUtils.java
index 926317a..ef335b9 100644
--- a/src/jalview/analysis/AlignmentUtils.java
+++ b/src/jalview/analysis/AlignmentUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,35 +20,45 @@
  */
 package jalview.analysis;
 
+import static jalview.io.gff.GffConstants.CLINICAL_SIGNIFICANCE;
+
 import jalview.datamodel.AlignedCodon;
 import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
-import jalview.datamodel.FeatureProperties;
+import jalview.datamodel.IncompleteCodonException;
 import jalview.datamodel.Mapping;
-import jalview.datamodel.SearchResults;
 import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.io.gff.SequenceOntologyFactory;
+import jalview.io.gff.SequenceOntologyI;
 import jalview.schemes.ResidueProperties;
+import jalview.util.Comparison;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
 import jalview.util.MappingUtils;
+import jalview.util.StringUtils;
 
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.TreeMap;
 
@@ -62,6 +72,40 @@ import java.util.TreeMap;
 public class AlignmentUtils
 {
 
+  private static final int CODON_LENGTH = 3;
+
+  private static final String SEQUENCE_VARIANT = "sequence_variant:";
+
+  private static final String ID = "ID";
+
+  /**
+   * A data model to hold the 'normal' base value at a position, and an optional
+   * sequence variant feature
+   */
+  static final class DnaVariant
+  {
+    final String base;
+
+    SequenceFeature variant;
+
+    DnaVariant(String nuc)
+    {
+      base = nuc;
+      variant = null;
+    }
+
+    DnaVariant(String nuc, SequenceFeature var)
+    {
+      base = nuc;
+      variant = var;
+    }
+
+    public String getSource()
+    {
+      return variant == null ? null : variant.getFeatureGroup();
+    }
+  }
+
   /**
    * given an existing alignment, create a new alignment including all, or up to
    * flankSize additional symbols from each sequence's dataset sequence
@@ -228,8 +272,8 @@ public class AlignmentUtils
    * @param cdnaAlignment
    * @return
    */
-  public static boolean mapProteinToCdna(final AlignmentI proteinAlignment,
-          final AlignmentI cdnaAlignment)
+  public static boolean mapProteinAlignmentToCdna(
+          final AlignmentI proteinAlignment, final AlignmentI cdnaAlignment)
   {
     if (proteinAlignment == null || cdnaAlignment == null)
     {
@@ -275,7 +319,7 @@ public class AlignmentUtils
           final AlignmentI cdnaAlignment, Set<SequenceI> mappedDna,
           Set<SequenceI> mappedProtein, boolean xrefsOnly)
   {
-    boolean mappingPerformed = false;
+    boolean mappingExistsOrAdded = false;
     List<SequenceI> thisSeqs = proteinAlignment.getSequences();
     for (SequenceI aaSeq : thisSeqs)
     {
@@ -308,14 +352,18 @@ public class AlignmentUtils
         {
           continue;
         }
-        if (!mappingExists(proteinAlignment.getCodonFrames(),
+        if (mappingExists(proteinAlignment.getCodonFrames(),
                 aaSeq.getDatasetSequence(), cdnaSeq.getDatasetSequence()))
         {
-          MapList map = mapProteinToCdna(aaSeq, cdnaSeq);
+          mappingExistsOrAdded = true;
+        }
+        else
+        {
+          MapList map = mapCdnaToProtein(aaSeq, cdnaSeq);
           if (map != null)
           {
             acf.addMap(cdnaSeq, aaSeq, map);
-            mappingPerformed = true;
+            mappingExistsOrAdded = true;
             proteinMapped = true;
             mappedDna.add(cdnaSeq);
             mappedProtein.add(aaSeq);
@@ -327,19 +375,19 @@ public class AlignmentUtils
         proteinAlignment.addCodonFrame(acf);
       }
     }
-    return mappingPerformed;
+    return mappingExistsOrAdded;
   }
 
   /**
    * Answers true if the mappings include one between the given (dataset)
    * sequences.
    */
-  public static boolean mappingExists(Set<AlignedCodonFrame> set,
+  public static boolean mappingExists(List<AlignedCodonFrame> mappings,
           SequenceI aaSeq, SequenceI cdnaSeq)
   {
-    if (set != null)
+    if (mappings != null)
     {
-      for (AlignedCodonFrame acf : set)
+      for (AlignedCodonFrame acf : mappings)
       {
         if (cdnaSeq == acf.getDnaForAaSeq(aaSeq))
         {
@@ -351,16 +399,22 @@ public class AlignmentUtils
   }
 
   /**
-   * Build a mapping (if possible) of a protein to a cDNA sequence. The cDNA
-   * must be three times the length of the protein, possibly after ignoring
-   * start and/or stop codons, and must translate to the protein. Returns null
-   * if no mapping is determined.
+   * Builds a mapping (if possible) of a cDNA to a protein sequence.
+   * <ul>
+   * <li>first checks if the cdna translates exactly to the protein sequence</li>
+   * <li>else checks for translation after removing a STOP codon</li>
+   * <li>else checks for translation after removing a START codon</li>
+   * <li>if that fails, inspect CDS features on the cDNA sequence</li>
+   * </ul>
+   * Returns null if no mapping is determined.
    * 
-   * @param proteinSeqs
+   * @param proteinSeq
+   *          the aligned protein sequence
    * @param cdnaSeq
+   *          the aligned cdna sequence
    * @return
    */
-  public static MapList mapProteinToCdna(SequenceI proteinSeq,
+  public static MapList mapCdnaToProtein(SequenceI proteinSeq,
           SequenceI cdnaSeq)
   {
     /*
@@ -382,26 +436,26 @@ public class AlignmentUtils
     /*
      * cdnaStart/End, proteinStartEnd are base 1 (for dataset sequence mapping)
      */
-    final int mappedLength = 3 * aaSeqChars.length;
+    final int mappedLength = CODON_LENGTH * aaSeqChars.length;
     int cdnaLength = cdnaSeqChars.length;
-    int cdnaStart = 1;
-    int cdnaEnd = cdnaLength;
-    final int proteinStart = 1;
-    final int proteinEnd = aaSeqChars.length;
+    int cdnaStart = cdnaSeq.getStart();
+    int cdnaEnd = cdnaSeq.getEnd();
+    final int proteinStart = proteinSeq.getStart();
+    final int proteinEnd = proteinSeq.getEnd();
 
     /*
-     * If lengths don't match, try ignoring stop codon.
+     * If lengths don't match, try ignoring stop codon (if present)
      */
     if (cdnaLength != mappedLength && cdnaLength > 2)
     {
-      String lastCodon = String.valueOf(cdnaSeqChars, cdnaLength - 3, 3)
-              .toUpperCase();
+      String lastCodon = String.valueOf(cdnaSeqChars,
+              cdnaLength - CODON_LENGTH, CODON_LENGTH).toUpperCase();
       for (String stop : ResidueProperties.STOP)
       {
         if (lastCodon.equals(stop))
         {
-          cdnaEnd -= 3;
-          cdnaLength -= 3;
+          cdnaEnd -= CODON_LENGTH;
+          cdnaLength -= CODON_LENGTH;
           break;
         }
       }
@@ -410,26 +464,31 @@ public class AlignmentUtils
     /*
      * If lengths still don't match, try ignoring start codon.
      */
+    int startOffset = 0;
     if (cdnaLength != mappedLength
             && cdnaLength > 2
-            && String.valueOf(cdnaSeqChars, 0, 3).toUpperCase()
+            && String.valueOf(cdnaSeqChars, 0, CODON_LENGTH).toUpperCase()
                     .equals(ResidueProperties.START))
     {
-      cdnaStart += 3;
-      cdnaLength -= 3;
+      startOffset += CODON_LENGTH;
+      cdnaStart += CODON_LENGTH;
+      cdnaLength -= CODON_LENGTH;
     }
 
-    if (cdnaLength != mappedLength)
-    {
-      return null;
-    }
-    if (!translatesAs(cdnaSeqChars, cdnaStart - 1, aaSeqChars))
+    if (translatesAs(cdnaSeqChars, startOffset, aaSeqChars))
     {
-      return null;
+      /*
+       * protein is translation of dna (+/- start/stop codons)
+       */
+      MapList map = new MapList(new int[] { cdnaStart, cdnaEnd }, new int[]
+      { proteinStart, proteinEnd }, CODON_LENGTH, 1);
+      return map;
     }
-    MapList map = new MapList(new int[] { cdnaStart, cdnaEnd }, new int[] {
-        proteinStart, proteinEnd }, 3, 1);
-    return map;
+
+    /*
+     * translation failed - try mapping CDS annotated regions of dna
+     */
+    return mapCdsToProtein(cdnaSeq, proteinSeq);
   }
 
   /**
@@ -450,16 +509,17 @@ public class AlignmentUtils
       return false;
     }
 
-    int aaResidue = 0;
-    for (int i = cdnaStart; i < cdnaSeqChars.length - 2
-            && aaResidue < aaSeqChars.length; i += 3, aaResidue++)
+    int aaPos = 0;
+    int dnaPos = cdnaStart;
+    for (; dnaPos < cdnaSeqChars.length - 2 && aaPos < aaSeqChars.length; dnaPos += CODON_LENGTH, aaPos++)
     {
-      String codon = String.valueOf(cdnaSeqChars, i, 3);
+      String codon = String.valueOf(cdnaSeqChars, dnaPos, CODON_LENGTH);
       final String translated = ResidueProperties.codonTranslate(codon);
+
       /*
        * allow * in protein to match untranslatable in dna
        */
-      final char aaRes = aaSeqChars[aaResidue];
+      final char aaRes = aaSeqChars[aaPos];
       if ((translated == null || "STOP".equals(translated)) && aaRes == '*')
       {
         continue;
@@ -472,8 +532,32 @@ public class AlignmentUtils
         return false;
       }
     }
-    // fail if we didn't match all of the aa sequence
-    return (aaResidue == aaSeqChars.length);
+
+    /*
+     * check we matched all of the protein sequence
+     */
+    if (aaPos != aaSeqChars.length)
+    {
+      return false;
+    }
+
+    /*
+     * check we matched all of the dna except
+     * for optional trailing STOP codon
+     */
+    if (dnaPos == cdnaSeqChars.length)
+    {
+      return true;
+    }
+    if (dnaPos == cdnaSeqChars.length - CODON_LENGTH)
+    {
+      String codon = String.valueOf(cdnaSeqChars, dnaPos, CODON_LENGTH);
+      if ("STOP".equals(ResidueProperties.codonTranslate(codon)))
+      {
+        return true;
+      }
+    }
+    return false;
   }
 
   /**
@@ -508,14 +592,14 @@ public class AlignmentUtils
 
     /*
      * Locate the aligned source sequence whose dataset sequence is mapped. We
-     * just take the first match here (as we can't align cDNA like more than one
-     * protein sequence).
+     * just take the first match here (as we can't align like more than one
+     * sequence).
      */
     SequenceI alignFrom = null;
     AlignedCodonFrame mapping = null;
     for (AlignedCodonFrame mp : mappings)
     {
-      alignFrom = mp.findAlignedSequence(seq.getDatasetSequence(), al);
+      alignFrom = mp.findAlignedSequence(seq, al);
       if (alignFrom != null)
       {
         mapping = mp;
@@ -535,8 +619,8 @@ public class AlignmentUtils
   /**
    * Align sequence 'alignTo' the same way as 'alignFrom', using the mapping to
    * match residues and codons. Flags control whether existing gaps in unmapped
-   * (intron) and mapped (exon) regions are preserved or not. Gaps linking intro
-   * and exon are only retained if both flags are set.
+   * (intron) and mapped (exon) regions are preserved or not. Gaps between
+   * intron and exon are only retained if both flags are set.
    * 
    * @param alignTo
    * @param alignFrom
@@ -552,9 +636,6 @@ public class AlignmentUtils
           boolean preserveUnmappedGaps)
   {
     // TODO generalise to work for Protein-Protein, dna-dna, dna-protein
-    final char[] thisSeq = alignTo.getSequence();
-    final char[] thatAligned = alignFrom.getSequence();
-    StringBuilder thisAligned = new StringBuilder(2 * thisSeq.length);
 
     // aligned and dataset sequence positions, all base zero
     int thisSeqPos = 0;
@@ -564,11 +645,17 @@ public class AlignmentUtils
     char myGapChar = myGap.charAt(0);
     int ratio = myGap.length();
 
-    /*
-     * Traverse the aligned protein sequence.
-     */
+    int fromOffset = alignFrom.getStart() - 1;
+    int toOffset = alignTo.getStart() - 1;
     int sourceGapMappedLength = 0;
     boolean inExon = false;
+    final char[] thisSeq = alignTo.getSequence();
+    final char[] thatAligned = alignFrom.getSequence();
+    StringBuilder thisAligned = new StringBuilder(2 * thisSeq.length);
+
+    /*
+     * Traverse the 'model' aligned sequence
+     */
     for (char sourceChar : thatAligned)
     {
       if (sourceChar == sourceGap)
@@ -578,20 +665,22 @@ public class AlignmentUtils
       }
 
       /*
-       * Found a residue. Locate its mapped codon (start) position.
+       * Found a non-gap character. Locate its mapped region if any.
        */
       sourceDsPos++;
       // Note mapping positions are base 1, our sequence positions base 0
       int[] mappedPos = mapping.getMappedRegion(alignTo, alignFrom,
-              sourceDsPos);
+              sourceDsPos + fromOffset);
       if (mappedPos == null)
       {
         /*
-         * Abort realignment if unmapped protein. Or could ignore it??
+         * unmapped position; treat like a gap
          */
-        System.err.println("Can't align: no codon mapping to residue "
-                + sourceDsPos + "(" + sourceChar + ")");
-        return;
+        sourceGapMappedLength += ratio;
+        // System.err.println("Can't align: no codon mapping to residue "
+        // + sourceDsPos + "(" + sourceChar + ")");
+        // return;
+        continue;
       }
 
       int mappedCodonStart = mappedPos[0]; // position (1...) of codon start
@@ -607,14 +696,15 @@ public class AlignmentUtils
        * But then 'align dna as protein' doesn't make much sense otherwise.
        */
       int intronLength = 0;
-      while (basesWritten < mappedCodonEnd && thisSeqPos < thisSeq.length)
+      while (basesWritten + toOffset < mappedCodonEnd
+              && thisSeqPos < thisSeq.length)
       {
         final char c = thisSeq[thisSeqPos++];
         if (c != myGapChar)
         {
           basesWritten++;
-
-          if (basesWritten < mappedCodonStart)
+          int sourcePosition = basesWritten + toOffset;
+          if (sourcePosition < mappedCodonStart)
           {
             /*
              * Found an unmapped (intron) base. First add in any preceding gaps
@@ -631,7 +721,7 @@ public class AlignmentUtils
           }
           else
           {
-            final boolean startOfCodon = basesWritten == mappedCodonStart;
+            final boolean startOfCodon = sourcePosition == mappedCodonStart;
             int gapsToAdd = calculateGapsToInsert(preserveMappedGaps,
                     preserveUnmappedGaps, sourceGapMappedLength, inExon,
                     trailingCopiedGap.length(), intronLength, startOfCodon);
@@ -660,8 +750,8 @@ public class AlignmentUtils
     }
 
     /*
-     * At end of protein sequence. Copy any remaining dna sequence, optionally
-     * including (intron) gaps. We do not copy trailing gaps in protein.
+     * At end of model aligned sequence. Copy any remaining target sequence, optionally
+     * including (intron) gaps.
      */
     while (thisSeqPos < thisSeq.length)
     {
@@ -670,6 +760,20 @@ public class AlignmentUtils
       {
         thisAligned.append(c);
       }
+      sourceGapMappedLength--;
+    }
+
+    /*
+     * finally add gaps to pad for any trailing source gaps or
+     * unmapped characters
+     */
+    if (preserveUnmappedGaps)
+    {
+      while (sourceGapMappedLength > 0)
+      {
+        thisAligned.append(myGapChar);
+        sourceGapMappedLength--;
+      }
     }
 
     /*
@@ -744,187 +848,341 @@ public class AlignmentUtils
   }
 
   /**
-   * Returns a list of sequences mapped from the given sequences and aligned
-   * (gapped) in the same way. For example, the cDNA for aligned protein, where
-   * a single gap in protein generates three gaps in cDNA.
+   * Realigns the given protein to match the alignment of the dna, using codon
+   * mappings to translate aligned codon positions to protein residues.
    * 
-   * @param sequences
-   * @param gapCharacter
-   * @param mappings
-   * @return
+   * @param protein
+   *          the alignment whose sequences are realigned by this method
+   * @param dna
+   *          the dna alignment whose alignment we are 'copying'
+   * @return the number of sequences that were realigned
    */
-  public static List<SequenceI> getAlignedTranslation(
-          List<SequenceI> sequences, char gapCharacter,
-          Set<AlignedCodonFrame> mappings)
+  public static int alignProteinAsDna(AlignmentI protein, AlignmentI dna)
   {
-    List<SequenceI> alignedSeqs = new ArrayList<SequenceI>();
-
-    for (SequenceI seq : sequences)
+    if (protein.isNucleotide() || !dna.isNucleotide())
     {
-      List<SequenceI> mapped = getAlignedTranslation(seq, gapCharacter,
-              mappings);
-      alignedSeqs.addAll(mapped);
+      System.err.println("Wrong alignment type in alignProteinAsDna");
+      return 0;
     }
-    return alignedSeqs;
+    List<SequenceI> unmappedProtein = new ArrayList<SequenceI>();
+    Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons = buildCodonColumnsMap(
+            protein, dna, unmappedProtein);
+    return alignProteinAs(protein, alignedCodons, unmappedProtein);
   }
 
   /**
-   * Returns sequences aligned 'like' the source sequence, as mapped by the
-   * given mappings. Normally we expect zero or one 'mapped' sequences, but this
-   * will support 1-to-many as well.
+   * Realigns the given dna to match the alignment of the protein, using codon
+   * mappings to translate aligned peptide positions to codons.
    * 
-   * @param seq
-   * @param gapCharacter
-   * @param mappings
-   * @return
+   * Always produces a padded CDS alignment.
+   * 
+   * @param dna
+   *          the alignment whose sequences are realigned by this method
+   * @param protein
+   *          the protein alignment whose alignment we are 'copying'
+   * @return the number of sequences that were realigned
    */
-  protected static List<SequenceI> getAlignedTranslation(SequenceI seq,
-          char gapCharacter, Set<AlignedCodonFrame> mappings)
+  public static int alignCdsAsProtein(AlignmentI dna, AlignmentI protein)
   {
-    List<SequenceI> result = new ArrayList<SequenceI>();
-    for (AlignedCodonFrame mapping : mappings)
+    if (protein.isNucleotide() || !dna.isNucleotide())
+    {
+      System.err.println("Wrong alignment type in alignProteinAsDna");
+      return 0;
+    }
+    // todo: implement this
+    List<AlignedCodonFrame> mappings = protein.getCodonFrames();
+    int alignedCount = 0;
+    int width = 0; // alignment width for padding CDS
+    for (SequenceI dnaSeq : dna.getSequences())
     {
-      if (mapping.involvesSequence(seq))
+      if (alignCdsSequenceAsProtein(dnaSeq, protein, mappings,
+              dna.getGapCharacter()))
       {
-        SequenceI mapped = getAlignedTranslation(seq, gapCharacter, mapping);
-        if (mapped != null)
-        {
-          result.add(mapped);
-        }
+        alignedCount++;
       }
+      width = Math.max(dnaSeq.getLength(), width);
     }
-    return result;
+    int oldwidth;
+    int diff;
+    for (SequenceI dnaSeq : dna.getSequences())
+    {
+      oldwidth = dnaSeq.getLength();
+      diff = width - oldwidth;
+      if (diff > 0)
+      {
+        dnaSeq.insertCharAt(oldwidth, diff, dna.getGapCharacter());
+      }
+    }
+    return alignedCount;
   }
 
   /**
-   * Returns the translation of 'seq' (as held in the mapping) with
-   * corresponding alignment (gaps).
+   * Helper method to align (if possible) the dna sequence to match the
+   * alignment of a mapped protein sequence. This is currently limited to
+   * handling coding sequence only.
    * 
-   * @param seq
-   * @param gapCharacter
-   * @param mapping
+   * @param cdsSeq
+   * @param protein
+   * @param mappings
+   * @param gapChar
    * @return
    */
-  protected static SequenceI getAlignedTranslation(SequenceI seq,
-          char gapCharacter, AlignedCodonFrame mapping)
+  static boolean alignCdsSequenceAsProtein(SequenceI cdsSeq,
+          AlignmentI protein, List<AlignedCodonFrame> mappings, char gapChar)
   {
-    String gap = String.valueOf(gapCharacter);
-    boolean toDna = false;
-    int fromRatio = 1;
-    SequenceI mapTo = mapping.getDnaForAaSeq(seq);
-    if (mapTo != null)
-    {
-      // mapping is from protein to nucleotide
-      toDna = true;
-      // should ideally get gap count ratio from mapping
-      gap = String.valueOf(new char[] { gapCharacter, gapCharacter,
-          gapCharacter });
-    }
-    else
+    SequenceI cdsDss = cdsSeq.getDatasetSequence();
+    if (cdsDss == null)
     {
-      // mapping is from nucleotide to protein
-      mapTo = mapping.getAaForDnaSeq(seq);
-      fromRatio = 3;
+      System.err
+              .println("alignCdsSequenceAsProtein needs aligned sequence!");
+      return false;
     }
-    StringBuilder newseq = new StringBuilder(seq.getLength()
-            * (toDna ? 3 : 1));
 
-    int residueNo = 0; // in seq, base 1
-    int[] phrase = new int[fromRatio];
-    int phraseOffset = 0;
-    int gapWidth = 0;
-    boolean first = true;
-    final Sequence alignedSeq = new Sequence("", "");
-
-    for (char c : seq.getSequence())
+    List<AlignedCodonFrame> dnaMappings = MappingUtils
+            .findMappingsForSequence(cdsSeq, mappings);
+    for (AlignedCodonFrame mapping : dnaMappings)
     {
-      if (c == gapCharacter)
-      {
-        gapWidth++;
-        if (gapWidth >= fromRatio)
-        {
-          newseq.append(gap);
-          gapWidth = 0;
-        }
-      }
-      else
+      SequenceI peptide = mapping.findAlignedSequence(cdsSeq, protein);
+      if (peptide != null)
       {
-        phrase[phraseOffset++] = residueNo + 1;
-        if (phraseOffset == fromRatio)
+        int peptideLength = peptide.getLength();
+        Mapping map = mapping.getMappingBetween(cdsSeq, peptide);
+        if (map != null)
         {
+          MapList mapList = map.getMap();
+          if (map.getTo() == peptide.getDatasetSequence())
+          {
+            mapList = mapList.getInverse();
+          }
+          int cdsLength = cdsDss.getLength();
+          int mappedFromLength = MappingUtils.getLength(mapList
+                  .getFromRanges());
+          int mappedToLength = MappingUtils
+                  .getLength(mapList.getToRanges());
+          boolean addStopCodon = (cdsLength == mappedFromLength
+                  * CODON_LENGTH + CODON_LENGTH)
+                  || (peptide.getDatasetSequence().getLength() == mappedFromLength - 1);
+          if (cdsLength != mappedToLength && !addStopCodon)
+          {
+            System.err
+                    .println(String
+                            .format("Can't align cds as protein (length mismatch %d/%d): %s",
+                                    cdsLength, mappedToLength,
+                                    cdsSeq.getName()));
+          }
+
+          /*
+           * pre-fill the aligned cds sequence with gaps
+           */
+          char[] alignedCds = new char[peptideLength * CODON_LENGTH
+                  + (addStopCodon ? CODON_LENGTH : 0)];
+          Arrays.fill(alignedCds, gapChar);
+
           /*
-           * Have read a whole codon (or protein residue), now translate: map
-           * source phrase to positions in target sequence add characters at
-           * these positions to newseq Note mapping positions are base 1, our
-           * sequence positions base 0.
+           * walk over the aligned peptide sequence and insert mapped 
+           * codons for residues in the aligned cds sequence 
            */
-          SearchResults sr = new SearchResults();
-          for (int pos : phrase)
+          char[] alignedPeptide = peptide.getSequence();
+          char[] nucleotides = cdsDss.getSequence();
+          int copiedBases = 0;
+          int cdsStart = cdsDss.getStart();
+          int proteinPos = peptide.getStart() - 1;
+          int cdsCol = 0;
+          for (char residue : alignedPeptide)
           {
-            mapping.markMappedRegion(seq, pos, sr);
+            if (Comparison.isGap(residue))
+            {
+              cdsCol += CODON_LENGTH;
+            }
+            else
+            {
+              proteinPos++;
+              int[] codon = mapList.locateInTo(proteinPos, proteinPos);
+              if (codon == null)
+              {
+                // e.g. incomplete start codon, X in peptide
+                cdsCol += CODON_LENGTH;
+              }
+              else
+              {
+                for (int j = codon[0]; j <= codon[1]; j++)
+                {
+                  char mappedBase = nucleotides[j - cdsStart];
+                  alignedCds[cdsCol++] = mappedBase;
+                  copiedBases++;
+                }
+              }
+            }
           }
-          newseq.append(sr.getCharacters());
-          if (first)
+
+          /*
+           * append stop codon if not mapped from protein,
+           * closing it up to the end of the mapped sequence
+           */
+          if (copiedBases == nucleotides.length - CODON_LENGTH)
           {
-            first = false;
-            // Hack: Copy sequence dataset, name and description from
-            // SearchResults.match[0].sequence
-            // TODO? carry over sequence names from original 'complement'
-            // alignment
-            SequenceI mappedTo = sr.getResultSequence(0);
-            alignedSeq.setName(mappedTo.getName());
-            alignedSeq.setDescription(mappedTo.getDescription());
-            alignedSeq.setDatasetSequence(mappedTo);
+            for (int i = alignedCds.length - 1; i >= 0; i--)
+            {
+              if (!Comparison.isGap(alignedCds[i]))
+              {
+                cdsCol = i + 1; // gap just after end of sequence
+                break;
+              }
+            }
+            for (int i = nucleotides.length - CODON_LENGTH; i < nucleotides.length; i++)
+            {
+              alignedCds[cdsCol++] = nucleotides[i];
+            }
           }
-          phraseOffset = 0;
+          cdsSeq.setSequence(new String(alignedCds));
+          return true;
         }
-        residueNo++;
       }
     }
-    alignedSeq.setSequence(newseq.toString());
-    return alignedSeq;
+    return false;
   }
 
   /**
-   * Realigns the given protein to match the alignment of the dna, using codon
-   * mappings to translate aligned codon positions to protein residues.
+   * Builds a map whose key is an aligned codon position (3 alignment column
+   * numbers base 0), and whose value is a map from protein sequence to each
+   * protein's peptide residue for that codon. The map generates an ordering of
+   * the codons, and allows us to read off the peptides at each position in
+   * order to assemble 'aligned' protein sequences.
    * 
    * @param protein
-   *          the alignment whose sequences are realigned by this method
+   *          the protein alignment
    * @param dna
-   *          the dna alignment whose alignment we are 'copying'
-   * @return the number of sequences that were realigned
+   *          the coding dna alignment
+   * @param unmappedProtein
+   *          any unmapped proteins are added to this list
+   * @return
    */
-  public static int alignProteinAsDna(AlignmentI protein, AlignmentI dna)
+  protected static Map<AlignedCodon, Map<SequenceI, AlignedCodon>> buildCodonColumnsMap(
+          AlignmentI protein, AlignmentI dna,
+          List<SequenceI> unmappedProtein)
   {
-    List<SequenceI> unmappedProtein = new ArrayList<SequenceI>();
+    /*
+     * maintain a list of any proteins with no mappings - these will be
+     * rendered 'as is' in the protein alignment as we can't align them
+     */
     unmappedProtein.addAll(protein.getSequences());
 
-    Set<AlignedCodonFrame> mappings = protein.getCodonFrames();
+    List<AlignedCodonFrame> mappings = protein.getCodonFrames();
 
     /*
      * Map will hold, for each aligned codon position e.g. [3, 5, 6], a map of
      * {dnaSequence, {proteinSequence, codonProduct}} at that position. The
      * comparator keeps the codon positions ordered.
      */
-    Map<AlignedCodon, Map<SequenceI, String>> alignedCodons = new TreeMap<AlignedCodon, Map<SequenceI, String>>(
+    Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons = new TreeMap<AlignedCodon, Map<SequenceI, AlignedCodon>>(
             new CodonComparator());
+
     for (SequenceI dnaSeq : dna.getSequences())
     {
       for (AlignedCodonFrame mapping : mappings)
       {
-        Mapping seqMap = mapping.getMappingForSequence(dnaSeq);
-        SequenceI prot = mapping.findAlignedSequence(
-                dnaSeq.getDatasetSequence(), protein);
+        SequenceI prot = mapping.findAlignedSequence(dnaSeq, protein);
         if (prot != null)
         {
+          Mapping seqMap = mapping.getMappingForSequence(dnaSeq);
           addCodonPositions(dnaSeq, prot, protein.getGapCharacter(),
                   seqMap, alignedCodons);
           unmappedProtein.remove(prot);
         }
       }
     }
-    return alignProteinAs(protein, alignedCodons, unmappedProtein);
+
+    /*
+     * Finally add any unmapped peptide start residues (e.g. for incomplete
+     * codons) as if at the codon position before the second residue
+     */
+    // TODO resolve JAL-2022 so this fudge can be removed
+    int mappedSequenceCount = protein.getHeight() - unmappedProtein.size();
+    addUnmappedPeptideStarts(alignedCodons, mappedSequenceCount);
+
+    return alignedCodons;
+  }
+
+  /**
+   * Scans for any protein mapped from position 2 (meaning unmapped start
+   * position e.g. an incomplete codon), and synthesizes a 'codon' for it at the
+   * preceding position in the alignment
+   * 
+   * @param alignedCodons
+   *          the codon-to-peptide map
+   * @param mappedSequenceCount
+   *          the number of distinct sequences in the map
+   */
+  protected static void addUnmappedPeptideStarts(
+          Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons,
+          int mappedSequenceCount)
+  {
+    // TODO delete this ugly hack once JAL-2022 is resolved
+    // i.e. we can model startPhase > 0 (incomplete start codon)
+
+    List<SequenceI> sequencesChecked = new ArrayList<SequenceI>();
+    AlignedCodon lastCodon = null;
+    Map<SequenceI, AlignedCodon> toAdd = new HashMap<SequenceI, AlignedCodon>();
+
+    for (Entry<AlignedCodon, Map<SequenceI, AlignedCodon>> entry : alignedCodons
+            .entrySet())
+    {
+      for (Entry<SequenceI, AlignedCodon> sequenceCodon : entry.getValue()
+              .entrySet())
+      {
+        SequenceI seq = sequenceCodon.getKey();
+        if (sequencesChecked.contains(seq))
+        {
+          continue;
+        }
+        sequencesChecked.add(seq);
+        AlignedCodon codon = sequenceCodon.getValue();
+        if (codon.peptideCol > 1)
+        {
+          System.err
+                  .println("Problem mapping protein with >1 unmapped start positions: "
+                          + seq.getName());
+        }
+        else if (codon.peptideCol == 1)
+        {
+          /*
+           * first position (peptideCol == 0) was unmapped - add it
+           */
+          if (lastCodon != null)
+          {
+            AlignedCodon firstPeptide = new AlignedCodon(lastCodon.pos1,
+                    lastCodon.pos2, lastCodon.pos3, String.valueOf(seq
+                            .getCharAt(0)), 0);
+            toAdd.put(seq, firstPeptide);
+          }
+          else
+          {
+            /*
+             * unmapped residue at start of alignment (no prior column) -
+             * 'insert' at nominal codon [0, 0, 0]
+             */
+            AlignedCodon firstPeptide = new AlignedCodon(0, 0, 0,
+                    String.valueOf(seq.getCharAt(0)), 0);
+            toAdd.put(seq, firstPeptide);
+          }
+        }
+        if (sequencesChecked.size() == mappedSequenceCount)
+        {
+          // no need to check past first mapped position in all sequences
+          break;
+        }
+      }
+      lastCodon = entry.getKey();
+    }
+
+    /*
+     * add any new codons safely after iterating over the map
+     */
+    for (Entry<SequenceI, AlignedCodon> startCodon : toAdd.entrySet())
+    {
+      addCodonToMap(alignedCodons, startCodon.getValue(),
+              startCodon.getKey());
+    }
   }
 
   /**
@@ -939,7 +1197,7 @@ public class AlignmentUtils
    * @return
    */
   protected static int alignProteinAs(AlignmentI protein,
-          Map<AlignedCodon, Map<SequenceI, String>> alignedCodons,
+          Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons,
           List<SequenceI> unmappedProtein)
   {
     /*
@@ -961,12 +1219,13 @@ public class AlignmentUtils
     int column = 0;
     for (AlignedCodon codon : alignedCodons.keySet())
     {
-      final Map<SequenceI, String> columnResidues = alignedCodons
+      final Map<SequenceI, AlignedCodon> columnResidues = alignedCodons
               .get(codon);
-      for (Entry<SequenceI, String> entry : columnResidues.entrySet())
+      for (Entry<SequenceI, AlignedCodon> entry : columnResidues.entrySet())
       {
         // place translated codon at its column position in sequence
-        entry.getKey().getSequence()[column] = entry.getValue().charAt(0);
+        entry.getKey().getSequence()[column] = entry.getValue().product
+                .charAt(0);
       }
       column++;
     }
@@ -991,23 +1250,51 @@ public class AlignmentUtils
    */
   static void addCodonPositions(SequenceI dna, SequenceI protein,
           char gapChar, Mapping seqMap,
-          Map<AlignedCodon, Map<SequenceI, String>> alignedCodons)
+          Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons)
   {
     Iterator<AlignedCodon> codons = seqMap.getCodonIterator(dna, gapChar);
+
+    /*
+     * add codon positions, and their peptide translations, to the alignment
+     * map, while remembering the first codon mapped
+     */
     while (codons.hasNext())
     {
-      AlignedCodon codon = codons.next();
-      Map<SequenceI, String> seqProduct = alignedCodons.get(codon);
-      if (seqProduct == null)
+      try
+      {
+        AlignedCodon codon = codons.next();
+        addCodonToMap(alignedCodons, codon, protein);
+      } catch (IncompleteCodonException e)
       {
-        seqProduct = new HashMap<SequenceI, String>();
-        alignedCodons.put(codon, seqProduct);
+        // possible incomplete trailing codon - ignore
+      } catch (NoSuchElementException e)
+      {
+        // possibly peptide lacking STOP
       }
-      seqProduct.put(protein, codon.product);
     }
   }
 
   /**
+   * Helper method to add a codon-to-peptide entry to the aligned codons map
+   * 
+   * @param alignedCodons
+   * @param codon
+   * @param protein
+   */
+  protected static void addCodonToMap(
+          Map<AlignedCodon, Map<SequenceI, AlignedCodon>> alignedCodons,
+          AlignedCodon codon, SequenceI protein)
+  {
+    Map<SequenceI, AlignedCodon> seqProduct = alignedCodons.get(codon);
+    if (seqProduct == null)
+    {
+      seqProduct = new HashMap<SequenceI, AlignedCodon>();
+      alignedCodons.put(codon, seqProduct);
+    }
+    seqProduct.put(protein, codon);
+  }
+
+  /**
    * Returns true if a cDNA/Protein mapping either exists, or could be made,
    * between at least one pair of sequences in the two alignments. Currently,
    * the logic is:
@@ -1039,7 +1326,7 @@ public class AlignmentUtils
     }
     AlignmentI dna = al1.isNucleotide() ? al1 : al2;
     AlignmentI protein = dna == al1 ? al2 : al1;
-    Set<AlignedCodonFrame> mappings = protein.getCodonFrames();
+    List<AlignedCodonFrame> mappings = protein.getCodonFrames();
     for (SequenceI dnaSeq : dna.getSequences())
     {
       for (SequenceI proteinSeq : protein.getSequences())
@@ -1063,7 +1350,7 @@ public class AlignmentUtils
    * @return
    */
   protected static boolean isMappable(SequenceI dnaSeq,
-          SequenceI proteinSeq, Set<AlignedCodonFrame> mappings)
+          SequenceI proteinSeq, List<AlignedCodonFrame> mappings)
   {
     if (dnaSeq == null || proteinSeq == null)
     {
@@ -1075,13 +1362,13 @@ public class AlignmentUtils
     SequenceI proteinDs = proteinSeq.getDatasetSequence() == null ? proteinSeq
             : proteinSeq.getDatasetSequence();
 
-    /*
-     * Already mapped?
-     */
     for (AlignedCodonFrame mapping : mappings)
     {
       if (proteinDs == mapping.getAaForDnaSeq(dnaDs))
       {
+        /*
+         * already mapped
+         */
         return true;
       }
     }
@@ -1090,7 +1377,7 @@ public class AlignmentUtils
      * Just try to make a mapping (it is not yet stored), test whether
      * successful.
      */
-    return mapProteinToCdna(proteinDs, dnaDs) != null;
+    return mapCdnaToProtein(proteinDs, dnaDs) != null;
   }
 
   /**
@@ -1232,15 +1519,19 @@ public class AlignmentUtils
           Collection<String> types, List<SequenceI> forSequences,
           boolean anyType, boolean doShow)
   {
-    for (AlignmentAnnotation aa : al.getAlignmentAnnotation())
+    AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
+    if (anns != null)
     {
-      if (anyType || types.contains(aa.label))
+      for (AlignmentAnnotation aa : anns)
       {
-        if ((aa.sequenceRef != null)
-                && (forSequences == null || forSequences
-                        .contains(aa.sequenceRef)))
+        if (anyType || types.contains(aa.label))
         {
-          aa.visible = doShow;
+          if ((aa.sequenceRef != null)
+                  && (forSequences == null || forSequences
+                          .contains(aa.sequenceRef)))
+          {
+            aa.visible = doShow;
+          }
         }
       }
     }
@@ -1275,7 +1566,7 @@ public class AlignmentUtils
       return false;
     }
     String name = seq2.getName();
-    final DBRefEntry[] xrefs = seq1.getDBRef();
+    final DBRefEntry[] xrefs = seq1.getDBRefs();
     if (xrefs != null)
     {
       for (DBRefEntry xref : xrefs)
@@ -1292,133 +1583,1402 @@ public class AlignmentUtils
   }
 
   /**
-   * Constructs an alignment consisting of the mapped exon regions in the given
-   * nucleotide sequences, and updates mappings to match.
+   * Constructs an alignment consisting of the mapped (CDS) regions in the given
+   * nucleotide sequences, and updates mappings to match. The CDS sequences are
+   * added to the original alignment's dataset, which is shared by the new
+   * alignment. Mappings from nucleotide to CDS, and from CDS to protein, are
+   * added to the alignment dataset.
    * 
    * @param dna
-   *          aligned dna sequences
-   * @param mappings
-   *          from dna to protein; these are replaced with new mappings
-   * @return an alignment whose sequences are the exon-only parts of the dna
-   *         sequences (or null if no exons are found)
+   *          aligned nucleotide (dna or cds) sequences
+   * @param dataset
+   *          the alignment dataset the sequences belong to
+   * @param products
+   *          (optional) to restrict results to CDS that map to specified
+   *          protein products
+   * @return an alignment whose sequences are the cds-only parts of the dna
+   *         sequences (or null if no mappings are found)
    */
-  public static AlignmentI makeExonAlignment(SequenceI[] dna,
-          Set<AlignedCodonFrame> mappings)
+  public static AlignmentI makeCdsAlignment(SequenceI[] dna,
+          AlignmentI dataset, SequenceI[] products)
   {
-    Set<AlignedCodonFrame> newMappings = new LinkedHashSet<AlignedCodonFrame>();
-    List<SequenceI> exonSequences = new ArrayList<SequenceI>();
-
-    for (SequenceI dnaSeq : dna)
+    if (dataset == null || dataset.getDataset() != null)
     {
-      final SequenceI ds = dnaSeq.getDatasetSequence();
-      List<AlignedCodonFrame> seqMappings = MappingUtils
-              .findMappingsForSequence(ds, mappings);
-      for (AlignedCodonFrame acf : seqMappings)
+      throw new IllegalArgumentException(
+              "IMPLEMENTATION ERROR: dataset.getDataset() must be null!");
+    }
+    List<SequenceI> foundSeqs = new ArrayList<SequenceI>();
+    List<SequenceI> cdsSeqs = new ArrayList<SequenceI>();
+    List<AlignedCodonFrame> mappings = dataset.getCodonFrames();
+    HashSet<SequenceI> productSeqs = null;
+    if (products != null)
+    {
+      productSeqs = new HashSet<SequenceI>();
+      for (SequenceI seq : products)
       {
-        AlignedCodonFrame newMapping = new AlignedCodonFrame();
-        final List<SequenceI> mappedExons = makeExonSequences(ds, acf,
-                newMapping);
-        if (!mappedExons.isEmpty())
-        {
-          exonSequences.addAll(mappedExons);
-          newMappings.add(newMapping);
-        }
+        productSeqs.add(seq.getDatasetSequence() == null ? seq : seq
+                .getDatasetSequence());
       }
     }
-    AlignmentI al = new Alignment(
-            exonSequences.toArray(new SequenceI[exonSequences.size()]));
-    al.setDataset(null);
 
     /*
-     * Replace the old mappings with the new ones
+     * Construct CDS sequences from mappings on the alignment dataset.
+     * The logic is:
+     * - find the protein product(s) mapped to from each dna sequence
+     * - if the mapping covers the whole dna sequence (give or take start/stop
+     *   codon), take the dna as the CDS sequence
+     * - else search dataset mappings for a suitable dna sequence, i.e. one
+     *   whose whole sequence is mapped to the protein 
+     * - if no sequence found, construct one from the dna sequence and mapping
+     *   (and add it to dataset so it is found if this is repeated)
      */
-    mappings.clear();
-    mappings.addAll(newMappings);
-
-    return al;
-  }
-
-  /**
-   * Helper method to make exon-only sequences and populate their mappings to
-   * protein products
-   * <p>
-   * For example, if ggCCaTTcGAg has mappings [3, 4, 6, 7, 9, 10] to protein
-   * then generate a sequence CCTTGA with mapping [1, 6] to the same protein
-   * residues
-   * <p>
-   * Typically eukaryotic dna will include exons encoding for a single peptide
-   * sequence i.e. return a single result. Bacterial dna may have overlapping
-   * exon mappings coding for multiple peptides so return multiple results
-   * (example EMBL KF591215).
-   * 
-   * @param dnaSeq
-   *          a dna dataset sequence
-   * @param mapping
-   *          containing one or more mappings of the sequence to protein
-   * @param newMapping
-   *          the new mapping to populate, from the exon-only sequences to their
-   *          mapped protein sequences
-   * @return
-   */
-  protected static List<SequenceI> makeExonSequences(SequenceI dnaSeq,
-          AlignedCodonFrame mapping, AlignedCodonFrame newMapping)
-  {
-    List<SequenceI> exonSequences = new ArrayList<SequenceI>();
-    List<Mapping> seqMappings = mapping.getMappingsForSequence(dnaSeq);
-    final char[] dna = dnaSeq.getSequence();
-    for (Mapping seqMapping : seqMappings)
+    for (SequenceI dnaSeq : dna)
     {
-      StringBuilder newSequence = new StringBuilder(dnaSeq.getLength());
+      SequenceI dnaDss = dnaSeq.getDatasetSequence() == null ? dnaSeq
+              : dnaSeq.getDatasetSequence();
 
-      /*
-       * Get the codon regions as { [2, 5], [7, 12], [14, 14] etc }
-       */
-      final List<int[]> dnaExonRanges = seqMapping.getMap().getFromRanges();
-      for (int[] range : dnaExonRanges)
+      List<AlignedCodonFrame> seqMappings = MappingUtils
+              .findMappingsForSequence(dnaSeq, mappings);
+      for (AlignedCodonFrame mapping : seqMappings)
       {
-        for (int pos = range[0]; pos <= range[1]; pos++)
-        {
-          newSequence.append(dna[pos - 1]);
-        }
-      }
+        List<Mapping> mappingsFromSequence = mapping
+                .getMappingsFromSequence(dnaSeq);
 
-      SequenceI exon = new Sequence(dnaSeq.getName(),
-              newSequence.toString());
-
-      /*
-       * Locate any xrefs to CDS database on the protein product and attach to
-       * the CDS sequence. Also add as a sub-token of the sequence name.
-       */
-      // default to "CDS" if we can't locate an actual gene id
-      String cdsAccId = FeatureProperties
-              .getCodingFeature(DBRefSource.EMBL);
-      DBRefEntry[] cdsRefs = DBRefUtils.selectRefs(seqMapping.getTo()
-              .getDBRef(), DBRefSource.CODINGDBS);
-      if (cdsRefs != null)
-      {
-        for (DBRefEntry cdsRef : cdsRefs)
+        for (Mapping aMapping : mappingsFromSequence)
         {
-          exon.addDBRef(new DBRefEntry(cdsRef));
-          cdsAccId = cdsRef.getAccessionId();
-        }
-      }
-      exon.setName(exon.getName() + "|" + cdsAccId);
-      exon.createDatasetSequence();
+          MapList mapList = aMapping.getMap();
+          if (mapList.getFromRatio() == 1)
+          {
+            /*
+             * not a dna-to-protein mapping (likely dna-to-cds)
+             */
+            continue;
+          }
 
-      /*
-       * Build new mappings - from the same protein regions, but now to
-       * contiguous exons
-       */
-      List<int[]> exonRange = new ArrayList<int[]>();
-      exonRange.add(new int[] { 1, newSequence.length() });
-      MapList map = new MapList(exonRange, seqMapping.getMap()
-              .getToRanges(), 3, 1);
-      newMapping.addMap(exon.getDatasetSequence(), seqMapping.getTo(), map);
-      MapList cdsToDnaMap = new MapList(dnaExonRanges, exonRange, 1, 1);
-      newMapping.addMap(dnaSeq, exon.getDatasetSequence(), cdsToDnaMap);
+          /*
+           * skip if mapping is not to one of the target set of proteins
+           */
+          SequenceI proteinProduct = aMapping.getTo();
+          if (productSeqs != null && !productSeqs.contains(proteinProduct))
+          {
+            continue;
+          }
+
+          /*
+           * try to locate the CDS from the dataset mappings;
+           * guard against duplicate results (for the case that protein has
+           * dbrefs to both dna and cds sequences)
+           */
+          SequenceI cdsSeq = findCdsForProtein(mappings, dnaSeq,
+                  seqMappings, aMapping);
+          if (cdsSeq != null)
+          {
+            if (!foundSeqs.contains(cdsSeq))
+            {
+              foundSeqs.add(cdsSeq);
+              SequenceI derivedSequence = cdsSeq.deriveSequence();
+              cdsSeqs.add(derivedSequence);
+              if (!dataset.getSequences().contains(cdsSeq))
+              {
+                dataset.addSequence(cdsSeq);
+              }
+            }
+            continue;
+          }
+
+          /*
+           * didn't find mapped CDS sequence - construct it and add
+           * its dataset sequence to the dataset
+           */
+          cdsSeq = makeCdsSequence(dnaSeq.getDatasetSequence(), aMapping,
+                  dataset).deriveSequence();
+          // cdsSeq has a name constructed as CDS|<dbref>
+          // <dbref> will be either the accession for the coding sequence,
+          // marked in the /via/ dbref to the protein product accession
+          // or it will be the original nucleotide accession.
+          SequenceI cdsSeqDss = cdsSeq.getDatasetSequence();
+
+          cdsSeqs.add(cdsSeq);
+
+          if (!dataset.getSequences().contains(cdsSeqDss))
+          {
+            // check if this sequence is a newly created one
+            // so needs adding to the dataset
+            dataset.addSequence(cdsSeqDss);
+          }
+
+          /*
+           * add a mapping from CDS to the (unchanged) mapped to range
+           */
+          List<int[]> cdsRange = Collections.singletonList(new int[] { 1,
+              cdsSeq.getLength() });
+          MapList cdsToProteinMap = new MapList(cdsRange,
+                  mapList.getToRanges(), mapList.getFromRatio(),
+                  mapList.getToRatio());
+          AlignedCodonFrame cdsToProteinMapping = new AlignedCodonFrame();
+          cdsToProteinMapping.addMap(cdsSeqDss, proteinProduct,
+                  cdsToProteinMap);
+
+          /*
+           * guard against duplicating the mapping if repeating this action
+           */
+          if (!mappings.contains(cdsToProteinMapping))
+          {
+            mappings.add(cdsToProteinMapping);
+          }
+
+          propagateDBRefsToCDS(cdsSeqDss, dnaSeq.getDatasetSequence(),
+                  proteinProduct, aMapping);
+          /*
+           * add another mapping from original 'from' range to CDS
+           */
+          AlignedCodonFrame dnaToCdsMapping = new AlignedCodonFrame();
+          MapList dnaToCdsMap = new MapList(mapList.getFromRanges(),
+                  cdsRange, 1, 1);
+          dnaToCdsMapping.addMap(dnaSeq.getDatasetSequence(), cdsSeqDss,
+                  dnaToCdsMap);
+          if (!mappings.contains(dnaToCdsMapping))
+          {
+            mappings.add(dnaToCdsMapping);
+          }
+
+          /*
+           * add DBRef with mapping from protein to CDS
+           * (this enables Get Cross-References from protein alignment)
+           * This is tricky because we can't have two DBRefs with the
+           * same source and accession, so need a different accession for
+           * the CDS from the dna sequence
+           */
+
+          // specific use case:
+          // Genomic contig ENSCHR:1, contains coding regions for ENSG01,
+          // ENSG02, ENSG03, with transcripts and products similarly named.
+          // cannot add distinct dbrefs mapping location on ENSCHR:1 to ENSG01
+
+          // JBPNote: ?? can't actually create an example that demonstrates we
+          // need to
+          // synthesize an xref.
+
+          for (DBRefEntry primRef : dnaDss.getPrimaryDBRefs())
+          {
+            // creates a complementary cross-reference to the source sequence's
+            // primary reference.
+
+            DBRefEntry cdsCrossRef = new DBRefEntry(primRef.getSource(),
+                    primRef.getSource() + ":" + primRef.getVersion(),
+                    primRef.getAccessionId());
+            cdsCrossRef
+                    .setMap(new Mapping(dnaDss, new MapList(dnaToCdsMap)));
+            cdsSeqDss.addDBRef(cdsCrossRef);
+
+            // problem here is that the cross-reference is synthesized -
+            // cdsSeq.getName() may be like 'CDS|dnaaccession' or
+            // 'CDS|emblcdsacc'
+            // assuming cds version same as dna ?!?
+
+            DBRefEntry proteinToCdsRef = new DBRefEntry(
+                    primRef.getSource(), primRef.getVersion(),
+                    cdsSeq.getName());
+            //
+            proteinToCdsRef.setMap(new Mapping(cdsSeqDss, cdsToProteinMap
+                    .getInverse()));
+            proteinProduct.addDBRef(proteinToCdsRef);
+          }
+
+          /*
+           * transfer any features on dna that overlap the CDS
+           */
+          transferFeatures(dnaSeq, cdsSeq, dnaToCdsMap, null,
+                  SequenceOntologyI.CDS);
+        }
+      }
+    }
+
+    AlignmentI cds = new Alignment(cdsSeqs.toArray(new SequenceI[cdsSeqs
+            .size()]));
+    cds.setDataset(dataset);
+
+    return cds;
+  }
+
+  /**
+   * A helper method that finds a CDS sequence in the alignment dataset that is
+   * mapped to the given protein sequence, and either is, or has a mapping from,
+   * the given dna sequence.
+   * 
+   * @param mappings
+   *          set of all mappings on the dataset
+   * @param dnaSeq
+   *          a dna (or cds) sequence we are searching from
+   * @param seqMappings
+   *          the set of mappings involving dnaSeq
+   * @param aMapping
+   *          an initial candidate from seqMappings
+   * @return
+   */
+  static SequenceI findCdsForProtein(List<AlignedCodonFrame> mappings,
+          SequenceI dnaSeq, List<AlignedCodonFrame> seqMappings,
+          Mapping aMapping)
+  {
+    /*
+     * TODO a better dna-cds-protein mapping data representation to allow easy
+     * navigation; until then this clunky looping around lists of mappings
+     */
+    SequenceI seqDss = dnaSeq.getDatasetSequence() == null ? dnaSeq
+            : dnaSeq.getDatasetSequence();
+    SequenceI proteinProduct = aMapping.getTo();
+
+    /*
+     * is this mapping from the whole dna sequence (i.e. CDS)?
+     * allowing for possible stop codon on dna but not peptide
+     */
+    int mappedFromLength = MappingUtils.getLength(aMapping.getMap()
+            .getFromRanges());
+    int dnaLength = seqDss.getLength();
+    if (mappedFromLength == dnaLength
+            || mappedFromLength == dnaLength - CODON_LENGTH)
+    {
+      return seqDss;
+    }
+
+    /*
+     * looks like we found the dna-to-protein mapping; search for the
+     * corresponding cds-to-protein mapping
+     */
+    List<AlignedCodonFrame> mappingsToPeptide = MappingUtils
+            .findMappingsForSequence(proteinProduct, mappings);
+    for (AlignedCodonFrame acf : mappingsToPeptide)
+    {
+      for (SequenceToSequenceMapping map : acf.getMappings())
+      {
+        Mapping mapping = map.getMapping();
+        if (mapping != aMapping
+                && mapping.getMap().getFromRatio() == CODON_LENGTH
+                && proteinProduct == mapping.getTo()
+                && seqDss != map.getFromSeq())
+        {
+          mappedFromLength = MappingUtils.getLength(mapping.getMap()
+                  .getFromRanges());
+          if (mappedFromLength == map.getFromSeq().getLength())
+          {
+            /*
+            * found a 3:1 mapping to the protein product which covers
+            * the whole dna sequence i.e. is from CDS; finally check it
+            * is from the dna start sequence
+            */
+            SequenceI cdsSeq = map.getFromSeq();
+            List<AlignedCodonFrame> dnaToCdsMaps = MappingUtils
+                    .findMappingsForSequence(cdsSeq, seqMappings);
+            if (!dnaToCdsMaps.isEmpty())
+            {
+              return cdsSeq;
+            }
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Helper method that makes a CDS sequence as defined by the mappings from the
+   * given sequence i.e. extracts the 'mapped from' ranges (which may be on
+   * forward or reverse strand).
+   * 
+   * @param seq
+   * @param mapping
+   * @param dataset
+   *          - existing dataset. We check for sequences that look like the CDS
+   *          we are about to construct, if one exists already, then we will
+   *          just return that one.
+   * @return CDS sequence (as a dataset sequence)
+   */
+  static SequenceI makeCdsSequence(SequenceI seq, Mapping mapping,
+          AlignmentI dataset)
+  {
+    char[] seqChars = seq.getSequence();
+    List<int[]> fromRanges = mapping.getMap().getFromRanges();
+    int cdsWidth = MappingUtils.getLength(fromRanges);
+    char[] newSeqChars = new char[cdsWidth];
+
+    int newPos = 0;
+    for (int[] range : fromRanges)
+    {
+      if (range[0] <= range[1])
+      {
+        // forward strand mapping - just copy the range
+        int length = range[1] - range[0] + 1;
+        System.arraycopy(seqChars, range[0] - 1, newSeqChars, newPos,
+                length);
+        newPos += length;
+      }
+      else
+      {
+        // reverse strand mapping - copy and complement one by one
+        for (int i = range[0]; i >= range[1]; i--)
+        {
+          newSeqChars[newPos++] = Dna.getComplement(seqChars[i - 1]);
+        }
+      }
+    }
+
+    /*
+     * assign 'from id' held in the mapping if set (e.g. EMBL protein_id),
+     * else generate a sequence name
+     */
+    String mapFromId = mapping.getMappedFromId();
+    String seqId = "CDS|" + (mapFromId != null ? mapFromId : seq.getName());
+    SequenceI newSeq = new Sequence(seqId, newSeqChars, 1, newPos);
+    if (dataset != null)
+    {
+      SequenceI[] matches = dataset.findSequenceMatch(newSeq.getName());
+      if (matches != null)
+      {
+        boolean matched = false;
+        for (SequenceI mtch : matches)
+        {
+          if (mtch.getStart() != newSeq.getStart())
+          {
+            continue;
+          }
+          if (mtch.getEnd() != newSeq.getEnd())
+          {
+            continue;
+          }
+          if (!Arrays.equals(mtch.getSequence(), newSeq.getSequence()))
+          {
+            continue;
+          }
+          if (!matched)
+          {
+            matched = true;
+            newSeq = mtch;
+          }
+          else
+          {
+            System.err
+                    .println("JAL-2154 regression: warning - found (and ignnored a duplicate CDS sequence):"
+                            + mtch.toString());
+          }
+        }
+      }
+    }
+    // newSeq.setDescription(mapFromId);
+
+    return newSeq;
+  }
+
+  /**
+   * add any DBRefEntrys to cdsSeq from contig that have a Mapping congruent to
+   * the given mapping.
+   * 
+   * @param cdsSeq
+   * @param contig
+   * @param mapping
+   * @return list of DBRefEntrys added.
+   */
+  public static List<DBRefEntry> propagateDBRefsToCDS(SequenceI cdsSeq,
+          SequenceI contig, SequenceI proteinProduct, Mapping mapping)
+  {
+
+    // gather direct refs from contig congrent with mapping
+    List<DBRefEntry> direct = new ArrayList<DBRefEntry>();
+    HashSet<String> directSources = new HashSet<String>();
+    if (contig.getDBRefs() != null)
+    {
+      for (DBRefEntry dbr : contig.getDBRefs())
+      {
+        if (dbr.hasMap() && dbr.getMap().getMap().isTripletMap())
+        {
+          MapList map = dbr.getMap().getMap();
+          // check if map is the CDS mapping
+          if (mapping.getMap().equals(map))
+          {
+            direct.add(dbr);
+            directSources.add(dbr.getSource());
+          }
+        }
+      }
+    }
+    DBRefEntry[] onSource = DBRefUtils.selectRefs(
+            proteinProduct.getDBRefs(),
+            directSources.toArray(new String[0]));
+    List<DBRefEntry> propagated = new ArrayList<DBRefEntry>();
+
+    // and generate appropriate mappings
+    for (DBRefEntry cdsref : direct)
+    {
+      // clone maplist and mapping
+      MapList cdsposmap = new MapList(Arrays.asList(new int[][] { new int[]
+      { cdsSeq.getStart(), cdsSeq.getEnd() } }), cdsref.getMap().getMap()
+              .getToRanges(), 3, 1);
+      Mapping cdsmap = new Mapping(cdsref.getMap().getTo(), cdsref.getMap()
+              .getMap());
+
+      // create dbref
+      DBRefEntry newref = new DBRefEntry(cdsref.getSource(),
+              cdsref.getVersion(), cdsref.getAccessionId(), new Mapping(
+                      cdsmap.getTo(), cdsposmap));
+
+      // and see if we can map to the protein product for this mapping.
+      // onSource is the filtered set of accessions on protein that we are
+      // tranferring, so we assume accession is the same.
+      if (cdsmap.getTo() == null && onSource != null)
+      {
+        List<DBRefEntry> sourceRefs = DBRefUtils.searchRefs(onSource,
+                cdsref.getAccessionId());
+        if (sourceRefs != null)
+        {
+          for (DBRefEntry srcref : sourceRefs)
+          {
+            if (srcref.getSource().equalsIgnoreCase(cdsref.getSource()))
+            {
+              // we have found a complementary dbref on the protein product, so
+              // update mapping's getTo
+              newref.getMap().setTo(proteinProduct);
+            }
+          }
+        }
+      }
+      cdsSeq.addDBRef(newref);
+      propagated.add(newref);
+    }
+    return propagated;
+  }
+
+  /**
+   * Transfers co-located features on 'fromSeq' to 'toSeq', adjusting the
+   * feature start/end ranges, optionally omitting specified feature types.
+   * Returns the number of features copied.
+   * 
+   * @param fromSeq
+   * @param toSeq
+   * @param select
+   *          if not null, only features of this type are copied (including
+   *          subtypes in the Sequence Ontology)
+   * @param mapping
+   *          the mapping from 'fromSeq' to 'toSeq'
+   * @param omitting
+   */
+  public static int transferFeatures(SequenceI fromSeq, SequenceI toSeq,
+          MapList mapping, String select, String... omitting)
+  {
+    SequenceI copyTo = toSeq;
+    while (copyTo.getDatasetSequence() != null)
+    {
+      copyTo = copyTo.getDatasetSequence();
+    }
+
+    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    int count = 0;
+    SequenceFeature[] sfs = fromSeq.getSequenceFeatures();
+    if (sfs != null)
+    {
+      for (SequenceFeature sf : sfs)
+      {
+        String type = sf.getType();
+        if (select != null && !so.isA(type, select))
+        {
+          continue;
+        }
+        boolean omit = false;
+        for (String toOmit : omitting)
+        {
+          if (type.equals(toOmit))
+          {
+            omit = true;
+          }
+        }
+        if (omit)
+        {
+          continue;
+        }
+
+        /*
+         * locate the mapped range - null if either start or end is
+         * not mapped (no partial overlaps are calculated)
+         */
+        int start = sf.getBegin();
+        int end = sf.getEnd();
+        int[] mappedTo = mapping.locateInTo(start, end);
+        /*
+         * if whole exon range doesn't map, try interpreting it
+         * as 5' or 3' exon overlapping the CDS range
+         */
+        if (mappedTo == null)
+        {
+          mappedTo = mapping.locateInTo(end, end);
+          if (mappedTo != null)
+          {
+            /*
+             * end of exon is in CDS range - 5' overlap
+             * to a range from the start of the peptide
+             */
+            mappedTo[0] = 1;
+          }
+        }
+        if (mappedTo == null)
+        {
+          mappedTo = mapping.locateInTo(start, start);
+          if (mappedTo != null)
+          {
+            /*
+             * start of exon is in CDS range - 3' overlap
+             * to a range up to the end of the peptide
+             */
+            mappedTo[1] = toSeq.getLength();
+          }
+        }
+        if (mappedTo != null)
+        {
+          SequenceFeature copy = new SequenceFeature(sf);
+          copy.setBegin(Math.min(mappedTo[0], mappedTo[1]));
+          copy.setEnd(Math.max(mappedTo[0], mappedTo[1]));
+          copyTo.addSequenceFeature(copy);
+          count++;
+        }
+      }
+    }
+    return count;
+  }
+
+  /**
+   * Returns a mapping from dna to protein by inspecting sequence features of
+   * type "CDS" on the dna.
+   * 
+   * @param dnaSeq
+   * @param proteinSeq
+   * @return
+   */
+  public static MapList mapCdsToProtein(SequenceI dnaSeq,
+          SequenceI proteinSeq)
+  {
+    List<int[]> ranges = findCdsPositions(dnaSeq);
+    int mappedDnaLength = MappingUtils.getLength(ranges);
+
+    int proteinLength = proteinSeq.getLength();
+    int proteinStart = proteinSeq.getStart();
+    int proteinEnd = proteinSeq.getEnd();
+
+    /*
+     * incomplete start codon may mean X at start of peptide
+     * we ignore both for mapping purposes
+     */
+    if (proteinSeq.getCharAt(0) == 'X')
+    {
+      // todo JAL-2022 support startPhase > 0
+      proteinStart++;
+      proteinLength--;
+    }
+    List<int[]> proteinRange = new ArrayList<int[]>();
+
+    /*
+     * dna length should map to protein (or protein plus stop codon)
+     */
+    int codesForResidues = mappedDnaLength / CODON_LENGTH;
+    if (codesForResidues == (proteinLength + 1))
+    {
+      // assuming extra codon is for STOP and not in peptide
+      codesForResidues--;
+    }
+    if (codesForResidues == proteinLength)
+    {
+      proteinRange.add(new int[] { proteinStart, proteinEnd });
+      return new MapList(ranges, proteinRange, CODON_LENGTH, 1);
+    }
+    return null;
+  }
+
+  /**
+   * Returns a list of CDS ranges found (as sequence positions base 1), i.e. of
+   * start/end positions of sequence features of type "CDS" (or a sub-type of
+   * CDS in the Sequence Ontology). The ranges are sorted into ascending start
+   * position order, so this method is only valid for linear CDS in the same
+   * sense as the protein product.
+   * 
+   * @param dnaSeq
+   * @return
+   */
+  public static List<int[]> findCdsPositions(SequenceI dnaSeq)
+  {
+    List<int[]> result = new ArrayList<int[]>();
+    SequenceFeature[] sfs = dnaSeq.getSequenceFeatures();
+    if (sfs == null)
+    {
+      return result;
+    }
+
+    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    int startPhase = 0;
+
+    for (SequenceFeature sf : sfs)
+    {
+      /*
+       * process a CDS feature (or a sub-type of CDS)
+       */
+      if (so.isA(sf.getType(), SequenceOntologyI.CDS))
+      {
+        int phase = 0;
+        try
+        {
+          phase = Integer.parseInt(sf.getPhase());
+        } catch (NumberFormatException e)
+        {
+          // ignore
+        }
+        /*
+         * phase > 0 on first codon means 5' incomplete - skip to the start
+         * of the next codon; example ENST00000496384
+         */
+        int begin = sf.getBegin();
+        int end = sf.getEnd();
+        if (result.isEmpty())
+        {
+          begin += phase;
+          if (begin > end)
+          {
+            // shouldn't happen!
+            System.err
+                    .println("Error: start phase extends beyond start CDS in "
+                            + dnaSeq.getName());
+          }
+        }
+        result.add(new int[] { begin, end });
+      }
+    }
+
+    /*
+     * remove 'startPhase' positions (usually 0) from the first range 
+     * so we begin at the start of a complete codon
+     */
+    if (!result.isEmpty())
+    {
+      // TODO JAL-2022 correctly model start phase > 0
+      result.get(0)[0] += startPhase;
+    }
+
+    /*
+     * Finally sort ranges by start position. This avoids a dependency on 
+     * keeping features in order on the sequence (if they are in order anyway,
+     * the sort will have almost no work to do). The implicit assumption is CDS
+     * ranges are assembled in order. Other cases should not use this method,
+     * but instead construct an explicit mapping for CDS (e.g. EMBL parsing).
+     */
+    Collections.sort(result, new Comparator<int[]>()
+    {
+      @Override
+      public int compare(int[] o1, int[] o2)
+      {
+        return Integer.compare(o1[0], o2[0]);
+      }
+    });
+    return result;
+  }
+
+  /**
+   * Maps exon features from dna to protein, and computes variants in peptide
+   * product generated by variants in dna, and adds them as sequence_variant
+   * features on the protein sequence. Returns the number of variant features
+   * added.
+   * 
+   * @param dnaSeq
+   * @param peptide
+   * @param dnaToProtein
+   */
+  public static int computeProteinFeatures(SequenceI dnaSeq,
+          SequenceI peptide, MapList dnaToProtein)
+  {
+    while (dnaSeq.getDatasetSequence() != null)
+    {
+      dnaSeq = dnaSeq.getDatasetSequence();
+    }
+    while (peptide.getDatasetSequence() != null)
+    {
+      peptide = peptide.getDatasetSequence();
+    }
+
+    transferFeatures(dnaSeq, peptide, dnaToProtein, SequenceOntologyI.EXON);
+
+    /*
+     * compute protein variants from dna variants and codon mappings;
+     * NB - alternatively we could retrieve this using the REST service e.g.
+     * http://rest.ensembl.org/overlap/translation
+     * /ENSP00000288602?feature=transcript_variation;content-type=text/xml
+     * which would be a bit slower but possibly more reliable
+     */
+
+    /*
+     * build a map with codon variations for each potentially varying peptide
+     */
+    LinkedHashMap<Integer, List<DnaVariant>[]> variants = buildDnaVariantsMap(
+            dnaSeq, dnaToProtein);
+
+    /*
+     * scan codon variations, compute peptide variants and add to peptide sequence
+     */
+    int count = 0;
+    for (Entry<Integer, List<DnaVariant>[]> variant : variants.entrySet())
+    {
+      int peptidePos = variant.getKey();
+      List<DnaVariant>[] codonVariants = variant.getValue();
+      count += computePeptideVariants(peptide, peptidePos, codonVariants);
+    }
+
+    /*
+     * sort to get sequence features in start position order
+     * - would be better to store in Sequence as a TreeSet or NCList?
+     */
+    if (peptide.getSequenceFeatures() != null)
+    {
+      Arrays.sort(peptide.getSequenceFeatures(),
+              new Comparator<SequenceFeature>()
+              {
+                @Override
+                public int compare(SequenceFeature o1, SequenceFeature o2)
+                {
+                  int c = Integer.compare(o1.getBegin(), o2.getBegin());
+                  return c == 0 ? Integer.compare(o1.getEnd(), o2.getEnd())
+                          : c;
+                }
+              });
+    }
+    return count;
+  }
+
+  /**
+   * Computes non-synonymous peptide variants from codon variants and adds them
+   * as sequence_variant features on the protein sequence (one feature per
+   * allele variant). Selected attributes (variant id, clinical significance)
+   * are copied over to the new features.
+   * 
+   * @param peptide
+   *          the protein sequence
+   * @param peptidePos
+   *          the position to compute peptide variants for
+   * @param codonVariants
+   *          a list of dna variants per codon position
+   * @return the number of features added
+   */
+  static int computePeptideVariants(SequenceI peptide, int peptidePos,
+          List<DnaVariant>[] codonVariants)
+  {
+    String residue = String.valueOf(peptide.getCharAt(peptidePos - 1));
+    int count = 0;
+    String base1 = codonVariants[0].get(0).base;
+    String base2 = codonVariants[1].get(0).base;
+    String base3 = codonVariants[2].get(0).base;
+
+    /*
+     * variants in first codon base
+     */
+    for (DnaVariant var : codonVariants[0])
+    {
+      if (var.variant != null)
+      {
+        String alleles = (String) var.variant.getValue("alleles");
+        if (alleles != null)
+        {
+          for (String base : alleles.split(","))
+          {
+            String codon = base + base2 + base3;
+            if (addPeptideVariant(peptide, peptidePos, residue, var, codon))
+            {
+              count++;
+            }
+          }
+        }
+      }
+    }
+
+    /*
+     * variants in second codon base
+     */
+    for (DnaVariant var : codonVariants[1])
+    {
+      if (var.variant != null)
+      {
+        String alleles = (String) var.variant.getValue("alleles");
+        if (alleles != null)
+        {
+          for (String base : alleles.split(","))
+          {
+            String codon = base1 + base + base3;
+            if (addPeptideVariant(peptide, peptidePos, residue, var, codon))
+            {
+              count++;
+            }
+          }
+        }
+      }
+    }
+
+    /*
+     * variants in third codon base
+     */
+    for (DnaVariant var : codonVariants[2])
+    {
+      if (var.variant != null)
+      {
+        String alleles = (String) var.variant.getValue("alleles");
+        if (alleles != null)
+        {
+          for (String base : alleles.split(","))
+          {
+            String codon = base1 + base2 + base;
+            if (addPeptideVariant(peptide, peptidePos, residue, var, codon))
+            {
+              count++;
+            }
+          }
+        }
+      }
+    }
+
+    return count;
+  }
+
+  /**
+   * Helper method that adds a peptide variant feature, provided the given codon
+   * translates to a value different to the current residue (is a non-synonymous
+   * variant). ID and clinical_significance attributes of the dna variant (if
+   * present) are copied to the new feature.
+   * 
+   * @param peptide
+   * @param peptidePos
+   * @param residue
+   * @param var
+   * @param codon
+   * @return true if a feature was added, else false
+   */
+  static boolean addPeptideVariant(SequenceI peptide, int peptidePos,
+          String residue, DnaVariant var, String codon)
+  {
+    /*
+     * get peptide translation of codon e.g. GAT -> D
+     * note that variants which are not single alleles,
+     * e.g. multibase variants or HGMD_MUTATION etc
+     * are currently ignored here
+     */
+    String trans = codon.contains("-") ? "-"
+            : (codon.length() > CODON_LENGTH ? null : ResidueProperties
+                    .codonTranslate(codon));
+    if (trans != null && !trans.equals(residue))
+    {
+      String residue3Char = StringUtils
+              .toSentenceCase(ResidueProperties.aa2Triplet.get(residue));
+      String trans3Char = StringUtils
+              .toSentenceCase(ResidueProperties.aa2Triplet.get(trans));
+      String desc = "p." + residue3Char + peptidePos + trans3Char;
+      // set score to 0f so 'graduated colour' option is offered! JAL-2060
+      SequenceFeature sf = new SequenceFeature(
+              SequenceOntologyI.SEQUENCE_VARIANT, desc, peptidePos,
+              peptidePos, 0f, var.getSource());
+      StringBuilder attributes = new StringBuilder(32);
+      String id = (String) var.variant.getValue(ID);
+      if (id != null)
+      {
+        if (id.startsWith(SEQUENCE_VARIANT))
+        {
+          id = id.substring(SEQUENCE_VARIANT.length());
+        }
+        sf.setValue(ID, id);
+        attributes.append(ID).append("=").append(id);
+        // TODO handle other species variants JAL-2064
+        StringBuilder link = new StringBuilder(32);
+        try
+        {
+          link.append(desc)
+                  .append(" ")
+                  .append(id)
+                  .append("|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=")
+                  .append(URLEncoder.encode(id, "UTF-8"));
+          sf.addLink(link.toString());
+        } catch (UnsupportedEncodingException e)
+        {
+          // as if
+        }
+      }
+      String clinSig = (String) var.variant.getValue(CLINICAL_SIGNIFICANCE);
+      if (clinSig != null)
+      {
+        sf.setValue(CLINICAL_SIGNIFICANCE, clinSig);
+        attributes.append(";").append(CLINICAL_SIGNIFICANCE).append("=")
+                .append(clinSig);
+      }
+      peptide.addSequenceFeature(sf);
+      if (attributes.length() > 0)
+      {
+        sf.setAttributes(attributes.toString());
+      }
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Builds a map whose key is position in the protein sequence, and value is a
+   * list of the base and all variants for each corresponding codon position
+   * 
+   * @param dnaSeq
+   * @param dnaToProtein
+   * @return
+   */
+  @SuppressWarnings("unchecked")
+  static LinkedHashMap<Integer, List<DnaVariant>[]> buildDnaVariantsMap(
+          SequenceI dnaSeq, MapList dnaToProtein)
+  {
+    /*
+     * map from peptide position to all variants of the codon which codes for it
+     * LinkedHashMap ensures we keep the peptide features in sequence order
+     */
+    LinkedHashMap<Integer, List<DnaVariant>[]> variants = new LinkedHashMap<Integer, List<DnaVariant>[]>();
+    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+
+    SequenceFeature[] dnaFeatures = dnaSeq.getSequenceFeatures();
+    if (dnaFeatures == null)
+    {
+      return variants;
+    }
+
+    int dnaStart = dnaSeq.getStart();
+    int[] lastCodon = null;
+    int lastPeptidePostion = 0;
+
+    /*
+     * build a map of codon variations for peptides
+     */
+    for (SequenceFeature sf : dnaFeatures)
+    {
+      int dnaCol = sf.getBegin();
+      if (dnaCol != sf.getEnd())
+      {
+        // not handling multi-locus variant features
+        continue;
+      }
+      if (so.isA(sf.getType(), SequenceOntologyI.SEQUENCE_VARIANT))
+      {
+        int[] mapsTo = dnaToProtein.locateInTo(dnaCol, dnaCol);
+        if (mapsTo == null)
+        {
+          // feature doesn't lie within coding region
+          continue;
+        }
+        int peptidePosition = mapsTo[0];
+        List<DnaVariant>[] codonVariants = variants.get(peptidePosition);
+        if (codonVariants == null)
+        {
+          codonVariants = new ArrayList[CODON_LENGTH];
+          codonVariants[0] = new ArrayList<DnaVariant>();
+          codonVariants[1] = new ArrayList<DnaVariant>();
+          codonVariants[2] = new ArrayList<DnaVariant>();
+          variants.put(peptidePosition, codonVariants);
+        }
+
+        /*
+         * extract dna variants to a string array
+         */
+        String alls = (String) sf.getValue("alleles");
+        if (alls == null)
+        {
+          continue;
+        }
+        String[] alleles = alls.toUpperCase().split(",");
+        int i = 0;
+        for (String allele : alleles)
+        {
+          alleles[i++] = allele.trim(); // lose any space characters "A, G"
+        }
+
+        /*
+         * get this peptide's codon positions e.g. [3, 4, 5] or [4, 7, 10]
+         */
+        int[] codon = peptidePosition == lastPeptidePostion ? lastCodon
+                : MappingUtils.flattenRanges(dnaToProtein.locateInFrom(
+                        peptidePosition, peptidePosition));
+        lastPeptidePostion = peptidePosition;
+        lastCodon = codon;
+
+        /*
+         * save nucleotide (and any variant) for each codon position
+         */
+        for (int codonPos = 0; codonPos < CODON_LENGTH; codonPos++)
+        {
+          String nucleotide = String.valueOf(
+                  dnaSeq.getCharAt(codon[codonPos] - dnaStart))
+                  .toUpperCase();
+          List<DnaVariant> codonVariant = codonVariants[codonPos];
+          if (codon[codonPos] == dnaCol)
+          {
+            if (!codonVariant.isEmpty()
+                    && codonVariant.get(0).variant == null)
+            {
+              /*
+               * already recorded base value, add this variant
+               */
+              codonVariant.get(0).variant = sf;
+            }
+            else
+            {
+              /*
+               * add variant with base value
+               */
+              codonVariant.add(new DnaVariant(nucleotide, sf));
+            }
+          }
+          else if (codonVariant.isEmpty())
+          {
+            /*
+             * record (possibly non-varying) base value
+             */
+            codonVariant.add(new DnaVariant(nucleotide));
+          }
+        }
+      }
+    }
+    return variants;
+  }
+
+  /**
+   * Makes an alignment with a copy of the given sequences, adding in any
+   * non-redundant sequences which are mapped to by the cross-referenced
+   * sequences.
+   * 
+   * @param seqs
+   * @param xrefs
+   * @param dataset
+   *          the alignment dataset shared by the new copy
+   * @return
+   */
+  public static AlignmentI makeCopyAlignment(SequenceI[] seqs,
+          SequenceI[] xrefs, AlignmentI dataset)
+  {
+    AlignmentI copy = new Alignment(new Alignment(seqs));
+    copy.setDataset(dataset);
+    boolean isProtein = !copy.isNucleotide();
+    SequenceIdMatcher matcher = new SequenceIdMatcher(seqs);
+    if (xrefs != null)
+    {
+      for (SequenceI xref : xrefs)
+      {
+        DBRefEntry[] dbrefs = xref.getDBRefs();
+        if (dbrefs != null)
+        {
+          for (DBRefEntry dbref : dbrefs)
+          {
+            if (dbref.getMap() == null || dbref.getMap().getTo() == null
+                    || dbref.getMap().getTo().isProtein() != isProtein)
+            {
+              continue;
+            }
+            SequenceI mappedTo = dbref.getMap().getTo();
+            SequenceI match = matcher.findIdMatch(mappedTo);
+            if (match == null)
+            {
+              matcher.add(mappedTo);
+              copy.addSequence(mappedTo);
+            }
+          }
+        }
+      }
+    }
+    return copy;
+  }
+
+  /**
+   * Try to align sequences in 'unaligned' to match the alignment of their
+   * mapped regions in 'aligned'. For example, could use this to align CDS
+   * sequences which are mapped to their parent cDNA sequences.
+   * 
+   * This method handles 1:1 mappings (dna-to-dna or protein-to-protein). For
+   * dna-to-protein or protein-to-dna use alternative methods.
+   * 
+   * @param unaligned
+   *          sequences to be aligned
+   * @param aligned
+   *          holds aligned sequences and their mappings
+   * @return
+   */
+  public static int alignAs(AlignmentI unaligned, AlignmentI aligned)
+  {
+    /*
+     * easy case - aligning a copy of aligned sequences
+     */
+    if (alignAsSameSequences(unaligned, aligned))
+    {
+      return unaligned.getHeight();
+    }
+
+    /*
+     * fancy case - aligning via mappings between sequences
+     */
+    List<SequenceI> unmapped = new ArrayList<SequenceI>();
+    Map<Integer, Map<SequenceI, Character>> columnMap = buildMappedColumnsMap(
+            unaligned, aligned, unmapped);
+    int width = columnMap.size();
+    char gap = unaligned.getGapCharacter();
+    int realignedCount = 0;
+    // TODO: verify this loop scales sensibly for very wide/high alignments
+
+    for (SequenceI seq : unaligned.getSequences())
+    {
+      if (!unmapped.contains(seq))
+      {
+        char[] newSeq = new char[width];
+        Arrays.fill(newSeq, gap); // JBPComment - doubt this is faster than the
+                                  // Integer iteration below
+        int newCol = 0;
+        int lastCol = 0;
+
+        /*
+         * traverse the map to find columns populated
+         * by our sequence
+         */
+        for (Integer column : columnMap.keySet())
+        {
+          Character c = columnMap.get(column).get(seq);
+          if (c != null)
+          {
+            /*
+             * sequence has a character at this position
+             * 
+             */
+            newSeq[newCol] = c;
+            lastCol = newCol;
+          }
+          newCol++;
+        }
+
+        /*
+         * trim trailing gaps
+         */
+        if (lastCol < width)
+        {
+          char[] tmp = new char[lastCol + 1];
+          System.arraycopy(newSeq, 0, tmp, 0, lastCol + 1);
+          newSeq = tmp;
+        }
+        // TODO: optimise SequenceI to avoid char[]->String->char[]
+        seq.setSequence(String.valueOf(newSeq));
+        realignedCount++;
+      }
+    }
+    return realignedCount;
+  }
+
+  /**
+   * If unaligned and aligned sequences share the same dataset sequences, then
+   * simply copies the aligned sequences to the unaligned sequences and returns
+   * true; else returns false
+   * 
+   * @param unaligned
+   *          - sequences to be aligned based on aligned
+   * @param aligned
+   *          - 'guide' alignment containing sequences derived from same dataset
+   *          as unaligned
+   * @return
+   */
+  static boolean alignAsSameSequences(AlignmentI unaligned,
+          AlignmentI aligned)
+  {
+    if (aligned.getDataset() == null || unaligned.getDataset() == null)
+    {
+      return false; // should only pass alignments with datasets here
+    }
+
+    // map from dataset sequence to alignment sequence(s)
+    Map<SequenceI, List<SequenceI>> alignedDatasets = new HashMap<SequenceI, List<SequenceI>>();
+    for (SequenceI seq : aligned.getSequences())
+    {
+      SequenceI ds = seq.getDatasetSequence();
+      if (alignedDatasets.get(ds) == null)
+      {
+        alignedDatasets.put(ds, new ArrayList<SequenceI>());
+      }
+      alignedDatasets.get(ds).add(seq);
+    }
+
+    /*
+     * first pass - check whether all sequences to be aligned share a dataset
+     * sequence with an aligned sequence
+     */
+    for (SequenceI seq : unaligned.getSequences())
+    {
+      if (!alignedDatasets.containsKey(seq.getDatasetSequence()))
+      {
+        return false;
+      }
+    }
+
+    /*
+     * second pass - copy aligned sequences;
+     * heuristic rule: pair off sequences in order for the case where 
+     * more than one shares the same dataset sequence 
+     */
+    for (SequenceI seq : unaligned.getSequences())
+    {
+      List<SequenceI> alignedSequences = alignedDatasets.get(seq
+              .getDatasetSequence());
+      // TODO: getSequenceAsString() will be deprecated in the future
+      // TODO: need to leave to SequenceI implementor to update gaps
+      seq.setSequence(alignedSequences.get(0).getSequenceAsString());
+      if (alignedSequences.size() > 0)
+      {
+        // pop off aligned sequences (except the last one)
+        alignedSequences.remove(0);
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * Returns a map whose key is alignment column number (base 1), and whose
+   * values are a map of sequence characters in that column.
+   * 
+   * @param unaligned
+   * @param aligned
+   * @param unmapped
+   * @return
+   */
+  static Map<Integer, Map<SequenceI, Character>> buildMappedColumnsMap(
+          AlignmentI unaligned, AlignmentI aligned, List<SequenceI> unmapped)
+  {
+    /*
+     * Map will hold, for each aligned column position, a map of
+     * {unalignedSequence, characterPerSequence} at that position.
+     * TreeMap keeps the entries in ascending column order. 
+     */
+    Map<Integer, Map<SequenceI, Character>> map = new TreeMap<Integer, Map<SequenceI, Character>>();
+
+    /*
+     * record any sequences that have no mapping so can't be realigned
+     */
+    unmapped.addAll(unaligned.getSequences());
+
+    List<AlignedCodonFrame> mappings = aligned.getCodonFrames();
+
+    for (SequenceI seq : unaligned.getSequences())
+    {
+      for (AlignedCodonFrame mapping : mappings)
+      {
+        SequenceI fromSeq = mapping.findAlignedSequence(seq, aligned);
+        if (fromSeq != null)
+        {
+          Mapping seqMap = mapping.getMappingBetween(fromSeq, seq);
+          if (addMappedPositions(seq, fromSeq, seqMap, map))
+          {
+            unmapped.remove(seq);
+          }
+        }
+      }
+    }
+    return map;
+  }
+
+  /**
+   * Helper method that adds to a map the mapped column positions of a sequence. <br>
+   * For example if aaTT-Tg-gAAA is mapped to TTTAAA then the map should record
+   * that columns 3,4,6,10,11,12 map to characters T,T,T,A,A,A of the mapped to
+   * sequence.
+   * 
+   * @param seq
+   *          the sequence whose column positions we are recording
+   * @param fromSeq
+   *          a sequence that is mapped to the first sequence
+   * @param seqMap
+   *          the mapping from 'fromSeq' to 'seq'
+   * @param map
+   *          a map to add the column positions (in fromSeq) of the mapped
+   *          positions of seq
+   * @return
+   */
+  static boolean addMappedPositions(SequenceI seq, SequenceI fromSeq,
+          Mapping seqMap, Map<Integer, Map<SequenceI, Character>> map)
+  {
+    if (seqMap == null)
+    {
+      return false;
+    }
+
+    /*
+     * invert mapping if it is from unaligned to aligned sequence
+     */
+    if (seqMap.getTo() == fromSeq.getDatasetSequence())
+    {
+      seqMap = new Mapping(seq.getDatasetSequence(), seqMap.getMap()
+              .getInverse());
+    }
+
+    char[] fromChars = fromSeq.getSequence();
+    int toStart = seq.getStart();
+    char[] toChars = seq.getSequence();
 
-      exonSequences.add(exon);
+    /*
+     * traverse [start, end, start, end...] ranges in fromSeq
+     */
+    for (int[] fromRange : seqMap.getMap().getFromRanges())
+    {
+      for (int i = 0; i < fromRange.length - 1; i += 2)
+      {
+        boolean forward = fromRange[i + 1] >= fromRange[i];
+
+        /*
+         * find the range mapped to (sequence positions base 1)
+         */
+        int[] range = seqMap.locateMappedRange(fromRange[i],
+                fromRange[i + 1]);
+        if (range == null)
+        {
+          System.err.println("Error in mapping " + seqMap + " from "
+                  + fromSeq.getName());
+          return false;
+        }
+        int fromCol = fromSeq.findIndex(fromRange[i]);
+        int mappedCharPos = range[0];
+
+        /*
+         * walk over the 'from' aligned sequence in forward or reverse
+         * direction; when a non-gap is found, record the column position
+         * of the next character of the mapped-to sequence; stop when all
+         * the characters of the range have been counted
+         */
+        while (mappedCharPos <= range[1] && fromCol <= fromChars.length
+                && fromCol >= 0)
+        {
+          if (!Comparison.isGap(fromChars[fromCol - 1]))
+          {
+            /*
+             * mapped from sequence has a character in this column
+             * record the column position for the mapped to character
+             */
+            Map<SequenceI, Character> seqsMap = map.get(fromCol);
+            if (seqsMap == null)
+            {
+              seqsMap = new HashMap<SequenceI, Character>();
+              map.put(fromCol, seqsMap);
+            }
+            seqsMap.put(seq, toChars[mappedCharPos - toStart]);
+            mappedCharPos++;
+          }
+          fromCol += (forward ? 1 : -1);
+        }
+      }
+    }
+    return true;
+  }
+
+  // strictly temporary hack until proper criteria for aligning protein to cds
+  // are in place; this is so Ensembl -> fetch xrefs Uniprot aligns the Uniprot
+  public static boolean looksLikeEnsembl(AlignmentI alignment)
+  {
+    for (SequenceI seq : alignment.getSequences())
+    {
+      String name = seq.getName();
+      if (!name.startsWith("ENSG") && !name.startsWith("ENST"))
+      {
+        return false;
+      }
     }
-    return exonSequences;
+    return true;
   }
 }
diff --git a/src/jalview/analysis/AnnotationSorter.java b/src/jalview/analysis/AnnotationSorter.java
index c1dd6a7..8714d92 100644
--- a/src/jalview/analysis/AnnotationSorter.java
+++ b/src/jalview/analysis/AnnotationSorter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/analysis/CodingUtils.java b/src/jalview/analysis/CodingUtils.java
index 91c14c0..f9d1af7 100644
--- a/src/jalview/analysis/CodingUtils.java
+++ b/src/jalview/analysis/CodingUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/analysis/CodonComparator.java b/src/jalview/analysis/CodonComparator.java
index e978193..4d5e965 100644
--- a/src/jalview/analysis/CodonComparator.java
+++ b/src/jalview/analysis/CodonComparator.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/analysis/Conservation.java b/src/jalview/analysis/Conservation.java
index f12d801..0b211b6 100644
--- a/src/jalview/analysis/Conservation.java
+++ b/src/jalview/analysis/Conservation.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,36 +22,52 @@ package jalview.analysis;
 
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.ResidueCount;
+import jalview.datamodel.ResidueCount.SymbolCounts;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
+import jalview.schemes.ResidueProperties;
+import jalview.util.Comparison;
 
 import java.awt.Color;
-import java.util.Enumeration;
-import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
 import java.util.Vector;
 
 /**
  * Calculates conservation values for a given set of sequences
- * 
- * @author $author$
- * @version $Revision$
  */
 public class Conservation
 {
+  /*
+   * need to have a minimum of 3% of sequences with a residue
+   * for it to be included in the conservation calculation
+   */
+  private static final int THRESHOLD_PERCENT = 3;
+
+  private static final int TOUPPERCASE = 'a' - 'A';
+
   SequenceI[] sequences;
 
   int start;
 
   int end;
 
-  Vector seqNums; // vector of int vectors where first is sequence checksum
+  Vector<int[]> seqNums; // vector of int vectors where first is sequence
+                         // checksum
 
   int maxLength = 0; // used by quality calcs
 
   boolean seqNumsChanged = false; // updated after any change via calcSeqNum;
 
-  Hashtable[] total;
+  /*
+   * a map per column with {property, conservation} where conservation value is
+   * 1 (property is conserved), 0 (absence of property is conserved) or -1
+   * (property is not conserved i.e. column has residues with and without it)
+   */
+  Map<String, Integer>[] total;
 
   boolean canonicaliseAa = true; // if true then conservation calculation will
 
@@ -60,34 +76,29 @@ public class Conservation
   // symbol
 
   /** Stores calculated quality values */
-  public Vector quality;
+  private Vector<Double> quality;
 
   /** Stores maximum and minimum values of quality values */
-  public Double[] qualityRange = new Double[2];
-
-  String consString = "";
+  private double[] qualityRange = new double[2];
 
-  Sequence consSequence;
+  private Sequence consSequence;
 
-  Hashtable propHash;
-
-  int threshold;
+  /*
+   * percentage of residues in a column to qualify for counting conservation
+   */
+  private int threshold;
 
-  String name = "";
+  private String name = "";
 
-  int[][] cons2;
+  private int[][] cons2;
 
   private String[] consSymbs;
 
   /**
-   * Creates a new Conservation object.
+   * Constructor using default threshold of 3%
    * 
    * @param name
    *          Name of conservation
-   * @param propHash
-   *          hash of properties for each symbol
-   * @param threshold
-   *          to count the residues in residueHash(). commonly used value is 3
    * @param sequences
    *          sequences to be used in calculation
    * @param start
@@ -95,11 +106,31 @@ public class Conservation
    * @param end
    *          end residue position
    */
-  public Conservation(String name, Hashtable propHash, int threshold,
+  public Conservation(String name, List<SequenceI> sequences, int start,
+          int end)
+  {
+    this(name, THRESHOLD_PERCENT, sequences, start, end);
+  }
+
+  /**
+   * Constructor
+   * 
+   * @param name
+   *          Name of conservation
+   * @param threshold
+   *          percentage of sequences at or below which property conservation is
+   *          ignored
+   * @param sequences
+   *          sequences to be used in calculation
+   * @param start
+   *          start column position
+   * @param end
+   *          end column position
+   */
+  public Conservation(String name, int threshold,
           List<SequenceI> sequences, int start, int end)
   {
     this.name = name;
-    this.propHash = propHash;
     this.threshold = threshold;
     this.start = start;
     this.end = end;
@@ -151,7 +182,7 @@ public class Conservation
         seqNums.addElement(new int[sq.length() + 1]);
       }
 
-      if (sq.hashCode() != ((int[]) seqNums.elementAt(i))[0])
+      if (sq.hashCode() != seqNums.elementAt(i)[0])
       {
         int j;
         int len;
@@ -193,178 +224,221 @@ public class Conservation
    */
   public void calculate()
   {
-    Hashtable resultHash, ht;
-    int thresh, j, jSize = sequences.length;
-    int[] values; // Replaces residueHash
-    String type, res = null;
-    char c;
-    Enumeration enumeration2;
+    int height = sequences.length;
 
-    total = new Hashtable[maxLength];
+    total = new Map[maxLength];
 
-    for (int i = start; i <= end; i++)
+    for (int column = start; column <= end; column++)
     {
-      values = new int[255];
+      ResidueCount values = countResidues(column);
 
-      for (j = 0; j < jSize; j++)
+      /*
+       * percentage count at or below which we ignore residues
+       */
+      int thresh = (threshold * height) / 100;
+
+      /*
+       * check observed residues in column and record whether each 
+       * physico-chemical property is conserved (+1), absence conserved (0),
+       * or not conserved (-1)
+       * Using TreeMap means properties are displayed in alphabetical order
+       */
+      Map<String, Integer> resultHash = new TreeMap<String, Integer>();
+      SymbolCounts symbolCounts = values.getSymbolCounts();
+      char[] symbols = symbolCounts.symbols;
+      int[] counts = symbolCounts.values;
+      for (int j = 0; j < symbols.length; j++)
       {
-        if (sequences[j].getLength() > i)
+        char c = symbols[j];
+        if (counts[j] > thresh)
         {
-          c = sequences[j].getCharAt(i);
-
-          if (canonicaliseAa)
-          { // lookup the base aa code symbol
-            c = (char) jalview.schemes.ResidueProperties.aaIndex[sequences[j]
-                    .getCharAt(i)];
-            if (c > 20)
-            {
-              c = '-';
-            }
-            else
-            {
-              // recover canonical aa symbol
-              c = jalview.schemes.ResidueProperties.aa[c].charAt(0);
-            }
-          }
-          else
-          {
-            // original behaviour - operate on ascii symbols directly
-            // No need to check if its a '-'
-            if (c == '.' || c == ' ')
-            {
-              c = '-';
-            }
-
-            if (!canonicaliseAa && 'a' <= c && c <= 'z')
-            {
-              c -= (32); // 32 = 'a' - 'A'
-            }
-          }
-          values[c]++;
+          recordConservation(resultHash, String.valueOf(c));
+        }
+      }
+      if (values.getGapCount() > thresh)
+      {
+        recordConservation(resultHash, "-");
+      }
+
+      if (total.length > 0)
+      {
+        total[column - start] = resultHash;
+      }
+    }
+  }
+
+  /**
+   * Updates the conservation results for an observed residue
+   * 
+   * @param resultMap
+   *          a map of {property, conservation} where conservation value is +1
+   *          (all residues have the property), 0 (no residue has the property)
+   *          or -1 (some do, some don't)
+   * @param res
+   */
+  protected static void recordConservation(Map<String, Integer> resultMap,
+          String res)
+  {
+    res = res.toUpperCase();
+    for (Entry<String, Map<String, Integer>> property : ResidueProperties.propHash
+            .entrySet())
+    {
+      String propertyName = property.getKey();
+      Integer residuePropertyValue = property.getValue().get(res);
+
+      if (!resultMap.containsKey(propertyName))
+      {
+        /*
+         * first time we've seen this residue - note whether it has this property
+         */
+        if (residuePropertyValue != null)
+        {
+          resultMap.put(propertyName, residuePropertyValue);
         }
         else
         {
-          values['-']++;
+          /*
+           * unrecognised residue - use default value for property
+           */
+          resultMap.put(propertyName, property.getValue().get("-"));
         }
       }
-
-      // What is the count threshold to count the residues in residueHash()
-      thresh = (threshold * (jSize)) / 100;
-
-      // loop over all the found residues
-      resultHash = new Hashtable();
-      for (char v = '-'; v < 'Z'; v++)
+      else
       {
-
-        if (values[v] > thresh)
+        Integer currentResult = resultMap.get(propertyName);
+        if (currentResult.intValue() != -1
+                && !currentResult.equals(residuePropertyValue))
         {
-          res = String.valueOf(v);
+          /*
+           * property is unconserved - residues seen both with and without it
+           */
+          resultMap.put(propertyName, Integer.valueOf(-1));
+        }
+      }
+    }
+  }
 
-          // Now loop over the properties
-          enumeration2 = propHash.keys();
+  /**
+   * Counts residues (upper-cased) and gaps in the given column
+   * 
+   * @param column
+   * @return
+   */
+  protected ResidueCount countResidues(int column)
+  {
+    ResidueCount values = new ResidueCount(false);
 
-          while (enumeration2.hasMoreElements())
-          {
-            type = (String) enumeration2.nextElement();
-            ht = (Hashtable) propHash.get(type);
-
-            // Have we ticked this before?
-            if (!resultHash.containsKey(type))
-            {
-              if (ht.containsKey(res))
-              {
-                resultHash.put(type, ht.get(res));
-              }
-              else
-              {
-                resultHash.put(type, ht.get("-"));
-              }
-            }
-            else if (((Integer) resultHash.get(type)).equals(ht.get(res)) == false)
-            {
-              resultHash.put(type, new Integer(-1));
-            }
-          }
+    for (int row = 0; row < sequences.length; row++)
+    {
+      if (sequences[row].getLength() > column)
+      {
+        char c = sequences[row].getCharAt(column);
+        if (canonicaliseAa)
+        {
+          int index = ResidueProperties.aaIndex[c];
+          c = index > 20 ? '-' : ResidueProperties.aa[index].charAt(0);
+        }
+        else
+        {
+          c = toUpperCase(c);
+        }
+        if (Comparison.isGap(c))
+        {
+          values.addGap();
+        }
+        else
+        {
+          values.add(c);
         }
       }
-
-      if (total.length > 0)
+      else
       {
-        total[i - start] = resultHash;
+        values.addGap();
       }
     }
+    return values;
   }
 
-  /*****************************************************************************
-   * count conservation for the j'th column of the alignment
+  /**
+   * Counts conservation and gaps for a column of the alignment
    * 
-   * @return { gap count, conserved residue count}
+   * @return { 1 if fully conserved, else 0, gap count }
    */
-  public int[] countConsNGaps(int j)
+  public int[] countConservationAndGaps(int column)
   {
-    int count = 0;
-    int cons = 0;
-    int nres = 0;
-    int[] r = new int[2];
-    char f = '$';
-    int i, iSize = sequences.length;
-    char c;
+    int gapCount = 0;
+    boolean fullyConserved = true;
+    int iSize = sequences.length;
 
-    for (i = 0; i < iSize; i++)
+    if (iSize == 0)
     {
-      if (j >= sequences[i].getLength())
+      return new int[] { 0, 0 };
+    }
+
+    char lastRes = '0';
+    for (int i = 0; i < iSize; i++)
+    {
+      if (column >= sequences[i].getLength())
       {
-        count++;
+        gapCount++;
         continue;
       }
 
-      c = sequences[i].getCharAt(j); // gaps do not have upper/lower case
+      char c = sequences[i].getCharAt(column); // gaps do not have upper/lower case
 
-      if (jalview.util.Comparison.isGap((c)))
+      if (Comparison.isGap((c)))
       {
-        count++;
+        gapCount++;
       }
       else
       {
-        nres++;
-
-        if (nres == 1)
+        c = toUpperCase(c);
+        if (lastRes == '0')
         {
-          f = c;
-          cons++;
+          lastRes = c;
         }
-        else if (f == c)
+        if (c != lastRes)
         {
-          cons++;
+          fullyConserved = false;
         }
       }
     }
 
-    r[0] = (nres == cons) ? 1 : 0;
-    r[1] = count;
-
+    int[] r = new int[] { fullyConserved ? 1 : 0, gapCount };
     return r;
   }
 
   /**
+   * Returns the upper-cased character if between 'a' and 'z', else the
+   * unchanged value
+   * 
+   * @param c
+   * @return
+   */
+  char toUpperCase(char c)
+  {
+    if ('a' <= c && c <= 'z')
+    {
+      c -= TOUPPERCASE;
+    }
+    return c;
+  }
+
+  /**
    * Calculates the conservation sequence
    * 
-   * @param consflag
-   *          if true, poitiveve conservation; false calculates negative
-   *          conservation
-   * @param percentageGaps
-   *          commonly used value is 25
+   * @param positiveOnly
+   *          if true, calculate positive conservation; else calculate both
+   *          positive and negative conservation
+   * @param maxPercentageGaps
+   *          the percentage of gaps in a column, at or above which no
+   *          conservation is asserted
    */
-  public void verdict(boolean consflag, float percentageGaps)
+  public void verdict(boolean positiveOnly, float maxPercentageGaps)
   {
-    StringBuffer consString = new StringBuffer();
-    String type;
-    Integer result;
-    int[] gapcons;
-    int totGaps, count;
-    float pgaps;
-    Hashtable resultHash;
-    Enumeration enumeration;
+    // TODO call this at the end of calculate(), should not be a public method
+
+    StringBuilder consString = new StringBuilder(end);
 
     // NOTE THIS SHOULD CHECK IF THE CONSEQUENCE ALREADY
     // EXISTS AND NOT OVERWRITE WITH '-', BUT THIS CASE
@@ -376,50 +450,50 @@ public class Conservation
     consSymbs = new String[end - start + 1];
     for (int i = start; i <= end; i++)
     {
-      gapcons = countConsNGaps(i);
-      totGaps = gapcons[1];
-      pgaps = ((float) totGaps * 100) / sequences.length;
-      consSymbs[i - start] = new String();
+      int[] gapcons = countConservationAndGaps(i);
+      boolean fullyConserved = gapcons[0] == 1;
+      int totGaps = gapcons[1];
+      float pgaps = (totGaps * 100f) / sequences.length;
 
-      if (percentageGaps > pgaps)
+      if (maxPercentageGaps > pgaps)
       {
-        resultHash = total[i - start];
-        // Now find the verdict
-        count = 0;
-        enumeration = resultHash.keys();
-
-        while (enumeration.hasMoreElements())
+        Map<String, Integer> resultHash = total[i - start];
+        int count = 0;
+        StringBuilder positives = new StringBuilder(64);
+        StringBuilder negatives = new StringBuilder(32);
+        for (String type : resultHash.keySet())
         {
-          type = (String) enumeration.nextElement();
-          result = (Integer) resultHash.get(type);
-          // Do we want to count +ve conservation or +ve and -ve cons.?
-          if (consflag)
+          int result = resultHash.get(type).intValue();
+          if (result == -1)
+          {
+            /*
+             * not conserved (present or absent)
+             */
+            continue;
+          }
+          count++;
+          if (result == 1)
           {
-            if (result.intValue() == 1)
-            {
-              consSymbs[i - start] = type + " " + consSymbs[i - start];
-              count++;
-            }
+            /*
+             * positively conserved property (all residues have it)
+             */
+            positives.append(positives.length() == 0 ? "" : " ");
+            positives.append(type);
           }
-          else
+          if (result == 0 && !positiveOnly)
           {
-            if (result.intValue() != -1)
-            {
-              {
-                if (result.intValue() == 0)
-                {
-                  consSymbs[i - start] = consSymbs[i - start] + " !" + type;
-                }
-                else
-                {
-                  consSymbs[i - start] = type + " " + consSymbs[i - start];
-                }
-              }
-
-              count++;
-            }
+            /*
+             * absense of property is conserved (all residues lack it)
+             */
+            negatives.append(negatives.length() == 0 ? "" : " ");
+            negatives.append("!").append(type);
           }
         }
+        if (negatives.length() > 0)
+        {
+          positives.append(" ").append(negatives);
+        }
+        consSymbs[i - start] = positives.toString();
 
         if (count < 10)
         {
@@ -427,7 +501,7 @@ public class Conservation
         }
         else
         {
-          consString.append((gapcons[0] == 1) ? "*" : "+");
+          consString.append(fullyConserved ? "*" : "+");
         }
       }
       else
@@ -460,7 +534,7 @@ public class Conservation
    */
   private void percentIdentity2()
   {
-    seqNums = new Vector();
+    seqNums = new Vector<int[]>();
     // calcSeqNum(s);
     int i = 0, iSize = sequences.length;
     // Do we need to calculate this again?
@@ -487,7 +561,7 @@ public class Conservation
 
       while (j < sequences.length)
       {
-        sqnum = (int[]) seqNums.elementAt(j);
+        sqnum = seqNums.elementAt(j);
 
         for (i = 1; i < sqnum.length; i++)
         {
@@ -517,17 +591,17 @@ public class Conservation
   /**
    * Calculates the quality of the set of sequences
    * 
-   * @param start
+   * @param startRes
    *          Start residue
-   * @param end
+   * @param endRes
    *          End residue
    */
-  public void findQuality(int start, int end)
+  public void findQuality(int startRes, int endRes)
   {
-    quality = new Vector();
+    quality = new Vector<Double>();
 
     double max = -10000;
-    int[][] BLOSUM62 = jalview.schemes.ResidueProperties.getBLOSUM62();
+    int[][] BLOSUM62 = ResidueProperties.getBLOSUM62();
 
     // Loop over columns // JBPNote Profiling info
     // long ts = System.currentTimeMillis();
@@ -542,10 +616,10 @@ public class Conservation
 
     for (l = 0; l < size; l++)
     {
-      lengths[l] = ((int[]) seqNums.elementAt(l)).length - 1;
+      lengths[l] = seqNums.elementAt(l).length - 1;
     }
 
-    for (j = start; j <= end; j++)
+    for (j = startRes; j <= endRes; j++)
     {
       bigtot = 0;
 
@@ -569,8 +643,10 @@ public class Conservation
       {
         tot = 0;
         xx = new double[24];
-        seqNum = (j < lengths[k]) ? ((int[]) seqNums.elementAt(k))[j + 1]
-                : 23; // Sequence, or gap at the end
+        seqNum = (j < lengths[k]) ? seqNums.elementAt(k)[j + 1] : 23; // Sequence,
+                                                                      // or gap
+                                                                      // at the
+                                                                      // end
 
         // This is a loop over r
         for (i = 0; i < 23; i++)
@@ -603,9 +679,9 @@ public class Conservation
 
     double newmax = -10000;
 
-    for (j = start; j <= end; j++)
+    for (j = startRes; j <= endRes; j++)
     {
-      tmp = ((Double) quality.elementAt(j)).doubleValue();
+      tmp = quality.elementAt(j).doubleValue();
       tmp = ((max - tmp) * (size - cons2[j][23])) / size;
 
       // System.out.println(tmp+ " " + j);
@@ -618,12 +694,12 @@ public class Conservation
     }
 
     // System.out.println("Quality " + s);
-    qualityRange[0] = new Double(0);
-    qualityRange[1] = new Double(newmax);
+    qualityRange[0] = 0D;
+    qualityRange[1] = newmax;
   }
 
   /**
-   * complete the given consensus and quuality annotation rows. Note: currently
+   * Complete the given consensus and quuality annotation rows. Note: currently
    * this method will enlarge the given annotation row if it is too small,
    * otherwise will leave its length unchanged.
    * 
@@ -661,7 +737,7 @@ public class Conservation
 
     char c;
 
-    if (conservation.annotations != null
+    if (conservation != null && conservation.annotations != null
             && conservation.annotations.length < alWidth)
     {
       conservation.annotations = new Annotation[alWidth];
@@ -669,17 +745,17 @@ public class Conservation
 
     if (quality2 != null)
     {
-      quality2.graphMax = qualityRange[1].floatValue();
+      quality2.graphMax = (float) qualityRange[1];
       if (quality2.annotations != null
               && quality2.annotations.length < alWidth)
       {
         quality2.annotations = new Annotation[alWidth];
       }
-      qmin = qualityRange[0].floatValue();
-      qmax = qualityRange[1].floatValue();
+      qmin = (float) qualityRange[0];
+      qmax = (float) qualityRange[1];
     }
 
-    for (int i = 0; i < alWidth; i++)
+    for (int i = istart; i < alWidth; i++)
     {
       float value = 0;
 
@@ -698,20 +774,23 @@ public class Conservation
         value = 10;
       }
 
-      float vprop = value - min;
-      vprop /= max;
-      int consp = i - start;
-      String conssym = (value > 0 && consp > -1 && consp < consSymbs.length) ? consSymbs[consp]
-              : "";
-      conservation.annotations[i] = new Annotation(String.valueOf(c),
-              conssym, ' ', value, new Color(minR + (maxR * vprop), minG
-                      + (maxG * vprop), minB + (maxB * vprop)));
+      if (conservation != null)
+      {
+        float vprop = value - min;
+        vprop /= max;
+        int consp = i - start;
+        String conssym = (value > 0 && consp > -1 && consp < consSymbs.length) ? consSymbs[consp]
+                : "";
+        conservation.annotations[i] = new Annotation(String.valueOf(c),
+                conssym, ' ', value, new Color(minR + (maxR * vprop), minG
+                        + (maxG * vprop), minB + (maxB * vprop)));
+      }
 
       // Quality calc
       if (quality2 != null)
       {
-        value = ((Double) quality.elementAt(i)).floatValue();
-        vprop = value - qmin;
+        value = quality.elementAt(i).floatValue();
+        float vprop = value - qmin;
         vprop /= qmax;
         quality2.annotations[i] = new Annotation(" ",
                 String.valueOf(value), ' ', value, new Color(minR
@@ -726,49 +805,27 @@ public class Conservation
    * 
    * @param name
    *          - name of conservation
-   * @param consHash
-   *          - hash table of properties for each amino acid (normally
-   *          ResidueProperties.propHash)
-   * @param threshold
-   *          - minimum number of conserved residues needed to indicate
-   *          conservation (typically 3)
    * @param seqs
    * @param start
    *          first column in calculation window
    * @param end
    *          last column in calculation window
-   * @param posOrNeg
-   *          positive (true) or negative (false) conservation
-   * @param consPercGaps
+   * @param positiveOnly
+   *          calculate positive (true) or positive and negative (false)
+   *          conservation
+   * @param maxPercentGaps
    *          percentage of gaps tolerated in column
    * @param calcQuality
    *          flag indicating if alignment quality should be calculated
    * @return Conservation object ready for use in visualization
    */
   public static Conservation calculateConservation(String name,
-          Hashtable consHash, int threshold, List<SequenceI> seqs,
-          int start, int end, boolean posOrNeg, int consPercGaps,
-          boolean calcQuality)
-  {
-    Conservation cons = new Conservation(name, consHash, threshold, seqs,
-            start, end);
-    return calculateConservation(cons, posOrNeg, consPercGaps, calcQuality);
-  }
-
-  /**
-   * @param b
-   *          positive (true) or negative (false) conservation
-   * @param consPercGaps
-   *          percentage of gaps tolerated in column
-   * @param calcQuality
-   *          flag indicating if alignment quality should be calculated
-   * @return Conservation object ready for use in visualization
-   */
-  public static Conservation calculateConservation(Conservation cons,
-          boolean b, int consPercGaps, boolean calcQuality)
+          List<SequenceI> seqs, int start, int end, boolean positiveOnly,
+          int maxPercentGaps, boolean calcQuality)
   {
+    Conservation cons = new Conservation(name, seqs, start, end);
     cons.calculate();
-    cons.verdict(b, consPercGaps);
+    cons.verdict(positiveOnly, maxPercentGaps);
 
     if (calcQuality)
     {
@@ -777,4 +834,24 @@ public class Conservation
 
     return cons;
   }
+
+  /**
+   * Returns the computed tooltip (annotation description) for a given column.
+   * The tip is empty if the conservation score is zero, otherwise holds the
+   * conserved properties (and, optionally, properties whose absence is
+   * conserved).
+   * 
+   * @param column
+   * @return
+   */
+  String getTooltip(int column)
+  {
+    char[] sequence = getConsSequence().getSequence();
+    char val = column < sequence.length ? sequence[column] : '-';
+    boolean hasConservation = val != '-' && val != '0';
+    int consp = column - start;
+    String tip = (hasConservation && consp > -1 && consp < consSymbs.length) ? consSymbs[consp]
+            : "";
+    return tip;
+  }
 }
diff --git a/src/jalview/analysis/CrossRef.java b/src/jalview/analysis/CrossRef.java
index 83317c1..fd42684 100644
--- a/src/jalview/analysis/CrossRef.java
+++ b/src/jalview/analysis/CrossRef.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -25,374 +25,915 @@ import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
+import jalview.datamodel.Mapping;
 import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.util.DBRefUtils;
-import jalview.ws.SequenceFetcher;
+import jalview.util.MapList;
+import jalview.ws.SequenceFetcherFactory;
 import jalview.ws.seqfetcher.ASequenceFetcher;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
-import java.util.Vector;
 
 /**
- * Functions for cross-referencing sequence databases. user must first specify
- * if cross-referencing from protein or dna (set dna==true)
+ * Functions for cross-referencing sequence databases.
  * 
  * @author JimP
  * 
  */
 public class CrossRef
 {
+  /*
+   * the dataset of the alignment for which we are searching for 
+   * cross-references; in some cases we may resolve xrefs by 
+   * searching in the dataset
+   */
+  private AlignmentI dataset;
+
+  /*
+   * the sequences for which we are seeking cross-references
+   */
+  private SequenceI[] fromSeqs;
+
   /**
-   * Select just the DNA or protein references for a protein or dna sequence
-   * 
-   * @param fromDna
-   *          if true, select references from DNA (i.e. Protein databases), else
-   *          DNA database references
-   * @param refs
-   *          a set of references to select from
-   * @return
+   * matcher built from dataset
    */
-  public static DBRefEntry[] findXDbRefs(boolean fromDna, DBRefEntry[] refs)
-  {
-    return DBRefUtils.selectRefs(refs, fromDna ? DBRefSource.PROTEINDBS
-            : DBRefSource.DNACODINGDBS);
-    // could attempt to find other cross
-    // refs here - ie PDB xrefs
-    // (not dna, not protein seq)
-  }
+  SequenceIdMatcher matcher;
 
   /**
-   * @param dna
-   *          true if seqs are DNA seqs
+   * sequences found by cross-ref searches to fromSeqs
+   */
+  List<SequenceI> rseqs;
+
+  /**
+   * Constructor
+   * 
    * @param seqs
-   * @return a list of sequence database cross reference source types
+   *          the sequences for which we are seeking cross-references
+   * @param ds
+   *          the containing alignment dataset (may be searched to resolve
+   *          cross-references)
    */
-  public static String[] findSequenceXrefTypes(boolean dna, SequenceI[] seqs)
+  public CrossRef(SequenceI[] seqs, AlignmentI ds)
   {
-    return findSequenceXrefTypes(dna, seqs, null);
+    fromSeqs = seqs;
+    dataset = ds.getDataset() == null ? ds : ds.getDataset();
   }
 
   /**
-   * Indirect references are references from other sequences from the dataset to
-   * any of the direct DBRefEntrys on the given sequences.
+   * Returns a list of distinct database sources for which sequences have either
+   * <ul>
+   * <li>a (dna-to-protein or protein-to-dna) cross-reference</li>
+   * <li>an indirect cross-reference - a (dna-to-protein or protein-to-dna)
+   * reference from another sequence in the dataset which has a cross-reference
+   * to a direct DBRefEntry on the given sequence</li>
+   * </ul>
    * 
    * @param dna
-   *          true if seqs are DNA seqs
-   * @param seqs
-   * @return a list of sequence database cross reference source types
+   *          - when true, cross-references *from* dna returned. When false,
+   *          cross-references *from* protein are returned
+   * @return
    */
-  public static String[] findSequenceXrefTypes(boolean dna,
-          SequenceI[] seqs, AlignmentI dataset)
+  public List<String> findXrefSourcesForSequences(boolean dna)
   {
-    String[] dbrefs = null;
-    List<String> refs = new ArrayList<String>();
-    for (int s = 0; s < seqs.length; s++)
+    List<String> sources = new ArrayList<String>();
+    for (SequenceI seq : fromSeqs)
     {
-      if (seqs[s] != null)
+      if (seq != null)
       {
-        SequenceI dss = seqs[s];
-        while (dss.getDatasetSequence() != null)
-        {
-          dss = dss.getDatasetSequence();
-        }
-        DBRefEntry[] rfs = findXDbRefs(dna, dss.getDBRef());
-        for (int r = 0; rfs != null && r < rfs.length; r++)
-        {
-          if (!refs.contains(rfs[r].getSource()))
-          {
-            refs.add(rfs[r].getSource());
-          }
-        }
-        if (dataset != null)
-        {
-          // search for references to this sequence's direct references.
-          DBRefEntry[] lrfs = CrossRef
-                  .findXDbRefs(!dna, seqs[s].getDBRef());
-          List<SequenceI> rseqs = new ArrayList<SequenceI>();
-          CrossRef.searchDatasetXrefs(seqs[s], !dna, lrfs, dataset, rseqs,
-                  null); // don't need to specify codon frame for mapping here
-          for (SequenceI rs : rseqs)
-          {
-            DBRefEntry[] xrs = findXDbRefs(dna, rs.getDBRef()); // not used??
-            for (int r = 0; rfs != null && r < rfs.length; r++)
-            {
-              if (!refs.contains(rfs[r].getSource()))
-              {
-                refs.add(rfs[r].getSource());
-              }
-            }
-          }
-        }
+        findXrefSourcesForSequence(seq, dna, sources);
       }
     }
-    if (refs.size() > 0)
+    sources.remove(DBRefSource.EMBL); // hack to prevent EMBL xrefs resulting in
+                                      // redundant datasets
+    if (dna)
     {
-      dbrefs = new String[refs.size()];
-      refs.toArray(dbrefs);
+      sources.remove(DBRefSource.ENSEMBL); // hack to prevent Ensembl and
+                                           // EnsemblGenomes xref option shown
+                                           // from cdna panel
+      sources.remove(DBRefSource.ENSEMBLGENOMES);
     }
-    return dbrefs;
+    // redundant datasets
+    return sources;
   }
 
-  /*
-   * if (dna) { if (rfs[r].hasMap()) { // most likely this is a protein cross
-   * reference if (!refs.contains(rfs[r].getSource())) {
-   * refs.addElement(rfs[r].getSource()); } } }
+  /**
+   * Returns a list of distinct database sources for which a sequence has either
+   * <ul>
+   * <li>a (dna-to-protein or protein-to-dna) cross-reference</li>
+   * <li>an indirect cross-reference - a (dna-to-protein or protein-to-dna)
+   * reference from another sequence in the dataset which has a cross-reference
+   * to a direct DBRefEntry on the given sequence</li>
+   * </ul>
+   * 
+   * @param seq
+   *          the sequence whose dbrefs we are searching against
+   * @param fromDna
+   *          when true, context is DNA - so sources identifying protein
+   *          products will be returned.
+   * @param sources
+   *          a list of sources to add matches to
    */
-  public static boolean hasCdnaMap(SequenceI[] seqs)
+  void findXrefSourcesForSequence(SequenceI seq, boolean fromDna,
+          List<String> sources)
   {
-    String[] reftypes = findSequenceXrefTypes(false, seqs);
-    for (int s = 0; s < reftypes.length; s++)
+    /*
+     * first find seq's xrefs (dna-to-peptide or peptide-to-dna)
+     */
+    DBRefEntry[] rfs = DBRefUtils.selectDbRefs(!fromDna, seq.getDBRefs());
+    addXrefsToSources(rfs, sources);
+    if (dataset != null)
     {
-      if (reftypes.equals(DBRefSource.EMBLCDS))
+      /*
+       * find sequence's direct (dna-to-dna, peptide-to-peptide) xrefs
+       */
+      DBRefEntry[] lrfs = DBRefUtils.selectDbRefs(fromDna, seq.getDBRefs());
+      List<SequenceI> foundSeqs = new ArrayList<SequenceI>();
+
+      /*
+       * find sequences in the alignment which xref one of these DBRefs
+       * i.e. is xref-ed to a common sequence identifier
+       */
+      searchDatasetXrefs(fromDna, seq, lrfs, foundSeqs, null);
+
+      /*
+       * add those sequences' (dna-to-peptide or peptide-to-dna) dbref sources
+       */
+      for (SequenceI rs : foundSeqs)
       {
-        return true;
-        // no map
+        DBRefEntry[] xrs = DBRefUtils
+                .selectDbRefs(!fromDna, rs.getDBRefs());
+        addXrefsToSources(xrs, sources);
       }
     }
-    return false;
   }
 
-  public static SequenceI[] getCdnaMap(SequenceI[] seqs)
+  /**
+   * Helper method that adds the source identifiers of some cross-references to
+   * a (non-redundant) list of database sources
+   * 
+   * @param xrefs
+   * @param sources
+   */
+  void addXrefsToSources(DBRefEntry[] xrefs, List<String> sources)
   {
-    Vector cseqs = new Vector();
-    for (int s = 0; s < seqs.length; s++)
+    if (xrefs != null)
     {
-      DBRefEntry[] cdna = findXDbRefs(true, seqs[s].getDBRef());
-      for (int c = 0; c < cdna.length; c++)
+      for (DBRefEntry ref : xrefs)
       {
-        if (cdna[c].getSource().equals(DBRefSource.EMBLCDS))
+        /*
+         * avoid duplication e.g. ENSEMBL and Ensembl
+         */
+        String source = DBRefUtils.getCanonicalName(ref.getSource());
+        if (!sources.contains(source))
         {
-          System.err
-                  .println("TODO: unimplemented sequence retrieval for coding region sequence.");
-          // TODO: retrieve CDS dataset sequences
-          // need global dataset sequence retriever/resolver to reuse refs
-          // and construct Mapping entry.
-          // insert gaps in CDS according to peptide gaps.
-          // add gapped sequence to cseqs
+          sources.add(source);
         }
       }
     }
-    if (cseqs.size() > 0)
-    {
-      SequenceI[] rsqs = new SequenceI[cseqs.size()];
-      cseqs.copyInto(rsqs);
-      return rsqs;
-    }
-    return null;
-
-  }
-
-  /**
-   * 
-   * @param dna
-   * @param seqs
-   * @return
-   */
-  public static Alignment findXrefSequences(SequenceI[] seqs, boolean dna,
-          String source)
-  {
-    return findXrefSequences(seqs, dna, source, null);
   }
 
   /**
+   * Attempts to find cross-references from the sequences provided in the
+   * constructor to the given source database. Cross-references may be found
+   * <ul>
+   * <li>in dbrefs on the sequence which hold a mapping to a sequence
+   * <ul>
+   * <li>provided with a fetched sequence (e.g. ENA translation), or</li>
+   * <li>populated previously after getting cross-references</li>
+   * </ul>
+   * <li>as other sequences in the alignment which share a dbref identifier with
+   * the sequence</li>
+   * <li>by fetching from the remote database</li>
+   * </ul>
+   * The cross-referenced sequences, and mappings to them, are added to the
+   * alignment dataset.
    * 
-   * @param seqs
-   * @param dna
    * @param source
-   * @param dataset
-   *          alignment to search for product sequences.
-   * @return products (as dataset sequences)
+   * @return cross-referenced sequences (as dataset sequences)
    */
-  public static Alignment findXrefSequences(SequenceI[] seqs, boolean dna,
-          String source, AlignmentI dataset)
+  public Alignment findXrefSequences(String source, boolean fromDna)
   {
-    List<SequenceI> rseqs = new ArrayList<SequenceI>();
-    Alignment ral = null;
-    AlignedCodonFrame cf = new AlignedCodonFrame(); // nominal width
-    for (int s = 0; s < seqs.length; s++)
+
+    rseqs = new ArrayList<SequenceI>();
+    AlignedCodonFrame cf = new AlignedCodonFrame();
+    matcher = new SequenceIdMatcher(dataset.getSequences());
+
+    for (SequenceI seq : fromSeqs)
     {
-      SequenceI dss = seqs[s];
+      SequenceI dss = seq;
       while (dss.getDatasetSequence() != null)
       {
         dss = dss.getDatasetSequence();
       }
       boolean found = false;
-      DBRefEntry[] xrfs = CrossRef.findXDbRefs(dna, dss.getDBRef());
+      DBRefEntry[] xrfs = DBRefUtils
+              .selectDbRefs(!fromDna, dss.getDBRefs());
+      // ENST & ENSP comes in to both Protein and nucleotide, so we need to
+      // filter them
+      // out later.
       if ((xrfs == null || xrfs.length == 0) && dataset != null)
       {
-        System.out.println("Attempting to find ds Xrefs refs.");
-        DBRefEntry[] lrfs = CrossRef.findXDbRefs(!dna, seqs[s].getDBRef());
-        // less ambiguous would be a 'find primary dbRefEntry' method.
-        // filter for desired source xref here
-        found = CrossRef.searchDatasetXrefs(dss, !dna, lrfs, dataset,
-                rseqs, cf);
+        /*
+         * found no suitable dbrefs on sequence - look for sequences in the
+         * alignment which share a dbref with this one
+         */
+        DBRefEntry[] lrfs = DBRefUtils.selectDbRefs(fromDna,
+                seq.getDBRefs());
+
+        /*
+         * find sequences (except this one!), of complementary type,
+         *  which have a dbref to an accession id for this sequence,
+         *  and add them to the results
+         */
+        found = searchDatasetXrefs(fromDna, dss, lrfs, rseqs, cf);
       }
-      for (int r = 0; xrfs != null && r < xrfs.length; r++)
+      if (xrfs == null && !found)
       {
-        if (source != null && !source.equals(xrfs[r].getSource()))
-        {
-          continue;
-        }
-        if (xrfs[r].hasMap())
+        /*
+         * no dbref to source on this sequence or matched
+         * complementary sequence in the dataset 
+         */
+        continue;
+      }
+      List<DBRefEntry> sourceRefs = DBRefUtils.searchRefsForSource(xrfs,
+              source);
+      Iterator<DBRefEntry> refIterator = sourceRefs.iterator();
+      // At this point, if we are retrieving Ensembl, we still don't filter out
+      // ENST when looking for protein crossrefs.
+      while (refIterator.hasNext())
+      {
+        DBRefEntry xref = refIterator.next();
+        found = false;
+        // we're only interested in coding cross-references, not
+        // locus->transcript
+        if (xref.hasMap() && xref.getMap().getMap().isTripletMap())
         {
-          if (xrfs[r].getMap().getTo() != null)
+          SequenceI mappedTo = xref.getMap().getTo();
+          if (mappedTo != null)
           {
-            SequenceI rsq = new Sequence(xrfs[r].getMap().getTo());
+            /*
+             * dbref contains the sequence it maps to; add it to the
+             * results unless we have done so already (could happen if 
+             * fetching xrefs for sequences which have xrefs in common)
+             * for example: UNIPROT {P0CE19, P0CE20} -> EMBL {J03321, X06707}
+             */
+            found = true;
+            /*
+             * problem: matcher.findIdMatch() is lenient - returns a sequence
+             * with a dbref to the search arg e.g. ENST for ENSP - wrong
+             * but findInDataset() matches ENSP when looking for Uniprot...
+             */
+            SequenceI matchInDataset = findInDataset(xref);
+            if (matchInDataset != null && xref.getMap().getTo() != null
+                    && matchInDataset != xref.getMap().getTo())
+            {
+              System.err
+                      .println("Implementation problem (reopen JAL-2154): CrossRef.findInDataset seems to have recovered a different sequence than the one explicitly mapped for xref."
+                              + "Found:"
+                              + matchInDataset
+                              + "\nExpected:"
+                              + xref.getMap().getTo()
+                              + "\nFor xref:"
+                              + xref);
+            }
+            /*matcher.findIdMatch(mappedTo);*/
+            if (matchInDataset != null)
+            {
+              if (!rseqs.contains(matchInDataset))
+              {
+                rseqs.add(matchInDataset);
+              }
+              // even if rseqs contained matchInDataset - check mappings between
+              // these seqs are added
+              // need to try harder to only add unique mappings
+              if (xref.getMap().getMap().isTripletMap()
+                      && dataset.getMapping(seq, matchInDataset) == null
+                      && cf.getMappingBetween(seq, matchInDataset) == null)
+              {
+                // materialise a mapping for highlighting between these
+                // sequences
+                if (fromDna)
+                {
+                  cf.addMap(dss, matchInDataset, xref.getMap().getMap(),
+                          xref.getMap().getMappedFromId());
+                }
+                else
+                {
+                  cf.addMap(matchInDataset, dss, xref.getMap().getMap()
+                          .getInverse(), xref.getMap().getMappedFromId());
+                }
+              }
+
+              refIterator.remove();
+              continue;
+            }
+            // TODO: need to determine if this should be a deriveSequence
+            SequenceI rsq = new Sequence(mappedTo);
             rseqs.add(rsq);
-            if (xrfs[r].getMap().getMap().getFromRatio() != xrfs[r]
-                    .getMap().getMap().getToRatio())
+            if (xref.getMap().getMap().isTripletMap())
             {
               // get sense of map correct for adding to product alignment.
-              if (dna)
+              if (fromDna)
               {
                 // map is from dna seq to a protein product
-                cf.addMap(dss, rsq, xrfs[r].getMap().getMap());
+                cf.addMap(dss, rsq, xref.getMap().getMap(), xref.getMap()
+                        .getMappedFromId());
               }
               else
               {
                 // map should be from protein seq to its coding dna
-                cf.addMap(rsq, dss, xrfs[r].getMap().getMap().getInverse());
+                cf.addMap(rsq, dss, xref.getMap().getMap().getInverse(),
+                        xref.getMap().getMappedFromId());
               }
             }
-            found = true;
           }
         }
+
         if (!found)
         {
-          // do a bit more work - search for sequences with references matching
-          // xrefs on this sequence.
-          if (dataset != null)
+          SequenceI matchedSeq = matcher.findIdMatch(xref.getSource() + "|"
+                  + xref.getAccessionId());
+          // if there was a match, check it's at least the right type of
+          // molecule!
+          if (matchedSeq != null && matchedSeq.isProtein() == fromDna)
           {
-            found |= searchDataset(dss, xrfs[r], dataset, rseqs, cf); // ,false,!dna);
-            if (found)
+            if (constructMapping(seq, matchedSeq, xref, cf, fromDna))
             {
-              xrfs[r] = null; // we've recovered seqs for this one.
+              found = true;
             }
           }
         }
+
+        if (!found)
+        {
+          // do a bit more work - search for sequences with references matching
+          // xrefs on this sequence.
+          found = searchDataset(fromDna, dss, xref, rseqs, cf, false);
+        }
+        if (found)
+        {
+          refIterator.remove();
+        }
+      }
+
+      /*
+       * fetch from source database any dbrefs we haven't resolved up to here
+       */
+      if (!sourceRefs.isEmpty())
+      {
+        retrieveCrossRef(sourceRefs, seq, xrfs, fromDna, cf);
+      }
+    }
+
+    Alignment ral = null;
+    if (rseqs.size() > 0)
+    {
+      ral = new Alignment(rseqs.toArray(new SequenceI[rseqs.size()]));
+      if (!cf.isEmpty())
+      {
+        dataset.addCodonFrame(cf);
       }
-      if (!found)
+    }
+    return ral;
+  }
+
+  private void retrieveCrossRef(List<DBRefEntry> sourceRefs, SequenceI seq,
+          DBRefEntry[] xrfs, boolean fromDna, AlignedCodonFrame cf)
+  {
+    ASequenceFetcher sftch = SequenceFetcherFactory.getSequenceFetcher();
+    SequenceI[] retrieved = null;
+    SequenceI dss = seq.getDatasetSequence() == null ? seq : seq
+            .getDatasetSequence();
+    // first filter in case we are retrieving crossrefs that have already been
+    // retrieved. this happens for cases where a database record doesn't yield
+    // protein products for CDS
+    removeAlreadyRetrievedSeqs(sourceRefs, fromDna);
+    if (sourceRefs.size() == 0)
+    {
+      // no more work to do! We already had all requested sequence records in
+      // the dataset.
+      return;
+    }
+    try
+    {
+      retrieved = sftch.getSequences(sourceRefs, !fromDna);
+    } catch (Exception e)
+    {
+      System.err
+              .println("Problem whilst retrieving cross references for Sequence : "
+                      + seq.getName());
+      e.printStackTrace();
+    }
+
+    if (retrieved != null)
+    {
+      boolean addedXref = false;
+      List<SequenceI> newDsSeqs = new ArrayList<SequenceI>(), doNotAdd = new ArrayList<SequenceI>();
+
+      for (SequenceI retrievedSequence : retrieved)
+      {
+        // dataset gets contaminated ccwith non-ds sequences. why ??!
+        // try: Ensembl -> Nuc->Ensembl, Nuc->Uniprot-->Protein->EMBL->
+        SequenceI retrievedDss = retrievedSequence.getDatasetSequence() == null ? retrievedSequence
+                : retrievedSequence.getDatasetSequence();
+        addedXref |= importCrossRefSeq(cf, newDsSeqs, doNotAdd, dss,
+                retrievedDss);
+      }
+      if (!addedXref)
       {
-        if (xrfs != null && xrfs.length > 0)
+        // try again, after looking for matching IDs
+        // shouldn't need to do this unless the dbref mechanism has broken.
+        updateDbrefMappings(seq, xrfs, retrieved, cf, fromDna);
+        for (SequenceI retrievedSequence : retrieved)
         {
-          // Try and get the sequence reference...
-          /*
-           * Ideal world - we ask for a sequence fetcher implementation here if
-           * (jalview.io.RunTimeEnvironment.getSequenceFetcher()) (
-           */
-          ASequenceFetcher sftch = new SequenceFetcher();
-          SequenceI[] retrieved = null;
-          int l = xrfs.length;
-          for (int r = 0; r < xrfs.length; r++)
+          // dataset gets contaminated ccwith non-ds sequences. why ??!
+          // try: Ensembl -> Nuc->Ensembl, Nuc->Uniprot-->Protein->EMBL->
+          SequenceI retrievedDss = retrievedSequence.getDatasetSequence() == null ? retrievedSequence
+                  : retrievedSequence.getDatasetSequence();
+          addedXref |= importCrossRefSeq(cf, newDsSeqs, doNotAdd, dss,
+                  retrievedDss);
+        }
+      }
+      for (SequenceI newToSeq : newDsSeqs)
+      {
+        if (!doNotAdd.contains(newToSeq)
+                && dataset.findIndex(newToSeq) == -1)
+        {
+          dataset.addSequence(newToSeq);
+          matcher.add(newToSeq);
+        }
+      }
+    }
+  }
+
+  /**
+   * Search dataset for sequences with a primary reference contained in
+   * sourceRefs.
+   * 
+   * @param sourceRefs
+   *          - list of references to filter.
+   * @param fromDna
+   *          - type of sequence to search for matching primary reference.
+   */
+  private void removeAlreadyRetrievedSeqs(List<DBRefEntry> sourceRefs,
+          boolean fromDna)
+  {
+    DBRefEntry[] dbrSourceSet = sourceRefs.toArray(new DBRefEntry[0]);
+    for (SequenceI sq : dataset.getSequences())
+    {
+      boolean dupeFound = false;
+      // !fromDna means we are looking only for nucleotide sequences, not
+      // protein
+      if (sq.isProtein() == fromDna)
+      {
+        for (DBRefEntry dbr : sq.getPrimaryDBRefs())
+        {
+          for (DBRefEntry found : DBRefUtils.searchRefs(dbrSourceSet, dbr))
           {
-            // filter out any irrelevant or irretrievable references
-            if (xrfs[r] == null
-                    || ((source != null && !source.equals(xrfs[r]
-                            .getSource())) || !sftch.isFetchable(xrfs[r]
-                            .getSource())))
-            {
-              l--;
-              xrfs[r] = null;
-            }
+            sourceRefs.remove(found);
+            dupeFound = true;
           }
-          if (l > 0)
+        }
+      }
+      if (dupeFound)
+      {
+        // rebuild the search array from the filtered sourceRefs list
+        dbrSourceSet = sourceRefs.toArray(new DBRefEntry[0]);
+      }
+    }
+  }
+
+  /**
+   * process sequence retrieved via a dbref on source sequence to resolve and
+   * transfer data
+   * 
+   * @param cf
+   * @param sourceSequence
+   * @param retrievedSequence
+   * @return true if retrieveSequence was imported
+   */
+  private boolean importCrossRefSeq(AlignedCodonFrame cf,
+          List<SequenceI> newDsSeqs, List<SequenceI> doNotAdd,
+          SequenceI sourceSequence, SequenceI retrievedSequence)
+  {
+    /**
+     * set when retrievedSequence has been verified as a crossreference for
+     * sourceSequence
+     */
+    boolean imported = false;
+    DBRefEntry[] dbr = retrievedSequence.getDBRefs();
+    if (dbr != null)
+    {
+      for (DBRefEntry dbref : dbr)
+      {
+        SequenceI matched = findInDataset(dbref);
+        if (matched == sourceSequence)
+        {
+          // verified retrieved and source sequence cross-reference each other
+          imported = true;
+        }
+        // find any entry where we should put in the sequence being
+        // cross-referenced into the map
+        Mapping map = dbref.getMap();
+        if (map != null)
+        {
+          if (map.getTo() != null && map.getMap() != null)
           {
-            System.out
-                    .println("Attempting to retrieve cross referenced sequences.");
-            DBRefEntry[] t = new DBRefEntry[l];
-            l = 0;
-            for (int r = 0; r < xrfs.length; r++)
+            if (map.getTo() == sourceSequence)
             {
-              if (xrfs[r] != null)
-              {
-                t[l++] = xrfs[r];
-              }
+              // already called to import once, and most likely this sequence
+              // already imported !
+              continue;
             }
-            xrfs = t;
-            try
-            {
-              retrieved = sftch.getSequences(xrfs); // problem here is we don't
-              // know which of xrfs
-              // resulted in which
-              // retrieved element
-            } catch (Exception e)
+            if (matched == null)
             {
-              System.err
-                      .println("Problem whilst retrieving cross references for Sequence : "
-                              + seqs[s].getName());
-              e.printStackTrace();
+              /*
+               * sequence is new to dataset, so save a reference so it can be added. 
+               */
+              newDsSeqs.add(map.getTo());
+              continue;
             }
-            if (retrieved != null)
+
+            /*
+             * there was a matching sequence in dataset, so now, check to see if we can update the map.getTo() sequence to the existing one.
+             */
+
+            try
             {
-              for (int rs = 0; rs < retrieved.length; rs++)
+              // compare ms with dss and replace with dss in mapping
+              // if map is congruent
+              SequenceI ms = map.getTo();
+              // TODO findInDataset requires exact sequence match but
+              // 'congruent' test is only for the mapped part
+              // maybe not a problem in practice since only ENA provide a
+              // mapping and it is to the full protein translation of CDS
+              // matcher.findIdMatch(map.getTo());
+              // TODO addendum: if matched is shorter than getTo, this will fail
+              // - when it should really succeed.
+              int sf = map.getMap().getToLowest();
+              int st = map.getMap().getToHighest();
+              SequenceI mappedrg = ms.getSubSequence(sf, st);
+              if (mappedrg.getLength() > 0
+                      && ms.getSequenceAsString().equals(
+                              matched.getSequenceAsString()))
               {
-                // TODO: examine each sequence for 'redundancy'
-                jalview.datamodel.DBRefEntry[] dbr = retrieved[rs]
-                        .getDBRef();
-                if (dbr != null && dbr.length > 0)
+                /*
+                 * sequences were a match, 
+                 */
+                String msg = "Mapping updated from " + ms.getName()
+                        + " to retrieved crossreference "
+                        + matched.getName();
+                System.out.println(msg);
+
+                DBRefEntry[] toRefs = map.getTo().getDBRefs();
+                if (toRefs != null)
+                {
+                  /*
+                   * transfer database refs
+                   */
+                  for (DBRefEntry ref : toRefs)
+                  {
+                    if (dbref.getSrcAccString().equals(
+                            ref.getSrcAccString()))
+                    {
+                      continue; // avoid overwriting the ref on source sequence
+                    }
+                    matched.addDBRef(ref); // add or update mapping
+                  }
+                }
+                doNotAdd.add(map.getTo());
+                map.setTo(matched);
+
+                /*
+                 * give the reverse reference the inverse mapping 
+                 * (if it doesn't have one already)
+                 */
+                setReverseMapping(matched, dbref, cf);
+
+                /*
+                 * copy sequence features as well, avoiding
+                 * duplication (e.g. same variation from two 
+                 * transcripts)
+                 */
+                SequenceFeature[] sfs = ms.getSequenceFeatures();
+                if (sfs != null)
                 {
-                  for (int di = 0; di < dbr.length; di++)
+                  for (SequenceFeature feat : sfs)
                   {
-                    // find any entry where we should put in the sequence being
-                    // cross-referenced into the map
-                    jalview.datamodel.Mapping map = dbr[di].getMap();
-                    if (map != null)
+                    /*
+                     * make a flyweight feature object which ignores Parent
+                     * attribute in equality test; this avoids creating many
+                     * otherwise duplicate exon features on genomic sequence
+                     */
+                    SequenceFeature newFeature = new SequenceFeature(feat)
                     {
-                      if (map.getTo() != null && map.getMap() != null)
+                      @Override
+                      public boolean equals(Object o)
                       {
-                        // should search the local dataset to find any existing
-                        // candidates for To !
-                        try
-                        {
-                          // compare ms with dss and replace with dss in mapping
-                          // if map is congruent
-                          SequenceI ms = map.getTo();
-                          int sf = map.getMap().getToLowest();
-                          int st = map.getMap().getToHighest();
-                          SequenceI mappedrg = ms.getSubSequence(sf, st);
-                          SequenceI loc = dss.getSubSequence(sf, st);
-                          if (mappedrg.getLength() > 0
-                                  && mappedrg.getSequenceAsString().equals(
-                                          loc.getSequenceAsString()))
-                          {
-                            System.err
-                                    .println("Mapping updated for retrieved crossreference");
-                            // method to update all refs of existing To on
-                            // retrieved sequence with dss and merge any props
-                            // on To onto dss.
-                            map.setTo(dss);
-                          }
-                        } catch (Exception e)
-                        {
-                          System.err
-                                  .println("Exception when consolidating Mapped sequence set...");
-                          e.printStackTrace(System.err);
-                        }
+                        return super.equals(o, true);
                       }
-                    }
+                    };
+                    matched.addSequenceFeature(newFeature);
                   }
                 }
-                retrieved[rs].updatePDBIds();
-                rseqs.add(retrieved[rs]);
+
               }
+              cf.addMap(retrievedSequence, map.getTo(), map.getMap());
+            } catch (Exception e)
+            {
+              System.err
+                      .println("Exception when consolidating Mapped sequence set...");
+              e.printStackTrace(System.err);
             }
           }
         }
       }
     }
-    if (rseqs.size() > 0)
+    if (imported)
     {
-      SequenceI[] rsqs = new SequenceI[rseqs.size()];
-      rseqs.toArray(rsqs);
-      ral = new Alignment(rsqs);
-      if (cf != null && cf.getProtMappings() != null)
+      retrievedSequence.updatePDBIds();
+      rseqs.add(retrievedSequence);
+      if (dataset.findIndex(retrievedSequence) == -1)
       {
-        ral.addCodonFrame(cf);
+        dataset.addSequence(retrievedSequence);
+        matcher.add(retrievedSequence);
       }
     }
-    return ral;
+    return imported;
+  }
+
+  /**
+   * Sets the inverse sequence mapping in the corresponding dbref of the mapped
+   * to sequence (if any). This is used after fetching a cross-referenced
+   * sequence, if the fetched sequence has a mapping to the original sequence,
+   * to set the mapping in the original sequence's dbref.
+   * 
+   * @param mapFrom
+   *          the sequence mapped from
+   * @param dbref
+   * @param mappings
+   */
+  void setReverseMapping(SequenceI mapFrom, DBRefEntry dbref,
+          AlignedCodonFrame mappings)
+  {
+    SequenceI mapTo = dbref.getMap().getTo();
+    if (mapTo == null)
+    {
+      return;
+    }
+    DBRefEntry[] dbrefs = mapTo.getDBRefs();
+    if (dbrefs == null)
+    {
+      return;
+    }
+    for (DBRefEntry toRef : dbrefs)
+    {
+      if (toRef.hasMap() && mapFrom == toRef.getMap().getTo())
+      {
+        /*
+         * found the reverse dbref; update its mapping if null
+         */
+        if (toRef.getMap().getMap() == null)
+        {
+          MapList inverse = dbref.getMap().getMap().getInverse();
+          toRef.getMap().setMap(inverse);
+          mappings.addMap(mapTo, mapFrom, inverse);
+        }
+      }
+    }
+  }
+
+  /**
+   * Returns null or the first sequence in the dataset which is identical to
+   * xref.mapTo, and has a) a primary dbref matching xref, or if none found, the
+   * first one with an ID source|xrefacc
+   * 
+   * @param xref
+   *          with map and mapped-to sequence
+   * @return
+   */
+  SequenceI findInDataset(DBRefEntry xref)
+  {
+    if (xref == null || !xref.hasMap() || xref.getMap().getTo() == null)
+    {
+      return null;
+    }
+    SequenceI mapsTo = xref.getMap().getTo();
+    String name = xref.getAccessionId();
+    String name2 = xref.getSource() + "|" + name;
+    SequenceI dss = mapsTo.getDatasetSequence() == null ? mapsTo : mapsTo
+            .getDatasetSequence();
+    // first check ds if ds is directly referenced
+    if (dataset.findIndex(dss) > -1)
+    {
+      return dss;
+    }
+    DBRefEntry template = new DBRefEntry(xref.getSource(), null,
+            xref.getAccessionId());
+    /**
+     * remember the first ID match - in case we don't find a match to template
+     */
+    SequenceI firstIdMatch = null;
+    for (SequenceI seq : dataset.getSequences())
+    {
+      // first check primary refs.
+      List<DBRefEntry> match = DBRefUtils.searchRefs(seq.getPrimaryDBRefs()
+              .toArray(new DBRefEntry[0]), template);
+      if (match != null && match.size() == 1 && sameSequence(seq, dss))
+      {
+        return seq;
+      }
+      /*
+       * clumsy alternative to using SequenceIdMatcher which currently
+       * returns sequences with a dbref to the matched accession id 
+       * which we don't want
+       */
+      if (firstIdMatch == null
+              && (name.equals(seq.getName()) || seq.getName().startsWith(
+                      name2)))
+      {
+        if (sameSequence(seq, dss))
+        {
+          firstIdMatch = seq;
+        }
+      }
+    }
+    return firstIdMatch;
+  }
+
+  /**
+   * Answers true if seq1 and seq2 contain exactly the same characters (ignoring
+   * case), else false. This method compares the lengths, then each character in
+   * turn, in order to 'fail fast'. For case-sensitive comparison, it would be
+   * possible to use Arrays.equals(seq1.getSequence(), seq2.getSequence()).
+   * 
+   * @param seq1
+   * @param seq2
+   * @return
+   */
+  // TODO move to Sequence / SequenceI
+  static boolean sameSequence(SequenceI seq1, SequenceI seq2)
+  {
+    if (seq1 == seq2)
+    {
+      return true;
+    }
+    if (seq1 == null || seq2 == null)
+    {
+      return false;
+    }
+    char[] c1 = seq1.getSequence();
+    char[] c2 = seq2.getSequence();
+    if (c1.length != c2.length)
+    {
+      return false;
+    }
+    for (int i = 0; i < c1.length; i++)
+    {
+      int diff = c1[i] - c2[i];
+      /*
+       * same char or differ in case only ('a'-'A' == 32)
+       */
+      if (diff != 0 && diff != 32 && diff != -32)
+      {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Updates any empty mappings in the cross-references with one to a compatible
+   * retrieved sequence if found, and adds any new mappings to the
+   * AlignedCodonFrame
+   * 
+   * @param mapFrom
+   * @param xrefs
+   * @param retrieved
+   * @param acf
+   */
+  void updateDbrefMappings(SequenceI mapFrom, DBRefEntry[] xrefs,
+          SequenceI[] retrieved, AlignedCodonFrame acf, boolean fromDna)
+  {
+    SequenceIdMatcher idMatcher = new SequenceIdMatcher(retrieved);
+    for (DBRefEntry xref : xrefs)
+    {
+      if (!xref.hasMap())
+      {
+        String targetSeqName = xref.getSource() + "|"
+                + xref.getAccessionId();
+        SequenceI[] matches = idMatcher.findAllIdMatches(targetSeqName);
+        if (matches == null)
+        {
+          return;
+        }
+        for (SequenceI seq : matches)
+        {
+          constructMapping(mapFrom, seq, xref, acf, fromDna);
+        }
+      }
+    }
+  }
+
+  /**
+   * Tries to make a mapping between sequences. If successful, adds the mapping
+   * to the dbref and the mappings collection and answers true, otherwise
+   * answers false. The following methods of making are mapping are tried in
+   * turn:
+   * <ul>
+   * <li>if 'mapTo' holds a mapping to 'mapFrom', take the inverse; this is, for
+   * example, the case after fetching EMBL cross-references for a Uniprot
+   * sequence</li>
+   * <li>else check if the dna translates exactly to the protein (give or take
+   * start and stop codons></li>
+   * <li>else try to map based on CDS features on the dna sequence</li>
+   * </ul>
+   * 
+   * @param mapFrom
+   * @param mapTo
+   * @param xref
+   * @param mappings
+   * @return
+   */
+  boolean constructMapping(SequenceI mapFrom, SequenceI mapTo,
+          DBRefEntry xref, AlignedCodonFrame mappings, boolean fromDna)
+  {
+    MapList mapping = null;
+    SequenceI dsmapFrom = mapFrom.getDatasetSequence() == null ? mapFrom
+            : mapFrom.getDatasetSequence();
+    SequenceI dsmapTo = mapTo.getDatasetSequence() == null ? mapTo : mapTo
+            .getDatasetSequence();
+    /*
+     * look for a reverse mapping, if found make its inverse. 
+     * Note - we do this on dataset sequences only.
+     */
+    if (dsmapTo.getDBRefs() != null)
+    {
+      for (DBRefEntry dbref : dsmapTo.getDBRefs())
+      {
+        String name = dbref.getSource() + "|" + dbref.getAccessionId();
+        if (dbref.hasMap() && dsmapFrom.getName().startsWith(name))
+        {
+          /*
+           * looks like we've found a map from 'mapTo' to 'mapFrom'
+           * - invert it to make the mapping the other way 
+           */
+          MapList reverse = dbref.getMap().getMap().getInverse();
+          xref.setMap(new Mapping(dsmapTo, reverse));
+          mappings.addMap(mapFrom, dsmapTo, reverse);
+          return true;
+        }
+      }
+    }
+
+    if (fromDna)
+    {
+      mapping = AlignmentUtils.mapCdnaToProtein(mapTo, mapFrom);
+    }
+    else
+    {
+      mapping = AlignmentUtils.mapCdnaToProtein(mapFrom, mapTo);
+      if (mapping != null)
+      {
+        mapping = mapping.getInverse();
+      }
+    }
+    if (mapping == null)
+    {
+      return false;
+    }
+    xref.setMap(new Mapping(mapTo, mapping));
+
+    /*
+     * and add a reverse DbRef with the inverse mapping
+     */
+    if (mapFrom.getDatasetSequence() != null && false)
+    // && mapFrom.getDatasetSequence().getSourceDBRef() != null)
+    {
+      // possible need to search primary references... except, why doesn't xref
+      // == getSourceDBRef ??
+      // DBRefEntry dbref = new DBRefEntry(mapFrom.getDatasetSequence()
+      // .getSourceDBRef());
+      // dbref.setMap(new Mapping(mapFrom.getDatasetSequence(), mapping
+      // .getInverse()));
+      // mapTo.addDBRef(dbref);
+    }
+
+    if (fromDna)
+    {
+      AlignmentUtils.computeProteinFeatures(mapFrom, mapTo, mapping);
+      mappings.addMap(mapFrom, mapTo, mapping);
+    }
+    else
+    {
+      mappings.addMap(mapTo, mapFrom, mapping.getInverse());
+    }
+
+    return true;
   }
 
   /**
@@ -400,15 +941,16 @@ public class CrossRef
    * dataset (that is not equal to sequenceI) Identifies matching DBRefEntry
    * based on source and accession string only - Map and Version are nulled.
    * 
+   * @param fromDna
+   *          - true if context was searching from Dna sequences, false if
+   *          context was searching from Protein sequences
    * @param sequenceI
    * @param lrfs
-   * @param dataset
-   * @param rseqs
+   * @param foundSeqs
    * @return true if matches were found.
    */
-  private static boolean searchDatasetXrefs(SequenceI sequenceI,
-          boolean dna, DBRefEntry[] lrfs, AlignmentI dataset,
-          List<SequenceI> rseqs, AlignedCodonFrame cf)
+  private boolean searchDatasetXrefs(boolean fromDna, SequenceI sequenceI,
+          DBRefEntry[] lrfs, List<SequenceI> foundSeqs, AlignedCodonFrame cf)
   {
     boolean found = false;
     if (lrfs == null)
@@ -421,50 +963,44 @@ public class CrossRef
       // add in wildcards
       xref.setVersion(null);
       xref.setMap(null);
-      found = searchDataset(sequenceI, xref, dataset, rseqs, cf, false, dna);
+      found |= searchDataset(fromDna, sequenceI, xref, foundSeqs, cf, false);
     }
     return found;
   }
 
   /**
-   * search a given sequence dataset for references matching cross-references to
-   * the given sequence
-   * 
-   * @param sequenceI
-   * @param xrf
-   * @param dataset
-   * @param rseqs
-   *          set of unique sequences
-   * @param cf
-   * @return true if one or more unique sequences were found and added
-   */
-  public static boolean searchDataset(SequenceI sequenceI, DBRefEntry xrf,
-          AlignmentI dataset, List<SequenceI> rseqs, AlignedCodonFrame cf)
-  {
-    return searchDataset(sequenceI, xrf, dataset, rseqs, cf, true, false);
-  }
-
-  /**
-   * TODO: generalise to different protein classifications Search dataset for
-   * DBRefEntrys matching the given one (xrf) and add the associated sequence to
-   * rseq.
+   * Searches dataset for DBRefEntrys matching the given one (xrf) and adds the
+   * associated sequence to rseqs
    * 
-   * @param sequenceI
+   * @param fromDna
+   *          true if context was searching for refs *from* dna sequence, false
+   *          if context was searching for refs *from* protein sequence
+   * @param fromSeq
+   *          a sequence to ignore (start point of search)
    * @param xrf
-   * @param dataset
-   * @param rseqs
+   *          a cross-reference to try to match
+   * @param foundSeqs
+   *          result list to add to
+   * @param mappings
+   *          a set of sequence mappings to add to
    * @param direct
-   *          - search all references or only subset
-   * @param dna
-   *          search dna or protein xrefs (if direct=false)
+   *          - indicates the type of relationship between returned sequences,
+   *          xrf, and sequenceI that is required.
+   *          <ul>
+   *          <li>direct implies xrf is a primary reference for sequenceI AND
+   *          the sequences to be located (eg a uniprot ID for a protein
+   *          sequence, and a uniprot ref on a transcript sequence).</li>
+   *          <li>indirect means xrf is a cross reference with respect to
+   *          sequenceI or all the returned sequences (eg a genomic reference
+   *          associated with a locus and one or more transcripts)</li>
+   *          </ul>
    * @return true if relationship found and sequence added.
    */
-  public static boolean searchDataset(SequenceI sequenceI, DBRefEntry xrf,
-          AlignmentI dataset, List<SequenceI> rseqs, AlignedCodonFrame cf,
-          boolean direct, boolean dna)
+  boolean searchDataset(boolean fromDna, SequenceI fromSeq, DBRefEntry xrf,
+          List<SequenceI> foundSeqs, AlignedCodonFrame mappings,
+          boolean direct)
   {
     boolean found = false;
-    SequenceI[] typer = new SequenceI[1];
     if (dataset == null)
     {
       return false;
@@ -484,107 +1020,85 @@ public class CrossRef
           if (nxt.getDatasetSequence() != null)
           {
             System.err
-                    .println("Implementation warning: getProducts passed a dataset alignment without dataset sequences in it!");
+                    .println("Implementation warning: CrossRef initialised with a dataset alignment with non-dataset sequences in it! ("
+                            + nxt.getDisplayId(true)
+                            + " has ds reference "
+                            + nxt.getDatasetSequence().getDisplayId(true)
+                            + ")");
+          }
+          if (nxt == fromSeq || nxt == fromSeq.getDatasetSequence())
+          {
+            continue;
           }
-          if (nxt != sequenceI && nxt != sequenceI.getDatasetSequence())
+          /*
+           * only look at same molecule type if 'direct', or
+           * complementary type if !direct
+           */
           {
-            // check if this is the correct sequence type
+            boolean isDna = !nxt.isProtein();
+            if (direct ? (isDna != fromDna) : (isDna == fromDna))
             {
-              typer[0] = nxt;
-              boolean isDna = jalview.util.Comparison.isNucleotide(typer);
-              if ((direct && isDna == dna) || (!direct && isDna != dna))
-              {
-                // skip this sequence because it is same molecule type
-                continue;
-              }
+              // skip this sequence because it is wrong molecule type
+              continue;
             }
+          }
 
-            // look for direct or indirect references in common
-            DBRefEntry[] poss = nxt.getDBRef(), cands = null;
-            if (direct)
-            {
-              cands = jalview.util.DBRefUtils.searchRefs(poss, xrf);
-            }
-            else
+          // look for direct or indirect references in common
+          DBRefEntry[] poss = nxt.getDBRefs();
+          List<DBRefEntry> cands = null;
+
+          // todo: indirect specifies we select either direct references to nxt
+          // that match xrf which is indirect to sequenceI, or indirect
+          // references to nxt that match xrf which is direct to sequenceI
+          cands = DBRefUtils.searchRefs(poss, xrf);
+          // else
+          // {
+          // poss = DBRefUtils.selectDbRefs(nxt.isProtein()!fromDna, poss);
+          // cands = DBRefUtils.searchRefs(poss, xrf);
+          // }
+          if (!cands.isEmpty())
+          {
+            if (foundSeqs.contains(nxt))
             {
-              poss = CrossRef.findXDbRefs(dna, poss); //
-              cands = jalview.util.DBRefUtils.searchRefs(poss, xrf);
+              continue;
             }
-            if (cands != null)
+            found = true;
+            foundSeqs.add(nxt);
+            if (mappings != null && !direct)
             {
-              if (!rseqs.contains(nxt))
+              /*
+               * if the matched sequence has mapped dbrefs to
+               * protein product / cdna, add equivalent mappings to
+               * our source sequence
+               */
+              for (DBRefEntry candidate : cands)
               {
-                rseqs.add(nxt);
-                boolean foundmap = cf != null;
-                // don't search if we aren't given a codon map object
-                for (int r = 0; foundmap && r < cands.length; r++)
+                Mapping mapping = candidate.getMap();
+                if (mapping != null)
                 {
-                  if (cands[r].hasMap())
+                  MapList map = mapping.getMap();
+                  if (mapping.getTo() != null
+                          && map.getFromRatio() != map.getToRatio())
                   {
-                    if (cands[r].getMap().getTo() != null
-                            && cands[r].getMap().getMap().getFromRatio() != cands[r]
-                                    .getMap().getMap().getToRatio())
+                    /*
+                     * add a mapping, as from dna to peptide sequence
+                     */
+                    if (map.getFromRatio() == 3)
                     {
-                      foundmap = true;
-                      // get sense of map correct for adding to product
-                      // alignment.
-                      if (dna)
-                      {
-                        // map is from dna seq to a protein product
-                        cf.addMap(sequenceI, nxt, cands[r].getMap()
-                                .getMap());
-                      }
-                      else
-                      {
-                        // map should be from protein seq to its coding dna
-                        cf.addMap(nxt, sequenceI, cands[r].getMap()
-                                .getMap().getInverse());
-                      }
+                      mappings.addMap(nxt, fromSeq, map);
+                    }
+                    else
+                    {
+                      mappings.addMap(nxt, fromSeq, map.getInverse());
                     }
                   }
                 }
-                // TODO: add mapping between sequences if necessary
-                found = true;
               }
             }
-
           }
         }
       }
     }
     return found;
   }
-
-  /**
-   * precalculate different products that can be found for seqs in dataset and
-   * return them.
-   * 
-   * @param dna
-   * @param seqs
-   * @param dataset
-   * @param fake
-   *          - don't actually build lists - just get types
-   * @return public static Object[] buildXProductsList(boolean dna, SequenceI[]
-   *         seqs, AlignmentI dataset, boolean fake) { String types[] =
-   *         jalview.analysis.CrossRef.findSequenceXrefTypes( dna, seqs,
-   *         dataset); if (types != null) { System.out.println("Xref Types for:
-   *         "+(dna ? "dna" : "prot")); for (int t = 0; t < types.length; t++) {
-   *         System.out.println("Type: " + types[t]); SequenceI[] prod =
-   *         jalview.analysis.CrossRef.findXrefSequences(seqs, dna, types[t]);
-   *         System.out.println("Found " + ((prod == null) ? "no" : "" +
-   *         prod.length) + " products"); if (prod!=null) { for (int p=0;
-   *         p<prod.length; p++) { System.out.println("Prod "+p+":
-   *         "+prod[p].getDisplayId(true)); } } } } else {
-   *         System.out.println("Trying getProducts for
-   *         "+al.getSequenceAt(0).getDisplayId(true));
-   *         System.out.println("Search DS Xref for: "+(dna ? "dna" : "prot"));
-   *         // have a bash at finding the products amongst all the retrieved
-   *         sequences. SequenceI[] prod =
-   *         jalview.analysis.CrossRef.findXrefSequences(al
-   *         .getSequencesArray(), dna, null, ds); System.out.println("Found " +
-   *         ((prod == null) ? "no" : "" + prod.length) + " products"); if
-   *         (prod!=null) { // select non-equivalent sequences from dataset list
-   *         for (int p=0; p<prod.length; p++) { System.out.println("Prod "+p+":
-   *         "+prod[p].getDisplayId(true)); } } } }
-   */
 }
diff --git a/src/jalview/analysis/Dna.java b/src/jalview/analysis/Dna.java
index 515bd91..8dfd0f9 100644
--- a/src/jalview/analysis/Dna.java
+++ b/src/jalview/analysis/Dna.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -69,7 +69,7 @@ public class Dna
 
   final private int dnaWidth;
 
-  final private Alignment dataset;
+  final private AlignmentI dataset;
 
   /*
    * Working variables for the translation.
@@ -208,13 +208,13 @@ public class Dna
     for (int gd = 0; gd < selection.length; gd++)
     {
       SequenceI dna = selection[gd];
-      DBRefEntry[] dnarefs = DBRefUtils.selectRefs(dna.getDBRef(),
+      DBRefEntry[] dnarefs = DBRefUtils.selectRefs(dna.getDBRefs(),
               jalview.datamodel.DBRefSource.DNACODINGDBS);
       if (dnarefs != null)
       {
         // intersect with pep
         List<DBRefEntry> mappedrefs = new ArrayList<DBRefEntry>();
-        DBRefEntry[] refs = dna.getDBRef();
+        DBRefEntry[] refs = dna.getDBRefs();
         for (int d = 0; d < refs.length; d++)
         {
           if (refs[d].getMap() != null && refs[d].getMap().getMap() != null
@@ -773,7 +773,7 @@ public class Dna
   {
     SequenceFeature[] sfs = dna.getSequenceFeatures();
     Boolean fgstate;
-    DBRefEntry[] dnarefs = DBRefUtils.selectRefs(dna.getDBRef(),
+    DBRefEntry[] dnarefs = DBRefUtils.selectRefs(dna.getDBRefs(),
             DBRefSource.DNACODINGDBS);
     if (dnarefs != null)
     {
@@ -806,4 +806,164 @@ public class Dna
       }
     }
   }
+
+  /**
+   * Returns an alignment consisting of the reversed (and optionally
+   * complemented) sequences set in this object's constructor
+   * 
+   * @param complement
+   * @return
+   */
+  public AlignmentI reverseCdna(boolean complement)
+  {
+    int sSize = selection.size();
+    List<SequenceI> reversed = new ArrayList<SequenceI>();
+    for (int s = 0; s < sSize; s++)
+    {
+      SequenceI newseq = reverseSequence(selection.get(s).getName(),
+              seqstring[s], complement);
+
+      if (newseq != null)
+      {
+        reversed.add(newseq);
+      }
+    }
+
+    SequenceI[] newseqs = reversed.toArray(new SequenceI[reversed.size()]);
+    AlignmentI al = new Alignment(newseqs);
+    ((Alignment) al).createDatasetAlignment();
+    return al;
+  }
+
+  /**
+   * Returns a reversed, and optionally complemented, sequence. The new
+   * sequence's name is the original name with "|rev" or "|revcomp" appended.
+   * aAcCgGtT and DNA ambiguity codes are complemented, any other characters are
+   * left unchanged.
+   * 
+   * @param seq
+   * @param complement
+   * @return
+   */
+  public static SequenceI reverseSequence(String seqName, String sequence,
+          boolean complement)
+  {
+    String newName = seqName + "|rev" + (complement ? "comp" : "");
+    char[] originalSequence = sequence.toCharArray();
+    int length = originalSequence.length;
+    char[] reversedSequence = new char[length];
+    int bases = 0;
+    for (int i = 0; i < length; i++)
+    {
+      char c = complement ? getComplement(originalSequence[i])
+              : originalSequence[i];
+      reversedSequence[length - i - 1] = c;
+      if (!Comparison.isGap(c))
+      {
+        bases++;
+      }
+    }
+    SequenceI reversed = new Sequence(newName, reversedSequence, 1, bases);
+    return reversed;
+  }
+
+  /**
+   * Returns dna complement (preserving case) for aAcCgGtTuU. Ambiguity codes
+   * are treated as on http://reverse-complement.com/. Anything else is left
+   * unchanged.
+   * 
+   * @param c
+   * @return
+   */
+  public static char getComplement(char c)
+  {
+    char result = c;
+    switch (c)
+    {
+    case '-':
+    case '.':
+    case ' ':
+      break;
+    case 'a':
+      result = 't';
+      break;
+    case 'A':
+      result = 'T';
+      break;
+    case 'c':
+      result = 'g';
+      break;
+    case 'C':
+      result = 'G';
+      break;
+    case 'g':
+      result = 'c';
+      break;
+    case 'G':
+      result = 'C';
+      break;
+    case 't':
+      result = 'a';
+      break;
+    case 'T':
+      result = 'A';
+      break;
+    case 'u':
+      result = 'a';
+      break;
+    case 'U':
+      result = 'A';
+      break;
+    case 'r':
+      result = 'y';
+      break;
+    case 'R':
+      result = 'Y';
+      break;
+    case 'y':
+      result = 'r';
+      break;
+    case 'Y':
+      result = 'R';
+      break;
+    case 'k':
+      result = 'm';
+      break;
+    case 'K':
+      result = 'M';
+      break;
+    case 'm':
+      result = 'k';
+      break;
+    case 'M':
+      result = 'K';
+      break;
+    case 'b':
+      result = 'v';
+      break;
+    case 'B':
+      result = 'V';
+      break;
+    case 'v':
+      result = 'b';
+      break;
+    case 'V':
+      result = 'B';
+      break;
+    case 'd':
+      result = 'h';
+      break;
+    case 'D':
+      result = 'H';
+      break;
+    case 'h':
+      result = 'd';
+      break;
+    case 'H':
+      result = 'D';
+      break;
+    }
+
+    return result;
+  }
 }
diff --git a/src/jalview/analysis/Finder.java b/src/jalview/analysis/Finder.java
index fae459f..0929290 100644
--- a/src/jalview/analysis/Finder.java
+++ b/src/jalview/analysis/Finder.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,24 +21,31 @@
 package jalview.analysis;
 
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
-import jalview.datamodel.Sequence;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+import jalview.util.Comparison;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Vector;
 
+import com.stevesoft.pat.Regex;
+
 public class Finder
 {
   /**
    * Implements the search algorithms for the Find dialog box.
    */
-  SearchResults searchResults;
+  SearchResultsI searchResults;
 
   AlignmentI alignment;
 
-  jalview.datamodel.SequenceGroup selection = null;
+  SequenceGroup selection = null;
 
-  Vector idMatch = null;
+  Vector<SequenceI> idMatch = null;
 
   boolean caseSensitive = false;
 
@@ -46,10 +53,10 @@ public class Finder
 
   boolean findAll = false;
 
-  com.stevesoft.pat.Regex regex = null;
+  Regex regex = null;
 
   /**
-   * hold's last-searched position between calles to find(false)
+   * holds last-searched position between calls to find(false)
    */
   int seqIndex = 0, resIndex = -1;
 
@@ -83,11 +90,10 @@ public class Finder
     {
       searchString = searchString.toUpperCase();
     }
-    regex = new com.stevesoft.pat.Regex(searchString);
+    regex = new Regex(searchString);
     regex.setIgnoreCase(!caseSensitive);
     searchResults = new SearchResults();
-    idMatch = new Vector();
-    Sequence seq;
+    idMatch = new Vector<SequenceI>();
     String item = null;
     boolean found = false;
     int end = alignment.getHeight();
@@ -102,10 +108,11 @@ public class Finder
         selection = null;
       }
     }
+    SearchResultMatchI lastm = null;
 
     while (!found && (seqIndex < end))
     {
-      seq = (Sequence) alignment.getSequenceAt(seqIndex);
+      SequenceI seq = alignment.getSequenceAt(seqIndex);
 
       if ((selection != null && selection.getSize() > 0)
               && !selection.getSequences(null).contains(seq))
@@ -140,7 +147,7 @@ public class Finder
         {
         }
 
-        if (regex.search(seq.getName()))
+        if (regex.search(seq.getName()) && !idMatch.contains(seq))
         {
           idMatch.addElement(seq);
           hasResults = true;
@@ -153,7 +160,8 @@ public class Finder
         }
 
         if (isIncludeDescription() && seq.getDescription() != null
-                && regex.search(seq.getDescription()))
+                && regex.search(seq.getDescription())
+                && !idMatch.contains(seq))
         {
           idMatch.addElement(seq);
           hasResults = true;
@@ -174,16 +182,16 @@ public class Finder
       }
 
       // /Shall we ignore gaps???? - JBPNote: Add Flag for forcing this or not
-      StringBuffer noGapsSB = new StringBuffer();
+      StringBuilder noGapsSB = new StringBuilder();
       int insertCount = 0;
-      Vector spaces = new Vector();
+      List<Integer> spaces = new ArrayList<Integer>();
 
       for (int j = 0; j < item.length(); j++)
       {
-        if (!jalview.util.Comparison.isGap(item.charAt(j)))
+        if (!Comparison.isGap(item.charAt(j)))
         {
           noGapsSB.append(item.charAt(j));
-          spaces.addElement(new Integer(insertCount));
+          spaces.add(Integer.valueOf(insertCount));
         }
         else
         {
@@ -192,7 +200,6 @@ public class Finder
       }
 
       String noGaps = noGapsSB.toString();
-
       for (int r = resIndex; r < noGaps.length(); r++)
       {
 
@@ -201,22 +208,22 @@ public class Finder
           resIndex = regex.matchedFrom();
 
           if ((selection != null && selection.getSize() > 0)
-                  && ((resIndex + Integer.parseInt(spaces.elementAt(
-                          resIndex).toString())) < selection.getStartRes()))
+                  && (resIndex + spaces.get(resIndex) < selection
+                          .getStartRes()))
           {
             continue;
           }
           // if invalid string used, then regex has no matched to/from
-          int sres = seq
-                  .findPosition(resIndex
-                          + Integer.parseInt(spaces.elementAt(resIndex)
-                                  .toString()));
-          int eres = seq.findPosition(regex.matchedTo()
-                  - 1
-                  + Integer.parseInt(spaces
-                          .elementAt(regex.matchedTo() - 1).toString()));
-
-          searchResults.addResult(seq, sres, eres);
+          int sres = seq.findPosition(resIndex + spaces.get(resIndex));
+          int eres = seq.findPosition(regex.matchedTo() - 1
+                  + (spaces.get(regex.matchedTo() - 1)));
+          // only add result if not contained in previous result
+          if (lastm == null
+                  || (lastm.getSequence() != seq || (!(lastm.getStart() <= sres && lastm
+                          .getEnd() >= eres))))
+          {
+            lastm = searchResults.addResult(seq, sres, eres);
+          }
           hasResults = true;
           if (!findAll)
           {
@@ -320,9 +327,12 @@ public class Finder
   }
 
   /**
-   * @return the idMatch
+   * Returns the (possibly empty) list of matching sequences (when search
+   * includes searching sequence names)
+   * 
+   * @return
    */
-  public Vector getIdMatch()
+  public Vector<SequenceI> getIdMatch()
   {
     return idMatch;
   }
@@ -338,7 +348,7 @@ public class Finder
   /**
    * @return the searchResults
    */
-  public SearchResults getSearchResults()
+  public SearchResultsI getSearchResults()
   {
     return searchResults;
   }
diff --git a/src/jalview/analysis/Grouping.java b/src/jalview/analysis/Grouping.java
index 18c1bb9..1a26852 100644
--- a/src/jalview/analysis/Grouping.java
+++ b/src/jalview/analysis/Grouping.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -127,6 +127,11 @@ public class Grouping
         }
       }
     }
+
+    /*
+     * get selected columns (in the order they were selected);
+     * note this could include right-to-left ranges
+     */
     int[] spos = new int[cs.getSelected().size()];
     int width = -1;
     int i = 0;
@@ -134,7 +139,7 @@ public class Grouping
     {
       spos[i++] = pos.intValue();
     }
-    ;
+
     for (i = 0; i < sequences.length; i++)
     {
       int slen = sequences[i].getLength();
diff --git a/src/jalview/analysis/NJTree.java b/src/jalview/analysis/NJTree.java
index e3a68ca..5926746 100644
--- a/src/jalview/analysis/NJTree.java
+++ b/src/jalview/analysis/NJTree.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -44,7 +44,7 @@ import java.util.Vector;
  */
 public class NJTree
 {
-  Vector cluster;
+  Vector<Cluster> cluster;
 
   SequenceI[] sequence;
 
@@ -68,7 +68,7 @@ public class NJTree
 
   float rj;
 
-  Vector groups = new Vector();
+  Vector<SequenceNode> groups = new Vector<SequenceNode>();
 
   SequenceNode maxdist;
 
@@ -80,7 +80,7 @@ public class NJTree
 
   int ycount;
 
-  Vector node;
+  Vector<SequenceNode> node;
 
   String type;
 
@@ -88,8 +88,6 @@ public class NJTree
 
   Object found = null;
 
-  Object leaves = null;
-
   boolean hasDistances = true; // normal case for jalview trees
 
   boolean hasBootstrap = false; // normal case for jalview trees
@@ -151,8 +149,7 @@ public class NJTree
 
     SequenceIdMatcher algnIds = new SequenceIdMatcher(seqs);
 
-    Vector leaves = new Vector();
-    findLeaves(top, leaves);
+    Vector<SequenceNode> leaves = findLeaves(top);
 
     int i = 0;
     int namesleft = seqs.length;
@@ -160,11 +157,11 @@ public class NJTree
     SequenceNode j;
     SequenceI nam;
     String realnam;
-    Vector one2many = new Vector();
+    Vector<SequenceI> one2many = new Vector<SequenceI>();
     int countOne2Many = 0;
     while (i < leaves.size())
     {
-      j = (SequenceNode) leaves.elementAt(i++);
+      j = leaves.elementAt(i++);
       realnam = j.getName();
       nam = null;
 
@@ -221,7 +218,7 @@ public class NJTree
           String pwtype, ScoreModelI sm, int start, int end)
   {
     this.sequence = sequence;
-    this.node = new Vector();
+    this.node = new Vector<SequenceNode>();
     this.type = type;
     this.pwtype = pwtype;
     if (seqData != null)
@@ -282,6 +279,7 @@ public class NJTree
    * 
    * @return Newick File with all tree data available
    */
+  @Override
   public String toString()
   {
     jalview.io.NewickFile fout = new jalview.io.NewickFile(getTopNode());
@@ -299,8 +297,7 @@ public class NJTree
    */
   public void UpdatePlaceHolders(List<SequenceI> list)
   {
-    Vector leaves = new Vector();
-    findLeaves(top, leaves);
+    Vector<SequenceNode> leaves = findLeaves(top);
 
     int sz = leaves.size();
     SequenceIdMatcher seqmatcher = null;
@@ -308,7 +305,7 @@ public class NJTree
 
     while (i < sz)
     {
-      SequenceNode leaf = (SequenceNode) leaves.elementAt(i++);
+      SequenceNode leaf = leaves.elementAt(i++);
 
       if (list.contains(leaf.element()))
       {
@@ -369,12 +366,12 @@ public class NJTree
     {
 
       @Override
-      public void transform(BinaryNode node)
+      public void transform(BinaryNode nd)
       {
-        Object el = node.element();
+        Object el = nd.element();
         if (el != null && el instanceof SequenceI)
         {
-          node.setName(((SequenceI) el).getName());
+          nd.setName(((SequenceI) el).getName());
         }
       }
     });
@@ -428,7 +425,7 @@ public class NJTree
     }
 
     joinClusters(one, two);
-    top = (SequenceNode) (node.elementAt(one));
+    top = (node.elementAt(one));
 
     reCount(top);
     findHeight(top);
@@ -449,19 +446,19 @@ public class NJTree
   {
     float dist = distance[i][j];
 
-    int noi = ((Cluster) cluster.elementAt(i)).value.length;
-    int noj = ((Cluster) cluster.elementAt(j)).value.length;
+    int noi = cluster.elementAt(i).value.length;
+    int noj = cluster.elementAt(j).value.length;
 
     int[] value = new int[noi + noj];
 
     for (int ii = 0; ii < noi; ii++)
     {
-      value[ii] = ((Cluster) cluster.elementAt(i)).value[ii];
+      value[ii] = cluster.elementAt(i).value[ii];
     }
 
     for (int ii = noi; ii < (noi + noj); ii++)
     {
-      value[ii] = ((Cluster) cluster.elementAt(j)).value[ii - noi];
+      value[ii] = cluster.elementAt(j).value[ii - noi];
     }
 
     Cluster c = new Cluster(value);
@@ -480,11 +477,11 @@ public class NJTree
 
     SequenceNode sn = new SequenceNode();
 
-    sn.setLeft((SequenceNode) (node.elementAt(i)));
-    sn.setRight((SequenceNode) (node.elementAt(j)));
+    sn.setLeft((node.elementAt(i)));
+    sn.setRight((node.elementAt(j)));
 
-    SequenceNode tmpi = (SequenceNode) (node.elementAt(i));
-    SequenceNode tmpj = (SequenceNode) (node.elementAt(j));
+    SequenceNode tmpi = (node.elementAt(i));
+    SequenceNode tmpj = (node.elementAt(j));
 
     if (type.equals("NJ"))
     {
@@ -576,8 +573,8 @@ public class NJTree
    */
   public void findClusterDistance(int i, int j)
   {
-    int noi = ((Cluster) cluster.elementAt(i)).value.length;
-    int noj = ((Cluster) cluster.elementAt(j)).value.length;
+    int noi = cluster.elementAt(i).value.length;
+    int noj = cluster.elementAt(j).value.length;
 
     // New distances from cluster to others
     float[] newdist = new float[noseqs];
@@ -733,7 +730,7 @@ public class NJTree
   public float[][] findDistances(ScoreModelI _pwmatrix)
   {
 
-    float[][] distance = new float[noseqs][noseqs];
+    float[][] dist = new float[noseqs][noseqs];
     if (_pwmatrix == null)
     {
       // Resolve substitution model
@@ -743,8 +740,8 @@ public class NJTree
         _pwmatrix = ResidueProperties.getScoreMatrix("BLOSUM62");
       }
     }
-    distance = _pwmatrix.findDistances(seqData);
-    return distance;
+    dist = _pwmatrix.findDistances(seqData);
+    return dist;
 
   }
 
@@ -753,7 +750,7 @@ public class NJTree
    */
   public void makeLeaves()
   {
-    cluster = new Vector();
+    cluster = new Vector<Cluster>();
 
     for (int i = 0; i < noseqs; i++)
     {
@@ -772,26 +769,42 @@ public class NJTree
   }
 
   /**
+   * Search for leaf nodes below (or at) the given node
+   * 
+   * @param nd
+   *          root node to search from
+   * 
+   * @return
+   */
+  public Vector<SequenceNode> findLeaves(SequenceNode nd)
+  {
+    Vector<SequenceNode> leaves = new Vector<SequenceNode>();
+    findLeaves(nd, leaves);
+    return leaves;
+  }
+
+  /**
    * Search for leaf nodes.
    * 
-   * @param node
+   * @param nd
    *          root node to search from
    * @param leaves
    *          Vector of leaves to add leaf node objects too.
    * 
    * @return Vector of leaf nodes on binary tree
    */
-  public Vector findLeaves(SequenceNode node, Vector leaves)
+  Vector<SequenceNode> findLeaves(SequenceNode nd,
+          Vector<SequenceNode> leaves)
   {
-    if (node == null)
+    if (nd == null)
     {
       return leaves;
     }
 
-    if ((node.left() == null) && (node.right() == null)) // Interior node
+    if ((nd.left() == null) && (nd.right() == null)) // Interior node
     // detection
     {
-      leaves.addElement(node);
+      leaves.addElement(nd);
 
       return leaves;
     }
@@ -801,8 +814,8 @@ public class NJTree
        * TODO: Identify internal nodes... if (node.isSequenceLabel()) {
        * leaves.addElement(node); }
        */
-      findLeaves((SequenceNode) node.left(), leaves);
-      findLeaves((SequenceNode) node.right(), leaves);
+      findLeaves((SequenceNode) nd.left(), leaves);
+      findLeaves((SequenceNode) nd.right(), leaves);
     }
 
     return leaves;
@@ -811,16 +824,16 @@ public class NJTree
   /**
    * Find the leaf node with a particular ycount
    * 
-   * @param node
+   * @param nd
    *          initial point on tree to search from
    * @param count
    *          value to search for
    * 
    * @return null or the node with ycound=count
    */
-  public Object findLeaf(SequenceNode node, int count)
+  public Object findLeaf(SequenceNode nd, int count)
   {
-    found = _findLeaf(node, count);
+    found = _findLeaf(nd, count);
 
     return found;
   }
@@ -828,23 +841,23 @@ public class NJTree
   /*
    * #see findLeaf(SequenceNode node, count)
    */
-  public Object _findLeaf(SequenceNode node, int count)
+  public Object _findLeaf(SequenceNode nd, int count)
   {
-    if (node == null)
+    if (nd == null)
     {
       return null;
     }
 
-    if (node.ycount == count)
+    if (nd.ycount == count)
     {
-      found = node.element();
+      found = nd.element();
 
       return found;
     }
     else
     {
-      _findLeaf((SequenceNode) node.left(), count);
-      _findLeaf((SequenceNode) node.right(), count);
+      _findLeaf((SequenceNode) nd.left(), count);
+      _findLeaf((SequenceNode) nd.right(), count);
     }
 
     return found;
@@ -853,58 +866,57 @@ public class NJTree
   /**
    * printNode is mainly for debugging purposes.
    * 
-   * @param node
+   * @param nd
    *          SequenceNode
    */
-  public void printNode(SequenceNode node)
+  public void printNode(SequenceNode nd)
   {
-    if (node == null)
+    if (nd == null)
     {
       return;
     }
 
-    if ((node.left() == null) && (node.right() == null))
+    if ((nd.left() == null) && (nd.right() == null))
     {
-      System.out
-              .println("Leaf = " + ((SequenceI) node.element()).getName());
-      System.out.println("Dist " + node.dist);
-      System.out.println("Boot " + node.getBootstrap());
+      System.out.println("Leaf = " + ((SequenceI) nd.element()).getName());
+      System.out.println("Dist " + nd.dist);
+      System.out.println("Boot " + nd.getBootstrap());
     }
     else
     {
-      System.out.println("Dist " + node.dist);
-      printNode((SequenceNode) node.left());
-      printNode((SequenceNode) node.right());
+      System.out.println("Dist " + nd.dist);
+      printNode((SequenceNode) nd.left());
+      printNode((SequenceNode) nd.right());
     }
   }
 
   /**
    * DOCUMENT ME!
    * 
-   * @param node
+   * @param nd
    *          DOCUMENT ME!
    */
-  public void findMaxDist(SequenceNode node)
+  public void findMaxDist(SequenceNode nd)
   {
-    if (node == null)
+    if (nd == null)
     {
       return;
     }
 
-    if ((node.left() == null) && (node.right() == null))
+    if ((nd.left() == null) && (nd.right() == null))
     {
-      float dist = node.dist;
+      float dist = nd.dist;
 
       if (dist > maxDistValue)
       {
-        maxdist = node;
+        maxdist = nd;
         maxDistValue = dist;
       }
     }
     else
     {
-      findMaxDist((SequenceNode) node.left());
-      findMaxDist((SequenceNode) node.right());
+      findMaxDist((SequenceNode) nd.left());
+      findMaxDist((SequenceNode) nd.right());
     }
   }
 
@@ -913,7 +925,7 @@ public class NJTree
    * 
    * @return DOCUMENT ME!
    */
-  public Vector getGroups()
+  public Vector<SequenceNode> getGroups()
   {
     return groups;
   }
@@ -931,51 +943,51 @@ public class NJTree
   /**
    * DOCUMENT ME!
    * 
-   * @param node
+   * @param nd
    *          DOCUMENT ME!
    * @param threshold
    *          DOCUMENT ME!
    */
-  public void groupNodes(SequenceNode node, float threshold)
+  public void groupNodes(SequenceNode nd, float threshold)
   {
-    if (node == null)
+    if (nd == null)
     {
       return;
     }
 
-    if ((node.height / maxheight) > threshold)
+    if ((nd.height / maxheight) > threshold)
     {
-      groups.addElement(node);
+      groups.addElement(nd);
     }
     else
     {
-      groupNodes((SequenceNode) node.left(), threshold);
-      groupNodes((SequenceNode) node.right(), threshold);
+      groupNodes((SequenceNode) nd.left(), threshold);
+      groupNodes((SequenceNode) nd.right(), threshold);
     }
   }
 
   /**
    * DOCUMENT ME!
    * 
-   * @param node
+   * @param nd
    *          DOCUMENT ME!
    * 
    * @return DOCUMENT ME!
    */
-  public float findHeight(SequenceNode node)
+  public float findHeight(SequenceNode nd)
   {
-    if (node == null)
+    if (nd == null)
     {
       return maxheight;
     }
 
-    if ((node.left() == null) && (node.right() == null))
+    if ((nd.left() == null) && (nd.right() == null))
     {
-      node.height = ((SequenceNode) node.parent()).height + node.dist;
+      nd.height = ((SequenceNode) nd.parent()).height + nd.dist;
 
-      if (node.height > maxheight)
+      if (nd.height > maxheight)
       {
-        return node.height;
+        return nd.height;
       }
       else
       {
@@ -984,18 +996,18 @@ public class NJTree
     }
     else
     {
-      if (node.parent() != null)
+      if (nd.parent() != null)
       {
-        node.height = ((SequenceNode) node.parent()).height + node.dist;
+        nd.height = ((SequenceNode) nd.parent()).height + nd.dist;
       }
       else
       {
         maxheight = 0;
-        node.height = (float) 0.0;
+        nd.height = (float) 0.0;
       }
 
-      maxheight = findHeight((SequenceNode) (node.left()));
-      maxheight = findHeight((SequenceNode) (node.right()));
+      maxheight = findHeight((SequenceNode) (nd.left()));
+      maxheight = findHeight((SequenceNode) (nd.right()));
     }
 
     return maxheight;
@@ -1078,43 +1090,42 @@ public class NJTree
   /**
    * DOCUMENT ME!
    * 
-   * @param node
+   * @param nd
    *          DOCUMENT ME!
    */
-  public void printN(SequenceNode node)
+  public void printN(SequenceNode nd)
   {
-    if (node == null)
+    if (nd == null)
     {
       return;
     }
 
-    if ((node.left() != null) && (node.right() != null))
+    if ((nd.left() != null) && (nd.right() != null))
     {
-      printN((SequenceNode) node.left());
-      printN((SequenceNode) node.right());
+      printN((SequenceNode) nd.left());
+      printN((SequenceNode) nd.right());
     }
     else
     {
-      System.out.println(" name = "
-              + ((SequenceI) node.element()).getName());
+      System.out.println(" name = " + ((SequenceI) nd.element()).getName());
     }
 
-    System.out.println(" dist = " + node.dist + " " + node.count + " "
-            + node.height);
+    System.out.println(" dist = " + nd.dist + " " + nd.count + " "
+            + nd.height);
   }
 
   /**
    * DOCUMENT ME!
    * 
-   * @param node
+   * @param nd
    *          DOCUMENT ME!
    */
-  public void reCount(SequenceNode node)
+  public void reCount(SequenceNode nd)
   {
     ycount = 0;
     _lycount = 0;
     // _lylimit = this.node.size();
-    _reCount(node);
+    _reCount(nd);
   }
 
   private long _lycount = 0, _lylimit = 0;
@@ -1122,37 +1133,37 @@ public class NJTree
   /**
    * DOCUMENT ME!
    * 
-   * @param node
+   * @param nd
    *          DOCUMENT ME!
    */
-  public void _reCount(SequenceNode node)
+  public void _reCount(SequenceNode nd)
   {
     // if (_lycount<_lylimit)
     // {
     // System.err.println("Warning: depth of _recount greater than number of nodes.");
     // }
-    if (node == null)
+    if (nd == null)
     {
       return;
     }
     _lycount++;
 
-    if ((node.left() != null) && (node.right() != null))
+    if ((nd.left() != null) && (nd.right() != null))
     {
 
-      _reCount((SequenceNode) node.left());
-      _reCount((SequenceNode) node.right());
+      _reCount((SequenceNode) nd.left());
+      _reCount((SequenceNode) nd.right());
 
-      SequenceNode l = (SequenceNode) node.left();
-      SequenceNode r = (SequenceNode) node.right();
+      SequenceNode l = (SequenceNode) nd.left();
+      SequenceNode r = (SequenceNode) nd.right();
 
-      node.count = l.count + r.count;
-      node.ycount = (l.ycount + r.ycount) / 2;
+      nd.count = l.count + r.count;
+      nd.ycount = (l.ycount + r.ycount) / 2;
     }
     else
     {
-      node.count = 1;
-      node.ycount = ycount++;
+      nd.count = 1;
+      nd.ycount = ycount++;
     }
     _lycount--;
   }
@@ -1160,80 +1171,80 @@ public class NJTree
   /**
    * DOCUMENT ME!
    * 
-   * @param node
+   * @param nd
    *          DOCUMENT ME!
    */
-  public void swapNodes(SequenceNode node)
+  public void swapNodes(SequenceNode nd)
   {
-    if (node == null)
+    if (nd == null)
     {
       return;
     }
 
-    SequenceNode tmp = (SequenceNode) node.left();
+    SequenceNode tmp = (SequenceNode) nd.left();
 
-    node.setLeft(node.right());
-    node.setRight(tmp);
+    nd.setLeft(nd.right());
+    nd.setRight(tmp);
   }
 
   /**
    * DOCUMENT ME!
    * 
-   * @param node
+   * @param nd
    *          DOCUMENT ME!
    * @param dir
    *          DOCUMENT ME!
    */
-  public void changeDirection(SequenceNode node, SequenceNode dir)
+  public void changeDirection(SequenceNode nd, SequenceNode dir)
   {
-    if (node == null)
+    if (nd == null)
     {
       return;
     }
 
-    if (node.parent() != top)
+    if (nd.parent() != top)
     {
-      changeDirection((SequenceNode) node.parent(), node);
+      changeDirection((SequenceNode) nd.parent(), nd);
 
-      SequenceNode tmp = (SequenceNode) node.parent();
+      SequenceNode tmp = (SequenceNode) nd.parent();
 
-      if (dir == node.left())
+      if (dir == nd.left())
       {
-        node.setParent(dir);
-        node.setLeft(tmp);
+        nd.setParent(dir);
+        nd.setLeft(tmp);
       }
-      else if (dir == node.right())
+      else if (dir == nd.right())
       {
-        node.setParent(dir);
-        node.setRight(tmp);
+        nd.setParent(dir);
+        nd.setRight(tmp);
       }
     }
     else
     {
-      if (dir == node.left())
+      if (dir == nd.left())
       {
-        node.setParent(node.left());
+        nd.setParent(nd.left());
 
-        if (top.left() == node)
+        if (top.left() == nd)
         {
-          node.setRight(top.right());
+          nd.setRight(top.right());
         }
         else
         {
-          node.setRight(top.left());
+          nd.setRight(top.left());
         }
       }
       else
       {
-        node.setParent(node.right());
+        nd.setParent(nd.right());
 
-        if (top.left() == node)
+        if (top.left() == nd)
         {
-          node.setLeft(top.right());
+          nd.setLeft(top.right());
         }
         else
         {
-          node.setLeft(top.left());
+          nd.setLeft(top.left());
         }
       }
     }
@@ -1289,8 +1300,9 @@ public class NJTree
    */
   public void applyToNodes(NodeTransformI nodeTransformI)
   {
-    for (Enumeration nodes = node.elements(); nodes.hasMoreElements(); nodeTransformI
-            .transform((BinaryNode) nodes.nextElement()))
+    for (Enumeration<SequenceNode> nodes = node.elements(); nodes
+            .hasMoreElements(); nodeTransformI.transform(nodes
+            .nextElement()))
     {
       ;
     }
diff --git a/src/jalview/analysis/PCA.java b/src/jalview/analysis/PCA.java
index ace4ca4..c013a18 100644
--- a/src/jalview/analysis/PCA.java
+++ b/src/jalview/analysis/PCA.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/analysis/ParseProperties.java b/src/jalview/analysis/ParseProperties.java
index d186fc8..ef4260a 100644
--- a/src/jalview/analysis/ParseProperties.java
+++ b/src/jalview/analysis/ParseProperties.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/analysis/Rna.java b/src/jalview/analysis/Rna.java
index 588ea6e..286066f 100644
--- a/src/jalview/analysis/Rna.java
+++ b/src/jalview/analysis/Rna.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -31,82 +31,108 @@ import jalview.datamodel.SequenceFeature;
 import jalview.util.MessageManager;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.Stack;
 import java.util.Vector;
 
 public class Rna
 {
 
-  static Hashtable<Integer, Integer> pairHash = new Hashtable();
-
-  private static final Character[] openingPars = { '(', '[', '{', '<', 'A',
-      'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
-      'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
-
-  private static final Character[] closingPars = { ')', ']', '}', '>', 'a',
-      'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
-      'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
-
-  private static HashSet<Character> openingParsSet = new HashSet<Character>(
-          Arrays.asList(openingPars));
-
-  private static HashSet<Character> closingParsSet = new HashSet<Character>(
-          Arrays.asList(closingPars));
+  /**
+   * Answers true if the character is a valid open pair rna secondary structure
+   * symbol. Currently accepts A-Z, ([{<
+   * 
+   * @param c
+   * @return
+   */
+  public static boolean isOpeningParenthesis(char c)
+  {
+    return ('A' <= c && c <= 'Z' || c == '(' || c == '[' || c == '{' || c == '<');
+  }
 
-  private static Hashtable<Character, Character> closingToOpening = new Hashtable<Character, Character>()
-  // Initializing final data structure
+  /**
+   * Answers true if the string is a valid open pair rna secondary structure
+   * symbol. Currently accepts A-Z, ([{<
+   * 
+   * @param s
+   * @return
+   */
+  public static boolean isOpeningParenthesis(String s)
   {
-    private static final long serialVersionUID = 1L;
-    {
-      for (int i = 0; i < openingPars.length; i++)
-      {
-        // System.out.println(closingPars[i] + "->" + openingPars[i]);
-        put(closingPars[i], openingPars[i]);
-      }
-    }
-  };
+    return s != null && s.length() == 1
+            && isOpeningParenthesis(s.charAt(0));
+  }
 
-  private static boolean isOpeningParenthesis(char c)
+  /**
+   * Answers true if the character is a valid close pair rna secondary structure
+   * symbol. Currently accepts a-z, )]}>
+   * 
+   * @param c
+   * @return
+   */
+  public static boolean isClosingParenthesis(char c)
   {
-    return openingParsSet.contains(c);
+    return ('a' <= c && c <= 'z' || c == ')' || c == ']' || c == '}' || c == '>');
   }
 
-  private static boolean isClosingParenthesis(char c)
+  /**
+   * Answers true if the string is a valid close pair rna secondary structure
+   * symbol. Currently accepts a-z, )]}>
+   * 
+   * @param s
+   * @return
+   */
+  public static boolean isClosingParenthesis(String s)
   {
-    return closingParsSet.contains(c);
+    return s != null && s.length() == 1
+            && isClosingParenthesis(s.charAt(0));
   }
 
-  private static char matchingOpeningParenthesis(char closingParenthesis)
-          throws WUSSParseException
+  /**
+   * Returns the matching open pair symbol for the given closing symbol.
+   * Currently returns A-Z for a-z, or ([{< for )]}>, or the input symbol if it
+   * is not a valid closing symbol.
+   * 
+   * @param c
+   * @return
+   */
+  public static char getMatchingOpeningParenthesis(char c)
   {
-    if (!isClosingParenthesis(closingParenthesis))
+    if ('a' <= c && c <= 'z')
     {
-      throw new WUSSParseException(
-              MessageManager.formatMessage(
-                      "exception.querying_matching_opening_parenthesis_for_non_closing_parenthesis",
-                      new String[] { new StringBuffer(closingParenthesis)
-                              .toString() }), -1);
+      return (char) (c + 'A' - 'a');
+    }
+    switch (c)
+    {
+    case ')':
+      return '(';
+    case ']':
+      return '[';
+    case '}':
+      return '{';
+    case '>':
+      return '<';
+    default:
+      return c;
     }
-
-    return closingToOpening.get(closingParenthesis);
   }
 
   /**
    * Based off of RALEE code ralee-get-base-pairs. Keeps track of open bracket
    * positions in "stack" vector. When a close bracket is reached, pair this
-   * with the last element in the "stack" vector and store in "pairs" vector.
-   * Remove last element in the "stack" vector. Continue in this manner until
-   * the whole string is processed.
+   * with the last matching element in the "stack" vector and store in "pairs"
+   * vector. Remove last element in the "stack" vector. Continue in this manner
+   * until the whole string is processed. Parse errors are thrown as exceptions
+   * wrapping the error location - position of the first unmatched closing
+   * bracket, or string length if there is an unmatched opening bracket.
    * 
    * @param line
    *          Secondary structure line of an RNA Stockholm file
-   * @return Array of SequenceFeature; type = RNA helix, begin is open base
-   *         pair, end is close base pair
+   * @return
+   * @throw {@link WUSSParseException}
    */
-  public static Vector<SimpleBP> GetSimpleBPs(CharSequence line)
+  public static Vector<SimpleBP> getSimpleBPs(CharSequence line)
           throws WUSSParseException
   {
     Hashtable<Character, Stack<Integer>> stacks = new Hashtable<Character, Stack<Integer>>();
@@ -128,13 +154,13 @@ public class Rna
       else if (isClosingParenthesis(base))
       {
 
-        char opening = matchingOpeningParenthesis(base);
+        char opening = getMatchingOpeningParenthesis(base);
 
         if (!stacks.containsKey(opening))
         {
           throw new WUSSParseException(MessageManager.formatMessage(
                   "exception.mismatched_unseen_closing_char",
-                  new String[] { new StringBuffer(base).toString() }), i);
+                  new String[] { String.valueOf(base) }), i);
         }
 
         Stack<Integer> stack = stacks.get(opening);
@@ -143,7 +169,7 @@ public class Rna
           // error whilst parsing i'th position. pass back
           throw new WUSSParseException(MessageManager.formatMessage(
                   "exception.mismatched_closing_char",
-                  new String[] { new StringBuffer(base).toString() }), i);
+                  new String[] { String.valueOf(base) }), i);
         }
         int temp = stack.pop();
 
@@ -156,33 +182,36 @@ public class Rna
       Stack<Integer> stack = stacks.get(opening);
       if (!stack.empty())
       {
+        /*
+         * we have an unmatched opening bracket; report error as at
+         * i (length of input string)
+         */
         throw new WUSSParseException(MessageManager.formatMessage(
                 "exception.mismatched_opening_char",
-                new String[] { new StringBuffer(opening).toString(),
-                    Integer.valueOf(stack.pop()).toString() }), i);
+                new String[] { String.valueOf(opening),
+                    String.valueOf(stack.pop()) }), i);
       }
     }
     return pairs;
   }
 
-  public static SequenceFeature[] GetBasePairs(CharSequence line)
+  public static SequenceFeature[] getBasePairs(List<SimpleBP> bps)
           throws WUSSParseException
   {
-    Vector<SimpleBP> bps = GetSimpleBPs(line);
     SequenceFeature[] outPairs = new SequenceFeature[bps.size()];
     for (int p = 0; p < bps.size(); p++)
     {
-      SimpleBP bp = bps.elementAt(p);
+      SimpleBP bp = bps.get(p);
       outPairs[p] = new SequenceFeature("RNA helix", "", "", bp.getBP5(),
               bp.getBP3(), "");
     }
     return outPairs;
   }
 
-  public static ArrayList<SimpleBP> GetModeleBP(CharSequence line)
+  public static List<SimpleBP> getModeleBP(CharSequence line)
           throws WUSSParseException
   {
-    Vector<SimpleBP> bps = GetSimpleBPs(line);
+    Vector<SimpleBP> bps = getSimpleBPs(line);
     return new ArrayList<SimpleBP>(bps);
   }
 
@@ -220,8 +249,8 @@ public class Rna
     int close; // Position of a close bracket under review
     int j; // Counter
 
-    Hashtable helices = new Hashtable(); // Keep track of helix number for each
-                                         // position
+    Hashtable<Integer, Integer> helices = new Hashtable<Integer, Integer>();
+    // Keep track of helix number for each position
 
     // Go through each base pair and assign positions a helix
     for (i = 0; i < pairs.length; i++)
@@ -255,7 +284,7 @@ public class Rna
         if ((popen < lastopen) && (popen > open))
         {
           if (helices.containsValue(popen)
-                  && (((Integer) helices.get(popen)) == helix))
+                  && ((helices.get(popen)) == helix))
           {
             continue;
           }
@@ -281,4 +310,194 @@ public class Rna
 
     }
   }
+
+  /**
+   * Answers true if the character is a recognised symbol for RNA secondary
+   * structure. Currently accepts a-z, A-Z, ()[]{}<>.
+   * 
+   * @param c
+   * @return
+   */
+  public static boolean isRnaSecondaryStructureSymbol(char c)
+  {
+    return isOpeningParenthesis(c) || isClosingParenthesis(c);
+  }
+
+  /**
+   * Answers true if the string is a recognised symbol for RNA secondary
+   * structure. Currently accepts a-z, A-Z, ()[]{}<>.
+   * 
+   * @param s
+   * @return
+   */
+  public static boolean isRnaSecondaryStructureSymbol(String s)
+  {
+    return isOpeningParenthesis(s) || isClosingParenthesis(s);
+  }
+
+  /**
+   * Translates a string to RNA secondary structure representation. Returns the
+   * string with any non-SS characters changed to spaces. Accepted characters
+   * are a-z, A-Z, and (){}[]<> brackets.
+   * 
+   * @param ssString
+   * @return
+   */
+  public static String getRNASecStrucState(String ssString)
+  {
+    if (ssString == null)
+    {
+      return null;
+    }
+    StringBuilder result = new StringBuilder(ssString.length());
+    for (int i = 0; i < ssString.length(); i++)
+    {
+      char c = ssString.charAt(i);
+      result.append(isRnaSecondaryStructureSymbol(c) ? c : " ");
+    }
+    return result.toString();
+  }
+
+  /**
+   * Answers true if the base-pair is either a Watson-Crick (A:T/U, C:G) or a
+   * wobble (G:T/U) pair (either way round), else false
+   * 
+   * @param first
+   * @param second
+   * @return
+   */
+  public static boolean isCanonicalOrWobblePair(char first, char second)
+  {
+    if (first > 'Z')
+    {
+      first -= 32;
+    }
+    if (second > 'Z')
+    {
+      second -= 32;
+    }
+
+    switch (first)
+    {
+    case 'A':
+      switch (second)
+      {
+      case 'T':
+      case 'U':
+        return true;
+      }
+      break;
+    case 'C':
+      switch (second)
+      {
+      case 'G':
+        return true;
+      }
+      break;
+    case 'T':
+    case 'U':
+      switch (second)
+      {
+      case 'A':
+      case 'G':
+        return true;
+      }
+      break;
+    case 'G':
+      switch (second)
+      {
+      case 'C':
+      case 'T':
+      case 'U':
+        return true;
+      }
+      break;
+    }
+    return false;
+  }
+
+  /**
+   * Answers true if the base-pair is Watson-Crick - (A:T/U or C:G, either way
+   * round), else false
+   * 
+   * @param first
+   * @param second
+   * @return
+   */
+  public static boolean isCanonicalPair(char first, char second)
+  {
+
+    if (first > 'Z')
+    {
+      first -= 32;
+    }
+    if (second > 'Z')
+    {
+      second -= 32;
+    }
+
+    switch (first)
+    {
+    case 'A':
+      switch (second)
+      {
+      case 'T':
+      case 'U':
+        return true;
+      }
+      break;
+    case 'G':
+      switch (second)
+      {
+      case 'C':
+        return true;
+      }
+      break;
+    case 'C':
+      switch (second)
+      {
+      case 'G':
+        return true;
+      }
+      break;
+    case 'T':
+    case 'U':
+      switch (second)
+      {
+      case 'A':
+        return true;
+      }
+      break;
+    }
+    return false;
+  }
+
+  /**
+   * Returns the matching close pair symbol for the given opening symbol.
+   * Currently returns a-z for A-Z, or )]}> for ([{<, or the input symbol if it
+   * is not a valid opening symbol.
+   * 
+   * @param c
+   * @return
+   */
+  public static char getMatchingClosingParenthesis(char c)
+  {
+    if ('A' <= c && c <= 'Z')
+    {
+      return (char) (c + 'a' - 'A');
+    }
+    switch (c)
+    {
+    case '(':
+      return ')';
+    case '[':
+      return ']';
+    case '{':
+      return '}';
+    case '<':
+      return '>';
+    default:
+      return c;
+    }
+  }
 }
diff --git a/src/jalview/analysis/SecStrConsensus.java b/src/jalview/analysis/SecStrConsensus.java
index bf75811..d7eadb1 100644
--- a/src/jalview/analysis/SecStrConsensus.java
+++ b/src/jalview/analysis/SecStrConsensus.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/analysis/SeqsetUtils.java b/src/jalview/analysis/SeqsetUtils.java
index c742892..1d9873c 100644
--- a/src/jalview/analysis/SeqsetUtils.java
+++ b/src/jalview/analysis/SeqsetUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -59,13 +59,19 @@ public class SeqsetUtils
         sfeat.addElement(sfarray[i]);
       }
     }
-    sqinfo.put("SeqFeatures", sfeat);
-    sqinfo.put("PdbId",
-            (seq.getAllPDBEntries() != null) ? seq.getAllPDBEntries()
-                    : new Vector<PDBEntry>());
-    sqinfo.put("datasetSequence",
-            (seq.getDatasetSequence() != null) ? seq.getDatasetSequence()
-                    : new Sequence("THISISAPLACEHOLDER", ""));
+    if (seq.getDatasetSequence() == null)
+    {
+      sqinfo.put("SeqFeatures", sfeat);
+      sqinfo.put("PdbId",
+              (seq.getAllPDBEntries() != null) ? seq.getAllPDBEntries()
+                      : new Vector<PDBEntry>());
+    }
+    else
+    {
+      sqinfo.put("datasetSequence",
+              (seq.getDatasetSequence() != null) ? seq.getDatasetSequence()
+                      : new Sequence("THISISAPLACEHOLDER", ""));
+    }
     return sqinfo;
   }
 
@@ -129,6 +135,11 @@ public class SeqsetUtils
             && !(seqds.getName().equals("THISISAPLACEHOLDER") && seqds
                     .getLength() == 0))
     {
+      if (sfeatures != null)
+      {
+        System.err
+                .println("Implementation error: setting dataset sequence for a sequence which has sequence features.\n\tDataset sequence features will not be visible.");
+      }
       sq.setDatasetSequence(seqds);
     }
 
diff --git a/src/jalview/analysis/SequenceIdMatcher.java b/src/jalview/analysis/SequenceIdMatcher.java
index c029ea9..d5f00ec 100644
--- a/src/jalview/analysis/SequenceIdMatcher.java
+++ b/src/jalview/analysis/SequenceIdMatcher.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -46,7 +46,7 @@ public class SequenceIdMatcher
   }
 
   /**
-   * add more sequences to this matcher - also used by the constructor
+   * Adds sequences to this matcher
    * 
    * @param seqs
    */
@@ -54,26 +54,36 @@ public class SequenceIdMatcher
   {
     for (SequenceI seq : seqs)
     {
-      // TODO: deal with ID collisions - SequenceI should be appended to list
-      // associated with this key.
-      names.put(new SeqIdName(seq.getDisplayId(true)), seq);
-      SequenceI dbseq = seq;
-      while (dbseq.getDatasetSequence() != null)
-      {
-        dbseq = dbseq.getDatasetSequence();
-      }
-      // add in any interesting identifiers
-      if (dbseq.getDBRef() != null)
+      add(seq);
+    }
+  }
+
+  /**
+   * Adds one sequence to this matcher
+   * 
+   * @param seq
+   */
+  public void add(SequenceI seq)
+  {
+    // TODO: deal with ID collisions - SequenceI should be appended to list
+    // associated with this key.
+    names.put(new SeqIdName(seq.getDisplayId(true)), seq);
+    SequenceI dbseq = seq;
+    while (dbseq.getDatasetSequence() != null)
+    {
+      dbseq = dbseq.getDatasetSequence();
+    }
+    // add in any interesting identifiers
+    if (dbseq.getDBRefs() != null)
+    {
+      DBRefEntry dbr[] = dbseq.getDBRefs();
+      SeqIdName sid = null;
+      for (int r = 0; r < dbr.length; r++)
       {
-        DBRefEntry dbr[] = dbseq.getDBRef();
-        SeqIdName sid = null;
-        for (int r = 0; r < dbr.length; r++)
+        sid = new SeqIdName(dbr[r].getAccessionId());
+        if (!names.containsKey(sid))
         {
-          sid = new SeqIdName(dbr[r].getAccessionId());
-          if (!names.containsKey(sid))
-          {
-            names.put(sid, seq);
-          }
+          names.put(sid, seq);
         }
       }
     }
@@ -272,7 +282,7 @@ public class SequenceIdMatcher
     return r;
   }
 
-  private class SeqIdName
+  class SeqIdName
   {
     String id;
 
@@ -280,7 +290,7 @@ public class SequenceIdMatcher
     {
       if (s != null)
       {
-        id = new String(s);
+        id = s.toLowerCase();
       }
       else
       {
@@ -304,13 +314,13 @@ public class SequenceIdMatcher
       }
       if (s instanceof SeqIdName)
       {
-        return this.equals((SeqIdName) s);
+        return this.stringequals(((SeqIdName) s).id);
       }
       else
       {
         if (s instanceof String)
         {
-          return this.equals((String) s);
+          return this.stringequals(((String) s).toLowerCase());
         }
       }
 
@@ -332,26 +342,9 @@ public class SequenceIdMatcher
      * todo: (JBPNote) Set separator characters appropriately
      * 
      * @param s
-     *          SeqIdName
      * @return boolean
      */
-    public boolean equals(SeqIdName s)
-    {
-      // TODO: JAL-732 patch for cases when name includes a list of IDs, and the
-      // match contains one ID flanked
-      if (id.length() > s.id.length())
-      {
-        return id.startsWith(s.id) ? (WORD_SEP.indexOf(id.charAt(s.id
-                .length())) > -1) : false;
-      }
-      else
-      {
-        return s.id.startsWith(id) ? (s.id.equals(id) ? true : (WORD_SEP
-                .indexOf(s.id.charAt(id.length())) > -1)) : false;
-      }
-    }
-
-    public boolean equals(String s)
+    private boolean stringequals(String s)
     {
       if (id.length() > s.length())
       {
@@ -364,5 +357,15 @@ public class SequenceIdMatcher
                 .indexOf(s.charAt(id.length())) > -1)) : false;
       }
     }
+
+    /**
+     * toString method returns the wrapped sequence id. For debugging purposes
+     * only, behaviour not guaranteed not to change.
+     */
+    @Override
+    public String toString()
+    {
+      return id;
+    }
   }
 }
diff --git a/src/jalview/analysis/StructureFrequency.java b/src/jalview/analysis/StructureFrequency.java
index 0fe85d9..52831d8 100644
--- a/src/jalview/analysis/StructureFrequency.java
+++ b/src/jalview/analysis/StructureFrequency.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -24,6 +24,7 @@ import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.util.Comparison;
 import jalview.util.Format;
 
 import java.util.ArrayList;
@@ -103,23 +104,24 @@ public class StructureFrequency
 
     SequenceFeature[] rna = rnaStruc._rnasecstr;
     char c, s, cEnd;
-    int count = 0, nonGap = 0, i, bpEnd = -1, j, jSize = sequences.length;
+    int bpEnd = -1;
+    int jSize = sequences.length;
     int[] values;
     int[][] pairs;
     float percentage;
-    boolean wooble = true;
-    for (i = start; i < end; i++) // foreach column
+
+    for (int i = start; i < end; i++) // foreach column
     {
-      residueHash = new Hashtable();
+      int canonicalOrWobblePairCount = 0, canonical = 0;
+      int otherPairCount = 0;
+      int nongap = 0;
       maxResidue = "-";
       values = new int[255];
       pairs = new int[255][255];
       bpEnd = -1;
-      // System.out.println("s="+struc[i]);
       if (i < struc.length)
       {
         s = struc[i];
-
       }
       else
       {
@@ -130,7 +132,7 @@ public class StructureFrequency
         s = '-';
       }
 
-      if (s != '(' && s != '[')
+      if (!Rna.isOpeningParenthesis(s))
       {
         if (s == '-')
         {
@@ -139,12 +141,11 @@ public class StructureFrequency
       }
       else
       {
-
         bpEnd = findPair(rna, i);
 
         if (bpEnd > -1)
         {
-          for (j = 0; j < jSize; j++) // foreach row
+          for (int j = 0; j < jSize; j++) // foreach row
           {
             if (sequences[j] == null)
             {
@@ -152,45 +153,45 @@ public class StructureFrequency
                       .println("WARNING: Consensus skipping null sequence - possible race condition.");
               continue;
             }
-            c = sequences[j].getCharAt(i);
-            // System.out.println("c="+c);
 
-            // standard representation for gaps in sequence and structure
-            if (c == '.' || c == ' ')
-            {
-              c = '-';
-            }
+            c = sequences[j].getCharAt(i);
+            cEnd = sequences[j].getCharAt(bpEnd);
 
-            if (c == '-')
+            if (Comparison.isGap(c) || Comparison.isGap(cEnd))
             {
               values['-']++;
               continue;
             }
-            cEnd = sequences[j].getCharAt(bpEnd);
-
-            // System.out.println("pairs ="+c+","+cEnd);
-            if (checkBpType(c, cEnd) == true)
+            nongap++;
+            /*
+             * ensure upper-case for counting purposes
+             */
+            if ('a' <= c && 'z' >= c)
             {
-              values['(']++; // H means it's a helix (structured)
-              maxResidue = "(";
-              wooble = true;
-              // System.out.println("It's a pair wc");
-
+              c += 'A' - 'a';
             }
-            if (checkBpType(c, cEnd) == false)
+            if ('a' <= cEnd && 'z' >= cEnd)
             {
-              wooble = false;
-              values['[']++; // H means it's a helix (structured)
-              maxResidue = "[";
-
+              cEnd += 'A' - 'a';
+            }
+            if (Rna.isCanonicalOrWobblePair(c, cEnd))
+            {
+              canonicalOrWobblePairCount++;
+              if (Rna.isCanonicalPair(c, cEnd))
+              {
+                canonical++;
+              }
+            }
+            else
+            {
+              otherPairCount++;
             }
             pairs[c][cEnd]++;
-
           }
         }
-        // nonGap++;
       }
-      // UPDATE this for new values
+
+      residueHash = new Hashtable();
       if (profile)
       {
         // TODO 1-dim array with jsize in [0], nongapped in [1]; or Pojo
@@ -199,13 +200,30 @@ public class StructureFrequency
 
         residueHash.put(PAIRPROFILE, pairs);
       }
-      if (wooble == true)
-      {
-        count = values['('];
-      }
-      if (wooble == false)
+      values['('] = canonicalOrWobblePairCount;
+      values['['] = canonical;
+      values['{'] = otherPairCount;
+      /*
+       * the count is the number of valid pairs (as a percentage, determines
+       * the relative size of the profile logo)
+       */
+      int count = canonicalOrWobblePairCount;
+
+      /*
+       * display '(' if most pairs are canonical, or as
+       * '[' if there are more wobble pairs. 
+       */
+      if (canonicalOrWobblePairCount > 0 || otherPairCount > 0)
       {
-        count = values['['];
+        if (canonicalOrWobblePairCount >= otherPairCount)
+        {
+          maxResidue = (canonicalOrWobblePairCount - canonical) < canonical ? "("
+                  : "[";
+        }
+        else
+        {
+          maxResidue = "{";
+        }
       }
       residueHash.put(MAXCOUNT, new Integer(count));
       residueHash.put(MAXRESIDUE, maxResidue);
@@ -213,8 +231,9 @@ public class StructureFrequency
       percentage = ((float) count * 100) / jSize;
       residueHash.put(PID_GAPS, new Float(percentage));
 
-      // percentage = ((float) count * 100) / (float) nongap;
-      // residueHash.put(PID_NOGAPS, new Float(percentage));
+      percentage = ((float) count * 100) / nongap;
+      residueHash.put(PID_NOGAPS, new Float(percentage));
+
       if (result[i] == null)
       {
         result[i] = residueHash;
@@ -223,19 +242,14 @@ public class StructureFrequency
       {
         values[')'] = values['('];
         values[']'] = values['['];
+        values['}'] = values['{'];
         values['('] = 0;
         values['['] = 0;
+        values['{'] = 0;
+        maxResidue = maxResidue.equals("(") ? ")"
+                : maxResidue.equals("[") ? "]" : "}";
+
         residueHash = new Hashtable();
-        if (wooble == true)
-        {
-          // System.out.println(maxResidue+","+wooble);
-          maxResidue = ")";
-        }
-        if (wooble == false)
-        {
-          // System.out.println(maxResidue+","+wooble);
-          maxResidue = "]";
-        }
         if (profile)
         {
           residueHash.put(PROFILE, new int[][] { values,
@@ -250,81 +264,12 @@ public class StructureFrequency
         percentage = ((float) count * 100) / jSize;
         residueHash.put(PID_GAPS, new Float(percentage));
 
-        result[bpEnd] = residueHash;
-
-      }
-    }
-  }
-
-  /**
-   * Method to check if a base-pair is a canonical or a wobble bp
-   * 
-   * @param up
-   *          5' base
-   * @param down
-   *          3' base
-   * @return True if it is a canonical/wobble bp
-   */
-  public static boolean checkBpType(char up, char down)
-  {
-    if (up > 'Z')
-    {
-      up -= 32;
-    }
-    if (down > 'Z')
-    {
-      down -= 32;
-    }
+        percentage = ((float) count * 100) / nongap;
+        residueHash.put(PID_NOGAPS, new Float(percentage));
 
-    switch (up)
-    {
-    case 'A':
-      switch (down)
-      {
-      case 'T':
-        return true;
-      case 'U':
-        return true;
-      }
-      break;
-    case 'C':
-      switch (down)
-      {
-      case 'G':
-        return true;
-      }
-      break;
-    case 'T':
-      switch (down)
-      {
-      case 'A':
-        return true;
-      case 'G':
-        return true;
-      }
-      break;
-    case 'G':
-      switch (down)
-      {
-      case 'C':
-        return true;
-      case 'T':
-        return true;
-      case 'U':
-        return true;
-      }
-      break;
-    case 'U':
-      switch (down)
-      {
-      case 'A':
-        return true;
-      case 'G':
-        return true;
+        result[bpEnd] = residueHash;
       }
-      break;
     }
-    return false;
   }
 
   /**
@@ -534,7 +479,7 @@ public class StructureFrequency
       for (String j : test)
       {
         System.out.println(i + "-" + j + ": "
-                + StructureFrequency.checkBpType(i.charAt(0), j.charAt(0)));
+                + Rna.isCanonicalOrWobblePair(i.charAt(0), j.charAt(0)));
       }
     }
   }
diff --git a/src/jalview/analysis/WUSSParseException.java b/src/jalview/analysis/WUSSParseException.java
index b8b4f09..7ba9572 100644
--- a/src/jalview/analysis/WUSSParseException.java
+++ b/src/jalview/analysis/WUSSParseException.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/analysis/scoremodels/FeatureScoreModel.java b/src/jalview/analysis/scoremodels/FeatureScoreModel.java
index 2218a0e..39592fe 100644
--- a/src/jalview/analysis/scoremodels/FeatureScoreModel.java
+++ b/src/jalview/analysis/scoremodels/FeatureScoreModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -23,12 +23,10 @@ package jalview.analysis.scoremodels;
 import jalview.api.analysis.ScoreModelI;
 import jalview.api.analysis.ViewBasedAnalysisI;
 import jalview.datamodel.AlignmentView;
+import jalview.datamodel.SeqCigar;
 import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.util.Comparison;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Hashtable;
 import java.util.List;
 
@@ -48,17 +46,11 @@ public class FeatureScoreModel implements ScoreModelI, ViewBasedAnalysisI
   public float[][] findDistances(AlignmentView seqData)
   {
     int nofeats = 0;
-    List<String> dft = Arrays.asList(fr.getDisplayedFeatureTypes());
-
-    if (dft != null)
-    {
-      nofeats = dft.size();
-    }
-
-    SequenceI[] sequenceString = seqData.getVisibleAlignment(
-            Comparison.GapChars.charAt(0)).getSequencesArray();
-    int noseqs = sequenceString.length;
-    int cpwidth = seqData.getWidth();
+    List<String> dft = fr.getDisplayedFeatureTypes();
+    nofeats = dft.size();
+    SeqCigar[] seqs = seqData.getSequences();
+    int noseqs = seqs.length;
+    int cpwidth = 0;// = seqData.getWidth();
     float[][] distance = new float[noseqs][noseqs];
     if (nofeats == 0)
     {
@@ -71,54 +63,64 @@ public class FeatureScoreModel implements ScoreModelI, ViewBasedAnalysisI
       }
       return distance;
     }
-    float max = 0;
-    for (int cpos = 0; cpos < cpwidth; cpos++)
+    // need to get real position for view position
+    int[] viscont = seqData.getVisibleContigs();
+    for (int vc = 0; vc < viscont.length; vc += 2)
     {
-      // get visible features at cpos under view's display settings and compare
-      // them
-      List<Hashtable<String, SequenceFeature>> sfap = new ArrayList<Hashtable<String, SequenceFeature>>();
-      for (int i = 0; i < noseqs; i++)
-      {
-        Hashtable<String, SequenceFeature> types = new Hashtable<String, SequenceFeature>();
-        List<SequenceFeature> sfs = fr.findFeaturesAtRes(sequenceString[i],
-                sequenceString[i].findPosition(cpos));
-        for (SequenceFeature sf : sfs)
-        {
-          types.put(sf.getType(), sf);
-        }
-        sfap.add(types);
-      }
-      for (int i = 0; i < (noseqs - 1); i++)
+
+      for (int cpos = viscont[vc]; cpos <= viscont[vc + 1]; cpos++)
       {
-        if (cpos == 0)
-        {
-          distance[i][i] = 0f;
-        }
-        for (int j = i + 1; j < noseqs; j++)
+        cpwidth++;
+        // get visible features at cpos under view's display settings and
+        // compare them
+        List<Hashtable<String, SequenceFeature>> sfap = new ArrayList<Hashtable<String, SequenceFeature>>();
+        for (int i = 0; i < noseqs; i++)
         {
-          int sfcommon = 0;
-          // compare the two lists of features...
-          Hashtable<String, SequenceFeature> fi = sfap.get(i), fk, fj = sfap
-                  .get(j);
-          if (fi.size() > fj.size())
+          Hashtable<String, SequenceFeature> types = new Hashtable<String, SequenceFeature>();
+          int spos = seqs[i].findPosition(cpos);
+          if (spos != -1)
           {
-            fk = fj;
+            List<SequenceFeature> sfs = fr.findFeaturesAtRes(
+                    seqs[i].getRefSeq(), spos);
+            for (SequenceFeature sf : sfs)
+            {
+              types.put(sf.getType(), sf);
+            }
           }
-          else
+          sfap.add(types);
+        }
+        for (int i = 0; i < (noseqs - 1); i++)
+        {
+          if (cpos == 0)
           {
-            fk = fi;
-            fi = fj;
+            distance[i][i] = 0f;
           }
-          for (String k : fi.keySet())
+          for (int j = i + 1; j < noseqs; j++)
           {
-            SequenceFeature sfj = fk.get(k);
-            if (sfj != null)
+            int sfcommon = 0;
+            // compare the two lists of features...
+            Hashtable<String, SequenceFeature> fi = sfap.get(i), fk, fj = sfap
+                    .get(j);
+            if (fi.size() > fj.size())
             {
-              sfcommon++;
+              fk = fj;
             }
+            else
+            {
+              fk = fi;
+              fi = fj;
+            }
+            for (String k : fi.keySet())
+            {
+              SequenceFeature sfj = fk.get(k);
+              if (sfj != null)
+              {
+                sfcommon++;
+              }
+            }
+            distance[i][j] += (fi.size() + fk.size() - 2f * sfcommon);
+            distance[j][i] += distance[i][j];
           }
-          distance[i][j] += (fi.size() + fk.size() - 2f * sfcommon);
-          distance[j][i] += distance[i][j];
         }
       }
     }
@@ -151,6 +153,7 @@ public class FeatureScoreModel implements ScoreModelI, ViewBasedAnalysisI
     return true;
   }
 
+  @Override
   public String toString()
   {
     return "Score between sequences based on hamming distance between binary vectors marking features displayed at each column";
diff --git a/src/jalview/analysis/scoremodels/PIDScoreModel.java b/src/jalview/analysis/scoremodels/PIDScoreModel.java
index b297213..151b013 100644
--- a/src/jalview/analysis/scoremodels/PIDScoreModel.java
+++ b/src/jalview/analysis/scoremodels/PIDScoreModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/analysis/scoremodels/PairwiseSeqScoreModel.java b/src/jalview/analysis/scoremodels/PairwiseSeqScoreModel.java
index 46cbee6..291170e 100644
--- a/src/jalview/analysis/scoremodels/PairwiseSeqScoreModel.java
+++ b/src/jalview/analysis/scoremodels/PairwiseSeqScoreModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/analysis/scoremodels/SWScoreModel.java b/src/jalview/analysis/scoremodels/SWScoreModel.java
index 9ba5273..c3b0f05 100644
--- a/src/jalview/analysis/scoremodels/SWScoreModel.java
+++ b/src/jalview/analysis/scoremodels/SWScoreModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/AlignCalcManagerI.java b/src/jalview/api/AlignCalcManagerI.java
index 3261f5b..053daf7 100644
--- a/src/jalview/api/AlignCalcManagerI.java
+++ b/src/jalview/api/AlignCalcManagerI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -35,17 +35,12 @@ public interface AlignCalcManagerI
   void notifyStart(AlignCalcWorkerI worker);
 
   /**
-   * check if a calculation of this type is already active
-   * 
-   * @param worker
-   * @return
-   */
-  boolean alreadyDoing(AlignCalcWorkerI worker);
-
-  /**
-   * tell manager that worker is now processing data
+   * tell manager that a thread running worker's run() loop is ready to start
+   * processing data
    * 
    * @param worker
+   * @return true if worker should start processing, false if another thread is
+   *         in progress
    */
   boolean notifyWorking(AlignCalcWorkerI worker);
 
@@ -63,7 +58,7 @@ public interface AlignCalcManagerI
    * 
    * @param worker
    */
-  void workerCannotRun(AlignCalcWorkerI worker);
+  void disableWorker(AlignCalcWorkerI worker);
 
   /**
    * indicate that a worker like this may be run on the platform.
@@ -71,7 +66,15 @@ public interface AlignCalcManagerI
    * @param worker
    *          of class to be removed from the execution blacklist
    */
-  void workerMayRun(AlignCalcWorkerI worker);
+  void enableWorker(AlignCalcWorkerI worker);
+
+  /**
+   * Answers true if the worker is disabled from running
+   * 
+   * @param worker
+   * @return
+   */
+  boolean isDisabled(AlignCalcWorkerI worker);
 
   /**
    * launch a new worker
@@ -83,7 +86,7 @@ public interface AlignCalcManagerI
   /**
    * 
    * @param worker
-   * @return
+   * @return true if the worker is currently running
    */
   boolean isWorking(AlignCalcWorkerI worker);
 
@@ -120,7 +123,7 @@ public interface AlignCalcManagerI
    * 
    * @param workerClass
    */
-  void updateAnnotationFor(Class workerClass);
+  void updateAnnotationFor(Class<? extends AlignCalcWorkerI> workerClass);
 
   /**
    * return any registered workers of the given class
@@ -128,17 +131,8 @@ public interface AlignCalcManagerI
    * @param workerClass
    * @return null or one or more workers of the given class
    */
-  List<AlignCalcWorkerI> getRegisteredWorkersOfClass(Class workerClass);
-
-  /**
-   * start any workers of the given class
-   * 
-   * @param workerClass
-   * @return false if no workers of given class were registered (note -
-   *         blacklisted classes cannot be restarted, so this method will return
-   *         true for blacklisted workers)
-   */
-  boolean startRegisteredWorkersOfClass(Class workerClass);
+  List<AlignCalcWorkerI> getRegisteredWorkersOfClass(
+          Class<? extends AlignCalcWorkerI> workerClass);
 
   /**
    * work out if there is an instance of a worker that is *waiting* to start
@@ -156,6 +150,15 @@ public interface AlignCalcManagerI
    * 
    * @param typeToRemove
    */
-  void removeRegisteredWorkersOfClass(Class typeToRemove);
+  void removeRegisteredWorkersOfClass(
+          Class<? extends AlignCalcWorkerI> typeToRemove);
 
+  /**
+   * Removes the worker that produces the given annotation, provided it is
+   * marked as 'deletable'. Some workers may need to continue to run as the
+   * results of their calculations are needed, e.g. for colour schemes.
+   * 
+   * @param ann
+   */
+  void removeWorkerForAnnotation(AlignmentAnnotation ann);
 }
diff --git a/src/jalview/api/AlignCalcWorkerI.java b/src/jalview/api/AlignCalcWorkerI.java
index 3145382..0e9ef33 100644
--- a/src/jalview/api/AlignCalcWorkerI.java
+++ b/src/jalview/api/AlignCalcWorkerI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,12 +22,39 @@ package jalview.api;
 
 import jalview.datamodel.AlignmentAnnotation;
 
+/**
+ * Interface describing a worker that calculates alignment annotation(s). The
+ * main (re-)calculation should be performed by the inherited run() method.
+ */
 public interface AlignCalcWorkerI extends Runnable
 {
+  /**
+   * Answers true if this worker updates the given annotation (regardless of its
+   * current state)
+   * 
+   * @param annot
+   * @return
+   */
+  boolean involves(AlignmentAnnotation annot);
 
-  public boolean involves(AlignmentAnnotation annot);
+  /**
+   * Updates the display of calculated annotation values (does not recalculate
+   * the values). This allows ßquick redraw of annotations when display settings
+   * are changed.
+   */
+  void updateAnnotation();
 
-  public void updateAnnotation();
+  /**
+   * Removes any annotation(s) managed by this worker from the alignment
+   */
+  void removeAnnotation();
 
-  void removeOurAnnotation();
+  /**
+   * Answers true if the worker should be deleted entirely when its annotation
+   * is deleted from the display, or false if it should continue to run. Some
+   * workers are required to run for their side-effects.
+   * 
+   * @return
+   */
+  boolean isDeletable();
 }
diff --git a/src/jalview/api/AlignExportSettingI.java b/src/jalview/api/AlignExportSettingI.java
index 72a4164..3037805 100644
--- a/src/jalview/api/AlignExportSettingI.java
+++ b/src/jalview/api/AlignExportSettingI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/AlignViewControllerGuiI.java b/src/jalview/api/AlignViewControllerGuiI.java
index db429bc..8a81571 100644
--- a/src/jalview/api/AlignViewControllerGuiI.java
+++ b/src/jalview/api/AlignViewControllerGuiI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/AlignViewControllerI.java b/src/jalview/api/AlignViewControllerI.java
index d379553..21928cc 100644
--- a/src/jalview/api/AlignViewControllerI.java
+++ b/src/jalview/api/AlignViewControllerI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,8 @@
  */
 package jalview.api;
 
+import java.util.List;
+
 /**
  * prototype abstract controller for a Jalview alignment view
  * 
@@ -62,7 +64,7 @@ public interface AlignViewControllerI
    * @return true if operation affected state
    */
   boolean markColumnsContainingFeatures(boolean invert,
-          boolean extendCurrent, boolean clearColumns, String featureType);
+          boolean extendCurrent, boolean toggle, String featureType);
 
   /**
    * sort the alignment or current selection by average score over the given set
@@ -71,7 +73,7 @@ public interface AlignViewControllerI
    * @param typ
    *          list of feature names or null to use currently displayed features
    */
-  void sortAlignmentByFeatureScore(String[] typ);
+  void sortAlignmentByFeatureScore(List<String> typ);
 
   /**
    * sort the alignment or current selection by distribution of the given set of
@@ -80,7 +82,7 @@ public interface AlignViewControllerI
    * @param typ
    *          list of feature names or null to use currently displayed features
    */
-  void sortAlignmentByFeatureDensity(String[] typ);
+  void sortAlignmentByFeatureDensity(List<String> typ);
 
   /**
    * add a features file of some kind to the current view
@@ -95,4 +97,16 @@ public interface AlignViewControllerI
   public boolean parseFeaturesFile(String file, String protocol,
           boolean relaxedIdMatching);
 
+  /**
+   * mark columns containing highlighted regions (e.g. from search, structure
+   * highlight, or a mouse over event in another viewer)
+   * 
+   * @param invert
+   * @param extendCurrent
+   * @param toggle
+   * @return
+   */
+  boolean markHighlightedColumns(boolean invert, boolean extendCurrent,
+          boolean toggle);
+
 }
diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java
index e533683..c27b32c 100644
--- a/src/jalview/api/AlignViewportI.java
+++ b/src/jalview/api/AlignViewportI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,6 +26,8 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.CigarArray;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.ProfilesI;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -53,6 +55,18 @@ public interface AlignViewportI extends ViewStyleI
    */
   public int calcPanelHeight();
 
+  /**
+   * Answers true if the viewport has at least one column selected
+   * 
+   * @return
+   */
+  boolean hasSelectedColumns();
+
+  /**
+   * Answers true if the viewport has at least one hidden column
+   * 
+   * @return
+   */
   boolean hasHiddenColumns();
 
   boolean isValidCharWidth();
@@ -69,7 +83,7 @@ public interface AlignViewportI extends ViewStyleI
 
   ColumnSelection getColumnSelection();
 
-  Hashtable[] getSequenceConsensusHash();
+  ProfilesI getSequenceConsensusHash();
 
   /**
    * Get consensus data table for the cDNA complement of this alignment (if any)
@@ -110,6 +124,11 @@ public interface AlignViewportI extends ViewStyleI
   boolean isClosed();
 
   /**
+   * Dispose of all references or resources held by the viewport
+   */
+  void dispose();
+
+  /**
    * get the associated calculation thread manager for the view
    * 
    * @return
@@ -127,7 +146,7 @@ public interface AlignViewportI extends ViewStyleI
    * 
    * @param hconsensus
    */
-  void setSequenceConsensusHash(Hashtable[] hconsensus);
+  void setSequenceConsensusHash(ProfilesI hconsensus);
 
   /**
    * Set the cDNA complement consensus for the viewport
@@ -235,11 +254,31 @@ public interface AlignViewportI extends ViewStyleI
    * This method returns the visible alignment as text, as seen on the GUI, ie
    * if columns are hidden they will not be returned in the result. Use this for
    * calculating trees, PCA, redundancy etc on views which contain hidden
+   * columns. This method doesn't exclude hidden sequences from the output.
+   *
+   * @param selectedRegionOnly
+   *          - determines if only the selected region or entire alignment is
+   *          exported
+   * @return String[]
+   */
+  String[] getViewAsString(boolean selectedRegionOnly);
+
+  /**
+   * This method returns the visible alignment as text, as seen on the GUI, ie
+   * if columns are hidden they will not be returned in the result. Use this for
+   * calculating trees, PCA, redundancy etc on views which contain hidden
    * columns.
    * 
+   * @param selectedRegionOnly
+   *          - determines if only the selected region or entire alignment is
+   *          exported
+   * @param isExportHiddenSeqs
+   *          - determines if hidden sequences would be exported or not.
+   * 
    * @return String[]
    */
-  String[] getViewAsString(boolean selectedRegionOnly);
+  String[] getViewAsString(boolean selectedRegionOnly,
+          boolean isExportHiddenSeqs);
 
   void setSelectionGroup(SequenceGroup sg);
 
@@ -375,4 +414,35 @@ public interface AlignViewportI extends ViewStyleI
    * Set whether view should scroll to show the highlighted region of a sequence
    */
   void setFollowHighlight(boolean b);
+
+  public void applyFeaturesStyle(FeatureSettingsModelI featureSettings);
+
+  /**
+   * check if current selection group is defined on the view, or is simply a
+   * temporary group.
+   * 
+   * @return true if group is defined on the alignment
+   */
+  boolean isSelectionDefinedGroup();
+
+  /**
+   * 
+   * @return true if there are search results on the view
+   */
+  boolean hasSearchResults();
+
+  /**
+   * set the search results for the view
+   * 
+   * @param results
+   *          - or null to clear current results
+   */
+  void setSearchResults(SearchResultsI results);
+
+  /**
+   * get search results for this view (if any)
+   * 
+   * @return search results or null
+   */
+  SearchResultsI getSearchResults();
 }
diff --git a/src/jalview/api/AlignmentViewPanel.java b/src/jalview/api/AlignmentViewPanel.java
index 0408fb1..6e0bd04 100644
--- a/src/jalview/api/AlignmentViewPanel.java
+++ b/src/jalview/api/AlignmentViewPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/BuildDetailsI.java b/src/jalview/api/BuildDetailsI.java
index 8b99aa4..7a45c49 100644
--- a/src/jalview/api/BuildDetailsI.java
+++ b/src/jalview/api/BuildDetailsI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/ComplexAlignFile.java b/src/jalview/api/ComplexAlignFile.java
index 4349382..50b3011 100644
--- a/src/jalview/api/ComplexAlignFile.java
+++ b/src/jalview/api/ComplexAlignFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,7 +22,6 @@ package jalview.api;
 
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SequenceI;
-import jalview.schemes.ColourSchemeI;
 
 /**
  * This interface should be implemented by complex file parser with the ability
@@ -44,7 +43,7 @@ public interface ComplexAlignFile
    * 
    * @return
    */
-  public ColourSchemeI getColourScheme();
+  public String getGlobalColourScheme();
 
   /**
    * Retrieves the Column selection/hidden column from a complex file parser
diff --git a/src/jalview/api/DBRefEntryI.java b/src/jalview/api/DBRefEntryI.java
new file mode 100644
index 0000000..0acbb26
--- /dev/null
+++ b/src/jalview/api/DBRefEntryI.java
@@ -0,0 +1,114 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.api;
+
+import jalview.datamodel.Mapping;
+
+//JBPComment: this is a datamodel API - so it should be in datamodel (it's a peer of SequenceI)
+
+public interface DBRefEntryI
+{
+  public boolean equalRef(DBRefEntryI entry);
+
+  /**
+   * 
+   * @return Source DB name for this entry
+   */
+  public String getSource();
+
+  /**
+   * 
+   * @return Accession Id for this entry
+   */
+  public String getAccessionId();
+
+  /**
+   * 
+   * @param accessionId
+   *          Accession Id for this entry
+   */
+  public void setAccessionId(String accessionId);
+
+  /**
+   * 
+   * @param source
+   *          Source DB name for this entry
+   */
+  public void setSource(String source);
+
+  /**
+   * 
+   * @return Source DB version for this entry
+   */
+  public String getVersion();
+
+  /**
+   * 
+   * @param version
+   *          Source DB version for this entry
+   */
+  public void setVersion(String version);
+
+  /**
+   * access a mapping, if present that can be used to map positions from the
+   * associated dataset sequence to the DBRef's sequence frame.
+   * 
+   * @return null or a valid mapping.
+   */
+  public Mapping getMap();
+
+  /**
+   * Answers true if this object is either equivalent to, or can be 'improved'
+   * by, the given entry. Specifically, answers true if
+   * <ul>
+   * <li>source and accession are identical</li>
+   * <li>version is identical, or this version is of the format "someSource:0",
+   * in which case the version for the other entry replaces it</li>
+   * <li>mappings are not compared but if this entry has no mapping, replace
+   * with that for the other entry</li>
+   * </ul>
+   * 
+   * @param otherEntry
+   * @return
+   */
+  public boolean updateFrom(DBRefEntryI otherEntry);
+
+  /**
+   * Answers true if the ref looks like a primary (direct) database reference. <br>
+   * The only way a dbref's mappings can be fully verified is via the local
+   * sequence frame, so rather than use isPrimaryCandidate directly, please use
+   * SequenceI.getPrimaryDbRefs(). <br>
+   * Primary references indicate the local sequence data directly corresponds
+   * with the database record. All other references are secondary. Direct
+   * references indicate that part or all of the local sequence data can be
+   * mapped with another sequence, enabling annotation transfer.
+   * Cross-references indicate the local sequence data can be corresponded to
+   * some other linear coordinate system via a transformation. <br>
+   * This method is also sufficient to distinguish direct DBRefEntry mappings
+   * from other relationships - e.g. coding relationships (imply a 1:3/3:1
+   * mapping), but not transcript relationships, which imply a (possibly
+   * non-contiguous) 1:1 mapping.
+   *
+   * @return true if this reference provides a primary accession for the
+   *         associated sequence object
+   */
+  public boolean isPrimaryCandidate();
+}
diff --git a/src/jalview/api/FeatureColourI.java b/src/jalview/api/FeatureColourI.java
new file mode 100644
index 0000000..8ac6715
--- /dev/null
+++ b/src/jalview/api/FeatureColourI.java
@@ -0,0 +1,181 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.api;
+
+import jalview.datamodel.SequenceFeature;
+
+import java.awt.Color;
+
+public interface FeatureColourI
+{
+
+  /**
+   * Answers true when the feature colour varies across the score range
+   * 
+   * @return
+   */
+  boolean isGraduatedColour();
+
+  /**
+   * Returns the feature colour (when isGraduatedColour answers false)
+   * 
+   * @return
+   */
+  Color getColour();
+
+  /**
+   * Returns the minimum colour (when isGraduatedColour answers true)
+   * 
+   * @return
+   */
+  Color getMinColour();
+
+  /**
+   * Returns the maximum colour (when isGraduatedColour answers true)
+   * 
+   * @return
+   */
+  Color getMaxColour();
+
+  /**
+   * Answers true if the feature has a single colour, i.e. if isColourByLabel()
+   * and isGraduatedColour() both answer false
+   * 
+   * @return
+   */
+  boolean isSimpleColour();
+
+  /**
+   * Answers true if the feature is coloured by label (description)
+   * 
+   * @return
+   */
+  boolean isColourByLabel();
+
+  void setColourByLabel(boolean b);
+
+  /**
+   * Answers true if the feature is coloured below a threshold value; only
+   * applicable when isGraduatedColour answers true
+   * 
+   * @return
+   */
+  boolean isBelowThreshold();
+
+  void setBelowThreshold(boolean b);
+
+  /**
+   * Answers true if the feature is coloured above a threshold value; only
+   * applicable when isGraduatedColour answers true
+   * 
+   * @return
+   */
+  boolean isAboveThreshold();
+
+  void setAboveThreshold(boolean b);
+
+  /**
+   * Answers true if the threshold is the minimum value (when
+   * isAboveThreshold()) or maximum value (when isBelowThreshold()) of the
+   * colour range; only applicable when isGraduatedColour and either
+   * isAboveThreshold() or isBelowThreshold() answers true
+   * 
+   * @return
+   */
+  boolean isThresholdMinMax();
+
+  void setThresholdMinMax(boolean b);
+
+  /**
+   * Returns the threshold value (if any), else zero
+   * 
+   * @return
+   */
+  float getThreshold();
+
+  void setThreshold(float f);
+
+  /**
+   * Answers true if the colour varies between the actual minimum and maximum
+   * score values of the feature, or false if between absolute minimum and
+   * maximum values (or if not a graduated colour).
+   * 
+   * @return
+   */
+  boolean isAutoScaled();
+
+  void setAutoScaled(boolean b);
+
+  /**
+   * Returns the maximum score of the graduated colour range
+   * 
+   * @return
+   */
+  float getMax();
+
+  /**
+   * Returns the minimum score of the graduated colour range
+   * 
+   * @return
+   */
+  float getMin();
+
+  /**
+   * Answers true if either isAboveThreshold or isBelowThreshold answers true
+   * 
+   * @return
+   */
+  boolean hasThreshold();
+
+  /**
+   * Returns the computed colour for the given sequence feature
+   * 
+   * @param feature
+   * @return
+   */
+  Color getColor(SequenceFeature feature);
+
+  /**
+   * Answers true if the feature has a simple colour, or is coloured by label,
+   * or has a graduated colour and the score of this feature instance is within
+   * the range to render (if any), i.e. does not lie below or above any
+   * threshold set.
+   * 
+   * @param feature
+   * @return
+   */
+  boolean isColored(SequenceFeature feature);
+
+  /**
+   * Update the min-max range for a graduated colour scheme
+   * 
+   * @param min
+   * @param max
+   */
+  void updateBounds(float min, float max);
+
+  /**
+   * Returns the colour in Jalview features file format
+   * 
+   * @return
+   */
+  String toJalviewFormat(String featureType);
+}
diff --git a/src/jalview/api/FeatureRenderer.java b/src/jalview/api/FeatureRenderer.java
index d5adea6..559767a 100644
--- a/src/jalview/api/FeatureRenderer.java
+++ b/src/jalview/api/FeatureRenderer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -60,17 +60,15 @@ public interface FeatureRenderer
    * @param ft
    * @return display style for a feature
    */
-  Object getFeatureStyle(String ft);
+  FeatureColourI getFeatureStyle(String ft);
 
   /**
    * update the feature style for a particular feature
    * 
    * @param ft
    * @param ggc
-   *          - currently allows java.awt.Color and
-   *          jalview.schemes.GraduatedColor
    */
-  void setColour(String ft, Object ggc);
+  void setColour(String ft, FeatureColourI ggc);
 
   AlignViewportI getViewport();
 
@@ -85,7 +83,7 @@ public interface FeatureRenderer
    * 
    * @return
    */
-  Map<String, Object> getFeatureColours();
+  Map<String, FeatureColourI> getFeatureColours();
 
   /**
    * query the alignment view to find all features
@@ -100,7 +98,7 @@ public interface FeatureRenderer
    * 
    * @return
    */
-  Map<String, Object> getDisplayedFeatureCols();
+  Map<String, FeatureColourI> getDisplayedFeatureCols();
 
   /**
    * get all registered groups
@@ -134,7 +132,8 @@ public interface FeatureRenderer
   void setGroupVisibility(String group, boolean visible);
 
   /**
-   * locate features at a particular position on the given sequence
+   * Returns features at the specified position on the given sequence.
+   * Non-positional features are not included.
    * 
    * @param sequence
    * @param res
@@ -143,25 +142,19 @@ public interface FeatureRenderer
   List<SequenceFeature> findFeaturesAtRes(SequenceI sequence, int res);
 
   /**
+   * get current displayed types, in ordering of rendering (on top last)
    * 
-   * @return true if the rendering platform supports transparency
+   * @return a (possibly empty) list of feature types
    */
-  boolean isTransparencyAvailable();
 
-  /**
-   * get current displayed types
-   * 
-   * @return
-   */
-
-  String[] getDisplayedFeatureTypes();
+  List<String> getDisplayedFeatureTypes();
 
   /**
    * get current displayed groups
    * 
-   * @return
+   * @return a (possibly empty) list of feature groups
    */
-  String[] getDisplayedFeatureGroups();
+  List<String> getDisplayedFeatureGroups();
 
   /**
    * display all features of these types
diff --git a/src/jalview/api/FeatureSettingsControllerI.java b/src/jalview/api/FeatureSettingsControllerI.java
index e2701f2..08c5ef7 100644
--- a/src/jalview/api/FeatureSettingsControllerI.java
+++ b/src/jalview/api/FeatureSettingsControllerI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/FeatureSettingsModelI.java b/src/jalview/api/FeatureSettingsModelI.java
index 7bf680e..01b54be 100644
--- a/src/jalview/api/FeatureSettingsModelI.java
+++ b/src/jalview/api/FeatureSettingsModelI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,76 @@
  */
 package jalview.api;
 
-public interface FeatureSettingsModelI
+import java.util.Comparator;
+
+/**
+ * An interface that describes the settings configurable in the Feature Settings
+ * dialog.
+ * 
+ * @author gmcarstairs
+ */
+public interface FeatureSettingsModelI extends Comparator<String>
 {
 
+  // note Java 8 will allow default implementations of these methods in the
+  // interface, simplifying instantiating classes
+
+  /**
+   * Answers true if the specified feature type is displayed
+   * 
+   * @param type
+   * @return
+   */
+  boolean isFeatureDisplayed(String type);
+
+  /**
+   * Answers true if the specified feature group is displayed
+   * 
+   * @param group
+   * @return
+   */
+  boolean isGroupDisplayed(String group);
+
+  /**
+   * Returns the colour (or graduated colour) for the feature type, or null if
+   * not known
+   * 
+   * @param type
+   * @return
+   */
+  FeatureColourI getFeatureColour(String type);
+
+  /**
+   * Returns the transparency value, from 0 (fully transparent) to 1 (fully
+   * opaque)
+   * 
+   * @return
+   */
+  float getTransparency();
+
+  /**
+   * Returns -1 if feature1 is displayed before (below) feature 2, +1 if
+   * feature2 is displayed after (on top of) feature1, or 0 if we don't care.
+   * 
+   * <br>
+   * Note that this is the opposite ordering to how features are displayed in
+   * the feature settings dialogue. FeatureRendererModel.setFeaturePriority
+   * takes care of converting between the two.
+   * 
+   * @param feature1
+   * @param feature2
+   * @return
+   */
+  @Override
+  int compare(String feature1, String feature2);
+
+  /**
+   * Answers true if features should be initially sorted so that features with a
+   * shorter average length are displayed on top of those with a longer average
+   * length
+   * 
+   * @return
+   */
+  boolean optimiseOrder();
+
 }
diff --git a/src/jalview/api/FeaturesDisplayedI.java b/src/jalview/api/FeaturesDisplayedI.java
index a7dc52f..3a15928 100644
--- a/src/jalview/api/FeaturesDisplayedI.java
+++ b/src/jalview/api/FeaturesDisplayedI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -44,6 +44,6 @@ public interface FeaturesDisplayedI
 
   int getVisibleFeatureCount();
 
-  int getRegisterdFeaturesCount();
+  int getRegisteredFeaturesCount();
 
 }
diff --git a/src/jalview/api/FeatureSettingsModelI.java b/src/jalview/api/FeaturesSourceI.java
similarity index 78%
copy from src/jalview/api/FeatureSettingsModelI.java
copy to src/jalview/api/FeaturesSourceI.java
index 7bf680e..7900d71 100644
--- a/src/jalview/api/FeatureSettingsModelI.java
+++ b/src/jalview/api/FeaturesSourceI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,9 @@
  */
 package jalview.api;
 
-public interface FeatureSettingsModelI
+/**
+ * A tagging interface to mark a source of sequence features
+ */
+public interface FeaturesSourceI
 {
-
 }
diff --git a/src/jalview/api/OOMHandlerI.java b/src/jalview/api/OOMHandlerI.java
index 23d018c..71c5cdf 100644
--- a/src/jalview/api/OOMHandlerI.java
+++ b/src/jalview/api/OOMHandlerI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/RotatableCanvasI.java b/src/jalview/api/RotatableCanvasI.java
index d28693a..69a041a 100644
--- a/src/jalview/api/RotatableCanvasI.java
+++ b/src/jalview/api/RotatableCanvasI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/SequenceRenderer.java b/src/jalview/api/SequenceRenderer.java
index 55efb8d..0a79f58 100644
--- a/src/jalview/api/SequenceRenderer.java
+++ b/src/jalview/api/SequenceRenderer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/SequenceStructureBinding.java b/src/jalview/api/SequenceStructureBinding.java
index d1e658a..d128b39 100644
--- a/src/jalview/api/SequenceStructureBinding.java
+++ b/src/jalview/api/SequenceStructureBinding.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/SiftsClientI.java b/src/jalview/api/SiftsClientI.java
new file mode 100644
index 0000000..48f4769
--- /dev/null
+++ b/src/jalview/api/SiftsClientI.java
@@ -0,0 +1,132 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.api;
+
+import jalview.datamodel.SequenceI;
+import jalview.structure.StructureMapping;
+import jalview.ws.sifts.MappingOutputPojo;
+import jalview.ws.sifts.SiftsException;
+import jalview.xml.binding.sifts.Entry.Entity;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+// JBPComment: this isn't a top-level Jalview API - should be in its own package api
+
+public interface SiftsClientI
+{
+  /**
+   * Get the DB Accession Id for the SIFTs Entry
+   * 
+   * @return
+   */
+  public String getDbAccessionId();
+
+  /**
+   * Get DB Coordinate system for the SIFTs Entry
+   * 
+   * @return
+   */
+  public String getDbCoordSys();
+
+  /**
+   * Get DB Source for the SIFTs Entry
+   * 
+   * @return
+   */
+  public String getDbSource();
+
+  /**
+   * Get DB version for the SIFTs Entry
+   * 
+   * @return
+   */
+  public String getDbVersion();
+
+  /**
+   * Get Number of Entities available in the SIFTs Entry
+   * 
+   * @return
+   */
+  public int getEntityCount();
+
+  /**
+   * Get a unique Entity by its Id
+   * 
+   * @param id
+   *          ID of the entity to fetch
+   * @return Entity
+   * @throws Exception
+   */
+  public Entity getEntityById(String id) throws SiftsException;
+
+  /**
+   * Get all accession Ids available in the current SIFTs entry
+   * 
+   * @return a unique set of discovered accession strings
+   */
+  public HashSet<String> getAllMappingAccession();
+
+  /**
+   * Check if the accessionId is available in current SIFTs Entry
+   * 
+   * @param accessionId
+   * @return
+   */
+  public boolean isAccessionMatched(String accessionId);
+
+  /**
+   * 
+   * @param mop
+   *          MappingOutputPojo
+   * @return Sequence<->Structure mapping as int[][]
+   * @throws SiftsException
+   */
+  public StringBuffer getMappingOutput(MappingOutputPojo mop)
+          throws SiftsException;
+
+  /**
+   * 
+   * @param seq
+   *          sequence to generate mapping against the structure
+   * @param pdbFile
+   *          PDB file for the mapping
+   * @param chain
+   *          the chain of the entry to use for mapping
+   * @return StructureMapping
+   * @throws SiftsException
+   */
+  public StructureMapping getSiftsStructureMapping(SequenceI seq,
+          String pdbFile, String chain) throws SiftsException;
+
+  /**
+   * Get residue by residue mapping for a given Sequence and SIFTs entity
+   * 
+   * @param entityId
+   *          Id of the target entity in the SIFTs entry
+   * @param seq
+   *          SequenceI
+   * @return generated mapping
+   * @throws Exception
+   */
+  public HashMap<Integer, int[]> getGreedyMapping(String entityId,
+          SequenceI seq, java.io.PrintStream os) throws SiftsException;
+}
\ No newline at end of file
diff --git a/src/jalview/api/SplitContainerI.java b/src/jalview/api/SplitContainerI.java
index 23439c6..ca50c1b 100644
--- a/src/jalview/api/SplitContainerI.java
+++ b/src/jalview/api/SplitContainerI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/StructureSelectionManagerProvider.java b/src/jalview/api/StructureSelectionManagerProvider.java
index 89a2df0..6b0675d 100644
--- a/src/jalview/api/StructureSelectionManagerProvider.java
+++ b/src/jalview/api/StructureSelectionManagerProvider.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/ViewStyleI.java b/src/jalview/api/ViewStyleI.java
index 8b313dc..db86246 100644
--- a/src/jalview/api/ViewStyleI.java
+++ b/src/jalview/api/ViewStyleI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/analysis/ScoreModelI.java b/src/jalview/api/analysis/ScoreModelI.java
index 4aab6d3..e16146e 100644
--- a/src/jalview/api/analysis/ScoreModelI.java
+++ b/src/jalview/api/analysis/ScoreModelI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/analysis/ViewBasedAnalysisI.java b/src/jalview/api/analysis/ViewBasedAnalysisI.java
index d366bcf..456eebe 100644
--- a/src/jalview/api/analysis/ViewBasedAnalysisI.java
+++ b/src/jalview/api/analysis/ViewBasedAnalysisI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/api/structures/JalviewStructureDisplayI.java b/src/jalview/api/structures/JalviewStructureDisplayI.java
index da16800..e122c2b 100644
--- a/src/jalview/api/structures/JalviewStructureDisplayI.java
+++ b/src/jalview/api/structures/JalviewStructureDisplayI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/APopupMenu.java b/src/jalview/appletgui/APopupMenu.java
index a726e3c..11bb39d 100644
--- a/src/jalview/appletgui/APopupMenu.java
+++ b/src/jalview/appletgui/APopupMenu.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,7 +29,6 @@ import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
@@ -43,7 +42,6 @@ import jalview.schemes.HelixColourScheme;
 import jalview.schemes.HydrophobicColourScheme;
 import jalview.schemes.NucleotideColourScheme;
 import jalview.schemes.PIDColourScheme;
-import jalview.schemes.ResidueProperties;
 import jalview.schemes.StrandColourScheme;
 import jalview.schemes.TaylorColourScheme;
 import jalview.schemes.TurnColourScheme;
@@ -60,6 +58,7 @@ import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -70,8 +69,6 @@ import java.util.Vector;
 public class APopupMenu extends java.awt.PopupMenu implements
         ActionListener, ItemListener
 {
-  private static final String ALL_ANNOTATIONS = "All";
-
   Menu groupMenu = new Menu();
 
   MenuItem editGroupName = new MenuItem();
@@ -256,122 +253,9 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
     if (links != null && links.size() > 0)
     {
-      Menu linkMenu = new Menu(MessageManager.getString("action.link"));
-      for (int i = 0; i < links.size(); i++)
-      {
-        String link = links.elementAt(i);
-        UrlLink urlLink = new UrlLink(link);
-        if (!urlLink.isValid())
-        {
-          System.err.println(urlLink.getInvalidMessage());
-          continue;
-        }
-        final String target = urlLink.getTarget(); // link.substring(0,
-        // link.indexOf("|"));
-        final String label = urlLink.getLabel();
-        if (seq != null && urlLink.isDynamic())
-        {
-
-          // collect matching db-refs
-          DBRefEntry[] dbr = jalview.util.DBRefUtils.selectRefs(
-                  seq.getDBRef(), new String[] { target });
-          // collect id string too
-          String id = seq.getName();
-          String descr = seq.getDescription();
-          if (descr != null && descr.length() < 1)
-          {
-            descr = null;
-          }
-          if (dbr != null)
-          {
-            for (int r = 0; r < dbr.length; r++)
-            {
-              if (id != null && dbr[r].getAccessionId().equals(id))
-              {
-                // suppress duplicate link creation for the bare sequence ID
-                // string with this link
-                id = null;
-              }
-              // create Bare ID link for this RUL
-              String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(),
-                      true);
-              if (urls != null)
-              {
-                for (int u = 0; u < urls.length; u += 2)
-                {
-                  addshowLink(linkMenu, label + "|" + urls[u], urls[u + 1]);
-                }
-              }
-            }
-          }
-          if (id != null)
-          {
-            // create Bare ID link for this RUL
-            String[] urls = urlLink.makeUrls(id, true);
-            if (urls != null)
-            {
-              for (int u = 0; u < urls.length; u += 2)
-              {
-                addshowLink(linkMenu, label, urls[u + 1]);
-              }
-            }
-            // addshowLink(linkMenu, target, url_pref + id + url_suff);
-          }
-          // Now construct URLs from description but only try to do it for regex
-          // URL links
-          if (descr != null && urlLink.getRegexReplace() != null)
-          {
-            // create link for this URL from description only if regex matches
-            String[] urls = urlLink.makeUrls(descr, true);
-            if (urls != null)
-            {
-              for (int u = 0; u < urls.length; u += 2)
-              {
-                addshowLink(linkMenu, label, urls[u + 1]);
-              }
-            }
-          }
-        }
-        else
-        {
-          addshowLink(linkMenu, target, urlLink.getUrl_prefix()); // link.substring(link.lastIndexOf("|")+1));
-        }
-        /*
-         * final String url;
-         * 
-         * if (link.indexOf("$SEQUENCE_ID$") > -1) { // Substitute SEQUENCE_ID
-         * string and any matching database reference accessions String url_pref
-         * = link.substring(link.indexOf("|") + 1,
-         * link.indexOf("$SEQUENCE_ID$"));
-         * 
-         * String url_suff = link.substring(link.indexOf("$SEQUENCE_ID$") + 13);
-         * // collect matching db-refs DBRefEntry[] dbr =
-         * jalview.util.DBRefUtils.selectRefs(seq.getDBRef(), new
-         * String[]{target}); // collect id string too String id =
-         * seq.getName(); if (id.indexOf("|") > -1) { id =
-         * id.substring(id.lastIndexOf("|") + 1); } if (dbr!=null) { for (int
-         * r=0;r<dbr.length; r++) { if (dbr[r].getAccessionId().equals(id)) { //
-         * suppress duplicate link creation for the bare sequence ID string with
-         * this link id = null; } addshowLink(linkMenu,
-         * dbr[r].getSource()+"|"+dbr[r].getAccessionId(), target,
-         * url_pref+dbr[r].getAccessionId()+url_suff); } } if (id!=null) { //
-         * create Bare ID link for this RUL addshowLink(linkMenu, target,
-         * url_pref + id + url_suff); } } else { addshowLink(linkMenu, target,
-         * link.substring(link.lastIndexOf("|")+1)); }
-         */
-      }
-      if (linkMenu.getItemCount() > 0)
-      {
-        if (seq != null)
-        {
-          seqMenu.add(linkMenu);
-        }
-        else
-        {
-          add(linkMenu);
-        }
-      }
+      addFeatureLinks(seq, links);
     }
+
     // TODO: add group link menu entry here
     if (seq != null)
     {
@@ -417,6 +301,71 @@ public class APopupMenu extends java.awt.PopupMenu implements
   }
 
   /**
+   * Adds a 'Link' menu item with a sub-menu item for each hyperlink provided.
+   * 
+   * @param seq
+   * @param links
+   */
+  void addFeatureLinks(final SequenceI seq, List<String> links)
+  {
+    Menu linkMenu = new Menu(MessageManager.getString("action.link"));
+    Map<String, List<String>> linkset = new LinkedHashMap<String, List<String>>();
+
+    for (String link : links)
+    {
+      UrlLink urlLink = null;
+      try
+      {
+        urlLink = new UrlLink(link);
+      } catch (Exception foo)
+      {
+        System.err.println("Exception for URLLink '" + link + "': "
+                + foo.getMessage());
+        continue;
+      }
+
+      if (!urlLink.isValid())
+      {
+        System.err.println(urlLink.getInvalidMessage());
+        continue;
+      }
+
+      urlLink.createLinksFromSeq(seq, linkset);
+    }
+
+    addshowLinks(linkMenu, linkset.values());
+
+    // disable link menu if there are no valid entries
+    if (linkMenu.getItemCount() > 0)
+    {
+      linkMenu.setEnabled(true);
+    }
+    else
+    {
+      linkMenu.setEnabled(false);
+    }
+
+    if (seq != null)
+    {
+      seqMenu.add(linkMenu);
+    }
+    else
+    {
+      add(linkMenu);
+    }
+
+  }
+
+  private void addshowLinks(Menu linkMenu, Collection<List<String>> linkset)
+  {
+    for (List<String> linkstrset : linkset)
+    {
+      // split linkstr into label and url
+      addshowLink(linkMenu, linkstrset.get(1), linkstrset.get(3));
+    }
+  }
+
+  /**
    * Build menus for annotation types that may be shown or hidden, and for
    * 'reference annotations' that may be added to the alignment.
    */
@@ -537,6 +486,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
     MenuItem item = new MenuItem(label);
     item.addActionListener(new java.awt.event.ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         ap.alignFrame.showURL(url, target);
@@ -545,6 +495,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
     linkMenu.add(item);
   }
 
+  @Override
   public void itemStateChanged(ItemEvent evt)
   {
     if (evt.getSource() == abovePIDColour)
@@ -569,6 +520,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
     }
   }
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     Object source = evt.getSource();
@@ -850,7 +802,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
     CutAndPasteTransfer cap = new CutAndPasteTransfer(false, ap.alignFrame);
 
-    StringBuffer contents = new StringBuffer();
+    StringBuilder contents = new StringBuilder(128);
     for (SequenceI seq : sequences)
     {
       contents.append(MessageManager.formatMessage(
@@ -861,7 +813,6 @@ public class APopupMenu extends java.awt.PopupMenu implements
               seq,
               true,
               true,
-              false,
               (ap.seqPanel.seqCanvas.fr != null) ? ap.seqPanel.seqCanvas.fr
                       .getMinMax() : null);
       contents.append("</p>");
@@ -892,9 +843,10 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
   void addPDB()
   {
-    if (seq.getAllPDBEntries() != null)
+    Vector<PDBEntry> pdbs = seq.getAllPDBEntries();
+    if (pdbs != null&& !pdbs.isEmpty())
     {
-      PDBEntry entry = seq.getAllPDBEntries().firstElement();
+      PDBEntry entry = pdbs.firstElement();
 
       if (ap.av.applet.jmolAvailable)
       {
@@ -963,7 +915,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
             "label.represent_group_with", new Object[] { "" }));
     revealAll.setLabel(MessageManager.getString("action.reveal_all"));
     revealSeq.setLabel(MessageManager.getString("action.reveal_sequences"));
-    menu1.setLabel(MessageManager.getString("label.group") + ":");
+    menu1.setLabel(MessageManager.getString("label.group:"));
     add(groupMenu);
     this.add(seqMenu);
     this.add(hideSeqs);
@@ -1206,11 +1158,10 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
     if (conservationMenuItem.getState())
     {
-
-      sg.cs.setConservation(Conservation.calculateConservation("Group",
-              ResidueProperties.propHash, 3, sg.getSequences(ap.av
-                      .getHiddenRepSequences()), 0, ap.av.getAlignment()
-                      .getWidth(), false, ap.av.getConsPercGaps(), false));
+      sg.cs.setConservation(Conservation.calculateConservation("Group", sg
+              .getSequences(ap.av.getHiddenRepSequences()), 0, ap.av
+              .getAlignment().getWidth(), false, ap.av.getConsPercGaps(),
+              false));
       SliderPanel.setConservationSlider(ap, sg.cs, sg.getName());
       SliderPanel.showConservationSlider();
     }
@@ -1299,35 +1250,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
   void hideSequences(boolean representGroup)
   {
-    SequenceGroup sg = ap.av.getSelectionGroup();
-    if (sg == null || sg.getSize() < 1)
-    {
-      ap.av.hideSequence(new SequenceI[] { seq });
-      return;
-    }
-
-    ap.av.setSelectionGroup(null);
-
-    if (representGroup)
-    {
-      ap.av.hideRepSequences(seq, sg);
-
-      return;
-    }
-
-    int gsize = sg.getSize();
-    SequenceI[] hseqs;
-
-    hseqs = new SequenceI[gsize];
-
-    int index = 0;
-    for (int i = 0; i < gsize; i++)
-    {
-      hseqs[index++] = sg.getSequenceAt(i);
-    }
-
-    ap.av.hideSequence(hseqs);
-    ap.av.sendSelection();
+    ap.av.hideSequences(seq, representGroup);
   }
 
   /**
@@ -1351,7 +1274,8 @@ public class APopupMenu extends java.awt.PopupMenu implements
     showMenu.removeAll();
     hideMenu.removeAll();
 
-    final List<String> all = Arrays.asList(ALL_ANNOTATIONS);
+    final List<String> all = Arrays.asList(new String[] { MessageManager
+            .getString("label.all") });
     addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true, true);
     addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
             false);
diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java
index e01c4c9..283d775 100644
--- a/src/jalview/appletgui/AlignFrame.java
+++ b/src/jalview/appletgui/AlignFrame.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -25,6 +25,7 @@ import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.api.AlignViewControllerGuiI;
 import jalview.api.AlignViewControllerI;
 import jalview.api.AlignViewportI;
+import jalview.api.FeatureColourI;
 import jalview.api.FeatureRenderer;
 import jalview.api.FeatureSettingsControllerI;
 import jalview.api.SequenceStructureBinding;
@@ -75,6 +76,7 @@ import java.awt.BorderLayout;
 import java.awt.Canvas;
 import java.awt.CheckboxMenuItem;
 import java.awt.Color;
+import java.awt.FlowLayout;
 import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.Frame;
@@ -83,6 +85,7 @@ import java.awt.Label;
 import java.awt.Menu;
 import java.awt.MenuBar;
 import java.awt.MenuItem;
+import java.awt.Panel;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.FocusEvent;
@@ -99,7 +102,6 @@ import java.net.URLEncoder;
 import java.util.Arrays;
 import java.util.Deque;
 import java.util.HashMap;
-import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
@@ -216,6 +218,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     {
       viewport.setColumnSelection(columnSelection);
     }
+    viewport.setScaleAboveWrapped(scaleAbove.getState());
 
     alignPanel = new AlignmentPanel(this, viewport);
     avc = new jalview.controller.AlignViewController(this, viewport,
@@ -232,8 +235,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     normSequenceLogo.setState(viewport.isNormaliseSequenceLogo());
     applyToAllGroups.setState(viewport.getColourAppliesToAllGroups());
     annotationPanelMenuItem.setState(viewport.isShowAnnotation());
-    showAlignmentAnnotations.setState(viewport.isShowAnnotation());
-    showSequenceAnnotations.setState(viewport.isShowAnnotation());
+    showAlignmentAnnotations.setEnabled(annotationPanelMenuItem.getState());
+    showSequenceAnnotations.setEnabled(annotationPanelMenuItem.getState());
+    showAlignmentAnnotations.setState(true);
+    showSequenceAnnotations.setState(false);
 
     seqLimits.setState(viewport.getShowJVSuffix());
 
@@ -302,6 +307,8 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     alignPanel.annotationSpaceFillerHolder.addKeyListener(this);
     alignPanel.alabels.addKeyListener(this);
 
+    setAnnotationsVisibility();
+
     if (addToDisplay)
     {
       addToDisplay(embedded);
@@ -358,18 +365,15 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   public boolean parseFeaturesFile(String file, String type,
           boolean autoenabledisplay)
   {
-    // TODO: test if importing a features file onto an alignment which already
-    // has features with links overwrites the original links.
-
-    Hashtable featureLinks = new Hashtable();
     boolean featuresFile = false;
     try
     {
-      featuresFile = new jalview.io.FeaturesFile(file, type).parse(viewport
-              .getAlignment(), alignPanel.seqPanel.seqCanvas
-              .getFeatureRenderer().getFeatureColours(), featureLinks,
-              true, viewport.applet.getDefaultParameter("relaxedidmatch",
-                      false));
+      Map<String, FeatureColourI> colours = alignPanel.seqPanel.seqCanvas
+              .getFeatureRenderer().getFeatureColours();
+      boolean relaxedIdMatching = viewport.applet.getDefaultParameter(
+              "relaxedidmatch", false);
+      featuresFile = new FeaturesFile(file, type).parse(
+              viewport.getAlignment(), colours, true, relaxedIdMatching);
     } catch (Exception ex)
     {
       ex.printStackTrace();
@@ -377,10 +381,6 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
     if (featuresFile)
     {
-      if (featureLinks.size() > 0)
-      {
-        alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureLinks = featureLinks;
-      }
       if (autoenabledisplay)
       {
         viewport.setShowSequenceFeatures(true);
@@ -702,9 +702,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       // Hide everything by the current selection - this is a hack - we do the
       // invert and then hide
       // first check that there will be visible columns after the invert.
-      if ((viewport.getColumnSelection() != null
-              && viewport.getColumnSelection().getSelected() != null && viewport
-              .getColumnSelection().getSelected().size() > 0)
+      if (viewport.hasSelectedColumns()
               || (sg != null && sg.getSize() > 0 && sg.getStartRes() <= sg
                       .getEndRes()))
       {
@@ -732,8 +730,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
         hide = true;
         viewport.hideAllSelectedSeqs();
       }
-      else if (!(toggleCols && viewport.getColumnSelection().getSelected()
-              .size() > 0))
+      else if (!(toggleCols && viewport.hasSelectedColumns()))
       {
         viewport.showAllHiddenSeqs();
       }
@@ -741,7 +738,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
     if (toggleCols)
     {
-      if (viewport.getColumnSelection().getSelected().size() > 0)
+      if (viewport.hasSelectedColumns())
       {
         viewport.hideSelectedColumns();
         if (!toggleSeqs)
@@ -753,6 +750,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       {
         viewport.showAllHiddenColumns();
       }
+      viewport.sendSelection();
     }
   }
 
@@ -812,8 +810,11 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
     else if (source == annotationPanelMenuItem)
     {
-      viewport.setShowAnnotation(annotationPanelMenuItem.getState());
-      alignPanel.setAnnotationVisible(annotationPanelMenuItem.getState());
+      boolean showAnnotations = annotationPanelMenuItem.getState();
+      showAlignmentAnnotations.setEnabled(showAnnotations);
+      showSequenceAnnotations.setEnabled(showAnnotations);
+      viewport.setShowAnnotation(showAnnotations);
+      alignPanel.setAnnotationVisible(showAnnotations);
     }
     else if (source == sequenceFeatures)
     {
@@ -913,7 +914,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   /**
    * Set the visibility state of sequence-related and/or alignment-related
-   * annotations depending on checkbox selections. Repaint after calling.
+   * annotations depending on checkbox selections, and repaint.
    * 
    * @param visible
    */
@@ -921,12 +922,15 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   {
     boolean showForAlignment = showAlignmentAnnotations.getState();
     boolean showForSequences = showSequenceAnnotations.getState();
-    for (AlignmentAnnotation aa : alignPanel.getAlignment()
-            .getAlignmentAnnotation())
+    if (alignPanel.getAlignment().getAlignmentAnnotation() != null)
     {
-      boolean visible = (aa.sequenceRef == null ? showForAlignment
-              : showForSequences);
-      aa.visible = visible;
+      for (AlignmentAnnotation aa : alignPanel.getAlignment()
+              .getAlignmentAnnotation())
+      {
+        boolean visible = (aa.sequenceRef == null ? showForAlignment
+                : showForSequences);
+        aa.visible = visible;
+      }
     }
     alignPanel.validateAnnotationDimensions(true);
     validate();
@@ -1068,11 +1072,14 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     else if (source == invertSequenceMenuItem)
     {
       invertSequenceMenuItem_actionPerformed();
+      // uncomment to slave sequence selections in split frame
+      // viewport.sendSelection();
     }
     else if (source == invertColSel)
     {
       viewport.invertColumnSelection();
       alignPanel.paintAlignment(true);
+      viewport.sendSelection();
     }
     else if (source == remove2LeftMenuItem)
     {
@@ -1106,27 +1113,34 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     {
       viewport.showAllHiddenColumns();
       alignPanel.paintAlignment(true);
+      viewport.sendSelection();
     }
     else if (source == showSeqs)
     {
       viewport.showAllHiddenSeqs();
       alignPanel.paintAlignment(true);
+      // uncomment if we want to slave sequence selections in split frame
+      // viewport.sendSelection();
     }
     else if (source == hideColumns)
     {
       viewport.hideSelectedColumns();
       alignPanel.paintAlignment(true);
+      viewport.sendSelection();
     }
     else if (source == hideSequences
             && viewport.getSelectionGroup() != null)
     {
       viewport.hideAllSelectedSeqs();
       alignPanel.paintAlignment(true);
+      // uncomment if we want to slave sequence selections in split frame
+      // viewport.sendSelection();
     }
     else if (source == hideAllButSelection)
     {
       toggleHiddenRegions(false, false);
       alignPanel.paintAlignment(true);
+      viewport.sendSelection();
     }
     else if (source == hideAllSelection)
     {
@@ -1135,12 +1149,14 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       viewport.hideAllSelectedSeqs();
       viewport.hideSelectedColumns();
       alignPanel.paintAlignment(true);
+      viewport.sendSelection();
     }
     else if (source == showAllHidden)
     {
       viewport.showAllHiddenColumns();
       viewport.showAllHiddenSeqs();
       alignPanel.paintAlignment(true);
+      viewport.sendSelection();
     }
     else if (source == showGroupConsensus)
     {
@@ -1381,7 +1397,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     return annotation;
   }
 
-  private Map<String, Object> getDisplayedFeatureCols()
+  private Map<String, FeatureColourI> getDisplayedFeatureCols()
   {
     if (alignPanel.getFeatureRenderer() != null
             && viewport.getFeaturesDisplayed() != null)
@@ -1395,15 +1411,15 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
   public String outputFeatures(boolean displayTextbox, String format)
   {
     String features;
+    FeaturesFile formatter = new FeaturesFile();
     if (format.equalsIgnoreCase("Jalview"))
     {
-      features = new FeaturesFile().printJalviewFormat(viewport
-              .getAlignment().getSequencesArray(),
-              getDisplayedFeatureCols());
+      features = formatter.printJalviewFormat(viewport.getAlignment()
+              .getSequencesArray(), getDisplayedFeatureCols());
     }
     else
     {
-      features = new FeaturesFile().printGFFFormat(viewport.getAlignment()
+      features = formatter.printGffFormat(viewport.getAlignment()
               .getSequencesArray(), getDisplayedFeatureCols());
     }
 
@@ -1743,9 +1759,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
               viewport, complement);
       complement.getAlignment().moveSelectedSequencesByOne(mappedSelection,
               up ? null : complement.getHiddenRepSequences(), up);
-      // TODO need to trigger a repaint of the complementary panel - how?
-      // would prefer to handle in SplitFrame but it is not overriding key
-      // listener chiz
+      getSplitFrame().getComplement(this).alignPanel.paintAlignment(true);
     }
   }
 
@@ -2059,9 +2073,32 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       seqs.addElement(seq);
     }
 
-    // If the cut affects all sequences, remove highlighted columns
+    /*
+     * If the cut affects all sequences, warn, remove highlighted columns
+     */
     if (sg.getSize() == viewport.getAlignment().getHeight())
     {
+      boolean isEntireAlignWidth = (((sg.getEndRes() - sg.getStartRes()) + 1) == viewport
+              .getAlignment().getWidth()) ? true : false;
+      if (isEntireAlignWidth)
+      {
+        String title = MessageManager.getString("label.delete_all");
+        Panel infoPanel = new Panel();
+        infoPanel.setLayout(new FlowLayout());
+        infoPanel
+                .add(new Label(MessageManager.getString("warn.delete_all")));
+
+        final JVDialog dialog = new JVDialog(this, title, true, 400, 200);
+        dialog.setMainPanel(infoPanel);
+        dialog.ok.setLabel(MessageManager.getString("action.ok"));
+        dialog.cancel.setLabel(MessageManager.getString("action.cancel"));
+        dialog.setVisible(true);
+
+        if (!dialog.accept)
+        {
+          return;
+        }
+      }
       viewport.getColumnSelection().removeElements(sg.getStartRes(),
               sg.getEndRes() + 1);
     }
@@ -2192,7 +2229,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
     sg.setEndRes(viewport.getAlignment().getWidth() - 1);
     viewport.setSelectionGroup(sg);
-    alignPanel.paintAlignment(true);
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    alignPanel.paintAlignment(false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
     viewport.sendSelection();
   }
@@ -2209,7 +2249,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.setSelectionGroup(null);
     alignPanel.idPanel.idCanvas.searchResults = null;
     alignPanel.seqPanel.seqCanvas.highlightSearchResults(null);
-    alignPanel.paintAlignment(true);
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    alignPanel.paintAlignment(false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
     viewport.sendSelection();
   }
@@ -2239,7 +2282,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     ColumnSelection colSel = viewport.getColumnSelection();
     int column;
 
-    if (colSel.size() > 0)
+    if (!colSel.isEmpty())
     {
       if (trimLeft)
       {
@@ -2264,18 +2307,14 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       TrimRegionCommand trimRegion;
       if (trimLeft)
       {
-        trimRegion = new TrimRegionCommand("Remove Left",
-                TrimRegionCommand.TRIM_LEFT, seqs, column,
-                viewport.getAlignment(), viewport.getColumnSelection(),
-                viewport.getSelectionGroup());
+        trimRegion = new TrimRegionCommand("Remove Left", true, seqs,
+                column, viewport.getAlignment());
         viewport.setStartRes(0);
       }
       else
       {
-        trimRegion = new TrimRegionCommand("Remove Right",
-                TrimRegionCommand.TRIM_RIGHT, seqs, column,
-                viewport.getAlignment(), viewport.getColumnSelection(),
-                viewport.getSelectionGroup());
+        trimRegion = new TrimRegionCommand("Remove Right", false, seqs,
+                column, viewport.getAlignment());
       }
 
       statusBar.setText(MessageManager.formatMessage(
@@ -2579,6 +2618,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   }
 
+  @Override
   public void changeColour(ColourSchemeI cs)
   {
 
@@ -3357,8 +3397,11 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
             MessageManager.getString("label.sort_annotations_by_label"));
     showAutoFirst = new CheckboxMenuItem(
             MessageManager.getString("label.show_first"));
+    showAutoFirst.setState(false); // pending applet parameter
+    setShowAutoCalculatedAbove(showAutoFirst.getState());
     showAutoLast = new CheckboxMenuItem(
             MessageManager.getString("label.show_last"));
+    showAutoLast.setState(!showAutoFirst.getState());
     showAlignmentAnnotations.addItemListener(this);
     showSequenceAnnotations.addItemListener(this);
     sortAnnBySequence.addItemListener(this);
@@ -3469,10 +3512,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     nucleotideColour.setLabel(MessageManager.getString("label.nucleotide"));
     nucleotideColour.addActionListener(this);
     modifyPID.setLabel(MessageManager
-            .getString("label.modify_identity_thereshold"));
+            .getString("label.modify_identity_threshold"));
     modifyPID.addActionListener(this);
     modifyConservation.setLabel(MessageManager
-            .getString("label.modify_conservation_thereshold"));
+            .getString("label.modify_conservation_threshold"));
     modifyConservation.addActionListener(this);
     annotationColour.setLabel(MessageManager
             .getString("action.by_annotation"));
@@ -3726,6 +3769,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     this.add(statusBar, BorderLayout.SOUTH);
   }
 
+  @Override
   public void setStatus(String string)
   {
     statusBar.setText(string);
@@ -3979,12 +4023,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
       }
       if (needtoadd)
       {
-        // make a note of the access mode and add
-        if (pdbentry.getProperty() == null)
-        {
-          pdbentry.setProperty(new Hashtable());
-        }
-        pdbentry.getProperty().put("protocol", protocol);
+        pdbentry.setProperty("protocol", protocol);
         toaddpdb.addPDBId(pdbentry);
         alignPanel.getStructureSelectionManager()
                 .registerPDBEntry(pdbentry);
@@ -4035,7 +4074,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     if (protocol == null || protocol.trim().length() == 0
             || protocol.equals("null"))
     {
-      protocol = (String) pdb.getProperty().get("protocol");
+      protocol = (String) pdb.getProperty("protocol");
       if (protocol == null)
       {
         System.err.println("Couldn't work out protocol to open structure: "
diff --git a/src/jalview/appletgui/AlignViewport.java b/src/jalview/appletgui/AlignViewport.java
index f8355c3..33a0e91 100644
--- a/src/jalview/appletgui/AlignViewport.java
+++ b/src/jalview/appletgui/AlignViewport.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,11 +22,13 @@ package jalview.appletgui;
 
 import jalview.analysis.NJTree;
 import jalview.api.AlignViewportI;
+import jalview.api.FeatureSettingsModelI;
 import jalview.bin.JalviewLite;
 import jalview.commands.CommandI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -57,6 +59,7 @@ public class AlignViewport extends AlignmentViewport implements
 
   private AnnotationColumnChooser annotationColumnSelectionState;
 
+  @Override
   public void finalize()
   {
     applet = null;
@@ -187,8 +190,13 @@ public class AlignViewport extends AlignmentViewport implements
 
     if (applet != null)
     {
-      String colour = applet.getParameter("defaultColour");
-
+      String colour = al.isNucleotide() ? applet
+              .getParameter("defaultColourNuc") : applet
+              .getParameter("defaultColourProt");
+      if (colour == null)
+      {
+        colour = applet.getParameter("defaultColour");
+      }
       if (colour == null)
       {
         colour = applet.getParameter("userDefinedColour");
@@ -320,6 +328,7 @@ public class AlignViewport extends AlignmentViewport implements
     return followSelection;
   }
 
+  @Override
   public void sendSelection()
   {
     getStructureSelectionManager().sendSelection(
@@ -340,39 +349,7 @@ public class AlignViewport extends AlignmentViewport implements
             .getStructureSelectionManager(applet);
   }
 
-  /**
-   * synthesize a column selection if none exists so it covers the given
-   * selection group. if wholewidth is false, no column selection is made if the
-   * selection group covers the whole alignment width.
-   * 
-   * @param sg
-   * @param wholewidth
-   */
-  public void expandColSelection(SequenceGroup sg, boolean wholewidth)
-  {
-    int sgs, sge;
-    if (sg != null
-            && (sgs = sg.getStartRes()) >= 0
-            && sg.getStartRes() <= (sge = sg.getEndRes())
-            && (colSel == null || colSel.getSelected() == null || colSel
-                    .getSelected().size() == 0))
-    {
-      if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
-      {
-        // do nothing
-        return;
-      }
-      if (colSel == null)
-      {
-        colSel = new ColumnSelection();
-      }
-      for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
-      {
-        colSel.addElement(cspos);
-      }
-    }
-  }
-
+  @Override
   public boolean isNormaliseSequenceLogo()
   {
     return normaliseSequenceLogo;
@@ -387,6 +364,7 @@ public class AlignViewport extends AlignmentViewport implements
    * 
    * @return true if alignment characters should be displayed
    */
+  @Override
   public boolean isValidCharWidth()
   {
     return validCharWidth;
@@ -456,7 +434,7 @@ public class AlignViewport extends AlignmentViewport implements
      * there is no complement, or it is not following highlights, or no mapping
      * is found, the result will be empty.
      */
-    SearchResults sr = new SearchResults();
+    SearchResultsI sr = new SearchResults();
     int seqOffset = findComplementScrollTarget(sr);
     if (!sr.isEmpty())
     {
@@ -465,4 +443,18 @@ public class AlignViewport extends AlignmentViewport implements
     }
   }
 
+  /**
+   * Applies the supplied feature settings descriptor to currently known
+   * features. This supports an 'initial configuration' of feature colouring
+   * based on a preset or user favourite. This may then be modified in the usual
+   * way using the Feature Settings dialogue.
+   * 
+   * @param featureSettings
+   */
+  @Override
+  public void applyFeaturesStyle(FeatureSettingsModelI featureSettings)
+  {
+    // TODO implement for applet
+  }
+
 }
diff --git a/src/jalview/appletgui/AlignmentPanel.java b/src/jalview/appletgui/AlignmentPanel.java
index d0088a1..280fe3f 100644
--- a/src/jalview/appletgui/AlignmentPanel.java
+++ b/src/jalview/appletgui/AlignmentPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -25,7 +25,7 @@ import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.bin.JalviewLite;
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceI;
 import jalview.structure.StructureSelectionManager;
 
@@ -68,7 +68,8 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
   // this value is set false when selection area being dragged
   boolean fastPaint = true;
 
-  public void finalize()
+  @Override
+  public void finalize() throws Throwable
   {
     alignFrame = null;
     av = null;
@@ -80,6 +81,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     annotationPanel = null;
     annotationPanelHolder = null;
     annotationSpaceFillerHolder = null;
+    super.finalize();
   }
 
   public AlignmentPanel(AlignFrame af, final AlignViewport av)
@@ -121,6 +123,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
 
     addComponentListener(new ComponentAdapter()
     {
+      @Override
       public void componentResized(ComponentEvent evt)
       {
         setScrollValues(av.getStartRes(), av.getStartSeq());
@@ -146,6 +149,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     final AlignmentPanel ap = this;
     av.addPropertyChangeListener(new java.beans.PropertyChangeListener()
     {
+      @Override
       public void propertyChange(java.beans.PropertyChangeEvent evt)
       {
         if (evt.getPropertyName().equals("alignment"))
@@ -289,7 +293,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
    * Highlight the given results on the alignment.
    * 
    */
-  public void highlightSearchResults(SearchResults results)
+  public void highlightSearchResults(SearchResultsI results)
   {
     scrollToPosition(results);
     seqPanel.seqCanvas.highlightSearchResults(results);
@@ -302,7 +306,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
    * @param results
    * @return false if results were not found
    */
-  public boolean scrollToPosition(SearchResults results)
+  public boolean scrollToPosition(SearchResultsI results)
   {
     return scrollToPosition(results, true);
   }
@@ -316,10 +320,10 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
    *          - when set, the overview will be recalculated (takes longer)
    * @return false if results were not found
    */
-  public boolean scrollToPosition(SearchResults results,
+  public boolean scrollToPosition(SearchResultsI results,
           boolean redrawOverview)
   {
-    return scrollToPosition(results, redrawOverview, false);
+    return scrollToPosition(results, 0, redrawOverview, false);
   }
 
   /**
@@ -331,7 +335,8 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
    *          - when set, the overview will be recalculated (takes longer)
    * @return false if results were not found
    */
-  public boolean scrollToPosition(SearchResults results,
+  public boolean scrollToPosition(SearchResultsI results,
+          int verticalOffset,
           boolean redrawOverview, boolean centre)
   {
     // do we need to scroll the panel?
@@ -343,6 +348,10 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       {
         return false;
       }
+      /*
+       * allow for offset of target sequence (actually scroll to one above it)
+       */
+
       SequenceI seq = alignment.getSequenceAt(seqIndex);
       int[] r = results.getResults(seq, 0, alignment.getWidth());
       if (r == null)
@@ -387,6 +396,11 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
       {
         return false;
       }
+
+      /*
+       * allow for offset of target sequence (actually scroll to one above it)
+       */
+      seqIndex = Math.max(0, seqIndex - verticalOffset);
       return scrollTo(start, end, seqIndex, false, redrawOverview);
     }
     return true;
@@ -415,6 +429,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     {
       start = ostart;
     }
+
     if (!av.getWrapAlignment())
     {
       /*
@@ -538,6 +553,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
    * automatically adjust annotation panel height for new annotation whilst
    * ensuring the alignment is still visible.
    */
+  @Override
   public void adjustAnnotationHeight()
   {
     // TODO: display vertical annotation scrollbar if necessary
@@ -770,6 +786,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
 
   }
 
+  @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
     int oldX = av.getStartRes();
@@ -896,14 +913,14 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
    * @param seqOffset
    *          the number of visible sequences to show above the mapped region
    */
-  protected void scrollToCentre(SearchResults sr, int seqOffset)
+  protected void scrollToCentre(SearchResultsI sr, int seqOffset)
   {
     /*
      * To avoid jumpy vertical scrolling (if some sequences are gapped or not
      * mapped), we can make the scroll-to location a sequence above the one
      * actually mapped.
      */
-    SequenceI mappedTo = sr.getResultSequence(0);
+    SequenceI mappedTo = sr.getResults().get(0).getSequence();
     List<SequenceI> seqs = av.getAlignment().getSequences();
 
     /*
@@ -925,16 +942,14 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     {
       return; // failsafe, shouldn't happen
     }
-    sequenceIndex = Math.max(0, sequenceIndex - seqOffset);
-    sr.getResults().get(0)
-            .setSequence(av.getAlignment().getSequenceAt(sequenceIndex));
 
     /*
      * Scroll to position but centring the target residue. Also set a state flag
      * to prevent adjustmentValueChanged performing this recursively.
      */
     setFollowingComplementScroll(true);
-    scrollToPosition(sr, true, true);
+    // this should be scrollToPosition(sr,verticalOffset,
+    scrollToPosition(sr, seqOffset, true, true);
   }
 
   private void sendViewPosition()
@@ -947,6 +962,7 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
   /**
    * Repaint the alignment and annotations, and, optionally, any overview window
    */
+  @Override
   public void paintAlignment(boolean updateOverview)
   {
     final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),
@@ -969,11 +985,13 @@ public class AlignmentPanel extends Panel implements AdjustmentListener,
     }
   }
 
+  @Override
   public void update(Graphics g)
   {
     paint(g);
   }
 
+  @Override
   public void paint(Graphics g)
   {
     invalidate();
diff --git a/src/jalview/appletgui/AnnotationColourChooser.java b/src/jalview/appletgui/AnnotationColourChooser.java
index 9df3b73..7acb32a 100644
--- a/src/jalview/appletgui/AnnotationColourChooser.java
+++ b/src/jalview/appletgui/AnnotationColourChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,7 @@
  */
 package jalview.appletgui;
 
+import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.SequenceGroup;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.ColourSchemeI;
@@ -96,7 +97,8 @@ public class AnnotationColourChooser extends Panel implements
     slider.addAdjustmentListener(this);
     slider.addMouseListener(this);
 
-    if (av.getAlignment().getAlignmentAnnotation() == null)
+    AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation();
+    if (anns == null)
     {
       return;
     }
@@ -117,11 +119,15 @@ public class AnnotationColourChooser extends Panel implements
       // seqAssociated.setState(acg.isSeqAssociated());
     }
 
-    Vector list = new Vector();
+    Vector<String> list = new Vector<String>();
     int index = 1;
-    for (int i = 0; i < av.getAlignment().getAlignmentAnnotation().length; i++)
+    for (int i = 0; i < anns.length; i++)
     {
-      String label = av.getAlignment().getAlignmentAnnotation()[i].label;
+      String label = anns[i].label;
+      if (anns[i].sequenceRef != null)
+      {
+        label = label + "_" + anns[i].sequenceRef.getName();
+      }
       if (!list.contains(label))
       {
         list.addElement(label);
@@ -138,11 +144,11 @@ public class AnnotationColourChooser extends Panel implements
     }
 
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_no_thereshold"));
+            .getString("label.threshold_feature_no_threshold"));
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_above_thereshold"));
+            .getString("label.threshold_feature_above_threshold"));
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_below_thereshold"));
+            .getString("label.threshold_feature_below_threshold"));
 
     if (oldcs instanceof AnnotationColourGradient)
     {
@@ -162,7 +168,7 @@ public class AnnotationColourChooser extends Panel implements
       default:
         throw new Error(
                 MessageManager
-                        .getString("error.implementation_error_dont_know_thereshold_annotationcolourgradient"));
+                        .getString("error.implementation_error_dont_know_threshold_annotationcolourgradient"));
       }
       thresholdIsMin.setState(acg.thresholdIsMinMax);
       thresholdValue.setText("" + acg.getAnnotationThreshold());
@@ -309,6 +315,7 @@ public class AnnotationColourChooser extends Panel implements
 
   Checkbox thresholdIsMin = new Checkbox();
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     if (evt.getSource() == thresholdValue)
@@ -351,6 +358,7 @@ public class AnnotationColourChooser extends Panel implements
     }
   }
 
+  @Override
   public void itemStateChanged(ItemEvent evt)
   {
     if (evt.getSource() == currentColours)
@@ -368,6 +376,7 @@ public class AnnotationColourChooser extends Panel implements
     changeColour();
   }
 
+  @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
     if (!adjusting)
@@ -552,23 +561,28 @@ public class AnnotationColourChooser extends Panel implements
 
   }
 
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
   }
 
+  @Override
   public void mousePressed(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     ap.paintAlignment(true);
   }
 
+  @Override
   public void mouseEntered(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseExited(MouseEvent evt)
   {
   }
diff --git a/src/jalview/appletgui/AnnotationColumnChooser.java b/src/jalview/appletgui/AnnotationColumnChooser.java
index 1a68682..877ad8d 100644
--- a/src/jalview/appletgui/AnnotationColumnChooser.java
+++ b/src/jalview/appletgui/AnnotationColumnChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -49,7 +49,7 @@ import java.awt.event.TextListener;
 import java.util.Iterator;
 import java.util.Vector;
 
-import javax.swing.JPanel;
+//import javax.swing.JPanel;
 
 //import net.miginfocom.swing.MigLayout;
 
@@ -135,17 +135,22 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
     slider.addAdjustmentListener(this);
     slider.addMouseListener(this);
 
-    if (av.getAlignment().getAlignmentAnnotation() == null)
+    AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation();
+    if (anns == null)
     {
       return;
     }
     setOldColumnSelection(av.getColumnSelection());
     adjusting = true;
-    Vector list = new Vector();
+    Vector<String> list = new Vector<String>();
     int index = 1;
-    for (int i = 0; i < av.getAlignment().getAlignmentAnnotation().length; i++)
+    for (int i = 0; i < anns.length; i++)
     {
-      String label = av.getAlignment().getAlignmentAnnotation()[i].label;
+      String label = anns[i].label;
+      if (anns[i].sequenceRef != null)
+      {
+        label = label + "_" + anns[i].sequenceRef.getName();
+      }
       if (!list.contains(label))
       {
         list.addElement(label);
@@ -241,7 +246,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
     actionPanel.add(ok);
     actionPanel.add(cancel);
 
-    JPanel staticPanel = new JPanel();
+    Panel staticPanel = new Panel();
     staticPanel.setLayout(new BorderLayout());
     staticPanel.setBackground(Color.white);
 
@@ -273,6 +278,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
     this.validate();
   }
 
+  @Override
   @SuppressWarnings("unchecked")
   public void reset()
   {
@@ -302,6 +308,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
 
   }
 
+  @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
     if (!adjusting)
@@ -343,6 +350,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
     });
   }
 
+  @Override
   public void valueChanged(boolean updateAllAnnotation)
   {
     if (slider.isEnabled())
@@ -866,6 +874,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
     }
   }
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     if (evt.getSource() == thresholdValue)
diff --git a/src/jalview/appletgui/AnnotationLabels.java b/src/jalview/appletgui/AnnotationLabels.java
index 201cfae..55e060b 100644
--- a/src/jalview/appletgui/AnnotationLabels.java
+++ b/src/jalview/appletgui/AnnotationLabels.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -162,6 +162,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
     return row;
   }
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
@@ -261,6 +262,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
 
   boolean resizePanel = false;
 
+  @Override
   public void mouseMoved(MouseEvent evt)
   {
     resizePanel = evt.getY() < 10 && evt.getX() < 14;
@@ -306,6 +308,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
     dragCancelled = true;
   }
 
+  @Override
   public void mouseDragged(MouseEvent evt)
   {
     if (dragCancelled)
@@ -365,10 +368,12 @@ public class AnnotationLabels extends Panel implements ActionListener,
     }
   }
 
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     if (!resizePanel && !dragCancelled)
@@ -400,6 +405,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
     ap.annotationPanel.repaint();
   }
 
+  @Override
   public void mouseEntered(MouseEvent evt)
   {
     if (evt.getY() < 10 && evt.getX() < 14)
@@ -409,6 +415,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
     }
   }
 
+  @Override
   public void mouseExited(MouseEvent evt)
   {
     dragCancelled = false;
@@ -427,6 +434,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
     repaint();
   }
 
+  @Override
   public void mousePressed(MouseEvent evt)
   {
     oldY = evt.getY();
@@ -522,6 +530,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
             final AlignmentAnnotation aaa = aa[selectedRow];
             cbmi.addItemListener(new ItemListener()
             {
+              @Override
               public void itemStateChanged(ItemEvent e)
               {
                 if (aaa.groupRef != null)
@@ -545,6 +554,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
                       aa[selectedRow].groupRef.isShowConsensusHistogram());
               chist.addItemListener(new ItemListener()
               {
+                @Override
                 public void itemStateChanged(ItemEvent e)
                 {
                   // TODO: pass on reference
@@ -564,6 +574,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
                       aa[selectedRow].groupRef.isShowSequenceLogo());
               cprofl.addItemListener(new ItemListener()
               {
+                @Override
                 public void itemStateChanged(ItemEvent e)
                 {
                   // TODO: pass on reference
@@ -585,6 +596,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
                       aa[selectedRow].groupRef.isNormaliseSequenceLogo());
               cprofn.addItemListener(new ItemListener()
               {
+                @Override
                 public void itemStateChanged(ItemEvent e)
                 {
                   // TODO: pass on reference
@@ -608,6 +620,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
                       av.isShowConsensusHistogram());
               chist.addItemListener(new ItemListener()
               {
+                @Override
                 public void itemStateChanged(ItemEvent e)
                 {
                   // TODO: pass on reference
@@ -631,6 +644,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
                       av.isShowSequenceLogo());
               cprof.addItemListener(new ItemListener()
               {
+                @Override
                 public void itemStateChanged(ItemEvent e)
                 {
                   // TODO: pass on reference
@@ -655,6 +669,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
                       av.isNormaliseSequenceLogo());
               cprofn.addItemListener(new ItemListener()
               {
+                @Override
                 public void itemStateChanged(ItemEvent e)
                 {
                   // TODO: pass on reference
@@ -697,11 +712,47 @@ public class AnnotationLabels extends Panel implements ActionListener,
             // todo: make the ap scroll to the selection - not necessary, first
             // click highlights/scrolls, second selects
             ap.seqPanel.ap.idPanel.highlightSearchResults(null);
-            ap.av.setSelectionGroup(// new SequenceGroup(
-            aa[selectedRow].groupRef); // );
-            ap.av.sendSelection();
+            // process modifiers
+            SequenceGroup sg = ap.av.getSelectionGroup();
+            if (sg == null
+                    || sg == aa[selectedRow].groupRef
+                    || !(jalview.util.Platform.isControlDown(evt) || evt
+                            .isShiftDown()))
+            {
+              if (jalview.util.Platform.isControlDown(evt)
+                      || evt.isShiftDown())
+              {
+                // clone a new selection group from the associated group
+                ap.av.setSelectionGroup(new SequenceGroup(
+                        aa[selectedRow].groupRef));
+              }
+              else
+              {
+                // set selection to the associated group so it can be edited
+                ap.av.setSelectionGroup(aa[selectedRow].groupRef);
+              }
+            }
+            else
+            {
+              // modify current selection with associated group
+              int remainToAdd = aa[selectedRow].groupRef.getSize();
+              for (SequenceI sgs : aa[selectedRow].groupRef.getSequences())
+              {
+                if (jalview.util.Platform.isControlDown(evt))
+                {
+                  sg.addOrRemove(sgs, --remainToAdd == 0);
+                }
+                else
+                {
+                  // notionally, we should also add intermediate sequences from
+                  // last added sequence ?
+                  sg.addSequence(sgs, --remainToAdd == 0);
+                }
+              }
+            }
             ap.paintAlignment(false);
             PaintRefresher.Refresh(ap, ap.av.getSequenceSetId());
+            ap.av.sendSelection();
           }
           else
           {
@@ -728,7 +779,8 @@ public class AnnotationLabels extends Panel implements ActionListener,
               // we make a copy rather than edit the current selection if no
               // modifiers pressed
               // see Enhancement JAL-1557
-              if (!(evt.isControlDown() || evt.isShiftDown()))
+              if (!(jalview.util.Platform.isControlDown(evt) || evt
+                      .isShiftDown()))
               {
                 sg = new SequenceGroup(sg);
                 sg.clear();
@@ -736,7 +788,7 @@ public class AnnotationLabels extends Panel implements ActionListener,
               }
               else
               {
-                if (evt.isControlDown())
+                if (jalview.util.Platform.isControlDown(evt))
                 {
                   sg.addOrRemove(aa[selectedRow].sequenceRef, true);
                 }
@@ -794,11 +846,13 @@ public class AnnotationLabels extends Panel implements ActionListener,
     }
   }
 
+  @Override
   public void update(Graphics g)
   {
     paint(g);
   }
 
+  @Override
   public void paint(Graphics g)
   {
     int w = getSize().width;
diff --git a/src/jalview/appletgui/AnnotationPanel.java b/src/jalview/appletgui/AnnotationPanel.java
index 2dfa480..aff3c0f 100644
--- a/src/jalview/appletgui/AnnotationPanel.java
+++ b/src/jalview/appletgui/AnnotationPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,9 +22,13 @@ package jalview.appletgui;
 
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceI;
 import jalview.renderer.AnnotationRenderer;
 import jalview.renderer.AwtRenderPanelI;
+import jalview.schemes.ResidueProperties;
+import jalview.util.Comparison;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.awt.Color;
 import java.awt.Dimension;
@@ -98,7 +102,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
 
   public AnnotationPanel(AlignmentPanel ap)
   {
-    MAC = new jalview.util.Platform().isAMac();
+    new jalview.util.Platform();
+    MAC = Platform.isAMac();
     this.ap = ap;
     av = ap.av;
     setLayout(null);
@@ -150,7 +155,7 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
 
     String label = "";
     if (av.getColumnSelection() != null
-            && av.getColumnSelection().size() > 0
+            && !av.getColumnSelection().isEmpty()
             && anot[av.getColumnSelection().getMin()] != null)
     {
       label = anot[av.getColumnSelection().getMin()].displayCharacter;
@@ -158,9 +163,12 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
 
     if (evt.getActionCommand().equals(REMOVE))
     {
-      for (int i = 0; i < av.getColumnSelection().size(); i++)
+      for (int index : av.getColumnSelection().getSelected())
       {
-        anot[av.getColumnSelection().columnAt(i)] = null;
+        if (av.getColumnSelection().isVisible(index))
+        {
+          anot[index] = null;
+        }
       }
     }
     else if (evt.getActionCommand().equals(LABEL))
@@ -177,10 +185,10 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
         aa[activeRow].hasText = true;
       }
 
-      for (int i = 0; i < av.getColumnSelection().size(); i++)
+      for (int index : av.getColumnSelection().getSelected())
       {
-        int index = av.getColumnSelection().columnAt(i);
-
+        // TODO: JAL-2001 - provide a fast method to list visible selected
+        // columns
         if (!av.getColumnSelection().isVisible(index))
         {
           continue;
@@ -201,10 +209,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
 
       Color col = udc.getColor();
 
-      for (int i = 0; i < av.getColumnSelection().size(); i++)
+      for (int index : av.getColumnSelection().getSelected())
       {
-        int index = av.getColumnSelection().columnAt(i);
-
         if (!av.getColumnSelection().isVisible(index))
         {
           continue;
@@ -238,7 +244,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
       else if (evt.getActionCommand().equals(STEM))
       {
         type = 'S';
-        symbol = "\u03C3";
+        int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
+        symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
       }
 
       if (!aa[activeRow].hasIcons)
@@ -262,10 +269,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
         }
       }
 
-      for (int i = 0; i < av.getColumnSelection().size(); i++)
+      for (int index : av.getColumnSelection().getSelected())
       {
-        int index = av.getColumnSelection().columnAt(i);
-
         if (!av.getColumnSelection().isVisible(index))
         {
           continue;
@@ -345,7 +350,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK
             && activeRow != -1)
     {
-      if (av.getColumnSelection() == null)
+      if (av.getColumnSelection() == null
+              || av.getColumnSelection().isEmpty())
       {
         return;
       }
@@ -353,10 +359,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
       PopupMenu pop = new PopupMenu(
               MessageManager.getString("label.structure_type"));
       MenuItem item;
-      /*
-       * Just display the needed structure options
-       */
-      if (av.getAlignment().isNucleotide() == true)
+
+      if (av.getAlignment().isNucleotide())
       {
         item = new MenuItem(STEM);
         item.addActionListener(this);
@@ -458,21 +462,67 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI,
       }
     }
 
-    int res = evt.getX() / av.getCharWidth() + av.getStartRes();
+    int column = evt.getX() / av.getCharWidth() + av.getStartRes();
 
     if (av.hasHiddenColumns())
     {
-      res = av.getColumnSelection().adjustForHiddenColumns(res);
+      column = av.getColumnSelection().adjustForHiddenColumns(column);
     }
 
-    if (row > -1 && res < aa[row].annotations.length
-            && aa[row].annotations[res] != null)
+    if (row > -1 && column < aa[row].annotations.length
+            && aa[row].annotations[column] != null)
     {
-      StringBuffer text = new StringBuffer("Sequence position " + (res + 1));
-      if (aa[row].annotations[res].description != null)
+      StringBuilder text = new StringBuilder();
+      text.append(MessageManager.getString("label.column")).append(" ")
+              .append(column + 1);
+      String description = aa[row].annotations[column].description;
+      if (description != null && description.length() > 0)
       {
-        text.append("  " + aa[row].annotations[res].description);
+        text.append("  ").append(description);
       }
+
+      /*
+       * if the annotation is sequence-specific, show the sequence number
+       * in the alignment, and (if not a gap) the residue and position
+       */
+      SequenceI seqref = aa[row].sequenceRef;
+      if (seqref != null)
+      {
+        int seqIndex = av.getAlignment().findIndex(seqref);
+        if (seqIndex != -1)
+        {
+          text.append(", ")
+                  .append(MessageManager.getString("label.sequence"))
+                  .append(" ").append(seqIndex + 1);
+          char residue = seqref.getCharAt(column);
+          if (!Comparison.isGap(residue))
+          {
+            text.append(" ");
+            String name;
+            if (av.getAlignment().isNucleotide())
+            {
+              name = ResidueProperties.nucleotideName.get(String
+                      .valueOf(residue));
+              text.append(" Nucleotide: ").append(
+                      name != null ? name : residue);
+            }
+            else
+            {
+              name = 'X' == residue ? "X" : ('*' == residue ? "STOP"
+                      : ResidueProperties.aa2Triplet.get(String
+                              .valueOf(residue)));
+              text.append(" Residue: ").append(
+                      name != null ? name : residue);
+            }
+            int residuePos = seqref.findPosition(column);
+            text.append(" (").append(residuePos).append(")");
+            // int residuePos = seqref.findPosition(column);
+            // text.append(residue).append(" (")
+            // .append(residuePos).append(")");
+          }
+        }
+      }
+
       ap.alignFrame.statusBar.setText(text.toString());
     }
   }
diff --git a/src/jalview/appletgui/AnnotationRowFilter.java b/src/jalview/appletgui/AnnotationRowFilter.java
index 45f7454..2bcc227 100644
--- a/src/jalview/appletgui/AnnotationRowFilter.java
+++ b/src/jalview/appletgui/AnnotationRowFilter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -85,45 +85,6 @@ public abstract class AnnotationRowFilter extends Panel
 
   }
 
-  public Vector getAnnotationItems(boolean isSeqAssociated)
-  {
-    Vector list = new Vector();
-    int index = 1;
-    int[] anmap = new int[av.getAlignment().getAlignmentAnnotation().length];
-    for (int i = 0; i < av.getAlignment().getAlignmentAnnotation().length; i++)
-    {
-      if (av.getAlignment().getAlignmentAnnotation()[i].sequenceRef == null)
-      {
-        if (isSeqAssociated)
-        {
-          continue;
-        }
-      }
-      else
-      {
-        enableSeqAss = true;
-      }
-      String label = av.getAlignment().getAlignmentAnnotation()[i].label;
-      if (!list.contains(label))
-      {
-        anmap[list.size()] = i;
-        list.add(label);
-
-      }
-      else
-      {
-        if (!isSeqAssociated)
-        {
-          anmap[list.size()] = i;
-          list.add(label + "_" + (index++));
-        }
-      }
-    }
-    this.annmap = new int[list.size()];
-    System.arraycopy(anmap, 0, this.annmap, 0, this.annmap.length);
-    return list;
-  }
-
   protected int getSelectedThresholdItem(int indexValue)
   {
     int selectedThresholdItem = -1;
@@ -186,11 +147,11 @@ public abstract class AnnotationRowFilter extends Panel
   protected void populateThresholdComboBox(Choice threshold)
   {
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_no_thereshold"));
+            .getString("label.threshold_feature_no_threshold"));
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_above_thereshold"));
+            .getString("label.threshold_feature_above_threshold"));
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_below_thereshold"));
+            .getString("label.threshold_feature_below_threshold"));
   }
 
   public jalview.datamodel.AlignmentAnnotation getCurrentAnnotation()
diff --git a/src/jalview/appletgui/AppletJmol.java b/src/jalview/appletgui/AppletJmol.java
index c9f275b..446236d 100644
--- a/src/jalview/appletgui/AppletJmol.java
+++ b/src/jalview/appletgui/AppletJmol.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -25,6 +25,7 @@ import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.FileParse;
+import jalview.io.StructureFile;
 import jalview.schemes.BuriedColourScheme;
 import jalview.schemes.HelixColourScheme;
 import jalview.schemes.HydrophobicColourScheme;
@@ -59,7 +60,6 @@ import java.awt.event.KeyListener;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.util.ArrayList;
-import java.util.Hashtable;
 import java.util.List;
 import java.util.Vector;
 
@@ -180,7 +180,7 @@ public class AppletJmol extends EmbmenuFrame implements
     this.ap = ap;
     jmb = new AppletJmolBinding(this, ap.getStructureSelectionManager(),
             new PDBEntry[] { pdbentry }, new SequenceI[][] { seq },
-            new String[][] { chains }, protocol);
+            protocol);
     jmb.setColourBySequence(true);
     if (pdbentry.getId() == null || pdbentry.getId().length() < 1)
     {
@@ -204,7 +204,7 @@ public class AppletJmol extends EmbmenuFrame implements
     String alreadyMapped = StructureSelectionManager
             .getStructureSelectionManager(ap.av.applet)
             .alreadyMappedToFile(pdbentry.getId());
-    MCview.PDBfile reader = null;
+    StructureFile reader = null;
     if (alreadyMapped != null)
     {
       reader = StructureSelectionManager.getStructureSelectionManager(
@@ -283,16 +283,14 @@ public class AppletJmol extends EmbmenuFrame implements
 
     this.addWindowListener(new WindowAdapter()
     {
+      @Override
       public void windowClosing(WindowEvent evt)
       {
         closeViewer();
       }
     });
-    if (pdbentry.getProperty() == null)
-    {
-      pdbentry.setProperty(new Hashtable());
-      pdbentry.getProperty().put("protocol", protocol);
-    }
+    pdbentry.setProperty("protocol", protocol);
+
     if (pdbentry.getFile() != null)
     {
       // import structure data from pdbentry.getFile based on given protocol
@@ -371,7 +369,7 @@ public class AppletJmol extends EmbmenuFrame implements
     jmb.loadInline(string);
   }
 
-  void setChainMenuItems(Vector<String> chains)
+  void setChainMenuItems(List<String> chains)
   {
     chainMenu.removeAll();
 
@@ -415,6 +413,7 @@ public class AppletJmol extends EmbmenuFrame implements
     this.setVisible(false);
   }
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     if (evt.getSource() == mappingMenuItem)
@@ -535,6 +534,7 @@ public class AppletJmol extends EmbmenuFrame implements
     jmb.setColourBySequence(itm == seqColour);
   }
 
+  @Override
   public void itemStateChanged(ItemEvent evt)
   {
     if (evt.getSource() == jmolColour)
@@ -553,6 +553,7 @@ public class AppletJmol extends EmbmenuFrame implements
     }
   }
 
+  @Override
   public void keyPressed(KeyEvent evt)
   {
     if (evt.getKeyCode() == KeyEvent.VK_ENTER && scriptWindow.isVisible())
@@ -564,10 +565,12 @@ public class AppletJmol extends EmbmenuFrame implements
 
   }
 
+  @Override
   public void keyTyped(KeyEvent evt)
   {
   }
 
+  @Override
   public void keyReleased(KeyEvent evt)
   {
   }
@@ -585,7 +588,7 @@ public class AppletJmol extends EmbmenuFrame implements
       repaint();
       return;
     }
-    setChainMenuItems(jmb.chainNames);
+    setChainMenuItems(jmb.getChainNames());
     jmb.colourBySequence(ap);
 
     setTitle(jmb.getViewerTitle());
@@ -641,11 +644,13 @@ public class AppletJmol extends EmbmenuFrame implements
   {
     Dimension currentSize = new Dimension();
 
+    @Override
     public void update(Graphics g)
     {
       paint(g);
     }
 
+    @Override
     public void paint(Graphics g)
     {
       currentSize = this.getSize();
diff --git a/src/jalview/appletgui/AppletJmolBinding.java b/src/jalview/appletgui/AppletJmolBinding.java
index f7a121c..85a4140 100644
--- a/src/jalview/appletgui/AppletJmolBinding.java
+++ b/src/jalview/appletgui/AppletJmolBinding.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,8 +29,6 @@ import jalview.structure.StructureSelectionManager;
 import java.awt.Container;
 import java.util.Map;
 
-import javajs.awt.Dimension;
-
 import org.jmol.api.JmolAppConsoleInterface;
 import org.jmol.console.AppletConsole;
 import org.jmol.java.BS;
@@ -45,9 +43,9 @@ class AppletJmolBinding extends JalviewJmolBinding
 
   public AppletJmolBinding(AppletJmol appletJmol,
           StructureSelectionManager sSm, PDBEntry[] pdbentry,
-          SequenceI[][] seq, String[][] chains, String protocol)
+          SequenceI[][] seq, String protocol)
   {
-    super(sSm, pdbentry, seq, chains, protocol);
+    super(sSm, pdbentry, seq, protocol);
     appletJmolBinding = appletJmol;
   }
 
@@ -113,12 +111,14 @@ class AppletJmolBinding extends JalviewJmolBinding
     appletJmolBinding.updateTitleAndMenus();
   }
 
+  @Override
   public void updateColours(Object source)
   {
     AlignmentPanel ap = (AlignmentPanel) source;
     colourBySequence(ap);
   }
 
+  @Override
   public void showUrl(String url)
   {
     try
@@ -143,6 +143,7 @@ class AppletJmolBinding extends JalviewJmolBinding
     // do nothing.
   }
 
+  @Override
   public void selectionChanged(BS arg0)
   {
     // TODO Auto-generated method stub
@@ -183,7 +184,7 @@ class AppletJmolBinding extends JalviewJmolBinding
   }
 
   @Override
-  public Dimension resizeInnerPanel(String data)
+  public int[] resizeInnerPanel(String data)
   {
     // TODO Auto-generated method stub
     return null;
diff --git a/src/jalview/appletgui/CutAndPasteTransfer.java b/src/jalview/appletgui/CutAndPasteTransfer.java
index c8bb972..7c831be 100644
--- a/src/jalview/appletgui/CutAndPasteTransfer.java
+++ b/src/jalview/appletgui/CutAndPasteTransfer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,6 +22,7 @@ package jalview.appletgui;
 
 import jalview.analysis.AlignmentUtils;
 import jalview.api.ComplexAlignFile;
+import jalview.api.FeaturesSourceI;
 import jalview.bin.JalviewLite;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
@@ -33,6 +34,7 @@ import jalview.io.FileParse;
 import jalview.io.IdentifyFile;
 import jalview.io.NewickFile;
 import jalview.io.TCoffeeScoreFile;
+import jalview.json.binding.biojson.v1.ColourSchemeMapper;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.TCoffeeColourScheme;
 import jalview.util.MessageManager;
@@ -115,6 +117,7 @@ public class CutAndPasteTransfer extends Panel implements ActionListener,
     addSequences.setVisible(false);
   }
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     if (evt.getSource() == accept)
@@ -222,7 +225,7 @@ public class CutAndPasteTransfer extends Panel implements ActionListener,
   {
     AlignmentI al = null;
 
-    String format = new IdentifyFile().Identify(text,
+    String format = new IdentifyFile().identify(text,
             AppletFormatAdapter.PASTE);
     AppletFormatAdapter afa = new AppletFormatAdapter(alignFrame.alignPanel);
     try
@@ -259,17 +262,27 @@ public class CutAndPasteTransfer extends Panel implements ActionListener,
                   .getHiddenSequences();
           boolean showSeqFeatures = ((ComplexAlignFile) source)
                   .isShowSeqFeatures();
-          ColourSchemeI cs = ((ComplexAlignFile) source).getColourScheme();
+          String colourSchemeName = ((ComplexAlignFile) source)
+                  .getGlobalColourScheme();
           af = new AlignFrame(al, hiddenSeqs, colSel,
                   alignFrame.viewport.applet, "Cut & Paste input - "
                           + format, false);
           af.getAlignViewport().setShowSequenceFeatures(showSeqFeatures);
-          af.changeColour(cs);
+          ColourSchemeI cs = ColourSchemeMapper.getJalviewColourScheme(
+                  colourSchemeName, al);
+          if (cs != null)
+          {
+            af.changeColour(cs);
+          }
         }
         else
         {
           af = new AlignFrame(al, alignFrame.viewport.applet,
                   "Cut & Paste input - " + format, false);
+          if (source instanceof FeaturesSourceI)
+          {
+            af.getAlignViewport().setShowSequenceFeatures(true);
+          }
         }
 
         af.statusBar
@@ -304,7 +317,7 @@ public class CutAndPasteTransfer extends Panel implements ActionListener,
     }
     AlignmentI protein = thisAlignment.isNucleotide() ? al : thisAlignment;
     AlignmentI dna = thisAlignment.isNucleotide() ? thisAlignment : al;
-    boolean mapped = AlignmentUtils.mapProteinToCdna(protein, dna);
+    boolean mapped = AlignmentUtils.mapProteinAlignmentToCdna(protein, dna);
     if (!mapped)
     {
       return false;
@@ -332,6 +345,11 @@ public class CutAndPasteTransfer extends Panel implements ActionListener,
     }
 
     /*
+     * 'align' the added alignment to match the current one
+     */
+    al.alignAs(thisAlignment);
+
+    /*
      * Open SplitFrame with DNA above and protein below, including the alignment
      * from textbox and a copy of the original.
      */
@@ -478,6 +496,7 @@ public class CutAndPasteTransfer extends Panel implements ActionListener,
     this.add(textarea, java.awt.BorderLayout.CENTER);
   }
 
+  @Override
   public void mousePressed(MouseEvent evt)
   {
     if (textarea.getText().startsWith(
@@ -487,18 +506,22 @@ public class CutAndPasteTransfer extends Panel implements ActionListener,
     }
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseEntered(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseExited(MouseEvent evt)
   {
   }
diff --git a/src/jalview/appletgui/EditNameDialog.java b/src/jalview/appletgui/EditNameDialog.java
index 3ebe791..5e50413 100644
--- a/src/jalview/appletgui/EditNameDialog.java
+++ b/src/jalview/appletgui/EditNameDialog.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/EmbmenuFrame.java b/src/jalview/appletgui/EmbmenuFrame.java
index 906933a..7b95530 100644
--- a/src/jalview/appletgui/EmbmenuFrame.java
+++ b/src/jalview/appletgui/EmbmenuFrame.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/ExtJmol.java b/src/jalview/appletgui/ExtJmol.java
index 6479296..830a0ce 100644
--- a/src/jalview/appletgui/ExtJmol.java
+++ b/src/jalview/appletgui/ExtJmol.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -49,11 +49,11 @@ public class ExtJmol extends JalviewJmolBinding
   private AlignmentPanel ap;
 
   protected ExtJmol(jalview.appletgui.AlignFrame alframe,
-          PDBEntry[] pdbentry, SequenceI[][] seq, String[][] chains,
+          PDBEntry[] pdbentry, SequenceI[][] seq,
           String protocol)
   {
     super(alframe.alignPanel.getStructureSelectionManager(), pdbentry, seq,
-            chains, protocol);
+            protocol);
   }
 
   public ExtJmol(Viewer viewer, AlignmentPanel alignPanel,
@@ -64,6 +64,7 @@ public class ExtJmol extends JalviewJmolBinding
     notifyFileLoaded(null, null, null, null, 0);
   }
 
+  @Override
   public void updateColours(Object source)
   {
 
@@ -71,6 +72,7 @@ public class ExtJmol extends JalviewJmolBinding
 
   }
 
+  @Override
   public void showUrl(String arg0)
   {
     showUrl(arg0, "jmol");
@@ -126,6 +128,7 @@ public class ExtJmol extends JalviewJmolBinding
     // ignore
   }
 
+  @Override
   public void selectionChanged(BS arg0)
   {
     System.out.println(arg0);
diff --git a/src/jalview/appletgui/FeatureColourChooser.java b/src/jalview/appletgui/FeatureColourChooser.java
index e6e87f2..4080345 100644
--- a/src/jalview/appletgui/FeatureColourChooser.java
+++ b/src/jalview/appletgui/FeatureColourChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,9 +20,10 @@
  */
 package jalview.appletgui;
 
+import jalview.api.FeatureColourI;
 import jalview.datamodel.GraphLine;
 import jalview.schemes.AnnotationColourGradient;
-import jalview.schemes.GraduatedColor;
+import jalview.schemes.FeatureColour;
 import jalview.util.MessageManager;
 
 import java.awt.Checkbox;
@@ -60,9 +61,9 @@ public class FeatureColourChooser extends Panel implements ActionListener,
 
   // AlignmentPanel ap;
 
-  GraduatedColor cs;
+  FeatureColourI cs;
 
-  Object oldcs;
+  FeatureColourI oldcs;
 
   Hashtable oldgroupColours;
 
@@ -91,29 +92,29 @@ public class FeatureColourChooser extends Panel implements ActionListener,
   {
     this.type = type;
     fr = frenderer;
-    float mm[] = ((float[][]) fr.getMinMax().get(type))[0];
+    float mm[] = fr.getMinMax().get(type)[0];
     min = mm[0];
     max = mm[1];
     oldcs = fr.getFeatureColours().get(type);
-    if (oldcs instanceof GraduatedColor)
+    if (oldcs.isGraduatedColour())
     {
-      cs = new GraduatedColor((GraduatedColor) oldcs, min, max);
+      cs = new FeatureColour((FeatureColour) oldcs, min, max);
     }
     else
     {
       // promote original color to a graduated color
       Color bl = Color.black;
-      if (oldcs instanceof Color)
+      if (oldcs.isSimpleColour())
       {
-        bl = (Color) oldcs;
+        bl = oldcs.getColour();
       }
       // original colour becomes the maximum colour
-      cs = new GraduatedColor(Color.white, bl, mm[0], mm[1]);
+      cs = new FeatureColour(Color.white, bl, mm[0], mm[1]);
     }
-    minColour.setBackground(cs.getMinColor());
-    maxColour.setBackground(cs.getMaxColor());
-    minColour.setForeground(cs.getMinColor());
-    maxColour.setForeground(cs.getMaxColor());
+    minColour.setBackground(cs.getMinColour());
+    maxColour.setBackground(cs.getMaxColour());
+    minColour.setForeground(cs.getMinColour());
+    maxColour.setForeground(cs.getMaxColour());
     colourFromLabel.setState(cs.isColourByLabel());
     adjusting = true;
 
@@ -123,10 +124,8 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     } catch (Exception ex)
     {
     }
-    threshold
-            .select(cs.getThreshType() == AnnotationColourGradient.NO_THRESHOLD ? 0
-                    : cs.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD ? 1
-                            : 2);
+    threshold.select(cs.isAboveThreshold() ? 1 : (cs.isBelowThreshold() ? 2
+            : 0));
 
     adjusting = false;
     changeColour();
@@ -192,11 +191,11 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     jPanel4.setBackground(Color.white);
     threshold.addItemListener(this);
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_no_thereshold"));
+            .getString("label.threshold_feature_no_threshold"));
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_above_thereshold"));
+            .getString("label.threshold_feature_above_threshold"));
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_below_thereshold"));
+            .getString("label.threshold_feature_below_threshold"));
     thresholdValue.addActionListener(this);
     slider.setBackground(Color.white);
     slider.setEnabled(false);
@@ -259,6 +258,7 @@ public class FeatureColourChooser extends Panel implements ActionListener,
 
   private GraphLine threshline;
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     if (evt.getSource() == thresholdValue)
@@ -286,6 +286,7 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     }
   }
 
+  @Override
   public void itemStateChanged(ItemEvent evt)
   {
     maxColour.setEnabled(!colourFromLabel.getState());
@@ -293,19 +294,20 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     changeColour();
   }
 
+  @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
     if (!adjusting)
     {
-      thresholdValue.setText(((float) slider.getValue() / 1000f) + "");
+      thresholdValue.setText((slider.getValue() / 1000f) + "");
       valueChanged();
     }
   }
 
   protected void valueChanged()
   {
-    threshline.value = (float) slider.getValue() / 1000f;
-    cs.setThresh(threshline.value);
+    threshline.value = slider.getValue() / 1000f;
+    cs.setThreshold(threshline.value);
     changeColour();
     PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
     // ap.paintAlignment(false);
@@ -369,7 +371,7 @@ public class FeatureColourChooser extends Panel implements ActionListener,
 
     slider.setEnabled(true);
     thresholdValue.setEnabled(true);
-    GraduatedColor acg = new GraduatedColor(minColour.getBackground(),
+    FeatureColour acg = new FeatureColour(minColour.getBackground(),
             maxColour.getBackground(), min, max);
 
     acg.setColourByLabel(colourFromLabel.getState());
@@ -393,7 +395,7 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
     {
       adjusting = true;
-      acg.setThresh(threshline.value);
+      acg.setThreshold(threshline.value);
 
       float range = max * 1000f - min * 1000f;
 
@@ -406,17 +408,17 @@ public class FeatureColourChooser extends Panel implements ActionListener,
       adjusting = false;
     }
 
-    acg.setThreshType(aboveThreshold);
+    acg.setAboveThreshold(true);
     if (thresholdIsMin.getState()
             && aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
     {
       if (aboveThreshold == AnnotationColourGradient.ABOVE_THRESHOLD)
       {
-        acg = new GraduatedColor(acg, threshline.value, max);
+        acg = new FeatureColour(acg, threshline.value, max);
       }
       else
       {
-        acg = new GraduatedColor(acg, min, threshline.value);
+        acg = new FeatureColour(acg, min, threshline.value);
       }
     }
 
@@ -434,14 +436,17 @@ public class FeatureColourChooser extends Panel implements ActionListener,
 
   }
 
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
   }
 
+  @Override
   public void mousePressed(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     if (evt.getSource() == minColour || evt.getSource() == maxColour)
@@ -456,10 +461,12 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     // ap.paintAlignment(true);
   }
 
+  @Override
   public void mouseEntered(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseExited(MouseEvent evt)
   {
   }
diff --git a/src/jalview/appletgui/FeatureRenderer.java b/src/jalview/appletgui/FeatureRenderer.java
index 1dc48d2..cd2f9ab 100644
--- a/src/jalview/appletgui/FeatureRenderer.java
+++ b/src/jalview/appletgui/FeatureRenderer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,11 +20,14 @@
  */
 package jalview.appletgui;
 
+import jalview.api.FeatureColourI;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
-import jalview.schemes.AnnotationColourGradient;
-import jalview.schemes.GraduatedColor;
+import jalview.io.FeaturesFile;
+import jalview.schemes.FeatureColour;
+import jalview.schemes.UserColourScheme;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 
@@ -43,6 +46,8 @@ import java.awt.TextArea;
 import java.awt.TextField;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
 import java.util.Hashtable;
 
 /**
@@ -63,15 +68,11 @@ public class FeatureRenderer extends
    * Creates a new FeatureRenderer object.
    * 
    * @param av
-   *          DOCUMENT ME!
    */
   public FeatureRenderer(AlignmentViewport av)
   {
-    super();
-    this.av = av;
+    super(av);
 
-    setTransparencyAvailable(!System.getProperty("java.version")
-            .startsWith("1.1"));
   }
 
   static String lastFeatureAdded;
@@ -97,51 +98,35 @@ public class FeatureRenderer extends
     /**
      * render a feature style in the amend feature dialog box
      */
-    public void updateColor(Object newcol)
+    public void updateColor(FeatureColourI newcol)
     {
-
-      Color bg, col = null;
-      GraduatedColor gcol = null;
+      Color bg = null;
       String vlabel = "";
-      if (newcol instanceof Color)
-      {
-        isGcol = false;
-        col = (Color) newcol;
-        gcol = null;
-      }
-      else if (newcol instanceof GraduatedColor)
+      if (newcol.isSimpleColour())
       {
-        isGcol = true;
-        gcol = (GraduatedColor) newcol;
-        col = null;
+        bg = newcol.getColour();
+        setBackground(bg);
       }
       else
       {
-        throw new Error(
-                MessageManager
-                        .getString("error.invalid_colour_for_mycheckbox"));
-      }
-      if (col != null)
-      {
-        setBackground(bg = col);
-      }
-      else
-      {
-        if (gcol.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
+        if (newcol.isAboveThreshold())
+        {
+          vlabel += " (>)";
+        }
+        else if (newcol.isBelowThreshold())
         {
-          vlabel += " "
-                  + ((gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD) ? "(>)"
-                          : "(<)");
+          vlabel += " (<)";
         }
-        if (isColourByLabel = gcol.isColourByLabel())
+
+        if (isColourByLabel = newcol.isColourByLabel())
         {
           setBackground(bg = Color.white);
           vlabel += " (by Label)";
         }
         else
         {
-          setBackground(bg = gcol.getMinColor());
-          maxCol = gcol.getMaxColor();
+          setBackground(bg = newcol.getMinColour());
+          maxCol = newcol.getMaxColour();
         }
       }
       label = vlabel;
@@ -154,6 +139,7 @@ public class FeatureRenderer extends
       super(null);
     }
 
+    @Override
     public void paint(Graphics g)
     {
       Dimension d = getSize();
@@ -227,6 +213,7 @@ public class FeatureRenderer extends
 
       overlaps.addItemListener(new java.awt.event.ItemListener()
       {
+        @Override
         public void itemStateChanged(java.awt.event.ItemEvent e)
         {
           int index = overlaps.getSelectedIndex();
@@ -239,18 +226,19 @@ public class FeatureRenderer extends
             start.setText(features[index].getBegin() + "");
             end.setText(features[index].getEnd() + "");
 
-            SearchResults highlight = new SearchResults();
+            SearchResultsI highlight = new SearchResults();
             highlight.addResult(sequences[0], features[index].getBegin(),
                     features[index].getEnd());
 
             ap.seqPanel.seqCanvas.highlightSearchResults(highlight);
 
           }
-          Object col = getFeatureStyle(name.getText());
+          FeatureColourI col = getFeatureStyle(name.getText());
           if (col == null)
           {
-            col = new jalview.schemes.UserColourScheme()
+            Color generatedColour = UserColourScheme
                     .createColourFromName(name.getText());
+            col = new FeatureColour(generatedColour);
           }
 
           colourPanel.updateColor(col);
@@ -264,23 +252,24 @@ public class FeatureRenderer extends
 
     tmp = new Panel();
     panel.add(tmp);
-    tmp.add(new Label("Name: ", Label.RIGHT));
+    tmp.add(new Label(MessageManager.getString("label.name:"), Label.RIGHT));
     tmp.add(name);
 
     tmp = new Panel();
     panel.add(tmp);
-    tmp.add(new Label("Group: ", Label.RIGHT));
+    tmp.add(new Label(MessageManager.getString("label.group:"), Label.RIGHT));
     tmp.add(source);
 
     tmp = new Panel();
     panel.add(tmp);
-    tmp.add(new Label("Colour: ", Label.RIGHT));
+    tmp.add(new Label(MessageManager.getString("label.colour"), Label.RIGHT));
     tmp.add(colourPanel);
 
     bigPanel.add(panel, BorderLayout.NORTH);
 
     panel = new Panel();
-    panel.add(new Label("Description: ", Label.RIGHT));
+    panel.add(new Label(MessageManager.getString("label.description:"),
+            Label.RIGHT));
     panel.add(new ScrollPane().add(description));
 
     if (!newFeatures)
@@ -288,9 +277,11 @@ public class FeatureRenderer extends
       bigPanel.add(panel, BorderLayout.SOUTH);
 
       panel = new Panel();
-      panel.add(new Label(" Start:", Label.RIGHT));
+      panel.add(new Label(MessageManager.getString("label.start"),
+              Label.RIGHT));
       panel.add(start);
-      panel.add(new Label("  End:", Label.RIGHT));
+      panel.add(new Label(MessageManager.getString("label.end"),
+              Label.RIGHT));
       panel.add(end);
       bigPanel.add(panel, BorderLayout.CENTER);
     }
@@ -344,6 +335,7 @@ public class FeatureRenderer extends
       dialog.buttonPanel.add(deleteButton, 1);
       deleteButton.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent evt)
         {
           deleteFeature = true;
@@ -357,20 +349,16 @@ public class FeatureRenderer extends
     start.setText(features[0].getBegin() + "");
     end.setText(features[0].getEnd() + "");
     description.setText(features[0].getDescription());
-    Color col = getColour(name.getText());
-    if (col == null)
-    {
-      col = new jalview.schemes.UserColourScheme()
-              .createColourFromName(name.getText());
-    }
-    Object fcol = getFeatureStyle(name.getText());
+    // lookup (or generate) the feature colour
+    FeatureColourI fcol = getFeatureStyle(name.getText());
     // simply display the feature color in a box
     colourPanel.updateColor(fcol);
     dialog.setResizable(true);
     // TODO: render the graduated color in the box.
-    colourPanel.addMouseListener(new java.awt.event.MouseAdapter()
+    colourPanel.addMouseListener(new MouseAdapter()
     {
-      public void mousePressed(java.awt.event.MouseEvent evt)
+      @Override
+      public void mousePressed(MouseEvent evt)
       {
         if (!colourPanel.isGcol)
         {
@@ -378,15 +366,14 @@ public class FeatureRenderer extends
         }
         else
         {
-          FeatureColourChooser fcc = new FeatureColourChooser(
-                  ap.alignFrame, name.getText());
+          new FeatureColourChooser(ap.alignFrame, name.getText());
           dialog.transferFocus();
         }
       }
     });
     dialog.setVisible(true);
 
-    jalview.io.FeaturesFile ffile = new jalview.io.FeaturesFile();
+    FeaturesFile ffile = new FeaturesFile();
 
     if (dialog.accept)
     {
@@ -415,7 +402,7 @@ public class FeatureRenderer extends
         if (!colourPanel.isGcol)
         {
           // update colour - otherwise its already done.
-          setColour(sf.type, colourPanel.getBackground());
+          setColour(sf.type, new FeatureColour(colourPanel.getBackground()));
         }
         try
         {
@@ -455,7 +442,7 @@ public class FeatureRenderer extends
         {
           setGroupVisibility(lastFeatureGroupAdded, true);
         }
-        setColour(lastFeatureAdded, newColour); // was fcol
+        setColour(lastFeatureAdded, new FeatureColour(newColour)); // was fcol
         setVisible(lastFeatureAdded);
         findAllFeatures(false); // different to original applet behaviour ?
         // findAllFeatures();
diff --git a/src/jalview/appletgui/FeatureSettings.java b/src/jalview/appletgui/FeatureSettings.java
index 01e096e..b09c0fa 100644
--- a/src/jalview/appletgui/FeatureSettings.java
+++ b/src/jalview/appletgui/FeatureSettings.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,11 +20,10 @@
  */
 package jalview.appletgui;
 
+import jalview.api.FeatureColourI;
 import jalview.api.FeatureSettingsControllerI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceFeature;
-import jalview.schemes.AnnotationColourGradient;
-import jalview.schemes.GraduatedColor;
 import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
@@ -57,9 +56,10 @@ import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
+import java.util.Arrays;
 import java.util.Enumeration;
-import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 import java.util.Vector;
 
 public class FeatureSettings extends Panel implements ItemListener,
@@ -94,14 +94,7 @@ public class FeatureSettings extends Panel implements ItemListener,
     transparency = new Scrollbar(Scrollbar.HORIZONTAL,
             100 - (int) (fr.getTransparency() * 100), 1, 1, 100);
 
-    if (fr.isTransparencyAvailable())
-    {
-      transparency.addAdjustmentListener(this);
-    }
-    else
-    {
-      transparency.setEnabled(false);
-    }
+    transparency.addAdjustmentListener(this);
 
     java.net.URL url = getClass().getResource("/images/link.gif");
     if (url != null)
@@ -133,17 +126,8 @@ public class FeatureSettings extends Panel implements ItemListener,
 
     Panel tPanel = new Panel(new BorderLayout());
 
-    if (fr.isTransparencyAvailable())
-    {
-      tPanel.add(transparency, BorderLayout.CENTER);
-      tPanel.add(new Label("Transparency"), BorderLayout.EAST);
-    }
-    else
-    {
-      tPanel.add(
-              new Label("Transparency not available in this web browser"),
-              BorderLayout.CENTER);
-    }
+    tPanel.add(transparency, BorderLayout.CENTER);
+    tPanel.add(new Label("Transparency"), BorderLayout.EAST);
 
     lowerPanel.add(tPanel, BorderLayout.SOUTH);
 
@@ -165,6 +149,7 @@ public class FeatureSettings extends Panel implements ItemListener,
     final FeatureSettings me = this;
     frame.addWindowListener(new WindowAdapter()
     {
+      @Override
       public void windowClosing(WindowEvent e)
       {
         if (me.av.featureSettings == me)
@@ -185,6 +170,7 @@ public class FeatureSettings extends Panel implements ItemListener,
             width, height);
   }
 
+  @Override
   public void paint(Graphics g)
   {
     g.setColor(Color.black);
@@ -198,12 +184,12 @@ public class FeatureSettings extends Panel implements ItemListener,
             60);
   }
 
-  protected void popupSort(final MyCheckbox check, final Hashtable minmax,
-          int x, int y)
+  protected void popupSort(final MyCheckbox check,
+          final Map<String, float[][]> minmax, int x, int y)
   {
     final String type = check.type;
-    final Object typeCol = fr.getFeatureStyle(type);
-    java.awt.PopupMenu men = new PopupMenu(MessageManager.formatMessage(
+    final FeatureColourI typeCol = fr.getFeatureStyle(type);
+    PopupMenu men = new PopupMenu(MessageManager.formatMessage(
             "label.settings_for_type", new String[] { type }));
     java.awt.MenuItem scr = new MenuItem(
             MessageManager.getString("label.sort_by_score"));
@@ -212,10 +198,11 @@ public class FeatureSettings extends Panel implements ItemListener,
     scr.addActionListener(new ActionListener()
     {
 
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        me.ap.alignFrame.avc
-                .sortAlignmentByFeatureScore(new String[] { type });
+        me.ap.alignFrame.avc.sortAlignmentByFeatureScore(Arrays
+                .asList(new String[] { type }));
       }
 
     });
@@ -224,17 +211,19 @@ public class FeatureSettings extends Panel implements ItemListener,
     dens.addActionListener(new ActionListener()
     {
 
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        me.ap.alignFrame.avc
-                .sortAlignmentByFeatureDensity(new String[] { type });
+        me.ap.alignFrame.avc.sortAlignmentByFeatureDensity(Arrays
+                .asList(new String[] { type }));
       }
 
     });
     men.add(dens);
+
     if (minmax != null)
     {
-      final Object typeMinMax = minmax.get(type);
+      final float[][] typeMinMax = minmax.get(type);
       /*
        * final java.awt.CheckboxMenuItem chb = new
        * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
@@ -247,20 +236,21 @@ public class FeatureSettings extends Panel implements ItemListener,
        * 
        * }); men.add(chb);
        */
-      if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
+      if (typeMinMax != null && typeMinMax[0] != null)
       {
         // graduated colourschemes for those where minmax exists for the
         // positional features
         MenuItem mxcol = new MenuItem(
-                (typeCol instanceof Color) ? "Graduated Colour"
+                (typeCol.isSimpleColour()) ? "Graduated Colour"
                         : "Single Colour");
         men.add(mxcol);
         mxcol.addActionListener(new ActionListener()
         {
 
+          @Override
           public void actionPerformed(ActionEvent e)
           {
-            if (typeCol instanceof Color)
+            if (typeCol.isSimpleColour())
             {
               new FeatureColourChooser(me, type);
               // write back the current colour object to update the table
@@ -268,14 +258,64 @@ public class FeatureSettings extends Panel implements ItemListener,
             }
             else
             {
-              new UserDefinedColours(me, check.type,
-                      ((GraduatedColor) typeCol));
+              new UserDefinedColours(me, check.type, typeCol);
             }
           }
 
         });
       }
     }
+
+    MenuItem selectContaining = new MenuItem(
+            MessageManager.getString("label.select_columns_containing"));
+    selectContaining.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        me.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
+                false, type);
+      }
+    });
+    men.add(selectContaining);
+
+    MenuItem selectNotContaining = new MenuItem(
+            MessageManager.getString("label.select_columns_not_containing"));
+    selectNotContaining.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        me.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
+                false, type);
+      }
+    });
+    men.add(selectNotContaining);
+
+    MenuItem hideContaining = new MenuItem(
+            MessageManager.getString("label.hide_columns_containing"));
+    hideContaining.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        hideFeatureColumns(type, true);
+      }
+    });
+    men.add(hideContaining);
+
+    MenuItem hideNotContaining = new MenuItem(
+            MessageManager.getString("label.hide_columns_not_containing"));
+    hideNotContaining.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        hideFeatureColumns(type, false);
+      }
+    });
+    men.add(hideNotContaining);
+
     this.featurePanel.add(men);
     men.show(this.featurePanel, x, y);
   }
@@ -312,9 +352,7 @@ public class FeatureSettings extends Panel implements ItemListener,
     for (String group : fr.getFeatureGroups())
     {
       boolean vis = fr.checkGroupVisibility(group, false);
-      Checkbox check = new MyCheckbox(group, vis,
-              (fr.featureLinks != null && fr.featureLinks
-                      .containsKey(group)));
+      Checkbox check = new MyCheckbox(group, vis, false);
       check.addMouseListener(this);
       check.setFont(new Font("Serif", Font.BOLD, 12));
       check.addItemListener(groupItemListener);
@@ -334,7 +372,7 @@ public class FeatureSettings extends Panel implements ItemListener,
   {
     SequenceFeature[] tmpfeatures;
     String group = null, type;
-    Vector visibleChecks = new Vector();
+    Vector<String> visibleChecks = new Vector<String>();
     AlignmentI alignment = av.getAlignment();
     for (int i = 0; i < alignment.getHeight(); i++)
     {
@@ -400,7 +438,7 @@ public class FeatureSettings extends Panel implements ItemListener,
 
     // now add checkboxes which should be visible,
     // if they have not already been added
-    Enumeration en = visibleChecks.elements();
+    Enumeration<String> en = visibleChecks.elements();
 
     while (en.hasMoreElements())
     {
@@ -452,10 +490,7 @@ public class FeatureSettings extends Panel implements ItemListener,
         selected = true;
       }
 
-      check = new MyCheckbox(
-              type,
-              selected,
-              (fr.featureLinks != null && fr.featureLinks.containsKey(type)),
+      check = new MyCheckbox(type, selected, false,
               fr.getFeatureStyle(type));
 
       check.addMouseListener(this);
@@ -474,6 +509,7 @@ public class FeatureSettings extends Panel implements ItemListener,
     }
   }
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     for (int i = 0; i < featurePanel.getComponentCount(); i++)
@@ -486,6 +522,7 @@ public class FeatureSettings extends Panel implements ItemListener,
 
   private ItemListener groupItemListener = new ItemListener()
   {
+    @Override
     public void itemStateChanged(ItemEvent evt)
     {
       Checkbox source = (Checkbox) evt.getSource();
@@ -500,6 +537,7 @@ public class FeatureSettings extends Panel implements ItemListener,
     };
   };
 
+  @Override
   public void itemStateChanged(ItemEvent evt)
   {
     selectionChanged();
@@ -533,22 +571,7 @@ public class FeatureSettings extends Panel implements ItemListener,
 
   boolean dragging = false;
 
-  public void mousePressed(MouseEvent evt)
-  {
-
-    selectedCheck = (MyCheckbox) evt.getSource();
-
-    if (fr.featureLinks != null
-            && fr.featureLinks.containsKey(selectedCheck.type))
-    {
-      if (evt.getX() > selectedCheck.stringWidth + 20)
-      {
-        evt.consume();
-      }
-    }
-
-  }
-
+  @Override
   public void mouseDragged(MouseEvent evt)
   {
     if (((Component) evt.getSource()).getParent() != featurePanel)
@@ -558,6 +581,7 @@ public class FeatureSettings extends Panel implements ItemListener,
     dragging = true;
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     if (((Component) evt.getSource()).getParent() != featurePanel)
@@ -610,19 +634,9 @@ public class FeatureSettings extends Panel implements ItemListener,
     }
   }
 
-  public void setUserColour(String feature, Object originalColour)
+  public void setUserColour(String feature, FeatureColourI originalColour)
   {
-    if (originalColour instanceof Color
-            || originalColour instanceof GraduatedColor)
-    {
-      fr.setColour(feature, originalColour);
-    }
-    else
-    {
-      throw new Error(
-              MessageManager
-                      .getString("error.implementation_error_unsupported_feature_colour_object"));
-    }
+    fr.setColour(feature, originalColour);
     refreshTable();
   }
 
@@ -633,14 +647,17 @@ public class FeatureSettings extends Panel implements ItemListener,
     ap.paintAlignment(true);
   }
 
+  @Override
   public void mouseEntered(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseExited(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
     MyCheckbox check = (MyCheckbox) evt.getSource();
@@ -648,16 +665,6 @@ public class FeatureSettings extends Panel implements ItemListener,
     {
       this.popupSort(check, fr.getMinMax(), evt.getX(), evt.getY());
     }
-    if (fr.featureLinks != null && fr.featureLinks.containsKey(check.type))
-    {
-      if (evt.getX() > check.stringWidth + 20)
-      {
-        evt.consume();
-        String link = fr.featureLinks.get(check.type).toString();
-        ap.alignFrame.showURL(link.substring(link.indexOf("|") + 1),
-                link.substring(0, link.indexOf("|")));
-      }
-    }
 
     if (check.getParent() != featurePanel)
     {
@@ -666,10 +673,10 @@ public class FeatureSettings extends Panel implements ItemListener,
 
     if (evt.getClickCount() > 1)
     {
-      Object fcol = fr.getFeatureStyle(check.type);
-      if (fcol instanceof Color)
+      FeatureColourI fcol = fr.getFeatureStyle(check.type);
+      if (fcol.isSimpleColour())
       {
-        new UserDefinedColours(this, check.type, (Color) fcol);
+        new UserDefinedColours(this, check.type, fcol.getColour());
       }
       else
       {
@@ -680,10 +687,12 @@ public class FeatureSettings extends Panel implements ItemListener,
     }
   }
 
+  @Override
   public void mouseMoved(MouseEvent evt)
   {
   }
 
+  @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
     fr.setTransparency((100 - transparency.getValue()) / 100f);
@@ -699,49 +708,34 @@ public class FeatureSettings extends Panel implements ItemListener,
 
     boolean hasLink;
 
-    GraduatedColor gcol;
+    FeatureColourI col;
 
-    Color col;
-
-    public void updateColor(Object newcol)
+    public void updateColor(FeatureColourI newcol)
     {
-      if (newcol instanceof Color)
-      {
-        col = (Color) newcol;
-        gcol = null;
-      }
-      else if (newcol instanceof GraduatedColor)
+      col = newcol;
+      if (col.isSimpleColour())
       {
-        gcol = (GraduatedColor) newcol;
-        col = null;
-      }
-      else
-      {
-        throw new Error(
-                MessageManager
-                        .getString("error.invalid_colour_for_mycheckbox"));
-      }
-      if (col != null)
-      {
-        setBackground(col);
+        setBackground(col.getColour());
       }
       else
       {
         String vlabel = type;
-        if (gcol.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
+        if (col.isAboveThreshold())
         {
-          vlabel += " "
-                  + ((gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD) ? "(>)"
-                          : "(<)");
+          vlabel += " (>)";
         }
-        if (gcol.isColourByLabel())
+        else if (col.isBelowThreshold())
+        {
+          vlabel += " (<)";
+        }
+        if (col.isColourByLabel())
         {
           setBackground(Color.white);
           vlabel += " (by Label)";
         }
         else
         {
-          setBackground(gcol.getMinColor());
+          setBackground(col.getMinColour());
         }
         this.setLabel(vlabel);
       }
@@ -758,18 +752,19 @@ public class FeatureSettings extends Panel implements ItemListener,
     }
 
     public MyCheckbox(String type, boolean selected, boolean b,
-            Object featureStyle)
+            FeatureColourI featureStyle)
     {
       this(type, selected, b);
       updateColor(featureStyle);
     }
 
+    @Override
     public void paint(Graphics g)
     {
       Dimension d = getSize();
-      if (gcol != null)
+      if (col != null)
       {
-        if (gcol.isColourByLabel())
+        if (col.isColourByLabel())
         {
           g.setColor(Color.white);
           g.fillRect(d.width / 2, 0, d.width / 2, d.height);
@@ -785,9 +780,9 @@ public class FeatureSettings extends Panel implements ItemListener,
            */
 
         }
-        else
+        else if (col.isGraduatedColour())
         {
-          Color maxCol = gcol.getMaxColor();
+          Color maxCol = col.getMaxColour();
           g.setColor(maxCol);
           g.fillRect(d.width / 2, 0, d.width / 2, d.height);
 
@@ -802,4 +797,30 @@ public class FeatureSettings extends Panel implements ItemListener,
     }
   }
 
+  /**
+   * Hide columns containing (or not containing) a given feature type
+   * 
+   * @param type
+   * @param columnsContaining
+   */
+  void hideFeatureColumns(final String type, boolean columnsContaining)
+  {
+    if (ap.alignFrame.avc.markColumnsContainingFeatures(columnsContaining,
+            false, false, type))
+    {
+      if (ap.alignFrame.avc.markColumnsContainingFeatures(
+              !columnsContaining, false, false, type))
+      {
+        ap.alignFrame.viewport.hideSelectedColumns();
+      }
+    }
+  }
+
+  @Override
+  public void mousePressed(MouseEvent e)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
 }
diff --git a/src/jalview/appletgui/Finder.java b/src/jalview/appletgui/Finder.java
index b1dcb88..9ee75ee 100644
--- a/src/jalview/appletgui/Finder.java
+++ b/src/jalview/appletgui/Finder.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,8 @@
  */
 package jalview.appletgui;
 
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultMatchI;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.util.MessageManager;
@@ -50,7 +51,7 @@ public class Finder extends Panel implements ActionListener
 
   Frame frame;
 
-  SearchResults searchResults;
+  SearchResultsI searchResults;
 
   int seqIndex = 0;
 
@@ -76,6 +77,7 @@ public class Finder extends Panel implements ActionListener
     frame.repaint();
     frame.addWindowListener(new WindowAdapter()
     {
+      @Override
       public void windowClosing(WindowEvent evt)
       {
         ap.highlightSearchResults(null);
@@ -84,6 +86,7 @@ public class Finder extends Panel implements ActionListener
     textfield.requestFocus();
   }
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     if (evt.getSource() == textfield)
@@ -114,13 +117,15 @@ public class Finder extends Panel implements ActionListener
     SequenceFeature[] features = new SequenceFeature[searchResults
             .getSize()];
 
-    for (int i = 0; i < searchResults.getSize(); i++)
+    int i = 0;
+    for (SearchResultMatchI match : searchResults.getResults())
     {
-      seqs[i] = searchResults.getResultSequence(i);
+      seqs[i] = match.getSequence().getDatasetSequence();
 
       features[i] = new SequenceFeature(textfield.getText().trim(),
-              "Search Results", null, searchResults.getResultStart(i),
-              searchResults.getResultEnd(i), "Search Results");
+              "Search Results", null, match.getStart(), match.getEnd(),
+              "Search Results");
+      i++;
     }
 
     if (ap.seqPanel.seqCanvas.getFeatureRenderer().amendFeatures(seqs,
@@ -152,7 +157,7 @@ public class Finder extends Panel implements ActionListener
     seqIndex = finder.getSeqIndex();
     resIndex = finder.getResIndex();
     searchResults = finder.getSearchResults();
-    Vector idMatch = finder.getIdMatch();
+    Vector<SequenceI> idMatch = finder.getIdMatch();
     boolean haveResults = false;
     // set or reset the GUI
     if ((idMatch.size() > 0))
@@ -246,6 +251,7 @@ public class Finder extends Panel implements ActionListener
     textfield.setBounds(new Rectangle(40, 17, 133, 21));
     textfield.addKeyListener(new java.awt.event.KeyAdapter()
     {
+      @Override
       public void keyTyped(KeyEvent e)
       {
         textfield_keyTyped(e);
diff --git a/src/jalview/appletgui/FontChooser.java b/src/jalview/appletgui/FontChooser.java
index 8ada2e9..545de1b 100644
--- a/src/jalview/appletgui/FontChooser.java
+++ b/src/jalview/appletgui/FontChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/IdCanvas.java b/src/jalview/appletgui/IdCanvas.java
index 58a4f10..e75413d 100644
--- a/src/jalview/appletgui/IdCanvas.java
+++ b/src/jalview/appletgui/IdCanvas.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -138,11 +138,13 @@ public class IdCanvas extends Panel
     repaint();
   }
 
+  @Override
   public void update(Graphics g)
   {
     paint(g);
   }
 
+  @Override
   public void paint(Graphics g)
   {
     if (getSize().height < 0 || getSize().width < 0)
@@ -378,7 +380,7 @@ public class IdCanvas extends Panel
     Font bold = new Font(av.getFont().getName(), Font.BOLD, av.getFont()
             .getSize());
 
-    if (av.isHiddenRepSequence(seq))
+    if (av.isReferenceSeq(seq) || av.isHiddenRepSequence(seq))
     {
       gg.setFont(bold);
       return true;
diff --git a/src/jalview/appletgui/IdPanel.java b/src/jalview/appletgui/IdPanel.java
index eba52d5..2ac2b06 100644
--- a/src/jalview/appletgui/IdPanel.java
+++ b/src/jalview/appletgui/IdPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,9 @@
  */
 package jalview.appletgui;
 
+import static jalview.util.UrlConstants.EMBLEBI_STRING;
+import static jalview.util.UrlConstants.SRS_STRING;
+
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
@@ -82,24 +85,22 @@ public class IdPanel extends Panel implements MouseListener,
     }
     {
       // upgrade old SRS link
-      int srsPos = links
-              .indexOf("SRS|http://srs.ebi.ac.uk/srsbin/cgi-bin/wgetz?-newId+(([uniprot-all:$SEQUENCE_ID$]))+-view+SwissEntry");
+      int srsPos = links.indexOf(SRS_STRING);
       if (srsPos > -1)
       {
-        links.setElementAt(
-                "EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$",
-                srsPos);
+        links.setElementAt(EMBLEBI_STRING, srsPos);
       }
     }
     if (links.size() < 1)
     {
       links = new java.util.Vector();
-      links.addElement("EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$");
+      links.addElement(EMBLEBI_STRING);
     }
   }
 
   Tooltip tooltip;
 
+  @Override
   public void mouseMoved(MouseEvent e)
   {
     int seq = alignPanel.seqPanel.findSeq(e);
@@ -188,6 +189,7 @@ public class IdPanel extends Panel implements MouseListener,
     tooltiptext = null;
   }
 
+  @Override
   public void mouseDragged(MouseEvent e)
   {
     mouseDragging = true;
@@ -207,6 +209,7 @@ public class IdPanel extends Panel implements MouseListener,
     alignPanel.paintAlignment(false);
   }
 
+  @Override
   public void mouseClicked(MouseEvent e)
   {
     if (e.getClickCount() < 2)
@@ -243,7 +246,14 @@ public class IdPanel extends Panel implements MouseListener,
         url = null;
         continue;
       }
-      ;
+
+      if (urlLink.usesDBAccession())
+      {
+        // this URL requires an accession id, not the name of a sequence
+        url = null;
+        continue;
+      }
+
       if (!urlLink.isValid())
       {
         System.err.println(urlLink.getInvalidMessage());
@@ -270,6 +280,7 @@ public class IdPanel extends Panel implements MouseListener,
     }
   }
 
+  @Override
   public void mouseEntered(MouseEvent e)
   {
     if (scrollThread != null)
@@ -278,6 +289,7 @@ public class IdPanel extends Panel implements MouseListener,
     }
   }
 
+  @Override
   public void mouseExited(MouseEvent e)
   {
     if (av.getWrapAlignment())
@@ -297,6 +309,7 @@ public class IdPanel extends Panel implements MouseListener,
     }
   }
 
+  @Override
   public void mousePressed(MouseEvent e)
   {
     if (e.getClickCount() > 1)
@@ -345,8 +358,8 @@ public class IdPanel extends Panel implements MouseListener,
     }
 
     if ((av.getSelectionGroup() == null)
-            || ((!e.isControlDown() && !e.isShiftDown()) && av
-                    .getSelectionGroup() != null))
+            || ((!jalview.util.Platform.isControlDown(e) && !e
+                    .isShiftDown()) && av.getSelectionGroup() != null))
     {
       av.setSelectionGroup(new SequenceGroup());
       av.getSelectionGroup().setStartRes(0);
@@ -401,6 +414,7 @@ public class IdPanel extends Panel implements MouseListener,
 
   }
 
+  @Override
   public void mouseReleased(MouseEvent e)
   {
     if (scrollThread != null)
@@ -455,6 +469,7 @@ public class IdPanel extends Panel implements MouseListener,
       running = false;
     }
 
+    @Override
     public void run()
     {
       running = true;
diff --git a/src/jalview/appletgui/IdwidthAdjuster.java b/src/jalview/appletgui/IdwidthAdjuster.java
index fe52738..45b9cce 100644
--- a/src/jalview/appletgui/IdwidthAdjuster.java
+++ b/src/jalview/appletgui/IdwidthAdjuster.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/JVDialog.java b/src/jalview/appletgui/JVDialog.java
index 59a5867..d7090f1 100644
--- a/src/jalview/appletgui/JVDialog.java
+++ b/src/jalview/appletgui/JVDialog.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,6 +28,8 @@ import java.awt.Frame;
 import java.awt.Panel;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
 
 public class JVDialog extends Dialog implements ActionListener
 {
@@ -56,6 +58,20 @@ public class JVDialog extends Dialog implements ActionListener
             width, height);
   }
 
+  public JVDialog(Frame owner, Panel mainPanel, String title,
+          boolean modal, int width, int height)
+  {
+    super(owner, title, modal);
+    this.owner = owner;
+
+    height += owner.getInsets().top + getInsets().bottom;
+
+    setBounds(owner.getBounds().x + (owner.getSize().width - width) / 2,
+            owner.getBounds().y + (owner.getSize().height - height) / 2,
+            width, height);
+    setMainPanel(mainPanel);
+  }
+
   void setMainPanel(Panel panel)
   {
     add(panel, BorderLayout.NORTH);
@@ -66,9 +82,17 @@ public class JVDialog extends Dialog implements ActionListener
     buttonPanel.add(cancel);
     ok.addActionListener(this);
     cancel.addActionListener(this);
-
     add(buttonPanel, BorderLayout.SOUTH);
 
+    addWindowListener(new WindowAdapter()
+    {
+      public void windowClosing(WindowEvent ev)
+      {
+        setVisible(false);
+        dispose();
+      }
+    });
+
     pack();
 
   }
@@ -81,6 +105,7 @@ public class JVDialog extends Dialog implements ActionListener
     }
 
     setVisible(false);
+    dispose();
   }
 
 }
diff --git a/src/jalview/appletgui/OverviewPanel.java b/src/jalview/appletgui/OverviewPanel.java
index 3a6fe49..6d9d20e 100644
--- a/src/jalview/appletgui/OverviewPanel.java
+++ b/src/jalview/appletgui/OverviewPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -265,7 +265,8 @@ public class OverviewPanel extends Panel implements Runnable,
   {
     miniMe = null;
     int alwidth = av.getAlignment().getWidth();
-    int alheight = av.getAlignment().getHeight();
+    int alheight = av.getAlignment().getHeight()
+            + av.getAlignment().getHiddenSequences().getSize();
 
     if (av.isShowSequenceFeatures())
     {
@@ -304,6 +305,10 @@ public class OverviewPanel extends Panel implements Runnable,
     AlignmentI alignment = av.getAlignment();
     for (row = 0; row <= sequencesHeight; row++)
     {
+      if (resizeAgain)
+      {
+        break;
+      }
       if ((int) (row * sampleRow) == lastrow)
       {
         sameRow++;
@@ -385,6 +390,10 @@ public class OverviewPanel extends Panel implements Runnable,
     {
       for (col = 0; col < width; col++)
       {
+        if (resizeAgain)
+        {
+          break;
+        }
         lastcol = (int) (col * sampleCol);
         {
           mg.translate(col, sequencesHeight);
diff --git a/src/jalview/appletgui/PCAPanel.java b/src/jalview/appletgui/PCAPanel.java
index b0b8c9c..dd456d9 100644
--- a/src/jalview/appletgui/PCAPanel.java
+++ b/src/jalview/appletgui/PCAPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/PaintRefresher.java b/src/jalview/appletgui/PaintRefresher.java
index 7724880..47f14d3 100644
--- a/src/jalview/appletgui/PaintRefresher.java
+++ b/src/jalview/appletgui/PaintRefresher.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/PairwiseAlignPanel.java b/src/jalview/appletgui/PairwiseAlignPanel.java
index 1c95c12..c269a29 100644
--- a/src/jalview/appletgui/PairwiseAlignPanel.java
+++ b/src/jalview/appletgui/PairwiseAlignPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/RedundancyPanel.java b/src/jalview/appletgui/RedundancyPanel.java
index d0fcd58..6def1b0 100644
--- a/src/jalview/appletgui/RedundancyPanel.java
+++ b/src/jalview/appletgui/RedundancyPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/RotatableCanvas.java b/src/jalview/appletgui/RotatableCanvas.java
index f38d291..237e922 100644
--- a/src/jalview/appletgui/RotatableCanvas.java
+++ b/src/jalview/appletgui/RotatableCanvas.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/ScalePanel.java b/src/jalview/appletgui/ScalePanel.java
index 2445e2c..f70e5e3 100644
--- a/src/jalview/appletgui/ScalePanel.java
+++ b/src/jalview/appletgui/ScalePanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,6 +22,8 @@ package jalview.appletgui;
 
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SequenceGroup;
+import jalview.renderer.ScaleRenderer;
+import jalview.renderer.ScaleRenderer.ScaleMark;
 import jalview.util.MessageManager;
 
 import java.awt.Color;
@@ -36,6 +38,7 @@ import java.awt.event.InputEvent;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
+import java.util.List;
 
 public class ScalePanel extends Panel implements MouseMotionListener,
         MouseListener
@@ -70,6 +73,7 @@ public class ScalePanel extends Panel implements MouseMotionListener,
 
   }
 
+  @Override
   public void mousePressed(MouseEvent evt)
   {
     int x = (evt.getX() / av.getCharWidth()) + av.getStartRes();
@@ -88,111 +92,139 @@ public class ScalePanel extends Panel implements MouseMotionListener,
     max = res;
     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
     {
-      PopupMenu pop = new PopupMenu();
-      if (reveal != null)
+      rightMouseButtonPressed(evt, res);
+    }
+    else
+    {
+      leftMouseButtonPressed(evt, res);
+    }
+  }
+
+  /**
+   * Handles left mouse button pressed (selection / clear selections)
+   * 
+   * @param evt
+   * @param res
+   */
+  protected void leftMouseButtonPressed(MouseEvent evt, final int res)
+  {
+    if (!evt.isControlDown() && !evt.isShiftDown())
+    {
+      av.getColumnSelection().clear();
+    }
+
+    av.getColumnSelection().addElement(res);
+    SequenceGroup sg = new SequenceGroup();
+    for (int i = 0; i < av.getAlignment().getSequences().size(); i++)
+    {
+      sg.addSequence(av.getAlignment().getSequenceAt(i), false);
+    }
+
+    sg.setStartRes(res);
+    sg.setEndRes(res);
+    av.setSelectionGroup(sg);
+
+    if (evt.isShiftDown())
+    {
+      int min = Math.min(av.getColumnSelection().getMin(), res);
+      int max = Math.max(av.getColumnSelection().getMax(), res);
+      for (int i = min; i < max; i++)
       {
-        MenuItem item = new MenuItem(
-                MessageManager.getString("label.reveal"));
-        item.addActionListener(new ActionListener()
-        {
-          public void actionPerformed(ActionEvent e)
-          {
-            av.showColumn(reveal[0]);
-            reveal = null;
-            ap.paintAlignment(true);
-            if (ap.overviewPanel != null)
-            {
-              ap.overviewPanel.updateOverviewImage();
-            }
-          }
-        });
-        pop.add(item);
+        av.getColumnSelection().addElement(i);
+      }
+      sg.setStartRes(min);
+      sg.setEndRes(max);
+    }
+    ap.paintAlignment(true);
+    av.sendSelection();
+  }
 
-        if (av.getColumnSelection().hasManyHiddenColumns())
+  /**
+   * Handles right mouse button press. If pressed in a selected column, opens
+   * context menu for 'Hide Columns'. If pressed on a hidden columns marker,
+   * opens context menu for 'Reveal / Reveal All'. Else does nothing.
+   * 
+   * @param evt
+   * @param res
+   */
+  protected void rightMouseButtonPressed(MouseEvent evt, final int res)
+  {
+    PopupMenu pop = new PopupMenu();
+    if (reveal != null)
+    {
+      MenuItem item = new MenuItem(MessageManager.getString("label.reveal"));
+      item.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent e)
         {
-          item = new MenuItem(MessageManager.getString("action.reveal_all"));
-          item.addActionListener(new ActionListener()
+          av.showColumn(reveal[0]);
+          reveal = null;
+          ap.paintAlignment(true);
+          if (ap.overviewPanel != null)
           {
-            public void actionPerformed(ActionEvent e)
-            {
-              av.showAllHiddenColumns();
-              reveal = null;
-              ap.paintAlignment(true);
-              if (ap.overviewPanel != null)
-              {
-                ap.overviewPanel.updateOverviewImage();
-              }
-            }
-          });
-          pop.add(item);
+            ap.overviewPanel.updateOverviewImage();
+          }
+          av.sendSelection();
         }
-        this.add(pop);
-        pop.show(this, evt.getX(), evt.getY());
-      }
-      else if (av.getColumnSelection().contains(res))
+      });
+      pop.add(item);
+
+      if (av.getColumnSelection().hasManyHiddenColumns())
       {
-        MenuItem item = new MenuItem(
-                MessageManager.getString("label.hide_columns"));
+        item = new MenuItem(MessageManager.getString("action.reveal_all"));
         item.addActionListener(new ActionListener()
         {
+          @Override
           public void actionPerformed(ActionEvent e)
           {
-            av.hideColumns(res, res);
-            if (av.getSelectionGroup() != null
-                    && av.getSelectionGroup().getSize() == av
-                            .getAlignment().getHeight())
-            {
-              av.setSelectionGroup(null);
-            }
-
+            av.showAllHiddenColumns();
+            reveal = null;
             ap.paintAlignment(true);
             if (ap.overviewPanel != null)
             {
               ap.overviewPanel.updateOverviewImage();
             }
+            av.sendSelection();
           }
         });
         pop.add(item);
-        this.add(pop);
-        pop.show(this, evt.getX(), evt.getY());
       }
+      this.add(pop);
+      pop.show(this, evt.getX(), evt.getY());
     }
-    else
-    // LEFT MOUSE TO SELECT
+    else if (av.getColumnSelection().contains(res))
     {
-      if (!evt.isControlDown() && !evt.isShiftDown())
+      MenuItem item = new MenuItem(
+              MessageManager.getString("label.hide_columns"));
+      item.addActionListener(new ActionListener()
       {
-        av.getColumnSelection().clear();
-      }
-
-      av.getColumnSelection().addElement(res);
-      SequenceGroup sg = new SequenceGroup();
-      for (int i = 0; i < av.getAlignment().getSequences().size(); i++)
-      {
-        sg.addSequence(av.getAlignment().getSequenceAt(i), false);
-      }
-
-      sg.setStartRes(res);
-      sg.setEndRes(res);
-      av.setSelectionGroup(sg);
-
-      if (evt.isShiftDown())
-      {
-        int min = Math.min(av.getColumnSelection().getMin(), res);
-        int max = Math.max(av.getColumnSelection().getMax(), res);
-        for (int i = min; i < max; i++)
+        @Override
+        public void actionPerformed(ActionEvent e)
         {
-          av.getColumnSelection().addElement(i);
+          av.hideColumns(res, res);
+          if (av.getSelectionGroup() != null
+                  && av.getSelectionGroup().getSize() == av.getAlignment()
+                          .getHeight())
+          {
+            av.setSelectionGroup(null);
+          }
+
+          ap.paintAlignment(true);
+          if (ap.overviewPanel != null)
+          {
+            ap.overviewPanel.updateOverviewImage();
+          }
+          av.sendSelection();
         }
-        sg.setStartRes(min);
-        sg.setEndRes(max);
-      }
+      });
+      pop.add(item);
+      this.add(pop);
+      pop.show(this, evt.getX(), evt.getY());
     }
-
-    ap.paintAlignment(true);
-    av.sendSelection();
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     mouseDragging = false;
@@ -232,6 +264,7 @@ public class ScalePanel extends Panel implements MouseMotionListener,
     av.sendSelection();
   }
 
+  @Override
   public void mouseDragged(MouseEvent evt)
   {
     mouseDragging = true;
@@ -301,6 +334,7 @@ public class ScalePanel extends Panel implements MouseMotionListener,
     }
   }
 
+  @Override
   public void mouseEntered(MouseEvent evt)
   {
     if (mouseDragging)
@@ -309,6 +343,7 @@ public class ScalePanel extends Panel implements MouseMotionListener,
     }
   }
 
+  @Override
   public void mouseExited(MouseEvent evt)
   {
     if (mouseDragging)
@@ -317,11 +352,13 @@ public class ScalePanel extends Panel implements MouseMotionListener,
     }
   }
 
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
 
   }
 
+  @Override
   public void mouseMoved(MouseEvent evt)
   {
     if (!av.hasHiddenColumns())
@@ -346,11 +383,13 @@ public class ScalePanel extends Panel implements MouseMotionListener,
     repaint();
   }
 
+  @Override
   public void update(Graphics g)
   {
     paint(g);
   }
 
+  @Override
   public void paint(Graphics g)
   {
     drawScale(g, av.getStartRes(), av.getEndRes(), getSize().width,
@@ -369,62 +408,69 @@ public class ScalePanel extends Panel implements MouseMotionListener,
 
     // Fill the selected columns
     ColumnSelection cs = av.getColumnSelection();
-    gg.setColor(new Color(220, 0, 0));
-    int avcharWidth = av.getCharWidth(), avcharHeight = av.getCharHeight();
-    for (int i = 0; i < cs.size(); i++)
+    int avCharWidth = av.getCharWidth();
+    int avcharHeight = av.getCharHeight();
+    if (cs != null)
     {
-      int sel = cs.columnAt(i);
-      if (av.hasHiddenColumns())
+      gg.setColor(new Color(220, 0, 0));
+      boolean hasHiddenColumns = cs.hasHiddenColumns();
+      for (int sel : cs.getSelected())
       {
-        sel = av.getColumnSelection().findColumnPosition(sel);
-      }
+        // TODO: JAL-2001 - provide a fast method to list visible selected in a
+        // given range
+        if (hasHiddenColumns)
+        {
+          if (cs.isVisible(sel))
+          {
+            sel = cs.findColumnPosition(sel);
+          }
+          else
+          {
+            continue;
+          }
+        }
 
-      if ((sel >= startx) && (sel <= endx))
-      {
-        gg.fillRect((sel - startx) * avcharWidth, 0, avcharWidth,
-                getSize().height);
+        if ((sel >= startx) && (sel <= endx))
+        {
+          gg.fillRect((sel - startx) * avCharWidth, 0, avCharWidth,
+                  getSize().height);
+        }
       }
     }
 
     // Draw the scale numbers
     gg.setColor(Color.black);
 
-    int scalestartx = (startx / 10) * 10;
-
-    FontMetrics fm = gg.getFontMetrics(av.getFont());
-    int y = avcharHeight - fm.getDescent();
-
-    if ((scalestartx % 10) == 0)
-    {
-      scalestartx += 5;
-    }
-
-    String string;
     int maxX = 0;
+    List<ScaleMark> marks = new ScaleRenderer().calculateMarks(av, startx,
+            endx);
 
-    for (int i = scalestartx; i < endx; i += 5)
+    FontMetrics fm = gg.getFontMetrics(av.getFont());
+    int y = avcharHeight;
+    int yOf = fm.getDescent();
+    y -= yOf;
+    for (ScaleMark mark : marks)
     {
-      if ((i % 10) == 0)
+      boolean major = mark.major;
+      int mpos = mark.column; // (i - startx - 1)
+      String mstring = mark.text;
+      if (mstring != null)
       {
-        string = String.valueOf(av.getColumnSelection()
-                .adjustForHiddenColumns(i));
-        if ((i - startx - 1) * avcharWidth > maxX)
+        if (mpos * avCharWidth > maxX)
         {
-          gg.drawString(string, (i - startx - 1) * avcharWidth, y);
-          maxX = (i - startx + 1) * avcharWidth + fm.stringWidth(string);
+          gg.drawString(mstring, mpos * avCharWidth, y);
+          maxX = (mpos + 2) * avCharWidth + fm.stringWidth(mstring);
         }
-
-        gg.drawLine(((i - startx - 1) * avcharWidth) + (avcharWidth / 2),
-                y + 2,
-                ((i - startx - 1) * avcharWidth) + (avcharWidth / 2), y
-                        + (fm.getDescent() * 2));
-
+      }
+      if (major)
+      {
+        gg.drawLine((mpos * avCharWidth) + (avCharWidth / 2), y + 2,
+                (mpos * avCharWidth) + (avCharWidth / 2), y + (yOf * 2));
       }
       else
       {
-        gg.drawLine(((i - startx - 1) * avcharWidth) + (avcharWidth / 2), y
-                + fm.getDescent(), ((i - startx - 1) * avcharWidth)
-                + (avcharWidth / 2), y + (fm.getDescent() * 2));
+        gg.drawLine((mpos * avCharWidth) + (avCharWidth / 2), y + yOf,
+                (mpos * avCharWidth) + (avCharWidth / 2), y + (yOf * 2));
       }
     }
 
@@ -434,33 +480,24 @@ public class ScalePanel extends Panel implements MouseMotionListener,
       int res;
       if (av.getShowHiddenMarkers())
       {
-        for (int i = 0; i < av.getColumnSelection().getHiddenColumns()
-                .size(); i++)
+        int widthx = 1 + endx - startx;
+        for (int i = 0; i < cs.getHiddenColumns().size(); i++)
         {
 
-          res = av.getColumnSelection().findHiddenRegionPosition(i)
-                  - startx;
+          res = cs.findHiddenRegionPosition(i) - startx;
 
-          if (res < 0 || res > endx - scalestartx)
+          if (res < 0 || res > widthx)
           {
             continue;
           }
 
-          gg.fillPolygon(new int[] { res * avcharWidth - avcharHeight / 4,
-              res * avcharWidth + avcharHeight / 4, res * avcharWidth },
-                  new int[] { y - avcharHeight / 2, y - avcharHeight / 2,
-                      y + 8 }, 3);
-
+          gg.fillPolygon(new int[] {
+              -1 + res * avCharWidth - avcharHeight / 4,
+              -1 + res * avCharWidth + avcharHeight / 4,
+              -1 + res * avCharWidth }, new int[] { y, y, y + 2 * yOf }, 3);
         }
       }
-
-      if (reveal != null && reveal[0] > startx && reveal[0] < endx)
-      {
-        gg.drawString(MessageManager.getString("label.reveal_columns"),
-                reveal[0] * avcharWidth, 0);
-      }
     }
-
   }
 
 }
diff --git a/src/jalview/appletgui/SeqCanvas.java b/src/jalview/appletgui/SeqCanvas.java
index 7689535..7e19597 100644
--- a/src/jalview/appletgui/SeqCanvas.java
+++ b/src/jalview/appletgui/SeqCanvas.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,9 +21,11 @@
 package jalview.appletgui;
 
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.renderer.ScaleRenderer;
+import jalview.renderer.ScaleRenderer.ScaleMark;
 import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Color;
@@ -48,8 +50,6 @@ public class SeqCanvas extends Panel
 
   AlignViewport av;
 
-  SearchResults searchResults = null;
-
   boolean fastPaint = false;
 
   int cursorX = 0;
@@ -90,26 +90,29 @@ public class SeqCanvas extends Panel
 
   private void drawNorthScale(Graphics g, int startx, int endx, int ypos)
   {
-    int scalestartx = startx - startx % 10 + 10;
-
+    updateViewport();
     g.setColor(Color.black);
-
-    // NORTH SCALE
-    for (int i = scalestartx; i < endx; i += 10)
+    for (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx,
+            endx))
     {
-      int value = i;
-      if (av.hasHiddenColumns())
+      int mpos = mark.column; // (i - startx - 1)
+      if (mpos < 0)
       {
-        value = av.getColumnSelection().adjustForHiddenColumns(value);
+        continue;
       }
+      String mstring = mark.text;
 
-      g.drawString(String.valueOf(value), (i - startx - 1) * avcharWidth,
-              ypos - (avcharHeight / 2));
-
-      g.drawLine(((i - startx - 1) * avcharWidth) + (avcharWidth / 2),
-              (ypos + 2) - (avcharHeight / 2),
-              ((i - startx - 1) * avcharWidth) + (avcharWidth / 2),
-              ypos - 2);
+      if (mark.major)
+      {
+        if (mstring != null)
+        {
+          g.drawString(mstring, mpos * avcharWidth, ypos
+                  - (avcharHeight / 2));
+        }
+        g.drawLine((mpos * avcharWidth) + (avcharWidth / 2), (ypos + 2)
+                - (avcharHeight / 2), (mpos * avcharWidth)
+                + (avcharWidth / 2), ypos - 2);
+      }
     }
   }
 
@@ -276,6 +279,7 @@ public class SeqCanvas extends Panel
    * at 0). NOTE 1: The av limits are set in setFont in this class and in the
    * adjustment listener in SeqPanel when the scrollbars move.
    */
+  @Override
   public void update(Graphics g)
   {
     paint(g);
@@ -573,10 +577,17 @@ public class SeqCanvas extends Panel
           g1.translate(-screenY * avcharWidth, 0);
           screenY += blockEnd - blockStart + 1;
           blockStart = hideEnd + 1;
+
+          if (screenY > (endRes - startRes))
+          {
+            // already rendered last block
+            return;
+          }
         }
       }
       if (screenY <= (endRes - startRes))
       {
+        // remaining visible region to render
         blockEnd = blockStart + (endRes - startRes) - screenY;
         g1.translate(screenY * avcharWidth, 0);
         draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
@@ -619,9 +630,10 @@ public class SeqCanvas extends Panel
 
       // / Highlight search Results once all sequences have been drawn
       // ////////////////////////////////////////////////////////
-      if (searchResults != null)
+      if (av.hasSearchResults())
       {
-        int[] visibleResults = searchResults.getResults(nextSeq, startRes,
+        int[] visibleResults = av.getSearchResults().getResults(nextSeq,
+                startRes,
                 endRes);
         if (visibleResults != null)
         {
@@ -830,10 +842,9 @@ public class SeqCanvas extends Panel
     }
   }
 
-  public void highlightSearchResults(SearchResults results)
+  public void highlightSearchResults(SearchResultsI results)
   {
-    searchResults = results;
-
+    av.setSearchResults(results);
     repaint();
   }
 
diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java
index db6add7..b302d2f 100644
--- a/src/jalview/appletgui/SeqPanel.java
+++ b/src/jalview/appletgui/SeqPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -25,8 +25,9 @@ import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
-import jalview.datamodel.SearchResults.Match;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
@@ -458,7 +459,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
    * @param results
    * @return true if results were matched, false if not
    */
-  private boolean setStatusMessage(SearchResults results)
+  private boolean setStatusMessage(SearchResultsI results)
   {
     AlignmentI al = this.av.getAlignment();
     int sequenceIndex = al.findIndex(results);
@@ -467,7 +468,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       return false;
     }
     SequenceI ds = al.getSequenceAt(sequenceIndex).getDatasetSequence();
-    for (Match m : results.getResults())
+    for (SearchResultMatchI m : results.getResults())
     {
       SequenceI seq = m.getSequence();
       if (seq.getDatasetSequence() != null)
@@ -481,7 +482,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
          * Convert position in sequence (base 1) to sequence character array
          * index (base 0)
          */
-        int start = m.getStart() - 1;
+        int start = m.getStart() - m.getSequence().getStart();
         setStatusMessage(seq, start, sequenceIndex);
         return true;
       }
@@ -489,6 +490,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     return false;
   }
 
+  @Override
   public void mousePressed(MouseEvent evt)
   {
     lastMousePress = evt.getPoint();
@@ -539,6 +541,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     return;
   }
 
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
     SequenceI sequence = av.getAlignment().getSequenceAt(findSeq(evt));
@@ -557,7 +560,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
       if (features != null && features.length > 0)
       {
-        SearchResults highlight = new SearchResults();
+        SearchResultsI highlight = new SearchResults();
         highlight.addResult(sequence, features[0].getBegin(),
                 features[0].getEnd());
         seqCanvas.highlightSearchResults(highlight);
@@ -572,6 +575,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     }
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     mouseDragging = false;
@@ -715,6 +719,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
   String lastMessage;
 
+  @Override
   public void mouseOverSequence(SequenceI sequence, int index, int pos)
   {
     String tmp = sequence.hashCode() + index + "";
@@ -726,7 +731,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     lastMessage = tmp;
   }
 
-  public void highlightSequence(SearchResults results)
+  @Override
+  public void highlightSequence(SearchResultsI results)
   {
     if (av.isFollowHighlight())
     {
@@ -746,12 +752,14 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     return this.ap == null ? null : this.ap.av;
   }
 
+  @Override
   public void updateColours(SequenceI seq, int index)
   {
     System.out.println("update the seqPanel colours");
     // repaint();
   }
 
+  @Override
   public void mouseMoved(MouseEvent evt)
   {
     int res = findRes(evt);
@@ -912,6 +920,15 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
   Tooltip tooltip;
 
+  /**
+   * set when the current UI interaction has resulted in a change that requires
+   * overview shading to be recalculated. this could be changed to something
+   * more expressive that indicates what actually has changed, so selective
+   * redraws can be applied
+   */
+  private boolean needOverviewUpdate; // TODO: refactor to avcontroller
+
+  @Override
   public void mouseDragged(MouseEvent evt)
   {
     if (mouseWheelPressed)
@@ -1513,9 +1530,11 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     {
       return;
     }
-
-    stretchGroup.recalcConservation(); // always do this - annotation has own
-                                       // state
+    // always do this - annotation has own state
+    // but defer colourscheme update until hidden sequences are passed in
+    boolean vischange = stretchGroup.recalcConservation(true);
+    // here we rely on stretchGroup == av.getSelection()
+    needOverviewUpdate |= vischange && av.isSelectionDefinedGroup();
     if (stretchGroup.cs != null)
     {
       stretchGroup.cs.alignmentChanged(stretchGroup,
@@ -1532,11 +1551,12 @@ public class SeqPanel extends Panel implements MouseMotionListener,
                 stretchGroup.getName());
       }
     }
+    PaintRefresher.Refresh(ap, av.getSequenceSetId());
+    ap.paintAlignment(needOverviewUpdate);
+    needOverviewUpdate = false;
     changeEndRes = false;
     changeStartRes = false;
     stretchGroup = null;
-    PaintRefresher.Refresh(ap, av.getSequenceSetId());
-    ap.paintAlignment(true);
     av.sendSelection();
   }
 
@@ -1588,6 +1608,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       if (res > (stretchGroup.getStartRes() - 1))
       {
         stretchGroup.setEndRes(res);
+        needOverviewUpdate |= av.isSelectionDefinedGroup();
       }
     }
     else if (changeStartRes)
@@ -1595,6 +1616,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       if (res < (stretchGroup.getEndRes() + 1))
       {
         stretchGroup.setStartRes(res);
+        needOverviewUpdate |= av.isSelectionDefinedGroup();
       }
     }
 
@@ -1628,6 +1650,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       if (stretchGroup.getSequences(null).contains(nextSeq))
       {
         stretchGroup.deleteSequence(seq, false);
+        needOverviewUpdate |= av.isSelectionDefinedGroup();
       }
       else
       {
@@ -1637,6 +1660,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
 
         stretchGroup.addSequence(nextSeq, false);
+        needOverviewUpdate |= av.isSelectionDefinedGroup();
       }
     }
 
@@ -1659,6 +1683,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     seqCanvas.repaint();
   }
 
+  @Override
   public void mouseEntered(MouseEvent e)
   {
     if (oldSeq < 0)
@@ -1673,6 +1698,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     }
   }
 
+  @Override
   public void mouseExited(MouseEvent e)
   {
     if (av.getWrapAlignment())
@@ -1732,6 +1758,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       running = false;
     }
 
+    @Override
     public void run()
     {
       running = true;
@@ -1776,6 +1803,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   /**
    * modify current selection according to a received message.
    */
+  @Override
   public void selection(SequenceGroup seqsel, ColumnSelection colsel,
           SelectionSource source)
   {
@@ -1801,9 +1829,12 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
     // do we want to thread this ? (contention with seqsel and colsel locks, I
     // suspect)
-    // rules are: colsel is copied if there is a real intersection between
-    // sequence selection
-    boolean repaint = false, copycolsel = true;
+    /*
+     * only copy colsel if there is a real intersection between
+     * sequence selection and this panel's alignment
+     */
+    boolean repaint = false;
+    boolean copycolsel = false;
     if (av.getSelectionGroup() == null || !av.isSelectionGroupChanged(true))
     {
       SequenceGroup sgroup = null;
@@ -1820,11 +1851,9 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
         sgroup = seqsel.intersect(av.getAlignment(),
                 (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
-        if ((sgroup == null || sgroup.getSize() == 0)
-                && (colsel == null || colsel.size() == 0))
+        if ((sgroup != null && sgroup.getSize() > 0))
         {
-          // don't copy columns if the region didn't intersect.
-          copycolsel = false;
+          copycolsel = true;
         }
       }
       if (sgroup != null && sgroup.getSize() > 0)
@@ -1843,7 +1872,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     {
       // the current selection is unset or from a previous message
       // so import the new colsel.
-      if (colsel == null || colsel.size() == 0)
+      if (colsel == null || colsel.isEmpty())
       {
         if (av.getColumnSelection() != null)
         {
@@ -1952,7 +1981,6 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
             av);
     av.setColumnSelection(cs);
-    av.isColSelChanged(true);
 
     ap.scalePanelHolder.repaint();
     ap.repaint();
diff --git a/src/jalview/appletgui/SequenceRenderer.java b/src/jalview/appletgui/SequenceRenderer.java
index b843526..bcc9461 100644
--- a/src/jalview/appletgui/SequenceRenderer.java
+++ b/src/jalview/appletgui/SequenceRenderer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -32,6 +32,8 @@ import java.awt.Graphics;
 
 public class SequenceRenderer implements jalview.api.SequenceRenderer
 {
+  final static int CHAR_TO_UPPER = 'A' - 'a';
+
   AlignViewport av;
 
   FontMetrics fm;
@@ -67,6 +69,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     this.renderGaps = renderGaps;
   }
 
+  @Override
   public Color getResidueBoxColour(SequenceI seq, int i)
   {
     allGroups = av.getAlignment().findAllGroups(seq);
@@ -256,7 +259,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
         }
         if (currentSequenceGroup.getShowNonconserved())
         {
-          s = getDisplayChar(srep, i, s, '.');
+          s = getDisplayChar(srep, i, s, '.', currentSequenceGroup);
         }
       }
       else
@@ -280,7 +283,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
         }
         if (av.getShowUnconserved())
         {
-          s = getDisplayChar(srep, i, s, '.');
+          s = getDisplayChar(srep, i, s, '.', null);
 
         }
       }
@@ -312,20 +315,43 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
   }
 
-  private char getDisplayChar(final boolean usesrep, int position, char s,
-          char c)
+  /**
+   * Returns 'conservedChar' to represent the given position if the sequence
+   * character at that position is equal to the consensus (ignoring case), else
+   * returns the sequence character
+   * 
+   * @param usesrep
+   * @param position
+   * @param sequenceChar
+   * @param conservedChar
+   * @return
+   */
+  private char getDisplayChar(final boolean usesrep, int position,
+          char sequenceChar, char conservedChar, SequenceGroup currentGroup)
   {
     // TODO - use currentSequenceGroup rather than alignment
     // currentSequenceGroup.getConsensus()
-    char conschar = (usesrep) ? av.getAlignment().getSeqrep()
-            .getCharAt(position)
-            : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
-                    .charAt(0);
-    if (!jalview.util.Comparison.isGap(conschar) && s == conschar)
+    char conschar = (usesrep) ? (currentGroup == null
+            || position < currentGroup.getStartRes()
+            || position > currentGroup.getEndRes() ? av.getAlignment()
+            .getSeqrep().getCharAt(position)
+            : (currentGroup.getSeqrep() != null ? currentGroup.getSeqrep()
+                    .getCharAt(position) : av.getAlignment().getSeqrep()
+                    .getCharAt(position)))
+            : (currentGroup != null && currentGroup.getConsensus() != null
+                    && position >= currentGroup.getStartRes()
+                    && position <= currentGroup.getEndRes() && currentGroup
+                    .getConsensus().annotations.length > position) ? currentGroup
+                    .getConsensus().annotations[position].displayCharacter
+                    .charAt(0)
+                    : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
+                            .charAt(0);
+    if (!jalview.util.Comparison.isGap(conschar)
+            && (sequenceChar == conschar || sequenceChar + CHAR_TO_UPPER == conschar))
     {
-      s = c;
+      sequenceChar = conservedChar;
     }
-    return s;
+    return sequenceChar;
   }
 
   boolean inCurrentSequenceGroup(int res)
diff --git a/src/jalview/appletgui/SliderPanel.java b/src/jalview/appletgui/SliderPanel.java
index d49cd6c..2c4afb3 100644
--- a/src/jalview/appletgui/SliderPanel.java
+++ b/src/jalview/appletgui/SliderPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -131,8 +131,9 @@ public class SliderPanel extends Panel implements ActionListener,
       pid = (SliderPanel) PIDSlider.getComponent(0);
       pid.cs = cs;
     }
-    PIDSlider.setTitle(MessageManager
-            .formatMessage("label.percentage_identity_thereshold",
+    PIDSlider
+            .setTitle(MessageManager.formatMessage(
+                    "label.percentage_identity_threshold",
                     new String[] { source }));
 
     if (ap.av.getAlignment().getGroups() != null)
diff --git a/src/jalview/appletgui/SplitFrame.java b/src/jalview/appletgui/SplitFrame.java
index 8255850..d82ebde 100644
--- a/src/jalview/appletgui/SplitFrame.java
+++ b/src/jalview/appletgui/SplitFrame.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,6 @@
  */
 package jalview.appletgui;
 
-import jalview.analysis.AlignmentUtils;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.ViewStyleI;
 import jalview.bin.JalviewLite;
@@ -44,12 +43,14 @@ public class SplitFrame extends EmbmenuFrame
   private Panel outermost;
 
   /**
-   * Constructor
+   * Constructs the split frame placing cdna in the top half. No 'alignment' is
+   * performed here, this should be done by the calling client if wanted.
    */
   public SplitFrame(AlignFrame af1, AlignFrame af2)
   {
-    topFrame = af1;
-    bottomFrame = af2;
+    boolean af1IsNucleotide = af1.viewport.getAlignment().isNucleotide();
+    topFrame = af1IsNucleotide ? af1 : af2;
+    bottomFrame = topFrame == af1 ? af2 : af1;
     init();
   }
 
@@ -75,20 +76,15 @@ public class SplitFrame extends EmbmenuFrame
     AlignmentViewport protein = !topAlignment.isNucleotide() ? topViewport
             : (!bottomAlignment.isNucleotide() ? bottomViewport : null);
 
-    boolean mapped = AlignmentUtils.mapProteinToCdna(
-            protein.getAlignment(), cdna.getAlignment());
-    if (mapped)
-    {
-      final StructureSelectionManager ssm = StructureSelectionManager
-              .getStructureSelectionManager(topViewport.applet);
-      ssm.registerMappings(protein.getAlignment().getCodonFrames());
-      topViewport.setCodingComplement(bottomViewport);
-      ssm.addCommandListener(cdna);
-      ssm.addCommandListener(protein);
-    }
+    final StructureSelectionManager ssm = StructureSelectionManager
+            .getStructureSelectionManager(topViewport.applet);
+    ssm.registerMappings(protein.getAlignment().getCodonFrames());
+    topViewport.setCodingComplement(bottomViewport);
+    ssm.addCommandListener(cdna);
+    ssm.addCommandListener(protein);
 
     /*
-     * Now mappings exist, can compute cDNA consensus on protein alignment
+     * Compute cDNA consensus on protein alignment
      */
     protein.initComplementConsensus();
     AlignmentViewPanel ap = topAlignment.isNucleotide() ? bottomFrame.alignPanel
diff --git a/src/jalview/appletgui/TitledPanel.java b/src/jalview/appletgui/TitledPanel.java
index 64a8cbc..c899684 100644
--- a/src/jalview/appletgui/TitledPanel.java
+++ b/src/jalview/appletgui/TitledPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/Tooltip.java b/src/jalview/appletgui/Tooltip.java
index e16c594..5d6e477 100644
--- a/src/jalview/appletgui/Tooltip.java
+++ b/src/jalview/appletgui/Tooltip.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/TreeCanvas.java b/src/jalview/appletgui/TreeCanvas.java
index 8d0cc42..ff834a4 100644
--- a/src/jalview/appletgui/TreeCanvas.java
+++ b/src/jalview/appletgui/TreeCanvas.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,7 +29,6 @@ import jalview.datamodel.SequenceI;
 import jalview.datamodel.SequenceNode;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
-import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
 import jalview.util.Format;
 import jalview.util.MappingUtils;
@@ -122,13 +121,13 @@ public class TreeCanvas extends Panel implements MouseListener,
     tree.findHeight(tree.getTopNode());
 
     // Now have to calculate longest name based on the leaves
-    Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
+    Vector<SequenceNode> leaves = tree.findLeaves(tree.getTopNode());
     boolean has_placeholders = false;
     longestName = "";
 
     for (int i = 0; i < leaves.size(); i++)
     {
-      SequenceNode lf = (SequenceNode) leaves.elementAt(i);
+      SequenceNode lf = leaves.elementAt(i);
 
       if (lf.isPlaceholder())
       {
@@ -525,13 +524,11 @@ public class TreeCanvas extends Panel implements MouseListener,
       }
       else
       {
-        Vector leaves = new Vector();
-        tree.findLeaves(highlightNode, leaves);
+        Vector<SequenceNode> leaves = tree.findLeaves(highlightNode);
 
         for (int i = 0; i < leaves.size(); i++)
         {
-          SequenceI seq = (SequenceI) ((SequenceNode) leaves.elementAt(i))
-                  .element();
+          SequenceI seq = (SequenceI) leaves.elementAt(i).element();
           treeSelectionChanged(seq);
         }
       }
@@ -628,16 +625,15 @@ public class TreeCanvas extends Panel implements MouseListener,
 
       Color col = new Color((int) (Math.random() * 255),
               (int) (Math.random() * 255), (int) (Math.random() * 255));
-      setColor((SequenceNode) tree.getGroups().elementAt(i), col.brighter());
+      setColor(tree.getGroups().elementAt(i), col.brighter());
 
-      Vector l = tree.findLeaves(
-              (SequenceNode) tree.getGroups().elementAt(i), new Vector());
+      Vector<SequenceNode> l = tree.findLeaves(tree.getGroups()
+              .elementAt(i));
 
-      Vector sequences = new Vector();
+      Vector<SequenceI> sequences = new Vector<SequenceI>();
       for (int j = 0; j < l.size(); j++)
       {
-        SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j))
-                .element();
+        SequenceI s1 = (SequenceI) l.elementAt(j).element();
         if (!sequences.contains(s1))
         {
           sequences.addElement(s1);
@@ -680,8 +676,7 @@ public class TreeCanvas extends Panel implements MouseListener,
       if (av.getGlobalColourScheme() != null
               && av.getGlobalColourScheme().conservationApplied())
       {
-        Conservation c = new Conservation("Group",
-                ResidueProperties.propHash, 3, sg.getSequences(null),
+        Conservation c = new Conservation("Group", sg.getSequences(null),
                 sg.getStartRes(), sg.getEndRes());
 
         c.calculate();
diff --git a/src/jalview/appletgui/TreePanel.java b/src/jalview/appletgui/TreePanel.java
index 64716b0..92b44c3 100644
--- a/src/jalview/appletgui/TreePanel.java
+++ b/src/jalview/appletgui/TreePanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/appletgui/UserDefinedColours.java b/src/jalview/appletgui/UserDefinedColours.java
index de3569a..1f75e1d 100644
--- a/src/jalview/appletgui/UserDefinedColours.java
+++ b/src/jalview/appletgui/UserDefinedColours.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,9 +20,10 @@
  */
 package jalview.appletgui;
 
+import jalview.api.FeatureColourI;
 import jalview.datamodel.SequenceGroup;
 import jalview.schemes.ColourSchemeI;
-import jalview.schemes.GraduatedColor;
+import jalview.schemes.FeatureColour;
 import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
 import jalview.util.MessageManager;
@@ -59,7 +60,7 @@ public class UserDefinedColours extends Panel implements ActionListener,
 
   Button selectedButton;
 
-  Vector oldColours = new Vector();
+  Vector<Color> oldColours = new Vector<Color>();
 
   ColourSchemeI oldColourScheme;
 
@@ -75,7 +76,7 @@ public class UserDefinedColours extends Panel implements ActionListener,
 
   String originalLabel;
 
-  Object originalColour;
+  FeatureColourI originalColour;
 
   int R = 0, G = 0, B = 0;
 
@@ -117,7 +118,7 @@ public class UserDefinedColours extends Panel implements ActionListener,
   public UserDefinedColours(FeatureRenderer fr, Frame alignframe)
   {
     caller = fr;
-    originalColour = fr.colourPanel.getBackground();
+    originalColour = new FeatureColour(fr.colourPanel.getBackground());
     originalLabel = "Feature Colour";
     setForDialog("Select Feature Colour", alignframe);
     setTargetColour(fr.colourPanel.getBackground());
@@ -134,21 +135,21 @@ public class UserDefinedColours extends Panel implements ActionListener,
    * 
    * @param caller
    *          - handles events
-   * @param col1
+   * @param col
    *          - original colour
    * @param alignframe
    *          - the parent Frame for the dialog
    * @param title
    *          - window title
    */
-  public UserDefinedColours(Component caller, Color col1, Frame alignframe,
+  public UserDefinedColours(Component caller, Color col, Frame alignframe,
           String title)
   {
     this.caller = caller;
-    originalColour = col1;
+    originalColour = new FeatureColour(col);
     originalLabel = title;
     setForDialog(title, alignframe);
-    setTargetColour(col1);
+    setTargetColour(col);
     dialog.setVisible(true);
   }
 
@@ -161,7 +162,7 @@ public class UserDefinedColours extends Panel implements ActionListener,
    */
   public UserDefinedColours(Object caller, String label, Color colour)
   {
-    this(caller, label, colour, colour);
+    this(caller, label, new FeatureColour(colour), colour);
   }
 
   /**
@@ -172,13 +173,13 @@ public class UserDefinedColours extends Panel implements ActionListener,
    * @param graduatedColor
    */
   public UserDefinedColours(FeatureSettings me, String type,
-          GraduatedColor graduatedColor)
+          FeatureColourI graduatedColor)
   {
-    this(me, type, graduatedColor, graduatedColor.getMaxColor());
+    this(me, type, graduatedColor, graduatedColor.getMaxColour());
   }
 
-  private UserDefinedColours(Object caller, String label, Object ocolour,
-          Color colour)
+  private UserDefinedColours(Object caller, String label,
+          FeatureColourI ocolour, Color colour)
   {
     this.caller = caller;
     originalColour = ocolour;
@@ -228,6 +229,7 @@ public class UserDefinedColours extends Panel implements ActionListener,
 
   }
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     final Object source = evt.getSource();
@@ -257,6 +259,7 @@ public class UserDefinedColours extends Panel implements ActionListener,
     }
   }
 
+  @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
     if (evt.getSource() == rScroller)
@@ -420,6 +423,7 @@ public class UserDefinedColours extends Panel implements ActionListener,
     button.setFont(new java.awt.Font("Verdana", 1, 10));
     button.addMouseListener(new java.awt.event.MouseAdapter()
     {
+      @Override
       public void mousePressed(MouseEvent e)
       {
         colourButtonPressed(e);
@@ -451,7 +455,8 @@ public class UserDefinedColours extends Panel implements ActionListener,
     {
       if (caller instanceof FeatureSettings)
       {
-        ((FeatureSettings) caller).setUserColour(originalLabel, getColor());
+        ((FeatureSettings) caller).setUserColour(originalLabel,
+                new FeatureColour(getColor()));
       }
       else if (caller instanceof AnnotationColourChooser)
       {
@@ -468,7 +473,8 @@ public class UserDefinedColours extends Panel implements ActionListener,
       }
       else if (caller instanceof FeatureRenderer)
       {
-        ((FeatureRenderer) caller).colourPanel.updateColor(getColor());
+        ((FeatureRenderer) caller).colourPanel
+                .updateColor(new FeatureColour(getColor()));
       }
       else if (caller instanceof FeatureColourChooser)
       {
@@ -537,12 +543,12 @@ public class UserDefinedColours extends Panel implements ActionListener,
         if (originalLabel.equals("Min Colour"))
         {
           ((AnnotationColourChooser) caller)
-                  .minColour_actionPerformed((Color) originalColour);
+                  .minColour_actionPerformed(originalColour.getColour());
         }
         else
         {
           ((AnnotationColourChooser) caller)
-                  .maxColour_actionPerformed((Color) originalColour);
+                  .maxColour_actionPerformed(originalColour.getColour());
         }
       }
       else if (caller instanceof FeatureRenderer)
@@ -556,12 +562,12 @@ public class UserDefinedColours extends Panel implements ActionListener,
         if (originalLabel.indexOf("inimum") > -1)
         {
           ((FeatureColourChooser) caller)
-                  .minColour_actionPerformed((Color) originalColour);
+                  .minColour_actionPerformed(originalColour.getColour());
         }
         else
         {
           ((FeatureColourChooser) caller)
-                  .maxColour_actionPerformed((Color) originalColour);
+                  .maxColour_actionPerformed(originalColour.getColour());
         }
       }
       if (dialog != null)
@@ -576,7 +582,7 @@ public class UserDefinedColours extends Panel implements ActionListener,
     Color[] newColours = new Color[24];
     for (int i = 0; i < 24; i++)
     {
-      newColours[i] = (Color) oldColours.elementAt(i);
+      newColours[i] = oldColours.elementAt(i);
       buttonPanel.getComponent(i).setBackground(newColours[i]);
     }
 
diff --git a/src/jalview/bin/ArgsParser.java b/src/jalview/bin/ArgsParser.java
new file mode 100644
index 0000000..da13ee0
--- /dev/null
+++ b/src/jalview/bin/ArgsParser.java
@@ -0,0 +1,117 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.bin;
+
+import java.net.URLDecoder;
+import java.util.Vector;
+
+/**
+ * Notes: this argParser does not distinguish between parameter switches,
+ * parameter values and argument text. If an argument happens to be identical to
+ * a parameter, it will be taken as such (even though it didn't have a '-'
+ * prefixing it).
+ * 
+ * @author Andrew Waterhouse and JBP documented.
+ * 
+ */
+public class ArgsParser
+{
+  Vector<String> vargs = null;
+
+  public ArgsParser(String[] args)
+  {
+    vargs = new Vector<String>();
+    for (int i = 0; i < args.length; i++)
+    {
+      String arg = args[i].trim();
+      if (arg.charAt(0) == '-')
+      {
+        arg = arg.substring(1);
+      }
+      vargs.addElement(arg);
+    }
+  }
+
+  /**
+   * check for and remove first occurence of arg+parameter in arglist.
+   * 
+   * @param arg
+   * @return return the argument following the given arg if arg was in list.
+   */
+  public String getValue(String arg)
+  {
+    return getValue(arg, false);
+  }
+
+  public String getValue(String arg, boolean utf8decode)
+  {
+    int index = vargs.indexOf(arg);
+    String dc = null, ret = null;
+    if (index != -1)
+    {
+      ret = vargs.elementAt(index + 1).toString();
+      vargs.removeElementAt(index);
+      vargs.removeElementAt(index);
+      if (utf8decode && ret != null)
+      {
+        try
+        {
+          dc = URLDecoder.decode(ret, "UTF-8");
+          ret = dc;
+        } catch (Exception e)
+        {
+          // TODO: log failure to decode
+        }
+      }
+    }
+    return ret;
+  }
+
+  /**
+   * check for and remove first occurence of arg in arglist.
+   * 
+   * @param arg
+   * @return true if arg was present in argslist.
+   */
+  public boolean contains(String arg)
+  {
+    if (vargs.contains(arg))
+    {
+      vargs.removeElement(arg);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  public String nextValue()
+  {
+    return vargs.remove(0);
+  }
+
+  public int getSize()
+  {
+    return vargs.size();
+  }
+
+}
diff --git a/src/jalview/bin/BuildDetails.java b/src/jalview/bin/BuildDetails.java
index f26afba..2595d4b 100644
--- a/src/jalview/bin/BuildDetails.java
+++ b/src/jalview/bin/BuildDetails.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/bin/Cache.java b/src/jalview/bin/Cache.java
index 21d5205..a52d5a4 100644
--- a/src/jalview/bin/Cache.java
+++ b/src/jalview/bin/Cache.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,8 +20,11 @@
  */
 package jalview.bin;
 
+import jalview.datamodel.PDBEntry;
+import jalview.structure.StructureImportSettings;
 import jalview.ws.dbsources.das.api.DasSourceRegistryI;
 import jalview.ws.dbsources.das.datamodel.DasSourceRegistry;
+import jalview.ws.sifts.SiftsSettings;
 
 import java.awt.Color;
 import java.io.BufferedReader;
@@ -35,6 +38,7 @@ import java.text.SimpleDateFormat;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Enumeration;
+import java.util.Locale;
 import java.util.Properties;
 import java.util.TreeSet;
 
@@ -216,6 +220,33 @@ public class Cache
 
   public static final String DAS_ACTIVE_SOURCE = "DAS_ACTIVE_SOURCE";
 
+  public static final String DEFAULT_SIFTS_DOWNLOAD_DIR = System
+          .getProperty("user.home")
+          + File.separatorChar
+          + ".sifts_downloads" + File.separatorChar;
+
+  private final static String DEFAULT_CACHE_THRESHOLD_IN_DAYS = "2";
+
+  private final static String DEFAULT_FAIL_SAFE_PID_THRESHOLD = "30";
+
+  /**
+   * Allowed values are PDB or mmCIF
+   */
+  private final static String PDB_DOWNLOAD_FORMAT = PDBEntry.Type.MMCIF
+          .toString();
+
+  private final static String DEFAULT_PDB_FILE_PARSER = StructureImportSettings.StructureParser.JMOL_PARSER
+          .toString();
+
+  /*
+   * a date formatter using a fixed (rather than the user's) locale; 
+   * this ensures that date properties can be written and re-read successfully
+   * even if the user changes their locale setting
+   */
+  private static final DateFormat date_format = SimpleDateFormat
+          .getDateTimeInstance(SimpleDateFormat.MEDIUM,
+                  SimpleDateFormat.MEDIUM, Locale.UK);
+
   /**
    * Initialises the Jalview Application Log
    */
@@ -394,9 +425,31 @@ public class Cache
       codeInstallation = " (" + codeInstallation + ")";
     }
     new BuildDetails(codeVersion, null, codeInstallation);
+
+    SiftsSettings
+            .setMapWithSifts(Cache.getDefault("MAP_WITH_SIFTS", false));
+
+    SiftsSettings.setSiftDownloadDirectory(jalview.bin.Cache.getDefault(
+            "sifts_download_dir", DEFAULT_SIFTS_DOWNLOAD_DIR));
+
+    SiftsSettings.setFailSafePIDThreshold(jalview.bin.Cache.getDefault(
+            "sifts_fail_safe_pid_threshold",
+            DEFAULT_FAIL_SAFE_PID_THRESHOLD));
+
+    SiftsSettings.setCacheThresholdInDays(jalview.bin.Cache.getDefault(
+            "sifts_cache_threshold_in_days",
+            DEFAULT_CACHE_THRESHOLD_IN_DAYS));
+
     System.out
             .println("Jalview Version: " + codeVersion + codeInstallation);
 
+    StructureImportSettings.setDefaultStructureFileFormat(jalview.bin.Cache
+            .getDefault("PDB_DOWNLOAD_FORMAT", PDB_DOWNLOAD_FORMAT));
+    StructureImportSettings
+            .setDefaultPDBFileParser(DEFAULT_PDB_FILE_PARSER);
+    // StructureImportSettings
+    // .setDefaultPDBFileParser(jalview.bin.Cache.getDefault(
+    // "DEFAULT_PDB_FILE_PARSER", DEFAULT_PDB_FILE_PARSER));
     // jnlpVersion will be null if we're using InstallAnywhere
     // Dont do this check if running in headless mode
     if (jnlpVersion == null
@@ -407,6 +460,7 @@ public class Cache
 
       class VersionChecker extends Thread
       {
+        @Override
         public void run()
         {
           String orgtimeout = System
@@ -417,7 +471,7 @@ public class Cache
             System.out.println("# INFO: Setting default net timeout to "
                     + orgtimeout + " seconds.");
           }
-          String jnlpVersion = null;
+          String remoteVersion = null;
           try
           {
             System.setProperty("sun.net.client.defaultConnectTimeout",
@@ -437,20 +491,20 @@ public class Cache
 
               line = line.substring(line.indexOf("value=") + 7);
               line = line.substring(0, line.lastIndexOf("\""));
-              jnlpVersion = line;
+              remoteVersion = line;
               break;
             }
           } catch (Exception ex)
           {
             System.out
-                    .println("Non-fatal exceptions when checking version at www.jalview.org :");
+                    .println("Non-fatal exception when checking version at www.jalview.org :");
             System.out.println(ex);
-            jnlpVersion = getProperty("VERSION");
+            remoteVersion = getProperty("VERSION");
           }
           System.setProperty("sun.net.client.defaultConnectTimeout",
                   orgtimeout);
 
-          setProperty("LATEST_VERSION", jnlpVersion);
+          setProperty("LATEST_VERSION", remoteVersion);
         }
       }
 
@@ -842,30 +896,31 @@ public class Cache
     setProperty(property, jalview.util.Format.getHexString(colour));
   }
 
-  public static final DateFormat date_format = SimpleDateFormat
-          .getDateTimeInstance();
-
   /**
-   * store a date in a jalview property
+   * Stores a formatted date in a jalview property, using a fixed locale.
    * 
-   * @param string
-   * @param time
+   * @param propertyName
+   * @param date
+   * @return the formatted date string
    */
-  public static void setDateProperty(String property, Date time)
+  public static String setDateProperty(String propertyName, Date date)
   {
-    setProperty(property, date_format.format(time));
+    String formatted = date_format.format(date);
+    setProperty(propertyName, formatted);
+    return formatted;
   }
 
   /**
-   * read a date stored in a jalview property
+   * Reads a date stored in a Jalview property, parses it (using a fixed locale
+   * format) and returns as a Date, or null if parsing fails
    * 
-   * @param property
-   * @return valid date as stored by setDateProperty, or null
+   * @param propertyName
+   * @return
    * 
    */
-  public static Date getDateProperty(String property)
+  public static Date getDateProperty(String propertyName)
   {
-    String val = getProperty(property);
+    String val = getProperty(propertyName);
     if (val != null)
     {
       try
@@ -874,7 +929,7 @@ public class Cache
       } catch (Exception ex)
       {
         System.err.println("Invalid or corrupt date in property '"
-                + property + "' : value was '" + val + "'");
+                + propertyName + "' : value was '" + val + "'");
       }
     }
     return null;
diff --git a/src/jalview/bin/Jalview.java b/src/jalview/bin/Jalview.java
index e29cbe1..de8db04 100644
--- a/src/jalview/bin/Jalview.java
+++ b/src/jalview/bin/Jalview.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,27 +20,39 @@
  */
 package jalview.bin;
 
+import groovy.lang.Binding;
+import groovy.util.GroovyScriptEngine;
+
+import jalview.ext.so.SequenceOntology;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
+import jalview.gui.PromptUserConfig;
+import jalview.io.AppletFormatAdapter;
 import jalview.io.BioJsHTMLOutput;
+import jalview.io.FileLoader;
+import jalview.io.FormatAdapter;
 import jalview.io.HtmlSvgOutput;
+import jalview.io.IdentifyFile;
+import jalview.io.NewickFile;
+import jalview.io.gff.SequenceOntologyFactory;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.UserColourScheme;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.ws.jws2.Jws2Discoverer;
 
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
-import java.lang.reflect.Constructor;
 import java.net.MalformedURLException;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
-import java.net.URLDecoder;
 import java.security.AllPermission;
 import java.security.CodeSource;
 import java.security.PermissionCollection;
@@ -51,7 +63,6 @@ import java.util.Map;
 import java.util.Vector;
 
 import javax.swing.UIManager;
-import javax.swing.UnsupportedLookAndFeelException;
 
 /**
  * Main class for Jalview Application <br>
@@ -63,11 +74,21 @@ import javax.swing.UnsupportedLookAndFeelException;
  */
 public class Jalview
 {
+  /*
+   * singleton instance of this class
+   */
+  private static Jalview instance;
+
+  private Desktop desktop;
+
+  public static AlignFrame currentAlignFrame;
+
   static
   {
     // grab all the rights we can the JVM
     Policy.setPolicy(new Policy()
     {
+      @Override
       public PermissionCollection getPermissions(CodeSource codesource)
       {
         Permissions perms = new Permissions();
@@ -75,6 +96,7 @@ public class Jalview
         return (perms);
       }
 
+      @Override
       public void refresh()
       {
       }
@@ -82,6 +104,71 @@ public class Jalview
   }
 
   /**
+   * keep track of feature fetching tasks.
+   * 
+   * @author JimP
+   * 
+   */
+  class FeatureFetcher
+  {
+    /*
+     * TODO: generalise to track all jalview events to orchestrate batch
+     * processing events.
+     */
+
+    private int queued = 0;
+
+    private int running = 0;
+
+    public FeatureFetcher()
+    {
+
+    }
+
+    public void addFetcher(final AlignFrame af,
+            final Vector<String> dasSources)
+    {
+      final long id = System.currentTimeMillis();
+      queued++;
+      final FeatureFetcher us = this;
+      new Thread(new Runnable()
+      {
+
+        @Override
+        public void run()
+        {
+          synchronized (us)
+          {
+            queued--;
+            running++;
+          }
+
+          af.setProgressBar(MessageManager
+                  .getString("status.das_features_being_retrived"), id);
+          af.featureSettings_actionPerformed(null);
+          af.featureSettings.fetchDasFeatures(dasSources, true);
+          af.setProgressBar(null, id);
+          synchronized (us)
+          {
+            running--;
+          }
+        }
+      }).start();
+    }
+
+    public synchronized boolean allFinished()
+    {
+      return queued == 0 && running == 0;
+    }
+
+  }
+
+  public static Jalview getInstance()
+  {
+    return instance;
+  }
+
+  /**
    * main class for Jalview application
    * 
    * @param args
@@ -89,6 +176,16 @@ public class Jalview
    */
   public static void main(String[] args)
   {
+    instance = new Jalview();
+    instance.doMain(args);
+  }
+
+  /**
+   * @param args
+   */
+  void doMain(String[] args)
+  {
+    System.setSecurityManager(null);
     System.out.println("Java version: "
             + System.getProperty("java.version"));
     System.out.println(System.getProperty("os.arch") + " "
@@ -161,7 +258,7 @@ public class Jalview
     try
     {
       Cache.initLogger();
-    } catch (java.lang.NoClassDefFoundError error)
+    } catch (NoClassDefFoundError error)
     {
       error.printStackTrace();
       System.out
@@ -170,7 +267,7 @@ public class Jalview
       System.exit(0);
     }
 
-    Desktop desktop = null;
+    desktop = null;
 
     try
     {
@@ -178,7 +275,7 @@ public class Jalview
     } catch (Exception ex)
     {
     }
-    if (new Platform().isAMac())
+    if (Platform.isAMac())
     {
       System.setProperty("com.apple.mrj.application.apple.menu.about.name",
               "Jalview");
@@ -187,13 +284,22 @@ public class Jalview
       {
         UIManager.setLookAndFeel(ch.randelshofer.quaqua.QuaquaManager
                 .getLookAndFeel());
-      } catch (UnsupportedLookAndFeelException e)
+      } catch (Throwable e)
       {
-        // TODO Auto-generated catch block
-        e.printStackTrace();
+        System.err.println("Failed to set QuaQua look and feel: "
+                + e.toString());
       }
     }
 
+    /*
+     * configure 'full' SO model if preferences say to, 
+     * else use the default (SO Lite)
+     */
+    if (Cache.getDefault("USE_FULL_SO", false))
+    {
+      SequenceOntologyFactory.setInstance(new SequenceOntology());
+    }
+
     if (!headless)
     {
       desktop = new Desktop();
@@ -234,7 +340,6 @@ public class Jalview
             Cache.log.debug("Starting questionnaire with default url: "
                     + defurl);
             desktop.checkForQuestionnaire(defurl);
-
           }
         }
       }
@@ -242,17 +347,19 @@ public class Jalview
       {
         System.err.println("CMD [-noquestionnaire] executed successfully!");
       }
-      desktop.checkForNews();
-    }
 
-    if (!isHeadlessMode())
-    {
+      if (!aparser.contains("nonews"))
+      {
+        desktop.checkForNews();
+      }
+
       BioJsHTMLOutput.updateBioJS();
     }
 
     String file = null, protocol = null, format = null, data = null;
-    jalview.io.FileLoader fileLoader = new jalview.io.FileLoader(!headless);
-    Vector getFeatures = null; // vector of das source nicknames to fetch
+    FileLoader fileLoader = new FileLoader(!headless);
+    Vector<String> getFeatures = null; // vector of das source nicknames to
+                                       // fetch
     // features from
     // loading is done.
     String groovyscript = null; // script to execute after all loading is
@@ -266,8 +373,8 @@ public class Jalview
       System.out.println("No files to open!");
       System.exit(1);
     }
-    String vamsasImport = aparser.getValue("vdoc"), vamsasSession = aparser
-            .getValue("vsess");
+    String vamsasImport = aparser.getValue("vdoc");
+    String vamsasSession = aparser.getValue("vsess");
     if (vamsasImport != null || vamsasSession != null)
     {
       if (desktop == null || headless)
@@ -282,13 +389,13 @@ public class Jalview
       {
         try
         {
-          String viprotocol = jalview.io.AppletFormatAdapter
+          String viprotocol = AppletFormatAdapter
                   .checkProtocol(vamsasImport);
           if (viprotocol == jalview.io.FormatAdapter.FILE)
           {
             inSession = desktop.vamsasImport(new File(vamsasImport));
           }
-          else if (viprotocol == jalview.io.FormatAdapter.URL)
+          else if (viprotocol == FormatAdapter.URL)
           {
             inSession = desktop.vamsasImport(new URL(vamsasImport));
           }
@@ -365,7 +472,7 @@ public class Jalview
 
       if (!file.startsWith("http://"))
       {
-        if (!(new java.io.File(file)).exists())
+        if (!(new File(file)).exists())
         {
           System.out.println("Can't find " + file);
           if (headless)
@@ -375,9 +482,9 @@ public class Jalview
         }
       }
 
-      protocol = jalview.io.AppletFormatAdapter.checkProtocol(file);
+      protocol = AppletFormatAdapter.checkProtocol(file);
 
-      format = new jalview.io.IdentifyFile().Identify(file, protocol);
+      format = new IdentifyFile().identify(file, protocol);
 
       AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol,
               format);
@@ -387,19 +494,18 @@ public class Jalview
       }
       else
       {
-
+        setCurrentAlignFrame(af);
         data = aparser.getValue("colour", true);
         if (data != null)
         {
           data.replaceAll("%20", " ");
 
-          jalview.schemes.ColourSchemeI cs = jalview.schemes.ColourSchemeProperty
-                  .getColour(af.getViewport().getAlignment(), data);
+          ColourSchemeI cs = ColourSchemeProperty.getColour(af
+                  .getViewport().getAlignment(), data);
 
           if (cs == null)
           {
-            jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
-                    "white");
+            UserColourScheme ucs = new UserColourScheme("white");
             ucs.parseAppletParameter(data);
             cs = ucs;
           }
@@ -416,7 +522,7 @@ public class Jalview
         if (data != null)
         {
           af.parseFeaturesFile(data,
-                  jalview.io.AppletFormatAdapter.checkProtocol(data));
+                  AppletFormatAdapter.checkProtocol(data));
           // System.out.println("Added " + data);
           System.out.println("CMD groups[-" + data
                   + "]  executed successfully!");
@@ -425,7 +531,7 @@ public class Jalview
         if (data != null)
         {
           af.parseFeaturesFile(data,
-                  jalview.io.AppletFormatAdapter.checkProtocol(data));
+                  AppletFormatAdapter.checkProtocol(data));
           // System.out.println("Added " + data);
           System.out.println("CMD [-features " + data
                   + "]  executed successfully!");
@@ -473,8 +579,8 @@ public class Jalview
           {
             System.out.println("CMD [-tree " + data
                     + "] executed successfully!");
-            fin = new jalview.io.NewickFile(data,
-                    jalview.io.AppletFormatAdapter.checkProtocol(data));
+            fin = new NewickFile(data,
+                    AppletFormatAdapter.checkProtocol(data));
             if (fin != null)
             {
               af.getViewport().setCurrentTree(
@@ -514,20 +620,10 @@ public class Jalview
         {
           // Execute the groovy script after we've done all the rendering stuff
           // and before any images or figures are generated.
-          if (jalview.bin.Cache.groovyJarsPresent())
-          {
-            System.out.println("Executing script " + groovyscript);
-            executeGroovyScript(groovyscript, new Object[] { desktop, af });
-
-            System.out.println("CMD groovy[" + groovyscript
-                    + "] executed successfully!");
-          }
-          else
-          {
-            System.err
-                    .println("Sorry. Groovy Support is not available, so ignoring the provided groovy script "
-                            + groovyscript);
-          }
+          System.out.println("Executing script " + groovyscript);
+          executeGroovyScript(groovyscript, af);
+          System.out.println("CMD groovy[" + groovyscript
+                  + "] executed successfully!");
           groovyscript = null;
         }
         String imageName = "unnamed.png";
@@ -538,14 +634,14 @@ public class Jalview
 
           if (format.equalsIgnoreCase("png"))
           {
-            af.createPNG(new java.io.File(file));
-            imageName = (new java.io.File(file)).getName();
+            af.createPNG(new File(file));
+            imageName = (new File(file)).getName();
             System.out.println("Creating PNG image: " + file);
             continue;
           }
           else if (format.equalsIgnoreCase("svg"))
           {
-            File imageFile = new java.io.File(file);
+            File imageFile = new File(file);
             imageName = imageFile.getName();
             af.createSVG(imageFile);
             System.out.println("Creating SVG image: " + file);
@@ -553,21 +649,44 @@ public class Jalview
           }
           else if (format.equalsIgnoreCase("html"))
           {
-            File imageFile = new java.io.File(file);
+            File imageFile = new File(file);
             imageName = imageFile.getName();
-            new HtmlSvgOutput(new java.io.File(file), af.alignPanel);
+            HtmlSvgOutput htmlSVG = new HtmlSvgOutput(af.alignPanel);
+            htmlSVG.exportHTML(file);
+
             System.out.println("Creating HTML image: " + file);
             continue;
           }
+          else if (format.equalsIgnoreCase("biojsmsa"))
+          {
+            if (file == null)
+            {
+              System.err.println("The output html file must not be null");
+              return;
+            }
+            try
+            {
+              BioJsHTMLOutput
+                      .refreshVersionInfo(BioJsHTMLOutput.BJS_TEMPLATES_LOCAL_DIRECTORY);
+            } catch (URISyntaxException e)
+            {
+              e.printStackTrace();
+            }
+            BioJsHTMLOutput bjs = new BioJsHTMLOutput(af.alignPanel);
+            bjs.exportHTML(file);
+            System.out.println("Creating BioJS MSA Viwer HTML file: "
+                    + file);
+            continue;
+          }
           else if (format.equalsIgnoreCase("imgMap"))
           {
-            af.createImageMap(new java.io.File(file), imageName);
+            af.createImageMap(new File(file), imageName);
             System.out.println("Creating image map: " + file);
             continue;
           }
           else if (format.equalsIgnoreCase("eps"))
           {
-            File outputFile = new java.io.File(file);
+            File outputFile = new File(file);
             System.out.println("Creating EPS file: "
                     + outputFile.getAbsolutePath());
             af.createEPS(outputFile);
@@ -627,7 +746,7 @@ public class Jalview
       }
       else
       {
-        format = new jalview.io.IdentifyFile().Identify(file, protocol);
+        format = new IdentifyFile().identify(file, protocol);
       }
 
       startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol,
@@ -649,11 +768,10 @@ public class Jalview
     // Once all other stuff is done, execute any groovy scripts (in order)
     if (groovyscript != null)
     {
-      if (jalview.bin.Cache.groovyJarsPresent())
+      if (Cache.groovyJarsPresent())
       {
         System.out.println("Executing script " + groovyscript);
-        executeGroovyScript(groovyscript, new Object[] { desktop,
-            startUpAlframe });
+        executeGroovyScript(groovyscript, startUpAlframe);
       }
       else
       {
@@ -695,10 +813,12 @@ public class Jalview
                     + "-png FILE\tCreate PNG image FILE from alignment.\n"
                     + "-svg FILE\tCreate SVG image FILE from alignment.\n"
                     + "-html FILE\tCreate HTML file from alignment.\n"
+                    + "-biojsMSA FILE\tCreate BioJS MSA Viewer HTML file from alignment.\n"
                     + "-imgMap FILE\tCreate HTML file FILE with image map of PNG image.\n"
                     + "-eps FILE\tCreate EPS file FILE from alignment.\n"
                     + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n"
                     + "-noquestionnaire\tTurn off questionnaire check.\n"
+                    + "-nonews\tTurn off check for Jalview news.\n"
                     + "-nousagestats\tTurn off google analytics tracking for this session.\n"
                     + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n"
                     // +
@@ -720,8 +840,8 @@ public class Jalview
     /**
      * start a User Config prompt asking if we can log usage statistics.
      */
-    jalview.gui.PromptUserConfig prompter = new jalview.gui.PromptUserConfig(
-            desktop.desktop,
+    PromptUserConfig prompter = new PromptUserConfig(
+            Desktop.desktop,
             "USAGESTATS",
             "Jalview Usage Statistics",
             "Do you want to help make Jalview better by enabling "
@@ -729,6 +849,7 @@ public class Jalview
                     + "\n\n(you can enable or disable usage tracking in the preferences)",
             new Runnable()
             {
+              @Override
               public void run()
               {
                 Cache.log
@@ -738,6 +859,7 @@ public class Jalview
               }
             }, new Runnable()
             {
+              @Override
               public void run()
               {
                 Cache.log.debug("Not enabling Google Tracking.");
@@ -755,14 +877,8 @@ public class Jalview
    *          the Jalview Desktop object passed in to the groovy binding as the
    *          'Jalview' object.
    */
-  private static void executeGroovyScript(String groovyscript,
-          Object[] jalviewContext)
+  private void executeGroovyScript(String groovyscript, AlignFrame af)
   {
-    if (jalviewContext == null)
-    {
-      System.err
-              .println("Sorry. Groovy support is currently only available when running with the Jalview GUI enabled.");
-    }
     /**
      * for scripts contained in files
      */
@@ -779,8 +895,8 @@ public class Jalview
         tfile = File.createTempFile("jalview", "groovy");
         PrintWriter outfile = new PrintWriter(new OutputStreamWriter(
                 new FileOutputStream(tfile)));
-        BufferedReader br = new BufferedReader(
-                new java.io.InputStreamReader(System.in));
+        BufferedReader br = new BufferedReader(new InputStreamReader(
+                System.in));
         String line = null;
         while ((line = br.readLine()) != null)
         {
@@ -844,75 +960,23 @@ public class Jalview
         }
       }
     }
-    boolean success = false;
     try
     {
-      /*
-       * The following code performs the GroovyScriptEngine invocation using
-       * reflection, and is equivalent to this fragment from the embedding
-       * groovy documentation on the groovy site: <code> import
-       * groovy.lang.Binding; import groovy.util.GroovyScriptEngine;
-       * 
-       * String[] roots = new String[] { "/my/groovy/script/path" };
-       * GroovyScriptEngine gse = new GroovyScriptEngine(roots); Binding binding
-       * = new Binding(); binding.setVariable("input", "world");
-       * gse.run("hello.groovy", binding); </code>
-       */
-      Class<?>[] bspec;
-      Object[] binding;
-      int blen = ((jalviewContext[0] == null) ? 0 : 1)
-              + ((jalviewContext[1] == null) ? 0 : 1);
-      String cnames[] = new String[] { "Jalview", "currentAlFrame" };
-      bspec = new Class[blen * 2];
-      binding = new Object[blen * 2];
-      blen = 0;
-      ClassLoader cl = null;
       Map<String, Object> vbinding = new HashMap<String, Object>();
-      for (int jc = 0; jc < jalviewContext.length; jc++)
+      vbinding.put("Jalview", this);
+      if (af != null)
       {
-        if (jalviewContext[jc] != null)
-        {
-          if (cl == null)
-          {
-            cl = jalviewContext[jc].getClass().getClassLoader();
-          }
-          bspec[blen * 2] = String.class;
-          bspec[blen * 2 + 1] = Object.class;
-          binding[blen * 2] = cnames[jc];
-          binding[blen * 2 + 1] = jalviewContext[jc];
-          vbinding.put(cnames[jc], jalviewContext[jc]);
-          blen++;
-        }
+        vbinding.put("currentAlFrame", af);
       }
-      Class<?> gbindingc = cl.loadClass("groovy.lang.Binding");
-      Constructor<?> gbcons;
-      Object gbinding;
-      try
-      {
-        gbcons = gbindingc.getConstructor(Map.class);
-        gbinding = gbcons.newInstance(vbinding);
-      } catch (NoSuchMethodException x)
+      Binding gbinding = new Binding(vbinding);
+      GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile });
+      gse.run(sfile.toString(), gbinding);
+      if ("STDIN".equals(groovyscript))
       {
-        // old style binding config - using series of string/object values to
-        // setVariable.
-        gbcons = gbindingc.getConstructor();
-        gbinding = gbcons.newInstance();
-        java.lang.reflect.Method setvar = gbindingc.getMethod(
-                "setVariable", bspec);
-        setvar.invoke(gbinding, binding);
+        // delete temp file that we made -
+        // only if it was successfully executed
+        tfile.delete();
       }
-
-      Class<?> gsec = cl.loadClass("groovy.util.GroovyScriptEngine");
-      Constructor<?> gseccons = gsec
-              .getConstructor(new Class[] { URL[].class }); // String[].class
-                                                            // });
-      Object gse = gseccons
-              .newInstance(new Object[] { new URL[] { sfile } }); // .toString()
-                                                                  // } });
-      java.lang.reflect.Method run = gsec.getMethod("run", new Class[] {
-          String.class, gbindingc });
-      run.invoke(gse, new Object[] { sfile.toString(), gbinding });
-      success = true;
     } catch (Exception e)
     {
       System.err.println("Exception Whilst trying to execute file " + sfile
@@ -920,12 +984,6 @@ public class Jalview
       e.printStackTrace(System.err);
 
     }
-    if (success && groovyscript.equals("STDIN"))
-    {
-      // delete temp file that we made - but only if it was successfully
-      // executed
-      tfile.delete();
-    }
   }
 
   /**
@@ -933,16 +991,15 @@ public class Jalview
    * 
    * @return vector of DAS source nicknames to retrieve from
    */
-  private static Vector checkDasArguments(ArgsParser aparser)
+  private static Vector<String> checkDasArguments(ArgsParser aparser)
   {
-    Vector source = null;
+    Vector<String> source = null;
     String data;
     String locsources = Cache.getProperty(Cache.DAS_LOCAL_SOURCE);
     while ((data = aparser.getValue("dasserver", true)) != null)
     {
       String nickname = null;
       String url = null;
-      boolean seq = false, feat = true;
       int pos = data.indexOf('=');
       // determine capabilities
       if (pos > 0)
@@ -972,7 +1029,7 @@ public class Jalview
                         + nickname + "|" + url);
         if (source == null)
         {
-          source = new Vector();
+          source = new Vector<String>();
         }
         source.addElement(nickname);
       }
@@ -990,7 +1047,7 @@ public class Jalview
       System.out.println("adding source '" + data + "'");
       if (source == null)
       {
-        source = new Vector();
+        source = new Vector<String>();
       }
       source.addElement(data);
     }
@@ -1002,7 +1059,8 @@ public class Jalview
    * 
    * @param dasSources
    */
-  private static FeatureFetcher startFeatureFetching(final Vector dasSources)
+  private FeatureFetcher startFeatureFetching(
+          final Vector<String> dasSources)
   {
     FeatureFetcher ff = new FeatureFetcher();
     AlignFrame afs[] = Desktop.getAlignFrames();
@@ -1026,173 +1084,37 @@ public class Jalview
     }
     return false;
   }
-}
-
-/**
- * Notes: this argParser does not distinguish between parameter switches,
- * parameter values and argument text. If an argument happens to be identical to
- * a parameter, it will be taken as such (even though it didn't have a '-'
- * prefixing it).
- * 
- * @author Andrew Waterhouse and JBP documented.
- * 
- */
-
-class rnabuttonlistener implements ActionListener
-{
-  public void actionPerformed(ActionEvent arg0)
-  {
-    System.out.println("Good idea ! ");
-
-  }
-}
-
-class pbuttonlistener implements ActionListener
-{
-  public void actionPerformed(ActionEvent arg0)
-  {
-
-  }
-}
-
-class ArgsParser
-{
-  Vector vargs = null;
-
-  public ArgsParser(String[] args)
-  {
-    vargs = new Vector();
-    for (int i = 0; i < args.length; i++)
-    {
-      String arg = args[i].trim();
-      if (arg.charAt(0) == '-')
-      {
-        arg = arg.substring(1);
-      }
-      vargs.addElement(arg);
-    }
-  }
 
-  /**
-   * check for and remove first occurence of arg+parameter in arglist.
-   * 
-   * @param arg
-   * @return return the argument following the given arg if arg was in list.
-   */
-  public String getValue(String arg)
+  public AlignFrame[] getAlignFrames()
   {
-    return getValue(arg, false);
-  }
+    return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() }
+            : Desktop.getAlignFrames();
 
-  public String getValue(String arg, boolean utf8decode)
-  {
-    int index = vargs.indexOf(arg);
-    String dc = null, ret = null;
-    if (index != -1)
-    {
-      ret = vargs.elementAt(index + 1).toString();
-      vargs.removeElementAt(index);
-      vargs.removeElementAt(index);
-      if (utf8decode && ret != null)
-      {
-        try
-        {
-          dc = URLDecoder.decode(ret, "UTF-8");
-          ret = dc;
-        } catch (Exception e)
-        {
-          // TODO: log failure to decode
-        }
-      }
-    }
-    return ret;
   }
 
   /**
-   * check for and remove first occurence of arg in arglist.
-   * 
-   * @param arg
-   * @return true if arg was present in argslist.
+   * Quit method delegates to Desktop.quit - unless running in headless mode
+   * when it just ends the JVM
    */
-  public boolean contains(String arg)
+  public void quit()
   {
-    if (vargs.contains(arg))
+    if (desktop != null)
     {
-      vargs.removeElement(arg);
-      return true;
+      desktop.quit();
     }
     else
     {
-      return false;
+      System.exit(0);
     }
   }
 
-  public String nextValue()
-  {
-    return vargs.remove(0).toString();
-  }
-
-  public int getSize()
-  {
-    return vargs.size();
-  }
-
-}
-
-/**
- * keep track of feature fetching tasks.
- * 
- * @author JimP
- * 
- */
-class FeatureFetcher
-{
-  /*
-   * TODO: generalise to track all jalview events to orchestrate batch
-   * processing events.
-   */
-
-  private int queued = 0;
-
-  private int running = 0;
-
-  public FeatureFetcher()
-  {
-
-  }
-
-  public void addFetcher(final AlignFrame af, final Vector dasSources)
+  public static AlignFrame getCurrentAlignFrame()
   {
-    final long id = System.currentTimeMillis();
-    queued++;
-    final FeatureFetcher us = this;
-    new Thread(new Runnable()
-    {
-
-      public void run()
-      {
-        synchronized (us)
-        {
-          queued--;
-          running++;
-        }
-
-        af.setProgressBar(MessageManager
-                .getString("status.das_features_being_retrived"), id);
-        af.featureSettings_actionPerformed(null);
-        af.featureSettings.fetchDasFeatures(dasSources, true);
-        af.setProgressBar(null, id);
-        synchronized (us)
-        {
-          running--;
-        }
-      }
-    }).start();
+    return Jalview.currentAlignFrame;
   }
 
-  public synchronized boolean allFinished()
+  public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
   {
-    return queued == 0 && running == 0;
+    Jalview.currentAlignFrame = currentAlignFrame;
   }
-
 }
diff --git a/src/jalview/bin/JalviewLite.java b/src/jalview/bin/JalviewLite.java
index 59424df..75d1957 100644
--- a/src/jalview/bin/JalviewLite.java
+++ b/src/jalview/bin/JalviewLite.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -45,8 +45,10 @@ import jalview.io.NewickFile;
 import jalview.javascript.JSFunctionExec;
 import jalview.javascript.JalviewLiteJsApi;
 import jalview.javascript.JsCallBack;
+import jalview.javascript.MouseOverStructureListener;
 import jalview.structure.SelectionListener;
 import jalview.structure.StructureSelectionManager;
+import jalview.util.HttpUtils;
 import jalview.util.MessageManager;
 
 import java.applet.Applet;
@@ -61,10 +63,9 @@ import java.awt.event.ActionEvent;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.StringTokenizer;
@@ -92,13 +93,14 @@ public class JalviewLite extends Applet implements
   }
 
   // /////////////////////////////////////////
-  // The following public methods maybe called
+  // The following public methods may be called
   // externally, eg via javascript in HTML page
   /*
    * (non-Javadoc)
    * 
    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences()
    */
+  @Override
   public String getSelectedSequences()
   {
     return getSelectedSequencesFrom(getDefaultTargetFrame());
@@ -109,6 +111,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#getSelectedSequences(java.lang.String)
    */
+  @Override
   public String getSelectedSequences(String sep)
   {
     return getSelectedSequencesFrom(getDefaultTargetFrame(), sep);
@@ -121,6 +124,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
    * .AlignFrame)
    */
+  @Override
   public String getSelectedSequencesFrom(AlignFrame alf)
   {
     return getSelectedSequencesFrom(alf, separator); // ""+0x00AC);
@@ -133,6 +137,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesFrom(jalview.appletgui
    * .AlignFrame, java.lang.String)
    */
+  @Override
   public String getSelectedSequencesFrom(AlignFrame alf, String sep)
   {
     StringBuffer result = new StringBuffer("");
@@ -161,6 +166,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#highlight(java.lang.String,
    * java.lang.String, java.lang.String)
    */
+  @Override
   public void highlight(String sequenceId, String position,
           String alignedPosition)
   {
@@ -174,6 +180,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#highlightIn(jalview.appletgui.AlignFrame,
    * java.lang.String, java.lang.String, java.lang.String)
    */
+  @Override
   public void highlightIn(final AlignFrame alf, final String sequenceId,
           final String position, final String alignedPosition)
   {
@@ -230,6 +237,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#select(java.lang.String,
    * java.lang.String)
    */
+  @Override
   public void select(String sequenceIds, String columns)
   {
     selectIn(getDefaultTargetFrame(), sequenceIds, columns, separator);
@@ -241,6 +249,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#select(java.lang.String,
    * java.lang.String, java.lang.String)
    */
+  @Override
   public void select(String sequenceIds, String columns, String sep)
   {
     selectIn(getDefaultTargetFrame(), sequenceIds, columns, sep);
@@ -252,6 +261,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#selectIn(jalview.appletgui.AlignFrame,
    * java.lang.String, java.lang.String)
    */
+  @Override
   public void selectIn(AlignFrame alf, String sequenceIds, String columns)
   {
     selectIn(alf, sequenceIds, columns, separator);
@@ -263,6 +273,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#selectIn(jalview.appletgui.AlignFrame,
    * java.lang.String, java.lang.String, java.lang.String)
    */
+  @Override
   public void selectIn(final AlignFrame alf, String sequenceIds,
           String columns, String sep)
   {
@@ -455,14 +466,11 @@ public class JalviewLite extends Applet implements
         SequenceI rs = sel.getSequenceAt(0);
         start = rs.findIndex(start);
         end = rs.findIndex(end);
-        if (csel != null)
+        List<Integer> cs = new ArrayList<Integer>(csel.getSelected());
+        csel.clear();
+        for (Integer selectedCol : cs)
         {
-          List<Integer> cs = csel.getSelected();
-          csel.clear();
-          for (Integer selectedCol : cs)
-          {
-            csel.addElement(rs.findIndex(selectedCol));
-          }
+          csel.addElement(rs.findIndex(selectedCol));
         }
       }
       sel.setStartRes(start);
@@ -485,6 +493,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesAsAlignment(java.lang.
    * String, java.lang.String)
    */
+  @Override
   public String getSelectedSequencesAsAlignment(String format, String suffix)
   {
     return getSelectedSequencesAsAlignmentFrom(getDefaultTargetFrame(),
@@ -498,6 +507,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#getSelectedSequencesAsAlignmentFrom(jalview
    * .appletgui.AlignFrame, java.lang.String, java.lang.String)
    */
+  @Override
   public String getSelectedSequencesAsAlignmentFrom(AlignFrame alf,
           String format, String suffix)
   {
@@ -527,6 +537,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#getAlignmentOrder()
    */
+  @Override
   public String getAlignmentOrder()
   {
     return getAlignmentOrderFrom(getDefaultTargetFrame());
@@ -539,6 +550,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#getAlignmentOrderFrom(jalview.appletgui.AlignFrame
    * )
    */
+  @Override
   public String getAlignmentOrderFrom(AlignFrame alf)
   {
     return getAlignmentOrderFrom(alf, separator);
@@ -551,6 +563,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#getAlignmentOrderFrom(jalview.appletgui.AlignFrame
    * , java.lang.String)
    */
+  @Override
   public String getAlignmentOrderFrom(AlignFrame alf, String sep)
   {
     AlignmentI alorder = alf.getAlignViewport().getAlignment();
@@ -568,6 +581,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#orderBy(java.lang.String,
    * java.lang.String)
    */
+  @Override
   public String orderBy(String order, String undoName)
   {
     return orderBy(order, undoName, separator);
@@ -579,6 +593,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#orderBy(java.lang.String,
    * java.lang.String, java.lang.String)
    */
+  @Override
   public String orderBy(String order, String undoName, String sep)
   {
     return orderAlignmentBy(getDefaultTargetFrame(), order, undoName, sep);
@@ -591,6 +606,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#orderAlignmentBy(jalview.appletgui.AlignFrame,
    * java.lang.String, java.lang.String, java.lang.String)
    */
+  @Override
   public String orderAlignmentBy(AlignFrame alf, String order,
           String undoName, String sep)
   {
@@ -647,6 +663,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#getAlignment(java.lang.String)
    */
+  @Override
   public String getAlignment(String format)
   {
     return getAlignmentFrom(getDefaultTargetFrame(), format, TRUE);
@@ -659,6 +676,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#getAlignmentFrom(jalview.appletgui.AlignFrame,
    * java.lang.String)
    */
+  @Override
   public String getAlignmentFrom(AlignFrame alf, String format)
   {
     return getAlignmentFrom(alf, format, TRUE);
@@ -670,6 +688,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#getAlignment(java.lang.String,
    * java.lang.String)
    */
+  @Override
   public String getAlignment(String format, String suffix)
   {
     return getAlignmentFrom(getDefaultTargetFrame(), format, suffix);
@@ -682,6 +701,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#getAlignmentFrom(jalview.appletgui.AlignFrame,
    * java.lang.String, java.lang.String)
    */
+  @Override
   public String getAlignmentFrom(AlignFrame alf, String format,
           String suffix)
   {
@@ -704,6 +724,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#loadAnnotation(java.lang.String)
    */
+  @Override
   public void loadAnnotation(String annotation)
   {
     loadAnnotationFrom(getDefaultTargetFrame(), annotation);
@@ -716,6 +737,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#loadAnnotationFrom(jalview.appletgui.AlignFrame
    * , java.lang.String)
    */
+  @Override
   public void loadAnnotationFrom(AlignFrame alf, String annotation)
   {
     if (new AnnotationFile().annotateAlignmentView(alf.getAlignViewport(),
@@ -735,6 +757,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#loadAnnotation(java.lang.String)
    */
+  @Override
   public void loadFeatures(String features, boolean autoenabledisplay)
   {
     loadFeaturesFrom(getDefaultTargetFrame(), features, autoenabledisplay);
@@ -747,6 +770,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#loadAnnotationFrom(jalview.appletgui.AlignFrame
    * , java.lang.String)
    */
+  @Override
   public boolean loadFeaturesFrom(AlignFrame alf, String features,
           boolean autoenabledisplay)
   {
@@ -759,6 +783,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#getFeatures(java.lang.String)
    */
+  @Override
   public String getFeatures(String format)
   {
     return getFeaturesFrom(getDefaultTargetFrame(), format);
@@ -771,6 +796,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#getFeaturesFrom(jalview.appletgui.AlignFrame,
    * java.lang.String)
    */
+  @Override
   public String getFeaturesFrom(AlignFrame alf, String format)
   {
     return alf.outputFeatures(false, format);
@@ -781,6 +807,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#getAnnotation()
    */
+  @Override
   public String getAnnotation()
   {
     return getAnnotationFrom(getDefaultTargetFrame());
@@ -793,6 +820,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#getAnnotationFrom(jalview.appletgui.AlignFrame
    * )
    */
+  @Override
   public String getAnnotationFrom(AlignFrame alf)
   {
     return alf.outputAnnotations(false);
@@ -803,6 +831,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#newView()
    */
+  @Override
   public AlignFrame newView()
   {
     return newViewFrom(getDefaultTargetFrame());
@@ -813,6 +842,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#newView(java.lang.String)
    */
+  @Override
   public AlignFrame newView(String name)
   {
     return newViewFrom(getDefaultTargetFrame(), name);
@@ -823,6 +853,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#newViewFrom(jalview.appletgui.AlignFrame)
    */
+  @Override
   public AlignFrame newViewFrom(AlignFrame alf)
   {
     return alf.newView(null);
@@ -834,6 +865,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#newViewFrom(jalview.appletgui.AlignFrame,
    * java.lang.String)
    */
+  @Override
   public AlignFrame newViewFrom(AlignFrame alf, String name)
   {
     return alf.newView(name);
@@ -845,11 +877,12 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#loadAlignment(java.lang.String,
    * java.lang.String)
    */
+  @Override
   public AlignFrame loadAlignment(String text, String title)
   {
     AlignmentI al = null;
 
-    String format = new IdentifyFile().Identify(text,
+    String format = new IdentifyFile().identify(text,
             AppletFormatAdapter.PASTE);
     try
     {
@@ -871,6 +904,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#setMouseoverListener(java.lang.String)
    */
+  @Override
   public void setMouseoverListener(String listener)
   {
     setMouseoverListener(currentAlignFrame, listener);
@@ -885,6 +919,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#setMouseoverListener(jalview.appletgui.AlignFrame
    * , java.lang.String)
    */
+  @Override
   public void setMouseoverListener(AlignFrame af, String listener)
   {
     if (listener != null)
@@ -917,6 +952,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#setSelectionListener(java.lang.String)
    */
+  @Override
   public void setSelectionListener(String listener)
   {
     setSelectionListener(null, listener);
@@ -929,6 +965,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#setSelectionListener(jalview.appletgui.AlignFrame
    * , java.lang.String)
    */
+  @Override
   public void setSelectionListener(AlignFrame af, String listener)
   {
     if (listener != null)
@@ -956,12 +993,18 @@ public class JalviewLite extends Applet implements
     }
   }
 
-  /*
-   * (non-Javadoc)
-   * 
+  /**
+   * Callable from javascript to register a javascript function to pass events
+   * to a structure viewer.
+   *
+   * @param listener
+   *          the name of a javascript function
+   * @param modelSet
+   *          a token separated list of PDB file names listened for
    * @see jalview.bin.JalviewLiteJsApi#setStructureListener(java.lang.String,
-   * java.lang.String)
+   *      java.lang.String)
    */
+  @Override
   public void setStructureListener(String listener, String modelSet)
   {
     if (listener != null)
@@ -974,8 +1017,8 @@ public class JalviewLite extends Applet implements
         return;
       }
     }
-    jalview.javascript.MouseOverStructureListener mol = new jalview.javascript.MouseOverStructureListener(
-            this, listener, separatorListToArray(modelSet));
+    MouseOverStructureListener mol = new MouseOverStructureListener(this,
+            listener, separatorListToArray(modelSet));
     javascriptListeners.addElement(mol);
     StructureSelectionManager.getStructureSelectionManager(this)
             .addStructureViewerListener(mol);
@@ -995,6 +1038,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#removeJavascriptListener(jalview.appletgui
    * .AlignFrame, java.lang.String)
    */
+  @Override
   public void removeJavascriptListener(AlignFrame af, String listener)
   {
     if (listener != null)
@@ -1044,12 +1088,14 @@ public class JalviewLite extends Applet implements
     }
   }
 
+  @Override
   public void stop()
   {
     System.err.println("Applet " + getName() + " stop().");
     tidyUp();
   }
 
+  @Override
   public void destroy()
   {
     System.err.println("Applet " + getName() + " destroy().");
@@ -1106,6 +1152,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#mouseOverStructure(java.lang.String,
    * java.lang.String, java.lang.String)
    */
+  @Override
   public void mouseOverStructure(final String pdbResNum,
           final String chain, final String pdbfile)
   {
@@ -1143,6 +1190,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#scrollViewToIn(jalview.appletgui.AlignFrame,
    * java.lang.String, java.lang.String)
    */
+  @Override
   public void scrollViewToIn(final AlignFrame alf, final String topRow,
           final String leftHandColumn)
   {
@@ -1345,9 +1393,10 @@ public class JalviewLite extends Applet implements
   /**
    * init method for Jalview Applet
    */
+  @Override
   public void init()
   {
-    // remove any handlers that might be hanging around from an earlier instance
+    debug = TRUE.equalsIgnoreCase(getParameter("debug"));
     try
     {
       if (debug)
@@ -1370,17 +1419,12 @@ public class JalviewLite extends Applet implements
         ex.printStackTrace();
       }
     }
-    /**
-     * turn on extra applet debugging
-     */
-    debug = TRUE.equalsIgnoreCase(getParameter("debug"));
+
     if (debug)
     {
-
       System.err.println("JalviewLite Version " + getVersion());
       System.err.println("Build Date : " + getBuildDate());
       System.err.println("Installation : " + getInstallation());
-
     }
     String externalsviewer = getParameter("externalstructureviewer");
     if (externalsviewer != null)
@@ -1486,6 +1530,7 @@ public class JalviewLite extends Applet implements
         add(launcher);
         launcher.addActionListener(new java.awt.event.ActionListener()
         {
+          @Override
           public void actionPerformed(ActionEvent e)
           {
             LoadingThread loader = new LoadingThread(file, file2,
@@ -1608,6 +1653,7 @@ public class JalviewLite extends Applet implements
     frame.setTitle(title);
     frame.addWindowListener(new WindowAdapter()
     {
+      @Override
       public void windowClosing(WindowEvent e)
       {
         if (frame instanceof AlignFrame)
@@ -1632,6 +1678,7 @@ public class JalviewLite extends Applet implements
         frame.dispose();
       }
 
+      @Override
       public void windowActivated(WindowEvent e)
       {
         if (frame instanceof AlignFrame)
@@ -1669,6 +1716,7 @@ public class JalviewLite extends Applet implements
    * @param g
    *          graphics context
    */
+  @Override
   public void paint(Graphics g)
   {
     if (!fileFound)
@@ -1720,6 +1768,7 @@ public class JalviewLite extends Applet implements
   {
     private boolean running = false;
 
+    @Override
     public void run()
     {
       if (running || checkedForJmol)
@@ -1789,27 +1838,72 @@ public class JalviewLite extends Applet implements
      * update the protocol state variable for accessing the datasource located
      * by file.
      * 
-     * @param file
+     * @param path
      * @return possibly updated datasource string
      */
-    public String setProtocolState(String file)
+    public String resolveFileProtocol(String path)
     {
-      if (file.startsWith("PASTE"))
+      /*
+       * is it paste data?
+       */
+      if (path.startsWith("PASTE"))
       {
-        file = file.substring(5);
         protocol = AppletFormatAdapter.PASTE;
+        return path.substring(5);
       }
-      else if (inArchive(file))
+
+      /*
+       * is it a URL?
+       */
+      if (path.indexOf("://") != -1)
       {
-        protocol = AppletFormatAdapter.CLASSLOADER;
+        protocol = AppletFormatAdapter.URL;
+        return path;
       }
-      else
+
+      /*
+       * try relative to document root
+       */
+      URL documentBase = getDocumentBase();
+      String withDocBase = resolveUrlForLocalOrAbsolute(path, documentBase);
+      if (HttpUtils.isValidUrl(withDocBase))
       {
-        file = addProtocol(file);
+        if (debug)
+        {
+          System.err.println("Prepended document base '" + documentBase
+                  + "' to make: '" + withDocBase + "'");
+        }
         protocol = AppletFormatAdapter.URL;
+        return withDocBase;
       }
-      dbgMsg("Protocol identified as '" + protocol + "'");
-      return file;
+
+      /*
+       * try relative to codebase (if different to document base)
+       */
+      URL codeBase = getCodeBase();
+      String withCodeBase = applet.resolveUrlForLocalOrAbsolute(path,
+              codeBase);
+      if (!withCodeBase.equals(withDocBase)
+              && HttpUtils.isValidUrl(withCodeBase))
+      {
+        protocol = AppletFormatAdapter.URL;
+        if (debug)
+        {
+          System.err.println("Prepended codebase '" + codeBase
+                  + "' to make: '" + withCodeBase + "'");
+        }
+        return withCodeBase;
+      }
+
+      /*
+       * try locating by classloader; try this last so files in the directory
+       * are resolved using document base
+       */
+      if (inArchive(path))
+      {
+        protocol = AppletFormatAdapter.CLASSLOADER;
+      }
+      return path;
     }
 
     public LoadingThread(String file, String file2, JalviewLite _applet)
@@ -1819,6 +1913,7 @@ public class JalviewLite extends Applet implements
       applet = _applet;
     }
 
+    @Override
     public void run()
     {
       LoadJmolThread jmolchecker = new LoadJmolThread();
@@ -1885,8 +1980,11 @@ public class JalviewLite extends Applet implements
       {
         AlignmentI al1 = af.viewport.getAlignment();
         AlignmentI al2 = af2.viewport.getAlignment();
-        if (AlignmentUtils.isMappable(al1, al2))
+        AlignmentI cdna = al1.isNucleotide() ? al1 : al2;
+        AlignmentI prot = al1.isNucleotide() ? al2 : al1;
+        if (AlignmentUtils.mapProteinAlignmentToCdna(prot, cdna))
         {
+          al2.alignAs(al1);
           SplitFrame sf = new SplitFrame(af, af2);
           sf.addToDisplay(embedded, JalviewLite.this);
           return;
@@ -1916,8 +2014,8 @@ public class JalviewLite extends Applet implements
       {
         return null;
       }
-      String resolvedFile = setProtocolState(fileParam);
-      String format = new IdentifyFile().Identify(resolvedFile, protocol);
+      String resolvedFile = resolveFileProtocol(fileParam);
+      String format = new IdentifyFile().identify(resolvedFile, protocol);
       dbgMsg("File identified as '" + format + "'");
       AlignmentI al = null;
       try
@@ -2051,8 +2149,8 @@ public class JalviewLite extends Applet implements
           else
           {
             param = st.nextToken();
-            Vector tmp = new Vector();
-            Vector tmp2 = new Vector();
+            List<SequenceI> tmp = new ArrayList<SequenceI>();
+            List<String> tmp2 = new ArrayList<String>();
 
             while (st.hasMoreTokens())
             {
@@ -2061,39 +2159,22 @@ public class JalviewLite extends Applet implements
               if (st2.countTokens() > 1)
               {
                 // This is the chain
-                tmp2.addElement(st2.nextToken());
+                tmp2.add(st2.nextToken());
                 seqstring = st2.nextToken();
               }
-              tmp.addElement(matcher == null ? (Sequence) alignFrame
+              tmp.add(matcher == null ? (Sequence) alignFrame
                       .getAlignViewport().getAlignment()
                       .findName(seqstring) : matcher.findIdMatch(seqstring));
             }
 
-            seqs = new SequenceI[tmp.size()];
-            tmp.copyInto(seqs);
+            seqs = tmp.toArray(new SequenceI[tmp.size()]);
             if (tmp2.size() == tmp.size())
             {
-              chains = new String[tmp2.size()];
-              tmp2.copyInto(chains);
+              chains = tmp2.toArray(new String[tmp2.size()]);
             }
           }
-          param = setProtocolState(param);
-
-          if (// !jmolAvailable
-          // &&
-          protocol == AppletFormatAdapter.CLASSLOADER && !useXtrnalSviewer)
-          {
-            // Re: JAL-357 : the bug isn't a problem if we are using an
-            // external viewer!
-            // TODO: verify this Re:
-            // https://mantis.lifesci.dundee.ac.uk/view.php?id=36605
-            // This exception preserves the current behaviour where, even if
-            // the local pdb file was identified in the class loader
-            protocol = AppletFormatAdapter.URL; // this is probably NOT
-            // CORRECT!
-            param = addProtocol(param); //
-          }
-
+          param = resolveFileProtocol(param);
+          // TODO check JAL-357 for files in a jar (CLASSLOADER)
           pdb.setFile(param);
 
           if (seqs != null)
@@ -2170,7 +2251,7 @@ public class JalviewLite extends Applet implements
       {
         try
         {
-          param = setProtocolState(param);
+          param = resolveFileProtocol(param);
           JPredFile predictions = new JPredFile(param, protocol);
           JnetAnnotationMaker.add_annotation(predictions,
                   alignFrame.viewport.getAlignment(), 0, false);
@@ -2205,7 +2286,7 @@ public class JalviewLite extends Applet implements
       String param = applet.getParameter("annotations");
       if (param != null)
       {
-        param = setProtocolState(param);
+        param = resolveFileProtocol(param);
 
         if (new AnnotationFile().annotateAlignmentView(alignFrame.viewport,
                 param, protocol))
@@ -2258,7 +2339,7 @@ public class JalviewLite extends Applet implements
       param = applet.getParameter("features");
       if (param != null)
       {
-        param = setProtocolState(param);
+        param = resolveFileProtocol(param);
 
         result = alignFrame.parseFeaturesFile(param, protocol);
       }
@@ -2327,7 +2408,7 @@ public class JalviewLite extends Applet implements
       {
         try
         {
-          treeFile = setProtocolState(treeFile);
+          treeFile = resolveFileProtocol(treeFile);
           NewickFile fin = new NewickFile(treeFile, protocol);
           fin.parse();
 
@@ -2352,114 +2433,29 @@ public class JalviewLite extends Applet implements
     /**
      * Discovers whether the given file is in the Applet Archive
      * 
-     * @param file
+     * @param f
      *          String
      * @return boolean
      */
-    boolean inArchive(String file)
+    boolean inArchive(String f)
     {
       // This might throw a security exception in certain browsers
       // Netscape Communicator for instance.
       try
       {
-        boolean rtn = (getClass().getResourceAsStream("/" + file) != null);
+        boolean rtn = (getClass().getResourceAsStream("/" + f) != null);
         if (debug)
         {
-          System.err.println("Resource '" + file + "' was "
-                  + (rtn ? "" : "not") + " located by classloader.");
+          System.err.println("Resource '" + f + "' was "
+                  + (rtn ? "" : "not ") + "located by classloader.");
         }
         return rtn;
       } catch (Exception ex)
       {
-        System.out.println("Exception checking resources: " + file + " "
-                + ex);
+        System.out.println("Exception checking resources: " + f + " " + ex);
         return false;
       }
     }
-
-    /**
-     * If the file is not already in URL format, tries to locate it by resolving
-     * as a URL.
-     * 
-     * @param f
-     * @return
-     */
-    String addProtocol(final String f)
-    {
-      if (f.indexOf("://") != -1)
-      {
-        // already has URL format
-        return f;
-      }
-
-      /*
-       * Try relative to document base
-       */
-      URL documentBase = getDocumentBase();
-      String url = applet.resolveUrlForLocalOrAbsolute(f, documentBase);
-      if (urlExists(url))
-      {
-        if (debug)
-        {
-          System.err.println("Prepended document base '" + documentBase
-                  + "' to make: '" + url + "'");
-        }
-        return url;
-      }
-
-      /*
-       * Try relative to codebase
-       */
-      URL codeBase = getCodeBase();
-      url = applet.resolveUrlForLocalOrAbsolute(f, codeBase);
-      if (urlExists(url))
-      {
-        if (debug)
-        {
-          System.err.println("Prepended codebase '" + codeBase
-                  + "' to make: '" + url + "'");
-        }
-        return url;
-      }
-
-      return f;
-    }
-
-    /**
-     * Returns true if an input stream can be opened on the specified URL, else
-     * false.
-     * 
-     * @param url
-     * @return
-     */
-    private boolean urlExists(String url)
-    {
-      InputStream is = null;
-      try
-      {
-        is = new URL(url).openStream();
-        if (is != null)
-        {
-          return true;
-        }
-      } catch (Exception x)
-      {
-        // ignore
-      } finally
-      {
-        if (is != null)
-        {
-          try
-          {
-            is.close();
-          } catch (IOException e)
-          {
-            // ignore
-          }
-        }
-      }
-      return false;
-    }
   }
 
   /**
@@ -2512,8 +2508,9 @@ public class JalviewLite extends Applet implements
    * @param separator
    * @return elements separated by separator
    */
-  public String[] separatorListToArray(String list, String separator)
+  public static String[] separatorListToArray(String list, String separator)
   {
+    // TODO use StringUtils version (slightly different...)
     int seplen = separator.length();
     if (list == null || list.equals("") || list.equals(separator))
     {
@@ -2579,8 +2576,9 @@ public class JalviewLite extends Applet implements
    * @param separator
    * @return concatenated string
    */
-  public String arrayToSeparatorList(String[] list, String separator)
+  public static String arrayToSeparatorList(String[] list, String separator)
   {
+    // TODO use StringUtils version
     StringBuffer v = new StringBuffer();
     if (list != null && list.length > 0)
     {
@@ -2616,6 +2614,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroups()
    */
+  @Override
   public String getFeatureGroups()
   {
     String lst = arrayToSeparatorList(getDefaultTargetFrame()
@@ -2630,6 +2629,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOn(jalview.appletgui.AlignFrame
    * )
    */
+  @Override
   public String getFeatureGroupsOn(AlignFrame alf)
   {
     String lst = arrayToSeparatorList(alf.getFeatureGroups());
@@ -2641,6 +2641,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfState(boolean)
    */
+  @Override
   public String getFeatureGroupsOfState(boolean visible)
   {
     return arrayToSeparatorList(getDefaultTargetFrame()
@@ -2654,6 +2655,7 @@ public class JalviewLite extends Applet implements
    * jalview.bin.JalviewLiteJsApi#getFeatureGroupsOfStateOn(jalview.appletgui
    * .AlignFrame, boolean)
    */
+  @Override
   public String getFeatureGroupsOfStateOn(AlignFrame alf, boolean visible)
   {
     return arrayToSeparatorList(alf.getFeatureGroupsOfState(visible));
@@ -2665,6 +2667,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupStateOn(jalview.appletgui.
    * AlignFrame, java.lang.String, boolean)
    */
+  @Override
   public void setFeatureGroupStateOn(final AlignFrame alf,
           final String groups, boolean state)
   {
@@ -2686,6 +2689,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#setFeatureGroupState(java.lang.String,
    * boolean)
    */
+  @Override
   public void setFeatureGroupState(String groups, boolean state)
   {
     setFeatureGroupStateOn(getDefaultTargetFrame(), groups, state);
@@ -2696,6 +2700,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#getSeparator()
    */
+  @Override
   public String getSeparator()
   {
     return separator;
@@ -2706,6 +2711,7 @@ public class JalviewLite extends Applet implements
    * 
    * @see jalview.bin.JalviewLiteJsApi#setSeparator(java.lang.String)
    */
+  @Override
   public void setSeparator(String separator)
   {
     if (separator == null || separator.length() < 1)
@@ -2750,6 +2756,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#addPdbFile(jalview.appletgui.AlignFrame,
    * java.lang.String, java.lang.String, java.lang.String)
    */
+  @Override
   public boolean addPdbFile(AlignFrame alFrame, String sequenceId,
           String pdbEntryString, String pdbFile)
   {
@@ -2766,6 +2773,7 @@ public class JalviewLite extends Applet implements
     return alignPdbStructures;
   }
 
+  @Override
   public void start()
   {
     // callInitCallback();
@@ -2799,6 +2807,7 @@ public class JalviewLite extends Applet implements
    * @see jalview.bin.JalviewLiteJsApi#getJsMessage(java.lang.String,
    * java.lang.String)
    */
+  @Override
   public String getJsMessage(String messageclass, String viewId)
   {
     Hashtable<String, String[]> msgset = jsmessages.get(messageclass);
@@ -2898,35 +2907,61 @@ public class JalviewLite extends Applet implements
    * form a complete URL given a path to a resource and a reference location on
    * the same server
    * 
-   * @param url
+   * @param targetPath
    *          - an absolute path on the same server as localref or a document
    *          located relative to localref
    * @param localref
    *          - a URL on the same server as url
    * @return a complete URL for the resource located by url
    */
-  private String resolveUrlForLocalOrAbsolute(String url, URL localref)
+  private String resolveUrlForLocalOrAbsolute(String targetPath,
+          URL localref)
   {
-    String codebase = localref.toString();
-    if (url.indexOf("/") == 0)
+    String resolvedPath = "";
+    if (targetPath.startsWith("/"))
     {
+      String codebase = localref.toString();
       String localfile = localref.getFile();
-      url = codebase.substring(0, codebase.length() - localfile.length())
-              + url;
+      resolvedPath = codebase.substring(0,
+              codebase.length() - localfile.length())
+              + targetPath;
+      return resolvedPath;
+    }
+
+    /*
+     * get URL path and strip off any trailing file e.g.
+     * www.jalview.org/examples/index.html#applets?a=b is trimmed to
+     * www.jalview.org/examples/
+     */
+    String urlPath = localref.toString();
+    String directoryPath = urlPath;
+    int lastSeparator = directoryPath.lastIndexOf("/");
+    if (lastSeparator > 0)
+    {
+      directoryPath = directoryPath.substring(0, lastSeparator + 1);
+    }
+
+    if (targetPath.startsWith("/"))
+    {
+      /*
+       * construct absolute URL to a file on the server - this is not allowed?
+       */
+      // String localfile = localref.getFile();
+      // resolvedPath = urlPath.substring(0,
+      // urlPath.length() - localfile.length())
+      // + targetPath;
+      resolvedPath = directoryPath + targetPath.substring(1);
     }
     else
     {
-      url = localref + url;
+      resolvedPath = directoryPath + targetPath;
     }
     if (debug)
     {
-      System.err.println("URL: " + localref.toString());
-      System.err.println("URL.getFile: " + localref.getFile());
-      System.err.println("URL.getPath: " + localref.getPath());
-      System.err.println("URL.getQuery: " + localref.getQuery());
-      System.err.println("returning " + url);
+      System.err.println("resolveUrlForLocalOrAbsolute returning "
+              + resolvedPath);
     }
-    return url;
+    return resolvedPath;
   }
 
   /**
@@ -2948,8 +2983,8 @@ public class JalviewLite extends Applet implements
         URL prepend;
         url = resolveUrlForLocalOrAbsolute(
                 url,
-                prepend = getDefaultParameter("resolvetocodebase", false) ? getDocumentBase()
-                        : getCodeBase());
+                prepend = getDefaultParameter("resolvetocodebase", false) ? getCodeBase()
+                        : getDocumentBase());
         if (debug)
         {
           System.err
diff --git a/src/jalview/bin/JalviewLiteURLRetrieve.java b/src/jalview/bin/JalviewLiteURLRetrieve.java
index 4167aa2..9b0c8a7 100644
--- a/src/jalview/bin/JalviewLiteURLRetrieve.java
+++ b/src/jalview/bin/JalviewLiteURLRetrieve.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -113,7 +113,7 @@ public class JalviewLiteURLRetrieve extends Applet
       String format = getParameter("format");
       if (format == null || format.length() == 0)
       {
-        format = new jalview.io.IdentifyFile().Identify(file, protocol);
+        format = new jalview.io.IdentifyFile().identify(file, protocol);
         System.out.println("Format is " + format);
       }
       else
diff --git a/src/jalview/commands/ChangeCaseCommand.java b/src/jalview/commands/ChangeCaseCommand.java
index b68b47c..648b275 100644
--- a/src/jalview/commands/ChangeCaseCommand.java
+++ b/src/jalview/commands/ChangeCaseCommand.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/commands/CommandI.java b/src/jalview/commands/CommandI.java
index 291dcde..cf80ccb 100644
--- a/src/jalview/commands/CommandI.java
+++ b/src/jalview/commands/CommandI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/commands/EditCommand.java b/src/jalview/commands/EditCommand.java
index 1cd6fcc..6c87595 100644
--- a/src/jalview/commands/EditCommand.java
+++ b/src/jalview/commands/EditCommand.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -1220,10 +1220,15 @@ public class EditCommand implements CommandI
         for (SequenceI seq : e.getSequences())
         {
           SequenceI ds = seq.getDatasetSequence();
-          SequenceI preEdit = result.get(ds);
-          if (preEdit == null)
+          // SequenceI preEdit = result.get(ds);
+          if (!result.containsKey(ds))
           {
-            preEdit = new Sequence("", seq.getSequenceAsString());
+            /*
+             * copy sequence including start/end (but don't use copy constructor
+             * as we don't need annotations)
+             */
+            SequenceI preEdit = new Sequence("", seq.getSequenceAsString(),
+                    seq.getStart(), seq.getEnd());
             preEdit.setDatasetSequence(ds);
             result.put(ds, preEdit);
           }
@@ -1236,10 +1241,10 @@ public class EditCommand implements CommandI
      * Work backwards through the edit list, deriving the sequences before each
      * was applied. The final result is the sequence set before any edits.
      */
-    Iterator<Edit> edits = new ReverseListIterator<Edit>(getEdits());
-    while (edits.hasNext())
+    Iterator<Edit> editList = new ReverseListIterator<Edit>(getEdits());
+    while (editList.hasNext())
     {
-      Edit oldEdit = edits.next();
+      Edit oldEdit = editList.next();
       Action action = oldEdit.getAction();
       int position = oldEdit.getPosition();
       int number = oldEdit.getNumber();
@@ -1250,7 +1255,8 @@ public class EditCommand implements CommandI
         SequenceI preEdit = result.get(ds);
         if (preEdit == null)
         {
-          preEdit = new Sequence("", seq.getSequenceAsString());
+          preEdit = new Sequence("", seq.getSequenceAsString(),
+                  seq.getStart(), seq.getEnd());
           preEdit.setDatasetSequence(ds);
           result.put(ds, preEdit);
         }
diff --git a/src/jalview/commands/OrderCommand.java b/src/jalview/commands/OrderCommand.java
index 393044d..ba97e30 100644
--- a/src/jalview/commands/OrderCommand.java
+++ b/src/jalview/commands/OrderCommand.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/commands/RemoveGapColCommand.java b/src/jalview/commands/RemoveGapColCommand.java
index c9d8b56..284a936 100644
--- a/src/jalview/commands/RemoveGapColCommand.java
+++ b/src/jalview/commands/RemoveGapColCommand.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/commands/RemoveGapsCommand.java b/src/jalview/commands/RemoveGapsCommand.java
index c81f04f..d66de5d 100644
--- a/src/jalview/commands/RemoveGapsCommand.java
+++ b/src/jalview/commands/RemoveGapsCommand.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/commands/SlideSequencesCommand.java b/src/jalview/commands/SlideSequencesCommand.java
index 153371a..289d306 100644
--- a/src/jalview/commands/SlideSequencesCommand.java
+++ b/src/jalview/commands/SlideSequencesCommand.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/commands/TrimRegionCommand.java b/src/jalview/commands/TrimRegionCommand.java
index 32b9847..7873055 100644
--- a/src/jalview/commands/TrimRegionCommand.java
+++ b/src/jalview/commands/TrimRegionCommand.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,39 +21,30 @@
 package jalview.commands;
 
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.ColumnSelection;
-import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
-import jalview.util.ShiftList;
-
-import java.util.List;
 
 public class TrimRegionCommand extends EditCommand
 {
-  public static String TRIM_LEFT = "TrimLeft";
-
-  public static String TRIM_RIGHT = "TrimRight";
-
-  public ColumnSelection colSel = null;
-
-  int[] start;
-
-  ShiftList shiftList;
-
-  SequenceGroup selectionGroup;
-
-  List<int[]> deletedHiddenColumns;
-
   int columnsDeleted;
 
-  public TrimRegionCommand(String description, String command,
-          SequenceI[] seqs, int column, AlignmentI al,
-          ColumnSelection colSel, SequenceGroup selectedRegion)
+  /**
+   * Constructs and performs a trim alignment command
+   * 
+   * @param description
+   *          (to show in Undo/Redo menu)
+   * @param trimLeft
+   *          if true trim to left of column, else to right
+   * @param seqs
+   *          the sequences to trim
+   * @param column
+   *          the alignment column (base 0) from which to trim
+   * @param al
+   */
+  public TrimRegionCommand(String description, boolean trimLeft,
+          SequenceI[] seqs, int column, AlignmentI al)
   {
     this.description = description;
-    this.selectionGroup = selectedRegion;
-    this.colSel = colSel;
-    if (command.equalsIgnoreCase(TRIM_LEFT))
+    if (trimLeft)
     {
       if (column == 0)
       {
@@ -64,109 +55,23 @@ public class TrimRegionCommand extends EditCommand
 
       setEdit(new Edit(Action.CUT, seqs, 0, column, al));
     }
-    else if (command.equalsIgnoreCase(TRIM_RIGHT))
+    else
     {
       int width = al.getWidth() - column - 1;
-      if (width < 2)
+      if (width < 1)
       {
         return;
       }
 
-      columnsDeleted = width - 1;
+      columnsDeleted = width;
 
       setEdit(new Edit(Action.CUT, seqs, column + 1, width, al));
     }
 
-    // We need to keep a record of the sequence start
-    // in order to restore the state after a redo
-    int i, isize = getEdit(0).seqs.length;
-    start = new int[isize];
-    for (i = 0; i < isize; i++)
-    {
-      start[i] = getEdit(0).seqs[i].getStart();
-    }
-
     performEdit(0, null);
   }
 
-  void cut(Edit command)
-  {
-    int column, j, jSize = command.seqs.length;
-    for (j = 0; j < jSize; j++)
-    {
-      if (command.position == 0)
-      {
-        // This is a TRIM_LEFT command
-        column = command.seqs[j].findPosition(command.number);
-        command.seqs[j].setStart(column);
-      }
-      else
-      {
-        // This is a TRIM_RIGHT command
-        column = command.seqs[j].findPosition(command.position) - 1;
-        command.seqs[j].setEnd(column);
-      }
-    }
-
-    super.cut(command, null);
-
-    if (command.position == 0)
-    {
-      deletedHiddenColumns = colSel.compensateForEdit(0, command.number);
-      if (selectionGroup != null)
-      {
-        selectionGroup.adjustForRemoveLeft(command.number);
-      }
-    }
-    else
-    {
-      deletedHiddenColumns = colSel.compensateForEdit(command.position,
-              command.number);
-      if (selectionGroup != null)
-      {
-        selectionGroup.adjustForRemoveRight(command.position);
-      }
-    }
-  }
-
-  void paste(Edit command)
-  {
-    super.paste(command, null);
-    int column, j, jSize = command.seqs.length;
-    for (j = 0; j < jSize; j++)
-    {
-      if (command.position == 0)
-      {
-        command.seqs[j].setStart(start[j]);
-      }
-      else
-      {
-        column = command.seqs[j].findPosition(command.number
-                + command.position) - 1;
-        command.seqs[j].setEnd(column);
-      }
-    }
-
-    if (command.position == 0)
-    {
-      colSel.compensateForEdit(0, -command.number);
-      if (selectionGroup != null)
-      {
-        selectionGroup.adjustForRemoveLeft(-command.number);
-      }
-    }
-
-    if (deletedHiddenColumns != null)
-    {
-      int[] region;
-      for (int i = 0; i < deletedHiddenColumns.size(); i++)
-      {
-        region = deletedHiddenColumns.get(i);
-        colSel.hideColumns(region[0], region[1]);
-      }
-    }
-  }
-
+  @Override
   public int getSize()
   {
     return columnsDeleted;
diff --git a/src/jalview/controller/AlignViewController.java b/src/jalview/controller/AlignViewController.java
index fd5a873..84135e5 100644
--- a/src/jalview/controller/AlignViewController.java
+++ b/src/jalview/controller/AlignViewController.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -37,7 +37,6 @@ import jalview.io.FeaturesFile;
 import jalview.util.MessageManager;
 
 import java.awt.Color;
-import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.List;
 
@@ -83,8 +82,7 @@ public class AlignViewController implements AlignViewControllerI
     SequenceGroup sg = viewport.getSelectionGroup();
     ColumnSelection cs = viewport.getColumnSelection();
     SequenceGroup[] gps = null;
-    if (sg != null
-            && (cs == null || cs.getSelected() == null || cs.size() == 0))
+    if (sg != null && (cs == null || cs.isEmpty()))
     {
       gps = jalview.analysis.Grouping.makeGroupsFrom(viewport
               .getSequenceSelection(), viewport.getAlignmentView(true)
@@ -171,193 +169,177 @@ public class AlignViewController implements AlignViewControllerI
     // JBPNote this routine could also mark rows, not just columns.
     // need a decent query structure to allow all types of feature searches
     BitSet bs = new BitSet();
-    int alw, alStart;
-    SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null ? viewport
-            .getAlignment() : viewport.getSelectionGroup());
-    alStart = sqcol.getStartRes();
-    alw = sqcol.getEndRes() + 1;
+    SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null || extendCurrent) ? viewport
+            .getAlignment() : viewport.getSelectionGroup();
+
+    int nseq = findColumnsWithFeature(featureType, sqcol, bs);
+
+    ColumnSelection cs = viewport.getColumnSelection();
+    if (cs == null)
+    {
+      cs = new ColumnSelection();
+    }
+
+    if (bs.cardinality() > 0 || invert)
+    {
+      boolean changed = cs.markColumns(bs, sqcol.getStartRes(),
+              sqcol.getEndRes(), invert, extendCurrent, toggle);
+      if (changed)
+      {
+        viewport.setColumnSelection(cs);
+        alignPanel.paintAlignment(true);
+        int columnCount = invert ? (sqcol.getEndRes() - sqcol.getStartRes() + 1)
+                - bs.cardinality()
+                : bs.cardinality();
+        avcg.setStatus(MessageManager.formatMessage(
+                "label.view_controller_toggled_marked",
+                new String[] {
+                    toggle ? MessageManager.getString("label.toggled")
+                            : MessageManager.getString("label.marked"),
+                    String.valueOf(columnCount),
+                    invert ? MessageManager
+                            .getString("label.not_containing")
+                            : MessageManager.getString("label.containing"),
+                    featureType, Integer.valueOf(nseq).toString() }));
+        return true;
+      }
+    }
+    else
+    {
+      avcg.setStatus(MessageManager.formatMessage(
+              "label.no_feature_of_type_found",
+              new String[] { featureType }));
+      if (!extendCurrent)
+      {
+        cs.clear();
+        alignPanel.paintAlignment(true);
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Sets a bit in the BitSet for each column (base 0) in the sequence
+   * collection which includes the specified feature type. Returns the number of
+   * sequences which have the feature in the selected range.
+   * 
+   * @param featureType
+   * @param sqcol
+   * @param bs
+   * @return
+   */
+  static int findColumnsWithFeature(String featureType,
+          SequenceCollectionI sqcol, BitSet bs)
+  {
+    final int startPosition = sqcol.getStartRes() + 1; // converted to base 1
+    final int endPosition = sqcol.getEndRes() + 1;
     List<SequenceI> seqs = sqcol.getSequences();
     int nseq = 0;
     for (SequenceI sq : seqs)
     {
-      int tfeat = 0;
+      boolean sequenceHasFeature = false;
       if (sq != null)
       {
-        SequenceFeature[] sf = sq.getSequenceFeatures();
-        if (sf != null)
+        SequenceFeature[] sfs = sq.getSequenceFeatures();
+        if (sfs != null)
         {
           int ist = sq.findIndex(sq.getStart());
           int iend = sq.findIndex(sq.getEnd());
-          if (iend < alStart || ist > alw)
+          if (iend < startPosition || ist > endPosition)
           {
             // sequence not in region
             continue;
           }
-          for (SequenceFeature sfpos : sf)
+          for (SequenceFeature sf : sfs)
           {
-            // future functionalty - featureType == null means mark columns
+            // future functionality - featureType == null means mark columns
             // containing all displayed features
-            if (sfpos != null && (featureType.equals(sfpos.getType())))
+            if (sf != null && (featureType.equals(sf.getType())))
             {
-              tfeat++;
               // optimisation - could consider 'spos,apos' like cursor argument
               // - findIndex wastes time by starting from first character and
               // counting
 
-              int i = sq.findIndex(sfpos.getBegin());
-              int j = sq.findIndex(sfpos.getEnd());
-              if (j < alStart || i > alw)
+              int sfStartCol = sq.findIndex(sf.getBegin());
+              int sfEndCol = sq.findIndex(sf.getEnd());
+
+              if (sf.isContactFeature())
+              {
+                /*
+                 * 'contact' feature - check for 'start' or 'end'
+                 * position within the selected region
+                 */
+                if (sfStartCol >= startPosition
+                        && sfStartCol <= endPosition)
+                {
+                  bs.set(sfStartCol - 1);
+                  sequenceHasFeature = true;
+                }
+                if (sfEndCol >= startPosition && sfEndCol <= endPosition)
+                {
+                  bs.set(sfEndCol - 1);
+                  sequenceHasFeature = true;
+                }
+                continue;
+              }
+
+              /*
+               * contiguous feature - select feature positions (if any) 
+               * within the selected region
+               */
+              if (sfStartCol > endPosition || sfEndCol < startPosition)
               {
                 // feature is outside selected region
                 continue;
               }
-              if (i < alStart)
+              sequenceHasFeature = true;
+              if (sfStartCol < startPosition)
               {
-                i = alStart;
+                sfStartCol = startPosition;
               }
-              if (i < ist)
+              if (sfStartCol < ist)
               {
-                i = ist;
+                sfStartCol = ist;
               }
-              if (j > alw)
+              if (sfEndCol > endPosition)
               {
-                j = alw;
+                sfEndCol = endPosition;
               }
-              for (; i <= j; i++)
+              for (; sfStartCol <= sfEndCol; sfStartCol++)
               {
-                bs.set(i - 1);
+                bs.set(sfStartCol - 1); // convert to base 0
               }
             }
           }
         }
 
-        if (tfeat > 0)
+        if (sequenceHasFeature)
         {
           nseq++;
         }
       }
     }
-    ColumnSelection cs = viewport.getColumnSelection();
-    if (bs.cardinality() > 0 || invert)
-    {
-      if (cs == null)
-      {
-        cs = new ColumnSelection();
-      }
-      else
-      {
-        if (!extendCurrent)
-        {
-          cs.clear();
-        }
-      }
-      if (invert)
-      {
-        // invert only in the currently selected sequence region
-        for (int i = bs.nextClearBit(alStart), ibs = bs.nextSetBit(alStart); i >= alStart
-                && i < (alw);)
-        {
-          if (ibs < 0 || i < ibs)
-          {
-            if (toggle && cs.contains(i))
-            {
-              cs.removeElement(i++);
-            }
-            else
-            {
-              cs.addElement(i++);
-            }
-          }
-          else
-          {
-            i = bs.nextClearBit(ibs);
-            ibs = bs.nextSetBit(i);
-          }
-        }
-      }
-      else
-      {
-        for (int i = bs.nextSetBit(alStart); i >= alStart; i = bs
-                .nextSetBit(i + 1))
-        {
-          if (toggle && cs.contains(i))
-          {
-            cs.removeElement(i);
-          }
-          else
-          {
-            cs.addElement(i);
-          }
-        }
-      }
-      viewport.setColumnSelection(cs);
-      alignPanel.paintAlignment(true);
-      avcg.setStatus(MessageManager.formatMessage(
-              "label.view_controller_toggled_marked",
-              new String[] {
-                  (toggle ? MessageManager.getString("label.toggled")
-                          : MessageManager.getString("label.marked")),
-                  (invert ? (Integer.valueOf((alw - alStart)
-                          - bs.cardinality()).toString()) : (Integer
-                          .valueOf(bs.cardinality()).toString())),
-                  featureType, Integer.valueOf(nseq).toString() }));
-      return true;
-    }
-    else
-    {
-      avcg.setStatus(MessageManager.formatMessage(
-              "label.no_feature_of_type_found",
-              new String[] { featureType }));
-      if (!extendCurrent && cs != null)
-      {
-        cs.clear();
-        alignPanel.paintAlignment(true);
-      }
-      return false;
-    }
+    return nseq;
   }
 
   @Override
-  public void sortAlignmentByFeatureDensity(String[] typ)
+  public void sortAlignmentByFeatureDensity(List<String> typ)
   {
     sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
   }
 
-  protected void sortBy(String[] typ, String methodText, final String method)
+  protected void sortBy(List<String> typ, String methodText,
+          final String method)
   {
     FeatureRenderer fr = alignPanel.getFeatureRenderer();
-    if (typ == null)
+    if (typ == null && fr != null)
     {
-      typ = fr == null ? null : fr.getDisplayedFeatureTypes();
+      typ = fr.getDisplayedFeatureTypes();
     }
-    String gps[] = null;
-    gps = fr == null ? null : fr.getDisplayedFeatureGroups();
-    if (typ != null)
+    List<String> gps = null;
+    if (fr != null)
     {
-      ArrayList types = new ArrayList();
-      for (int i = 0; i < typ.length; i++)
-      {
-        if (typ[i] != null)
-        {
-          types.add(typ[i]);
-        }
-        typ = new String[types.size()];
-        types.toArray(typ);
-      }
-    }
-    if (gps != null)
-    {
-      ArrayList grps = new ArrayList();
-
-      for (int i = 0; i < gps.length; i++)
-      {
-        if (gps[i] != null)
-        {
-          grps.add(gps[i]);
-        }
-      }
-      gps = new String[grps.size()];
-      grps.toArray(gps);
+      gps = fr.getDisplayedFeatureGroups();
     }
     AlignmentI al = viewport.getAlignment();
 
@@ -382,7 +364,7 @@ public class AlignViewController implements AlignViewControllerI
   }
 
   @Override
-  public void sortAlignmentByFeatureScore(String[] typ)
+  public void sortAlignmentByFeatureScore(List<String> typ)
   {
     sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
   }
@@ -394,7 +376,7 @@ public class AlignViewController implements AlignViewControllerI
     boolean featuresFile = false;
     try
     {
-      featuresFile = new FeaturesFile(file, protocol).parse(viewport
+      featuresFile = new FeaturesFile(false, file, protocol).parse(viewport
               .getAlignment().getDataset(), alignPanel.getFeatureRenderer()
               .getFeatureColours(), false, relaxedIdMatching);
     } catch (Exception ex)
@@ -420,4 +402,66 @@ public class AlignViewController implements AlignViewControllerI
     return featuresFile;
 
   }
+
+  @Override
+  public boolean markHighlightedColumns(boolean invert,
+          boolean extendCurrent, boolean toggle)
+  {
+    if (!viewport.hasSearchResults())
+    {
+      // do nothing if no selection exists
+      return false;
+    }
+    // JBPNote this routine could also mark rows, not just columns.
+    BitSet bs = new BitSet();
+    SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null || extendCurrent) ? viewport
+            .getAlignment() : viewport.getSelectionGroup();
+
+    // this could be a lambda... - the remains of the method is boilerplate,
+    // except for the different messages for reporting selection.
+    int nseq = viewport.getSearchResults().markColumns(sqcol, bs);
+
+    ColumnSelection cs = viewport.getColumnSelection();
+    if (cs == null)
+    {
+      cs = new ColumnSelection();
+    }
+
+    if (bs.cardinality() > 0 || invert)
+    {
+      boolean changed = cs.markColumns(bs, sqcol.getStartRes(),
+              sqcol.getEndRes(), invert, extendCurrent, toggle);
+      if (changed)
+      {
+        viewport.setColumnSelection(cs);
+        alignPanel.paintAlignment(true);
+        int columnCount = invert ? (sqcol.getEndRes() - sqcol.getStartRes() + 1)
+                - bs.cardinality()
+                : bs.cardinality();
+        avcg.setStatus(MessageManager.formatMessage(
+                "label.view_controller_toggled_marked",
+                new String[] {
+                    toggle ? MessageManager.getString("label.toggled")
+                            : MessageManager.getString("label.marked"),
+                    String.valueOf(columnCount),
+                    invert ? MessageManager
+                            .getString("label.not_containing")
+                            : MessageManager.getString("label.containing"),
+                    "Highlight", Integer.valueOf(nseq).toString() }));
+        return true;
+      }
+    }
+    else
+    {
+      avcg.setStatus(MessageManager
+              .formatMessage("No highlighted regions marked"));
+      if (!extendCurrent)
+      {
+        cs.clear();
+        alignPanel.paintAlignment(true);
+      }
+    }
+    return false;
+  }
+
 }
diff --git a/src/jalview/controller/FeatureSettingsController.java b/src/jalview/controller/FeatureSettingsController.java
index 99fc355..fa457df 100644
--- a/src/jalview/controller/FeatureSettingsController.java
+++ b/src/jalview/controller/FeatureSettingsController.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/controller/FeatureSettingsControllerGuiI.java b/src/jalview/controller/FeatureSettingsControllerGuiI.java
index 80c91fd..5dcee35 100644
--- a/src/jalview/controller/FeatureSettingsControllerGuiI.java
+++ b/src/jalview/controller/FeatureSettingsControllerGuiI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/ASequence.java b/src/jalview/datamodel/ASequence.java
index 3913afc..d24c033 100644
--- a/src/jalview/datamodel/ASequence.java
+++ b/src/jalview/datamodel/ASequence.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/ASequenceI.java b/src/jalview/datamodel/ASequenceI.java
index 005f3c7..175dde1 100644
--- a/src/jalview/datamodel/ASequenceI.java
+++ b/src/jalview/datamodel/ASequenceI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/AlignedCodon.java b/src/jalview/datamodel/AlignedCodon.java
index a9ca1e3..f416585 100644
--- a/src/jalview/datamodel/AlignedCodon.java
+++ b/src/jalview/datamodel/AlignedCodon.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -27,32 +27,38 @@ package jalview.datamodel;
  * 
  * Example: in "G-AT-C-GA" the aligned codons are (0, 2, 3) and (5, 7, 8).
  * 
- * JBPComment: Is this useful anywhere other than jalview.analysis.Dna ?
- * 
  * @author gmcarstairs
  *
  */
 public final class AlignedCodon
 {
+  // base 1 aligned sequence position (base 0)
   public final int pos1;
 
+  // base 2 aligned sequence position (base 0)
   public final int pos2;
 
+  // base 3 aligned sequence position (base 0)
   public final int pos3;
 
+  // peptide aligned sequence position (base 0)
+  public final int peptideCol;
+
+  // peptide coded for by this codon
   public final String product;
 
   public AlignedCodon(int i, int j, int k)
   {
-    this(i, j, k, null);
+    this(i, j, k, null, 0);
   }
 
-  public AlignedCodon(int i, int j, int k, String prod)
+  public AlignedCodon(int i, int j, int k, String prod, int prodCol)
   {
     pos1 = i;
     pos2 = j;
     pos3 = k;
     product = prod;
+    peptideCol = prodCol;
   }
 
   /**
diff --git a/src/jalview/datamodel/AlignedCodonFrame.java b/src/jalview/datamodel/AlignedCodonFrame.java
index 8a9cd52..d70ae9e 100644
--- a/src/jalview/datamodel/AlignedCodonFrame.java
+++ b/src/jalview/datamodel/AlignedCodonFrame.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -23,6 +23,7 @@ package jalview.datamodel;
 import jalview.util.MapList;
 import jalview.util.MappingUtils;
 
+import java.util.AbstractList;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -33,23 +34,88 @@ import java.util.List;
 public class AlignedCodonFrame
 {
 
-  /**
-   * tied array of na Sequence objects.
+  /*
+   * Data bean to hold mappings from one sequence to another
    */
-  private SequenceI[] dnaSeqs = null;
+  public class SequenceToSequenceMapping
+  {
+    private SequenceI fromSeq;
 
-  /**
-   * tied array of Mappings to protein sequence Objects and SequenceI[]
-   * aaSeqs=null; MapLists where each maps from the corresponding dnaSeqs
-   * element to corresponding aaSeqs element
-   */
-  private Mapping[] dnaToProt = null;
+    private Mapping mapping;
+
+    SequenceToSequenceMapping(SequenceI from, Mapping map)
+    {
+      this.fromSeq = from;
+      this.mapping = map;
+    }
+
+    /**
+     * Readable representation for debugging only, not guaranteed not to change
+     */
+    @Override
+    public String toString()
+    {
+      return String.format("From %s %s", fromSeq.getName(),
+              mapping.toString());
+    }
+
+    /**
+     * Returns a hashCode derived from the hashcodes of the mappings and fromSeq
+     * 
+     * @see SequenceToSequenceMapping#hashCode()
+     */
+    @Override
+    public int hashCode()
+    {
+      return (fromSeq == null ? 0 : fromSeq.hashCode() * 31)
+              + mapping.hashCode();
+    }
+
+    /**
+     * Answers true if the objects hold the same mapping between the same two
+     * sequences
+     * 
+     * @see Mapping#equals
+     */
+    @Override
+    public boolean equals(Object obj)
+    {
+      if (!(obj instanceof SequenceToSequenceMapping))
+      {
+        return false;
+      }
+      SequenceToSequenceMapping that = (SequenceToSequenceMapping) obj;
+      if (this.mapping == null)
+      {
+        return that.mapping == null;
+      }
+      // TODO: can simplify by asserting fromSeq is a dataset sequence
+      return (this.fromSeq == that.fromSeq || (this.fromSeq != null
+              && that.fromSeq != null
+              && this.fromSeq.getDatasetSequence() != null && this.fromSeq
+              .getDatasetSequence() == that.fromSeq.getDatasetSequence()))
+              && this.mapping.equals(that.mapping);
+    }
+
+    public SequenceI getFromSeq()
+    {
+      return fromSeq;
+    }
+
+    public Mapping getMapping()
+    {
+      return mapping;
+    }
+  }
+
+  private List<SequenceToSequenceMapping> mappings;
 
   /**
    * Constructor
    */
   public AlignedCodonFrame()
   {
+    mappings = new ArrayList<SequenceToSequenceMapping>();
   }
 
   /**
@@ -62,68 +128,93 @@ public class AlignedCodonFrame
    */
   public void addMap(SequenceI dnaseq, SequenceI aaseq, MapList map)
   {
-    int nlen = 1;
-    if (dnaSeqs != null)
-    {
-      nlen = dnaSeqs.length + 1;
-    }
-    SequenceI[] ndna = new SequenceI[nlen];
-    Mapping[] ndtp = new Mapping[nlen];
-    if (dnaSeqs != null)
-    {
-      System.arraycopy(dnaSeqs, 0, ndna, 0, dnaSeqs.length);
-      System.arraycopy(dnaToProt, 0, ndtp, 0, dnaSeqs.length);
-    }
-    dnaSeqs = ndna;
-    dnaToProt = ndtp;
-    nlen--;
-    dnaSeqs[nlen] = (dnaseq.getDatasetSequence() == null) ? dnaseq : dnaseq
-            .getDatasetSequence();
-    Mapping mp = new Mapping(map);
+    addMap(dnaseq, aaseq, map, null);
+  }
+
+  /**
+   * Adds a mapping between the dataset sequences for the associated dna and
+   * protein sequence objects
+   * 
+   * @param dnaseq
+   * @param aaseq
+   * @param map
+   * @param mapFromId
+   */
+  public void addMap(SequenceI dnaseq, SequenceI aaseq, MapList map,
+          String mapFromId)
+  {
     // JBPNote DEBUG! THIS !
     // dnaseq.transferAnnotation(aaseq, mp);
     // aaseq.transferAnnotation(dnaseq, new Mapping(map.getInverse()));
-    mp.to = (aaseq.getDatasetSequence() == null) ? aaseq : aaseq
+
+    SequenceI fromSeq = (dnaseq.getDatasetSequence() == null) ? dnaseq
+            : dnaseq.getDatasetSequence();
+    SequenceI toSeq = (aaseq.getDatasetSequence() == null) ? aaseq : aaseq
             .getDatasetSequence();
-    dnaToProt[nlen] = mp;
+
+    /*
+     * if we already hold a mapping between these sequences, just add to it 
+     * note that 'adding' a duplicate map does nothing; this protects against
+     * creating duplicate mappings in AlignedCodonFrame
+     */
+    for (SequenceToSequenceMapping ssm : mappings)
+    {
+      if (ssm.fromSeq == fromSeq && ssm.mapping.to == toSeq)
+      {
+        ssm.mapping.map.addMapList(map);
+        return;
+      }
+    }
+
+    /*
+     * otherwise, add a new sequence mapping
+     */
+    Mapping mp = new Mapping(toSeq, map);
+    mp.setMappedFromId(mapFromId);
+    mappings.add(new SequenceToSequenceMapping(fromSeq, mp));
   }
 
   public SequenceI[] getdnaSeqs()
   {
-    return dnaSeqs;
+    // TODO return a list instead?
+    // return dnaSeqs;
+    List<SequenceI> seqs = new ArrayList<SequenceI>();
+    for (SequenceToSequenceMapping ssm : mappings)
+    {
+      seqs.add(ssm.fromSeq);
+    }
+    return seqs.toArray(new SequenceI[seqs.size()]);
   }
 
   public SequenceI[] getAaSeqs()
   {
-    if (dnaToProt == null)
-    {
-      return null;
-    }
-    SequenceI[] sqs = new SequenceI[dnaToProt.length];
-    for (int sz = 0; sz < dnaToProt.length; sz++)
+    // TODO not used - remove?
+    List<SequenceI> seqs = new ArrayList<SequenceI>();
+    for (SequenceToSequenceMapping ssm : mappings)
     {
-      sqs[sz] = dnaToProt[sz].to;
+      seqs.add(ssm.mapping.to);
     }
-    return sqs;
+    return seqs.toArray(new SequenceI[seqs.size()]);
   }
 
   public MapList[] getdnaToProt()
   {
-    if (dnaToProt == null)
+    List<MapList> maps = new ArrayList<MapList>();
+    for (SequenceToSequenceMapping ssm : mappings)
     {
-      return null;
+      maps.add(ssm.mapping.map);
     }
-    MapList[] sqs = new MapList[dnaToProt.length];
-    for (int sz = 0; sz < dnaToProt.length; sz++)
-    {
-      sqs[sz] = dnaToProt[sz].map;
-    }
-    return sqs;
+    return maps.toArray(new MapList[maps.size()]);
   }
 
   public Mapping[] getProtMappings()
   {
-    return dnaToProt;
+    List<Mapping> maps = new ArrayList<Mapping>();
+    for (SequenceToSequenceMapping ssm : mappings)
+    {
+      maps.add(ssm.mapping);
+    }
+    return maps.toArray(new Mapping[maps.size()]);
   }
 
   /**
@@ -135,18 +226,14 @@ public class AlignedCodonFrame
    */
   public Mapping getMappingForSequence(SequenceI seq)
   {
-    if (dnaSeqs == null)
-    {
-      return null;
-    }
     SequenceI seqDs = seq.getDatasetSequence();
     seqDs = seqDs != null ? seqDs : seq;
 
-    for (int ds = 0; ds < dnaSeqs.length; ds++)
+    for (SequenceToSequenceMapping ssm : mappings)
     {
-      if (dnaSeqs[ds] == seqDs || dnaToProt[ds].to == seqDs)
+      if (ssm.fromSeq == seqDs || ssm.mapping.to == seqDs)
       {
-        return dnaToProt[ds];
+        return ssm.mapping;
       }
     }
     return null;
@@ -161,16 +248,12 @@ public class AlignedCodonFrame
    */
   public SequenceI getAaForDnaSeq(SequenceI dnaSeqRef)
   {
-    if (dnaSeqs == null)
-    {
-      return null;
-    }
     SequenceI dnads = dnaSeqRef.getDatasetSequence();
-    for (int ds = 0; ds < dnaSeqs.length; ds++)
+    for (SequenceToSequenceMapping ssm : mappings)
     {
-      if (dnaSeqs[ds] == dnaSeqRef || dnaSeqs[ds] == dnads)
+      if (ssm.fromSeq == dnaSeqRef || ssm.fromSeq == dnads)
       {
-        return dnaToProt[ds].to;
+        return ssm.mapping.to;
       }
     }
     return null;
@@ -183,16 +266,12 @@ public class AlignedCodonFrame
    */
   public SequenceI getDnaForAaSeq(SequenceI aaSeqRef)
   {
-    if (dnaToProt == null)
-    {
-      return null;
-    }
     SequenceI aads = aaSeqRef.getDatasetSequence();
-    for (int as = 0; as < dnaToProt.length; as++)
+    for (SequenceToSequenceMapping ssm : mappings)
     {
-      if (dnaToProt[as].to == aaSeqRef || dnaToProt[as].to == aads)
+      if (ssm.mapping.to == aaSeqRef || ssm.mapping.to == aads)
       {
-        return dnaSeqs[as];
+        return ssm.fromSeq;
       }
     }
     return null;
@@ -222,38 +301,32 @@ public class AlignedCodonFrame
    *          where highlighted regions go
    */
   public void markMappedRegion(SequenceI seq, int index,
-          SearchResults results)
+          SearchResultsI results)
   {
-    if (dnaToProt == null)
-    {
-      return;
-    }
     int[] codon;
     SequenceI ds = seq.getDatasetSequence();
-    for (int mi = 0; mi < dnaToProt.length; mi++)
+    for (SequenceToSequenceMapping ssm : mappings)
     {
-      if (dnaSeqs[mi] == seq || dnaSeqs[mi] == ds)
+      if (ssm.fromSeq == seq || ssm.fromSeq == ds)
       {
-        // DEBUG System.err.println("dna pos "+index);
-        codon = dnaToProt[mi].map.locateInTo(index, index);
+        codon = ssm.mapping.map.locateInTo(index, index);
         if (codon != null)
         {
           for (int i = 0; i < codon.length; i += 2)
           {
-            results.addResult(dnaToProt[mi].to, codon[i], codon[i + 1]);
+            results.addResult(ssm.mapping.to, codon[i], codon[i + 1]);
           }
         }
       }
-      else if (dnaToProt[mi].to == seq || dnaToProt[mi].to == ds)
+      else if (ssm.mapping.to == seq || ssm.mapping.to == ds)
       {
-        // DEBUG System.err.println("aa pos "+index);
         {
-          codon = dnaToProt[mi].map.locateInFrom(index, index);
+          codon = ssm.mapping.map.locateInFrom(index, index);
           if (codon != null)
           {
             for (int i = 0; i < codon.length; i += 2)
             {
-              results.addResult(dnaSeqs[mi], codon[i], codon[i + 1]);
+              results.addResult(ssm.fromSeq, codon[i], codon[i + 1]);
             }
           }
         }
@@ -282,20 +355,23 @@ public class AlignedCodonFrame
      * Adapted from markMappedRegion().
      */
     MapList ml = null;
-    for (int i = 0; i < dnaToProt.length; i++)
+    int i = 0;
+    for (SequenceToSequenceMapping ssm : mappings)
     {
-      if (dnaSeqs[i] == seq)
+      if (ssm.fromSeq == seq)
       {
         ml = getdnaToProt()[i];
         break;
       }
+      i++;
     }
     return ml == null ? null : ml.locateInFrom(aaPos, aaPos);
   }
 
   /**
    * Convenience method to return the first aligned sequence in the given
-   * alignment whose dataset has a mapping with the given dataset sequence.
+   * alignment whose dataset has a mapping with the given (aligned or dataset)
+   * sequence.
    * 
    * @param seq
    * 
@@ -307,18 +383,16 @@ public class AlignedCodonFrame
     /*
      * Search mapped protein ('to') sequences first.
      */
-    if (this.dnaToProt != null)
+    for (SequenceToSequenceMapping ssm : mappings)
     {
-      for (int i = 0; i < dnaToProt.length; i++)
+      if (ssm.fromSeq == seq || ssm.fromSeq == seq.getDatasetSequence())
       {
-        if (this.dnaSeqs[i] == seq)
+        for (SequenceI sourceAligned : al.getSequences())
         {
-          for (SequenceI sourceAligned : al.getSequences())
+          if (ssm.mapping.to == sourceAligned.getDatasetSequence()
+                  || ssm.mapping.to == sourceAligned)
           {
-            if (this.dnaToProt[i].to == sourceAligned.getDatasetSequence())
-            {
-              return sourceAligned;
-            }
+            return sourceAligned;
           }
         }
       }
@@ -327,18 +401,16 @@ public class AlignedCodonFrame
     /*
      * Then try mapped dna sequences.
      */
-    if (this.dnaToProt != null)
+    for (SequenceToSequenceMapping ssm : mappings)
     {
-      for (int i = 0; i < dnaToProt.length; i++)
+      if (ssm.mapping.to == seq
+              || ssm.mapping.to == seq.getDatasetSequence())
       {
-        if (this.dnaToProt[i].to == seq)
+        for (SequenceI sourceAligned : al.getSequences())
         {
-          for (SequenceI sourceAligned : al.getSequences())
+          if (ssm.fromSeq == sourceAligned.getDatasetSequence())
           {
-            if (this.dnaSeqs[i] == sourceAligned.getDatasetSequence())
-            {
-              return sourceAligned;
-            }
+            return sourceAligned;
           }
         }
       }
@@ -348,31 +420,45 @@ public class AlignedCodonFrame
   }
 
   /**
-   * Returns the region in the 'mappedFrom' sequence's dataset that is mapped to
-   * position 'pos' (base 1) in the 'mappedTo' sequence's dataset. The region is
-   * a set of start/end position pairs.
+   * Returns the region in the target sequence's dataset that is mapped to the
+   * given position (base 1) in the query sequence's dataset. The region is a
+   * set of start/end position pairs.
    * 
-   * @param mappedFrom
-   * @param mappedTo
-   * @param pos
+   * @param target
+   * @param query
+   * @param queryPos
    * @return
    */
-  public int[] getMappedRegion(SequenceI mappedFrom, SequenceI mappedTo,
-          int pos)
+  public int[] getMappedRegion(SequenceI target, SequenceI query,
+          int queryPos)
   {
-    SequenceI targetDs = mappedFrom.getDatasetSequence() == null ? mappedFrom
-            : mappedFrom.getDatasetSequence();
-    SequenceI sourceDs = mappedTo.getDatasetSequence() == null ? mappedTo
-            : mappedTo.getDatasetSequence();
-    if (targetDs == null || sourceDs == null || dnaToProt == null)
+    SequenceI targetDs = target.getDatasetSequence() == null ? target
+            : target.getDatasetSequence();
+    SequenceI queryDs = query.getDatasetSequence() == null ? query : query
+            .getDatasetSequence();
+    if (targetDs == null || queryDs == null /*|| dnaToProt == null*/)
     {
       return null;
     }
-    for (int mi = 0; mi < dnaToProt.length; mi++)
+    for (SequenceToSequenceMapping ssm : mappings)
     {
-      if (dnaSeqs[mi] == targetDs && dnaToProt[mi].to == sourceDs)
+      /*
+       * try mapping from target to query
+       */
+      if (ssm.fromSeq == targetDs && ssm.mapping.to == queryDs)
       {
-        int[] codon = dnaToProt[mi].map.locateInFrom(pos, pos);
+        int[] codon = ssm.mapping.map.locateInFrom(queryPos, queryPos);
+        if (codon != null)
+        {
+          return codon;
+        }
+      }
+      /*
+       * else try mapping from query to target
+       */
+      else if (ssm.fromSeq == queryDs && ssm.mapping.to == targetDs)
+      {
+        int[] codon = ssm.mapping.map.locateInTo(queryPos, queryPos);
         if (codon != null)
         {
           return codon;
@@ -383,8 +469,10 @@ public class AlignedCodonFrame
   }
 
   /**
-   * Returns the DNA codon for the given position (base 1) in a mapped protein
-   * sequence, or null if no mapping is found.
+   * Returns the mapped DNA codons for the given position in a protein sequence,
+   * or null if no mapping is found. Returns a list of (e.g.) ['g', 'c', 't']
+   * codons. There may be more than one codon mapped to the protein if (for
+   * example), there are mappings to cDNA variants.
    * 
    * @param protein
    *          the peptide dataset sequence
@@ -392,63 +480,57 @@ public class AlignedCodonFrame
    *          residue position (base 1) in the peptide sequence
    * @return
    */
-  public char[] getMappedCodon(SequenceI protein, int aaPos)
+  public List<char[]> getMappedCodons(SequenceI protein, int aaPos)
   {
-    if (dnaToProt == null)
-    {
-      return null;
-    }
     MapList ml = null;
-    char[] dnaSeq = null;
-    for (int i = 0; i < dnaToProt.length; i++)
+    SequenceI dnaSeq = null;
+    List<char[]> result = new ArrayList<char[]>();
+
+    for (SequenceToSequenceMapping ssm : mappings)
     {
-      if (dnaToProt[i].to == protein)
+      if (ssm.mapping.to == protein
+              && ssm.mapping.getMap().getFromRatio() == 3)
       {
-        ml = getdnaToProt()[i];
-        dnaSeq = dnaSeqs[i].getSequence();
-        break;
+        ml = ssm.mapping.map;
+        dnaSeq = ssm.fromSeq;
+
+        int[] codonPos = ml.locateInFrom(aaPos, aaPos);
+        if (codonPos == null)
+        {
+          return null;
+        }
+
+        /*
+         * Read off the mapped nucleotides (converting to position base 0)
+         */
+        codonPos = MappingUtils.flattenRanges(codonPos);
+        char[] dna = dnaSeq.getSequence();
+        int start = dnaSeq.getStart();
+        result.add(new char[] { dna[codonPos[0] - start],
+            dna[codonPos[1] - start], dna[codonPos[2] - start] });
       }
     }
-    if (ml == null)
-    {
-      return null;
-    }
-    int[] codonPos = ml.locateInFrom(aaPos, aaPos);
-    if (codonPos == null)
-    {
-      return null;
-    }
-
-    /*
-     * Read off the mapped nucleotides (converting to position base 0)
-     */
-    codonPos = MappingUtils.flattenRanges(codonPos);
-    return new char[] { dnaSeq[codonPos[0] - 1], dnaSeq[codonPos[1] - 1],
-        dnaSeq[codonPos[2] - 1] };
+    return result.isEmpty() ? null : result;
   }
 
   /**
-   * Returns any mappings found which are to (or from) the given sequence, and
-   * to distinct sequences.
+   * Returns any mappings found which are from the given sequence, and to
+   * distinct sequences.
    * 
    * @param seq
    * @return
    */
-  public List<Mapping> getMappingsForSequence(SequenceI seq)
+  public List<Mapping> getMappingsFromSequence(SequenceI seq)
   {
     List<Mapping> result = new ArrayList<Mapping>();
-    if (dnaSeqs == null)
-    {
-      return result;
-    }
     List<SequenceI> related = new ArrayList<SequenceI>();
     SequenceI seqDs = seq.getDatasetSequence();
     seqDs = seqDs != null ? seqDs : seq;
 
-    for (int ds = 0; ds < dnaSeqs.length; ds++)
+    for (SequenceToSequenceMapping ssm : mappings)
     {
-      final Mapping mapping = dnaToProt[ds];
-      if (dnaSeqs[ds] == seqDs || mapping.to == seqDs)
+      final Mapping mapping = ssm.mapping;
+      if (ssm.fromSeq == seqDs)
       {
         if (!related.contains(mapping.to))
         {
@@ -459,4 +541,243 @@ public class AlignedCodonFrame
     }
     return result;
   }
+
+  /**
+   * Test whether the given sequence is substitutable for one or more dummy
+   * sequences in this mapping
+   * 
+   * @param map
+   * @param seq
+   * @return
+   */
+  public boolean isRealisableWith(SequenceI seq)
+  {
+    return realiseWith(seq, false) > 0;
+  }
+
+  /**
+   * Replace any matchable mapped dummy sequences with the given real one.
+   * Returns the count of sequence mappings instantiated.
+   * 
+   * @param seq
+   * @return
+   */
+  public int realiseWith(SequenceI seq)
+  {
+    return realiseWith(seq, true);
+  }
+
+  /**
+   * Returns the number of mapped dummy sequences that could be replaced with
+   * the given real sequence.
+   * 
+   * @param seq
+   *          a dataset sequence
+   * @param doUpdate
+   *          if true, performs replacements, else only counts
+   * @return
+   */
+  protected int realiseWith(SequenceI seq, boolean doUpdate)
+  {
+    SequenceI ds = seq.getDatasetSequence() != null ? seq
+            .getDatasetSequence() : seq;
+    int count = 0;
+
+    /*
+     * check for replaceable DNA ('map from') sequences
+     */
+    for (SequenceToSequenceMapping ssm : mappings)
+    {
+      SequenceI dna = ssm.fromSeq;
+      if (dna instanceof SequenceDummy
+              && dna.getName().equals(ds.getName()))
+      {
+        Mapping mapping = ssm.mapping;
+        int mapStart = mapping.getMap().getFromLowest();
+        int mapEnd = mapping.getMap().getFromHighest();
+        boolean mappable = couldRealiseSequence(dna, ds, mapStart, mapEnd);
+        if (mappable)
+        {
+          count++;
+          if (doUpdate)
+          {
+            // TODO: new method ? ds.realise(dna);
+            // might want to copy database refs as well
+            ds.setSequenceFeatures(dna.getSequenceFeatures());
+            // dnaSeqs[i] = ds;
+            ssm.fromSeq = ds;
+            System.out.println("Realised mapped sequence " + ds.getName());
+          }
+        }
+      }
+
+      /*
+       * check for replaceable protein ('map to') sequences
+       */
+      Mapping mapping = ssm.mapping;
+      SequenceI prot = mapping.getTo();
+      int mapStart = mapping.getMap().getToLowest();
+      int mapEnd = mapping.getMap().getToHighest();
+      boolean mappable = couldRealiseSequence(prot, ds, mapStart, mapEnd);
+      if (mappable)
+      {
+        count++;
+        if (doUpdate)
+        {
+          // TODO: new method ? ds.realise(dna);
+          // might want to copy database refs as well
+          ds.setSequenceFeatures(dna.getSequenceFeatures());
+          ssm.mapping.setTo(ds);
+        }
+      }
+    }
+    return count;
+  }
+
+  /**
+   * Helper method to test whether a 'real' sequence could replace a 'dummy'
+   * sequence in the map. The criteria are that they have the same name, and
+   * that the mapped region overlaps the candidate sequence.
+   * 
+   * @param existing
+   * @param replacement
+   * @param mapStart
+   * @param mapEnd
+   * @return
+   */
+  protected static boolean couldRealiseSequence(SequenceI existing,
+          SequenceI replacement, int mapStart, int mapEnd)
+  {
+    if (existing instanceof SequenceDummy
+            && !(replacement instanceof SequenceDummy)
+            && existing.getName().equals(replacement.getName()))
+    {
+      int start = replacement.getStart();
+      int end = replacement.getEnd();
+      boolean mappingOverlapsSequence = (mapStart >= start && mapStart <= end)
+              || (mapEnd >= start && mapEnd <= end);
+      if (mappingOverlapsSequence)
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Change any mapping to the given sequence to be to its dataset sequence
+   * instead. For use when mappings are created before their referenced
+   * sequences are instantiated, for example when parsing GFF data.
+   * 
+   * @param seq
+   */
+  public void updateToDataset(SequenceI seq)
+  {
+    if (seq == null || seq.getDatasetSequence() == null)
+    {
+      return;
+    }
+    SequenceI ds = seq.getDatasetSequence();
+
+    for (SequenceToSequenceMapping ssm : mappings)
+    /*
+     * 'from' sequences
+     */
+    {
+      if (ssm.fromSeq == seq)
+      {
+        ssm.fromSeq = ds;
+      }
+
+      /*
+       * 'to' sequences
+       */
+      if (ssm.mapping.to == seq)
+      {
+        ssm.mapping.to = ds;
+      }
+    }
+  }
+
+  /**
+   * Answers true if this object contains no mappings
+   * 
+   * @return
+   */
+  public boolean isEmpty()
+  {
+    return mappings.isEmpty();
+  }
+
+  /**
+   * Method for debug / inspection purposes only, may change in future
+   */
+  @Override
+  public String toString()
+  {
+    return mappings == null ? "null" : mappings.toString();
+  }
+
+  /**
+   * Returns the first mapping found that is between 'fromSeq' and 'toSeq', or
+   * null if none found
+   * 
+   * @param fromSeq
+   *          aligned or dataset sequence
+   * @param toSeq
+   *          aligned or dataset sequence
+   * @return
+   */
+  public Mapping getMappingBetween(SequenceI fromSeq, SequenceI toSeq)
+  {
+    SequenceI dssFrom = fromSeq.getDatasetSequence() == null ? fromSeq
+            : fromSeq.getDatasetSequence();
+    SequenceI dssTo = toSeq.getDatasetSequence() == null ? toSeq : toSeq
+            .getDatasetSequence();
+
+    for (SequenceToSequenceMapping mapping : mappings)
+    {
+      SequenceI from = mapping.fromSeq;
+      SequenceI to = mapping.mapping.to;
+      if ((from == dssFrom && to == dssTo)
+              || (from == dssTo && to == dssFrom))
+      {
+        return mapping.mapping;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Returns a hashcode derived from the list of sequence mappings
+   * 
+   * @see SequenceToSequenceMapping#hashCode()
+   * @see AbstractList#hashCode()
+   */
+  @Override
+  public int hashCode()
+  {
+    return this.mappings.hashCode();
+  }
+
+  /**
+   * Two AlignedCodonFrame objects are equal if they hold the same ordered list
+   * of mappings
+   * 
+   * @see SequenceToSequenceMapping#
+   */
+  @Override
+  public boolean equals(Object obj)
+  {
+    if (!(obj instanceof AlignedCodonFrame))
+    {
+      return false;
+    }
+    return this.mappings.equals(((AlignedCodonFrame) obj).mappings);
+  }
+
+  public List<SequenceToSequenceMapping> getMappings()
+  {
+    return mappings;
+  }
 }
diff --git a/src/jalview/datamodel/Alignment.java b/src/jalview/datamodel/Alignment.java
index 75b18d9..0d6da83 100644
--- a/src/jalview/datamodel/Alignment.java
+++ b/src/jalview/datamodel/Alignment.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,14 +21,17 @@
 package jalview.datamodel;
 
 import jalview.analysis.AlignmentUtils;
+import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
 import jalview.io.FastaFile;
+import jalview.util.Comparison;
+import jalview.util.LinkedIdentityHashSet;
 import jalview.util.MessageManager;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -43,12 +46,11 @@ import java.util.Vector;
  */
 public class Alignment implements AlignmentI
 {
-  protected Alignment dataset;
+  private Alignment dataset;
 
   protected List<SequenceI> sequences;
 
-  protected List<SequenceGroup> groups = java.util.Collections
-          .synchronizedList(new ArrayList<SequenceGroup>());
+  protected List<SequenceGroup> groups;
 
   protected char gapCharacter = '-';
 
@@ -60,20 +62,21 @@ public class Alignment implements AlignmentI
 
   public boolean hasRNAStructure = false;
 
-  /** DOCUMENT ME!! */
   public AlignmentAnnotation[] annotations;
 
-  HiddenSequences hiddenSequences = new HiddenSequences(this);
+  HiddenSequences hiddenSequences;
 
   public Hashtable alignmentProperties;
 
-  private Set<AlignedCodonFrame> codonFrameList = new LinkedHashSet<AlignedCodonFrame>();
+  private List<AlignedCodonFrame> codonFrameList;
 
   private void initAlignment(SequenceI[] seqs)
   {
-    int i = 0;
+    groups = Collections.synchronizedList(new ArrayList<SequenceGroup>());
+    hiddenSequences = new HiddenSequences(this);
+    codonFrameList = new ArrayList<AlignedCodonFrame>();
 
-    if (jalview.util.Comparison.isNucleotide(seqs))
+    if (Comparison.isNucleotide(seqs))
     {
       type = NUCLEOTIDE;
     }
@@ -82,10 +85,9 @@ public class Alignment implements AlignmentI
       type = PROTEIN;
     }
 
-    sequences = java.util.Collections
-            .synchronizedList(new ArrayList<SequenceI>());
+    sequences = Collections.synchronizedList(new ArrayList<SequenceI>());
 
-    for (i = 0; i < seqs.length; i++)
+    for (int i = 0; i < seqs.length; i++)
     {
       sequences.add(seqs[i]);
     }
@@ -104,13 +106,15 @@ public class Alignment implements AlignmentI
       seqs[i] = new Sequence(seqs[i]);
     }
 
+    initAlignment(seqs);
+
     /*
-     * Share the same dataset sequence mappings (if any). TODO: find a better
-     * place for these to live (alignment dataset?).
+     * Share the same dataset sequence mappings (if any). 
      */
-    this.codonFrameList = ((Alignment) al).codonFrameList;
-
-    initAlignment(seqs);
+    if (dataset == null && al.getDataset() == null)
+    {
+      this.setCodonFrames(al.getCodonFrames());
+    }
   }
 
   /**
@@ -223,18 +227,21 @@ public class Alignment implements AlignmentI
   {
     if (dataset != null)
     {
+
       // maintain dataset integrity
-      if (snew.getDatasetSequence() != null)
-      {
-        getDataset().addSequence(snew.getDatasetSequence());
-      }
-      else
+      SequenceI dsseq = snew.getDatasetSequence();
+      if (dsseq == null)
       {
         // derive new sequence
         SequenceI adding = snew.deriveSequence();
-        getDataset().addSequence(adding.getDatasetSequence());
         snew = adding;
+        dsseq = snew.getDatasetSequence();
+      }
+      if (getDataset().findIndex(dsseq) == -1)
+      {
+        getDataset().addSequence(dsseq);
       }
+
     }
     if (sequences == null)
     {
@@ -253,18 +260,22 @@ public class Alignment implements AlignmentI
     }
   }
 
-  /**
-   * Adds a sequence to the alignment. Recalculates maxLength and size.
-   * 
-   * @param snew
-   */
   @Override
-  public void setSequenceAt(int i, SequenceI snew)
+  public SequenceI replaceSequenceAt(int i, SequenceI snew)
   {
     synchronized (sequences)
     {
-      deleteSequence(i);
-      sequences.set(i, snew);
+      if (sequences.size() > i)
+      {
+        return sequences.set(i, snew);
+
+      }
+      else
+      {
+        sequences.add(snew);
+        hiddenSequences.adjustHeightSequenceAdded();
+      }
+      return null;
     }
   }
 
@@ -280,13 +291,23 @@ public class Alignment implements AlignmentI
   }
 
   @Override
-  public void finalize()
+  public void finalize() throws Throwable
   {
     if (getDataset() != null)
     {
       getDataset().removeAlignmentRef();
     }
 
+    nullReferences();
+    super.finalize();
+  }
+
+  /**
+   * Defensively nulls out references in case this object is not garbage
+   * collected
+   */
+  void nullReferences()
+  {
     dataset = null;
     sequences = null;
     groups = null;
@@ -295,14 +316,16 @@ public class Alignment implements AlignmentI
   }
 
   /**
-   * decrement the alignmentRefs counter by one and call finalize if it goes to
-   * zero.
+   * decrement the alignmentRefs counter by one and null references if it goes
+   * to zero.
+   * 
+   * @throws Throwable
    */
-  private void removeAlignmentRef()
+  private void removeAlignmentRef() throws Throwable
   {
     if (--alignmentRefs == 0)
     {
-      finalize();
+      nullReferences();
     }
   }
 
@@ -638,7 +661,7 @@ public class Alignment implements AlignmentI
    * jalview.datamodel.AlignmentI#findIndex(jalview.datamodel.SearchResults)
    */
   @Override
-  public int findIndex(SearchResults results)
+  public int findIndex(SearchResultsI results)
   {
     int i = 0;
 
@@ -987,33 +1010,20 @@ public class Alignment implements AlignmentI
   }
 
   @Override
-  public void setDataset(Alignment data)
+  public void setDataset(AlignmentI data)
   {
     if (dataset == null && data == null)
     {
-      // Create a new dataset for this alignment.
-      // Can only be done once, if dataset is not null
-      // This will not be performed
-      SequenceI[] seqs = new SequenceI[getHeight()];
-      SequenceI currentSeq;
-      for (int i = 0; i < getHeight(); i++)
-      {
-        currentSeq = getSequenceAt(i);
-        if (currentSeq.getDatasetSequence() != null)
-        {
-          seqs[i] = currentSeq.getDatasetSequence();
-        }
-        else
-        {
-          seqs[i] = currentSeq.createDatasetSequence();
-        }
-      }
-
-      dataset = new Alignment(seqs);
+      createDatasetAlignment();
     }
     else if (dataset == null && data != null)
     {
-      dataset = data;
+      if (!(data instanceof Alignment))
+      {
+        throw new Error(
+                "Implementation Error: jalview.datamodel.Alignment does not yet support other implementations of AlignmentI as its dataset reference");
+      }
+      dataset = (Alignment) data;
       for (int i = 0; i < getHeight(); i++)
       {
         SequenceI currentSeq = getSequenceAt(i);
@@ -1040,6 +1050,111 @@ public class Alignment implements AlignmentI
   }
 
   /**
+   * add dataset sequences to seq for currentSeq and any sequences it references
+   */
+  private void resolveAndAddDatasetSeq(SequenceI currentSeq,
+          Set<SequenceI> seqs, boolean createDatasetSequence)
+  {
+    SequenceI alignedSeq = currentSeq;
+    if (currentSeq.getDatasetSequence() != null)
+    {
+      currentSeq = currentSeq.getDatasetSequence();
+    }
+    else
+    {
+      if (createDatasetSequence)
+      {
+        currentSeq = currentSeq.createDatasetSequence();
+      }
+    }
+    if (seqs.contains(currentSeq))
+    {
+      return;
+    }
+    List<SequenceI> toProcess = new ArrayList<SequenceI>();
+    toProcess.add(currentSeq);
+    while (toProcess.size() > 0)
+    {
+      // use a queue ?
+      SequenceI curDs = toProcess.remove(0);
+      if (seqs.contains(curDs))
+      {
+        continue;
+      }
+      seqs.add(curDs);
+      // iterate over database references, making sure we add forward referenced
+      // sequences
+      if (curDs.getDBRefs() != null)
+      {
+        for (DBRefEntry dbr : curDs.getDBRefs())
+        {
+          if (dbr.getMap() != null && dbr.getMap().getTo() != null)
+          {
+            if (dbr.getMap().getTo() == alignedSeq)
+            {
+              /*
+               * update mapping to be to the newly created dataset sequence
+               */
+              dbr.getMap().setTo(currentSeq);
+            }
+            if (dbr.getMap().getTo().getDatasetSequence() != null)
+            {
+              throw new Error(
+                      "Implementation error: Map.getTo() for dbref " + dbr
+                              + " from " + curDs.getName()
+                              + " is not a dataset sequence.");
+            }
+            // we recurse to add all forward references to dataset sequences via
+            // DBRefs/etc
+            toProcess.add(dbr.getMap().getTo());
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Creates a new dataset for this alignment. Can only be done once - if
+   * dataset is not null this will not be performed.
+   */
+  public void createDatasetAlignment()
+  {
+    if (dataset != null)
+    {
+      return;
+    }
+    // try to avoid using SequenceI.equals at this stage, it will be expensive
+    Set<SequenceI> seqs = new LinkedIdentityHashSet<SequenceI>();
+
+    for (int i = 0; i < getHeight(); i++)
+    {
+      SequenceI currentSeq = getSequenceAt(i);
+      resolveAndAddDatasetSeq(currentSeq, seqs, true);
+    }
+
+    // verify all mappings are in dataset
+    for (AlignedCodonFrame cf : codonFrameList)
+    {
+      for (SequenceToSequenceMapping ssm : cf.getMappings())
+      {
+        if (!seqs.contains(ssm.getFromSeq()))
+        {
+          resolveAndAddDatasetSeq(ssm.getFromSeq(), seqs, false);
+        }
+        if (!seqs.contains(ssm.getMapping().getTo()))
+        {
+          resolveAndAddDatasetSeq(ssm.getMapping().getTo(), seqs, false);
+        }
+      }
+    }
+    // finally construct dataset
+    dataset = new Alignment(seqs.toArray(new SequenceI[seqs.size()]));
+    // move mappings to the dataset alignment
+    dataset.codonFrameList = this.codonFrameList;
+    this.codonFrameList = null;
+  }
+
+  /**
    * reference count for number of alignments referencing this one.
    */
   int alignmentRefs = 0;
@@ -1261,19 +1376,17 @@ public class Alignment implements AlignmentI
     return alignmentProperties;
   }
 
-  /*
-   * (non-Javadoc)
-   * 
-   * @see
-   * jalview.datamodel.AlignmentI#addCodonFrame(jalview.datamodel.AlignedCodonFrame
-   * )
+  /**
+   * Adds the given mapping to the stored set. Note this may be held on the
+   * dataset alignment.
    */
   @Override
   public void addCodonFrame(AlignedCodonFrame codons)
   {
-    if (codons != null)
+    List<AlignedCodonFrame> acfs = getCodonFrames();
+    if (codons != null && acfs != null && !acfs.contains(codons))
     {
-      codonFrameList.add(codons);
+      acfs.add(codons);
     }
   }
 
@@ -1291,7 +1404,7 @@ public class Alignment implements AlignmentI
       return null;
     }
     List<AlignedCodonFrame> cframes = new ArrayList<AlignedCodonFrame>();
-    for (AlignedCodonFrame acf : codonFrameList)
+    for (AlignedCodonFrame acf : getCodonFrames())
     {
       if (acf.involvesSequence(seq))
       {
@@ -1302,52 +1415,60 @@ public class Alignment implements AlignmentI
   }
 
   /**
-   * Sets the codon frame mappings (replacing any existing mappings).
+   * Sets the codon frame mappings (replacing any existing mappings). Note the
+   * mappings are set on the dataset alignment instead if there is one.
    * 
    * @see jalview.datamodel.AlignmentI#setCodonFrames()
    */
   @Override
-  public void setCodonFrames(Set<AlignedCodonFrame> acfs)
+  public void setCodonFrames(List<AlignedCodonFrame> acfs)
   {
-    this.codonFrameList = acfs;
+    if (dataset != null)
+    {
+      dataset.setCodonFrames(acfs);
+    }
+    else
+    {
+      this.codonFrameList = acfs;
+    }
   }
 
   /**
    * Returns the set of codon frame mappings. Any changes to the returned set
-   * will affect the alignment.
+   * will affect the alignment. The mappings are held on (and read from) the
+   * dataset alignment if there is one.
    * 
    * @see jalview.datamodel.AlignmentI#getCodonFrames()
    */
   @Override
-  public Set<AlignedCodonFrame> getCodonFrames()
+  public List<AlignedCodonFrame> getCodonFrames()
   {
-    return codonFrameList;
+    // TODO: Fix this method to fix failing AlignedCodonFrame tests
+    // this behaviour is currently incorrect. method should return codon frames
+    // for just the alignment,
+    // selected from dataset
+    return dataset != null ? dataset.getCodonFrames() : codonFrameList;
   }
 
-  /*
-   * (non-Javadoc)
-   * 
-   * @seejalview.datamodel.AlignmentI#removeCodonFrame(jalview.datamodel.
-   * AlignedCodonFrame)
+  /**
+   * Removes the given mapping from the stored set. Note that the mappings are
+   * held on the dataset alignment if there is one.
    */
   @Override
   public boolean removeCodonFrame(AlignedCodonFrame codons)
   {
-    if (codons == null || codonFrameList == null)
+    List<AlignedCodonFrame> acfs = getCodonFrames();
+    if (codons == null || acfs == null)
     {
       return false;
     }
-    return codonFrameList.remove(codons);
+    return acfs.remove(codons);
   }
 
   @Override
   public void append(AlignmentI toappend)
   {
-    if (toappend == this)
-    {
-      System.err.println("Self append may cause a deadlock.");
-    }
-    // TODO test this method for a future 2.5 release
+    // TODO JAL-1270 needs test coverage
     // currently tested for use in jalview.gui.SequenceFetcher
     boolean samegap = toappend.getGapCharacter() == getGapCharacter();
     char oldc = toappend.getGapCharacter();
@@ -1358,6 +1479,8 @@ public class Alignment implements AlignmentI
             .getFullAlignment().getSequences() : toappend.getSequences();
     if (sqs != null)
     {
+      // avoid self append deadlock by
+      List<SequenceI> toappendsq = new ArrayList<SequenceI>();
       synchronized (sqs)
       {
         for (SequenceI addedsq : sqs)
@@ -1373,9 +1496,13 @@ public class Alignment implements AlignmentI
               }
             }
           }
-          addSequence(addedsq);
+          toappendsq.add(addedsq);
         }
       }
+      for (SequenceI addedsq : toappendsq)
+      {
+        addSequence(addedsq);
+      }
     }
     AlignmentAnnotation[] alan = toappend.getAlignmentAnnotation();
     for (int a = 0; alan != null && a < alan.length; a++)
@@ -1383,7 +1510,8 @@ public class Alignment implements AlignmentI
       addAnnotation(alan[a]);
     }
 
-    this.codonFrameList.addAll(toappend.getCodonFrames());
+    // use add method
+    getCodonFrames().addAll(toappend.getCodonFrames());
 
     List<SequenceGroup> sg = toappend.getGroups();
     if (sg != null)
@@ -1595,6 +1723,7 @@ public class Alignment implements AlignmentI
    * 
    * @return the representative sequence for this group
    */
+  @Override
   public SequenceI getSeqrep()
   {
     return seqrep;
@@ -1607,6 +1736,7 @@ public class Alignment implements AlignmentI
    * @param seqrep
    *          the seqrep to set (null means no sequence representative)
    */
+  @Override
   public void setSeqrep(SequenceI seqrep)
   {
     this.seqrep = seqrep;
@@ -1616,6 +1746,7 @@ public class Alignment implements AlignmentI
    * 
    * @return true if group has a sequence representative
    */
+  @Override
   public boolean hasSeqrep()
   {
     return seqrep != null;
@@ -1670,9 +1801,11 @@ public class Alignment implements AlignmentI
    * Parameters control whether gaps in exon (mapped) and intron (unmapped)
    * regions are preserved. Gaps that connect introns to exons are treated
    * conservatively, i.e. only preserved if both intron and exon gaps are
-   * preserved.
+   * preserved. TODO: check caveats below where the implementation fails
    * 
    * @param al
+   *          - must have same dataset, and sequences in al must have equivalent
+   *          dataset sequence and start/end bounds under given mapping
    * @param preserveMappedGaps
    *          if true, gaps within and between mapped codons are preserved
    * @param preserveUnmappedGaps
@@ -1683,31 +1816,18 @@ public class Alignment implements AlignmentI
           boolean preserveUnmappedGaps)
   {
     // TODO should this method signature be the one in the interface?
-    int count = 0;
+    // JBPComment - yes - neither flag is used, so should be deleted.
     boolean thisIsNucleotide = this.isNucleotide();
     boolean thatIsProtein = !al.isNucleotide();
     if (!thatIsProtein && !thisIsNucleotide)
     {
       return AlignmentUtils.alignProteinAsDna(this, al);
     }
-
-    char thisGapChar = this.getGapCharacter();
-    String gap = thisIsNucleotide && thatIsProtein ? String
-            .valueOf(new char[] { thisGapChar, thisGapChar, thisGapChar })
-            : String.valueOf(thisGapChar);
-
-    // TODO handle intron regions? Needs a 'holistic' alignment of dna,
-    // not just sequence by sequence. But how to 'gap' intron regions?
-
-    /*
-     * Get mappings from 'that' alignment's sequences to this.
-     */
-    for (SequenceI alignTo : getSequences())
+    else if (thatIsProtein && thisIsNucleotide)
     {
-      count += AlignmentUtils.alignSequenceAs(alignTo, al, gap,
-              preserveMappedGaps, preserveUnmappedGaps) ? 1 : 0;
+      return AlignmentUtils.alignCdsAsProtein(this, al);
     }
-    return count;
+    return AlignmentUtils.alignAs(this, al);
   }
 
   /**
@@ -1748,4 +1868,82 @@ public class Alignment implements AlignmentI
     }
     return hasValidSeq;
   }
+
+  /**
+   * Update any mappings to 'virtual' sequences to compatible real ones, if
+   * present in the added sequences. Returns a count of mappings updated.
+   * 
+   * @param seqs
+   * @return
+   */
+  @Override
+  public int realiseMappings(List<SequenceI> seqs)
+  {
+    int count = 0;
+    for (SequenceI seq : seqs)
+    {
+      for (AlignedCodonFrame mapping : getCodonFrames())
+      {
+        count += mapping.realiseWith(seq);
+      }
+    }
+    return count;
+  }
+
+  /**
+   * Returns the first AlignedCodonFrame that has a mapping between the given
+   * dataset sequences
+   * 
+   * @param mapFrom
+   * @param mapTo
+   * @return
+   */
+  @Override
+  public AlignedCodonFrame getMapping(SequenceI mapFrom, SequenceI mapTo)
+  {
+    for (AlignedCodonFrame acf : getCodonFrames())
+    {
+      if (acf.getAaForDnaSeq(mapFrom) == mapTo)
+      {
+        return acf;
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public int[] getVisibleStartAndEndIndex(List<int[]> hiddenCols)
+  {
+    int[] alignmentStartEnd = new int[] { 0, getWidth() - 1 };
+    int startPos = alignmentStartEnd[0];
+    int endPos = alignmentStartEnd[1];
+
+    int[] lowestRange = new int[] { -1, -1 };
+    int[] higestRange = new int[] { -1, -1 };
+
+    for (int[] hiddenCol : hiddenCols)
+    {
+      lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
+      higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
+    }
+
+    if (lowestRange[0] == -1 && lowestRange[1] == -1)
+    {
+      startPos = alignmentStartEnd[0];
+    }
+    else
+    {
+      startPos = lowestRange[1] + 1;
+    }
+
+    if (higestRange[0] == -1 && higestRange[1] == -1)
+    {
+      endPos = alignmentStartEnd[1];
+    }
+    else
+    {
+      endPos = higestRange[0] - 1;
+    }
+    return new int[] { startPos, endPos };
+  }
 }
diff --git a/src/jalview/datamodel/AlignmentAnnotation.java b/src/jalview/datamodel/AlignmentAnnotation.java
index ab11089..85992a6 100644
--- a/src/jalview/datamodel/AlignmentAnnotation.java
+++ b/src/jalview/datamodel/AlignmentAnnotation.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -24,11 +24,11 @@ import jalview.analysis.Rna;
 import jalview.analysis.SecStrConsensus.SimpleBP;
 import jalview.analysis.WUSSParseException;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
@@ -79,7 +79,7 @@ public class AlignmentAnnotation
   /** Array of annotations placed in the current coordinate system */
   public Annotation[] annotations;
 
-  public ArrayList<SimpleBP> bps = null;
+  public List<SimpleBP> bps = null;
 
   /**
    * RNA secondary structure contact positions
@@ -102,8 +102,8 @@ public class AlignmentAnnotation
   {
     try
     {
-      _rnasecstr = Rna.GetBasePairs(RNAannot);
-      bps = Rna.GetModeleBP(RNAannot);
+      bps = Rna.getModeleBP(RNAannot);
+      _rnasecstr = Rna.getBasePairs(bps);
       invalidrnastruc = -1;
     } catch (WUSSParseException px)
     {
@@ -245,6 +245,7 @@ public class AlignmentAnnotation
    * 
    * @see java.lang.Object#finalize()
    */
+  @Override
   protected void finalize() throws Throwable
   {
     sequenceRef = null;
@@ -271,7 +272,7 @@ public class AlignmentAnnotation
   // JBPNote: what does this do ?
   public void ConcenStru(CharSequence RNAannot) throws WUSSParseException
   {
-    bps = Rna.GetModeleBP(RNAannot);
+    bps = Rna.getModeleBP(RNAannot);
   }
 
   /**
@@ -484,7 +485,7 @@ public class AlignmentAnnotation
       this(0, annotations.length);
     }
 
-    public AnnotCharSequence(int start, int end)
+    AnnotCharSequence(int start, int end)
     {
       offset = start;
       max = end;
@@ -592,6 +593,7 @@ public class AlignmentAnnotation
     if (annotations == null)
     {
       visible = false; // try to prevent renderer from displaying.
+      invalidrnastruc = -1;
       return; // this is a non-annotation row annotation - ie a sequence score.
     }
 
@@ -957,6 +959,12 @@ public class AlignmentAnnotation
 
   }
 
+  /**
+   * When positional annotation and a sequence reference is present, clears and
+   * resizes the annotations array to the current alignment width, and adds
+   * annotation according to aligned positions of the sequenceRef given by
+   * sequenceMapping.
+   */
   public void adjustForAlignment()
   {
     if (sequenceRef == null)
@@ -980,18 +988,20 @@ public class AlignmentAnnotation
     int position;
     Annotation[] temp = new Annotation[aSize];
     Integer index;
-
-    for (a = sequenceRef.getStart(); a <= sequenceRef.getEnd(); a++)
+    if (sequenceMapping != null)
     {
-      index = new Integer(a);
-      if (sequenceMapping.containsKey(index))
+      for (a = sequenceRef.getStart(); a <= sequenceRef.getEnd(); a++)
       {
-        position = sequenceRef.findIndex(a) - 1;
+        index = new Integer(a);
+        Annotation annot = sequenceMapping.get(index);
+        if (annot != null)
+        {
+          position = sequenceRef.findIndex(a) - 1;
 
-        temp[position] = sequenceMapping.get(index);
+          temp[position] = annot;
+        }
       }
     }
-
     annotations = temp;
   }
 
@@ -1028,11 +1038,11 @@ public class AlignmentAnnotation
   }
 
   /**
-   * Associate this annotion with the aligned residues of a particular sequence.
-   * sequenceMapping will be updated in the following way: null sequenceI -
-   * existing mapping will be discarded but annotations left in mapped
-   * positions. valid sequenceI not equal to current sequenceRef: mapping is
-   * discarded and rebuilt assuming 1:1 correspondence TODO: overload with
+   * Associate this annotation with the aligned residues of a particular
+   * sequence. sequenceMapping will be updated in the following way: null
+   * sequenceI - existing mapping will be discarded but annotations left in
+   * mapped positions. valid sequenceI not equal to current sequenceRef: mapping
+   * is discarded and rebuilt assuming 1:1 correspondence TODO: overload with
    * parameter to specify correspondence between current and new sequenceRef
    * 
    * @param sequenceI
@@ -1296,15 +1306,15 @@ public class AlignmentAnnotation
    * @note caller should add the remapped annotation to newref if they have not
    *       already
    */
-  public void remap(SequenceI newref, int[][] mapping, int from, int to,
-          int idxoffset)
+  public void remap(SequenceI newref, HashMap<Integer, int[]> mapping,
+          int from, int to, int idxoffset)
   {
     if (mapping != null)
     {
       Map<Integer, Annotation> old = sequenceMapping;
       Map<Integer, Annotation> remap = new HashMap<Integer, Annotation>();
       int index = -1;
-      for (int mp[] : mapping)
+      for (int mp[] : mapping.values())
       {
         if (index++ < 0)
         {
@@ -1401,6 +1411,77 @@ public class AlignmentAnnotation
     this.annotationId = ANNOTATION_ID_PREFIX + Long.toString(nextId());
   }
 
+  /**
+   * Returns the match for the last unmatched opening RNA helix pair symbol
+   * preceding the given column, or '(' if nothing found to match.
+   * 
+   * @param column
+   * @return
+   */
+  public String getDefaultRnaHelixSymbol(int column)
+  {
+    String result = "(";
+    if (annotations == null)
+    {
+      return result;
+    }
+
+    /*
+     * for each preceding column, if it contains an open bracket, 
+     * count whether it is still unmatched at column, if so return its pair
+     * (likely faster than the fancy alternative using stacks)
+     */
+    for (int col = column - 1; col >= 0; col--)
+    {
+      Annotation annotation = annotations[col];
+      if (annotation == null)
+      {
+        continue;
+      }
+      String displayed = annotation.displayCharacter;
+      if (displayed == null || displayed.length() != 1)
+      {
+        continue;
+      }
+      char symbol = displayed.charAt(0);
+      if (!Rna.isOpeningParenthesis(symbol))
+      {
+        continue;
+      }
+
+      /*
+       * found an opening bracket symbol
+       * count (closing-opening) symbols of this type that follow it,
+       * up to and excluding the target column; if the count is less
+       * than 1, the opening bracket is unmatched, so return its match
+       */
+      String closer = String.valueOf(Rna
+              .getMatchingClosingParenthesis(symbol));
+      String opener = String.valueOf(symbol);
+      int count = 0;
+      for (int j = col + 1; j < column; j++)
+      {
+        if (annotations[j] != null)
+        {
+          String s = annotations[j].displayCharacter;
+          if (closer.equals(s))
+          {
+            count++;
+          }
+          else if (opener.equals(s))
+          {
+            count--;
+          }
+        }
+      }
+      if (count < 1)
+      {
+        return closer;
+      }
+    }
+    return result;
+  }
+
   protected static synchronized long nextId()
   {
     return counter++;
diff --git a/src/jalview/datamodel/AlignmentExportData.java b/src/jalview/datamodel/AlignmentExportData.java
index 3aaae1f..4f69cab 100644
--- a/src/jalview/datamodel/AlignmentExportData.java
+++ b/src/jalview/datamodel/AlignmentExportData.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/AlignmentI.java b/src/jalview/datamodel/AlignmentI.java
index 6d257a9..265d296 100644
--- a/src/jalview/datamodel/AlignmentI.java
+++ b/src/jalview/datamodel/AlignmentI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -41,7 +41,8 @@ public interface AlignmentI extends AnnotatedCollectionI
    * 
    * Calculates the maximum width of the alignment, including gaps.
    * 
-   * @return Greatest sequence length within alignment.
+   * @return Greatest sequence length within alignment, or -1 if no sequences
+   *         present
    */
   @Override
   int getWidth();
@@ -107,11 +108,14 @@ public interface AlignmentI extends AnnotatedCollectionI
    * Used to set a particular index of the alignment with the given sequence.
    * 
    * @param i
-   *          Index of sequence to be updated.
+   *          Index of sequence to be updated. if i>length, sequence will be
+   *          added to end, with no intervening positions.
    * @param seq
-   *          New sequence to be inserted.
+   *          New sequence to be inserted. The existing sequence at position i
+   *          will be replaced.
+   * @return existing sequence (or null if i>current length)
    */
-  void setSequenceAt(int i, SequenceI seq);
+  SequenceI replaceSequenceAt(int i, SequenceI seq);
 
   /**
    * Deletes a sequence from the alignment
@@ -305,7 +309,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @return Alignment containing dataset sequences or null of this is a
    *         dataset.
    */
-  Alignment getDataset();
+  AlignmentI getDataset();
 
   /**
    * Set the associated dataset for the alignment, or create one.
@@ -313,7 +317,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param dataset
    *          The dataset alignment or null to construct one.
    */
-  void setDataset(Alignment dataset);
+  void setDataset(AlignmentI dataset);
 
   /**
    * pads sequences with gaps (to ensure the set looks like an alignment)
@@ -375,12 +379,12 @@ public interface AlignmentI extends AnnotatedCollectionI
    * 
    * @return
    */
-  Set<AlignedCodonFrame> getCodonFrames();
+  List<AlignedCodonFrame> getCodonFrames();
 
   /**
-   * Set the codon frame mappings (replacing any existing set).
+   * Set the codon frame mappings (replacing any existing list).
    */
-  void setCodonFrames(Set<AlignedCodonFrame> acfs);
+  void setCodonFrames(List<AlignedCodonFrame> acfs);
 
   /**
    * get codon frames involving sequenceI
@@ -422,7 +426,7 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @param results
    * @return -1 or index of sequence in alignment
    */
-  int findIndex(SearchResults results);
+  int findIndex(SearchResultsI results);
 
   /**
    * append sequences and annotation from another alignment object to this one.
@@ -524,4 +528,32 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @return
    */
   public boolean hasValidSequence();
+
+  /**
+   * Update any mappings to 'virtual' sequences to compatible real ones, if
+   * present in the added sequences. Returns a count of mappings updated.
+   * 
+   * @param seqs
+   * @return
+   */
+  int realiseMappings(List<SequenceI> seqs);
+
+  /**
+   * Returns the first AlignedCodonFrame that has a mapping between the given
+   * dataset sequences
+   * 
+   * @param mapFrom
+   * @param mapTo
+   * @return
+   */
+  AlignedCodonFrame getMapping(SequenceI mapFrom, SequenceI mapTo);
+
+  /**
+   * Calculate the visible start and end index of an alignment. The result is
+   * returned an int array where: int[0] = startIndex, and int[1] = endIndex.
+   * 
+   * @param hiddenCols
+   * @return
+   */
+  public int[] getVisibleStartAndEndIndex(List<int[]> hiddenCols);
 }
diff --git a/src/jalview/datamodel/AlignmentOrder.java b/src/jalview/datamodel/AlignmentOrder.java
index 9023ade..f8821e5 100644
--- a/src/jalview/datamodel/AlignmentOrder.java
+++ b/src/jalview/datamodel/AlignmentOrder.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/AlignmentView.java b/src/jalview/datamodel/AlignmentView.java
index 88e9a9c..ab4b2f1 100644
--- a/src/jalview/datamodel/AlignmentView.java
+++ b/src/jalview/datamodel/AlignmentView.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,7 +26,6 @@ import jalview.util.ShiftList;
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Vector;
 
 /**
  * Transient object compactly representing a 'view' of an alignment - with
@@ -69,13 +68,51 @@ public class AlignmentView
    */
   private class ScGroup
   {
-    public Vector seqs;
+    public List<SeqCigar> seqs;
 
     public SequenceGroup sg;
 
     ScGroup()
     {
-      seqs = new Vector();
+      seqs = new ArrayList<SeqCigar>();
+    }
+
+    /**
+     * @param seq
+     * @return true if seq was not a member before and was added to group
+     */
+    public boolean add(SeqCigar seq)
+    {
+      if (!seq.isMemberOf(this))
+      {
+        seqs.add(seq);
+        seq.setGroupMembership(this);
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+    }
+
+    /**
+     * 
+     * @param seq
+     * @return true if seq was a member and was removed from group
+     */
+    public boolean remove(SeqCigar seq)
+    {
+      if (seq.removeGroupMembership(this))
+      {
+        seqs.remove(seq);
+        return true;
+      }
+      return false;
+    }
+
+    public int size()
+    {
+      return seqs.size();
     }
   }
 
@@ -83,7 +120,7 @@ public class AlignmentView
    * vector of selected seqCigars. This vector is also referenced by each
    * seqCigar contained in it.
    */
-  private Vector selected;
+  private ScGroup selected;
 
   /**
    * Construct an alignmentView from a live jalview alignment view. Note -
@@ -124,7 +161,7 @@ public class AlignmentView
     if (selection != null && selection.getSize() > 0)
     {
       List<SequenceI> sel = selection.getSequences(null);
-      this.selected = new Vector();
+      this.selected = new ScGroup();
       selseqs = selection
               .getSequencesInOrder(alignment, selectedRegionOnly);
     }
@@ -194,8 +231,7 @@ public class AlignmentView
         if (selection != null && selection.getSize() > 0
                 && !selectedRegionOnly)
         {
-          sequences[csi].setGroupMembership(selected);
-          selected.addElement(sequences[csi]);
+          selected.add(sequences[csi]);
         }
         if (seqsets != null)
         {
@@ -203,9 +239,8 @@ public class AlignmentView
           {
             if ((seqsets.get(sg)).contains(selseqs[i]))
             {
-              sequences[csi].setGroupMembership(sgrps[sg]);
               sgrps[sg].sg.deleteSequence(selseqs[i], false);
-              sgrps[sg].seqs.addElement(sequences[csi]);
+              sgrps[sg].add(sequences[csi]);
               if (!addedgps[sg])
               {
                 if (scGroups == null)
@@ -242,8 +277,7 @@ public class AlignmentView
     if (!seqcigararray.isSeqCigarArray())
     {
       throw new Error(
-              MessageManager
-                      .getString("error.implementation_error_can_only_make_alignmnet_from_cigararray"));
+              "Implementation Error - can only make an alignment view from a CigarArray of sequences.");
     }
     // contigs = seqcigararray.applyDeletions();
     contigs = seqcigararray.getDeletedRegions();
@@ -1073,10 +1107,10 @@ public class AlignmentView
                 + sgr.sg.getEndRes());
         for (int s = 0; s < sgr.seqs.size(); s++)
         {
-          if (!((SeqCigar) sgr.seqs.elementAt(s)).isMemberOf(sgr))
+          // JBPnote this should be a unit test for ScGroup
+          if (!sgr.seqs.get(s).isMemberOf(sgr))
           {
-            os.println("** WARNING: sequence "
-                    + ((SeqCigar) sgr.seqs.elementAt(s)).toString()
+            os.println("** WARNING: sequence " + sgr.seqs.get(s).toString()
                     + " is not marked as member of group.");
           }
         }
diff --git a/src/jalview/datamodel/AnnotatedCollectionI.java b/src/jalview/datamodel/AnnotatedCollectionI.java
index 0c159b3..cd3a8fb 100644
--- a/src/jalview/datamodel/AnnotatedCollectionI.java
+++ b/src/jalview/datamodel/AnnotatedCollectionI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/Annotation.java b/src/jalview/datamodel/Annotation.java
index 1900826..6caab20 100644
--- a/src/jalview/datamodel/Annotation.java
+++ b/src/jalview/datamodel/Annotation.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/BinaryNode.java b/src/jalview/datamodel/BinaryNode.java
index ec6e346..aebe093 100644
--- a/src/jalview/datamodel/BinaryNode.java
+++ b/src/jalview/datamodel/BinaryNode.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/BinarySequence.java b/src/jalview/datamodel/BinarySequence.java
index ff2784d..97194ea 100644
--- a/src/jalview/datamodel/BinarySequence.java
+++ b/src/jalview/datamodel/BinarySequence.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/CigarArray.java b/src/jalview/datamodel/CigarArray.java
index 61f3c33..d18a2b3 100644
--- a/src/jalview/datamodel/CigarArray.java
+++ b/src/jalview/datamodel/CigarArray.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/CigarBase.java b/src/jalview/datamodel/CigarBase.java
index 0dfa84d..45a7a93 100644
--- a/src/jalview/datamodel/CigarBase.java
+++ b/src/jalview/datamodel/CigarBase.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -533,6 +533,7 @@ public abstract class CigarBase
       {
       case M:
         cursor += range[i];
+        break;
       case I:
         vcursor += range[i];
         break;
diff --git a/src/jalview/datamodel/CigarCigar.java b/src/jalview/datamodel/CigarCigar.java
index f65a3c1..d5076a4 100644
--- a/src/jalview/datamodel/CigarCigar.java
+++ b/src/jalview/datamodel/CigarCigar.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/CigarSimple.java b/src/jalview/datamodel/CigarSimple.java
index 00e120d..a2146df 100644
--- a/src/jalview/datamodel/CigarSimple.java
+++ b/src/jalview/datamodel/CigarSimple.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/ColumnSelection.java b/src/jalview/datamodel/ColumnSelection.java
index 15bbffa..11b4bb9 100644
--- a/src/jalview/datamodel/ColumnSelection.java
+++ b/src/jalview/datamodel/ColumnSelection.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,23 +20,258 @@
  */
 package jalview.datamodel;
 
+import jalview.util.Comparison;
 import jalview.util.ShiftList;
 import jalview.viewmodel.annotationfilter.AnnotationFilterParameter;
 import jalview.viewmodel.annotationfilter.AnnotationFilterParameter.SearchableAnnotationField;
 
 import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.List;
 import java.util.Vector;
 
 /**
- * NOTE: Columns are zero based.
+ * Data class holding the selected columns and hidden column ranges for a view.
+ * Ranges are base 1.
  */
 public class ColumnSelection
 {
-  Vector<Integer> selected = new Vector<Integer>();
+  /**
+   * A class to hold an efficient representation of selected columns
+   */
+  private class IntList
+  {
+    /*
+     * list of selected columns (ordered by selection order, not column order)
+     */
+    private List<Integer> order;
+
+    /*
+     * an unmodifiable view of the selected columns list
+     */
+    private List<Integer> _uorder;
+
+    /**
+     * bitfield for column selection - allows quick lookup
+     */
+    private BitSet selected;
+
+    /**
+     * Constructor
+     */
+    IntList()
+    {
+      order = new ArrayList<Integer>();
+      _uorder = Collections.unmodifiableList(order);
+      selected = new BitSet();
+    }
+
+    /**
+     * Copy constructor
+     * 
+     * @param other
+     */
+    IntList(IntList other)
+    {
+      this();
+      if (other != null)
+      {
+        int j = other.size();
+        for (int i = 0; i < j; i++)
+        {
+          add(other.elementAt(i));
+        }
+      }
+    }
+
+    /**
+     * adds a new column i to the selection - only if i is not already selected
+     * 
+     * @param i
+     */
+    void add(int i)
+    {
+      if (!selected.get(i))
+      {
+        order.add(Integer.valueOf(i));
+        selected.set(i);
+      }
+    }
+
+    void clear()
+    {
+      order.clear();
+      selected.clear();
+    }
+
+    void remove(int col)
+    {
+
+      Integer colInt = new Integer(col);
+
+      if (selected.get(col))
+      {
+        // if this ever changes to List.remove(), ensure Integer not int
+        // argument
+        // as List.remove(int i) removes the i'th item which is wrong
+        order.remove(colInt);
+        selected.clear(col);
+      }
+    }
+
+    boolean contains(Integer colInt)
+    {
+      return selected.get(colInt);
+    }
+
+    boolean isEmpty()
+    {
+      return order.isEmpty();
+    }
+
+    /**
+     * Returns a read-only view of the selected columns list
+     * 
+     * @return
+     */
+    List<Integer> getList()
+    {
+      return _uorder;
+    }
+
+    int size()
+    {
+      return order.size();
+    }
+
+    /**
+     * gets the column that was selected first, second or i'th
+     * 
+     * @param i
+     * @return
+     */
+    int elementAt(int i)
+    {
+      return order.get(i);
+    }
+
+    protected boolean pruneColumnList(final List<int[]> shifts)
+    {
+      int s = 0, t = shifts.size();
+      int[] sr = shifts.get(s++);
+      boolean pruned = false;
+      int i = 0, j = order.size();
+      while (i < j && s <= t)
+      {
+        int c = order.get(i++).intValue();
+        if (sr[0] <= c)
+        {
+          if (sr[1] + sr[0] >= c)
+          { // sr[1] -ve means inseriton.
+            order.remove(--i);
+            selected.clear(c);
+            j--;
+          }
+          else
+          {
+            if (s < t)
+            {
+              sr = shifts.get(s);
+            }
+            s++;
+          }
+        }
+      }
+      return pruned;
+    }
+
+    /**
+     * shift every selected column at or above start by change
+     * 
+     * @param start
+     *          - leftmost column to be shifted
+     * @param change
+     *          - delta for shift
+     */
+    void compensateForEdits(int start, int change)
+    {
+      BitSet mask = new BitSet();
+      for (int i = 0; i < order.size(); i++)
+      {
+        int temp = order.get(i);
+
+        if (temp >= start)
+        {
+          // clear shifted bits and update List of selected columns
+          selected.clear(temp);
+          mask.set(temp - change);
+          order.set(i, new Integer(temp - change));
+        }
+      }
+      // lastly update the bitfield all at once
+      selected.or(mask);
+    }
+
+    boolean isSelected(int column)
+    {
+      return selected.get(column);
+    }
+
+    int getMaxColumn()
+    {
+      return selected.length() - 1;
+    }
+
+    int getMinColumn()
+    {
+      return selected.get(0) ? 0 : selected.nextSetBit(0);
+    }
+
+    /**
+     * @return a series of selection intervals along the range
+     */
+    List<int[]> getRanges()
+    {
+      List<int[]> rlist = new ArrayList<int[]>();
+      if (selected.isEmpty())
+      {
+        return rlist;
+      }
+      int next = selected.nextSetBit(0), clear = -1;
+      while (next != -1)
+      {
+        clear = selected.nextClearBit(next);
+        rlist.add(new int[] { next, clear - 1 });
+        next = selected.nextSetBit(clear);
+      }
+      return rlist;
+    }
+
+    @Override
+    public int hashCode()
+    {
+      // TODO Auto-generated method stub
+      return selected.hashCode();
+    }
 
-  // Vector of int [] {startCol, endCol}
+    @Override
+    public boolean equals(Object obj)
+    {
+      if (obj instanceof IntList)
+      {
+        return ((IntList) obj).selected.equals(selected);
+      }
+      return false;
+    }
+  }
+
+  IntList selection = new IntList();
+
+  /*
+   * list of hidden column [start, end] ranges; the list is maintained in
+   * ascending start column order
+   */
   Vector<int[]> hiddenColumns;
 
   /**
@@ -47,11 +282,7 @@ public class ColumnSelection
    */
   public void addElement(int col)
   {
-    Integer column = new Integer(col);
-    if (!selected.contains(column))
-    {
-      selected.addElement(column);
-    }
+    selection.add(col);
   }
 
   /**
@@ -59,7 +290,7 @@ public class ColumnSelection
    */
   public void clear()
   {
-    selected.removeAllElements();
+    selection.clear();
   }
 
   /**
@@ -70,14 +301,7 @@ public class ColumnSelection
    */
   public void removeElement(int col)
   {
-    Integer colInt = new Integer(col);
-
-    if (selected.contains(colInt))
-    {
-      // if this ever changes to List.remove(), ensure Integer not int argument
-      // as List.remove(int i) removes the i'th item which is wrong
-      selected.removeElement(colInt);
-    }
+    selection.remove(col);
   }
 
   /**
@@ -94,55 +318,55 @@ public class ColumnSelection
     for (int i = start; i < end; i++)
     {
       colInt = new Integer(i);
-      if (selected.contains(colInt))
+      if (selection.contains(colInt))
       {
-        selected.removeElement(colInt);
+        selection.remove(colInt);
       }
     }
   }
 
   /**
-   * 
-   * @return Vector containing selected columns as Integers
+   * Returns a read-only view of the (possibly empty) list of selected columns
+   * <p>
+   * The list contains no duplicates but is not necessarily ordered. It also may
+   * include columns hidden from the current view. To modify (for example sort)
+   * the list, you should first make a copy.
+   * <p>
+   * The list is not thread-safe: iterating over it could result in
+   * ConcurrentModificationException if it is modified by another thread.
    */
-  public Vector<Integer> getSelected()
+  public List<Integer> getSelected()
   {
-    return selected;
+    return selection.getList();
   }
 
   /**
-   * 
-   * @param col
-   *          index to search for in column selection
-   * 
-   * @return true if Integer(col) is in selection.
+   * @return list of int arrays containing start and end column position for
+   *         runs of selected columns ordered from right to left.
    */
-  public boolean contains(int col)
+  public List<int[]> getSelectedRanges()
   {
-    return selected.contains(new Integer(col));
+    return selection.getRanges();
   }
 
   /**
-   * Column number at position i in selection
    * 
-   * @param i
-   *          index into selected columns
+   * @param col
+   *          index to search for in column selection
    * 
-   * @return column number in alignment
+   * @return true if col is selected
    */
-  public int columnAt(int i)
+  public boolean contains(int col)
   {
-    return selected.elementAt(i).intValue();
+    return (col > -1) ? selection.isSelected(col) : false;
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
+   * Answers true if no columns are selected, else false
    */
-  public int size()
+  public boolean isEmpty()
   {
-    return selected.size();
+    return selection == null || selection.isEmpty();
   }
 
   /**
@@ -152,17 +376,11 @@ public class ColumnSelection
    */
   public int getMax()
   {
-    int max = -1;
-
-    for (int i = 0; i < selected.size(); i++)
+    if (selection.isEmpty())
     {
-      if (columnAt(i) > max)
-      {
-        max = columnAt(i);
-      }
+      return -1;
     }
-
-    return max;
+    return selection.getMaxColumn();
   }
 
   /**
@@ -172,17 +390,11 @@ public class ColumnSelection
    */
   public int getMin()
   {
-    int min = 1000000000;
-
-    for (int i = 0; i < selected.size(); i++)
+    if (selection.isEmpty())
     {
-      if (columnAt(i) < min)
-      {
-        min = columnAt(i);
-      }
+      return 1000000000;
     }
-
-    return min;
+    return selection.getMinColumn();
   }
 
   /**
@@ -196,16 +408,7 @@ public class ColumnSelection
   public List<int[]> compensateForEdit(int start, int change)
   {
     List<int[]> deletedHiddenColumns = null;
-    for (int i = 0; i < size(); i++)
-    {
-      int temp = columnAt(i);
-
-      if (temp >= start)
-      {
-        // if this ever changes to List.set(), swap parameter order!!
-        selected.setElementAt(new Integer(temp - change), i);
-      }
-    }
+    selection.compensateForEdits(start, change);
 
     if (hiddenColumns != null)
     {
@@ -254,16 +457,8 @@ public class ColumnSelection
    */
   private void compensateForDelEdits(int start, int change)
   {
-    for (int i = 0; i < size(); i++)
-    {
-      int temp = columnAt(i);
 
-      if (temp >= start)
-      {
-        // if this ever changes to List.set(), swap parameter order!!
-        selected.setElementAt(new Integer(temp - change), i);
-      }
-    }
+    selection.compensateForEdits(start, change);
 
     if (hiddenColumns != null)
     {
@@ -418,36 +613,6 @@ public class ColumnSelection
     // operations.
   }
 
-  private boolean pruneColumnList(final List<int[]> shifts,
-          Vector<Integer> list)
-  {
-    int s = 0, t = shifts.size();
-    int[] sr = shifts.get(s++);
-    boolean pruned = false;
-    int i = 0, j = list.size();
-    while (i < j && s <= t)
-    {
-      int c = list.elementAt(i++).intValue();
-      if (sr[0] <= c)
-      {
-        if (sr[1] + sr[0] >= c)
-        { // sr[1] -ve means inseriton.
-          list.removeElementAt(--i);
-          j--;
-        }
-        else
-        {
-          if (s < t)
-          {
-            sr = shifts.get(s);
-          }
-          s++;
-        }
-      }
-    }
-    return pruned;
-  }
-
   /**
    * remove any hiddenColumns or selected columns and shift remaining based on a
    * series of position, range deletions.
@@ -470,12 +635,12 @@ public class ColumnSelection
             hiddenColumns = null;
           }
         }
-        if (selected != null && selected.size() > 0)
+        if (selection != null && selection.size() > 0)
         {
-          pruneColumnList(shifts, selected);
-          if (selected != null && selected.size() == 0)
+          selection.pruneColumnList(shifts);
+          if (selection != null && selection.size() == 0)
           {
-            selected = null;
+            selection = null;
           }
         }
         // and shift the rest.
@@ -499,7 +664,7 @@ public class ColumnSelection
    * Return absolute column index for a visible column index
    * 
    * @param column
-   *          int column index in alignment view
+   *          int column index in alignment view (count from zero)
    * @return alignment column index for column
    */
   public int adjustForHiddenColumns(int column)
@@ -553,6 +718,10 @@ public class ColumnSelection
 
   /**
    * Use this method to determine where the next hiddenRegion starts
+   * 
+   * @param hiddenRegion
+   *          index of hidden region (counts from 0)
+   * @return column number in visible view
    */
   public int findHiddenRegionPosition(int hiddenRegion)
   {
@@ -572,7 +741,7 @@ public class ColumnSelection
         gaps += region[1] + 1 - region[0];
         result = region[1] + 1;
         index++;
-      } while (index < hiddenRegion + 1);
+      } while (index <= hiddenRegion);
 
       result -= gaps;
     }
@@ -638,14 +807,23 @@ public class ColumnSelection
 
   public void hideSelectedColumns()
   {
-    while (size() > 0)
+    synchronized (selection)
     {
-      int column = getSelected().firstElement().intValue();
-      hideColumns(column);
+      for (int[] selregions : selection.getRanges())
+      {
+        hideColumns(selregions[0], selregions[1]);
+      }
+      selection.clear();
     }
 
   }
 
+  /**
+   * Adds the specified column range to the hidden columns
+   * 
+   * @param start
+   * @param end
+   */
   public void hideColumns(int start, int end)
   {
     if (hiddenColumns == null)
@@ -653,48 +831,68 @@ public class ColumnSelection
       hiddenColumns = new Vector<int[]>();
     }
 
-    boolean added = false;
-    boolean overlap = false;
-
+    /*
+     * traverse existing hidden ranges and insert / amend / append as
+     * appropriate
+     */
     for (int i = 0; i < hiddenColumns.size(); i++)
     {
       int[] region = hiddenColumns.elementAt(i);
-      if (start <= region[1] && end >= region[0])
+
+      if (end < region[0] - 1)
       {
-        hiddenColumns.removeElementAt(i);
-        overlap = true;
-        break;
+        /*
+         * insert discontiguous preceding range
+         */
+        hiddenColumns.insertElementAt(new int[] { start, end }, i);
+        return;
       }
-      else if (end < region[0] && start < region[0])
+
+      if (end <= region[1])
       {
-        hiddenColumns.insertElementAt(new int[] { start, end }, i);
-        added = true;
-        break;
+        /*
+         * new range overlaps existing, or is contiguous preceding it - adjust
+         * start column
+         */
+        region[0] = Math.min(region[0], start);
+        return;
       }
-    }
 
-    if (overlap)
-    {
-      hideColumns(start, end);
-    }
-    else if (!added)
-    {
-      hiddenColumns.addElement(new int[] { start, end });
+      if (start <= region[1] + 1)
+      {
+        /*
+         * new range overlaps existing, or is contiguous following it - adjust
+         * start and end columns
+         */
+        region[0] = Math.min(region[0], start);
+        region[1] = Math.max(region[1], end);
+        return;
+      }
     }
 
+    /*
+     * remaining case is that the new range follows everything else
+     */
+    hiddenColumns.addElement(new int[] { start, end });
   }
 
   /**
-   * This method will find a range of selected columns around the column
-   * specified
+   * Hides the specified column and any adjacent selected columns
    * 
    * @param res
    *          int
    */
   public void hideColumns(int col)
   {
-    // First find out range of columns to hide
-    int min = col, max = col + 1;
+    /*
+     * deselect column (whether selected or not!)
+     */
+    removeElement(col);
+
+    /*
+     * find adjacent selected columns
+     */
+    int min = col - 1, max = col + 1;
     while (contains(min))
     {
       removeElement(min);
@@ -707,6 +905,9 @@ public class ColumnSelection
       max++;
     }
 
+    /*
+     * min, max are now the closest unselected columns
+     */
     min++;
     max--;
     if (min > max)
@@ -717,6 +918,9 @@ public class ColumnSelection
     hideColumns(min, max);
   }
 
+  /**
+   * Unhides, and adds to the selection list, all hidden columns
+   */
   public void revealAllHiddenColumns()
   {
     if (hiddenColumns != null)
@@ -734,12 +938,18 @@ public class ColumnSelection
     hiddenColumns = null;
   }
 
-  public void revealHiddenColumns(int res)
+  /**
+   * Reveals, and marks as selected, the hidden column range with the given
+   * start column
+   * 
+   * @param start
+   */
+  public void revealHiddenColumns(int start)
   {
     for (int i = 0; i < hiddenColumns.size(); i++)
     {
       int[] region = hiddenColumns.elementAt(i);
-      if (res == region[0])
+      if (start == region[0])
       {
         for (int j = region[0]; j < region[1] + 1; j++)
         {
@@ -760,9 +970,8 @@ public class ColumnSelection
   {
     if (hiddenColumns != null)
     {
-      for (int i = 0; i < hiddenColumns.size(); i++)
+      for (int[] region : hiddenColumns)
       {
-        int[] region = hiddenColumns.elementAt(i);
         if (column >= region[0] && column <= region[1])
         {
           return false;
@@ -782,14 +991,7 @@ public class ColumnSelection
   {
     if (copy != null)
     {
-      if (copy.selected != null)
-      {
-        selected = new Vector<Integer>();
-        for (int i = 0, j = copy.selected.size(); i < j; i++)
-        {
-          selected.addElement(copy.selected.elementAt(i));
-        }
-      }
+      selection = new IntList(copy.selection);
       if (copy.hiddenColumns != null)
       {
         hiddenColumns = new Vector<int[]>(copy.hiddenColumns.size());
@@ -819,7 +1021,7 @@ public class ColumnSelection
           SequenceI[] seqs)
   {
     int i, iSize = seqs.length;
-    String selection[] = new String[iSize];
+    String selections[] = new String[iSize];
     if (hiddenColumns != null && hiddenColumns.size() > 0)
     {
       for (i = 0; i < iSize; i++)
@@ -861,18 +1063,18 @@ public class ColumnSelection
           visibleSeq.append(seqs[i].getSequence(blockStart, end));
         }
 
-        selection[i] = visibleSeq.toString();
+        selections[i] = visibleSeq.toString();
       }
     }
     else
     {
       for (i = 0; i < iSize; i++)
       {
-        selection[i] = seqs[i].getSequenceAsString(start, end);
+        selections[i] = seqs[i].getSequenceAsString(start, end);
       }
     }
 
-    return selection;
+    return selections;
   }
 
   /**
@@ -935,6 +1137,84 @@ public class ColumnSelection
   }
 
   /**
+   * Locate the first and last position visible for this sequence. if seq isn't
+   * visible then return the position of the left and right of the hidden
+   * boundary region, and the corresponding alignment column indices for the
+   * extent of the sequence
+   * 
+   * @param seq
+   * @return int[] { visible start, visible end, first seqpos, last seqpos,
+   *         alignment index for seq start, alignment index for seq end }
+   */
+  public int[] locateVisibleBoundsOfSequence(SequenceI seq)
+  {
+    int fpos = seq.getStart(), lpos = seq.getEnd();
+    int start = 0;
+
+    if (hiddenColumns == null || hiddenColumns.size() == 0)
+    {
+      int ifpos = seq.findIndex(fpos) - 1, ilpos = seq.findIndex(lpos) - 1;
+      return new int[] { ifpos, ilpos, fpos, lpos, ifpos, ilpos };
+    }
+
+    // Simply walk along the sequence whilst watching for hidden column
+    // boundaries
+    List<int[]> regions = getHiddenColumns();
+    int spos = fpos, lastvispos = -1, rcount = 0, hideStart = seq
+            .getLength(), hideEnd = -1;
+    int visPrev = 0, visNext = 0, firstP = -1, lastP = -1;
+    boolean foundStart = false;
+    for (int p = 0, pLen = seq.getLength(); spos <= seq.getEnd()
+            && p < pLen; p++)
+    {
+      if (!Comparison.isGap(seq.getCharAt(p)))
+      {
+        // keep track of first/last column
+        // containing sequence data regardless of visibility
+        if (firstP == -1)
+        {
+          firstP = p;
+        }
+        lastP = p;
+        // update hidden region start/end
+        while (hideEnd < p && rcount < regions.size())
+        {
+          int[] region = regions.get(rcount++);
+          visPrev = visNext;
+          visNext += region[0] - visPrev;
+          hideStart = region[0];
+          hideEnd = region[1];
+        }
+        if (hideEnd < p)
+        {
+          hideStart = seq.getLength();
+        }
+        // update visible boundary for sequence
+        if (p < hideStart)
+        {
+          if (!foundStart)
+          {
+            fpos = spos;
+            start = p;
+            foundStart = true;
+          }
+          lastvispos = p;
+          lpos = spos;
+        }
+        // look for next sequence position
+        spos++;
+      }
+    }
+    if (foundStart)
+    {
+      return new int[] { findColumnPosition(start),
+          findColumnPosition(lastvispos), fpos, lpos, firstP, lastP };
+    }
+    // otherwise, sequence was completely hidden
+    return new int[] { visPrev, visNext, 0, 0, firstP, lastP };
+  }
+
+  /**
    * delete any columns in alignmentAnnotation that are hidden (including
    * sequence associated annotation).
    * 
@@ -1078,16 +1358,13 @@ public class ColumnSelection
    */
   public void addElementsFrom(ColumnSelection colsel)
   {
-    if (colsel != null && colsel.size() > 0)
+    if (colsel != null && !colsel.isEmpty())
     {
       for (Integer col : colsel.getSelected())
       {
         if (hiddenColumns != null && isVisible(col.intValue()))
         {
-          if (!selected.contains(col))
-          {
-            selected.addElement(col);
-          }
+          selection.add(col);
         }
       }
     }
@@ -1101,8 +1378,8 @@ public class ColumnSelection
    */
   public void setElementsFrom(ColumnSelection colsel)
   {
-    selected = new Vector<Integer>();
-    if (colsel.selected != null && colsel.selected.size() > 0)
+    selection = new IntList();
+    if (colsel.selection != null && colsel.selection.size() > 0)
     {
       if (hiddenColumns != null && hiddenColumns.size() > 0)
       {
@@ -1269,7 +1546,7 @@ public class ColumnSelection
    */
   public boolean hasSelectedColumns()
   {
-    return (selected != null && selected.size() > 0);
+    return (selection != null && selection.size() > 0);
   }
 
   /**
@@ -1308,6 +1585,8 @@ public class ColumnSelection
   public boolean filterAnnotations(Annotation[] annotations,
           AnnotationFilterParameter filterParams)
   {
+    // JBPNote - this method needs to be refactored to become independent of
+    // viewmodel package
     this.revealAllHiddenColumns();
     this.clear();
     int count = 0;
@@ -1389,4 +1668,149 @@ public class ColumnSelection
     } while (count < annotations.length);
     return false;
   }
+
+  /**
+   * Returns a hashCode built from selected columns and hidden column ranges
+   */
+  @Override
+  public int hashCode()
+  {
+    int hashCode = selection.hashCode();
+    if (hiddenColumns != null)
+    {
+      for (int[] hidden : hiddenColumns)
+      {
+        hashCode = 31 * hashCode + hidden[0];
+        hashCode = 31 * hashCode + hidden[1];
+      }
+    }
+    return hashCode;
+  }
+
+  /**
+   * Answers true if comparing to a ColumnSelection with the same selected
+   * columns and hidden columns, else false
+   */
+  @Override
+  public boolean equals(Object obj)
+  {
+    if (!(obj instanceof ColumnSelection))
+    {
+      return false;
+    }
+    ColumnSelection that = (ColumnSelection) obj;
+
+    /*
+     * check columns selected are either both null, or match
+     */
+    if (this.selection == null)
+    {
+      if (that.selection != null)
+      {
+        return false;
+      }
+    }
+    if (!this.selection.equals(that.selection))
+    {
+      return false;
+    }
+
+    /*
+     * check hidden columns are either both null, or match
+     */
+    if (this.hiddenColumns == null)
+    {
+      return (that.hiddenColumns == null);
+    }
+    if (that.hiddenColumns == null
+            || that.hiddenColumns.size() != this.hiddenColumns.size())
+    {
+      return false;
+    }
+    int i = 0;
+    for (int[] thisRange : hiddenColumns)
+    {
+      int[] thatRange = that.hiddenColumns.get(i++);
+      if (thisRange[0] != thatRange[0] || thisRange[1] != thatRange[1])
+      {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Updates the column selection depending on the parameters, and returns true
+   * if any change was made to the selection
+   * 
+   * @param markedColumns
+   *          a set identifying marked columns (base 0)
+   * @param startCol
+   *          the first column of the range to operate over (base 0)
+   * @param endCol
+   *          the last column of the range to operate over (base 0)
+   * @param invert
+   *          if true, deselect marked columns and select unmarked
+   * @param extendCurrent
+   *          if true, extend rather than replacing the current column selection
+   * @param toggle
+   *          if true, toggle the selection state of marked columns
+   * 
+   * @return
+   */
+  public boolean markColumns(BitSet markedColumns, int startCol,
+          int endCol, boolean invert, boolean extendCurrent, boolean toggle)
+  {
+    boolean changed = false;
+    if (!extendCurrent && !toggle)
+    {
+      changed = !this.isEmpty();
+      clear();
+    }
+    if (invert)
+    {
+      // invert only in the currently selected sequence region
+      int i = markedColumns.nextClearBit(startCol);
+      int ibs = markedColumns.nextSetBit(startCol);
+      while (i >= startCol && i <= endCol)
+      {
+        if (ibs < 0 || i < ibs)
+        {
+          changed = true;
+          if (toggle && contains(i))
+          {
+            removeElement(i++);
+          }
+          else
+          {
+            addElement(i++);
+          }
+        }
+        else
+        {
+          i = markedColumns.nextClearBit(ibs);
+          ibs = markedColumns.nextSetBit(i);
+        }
+      }
+    }
+    else
+    {
+      int i = markedColumns.nextSetBit(startCol);
+      while (i >= startCol && i <= endCol)
+      {
+        changed = true;
+        if (toggle && contains(i))
+        {
+          removeElement(i);
+        }
+        else
+        {
+          addElement(i);
+        }
+        i = markedColumns.nextSetBit(i + 1);
+      }
+    }
+    return changed;
+  }
+
 }
diff --git a/src/jalview/datamodel/DBRefEntry.java b/src/jalview/datamodel/DBRefEntry.java
index bc5588a..83b69d5 100644
--- a/src/jalview/datamodel/DBRefEntry.java
+++ b/src/jalview/datamodel/DBRefEntry.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,12 @@
  */
 package jalview.datamodel;
 
-public class DBRefEntry
+import jalview.api.DBRefEntryI;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class DBRefEntry implements DBRefEntryI
 {
   String source = "", version = "", accessionId = "";
 
@@ -60,13 +65,14 @@ public class DBRefEntry
     this.map = map;
   }
 
-  public DBRefEntry(DBRefEntry entry)
+  public DBRefEntry(DBRefEntryI entry)
   {
-    this(
-            (entry.source == null ? "" : new String(entry.source)),
-            (entry.version == null ? "" : new String(entry.version)),
-            (entry.accessionId == null ? "" : new String(entry.accessionId)),
-            (entry.map == null ? null : new Mapping(entry.map)));
+    this((entry.getSource() == null ? "" : new String(entry.getSource())),
+            (entry.getVersion() == null ? "" : new String(
+                    entry.getVersion())),
+            (entry.getAccessionId() == null ? "" : new String(
+                    entry.getAccessionId())),
+            (entry.getMap() == null ? null : new Mapping(entry.getMap())));
   }
 
   @Override
@@ -93,13 +99,95 @@ public class DBRefEntry
   }
 
   /**
+   * Answers true if this object is either equivalent to, or can be 'improved'
+   * by, the given entry. Specifically, answers true if
+   * <ul>
+   * <li>source and accession are identical (ignoring case)</li>
+   * <li>version is identical (ignoring case), or this version is of the format
+   * "someSource:0", in which case the version for the other entry replaces it</li>
+   * <li>mappings are not compared but if this entry has no mapping, replace
+   * with that for the other entry</li>
+   * </ul>
+   * 
+   * @param other
+   * @return
+   */
+  @Override
+  public boolean updateFrom(DBRefEntryI other)
+  {
+    if (other == null)
+    {
+      return false;
+    }
+    if (other == this)
+    {
+      return true;
+    }
+
+    /*
+     * source must either match or be both null
+     */
+    String otherSource = other.getSource();
+    if ((source == null && otherSource != null)
+            || (source != null && otherSource == null)
+            || (source != null && !source.equalsIgnoreCase(otherSource)))
+    {
+      return false;
+    }
+
+    /*
+     * accession id must either match or be both null
+     */
+    String otherAccession = other.getAccessionId();
+    if ((accessionId == null && otherAccession != null)
+            || (accessionId != null && otherAccession == null)
+            || (accessionId != null && !accessionId
+                    .equalsIgnoreCase(otherAccession)))
+    {
+      return false;
+    }
+
+    /*
+     * if my version is null, "0" or "source:0" then replace with other version,
+     * otherwise the versions have to match
+     */
+    String otherVersion = other.getVersion();
+
+    if ((version == null || version.equals("0") || version.endsWith(":0"))
+            && otherVersion != null)
+    {
+      setVersion(otherVersion);
+    }
+    else
+    {
+      if (version != null
+              && (otherVersion == null || !version
+                      .equalsIgnoreCase(otherVersion)))
+      {
+        return false;
+      }
+    }
+
+    /*
+     * if I have no mapping, take that of the other dbref
+     */
+    if (map == null)
+    {
+      setMap(other.getMap());
+    }
+    return true;
+  }
+
+  /**
    * test for similar DBRef attributes, except for the map object.
    * 
    * @param entry
    * @return true if source, accession and version are equal with those of entry
    */
-  public boolean equalRef(DBRefEntry entry)
+  @Override
+  public boolean equalRef(DBRefEntryI entry)
   {
+    // TODO is this method and equals() not needed?
     if (entry == null)
     {
       return false;
@@ -108,63 +196,56 @@ public class DBRefEntry
     {
       return true;
     }
-    if ((source != null && entry.source != null && source
-            .equalsIgnoreCase(entry.source))
-            && (accessionId != null && entry.accessionId != null && accessionId
-                    .equalsIgnoreCase(entry.accessionId))
-            && (version != null && entry.version != null && version
-                    .equalsIgnoreCase(entry.version)))
+    if (entry != null
+            && (source != null && entry.getSource() != null && source
+                    .equalsIgnoreCase(entry.getSource()))
+            && (accessionId != null && entry.getAccessionId() != null && accessionId
+                    .equalsIgnoreCase(entry.getAccessionId()))
+            && (version != null && entry.getVersion() != null && version
+                    .equalsIgnoreCase(entry.getVersion())))
     {
       return true;
     }
     return false;
   }
 
+  @Override
   public String getSource()
   {
     return source;
   }
 
+  @Override
   public String getVersion()
   {
     return version;
   }
 
+  @Override
   public String getAccessionId()
   {
     return accessionId;
   }
 
-  /**
-   * @param accessionId
-   *          the accessionId to set
-   */
+  @Override
   public void setAccessionId(String accessionId)
   {
     this.accessionId = accessionId;
   }
 
-  /**
-   * @param source
-   *          the source to set
-   */
+  @Override
   public void setSource(String source)
   {
     this.source = source;
   }
 
-  /**
-   * @param version
-   *          the version to set
-   */
+  @Override
   public void setVersion(String version)
   {
     this.version = version;
   }
 
-  /**
-   * @return the map
-   */
+  @Override
   public Mapping getMap()
   {
     return map;
@@ -194,8 +275,61 @@ public class DBRefEntry
             + ((accessionId != null) ? accessionId : "");
   }
 
+  @Override
   public String toString()
   {
     return getSrcAccString();
   }
+
+  @Override
+  public boolean isPrimaryCandidate()
+  {
+    /*
+     * if a map is present, unless it is 1:1 and has no SequenceI mate, it cannot be a primary reference.  
+     */
+    if (map != null)
+    {
+      if (map.getTo() != null)
+      {
+        return false;
+      }
+      if (map.getMap().getFromRatio() != map.getMap().getToRatio()
+              || map.getMap().getFromRatio() != 1)
+      {
+        return false;
+      }
+      // check map is between identical single contiguous ranges
+      List<int[]> fromRanges = map.getMap().getFromRanges();
+      List<int[]> toRanges = map.getMap().getToRanges();
+      if (fromRanges.size() != 1 || toRanges.size() != 1)
+      {
+        return false;
+      }
+      if (fromRanges.get(0)[0] != toRanges.get(0)[0]
+              || fromRanges.get(0)[1] != toRanges.get(0)[1])
+      {
+        return false;
+      }
+    }
+    if (version == null)
+    {
+      // no version string implies the reference has not been verified at all.
+      return false;
+    }
+    // tricky - this test really needs to search the sequence's set of dbrefs to
+    // see if there is a primary reference that derived this reference.
+    String ucv = version.toUpperCase();
+    for (String primsrc : Arrays.asList(DBRefSource.allSources()))
+    {
+      if (ucv.startsWith(primsrc.toUpperCase()))
+      {
+        // by convention, many secondary references inherit the primary
+        // reference's
+        // source string as a prefix for any version information from the
+        // secondary reference.
+        return false;
+      }
+    }
+    return true;
+  }
 }
diff --git a/src/jalview/datamodel/DBRefSource.java b/src/jalview/datamodel/DBRefSource.java
index 3982802..e842e6f 100644
--- a/src/jalview/datamodel/DBRefSource.java
+++ b/src/jalview/datamodel/DBRefSource.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,10 +20,17 @@
  */
 package jalview.datamodel;
 
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Defines internal constants for unambiguous annotation of DbRefEntry source
  * strings and describing the data retrieved from external database sources (see
- * jalview.ws.DbSourcProxy)
+ * jalview.ws.DbSourcProxy) <br/>
+ * TODO: replace with ontology to allow recognition of particular attributes
+ * (e.g. protein coding, alignment (ortholog db, paralog db, domain db),
+ * genomic, transcriptomic, 3D structure providing (PDB, MODBASE, etc) ..).
  * 
  * @author JimP
  * 
@@ -33,12 +40,12 @@ public class DBRefSource
   /**
    * UNIPROT Accession Number
    */
-  public static String UNIPROT = "UNIPROT";
+  public static final String UNIPROT = "UNIPROT";
 
   /**
    * UNIPROT Entry Name
    */
-  public static String UP_NAME = "UNIPROT_NAME".toUpperCase();
+  public static final String UP_NAME = "UNIPROT_NAME".toUpperCase();
 
   /**
    * Uniprot Knowledgebase/TrEMBL as served from EMBL protein products.
@@ -51,27 +58,27 @@ public class DBRefSource
   /**
    * PDB Entry Code
    */
-  public static String PDB = "PDB";
+  public static final String PDB = "PDB";
 
   /**
    * EMBL ID
    */
-  public static String EMBL = "EMBL";
+  public static final String EMBL = "EMBL";
 
   /**
    * EMBLCDS ID
    */
-  public static String EMBLCDS = "EMBLCDS";
+  public static final String EMBLCDS = "EMBLCDS";
 
   /**
    * PFAM ID
    */
-  public static String PFAM = "PFAM";
+  public static final String PFAM = "PFAM";
 
   /**
    * RFAM ID
    */
-  public static String RFAM = "RFAM";
+  public static final String RFAM = "RFAM";
 
   /**
    * GeneDB ID
@@ -79,62 +86,39 @@ public class DBRefSource
   public static final String GENEDB = "GeneDB".toUpperCase();
 
   /**
-   * List of databases whose sequences might have coding regions annotated
-   */
-  public static final String[] DNACODINGDBS = { EMBL, EMBLCDS, GENEDB };
-
-  public static final String[] CODINGDBS = { EMBLCDS, GENEDB };
-
-  public static final String[] PROTEINDBS = { UNIPROT, PDB, UNIPROTKB,
-      EMBLCDSProduct };
-
-  public static final String[] PROTEINSEQ = { UNIPROT, UNIPROTKB,
-      EMBLCDSProduct };
-
-  public static final String[] PROTEINSTR = { PDB };
-
-  public static final String[] DOMAINDBS = { PFAM, RFAM };
-
-  /**
-   * set of unique DBRefSource property constants. These could be used to
-   * reconstruct the above groupings
-   */
-  public static final Object SEQDB = "SQ";
-
-  /**
-   * database of nucleic acid sequences
-   */
-  public static final Object DNASEQDB = "NASQ";
-
-  /**
-   * database of amino acid sequences
-   */
-  public static final Object PROTSEQDB = "PROTSQ";
-
-  /**
-   * database of cDNA sequences
-   */
-  public static final Object CODINGSEQDB = "CODING";
-
-  /**
-   * database of na sequences with exon annotation
+   * Ensembl
    */
-  public static final Object DNACODINGSEQDB = "XONCODING";
+  public static final String ENSEMBL = "ENSEMBL";
 
-  /**
-   * DB returns several sequences associated with a protein/nucleotide domain
-   */
-  public static final Object DOMAINDB = "DOMAIN";
+  public static final String ENSEMBLGENOMES = "ENSEMBLGENOMES";
 
   /**
-   * DB query can take multiple accession codes concatenated by a separator.
-   * Value of property indicates maximum number of accession codes to send at a
-   * time.
-   */
-  public static final Object MULTIACC = "MULTIACC";
-
-  /**
-   * DB query returns an alignment for each accession provided.
+   * List of databases whose sequences might have coding regions annotated
    */
-  public static final Object ALIGNMENTDB = "ALIGNMENTS";
+  public static final String[] DNACODINGDBS = { EMBL, EMBLCDS, GENEDB,
+      ENSEMBL };
+
+  public static final String[] CODINGDBS = { EMBLCDS, GENEDB, ENSEMBL };
+
+  public static final String[] PROTEINDBS = { UNIPROT, UNIPROTKB,
+      EMBLCDSProduct, ENSEMBL }; // Ensembl ENSP* entries are protein
+
+  public static String[] allSources()
+  {
+    List<String> src = new ArrayList<String>();
+    for (Field f : DBRefSource.class.getFields())
+    {
+      if (String.class.equals(f.getType()))
+      {
+        try
+        {
+          src.add((String) f.get(null));
+        } catch (Exception x)
+        {
+          x.printStackTrace();
+        }
+      }
+    }
+    return src.toArray(new String[0]);
+  }
 }
diff --git a/src/jalview/datamodel/FeatureProperties.java b/src/jalview/datamodel/FeatureProperties.java
index 17a2e64..2692b7b 100644
--- a/src/jalview/datamodel/FeatureProperties.java
+++ b/src/jalview/datamodel/FeatureProperties.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,8 +28,7 @@ package jalview.datamodel;
  */
 public class FeatureProperties
 {
-
-  private static final String EMBL_CODING_FEATURE = "CDS";
+  public static final String EMBL_CODING_FEATURE = "CDS";
 
   public static final String EXONPOS = "exon number";
 
diff --git a/src/jalview/datamodel/GraphLine.java b/src/jalview/datamodel/GraphLine.java
index 5fdd946..da9ceb2 100644
--- a/src/jalview/datamodel/GraphLine.java
+++ b/src/jalview/datamodel/GraphLine.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/HiddenSequences.java b/src/jalview/datamodel/HiddenSequences.java
index 09c1b78..4ace909 100644
--- a/src/jalview/datamodel/HiddenSequences.java
+++ b/src/jalview/datamodel/HiddenSequences.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -33,11 +33,21 @@ public class HiddenSequences
 
   AlignmentI alignment;
 
+  /**
+   * Constructor given a reference to an alignment (with no hidden sequences)
+   * 
+   * @param al
+   */
   public HiddenSequences(AlignmentI al)
   {
     alignment = al;
   }
 
+  /**
+   * Answers the number of hidden sequences
+   * 
+   * @return
+   */
   public int getSize()
   {
     if (hiddenSequences == null)
@@ -45,9 +55,9 @@ public class HiddenSequences
       return 0;
     }
     int count = 0;
-    for (int i = 0; i < hiddenSequences.length; i++)
+    for (SequenceI seq : hiddenSequences)
     {
-      if (hiddenSequences[i] != null)
+      if (seq != null)
       {
         count++;
       }
@@ -56,15 +66,23 @@ public class HiddenSequences
     return count;
   }
 
+  /**
+   * Answers the length of the longest hidden sequence
+   * 
+   * @return
+   */
   public int getWidth()
   {
+    if (hiddenSequences == null)
+    {
+      return 0;
+    }
     int width = 0;
-    for (int i = 0; i < hiddenSequences.length; i++)
+    for (SequenceI seq : hiddenSequences)
     {
-      if (hiddenSequences[i] != null
-              && hiddenSequences[i].getLength() > width)
+      if (seq != null && seq.getLength() > width)
       {
-        width = hiddenSequences[i].getLength();
+        width = seq.getLength();
       }
     }
 
@@ -72,7 +90,7 @@ public class HiddenSequences
   }
 
   /**
-   * Call this method if sequences are removed from the main alignment
+   * Call this method after a sequence is removed from the main alignment
    */
   public void adjustHeightSequenceDeleted(int seqIndex)
   {
@@ -108,8 +126,7 @@ public class HiddenSequences
   }
 
   /**
-   * Call this method if sequences are added to or removed from the main
-   * alignment
+   * Call this method after a sequence is added to the main alignment
    */
   public void adjustHeightSequenceAdded()
   {
@@ -125,6 +142,11 @@ public class HiddenSequences
     hiddenSequences = tmp;
   }
 
+  /**
+   * Mark the specified sequence as hidden
+   * 
+   * @param sequence
+   */
   public void hideSequence(SequenceI sequence)
   {
     if (hiddenSequences == null)
@@ -163,6 +185,17 @@ public class HiddenSequences
     return revealedSeqs;
   }
 
+  /**
+   * Reveals (unhides) consecutive hidden sequences just above the given
+   * alignment index. The revealed sequences are selected (including their
+   * visible representative sequence if there was one and 'reveal' is being
+   * performed on it).
+   * 
+   * @param alignmentIndex
+   * @param hiddenRepSequences
+   *          a map of representative sequences to the sequences they represent
+   * @return
+   */
   public List<SequenceI> showSequence(int alignmentIndex,
           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
   {
@@ -203,20 +236,22 @@ public class HiddenSequences
                     + " has been deleted whilst hidden");
           }
         }
-
       }
     }
-
     return revealedSeqs;
   }
 
   public SequenceI getHiddenSequence(int alignmentIndex)
   {
-    return hiddenSequences[alignmentIndex];
+    return hiddenSequences == null ? null : hiddenSequences[alignmentIndex];
   }
 
   public int findIndexWithoutHiddenSeqs(int alignmentIndex)
   {
+    if (hiddenSequences == null)
+    {
+      return alignmentIndex;
+    }
     int index = 0;
     int hiddenSeqs = 0;
     if (hiddenSequences.length <= alignmentIndex)
@@ -232,13 +267,16 @@ public class HiddenSequences
       }
       index++;
     }
-    ;
 
     return (alignmentIndex - hiddenSeqs);
   }
 
   public int adjustForHiddenSeqs(int alignmentIndex)
   {
+    if (hiddenSequences == null)
+    {
+      return alignmentIndex;
+    }
     int index = 0;
     int hSize = hiddenSequences.length;
     while (index <= alignmentIndex && index < hSize)
@@ -254,22 +292,37 @@ public class HiddenSequences
     return alignmentIndex;
   }
 
+  /**
+   * makes a copy of the alignment with hidden sequences included. Using the
+   * copy for anything other than simple output is not recommended. Note - this
+   * method DOES NOT USE THE AlignmentI COPY CONSTRUCTOR!
+   * 
+   * @return
+   */
   public AlignmentI getFullAlignment()
   {
-    int isize = hiddenSequences.length;
-    SequenceI[] seq = new Sequence[isize];
-
-    int index = 0;
-    for (int i = 0; i < hiddenSequences.length; i++)
+    SequenceI[] seq;
+    if (hiddenSequences == null)
     {
-      if (hiddenSequences[i] != null)
-      {
-        seq[i] = hiddenSequences[i];
-      }
-      else
+      seq = alignment.getSequencesArray();
+    }
+    else
+    {
+      int isize = hiddenSequences.length;
+      seq = new Sequence[isize];
+
+      int index = 0;
+      for (int i = 0; i < hiddenSequences.length; i++)
       {
-        seq[i] = alignment.getSequenceAt(index);
-        index++;
+        if (hiddenSequences[i] != null)
+        {
+          seq[i] = hiddenSequences[i];
+        }
+        else
+        {
+          seq[i] = alignment.getSequenceAt(index);
+          index++;
+        }
       }
     }
     Alignment fAlignmt = new Alignment(seq);
@@ -277,6 +330,7 @@ public class HiddenSequences
     fAlignmt.alignmentProperties = alignment.getProperties();
     fAlignmt.groups = alignment.getGroups();
     fAlignmt.hasRNAStructure = alignment.hasRNAStructure();
+    fAlignmt.setSeqrep(alignment.getSeqrep());
 
     return fAlignmt;
   }
diff --git a/src/jalview/datamodel/IncompleteCodonException.java b/src/jalview/datamodel/IncompleteCodonException.java
index f2b5a50..29848ed 100644
--- a/src/jalview/datamodel/IncompleteCodonException.java
+++ b/src/jalview/datamodel/IncompleteCodonException.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/Mapping.java b/src/jalview/datamodel/Mapping.java
index 2e76e41..cfba5f1 100644
--- a/src/jalview/datamodel/Mapping.java
+++ b/src/jalview/datamodel/Mapping.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,7 @@
  */
 package jalview.datamodel;
 
+import jalview.util.Comparison;
 import jalview.util.MapList;
 
 import java.util.Iterator;
@@ -48,6 +49,11 @@ public class Mapping
     private final char[] alignedSeq;
 
     /*
+     * the sequence start residue
+     */
+    private int start;
+
+    /*
      * Next position (base 0) in the aligned sequence
      */
     private int alignedColumn = 0;
@@ -90,13 +96,14 @@ public class Mapping
     /**
      * Constructor
      * 
-     * @param cs
-     *          the aligned sequence characters
+     * @param seq
+     *          the aligned sequence
      * @param gapChar
      */
-    public AlignedCodonIterator(char[] cs, char gapChar)
+    public AlignedCodonIterator(SequenceI seq, char gapChar)
     {
-      this.alignedSeq = cs;
+      this.alignedSeq = seq.getSequence();
+      this.start = seq.getStart();
       this.gap = gapChar;
       fromRanges = map.getFromRanges().iterator();
       toRanges = map.getToRanges().iterator();
@@ -149,8 +156,9 @@ public class Mapping
       int[] alignedCodon = getAlignedCodon(codon);
 
       String peptide = getPeptide();
+      int peptideCol = toPosition - 1 - Mapping.this.to.getStart();
       return new AlignedCodon(alignedCodon[0], alignedCodon[1],
-              alignedCodon[2], peptide);
+              alignedCodon[2], peptide, peptideCol);
     }
 
     /**
@@ -158,6 +166,8 @@ public class Mapping
      * sequence.
      * 
      * @return
+     * @throws NoSuchElementException
+     *           if the 'toRange' is exhausted (nothing to map to)
      */
     private String getPeptide()
     {
@@ -165,7 +175,8 @@ public class Mapping
       // i.e. code like getNextCodon()
       if (toPosition <= currentToRange[1])
       {
-        char pep = Mapping.this.to.getSequence()[toPosition - 1];
+        SequenceI seq = Mapping.this.to;
+        char pep = seq.getSequence()[toPosition - seq.getStart()];
         toPosition++;
         return String.valueOf(pep);
       }
@@ -242,10 +253,14 @@ public class Mapping
      */
     private int getAlignedColumn(int sequencePos)
     {
-      while (alignedBases < sequencePos
-              && alignedColumn < alignedSeq.length)
+      /*
+       * allow for offset e.g. treat pos 8 as 2 if sequence starts at 7
+       */
+      int truePos = sequencePos - (start - 1);
+      while (alignedBases < truePos && alignedColumn < alignedSeq.length)
       {
-        if (alignedSeq[alignedColumn++] != gap)
+        char c = alignedSeq[alignedColumn++];
+        if (c != gap && !Comparison.isGap(c))
         {
           alignedBases++;
         }
@@ -261,18 +276,23 @@ public class Mapping
 
   }
 
-  /**
+  /*
    * Contains the start-end pairs mapping from the associated sequence to the
    * sequence in the database coordinate system. It also takes care of step
    * difference between coordinate systems.
    */
   MapList map = null;
 
-  /**
+  /*
    * The sequence that map maps the associated sequence to (if any).
    */
   SequenceI to = null;
 
+  /*
+   * optional sequence id for the 'from' ranges
+   */
+  private String mappedFromId;
+
   public Mapping(MapList map)
   {
     super();
@@ -320,6 +340,7 @@ public class Mapping
         map = new MapList(map2.map);
       }
       to = map2.to;
+      mappedFromId = map2.mappedFromId;
     }
   }
 
@@ -343,14 +364,13 @@ public class Mapping
   /**
    * Equals that compares both the to references and MapList mappings.
    * 
-   * @param other
+   * @param o
    * @return
+   * @see MapList#equals
    */
   @Override
   public boolean equals(Object o)
   {
-    // TODO should override Object.hashCode() to ensure that equal objects have
-    // equal hashcodes
     if (o == null || !(o instanceof Mapping))
     {
       return false;
@@ -377,6 +397,21 @@ public class Mapping
   }
 
   /**
+   * Returns a hashCode made from the sequence and maplist
+   */
+  @Override
+  public int hashCode()
+  {
+    int hashCode = (this.to == null ? 1 : this.to.hashCode());
+    if (this.map != null)
+    {
+      hashCode = hashCode * 31 + this.map.hashCode();
+    }
+
+    return hashCode;
+  }
+
+  /**
    * get the 'initial' position in the associated sequence for a position in the
    * mapped reference frame
    * 
@@ -683,6 +718,7 @@ public class Mapping
    * 
    * @see java.lang.Object#finalize()
    */
+  @Override
   protected void finalize() throws Throwable
   {
     map = null;
@@ -690,9 +726,46 @@ public class Mapping
     super.finalize();
   }
 
+  /**
+   * Returns an iterator which can serve up the aligned codon column positions
+   * and their corresponding peptide products
+   * 
+   * @param seq
+   *          an aligned (i.e. possibly gapped) sequence
+   * @param gapChar
+   * @return
+   */
   public Iterator<AlignedCodon> getCodonIterator(SequenceI seq, char gapChar)
   {
-    return new AlignedCodonIterator(seq.getSequence(), gapChar);
+    return new AlignedCodonIterator(seq, gapChar);
+  }
+
+  /**
+   * Readable representation for debugging only, not guaranteed not to change
+   */
+  @Override
+  public String toString()
+  {
+    return String.format("%s %s", this.map.toString(), this.to == null ? ""
+            : this.to.getName());
+  }
+
+  /**
+   * Returns the identifier for the 'from' range sequence, or null if not set
+   * 
+   * @return
+   */
+  public String getMappedFromId()
+  {
+    return mappedFromId;
+  }
+
+  /**
+   * Sets the identifier for the 'from' range sequence
+   */
+  public void setMappedFromId(String mappedFromId)
+  {
+    this.mappedFromId = mappedFromId;
   }
 
 }
diff --git a/src/jalview/datamodel/MappingType.java b/src/jalview/datamodel/MappingType.java
new file mode 100644
index 0000000..e15f840
--- /dev/null
+++ b/src/jalview/datamodel/MappingType.java
@@ -0,0 +1,83 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.datamodel;
+
+/**
+ * An enumeration of the kinds of mapping (from nucleotide or peptide, to
+ * nucleotide or peptide), and the corresponding word lengths
+ */
+public enum MappingType
+{
+  NucleotideToPeptide(3, 1)
+  {
+    @Override
+    public MappingType getInverse()
+    {
+      return PeptideToNucleotide;
+    }
+  },
+  PeptideToNucleotide(1, 3)
+  {
+    @Override
+    public MappingType getInverse()
+    {
+      return NucleotideToPeptide;
+    }
+  },
+  NucleotideToNucleotide(1, 1)
+  {
+    @Override
+    public MappingType getInverse()
+    {
+      return NucleotideToNucleotide;
+    }
+  },
+  PeptideToPeptide(1, 1)
+  {
+    @Override
+    public MappingType getInverse()
+    {
+      return PeptideToPeptide;
+    }
+  };
+
+  private int fromRatio;
+
+  private int toRatio;
+
+  private MappingType(int fromSize, int toSize)
+  {
+    fromRatio = fromSize;
+    toRatio = toSize;
+  }
+
+  public abstract MappingType getInverse();
+
+  public int getFromRatio()
+  {
+    return fromRatio;
+  }
+
+  public int getToRatio()
+  {
+    return toRatio;
+  }
+}
diff --git a/src/jalview/datamodel/NodeTransformI.java b/src/jalview/datamodel/NodeTransformI.java
index 4bc3871..b095838 100644
--- a/src/jalview/datamodel/NodeTransformI.java
+++ b/src/jalview/datamodel/NodeTransformI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/PDBEntry.java b/src/jalview/datamodel/PDBEntry.java
index 06790d7..6579935 100644
--- a/src/jalview/datamodel/PDBEntry.java
+++ b/src/jalview/datamodel/PDBEntry.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,29 +20,67 @@
  */
 package jalview.datamodel;
 
+import jalview.util.CaseInsensitiveString;
+
+import java.util.Collections;
+import java.util.Enumeration;
 import java.util.Hashtable;
 
 public class PDBEntry
 {
+
+  /**
+   * constant for storing chain code in properties table
+   */
+  private static final String CHAIN_ID = "chain_code";
+
+  private Hashtable<String, Object> properties;
+
+  private static final int PDB_ID_LENGTH = 4;
+
   private String file;
 
   private String type;
 
   private String id;
 
-  private String chainCode;
-
   public enum Type
   {
-    PDB, FILE
+    PDB, MMCIF, FILE;
+    /**
+     * case insensitive matching for Type enum
+     * 
+     * @param value
+     * @return
+     */
+    public static Type getType(String value)
+    {
+      for (Type t : Type.values())
+      {
+        if (t.toString().equalsIgnoreCase(value))
+        {
+          return t;
+        }
+      }
+      return null;
+    }
+
+    /**
+     * case insensitive equivalence for strings resolving to PDBEntry type
+     * 
+     * @param t
+     * @return
+     */
+    public boolean matches(String t)
+    {
+      return (this.toString().equalsIgnoreCase(t));
+    }
   }
 
-  Hashtable properties;
 
-  /*
-   * (non-Javadoc)
-   * 
-   * @see java.lang.Object#equals(java.lang.Object)
+  /**
+   * Answers true if obj is a PDBEntry with the same id and chain code (both
+   * ignoring case), file, type and properties
    */
   @Override
   public boolean equals(Object obj)
@@ -56,17 +94,24 @@ public class PDBEntry
       return true;
     }
     PDBEntry o = (PDBEntry) obj;
-    return (type == o.type || (type != null && o.type != null && o.type
-            .equals(type)))
-            && (id == o.id || (id != null && o.id != null && o.id
-                    .equalsIgnoreCase(id)))
-            && (chainCode == o.chainCode || (chainCode != null
-                    && o.chainCode != null && o.chainCode
-                      .equalsIgnoreCase(chainCode)))
-            && (properties == o.properties || (properties != null
-                    && o.properties != null && properties
-                      .equals(o.properties)));
 
+    /*
+     * note that chain code is stored as a property wrapped by a 
+     * CaseInsensitiveString, so we are in effect doing a 
+     * case-insensitive comparison of chain codes
+     */
+    boolean idMatches = id == o.id
+            || (id != null && id.equalsIgnoreCase(o.id));
+    boolean fileMatches = file == o.file
+            || (file != null && file.equals(o.file));
+    boolean typeMatches = type == o.type
+            || (type != null && type.equals(o.type));
+    if (idMatches && fileMatches && typeMatches)
+    {
+      return properties == o.properties
+              || (properties != null && properties.equals(o.properties));
+    }
+    return false;
   }
 
   /**
@@ -76,24 +121,25 @@ public class PDBEntry
   {
   }
 
-  /**
-   * Constructor given file path and PDB id.
-   * 
-   * @param filePath
-   */
-  // public PDBEntry(String filePath, String pdbId)
-  // {
-  // this.file = filePath;
-  // this.id = pdbId;
-  // }
 
   public PDBEntry(String pdbId, String chain, PDBEntry.Type type,
           String filePath)
   {
+    init(pdbId, chain, type, filePath);
+  }
+
+  /**
+   * @param pdbId
+   * @param chain
+   * @param entryType
+   * @param filePath
+   */
+  void init(String pdbId, String chain, PDBEntry.Type entryType, String filePath)
+  {
     this.id = pdbId;
-    this.chainCode = chain;
-    this.type = type == null ? null : type.toString();
+    this.type = entryType == null ? null : entryType.toString();
     this.file = filePath;
+    setChainCode(chain);
   }
 
   /**
@@ -106,16 +152,44 @@ public class PDBEntry
     file = entry.file;
     type = entry.type;
     id = entry.id;
-    chainCode = entry.chainCode;
     if (entry.properties != null)
     {
-      properties = (Hashtable) entry.properties.clone();
+      properties = (Hashtable<String, Object>) entry.properties.clone();
     }
   }
 
-  public void setFile(String file)
+  /**
+   * Make a PDBEntry from a DBRefEntry. The accession code is used for the PDB
+   * id, but if it is 5 characters in length, the last character is removed and
+   * set as the chain code instead.
+   * 
+   * @param dbr
+   */
+  public PDBEntry(DBRefEntry dbr)
   {
-    this.file = file;
+    if (!DBRefSource.PDB.equals(dbr.getSource()))
+    {
+      throw new IllegalArgumentException("Invalid source: "
+              + dbr.getSource());
+    }
+
+    String pdbId = dbr.getAccessionId();
+    String chainCode = null;
+    if (pdbId.length() == PDB_ID_LENGTH + 1)
+    {
+      char chain = pdbId.charAt(PDB_ID_LENGTH);
+      if (('a' <= chain && chain <= 'z') || ('A' <= chain && chain <= 'Z'))
+      {
+        pdbId = pdbId.substring(0, PDB_ID_LENGTH);
+        chainCode = String.valueOf(chain);
+      }
+    }
+    init(pdbId, chainCode, null, null);
+  }
+
+  public void setFile(String f)
+  {
+    this.file = f;
   }
 
   public String getFile()
@@ -148,28 +222,211 @@ public class PDBEntry
     return id;
   }
 
-  public void setProperty(Hashtable property)
+  public void setProperty(String key, Object value)
   {
-    this.properties = property;
+    if (this.properties == null)
+    {
+      this.properties = new Hashtable<String, Object>();
+    }
+    properties.put(key, value);
   }
 
-  public Hashtable getProperty()
+  public Object getProperty(String key)
   {
-    return properties;
+    return properties == null ? null : properties.get(key);
   }
 
+  /**
+   * Returns an enumeration of the keys of this object's properties (or an empty
+   * enumeration if it has no properties)
+   * 
+   * @return
+   */
+  public Enumeration<String> getProperties()
+  {
+    if (properties == null)
+    {
+      return Collections.emptyEnumeration();
+    }
+    return properties.keys();
+  }
+
+  /**
+   * 
+   * @return null or a string for associated chain IDs
+   */
   public String getChainCode()
   {
-    return chainCode;
+    return (properties == null || properties.get(CHAIN_ID) == null) ? null
+            : properties.get(CHAIN_ID).toString();
   }
 
+  /**
+   * Sets a non-case-sensitive property for the given chain code. Two PDBEntry
+   * objects which differ only in the case of their chain code are considered
+   * equal. This avoids duplication of objects in lists of PDB ids.
+   * 
+   * @param chainCode
+   */
   public void setChainCode(String chainCode)
   {
-    this.chainCode = chainCode;
+    if (chainCode == null)
+    {
+      deleteProperty(CHAIN_ID);
+    }
+    else
+    {
+      setProperty(CHAIN_ID, new CaseInsensitiveString(chainCode));
+    }
+  }
+
+  /**
+   * Deletes the property with the given key, and returns the deleted value (or
+   * null)
+   */
+  Object deleteProperty(String key)
+  {
+    Object result = null;
+    if (properties != null)
+    {
+      result = properties.remove(key);
+    }
+    return result;
   }
 
+  @Override
   public String toString()
   {
     return id;
   }
+
+  /**
+   * Getter provided for Castor binding only. Application code should call
+   * getProperty() or getProperties() instead.
+   * 
+   * @deprecated
+   * @see #getProperty(String)
+   * @see #getProperties()
+   * @see jalview.ws.dbsources.Uniprot#getUniprotEntries
+   * @return
+   */
+  @Deprecated
+  public Hashtable<String, Object> getProps()
+  {
+    return properties;
+  }
+
+  /**
+   * Setter provided for Castor binding only. Application code should call
+   * setProperty() instead.
+   * 
+   * @deprecated
+   * @return
+   */
+  @Deprecated
+  public void setProps(Hashtable<String, Object> props)
+  {
+    properties = props;
+  }
+
+  /**
+   * Answers true if this object is either equivalent to, or can be 'improved'
+   * by, the given entry.
+   * <p>
+   * If newEntry has the same id (ignoring case), and doesn't have a conflicting
+   * file spec or chain code, then update this entry from its file and/or chain
+   * code.
+   * 
+   * @param newEntry
+   * @return true if modifications were made
+   */
+  public boolean updateFrom(PDBEntry newEntry)
+  {
+    if (this.equals(newEntry))
+    {
+      return true;
+    }
+
+    String newId = newEntry.getId();
+    if (newId == null || getId() == null)
+    {
+      return false; // shouldn't happen
+    }
+
+    /*
+     * id has to match (ignoring case)
+     */
+    if (!getId().equalsIgnoreCase(newId))
+    {
+      return false;
+    }
+
+    /*
+     * Don't update if associated with different structure files
+     */
+    String newFile = newEntry.getFile();
+    if (newFile != null && getFile() != null && !newFile.equals(getFile()))
+    {
+      return false;
+    }
+
+    /*
+     * Don't update if associated with different chains (ignoring case)
+     */
+    String newChain = newEntry.getChainCode();
+    if (newChain != null && newChain.length() > 0 && getChainCode() != null
+            && getChainCode().length() > 0
+            && !getChainCode().equalsIgnoreCase(newChain))
+    {
+      return false;
+    }
+
+    /*
+     * set file path if not already set
+     */
+    String newType = newEntry.getType();
+    if (getFile() == null && newFile != null)
+    {
+      setFile(newFile);
+      setType(newType);
+    }
+
+    /*
+     * set file type if new entry has it and we don't
+     * (for the case where file was not updated)
+     */
+    if (getType() == null && newType != null)
+    {
+      setType(newType);
+    }
+
+    /*
+     * set chain if not already set (we excluded differing 
+     * chains earlier) (ignoring case change only)
+     */
+    if (newChain != null && newChain.length() > 0
+            && !newChain.equalsIgnoreCase(getChainCode()))
+    {
+      setChainCode(newChain);
+    }
+
+    /*
+     * copy any new or modified properties
+     */
+    Enumeration<String> newProps = newEntry.getProperties();
+    while (newProps.hasMoreElements())
+    {
+      /*
+       * copy properties unless value matches; this defends against changing
+       * the case of chain_code which is wrapped in a CaseInsensitiveString
+       */
+      String key = newProps.nextElement();
+      Object value = newEntry.getProperty(key);
+      if (!value.equals(getProperty(key)))
+      {
+        setProperty(key, value);
+      }
+    }
+    return true;
+  }
 }
diff --git a/src/jalview/datamodel/Profile.java b/src/jalview/datamodel/Profile.java
new file mode 100644
index 0000000..b581242
--- /dev/null
+++ b/src/jalview/datamodel/Profile.java
@@ -0,0 +1,163 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.datamodel;
+
+
+/**
+ * A profile for one column of an alignment
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class Profile implements ProfileI
+{
+  /*
+   * an object holding counts of symbols in the profile
+   */
+  private ResidueCount counts;
+
+  /*
+   * the number of sequences (gapped or not) in the profile
+   */
+  private int height;
+
+  /*
+   * the number of non-gapped sequences in the profile
+   */
+  private int gapped;
+
+  /*
+   * the highest count for any residue in the profile
+   */
+  private int maxCount;
+
+  /*
+   * the residue (e.g. K) or residues (e.g. KQW) with the
+   * highest count in the profile
+   */
+  private String modalResidue;
+
+  /**
+   * Constructor which allows derived data to be stored without having to store
+   * the full profile
+   * 
+   * @param seqCount
+   *          the number of sequences in the profile
+   * @param gaps
+   *          the number of gapped sequences
+   * @param max
+   *          the highest count for any residue
+   * @param modalres
+   *          the residue (or concatenated residues) with the highest count
+   */
+  public Profile(int seqCount, int gaps, int max, String modalRes)
+  {
+    this.height = seqCount;
+    this.gapped = gaps;
+    this.maxCount = max;
+    this.modalResidue = modalRes;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#setCounts(jalview.datamodel.ResidueCount)
+   */
+  @Override
+  public void setCounts(ResidueCount residueCounts)
+  {
+    this.counts = residueCounts;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getPercentageIdentity(boolean)
+   */
+  @Override
+  public float getPercentageIdentity(boolean ignoreGaps)
+  {
+    if (height == 0)
+    {
+      return 0f;
+    }
+    float pid = 0f;
+    if (ignoreGaps && gapped < height)
+    {
+      pid = (maxCount * 100f) / (height - gapped);
+    }
+    else
+    {
+      pid = (maxCount * 100f) / height;
+    }
+    return pid;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getCounts()
+   */
+  @Override
+  public ResidueCount getCounts()
+  {
+    return counts;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getHeight()
+   */
+  @Override
+  public int getHeight()
+  {
+    return height;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getGapped()
+   */
+  @Override
+  public int getGapped()
+  {
+    return gapped;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getMaxCount()
+   */
+  @Override
+  public int getMaxCount()
+  {
+    return maxCount;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getModalResidue()
+   */
+  @Override
+  public String getModalResidue()
+  {
+    return modalResidue;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getNonGapped()
+   */
+  @Override
+  public int getNonGapped()
+  {
+    return height - gapped;
+  }
+}
diff --git a/src/jalview/datamodel/ProfileI.java b/src/jalview/datamodel/ProfileI.java
new file mode 100644
index 0000000..99d1df9
--- /dev/null
+++ b/src/jalview/datamodel/ProfileI.java
@@ -0,0 +1,87 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.datamodel;
+
+public interface ProfileI
+{
+
+  /**
+   * Set the full profile of counts
+   * 
+   * @param residueCounts
+   */
+  public abstract void setCounts(ResidueCount residueCounts);
+
+  /**
+   * Returns the percentage identity of the profile, i.e. the highest proportion
+   * of conserved (equal) symbols. The percentage is as a fraction of all
+   * sequences, or only ungapped sequences if flag ignoreGaps is set true.
+   * 
+   * @param ignoreGaps
+   * @return
+   */
+  public abstract float getPercentageIdentity(boolean ignoreGaps);
+
+  /**
+   * Returns the full symbol counts for this profile
+   * 
+   * @return
+   */
+  public abstract ResidueCount getCounts();
+
+  /**
+   * Returns the number of sequences in the profile
+   * 
+   * @return
+   */
+  public abstract int getHeight();
+
+  /**
+   * Returns the number of sequences in the profile which had a gap character
+   * (or were too short to be included in this column's profile)
+   * 
+   * @return
+   */
+  public abstract int getGapped();
+
+  /**
+   * Returns the highest count for any symbol(s) in the profile
+   * 
+   * @return
+   */
+  public abstract int getMaxCount();
+
+  /**
+   * Returns the symbol (or concatenated symbols) which have the highest count
+   * in the profile, or an empty string if there were no symbols counted
+   * 
+   * @return
+   */
+  public abstract String getModalResidue();
+
+  /**
+   * Answers the number of non-gapped sequences in the profile
+   * 
+   * @return
+   */
+  public abstract int getNonGapped();
+
+}
\ No newline at end of file
diff --git a/src/jalview/datamodel/xdb/embl/BasePosition.java b/src/jalview/datamodel/Profiles.java
similarity index 51%
rename from src/jalview/datamodel/xdb/embl/BasePosition.java
rename to src/jalview/datamodel/Profiles.java
index d3ad382..4142e44 100644
--- a/src/jalview/datamodel/xdb/embl/BasePosition.java
+++ b/src/jalview/datamodel/Profiles.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,51 +18,46 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.datamodel.xdb.embl;
+package jalview.datamodel;
 
-/**
- * Data model for a feature/location/locationElement/basePosition read from an
- * EMBL query reply
- * 
- * @see embl_mapping.xml
- */
-public class BasePosition
+public class Profiles implements ProfilesI
 {
-  String type;
 
-  String pos;
+  private ProfileI[] profiles;
 
-  /**
-   * @return the pos
-   */
-  public String getPos()
+  public Profiles(ProfileI[] p)
   {
-    return pos;
+    profiles = p;
   }
 
   /**
-   * @param pos
-   *          the pos to set
+   * Returns the profile for the given column, or null if none found
+   * 
+   * @param col
    */
-  public void setPos(String pos)
+  @Override
+  public ProfileI get(int col)
   {
-    this.pos = pos;
+    return profiles != null && col >= 0 && col < profiles.length ? profiles[col]
+            : null;
   }
 
   /**
-   * @return the type
+   * Returns the first column (base 0) covered by the profiles
    */
-  public String getType()
+  @Override
+  public int getStartColumn()
   {
-    return type;
+    return 0;
   }
 
   /**
-   * @param type
-   *          the type to set
+   * Returns the last column (base 0) covered by the profiles
    */
-  public void setType(String type)
+  @Override
+  public int getEndColumn()
   {
-    this.type = type;
+    return profiles == null ? 0 : profiles.length - 1;
   }
+
 }
diff --git a/src/jalview/datamodel/NodeTransformI.java b/src/jalview/datamodel/ProfilesI.java
similarity index 78%
copy from src/jalview/datamodel/NodeTransformI.java
copy to src/jalview/datamodel/ProfilesI.java
index 4bc3871..607af4b 100644
--- a/src/jalview/datamodel/NodeTransformI.java
+++ b/src/jalview/datamodel/ProfilesI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,11 +20,13 @@
  */
 package jalview.datamodel;
 
-/**
- * @author JimP
- * 
- */
-public interface NodeTransformI
+public interface ProfilesI
 {
-  public void transform(BinaryNode node);
+
+  ProfileI get(int i);
+
+  int getStartColumn();
+
+  int getEndColumn();
+
 }
diff --git a/src/jalview/datamodel/Provenance.java b/src/jalview/datamodel/Provenance.java
index 48752aa..de45129 100644
--- a/src/jalview/datamodel/Provenance.java
+++ b/src/jalview/datamodel/Provenance.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/ProvenanceEntry.java b/src/jalview/datamodel/ProvenanceEntry.java
index 8f168ab..926dcbd 100644
--- a/src/jalview/datamodel/ProvenanceEntry.java
+++ b/src/jalview/datamodel/ProvenanceEntry.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/ResidueCount.java b/src/jalview/datamodel/ResidueCount.java
new file mode 100644
index 0000000..de852dd
--- /dev/null
+++ b/src/jalview/datamodel/ResidueCount.java
@@ -0,0 +1,641 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.datamodel;
+
+import jalview.util.Comparison;
+import jalview.util.Format;
+import jalview.util.QuickSort;
+import jalview.util.SparseCount;
+
+/**
+ * A class to count occurrences of residues in a profile, optimised for speed
+ * and memory footprint.
+ * @author gmcarstairs
+ *
+ */
+public class ResidueCount
+{
+  /**
+   * A data bean to hold the results of counting symbols
+   */
+  public class SymbolCounts
+  {
+    /**
+     * the symbols seen (as char values), in no particular order
+     */
+    public final char[] symbols;
+
+    /**
+     * the counts for each symbol, in the same order as the symbols
+     */
+    public final int[] values;
+
+    SymbolCounts(char[] s, int[] v)
+    {
+      symbols = s;
+      values = v;
+    }
+  }
+
+  private static final int TOUPPERCASE = 'A' - 'a';
+
+  /*
+   * nucleotide symbols to count (including N unknown)
+   */
+  private static final String NUCS = "ACGNTU";
+
+  /*
+   * amino acid symbols to count (including X unknown)
+   * NB we also include U so as to support counting of RNA bases
+   * in the "don't know" case of nucleotide / peptide
+   */
+  private static final String AAS = "ACDEFGHIKLMNPQRSTUVWXY";
+
+  private static final int GAP_COUNT = 0;
+
+  /*
+   * fast lookup tables holding the index into our count
+   * arrays of each symbol; index 0 is reserved for gap counting
+   */
+  private static int[] NUC_INDEX = new int[26];
+
+  private static int[] AA_INDEX = new int[26];
+  static
+  {
+    for (int i = 0; i < NUCS.length(); i++)
+    {
+      NUC_INDEX[NUCS.charAt(i) - 'A'] = i + 1;
+    }
+    for (int i = 0; i < AAS.length(); i++)
+    {
+      AA_INDEX[AAS.charAt(i) - 'A'] = i + 1;
+    }
+  }
+
+  /*
+   * counts array, just big enough for the nucleotide or peptide
+   * character set (plus gap counts in position 0)
+   */
+  private short[] counts;
+
+  /*
+   * alternative array of int counts for use if any count 
+   * exceeds the maximum value of short (32767)
+   */
+  private int[] intCounts;
+
+  /*
+   * flag set if we switch from short to int counts
+   */
+  private boolean useIntCounts;
+
+  /*
+   * general-purpose counter, only for use for characters
+   * that are not in the expected alphabet
+   */
+  private SparseCount otherData;
+
+  /*
+   * keeps track of the maximum count value recorded
+   * (if this class ever allows decrements, would need to
+   * calculate this on request instead) 
+   */
+  int maxCount;
+
+  /*
+   * if we think we are counting nucleotide, can get by with smaller
+   * array to hold counts
+   */
+  private boolean isNucleotide;
+
+  /**
+   * Default constructor allocates arrays able to count either nucleotide or
+   * peptide bases. Use this constructor if not sure which the data is.
+   */
+  public ResidueCount()
+  {
+    this(false);
+  }
+
+  /**
+   * Constructor that allocates an array just big enough for the anticipated
+   * characters, plus one position to count gaps
+   */
+  public ResidueCount(boolean nucleotide)
+  {
+    isNucleotide = nucleotide;
+    int charsToCount = nucleotide ? NUCS.length() : AAS.length();
+    counts = new short[charsToCount + 1];
+  }
+
+  /**
+   * Increments the count for the given character. The supplied character may be
+   * upper or lower case but counts are for the upper case only. Gap characters
+   * (space, ., -) are all counted together.
+   * 
+   * @param c
+   * @return the new value of the count for the character
+   */
+  public int add(final char c)
+  {
+    char u = toUpperCase(c);
+    int newValue = 0;
+    int offset = getOffset(u);
+
+    /*
+     * offset 0 is reserved for gap counting, so 0 here means either
+     * an unexpected character, or a gap character passed in error
+     */
+    if (offset == 0)
+    {
+      if (Comparison.isGap(u))
+      {
+        newValue = addGap();
+      }
+      else
+      {
+        newValue = addOtherCharacter(u);
+      }
+    }
+    else
+    {
+      newValue = increment(offset);
+    }
+    return newValue;
+  }
+
+  /**
+   * Increment the count at the specified offset. If this would result in short
+   * overflow, promote to counting int values instead.
+   * 
+   * @param offset
+   * @return the new value of the count at this offset
+   */
+  int increment(int offset)
+  {
+    int newValue = 0;
+    if (useIntCounts)
+    {
+      newValue = intCounts[offset];
+      intCounts[offset] = ++newValue;
+    }
+    else
+    {
+      if (counts[offset] == Short.MAX_VALUE)
+      {
+        handleOverflow();
+        newValue = intCounts[offset];
+        intCounts[offset] = ++newValue;
+      }
+      else
+      {
+        newValue = counts[offset];
+        counts[offset] = (short) ++newValue;
+      }
+    }
+    maxCount = Math.max(maxCount, newValue);
+    return newValue;
+  }
+
+  /**
+   * Switch from counting in short to counting in int
+   */
+  synchronized void handleOverflow()
+  {
+    intCounts = new int[counts.length];
+    for (int i = 0; i < counts.length; i++)
+    {
+      intCounts[i] = counts[i];
+    }
+    counts = null;
+    useIntCounts = true;
+  }
+
+  /**
+   * Returns this character's offset in the count array
+   * 
+   * @param c
+   * @return
+   */
+  int getOffset(char c)
+  {
+    int offset = 0;
+    if ('A' <= c && c <= 'Z')
+    {
+      offset = isNucleotide ? NUC_INDEX[c - 'A'] : AA_INDEX[c - 'A'];
+    }
+    return offset;
+  }
+
+  /**
+   * @param c
+   * @return
+   */
+  protected char toUpperCase(final char c)
+  {
+    char u = c;
+    if ('a' <= c && c <= 'z')
+    {
+      u = (char) (c + TOUPPERCASE);
+    }
+    return u;
+  }
+
+  /**
+   * Increment count for some unanticipated character. The first time this
+   * called, a SparseCount is instantiated to hold these 'extra' counts.
+   * 
+   * @param c
+   * @return the new value of the count for the character
+   */
+  int addOtherCharacter(char c)
+  {
+    if (otherData == null)
+    {
+      otherData = new SparseCount();
+    }
+    int newValue = otherData.add(c, 1);
+    maxCount = Math.max(maxCount, newValue);
+    return newValue;
+  }
+
+  /**
+   * Set count for some unanticipated character. The first time this called, a
+   * SparseCount is instantiated to hold these 'extra' counts.
+   * 
+   * @param c
+   * @param value
+   */
+  void setOtherCharacter(char c, int value)
+  {
+    if (otherData == null)
+    {
+      otherData = new SparseCount();
+    }
+    otherData.put(c, value);
+  }
+
+  /**
+   * Increment count of gap characters
+   * 
+   * @return the new count of gaps
+   */
+  public int addGap()
+  {
+    int newValue;
+    if (useIntCounts)
+    {
+      newValue = ++intCounts[GAP_COUNT];
+    }
+    else
+    {
+      newValue = ++counts[GAP_COUNT];
+    }
+    return newValue;
+  }
+
+  /**
+   * Answers true if we are counting ints (only after overflow of short counts)
+   * 
+   * @return
+   */
+  boolean isCountingInts()
+  {
+    return useIntCounts;
+  }
+
+  /**
+   * Sets the count for the given character. The supplied character may be upper
+   * or lower case but counts are for the upper case only.
+   * 
+   * @param c
+   * @param count
+   */
+  public void put(char c, int count)
+  {
+    char u = toUpperCase(c);
+    int offset = getOffset(u);
+
+    /*
+     * offset 0 is reserved for gap counting, so 0 here means either
+     * an unexpected character, or a gap character passed in error
+     */
+    if (offset == 0)
+    {
+      if (Comparison.isGap(u))
+      {
+        set(0, count);
+      }
+      else
+      {
+        setOtherCharacter(u, count);
+        maxCount = Math.max(maxCount, count);
+      }
+    }
+    else
+    {
+      set(offset, count);
+      maxCount = Math.max(maxCount, count);
+    }
+  }
+
+  /**
+   * Sets the count at the specified offset. If this would result in short
+   * overflow, promote to counting int values instead.
+   * 
+   * @param offset
+   * @param value
+   */
+  void set(int offset, int value)
+  {
+    if (useIntCounts)
+    {
+      intCounts[offset] = value;
+    }
+    else
+    {
+      if (value > Short.MAX_VALUE || value < Short.MIN_VALUE)
+      {
+        handleOverflow();
+        intCounts[offset] = value;
+      }
+      else
+      {
+        counts[offset] = (short) value;
+      }
+    }
+  }
+
+  /**
+   * Returns the count for the given character, or zero if no count held
+   * 
+   * @param c
+   * @return
+   */
+  public int getCount(char c)
+  {
+    char u = toUpperCase(c);
+    int offset = getOffset(u);
+    if (offset == 0)
+    {
+      if (!Comparison.isGap(u))
+      {
+        // should have called getGapCount()
+        return otherData == null ? 0 : otherData.get(u);
+      }
+    }
+    return useIntCounts ? intCounts[offset] : counts[offset];
+  }
+
+  public int getGapCount()
+  {
+    return useIntCounts ? intCounts[0] : counts[0];
+  }
+
+  /**
+   * Answers true if this object wraps a counter for unexpected characters
+   * 
+   * @return
+   */
+  boolean isUsingOtherData()
+  {
+    return otherData != null;
+  }
+
+  /**
+   * Returns the character (or concatenated characters) for the symbol(s) with
+   * the given count in the profile. Can be used to get the modal residue by
+   * supplying the modal count value. Returns an empty string if no symbol has
+   * the given count. The symbols are in alphabetic order of standard peptide or
+   * nucleotide characters, followed by 'other' symbols if any.
+   * 
+   * @return
+   */
+  public String getResiduesForCount(int count)
+  {
+    if (count == 0)
+    {
+      return "";
+    }
+
+    /*
+     * find counts for the given value and append the
+     * corresponding symbol
+     */
+    StringBuilder modal = new StringBuilder();
+    if (useIntCounts)
+    {
+      for (int i = 1; i < intCounts.length; i++)
+      {
+        if (intCounts[i] == count)
+        {
+          modal.append(isNucleotide ? NUCS.charAt(i - 1) : AAS
+                  .charAt(i - 1));
+        }
+      }
+    }
+    else
+    {
+      for (int i = 1; i < counts.length; i++)
+      {
+        if (counts[i] == count)
+        {
+          modal.append(isNucleotide ? NUCS.charAt(i - 1) : AAS
+                  .charAt(i - 1));
+        }
+      }
+    }
+    if (otherData != null)
+    {
+      for (int i = 0; i < otherData.size(); i++)
+      {
+        if (otherData.valueAt(i) == count)
+        {
+          modal.append((char) otherData.keyAt(i));
+        }
+      }
+    }
+    return modal.toString();
+  }
+
+  /**
+   * Returns the highest count for any symbol(s) in the profile (excluding gap)
+   * 
+   * @return
+   */
+  public int getModalCount()
+  {
+    return maxCount;
+  }
+
+  /**
+   * Returns the number of distinct symbols with a non-zero count (excluding the
+   * gap symbol)
+   * 
+   * @return
+   */
+  public int size() {
+    int size = 0;
+    if (useIntCounts)
+    {
+      for (int i = 1; i < intCounts.length; i++)
+      {
+        if (intCounts[i] > 0)
+        {
+          size++;
+        }
+      }
+    }
+    else
+    {
+      for (int i = 1; i < counts.length; i++)
+      {
+        if (counts[i] > 0)
+        {
+          size++;
+        }
+      }
+    }
+
+    /*
+     * include 'other' characters recorded (even if count is zero
+     * though that would be a strange use case)
+     */
+    if (otherData != null)
+    {
+      size += otherData.size();
+    }
+
+    return size;
+  }
+
+  /**
+   * Returns a data bean holding those symbols that have a non-zero count
+   * (excluding the gap symbol), with their counts.
+   * 
+   * @return
+   */
+  public SymbolCounts getSymbolCounts()
+  {
+    int size = size();
+    char[] symbols = new char[size];
+    int[] values = new int[size];
+    int j = 0;
+
+    if (useIntCounts)
+    {
+      for (int i = 1; i < intCounts.length; i++)
+      {
+        if (intCounts[i] > 0)
+        {
+          char symbol = isNucleotide ? NUCS.charAt(i - 1) : AAS
+                  .charAt(i - 1);
+          symbols[j] = symbol;
+          values[j] = intCounts[i];
+          j++;
+        }
+      }
+    }
+    else
+    {
+      for (int i = 1; i < counts.length; i++)
+      {
+        if (counts[i] > 0)
+        {
+          char symbol = isNucleotide ? NUCS.charAt(i - 1) : AAS
+                  .charAt(i - 1);
+          symbols[j] = symbol;
+          values[j] = counts[i];
+          j++;
+        }
+      }
+    }
+    if (otherData != null)
+    {
+      for (int i = 0; i < otherData.size(); i++)
+      {
+        symbols[j] = (char) otherData.keyAt(i);
+        values[j] = otherData.valueAt(i);
+        j++;
+      }
+    }
+
+    return new SymbolCounts(symbols, values);
+  }
+
+  /**
+   * Returns a tooltip string showing residues in descending order of their
+   * percentage frequency in the profile
+   * 
+   * @param normaliseBy
+   *          the divisor for residue counts (may or may not include gapped
+   *          sequence count)
+   * @param percentageDecPl
+   *          the number of decimal places to show in percentages
+   * @return
+   */
+  public String getTooltip(int normaliseBy, int percentageDecPl)
+  {
+    SymbolCounts symbolCounts = getSymbolCounts();
+    char[] ca = symbolCounts.symbols;
+    int[] vl = symbolCounts.values;
+
+    /*
+     * sort characters into ascending order of their counts
+     */
+    QuickSort.sort(vl, ca);
+
+    /*
+     * traverse in reverse order (highest count first) to build tooltip
+     */
+    boolean first = true;
+    StringBuilder sb = new StringBuilder(64);
+    for (int c = ca.length - 1; c >= 0; c--)
+    {
+      final char residue = ca[c];
+      // TODO combine residues which share a percentage
+      // (see AAFrequency.completeCdnaConsensus)
+      float tval = (vl[c] * 100f) / normaliseBy;
+      sb.append(first ? "" : "; ").append(residue).append(" ");
+      Format.appendPercentage(sb, tval, percentageDecPl);
+      sb.append("%");
+      first = false;
+    }
+    return sb.toString();
+  }
+
+  /**
+   * Returns a string representation of the symbol counts, for debug purposes.
+   */
+  @Override
+  public String toString()
+  {
+    StringBuilder sb = new StringBuilder();
+    sb.append("[ ");
+    SymbolCounts sc = getSymbolCounts();
+    for (int i = 0; i < sc.symbols.length; i++)
+    {
+      sb.append(sc.symbols[i]).append(":").append(sc.values[i]).append(" ");
+    }
+    sb.append("]");
+    return sb.toString();
+  }
+}
diff --git a/src/jalview/datamodel/RnaViewerModel.java b/src/jalview/datamodel/RnaViewerModel.java
index d76c28e..463cfb0 100644
--- a/src/jalview/datamodel/RnaViewerModel.java
+++ b/src/jalview/datamodel/RnaViewerModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/ArgumentI.java b/src/jalview/datamodel/SearchResultMatchI.java
similarity index 58%
copy from src/jalview/ws/params/ArgumentI.java
copy to src/jalview/datamodel/SearchResultMatchI.java
index 8d57d0a..0201d5e 100644
--- a/src/jalview/ws/params/ArgumentI.java
+++ b/src/jalview/datamodel/SearchResultMatchI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,27 +18,33 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.ws.params;
+package jalview.datamodel;
 
-public interface ArgumentI
+/**
+ * An interface that describes one matched region of an alignment, as one
+ * contiguous portion of a single dataset sequence
+ */
+public interface SearchResultMatchI
 {
   /**
+   * Returns the matched sequence
    * 
-   * @return name for this argument
+   * @return
    */
-  String getName();
+  SequenceI getSequence();
 
   /**
+   * Returns the start position of the match in the sequence (base 1)
    * 
-   * @return current value for the argument (may equal the name)
+   * @return
    */
-  String getValue();
+  int getStart();
 
   /**
-   * set the current value for the argument.
+   * Returns the end position of the match in the sequence (base 1)
    * 
-   * @param selectedItem
+   * @return
    */
-  void setValue(String selectedItem);
+  int getEnd();
 
-}
+}
\ No newline at end of file
diff --git a/src/jalview/datamodel/SearchResults.java b/src/jalview/datamodel/SearchResults.java
index d9d12db..9846c51 100644
--- a/src/jalview/datamodel/SearchResults.java
+++ b/src/jalview/datamodel/SearchResults.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,26 +21,26 @@
 package jalview.datamodel;
 
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.BitSet;
 import java.util.List;
 
 /**
  * Holds a list of search result matches, where each match is a contiguous
  * stretch of a single sequence.
  * 
- * @author gmcarstairs
+ * @author gmcarstairs amwaterhouse
  *
  */
-public class SearchResults
+public class SearchResults implements SearchResultsI
 {
 
-  private List<Match> matches = new ArrayList<Match>();
+  private List<SearchResultMatchI> matches = new ArrayList<SearchResultMatchI>();
 
   /**
    * One match consists of a sequence reference, start and end positions.
    * Discontiguous ranges in a sequence require two or more Match objects.
    */
-  public class Match
+  public class Match implements SearchResultMatchI
   {
     SequenceI sequence;
 
@@ -55,7 +55,10 @@ public class SearchResults
     int end;
 
     /**
-     * Constructor
+     * create a Match on a range of sequence. Match always holds region in
+     * forwards order, even if given in reverse order (such as from a mapping to
+     * a reverse strand); this avoids trouble for routines that highlight search
+     * results etc
      * 
      * @param seq
      *          a sequence
@@ -67,47 +70,67 @@ public class SearchResults
     public Match(SequenceI seq, int start, int end)
     {
       sequence = seq;
-      this.start = start;
-      this.end = end;
+
+      /*
+       * always hold in forwards order, even if given in reverse order
+       * (such as from a mapping to a reverse strand); this avoids
+       * trouble for routines that highlight search results etc
+       */
+      if (start <= end)
+      {
+        this.start = start;
+        this.end = end;
+      }
+      else
+      {
+        // TODO: JBP could mark match as being specified in reverse direction
+        // for use
+        // by caller ? e.g. visualizing reverse strand highlight
+        this.start = end;
+        this.end = start;
+      }
     }
 
+    /* (non-Javadoc)
+     * @see jalview.datamodel.SearchResultMatchI#getSequence()
+     */
+    @Override
     public SequenceI getSequence()
     {
       return sequence;
     }
 
+    /* (non-Javadoc)
+     * @see jalview.datamodel.SearchResultMatchI#getStart()
+     */
+    @Override
     public int getStart()
     {
       return start;
     }
 
+    /* (non-Javadoc)
+     * @see jalview.datamodel.SearchResultMatchI#getEnd()
+     */
+    @Override
     public int getEnd()
     {
       return end;
     }
 
     /**
-     * Returns the string of characters in the matched region, prefixed by the
-     * start position, e.g. "12CGT" or "208K"
+     * Returns a representation as "seqid/start-end"
      */
     @Override
     public String toString()
     {
-      final int from = Math.max(start - 1, 0);
-      String startPosition = String.valueOf(from);
-      return startPosition + getCharacters();
-    }
-
-    /**
-     * Returns the string of characters in the matched region.
-     */
-    public String getCharacters()
-    {
-      char[] chars = sequence.getSequence();
-      // convert start/end to base 0 (with bounds check)
-      final int from = Math.max(start - 1, 0);
-      final int to = Math.min(end, chars.length + 1);
-      return String.valueOf(Arrays.copyOfRange(chars, from, to));
+      StringBuilder sb = new StringBuilder();
+      if (sequence != null)
+      {
+        sb.append(sequence.getName()).append("/");
+      }
+      sb.append(start).append("-").append(end);
+      return sb.toString();
     }
 
     public void setSequence(SequenceI seq)
@@ -136,46 +159,38 @@ public class SearchResults
     @Override
     public boolean equals(Object obj)
     {
-      if (obj == null || !(obj instanceof Match))
+      if (obj == null || !(obj instanceof SearchResultMatchI))
       {
         return false;
       }
-      Match m = (Match) obj;
-      return (this.sequence == m.sequence && this.start == m.start && this.end == m.end);
+      SearchResultMatchI m = (SearchResultMatchI) obj;
+      return (sequence == m.getSequence() && start == m.getStart() && end == m
+              .getEnd());
     }
   }
 
-  /**
-   * This method replaces the old search results which merely held an alignment
-   * index of search matches. This broke when sequences were moved around the
-   * alignment
-   * 
-   * @param seq
-   *          Sequence
-   * @param start
-   *          int
-   * @param end
-   *          int
+  /* (non-Javadoc)
+   * @see jalview.datamodel.SearchResultsI#addResult(jalview.datamodel.SequenceI, int, int)
    */
-  public void addResult(SequenceI seq, int start, int end)
+  @Override
+  public SearchResultMatchI addResult(SequenceI seq, int start, int end)
   {
-    matches.add(new Match(seq, start, end));
+    Match m = new Match(seq, start, end);
+    matches.add(m);
+    return m;
   }
 
-  /**
-   * Quickly check if the given sequence is referred to in the search results
-   * 
-   * @param sequence
-   *          (specific alignment sequence or a dataset sequence)
-   * @return true if the results involve sequence
+  /* (non-Javadoc)
+   * @see jalview.datamodel.SearchResultsI#involvesSequence(jalview.datamodel.SequenceI)
    */
+  @Override
   public boolean involvesSequence(SequenceI sequence)
   {
     SequenceI ds = sequence.getDatasetSequence();
-    for (Match m : matches)
+    for (SearchResultMatchI _m : matches)
     {
-      if (m.sequence != null
-              && (m.sequence == sequence || m.sequence == ds))
+      SequenceI matched = _m.getSequence();
+      if (matched != null && (matched == sequence || matched == ds))
       {
         return true;
       }
@@ -183,11 +198,10 @@ public class SearchResults
     return false;
   }
 
-  /**
-   * This Method returns the search matches which lie between the start and end
-   * points of the sequence in question. It is optimised for returning objects
-   * for drawing on SequenceCanvas
+  /* (non-Javadoc)
+   * @see jalview.datamodel.SearchResultsI#getResults(jalview.datamodel.SequenceI, int, int)
    */
+  @Override
   public int[] getResults(SequenceI sequence, int start, int end)
   {
     if (matches.isEmpty())
@@ -199,8 +213,11 @@ public class SearchResults
     int[] tmp = null;
     int resultLength, matchStart = 0, matchEnd = 0;
     boolean mfound;
-    for (Match m : matches)
+    Match m;
+    for (SearchResultMatchI _m : matches)
     {
+      m = (Match) _m;
+
       mfound = false;
       if (m.sequence == sequence)
       {
@@ -255,97 +272,76 @@ public class SearchResults
     return result;
   }
 
-  public int getSize()
-  {
-    return matches.size();
-  }
-
-  public SequenceI getResultSequence(int index)
-  {
-    return matches.get(index).sequence;
-  }
-
-  /**
-   * Returns the start position of the i'th match in the search results.
-   * 
-   * @param i
-   * @return
-   */
-  public int getResultStart(int i)
+  @Override
+  public int markColumns(SequenceCollectionI sqcol, BitSet bs)
   {
-    return matches.get(i).start;
+    int count = 0;
+    BitSet mask = new BitSet();
+    for (SequenceI s : sqcol.getSequences())
+    {
+      int[] cols = getResults(s, sqcol.getStartRes(), sqcol.getEndRes());
+      if (cols != null)
+      {
+        for (int pair = 0; pair < cols.length; pair += 2)
+        {
+          mask.set(cols[pair], cols[pair + 1] + 1);
+        }
+      }
+    }
+    // compute columns that were newly selected
+    BitSet original = (BitSet) bs.clone();
+    original.and(mask);
+    count = mask.cardinality() - original.cardinality();
+    // and mark ranges not already marked
+    bs.or(mask);
+    return count;
   }
 
-  /**
-   * Returns the end position of the i'th match in the search results.
-   * 
-   * @param i
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.SearchResultsI#getSize()
    */
-  public int getResultEnd(int i)
+  @Override
+  public int getSize()
   {
-    return matches.get(i).end;
+    return matches.size();
   }
 
-  /**
-   * Returns true if no search result matches are held.
-   * 
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.SearchResultsI#isEmpty()
    */
+  @Override
   public boolean isEmpty()
   {
     return matches.isEmpty();
   }
 
-  /**
-   * Returns the list of matches.
-   * 
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.SearchResultsI#getResults()
    */
-  public List<Match> getResults()
+  @Override
+  public List<SearchResultMatchI> getResults()
   {
     return matches;
   }
 
   /**
-   * Return the results as a string of characters (bases) prefixed by start
-   * position(s). Meant for use when the context ensures that all matches are to
-   * regions of the same sequence (otherwise the result is meaningless).
+   * Return the results as a list of matches [seq1/from-to, seq2/from-to, ...]
    * 
    * @return
    */
   @Override
   public String toString()
   {
-    StringBuilder result = new StringBuilder(256);
-    for (Match m : matches)
-    {
-      result.append(m.toString());
-    }
-    return result.toString();
-  }
-
-  /**
-   * Return the results as a string of characters (bases). Meant for use when
-   * the context ensures that all matches are to regions of the same sequence
-   * (otherwise the result is meaningless).
-   * 
-   * @return
-   */
-  public String getCharacters()
-  {
-    StringBuilder result = new StringBuilder(256);
-    for (Match m : matches)
-    {
-      result.append(m.getCharacters());
-    }
-    return result.toString();
+    return matches == null ? "" : matches.toString();
   }
 
   /**
-   * Hashcode is has derived from the list of matches. This ensures that when
-   * two SearchResults objects satisfy the test for equals(), then they have the
+   * Hashcode is derived from the list of matches. This ensures that when two
+   * SearchResults objects satisfy the test for equals(), then they have the
    * same hashcode.
+   * 
+   * @see Match#hashCode()
+   * @see java.util.AbstractList#hashCode()
    */
   @Override
   public int hashCode()
@@ -360,11 +356,11 @@ public class SearchResults
   @Override
   public boolean equals(Object obj)
   {
-    if (obj == null || !(obj instanceof SearchResults))
+    if (obj == null || !(obj instanceof SearchResultsI))
     {
       return false;
     }
-    SearchResults sr = (SearchResults) obj;
-    return ((ArrayList<Match>) this.matches).equals(sr.matches);
+    SearchResultsI sr = (SearchResultsI) obj;
+    return matches.equals(sr.getResults());
   }
 }
diff --git a/src/jalview/datamodel/SearchResultsI.java b/src/jalview/datamodel/SearchResultsI.java
new file mode 100644
index 0000000..5db6101
--- /dev/null
+++ b/src/jalview/datamodel/SearchResultsI.java
@@ -0,0 +1,104 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.datamodel;
+
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * An interface describing the result of a search or other operation which
+ * highlights matched regions of an alignment
+ */
+public interface SearchResultsI
+{
+
+  /**
+   * Adds one region to the results
+   * 
+   * @param seq
+   *          Sequence
+   * @param start
+   *          int
+   * @param end
+   *          int
+   * @return
+   */
+  SearchResultMatchI addResult(SequenceI seq, int start, int end);
+
+  /**
+   * Answers true if the search results include the given sequence (or its
+   * dataset sequence), else false
+   * 
+   * @param sequence
+   * @return
+   */
+  boolean involvesSequence(SequenceI sequence);
+
+  /**
+   * Returns an array of [from, to, from, to..] matched columns (base 0) between
+   * the given start and end columns of the given sequence. Returns null if no
+   * matches overlap the specified region.
+   * <p>
+   * Implementations should provide an optimised method to return locations to
+   * highlight on a visible portion of an alignment.
+   * 
+   * @param sequence
+   * @param start
+   *          first column of range (base 0, inclusive)
+   * @param end
+   *          last column of range base 0, inclusive)
+   * @return int[]
+   */
+  int[] getResults(SequenceI sequence, int start, int end);
+
+  /**
+   * Returns the number of matches found
+   * 
+   * @return
+   */
+  int getSize();
+
+  /**
+   * Returns true if no search result matches are held.
+   * 
+   * @return
+   */
+  boolean isEmpty();
+
+  /**
+   * Returns the list of matches.
+   * 
+   * @return
+   */
+  List<SearchResultMatchI> getResults();
+
+  /**
+   * Set bits in a bitfield for all columns in the given sequence collection
+   * that are highlighted
+   * 
+   * @param sqcol
+   *          the set of sequences to search for highlighted regions
+   * @param bs
+   *          bitset to set
+   * @return number of bits set
+   */
+  int markColumns(SequenceCollectionI sqcol, BitSet bs);
+}
\ No newline at end of file
diff --git a/src/jalview/datamodel/SecondaryStructureAnnotation.java b/src/jalview/datamodel/SecondaryStructureAnnotation.java
index e488265..1b3dff7 100644
--- a/src/jalview/datamodel/SecondaryStructureAnnotation.java
+++ b/src/jalview/datamodel/SecondaryStructureAnnotation.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/SeqCigar.java b/src/jalview/datamodel/SeqCigar.java
index 33cfda4..5d5e3af 100644
--- a/src/jalview/datamodel/SeqCigar.java
+++ b/src/jalview/datamodel/SeqCigar.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -68,10 +68,49 @@ public class SeqCigar extends CigarSimple
   }
 
   /**
+   * 
+   * @param column
+   * @return position in sequence for column (or -1 if no match state exists)
+   */
+  public int findPosition(int column)
+  {
+    int w = 0, ew, p = refseq.findPosition(start);
+    if (column < 0)
+    {
+      return -1;
+    }
+    if (range != null)
+    {
+      for (int i = 0; i < length; i++)
+      {
+        if (operation[i] == M || operation[i] == D)
+        {
+          p += range[i];
+        }
+        if (operation[i] == M || operation[i] == I)
+        {
+          ew = w + range[i];
+          if (column < ew)
+          {
+            if (operation[i] == I)
+            {
+              return -1;
+            }
+            return p - (ew - column);
+          }
+          w = ew;
+        }
+      }
+    }
+    return -1;
+  }
+
+  /**
    * Returns sequence as a string with cigar operations applied to it
    * 
    * @return String
    */
+  @Override
   public String getSequenceString(char GapChar)
   {
     return (length == 0) ? "" : (String) getSequenceAndDeletions(
diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java
index def9c47..348c3c5 100644
--- a/src/jalview/datamodel/Sequence.java
+++ b/src/jalview/datamodel/Sequence.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,9 +21,15 @@
 package jalview.datamodel;
 
 import jalview.analysis.AlignSeq;
+import jalview.api.DBRefEntryI;
+import jalview.util.Comparison;
+import jalview.util.DBRefUtils;
+import jalview.util.MapList;
 import jalview.util.StringUtils;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.Vector;
@@ -182,12 +188,13 @@ public class Sequence extends ASequence implements SequenceI
   }
 
   /**
-   * Creates a new Sequence object with new features, DBRefEntries,
-   * AlignmentAnnotations, and PDBIds but inherits any existing dataset sequence
-   * reference.
+   * Creates a new Sequence object with new AlignmentAnnotations but inherits
+   * any existing dataset sequence reference. If non exists, everything is
+   * copied.
    * 
    * @param seq
-   *          DOCUMENT ME!
+   *          if seq is a dataset sequence, behaves like a plain old copy
+   *          constructor
    */
   public Sequence(SequenceI seq)
   {
@@ -210,29 +217,45 @@ public class Sequence extends ASequence implements SequenceI
 
   }
 
+  /**
+   * does the heavy lifting when cloning a dataset sequence, or coping data from
+   * dataset to a new derived sequence.
+   * 
+   * @param seq
+   *          - source of attributes.
+   * @param alAnnotation
+   *          - alignment annotation present on seq that should be copied onto
+   *          this sequence
+   */
   protected void initSeqFrom(SequenceI seq,
           AlignmentAnnotation[] alAnnotation)
   {
-    initSeqAndName(seq.getName(), seq.getSequence(), seq.getStart(),
-            seq.getEnd());
+    {
+      char[] oseq = seq.getSequence();
+      initSeqAndName(seq.getName(), Arrays.copyOf(oseq, oseq.length),
+              seq.getStart(), seq.getEnd());
+    }
     description = seq.getDescription();
-    if (seq.getSequenceFeatures() != null)
+    if (seq != datasetSequence)
     {
-      SequenceFeature[] sf = seq.getSequenceFeatures();
-      for (int i = 0; i < sf.length; i++)
-      {
-        addSequenceFeature(new SequenceFeature(sf[i]));
-      }
+      setDatasetSequence(seq.getDatasetSequence());
     }
-    setDatasetSequence(seq.getDatasetSequence());
-    if (datasetSequence == null && seq.getDBRef() != null)
+    if (datasetSequence == null && seq.getDBRefs() != null)
     {
-      // only copy DBRefs if we really are a dataset sequence
-      DBRefEntry[] dbr = seq.getDBRef();
+      // only copy DBRefs and seqfeatures if we really are a dataset sequence
+      DBRefEntry[] dbr = seq.getDBRefs();
       for (int i = 0; i < dbr.length; i++)
       {
         addDBRef(new DBRefEntry(dbr[i]));
       }
+      if (seq.getSequenceFeatures() != null)
+      {
+        SequenceFeature[] sf = seq.getSequenceFeatures();
+        for (int i = 0; i < sf.length; i++)
+        {
+          addSequenceFeature(new SequenceFeature(sf[i]));
+        }
+      }
     }
     if (seq.getAnnotation() != null)
     {
@@ -261,28 +284,43 @@ public class Sequence extends ASequence implements SequenceI
     }
     if (seq.getAllPDBEntries() != null)
     {
-      Vector ids = seq.getAllPDBEntries();
-      Enumeration e = ids.elements();
-      while (e.hasMoreElements())
+      Vector<PDBEntry> ids = seq.getAllPDBEntries();
+      for (PDBEntry pdb : ids)
       {
-        this.addPDBId(new PDBEntry((PDBEntry) e.nextElement()));
+        this.addPDBId(new PDBEntry(pdb));
       }
     }
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param v
-   *          DOCUMENT ME!
-   */
+  @Override
   public void setSequenceFeatures(SequenceFeature[] features)
   {
-    sequenceFeatures = features;
+    if (datasetSequence == null)
+    {
+      sequenceFeatures = features;
+    }
+    else
+    {
+      if (datasetSequence.getSequenceFeatures() != features
+              && datasetSequence.getSequenceFeatures() != null
+              && datasetSequence.getSequenceFeatures().length > 0)
+      {
+        new Exception(
+                "Warning: JAL-2046 side effect ? Possible implementation error: overwriting dataset sequence features by setting sequence features on alignment")
+                .printStackTrace();
+      }
+      datasetSequence.setSequenceFeatures(features);
+    }
   }
 
+  @Override
   public synchronized void addSequenceFeature(SequenceFeature sf)
   {
+    if (sequenceFeatures == null && datasetSequence != null)
+    {
+      datasetSequence.addSequenceFeature(sf);
+      return;
+    }
     if (sequenceFeatures == null)
     {
       sequenceFeatures = new SequenceFeature[0];
@@ -303,10 +341,15 @@ public class Sequence extends ASequence implements SequenceI
     sequenceFeatures = temp;
   }
 
+  @Override
   public void deleteFeature(SequenceFeature sf)
   {
     if (sequenceFeatures == null)
     {
+      if (datasetSequence != null)
+      {
+        datasetSequence.deleteFeature(sf);
+      }
       return;
     }
 
@@ -352,6 +395,7 @@ public class Sequence extends ASequence implements SequenceI
    * 
    * @return
    */
+  @Override
   public SequenceFeature[] getSequenceFeatures()
   {
     SequenceFeature[] features = sequenceFeatures;
@@ -367,28 +411,25 @@ public class Sequence extends ASequence implements SequenceI
     return features;
   }
 
-  public void addPDBId(PDBEntry entry)
+  @Override
+  public boolean addPDBId(PDBEntry entry)
   {
     if (pdbIds == null)
     {
       pdbIds = new Vector<PDBEntry>();
+      pdbIds.add(entry);
+      return true;
     }
-    if (pdbIds.contains(entry))
-    {
-      updatePDBEntry(pdbIds.get(pdbIds.indexOf(entry)), entry);
-    }
-    else
-    {
-      pdbIds.addElement(entry);
-    }
-  }
 
-  private static void updatePDBEntry(PDBEntry oldEntry, PDBEntry newEntry)
-  {
-    if (newEntry.getFile() != null)
+    for (PDBEntry pdbe : pdbIds)
     {
-      oldEntry.setFile(newEntry.getFile());
+      if (pdbe.updateFrom(entry))
+      {
+        return false;
+      }
     }
+    pdbIds.addElement(entry);
+    return true;
   }
 
   /**
@@ -411,7 +452,7 @@ public class Sequence extends ASequence implements SequenceI
   @Override
   public Vector<PDBEntry> getAllPDBEntries()
   {
-    return pdbIds;
+    return pdbIds == null ? new Vector<PDBEntry>() : pdbIds;
   }
 
   /**
@@ -419,6 +460,7 @@ public class Sequence extends ASequence implements SequenceI
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public String getDisplayId(boolean jvsuffix)
   {
     StringBuffer result = new StringBuffer(name);
@@ -436,6 +478,7 @@ public class Sequence extends ASequence implements SequenceI
    * @param name
    *          DOCUMENT ME!
    */
+  @Override
   public void setName(String name)
   {
     this.name = name;
@@ -447,6 +490,7 @@ public class Sequence extends ASequence implements SequenceI
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public String getName()
   {
     return this.name;
@@ -458,6 +502,7 @@ public class Sequence extends ASequence implements SequenceI
    * @param start
    *          DOCUMENT ME!
    */
+  @Override
   public void setStart(int start)
   {
     this.start = start;
@@ -468,6 +513,7 @@ public class Sequence extends ASequence implements SequenceI
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public int getStart()
   {
     return this.start;
@@ -479,6 +525,7 @@ public class Sequence extends ASequence implements SequenceI
    * @param end
    *          DOCUMENT ME!
    */
+  @Override
   public void setEnd(int end)
   {
     this.end = end;
@@ -489,6 +536,7 @@ public class Sequence extends ASequence implements SequenceI
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public int getEnd()
   {
     return this.end;
@@ -499,6 +547,7 @@ public class Sequence extends ASequence implements SequenceI
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public int getLength()
   {
     return this.sequence.length;
@@ -510,22 +559,26 @@ public class Sequence extends ASequence implements SequenceI
    * @param seq
    *          DOCUMENT ME!
    */
+  @Override
   public void setSequence(String seq)
   {
     this.sequence = seq.toCharArray();
     checkValidRange();
   }
 
+  @Override
   public String getSequenceAsString()
   {
     return new String(sequence);
   }
 
+  @Override
   public String getSequenceAsString(int start, int end)
   {
     return new String(getSequence(start, end));
   }
 
+  @Override
   public char[] getSequence()
   {
     return sequence;
@@ -536,6 +589,7 @@ public class Sequence extends ASequence implements SequenceI
    * 
    * @see jalview.datamodel.SequenceI#getSequence(int, int)
    */
+  @Override
   public char[] getSequence(int start, int end)
   {
     if (start < 0)
@@ -589,16 +643,15 @@ public class Sequence extends ASequence implements SequenceI
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param i
-   *          DOCUMENT ME!
+   * Returns the character of the aligned sequence at the given position (base
+   * zero), or space if the position is not within the sequence's bounds
    * 
-   * @return DOCUMENT ME!
+   * @return
    */
+  @Override
   public char getCharAt(int i)
   {
-    if (i < sequence.length)
+    if (i >= 0 && i < sequence.length)
     {
       return sequence[i];
     }
@@ -614,6 +667,7 @@ public class Sequence extends ASequence implements SequenceI
    * @param desc
    *          DOCUMENT ME!
    */
+  @Override
   public void setDescription(String desc)
   {
     this.description = desc;
@@ -624,6 +678,7 @@ public class Sequence extends ASequence implements SequenceI
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public String getDescription()
   {
     return this.description;
@@ -634,6 +689,7 @@ public class Sequence extends ASequence implements SequenceI
    * 
    * @see jalview.datamodel.SequenceI#findIndex(int)
    */
+  @Override
   public int findIndex(int pos)
   {
     // returns the alignment position for a residue
@@ -686,6 +742,7 @@ public class Sequence extends ASequence implements SequenceI
    * @return int[SequenceI.getEnd()-SequenceI.getStart()+1] or null if no
    *         residues in SequenceI object
    */
+  @Override
   public int[] gapMap()
   {
     String seq = jalview.analysis.AlignSeq.extractGaps(
@@ -885,18 +942,28 @@ public class Sequence extends ASequence implements SequenceI
   }
 
   @Override
-  public void setDBRef(DBRefEntry[] dbref)
+  public void setDBRefs(DBRefEntry[] dbref)
   {
+    if (dbrefs == null && datasetSequence != null
+            && this != datasetSequence)
+    {
+      datasetSequence.setDBRefs(dbref);
+      return;
+    }
     dbrefs = dbref;
+    if (dbrefs != null)
+    {
+      DBRefUtils.ensurePrimaries(this);
+    }
   }
 
   @Override
-  public DBRefEntry[] getDBRef()
+  public DBRefEntry[] getDBRefs()
   {
     if (dbrefs == null && datasetSequence != null
             && this != datasetSequence)
     {
-      return datasetSequence.getDBRef();
+      return datasetSequence.getDBRefs();
     }
     return dbrefs;
   }
@@ -904,39 +971,56 @@ public class Sequence extends ASequence implements SequenceI
   @Override
   public void addDBRef(DBRefEntry entry)
   {
+    if (datasetSequence != null)
+    {
+      datasetSequence.addDBRef(entry);
+      return;
+    }
+
     if (dbrefs == null)
     {
       dbrefs = new DBRefEntry[0];
     }
 
-    int i, iSize = dbrefs.length;
-
-    for (i = 0; i < iSize; i++)
+    for (DBRefEntryI dbr : dbrefs)
     {
-      if (dbrefs[i].equalRef(entry))
+      if (dbr.updateFrom(entry))
       {
-        if (entry.getMap() != null)
-        {
-          if (dbrefs[i].getMap() == null)
-          {
-            // overwrite with 'superior' entry that contains a mapping.
-            dbrefs[i] = entry;
-          }
-        }
+        /*
+         * found a dbref that either matched, or could be
+         * updated from, the new entry - no need to add it
+         */
         return;
       }
     }
 
-    DBRefEntry[] temp = new DBRefEntry[iSize + 1];
-    System.arraycopy(dbrefs, 0, temp, 0, iSize);
+    /*
+     * extend the array to make room for one more
+     */
+    // TODO use an ArrayList instead
+    int j = dbrefs.length;
+    DBRefEntry[] temp = new DBRefEntry[j + 1];
+    System.arraycopy(dbrefs, 0, temp, 0, j);
     temp[temp.length - 1] = entry;
 
     dbrefs = temp;
+
+    DBRefUtils.ensurePrimaries(this);
   }
 
   @Override
   public void setDatasetSequence(SequenceI seq)
   {
+    if (seq == this)
+    {
+      throw new IllegalArgumentException(
+              "Implementation Error: self reference passed to SequenceI.setDatasetSequence");
+    }
+    if (seq != null && seq.getDatasetSequence() != null)
+    {
+      throw new IllegalArgumentException(
+              "Implementation error: cascading dataset sequences are not allowed.");
+    }
     datasetSequence = seq;
   }
 
@@ -973,6 +1057,7 @@ public class Sequence extends ASequence implements SequenceI
     annotation.setSequenceRef(this);
   }
 
+  @Override
   public void removeAlignmentAnnotation(AlignmentAnnotation annotation)
   {
     if (this.annotation != null)
@@ -1008,56 +1093,80 @@ public class Sequence extends ASequence implements SequenceI
   @Override
   public SequenceI deriveSequence()
   {
-    SequenceI seq = new Sequence(this);
-    if (datasetSequence != null)
-    {
-      // duplicate current sequence with same dataset
-      seq.setDatasetSequence(datasetSequence);
-    }
-    else
+    Sequence seq = null;
+    if (datasetSequence == null)
     {
       if (isValidDatasetSequence())
       {
         // Use this as dataset sequence
+        seq = new Sequence(getName(), "", 1, -1);
         seq.setDatasetSequence(this);
+        seq.initSeqFrom(this, getAnnotation());
+        return seq;
       }
       else
       {
         // Create a new, valid dataset sequence
-        SequenceI ds = seq;
-        ds.setSequence(AlignSeq.extractGaps(
-                jalview.util.Comparison.GapChars, new String(sequence)));
-        setDatasetSequence(ds);
-        ds.setSequenceFeatures(getSequenceFeatures());
-        seq = this; // and return this sequence as the derived sequence.
+        createDatasetSequence();
       }
     }
-    return seq;
+    return new Sequence(this);
   }
 
+  private boolean _isNa;
+
+  private long _seqhash = 0;
+
+  /**
+   * Answers false if the sequence is more than 85% nucleotide (ACGTU), else
+   * true
+   */
+  @Override
+  public boolean isProtein()
+  {
+    if (datasetSequence != null)
+    {
+      return datasetSequence.isProtein();
+    }
+    if (_seqhash != sequence.hashCode())
+    {
+      _seqhash = sequence.hashCode();
+      _isNa = Comparison.isNucleotide(this);
+    }
+    return !_isNa;
+  };
+
   /*
    * (non-Javadoc)
    * 
    * @see jalview.datamodel.SequenceI#createDatasetSequence()
    */
+  @Override
   public SequenceI createDatasetSequence()
   {
     if (datasetSequence == null)
     {
-      datasetSequence = new Sequence(getName(), AlignSeq.extractGaps(
+      Sequence dsseq = new Sequence(getName(), AlignSeq.extractGaps(
               jalview.util.Comparison.GapChars, getSequenceAsString()),
               getStart(), getEnd());
-      datasetSequence.setSequenceFeatures(getSequenceFeatures());
-      datasetSequence.setDescription(getDescription());
-      setSequenceFeatures(null);
-      // move database references onto dataset sequence
-      datasetSequence.setDBRef(getDBRef());
-      setDBRef(null);
-      datasetSequence.setPDBId(getAllPDBEntries());
-      setPDBId(null);
+
+      datasetSequence = dsseq;
+
+      dsseq.setDescription(description);
+      // move features and database references onto dataset sequence
+      dsseq.sequenceFeatures = sequenceFeatures;
+      sequenceFeatures = null;
+      dsseq.dbrefs = dbrefs;
+      dbrefs = null;
+      // TODO: search and replace any references to this sequence with
+      // references to the dataset sequence in Mappings on dbref
+      dsseq.pdbIds = pdbIds;
+      pdbIds = null;
       datasetSequence.updatePDBIds();
       if (annotation != null)
       {
+        // annotation is cloned rather than moved, to preserve what's currently
+        // on the alignment
         for (AlignmentAnnotation aa : annotation)
         {
           AlignmentAnnotation _aa = new AlignmentAnnotation(aa);
@@ -1078,6 +1187,7 @@ public class Sequence extends ASequence implements SequenceI
    * jalview.datamodel.SequenceI#setAlignmentAnnotation(AlignmmentAnnotation[]
    * annotations)
    */
+  @Override
   public void setAlignmentAnnotation(AlignmentAnnotation[] annotations)
   {
     if (annotation != null)
@@ -1141,46 +1251,22 @@ public class Sequence extends ASequence implements SequenceI
     {
       return false;
     }
-    Vector newpdb = new Vector();
-    for (int i = 0; i < dbrefs.length; i++)
-    {
-      if (DBRefSource.PDB.equals(dbrefs[i].getSource()))
-      {
-        PDBEntry pdbe = new PDBEntry();
-        pdbe.setId(dbrefs[i].getAccessionId());
-        if (pdbIds == null || pdbIds.size() == 0)
-        {
-          newpdb.addElement(pdbe);
-        }
-        else
-        {
-          Enumeration en = pdbIds.elements();
-          boolean matched = false;
-          while (!matched && en.hasMoreElements())
-          {
-            PDBEntry anentry = (PDBEntry) en.nextElement();
-            if (anentry.getId().equals(pdbe.getId()))
-            {
-              matched = true;
-            }
-          }
-          if (!matched)
-          {
-            newpdb.addElement(pdbe);
-          }
-        }
-      }
-    }
-    if (newpdb.size() > 0)
+    boolean added = false;
+    for (DBRefEntry dbr : dbrefs)
     {
-      Enumeration en = newpdb.elements();
-      while (en.hasMoreElements())
+      if (DBRefSource.PDB.equals(dbr.getSource()))
       {
-        addPDBId((PDBEntry) en.nextElement());
+        /*
+         * 'Add' any PDB dbrefs as a PDBEntry - add is only performed if the
+         * PDB id is not already present in a 'matching' PDBEntry
+         * Constructor parses out a chain code if appended to the accession id
+         * (a fudge used to 'store' the chain code in the DBRef)
+         */
+        PDBEntry pdbe = new PDBEntry(dbr);
+        added |= addPDBId(pdbe);
       }
-      return true;
     }
-    return false;
+    return added;
   }
 
   @Override
@@ -1226,7 +1312,7 @@ public class Sequence extends ASequence implements SequenceI
       }
     }
     // transfer database references
-    DBRefEntry[] entryRefs = entry.getDBRef();
+    DBRefEntry[] entryRefs = entry.getDBRefs();
     if (entryRefs != null)
     {
       for (int r = 0; r < entryRefs.length; r++)
@@ -1250,6 +1336,7 @@ public class Sequence extends ASequence implements SequenceI
    * @return The index (zero-based) on this sequence in the MSA. It returns
    *         {@code -1} if this information is not available.
    */
+  @Override
   public int getIndex()
   {
     return index;
@@ -1263,16 +1350,19 @@ public class Sequence extends ASequence implements SequenceI
    *          position for this sequence. This value is zero-based (zero for
    *          this first sequence)
    */
+  @Override
   public void setIndex(int value)
   {
     index = value;
   }
 
+  @Override
   public void setRNA(RNA r)
   {
     rna = r;
   }
 
+  @Override
   public RNA getRNA()
   {
     return rna;
@@ -1297,6 +1387,7 @@ public class Sequence extends ASequence implements SequenceI
     return result;
   }
 
+  @Override
   public String toString()
   {
     return getDisplayId(false);
@@ -1305,12 +1396,15 @@ public class Sequence extends ASequence implements SequenceI
   @Override
   public PDBEntry getPDBEntry(String pdbIdStr)
   {
-    if (getDatasetSequence() == null
-            || getDatasetSequence().getAllPDBEntries() == null)
+    if (getDatasetSequence() != null)
+    {
+      return getDatasetSequence().getPDBEntry(pdbIdStr);
+    }
+    if (pdbIds == null)
     {
       return null;
     }
-    List<PDBEntry> entries = getDatasetSequence().getAllPDBEntries();
+    List<PDBEntry> entries = getAllPDBEntries();
     for (PDBEntry entry : entries)
     {
       if (entry.getId().equalsIgnoreCase(pdbIdStr))
@@ -1321,4 +1415,64 @@ public class Sequence extends ASequence implements SequenceI
     return null;
   }
 
+  @Override
+  public List<DBRefEntry> getPrimaryDBRefs()
+  {
+    if (datasetSequence != null)
+    {
+      return datasetSequence.getPrimaryDBRefs();
+    }
+    if (dbrefs == null || dbrefs.length == 0)
+    {
+      return Collections.emptyList();
+    }
+    synchronized (dbrefs)
+    {
+      List<DBRefEntry> primaries = new ArrayList<DBRefEntry>();
+      DBRefEntry[] tmp = new DBRefEntry[1];
+      for (DBRefEntry ref : dbrefs)
+      {
+        if (!ref.isPrimaryCandidate())
+        {
+          continue;
+        }
+        if (ref.hasMap())
+        {
+          MapList mp = ref.getMap().getMap();
+          if (mp.getFromLowest() > start || mp.getFromHighest() < end)
+          {
+            // map only involves a subsequence, so cannot be primary
+            continue;
+          }
+        }
+        // whilst it looks like it is a primary ref, we also sanity check type
+        if (DBRefUtils.getCanonicalName(DBRefSource.PDB).equals(
+                DBRefUtils.getCanonicalName(ref.getSource())))
+        {
+          // PDB dbrefs imply there should be a PDBEntry associated
+          // TODO: tighten PDB dbrefs
+          // formally imply Jalview has actually downloaded and
+          // parsed the pdb file. That means there should be a cached file
+          // handle on the PDBEntry, and a real mapping between sequence and
+          // extracted sequence from PDB file
+          PDBEntry pdbentry = getPDBEntry(ref.getAccessionId());
+          if (pdbentry != null && pdbentry.getFile() != null)
+          {
+            primaries.add(ref);
+          }
+          continue;
+        }
+        // check standard protein or dna sources
+        tmp[0] = ref;
+        DBRefEntry[] res = DBRefUtils.selectDbRefs(!isProtein(), tmp);
+        if (res != null && res[0] == tmp[0])
+        {
+          primaries.add(ref);
+          continue;
+        }
+      }
+      return primaries;
+    }
+  }
+
 }
diff --git a/src/jalview/datamodel/SequenceCollectionI.java b/src/jalview/datamodel/SequenceCollectionI.java
index 96c7af8..6b85647 100644
--- a/src/jalview/datamodel/SequenceCollectionI.java
+++ b/src/jalview/datamodel/SequenceCollectionI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/SequenceDummy.java b/src/jalview/datamodel/SequenceDummy.java
index e189f31..d55b300 100644
--- a/src/jalview/datamodel/SequenceDummy.java
+++ b/src/jalview/datamodel/SequenceDummy.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,7 @@
  */
 package jalview.datamodel;
 
-public class SequenceDummy extends Sequence implements SequenceI
+public class SequenceDummy extends Sequence
 {
   public SequenceDummy(String sequenceId)
   {
@@ -50,4 +50,14 @@ public class SequenceDummy extends Sequence implements SequenceI
   {
     return dummy;
   }
+
+  /**
+   * Always suppress /start-end for display name as we don't know it
+   */
+  @Override
+  public String getDisplayId(boolean jvsuffix)
+  {
+    // required for correct behaviour of SequenceIdMatcher
+    return super.getDisplayId(false);
+  }
 }
diff --git a/src/jalview/datamodel/SequenceFeature.java b/src/jalview/datamodel/SequenceFeature.java
index 674f1e7..53e33e5 100644
--- a/src/jalview/datamodel/SequenceFeature.java
+++ b/src/jalview/datamodel/SequenceFeature.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,8 @@
  */
 package jalview.datamodel;
 
-import java.util.Hashtable;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Vector;
 
 /**
@@ -31,6 +32,22 @@ import java.util.Vector;
  */
 public class SequenceFeature
 {
+  private static final String STATUS = "status";
+
+  private static final String STRAND = "STRAND";
+
+  // private key for Phase designed not to conflict with real GFF data
+  private static final String PHASE = "!Phase";
+
+  // private key for ENA location designed not to conflict with real GFF data
+  private static final String LOCATION = "!Location";
+
+  /*
+   * ATTRIBUTES is reserved for the GFF 'column 9' data, formatted as
+   * name1=value1;name2=value2,value3;...etc
+   */
+  private static final String ATTRIBUTES = "ATTRIBUTES";
+
   public int begin;
 
   public int end;
@@ -41,7 +58,11 @@ public class SequenceFeature
 
   public String description;
 
-  public Hashtable otherDetails;
+  /*
+   * a map of key-value pairs; may be populated from GFF 'column 9' data,
+   * other data sources (e.g. GenBank file), or programmatically
+   */
+  public Map<String, Object> otherDetails;
 
   public Vector<String> links;
 
@@ -54,9 +75,9 @@ public class SequenceFeature
   }
 
   /**
-   * Constructs a duplicate feature. Note: Uses clone on the otherDetails so
-   * only shallow copies are made of additional properties and method will
-   * silently fail if unclonable objects are found in the hash.
+   * Constructs a duplicate feature. Note: Uses makes a shallow copy of the
+   * otherDetails map, so the new and original SequenceFeature may reference the
+   * same objects in the map.
    * 
    * @param cpy
    */
@@ -83,10 +104,11 @@ public class SequenceFeature
       {
         try
         {
-          otherDetails = (Hashtable) cpy.otherDetails.clone();
+          otherDetails = (Map<String, Object>) ((HashMap<String, Object>) cpy.otherDetails)
+                  .clone();
         } catch (Exception e)
         {
-          // Uncloneable objects in the otherDetails - don't complain
+          // ignore
         }
       }
       if (cpy.links != null && cpy.links.size() > 0)
@@ -100,45 +122,148 @@ public class SequenceFeature
     }
   }
 
+  /**
+   * Constructor including a Status value
+   * 
+   * @param type
+   * @param desc
+   * @param status
+   * @param begin
+   * @param end
+   * @param featureGroup
+   */
   public SequenceFeature(String type, String desc, String status,
           int begin, int end, String featureGroup)
   {
+    this(type, desc, begin, end, featureGroup);
+    setStatus(status);
+  }
+
+  /**
+   * Constructor
+   * 
+   * @param type
+   * @param desc
+   * @param begin
+   * @param end
+   * @param featureGroup
+   */
+  SequenceFeature(String type, String desc, int begin, int end,
+          String featureGroup)
+  {
     this.type = type;
     this.description = desc;
-    setValue("status", status);
     this.begin = begin;
     this.end = end;
     this.featureGroup = featureGroup;
   }
 
+  /**
+   * Constructor including a score value
+   * 
+   * @param type
+   * @param desc
+   * @param begin
+   * @param end
+   * @param score
+   * @param featureGroup
+   */
   public SequenceFeature(String type, String desc, int begin, int end,
           float score, String featureGroup)
   {
-    this.type = type;
-    this.description = desc;
-    this.begin = begin;
-    this.end = end;
+    this(type, desc, begin, end, featureGroup);
     this.score = score;
-    this.featureGroup = featureGroup;
   }
 
-  public boolean equals(SequenceFeature sf)
+  /**
+   * Two features are considered equal if they have the same type, group,
+   * description, start, end, phase, strand, and (if present) 'Name', ID' and
+   * 'Parent' attributes.
+   * 
+   * Note we need to check Parent to distinguish the same exon occurring in
+   * different transcripts (in Ensembl GFF). This allows assembly of transcript
+   * sequences from their component exon regions.
+   */
+  @Override
+  public boolean equals(Object o)
   {
-    if (begin != sf.begin || end != sf.end || score != sf.score)
+    return equals(o, false);
+  }
+
+  /**
+   * Overloaded method allows the equality test to optionally ignore the
+   * 'Parent' attribute of a feature. This supports avoiding adding many
+   * superficially duplicate 'exon' or CDS features to genomic or protein
+   * sequence.
+   * 
+   * @param o
+   * @param ignoreParent
+   * @return
+   */
+  public boolean equals(Object o, boolean ignoreParent)
+  {
+    if (o == null || !(o instanceof SequenceFeature))
+    {
+      return false;
+    }
+
+    SequenceFeature sf = (SequenceFeature) o;
+    boolean sameScore = Float.isNaN(score) ? Float.isNaN(sf.score)
+            : score == sf.score;
+    if (begin != sf.begin || end != sf.end || !sameScore)
     {
       return false;
     }
 
-    if (!(type + description + featureGroup).equals(sf.type
-            + sf.description + sf.featureGroup))
+    if (getStrand() != sf.getStrand())
     {
       return false;
     }
 
+    if (!(type + description + featureGroup + getPhase()).equals(sf.type
+            + sf.description + sf.featureGroup + sf.getPhase()))
+    {
+      return false;
+    }
+    if (!equalAttribute(getValue("ID"), sf.getValue("ID")))
+    {
+      return false;
+    }
+    if (!equalAttribute(getValue("Name"), sf.getValue("Name")))
+    {
+      return false;
+    }
+    if (!ignoreParent)
+    {
+      if (!equalAttribute(getValue("Parent"), sf.getValue("Parent")))
+      {
+        return false;
+      }
+    }
     return true;
   }
 
   /**
+   * Returns true if both values are null, are both non-null and equal
+   * 
+   * @param att1
+   * @param att2
+   * @return
+   */
+  protected static boolean equalAttribute(Object att1, Object att2)
+  {
+    if (att1 == null && att2 == null)
+    {
+      return true;
+    }
+    if (att1 != null)
+    {
+      return att1.equals(att2);
+    }
+    return att2.equals(att1);
+  }
+
+  /**
    * DOCUMENT ME!
    * 
    * @return DOCUMENT ME!
@@ -229,7 +354,7 @@ public class SequenceFeature
   }
 
   /**
-   * Used for getting values which are not in the basic set. eg STRAND, FRAME
+   * Used for getting values which are not in the basic set. eg STRAND, PHASE
    * for GFF file
    * 
    * @param key
@@ -248,6 +373,20 @@ public class SequenceFeature
   }
 
   /**
+   * Returns a property value for the given key if known, else the specified
+   * default value
+   * 
+   * @param key
+   * @param defaultValue
+   * @return
+   */
+  public Object getValue(String key, Object defaultValue)
+  {
+    Object value = getValue(key);
+    return value == null ? defaultValue : value;
+  }
+
+  /**
    * Used for setting values which are not in the basic set. eg STRAND, FRAME
    * for GFF file
    * 
@@ -262,7 +401,7 @@ public class SequenceFeature
     {
       if (otherDetails == null)
       {
-        otherDetails = new Hashtable();
+        otherDetails = new HashMap<String, Object>();
       }
 
       otherDetails.put(key, value);
@@ -275,20 +414,22 @@ public class SequenceFeature
    */
   public void setStatus(String status)
   {
-    setValue("status", status);
+    setValue(STATUS, status);
   }
 
   public String getStatus()
   {
-    if (otherDetails != null)
-    {
-      String stat = (String) otherDetails.get("status");
-      if (stat != null)
-      {
-        return new String(stat);
-      }
-    }
-    return null;
+    return (String) getValue(STATUS);
+  }
+
+  public void setAttributes(String attr)
+  {
+    setValue(ATTRIBUTES, attr);
+  }
+
+  public String getAttributes()
+  {
+    return (String) getValue(ATTRIBUTES);
   }
 
   public void setPosition(int pos)
@@ -302,23 +443,109 @@ public class SequenceFeature
     return begin;
   }
 
+  /**
+   * Return 1 for forward strand ('+' in GFF), -1 for reverse strand ('-' in
+   * GFF), and 0 for unknown or not (validly) specified
+   * 
+   * @return
+   */
   public int getStrand()
   {
-    String str;
-    if (otherDetails == null
-            || (str = otherDetails.get("STRAND").toString()) == null)
-    {
-      return 0;
-    }
-    if (str.equals("-"))
+    int strand = 0;
+    if (otherDetails != null)
     {
-      return -1;
+      Object str = otherDetails.get(STRAND);
+      if ("-".equals(str))
+      {
+        strand = -1;
+      }
+      else if ("+".equals(str))
+      {
+        strand = 1;
+      }
     }
-    if (str.equals("+"))
+    return strand;
+  }
+
+  /**
+   * Set the value of strand
+   * 
+   * @param strand
+   *          should be "+" for forward, or "-" for reverse
+   */
+  public void setStrand(String strand)
+  {
+    setValue(STRAND, strand);
+  }
+
+  public void setPhase(String phase)
+  {
+    setValue(PHASE, phase);
+  }
+
+  public String getPhase()
+  {
+    return (String) getValue(PHASE);
+  }
+
+  /**
+   * Sets the 'raw' ENA format location specifier e.g. join(12..45,89..121)
+   * 
+   * @param loc
+   */
+  public void setEnaLocation(String loc)
+  {
+    setValue(LOCATION, loc);
+  }
+
+  /**
+   * Gets the 'raw' ENA format location specifier e.g. join(12..45,89..121)
+   * 
+   * @param loc
+   */
+  public String getEnaLocation()
+  {
+    return (String) getValue(LOCATION);
+  }
+
+  /**
+   * Readable representation, for debug only, not guaranteed not to change
+   * between versions
+   */
+  @Override
+  public String toString()
+  {
+    return String.format("%d %d %s %s", getBegin(), getEnd(), getType(),
+            getDescription());
+  }
+
+  /**
+   * Overridden to ensure that whenever two objects are equal, they have the
+   * same hashCode
+   */
+  @Override
+  public int hashCode()
+  {
+    String s = getType() + getDescription() + getFeatureGroup()
+            + getValue("ID") + getValue("Name") + getValue("Parent")
+            + getPhase();
+    return s.hashCode() + getBegin() + getEnd() + (int) getScore()
+            + getStrand();
+  }
+
+  /**
+   * Answers true if the feature's start/end values represent two related
+   * positions, rather than ends of a range. Such features may be visualised or
+   * reported differently to features on a range.
+   */
+  public boolean isContactFeature()
+  {
+    // TODO abstract one day to a FeatureType class
+    if ("disulfide bond".equalsIgnoreCase(type)
+            || "disulphide bond".equalsIgnoreCase(type))
     {
-      return 1;
+      return true;
     }
-    return 0;
+    return false;
   }
-
 }
diff --git a/src/jalview/datamodel/SequenceGroup.java b/src/jalview/datamodel/SequenceGroup.java
index c02a5e3..247c275 100644
--- a/src/jalview/datamodel/SequenceGroup.java
+++ b/src/jalview/datamodel/SequenceGroup.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -23,14 +23,11 @@ package jalview.datamodel;
 import jalview.analysis.AAFrequency;
 import jalview.analysis.Conservation;
 import jalview.schemes.ColourSchemeI;
-import jalview.schemes.ResidueProperties;
 
 import java.awt.Color;
 import java.util.ArrayList;
-import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
-import java.util.Vector;
 
 /**
  * Collects a set contiguous ranges on a set of sequences
@@ -46,8 +43,6 @@ public class SequenceGroup implements AnnotatedCollectionI
 
   Conservation conserve;
 
-  Vector aaFrequency;
-
   boolean displayBoxes = true;
 
   boolean displayText = true;
@@ -209,7 +204,7 @@ public class SequenceGroup implements AnnotatedCollectionI
       if (seqs[ipos] != null)
       {
         seqs[ipos].setDescription(seq.getDescription());
-        seqs[ipos].setDBRef(seq.getDBRef());
+        seqs[ipos].setDBRefs(seq.getDBRefs());
         seqs[ipos].setSequenceFeatures(seq.getSequenceFeatures());
         if (seq.getDatasetSequence() != null)
         {
@@ -504,32 +499,51 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
-   * calculate residue conservation for group - but only if necessary.
+   * calculate residue conservation and colourschemes for group - but only if
+   * necessary. returns true if the calculation resulted in a visible change to
+   * group
+   */
+  public boolean recalcConservation()
+  {
+    return recalcConservation(false);
+  }
+
+  /**
+   * calculate residue conservation for group - but only if necessary. returns
+   * true if the calculation resulted in a visible change to group
+   * 
+   * @param defer
+   *          when set, colourschemes for this group are not refreshed after
+   *          recalculation
    */
-  public void recalcConservation()
+  public boolean recalcConservation(boolean defer)
   {
     if (cs == null && consensus == null && conservation == null)
     {
-      return;
+      return false;
     }
+    // TODO: try harder to detect changes in state in order to minimise
+    // recalculation effort
+    boolean upd = false;
     try
     {
-      Hashtable cnsns[] = AAFrequency.calculate(sequences, startRes,
+      ProfilesI cnsns = AAFrequency.calculate(sequences, startRes,
               endRes + 1, showSequenceLogo);
       if (consensus != null)
       {
         _updateConsensusRow(cnsns, sequences.size());
+        upd = true;
       }
       if (cs != null)
       {
         cs.setConsensus(cnsns);
+        upd = true;
       }
 
       if ((conservation != null)
               || (cs != null && cs.conservationApplied()))
       {
-        Conservation c = new Conservation(groupName,
-                ResidueProperties.propHash, 3, sequences, startRes,
+        Conservation c = new Conservation(groupName, sequences, startRes,
                 endRes + 1);
         c.calculate();
         c.verdict(false, consPercGaps);
@@ -544,17 +558,25 @@ public class SequenceGroup implements AnnotatedCollectionI
             cs.setConservation(c);
           }
         }
+        // eager update - will cause a refresh of overview regardless
+        upd = true;
       }
-      if (cs != null)
+      if (cs != null && !defer)
       {
+        // TODO: JAL-2034 should cs.alignmentChanged modify return state
         cs.alignmentChanged(context != null ? context : this, null);
+        return true;
+      }
+      else
+      {
+        return upd;
       }
     } catch (java.lang.OutOfMemoryError err)
     {
       // TODO: catch OOM
       System.out.println("Out of memory loading groups: " + err);
     }
-
+    return upd;
   }
 
   private void _updateConservationRow(Conservation c)
@@ -577,9 +599,9 @@ public class SequenceGroup implements AnnotatedCollectionI
     c.completeAnnotations(conservation, null, startRes, endRes + 1);
   }
 
-  public Hashtable[] consensusData = null;
+  public ProfilesI consensusData = null;
 
-  private void _updateConsensusRow(Hashtable[] cnsns, long nseq)
+  private void _updateConsensusRow(ProfilesI cnsns, long nseq)
   {
     if (consensus == null)
     {
@@ -894,6 +916,7 @@ public class SequenceGroup implements AnnotatedCollectionI
   /**
    * @return the representative sequence for this group
    */
+  @Override
   public SequenceI getSeqrep()
   {
     return seqrep;
@@ -906,6 +929,7 @@ public class SequenceGroup implements AnnotatedCollectionI
    * @param seqrep
    *          the seqrep to set (null means no sequence representative)
    */
+  @Override
   public void setSeqrep(SequenceI seqrep)
   {
     this.seqrep = seqrep;
@@ -915,6 +939,7 @@ public class SequenceGroup implements AnnotatedCollectionI
    * 
    * @return true if group has a sequence representative
    */
+  @Override
   public boolean hasSeqrep()
   {
     return seqrep != null;
@@ -1036,7 +1061,8 @@ public class SequenceGroup implements AnnotatedCollectionI
 
   /**
    * 
-   * @return automatically calculated consensus row
+   * @return automatically calculated consensus row note: the row is a stub if a
+   *         consensus calculation has not yet been performed on the group
    */
   public AlignmentAnnotation getConsensus()
   {
diff --git a/src/jalview/datamodel/SequenceI.java b/src/jalview/datamodel/SequenceI.java
index 30ba758..d59e8b8 100644
--- a/src/jalview/datamodel/SequenceI.java
+++ b/src/jalview/datamodel/SequenceI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -217,6 +217,15 @@ public interface SequenceI extends ASequenceI
   public int[] findPositionMap();
 
   /**
+   * Answers true if the sequence is composed of amino acid characters. Note
+   * that implementations may use heuristic methods which are not guaranteed to
+   * give the biologically 'right' answer.
+   * 
+   * @return
+   */
+  public boolean isProtein();
+
+  /**
    * Delete a range of aligned sequence columns, creating a new dataset sequence
    * if necessary and adjusting start and end positions accordingly.
    * 
@@ -231,34 +240,39 @@ public interface SequenceI extends ASequenceI
    * DOCUMENT ME!
    * 
    * @param i
-   *          DOCUMENT ME!
+   *          alignment column number
    * @param c
-   *          DOCUMENT ME!
+   *          character to insert
    */
   public void insertCharAt(int i, char c);
 
   /**
-   * DOCUMENT ME!
+   * insert given character at alignment column position
    * 
    * @param position
-   *          DOCUMENT ME!
+   *          alignment column number
+   * @param count
+   *          length of insert
    * @param ch
-   *          DOCUMENT ME!
+   *          character to insert
    */
   public void insertCharAt(int position, int count, char ch);
 
   /**
-   * DOCUMENT ME!
+   * Gets array holding sequence features associated with this sequence. The
+   * array may be held by the sequence's dataset sequence if that is defined.
    * 
-   * @return DOCUMENT ME!
+   * @return hard reference to array
    */
   public SequenceFeature[] getSequenceFeatures();
 
   /**
-   * DOCUMENT ME!
+   * Replaces the array of sequence features associated with this sequence with
+   * a new array reference. If this sequence has a dataset sequence, then this
+   * method will update the dataset sequence's feature array
    * 
-   * @param v
-   *          DOCUMENT ME!
+   * @param features
+   *          New array of sequence features
    */
   public void setSequenceFeatures(SequenceFeature[] features);
 
@@ -278,11 +292,18 @@ public interface SequenceI extends ASequenceI
   public Vector<PDBEntry> getAllPDBEntries();
 
   /**
-   * add entry to the vector of PDBIds, if it isn't in the list already
+   * Adds the entry to the *normalised* list of PDBIds.
+   * 
+   * If a PDBEntry is passed with the same entry.getID() string as one already
+   * in the list, or one is added that appears to be the same but has a chain ID
+   * appended, then the existing PDBEntry will be updated with the new
+   * attributes instead, unless the entries have distinct chain codes or
+   * associated structure files.
    * 
    * @param entry
+   * @return true if the entry was added, false if updated
    */
-  public void addPDBId(PDBEntry entry);
+  public boolean addPDBId(PDBEntry entry);
 
   /**
    * update the list of PDBEntrys to include any DBRefEntrys citing structural
@@ -296,9 +317,17 @@ public interface SequenceI extends ASequenceI
 
   public void setVamsasId(String id);
 
-  public void setDBRef(DBRefEntry[] dbs);
+  /**
+   * set the array of Database references for the sequence.
+   * 
+   * @param dbs
+   * @deprecated - use is discouraged since side-effects may occur if DBRefEntry
+   *             set are not normalised.
+   */
+  @Deprecated
+  public void setDBRefs(DBRefEntry[] dbs);
 
-  public DBRefEntry[] getDBRef();
+  public DBRefEntry[] getDBRefs();
 
   /**
    * add the given entry to the list of DBRefs for this sequence, or replace a
@@ -429,4 +458,14 @@ public interface SequenceI extends ASequenceI
    * @return
    */
   public PDBEntry getPDBEntry(String pdbId);
+
+  /**
+   * Get all primary database/accessions for this sequence's data. These
+   * DBRefEntry are expected to resolve to a valid record in the associated
+   * external database, either directly or via a provided 1:1 Mapping.
+   * 
+   * @return just the primary references (if any) for this sequence, or an empty
+   *         list
+   */
+  public List<DBRefEntry> getPrimaryDBRefs();
 }
diff --git a/src/jalview/datamodel/SequenceNode.java b/src/jalview/datamodel/SequenceNode.java
index 84b1069..c523060 100644
--- a/src/jalview/datamodel/SequenceNode.java
+++ b/src/jalview/datamodel/SequenceNode.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/SequencePoint.java b/src/jalview/datamodel/SequencePoint.java
index 122400a..4a3b3b3 100644
--- a/src/jalview/datamodel/SequencePoint.java
+++ b/src/jalview/datamodel/SequencePoint.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/StructureViewerModel.java b/src/jalview/datamodel/StructureViewerModel.java
index 9a23813..e55eacb 100644
--- a/src/jalview/datamodel/StructureViewerModel.java
+++ b/src/jalview/datamodel/StructureViewerModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/UniprotEntry.java b/src/jalview/datamodel/UniprotEntry.java
index e1a09ea..dfc8afe 100644
--- a/src/jalview/datamodel/UniprotEntry.java
+++ b/src/jalview/datamodel/UniprotEntry.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/UniprotFile.java b/src/jalview/datamodel/UniprotFile.java
index 0f61b89..0bf9d33 100644
--- a/src/jalview/datamodel/UniprotFile.java
+++ b/src/jalview/datamodel/UniprotFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/UniprotProteinName.java b/src/jalview/datamodel/UniprotProteinName.java
index 2e6a0dc..653d41d 100644
--- a/src/jalview/datamodel/UniprotProteinName.java
+++ b/src/jalview/datamodel/UniprotProteinName.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/UniprotSequence.java b/src/jalview/datamodel/UniprotSequence.java
index c9b84fa..0105818 100644
--- a/src/jalview/datamodel/UniprotSequence.java
+++ b/src/jalview/datamodel/UniprotSequence.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/xdb/embl/EmblEntry.java b/src/jalview/datamodel/xdb/embl/EmblEntry.java
index f5dfef4..fd2ee65 100644
--- a/src/jalview/datamodel/xdb/embl/EmblEntry.java
+++ b/src/jalview/datamodel/xdb/embl/EmblEntry.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,8 @@
  */
 package jalview.datamodel.xdb.embl;
 
+import jalview.analysis.SequenceIdMatcher;
+import jalview.bin.Cache;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.FeatureProperties;
@@ -27,34 +29,58 @@ import jalview.datamodel.Mapping;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
-
+import jalview.util.DBRefUtils;
+import jalview.util.DnaUtils;
+import jalview.util.MapList;
+import jalview.util.MappingUtils;
+import jalview.util.StringUtils;
+
+import java.text.ParseException;
+import java.util.Arrays;
 import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Vector;
+import java.util.regex.Pattern;
 
 /**
  * Data model for one entry returned from an EMBL query, as marshalled by a
  * Castor binding file
  * 
- * For example: http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/embl/x53828/emblxml
+ * For example: http://www.ebi.ac.uk/ena/data/view/J03321&display=xml
  * 
  * @see embl_mapping.xml
  */
 public class EmblEntry
 {
+  private static final Pattern SPACE_PATTERN = Pattern.compile(" ");
+
   String accession;
 
-  String version;
+  String entryVersion;
+
+  String sequenceVersion;
+
+  String dataClass;
+
+  String moleculeType;
 
-  String taxDivision;
+  String topology;
 
-  String desc;
+  String sequenceLength;
 
-  String rCreated;
+  String taxonomicDivision;
 
-  String rLastUpdated;
+  String description;
 
-  String lastUpdated;
+  String firstPublicDate;
+
+  String firstPublicRelease;
+
+  String lastUpdatedDate;
+
+  String lastUpdatedRelease;
 
   Vector<String> keywords;
 
@@ -99,23 +125,6 @@ public class EmblEntry
   }
 
   /**
-   * @return the desc
-   */
-  public String getDesc()
-  {
-    return desc;
-  }
-
-  /**
-   * @param desc
-   *          the desc to set
-   */
-  public void setDesc(String desc)
-  {
-    this.desc = desc;
-  }
-
-  /**
    * @return the features
    */
   public Vector<EmblFeature> getFeatures()
@@ -150,57 +159,6 @@ public class EmblEntry
   }
 
   /**
-   * @return the lastUpdated
-   */
-  public String getLastUpdated()
-  {
-    return lastUpdated;
-  }
-
-  /**
-   * @param lastUpdated
-   *          the lastUpdated to set
-   */
-  public void setLastUpdated(String lastUpdated)
-  {
-    this.lastUpdated = lastUpdated;
-  }
-
-  /**
-   * @return the releaseCreated
-   */
-  public String getRCreated()
-  {
-    return rCreated;
-  }
-
-  /**
-   * @param releaseCreated
-   *          the releaseCreated to set
-   */
-  public void setRCreated(String releaseCreated)
-  {
-    this.rCreated = releaseCreated;
-  }
-
-  /**
-   * @return the releaseLastUpdated
-   */
-  public String getRLastUpdated()
-  {
-    return rLastUpdated;
-  }
-
-  /**
-   * @param releaseLastUpdated
-   *          the releaseLastUpdated to set
-   */
-  public void setRLastUpdated(String releaseLastUpdated)
-  {
-    this.rLastUpdated = releaseLastUpdated;
-  }
-
-  /**
    * @return the sequence
    */
   public EmblSequence getSequence()
@@ -218,240 +176,49 @@ public class EmblEntry
   }
 
   /**
-   * @return the taxDivision
-   */
-  public String getTaxDivision()
-  {
-    return taxDivision;
-  }
-
-  /**
-   * @param taxDivision
-   *          the taxDivision to set
-   */
-  public void setTaxDivision(String taxDivision)
-  {
-    this.taxDivision = taxDivision;
-  }
-
-  /**
-   * @return the version
-   */
-  public String getVersion()
-  {
-    return version;
-  }
-
-  /**
-   * @param version
-   *          the version to set
-   */
-  public void setVersion(String version)
-  {
-    this.version = version;
-  }
-
-  /*
-   * EMBL Feature support is limited. The text below is included for the benefit
-   * of any developer working on improving EMBL feature import in Jalview.
-   * Extract from EMBL feature specification see
-   * http://www.embl-ebi.ac.uk/embl/Documentation
-   * /FT_definitions/feature_table.html 3.5 Location 3.5.1 Purpose
-   * 
-   * The location indicates the region of the presented sequence which
-   * corresponds to a feature.
-   * 
-   * 3.5.2 Format and conventions The location contains at least one sequence
-   * location descriptor and may contain one or more operators with one or more
-   * sequence location descriptors. Base numbers refer to the numbering in the
-   * entry. This numbering designates the first base (5' end) of the presented
-   * sequence as base 1. Base locations beyond the range of the presented
-   * sequence may not be used in location descriptors, the only exception being
-   * location in a remote entry (see 3.5.2.1, e).
-   * 
-   * Location operators and descriptors are discussed in more detail below.
-   * 
-   * 3.5.2.1 Location descriptors
-   * 
-   * The location descriptor can be one of the following: (a) a single base
-   * number (b) a site between two indicated adjoining bases (c) a single base
-   * chosen from within a specified range of bases (not allowed for new entries)
-   * (d) the base numbers delimiting a sequence span (e) a remote entry
-   * identifier followed by a local location descriptor (i.e., a-d)
-   * 
-   * A site between two adjoining nucleotides, such as endonucleolytic cleavage
-   * site, is indicated by listing the two points separated by a carat (^). The
-   * permitted formats for this descriptor are n^n+1 (for example 55^56), or,
-   * for circular molecules, n^1, where "n" is the full length of the molecule,
-   * ie 1000^1 for circular molecule with length 1000.
-   * 
-   * A single base chosen from a range of bases is indicated by the first base
-   * number and the last base number of the range separated by a single period
-   * (e.g., '12.21' indicates a single base taken from between the indicated
-   * points). From October 2006 the usage of this descriptor is restricted : it
-   * is illegal to use "a single base from a range" (c) either on its own or in
-   * combination with the "sequence span" (d) descriptor for newly created
-   * entries. The existing entries where such descriptors exist are going to be
-   * retrofitted.
-   * 
-   * Sequence spans are indicated by the starting base number and the ending
-   * base number separated by two periods (e.g., '34..456'). The '<' and '>'
-   * symbols may be used with the starting and ending base numbers to indicate
-   * that an end point is beyond the specified base number. The starting and
-   * ending base positions can be represented as distinct base numbers
-   * ('34..456') or a site between two indicated adjoining bases.
-   * 
-   * A location in a remote entry (not the entry to which the feature table
-   * belongs) can be specified by giving the accession-number and sequence
-   * version of the remote entry, followed by a colon ":", followed by a
-   * location descriptor which applies to that entry's sequence (i.e.
-   * J12345.1:1..15, see also examples below)
-   * 
-   * 3.5.2.2 Operators
-   * 
-   * The location operator is a prefix that specifies what must be done to the
-   * indicated sequence to find or construct the location corresponding to the
-   * feature. A list of operators is given below with their definitions and most
-   * common format.
-   * 
-   * complement(location) Find the complement of the presented sequence in the
-   * span specified by " location" (i.e., read the complement of the presented
-   * strand in its 5'-to-3' direction)
-   * 
-   * join(location,location, ... location) The indicated elements should be
-   * joined (placed end-to-end) to form one contiguous sequence
-   * 
-   * order(location,location, ... location) The elements can be found in the
-   * specified order (5' to 3' direction), but nothing is implied about the
-   * reasonableness about joining them
-   * 
-   * Note : location operator "complement" can be used in combination with
-   * either " join" or "order" within the same location; combinations of "join"
-   * and "order" within the same location (nested operators) are illegal.
-   * 
-   * 
-   * 
-   * 3.5.3 Location examples
-   * 
-   * The following is a list of common location descriptors with their meanings:
-   * 
-   * Location Description
-   * 
-   * 467 Points to a single base in the presented sequence
-   * 
-   * 340..565 Points to a continuous range of bases bounded by and including the
-   * starting and ending bases
-   * 
-   * <345..500 Indicates that the exact lower boundary point of a feature is
-   * unknown. The location begins at some base previous to the first base
-   * specified (which need not be contained in the presented sequence) and
-   * continues to and includes the ending base
-   * 
-   * <1..888 The feature starts before the first sequenced base and continues to
-   * and includes base 888
-   * 
-   * 1..>888 The feature starts at the first sequenced base and continues beyond
-   * base 888
-   * 
-   * 102.110 Indicates that the exact location is unknown but that it is one of
-   * the bases between bases 102 and 110, inclusive
-   * 
-   * 123^124 Points to a site between bases 123 and 124
-   * 
-   * join(12..78,134..202) Regions 12 to 78 and 134 to 202 should be joined to
-   * form one contiguous sequence
-   * 
-   * 
-   * complement(34..126) Start at the base complementary to 126 and finish at
-   * the base complementary to base 34 (the feature is on the strand
-   * complementary to the presented strand)
-   * 
-   * 
-   * complement(join(2691..4571,4918..5163)) Joins regions 2691 to 4571 and 4918
-   * to 5163, then complements the joined segments (the feature is on the strand
-   * complementary to the presented strand)
-   * 
-   * join(complement(4918..5163),complement(2691..4571)) Complements regions
-   * 4918 to 5163 and 2691 to 4571, then joins the complemented segments (the
-   * feature is on the strand complementary to the presented strand)
-   * 
-   * J00194.1:100..202 Points to bases 100 to 202, inclusive, in the entry (in
-   * this database) with primary accession number 'J00194'
-   * 
-   * join(1..100,J00194.1:100..202) Joins region 1..100 of the existing entry
-   * with the region 100..202 of remote entry J00194
-   */
-  /**
    * Recover annotated sequences from EMBL file
    * 
-   * @param noNa
-   *          don't return nucleic acid sequences
    * @param sourceDb
-   *          TODO
-   * @param noProtein
-   *          don't return any translated protein sequences marked in features
-   * @return dataset sequences with DBRefs and features - DNA always comes first
+   * @param peptides
+   *          a list of protein products found so far (to add to)
+   * @return dna dataset sequence with DBRefs and features
    */
-  public jalview.datamodel.SequenceI[] getSequences(boolean noNa,
-          boolean noPeptide, String sourceDb)
-  { // TODO: ensure emblEntry.getSequences behaves correctly for returning all
-    // cases of noNa and noPeptide
-    Vector<SequenceI> seqs = new Vector<SequenceI>();
-    Sequence dna = null;
-    if (!noNa)
+  public SequenceI getSequence(String sourceDb, List<SequenceI> peptides)
+  {
+    SequenceI dna = makeSequence(sourceDb);
+    if (dna == null)
     {
-      // In theory we still need to create this if noNa is set to avoid a null
-      // pointer exception
-      dna = new Sequence(sourceDb + "|" + accession, sequence.getSequence());
-      dna.setDescription(desc);
-      DBRefEntry retrievedref = new DBRefEntry(sourceDb, version, accession);
-      dna.addDBRef(retrievedref);
-      // add map to indicate the sequence is a valid coordinate frame for the
-      // dbref
-      retrievedref.setMap(new Mapping(null,
-              new int[] { 1, dna.getLength() }, new int[] { 1,
-                  dna.getLength() }, 1, 1));
-      // TODO: transform EMBL Database refs to canonical form
-      if (dbRefs != null)
+      return null;
+    }
+    dna.setDescription(description);
+    DBRefEntry retrievedref = new DBRefEntry(sourceDb,
+            getSequenceVersion(), accession);
+    dna.addDBRef(retrievedref);
+    // add map to indicate the sequence is a valid coordinate frame for the
+    // dbref
+    retrievedref.setMap(new Mapping(null, new int[] { 1, dna.getLength() },
+            new int[] { 1, dna.getLength() }, 1, 1));
+
+    /*
+     * transform EMBL Database refs to canonical form
+     */
+    if (dbRefs != null)
+    {
+      for (DBRefEntry dbref : dbRefs)
       {
-        for (DBRefEntry dbref : dbRefs)
-        {
-          dna.addDBRef(dbref);
-        }
+        dbref.setSource(DBRefUtils.getCanonicalName(dbref.getSource()));
+        dna.addDBRef(dbref);
       }
     }
+
+    SequenceIdMatcher matcher = new SequenceIdMatcher(peptides);
     try
     {
       for (EmblFeature feature : features)
       {
-        if (!noNa)
-        {
-          if (feature.dbRefs != null)
-          {
-            for (DBRefEntry dbref : feature.dbRefs)
-            {
-              dna.addDBRef(dbref);
-            }
-          }
-        }
         if (FeatureProperties.isCodingFeature(sourceDb, feature.getName()))
         {
-          parseCodingFeature(feature, sourceDb, seqs, dna, noPeptide);
-        }
-        else
-        {
-          // General feature type.
-          // TODO this is just duplicated code ??
-          if (!noNa)
-          {
-            if (feature.dbRefs != null)
-            {
-              for (DBRefEntry dbref : feature.dbRefs)
-              {
-                dna.addDBRef(dbref);
-              }
-            }
-          }
+          parseCodingFeature(feature, sourceDb, dna, peptides, matcher);
         }
       }
     } catch (Exception e)
@@ -463,65 +230,64 @@ public class EmblEntry
       System.err.println("Resulted in exception: " + e.getMessage());
       e.printStackTrace(System.err);
     }
-    if (!noNa && dna != null)
-    {
-      seqs.add(dna);
-    }
-    SequenceI[] sqs = new SequenceI[seqs.size()];
-    for (int i = 0, j = seqs.size(); i < j; i++)
+
+    return dna;
+  }
+
+  /**
+   * @param sourceDb
+   * @return
+   */
+  SequenceI makeSequence(String sourceDb)
+  {
+    if (sequence == null)
     {
-      sqs[i] = seqs.elementAt(i);
-      seqs.set(i, null);
+      System.err.println("No sequence was returned for ENA accession "
+              + accession);
+      return null;
     }
-    return sqs;
+    SequenceI dna = new Sequence(sourceDb + "|" + accession,
+            sequence.getSequence());
+    return dna;
   }
 
   /**
-   * attempt to extract coding region and product from a feature and properly
-   * decorate it with annotations.
+   * Extracts coding region and product from a CDS feature and properly decorate
+   * it with annotations.
    * 
    * @param feature
    *          coding feature
    * @param sourceDb
    *          source database for the EMBLXML
-   * @param seqs
-   *          place where sequences go
    * @param dna
    *          parent dna sequence for this record
-   * @param noPeptide
-   *          flag for generation of Peptide sequence objects
+   * @param peptides
+   *          list of protein product sequences for Embl entry
+   * @param matcher
+   *          helper to match xrefs in already retrieved sequences
    */
-  private void parseCodingFeature(EmblFeature feature, String sourceDb,
-          Vector<SequenceI> seqs, Sequence dna, boolean noPeptide)
+  void parseCodingFeature(EmblFeature feature, String sourceDb,
+          SequenceI dna, List<SequenceI> peptides, SequenceIdMatcher matcher)
   {
     boolean isEmblCdna = sourceDb.equals(DBRefSource.EMBLCDS);
-    // extract coding region(s)
-    jalview.datamodel.Mapping map = null;
-    int[] exon = null;
-    if (feature.locations != null)
-    {
-      for (EmblFeatureLocations loc : feature.locations)
-      {
-        int[] se = loc.getElementRanges(accession);
-        if (exon == null)
-        {
-          exon = se;
-        }
-        else
-        {
-          int[] t = new int[exon.length + se.length];
-          System.arraycopy(exon, 0, t, 0, exon.length);
-          System.arraycopy(se, 0, t, exon.length, se.length);
-          exon = t;
-        }
-      }
-    }
-    String prseq = null;
-    String prname = new String();
-    String prid = null;
-    Hashtable<String, String> vals = new Hashtable<String, String>();
-    int prstart = 1;
-    // get qualifiers
+
+    int[] exons = getCdsRanges(feature);
+
+    String translation = null;
+    String proteinName = "";
+    String proteinId = null;
+    Map<String, String> vals = new Hashtable<String, String>();
+
+    /*
+     * codon_start 1/2/3 in EMBL corresponds to phase 0/1/2 in CDS
+     * (phase is required for CDS features in GFF3 format)
+     */
+    int codonStart = 1;
+
+    /*
+     * parse qualifiers, saving protein translation, protein id,
+     * codon start position, product (name), and 'other values'
+     */
     if (feature.getQualifiers() != null)
     {
       for (Qualifier q : feature.getQualifiers())
@@ -529,88 +295,97 @@ public class EmblEntry
         String qname = q.getName();
         if (qname.equals("translation"))
         {
-          StringBuilder prsq = new StringBuilder(q.getValues()[0]);
-          int p = prsq.indexOf(" ");
-          while (p > -1)
-          {
-            prsq.deleteCharAt(p);
-            p = prsq.indexOf(" ", p);
-          }
-          prseq = prsq.toString();
-          prsq = null;
-
+          // remove all spaces (precompiled String.replaceAll(" ", ""))
+          translation = SPACE_PATTERN.matcher(q.getValues()[0]).replaceAll(
+                  "");
         }
         else if (qname.equals("protein_id"))
         {
-          prid = q.getValues()[0];
+          proteinId = q.getValues()[0].trim();
         }
         else if (qname.equals("codon_start"))
         {
-          prstart = Integer.parseInt(q.getValues()[0]);
+          try
+          {
+            codonStart = Integer.parseInt(q.getValues()[0].trim());
+          } catch (NumberFormatException e)
+          {
+            System.err.println("Invalid codon_start in XML for "
+                    + accession + ": " + e.getMessage());
+          }
         }
         else if (qname.equals("product"))
         {
-          prname = q.getValues()[0];
+          // sometimes name is returned e.g. for V00488
+          proteinName = q.getValues()[0].trim();
         }
         else
         {
           // throw anything else into the additional properties hash
-          String[] s = q.getValues();
-          StringBuilder sb = new StringBuilder();
-          if (s != null)
+          String[] qvals = q.getValues();
+          if (qvals != null)
           {
-            for (int i = 0; i < s.length; i++)
-            {
-              sb.append(s[i]);
-              sb.append("\n");
-            }
+            String commaSeparated = StringUtils.arrayToSeparatorList(qvals,
+                    ",");
+            vals.put(qname, commaSeparated);
           }
-          vals.put(qname, sb.toString());
         }
       }
     }
-    Sequence product = null;
-    DBRefEntry protEMBLCDS = null;
-    exon = adjustForPrStart(prstart, exon);
-    boolean noProteinDbref = true;
 
-    if (prseq != null && prname != null && prid != null)
+    DBRefEntry proteinToEmblProteinRef = null;
+    exons = MappingUtils.removeStartPositions(codonStart - 1, exons);
+
+    SequenceI product = null;
+    Mapping dnaToProteinMapping = null;
+    if (translation != null && proteinName != null && proteinId != null)
     {
-      // extract proteins.
-      product = new Sequence(prid, prseq, 1, prseq.length());
-      product.setDescription(((prname.length() == 0) ? "Protein Product from "
-              + sourceDb
-              : prname));
-      if (!noPeptide)
+      int translationLength = translation.length();
+
+      /*
+       * look for product in peptides list, if not found, add it
+       */
+      product = matcher.findIdMatch(proteinId);
+      if (product == null)
       {
-        // Protein is also added to vector of sequences returned
-        seqs.add(product);
+        product = new Sequence(proteinId, translation, 1, translationLength);
+        product.setDescription(((proteinName.length() == 0) ? "Protein Product from "
+                + sourceDb
+                : proteinName));
+        peptides.add(product);
+        matcher.add(product);
       }
+
       // we have everything - create the mapping and perhaps the protein
       // sequence
-      if (exon == null || exon.length == 0)
+      if (exons == null || exons.length == 0)
       {
+        /*
+         * workaround until we handle dna location for CDS sequence
+         * e.g. location="X53828.1:60..1058" correctly
+         */
         System.err
                 .println("Implementation Notice: EMBLCDS records not properly supported yet - Making up the CDNA region of this sequence... may be incorrect ("
                         + sourceDb + ":" + getAccession() + ")");
-        if (prseq.length() * 3 == (1 - prstart + dna.getSequence().length))
+        if (translationLength * 3 == (1 - codonStart + dna.getSequence().length))
         {
           System.err
                   .println("Not allowing for additional stop codon at end of cDNA fragment... !");
-          // this might occur for CDS sequences where no features are
-          // marked.
-          exon = new int[] { dna.getStart() + (prstart - 1), dna.getEnd() };
-          map = new jalview.datamodel.Mapping(product, exon, new int[] { 1,
-              prseq.length() }, 3, 1);
+          // this might occur for CDS sequences where no features are marked
+          exons = new int[] { dna.getStart() + (codonStart - 1),
+              dna.getEnd() };
+          dnaToProteinMapping = new Mapping(product, exons, new int[] { 1,
+              translationLength }, 3, 1);
         }
-        if ((prseq.length() + 1) * 3 == (1 - prstart + dna.getSequence().length))
+        if ((translationLength + 1) * 3 == (1 - codonStart + dna
+                .getSequence().length))
         {
           System.err
                   .println("Allowing for additional stop codon at end of cDNA fragment... will probably cause an error in VAMSAs!");
-          exon = new int[] { dna.getStart() + (prstart - 1),
+          exons = new int[] { dna.getStart() + (codonStart - 1),
               dna.getEnd() - 3 };
-          map = new jalview.datamodel.Mapping(product, exon, new int[] { 1,
-              prseq.length() }, 3, 1);
+          dnaToProteinMapping = new Mapping(product, exons, new int[] { 1,
+              translationLength }, 3, 1);
         }
       }
       else
@@ -628,218 +403,452 @@ public class EmblEntry
         }
         else
         {
-          // final product length trunctation check
-
-          map = new jalview.datamodel.Mapping(product,
-                  adjustForProteinLength(prseq.length(), exon), new int[] {
-                      1, prseq.length() }, 3, 1);
-          // reconstruct the EMBLCDS entry
-          // TODO: this is only necessary when there codon annotation is
-          // complete (I think JBPNote)
-          DBRefEntry pcdnaref = new DBRefEntry();
-          pcdnaref.setAccessionId(prid);
-          pcdnaref.setSource(DBRefSource.EMBLCDS);
-          pcdnaref.setVersion(getVersion()); // same as parent EMBL version.
-          jalview.util.MapList mp = new jalview.util.MapList(new int[] { 1,
-              prseq.length() }, new int[] { 1 + (prstart - 1),
-              (prstart - 1) + 3 * prseq.length() }, 1, 3);
-          // { 1 + (prstart - 1) * 3,
-          // 1 + (prstart - 1) * 3 + prseq.length() * 3 - 1 }, new int[]
-          // { 1prstart, prstart + prseq.length() - 1 }, 3, 1);
-          pcdnaref.setMap(new Mapping(mp));
+          // final product length truncation check
+          int[] cdsRanges = adjustForProteinLength(translationLength, exons);
+          dnaToProteinMapping = new Mapping(product, cdsRanges, new int[] {
+              1, translationLength }, 3, 1);
           if (product != null)
           {
-            product.addDBRef(pcdnaref);
-            protEMBLCDS = new DBRefEntry(pcdnaref);
-            protEMBLCDS.setSource(DBRefSource.EMBLCDSProduct);
-            product.addDBRef(protEMBLCDS);
-
+            /*
+             * make xref with mapping from protein to EMBL dna
+             */
+            DBRefEntry proteinToEmblRef = new DBRefEntry(DBRefSource.EMBL,
+                    getSequenceVersion(), proteinId, new Mapping(
+                            dnaToProteinMapping.getMap().getInverse()));
+            product.addDBRef(proteinToEmblRef);
+
+            /*
+             * make xref from protein to EMBLCDS; we assume here that the 
+             * CDS sequence version is same as dna sequence (?!)
+             */
+            MapList proteinToCdsMapList = new MapList(new int[] { 1,
+                translationLength }, new int[] { 1 + (codonStart - 1),
+                (codonStart - 1) + 3 * translationLength }, 1, 3);
+            DBRefEntry proteinToEmblCdsRef = new DBRefEntry(
+                    DBRefSource.EMBLCDS, getSequenceVersion(), proteinId,
+                    new Mapping(proteinToCdsMapList));
+            product.addDBRef(proteinToEmblCdsRef);
+
+            /*
+             * make 'direct' xref from protein to EMBLCDSPROTEIN
+             */
+            proteinToEmblProteinRef = new DBRefEntry(proteinToEmblCdsRef);
+            proteinToEmblProteinRef.setSource(DBRefSource.EMBLCDSProduct);
+            proteinToEmblProteinRef.setMap(null);
+            product.addDBRef(proteinToEmblProteinRef);
           }
-
         }
       }
-      // add cds feature to dna seq - this may include the stop codon
-      for (int xint = 0; exon != null && xint < exon.length; xint += 2)
+
+      /*
+       * add cds features to dna sequence
+       */
+      for (int xint = 0; exons != null && xint < exons.length; xint += 2)
       {
-        SequenceFeature sf = new SequenceFeature();
-        sf.setBegin(exon[xint]);
-        sf.setEnd(exon[xint + 1]);
-        sf.setType(feature.getName());
+        SequenceFeature sf = makeCdsFeature(exons, xint, proteinName,
+                proteinId, vals, codonStart);
+        sf.setType(feature.getName()); // "CDS"
+        sf.setEnaLocation(feature.getLocation());
         sf.setFeatureGroup(sourceDb);
-        sf.setDescription("Exon " + (1 + xint / 2) + " for protein '"
-                + prname + "' EMBLCDS:" + prid);
-        sf.setValue(FeatureProperties.EXONPOS, new Integer(1 + xint));
-        sf.setValue(FeatureProperties.EXONPRODUCT, prname);
-        if (vals != null)
-        {
-          for (Entry<String, String> val : vals.entrySet())
-          {
-            sf.setValue(val.getKey(), val.getValue());
-          }
-        }
         dna.addSequenceFeature(sf);
       }
     }
-    // add dbRefs to sequence
+
+    /*
+     * add feature dbRefs to sequence, and mappings for Uniprot xrefs
+     */
+    boolean hasUniprotDbref = false;
     if (feature.dbRefs != null)
     {
+      boolean mappingUsed = false;
       for (DBRefEntry ref : feature.dbRefs)
       {
-        ref.setSource(jalview.util.DBRefUtils.getCanonicalName(ref
-                .getSource()));
-        // Hard code the kind of protein product accessions that EMBL cite
-        if (ref.getSource().equals(jalview.datamodel.DBRefSource.UNIPROT))
+        /*
+         * ensure UniProtKB/Swiss-Prot converted to UNIPROT
+         */
+        String source = DBRefUtils.getCanonicalName(ref.getSource());
+        ref.setSource(source);
+        DBRefEntry proteinDbRef = new DBRefEntry(ref.getSource(),
+                ref.getVersion(), ref.getAccessionId());
+        if (source.equals(DBRefSource.UNIPROT))
         {
-          ref.setMap(map);
-          if (map != null && map.getTo() != null)
+          String proteinSeqName = DBRefSource.UNIPROT + "|"
+                  + ref.getAccessionId();
+          if (dnaToProteinMapping != null
+                  && dnaToProteinMapping.getTo() != null)
           {
-            map.getTo().addDBRef(
-                    new DBRefEntry(ref.getSource(), ref.getVersion(), ref
-                            .getAccessionId())); // don't copy map over.
-            if (map.getTo().getName().indexOf(prid) == 0)
+            if (mappingUsed)
             {
-              map.getTo().setName(
-                      jalview.datamodel.DBRefSource.UNIPROT + "|"
-                              + ref.getAccessionId());
+              /*
+               * two or more Uniprot xrefs for the same CDS - 
+               * each needs a distinct Mapping (as to a different sequence)
+               */
+              dnaToProteinMapping = new Mapping(dnaToProteinMapping);
             }
+            mappingUsed = true;
+
+            /*
+             * try to locate the protein mapped to (possibly by a 
+             * previous CDS feature); if not found, construct it from
+             * the EMBL translation
+             */
+            SequenceI proteinSeq = matcher.findIdMatch(proteinSeqName);
+            if (proteinSeq == null)
+            {
+              proteinSeq = new Sequence(proteinSeqName,
+                      product.getSequenceAsString());
+              matcher.add(proteinSeq);
+              peptides.add(proteinSeq);
+            }
+            dnaToProteinMapping.setTo(proteinSeq);
+            dnaToProteinMapping.setMappedFromId(proteinId);
+            proteinSeq.addDBRef(proteinDbRef);
+            ref.setMap(dnaToProteinMapping);
           }
-          noProteinDbref = false;
+          hasUniprotDbref = true;
         }
         if (product != null)
         {
-          DBRefEntry pref = new DBRefEntry(ref.getSource(),
-                  ref.getVersion(), ref.getAccessionId());
+          /*
+           * copy feature dbref to our protein product
+           */
+          DBRefEntry pref = proteinDbRef;
           pref.setMap(null); // reference is direct
           product.addDBRef(pref);
           // Add converse mapping reference
-          if (map != null)
+          if (dnaToProteinMapping != null)
           {
-            Mapping pmap = new Mapping(dna, map.getMap().getInverse());
-            pref = new DBRefEntry(sourceDb, getVersion(),
+            Mapping pmap = new Mapping(dna, dnaToProteinMapping.getMap()
+                    .getInverse());
+            pref = new DBRefEntry(sourceDb, getSequenceVersion(),
                     this.getAccession());
             pref.setMap(pmap);
-            if (map.getTo() != null)
+            if (dnaToProteinMapping.getTo() != null)
             {
-              map.getTo().addDBRef(pref);
+              dnaToProteinMapping.getTo().addDBRef(pref);
             }
           }
         }
         dna.addDBRef(ref);
       }
-      if (noProteinDbref && product != null)
+    }
+
+    /*
+     * if we have a product (translation) but no explicit Uniprot dbref
+     * (example: EMBL AAFI02000057 protein_id EAL65544.1)
+     * then construct mappings to an assumed EMBLCDSPROTEIN accession
+     */
+    if (!hasUniprotDbref && product != null)
+    {
+      if (proteinToEmblProteinRef == null)
       {
-        // add protein coding reference to dna sequence so xref matches
-        if (protEMBLCDS == null)
-        {
-          protEMBLCDS = new DBRefEntry();
-          protEMBLCDS.setAccessionId(prid);
-          protEMBLCDS.setSource(DBRefSource.EMBLCDSProduct);
-          protEMBLCDS.setVersion(getVersion());
-          protEMBLCDS
-                  .setMap(new Mapping(product, map.getMap().getInverse()));
-        }
-        product.addDBRef(protEMBLCDS);
+        // assuming CDSPROTEIN sequence version = dna version (?!)
+        proteinToEmblProteinRef = new DBRefEntry(
+                DBRefSource.EMBLCDSProduct, getSequenceVersion(), proteinId);
+      }
+      product.addDBRef(proteinToEmblProteinRef);
 
-        // Add converse mapping reference
-        if (map != null)
-        {
-          Mapping pmap = new Mapping(product, protEMBLCDS.getMap().getMap()
-                  .getInverse());
-          DBRefEntry ncMap = new DBRefEntry(protEMBLCDS);
-          ncMap.setMap(pmap);
-          if (map.getTo() != null)
-          {
-            dna.addDBRef(ncMap);
-          }
-        }
+      if (dnaToProteinMapping != null
+              && dnaToProteinMapping.getTo() != null)
+      {
+        DBRefEntry dnaToEmblProteinRef = new DBRefEntry(
+                DBRefSource.EMBLCDSProduct, getSequenceVersion(), proteinId);
+        dnaToEmblProteinRef.setMap(dnaToProteinMapping);
+        dnaToProteinMapping.setMappedFromId(proteinId);
+        dna.addDBRef(dnaToEmblProteinRef);
       }
     }
   }
 
-  private int[] adjustForPrStart(int prstart, int[] exon)
+  /**
+   * Helper method to construct a SequenceFeature for one cds range
+   * 
+   * @param exons
+   *          array of cds [start, end, ...] positions
+   * @param exonStartIndex
+   *          offset into the exons array
+   * @param proteinName
+   * @param proteinAccessionId
+   * @param vals
+   *          map of 'miscellaneous values' for feature
+   * @param codonStart
+   *          codon start position for CDS (1/2/3, normally 1)
+   * @return
+   */
+  protected SequenceFeature makeCdsFeature(int[] exons, int exonStartIndex,
+          String proteinName, String proteinAccessionId,
+          Map<String, String> vals, int codonStart)
   {
-
-    int origxon[], sxpos = -1;
-    int sxstart, sxstop; // unnecessary variables used for debugging
-    // first adjust range for codon start attribute
-    if (prstart > 1)
+    int exonNumber = exonStartIndex / 2 + 1;
+    SequenceFeature sf = new SequenceFeature();
+    sf.setBegin(Math.min(exons[exonStartIndex], exons[exonStartIndex + 1]));
+    sf.setEnd(Math.max(exons[exonStartIndex], exons[exonStartIndex + 1]));
+    sf.setDescription(String.format("Exon %d for protein '%s' EMBLCDS:%s",
+            exonNumber, proteinName, proteinAccessionId));
+    sf.setPhase(String.valueOf(codonStart - 1));
+    sf.setStrand(exons[exonStartIndex] <= exons[exonStartIndex + 1] ? "+"
+            : "-");
+    sf.setValue(FeatureProperties.EXONPOS, exonNumber);
+    sf.setValue(FeatureProperties.EXONPRODUCT, proteinName);
+    if (!vals.isEmpty())
     {
-      origxon = new int[exon.length];
-      System.arraycopy(exon, 0, origxon, 0, exon.length);
-      int cdspos = 0;
-      for (int x = 0; x < exon.length && sxpos == -1; x += 2)
+      StringBuilder sb = new StringBuilder();
+      boolean first = true;
+      for (Entry<String, String> val : vals.entrySet())
       {
-        cdspos += exon[x + 1] - exon[x] + 1;
-        if (prstart <= cdspos)
+        if (!first)
         {
-          sxpos = x;
-          sxstart = exon[x];
-          sxstop = exon[x + 1];
-          // and adjust start boundary of first exon.
-          exon[x] = exon[x + 1] - cdspos + prstart;
-          break;
+          sb.append(";");
         }
+        sb.append(val.getKey()).append("=").append(val.getValue());
+        first = false;
+        sf.setValue(val.getKey(), val.getValue());
       }
+      sf.setAttributes(sb.toString());
+    }
+    return sf;
+  }
 
-      if (sxpos > 0)
-      {
-        int[] nxon = new int[exon.length - sxpos];
-        System.arraycopy(exon, sxpos, nxon, 0, exon.length - sxpos);
-        exon = nxon;
-      }
+  /**
+   * Returns the CDS positions as a single array of [start, end, start, end...]
+   * positions. If on the reverse strand, these will be in descending order.
+   * 
+   * @param feature
+   * @return
+   */
+  protected int[] getCdsRanges(EmblFeature feature)
+  {
+    if (feature.location == null)
+    {
+      return new int[] {};
     }
-    return exon;
+
+    try
+    {
+      List<int[]> ranges = DnaUtils.parseLocation(feature.location);
+      return listToArray(ranges);
+    } catch (ParseException e)
+    {
+      Cache.log.warn(String.format(
+              "Not parsing inexact CDS location %s in ENA %s",
+              feature.location, this.accession));
+      return new int[] {};
+    }
+  }
+
+  /**
+   * Converts a list of [start, end] ranges to a single array of [start, end,
+   * start, end ...]
+   * 
+   * @param ranges
+   * @return
+   */
+  int[] listToArray(List<int[]> ranges)
+  {
+    int[] result = new int[ranges.size() * 2];
+    int i = 0;
+    for (int[] range : ranges)
+    {
+      result[i++] = range[0];
+      result[i++] = range[1];
+    }
+    return result;
   }
 
   /**
-   * truncate the last exon interval to the prlength'th codon
+   * Truncates (if necessary) the exon intervals to match 3 times the length of
+   * the protein; also accepts 3 bases longer (for stop codon not included in
+   * protein)
    * 
-   * @param prlength
+   * @param proteinLength
    * @param exon
-   * @return new exon
+   *          an array of [start, end, start, end...] intervals
+   * @return the same array (if unchanged) or a truncated copy
    */
-  private int[] adjustForProteinLength(int prlength, int[] exon)
+  static int[] adjustForProteinLength(int proteinLength, int[] exon)
   {
+    if (proteinLength <= 0 || exon == null)
+    {
+      return exon;
+    }
+    int expectedCdsLength = proteinLength * 3;
+    int exonLength = MappingUtils.getLength(Arrays.asList(exon));
+
+    /*
+     * if exon length matches protein, or is shorter, or longer by the 
+     * length of a stop codon (3 bases), then leave it unchanged
+     */
+    if (expectedCdsLength >= exonLength
+            || expectedCdsLength == exonLength - 3)
+    {
+      return exon;
+    }
 
-    int origxon[], sxpos = -1, endxon = 0, cdslength = prlength * 3;
-    int sxstart, sxstop; // unnecessary variables used for debugging
-    // first adjust range for codon start attribute
-    if (prlength >= 1 && exon != null)
+    int origxon[];
+    int sxpos = -1;
+    int endxon = 0;
+    origxon = new int[exon.length];
+    System.arraycopy(exon, 0, origxon, 0, exon.length);
+    int cdspos = 0;
+    for (int x = 0; x < exon.length; x += 2)
     {
-      origxon = new int[exon.length];
-      System.arraycopy(exon, 0, origxon, 0, exon.length);
-      int cdspos = 0;
-      for (int x = 0; x < exon.length && sxpos == -1; x += 2)
+      cdspos += Math.abs(exon[x + 1] - exon[x]) + 1;
+      if (expectedCdsLength <= cdspos)
       {
-        cdspos += exon[x + 1] - exon[x] + 1;
-        if (cdslength <= cdspos)
+        // advanced beyond last codon.
+        sxpos = x;
+        if (expectedCdsLength != cdspos)
         {
-          // advanced beyond last codon.
-          sxpos = x;
-          sxstart = exon[x];
-          sxstop = exon[x + 1];
-          if (cdslength != cdspos)
-          {
-            System.err
-                    .println("Truncating final exon interval on region by "
-                            + (cdspos - cdslength));
-          }
-          // locate the new end boundary of final exon as endxon
-          endxon = exon[x + 1] - cdspos + cdslength;
-          break;
+          // System.err
+          // .println("Truncating final exon interval on region by "
+          // + (cdspos - cdslength));
         }
-      }
 
-      if (sxpos != -1)
-      {
-        // and trim the exon interval set if necessary
-        int[] nxon = new int[sxpos + 2];
-        System.arraycopy(exon, 0, nxon, 0, sxpos + 2);
-        nxon[sxpos + 1] = endxon; // update the end boundary for the new exon
-                                  // set
-        exon = nxon;
+        /*
+         * shrink the final exon - reduce end position if forward
+         * strand, increase it if reverse
+         */
+        if (exon[x + 1] >= exon[x])
+        {
+          endxon = exon[x + 1] - cdspos + expectedCdsLength;
+        }
+        else
+        {
+          endxon = exon[x + 1] + cdspos - expectedCdsLength;
+        }
+        break;
       }
     }
+
+    if (sxpos != -1)
+    {
+      // and trim the exon interval set if necessary
+      int[] nxon = new int[sxpos + 2];
+      System.arraycopy(exon, 0, nxon, 0, sxpos + 2);
+      nxon[sxpos + 1] = endxon; // update the end boundary for the new exon
+                                // set
+      exon = nxon;
+    }
     return exon;
   }
+
+  public String getSequenceVersion()
+  {
+    return sequenceVersion;
+  }
+
+  public void setSequenceVersion(String sequenceVersion)
+  {
+    this.sequenceVersion = sequenceVersion;
+  }
+
+  public String getSequenceLength()
+  {
+    return sequenceLength;
+  }
+
+  public void setSequenceLength(String sequenceLength)
+  {
+    this.sequenceLength = sequenceLength;
+  }
+
+  public String getEntryVersion()
+  {
+    return entryVersion;
+  }
+
+  public void setEntryVersion(String entryVersion)
+  {
+    this.entryVersion = entryVersion;
+  }
+
+  public String getMoleculeType()
+  {
+    return moleculeType;
+  }
+
+  public void setMoleculeType(String moleculeType)
+  {
+    this.moleculeType = moleculeType;
+  }
+
+  public String getTopology()
+  {
+    return topology;
+  }
+
+  public void setTopology(String topology)
+  {
+    this.topology = topology;
+  }
+
+  public String getTaxonomicDivision()
+  {
+    return taxonomicDivision;
+  }
+
+  public void setTaxonomicDivision(String taxonomicDivision)
+  {
+    this.taxonomicDivision = taxonomicDivision;
+  }
+
+  public String getDescription()
+  {
+    return description;
+  }
+
+  public void setDescription(String description)
+  {
+    this.description = description;
+  }
+
+  public String getFirstPublicDate()
+  {
+    return firstPublicDate;
+  }
+
+  public void setFirstPublicDate(String firstPublicDate)
+  {
+    this.firstPublicDate = firstPublicDate;
+  }
+
+  public String getFirstPublicRelease()
+  {
+    return firstPublicRelease;
+  }
+
+  public void setFirstPublicRelease(String firstPublicRelease)
+  {
+    this.firstPublicRelease = firstPublicRelease;
+  }
+
+  public String getLastUpdatedDate()
+  {
+    return lastUpdatedDate;
+  }
+
+  public void setLastUpdatedDate(String lastUpdatedDate)
+  {
+    this.lastUpdatedDate = lastUpdatedDate;
+  }
+
+  public String getLastUpdatedRelease()
+  {
+    return lastUpdatedRelease;
+  }
+
+  public void setLastUpdatedRelease(String lastUpdatedRelease)
+  {
+    this.lastUpdatedRelease = lastUpdatedRelease;
+  }
+
+  public String getDataClass()
+  {
+    return dataClass;
+  }
+
+  public void setDataClass(String dataClass)
+  {
+    this.dataClass = dataClass;
+  }
 }
diff --git a/src/jalview/datamodel/xdb/embl/EmblError.java b/src/jalview/datamodel/xdb/embl/EmblError.java
index 232d15e..321343e 100644
--- a/src/jalview/datamodel/xdb/embl/EmblError.java
+++ b/src/jalview/datamodel/xdb/embl/EmblError.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/xdb/embl/EmblFeature.java b/src/jalview/datamodel/xdb/embl/EmblFeature.java
index 55d7c94..1784cc4 100644
--- a/src/jalview/datamodel/xdb/embl/EmblFeature.java
+++ b/src/jalview/datamodel/xdb/embl/EmblFeature.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -37,7 +37,7 @@ public class EmblFeature
 
   Vector<Qualifier> qualifiers;
 
-  Vector<EmblFeatureLocations> locations;
+  String location;
 
   /**
    * @return the dbRefs
@@ -57,20 +57,19 @@ public class EmblFeature
   }
 
   /**
-   * @return the locations
+   * @return the location
    */
-  public Vector<EmblFeatureLocations> getLocations()
+  public String getLocation()
   {
-    return locations;
+    return location;
   }
 
   /**
-   * @param locations
-   *          the locations to set
+   * @param loc
    */
-  public void setLocations(Vector<EmblFeatureLocations> locations)
+  public void setLocation(String loc)
   {
-    this.locations = locations;
+    this.location = loc;
   }
 
   /**
diff --git a/src/jalview/datamodel/xdb/embl/EmblFeatureLocElement.java b/src/jalview/datamodel/xdb/embl/EmblFeatureLocElement.java
deleted file mode 100644
index d146484..0000000
--- a/src/jalview/datamodel/xdb/embl/EmblFeatureLocElement.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.embl;
-
-/**
- * Data model for a feature/location/locationElement read from an EMBL query
- * reply
- * 
- * @see embl_mapping.xml
- */
-public class EmblFeatureLocElement
-{
-  String type;
-
-  String accession;
-
-  String version;
-
-  boolean complement;
-
-  BasePosition basePositions[];
-
-  /**
-   * @return the accession
-   */
-  public String getAccession()
-  {
-    return accession;
-  }
-
-  /**
-   * @param accession
-   *          the accession to set
-   */
-  public void setAccession(String accession)
-  {
-    this.accession = accession;
-  }
-
-  /**
-   * @return the basePositions
-   */
-  public BasePosition[] getBasePositions()
-  {
-    return basePositions;
-  }
-
-  /**
-   * @param basePositions
-   *          the basePositions to set
-   */
-  public void setBasePositions(BasePosition[] basePositions)
-  {
-    this.basePositions = basePositions;
-  }
-
-  /**
-   * @return the complement
-   */
-  public boolean isComplement()
-  {
-    return complement;
-  }
-
-  /**
-   * @param complement
-   *          the complement to set
-   */
-  public void setComplement(boolean complement)
-  {
-    this.complement = complement;
-  }
-
-  /**
-   * @return the type
-   */
-  public String getType()
-  {
-    return type;
-  }
-
-  /**
-   * @param type
-   *          the type to set
-   */
-  public void setType(String type)
-  {
-    this.type = type;
-  }
-
-  /**
-   * @return the version
-   */
-  public String getVersion()
-  {
-    return version;
-  }
-
-  /**
-   * @param version
-   *          the version to set
-   */
-  public void setVersion(String version)
-  {
-    this.version = version;
-  }
-}
diff --git a/src/jalview/datamodel/xdb/embl/EmblFeatureLocations.java b/src/jalview/datamodel/xdb/embl/EmblFeatureLocations.java
deleted file mode 100644
index 94105ae..0000000
--- a/src/jalview/datamodel/xdb/embl/EmblFeatureLocations.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.datamodel.xdb.embl;
-
-import java.util.Vector;
-
-/**
- * Data model for a <loctaion> child element of a <feature> read
- * from an EMBL query reply
- * 
- * @see embl_mapping.xml
- */
-public class EmblFeatureLocations
-{
-  Vector<EmblFeatureLocElement> locElements;
-
-  String locationType;
-
-  boolean locationComplement;
-
-  /**
-   * @return the locationComplement
-   */
-  public boolean isLocationComplement()
-  {
-    return locationComplement;
-  }
-
-  /**
-   * @param locationComplement
-   *          the locationComplement to set
-   */
-  public void setLocationComplement(boolean locationComplement)
-  {
-    this.locationComplement = locationComplement;
-  }
-
-  /**
-   * @return the locationType
-   */
-  public String getLocationType()
-  {
-    return locationType;
-  }
-
-  /**
-   * @param locationType
-   *          the locationType to set
-   */
-  public void setLocationType(String locationType)
-  {
-    this.locationType = locationType;
-  }
-
-  /**
-   * @return the locElements
-   */
-  public Vector<EmblFeatureLocElement> getLocElements()
-  {
-    return locElements;
-  }
-
-  /**
-   * @param locElements
-   *          the locElements to set
-   */
-  public void setLocElements(Vector<EmblFeatureLocElement> locElements)
-  {
-    this.locElements = locElements;
-  }
-
-  /**
-   * Return all location elements as start-end pairs (without accessions) TODO:
-   * pass back complement and 'less than or more than' range information Note:
-   * do not use this since it throws away any accessionIds associated with each
-   * location!
-   * 
-   * @return int[] { start1, end1, ... }
-   */
-  public int[] getElementRanges()
-  {
-    return getElementRanges(null);
-  }
-
-  /**
-   * Return all location elements concerning given accession as start-end pairs
-   * TODO: pass back complement and 'less than or more than' range information
-   * TODO: deal with multiple accessions
-   * 
-   * @param accession
-   *          the accession string for which locations are requested, or null
-   *          for all locations
-   * @return null or int[] { start1, end1, ... }
-   */
-
-  public int[] getElementRanges(String accession)
-  {
-    int sepos = 0;
-    int[] se = new int[locElements.size() * 2];
-    if (locationType.equalsIgnoreCase("single")) // TODO: or "simple" ?
-    {
-      for (EmblFeatureLocElement loce : locElements)
-      {
-        if (accession == null || loce.accession != null
-                && accession.equals(loce.accession))
-        {
-          BasePosition bp[] = loce.getBasePositions();
-          if (bp.length == 2)
-          {
-            se[sepos++] = Integer.parseInt(bp[0].getPos());
-            se[sepos++] = Integer.parseInt(bp[1].getPos());
-          }
-        }
-      }
-    }
-    else if (locationType.equalsIgnoreCase("join"))
-    {
-      for (EmblFeatureLocElement loce : locElements)
-      {
-        if (accession == null || loce.accession != null
-                && accession.equals(loce.accession))
-        {
-          BasePosition bp[] = loce.getBasePositions();
-          if (bp.length == 2)
-          {
-            se[sepos++] = Integer.parseInt(bp[0].getPos());
-            se[sepos++] = Integer.parseInt(bp[1].getPos());
-          }
-        }
-      }
-      return se;
-    }
-    else if (locationType != null)
-    {
-      if (jalview.bin.Cache.log != null)
-      {
-        jalview.bin.Cache.log
-                .error("EmbleFeatureLocations.getElementRanges cannot deal with locationType=='"
-                        + locationType + "'");
-      }
-      else
-      {
-        System.err
-                .println("EmbleFeatureLocations.getElementRanges cannot deal with locationType=='"
-                        + locationType + "'");
-      }
-    }
-    // trim range if necessary.
-    if (se != null && sepos != se.length)
-    {
-      int[] trimmed = new int[sepos];
-      System.arraycopy(se, 0, trimmed, 0, sepos);
-      se = trimmed;
-    }
-    return se;
-  }
-}
diff --git a/src/jalview/datamodel/xdb/embl/EmblFile.java b/src/jalview/datamodel/xdb/embl/EmblFile.java
index bea5e07..47ddbc1 100644
--- a/src/jalview/datamodel/xdb/embl/EmblFile.java
+++ b/src/jalview/datamodel/xdb/embl/EmblFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,9 @@
  */
 package jalview.datamodel.xdb.embl;
 
+import jalview.datamodel.DBRefEntry;
+import jalview.ws.dbsources.Uniprot;
+
 import java.io.File;
 import java.io.FileReader;
 import java.io.PrintWriter;
@@ -43,6 +46,8 @@ public class EmblFile
 
   Vector<EmblError> errors;
 
+  String text;
+
   /**
    * @return the entries
    */
@@ -129,6 +134,8 @@ public class EmblFile
       unmar.setMapping(map);
       unmar.setLogWriter(new PrintWriter(System.out));
       record = (EmblFile) unmar.unmarshal(file);
+
+      canonicaliseDbRefs(record);
     } catch (Exception e)
     {
       e.printStackTrace(System.err);
@@ -137,4 +144,59 @@ public class EmblFile
 
     return record;
   }
+
+  /**
+   * Change blank version to "0" in any DBRefEntry, to ensure consistent
+   * comparison with other DBRefEntry in Jalview
+   * 
+   * @param record
+   * @see Uniprot#getDbVersion
+   */
+  static void canonicaliseDbRefs(EmblFile record)
+  {
+    if (record.getEntries() == null)
+    {
+      return;
+    }
+    for (EmblEntry entry : record.getEntries())
+    {
+      if (entry.getDbRefs() != null)
+      {
+        for (DBRefEntry dbref : entry.getDbRefs())
+        {
+          if ("".equals(dbref.getVersion()))
+          {
+            dbref.setVersion("0");
+          }
+        }
+      }
+
+      if (entry.getFeatures() != null)
+      {
+        for (EmblFeature feature : entry.getFeatures())
+        {
+          if (feature.getDbRefs() != null)
+          {
+            for (DBRefEntry dbref : feature.getDbRefs())
+            {
+              if ("".equals(dbref.getVersion()))
+              {
+                dbref.setVersion("0");
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  public String getText()
+  {
+    return text;
+  }
+
+  public void setText(String text)
+  {
+    this.text = text;
+  }
 }
diff --git a/src/jalview/datamodel/xdb/embl/EmblSequence.java b/src/jalview/datamodel/xdb/embl/EmblSequence.java
index 914041d..ef41d09 100644
--- a/src/jalview/datamodel/xdb/embl/EmblSequence.java
+++ b/src/jalview/datamodel/xdb/embl/EmblSequence.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -27,12 +27,8 @@ package jalview.datamodel.xdb.embl;
  */
 public class EmblSequence
 {
-  String version;
-
   String sequence;
 
-  String type;
-
   /**
    * @return the sequence
    */
@@ -47,40 +43,7 @@ public class EmblSequence
    */
   public void setSequence(String sequence)
   {
-    this.sequence = sequence;
-  }
-
-  /**
-   * @return the type
-   */
-  public String getType()
-  {
-    return type;
-  }
-
-  /**
-   * @param type
-   *          the type to set
-   */
-  public void setType(String type)
-  {
-    this.type = type;
-  }
-
-  /**
-   * @return the version
-   */
-  public String getVersion()
-  {
-    return version;
-  }
-
-  /**
-   * @param version
-   *          the version to set
-   */
-  public void setVersion(String version)
-  {
-    this.version = version;
+    // remove spaces introduced by unmarshalling of newline characters
+    this.sequence = sequence.replace(" ", "");
   }
 }
diff --git a/src/jalview/datamodel/xdb/embl/Qualifier.java b/src/jalview/datamodel/xdb/embl/Qualifier.java
index 9020bbe..6450f1a 100644
--- a/src/jalview/datamodel/xdb/embl/Qualifier.java
+++ b/src/jalview/datamodel/xdb/embl/Qualifier.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/exceptions/JalviewException.java b/src/jalview/exceptions/JalviewException.java
index e7a88f1..bd4294d 100644
--- a/src/jalview/exceptions/JalviewException.java
+++ b/src/jalview/exceptions/JalviewException.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/exceptions/NoFileSelectedException.java b/src/jalview/exceptions/NoFileSelectedException.java
index 5d38acf..aff406c 100644
--- a/src/jalview/exceptions/NoFileSelectedException.java
+++ b/src/jalview/exceptions/NoFileSelectedException.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ext/android/ContainerHelpers.java b/src/jalview/ext/android/ContainerHelpers.java
new file mode 100644
index 0000000..4033dcc
--- /dev/null
+++ b/src/jalview/ext/android/ContainerHelpers.java
@@ -0,0 +1,108 @@
+package jalview.ext.android;
+
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copied to Jalview September 2016.
+ * Only the members of this class required for SparseIntArray were copied.
+ * Method binarySearch(short[] array, int size, short value) added to support
+ * SparseShortArray.
+ */
+class ContainerHelpers
+{
+  static final boolean[] EMPTY_BOOLEANS = new boolean[0];
+
+  static final int[] EMPTY_INTS = new int[0];
+
+  static final long[] EMPTY_LONGS = new long[0];
+
+  static final Object[] EMPTY_OBJECTS = new Object[0];
+
+  // This is Arrays.binarySearch(), but doesn't do any argument validation.
+  static int binarySearch(int[] array, int size, int value)
+  {
+    int lo = 0;
+    int hi = size - 1;
+    while (lo <= hi)
+    {
+      final int mid = (lo + hi) >>> 1;
+      final int midVal = array[mid];
+      if (midVal < value)
+      {
+        lo = mid + 1;
+      }
+      else if (midVal > value)
+      {
+        hi = mid - 1;
+      }
+      else
+      {
+        return mid; // value found
+      }
+    }
+    return ~lo; // value not present
+  }
+
+  static int binarySearch(long[] array, int size, long value)
+  {
+    int lo = 0;
+    int hi = size - 1;
+    while (lo <= hi)
+    {
+      final int mid = (lo + hi) >>> 1;
+      final long midVal = array[mid];
+      if (midVal < value)
+      {
+        lo = mid + 1;
+      }
+      else if (midVal > value)
+      {
+        hi = mid - 1;
+      }
+      else
+      {
+        return mid; // value found
+      }
+    }
+    return ~lo; // value not present
+  }
+
+  // This is Arrays.binarySearch(), but doesn't do any argument validation.
+  static int binarySearch(short[] array, int size, short value)
+  {
+    int lo = 0;
+    int hi = size - 1;
+    while (lo <= hi)
+    {
+      final int mid = (lo + hi) >>> 1;
+      final short midVal = array[mid];
+      if (midVal < value)
+      {
+        lo = mid + 1;
+      }
+      else if (midVal > value)
+      {
+        hi = mid - 1;
+      }
+      else
+      {
+        return mid; // value found
+      }
+    }
+    return ~lo; // value not present
+  }
+}
diff --git a/src/jalview/ext/android/SparseIntArray.java b/src/jalview/ext/android/SparseIntArray.java
new file mode 100644
index 0000000..fcd4f1f
--- /dev/null
+++ b/src/jalview/ext/android/SparseIntArray.java
@@ -0,0 +1,432 @@
+package jalview.ext.android;
+
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * SparseIntArrays map integers to integers. Unlike a normal array of integers,
+ * there can be gaps in the indices. It is intended to be more memory efficient
+ * than using a HashMap to map Integers to Integers, both because it avoids
+ * auto-boxing keys and values and its data structure doesn't rely on an extra
+ * entry object for each mapping.
+ *
+ * <p>
+ * Note that this container keeps its mappings in an array data structure, using
+ * a binary search to find keys. The implementation is not intended to be
+ * appropriate for data structures that may contain large numbers of items. It
+ * is generally slower than a traditional HashMap, since lookups require a
+ * binary search and adds and removes require inserting and deleting entries in
+ * the array. For containers holding up to hundreds of items, the performance
+ * difference is not significant, less than 50%.
+ * </p>
+ *
+ * <p>
+ * It is possible to iterate over the items in this container using
+ * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using
+ * <code>keyAt(int)</code> with ascending values of the index will return the
+ * keys in ascending order, or the values corresponding to the keys in ascending
+ * order in the case of <code>valueAt(int)<code>.
+ * </p>
+ */
+
+/*
+ * Imported into Jalview September 2016
+ * Change log:
+ *   Sep 2016 method add(int, int) added for more efficient increment of counts
+ *            (a single binary search, rather than one on read and one on write)
+ */
+public class SparseIntArray implements Cloneable
+{
+  private int[] mKeys;
+
+  private int[] mValues;
+
+  private int mSize;
+
+  /**
+   * Creates a new SparseIntArray containing no mappings.
+   */
+  public SparseIntArray()
+  {
+    this(10);
+  }
+
+  /**
+   * Creates a new SparseIntArray containing no mappings that will not require
+   * any additional memory allocation to store the specified number of mappings.
+   * If you supply an initial capacity of 0, the sparse array will be
+   * initialized with a light-weight representation not requiring any additional
+   * array allocations.
+   */
+  public SparseIntArray(int initialCapacity)
+  {
+    if (initialCapacity == 0)
+    {
+      mKeys = ContainerHelpers.EMPTY_INTS;
+      mValues = ContainerHelpers.EMPTY_INTS;
+    }
+    else
+    {
+      initialCapacity = idealIntArraySize(initialCapacity);
+      mKeys = new int[initialCapacity];
+      mValues = new int[initialCapacity];
+    }
+    mSize = 0;
+  }
+
+  @Override
+  public SparseIntArray clone()
+  {
+    SparseIntArray clone = null;
+    try
+    {
+      clone = (SparseIntArray) super.clone();
+      clone.mKeys = mKeys.clone();
+      clone.mValues = mValues.clone();
+    } catch (CloneNotSupportedException cnse)
+    {
+      /* ignore */
+    }
+    return clone;
+  }
+
+  /**
+   * Gets the int mapped from the specified key, or <code>0</code> if no such
+   * mapping has been made.
+   */
+  public int get(int key)
+  {
+    return get(key, 0);
+  }
+
+  /**
+   * Gets the int mapped from the specified key, or the specified value if no
+   * such mapping has been made.
+   */
+  public int get(int key, int valueIfKeyNotFound)
+  {
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+    if (i < 0)
+    {
+      return valueIfKeyNotFound;
+    }
+    else
+    {
+      return mValues[i];
+    }
+  }
+
+  /**
+   * Removes the mapping from the specified key, if there was any.
+   */
+  public void delete(int key)
+  {
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+    if (i >= 0)
+    {
+      removeAt(i);
+    }
+  }
+
+  /**
+   * Removes the mapping at the given index.
+   */
+  public void removeAt(int index)
+  {
+    System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
+    System.arraycopy(mValues, index + 1, mValues, index, mSize
+            - (index + 1));
+    mSize--;
+  }
+
+  /**
+   * Adds a mapping from the specified key to the specified value, replacing the
+   * previous mapping from the specified key if there was one.
+   */
+  public void put(int key, int value)
+  {
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+    if (i >= 0)
+    {
+      mValues[i] = value;
+    }
+    else
+    {
+      i = ~i;
+      if (mSize >= mKeys.length)
+      {
+        int n = idealIntArraySize(mSize + 1);
+        int[] nkeys = new int[n];
+        int[] nvalues = new int[n];
+        // Log.e("SparseIntArray", "grow " + mKeys.length + " to " + n);
+        System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+        System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+        mKeys = nkeys;
+        mValues = nvalues;
+      }
+      if (mSize - i != 0)
+      {
+        // Log.e("SparseIntArray", "move " + (mSize - i));
+        System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+        System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+      }
+      mKeys[i] = key;
+      mValues[i] = value;
+      mSize++;
+    }
+  }
+
+  /**
+   * Returns the number of key-value mappings that this SparseIntArray currently
+   * stores.
+   */
+  public int size()
+  {
+    return mSize;
+  }
+
+  /**
+   * Given an index in the range <code>0...size()-1</code>, returns the key from
+   * the <code>index</code>th key-value mapping that this SparseIntArray stores.
+   *
+   * <p>
+   * The keys corresponding to indices in ascending order are guaranteed to be
+   * in ascending order, e.g., <code>keyAt(0)</code> will return the smallest
+   * key and <code>keyAt(size()-1)</code> will return the largest key.
+   * </p>
+   */
+  public int keyAt(int index)
+  {
+    return mKeys[index];
+  }
+
+  /**
+   * Given an index in the range <code>0...size()-1</code>, returns the value
+   * from the <code>index</code>th key-value mapping that this SparseIntArray
+   * stores.
+   *
+   * <p>
+   * The values corresponding to indices in ascending order are guaranteed to be
+   * associated with keys in ascending order, e.g., <code>valueAt(0)</code> will
+   * return the value associated with the smallest key and
+   * <code>valueAt(size()-1)</code> will return the value associated with the
+   * largest key.
+   * </p>
+   */
+  public int valueAt(int index)
+  {
+    return mValues[index];
+  }
+
+  /**
+   * Returns the index for which {@link #keyAt} would return the specified key,
+   * or a negative number if the specified key is not mapped.
+   */
+  public int indexOfKey(int key)
+  {
+    return ContainerHelpers.binarySearch(mKeys, mSize, key);
+  }
+
+  /**
+   * Returns an index for which {@link #valueAt} would return the specified key,
+   * or a negative number if no keys map to the specified value. Beware that
+   * this is a linear search, unlike lookups by key, and that multiple keys can
+   * map to the same value and this will find only one of them.
+   */
+  public int indexOfValue(int value)
+  {
+    for (int i = 0; i < mSize; i++)
+    {
+      if (mValues[i] == value)
+      {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * Removes all key-value mappings from this SparseIntArray.
+   */
+  public void clear()
+  {
+    mSize = 0;
+  }
+
+  /**
+   * Puts a key/value pair into the array, optimizing for the case where the key
+   * is greater than all existing keys in the array.
+   */
+  public void append(int key, int value)
+  {
+    if (mSize != 0 && key <= mKeys[mSize - 1])
+    {
+      put(key, value);
+      return;
+    }
+    int pos = mSize;
+    if (pos >= mKeys.length)
+    {
+      int n = idealIntArraySize(pos + 1);
+      int[] nkeys = new int[n];
+      int[] nvalues = new int[n];
+      // Log.e("SparseIntArray", "grow " + mKeys.length + " to " + n);
+      System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+      System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+      mKeys = nkeys;
+      mValues = nvalues;
+    }
+    mKeys[pos] = key;
+    mValues[pos] = value;
+    mSize = pos + 1;
+  }
+
+  /**
+   * Inlined here by copying from com.android.internal.util.ArrayUtils
+   * 
+   * @param i
+   * @return
+   */
+  public static int idealIntArraySize(int need)
+  {
+    return idealByteArraySize(need * 4) / 4;
+  }
+
+  /**
+   * Inlined here by copying from com.android.internal.util.ArrayUtils
+   * 
+   * @param i
+   * @return
+   */
+  public static int idealByteArraySize(int need)
+  {
+    for (int i = 4; i < 32; i++)
+    {
+      if (need <= (1 << i) - 12)
+      {
+        return (1 << i) - 12;
+      }
+    }
+
+    return need;
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * <p>
+   * This implementation composes a string by iterating over its mappings.
+   */
+  @Override
+  public String toString()
+  {
+    if (size() <= 0)
+    {
+      return "{}";
+    }
+    StringBuilder buffer = new StringBuilder(mSize * 28);
+    buffer.append('{');
+    for (int i = 0; i < mSize; i++)
+    {
+      if (i > 0)
+      {
+        buffer.append(", ");
+      }
+      int key = keyAt(i);
+      buffer.append(key);
+      buffer.append('=');
+      int value = valueAt(i);
+      buffer.append(value);
+    }
+    buffer.append('}');
+    return buffer.toString();
+  }
+
+  /**
+   * Method (copied from put) added for Jalview to efficiently increment a key's
+   * value if present, else add it with the given value. This avoids a double
+   * binary search (once to get the value, again to put the updated value).
+   * 
+   * @param key
+   * @oparam toAdd
+   * @return the new value of the count for the key
+   * @throw ArithmeticException if the result would exceed the maximum value of
+   *        an int
+   */
+  public int add(int key, int toAdd)
+  {
+    int newValue = toAdd;
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+    if (i >= 0)
+    {
+      checkOverflow(mValues[i], toAdd);
+      mValues[i] += toAdd;
+      newValue = mValues[i];
+    }
+    else
+    {
+      i = ~i;
+      if (mSize >= mKeys.length)
+      {
+        int n = idealIntArraySize(mSize + 1);
+        int[] nkeys = new int[n];
+        int[] nvalues = new int[n];
+        System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+        System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+        mKeys = nkeys;
+        mValues = nvalues;
+      }
+      if (mSize - i != 0)
+      {
+        System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+        System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+      }
+      mKeys[i] = key;
+      mValues[i] = toAdd;
+      mSize++;
+    }
+    return newValue;
+  }
+
+  /**
+   * Throws ArithmeticException if adding addend to value would exceed the range
+   * of int
+   * 
+   * @param value
+   * @param addend
+   */
+  static void checkOverflow(int value, int addend)
+  {
+    /*
+     * test cases being careful to avoid overflow while testing!
+     */
+    if (addend > 0)
+    {
+      if (value > 0 && Integer.MAX_VALUE - value < addend)
+      {
+        throw new ArithmeticException("Integer overflow adding " + addend
+                + " to  " + value);
+      }
+    }
+    else if (addend < 0)
+    {
+      if (value < 0 && Integer.MIN_VALUE - value > addend)
+      {
+        throw new ArithmeticException("Integer underflow adding " + addend
+                + " to  " + value);
+      }
+    }
+  }
+}
diff --git a/src/jalview/ext/android/SparseShortArray.java b/src/jalview/ext/android/SparseShortArray.java
new file mode 100644
index 0000000..f961f55
--- /dev/null
+++ b/src/jalview/ext/android/SparseShortArray.java
@@ -0,0 +1,442 @@
+package jalview.ext.android;
+
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * SparseShortArrays map shorts to shorts. Unlike a normal array of shorts,
+ * there can be gaps in the indices. It is intended to be more memory efficient
+ * than using a HashMap to map Shorts to Shorts, both because it avoids
+ * auto-boxing keys and values and its data structure doesn't rely on an extra
+ * entry object for each mapping.
+ *
+ * <p>
+ * Note that this container keeps its mappings in an array data structure, using
+ * a binary search to find keys. The implementation is not intended to be
+ * appropriate for data structures that may contain large numbers of items. It
+ * is generally slower than a traditional HashMap, since lookups require a
+ * binary search and adds and removes require inserting and deleting entries in
+ * the array. For containers holding up to hundreds of items, the performance
+ * difference is not significant, less than 50%.
+ * </p>
+ *
+ * <p>
+ * It is possible to iterate over the items in this container using
+ * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using
+ * <code>keyAt(int)</code> with ascending values of the index will return the
+ * keys in ascending order, or the values corresponding to the keys in ascending
+ * order in the case of <code>valueAt(int)<code>.
+ * </p>
+ */
+
+/*
+ * Added to Jalview September 2016. A copy of SparseIntArray designed to store
+ * short values (to minimise space usage).
+ * <p>
+ * Note that operations append, put, add throw ArithmeticException if either the
+ * key or the resulting value overflows the range of a short. Calling code
+ * should trap and handle this, for example by switching to using a
+ * SparseIntArray instead.
+ */
+public class SparseShortArray implements Cloneable
+{
+  private short[] mKeys;
+
+  private short[] mValues;
+
+  private int mSize;
+
+  /**
+   * Creates a new SparseShortArray containing no mappings.
+   */
+  public SparseShortArray()
+  {
+    this(10);
+  }
+
+  /**
+   * Creates a new SparseShortArray containing no mappings that will not require
+   * any additional memory allocation to store the specified number of mappings.
+   * If you supply an initial capacity of 0, the sparse array will be
+   * initialized with a light-weight representation not requiring any additional
+   * array allocations.
+   */
+  public SparseShortArray(int initialCapacity)
+  {
+    if (initialCapacity == 0)
+    {
+      mKeys = new short[0];
+      mValues = new short[0];
+    }
+    else
+    {
+      initialCapacity = idealShortArraySize(initialCapacity);
+      mKeys = new short[initialCapacity];
+      mValues = new short[initialCapacity];
+    }
+    mSize = 0;
+  }
+
+  @Override
+  public SparseShortArray clone()
+  {
+    SparseShortArray clone = null;
+    try
+    {
+      clone = (SparseShortArray) super.clone();
+      clone.mKeys = mKeys.clone();
+      clone.mValues = mValues.clone();
+    } catch (CloneNotSupportedException cnse)
+    {
+      /* ignore */
+    }
+    return clone;
+  }
+
+  /**
+   * Gets the int mapped from the specified key, or <code>0</code> if no such
+   * mapping has been made.
+   */
+  public int get(int key)
+  {
+    return get(key, 0);
+  }
+
+  /**
+   * Gets the int mapped from the specified key, or the specified value if no
+   * such mapping has been made.
+   * 
+   * @throws ArithmeticException
+   *           if key is outside the range of a short value
+   */
+  public int get(int key, int valueIfKeyNotFound)
+  {
+    checkOverflow(key);
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, (short) key);
+    if (i < 0)
+    {
+      return valueIfKeyNotFound;
+    }
+    else
+    {
+      return mValues[i];
+    }
+  }
+
+  /**
+   * Removes the mapping from the specified key, if there was any.
+   * 
+   * @throws ArithmeticException
+   *           if key is outside the range of a short value
+   */
+  public void delete(int key)
+  {
+    checkOverflow(key);
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, (short) key);
+    if (i >= 0)
+    {
+      removeAt(i);
+    }
+  }
+
+  /**
+   * Removes the mapping at the given index.
+   */
+  public void removeAt(int index)
+  {
+    System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
+    System.arraycopy(mValues, index + 1, mValues, index, mSize
+            - (index + 1));
+    mSize--;
+  }
+
+  /**
+   * Adds a mapping from the specified key to the specified value, replacing the
+   * previous mapping from the specified key if there was one.
+   * 
+   * @throws ArithmeticException
+   *           if either argument is outside the range of a short value
+   */
+  public void put(int key, int value)
+  {
+    checkOverflow(key);
+    checkOverflow(value);
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, (short) key);
+    if (i >= 0)
+    {
+      mValues[i] = (short) value;
+    }
+    else
+    {
+      i = ~i;
+      if (mSize >= mKeys.length)
+      {
+        int n = idealShortArraySize(mSize + 1);
+        short[] nkeys = new short[n];
+        short[] nvalues = new short[n];
+        // Log.e("SparseShortArray", "grow " + mKeys.length + " to " + n);
+        System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+        System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+        mKeys = nkeys;
+        mValues = nvalues;
+      }
+      if (mSize - i != 0)
+      {
+        // Log.e("SparseShortArray", "move " + (mSize - i));
+        System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+        System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+      }
+      mKeys[i] = (short) key;
+      mValues[i] = (short) value;
+      mSize++;
+    }
+  }
+
+  /**
+   * Returns the number of key-value mappings that this SparseShortArray
+   * currently stores.
+   */
+  public int size()
+  {
+    return mSize;
+  }
+
+  /**
+   * Given an index in the range <code>0...size()-1</code>, returns the key from
+   * the <code>index</code>th key-value mapping that this SparseShortArray
+   * stores.
+   *
+   * <p>
+   * The keys corresponding to indices in ascending order are guaranteed to be
+   * in ascending order, e.g., <code>keyAt(0)</code> will return the smallest
+   * key and <code>keyAt(size()-1)</code> will return the largest key.
+   * </p>
+   */
+  public short keyAt(int index)
+  {
+    return mKeys[index];
+  }
+
+  /**
+   * Given an index in the range <code>0...size()-1</code>, returns the value
+   * from the <code>index</code>th key-value mapping that this SparseShortArray
+   * stores.
+   *
+   * <p>
+   * The values corresponding to indices in ascending order are guaranteed to be
+   * associated with keys in ascending order, e.g., <code>valueAt(0)</code> will
+   * return the value associated with the smallest key and
+   * <code>valueAt(size()-1)</code> will return the value associated with the
+   * largest key.
+   * </p>
+   */
+  public short valueAt(int index)
+  {
+    return mValues[index];
+  }
+
+  /**
+   * Returns the index for which {@link #keyAt} would return the specified key,
+   * or a negative number if the specified key is not mapped.
+   * 
+   * @throws ArithmeticException
+   *           if key is outside the range of a short value
+   */
+  public int indexOfKey(int key)
+  {
+    checkOverflow(key);
+    return ContainerHelpers.binarySearch(mKeys, mSize, (short) key);
+  }
+
+  /**
+   * Returns an index for which {@link #valueAt} would return the specified key,
+   * or a negative number if no keys map to the specified value. Beware that
+   * this is a linear search, unlike lookups by key, and that multiple keys can
+   * map to the same value and this will find only one of them.
+   */
+  public int indexOfValue(int value)
+  {
+    for (int i = 0; i < mSize; i++)
+    {
+      if (mValues[i] == value)
+      {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * Removes all key-value mappings from this SparseShortArray.
+   */
+  public void clear()
+  {
+    mSize = 0;
+  }
+
+  /**
+   * Puts a key/value pair into the array, optimizing for the case where the key
+   * is greater than all existing keys in the array.
+   */
+  public void append(int key, int value)
+  {
+    if (mSize != 0 && key <= mKeys[mSize - 1])
+    {
+      put(key, value);
+      return;
+    }
+    int pos = mSize;
+    if (pos >= mKeys.length)
+    {
+      int n = idealShortArraySize(pos + 1);
+      short[] nkeys = new short[n];
+      short[] nvalues = new short[n];
+      // Log.e("SparseShortArray", "grow " + mKeys.length + " to " + n);
+      System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+      System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+      mKeys = nkeys;
+      mValues = nvalues;
+    }
+    checkOverflow(key);
+    checkOverflow(value);
+    mKeys[pos] = (short) key;
+    mValues[pos] = (short) value;
+    mSize = pos + 1;
+  }
+
+  /**
+   * Throws an exception if the value is outside the range of a short.
+   * 
+   * @param value
+   * @throws ArithmeticException
+   */
+  public static void checkOverflow(int value)
+  {
+    if (value > Short.MAX_VALUE || value < Short.MIN_VALUE)
+    {
+      throw new ArithmeticException(String.valueOf(value));
+    }
+  }
+
+  /**
+   * Inlined here by copying from com.android.internal.util.ArrayUtils
+   * 
+   * @param i
+   * @return
+   */
+  public static int idealShortArraySize(int need)
+  {
+    return idealByteArraySize(need * 2) / 2;
+  }
+
+  /**
+   * Inlined here by copying from com.android.internal.util.ArrayUtils
+   * 
+   * @param i
+   * @return
+   */
+  public static int idealByteArraySize(int need)
+  {
+    for (int i = 4; i < 32; i++)
+    {
+      if (need <= (1 << i) - 12)
+      {
+        return (1 << i) - 12;
+      }
+    }
+
+    return need;
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * <p>
+   * This implementation composes a string by iterating over its mappings.
+   */
+  @Override
+  public String toString()
+  {
+    if (size() <= 0)
+    {
+      return "{}";
+    }
+    StringBuilder buffer = new StringBuilder(mSize * 28);
+    buffer.append('{');
+    for (int i = 0; i < mSize; i++)
+    {
+      if (i > 0)
+      {
+        buffer.append(", ");
+      }
+      int key = keyAt(i);
+      buffer.append(key);
+      buffer.append('=');
+      int value = valueAt(i);
+      buffer.append(value);
+    }
+    buffer.append('}');
+    return buffer.toString();
+  }
+
+  /**
+   * Method (copied from put) added for Jalview to efficiently increment a key's
+   * value if present, else add it with the given value. This avoids a double
+   * binary search (once to get the value, again to put the updated value).
+   * 
+   * @param key
+   * @oparam toAdd
+   * @return the new value of the count for the key
+   * @throws ArithmeticException
+   *           if key, or result of adding toAdd, is outside the range of a
+   *           short value
+   */
+  public int add(int key, int toAdd)
+  {
+    int newValue = toAdd;
+    checkOverflow(key);
+    int i = ContainerHelpers.binarySearch(mKeys, mSize, (short) key);
+    if (i >= 0)
+    {
+      checkOverflow(toAdd + mValues[i]);
+      mValues[i] += (short) toAdd;
+      newValue = mValues[i];
+    }
+    else
+    {
+      checkOverflow(toAdd);
+      i = ~i;
+      if (mSize >= mKeys.length)
+      {
+        int n = idealShortArraySize(mSize + 1);
+        short[] nkeys = new short[n];
+        short[] nvalues = new short[n];
+        System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+        System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+        mKeys = nkeys;
+        mValues = nvalues;
+      }
+      if (mSize - i != 0)
+      {
+        System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+        System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+      }
+      mKeys[i] = (short) key;
+      mValues[i] = (short) toAdd;
+      mSize++;
+    }
+    return newValue;
+  }
+}
diff --git a/src/jalview/ext/ensembl/EnsemblCdna.java b/src/jalview/ext/ensembl/EnsemblCdna.java
new file mode 100644
index 0000000..cd4539c
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblCdna.java
@@ -0,0 +1,131 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+import jalview.datamodel.SequenceFeature;
+import jalview.io.gff.SequenceOntologyFactory;
+import jalview.io.gff.SequenceOntologyI;
+
+import com.stevesoft.pat.Regex;
+
+/**
+ * A client to fetch CDNA sequence from Ensembl (i.e. that part of the genomic
+ * sequence that is transcribed to RNA, but not necessarily translated to
+ * protein)
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class EnsemblCdna extends EnsemblSeqProxy
+{
+  /*
+   * accepts ENST or ENSTG with 11 digits
+   * or ENSMUST or similar for other species
+   * or CCDSnnnnn.nn with at least 3 digits
+   */
+  private static final Regex ACCESSION_REGEX = new Regex(
+          "(ENS([A-Z]{3}|)[TG][0-9]{11}$)" + "|" + "(CCDS[0-9.]{3,}$)");
+
+  /*
+   * fetch exon features on genomic sequence (to identify the cdna regions)
+   * and cds and variation features (to retain)
+   */
+  private static final EnsemblFeatureType[] FEATURES_TO_FETCH = {
+      EnsemblFeatureType.exon, EnsemblFeatureType.cds,
+      EnsemblFeatureType.variation };
+
+  /**
+   * Default constructor (to use rest.ensembl.org)
+   */
+  public EnsemblCdna()
+  {
+    super();
+  }
+
+  /**
+   * Constructor given the target domain to fetch data from
+   * 
+   * @param d
+   */
+  public EnsemblCdna(String d)
+  {
+    super(d);
+  }
+
+  @Override
+  public String getDbName()
+  {
+    return "ENSEMBL (CDNA)";
+  }
+
+  @Override
+  protected EnsemblSeqType getSourceEnsemblType()
+  {
+    return EnsemblSeqType.CDNA;
+  }
+
+  @Override
+  public Regex getAccessionValidator()
+  {
+    return ACCESSION_REGEX;
+  }
+
+  @Override
+  protected EnsemblFeatureType[] getFeaturesToFetch()
+  {
+    return FEATURES_TO_FETCH;
+  }
+
+  /**
+   * Answers true unless the feature type is 'transcript' (or a sub-type in the
+   * Sequence Ontology).
+   */
+  @Override
+  protected boolean retainFeature(SequenceFeature sf, String accessionId)
+  {
+    if (isTranscript(sf.getType()))
+    {
+      return false;
+    }
+    return featureMayBelong(sf, accessionId);
+  }
+
+  /**
+   * Answers true if the sequence feature type is 'exon' (or a subtype of exon
+   * in the Sequence Ontology), and the Parent of the feature is the transcript
+   * we are retrieving
+   */
+  @Override
+  protected boolean identifiesSequence(SequenceFeature sf, String accId)
+  {
+    if (SequenceOntologyFactory.getInstance().isA(sf.getType(),
+            SequenceOntologyI.EXON))
+    {
+      String parentFeature = (String) sf.getValue(PARENT);
+      if (("transcript:" + accId).equals(parentFeature))
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+
+}
diff --git a/src/jalview/ext/ensembl/EnsemblCds.java b/src/jalview/ext/ensembl/EnsemblCds.java
new file mode 100644
index 0000000..f797a24
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblCds.java
@@ -0,0 +1,138 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.io.gff.SequenceOntologyFactory;
+import jalview.io.gff.SequenceOntologyI;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A client for direct fetching of CDS sequences from Ensembl (i.e. that part of
+ * the genomic sequence that is translated to protein)
+ * 
+ * TODO: not currently used as CDS sequences are computed from CDS features on
+ * transcripts - delete this class?
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class EnsemblCds extends EnsemblSeqProxy
+{
+  /*
+   * fetch cds features on genomic sequence (to identify the CDS regions)
+   * and exon and variation features (to retain for display)
+   */
+  private static final EnsemblFeatureType[] FEATURES_TO_FETCH = {
+      EnsemblFeatureType.cds, EnsemblFeatureType.exon,
+      EnsemblFeatureType.variation };
+
+  /**
+   * Default constructor (to use rest.ensembl.org)
+   */
+  public EnsemblCds()
+  {
+    super();
+  }
+
+  /**
+   * Constructor given the target domain to fetch data from
+   * 
+   * @param d
+   */
+  public EnsemblCds(String d)
+  {
+    super(d);
+  }
+
+  @Override
+  public String getDbName()
+  {
+    return "ENSEMBL (CDS)";
+  }
+
+  @Override
+  protected EnsemblSeqType getSourceEnsemblType()
+  {
+    return EnsemblSeqType.CDS;
+  }
+
+  @Override
+  protected EnsemblFeatureType[] getFeaturesToFetch()
+  {
+    return FEATURES_TO_FETCH;
+  }
+
+  /**
+   * Answers true unless the feature type is 'CDS' (or a sub-type of CDS in the
+   * Sequence Ontology). CDS features are only retrieved in order to identify
+   * the cds sequence range, and are redundant information on the cds sequence
+   * itself.
+   */
+  @Override
+  protected boolean retainFeature(SequenceFeature sf, String accessionId)
+  {
+    if (SequenceOntologyFactory.getInstance().isA(sf.getType(),
+            SequenceOntologyI.CDS))
+    {
+      return false;
+    }
+    return featureMayBelong(sf, accessionId);
+  }
+
+  /**
+   * Answers true if the sequence feature type is 'CDS' (or a subtype of CDS in
+   * the Sequence Ontology), and the Parent of the feature is the transcript we
+   * are retrieving
+   */
+  @Override
+  protected boolean identifiesSequence(SequenceFeature sf, String accId)
+  {
+    if (SequenceOntologyFactory.getInstance().isA(sf.getType(),
+            SequenceOntologyI.CDS))
+    {
+      String parentFeature = (String) sf.getValue(PARENT);
+      if (("transcript:" + accId).equals(parentFeature))
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Overrides this method to trivially return a range which is the whole of the
+   * nucleotide sequence. This is both faster than scanning for CDS features,
+   * and also means we don't need to keep CDS features on CDS sequence (where
+   * they are redundant information).
+   */
+  protected List<int[]> getCdsRanges(SequenceI dnaSeq)
+  {
+    int len = dnaSeq.getLength();
+    List<int[]> ranges = new ArrayList<int[]>();
+    ranges.add(new int[] { 1, len });
+    return ranges;
+  }
+
+}
diff --git a/src/jalview/ext/ensembl/EnsemblFeatures.java b/src/jalview/ext/ensembl/EnsemblFeatures.java
new file mode 100644
index 0000000..064d9d4
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblFeatures.java
@@ -0,0 +1,160 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.io.FeaturesFile;
+import jalview.io.FileParse;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A client for fetching and processing Ensembl feature data in GFF format by
+ * calling the overlap REST service
+ * 
+ * @author gmcarstairs
+ * @see http://rest.ensembl.org/documentation/info/overlap_id
+ */
+class EnsemblFeatures extends EnsemblRestClient
+{
+  /*
+   * The default features to retrieve from Ensembl
+   * can override in getSequenceRecords parameter
+   */
+  private EnsemblFeatureType[] featuresWanted = { EnsemblFeatureType.cds,
+      EnsemblFeatureType.exon, EnsemblFeatureType.variation };
+
+  /**
+   * Default constructor (to use rest.ensembl.org)
+   */
+  public EnsemblFeatures()
+  {
+    super();
+  }
+
+  /**
+   * Constructor given the target domain to fetch data from
+   * 
+   * @param d
+   */
+  public EnsemblFeatures(String d)
+  {
+    super(d);
+  }
+
+  @Override
+  public String getDbName()
+  {
+    return "ENSEMBL (features)";
+  }
+
+  /**
+   * Makes a query to the REST overlap endpoint for the given sequence
+   * identifier. This returns an 'alignment' consisting of one 'dummy sequence'
+   * (the genomic sequence for which overlap features are returned by the
+   * service). This sequence will have on it sequence features which are the
+   * real information of interest, such as CDS regions or sequence variations.
+   */
+  @Override
+  public AlignmentI getSequenceRecords(String query) throws IOException
+  {
+    // TODO: use a vararg String... for getSequenceRecords instead?
+    List<String> queries = new ArrayList<String>();
+    queries.add(query);
+    FileParse fp = getSequenceReader(queries);
+    FeaturesFile fr = new FeaturesFile(fp);
+    return new Alignment(fr.getSeqsAsArray());
+  }
+
+  /**
+   * Returns a URL for the REST overlap endpoint
+   * 
+   * @param ids
+   * @return
+   */
+  @Override
+  protected URL getUrl(List<String> ids) throws MalformedURLException
+  {
+    StringBuffer urlstring = new StringBuffer(128);
+    urlstring.append(getDomain()).append("/overlap/id/").append(ids.get(0));
+
+    // @see https://github.com/Ensembl/ensembl-rest/wiki/Output-formats
+    urlstring.append("?content-type=text/x-gff3");
+
+    /*
+     * specify  features to retrieve
+     * @see http://rest.ensembl.org/documentation/info/overlap_id
+     * could make the list a configurable entry in jalview.properties
+     */
+    for (EnsemblFeatureType feature : featuresWanted)
+    {
+      urlstring.append("&feature=").append(feature.name());
+    }
+
+    return new URL(urlstring.toString());
+  }
+
+  @Override
+  protected boolean useGetRequest()
+  {
+    return true;
+  }
+
+  /**
+   * Returns the MIME type for GFF3. For GET requests the Content-type header
+   * describes the required encoding of the response.
+   */
+  @Override
+  protected String getRequestMimeType(boolean multipleIds)
+  {
+    return "text/x-gff3";
+  }
+
+  /**
+   * Returns the MIME type for GFF3.
+   */
+  @Override
+  protected String getResponseMimeType()
+  {
+    return "text/x-gff3";
+  }
+
+  /**
+   * Overloaded method that allows a list of features to retrieve to be
+   * specified
+   * 
+   * @param accId
+   * @param features
+   * @return
+   * @throws IOException
+   */
+  protected AlignmentI getSequenceRecords(String accId,
+          EnsemblFeatureType[] features) throws IOException
+  {
+    featuresWanted = features;
+    return getSequenceRecords(accId);
+  }
+}
diff --git a/src/jalview/ext/ensembl/EnsemblGene.java b/src/jalview/ext/ensembl/EnsemblGene.java
new file mode 100644
index 0000000..cf2a3cd
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblGene.java
@@ -0,0 +1,615 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+import jalview.api.FeatureColourI;
+import jalview.api.FeatureSettingsModelI;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.io.gff.SequenceOntologyFactory;
+import jalview.io.gff.SequenceOntologyI;
+import jalview.schemes.FeatureColour;
+import jalview.schemes.FeatureSettingsAdapter;
+import jalview.util.MapList;
+
+import java.awt.Color;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.stevesoft.pat.Regex;
+
+/**
+ * A class that fetches genomic sequence and all transcripts for an Ensembl gene
+ * 
+ * @author gmcarstairs
+ */
+public class EnsemblGene extends EnsemblSeqProxy
+{
+  private static final String GENE_PREFIX = "gene:";
+
+  /*
+   * accepts anything as we will attempt lookup of gene or 
+   * transcript id or gene name
+   */
+  private static final Regex ACCESSION_REGEX = new Regex(".*");
+
+  private static final EnsemblFeatureType[] FEATURES_TO_FETCH = {
+      EnsemblFeatureType.gene, EnsemblFeatureType.transcript,
+      EnsemblFeatureType.exon, EnsemblFeatureType.cds,
+      EnsemblFeatureType.variation };
+
+  /**
+   * Default constructor (to use rest.ensembl.org)
+   */
+  public EnsemblGene()
+  {
+    super();
+  }
+
+  /**
+   * Constructor given the target domain to fetch data from
+   * 
+   * @param d
+   */
+  public EnsemblGene(String d)
+  {
+    super(d);
+  }
+
+  @Override
+  public String getDbName()
+  {
+    return "ENSEMBL";
+  }
+
+  @Override
+  protected EnsemblFeatureType[] getFeaturesToFetch()
+  {
+    return FEATURES_TO_FETCH;
+  }
+
+  @Override
+  protected EnsemblSeqType getSourceEnsemblType()
+  {
+    return EnsemblSeqType.GENOMIC;
+  }
+
+  /**
+   * Returns an alignment containing the gene(s) for the given gene or
+   * transcript identifier, or external identifier (e.g. Uniprot id). If given a
+   * gene name or external identifier, returns any related gene sequences found
+   * for model organisms. If only a single gene is queried for, then its
+   * transcripts are also retrieved and added to the alignment. <br>
+   * Method:
+   * <ul>
+   * <li>resolves a transcript identifier by looking up its parent gene id</li>
+   * <li>resolves an external identifier by looking up xref-ed gene ids</li>
+   * <li>fetches the gene sequence</li>
+   * <li>fetches features on the sequence</li>
+   * <li>identifies "transcript" features whose Parent is the requested gene</li>
+   * <li>fetches the transcript sequence for each transcript</li>
+   * <li>makes a mapping from the gene to each transcript</li>
+   * <li>copies features from gene to transcript sequences</li>
+   * <li>fetches the protein sequence for each transcript, maps and saves it as
+   * a cross-reference</li>
+   * <li>aligns each transcript against the gene sequence based on the position
+   * mappings</li>
+   * </ul>
+   * 
+   * @param query
+   *          a single gene or transcript identifier or gene name
+   * @return an alignment containing a gene, and possibly transcripts, or null
+   */
+  @Override
+  public AlignmentI getSequenceRecords(String query) throws Exception
+  {
+    /*
+     * convert to a non-duplicated list of gene identifiers
+     */
+    List<String> geneIds = getGeneIds(query);
+
+    AlignmentI al = null;
+    for (String geneId : geneIds)
+    {
+      /*
+       * fetch the gene sequence(s) with features and xrefs
+       */
+      AlignmentI geneAlignment = super.getSequenceRecords(geneId);
+      if (geneAlignment == null)
+      {
+        continue;
+      }
+      if (geneAlignment.getHeight() == 1)
+      {
+        getTranscripts(geneAlignment, geneId);
+      }
+      if (al == null)
+      {
+        al = geneAlignment;
+      }
+      else
+      {
+        al.append(geneAlignment);
+      }
+    }
+    return al;
+  }
+
+  /**
+   * Converts a query, which may contain one or more gene or transcript
+   * identifiers, into a non-redundant list of gene identifiers.
+   * 
+   * @param accessions
+   * @return
+   */
+  List<String> getGeneIds(String accessions)
+  {
+    List<String> geneIds = new ArrayList<String>();
+
+    for (String acc : accessions.split(getAccessionSeparator()))
+    {
+      if (isGeneIdentifier(acc))
+      {
+        if (!geneIds.contains(acc))
+        {
+          geneIds.add(acc);
+        }
+      }
+
+      /*
+       * if given a transcript id, look up its gene parent
+       */
+      else if (isTranscriptIdentifier(acc))
+      {
+        String geneId = new EnsemblLookup(getDomain()).getParent(acc);
+        if (geneId != null && !geneIds.contains(geneId))
+        {
+          geneIds.add(geneId);
+        }
+      }
+
+      /*
+       * if given a gene or other external name, lookup and fetch 
+       * the corresponding gene for all model organisms 
+       */
+      else
+      {
+        List<String> ids = new EnsemblSymbol(getDomain(), getDbSource(),
+                getDbVersion()).getIds(acc);
+        for (String geneId : ids)
+        {
+          if (!geneIds.contains(geneId))
+          {
+            geneIds.add(geneId);
+          }
+        }
+      }
+    }
+    return geneIds;
+  }
+
+  /**
+   * Attempts to get Ensembl stable identifiers for model organisms for a gene
+   * name by calling the xrefs symbol REST service to resolve the gene name.
+   * 
+   * @param query
+   * @return
+   */
+  protected String getGeneIdentifiersForName(String query)
+  {
+    List<String> ids = new EnsemblSymbol(getDomain(), getDbSource(),
+            getDbVersion()).getIds(query);
+    if (ids != null)
+    {
+      for (String id : ids)
+      {
+        if (isGeneIdentifier(id))
+        {
+          return id;
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Constructs all transcripts for the gene, as identified by "transcript"
+   * features whose Parent is the requested gene. The coding transcript
+   * sequences (i.e. with introns omitted) are added to the alignment.
+   * 
+   * @param al
+   * @param accId
+   * @throws Exception
+   */
+  protected void getTranscripts(AlignmentI al, String accId)
+          throws Exception
+  {
+    SequenceI gene = al.getSequenceAt(0);
+    List<SequenceFeature> transcriptFeatures = getTranscriptFeatures(accId,
+            gene);
+
+    for (SequenceFeature transcriptFeature : transcriptFeatures)
+    {
+      makeTranscript(transcriptFeature, al, gene);
+    }
+
+    clearGeneFeatures(gene);
+  }
+
+  /**
+   * Remove unwanted features (transcript, exon, CDS) from the gene sequence
+   * after we have used them to derive transcripts and transfer features
+   * 
+   * @param gene
+   */
+  protected void clearGeneFeatures(SequenceI gene)
+  {
+    SequenceFeature[] sfs = gene.getSequenceFeatures();
+    if (sfs != null)
+    {
+      SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+      List<SequenceFeature> filtered = new ArrayList<SequenceFeature>();
+      for (SequenceFeature sf : sfs)
+      {
+        String type = sf.getType();
+        if (!isTranscript(type) && !so.isA(type, SequenceOntologyI.EXON)
+                && !so.isA(type, SequenceOntologyI.CDS))
+        {
+          filtered.add(sf);
+        }
+      }
+      gene.setSequenceFeatures(filtered
+              .toArray(new SequenceFeature[filtered.size()]));
+    }
+  }
+
+  /**
+   * Constructs a spliced transcript sequence by finding 'exon' features for the
+   * given id (or failing that 'CDS'). Copies features on to the new sequence.
+   * 'Aligns' the new sequence against the gene sequence by padding with gaps,
+   * and adds it to the alignment.
+   * 
+   * @param transcriptFeature
+   * @param al
+   *          the alignment to which to add the new sequence
+   * @param gene
+   *          the parent gene sequence, with features
+   * @return
+   */
+  SequenceI makeTranscript(SequenceFeature transcriptFeature,
+          AlignmentI al, SequenceI gene)
+  {
+    String accId = getTranscriptId(transcriptFeature);
+    if (accId == null)
+    {
+      return null;
+    }
+
+    /*
+     * NB we are mapping from gene sequence (not genome), so do not
+     * need to check for reverse strand (gene and transcript sequences 
+     * are in forward sense)
+     */
+
+    /*
+     * make a gene-length sequence filled with gaps
+     * we will fill in the bases for transcript regions
+     */
+    char[] seqChars = new char[gene.getLength()];
+    Arrays.fill(seqChars, al.getGapCharacter());
+
+    /*
+     * look for exon features of the transcript, failing that for CDS
+     * (for example ENSG00000124610 has 1 CDS but no exon features)
+     */
+    String parentId = "transcript:" + accId;
+    List<SequenceFeature> splices = findFeatures(gene,
+            SequenceOntologyI.EXON, parentId);
+    if (splices.isEmpty())
+    {
+      splices = findFeatures(gene, SequenceOntologyI.CDS, parentId);
+    }
+
+    int transcriptLength = 0;
+    final char[] geneChars = gene.getSequence();
+    int offset = gene.getStart(); // to convert to 0-based positions
+    List<int[]> mappedFrom = new ArrayList<int[]>();
+
+    for (SequenceFeature sf : splices)
+    {
+      int start = sf.getBegin() - offset;
+      int end = sf.getEnd() - offset;
+      int spliceLength = end - start + 1;
+      System.arraycopy(geneChars, start, seqChars, start, spliceLength);
+      transcriptLength += spliceLength;
+      mappedFrom.add(new int[] { sf.getBegin(), sf.getEnd() });
+    }
+
+    Sequence transcript = new Sequence(accId, seqChars, 1, transcriptLength);
+
+    /*
+     * Ensembl has gene name as transcript Name
+     * EnsemblGenomes doesn't, but has a url-encoded description field
+     */
+    String description = (String) transcriptFeature.getValue(NAME);
+    if (description == null)
+    {
+      description = (String) transcriptFeature.getValue(DESCRIPTION);
+    }
+    if (description != null)
+    {
+      try
+      {
+        transcript.setDescription(URLDecoder.decode(description, "UTF-8"));
+      } catch (UnsupportedEncodingException e)
+      {
+        e.printStackTrace(); // as if
+      }
+    }
+    transcript.createDatasetSequence();
+
+    al.addSequence(transcript);
+
+    /*
+     * transfer features to the new sequence; we use EnsemblCdna to do this,
+     * to filter out unwanted features types (see method retainFeature)
+     */
+    List<int[]> mapTo = new ArrayList<int[]>();
+    mapTo.add(new int[] { 1, transcriptLength });
+    MapList mapping = new MapList(mappedFrom, mapTo, 1, 1);
+    EnsemblCdna cdna = new EnsemblCdna(getDomain());
+    cdna.transferFeatures(gene.getSequenceFeatures(),
+            transcript.getDatasetSequence(), mapping, parentId);
+
+    /*
+     * fetch and save cross-references
+     */
+    cdna.getCrossReferences(transcript);
+
+    /*
+     * and finally fetch the protein product and save as a cross-reference
+     */
+    cdna.addProteinProduct(transcript);
+
+    return transcript;
+  }
+
+  /**
+   * Returns the 'transcript_id' property of the sequence feature (or null)
+   * 
+   * @param feature
+   * @return
+   */
+  protected String getTranscriptId(SequenceFeature feature)
+  {
+    return (String) feature.getValue("transcript_id");
+  }
+
+  /**
+   * Returns a list of the transcript features on the sequence whose Parent is
+   * the gene for the accession id.
+   * 
+   * @param accId
+   * @param geneSequence
+   * @return
+   */
+  protected List<SequenceFeature> getTranscriptFeatures(String accId,
+          SequenceI geneSequence)
+  {
+    List<SequenceFeature> transcriptFeatures = new ArrayList<SequenceFeature>();
+
+    String parentIdentifier = GENE_PREFIX + accId;
+    SequenceFeature[] sfs = geneSequence.getSequenceFeatures();
+
+    if (sfs != null)
+    {
+      for (SequenceFeature sf : sfs)
+      {
+        if (isTranscript(sf.getType()))
+        {
+          String parent = (String) sf.getValue(PARENT);
+          if (parentIdentifier.equals(parent))
+          {
+            transcriptFeatures.add(sf);
+          }
+        }
+      }
+    }
+
+    return transcriptFeatures;
+  }
+
+  @Override
+  public String getDescription()
+  {
+    return "Fetches all transcripts and variant features for a gene or transcript";
+  }
+
+  /**
+   * Default test query is a gene id (can also enter a transcript id)
+   */
+  @Override
+  public String getTestQuery()
+  {
+    return "ENSG00000157764"; // BRAF, 5 transcripts, reverse strand
+    // ENSG00000090266 // NDUFB2, 15 transcripts, forward strand
+    // ENSG00000101812 // H2BFM histone, 3 transcripts, forward strand
+    // ENSG00000123569 // H2BFWT histone, 2 transcripts, reverse strand
+  }
+
+  /**
+   * Answers true for a feature of type 'gene' (or a sub-type of gene in the
+   * Sequence Ontology), whose ID is the accession we are retrieving
+   */
+  @Override
+  protected boolean identifiesSequence(SequenceFeature sf, String accId)
+  {
+    if (SequenceOntologyFactory.getInstance().isA(sf.getType(),
+            SequenceOntologyI.GENE))
+    {
+      String id = (String) sf.getValue(ID);
+      if ((GENE_PREFIX + accId).equals(id))
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Answers true unless feature type is 'gene', or 'transcript' with a parent
+   * which is a different gene. We need the gene features to identify the range,
+   * but it is redundant information on the gene sequence. Checking the parent
+   * allows us to drop transcript features which belong to different
+   * (overlapping) genes.
+   */
+  @Override
+  protected boolean retainFeature(SequenceFeature sf, String accessionId)
+  {
+    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    String type = sf.getType();
+    if (so.isA(type, SequenceOntologyI.GENE))
+    {
+      return false;
+    }
+    if (isTranscript(type))
+    {
+      String parent = (String) sf.getValue(PARENT);
+      if (!(GENE_PREFIX + accessionId).equals(parent))
+      {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Answers false. This allows an optimisation - a single 'gene' feature is all
+   * that is needed to identify the positions of the gene on the genomic
+   * sequence.
+   */
+  @Override
+  protected boolean isSpliceable()
+  {
+    return false;
+  }
+
+  /**
+   * Override to do nothing as Ensembl doesn't return a protein sequence for a
+   * gene identifier
+   */
+  @Override
+  protected void addProteinProduct(SequenceI querySeq)
+  {
+  }
+
+  @Override
+  public Regex getAccessionValidator()
+  {
+    return ACCESSION_REGEX;
+  }
+
+  /**
+   * Returns a descriptor for suitable feature display settings with
+   * <ul>
+   * <li>only exon or sequence_variant features (or their subtypes in the
+   * Sequence Ontology) visible</li>
+   * <li>variant features coloured red</li>
+   * <li>exon features coloured by label (exon name)</li>
+   * <li>variants displayed above (on top of) exons</li>
+   * </ul>
+   */
+  @Override
+  public FeatureSettingsModelI getFeatureColourScheme()
+  {
+    return new FeatureSettingsAdapter()
+    {
+      SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+
+      @Override
+      public boolean isFeatureDisplayed(String type)
+      {
+        return (so.isA(type, SequenceOntologyI.EXON) || so.isA(type,
+                SequenceOntologyI.SEQUENCE_VARIANT));
+      }
+
+      @Override
+      public FeatureColourI getFeatureColour(String type)
+      {
+        if (so.isA(type, SequenceOntologyI.EXON))
+        {
+          return new FeatureColour()
+          {
+            @Override
+            public boolean isColourByLabel()
+            {
+              return true;
+            }
+          };
+        }
+        if (so.isA(type, SequenceOntologyI.SEQUENCE_VARIANT))
+        {
+          return new FeatureColour()
+          {
+
+            @Override
+            public Color getColour()
+            {
+              return Color.RED;
+            }
+          };
+        }
+        return null;
+      }
+
+      /**
+       * order to render sequence_variant after exon after the rest
+       */
+      @Override
+      public int compare(String feature1, String feature2)
+      {
+        if (so.isA(feature1, SequenceOntologyI.SEQUENCE_VARIANT))
+        {
+          return +1;
+        }
+        if (so.isA(feature2, SequenceOntologyI.SEQUENCE_VARIANT))
+        {
+          return -1;
+        }
+        if (so.isA(feature1, SequenceOntologyI.EXON))
+        {
+          return +1;
+        }
+        if (so.isA(feature2, SequenceOntologyI.EXON))
+        {
+          return -1;
+        }
+        return 0;
+      }
+    };
+  }
+
+}
diff --git a/src/jalview/ext/ensembl/EnsemblGenome.java b/src/jalview/ext/ensembl/EnsemblGenome.java
new file mode 100644
index 0000000..dab39d3
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblGenome.java
@@ -0,0 +1,115 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+import jalview.datamodel.SequenceFeature;
+
+/**
+ * A client to fetch genomic sequence from Ensembl
+ * 
+ * TODO: not currently used - delete?
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class EnsemblGenome extends EnsemblSeqProxy
+{
+  /*
+   * fetch transcript features on genomic sequence (to identify the transcript 
+   * regions) and cds, exon and variation features (to retain)
+   */
+  private static final EnsemblFeatureType[] FEATURES_TO_FETCH = {
+      EnsemblFeatureType.transcript, EnsemblFeatureType.exon,
+      EnsemblFeatureType.cds, EnsemblFeatureType.variation };
+
+  /**
+   * Default constructor (to use rest.ensembl.org)
+   */
+  public EnsemblGenome()
+  {
+    super();
+  }
+
+  /**
+   * Constructor given the target domain to fetch data from
+   * 
+   * @param d
+   */
+  public EnsemblGenome(String d)
+  {
+    super(d);
+  }
+
+  @Override
+  public String getDbName()
+  {
+    return "ENSEMBL (Genomic)";
+  }
+
+  @Override
+  protected EnsemblSeqType getSourceEnsemblType()
+  {
+    return EnsemblSeqType.GENOMIC;
+  }
+
+  @Override
+  protected EnsemblFeatureType[] getFeaturesToFetch()
+  {
+    return FEATURES_TO_FETCH;
+  }
+
+  /**
+   * Answers true unless the feature type is 'transcript' (or a sub-type of
+   * transcript in the Sequence Ontology), or has a parent other than the given
+   * accession id. Transcript features are only retrieved in order to identify
+   * the transcript sequence range, and are redundant information on the
+   * transcript sequence itself.
+   */
+  @Override
+  protected boolean retainFeature(SequenceFeature sf, String accessionId)
+  {
+    if (isTranscript(sf.getType()))
+    {
+      return false;
+    }
+    return featureMayBelong(sf, accessionId);
+  }
+
+  /**
+   * Answers true if the sequence feature type is 'transcript' (or a subtype of
+   * transcript in the Sequence Ontology), and the ID of the feature is the
+   * transcript we are retrieving
+   */
+  @Override
+  protected boolean identifiesSequence(SequenceFeature sf, String accId)
+  {
+    if (isTranscript(sf.getType()))
+    {
+      String id = (String) sf.getValue(ID);
+      if (("transcript:" + accId).equals(id))
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+
+}
diff --git a/src/jalview/util/ReverseListIterator.java b/src/jalview/ext/ensembl/EnsemblGenomes.java
similarity index 53%
copy from src/jalview/util/ReverseListIterator.java
copy to src/jalview/ext/ensembl/EnsemblGenomes.java
index 1145b66..0223826 100644
--- a/src/jalview/util/ReverseListIterator.java
+++ b/src/jalview/ext/ensembl/EnsemblGenomes.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,46 +18,48 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.util;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
+package jalview.ext.ensembl;
 
 /**
- * An iterator that traverses a list backwards.
+ * A class to behave much like EnsemblGene but referencing the ensemblgenomes
+ * domain and data
  * 
- * @author gmcarstairs (and checked against
- *         org.codehaus.groovey.runtime.ReverseListIterator)
+ * @author gmcarstairs
  *
- * @param <E>
  */
-public class ReverseListIterator<E> implements Iterator<E>
+public class EnsemblGenomes extends EnsemblGene
 {
+  /**
+   * Constructor sets domain to rest.ensemblgenomes.org instead of the 'usual'
+   * rest.ensembl.org
+   */
+  public EnsemblGenomes()
+  {
+    super(ENSEMBL_GENOMES_REST);
+  }
 
-  private ListIterator<E> iterator;
-
-  public ReverseListIterator(List<E> stuff)
+  @Override
+  public boolean isGeneIdentifier(String query)
   {
-    this.iterator = stuff.listIterator(stuff.size());
+    return true;
   }
 
   @Override
-  public boolean hasNext()
+  public String getDbName()
   {
-    return iterator.hasPrevious();
+    return "EnsemblGenomes";
   }
 
   @Override
-  public E next()
+  public String getTestQuery()
   {
-    return iterator.previous();
+    return "DDB_G0283883";
   }
 
   @Override
-  public void remove()
+  public String getDbSource()
   {
-    iterator.remove();
+    return "EnsemblGenomes";
   }
 
 }
diff --git a/src/jalview/ext/ensembl/EnsemblInfo.java b/src/jalview/ext/ensembl/EnsemblInfo.java
new file mode 100644
index 0000000..036b5cc
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblInfo.java
@@ -0,0 +1,91 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+/**
+ * A data class to model the data and rest version of one Ensembl domain,
+ * currently for rest.ensembl.org and rest.ensemblgenomes.org
+ * 
+ * @author gmcarstairs
+ */
+class EnsemblInfo
+{
+  /*
+   * The http domain this object is holding data values for
+   */
+  String domain;
+
+  /*
+   * The latest version Jalview has tested for, e.g. "4.5"; a minor version change should be
+   * ok, a major version change may break stuff 
+   */
+  String expectedRestVersion;
+
+  /*
+   * Major / minor / point version e.g. "4.5.1"
+   * @see http://rest.ensembl.org/info/rest/?content-type=application/json
+   */
+  String restVersion;
+
+  /*
+   * data version
+   * @see http://rest.ensembl.org/info/data/?content-type=application/json
+   */
+  String dataVersion;
+
+  /*
+   * true when http://rest.ensembl.org/info/ping/?content-type=application/json
+   * returns response code 200 and not {"error":"Database is unavailable"}
+   */
+  boolean restAvailable;
+
+  /*
+   * absolute time when availability was last checked
+   */
+  long lastAvailableCheckTime;
+
+  /*
+   * absolute time when version numbers were last checked
+   */
+  long lastVersionCheckTime;
+
+  // flag set to true if REST major version is not the one expected
+  boolean restMajorVersionMismatch;
+
+  /*
+   * absolute time to wait till if we overloaded the REST service
+   */
+  long retryAfter;
+
+  /**
+   * Constructor given expected REST version number e.g 4.5 or 3.4.3
+   * 
+   * @param restExpected
+   */
+  EnsemblInfo(String theDomain, String restExpected)
+  {
+    domain = theDomain;
+    expectedRestVersion = restExpected;
+    lastAvailableCheckTime = -1;
+    lastVersionCheckTime = -1;
+  }
+
+}
diff --git a/src/jalview/ext/ensembl/EnsemblLookup.java b/src/jalview/ext/ensembl/EnsemblLookup.java
new file mode 100644
index 0000000..5b1385b
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblLookup.java
@@ -0,0 +1,180 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+import jalview.datamodel.AlignmentI;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
+
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+/**
+ * A client for the Ensembl lookup REST endpoint; used to find the Parent gene
+ * identifier given a transcript identifier.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class EnsemblLookup extends EnsemblRestClient
+{
+
+  /**
+   * Default constructor (to use rest.ensembl.org)
+   */
+  public EnsemblLookup()
+  {
+    super();
+  }
+
+  /**
+   * Constructor given the target domain to fetch data from
+   * 
+   * @param
+   */
+  public EnsemblLookup(String d)
+  {
+    super(d);
+  }
+
+  @Override
+  public String getDbName()
+  {
+    return "ENSEMBL";
+  }
+
+  @Override
+  public AlignmentI getSequenceRecords(String queries) throws Exception
+  {
+    return null;
+  }
+
+  @Override
+  protected URL getUrl(List<String> ids) throws MalformedURLException
+  {
+    String identifier = ids.get(0);
+    return getUrl(identifier);
+  }
+
+  /**
+   * @param identifier
+   * @return
+   */
+  protected URL getUrl(String identifier)
+  {
+    String url = getDomain() + "/lookup/id/" + identifier
+            + "?content-type=application/json";
+    try
+    {
+      return new URL(url);
+    } catch (MalformedURLException e)
+    {
+      return null;
+    }
+  }
+
+  @Override
+  protected boolean useGetRequest()
+  {
+    return true;
+  }
+
+  @Override
+  protected String getRequestMimeType(boolean multipleIds)
+  {
+    return "application/json";
+  }
+
+  @Override
+  protected String getResponseMimeType()
+  {
+    return "application/json";
+  }
+
+  /**
+   * Calls the Ensembl lookup REST endpoint and retrieves the 'Parent' for the
+   * given identifier, or null if not found
+   * 
+   * @param identifier
+   * @return
+   */
+  public String getParent(String identifier)
+  {
+    List<String> ids = Arrays.asList(new String[] { identifier });
+
+    BufferedReader br = null;
+    try
+    {
+      URL url = getUrl(identifier);
+      if (url != null)
+      {
+        br = getHttpResponse(url, ids);
+      }
+      return (parseResponse(br));
+    } catch (IOException e)
+    {
+      // ignore
+      return null;
+    } finally
+    {
+      if (br != null)
+      {
+        try
+        {
+          br.close();
+        } catch (IOException e)
+        {
+          // ignore
+        }
+      }
+    }
+  }
+
+  /**
+   * Parses "Parent" from the JSON response and returns the value, or null if
+   * not found
+   * 
+   * @param br
+   * @return
+   * @throws IOException
+   */
+  protected String parseResponse(BufferedReader br) throws IOException
+  {
+    String parent = null;
+    JSONParser jp = new JSONParser();
+    try
+    {
+      JSONObject val = (JSONObject) jp.parse(br);
+      parent = val.get("Parent").toString();
+    } catch (ParseException e)
+    {
+      // ignore
+    }
+    return parent;
+  }
+
+}
diff --git a/src/jalview/ext/ensembl/EnsemblProtein.java b/src/jalview/ext/ensembl/EnsemblProtein.java
new file mode 100644
index 0000000..11f5e00
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblProtein.java
@@ -0,0 +1,147 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceFeature;
+
+import java.util.List;
+
+import com.stevesoft.pat.Regex;
+
+/**
+ * A client to fetch protein translated sequence for an Ensembl identifier
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class EnsemblProtein extends EnsemblSeqProxy
+{
+  /*
+   * accepts ENSP with 11 digits
+   * or ENSMUSP or similar for other species
+   * or CCDSnnnnn.nn with at least 3 digits
+   */
+  private static final Regex ACCESSION_REGEX = new Regex(
+          "(ENS([A-Z]{3}|)P[0-9]{11}$)" + "|" + "(CCDS[0-9.]{3,}$)");
+
+  /**
+   * Default constructor (to use rest.ensembl.org)
+   */
+  public EnsemblProtein()
+  {
+    super();
+  }
+
+  /**
+   * Constructor given the target domain to fetch data from
+   * 
+   * @param d
+   */
+  public EnsemblProtein(String d)
+  {
+    super(d);
+  }
+
+  @Override
+  public String getDbName()
+  {
+    return "ENSEMBL (Protein)";
+  }
+
+  @Override
+  protected EnsemblSeqType getSourceEnsemblType()
+  {
+    return EnsemblSeqType.PROTEIN;
+  }
+
+  /**
+   * Returns false, as this fetcher does not retrieve DNA sequences.
+   */
+  @Override
+  public boolean isDnaCoding()
+  {
+    return false;
+  }
+
+  /**
+   * Test query is to the protein translation of transcript ENST00000288602
+   */
+  @Override
+  public String getTestQuery()
+  {
+    return "ENSP00000288602";
+  }
+
+  /**
+   * Overrides base class method to do nothing - genomic features are not
+   * applicable to the protein product sequence
+   */
+  @Override
+  protected void addFeaturesAndProduct(String accId, AlignmentI alignment)
+  {
+  }
+
+  @Override
+  protected EnsemblFeatureType[] getFeaturesToFetch()
+  {
+    // not applicable - can't fetch genomic features for a protein sequence
+    return null;
+  }
+
+  @Override
+  protected boolean identifiesSequence(SequenceFeature sf, String accId)
+  {
+    // not applicable - protein sequence is not a 'subset' of genomic sequence
+    return false;
+  }
+
+  @Override
+  public Regex getAccessionValidator()
+  {
+    return ACCESSION_REGEX;
+  }
+
+  /**
+   * Returns an accession id for a query, including conversion of ENST* to
+   * ENSP*. This supports querying for the protein sequence for a transcript
+   * (ENST identifier) and returning the ENSP identifier.
+   */
+  @Override
+  public String getAccessionIdFromQuery(String query)
+  {
+    String accId = super.getAccessionIdFromQuery(query);
+
+    /*
+     * ensure last character before (11) digits is P
+     * ENST00000288602 -> ENSP00000288602
+     * ENSMUST00000288602 -> ENSMUSP00000288602
+     */
+    if (accId != null && accId.length() >= 12)
+    {
+      char[] chars = accId.toCharArray();
+      chars[chars.length - 12] = 'P';
+      accId = new String(chars);
+    }
+    return accId;
+  }
+
+}
diff --git a/src/jalview/ext/ensembl/EnsemblRestClient.java b/src/jalview/ext/ensembl/EnsemblRestClient.java
new file mode 100644
index 0000000..d197b4a
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblRestClient.java
@@ -0,0 +1,567 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+import jalview.io.FileParse;
+import jalview.util.StringUtils;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.HttpMethod;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+
+import com.stevesoft.pat.Regex;
+
+/**
+ * Base class for Ensembl REST service clients
+ * 
+ * @author gmcarstairs
+ */
+abstract class EnsemblRestClient extends EnsemblSequenceFetcher
+{
+  private static final int DEFAULT_READ_TIMEOUT = 5 * 60 * 1000; // 5 minutes
+
+  private static final int CONNECT_TIMEOUT_MS = 10 * 1000; // 10 seconds
+
+  /*
+   * update these constants when Jalview has been checked / updated for
+   * changes to Ensembl REST API
+   * @see https://github.com/Ensembl/ensembl-rest/wiki/Change-log
+   * @see http://rest.ensembl.org/info/rest?content-type=application/json
+   */
+  private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "4.6";
+
+  private static final String LATEST_ENSEMBL_REST_VERSION = "4.7";
+
+  private static final String REST_CHANGE_LOG = "https://github.com/Ensembl/ensembl-rest/wiki/Change-log";
+
+  private static Map<String, EnsemblInfo> domainData;
+
+  // @see https://github.com/Ensembl/ensembl-rest/wiki/Output-formats
+  private static final String PING_URL = "http://rest.ensembl.org/info/ping.json";
+
+  private final static long AVAILABILITY_RETEST_INTERVAL = 10000L; // 10 seconds
+
+  private final static long VERSION_RETEST_INTERVAL = 1000L * 3600; // 1 hr
+
+  private static final Regex TRANSCRIPT_REGEX = new Regex(
+          "(ENS)([A-Z]{3}|)T[0-9]{11}$");
+
+  private static final Regex GENE_REGEX = new Regex(
+          "(ENS)([A-Z]{3}|)G[0-9]{11}$");
+
+  static
+  {
+    domainData = new HashMap<String, EnsemblInfo>();
+    domainData.put(ENSEMBL_REST, new EnsemblInfo(ENSEMBL_REST,
+            LATEST_ENSEMBL_REST_VERSION));
+    domainData.put(ENSEMBL_GENOMES_REST, new EnsemblInfo(
+            ENSEMBL_GENOMES_REST, LATEST_ENSEMBLGENOMES_REST_VERSION));
+  }
+
+  protected volatile boolean inProgress = false;
+
+  /**
+   * Default constructor to use rest.ensembl.org
+   */
+  public EnsemblRestClient()
+  {
+    this(ENSEMBL_REST);
+  }
+
+  /**
+   * Constructor given the target domain to fetch data from
+   * 
+   * @param d
+   */
+  public EnsemblRestClient(String d)
+  {
+    setDomain(d);
+  }
+
+  /**
+   * Answers true if the query matches the regular expression pattern for an
+   * Ensembl transcript stable identifier
+   * 
+   * @param query
+   * @return
+   */
+  public boolean isTranscriptIdentifier(String query)
+  {
+    return query == null ? false : TRANSCRIPT_REGEX.search(query);
+  }
+
+  /**
+   * Answers true if the query matches the regular expression pattern for an
+   * Ensembl gene stable identifier
+   * 
+   * @param query
+   * @return
+   */
+  public boolean isGeneIdentifier(String query)
+  {
+    return query == null ? false : GENE_REGEX.search(query);
+  }
+
+  @Override
+  public boolean queryInProgress()
+  {
+    return inProgress;
+  }
+
+  @Override
+  public StringBuffer getRawRecords()
+  {
+    return null;
+  }
+
+  /**
+   * Returns the URL for the client http request
+   * 
+   * @param ids
+   * @return
+   * @throws MalformedURLException
+   */
+  protected abstract URL getUrl(List<String> ids)
+          throws MalformedURLException;
+
+  /**
+   * Returns true if client uses GET method, false if it uses POST
+   * 
+   * @return
+   */
+  protected abstract boolean useGetRequest();
+
+  /**
+   * Return the desired value for the Content-Type request header
+   * 
+   * @param multipleIds
+   * 
+   * @return
+   * @see https://github.com/Ensembl/ensembl-rest/wiki/HTTP-Headers
+   */
+  protected abstract String getRequestMimeType(boolean multipleIds);
+
+  /**
+   * Return the desired value for the Accept request header
+   * 
+   * @return
+   * @see https://github.com/Ensembl/ensembl-rest/wiki/HTTP-Headers
+   */
+  protected abstract String getResponseMimeType();
+
+  /**
+   * Checks Ensembl's REST 'ping' endpoint, and returns true if response
+   * indicates available, else false
+   * 
+   * @see http://rest.ensembl.org/documentation/info/ping
+   * @return
+   */
+  private boolean checkEnsembl()
+  {
+    BufferedReader br = null;
+    try
+    {
+      // note this format works for both ensembl and ensemblgenomes
+      // info/ping.json works for ensembl only (March 2016)
+      URL ping = new URL(getDomain()
+              + "/info/ping?content-type=application/json");
+
+      /*
+       * expect {"ping":1} if ok
+       * if ping takes more than 2 seconds to respond, treat as if unavailable
+       */
+      br = getHttpResponse(ping, null, 2 * 1000);
+      JSONParser jp = new JSONParser();
+      JSONObject val = (JSONObject) jp.parse(br);
+      String pingString = val.get("ping").toString();
+      return pingString != null;
+    } catch (Throwable t)
+    {
+      System.err.println("Error connecting to " + PING_URL + ": "
+              + t.getMessage());
+    } finally
+    {
+      if (br != null)
+      {
+        try
+        {
+          br.close();
+        } catch (IOException e)
+        {
+          // ignore
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * returns a reader to a Fasta response from the Ensembl sequence endpoint
+   * 
+   * @param ids
+   * @return
+   * @throws IOException
+   */
+  protected FileParse getSequenceReader(List<String> ids)
+          throws IOException
+  {
+    URL url = getUrl(ids);
+
+    BufferedReader reader = getHttpResponse(url, ids);
+    if (reader == null)
+    {
+      // request failed
+      return null;
+    }
+    FileParse fp = new FileParse(reader, url.toString(), "HTTP_POST");
+    return fp;
+  }
+
+  /**
+   * Gets a reader to the HTTP response, using the default read timeout of 5
+   * minutes
+   * 
+   * @param url
+   * @param ids
+   * @return
+   * @throws IOException
+   */
+  protected BufferedReader getHttpResponse(URL url, List<String> ids)
+          throws IOException
+  {
+    return getHttpResponse(url, ids, DEFAULT_READ_TIMEOUT);
+  }
+
+  /**
+   * Writes the HTTP request and gets the response as a reader.
+   * 
+   * @param url
+   * @param ids
+   *          written as Json POST body if more than one
+   * @param readTimeout
+   *          in milliseconds
+   * @return
+   * @throws IOException
+   *           if response code was not 200, or other I/O error
+   */
+  protected BufferedReader getHttpResponse(URL url, List<String> ids,
+          int readTimeout) throws IOException
+  {
+    // long now = System.currentTimeMillis();
+    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+    /*
+     * POST method allows multiple queries in one request; it is supported for
+     * sequence queries, but not for overlap
+     */
+    boolean multipleIds = ids != null && ids.size() > 1;
+    connection.setRequestMethod(multipleIds ? HttpMethod.POST
+            : HttpMethod.GET);
+    connection.setRequestProperty("Content-Type",
+            getRequestMimeType(multipleIds));
+    connection.setRequestProperty("Accept", getResponseMimeType());
+
+    connection.setUseCaches(false);
+    connection.setDoInput(true);
+    connection.setDoOutput(multipleIds);
+
+    connection.setConnectTimeout(CONNECT_TIMEOUT_MS);
+    connection.setReadTimeout(readTimeout);
+
+    if (multipleIds)
+    {
+      writePostBody(connection, ids);
+    }
+
+    int responseCode = connection.getResponseCode();
+
+    if (responseCode != 200)
+    {
+      /*
+       * note: a GET request for an invalid id returns an error code e.g. 415
+       * but POST request returns 200 and an empty Fasta response 
+       */
+      System.err.println("Response code " + responseCode + " for " + url);
+      return null;
+    }
+    // get content
+    InputStream response = connection.getInputStream();
+
+    // System.out.println(getClass().getName() + " took "
+    // + (System.currentTimeMillis() - now) + "ms to fetch");
+
+    checkRateLimits(connection);
+
+    BufferedReader reader = null;
+    reader = new BufferedReader(new InputStreamReader(response, "UTF-8"));
+    return reader;
+  }
+
+  /**
+   * Inspect response headers for any sign of server overload and respect any
+   * 'retry-after' directive
+   * 
+   * @see https://github.com/Ensembl/ensembl-rest/wiki/Rate-Limits
+   * @param connection
+   */
+  void checkRateLimits(HttpURLConnection connection)
+  {
+    // number of requests allowed per time interval:
+    String limit = connection.getHeaderField("X-RateLimit-Limit");
+    // length of quota time interval in seconds:
+    // String period = connection.getHeaderField("X-RateLimit-Period");
+    // seconds remaining until usage quota is reset:
+    String reset = connection.getHeaderField("X-RateLimit-Reset");
+    // number of requests remaining from quota for current period:
+    String remaining = connection.getHeaderField("X-RateLimit-Remaining");
+    // number of seconds to wait before retrying (if remaining == 0)
+    String retryDelay = connection.getHeaderField("Retry-After");
+
+    // to test:
+    // retryDelay = "5";
+
+    EnsemblInfo info = domainData.get(getDomain());
+    if (retryDelay != null)
+    {
+      System.err.println("Ensembl REST service rate limit exceeded, wait "
+              + retryDelay + " seconds before retrying");
+      try
+      {
+        info.retryAfter = System.currentTimeMillis()
+                + (1000 * Integer.valueOf(retryDelay));
+      } catch (NumberFormatException e)
+      {
+        System.err.println("Unexpected value for Retry-After: "
+                + retryDelay);
+      }
+    }
+    else
+    {
+      info.retryAfter = 0;
+      // debug:
+      // System.out.println(String.format(
+      // "%s Ensembl requests remaining of %s (reset in %ss)",
+      // remaining, limit, reset));
+    }
+  }
+
+  /**
+   * Rechecks if Ensembl is responding, unless the last check was successful and
+   * the retest interval has not yet elapsed. Returns true if Ensembl is up,
+   * else false. Also retrieves and saves the current version of Ensembl data
+   * and REST services at intervals.
+   * 
+   * @return
+   */
+  protected boolean isEnsemblAvailable()
+  {
+    EnsemblInfo info = domainData.get(getDomain());
+
+    long now = System.currentTimeMillis();
+
+    /*
+     * check if we are waiting for 'Retry-After' to expire
+     */
+    if (info.retryAfter > now)
+    {
+      System.err.println("Still " + (1 + (info.retryAfter - now) / 1000)
+              + " secs to wait before retrying Ensembl");
+      return false;
+    }
+    else
+    {
+      info.retryAfter = 0;
+    }
+
+    /*
+     * recheck if Ensembl is up if it was down, or the recheck period has elapsed
+     */
+    boolean retestAvailability = (now - info.lastAvailableCheckTime) > AVAILABILITY_RETEST_INTERVAL;
+    if (!info.restAvailable || retestAvailability)
+    {
+      info.restAvailable = checkEnsembl();
+      info.lastAvailableCheckTime = now;
+    }
+
+    /*
+     * refetch Ensembl versions if the recheck period has elapsed
+     */
+    boolean refetchVersion = (now - info.lastVersionCheckTime) > VERSION_RETEST_INTERVAL;
+    if (refetchVersion)
+    {
+      checkEnsemblRestVersion();
+      checkEnsemblDataVersion();
+      info.lastVersionCheckTime = now;
+    }
+
+    return info.restAvailable;
+  }
+
+  /**
+   * Constructs, writes and flushes the POST body of the request, containing the
+   * query ids in JSON format
+   * 
+   * @param connection
+   * @param ids
+   * @throws IOException
+   */
+  protected void writePostBody(HttpURLConnection connection,
+          List<String> ids) throws IOException
+  {
+    boolean first;
+    StringBuilder postBody = new StringBuilder(64);
+    postBody.append("{\"ids\":[");
+    first = true;
+    for (String id : ids)
+    {
+      if (!first)
+      {
+        postBody.append(",");
+      }
+      first = false;
+      postBody.append("\"");
+      postBody.append(id.trim());
+      postBody.append("\"");
+    }
+    postBody.append("]}");
+    byte[] thepostbody = postBody.toString().getBytes();
+    connection.setRequestProperty("Content-Length",
+            Integer.toString(thepostbody.length));
+    DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
+    wr.write(thepostbody);
+    wr.flush();
+    wr.close();
+  }
+
+  /**
+   * Fetches and checks Ensembl's REST version number
+   * 
+   * @return
+   */
+  private void checkEnsemblRestVersion()
+  {
+    EnsemblInfo info = domainData.get(getDomain());
+
+    JSONParser jp = new JSONParser();
+    URL url = null;
+    try
+    {
+      url = new URL(getDomain()
+              + "/info/rest?content-type=application/json");
+      BufferedReader br = getHttpResponse(url, null);
+      JSONObject val = (JSONObject) jp.parse(br);
+      String version = val.get("release").toString();
+      String majorVersion = version.substring(0, version.indexOf("."));
+      String expected = info.expectedRestVersion;
+      String expectedMajorVersion = expected.substring(0,
+              expected.indexOf("."));
+      info.restMajorVersionMismatch = false;
+      try
+      {
+        /*
+         * if actual REST major version is ahead of what we expect,
+         * record this in case we want to warn the user
+         */
+        if (Float.valueOf(majorVersion) > Float
+                .valueOf(expectedMajorVersion))
+        {
+          info.restMajorVersionMismatch = true;
+        }
+      } catch (NumberFormatException e)
+      {
+        System.err.println("Error in REST version: " + e.toString());
+      }
+
+      /*
+       * check if REST version is later than what Jalview has tested against,
+       * if so warn; we don't worry if it is earlier (this indicates Jalview has
+       * been tested in advance against the next pending REST version)
+       */
+      boolean laterVersion = StringUtils.compareVersions(version, expected) == 1;
+      if (laterVersion)
+      {
+        System.err.println(String.format(
+                "Expected %s REST version %s but found %s, see %s",
+                getDbSource(), expected, version, REST_CHANGE_LOG));
+      }
+      info.restVersion = version;
+    } catch (Throwable t)
+    {
+      System.err.println("Error checking Ensembl REST version: "
+              + t.getMessage());
+    }
+  }
+
+  public boolean isRestMajorVersionMismatch()
+  {
+    return domainData.get(getDomain()).restMajorVersionMismatch;
+  }
+
+  /**
+   * Fetches and checks Ensembl's data version number
+   * 
+   * @return
+   */
+  private void checkEnsemblDataVersion()
+  {
+    JSONParser jp = new JSONParser();
+    URL url = null;
+    try
+    {
+      url = new URL(getDomain()
+              + "/info/data?content-type=application/json");
+      BufferedReader br = getHttpResponse(url, null);
+      JSONObject val = (JSONObject) jp.parse(br);
+      JSONArray versions = (JSONArray) val.get("releases");
+      domainData.get(getDomain()).dataVersion = versions.get(0).toString();
+    } catch (Throwable t)
+    {
+      System.err.println("Error checking Ensembl data version: "
+              + t.getMessage());
+    }
+  }
+
+  public String getEnsemblDataVersion()
+  {
+    return domainData.get(getDomain()).dataVersion;
+  }
+
+  @Override
+  public String getDbVersion()
+  {
+    return getEnsemblDataVersion();
+  }
+
+}
diff --git a/src/jalview/ext/ensembl/EnsemblSeqProxy.java b/src/jalview/ext/ensembl/EnsemblSeqProxy.java
new file mode 100644
index 0000000..5b95387
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblSeqProxy.java
@@ -0,0 +1,955 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+import jalview.analysis.AlignmentUtils;
+import jalview.analysis.Dna;
+import jalview.bin.Cache;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.exceptions.JalviewException;
+import jalview.io.FastaFile;
+import jalview.io.FileParse;
+import jalview.io.gff.SequenceOntologyFactory;
+import jalview.io.gff.SequenceOntologyI;
+import jalview.util.Comparison;
+import jalview.util.DBRefUtils;
+import jalview.util.MapList;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Base class for Ensembl sequence fetchers
+ * 
+ * @see http://rest.ensembl.org/documentation/info/sequence_id
+ * @author gmcarstairs
+ */
+public abstract class EnsemblSeqProxy extends EnsemblRestClient
+{
+  private static final String ALLELES = "alleles";
+
+  protected static final String PARENT = "Parent";
+
+  protected static final String ID = "ID";
+
+  protected static final String NAME = "Name";
+
+  protected static final String DESCRIPTION = "description";
+
+  /*
+   * enum for 'type' parameter to the /sequence REST service
+   */
+  public enum EnsemblSeqType
+  {
+    /**
+     * type=genomic to fetch full dna including introns
+     */
+    GENOMIC("genomic"),
+
+    /**
+     * type=cdna to fetch coding dna including UTRs
+     */
+    CDNA("cdna"),
+
+    /**
+     * type=cds to fetch coding dna excluding UTRs
+     */
+    CDS("cds"),
+
+    /**
+     * type=protein to fetch peptide product sequence
+     */
+    PROTEIN("protein");
+
+    /*
+     * the value of the 'type' parameter to fetch this version of 
+     * an Ensembl sequence
+     */
+    private String type;
+
+    EnsemblSeqType(String t)
+    {
+      type = t;
+    }
+
+    public String getType()
+    {
+      return type;
+    }
+
+  }
+
+  /**
+   * A comparator to sort ranges into ascending start position order
+   */
+  private class RangeSorter implements Comparator<int[]>
+  {
+    boolean forwards;
+
+    RangeSorter(boolean forward)
+    {
+      forwards = forward;
+    }
+
+    @Override
+    public int compare(int[] o1, int[] o2)
+    {
+      return (forwards ? 1 : -1) * Integer.compare(o1[0], o2[0]);
+    }
+
+  }
+
+  /**
+   * Default constructor (to use rest.ensembl.org)
+   */
+  public EnsemblSeqProxy()
+  {
+    super();
+  }
+
+  /**
+   * Constructor given the target domain to fetch data from
+   */
+  public EnsemblSeqProxy(String d)
+  {
+    super(d);
+  }
+
+  /**
+   * Makes the sequence queries to Ensembl's REST service and returns an
+   * alignment consisting of the returned sequences.
+   */
+  @Override
+  public AlignmentI getSequenceRecords(String query) throws Exception
+  {
+    // TODO use a String... query vararg instead?
+
+    // danger: accession separator used as a regex here, a string elsewhere
+    // in this case it is ok (it is just a space), but (e.g.) '\' would not be
+    List<String> allIds = Arrays.asList(query
+            .split(getAccessionSeparator()));
+    AlignmentI alignment = null;
+    inProgress = true;
+
+    /*
+     * execute queries, if necessary in batches of the
+     * maximum allowed number of ids
+     */
+    int maxQueryCount = getMaximumQueryCount();
+    for (int v = 0, vSize = allIds.size(); v < vSize; v += maxQueryCount)
+    {
+      int p = Math.min(vSize, v + maxQueryCount);
+      List<String> ids = allIds.subList(v, p);
+      try
+      {
+        alignment = fetchSequences(ids, alignment);
+      } catch (Throwable r)
+      {
+        inProgress = false;
+        String msg = "Aborting ID retrieval after " + v
+                + " chunks. Unexpected problem (" + r.getLocalizedMessage()
+                + ")";
+        System.err.println(msg);
+        r.printStackTrace();
+        break;
+      }
+    }
+
+    if (alignment == null)
+    {
+      return null;
+    }
+
+    /*
+     * fetch and transfer genomic sequence features,
+     * fetch protein product and add as cross-reference
+     */
+    for (String accId : allIds)
+    {
+      addFeaturesAndProduct(accId, alignment);
+    }
+
+    for (SequenceI seq : alignment.getSequences())
+    {
+      getCrossReferences(seq);
+    }
+
+    return alignment;
+  }
+
+  /**
+   * Fetches Ensembl features using the /overlap REST endpoint, and adds them to
+   * the sequence in the alignment. Also fetches the protein product, maps it
+   * from the CDS features of the sequence, and saves it as a cross-reference of
+   * the dna sequence.
+   * 
+   * @param accId
+   * @param alignment
+   */
+  protected void addFeaturesAndProduct(String accId, AlignmentI alignment)
+  {
+    if (alignment == null)
+    {
+      return;
+    }
+
+    try
+    {
+      /*
+       * get 'dummy' genomic sequence with exon, cds and variation features
+       */
+      SequenceI genomicSequence = null;
+      EnsemblFeatures gffFetcher = new EnsemblFeatures(getDomain());
+      EnsemblFeatureType[] features = getFeaturesToFetch();
+      AlignmentI geneFeatures = gffFetcher.getSequenceRecords(accId,
+              features);
+      if (geneFeatures.getHeight() > 0)
+      {
+        genomicSequence = geneFeatures.getSequenceAt(0);
+      }
+      if (genomicSequence != null)
+      {
+        /*
+         * transfer features to the query sequence
+         */
+        SequenceI querySeq = alignment.findName(accId);
+        if (transferFeatures(accId, genomicSequence, querySeq))
+        {
+
+          /*
+           * fetch and map protein product, and add it as a cross-reference
+           * of the retrieved sequence
+           */
+          addProteinProduct(querySeq);
+        }
+      }
+    } catch (IOException e)
+    {
+      System.err.println("Error transferring Ensembl features: "
+              + e.getMessage());
+    }
+  }
+
+  /**
+   * Returns those sequence feature types to fetch from Ensembl. We may want
+   * features either because they are of interest to the user, or as means to
+   * identify the locations of the sequence on the genomic sequence (CDS
+   * features identify CDS, exon features identify cDNA etc).
+   * 
+   * @return
+   */
+  protected abstract EnsemblFeatureType[] getFeaturesToFetch();
+
+  /**
+   * Fetches and maps the protein product, and adds it as a cross-reference of
+   * the retrieved sequence
+   */
+  protected void addProteinProduct(SequenceI querySeq)
+  {
+    String accId = querySeq.getName();
+    try
+    {
+      AlignmentI protein = new EnsemblProtein(getDomain())
+              .getSequenceRecords(accId);
+      if (protein == null || protein.getHeight() == 0)
+      {
+        System.out.println("No protein product found for " + accId);
+        return;
+      }
+      SequenceI proteinSeq = protein.getSequenceAt(0);
+
+      /*
+       * need dataset sequences (to be the subject of mappings)
+       */
+      proteinSeq.createDatasetSequence();
+      querySeq.createDatasetSequence();
+
+      MapList mapList = AlignmentUtils
+              .mapCdsToProtein(querySeq, proteinSeq);
+      if (mapList != null)
+      {
+        // clunky: ensure Uniprot xref if we have one is on mapped sequence
+        SequenceI ds = proteinSeq.getDatasetSequence();
+        // TODO: Verify ensp primary ref is on proteinSeq.getDatasetSequence()
+        Mapping map = new Mapping(ds, mapList);
+        DBRefEntry dbr = new DBRefEntry(getDbSource(),
+                getEnsemblDataVersion(), proteinSeq.getName(), map);
+        querySeq.getDatasetSequence().addDBRef(dbr);
+        DBRefEntry[] uprots = DBRefUtils.selectRefs(ds.getDBRefs(),
+                new String[] { DBRefSource.UNIPROT });
+        DBRefEntry[] upxrefs = DBRefUtils.selectRefs(querySeq.getDBRefs(),
+                new String[] { DBRefSource.UNIPROT });
+        if (uprots != null)
+        {
+          for (DBRefEntry up : uprots)
+          {
+            // locate local uniprot ref and map
+            List<DBRefEntry> upx = DBRefUtils.searchRefs(upxrefs,
+                    up.getAccessionId());
+            DBRefEntry upxref;
+            if (upx.size() != 0)
+            {
+              upxref = upx.get(0);
+
+              if (upx.size() > 1)
+              {
+                Cache.log
+                        .warn("Implementation issue - multiple uniprot acc on product sequence.");
+              }
+            }
+            else
+            {
+              upxref = new DBRefEntry(DBRefSource.UNIPROT,
+                      getEnsemblDataVersion(), up.getAccessionId());
+            }
+
+            Mapping newMap = new Mapping(ds, mapList);
+            upxref.setVersion(getEnsemblDataVersion());
+            upxref.setMap(newMap);
+            if (upx.size() == 0)
+            {
+              // add the new uniprot ref
+              querySeq.getDatasetSequence().addDBRef(upxref);
+            }
+
+          }
+        }
+
+        /*
+         * copy exon features to protein, compute peptide variants from dna 
+         * variants and add as features on the protein sequence ta-da
+         */
+        AlignmentUtils
+                .computeProteinFeatures(querySeq, proteinSeq, mapList);
+      }
+    } catch (Exception e)
+    {
+      System.err
+              .println(String.format("Error retrieving protein for %s: %s",
+                      accId, e.getMessage()));
+    }
+  }
+
+  /**
+   * Get database xrefs from Ensembl, and attach them to the sequence
+   * 
+   * @param seq
+   */
+  protected void getCrossReferences(SequenceI seq)
+  {
+    while (seq.getDatasetSequence() != null)
+    {
+      seq = seq.getDatasetSequence();
+    }
+
+    EnsemblXref xrefFetcher = new EnsemblXref(getDomain(), getDbSource(),
+            getEnsemblDataVersion());
+    List<DBRefEntry> xrefs = xrefFetcher.getCrossReferences(seq.getName());
+    for (DBRefEntry xref : xrefs)
+    {
+      seq.addDBRef(xref);
+    }
+
+    /*
+     * and add a reference to itself
+     */
+    DBRefEntry self = new DBRefEntry(getDbSource(),
+            getEnsemblDataVersion(), seq.getName());
+    seq.addDBRef(self);
+  }
+
+  /**
+   * Fetches sequences for the list of accession ids and adds them to the
+   * alignment. Returns the extended (or created) alignment.
+   * 
+   * @param ids
+   * @param alignment
+   * @return
+   * @throws JalviewException
+   * @throws IOException
+   */
+  protected AlignmentI fetchSequences(List<String> ids, AlignmentI alignment)
+          throws JalviewException, IOException
+  {
+    if (!isEnsemblAvailable())
+    {
+      inProgress = false;
+      throw new JalviewException("ENSEMBL Rest API not available.");
+    }
+    FileParse fp = getSequenceReader(ids);
+    if (fp == null)
+    {
+      return alignment;
+    }
+
+    FastaFile fr = new FastaFile(fp);
+    if (fr.hasWarningMessage())
+    {
+      System.out.println(String.format(
+              "Warning when retrieving %d ids %s\n%s", ids.size(),
+              ids.toString(), fr.getWarningMessage()));
+    }
+    else if (fr.getSeqs().size() != ids.size())
+    {
+      System.out.println(String.format(
+              "Only retrieved %d sequences for %d query strings", fr
+                      .getSeqs().size(), ids.size()));
+    }
+
+    if (fr.getSeqs().size() == 1 && fr.getSeqs().get(0).getLength() == 0)
+    {
+      /*
+       * POST request has returned an empty FASTA file e.g. for invalid id
+       */
+      throw new IOException("No data returned for " + ids);
+    }
+
+    if (fr.getSeqs().size() > 0)
+    {
+      AlignmentI seqal = new Alignment(fr.getSeqsAsArray());
+      for (SequenceI sq : seqal.getSequences())
+      {
+        if (sq.getDescription() == null)
+        {
+          sq.setDescription(getDbName());
+        }
+        String name = sq.getName();
+        if (ids.contains(name)
+                || ids.contains(name.replace("ENSP", "ENST")))
+        {
+          DBRefEntry dbref = DBRefUtils.parseToDbRef(sq, getDbSource(),
+                  getEnsemblDataVersion(), name);
+          sq.addDBRef(dbref);
+        }
+      }
+      if (alignment == null)
+      {
+        alignment = seqal;
+      }
+      else
+      {
+        alignment.append(seqal);
+      }
+    }
+    return alignment;
+  }
+
+  /**
+   * Returns the URL for the REST call
+   * 
+   * @return
+   * @throws MalformedURLException
+   */
+  @Override
+  protected URL getUrl(List<String> ids) throws MalformedURLException
+  {
+    /*
+     * a single id is included in the URL path
+     * multiple ids go in the POST body instead
+     */
+    StringBuffer urlstring = new StringBuffer(128);
+    urlstring.append(getDomain() + "/sequence/id");
+    if (ids.size() == 1)
+    {
+      urlstring.append("/").append(ids.get(0));
+    }
+    // @see https://github.com/Ensembl/ensembl-rest/wiki/Output-formats
+    urlstring.append("?type=").append(getSourceEnsemblType().getType());
+    urlstring.append(("&Accept=text/x-fasta"));
+
+    URL url = new URL(urlstring.toString());
+    return url;
+  }
+
+  /**
+   * A sequence/id POST request currently allows up to 50 queries
+   * 
+   * @see http://rest.ensembl.org/documentation/info/sequence_id_post
+   */
+  @Override
+  public int getMaximumQueryCount()
+  {
+    return 50;
+  }
+
+  @Override
+  protected boolean useGetRequest()
+  {
+    return false;
+  }
+
+  @Override
+  protected String getRequestMimeType(boolean multipleIds)
+  {
+    return multipleIds ? "application/json" : "text/x-fasta";
+  }
+
+  @Override
+  protected String getResponseMimeType()
+  {
+    return "text/x-fasta";
+  }
+
+  /**
+   * 
+   * @return the configured sequence return type for this source
+   */
+  protected abstract EnsemblSeqType getSourceEnsemblType();
+
+  /**
+   * Returns a list of [start, end] genomic ranges corresponding to the sequence
+   * being retrieved.
+   * 
+   * The correspondence between the frames of reference is made by locating
+   * those features on the genomic sequence which identify the retrieved
+   * sequence. Specifically
+   * <ul>
+   * <li>genomic sequence is identified by "transcript" features with
+   * ID=transcript:transcriptId</li>
+   * <li>cdna sequence is identified by "exon" features with
+   * Parent=transcript:transcriptId</li>
+   * <li>cds sequence is identified by "CDS" features with
+   * Parent=transcript:transcriptId</li>
+   * </ul>
+   * 
+   * The returned ranges are sorted to run forwards (for positive strand) or
+   * backwards (for negative strand). Aborts and returns null if both positive
+   * and negative strand are found (this should not normally happen).
+   * 
+   * @param sourceSequence
+   * @param accId
+   * @param start
+   *          the start position of the sequence we are mapping to
+   * @return
+   */
+  protected MapList getGenomicRangesFromFeatures(SequenceI sourceSequence,
+          String accId, int start)
+  {
+    SequenceFeature[] sfs = sourceSequence.getSequenceFeatures();
+    if (sfs == null)
+    {
+      return null;
+    }
+
+    /*
+     * generously initial size for number of cds regions
+     * (worst case titin Q8WZ42 has c. 313 exons)
+     */
+    List<int[]> regions = new ArrayList<int[]>(100);
+    int mappedLength = 0;
+    int direction = 1; // forward
+    boolean directionSet = false;
+
+    for (SequenceFeature sf : sfs)
+    {
+      /*
+       * accept the target feature type or a specialisation of it
+       * (e.g. coding_exon for exon)
+       */
+      if (identifiesSequence(sf, accId))
+      {
+        int strand = sf.getStrand();
+        strand = strand == 0 ? 1 : strand; // treat unknown as forward
+
+        if (directionSet && strand != direction)
+        {
+          // abort - mix of forward and backward
+          System.err.println("Error: forward and backward strand for "
+                  + accId);
+          return null;
+        }
+        direction = strand;
+        directionSet = true;
+
+        /*
+         * add to CDS ranges, semi-sorted forwards/backwards
+         */
+        if (strand < 0)
+        {
+          regions.add(0, new int[] { sf.getEnd(), sf.getBegin() });
+        }
+        else
+        {
+          regions.add(new int[] { sf.getBegin(), sf.getEnd() });
+        }
+        mappedLength += Math.abs(sf.getEnd() - sf.getBegin() + 1);
+
+        if (!isSpliceable())
+        {
+          /*
+           * 'gene' sequence is contiguous so we can stop as soon as its
+           * identifying feature has been found
+           */
+          break;
+        }
+      }
+    }
+
+    if (regions.isEmpty())
+    {
+      System.out.println("Failed to identify target sequence for " + accId
+              + " from genomic features");
+      return null;
+    }
+
+    /*
+     * a final sort is needed since Ensembl returns CDS sorted within source
+     * (havana / ensembl_havana)
+     */
+    Collections.sort(regions, new RangeSorter(direction == 1));
+
+    List<int[]> to = Arrays.asList(new int[] { start,
+        start + mappedLength - 1 });
+
+    return new MapList(regions, to, 1, 1);
+  }
+
+  /**
+   * Answers true if the sequence being retrieved may occupy discontiguous
+   * regions on the genomic sequence.
+   */
+  protected boolean isSpliceable()
+  {
+    return true;
+  }
+
+  /**
+   * Returns true if the sequence feature marks positions of the genomic
+   * sequence feature which are within the sequence being retrieved. For
+   * example, an 'exon' feature whose parent is the target transcript marks the
+   * cdna positions of the transcript.
+   * 
+   * @param sf
+   * @param accId
+   * @return
+   */
+  protected abstract boolean identifiesSequence(SequenceFeature sf,
+          String accId);
+
+  /**
+   * Transfers the sequence feature to the target sequence, locating its start
+   * and end range based on the mapping. Features which do not overlap the
+   * target sequence are ignored.
+   * 
+   * @param sf
+   * @param targetSequence
+   * @param mapping
+   *          mapping from the sequence feature's coordinates to the target
+   *          sequence
+   * @param forwardStrand
+   */
+  protected void transferFeature(SequenceFeature sf,
+          SequenceI targetSequence, MapList mapping, boolean forwardStrand)
+  {
+    int start = sf.getBegin();
+    int end = sf.getEnd();
+    int[] mappedRange = mapping.locateInTo(start, end);
+
+    if (mappedRange != null)
+    {
+      SequenceFeature copy = new SequenceFeature(sf);
+      copy.setBegin(Math.min(mappedRange[0], mappedRange[1]));
+      copy.setEnd(Math.max(mappedRange[0], mappedRange[1]));
+      if (".".equals(copy.getFeatureGroup()))
+      {
+        copy.setFeatureGroup(getDbSource());
+      }
+      targetSequence.addSequenceFeature(copy);
+
+      /*
+       * for sequence_variant on reverse strand, have to convert the allele
+       * values to their complements
+       */
+      if (!forwardStrand
+              && SequenceOntologyFactory.getInstance().isA(sf.getType(),
+                      SequenceOntologyI.SEQUENCE_VARIANT))
+      {
+        reverseComplementAlleles(copy);
+      }
+    }
+  }
+
+  /**
+   * Change the 'alleles' value of a feature by converting to complementary
+   * bases, and also update the feature description to match
+   * 
+   * @param sf
+   */
+  static void reverseComplementAlleles(SequenceFeature sf)
+  {
+    final String alleles = (String) sf.getValue(ALLELES);
+    if (alleles == null)
+    {
+      return;
+    }
+    StringBuilder complement = new StringBuilder(alleles.length());
+    for (String allele : alleles.split(","))
+    {
+      reverseComplementAllele(complement, allele);
+    }
+    String comp = complement.toString();
+    sf.setValue(ALLELES, comp);
+    sf.setDescription(comp);
+
+    /*
+     * replace value of "alleles=" in sf.ATTRIBUTES as well
+     * so 'output as GFF' shows reverse complement alleles
+     */
+    String atts = sf.getAttributes();
+    if (atts != null)
+    {
+      atts = atts.replace(ALLELES + "=" + alleles, ALLELES + "=" + comp);
+      sf.setAttributes(atts);
+    }
+  }
+
+  /**
+   * Makes the 'reverse complement' of the given allele and appends it to the
+   * buffer, after a comma separator if not the first
+   * 
+   * @param complement
+   * @param allele
+   */
+  static void reverseComplementAllele(StringBuilder complement,
+          String allele)
+  {
+    if (complement.length() > 0)
+    {
+      complement.append(",");
+    }
+
+    /*
+     * some 'alleles' are actually descriptive terms 
+     * e.g. HGMD_MUTATION, PhenCode_variation
+     * - we don't want to 'reverse complement' these
+     */
+    if (!Comparison.isNucleotideSequence(allele, true))
+    {
+      complement.append(allele);
+    }
+    else
+    {
+      for (int i = allele.length() - 1; i >= 0; i--)
+      {
+        complement.append(Dna.getComplement(allele.charAt(i)));
+      }
+    }
+  }
+
+  /**
+   * Transfers features from sourceSequence to targetSequence
+   * 
+   * @param accessionId
+   * @param sourceSequence
+   * @param targetSequence
+   * @return true if any features were transferred, else false
+   */
+  protected boolean transferFeatures(String accessionId,
+          SequenceI sourceSequence, SequenceI targetSequence)
+  {
+    if (sourceSequence == null || targetSequence == null)
+    {
+      return false;
+    }
+
+    // long start = System.currentTimeMillis();
+    SequenceFeature[] sfs = sourceSequence.getSequenceFeatures();
+    MapList mapping = getGenomicRangesFromFeatures(sourceSequence,
+            accessionId, targetSequence.getStart());
+    if (mapping == null)
+    {
+      return false;
+    }
+
+    boolean result = transferFeatures(sfs, targetSequence, mapping,
+            accessionId);
+    // System.out.println("transferFeatures (" + (sfs.length) + " --> "
+    // + targetSequence.getSequenceFeatures().length + ") to "
+    // + targetSequence.getName()
+    // + " took " + (System.currentTimeMillis() - start) + "ms");
+    return result;
+  }
+
+  /**
+   * Transfer features to the target sequence. The start/end positions are
+   * converted using the mapping. Features which do not overlap are ignored.
+   * Features whose parent is not the specified identifier are also ignored.
+   * 
+   * @param features
+   * @param targetSequence
+   * @param mapping
+   * @param parentId
+   * @return
+   */
+  protected boolean transferFeatures(SequenceFeature[] features,
+          SequenceI targetSequence, MapList mapping, String parentId)
+  {
+    final boolean forwardStrand = mapping.isFromForwardStrand();
+
+    /*
+     * sort features by start position (which corresponds to end
+     * position descending if reverse strand) so as to add them in
+     * 'forwards' order to the target sequence
+     */
+    sortFeatures(features, forwardStrand);
+
+    boolean transferred = false;
+    for (SequenceFeature sf : features)
+    {
+      if (retainFeature(sf, parentId))
+      {
+        transferFeature(sf, targetSequence, mapping, forwardStrand);
+        transferred = true;
+      }
+    }
+    return transferred;
+  }
+
+  /**
+   * Sort features by start position ascending (if on forward strand), or end
+   * position descending (if on reverse strand)
+   * 
+   * @param features
+   * @param forwardStrand
+   */
+  protected static void sortFeatures(SequenceFeature[] features,
+          final boolean forwardStrand)
+  {
+    Arrays.sort(features, new Comparator<SequenceFeature>()
+    {
+      @Override
+      public int compare(SequenceFeature o1, SequenceFeature o2)
+      {
+        if (forwardStrand)
+        {
+          return Integer.compare(o1.getBegin(), o2.getBegin());
+        }
+        else
+        {
+          return Integer.compare(o2.getEnd(), o1.getEnd());
+        }
+      }
+    });
+  }
+
+  /**
+   * Answers true if the feature type is one we want to keep for the sequence.
+   * Some features are only retrieved in order to identify the sequence range,
+   * and may then be discarded as redundant information (e.g. "CDS" feature for
+   * a CDS sequence).
+   */
+  @SuppressWarnings("unused")
+  protected boolean retainFeature(SequenceFeature sf, String accessionId)
+  {
+    return true; // override as required
+  }
+
+  /**
+   * Answers true if the feature has a Parent which refers to the given
+   * accession id, or if the feature has no parent. Answers false if the
+   * feature's Parent is for a different accession id.
+   * 
+   * @param sf
+   * @param identifier
+   * @return
+   */
+  protected boolean featureMayBelong(SequenceFeature sf, String identifier)
+  {
+    String parent = (String) sf.getValue(PARENT);
+    // using contains to allow for prefix "gene:", "transcript:" etc
+    if (parent != null && !parent.contains(identifier))
+    {
+      // this genomic feature belongs to a different transcript
+      return false;
+    }
+    return true;
+  }
+
+  @Override
+  public String getDescription()
+  {
+    return "Ensembl " + getSourceEnsemblType().getType()
+            + " sequence with variant features";
+  }
+
+  /**
+   * Returns a (possibly empty) list of features on the sequence which have the
+   * specified sequence ontology type (or a sub-type of it), and the given
+   * identifier as parent
+   * 
+   * @param sequence
+   * @param type
+   * @param parentId
+   * @return
+   */
+  protected List<SequenceFeature> findFeatures(SequenceI sequence,
+          String type, String parentId)
+  {
+    List<SequenceFeature> result = new ArrayList<SequenceFeature>();
+
+    SequenceFeature[] sfs = sequence.getSequenceFeatures();
+    if (sfs != null)
+    {
+      SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+      for (SequenceFeature sf : sfs)
+      {
+        if (so.isA(sf.getType(), type))
+        {
+          String parent = (String) sf.getValue(PARENT);
+          if (parent.equals(parentId))
+          {
+            result.add(sf);
+          }
+        }
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Answers true if the feature type is either 'NMD_transcript_variant' or
+   * 'transcript' or one of its sub-types in the Sequence Ontology. This is
+   * needed because NMD_transcript_variant behaves like 'transcript' in Ensembl
+   * although strictly speaking it is not (it is a sub-type of
+   * sequence_variant).
+   * 
+   * @param featureType
+   * @return
+   */
+  public static boolean isTranscript(String featureType)
+  {
+    return SequenceOntologyI.NMD_TRANSCRIPT_VARIANT.equals(featureType)
+            || SequenceOntologyFactory.getInstance().isA(featureType,
+                    SequenceOntologyI.TRANSCRIPT);
+  }
+}
diff --git a/src/jalview/ext/ensembl/EnsemblSequenceFetcher.java b/src/jalview/ext/ensembl/EnsemblSequenceFetcher.java
new file mode 100644
index 0000000..3b45093
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblSequenceFetcher.java
@@ -0,0 +1,133 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+import jalview.datamodel.DBRefSource;
+import jalview.ws.seqfetcher.DbSourceProxyImpl;
+
+import com.stevesoft.pat.Regex;
+
+/**
+ * A base class for Ensembl sequence fetchers
+ * 
+ * @author gmcarstairs
+ */
+abstract class EnsemblSequenceFetcher extends DbSourceProxyImpl
+{
+  /*
+   * accepts ENSG/T/E/P with 11 digits
+   * or ENSMUSP or similar for other species
+   * or CCDSnnnnn.nn with at least 3 digits
+   */
+  private static final Regex ACCESSION_REGEX = new Regex(
+          "(ENS([A-Z]{3}|)[GTEP]{1}[0-9]{11}$)" + "|" + "(CCDS[0-9.]{3,}$)");
+
+  protected static final String ENSEMBL_GENOMES_REST = "http://rest.ensemblgenomes.org";
+
+  protected static final String ENSEMBL_REST = "http://rest.ensembl.org";
+
+  /*
+   * possible values for the 'feature' parameter of the /overlap REST service
+   * @see http://rest.ensembl.org/documentation/info/overlap_id
+   */
+  protected enum EnsemblFeatureType
+  {
+    gene, transcript, cds, exon, repeat, simple, misc, variation,
+    somatic_variation, structural_variation, somatic_structural_variation,
+    constrained, regulatory
+  }
+
+  private String domain = ENSEMBL_REST;
+
+  @Override
+  public String getDbSource()
+  {
+    // NB ensure Uniprot xrefs are canonicalised from "Ensembl" to "ENSEMBL"
+    if (ENSEMBL_GENOMES_REST.equals(getDomain()))
+    {
+      return DBRefSource.ENSEMBLGENOMES;
+    }
+    return DBRefSource.ENSEMBL;
+  }
+
+  @Override
+  public String getAccessionSeparator()
+  {
+    return " ";
+  }
+
+  /**
+   * Ensembl accession are ENST + 11 digits for human transcript, ENSG for human
+   * gene. Other species insert 3 letters e.g. ENSMUST..., ENSMUSG...
+   * 
+   * @see http://www.ensembl.org/Help/View?id=151
+   */
+  @Override
+  public Regex getAccessionValidator()
+  {
+    return ACCESSION_REGEX;
+  }
+
+  @Override
+  public boolean isValidReference(String accession)
+  {
+    return getAccessionValidator().search(accession);
+  }
+
+  @Override
+  public int getTier()
+  {
+    return 0;
+  }
+
+  /**
+   * Default test query is a transcript
+   */
+  @Override
+  public String getTestQuery()
+  {
+    // has CDS on reverse strand:
+    return "ENST00000288602";
+    // ENST00000461457 // forward strand
+  }
+
+  @Override
+  public boolean isDnaCoding()
+  {
+    return true;
+  }
+
+  /**
+   * Returns the domain name to query e.g. http://rest.ensembl.org or
+   * http://rest.ensemblgenomes.org
+   * 
+   * @return
+   */
+  protected String getDomain()
+  {
+    return domain;
+  }
+
+  protected void setDomain(String d)
+  {
+    domain = d;
+  }
+}
diff --git a/src/jalview/ext/ensembl/EnsemblSymbol.java b/src/jalview/ext/ensembl/EnsemblSymbol.java
new file mode 100644
index 0000000..a29ab9c
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblSymbol.java
@@ -0,0 +1,159 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+/**
+ * A client for the Ensembl xrefs/symbol REST service;
+ * 
+ * @see http://rest.ensembl.org/documentation/info/xref_external
+ * @author gmcarstairs
+ *
+ */
+public class EnsemblSymbol extends EnsemblXref
+{
+  /**
+   * Constructor given the target domain to fetch data from
+   * 
+   * @param domain
+   * @param dbName
+   * @param dbVersion
+   */
+  public EnsemblSymbol(String domain, String dbName, String dbVersion)
+  {
+    super(domain, dbName, dbVersion);
+  }
+
+  /**
+   * Returns the first "id" value in gene identifier format from the JSON
+   * response, or null if none found
+   * 
+   * @param br
+   * @return
+   * @throws IOException
+   */
+  protected String parseSymbolResponse(BufferedReader br)
+          throws IOException
+  {
+    JSONParser jp = new JSONParser();
+    String result = null;
+    try
+    {
+      JSONArray responses = (JSONArray) jp.parse(br);
+      Iterator rvals = responses.iterator();
+      while (rvals.hasNext())
+      {
+        JSONObject val = (JSONObject) rvals.next();
+        String id = val.get("id").toString();
+        if (id != null && isGeneIdentifier(id))
+        {
+          result = id;
+          break;
+        }
+      }
+    } catch (ParseException e)
+    {
+      // ignore
+    }
+    return result;
+  }
+
+  protected URL getUrl(String id, Species species)
+  {
+    String url = getDomain() + "/xrefs/symbol/" + species.toString() + "/"
+            + id + "?content-type=application/json";
+    try
+    {
+      return new URL(url);
+    } catch (MalformedURLException e)
+    {
+      return null;
+    }
+  }
+
+  /**
+   * Calls the Ensembl xrefs REST 'symbol' endpoint and retrieves any gene ids
+   * for the given identifier, for any known model organisms
+   * 
+   * @param identifier
+   * @return
+   */
+  public List<String> getIds(String identifier)
+  {
+    List<String> result = new ArrayList<String>();
+    List<String> ids = new ArrayList<String>();
+    ids.add(identifier);
+
+    String[] queries = identifier.split(getAccessionSeparator());
+    BufferedReader br = null;
+    try
+    {
+      for (String query : queries)
+      {
+        for (Species taxon : Species.values())
+        {
+          if (taxon.isModelOrganism())
+          {
+            URL url = getUrl(query, taxon);
+            if (url != null)
+            {
+              br = getHttpResponse(url, ids);
+            }
+            String geneId = parseSymbolResponse(br);
+            if (geneId != null)
+            {
+              result.add(geneId);
+            }
+          }
+        }
+      }
+    } catch (IOException e)
+    {
+      // ignore
+    } finally
+    {
+      if (br != null)
+      {
+        try
+        {
+          br.close();
+        } catch (IOException e)
+        {
+          // ignore
+        }
+      }
+    }
+    return result;
+  }
+
+}
diff --git a/src/jalview/ext/ensembl/EnsemblXref.java b/src/jalview/ext/ensembl/EnsemblXref.java
new file mode 100644
index 0000000..8a73ac0
--- /dev/null
+++ b/src/jalview/ext/ensembl/EnsemblXref.java
@@ -0,0 +1,224 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.util.DBRefUtils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+/**
+ * A class to fetch cross-references from Ensembl by calling the /xrefs REST
+ * service
+ * 
+ * @author gmcarstairs
+ * @see http://rest.ensembl.org/documentation/info/xref_id
+ */
+class EnsemblXref extends EnsemblRestClient
+{
+
+  private static final String GO_GENE_ONTOLOGY = "GO";
+
+  private String dbName = "ENSEMBL (xref)";
+
+  /**
+   * Constructor given the target domain to fetch data from
+   * 
+   * @param d
+   */
+  public EnsemblXref(String d, String dbSource, String version)
+  {
+    super(d);
+    dbName = dbSource;
+    xrefVersion = dbSource + ":" + version;
+
+  }
+
+  @Override
+  public String getDbName()
+  {
+    return dbName;
+  }
+
+  @Override
+  public AlignmentI getSequenceRecords(String queries) throws Exception
+  {
+    return null;
+  }
+
+  @Override
+  protected URL getUrl(List<String> ids) throws MalformedURLException
+  {
+    return getUrl(ids.get(0));
+  }
+
+  @Override
+  protected boolean useGetRequest()
+  {
+    return true;
+  }
+
+  @Override
+  protected String getRequestMimeType(boolean multipleIds)
+  {
+    return "application/json";
+  }
+
+  @Override
+  protected String getResponseMimeType()
+  {
+    return "application/json";
+  }
+
+  /**
+   * Calls the Ensembl xrefs REST endpoint and retrieves any cross-references
+   * ("primary_id") for the given identifier (Ensembl accession id) and database
+   * names. The "dbname" returned by Ensembl is canonicalised to Jalview's
+   * standard version, and a DBRefEntry constructed. Currently takes all
+   * identifiers apart from GO terms and synonyms.
+   * 
+   * @param identifier
+   *          an Ensembl stable identifier
+   * @return
+   */
+  public List<DBRefEntry> getCrossReferences(String identifier)
+  {
+    List<DBRefEntry> result = new ArrayList<DBRefEntry>();
+    List<String> ids = new ArrayList<String>();
+    ids.add(identifier);
+
+    BufferedReader br = null;
+    try
+    {
+      URL url = getUrl(identifier);
+      if (url != null)
+      {
+        br = getHttpResponse(url, ids);
+      }
+      return (parseResponse(br));
+    } catch (IOException e)
+    {
+      // ignore
+    } finally
+    {
+      if (br != null)
+      {
+        try
+        {
+          br.close();
+        } catch (IOException e)
+        {
+          // ignore
+        }
+      }
+    }
+
+    return result;
+  }
+
+  /**
+   * Parses "primary_id" and "dbname" values from the JSON response and
+   * constructs a DBRefEntry. Returns a list of the DBRefEntry created. Note we
+   * don't parse "synonyms" as they appear to be either redirected or obsolete
+   * in Uniprot.
+   * 
+   * @param br
+   * @return
+   * @throws IOException
+   */
+  protected List<DBRefEntry> parseResponse(BufferedReader br)
+          throws IOException
+  {
+    JSONParser jp = new JSONParser();
+    List<DBRefEntry> result = new ArrayList<DBRefEntry>();
+    try
+    {
+      JSONArray responses = (JSONArray) jp.parse(br);
+      Iterator rvals = responses.iterator();
+      while (rvals.hasNext())
+      {
+        JSONObject val = (JSONObject) rvals.next();
+        String dbName = val.get("dbname").toString();
+        if (dbName.equals(GO_GENE_ONTOLOGY))
+        {
+          continue;
+        }
+        String id = val.get("primary_id").toString();
+        if (dbName != null && id != null)
+        {
+          dbName = DBRefUtils.getCanonicalName(dbName);
+          DBRefEntry dbref = new DBRefEntry(dbName, getXRefVersion(), id);
+          result.add(dbref);
+        }
+      }
+    } catch (ParseException e)
+    {
+      // ignore
+    }
+    return result;
+  }
+
+  private String xrefVersion = "ENSEMBL:0";
+
+  /**
+   * version string for Xrefs - for 2.10, hardwired for ENSEMBL:0
+   * 
+   * @return
+   */
+  public String getXRefVersion()
+  {
+    return xrefVersion;
+  }
+
+  /**
+   * Returns the URL for the REST endpoint to fetch all cross-references for an
+   * identifier. Note this may return protein cross-references for nucleotide.
+   * Filter the returned list as required.
+   * 
+   * @param identifier
+   * @return
+   */
+  protected URL getUrl(String identifier)
+  {
+    String url = getDomain() + "/xrefs/id/" + identifier
+            + "?content-type=application/json&all_levels=1";
+    try
+    {
+      return new URL(url);
+    } catch (MalformedURLException e)
+    {
+      return null;
+    }
+  }
+
+}
diff --git a/src/jalview/ext/ensembl/Species.java b/src/jalview/ext/ensembl/Species.java
new file mode 100644
index 0000000..1ebf153
--- /dev/null
+++ b/src/jalview/ext/ensembl/Species.java
@@ -0,0 +1,52 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.ensembl;
+
+/**
+ * Selected species identifiers used by Ensembl
+ * 
+ * @author gmcarstairs
+ * @see http://rest.ensembl.org/info/species?content-type=text/xml
+ */
+enum Species
+{
+  /*
+   * using any suitably readable alias as the enum name; these are all
+   * valid species parameters to Ensembl REST services where applicable
+   */
+  human(true), mouse(true), s_cerevisiae(true), cow(false), pig(false),
+  rat(true), celegans(true), sheep(false), horse(false), gorilla(false),
+  rabbit(false), gibbon(false), dog(false), orangutan(false),
+  xenopus(true), chimpanzee(false), cat(false), zebrafish(true), chicken(
+          true), dmelanogaster(true);
+
+  boolean modelOrganism;
+
+  private Species(boolean model)
+  {
+    this.modelOrganism = model;
+  }
+
+  boolean isModelOrganism()
+  {
+    return modelOrganism;
+  }
+}
diff --git a/src/jalview/ext/htsjdk/HtsContigDb.java b/src/jalview/ext/htsjdk/HtsContigDb.java
new file mode 100644
index 0000000..e706a48
--- /dev/null
+++ b/src/jalview/ext/htsjdk/HtsContigDb.java
@@ -0,0 +1,232 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.htsjdk;
+
+import htsjdk.samtools.SAMSequenceDictionary;
+import htsjdk.samtools.SAMSequenceRecord;
+import htsjdk.samtools.reference.ReferenceSequence;
+import htsjdk.samtools.reference.ReferenceSequenceFile;
+import htsjdk.samtools.reference.ReferenceSequenceFileFactory;
+import htsjdk.samtools.util.StringUtil;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+import java.io.File;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * a source of sequence data accessed via the HTSJDK
+ * 
+ * @author jprocter
+ *
+ */
+public class HtsContigDb
+{
+
+  private String name;
+
+  private File dbLocation;
+
+  private htsjdk.samtools.reference.ReferenceSequenceFile refFile = null;
+
+  public HtsContigDb(String name, File descriptor) throws Exception
+  {
+    if (descriptor.isFile())
+    {
+      this.name = name;
+      dbLocation = descriptor;
+    }
+    initSource();
+  }
+
+  private void initSource() throws Exception
+  {
+    if (refFile != null)
+    {
+      return;
+    }
+
+    refFile = ReferenceSequenceFileFactory.getReferenceSequenceFile(
+            dbLocation, true);
+    if (refFile == null || refFile.getSequenceDictionary() == null)
+    {
+      // refFile = initSequenceDictionaryFor(dbLocation);
+    }
+
+  }
+
+  SAMSequenceDictionary rrefDict = null;
+
+  private ReferenceSequenceFile initSequenceDictionaryFor(File dbLocation2)
+          throws Exception
+  {
+    rrefDict = getDictionary(dbLocation2, true);
+    if (rrefDict != null)
+    {
+      ReferenceSequenceFile rrefFile = ReferenceSequenceFileFactory
+              .getReferenceSequenceFile(dbLocation2, true);
+      return rrefFile;
+    }
+    return null;
+  }
+
+  /**
+   * code below hacked out from picard ----
+   * 
+   * picard/src/java/picard/sam/CreateSequenceDictionary.java
+   * https://github.com/
+   * broadinstitute/picard/commit/270580d3e28123496576f0b91b3433179bb5d876
+   */
+
+  /*
+   * The MIT License
+   * 
+   * Copyright (c) 2009 The Broad Institute
+   * 
+   * Permission is hereby granted, free of charge, to any person obtaining a
+   * copy of this software and associated documentation files (the "Software"),
+   * to deal in the Software without restriction, including without limitation
+   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+   * and/or sell copies of the Software, and to permit persons to whom the
+   * Software is furnished to do so, subject to the following conditions:
+   * 
+   * The above copyright notice and this permission notice shall be included in
+   * all copies or substantial portions of the Software.
+   * 
+   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+   * DEALINGS IN THE SOFTWARE.
+   */
+  /**
+   * 
+   * @param f
+   * @param truncate
+   * @return
+   * @throws Exception
+   */
+  SAMSequenceDictionary getDictionary(File f, boolean truncate)
+          throws Exception
+  {
+    if (md5 == null)
+    {
+      initCreateSequenceDictionary();
+    }
+    final ReferenceSequenceFile refSeqFile = ReferenceSequenceFileFactory
+            .getReferenceSequenceFile(f, truncate);
+    ReferenceSequence refSeq;
+    List<SAMSequenceRecord> ret = new ArrayList<SAMSequenceRecord>();
+    Set<String> sequenceNames = new HashSet<String>();
+    for (int numSequences = 0; (refSeq = refSeqFile.nextSequence()) != null; ++numSequences)
+    {
+      if (sequenceNames.contains(refSeq.getName()))
+      {
+        throw new Exception(
+                "Sequence name appears more than once in reference: "
+                        + refSeq.getName());
+      }
+      sequenceNames.add(refSeq.getName());
+      ret.add(makeSequenceRecord(refSeq));
+    }
+    return new SAMSequenceDictionary(ret);
+  }
+
+  public boolean isValid()
+  {
+    return dbLocation != null && refFile != null;
+  }
+
+  /**
+   * Create one SAMSequenceRecord from a single fasta sequence
+   */
+  private SAMSequenceRecord makeSequenceRecord(
+          final ReferenceSequence refSeq)
+  {
+
+    final SAMSequenceRecord ret = new SAMSequenceRecord(refSeq.getName(),
+            refSeq.length());
+
+    // Compute MD5 of upcased bases
+    final byte[] bases = refSeq.getBases();
+    for (int i = 0; i < bases.length; ++i)
+    {
+      bases[i] = StringUtil.toUpperCase(bases[i]);
+    }
+
+    ret.setAttribute(SAMSequenceRecord.MD5_TAG, md5Hash(bases));
+    // if (GENOME_ASSEMBLY != null) {
+    // ret.setAttribute(SAMSequenceRecord.ASSEMBLY_TAG, GENOME_ASSEMBLY);
+    // }
+    // ret.setAttribute(SAMSequenceRecord.URI_TAG, URI);
+    // if (SPECIES != null) {
+    // ret.setAttribute(SAMSequenceRecord.SPECIES_TAG, SPECIES);
+    // }
+    return ret;
+  }
+
+  private MessageDigest md5;
+
+  public void initCreateSequenceDictionary() throws Exception
+  {
+    try
+    {
+      md5 = MessageDigest.getInstance("MD5");
+    } catch (NoSuchAlgorithmException e)
+    {
+      throw new Exception("MD5 algorithm not found", e);
+    }
+  }
+
+  private String md5Hash(final byte[] bytes)
+  {
+    md5.reset();
+    md5.update(bytes);
+    String s = new BigInteger(1, md5.digest()).toString(16);
+    if (s.length() != 32)
+    {
+      final String zeros = "00000000000000000000000000000000";
+      s = zeros.substring(0, 32 - s.length()) + s;
+    }
+    return s;
+  }
+
+  // ///// end of hts bits.
+
+  SequenceI getSequenceProxy(String id)
+  {
+    if (!isValid())
+    {
+      return null;
+    }
+
+    ReferenceSequence sseq = refFile.getSequence(id);
+    return new Sequence(sseq.getName(), new String(sseq.getBases()));
+  }
+}
diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java
index 58325fd..cc338b2 100644
--- a/src/jalview/ext/jmol/JalviewJmolBinding.java
+++ b/src/jalview/ext/jmol/JalviewJmolBinding.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,6 +28,7 @@ import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.io.AppletFormatAdapter;
+import jalview.io.StructureFile;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ResidueProperties;
 import jalview.structure.AtomSpec;
@@ -42,13 +43,12 @@ import java.awt.event.ComponentListener;
 import java.io.File;
 import java.net.URL;
 import java.security.AccessControlException;
+import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
 import java.util.Vector;
 
-import javajs.awt.Dimension;
-
 import org.jmol.adapter.smarter.SmarterJmolAdapter;
 import org.jmol.api.JmolAppConsoleInterface;
 import org.jmol.api.JmolSelectionListener;
@@ -56,7 +56,6 @@ import org.jmol.api.JmolStatusListener;
 import org.jmol.api.JmolViewer;
 import org.jmol.c.CBK;
 import org.jmol.script.T;
-import org.jmol.viewer.JC;
 import org.jmol.viewer.Viewer;
 
 public abstract class JalviewJmolBinding extends AAStructureBindingModel
@@ -73,7 +72,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
 
   Vector<String> atomsPicked = new Vector<String>();
 
-  public Vector<String> chainNames;
+  private List<String> chainNames;
 
   Hashtable<String, String> chainFile;
 
@@ -93,20 +92,15 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
 
   boolean loadedInline;
 
-  /**
-   * current set of model filenames loaded in the Jmol instance
-   */
-  String[] modelFileNames = null;
-
   StringBuffer resetLastRes = new StringBuffer();
 
   public Viewer viewer;
 
   public JalviewJmolBinding(StructureSelectionManager ssm,
-          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
+          PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
           String protocol)
   {
-    super(ssm, pdbentry, sequenceIs, chains, protocol);
+    super(ssm, pdbentry, sequenceIs, protocol);
     /*
      * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
      * "jalviewJmol", ap.av.applet .getDocumentBase(),
@@ -169,12 +163,9 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
 
   public void closeViewer()
   {
-    viewer.acm.setModeMouse(JC.MOUSE_NONE);
     // remove listeners for all structures in viewer
     getSsm().removeStructureViewerListener(this, this.getPdbFile());
-    // and shut down jmol
-    viewer.evalStringQuiet("zap");
-    viewer.setJmolStatusListener(null);
+    viewer.dispose();
     lastCommand = null;
     viewer = null;
     releaseUIResources();
@@ -261,8 +252,12 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       } catch (InterruptedException i)
       {
       }
-      ;
     }
+
+    /*
+     * get the distinct structure files modelled
+     * (a file with multiple chains may map to multiple sequences)
+     */
     String[] files = getPdbFile();
     if (!waitForFileLoad(files))
     {
@@ -310,6 +305,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
        * 'matched' array will hold 'true' for visible alignment columns where
        * all sequences have a residue with a mapping to the PDB structure
        */
+      // TODO could use a BitSet for matched
       boolean matched[] = new boolean[alignment.getWidth()];
       for (int m = 0; m < matched.length; m++)
       {
@@ -355,6 +351,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
        * generate select statements to select regions to superimpose structures
        */
       {
+        // TODO extract method to construct selection statements
         for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
         {
           String chainCd = ":" + structures[pdbfnum].chain;
@@ -422,6 +419,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
         }
       }
       StringBuilder command = new StringBuilder(256);
+      // command.append("set spinFps 10;\n");
+
       for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
       {
         if (pdbfnum == refStructure || selcom[pdbfnum] == null
@@ -452,12 +451,13 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       }
       if (selectioncom.length() > 0)
       {
-        System.out.println("Select regions:\n" + selectioncom.toString());
+        // TODO is performing selectioncom redundant here? is done later on
+        // System.out.println("Select regions:\n" + selectioncom.toString());
         evalStateCommand("select *; cartoons off; backbone; select ("
                 + selectioncom.toString() + "); cartoons; ");
         // selcom.append("; ribbons; ");
         String cmdString = command.toString();
-        System.out.println("Superimpose command(s):\n" + cmdString);
+        // System.out.println("Superimpose command(s):\n" + cmdString);
 
         evalStateCommand(cmdString);
       }
@@ -468,7 +468,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       {
         selectioncom.setLength(selectioncom.length() - 1);
       }
-      System.out.println("Select regions:\n" + selectioncom.toString());
+      // System.out.println("Select regions:\n" + selectioncom.toString());
       evalStateCommand("select *; cartoons off; backbone; select ("
               + selectioncom.toString() + "); cartoons; ");
       // evalStateCommand("select *; backbone; select "+selcom.toString()+"; cartoons; center "+selcom.toString());
@@ -552,6 +552,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     System.out.println("JMOL CREATE IMAGE");
   }
 
+  @Override
   public String createImage(String fileName, String type,
           Object textOrBytes, int quality)
   {
@@ -559,6 +560,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     return null;
   }
 
+  @Override
   public String eval(String strEval)
   {
     // System.out.println(strEval);
@@ -569,11 +571,13 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   // End StructureListener
   // //////////////////////////
 
+  @Override
   public float[][] functionXY(String functionName, int x, int y)
   {
     return null;
   }
 
+  @Override
   public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
   {
     // TODO Auto-generated method stub
@@ -646,15 +650,15 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     }
     if (modelFileNames == null)
     {
-      String mset[] = new String[viewer.ms.mc];
-      _modelFileNameMap = new int[mset.length];
+      List<String> mset = new ArrayList<String>();
+      _modelFileNameMap = new int[viewer.ms.mc];
       String m = viewer.ms.getModelFileName(0);
       if (m != null)
       {
-        mset[0] = m;
+        String filePath = m;
         try
         {
-          mset[0] = new File(m).getAbsolutePath();
+          filePath = new File(m).getAbsolutePath();
         } catch (AccessControlException x)
         {
           // usually not allowed to do this in applet
@@ -662,39 +666,43 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
                   .println("jmolBinding: Using local file string from Jmol: "
                           + m);
         }
-        if (mset[0].indexOf("/file:") != -1)
+        if (filePath.indexOf("/file:") != -1)
         {
           // applet path with docroot - discard as format won't match pdbfile
-          mset[0] = m;
+          filePath = m;
         }
+        mset.add(filePath);
         _modelFileNameMap[0] = 0; // filename index for first model is always 0.
       }
       int j = 1;
-      for (int i = 1; i < mset.length; i++)
+      for (int i = 1; i < viewer.ms.mc; i++)
       {
         m = viewer.ms.getModelFileName(i);
-        mset[j] = m;
+        String filePath = m;
         if (m != null)
         {
           try
           {
-            mset[j] = new File(m).getAbsolutePath();
+            filePath = new File(m).getAbsolutePath();
           } catch (AccessControlException x)
           {
             // usually not allowed to do this in applet, so keep raw handle
             // System.err.println("jmolBinding: Using local file string from Jmol: "+m);
           }
         }
-        _modelFileNameMap[j] = i; // record the model index for the filename
-        // skip any additional models in the same file (NMR structures)
-        if ((mset[j] == null ? mset[j] != mset[j - 1]
-                : (mset[j - 1] == null || !mset[j].equals(mset[j - 1]))))
+
+        /*
+         * add this model unless it is read from a structure file we have
+         * already seen (example: 2MJW is an NMR structure with 10 models)
+         */
+        if (!mset.contains(filePath))
         {
+          mset.add(filePath);
+          _modelFileNameMap[j] = i; // record the model index for the filename
           j++;
         }
       }
-      modelFileNames = new String[j];
-      System.arraycopy(mset, 0, modelFileNames, 0, j);
+      modelFileNames = mset.toArray(new String[mset.size()]);
     }
     return modelFileNames;
   }
@@ -737,6 +745,11 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   {
     if (atoms != null)
     {
+      if (resetLastRes.length() > 0)
+      {
+        viewer.evalStringQuiet(resetLastRes.toString());
+        resetLastRes.setLength(0);
+      }
       for (AtomSpec atom : atoms)
       {
         highlightAtom(atom.getAtomIndex(), atom.getPdbResNum(),
@@ -768,17 +781,10 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     }
 
     jmolHistory(false);
-    // if (!pdbfile.equals(pdbentry.getFile()))
-    // return;
-    if (resetLastRes.length() > 0)
-    {
-      viewer.evalStringQuiet(resetLastRes.toString());
-    }
 
     StringBuilder cmd = new StringBuilder(64);
     cmd.append("select " + pdbResNum); // +modelNum
 
-    resetLastRes.setLength(0);
     resetLastRes.append("select " + pdbResNum); // +modelNum
 
     cmd.append(":");
@@ -1075,7 +1081,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     fileLoadingError = null;
     String[] oldmodels = modelFileNames;
     modelFileNames = null;
-    chainNames = new Vector<String>();
+    chainNames = new ArrayList<String>();
     chainFile = new Hashtable<String, String>();
     boolean notifyLoaded = false;
     String[] modelfilenames = getPdbFile();
@@ -1119,7 +1125,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     {
       String fileName = modelfilenames[modelnum];
       boolean foundEntry = false;
-      MCview.PDBfile pdb = null;
+      StructureFile pdb = null;
       String pdbfile = null;
       // model was probably loaded inline - so check the pdb file hashcode
       if (loadedInline)
@@ -1135,6 +1141,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       for (int pe = 0; pe < getPdbCount(); pe++)
       {
         boolean matches = false;
+        addSequence(pe, getSequence()[pe]);
         if (fileName == null)
         {
           if (false)
@@ -1142,7 +1149,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
           {
             pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
                     pdbfile, AppletFormatAdapter.PASTE);
-            getPdbEntry(modelnum).setFile("INLINE" + pdb.id);
+            getPdbEntry(modelnum).setFile("INLINE" + pdb.getId());
             matches = true;
             foundEntry = true;
           }
@@ -1181,12 +1188,12 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
         if (matches)
         {
           // add an entry for every chain in the model
-          for (int i = 0; i < pdb.chains.size(); i++)
+          for (int i = 0; i < pdb.getChains().size(); i++)
           {
-            String chid = new String(pdb.id + ":"
-                    + pdb.chains.elementAt(i).id);
+            String chid = new String(pdb.getId() + ":"
+                    + pdb.getChains().elementAt(i).id);
             chainFile.put(chid, fileName);
-            chainNames.addElement(chid);
+            chainNames.add(chid);
           }
           notifyLoaded = true;
         }
@@ -1234,6 +1241,12 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     setLoadingFromArchive(false);
   }
 
+  @Override
+  public List<String> getChainNames()
+  {
+    return chainNames;
+  }
+
   public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
   {
     notifyAtomPicked(iatom, strMeasure, null);
@@ -1264,6 +1277,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
    */
   public abstract void sendConsoleMessage(String strStatus);
 
+  @Override
   public void setCallbackFunction(String callbackType,
           String callbackFunction)
   {
@@ -1395,7 +1409,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   }
 
   @Override
-  public Dimension resizeInnerPanel(String data)
+  public int[] resizeInnerPanel(String data)
   {
     // Jalview doesn't honour resize panel requests
     return null;
diff --git a/src/jalview/ext/jmol/JmolCommands.java b/src/jalview/ext/jmol/JmolCommands.java
index 9ca2548..c2ef436 100644
--- a/src/jalview/ext/jmol/JmolCommands.java
+++ b/src/jalview/ext/jmol/JmolCommands.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -64,7 +64,9 @@ public class JmolCommands
       ArrayList<String> str = new ArrayList<String>();
 
       if (mapping == null || mapping.length < 1)
+      {
         continue;
+      }
 
       int lastPos = -1;
       for (int s = 0; s < sequence[pdbfnum].length; s++)
@@ -85,14 +87,18 @@ public class JmolCommands
               int pos = mapping[m].getPDBResNum(asp.findPosition(r));
 
               if (pos < 1 || pos == lastPos)
+              {
                 continue;
+              }
 
               lastPos = pos;
 
               Color col = sr.getResidueBoxColour(sequence[pdbfnum][s], r);
 
               if (fr != null)
+              {
                 col = fr.findFeatureColour(col, sequence[pdbfnum][s], r);
+              }
               String newSelcom = (mapping[m].getChain() != " " ? ":"
                       + mapping[m].getChain() : "")
                       + "/"
@@ -125,7 +131,7 @@ public class JmolCommands
               command.append("select " + pos);
               command.append(newSelcom);
             }
-            break;
+            // break;
           }
         }
       }
diff --git a/src/jalview/ext/jmol/JmolParser.java b/src/jalview/ext/jmol/JmolParser.java
new file mode 100644
index 0000000..98b9a59
--- /dev/null
+++ b/src/jalview/ext/jmol/JmolParser.java
@@ -0,0 +1,658 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.jmol;
+
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.io.FileParse;
+import jalview.io.StructureFile;
+import jalview.schemes.ResidueProperties;
+import jalview.structure.StructureImportSettings;
+import jalview.util.Format;
+import jalview.util.MessageManager;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.jmol.api.JmolStatusListener;
+import org.jmol.api.JmolViewer;
+import org.jmol.c.CBK;
+import org.jmol.c.STR;
+import org.jmol.modelset.ModelSet;
+import org.jmol.viewer.Viewer;
+
+import MCview.Atom;
+import MCview.PDBChain;
+import MCview.Residue;
+
+/**
+ * Import and process files with Jmol for file like PDB, mmCIF
+ * 
+ * @author jprocter
+ * 
+ */
+public class JmolParser extends StructureFile implements JmolStatusListener
+{
+  Viewer viewer = null;
+
+  public JmolParser(String inFile, String type) throws IOException
+  {
+    super(inFile, type);
+  }
+
+  public JmolParser(FileParse fp) throws IOException
+  {
+    super(fp);
+  }
+
+  public JmolParser()
+  {
+  }
+
+  /**
+   * Calls the Jmol library to parse the PDB/mmCIF file, and then inspects the
+   * resulting object model to generate Jalview-style sequences, with secondary
+   * structure annotation added where available (i.e. where it has been computed
+   * by Jmol using DSSP).
+   * 
+   * @see jalview.io.AlignFile#parse()
+   */
+  @Override
+  public void parse() throws IOException
+  {
+    setChains(new Vector<PDBChain>());
+    Viewer jmolModel = getJmolData();
+    jmolModel.openReader(getDataName(), getDataName(), getReader());
+    waitForScript(jmolModel);
+
+    /*
+     * Convert one or more Jmol Model objects to Jalview sequences
+     */
+    if (jmolModel.ms.mc > 0)
+    {
+      // ideally we do this
+      // try
+      // {
+      // setStructureFileType(jmolModel.evalString("show _fileType"));
+      // } catch (Exception q)
+      // {
+      // }
+      // ;
+      // instead, we distinguish .cif from non-.cif by filename
+      setStructureFileType(getDataName().toLowerCase().endsWith(".cif") ? PDBEntry.Type.MMCIF
+              .toString() : "PDB");
+
+      transformJmolModelToJalview(jmolModel.ms);
+    }
+  }
+
+  /**
+   * create a headless jmol instance for dataprocessing
+   * 
+   * @return
+   */
+  private Viewer getJmolData()
+  {
+    if (viewer == null)
+    {
+      try
+      {
+        /*
+         * params -o (output to sysout) -n (nodisplay) -x (exit when finished)
+         * see http://wiki.jmol.org/index.php/Jmol_Application
+         */
+        viewer = (Viewer) JmolViewer.allocateViewer(null, null, null, null,
+                null, "-x -o -n", this);
+        // ensure the 'new' (DSSP) not 'old' (Ramachandran) SS method is used
+        viewer.setBooleanProperty("defaultStructureDSSP", true);
+      } catch (ClassCastException x)
+      {
+        throw new Error(MessageManager.formatMessage(
+                "error.jmol_version_not_compatible_with_jalview_version",
+                new String[] { JmolViewer.getJmolVersion() }), x);
+      }
+    }
+    return viewer;
+  }
+
+  public void transformJmolModelToJalview(ModelSet ms) throws IOException
+  {
+    try
+    {
+      String lastID = "";
+      List<SequenceI> rna = new ArrayList<SequenceI>();
+      List<SequenceI> prot = new ArrayList<SequenceI>();
+      PDBChain tmpchain;
+      String pdbId = (String) ms.getInfo(0, "title");
+
+      if (pdbId == null)
+      {
+        setId(safeName(getDataName()));
+        setPDBIdAvailable(false);
+      }
+      else
+      {
+        setId(pdbId);
+        setPDBIdAvailable(true);
+      }
+      List<Atom> significantAtoms = convertSignificantAtoms(ms);
+      for (Atom tmpatom : significantAtoms)
+      {
+        try
+        {
+          tmpchain = findChain(tmpatom.chain);
+          if (tmpatom.resNumIns.trim().equals(lastID))
+          {
+            // phosphorylated protein - seen both CA and P..
+            continue;
+          }
+          tmpchain.atoms.addElement(tmpatom);
+        } catch (Exception e)
+        {
+          tmpchain = new PDBChain(getId(), tmpatom.chain);
+          getChains().add(tmpchain);
+          tmpchain.atoms.addElement(tmpatom);
+        }
+        lastID = tmpatom.resNumIns.trim();
+      }
+      xferSettings();
+
+      makeResidueList();
+      makeCaBondList();
+
+      for (PDBChain chain : getChains())
+      {
+        SequenceI chainseq = postProcessChain(chain);
+        if (isRNA(chainseq))
+        {
+          rna.add(chainseq);
+        }
+        else
+        {
+          prot.add(chainseq);
+        }
+
+        if (StructureImportSettings.isProcessSecondaryStructure())
+        {
+          createAnnotation(chainseq, chain, ms.at);
+        }
+      }
+    } catch (OutOfMemoryError er)
+    {
+      System.out
+              .println("OUT OF MEMORY LOADING TRANSFORMING JMOL MODEL TO JALVIEW MODEL");
+      throw new IOException(
+              MessageManager
+                      .getString("exception.outofmemory_loading_mmcif_file"));
+    }
+  }
+
+  private List<Atom> convertSignificantAtoms(ModelSet ms)
+  {
+    List<Atom> significantAtoms = new ArrayList<Atom>();
+    HashMap<String, org.jmol.modelset.Atom> chainTerMap = new HashMap<String, org.jmol.modelset.Atom>();
+    org.jmol.modelset.Atom prevAtom = null;
+    for (org.jmol.modelset.Atom atom : ms.at)
+    {
+      if (atom.getAtomName().equalsIgnoreCase("CA")
+              || atom.getAtomName().equalsIgnoreCase("P"))
+      {
+        if (!atomValidated(atom, prevAtom, chainTerMap))
+        {
+          continue;
+        }
+        Atom curAtom = new Atom(atom.x, atom.y, atom.z);
+        curAtom.atomIndex = atom.getIndex();
+        curAtom.chain = atom.getChainIDStr();
+        curAtom.insCode = atom.group.getInsertionCode() == '\000' ? ' '
+                : atom.group.getInsertionCode();
+        curAtom.name = atom.getAtomName();
+        curAtom.number = atom.getAtomNumber();
+        curAtom.resName = atom.getGroup3(true);
+        curAtom.resNumber = atom.getResno();
+        curAtom.occupancy = ms.occupancies != null ? ms.occupancies[atom
+                .getIndex()] : Float.valueOf(atom.getOccupancy100());
+        String fmt = new Format("%4i").form(curAtom.resNumber);
+        curAtom.resNumIns = (fmt + curAtom.insCode);
+        curAtom.tfactor = atom.getBfactor100() / 100f;
+        curAtom.type = 0;
+        // significantAtoms.add(curAtom);
+        // ignore atoms from subsequent models
+        if (!significantAtoms.contains(curAtom))
+        {
+          significantAtoms.add(curAtom);
+        }
+        prevAtom = atom;
+      }
+    }
+    return significantAtoms;
+  }
+
+  private boolean atomValidated(org.jmol.modelset.Atom curAtom,
+          org.jmol.modelset.Atom prevAtom,
+          HashMap<String, org.jmol.modelset.Atom> chainTerMap)
+  {
+    // System.out.println("Atom: " + curAtom.getAtomNumber()
+    // + "   Last atom index " + curAtom.group.lastAtomIndex);
+    if (chainTerMap == null || prevAtom == null)
+    {
+      return true;
+    }
+    String curAtomChId = curAtom.getChainIDStr();
+    String prevAtomChId = prevAtom.getChainIDStr();
+    // new chain encoutered
+    if (!prevAtomChId.equals(curAtomChId))
+    {
+      // On chain switch add previous chain termination to xTerMap if not exists
+      if (!chainTerMap.containsKey(prevAtomChId))
+      {
+        chainTerMap.put(prevAtomChId, prevAtom);
+      }
+      // if current atom belongs to an already terminated chain and the resNum
+      // diff < 5 then mark as valid and update termination Atom
+      if (chainTerMap.containsKey(curAtomChId))
+      {
+        if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
+        {
+          return false;
+        }
+        if ((curAtom.getResno() - chainTerMap.get(curAtomChId).getResno()) < 5)
+        {
+          chainTerMap.put(curAtomChId, curAtom);
+          return true;
+        }
+        return false;
+      }
+    }
+    // atom with previously terminated chain encountered
+    else if (chainTerMap.containsKey(curAtomChId))
+    {
+      if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
+      {
+        return false;
+      }
+      if ((curAtom.getResno() - chainTerMap.get(curAtomChId).getResno()) < 5)
+      {
+        chainTerMap.put(curAtomChId, curAtom);
+        return true;
+      }
+      return false;
+    }
+    // HETATM with resNum jump > 2
+    return !(curAtom.isHetero() && ((curAtom.getResno() - prevAtom
+            .getResno()) > 2));
+  }
+
+  private void createAnnotation(SequenceI sequence, PDBChain chain,
+          org.jmol.modelset.Atom[] jmolAtoms)
+  {
+    char[] secstr = new char[sequence.getLength()];
+    char[] secstrcode = new char[sequence.getLength()];
+
+    // Ensure Residue size equals Seq size
+    if (chain.residues.size() != sequence.getLength())
+    {
+      return;
+    }
+    int annotIndex = 0;
+    for (Residue residue : chain.residues)
+    {
+      Atom repAtom = residue.getAtoms().get(0);
+      STR proteinStructureSubType = jmolAtoms[repAtom.atomIndex].group
+              .getProteinStructureSubType();
+      setSecondaryStructure(proteinStructureSubType, annotIndex, secstr,
+              secstrcode);
+      ++annotIndex;
+    }
+    addSecondaryStructureAnnotation(chain.pdbid, sequence, secstr,
+            secstrcode, chain.id, sequence.getStart());
+  }
+
+  /**
+   * Helper method that adds an AlignmentAnnotation for secondary structure to
+   * the sequence, provided at least one secondary structure prediction has been
+   * made
+   * 
+   * @param modelTitle
+   * @param seq
+   * @param secstr
+   * @param secstrcode
+   * @param chainId
+   * @param firstResNum
+   * @return
+   */
+  protected void addSecondaryStructureAnnotation(String modelTitle,
+          SequenceI sq, char[] secstr, char[] secstrcode, String chainId,
+          int firstResNum)
+  {
+    char[] seq = sq.getSequence();
+    boolean ssFound = false;
+    Annotation asecstr[] = new Annotation[seq.length + firstResNum - 1];
+    for (int p = 0; p < seq.length; p++)
+    {
+      if (secstr[p] >= 'A' && secstr[p] <= 'z')
+      {
+        try
+        {
+          asecstr[p] = new Annotation(String.valueOf(secstr[p]), null,
+                  secstrcode[p], Float.NaN);
+          ssFound = true;
+        } catch (Exception e)
+        {
+          // e.printStackTrace();
+        }
+      }
+    }
+
+    if (ssFound)
+    {
+      String mt = modelTitle == null ? getDataName() : modelTitle;
+      mt += chainId;
+      AlignmentAnnotation ann = new AlignmentAnnotation(
+              "Secondary Structure", "Secondary Structure for " + mt,
+              asecstr);
+      ann.belowAlignment = true;
+      ann.visible = true;
+      ann.autoCalculated = false;
+      ann.setCalcId(getClass().getName());
+      ann.adjustForAlignment();
+      ann.validateRangeAndDisplay();
+      annotations.add(ann);
+      sq.addAlignmentAnnotation(ann);
+    }
+  }
+
+  private void waitForScript(Viewer jmd)
+  {
+    while (jmd.isScriptExecuting())
+    {
+      try
+      {
+        Thread.sleep(50);
+
+      } catch (InterruptedException x)
+      {
+      }
+    }
+  }
+
+  /**
+   * Convert Jmol's secondary structure code to Jalview's, and stored it in the
+   * secondary structure arrays at the given sequence position
+   * 
+   * @param proteinStructureSubType
+   * @param pos
+   * @param secstr
+   * @param secstrcode
+   */
+  protected void setSecondaryStructure(STR proteinStructureSubType,
+          int pos, char[] secstr, char[] secstrcode)
+  {
+    switch (proteinStructureSubType)
+    {
+    case HELIX310:
+      secstr[pos] = '3';
+      break;
+    case HELIX:
+    case HELIXALPHA:
+      secstr[pos] = 'H';
+      break;
+    case HELIXPI:
+      secstr[pos] = 'P';
+      break;
+    case SHEET:
+      secstr[pos] = 'E';
+      break;
+    default:
+      secstr[pos] = 0;
+    }
+
+    switch (proteinStructureSubType)
+    {
+    case HELIX310:
+    case HELIXALPHA:
+    case HELIXPI:
+    case HELIX:
+      secstrcode[pos] = 'H';
+      break;
+    case SHEET:
+      secstrcode[pos] = 'E';
+      break;
+    default:
+      secstrcode[pos] = 0;
+    }
+  }
+
+  /**
+   * Convert any non-standard peptide codes to their standard code table
+   * equivalent. (Initial version only does Selenomethionine MSE->MET.)
+   * 
+   * @param threeLetterCode
+   * @param seq
+   * @param pos
+   */
+  protected void replaceNonCanonicalResidue(String threeLetterCode,
+          char[] seq, int pos)
+  {
+    String canonical = ResidueProperties
+            .getCanonicalAminoAcid(threeLetterCode);
+    if (canonical != null && !canonical.equalsIgnoreCase(threeLetterCode))
+    {
+      seq[pos] = ResidueProperties.getSingleCharacterCode(canonical);
+    }
+  }
+
+  /**
+   * Not implemented - returns null
+   */
+  @Override
+  public String print()
+  {
+    return null;
+  }
+
+  /**
+   * Not implemented
+   */
+  @Override
+  public void setCallbackFunction(String callbackType,
+          String callbackFunction)
+  {
+  }
+
+  @Override
+  public void notifyCallback(CBK cbType, Object[] data)
+  {
+    String strInfo = (data == null || data[1] == null ? null : data[1]
+            .toString());
+    switch (cbType)
+    {
+    case ECHO:
+      sendConsoleEcho(strInfo);
+      break;
+    case SCRIPT:
+      notifyScriptTermination((String) data[2],
+              ((Integer) data[3]).intValue());
+      break;
+    case MEASURE:
+      String mystatus = (String) data[3];
+      if (mystatus.indexOf("Picked") >= 0
+              || mystatus.indexOf("Sequence") >= 0)
+      {
+        // Picking mode
+        sendConsoleMessage(strInfo);
+      }
+      else if (mystatus.indexOf("Completed") >= 0)
+      {
+        sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
+                strInfo.length() - 1));
+      }
+      break;
+    case MESSAGE:
+      sendConsoleMessage(data == null ? null : strInfo);
+      break;
+    case PICK:
+      sendConsoleMessage(strInfo);
+      break;
+    default:
+      break;
+    }
+  }
+
+  String lastConsoleEcho = "";
+
+  private void sendConsoleEcho(String string)
+  {
+    lastConsoleEcho += string;
+    lastConsoleEcho += "\n";
+  }
+
+  String lastConsoleMessage = "";
+
+  private void sendConsoleMessage(String string)
+  {
+    lastConsoleMessage += string;
+    lastConsoleMessage += "\n";
+  }
+
+  int lastScriptTermination = -1;
+
+  String lastScriptMessage = "";
+
+  private void notifyScriptTermination(String string, int intValue)
+  {
+    lastScriptMessage += string;
+    lastScriptMessage += "\n";
+    lastScriptTermination = intValue;
+  }
+
+  @Override
+  public boolean notifyEnabled(CBK callbackPick)
+  {
+    switch (callbackPick)
+    {
+    case MESSAGE:
+    case SCRIPT:
+    case ECHO:
+    case LOADSTRUCT:
+    case ERROR:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  /**
+   * Not implemented - returns null
+   */
+  @Override
+  public String eval(String strEval)
+  {
+    return null;
+  }
+
+  /**
+   * Not implemented - returns null
+   */
+  @Override
+  public float[][] functionXY(String functionName, int x, int y)
+  {
+    return null;
+  }
+
+  /**
+   * Not implemented - returns null
+   */
+  @Override
+  public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
+  {
+    return null;
+  }
+
+  /**
+   * Not implemented - returns null
+   */
+  @Override
+  public String createImage(String fileName, String imageType,
+          Object text_or_bytes, int quality)
+  {
+    return null;
+  }
+
+  /**
+   * Not implemented - returns null
+   */
+  @Override
+  public Map<String, Object> getRegistryInfo()
+  {
+    return null;
+  }
+
+  /**
+   * Not implemented
+   */
+  @Override
+  public void showUrl(String url)
+  {
+  }
+
+  /**
+   * Not implemented - returns null
+   */
+  @Override
+  public int[] resizeInnerPanel(String data)
+  {
+    return null;
+  }
+
+  @Override
+  public Map<String, Object> getJSpecViewProperty(String arg0)
+  {
+    return null;
+  }
+
+  public boolean isPredictSecondaryStructure()
+  {
+    return predictSecondaryStructure;
+  }
+
+  public void setPredictSecondaryStructure(boolean predictSecondaryStructure)
+  {
+    this.predictSecondaryStructure = predictSecondaryStructure;
+  }
+
+  public boolean isVisibleChainAnnotation()
+  {
+    return visibleChainAnnotation;
+  }
+
+  public void setVisibleChainAnnotation(boolean visibleChainAnnotation)
+  {
+    this.visibleChainAnnotation = visibleChainAnnotation;
+  }
+
+}
diff --git a/src/jalview/ext/jmol/PDBFileWithJmol.java b/src/jalview/ext/jmol/PDBFileWithJmol.java
deleted file mode 100644
index 69cb8bd..0000000
--- a/src/jalview/ext/jmol/PDBFileWithJmol.java
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.ext.jmol;
-
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.Annotation;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceI;
-import jalview.io.AlignFile;
-import jalview.io.FileParse;
-import jalview.schemes.ResidueProperties;
-import jalview.util.MessageManager;
-
-import java.io.IOException;
-import java.util.Hashtable;
-import java.util.Map;
-
-import javajs.awt.Dimension;
-
-import org.jmol.api.JmolStatusListener;
-import org.jmol.api.JmolViewer;
-import org.jmol.c.CBK;
-import org.jmol.modelset.Group;
-import org.jmol.modelset.Model;
-import org.jmol.modelset.ModelSet;
-import org.jmol.modelsetbio.BioModel;
-import org.jmol.modelsetbio.BioPolymer;
-import org.jmol.viewer.Viewer;
-
-/**
- * Import and process PDB files with Jmol
- * 
- * @author jprocter
- * 
- */
-public class PDBFileWithJmol extends AlignFile implements
-        JmolStatusListener
-{
-  Viewer viewer = null;
-
-  public PDBFileWithJmol(String inFile, String type) throws IOException
-  {
-    super(inFile, type);
-  }
-
-  public PDBFileWithJmol(FileParse fp) throws IOException
-  {
-    super(fp);
-  }
-
-  public PDBFileWithJmol()
-  {
-  }
-
-  /**
-   * create a headless jmol instance for dataprocessing
-   * 
-   * @return
-   */
-  private Viewer getJmolData()
-  {
-    if (viewer == null)
-    {
-      try
-      {
-        viewer = (Viewer) JmolViewer.allocateViewer(null, null, null, null,
-                null, "-x -o -n", this);
-      } catch (ClassCastException x)
-      {
-        throw new Error(MessageManager.formatMessage(
-                "error.jmol_version_not_compatible_with_jalview_version",
-                new String[] { JmolViewer.getJmolVersion() }), x);
-      }
-    }
-    return viewer;
-  }
-
-  private void waitForScript(Viewer jmd)
-  {
-    while (jmd.isScriptExecuting())
-    {
-      try
-      {
-        Thread.sleep(50);
-
-      } catch (InterruptedException x)
-      {
-      }
-    }
-  }
-
-  /*
-   * (non-Javadoc)
-   * 
-   * @see jalview.io.AlignFile#parse()
-   */
-  @Override
-  public void parse() throws IOException
-  {
-    Viewer jmd = getJmolData();
-    jmd.openReader(getDataName(), getDataName(), getReader());
-    waitForScript(jmd);
-
-    if (jmd.ms.mc > 0)
-    {
-      ModelSet ms = jmd.ms;
-      // Jmol 14.2 added third argument doReport = false
-      ms.calculateStructures(null, true, false, false, true);
-      // System.out.println("Structs\n"+structs);
-      Group group = null;
-      int modelIndex = -1;
-      for (Model model : ms.am)
-      {
-        modelIndex++;
-        for (BioPolymer bp : ((BioModel) model).bioPolymers)
-        {
-          int lastChainId = 0; // int value of character e.g. 65 for A
-          String lastChainIdAlpha = "";
-
-          int[] groups = bp.getLeadAtomIndices();
-          char seq[] = new char[groups.length], secstr[] = new char[groups.length], secstrcode[] = new char[groups.length];
-          int groupc = 0, len = 0, firstrnum = 1, lastrnum = 0;
-
-          do
-          {
-            if (groupc >= groups.length
-                    || ms.at[groups[groupc]].group.chain.chainID != lastChainId)
-            {
-              /*
-               * on change of chain (or at end), construct the sequence and
-               * secondary structure annotation for the last chain
-               */
-              if (len > 0)
-              {
-                boolean isNa = bp.isNucleic();
-                // normalise sequence from Jmol to jalview
-                int[] cinds = isNa ? ResidueProperties.nucleotideIndex
-                        : ResidueProperties.aaIndex;
-                int nonGap = isNa ? ResidueProperties.maxNucleotideIndex
-                        : ResidueProperties.maxProteinIndex;
-                char ngc = 'X';
-                char newseq[] = new char[len];
-                Annotation asecstr[] = new Annotation[len + firstrnum - 1];
-                for (int p = 0; p < len; p++)
-                {
-                  newseq[p] = cinds[seq[p]] == nonGap ? ngc : seq[p];
-                  if (secstr[p] >= 'A' && secstr[p] <= 'z')
-                  {
-                    try
-                    {
-                      asecstr[p] = new Annotation("" + secstr[p], null,
-                              secstrcode[p], Float.NaN);
-                    } catch (ArrayIndexOutOfBoundsException e)
-                    {
-                      // skip - patch for JAL-1836
-                    }
-                  }
-                }
-                String modelTitle = (String) ms
-                        .getInfo(modelIndex, "title");
-                SequenceI sq = new Sequence("" + getDataName() + "|"
-                        + modelTitle + "|" + lastChainIdAlpha, newseq,
-                        firstrnum, lastrnum);
-                PDBEntry pdbe = new PDBEntry();
-                pdbe.setFile(getDataName());
-                pdbe.setId(getDataName());
-                pdbe.setProperty(new Hashtable());
-                // pdbe.getProperty().put("CHAIN", "" + _lastChainId);
-                pdbe.setChainCode(lastChainIdAlpha);
-                sq.addPDBId(pdbe);
-                // JAL-1533
-                // Need to put the number of models for this polymer somewhere
-                // for Chimera/others to grab
-                // pdbe.getProperty().put("PDBMODELS", biopoly.)
-                seqs.add(sq);
-                if (!isNa)
-                {
-                  String mt = modelTitle == null ? getDataName()
-                          : modelTitle;
-                  if (lastChainId >= ' ')
-                  {
-                    mt += lastChainIdAlpha;
-                  }
-                  AlignmentAnnotation ann = new AlignmentAnnotation(
-                          "Secondary Structure", "Secondary Structure for "
-                                  + mt, asecstr);
-                  ann.belowAlignment = true;
-                  ann.visible = true;
-                  ann.autoCalculated = false;
-                  ann.setCalcId(getClass().getName());
-                  sq.addAlignmentAnnotation(ann);
-                  ann.adjustForAlignment();
-                  ann.validateRangeAndDisplay();
-                  annotations.add(ann);
-                }
-              }
-              len = 0;
-              firstrnum = 1;
-              lastrnum = 0;
-            }
-            if (groupc < groups.length)
-            {
-              group = ms.at[groups[groupc]].group;
-              if (len == 0)
-              {
-                firstrnum = group.getResno();
-                lastChainId = group.chain.chainID;
-                lastChainIdAlpha = group.chain.getIDStr();
-              }
-              else
-              {
-                lastrnum = group.getResno();
-              }
-              seq[len] = group.getGroup1();
-
-              /*
-               * JAL-1828 replace a modified amino acid with its standard
-               * equivalent (e.g. MSE with MET->M) to maximise sequence matching
-               */
-              String threeLetterCode = group.getGroup3();
-              String canonical = ResidueProperties
-                      .getCanonicalAminoAcid(threeLetterCode);
-              if (canonical != null
-                      && !canonical.equalsIgnoreCase(threeLetterCode))
-              {
-                seq[len] = ResidueProperties
-                        .getSingleCharacterCode(canonical);
-              }
-              switch (group.getProteinStructureSubType())
-              {
-              case HELIX310:
-                if (secstr[len] == 0)
-                {
-                  secstr[len] = '3';
-                }
-              case HELIXALPHA:
-                if (secstr[len] == 0)
-                {
-                  secstr[len] = 'H';
-                }
-              case HELIXPI:
-                if (secstr[len] == 0)
-                {
-                  secstr[len] = 'P';
-                }
-              case HELIX:
-                if (secstr[len] == 0)
-                {
-                  secstr[len] = 'H';
-                }
-                secstrcode[len] = 'H';
-                break;
-              case SHEET:
-                secstr[len] = 'E';
-                secstrcode[len] = 'E';
-                break;
-              default:
-                secstr[len] = 0;
-                secstrcode[len] = 0;
-              }
-              len++;
-            }
-          } while (groupc++ < groups.length);
-        }
-      }
-
-      /*
-       * lastScriptTermination = -9465; String dsspOut =
-       * jmd.evalString("calculate STRUCTURE"); if (dsspOut.equals("pending")) {
-       * while (lastScriptTermination == -9465) { try { Thread.sleep(50); }
-       * catch (Exception x) { } ; } } System.out.println(lastConsoleEcho);
-       */
-    }
-  }
-
-  /*
-   * (non-Javadoc)
-   * 
-   * @see jalview.io.AlignFile#print()
-   */
-  @Override
-  public String print()
-  {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  @Override
-  public void setCallbackFunction(String callbackType,
-          String callbackFunction)
-  {
-    // TODO Auto-generated method stub
-
-  }
-
-  /*
-   * @Override public void notifyCallback(EnumCallback type, Object[] data) {
-   * try { switch (type) { case ERROR: case SCRIPT:
-   * notifyScriptTermination((String) data[2], ((Integer) data[3]).intValue());
-   * break; case MESSAGE: sendConsoleMessage((data == null) ? ((String) null) :
-   * (String) data[1]); break; case LOADSTRUCT: notifyFileLoaded((String)
-   * data[1], (String) data[2], (String) data[3], (String) data[4], ((Integer)
-   * data[5]).intValue());
-   * 
-   * break; default: // System.err.println("Unhandled callback " + type + " " //
-   * + data[1].toString()); break; } } catch (Exception e) {
-   * System.err.println("Squashed Jmol callback handler error:");
-   * e.printStackTrace(); } }
-   */
-  public void notifyCallback(CBK type, Object[] data)
-  {
-    String strInfo = (data == null || data[1] == null ? null : data[1]
-            .toString());
-    switch (type)
-    {
-    case ECHO:
-      sendConsoleEcho(strInfo);
-      break;
-    case SCRIPT:
-      notifyScriptTermination((String) data[2],
-              ((Integer) data[3]).intValue());
-      break;
-    case MEASURE:
-      String mystatus = (String) data[3];
-      if (mystatus.indexOf("Picked") >= 0
-              || mystatus.indexOf("Sequence") >= 0)
-      {
-        // Picking mode
-        sendConsoleMessage(strInfo);
-      }
-      else if (mystatus.indexOf("Completed") >= 0)
-      {
-        sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
-                strInfo.length() - 1));
-      }
-      break;
-    case MESSAGE:
-      sendConsoleMessage(data == null ? null : strInfo);
-      break;
-    case PICK:
-      sendConsoleMessage(strInfo);
-      break;
-    default:
-      break;
-    }
-  }
-
-  String lastConsoleEcho = "";
-
-  private void sendConsoleEcho(String string)
-  {
-    lastConsoleEcho += string;
-    lastConsoleEcho += "\n";
-  }
-
-  String lastConsoleMessage = "";
-
-  private void sendConsoleMessage(String string)
-  {
-    lastConsoleMessage += string;
-    lastConsoleMessage += "\n";
-  }
-
-  int lastScriptTermination = -1;
-
-  String lastScriptMessage = "";
-
-  private void notifyScriptTermination(String string, int intValue)
-  {
-    lastScriptMessage += string;
-    lastScriptMessage += "\n";
-    lastScriptTermination = intValue;
-  }
-
-  @Override
-  public boolean notifyEnabled(CBK callbackPick)
-  {
-    switch (callbackPick)
-    {
-    case MESSAGE:
-    case SCRIPT:
-    case ECHO:
-    case LOADSTRUCT:
-    case ERROR:
-      return true;
-    default:
-      return false;
-    }
-  }
-
-  @Override
-  public String eval(String strEval)
-  {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  @Override
-  public float[][] functionXY(String functionName, int x, int y)
-  {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  @Override
-  public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
-  {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  @Override
-  public String createImage(String fileName, String type,
-          Object text_or_bytes, int quality)
-  {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  @Override
-  public Map<String, Object> getRegistryInfo()
-  {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  @Override
-  public void showUrl(String url)
-  {
-    // TODO Auto-generated method stub
-
-  }
-
-  @Override
-  public Dimension resizeInnerPanel(String data)
-  {
-    return null;
-  }
-
-  @Override
-  public Map<String, Object> getJSpecViewProperty(String arg0)
-  {
-    return null;
-  }
-
-}
diff --git a/src/jalview/ext/paradise/Annotate3D.java b/src/jalview/ext/paradise/Annotate3D.java
index 885945e..9f796b1 100644
--- a/src/jalview/ext/paradise/Annotate3D.java
+++ b/src/jalview/ext/paradise/Annotate3D.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -45,13 +45,20 @@ import org.json.simple.parser.ParseException;
  * 
  * @author jimp
  * 
- *         History: v1.0 revised from original due to refactoring of
- *         paradise-ubmc.u-strasbg.fr/webservices/annotate3d to
- *         http://arn-ibmc.in2p3.fr/api/compute/2d?tool=rnaview
+ * @version v1.0 revised from original due to refactoring of
+ *          paradise-ubmc.u-strasbg.fr/webservices/annotate3d to
+ *          http://arn-ibmc.in2p3.fr/api/compute/2d?tool=rnaview <br/>
+ *          See also testing URL from fjossinet:<br/>
+ *          http://charn2-ibmc.u-strasbg.fr:8080/api/compute/2d <br/>
+ *          If in doubt, check against the REST client at:
+ *          https://github.com/fjossinet/RNA-Science
+ *          -Toolbox/blob/master/pyrna/restclient.py
  */
 public class Annotate3D
 {
-  private static String twoDtoolsURL = "http://arn-ibmc.in2p3.fr/api/compute/2d";
+  // also test with
+  // "http://charn2-ibmc.u-strasbg.fr:8080/api/compute/2d";
+  private static String twoDtoolsURL = "http://arn-ibmc.in2p3.fr/api/compute/2d?tool=rnaview";
 
   private static ContentHandler createContentHandler()
   {
diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java
index a529502..180f198 100644
--- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java
+++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -232,7 +232,7 @@ public class ChimeraCommands
               addColourRange(colourMap, lastColour, pdbfnum, startPos,
                       lastPos, lastChain);
             }
-            break;
+            // break;
           }
         }
       }
diff --git a/src/jalview/ext/rbvi/chimera/ChimeraListener.java b/src/jalview/ext/rbvi/chimera/ChimeraListener.java
index 979955a..32401d2 100644
--- a/src/jalview/ext/rbvi/chimera/ChimeraListener.java
+++ b/src/jalview/ext/rbvi/chimera/ChimeraListener.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
index 415fc1b..e1aac16 100644
--- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
+++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -40,6 +40,7 @@ import jalview.util.MessageManager;
 import java.awt.Color;
 import java.net.BindException;
 import java.util.ArrayList;
+import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -63,6 +64,9 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
   private static final String ALPHACARBON = "CA";
 
+  private List<String> chainNames = new ArrayList<String>();
+
+  private Hashtable<String, String> chainFile = new Hashtable<String, String>();
   /*
    * Object through which we talk to Chimera
    */
@@ -101,16 +105,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
   private String lastCommand;
 
-  private boolean loadedInline;
-
-  /**
-   * current set of model filenames loaded
-   */
-  String[] modelFileNames = null;
-
-  String lastMousedOverAtomSpec;
-
-  private List<String> lastReply;
+  String lastHighlightCommand;
 
   /*
    * incremented every time a load notification is successfully handled -
@@ -200,10 +195,9 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    * @param protocol
    */
   public JalviewChimeraBinding(StructureSelectionManager ssm,
-          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
-          String protocol)
+          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String protocol)
   {
-    super(ssm, pdbentry, sequenceIs, chains, protocol);
+    super(ssm, pdbentry, sequenceIs, protocol);
     viewer = new ChimeraManager(
             new ext.edu.ucsf.rbvi.strucviz2.StructureManager(true));
   }
@@ -253,11 +247,14 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     boolean first = true;
     for (String chain : toshow)
     {
+      int modelNumber = getModelNoForChain(chain);
+      String showChainCmd = modelNumber == -1 ? "" : modelNumber + ":."
+              + chain.split(":")[1];
       if (!first)
       {
         cmd.append(",");
       }
-      cmd.append(":.").append(chain);
+      cmd.append(showChainCmd);
       first = false;
     }
 
@@ -266,7 +263,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
      * window, but it looks more helpful not to (easier to relate chains to the
      * whole)
      */
-    final String command = "~display #*; ~ribbon #*; ribbon "
+    final String command = "~display #*; ~ribbon #*; ribbon :"
             + cmd.toString();
     sendChimeraCommand(command, false);
   }
@@ -617,7 +614,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     if (lastCommand == null || !lastCommand.equals(command))
     {
       // trim command or it may never find a match in the replyLog!!
-      lastReply = viewer.sendChimeraCommand(command.trim(), logResponse);
+      List<String> lastReply = viewer.sendChimeraCommand(command.trim(),
+              logResponse);
       if (logResponse && debug)
       {
         log("Response from command ('" + command + "') was:\n" + lastReply);
@@ -715,17 +713,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   // End StructureListener
   // //////////////////////////
 
-  public Color getColour(int atomIndex, int pdbResNum, String chain,
-          String pdbfile)
-  {
-    if (getModelNum(pdbfile) < 0)
-    {
-      return null;
-    }
-    log("get model / residue colour attribute unimplemented");
-    return null;
-  }
-
   /**
    * returns the current featureRenderer that should be used to colour the
    * structures
@@ -777,33 +764,12 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     {
       return new String[0];
     }
-    // if (modelFileNames == null)
-    // {
-    // Collection<ChimeraModel> chimodels = viewer.getChimeraModels();
-    // _modelFileNameMap = new int[chimodels.size()];
-    // int j = 0;
-    // for (ChimeraModel chimodel : chimodels)
-    // {
-    // String mdlName = chimodel.getModelName();
-    // }
-    // modelFileNames = new String[j];
-    // // System.arraycopy(mset, 0, modelFileNames, 0, j);
-    // }
 
     return chimeraMaps.keySet().toArray(
             modelFileNames = new String[chimeraMaps.size()]);
   }
 
   /**
-   * map from string to applet
-   */
-  public Map getRegistryInfo()
-  {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  /**
    * returns the current sequenceRenderer that should be used to colour the
    * structures
    * 
@@ -815,22 +781,22 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
           AlignmentViewPanel alignment);
 
   /**
-   * Construct and send a command to highlight zero, one or more atoms.
-   * 
-   * <pre>
-   * Done by generating a command like (to 'highlight' position 44)
-   *   show #0:44.C
-   * </pre>
+   * Construct and send a command to highlight zero, one or more atoms. We do
+   * this by sending an "rlabel" command to show the residue label at that
+   * position.
    */
   @Override
   public void highlightAtoms(List<AtomSpec> atoms)
   {
-    if (atoms == null)
+    if (atoms == null || atoms.size() == 0)
     {
       return;
     }
-    StringBuilder atomSpecs = new StringBuilder();
+
+    StringBuilder cmd = new StringBuilder(128);
     boolean first = true;
+    boolean found = false;
+
     for (AtomSpec atom : atoms)
     {
       int pdbResNum = atom.getPdbResNum();
@@ -839,39 +805,46 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
       List<ChimeraModel> cms = chimeraMaps.get(pdbfile);
       if (cms != null && !cms.isEmpty())
       {
-        /*
-         * Formatting as #0:34.A,#1:33.A doesn't work as desired, so instead we
-         * concatenate multiple 'show' commands
-         */
-        atomSpecs.append(first ? "" : ";show ");
+        if (first)
+        {
+          cmd.append("rlabel #").append(cms.get(0).getModelNumber())
+                  .append(":");
+        }
+        else
+        {
+          cmd.append(",");
+        }
         first = false;
-        atomSpecs.append("#" + cms.get(0).getModelNumber());
-        atomSpecs.append(":" + pdbResNum);
+        cmd.append(pdbResNum);
         if (!chain.equals(" "))
         {
-          atomSpecs.append("." + chain);
+          cmd.append(".").append(chain);
         }
+        found = true;
       }
     }
-    String atomSpec = atomSpecs.toString();
+    String command = cmd.toString();
 
     /*
-     * Avoid repeated commands for the same residue
+     * avoid repeated commands for the same residue
      */
-    if (atomSpec.equals(lastMousedOverAtomSpec))
+    if (command.equals(lastHighlightCommand))
     {
       return;
     }
 
-    StringBuilder command = new StringBuilder(32);
-    viewerCommandHistory(false);
-    if (atomSpec.length() > 0)
+    /*
+     * unshow the label for the previous residue
+     */
+    if (lastHighlightCommand != null)
     {
-      command.append("show ").append(atomSpec);
-      viewer.sendChimeraCommand(command.toString(), false);
+      viewer.sendChimeraCommand("~" + lastHighlightCommand, false);
     }
-    viewerCommandHistory(true);
-    this.lastMousedOverAtomSpec = atomSpec;
+    if (found)
+    {
+      viewer.sendChimeraCommand(command, false);
+    }
+    this.lastHighlightCommand = command;
   }
 
   /**
@@ -994,6 +967,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    */
   public abstract void refreshGUI();
 
+  @Override
   public void setLoadingFromArchive(boolean loadingFromArchive)
   {
     this.loadingFromArchive = loadingFromArchive;
@@ -1004,6 +978,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    * @return true if Chimeral is still restoring state or loading is still going
    *         on (see setFinsihedLoadingFromArchive)
    */
+  @Override
   public boolean isLoadingFromArchive()
   {
     return loadingFromArchive && !loadingFinished;
@@ -1015,6 +990,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    * 
    * @param finishedLoading
    */
+  @Override
   public void setFinishedLoadingFromArchive(boolean finishedLoading)
   {
     loadingFinished = finishedLoading;
@@ -1085,34 +1061,67 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
    * 
    * @return
    */
-  public List<String> getChainNames()
+
+  /**
+   * Send a 'focus' command to Chimera to recentre the visible display
+   */
+  public void focusView()
+  {
+    sendChimeraCommand("focus", false);
+  }
+
+  /**
+   * Send a 'show' command for all atoms in the currently selected columns
+   * 
+   * TODO: pull up to abstract structure viewer interface
+   * 
+   * @param vp
+   */
+  public void highlightSelection(AlignmentViewPanel vp)
   {
-    List<String> names = new ArrayList<String>();
-    String[][] allNames = getChains();
-    if (allNames != null)
+    List<Integer> cols = vp.getAlignViewport().getColumnSelection()
+            .getSelected();
+    AlignmentI alignment = vp.getAlignment();
+    StructureSelectionManager sm = getSsm();
+    for (SequenceI seq : alignment.getSequences())
     {
-      for (String[] chainsForPdb : allNames)
+      /*
+       * convert selected columns into sequence positions
+       */
+      int[] positions = new int[cols.size()];
+      int i = 0;
+      for (Integer col : cols)
       {
-        if (chainsForPdb != null)
-        {
-          for (String chain : chainsForPdb)
-          {
-            if (chain != null && !names.contains(chain))
-            {
-              names.add(chain);
-            }
-          }
-        }
+        positions[i++] = seq.findPosition(col);
       }
+      sm.highlightStructure(this, seq, positions);
     }
-    return names;
   }
 
-  /**
-   * Send a 'focus' command to Chimera to recentre the visible display
-   */
-  public void focusView()
+
+  @Override
+  public List<String> getChainNames()
   {
-    sendChimeraCommand("focus", false);
+    return chainNames;
+  }
+
+  public Hashtable<String, String> getChainFile()
+  {
+    return chainFile;
+  }
+
+  public List<ChimeraModel> getChimeraModelByChain(String chain)
+  {
+    return chimeraMaps.get(chainFile.get(chain));
+  }
+
+  public int getModelNoForChain(String chain)
+  {
+    List<ChimeraModel> foundModels = getChimeraModelByChain(chain);
+    if (foundModels != null && !foundModels.isEmpty())
+    {
+      return foundModels.get(0).getModelNumber();
+    }
+    return -1;
   }
 }
diff --git a/src/jalview/ext/so/SequenceOntology.java b/src/jalview/ext/so/SequenceOntology.java
new file mode 100644
index 0000000..41d78b4
--- /dev/null
+++ b/src/jalview/ext/so/SequenceOntology.java
@@ -0,0 +1,474 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ext.so;
+
+import jalview.io.gff.SequenceOntologyI;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.biojava.nbio.ontology.Ontology;
+import org.biojava.nbio.ontology.Term;
+import org.biojava.nbio.ontology.Term.Impl;
+import org.biojava.nbio.ontology.Triple;
+import org.biojava.nbio.ontology.io.OboParser;
+import org.biojava.nbio.ontology.utils.Annotation;
+
+/**
+ * A wrapper class that parses the Sequence Ontology and exposes useful access
+ * methods. This version uses the BioJava parser.
+ */
+public class SequenceOntology implements SequenceOntologyI
+{
+  /*
+   * the parsed Ontology data as modelled by BioJava
+   */
+  private Ontology ontology;
+
+  /*
+   * the ontology term for the isA relationship
+   */
+  private Term isA;
+
+  /*
+   * lookup of terms by user readable name (NB not guaranteed unique)
+   */
+  private Map<String, Term> termsByDescription;
+
+  /*
+   * Map where key is a Term and value is a (possibly empty) list of 
+   * all Terms to which the key has an 'isA' relationship, either
+   * directly or indirectly (A isA B isA C)
+   */
+  private Map<Term, List<Term>> termIsA;
+
+  private List<String> termsFound;
+
+  private List<String> termsNotFound;
+
+  /**
+   * Package private constructor to enforce use of singleton. Parses and caches
+   * the SO OBO data file.
+   */
+  public SequenceOntology()
+  {
+    termsFound = new ArrayList<String>();
+    termsNotFound = new ArrayList<String>();
+    termsByDescription = new HashMap<String, Term>();
+    termIsA = new HashMap<Term, List<Term>>();
+
+    loadOntologyZipFile("so-xp-simple.obo");
+  }
+
+  /**
+   * Loads the given ontology file from a zip file with ".zip" appended
+   * 
+   * @param ontologyFile
+   */
+  protected void loadOntologyZipFile(String ontologyFile)
+  {
+    long now = System.currentTimeMillis();
+    ZipInputStream zipStream = null;
+    try
+    {
+      String zipFile = ontologyFile + ".zip";
+      InputStream inStream = this.getClass().getResourceAsStream(
+              "/" + zipFile);
+      zipStream = new ZipInputStream(new BufferedInputStream(inStream));
+      ZipEntry entry;
+      while ((entry = zipStream.getNextEntry()) != null)
+      {
+        if (entry.getName().equals(ontologyFile))
+        {
+          loadOboFile(zipStream);
+        }
+      }
+      long elapsed = System.currentTimeMillis() - now;
+      System.out.println("Loaded Sequence Ontology from " + zipFile + " ("
+              + elapsed + "ms)");
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    } finally
+    {
+      closeStream(zipStream);
+    }
+  }
+
+  /**
+   * Closes the input stream, swallowing all exceptions
+   * 
+   * @param is
+   */
+  protected void closeStream(InputStream is)
+  {
+    if (is != null)
+    {
+      try
+      {
+        is.close();
+      } catch (IOException e)
+      {
+        // ignore
+      }
+    }
+  }
+
+  /**
+   * Reads, parses and stores the OBO file data
+   * 
+   * @param is
+   * @throws ParseException
+   * @throws IOException
+   */
+  protected void loadOboFile(InputStream is) throws ParseException,
+          IOException
+  {
+    BufferedReader oboFile = new BufferedReader(new InputStreamReader(is));
+    OboParser parser = new OboParser();
+    ontology = parser.parseOBO(oboFile, "SO", "the SO ontology");
+    isA = ontology.getTerm("is_a");
+    storeTermNames();
+  }
+
+  /**
+   * Stores a lookup table of terms by description. Note that description is not
+   * guaranteed unique. Where duplicate descriptions are found, try to discard
+   * the term that is flagged as obsolete. However we do store obsolete terms
+   * where there is no duplication of description.
+   */
+  protected void storeTermNames()
+  {
+    for (Term term : ontology.getTerms())
+    {
+      if (term instanceof Impl)
+      {
+        String description = term.getDescription();
+        if (description != null)
+        {
+          Term replaced = termsByDescription.get(description);
+          if (replaced != null)
+          {
+            boolean newTermIsObsolete = isObsolete(term);
+            boolean oldTermIsObsolete = isObsolete(replaced);
+            if (newTermIsObsolete && !oldTermIsObsolete)
+            {
+              System.err.println("Ignoring " + term.getName()
+                      + " as obsolete and duplicated by "
+                      + replaced.getName());
+              term = replaced;
+            }
+            else if (!newTermIsObsolete && oldTermIsObsolete)
+            {
+              System.err.println("Ignoring " + replaced.getName()
+                      + " as obsolete and duplicated by " + term.getName());
+            }
+            else
+            {
+              System.err.println("Warning: " + term.getName()
+                      + " has replaced " + replaced.getName()
+                      + " for lookup of '" + description + "'");
+            }
+          }
+          termsByDescription.put(description, term);
+        }
+      }
+    }
+  }
+
+  /**
+   * Answers true if the term has property "is_obsolete" with value true, else
+   * false
+   * 
+   * @param term
+   * @return
+   */
+  public static boolean isObsolete(Term term)
+  {
+    Annotation ann = term.getAnnotation();
+    if (ann != null)
+    {
+      try
+      {
+        if (Boolean.TRUE.equals(ann.getProperty("is_obsolete")))
+        {
+          return true;
+        }
+      } catch (NoSuchElementException e)
+      {
+        // fall through to false
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Test whether the given Sequence Ontology term is nucleotide_match (either
+   * directly or via is_a relationship)
+   * 
+   * @param soTerm
+   *          SO name or description
+   * @return
+   */
+  public boolean isNucleotideMatch(String soTerm)
+  {
+    return isA(soTerm, NUCLEOTIDE_MATCH);
+  }
+
+  /**
+   * Test whether the given Sequence Ontology term is protein_match (either
+   * directly or via is_a relationship)
+   * 
+   * @param soTerm
+   *          SO name or description
+   * @return
+   */
+  public boolean isProteinMatch(String soTerm)
+  {
+    return isA(soTerm, PROTEIN_MATCH);
+  }
+
+  /**
+   * Test whether the given Sequence Ontology term is polypeptide (either
+   * directly or via is_a relationship)
+   * 
+   * @param soTerm
+   *          SO name or description
+   * @return
+   */
+  public boolean isPolypeptide(String soTerm)
+  {
+    return isA(soTerm, POLYPEPTIDE);
+  }
+
+  /**
+   * Returns true if the given term has a (direct or indirect) 'isA'
+   * relationship with the parent
+   * 
+   * @param child
+   * @param parent
+   * @return
+   */
+  @Override
+  public boolean isA(String child, String parent)
+  {
+    if (child == null || parent == null)
+    {
+      return false;
+    }
+    /*
+     * optimise trivial checks like isA("CDS", "CDS")
+     */
+    if (child.equals(parent))
+    {
+      termFound(child);
+      return true;
+    }
+
+    Term childTerm = getTerm(child);
+    if (childTerm != null)
+    {
+      termFound(child);
+    }
+    else
+    {
+      termNotFound(child);
+    }
+    Term parentTerm = getTerm(parent);
+
+    return termIsA(childTerm, parentTerm);
+  }
+
+  /**
+   * Records a valid term queried for, for reporting purposes
+   * 
+   * @param term
+   */
+  private void termFound(String term)
+  {
+    synchronized (termsFound)
+    {
+      if (!termsFound.contains(term))
+      {
+        termsFound.add(term);
+      }
+    }
+  }
+
+  /**
+   * Records an invalid term queried for, for reporting purposes
+   * 
+   * @param term
+   */
+  private void termNotFound(String term)
+  {
+    synchronized (termsNotFound)
+    {
+      if (!termsNotFound.contains(term))
+      {
+        System.err.println("SO term " + term + " invalid");
+        termsNotFound.add(term);
+      }
+    }
+  }
+
+  /**
+   * Returns true if the childTerm 'isA' parentTerm (directly or indirectly).
+   * 
+   * @param childTerm
+   * @param parentTerm
+   * @return
+   */
+  protected synchronized boolean termIsA(Term childTerm, Term parentTerm)
+  {
+    /*
+     * null term could arise from a misspelled SO description
+     */
+    if (childTerm == null || parentTerm == null)
+    {
+      return false;
+    }
+
+    /*
+     * recursive search endpoint:
+     */
+    if (childTerm == parentTerm)
+    {
+      return true;
+    }
+
+    /*
+     * lazy initialisation - find all of a term's parents (recursively) 
+     * the first time this is called, and save them in a map.
+     */
+    if (!termIsA.containsKey(childTerm))
+    {
+      findParents(childTerm);
+    }
+
+    List<Term> parents = termIsA.get(childTerm);
+    for (Term parent : parents)
+    {
+      if (termIsA(parent, parentTerm))
+      {
+        /*
+         * add (great-)grandparents to parents list as they are discovered,
+         * for faster lookup next time
+         */
+        if (!parents.contains(parentTerm))
+        {
+          parents.add(parentTerm);
+        }
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Finds all the 'isA' parents of the childTerm and stores them as a (possibly
+   * empty) list.
+   * 
+   * @param childTerm
+   */
+  protected synchronized void findParents(Term childTerm)
+  {
+    List<Term> result = new ArrayList<Term>();
+    for (Triple triple : ontology.getTriples(childTerm, null, isA))
+    {
+      Term parent = triple.getObject();
+      result.add(parent);
+
+      /*
+       * and search for the parent's parents recursively
+       */
+      findParents(parent);
+    }
+    termIsA.put(childTerm, result);
+  }
+
+  /**
+   * Returns the Term for a given name (e.g. "SO:0000735") or description (e.g.
+   * "sequence_location"), or null if not found.
+   * 
+   * @param child
+   * @return
+   */
+  protected Term getTerm(String nameOrDescription)
+  {
+    Term t = termsByDescription.get(nameOrDescription);
+    if (t == null)
+    {
+      try
+      {
+        t = ontology.getTerm(nameOrDescription);
+      } catch (NoSuchElementException e)
+      {
+        // not found
+      }
+    }
+    return t;
+  }
+
+  public boolean isSequenceVariant(String soTerm)
+  {
+    return isA(soTerm, SEQUENCE_VARIANT);
+  }
+
+  /**
+   * Sorts (case-insensitive) and returns the list of valid terms queried for
+   */
+  @Override
+  public List<String> termsFound()
+  {
+    synchronized (termsFound)
+    {
+      Collections.sort(termsFound, String.CASE_INSENSITIVE_ORDER);
+      return termsFound;
+    }
+  }
+
+  /**
+   * Sorts (case-insensitive) and returns the list of invalid terms queried for
+   */
+  @Override
+  public List<String> termsNotFound()
+  {
+    synchronized (termsNotFound)
+    {
+      Collections.sort(termsNotFound, String.CASE_INSENSITIVE_ORDER);
+      return termsNotFound;
+    }
+  }
+}
diff --git a/src/jalview/ext/varna/JalviewVarnaBinding.java b/src/jalview/ext/varna/JalviewVarnaBinding.java
index 3bec35a..ba6cb63 100644
--- a/src/jalview/ext/varna/JalviewVarnaBinding.java
+++ b/src/jalview/ext/varna/JalviewVarnaBinding.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ext/varna/RnaModel.java b/src/jalview/ext/varna/RnaModel.java
index 2a8de6e..f2d6eab 100644
--- a/src/jalview/ext/varna/RnaModel.java
+++ b/src/jalview/ext/varna/RnaModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ext/varna/VarnaCommands.java b/src/jalview/ext/varna/VarnaCommands.java
index f2a2b63..67a15f2 100644
--- a/src/jalview/ext/varna/VarnaCommands.java
+++ b/src/jalview/ext/varna/VarnaCommands.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/ArgumentI.java b/src/jalview/fts/api/FTSData.java
similarity index 62%
copy from src/jalview/ws/params/ArgumentI.java
copy to src/jalview/fts/api/FTSData.java
index 8d57d0a..7abcd7c 100644
--- a/src/jalview/ws/params/ArgumentI.java
+++ b/src/jalview/fts/api/FTSData.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,27 +18,29 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.ws.params;
 
-public interface ArgumentI
+package jalview.fts.api;
+
+/**
+ * This interface provides a model for the summary data;
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public interface FTSData
 {
-  /**
-   * 
-   * @return name for this argument
-   */
-  String getName();
 
   /**
+   * Return an array of Objects representing the retrieved FTS data
    * 
-   * @return current value for the argument (may equal the name)
+   * @return
    */
-  String getValue();
+  public Object[] getSummaryData();
 
   /**
-   * set the current value for the argument.
+   * The primary key object for the retrieved FTS data
    * 
-   * @param selectedItem
+   * @return
    */
-  void setValue(String selectedItem);
-
+  public Object getPrimaryKey();
 }
diff --git a/src/jalview/fts/api/FTSDataColumnI.java b/src/jalview/fts/api/FTSDataColumnI.java
new file mode 100644
index 0000000..e5ce145
--- /dev/null
+++ b/src/jalview/fts/api/FTSDataColumnI.java
@@ -0,0 +1,161 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.fts.api;
+
+/**
+ * This interface provides a model for the dynamic data column configuration
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public interface FTSDataColumnI
+{
+  /**
+   * Returns the name of the data column
+   * 
+   * @return the data column's name
+   */
+  public String getName();
+
+  /**
+   * Returns the code of the data column
+   * 
+   * @return the data column's code
+   */
+  public String getCode();
+
+  /**
+   * Returns the alternative code value for the data column
+   * 
+   * @return the data column's code
+   */
+  public String getAltCode();
+
+  /**
+   * Returns the minimum width of the data column
+   * 
+   * @return the data column's minimum width
+   */
+  public int getMinWidth();
+
+  /**
+   * Returns the maximum width of the data column
+   * 
+   * @return the data column's maximum width
+   */
+  public int getMaxWidth();
+
+  /**
+   * Returns the preferred width of the data column
+   * 
+   * @return the data column's preferred width
+   */
+  public int getPreferredWidth();
+
+  /**
+   * Determines if the data column is the primary key column
+   * 
+   * @return true if data column is the primary key column, otherwise false
+   */
+  public boolean isPrimaryKeyColumn();
+
+  /**
+   * Checks if the data column field can be used to perform a search query
+   * 
+   * @return true means the data column is searchable
+   */
+  public boolean isSearchable();
+
+  /**
+   * Checks if the data column is displayed by default
+   * 
+   * @return true means the data column is shown by default
+   */
+  public boolean isVisibleByDefault();
+
+  /**
+   * Returns the data column's FTS data column group
+   * 
+   * @return the FTSDataColumnGroupI for the column
+   */
+  public FTSDataColumnGroupI getGroup();
+
+  /**
+   * Returns the data columns data type POJO
+   * 
+   * @return the DataTypeI for the column
+   */
+  public DataTypeI getDataType();
+
+  /**
+   * This interface provides a model for the dynamic data column group
+   * 
+   */
+  public interface FTSDataColumnGroupI
+  {
+    /**
+     * Returns the Id of the data column's group
+     * 
+     * @return the data column's group Id
+     */
+    public String getID();
+
+    /**
+     * Returns the name of the group
+     * 
+     * @return the group's name
+     */
+    public String getName();
+
+    /**
+     * Returns the sort order of the group
+     * 
+     * @return the group's sort order
+     */
+    public int getSortOrder();
+  }
+
+  public interface DataTypeI
+  {
+    /**
+     * Returns the data column's data type class
+     * 
+     * @return the Class for the data column's data type
+     */
+    public Class getDataTypeClass();
+
+    /**
+     * Checks if the numeric data column's data will be formated
+     * 
+     * @return true means the numeric data column shall be formatted
+     */
+    public boolean isFormtted();
+
+    /**
+     * Returns the number of significant figure to be used for the numeric value
+     * formatting
+     * 
+     * @return the number of significant figures
+     */
+    public int getSignificantFigures();
+  }
+}
diff --git a/src/jalview/fts/api/FTSRestClientI.java b/src/jalview/fts/api/FTSRestClientI.java
new file mode 100644
index 0000000..9b4ff2f
--- /dev/null
+++ b/src/jalview/fts/api/FTSRestClientI.java
@@ -0,0 +1,139 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.fts.api;
+
+import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+
+import java.util.Collection;
+
+/**
+ * Methods for FTS Rest client.
+ * 
+ * @author tcnofoegbu
+ */
+public interface FTSRestClientI
+{
+
+  /**
+   * Execute a given FTS request, process the response and return it as an
+   * FTSRestResponse object
+   * 
+   * @param ftsRestRequest
+   *          the FTS request to execute
+   * @return FTSRestResponse - the response after executing an FTS request
+   * @throws Exception
+   */
+  public FTSRestResponse executeRequest(FTSRestRequest ftsRequest)
+          throws Exception;
+
+  /**
+   * Return the resource file path for the data columns configuration file
+   * 
+   * @return
+   */
+  public String getColumnDataConfigFileName();
+
+  /**
+   * Fetch FTSDataColumnGroupI by the group's Id
+   * 
+   * @param groupId
+   * @return FTSDataColumnGroupI
+   * @throws Exception
+   */
+  public FTSDataColumnGroupI getDataColumnGroupById(String groupId)
+          throws Exception;
+
+  /**
+   * Fetch FTSDataColumnI by name or code
+   * 
+   * @param nameOrCode
+   * @return FTSDataColumnI
+   * @throws Exception
+   */
+  public FTSDataColumnI getDataColumnByNameOrCode(String nameOrCode)
+          throws Exception;
+
+  /**
+   * Convert collection of FTSDataColumnI objects to a comma delimited string of
+   * the 'code' values
+   * 
+   * @param wantedFields
+   *          the collection of FTSDataColumnI to process
+   * @return the generated comma delimited string from the supplied
+   *         FTSDataColumnI collection
+   */
+  public String getDataColumnsFieldsAsCommaDelimitedString(
+          Collection<FTSDataColumnI> wantedFields);
+
+  /**
+   * Fetch index of the primary key column for the dynamic table
+   * 
+   * @param wantedFields
+   *          the available table columns
+   * @param hasRefSeq
+   *          true if the data columns has an additional column for reference
+   *          sequence
+   * @return index of the primary key column
+   * @throws Exception
+   */
+  public int getPrimaryKeyColumIndex(
+          Collection<FTSDataColumnI> wantedFields, boolean hasRefSeq)
+          throws Exception;
+
+  /**
+   * Fetch the primary key data column object
+   * 
+   * @return the FTSDataColumnI object for the primary key column
+   */
+  public FTSDataColumnI getPrimaryKeyColumn();
+
+  /**
+   * Returns list of FTSDataColumnI objects to be displayed by default
+   * 
+   * @return list of columns to display by default
+   */
+  public Collection<FTSDataColumnI> getAllDefaultDisplayedFTSDataColumns();
+
+  /**
+   * Return list of FTSDataColumnI objects that can be used to perform a search
+   * query
+   * 
+   * @return list of searchable FTSDataColumnI object
+   */
+  public Collection<FTSDataColumnI> getSearchableDataColumns();
+
+  /**
+   * Return list of all available FTSDataColumnI object
+   * 
+   * @return list of all FTSColumnI objcet
+   */
+  public Collection<FTSDataColumnI> getAllFTSDataColumns();
+
+  /**
+   * Return the default response page limit
+   * 
+   * @return the default response page size
+   */
+  public int getDefaultResponsePageSize();
+}
diff --git a/src/jalview/fts/api/GFTSPanelI.java b/src/jalview/fts/api/GFTSPanelI.java
new file mode 100644
index 0000000..c7ed1a9
--- /dev/null
+++ b/src/jalview/fts/api/GFTSPanelI.java
@@ -0,0 +1,140 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.fts.api;
+
+import java.util.Map;
+
+import javax.swing.JTable;
+
+/**
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public interface GFTSPanelI
+{
+
+  /**
+   * Action performed when a text is entered in the search field.
+   * 
+   * @param isFreshSearch
+   *          if true a fresh search is executed else a pagination search is
+   *          executed
+   */
+  public void searchAction(boolean isFreshSearch);
+
+  /**
+   * Action performed when search results are selected and the 'ok' button is
+   * pressed.
+   */
+  public void okAction();
+
+  /**
+   * Return the entered text
+   * 
+   * @return the entered text
+   */
+  public String getTypedText();
+
+  /**
+   * The JTable for presenting the query result
+   * 
+   * @return JTable
+   */
+  public JTable getResultTable();
+
+  /**
+   * Return the title to display on the search interface main panel
+   * 
+   * @return String - the title
+   */
+  public String getFTSFrameTitle();
+
+  /**
+   * Return a singleton instance of FTSRestClientI
+   * 
+   * @return FTSRestClientI
+   */
+  public FTSRestClientI getFTSRestClient();
+
+  /**
+   * Set error message when one occurs
+   * 
+   * @param message
+   *          the error message to set
+   */
+  public void setErrorMessage(String message);
+
+  /**
+   * Updates the title displayed on the search interface's main panel
+   * 
+   * @param newTitle
+   */
+  public void updateSearchFrameTitle(String newTitle);
+
+  /**
+   * Controls the progress spinner, set to 'true' while search operation is in
+   * progress and 'false' after it completes
+   * 
+   * @param isSearchInProgress
+   */
+  public void setSearchInProgress(Boolean isSearchInProgress);
+
+  /**
+   * Action performed when previous page (<<) button is pressed pressed.
+   */
+  public void prevPageAction();
+
+  /**
+   * Action performed when next page (>>) button is pressed pressed.
+   */
+  public void nextPageAction();
+
+  /**
+   * Checks if the current service's search result is paginate-able
+   * 
+   * @return true means the service provides paginated results
+   */
+  public boolean isPaginationEnabled();
+
+  /**
+   * Updates the 'enabled' state for the previous page button
+   * 
+   * @param isEnabled
+   */
+  public void setPrevPageButtonEnabled(boolean isEnabled);
+
+  /**
+   * Updates the 'enabled' state for the next page button
+   * 
+   * @param isEnabled
+   */
+  public void setNextPageButtonEnabled(boolean isEnabled);
+
+  /**
+   * The HashMap used to store user preferences for summary table columns,
+   * window size and position
+   * 
+   * @return
+   */
+  public Map<String, Integer> getTempUserPrefs();
+}
diff --git a/src/jalview/fts/core/DecimalFormatTableCellRenderer.java b/src/jalview/fts/core/DecimalFormatTableCellRenderer.java
new file mode 100644
index 0000000..d11984b
--- /dev/null
+++ b/src/jalview/fts/core/DecimalFormatTableCellRenderer.java
@@ -0,0 +1,80 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.fts.core;
+
+import java.awt.Component;
+import java.text.DecimalFormat;
+
+import javax.swing.JLabel;
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableCellRenderer;
+
+/**
+ * The class to handle the formatting of the double values for JTable cells.
+ */
+public class DecimalFormatTableCellRenderer extends
+        DefaultTableCellRenderer
+{
+  private DecimalFormat formatter;
+
+  public DecimalFormatTableCellRenderer(boolean isFormated,
+          int significantFigures)
+  {
+    String integerFormater = isFormated ? "###,##0" : "0";
+    String fractionFormater = isFormated ? "###,##0." : "0.";
+    if (significantFigures > 0)
+    {
+      StringBuilder significantFigureBuilder = new StringBuilder();
+      for (int x = 1; x <= significantFigures; ++x)
+      {
+        significantFigureBuilder.append("0");
+      }
+      formatter = new DecimalFormat(fractionFormater
+              + significantFigureBuilder.toString());
+    }
+    else
+    {
+      formatter = new DecimalFormat(integerFormater);
+    }
+    super.setHorizontalAlignment(JLabel.RIGHT);
+  }
+
+  public DecimalFormatTableCellRenderer()
+  {
+    super.setHorizontalAlignment(JLabel.RIGHT);
+  }
+
+  @Override
+  public Component getTableCellRendererComponent(JTable table,
+          Object value, boolean isSelected, boolean hasFocus, int row,
+          int column)
+  {
+    if (value == null)
+    {
+      return null;
+    }
+
+    value = formatter.format(value);
+
+    return super.getTableCellRendererComponent(table, value, isSelected,
+            hasFocus, row, column);
+  }
+}
diff --git a/src/jalview/fts/core/FTSDataColumnPreferences.java b/src/jalview/fts/core/FTSDataColumnPreferences.java
new file mode 100644
index 0000000..27e535c
--- /dev/null
+++ b/src/jalview/fts/core/FTSDataColumnPreferences.java
@@ -0,0 +1,338 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.fts.core;
+
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.service.pdb.PDBFTSRestClient;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.RowSorter;
+import javax.swing.SortOrder;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableModel;
+import javax.swing.table.TableRowSorter;
+
+ at SuppressWarnings("serial")
+public class FTSDataColumnPreferences extends JScrollPane
+{
+  protected JTable tbl_FTSDataColumnPrefs = new JTable();
+
+  protected JScrollPane scrl_pdbDocFieldConfig = new JScrollPane(
+          tbl_FTSDataColumnPrefs);
+
+  private HashMap<String, FTSDataColumnI> map = new HashMap<String, FTSDataColumnI>();
+
+  private Collection<FTSDataColumnI> structSummaryColumns = new LinkedHashSet<FTSDataColumnI>();
+
+  private Collection<FTSDataColumnI> allFTSDataColumns = new LinkedHashSet<FTSDataColumnI>();
+
+  public enum PreferenceSource
+  {
+    SEARCH_SUMMARY, STRUCTURE_CHOOSER, PREFERENCES;
+  }
+
+  private PreferenceSource currentSource;
+
+  private FTSRestClientI ftsRestClient;
+
+  public FTSDataColumnPreferences(PreferenceSource source,
+          FTSRestClientI ftsRestClient)
+  {
+    this.ftsRestClient = ftsRestClient;
+    if (source.equals(PreferenceSource.STRUCTURE_CHOOSER)
+            || source.equals(PreferenceSource.PREFERENCES))
+    {
+      structSummaryColumns = ((PDBFTSRestClient) ftsRestClient)
+              .getAllDefaultDisplayedStructureDataColumns();
+    }
+    allFTSDataColumns.addAll(ftsRestClient.getAllFTSDataColumns());
+
+    tbl_FTSDataColumnPrefs.setAutoCreateRowSorter(true);
+    this.getViewport().add(tbl_FTSDataColumnPrefs);
+    this.currentSource = source;
+
+    String[] columnNames = null;
+    switch (source)
+    {
+    case SEARCH_SUMMARY:
+      columnNames = new String[] { "", "Display", "Group" };
+      break;
+    case STRUCTURE_CHOOSER:
+      columnNames = new String[] { "", "Display", "Group" };
+      break;
+    case PREFERENCES:
+      columnNames = new String[] { "PDB Field", "Show in search summary",
+          "Show in structure summary" };
+      break;
+    default:
+      break;
+    }
+
+    Object[][] data = new Object[allFTSDataColumns.size() - 1][3];
+
+    int x = 0;
+    for (FTSDataColumnI field : allFTSDataColumns)
+    {
+      if (field.getName().equalsIgnoreCase("all"))
+      {
+        continue;
+      }
+
+      switch (source)
+      {
+      case SEARCH_SUMMARY:
+        data[x++] = new Object[] {
+            ftsRestClient.getAllDefaultDisplayedFTSDataColumns().contains(
+                    field), field.getName(), field.getGroup() };
+        break;
+      case STRUCTURE_CHOOSER:
+        data[x++] = new Object[] { structSummaryColumns.contains(field),
+            field.getName(), field.getGroup() };
+        break;
+      case PREFERENCES:
+        data[x++] = new Object[] {
+            field.getName(),
+            ftsRestClient.getAllDefaultDisplayedFTSDataColumns().contains(
+                    field), structSummaryColumns.contains(field) };
+        break;
+      default:
+        break;
+      }
+      map.put(field.getName(), field);
+    }
+
+    FTSDataColumnPrefsTableModel model = new FTSDataColumnPrefsTableModel(
+            columnNames, data);
+    tbl_FTSDataColumnPrefs.setModel(model);
+
+    switch (source)
+    {
+    case SEARCH_SUMMARY:
+    case STRUCTURE_CHOOSER:
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(0)
+              .setPreferredWidth(30);
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(0).setMinWidth(20);
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(0).setMaxWidth(40);
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(1)
+              .setPreferredWidth(150);
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(1).setMinWidth(150);
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(2)
+              .setPreferredWidth(150);
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(2).setMinWidth(150);
+
+      TableRowSorter<TableModel> sorter = new TableRowSorter<>(
+              tbl_FTSDataColumnPrefs.getModel());
+      tbl_FTSDataColumnPrefs.setRowSorter(sorter);
+      List<RowSorter.SortKey> sortKeys = new ArrayList<>();
+      int columnIndexToSort = 2;
+      sortKeys.add(new RowSorter.SortKey(columnIndexToSort,
+              SortOrder.ASCENDING));
+      sorter.setSortKeys(sortKeys);
+      sorter.setComparator(columnIndexToSort,
+              new Comparator<FTSDataColumnGroupI>()
+              {
+                @Override
+                public int compare(FTSDataColumnGroupI o1,
+                        FTSDataColumnGroupI o2)
+                {
+                  return o1.getSortOrder() - o2.getSortOrder();
+                }
+              });
+      sorter.sort();
+
+      tbl_FTSDataColumnPrefs
+              .setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
+      break;
+    case PREFERENCES:
+    default:
+      break;
+    }
+
+  }
+
+  public Collection<FTSDataColumnI> getStructureSummaryFields()
+  {
+    return structSummaryColumns;
+  }
+
+  class FTSDataColumnPrefsTableModel extends AbstractTableModel
+  {
+
+    public FTSDataColumnPrefsTableModel(String[] columnNames,
+            Object[][] data)
+    {
+      this.data = data;
+      this.columnNames = columnNames;
+    }
+
+    private Object[][] data;
+
+    private String[] columnNames;
+
+    @Override
+    public int getColumnCount()
+    {
+      return columnNames.length;
+    }
+
+    @Override
+    public int getRowCount()
+    {
+      return data.length;
+    }
+
+    @Override
+    public String getColumnName(int col)
+    {
+      return columnNames[col];
+    }
+
+    @Override
+    public Object getValueAt(int row, int col)
+    {
+      return data[row][col];
+    }
+
+    /*
+     * JTable uses this method to determine the default renderer/ editor for
+     * each cell. If we didn't implement this method, then the last column would
+     * contain text ("true"/"false"), rather than a check box.
+     */
+    @Override
+    public Class getColumnClass(int c)
+    {
+      return getValueAt(0, c).getClass();
+    }
+
+    /*
+     * Don't need to implement this method unless your table's editable.
+     */
+    @Override
+    public boolean isCellEditable(int row, int col)
+    {
+      // Note that the data/cell address is constant,
+      // no matter where the cell appears onscreen.
+      // !isPDBID(row, col) ensures the PDB_Id cell is never editable as it
+      // serves as a unique id for each row.
+      // return (col == 1 || col == 2) && !isPDBID(row, col);
+      switch (currentSource)
+      {
+      case SEARCH_SUMMARY:
+      case STRUCTURE_CHOOSER:
+        return (col == 0) && !isPrimaryKeyCell(row, 1);
+      case PREFERENCES:
+        return (col == 1 || col == 2) && !isPrimaryKeyCell(row, 0);
+      default:
+        return false;
+      }
+
+    }
+
+    /**
+     * Determines whether the data in a given cell is a PDB ID.
+     * 
+     * @param row
+     * @param col
+     * @return
+     */
+
+    public boolean isPrimaryKeyCell(int row, int col)
+    {
+      String name = getValueAt(row, col).toString();
+      FTSDataColumnI pdbField = map.get(name);
+      return pdbField.isPrimaryKeyColumn();
+    }
+
+    /*
+     * Don't need to implement this method unless your table's data can change.
+     */
+    @Override
+    public void setValueAt(Object value, int row, int col)
+    {
+      data[row][col] = value;
+      fireTableCellUpdated(row, col);
+
+      String name = null;
+      switch (currentSource)
+      {
+      case SEARCH_SUMMARY:
+      case STRUCTURE_CHOOSER:
+        name = getValueAt(row, 1).toString();
+        break;
+      case PREFERENCES:
+        name = getValueAt(row, 0).toString();
+        break;
+      default:
+        break;
+      }
+      boolean selected = ((Boolean) value).booleanValue();
+
+      FTSDataColumnI ftsDataColumn = map.get(name);
+
+      if (currentSource == PreferenceSource.SEARCH_SUMMARY)
+      {
+        updatePrefs(ftsRestClient.getAllDefaultDisplayedFTSDataColumns(),
+                ftsDataColumn, selected);
+      }
+      else if (currentSource == PreferenceSource.STRUCTURE_CHOOSER)
+      {
+        updatePrefs(structSummaryColumns, ftsDataColumn, selected);
+      }
+      else if (currentSource == PreferenceSource.PREFERENCES)
+      {
+        if (col == 1)
+        {
+          updatePrefs(ftsRestClient.getAllDefaultDisplayedFTSDataColumns(),
+                  ftsDataColumn, selected);
+        }
+        else if (col == 2)
+        {
+          updatePrefs(structSummaryColumns, ftsDataColumn, selected);
+        }
+      }
+    }
+
+    private void updatePrefs(Collection<FTSDataColumnI> prefConfig,
+            FTSDataColumnI dataColumn, boolean selected)
+    {
+      if (prefConfig.contains(dataColumn) && !selected)
+      {
+        prefConfig.remove(dataColumn);
+      }
+
+      if (!prefConfig.contains(dataColumn) && selected)
+      {
+        prefConfig.add(dataColumn);
+      }
+    }
+
+  }
+}
diff --git a/src/jalview/fts/core/FTSRestClient.java b/src/jalview/fts/core/FTSRestClient.java
new file mode 100644
index 0000000..3ec5748
--- /dev/null
+++ b/src/jalview/fts/core/FTSRestClient.java
@@ -0,0 +1,506 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.fts.core;
+
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.util.MessageManager;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ * Base class providing implementation for common methods defined in
+ * FTSRestClientI
+ * 
+ * @author tcnofoegbu
+ * 
+ * @note implementations MUST be accessed as a singleton.
+ */
+public abstract class FTSRestClient implements FTSRestClientI
+{
+  protected Collection<FTSDataColumnI> dataColumns = new ArrayList<FTSDataColumnI>();
+
+  protected Collection<FTSDataColumnGroupI> dataColumnGroups = new ArrayList<FTSDataColumnGroupI>();
+
+  protected Collection<FTSDataColumnI> searchableDataColumns = new ArrayList<FTSDataColumnI>();
+
+  protected Collection<FTSDataColumnI> defaulDisplayedDataColumns = new ArrayList<FTSDataColumnI>();
+
+  protected FTSDataColumnI primaryKeyColumn;
+
+  private String primaryKeyColumnCode = null;
+
+  private int defaultResponsePageSize = 100;
+
+  protected FTSRestClient()
+  {
+
+  }
+
+  public void parseDataColumnsConfigFile()
+  {
+    String fileName = getColumnDataConfigFileName();
+
+    InputStream in = getClass().getResourceAsStream(fileName);
+
+    try (BufferedReader br = new BufferedReader(new InputStreamReader(in)))
+    {
+      String line;
+      while ((line = br.readLine()) != null)
+      {
+        final String[] lineData = line.split(";");
+        try
+        {
+          if (lineData.length == 2)
+          {
+            if (lineData[0].equalsIgnoreCase("_data_column.primary_key"))
+            {
+              primaryKeyColumnCode = lineData[1];
+            }
+            if (lineData[0]
+                    .equalsIgnoreCase("_data_column.default_response_page_size"))
+            {
+              defaultResponsePageSize = Integer.valueOf(lineData[1]);
+            }
+          }
+          else if (lineData.length == 3)
+          {
+            dataColumnGroups.add(new FTSDataColumnGroupI()
+            {
+              @Override
+              public String getID()
+              {
+                return lineData[0];
+              }
+
+              @Override
+              public String getName()
+              {
+                return lineData[1];
+              }
+
+              @Override
+              public int getSortOrder()
+              {
+                return Integer.valueOf(lineData[2]);
+              }
+
+              @Override
+              public String toString()
+              {
+                return lineData[1];
+              }
+
+              @Override
+              public int hashCode()
+              {
+                return Objects.hash(this.getID(), this.getName(),
+                        this.getSortOrder());
+              }
+
+              @Override
+              public boolean equals(Object otherObject)
+              {
+                FTSDataColumnGroupI that = (FTSDataColumnGroupI) otherObject;
+                return this.getID().equals(that.getID())
+                        && this.getName().equals(that.getName())
+                        && this.getSortOrder() == that.getSortOrder();
+              }
+            });
+          }
+          else if (lineData.length > 6)
+          {
+            FTSDataColumnI dataCol = new FTSDataColumnI()
+            {
+              @Override
+              public String toString()
+              {
+                return lineData[0];
+              }
+
+              @Override
+              public String getName()
+              {
+                return lineData[0];
+              }
+
+              @Override
+              public String getCode()
+              {
+                return lineData[1].split("\\|")[0];
+              }
+
+              @Override
+              public String getAltCode()
+              {
+                return lineData[1].split("\\|").length > 1 ? lineData[1]
+                        .split("\\|")[1] : getCode();
+              }
+
+              @Override
+              public DataTypeI getDataType()
+              {
+                final String[] dataTypeString = lineData[2].split("\\|");
+                final String classString = dataTypeString[0].toUpperCase();
+
+                return new DataTypeI()
+                {
+
+                  @Override
+                  public boolean isFormtted()
+                  {
+                    if (dataTypeString.length > 1
+                            && dataTypeString[1] != null)
+                    {
+                      switch (dataTypeString[1].toUpperCase())
+                      {
+                      case "T":
+                      case "TRUE":
+                        return true;
+                      case "F":
+                      case "False":
+                      default:
+                        return false;
+                      }
+                    }
+                    return false;
+                  }
+
+                  @Override
+                  public int getSignificantFigures()
+                  {
+                    if (dataTypeString.length > 2
+                            && dataTypeString[2] != null)
+                    {
+                      return Integer.valueOf(dataTypeString[2]);
+                    }
+                    return 0;
+                  }
+
+                  @Override
+                  public Class getDataTypeClass()
+                  {
+                    switch (classString)
+                    {
+                    case "INT":
+                    case "INTEGER":
+                      return Integer.class;
+                    case "DOUBLE":
+                      return Double.class;
+                    case "STRING":
+                    default:
+                      return String.class;
+                    }
+                  }
+                };
+
+              }
+
+              @Override
+              public FTSDataColumnGroupI getGroup()
+              {
+                FTSDataColumnGroupI group = null;
+                try
+                {
+                  group = getDataColumnGroupById(lineData[3]);
+                } catch (Exception e)
+                {
+                  e.printStackTrace();
+                }
+                return group;
+              }
+
+              @Override
+              public int getMinWidth()
+              {
+                return Integer.valueOf(lineData[4]);
+              }
+
+              @Override
+              public int getMaxWidth()
+              {
+                return Integer.valueOf(lineData[5]);
+              }
+
+              @Override
+              public int getPreferredWidth()
+              {
+                return Integer.valueOf(lineData[6]);
+              }
+
+              @Override
+              public boolean isPrimaryKeyColumn()
+              {
+                return getName().equalsIgnoreCase(primaryKeyColumnCode)
+                        || getCode().equalsIgnoreCase(primaryKeyColumnCode);
+              }
+
+              @Override
+              public boolean isVisibleByDefault()
+              {
+                return Boolean.valueOf(lineData[7]);
+              }
+
+              @Override
+              public boolean isSearchable()
+              {
+                return Boolean.valueOf(lineData[8]);
+              }
+
+              @Override
+              public int hashCode()
+              {
+                return Objects.hash(this.getName(), this.getCode(),
+                        this.getGroup());
+              }
+
+              @Override
+              public boolean equals(Object otherObject)
+              {
+                FTSDataColumnI that = (FTSDataColumnI) otherObject;
+                return this.getCode().equals(that.getCode())
+                        && this.getName().equals(that.getName())
+                        && this.getGroup().equals(that.getGroup());
+              }
+
+            };
+            dataColumns.add(dataCol);
+
+            if (dataCol.isSearchable())
+            {
+              searchableDataColumns.add(dataCol);
+            }
+
+            if (dataCol.isVisibleByDefault())
+            {
+              defaulDisplayedDataColumns.add(dataCol);
+            }
+
+          }
+          else
+          {
+            continue;
+          }
+        } catch (Exception e)
+        {
+          e.printStackTrace();
+        }
+      }
+      try
+      {
+        this.primaryKeyColumn = getDataColumnByNameOrCode(primaryKeyColumnCode);
+      } catch (Exception e)
+      {
+        e.printStackTrace();
+      }
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public int getPrimaryKeyColumIndex(
+          Collection<FTSDataColumnI> wantedFields, boolean hasRefSeq)
+          throws Exception
+  {
+
+    // If a reference sequence is attached then start counting from 1 else
+    // start from zero
+    int pdbFieldIndexCounter = hasRefSeq ? 1 : 0;
+
+    for (FTSDataColumnI field : wantedFields)
+    {
+      if (field.isPrimaryKeyColumn())
+      {
+        break; // Once PDB Id index is determined exit iteration
+      }
+      ++pdbFieldIndexCounter;
+    }
+    return pdbFieldIndexCounter;
+  }
+
+  @Override
+  public String getDataColumnsFieldsAsCommaDelimitedString(
+          Collection<FTSDataColumnI> dataColumnFields)
+  {
+    String result = "";
+    if (dataColumnFields != null && !dataColumnFields.isEmpty())
+    {
+      StringBuilder returnedFields = new StringBuilder();
+      for (FTSDataColumnI field : dataColumnFields)
+      {
+        returnedFields.append(",").append(field.getCode());
+      }
+      returnedFields.deleteCharAt(0);
+      result = returnedFields.toString();
+    }
+    return result;
+  }
+
+  @Override
+  public Collection<FTSDataColumnI> getAllFTSDataColumns()
+  {
+    if (dataColumns == null || dataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    return dataColumns;
+  }
+
+  @Override
+  public Collection<FTSDataColumnI> getSearchableDataColumns()
+  {
+    if (searchableDataColumns == null || searchableDataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    return searchableDataColumns;
+  }
+
+  @Override
+  public Collection<FTSDataColumnI> getAllDefaultDisplayedFTSDataColumns()
+  {
+    if (defaulDisplayedDataColumns == null
+            || defaulDisplayedDataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    return defaulDisplayedDataColumns;
+  }
+
+  @Override
+  public FTSDataColumnI getPrimaryKeyColumn()
+  {
+    if (defaulDisplayedDataColumns == null
+            || defaulDisplayedDataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    return primaryKeyColumn;
+  }
+
+  @Override
+  public FTSDataColumnI getDataColumnByNameOrCode(String nameOrCode)
+          throws Exception
+  {
+    if (dataColumns == null || dataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    for (FTSDataColumnI column : dataColumns)
+    {
+      if (column.getName().equalsIgnoreCase(nameOrCode)
+              || column.getCode().equalsIgnoreCase(nameOrCode))
+      {
+        return column;
+      }
+    }
+    throw new Exception("Couldn't find data column with name : "
+            + nameOrCode);
+  }
+
+  @Override
+  public FTSDataColumnGroupI getDataColumnGroupById(String id)
+          throws Exception
+  {
+    if (dataColumns == null || dataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    for (FTSDataColumnGroupI columnGroup : dataColumnGroups)
+    {
+      if (columnGroup.getID().equalsIgnoreCase(id))
+      {
+        return columnGroup;
+      }
+    }
+    throw new Exception("Couldn't find data column group with id : " + id);
+  }
+
+  public String getMessageByHTTPStatusCode(int code, String service)
+  {
+    String message = "";
+    switch (code)
+    {
+    case 400:
+      message = MessageManager.getString("exception.bad_request");
+      break;
+
+    case 410:
+      message = MessageManager.formatMessage(
+              "exception.fts_rest_service_no_longer_available", service);
+      break;
+    case 403:
+    case 404:
+      message = MessageManager.getString("exception.resource_not_be_found");
+      break;
+    case 408:
+    case 409:
+    case 500:
+    case 501:
+    case 502:
+    case 504:
+    case 505:
+      message = MessageManager.formatMessage("exception.fts_server_error",
+              service);
+      break;
+    case 503:
+      message = MessageManager.getString("exception.service_not_available");
+      break;
+    default:
+      break;
+    }
+    return message;
+  }
+
+  protected String getResourceFile(String fileName)
+  {
+    String result = "";
+    try
+    {
+      result = getClass().getResource(fileName).getFile();
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    return result;
+
+  }
+
+  @Override
+  public int getDefaultResponsePageSize()
+  {
+    if (dataColumns == null || dataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    return defaultResponsePageSize;
+  }
+
+}
diff --git a/src/jalview/ws/uimodel/PDBRestRequest.java b/src/jalview/fts/core/FTSRestRequest.java
similarity index 61%
rename from src/jalview/ws/uimodel/PDBRestRequest.java
rename to src/jalview/fts/core/FTSRestRequest.java
index 7826fbd..7656d87 100644
--- a/src/jalview/ws/uimodel/PDBRestRequest.java
+++ b/src/jalview/fts/core/FTSRestRequest.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -19,20 +19,21 @@
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 
-package jalview.ws.uimodel;
+package jalview.fts.core;
 
+import jalview.bin.Cache;
 import jalview.datamodel.SequenceI;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+import jalview.fts.api.FTSDataColumnI;
 
 import java.util.Collection;
 
 /**
- * Represents the PDB request to be consumed by the PDBRestClient
+ * Represents the FTS request to be consumed by the FTSRestClient
  * 
  * @author tcnofoegbu
  *
  */
-public class PDBRestRequest
+public class FTSRestRequest
 {
   private String fieldToSearchBy;
 
@@ -44,12 +45,22 @@ public class PDBRestRequest
 
   private boolean allowEmptySequence;
 
+  private boolean allowUnpublishedEntries = Cache.getDefault(
+          "ALLOW_UNPUBLISHED_PDB_QUERYING", false);
+
+  private boolean facet;
+
+  private String facetPivot;
+
+  private int facetPivotMinCount;
+
   private int responseSize;
 
+  private int offSet;
+
   private boolean isSortAscending;
 
-  private Collection<PDBDocField> wantedFields;// = new
-                                               // Collection<PDBDocField>();
+  private Collection<FTSDataColumnI> wantedFields;
 
   public String getFieldToSearchBy()
   {
@@ -91,12 +102,12 @@ public class PDBRestRequest
     this.responseSize = responseSize;
   }
 
-  public Collection<PDBDocField> getWantedFields()
+  public Collection<FTSDataColumnI> getWantedFields()
   {
     return wantedFields;
   }
 
-  public void setWantedFields(Collection<PDBDocField> wantedFields)
+  public void setWantedFields(Collection<FTSDataColumnI> wantedFields)
   {
     this.wantedFields = wantedFields;
   }
@@ -127,16 +138,53 @@ public class PDBRestRequest
     this.associatedSequence = associatedSequence;
   }
 
-  public String getQuery()
+  public boolean isAllowUnpublishedEntries()
+  {
+    return allowUnpublishedEntries;
+  }
+
+  public void setAllowUnpublishedEntries(boolean allowUnpublishedEntries)
+  {
+    this.allowUnpublishedEntries = allowUnpublishedEntries;
+  }
+
+  public boolean isFacet()
+  {
+    return facet;
+  }
+
+  public void setFacet(boolean facet)
+  {
+    this.facet = facet;
+  }
+
+  public String getFacetPivot()
+  {
+    return facetPivot;
+  }
+
+  public void setFacetPivot(String facetPivot)
+  {
+    this.facetPivot = facetPivot;
+  }
+
+  public int getFacetPivotMinCount()
+  {
+    return facetPivotMinCount;
+  }
+
+  public void setFacetPivotMinCount(int facetPivotMinCount)
+  {
+    this.facetPivotMinCount = facetPivotMinCount;
+  }
+
+  public int getOffSet()
   {
-    return fieldToSearchBy + searchTerm
-            + (isAllowEmptySeq() ? "" : " AND molecule_sequence:['' TO *]");
+    return offSet;
   }
 
-  public String toString()
+  public void setOffSet(int offSet)
   {
-    return "Query : " + getQuery() + " sort field: " + fieldToSortBy
-            + " isAsc: " + isAscending() + " Associated Seq : "
-            + associatedSequence;
+    this.offSet = offSet;
   }
 }
diff --git a/src/jalview/fts/core/FTSRestResponse.java b/src/jalview/fts/core/FTSRestResponse.java
new file mode 100644
index 0000000..669f5de
--- /dev/null
+++ b/src/jalview/fts/core/FTSRestResponse.java
@@ -0,0 +1,174 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.fts.core;
+
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+
+import java.util.Collection;
+import java.util.Map;
+
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableModel;
+
+/**
+ * Represents the response model generated by the FTSRestClient upon successful
+ * execution of a given FTS request
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public class FTSRestResponse
+{
+  private int numberOfItemsFound;
+
+  private String responseTime;
+
+  private Collection<FTSData> searchSummary;
+
+  public int getNumberOfItemsFound()
+  {
+    return numberOfItemsFound;
+  }
+
+  public void setNumberOfItemsFound(int itemFound)
+  {
+    this.numberOfItemsFound = itemFound;
+  }
+
+  public String getResponseTime()
+  {
+    return responseTime;
+  }
+
+  public void setResponseTime(String responseTime)
+  {
+    this.responseTime = responseTime;
+  }
+
+  public Collection<FTSData> getSearchSummary()
+  {
+    return searchSummary;
+  }
+
+  public void setSearchSummary(Collection<FTSData> searchSummary)
+  {
+    this.searchSummary = searchSummary;
+  }
+
+  /**
+   * Convenience method to obtain a Table model for a given summary List based
+   * on the request parameters
+   * 
+   * @param request
+   *          the FTSRestRequest object which holds useful information for
+   *          creating a table model
+   * @param summariesList
+   *          the summary list which contains the data for populating the
+   *          table's rows
+   * @return the table model which was dynamically generated
+   */
+  public static DefaultTableModel getTableModel(FTSRestRequest request,
+          Collection<FTSData> summariesList)
+  {
+    final FTSDataColumnI[] cols = request.getWantedFields().toArray(
+            new FTSDataColumnI[0]);
+    final int colOffset = request.getAssociatedSequence() == null ? 0 : 1;
+    DefaultTableModel tableModel = new DefaultTableModel()
+    {
+      @Override
+      public boolean isCellEditable(int row, int column)
+      {
+        return false;
+      }
+
+      @Override
+      public Class<?> getColumnClass(int columnIndex)
+      {
+        if (colOffset == 1 && columnIndex == 0)
+        {
+          return String.class;
+        }
+        return cols[columnIndex - colOffset].getDataType()
+                .getDataTypeClass();
+      }
+
+    };
+    if (request.getAssociatedSequence() != null)
+    {
+      tableModel.addColumn("Ref Sequence"); // Create sequence column header if
+      // exists in the request
+    }
+    for (FTSDataColumnI field : request.getWantedFields())
+    {
+      tableModel.addColumn(field.getName()); // Create sequence column header if
+                                             // exists in the request
+    }
+
+    for (FTSData res : summariesList)
+    {
+      tableModel.addRow(res.getSummaryData()); // Populate table rows with
+                                               // summary list
+    }
+
+    return tableModel;
+  }
+
+  public static void configureTableColumn(JTable tbl_summary,
+          Collection<FTSDataColumnI> wantedFields,
+          Map<String, Integer> columnPrefs)
+  {
+    for (FTSDataColumnI wantedField : wantedFields)
+    {
+      try
+      {
+        tbl_summary.getColumn(wantedField.getName()).setMinWidth(
+                wantedField.getMinWidth());
+        tbl_summary.getColumn(wantedField.getName()).setMaxWidth(
+                wantedField.getMaxWidth());
+        int prefedWidth = columnPrefs.get(wantedField.getName()) == null ? wantedField
+                .getPreferredWidth() : columnPrefs.get(wantedField
+                .getName());
+        tbl_summary.getColumn(wantedField.getName()).setPreferredWidth(
+                prefedWidth);
+      } catch (Exception e)
+      {
+        e.printStackTrace();
+      }
+      if (wantedField.getDataType().getDataTypeClass() == Double.class)
+      {
+        DecimalFormatTableCellRenderer dfr = new DecimalFormatTableCellRenderer(
+                wantedField.getDataType().isFormtted(), wantedField
+                        .getDataType().getSignificantFigures());
+        tbl_summary.getColumn(wantedField.getName()).setCellRenderer(dfr);
+      }
+      else if (wantedField.getDataType().getDataTypeClass() == Integer.class)
+      {
+        DecimalFormatTableCellRenderer dfr = new DecimalFormatTableCellRenderer(
+                wantedField.getDataType().isFormtted(), wantedField
+                        .getDataType().getSignificantFigures());
+        tbl_summary.getColumn(wantedField.getName()).setCellRenderer(dfr);
+      }
+    }
+  }
+
+}
diff --git a/src/jalview/fts/core/GFTSPanel.java b/src/jalview/fts/core/GFTSPanel.java
new file mode 100644
index 0000000..f2cdf33
--- /dev/null
+++ b/src/jalview/fts/core/GFTSPanel.java
@@ -0,0 +1,992 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.fts.core;
+
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.GFTSPanelI;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.gui.Desktop;
+import jalview.gui.IProgressIndicator;
+import jalview.gui.JvSwingUtils;
+import jalview.gui.SequenceFetcher;
+import jalview.util.MessageManager;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.Timer;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableColumn;
+
+/**
+ * This class provides the swing GUI layout for FTS Panel and implements most of
+ * the contracts defined in GFSPanelI
+ * 
+ * @author tcnofoegbu
+ *
+ */
+
+ at SuppressWarnings("serial")
+public abstract class GFTSPanel extends JPanel implements GFTSPanelI
+{
+  protected JInternalFrame mainFrame = new JInternalFrame(
+          getFTSFrameTitle());
+
+  protected IProgressIndicator progressIndicator;
+
+  protected JComboBox<FTSDataColumnI> cmb_searchTarget = new JComboBox<FTSDataColumnI>();
+
+  protected JButton btn_ok = new JButton();
+
+  protected JButton btn_back = new JButton();
+
+  protected JButton btn_cancel = new JButton();
+
+  protected JTextField txt_search = new JTextField(30);
+
+  protected SequenceFetcher seqFetcher;
+
+  protected Collection<FTSDataColumnI> wantedFields;
+
+  private String lastSearchTerm = "";
+
+  protected JButton btn_next_page = new JButton();
+
+  protected JButton btn_prev_page = new JButton();
+
+  protected StringBuilder errorWarning = new StringBuilder();
+
+  protected ImageIcon warningImage = new ImageIcon(getClass().getResource(
+          "/images/warning.gif"));
+
+  protected ImageIcon loadingImage = new ImageIcon(getClass().getResource(
+          "/images/loading.gif"));
+
+  protected ImageIcon balnkPlaceholderImage = new ImageIcon(getClass()
+          .getResource("/images/blank_16x16_placeholder.png"));
+
+  protected JLabel lbl_warning = new JLabel(warningImage);
+
+  protected JLabel lbl_loading = new JLabel(loadingImage);
+
+  protected JLabel lbl_blank = new JLabel(balnkPlaceholderImage);
+
+  private JTabbedPane tabbedPane = new JTabbedPane();
+
+  private JPanel pnl_actions = new JPanel();
+
+  private JPanel pnl_results = new JPanel(new CardLayout());
+
+  private JPanel pnl_inputs = new JPanel();
+
+  private BorderLayout mainLayout = new BorderLayout();
+
+  protected Object[] previousWantedFields;
+
+  protected int resultSetCount;
+
+  protected int totalResultSetCount;
+
+  protected int offSet;
+
+  protected int pageLimit;
+
+  protected HashSet<String> paginatorCart = new HashSet<String>();
+
+  protected static final DecimalFormat totalNumberformatter = new DecimalFormat(
+          "###,###");
+
+  private JTable tbl_summary = new JTable()
+  {
+    private boolean inLayout;
+
+    @Override
+    public boolean getScrollableTracksViewportWidth()
+    {
+      return hasExcessWidth();
+
+    }
+
+    @Override
+    public void doLayout()
+    {
+      if (hasExcessWidth())
+      {
+        autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
+      }
+      inLayout = true;
+      super.doLayout();
+      inLayout = false;
+      autoResizeMode = AUTO_RESIZE_OFF;
+    }
+
+    protected boolean hasExcessWidth()
+    {
+      return getPreferredSize().width < getParent().getWidth();
+    }
+
+    @Override
+    public void columnMarginChanged(ChangeEvent e)
+    {
+      if (isEditing())
+      {
+        removeEditor();
+      }
+      TableColumn resizingColumn = getTableHeader().getResizingColumn();
+      // Need to do this here, before the parent's
+      // layout manager calls getPreferredSize().
+      if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF
+              && !inLayout)
+      {
+        resizingColumn.setPreferredWidth(resizingColumn.getWidth());
+        String colHeader = resizingColumn.getHeaderValue().toString();
+        getTempUserPrefs().put(colHeader, resizingColumn.getWidth());
+      }
+      resizeAndRepaint();
+    }
+
+    @Override
+    public String getToolTipText(MouseEvent evt)
+    {
+      String toolTipText = null;
+      java.awt.Point pnt = evt.getPoint();
+      int rowIndex = rowAtPoint(pnt);
+      int colIndex = columnAtPoint(pnt);
+
+      try
+      {
+        if (getValueAt(rowIndex, colIndex) == null)
+        {
+          return null;
+        }
+        toolTipText = getValueAt(rowIndex, colIndex).toString();
+
+      } catch (Exception e)
+      {
+        e.printStackTrace();
+      }
+      toolTipText = (toolTipText == null ? null
+              : (toolTipText.length() > 500 ? JvSwingUtils.wrapTooltip(
+                      true, toolTipText.subSequence(0, 500) + "...")
+                      : JvSwingUtils.wrapTooltip(true, toolTipText)));
+
+      return toolTipText;
+    }
+  };
+
+  protected JScrollPane scrl_searchResult = new JScrollPane(tbl_summary);
+
+  public GFTSPanel()
+  {
+    try
+    {
+      jbInit();
+      mainFrame.addFocusListener(new FocusAdapter()
+      {
+        @Override
+        public void focusGained(FocusEvent e)
+        {
+          txt_search.requestFocusInWindow();
+        }
+      });
+      mainFrame.invalidate();
+      mainFrame.pack();
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Initializes the GUI default properties
+   * 
+   * @throws Exception
+   */
+  private void jbInit() throws Exception
+  {
+    Integer width = getTempUserPrefs().get("FTSPanel.width") == null ? 800
+            : getTempUserPrefs().get("FTSPanel.width");
+    Integer height = getTempUserPrefs().get("FTSPanel.height") == null ? 400
+            : getTempUserPrefs().get("FTSPanel.height");
+    lbl_warning.setVisible(false);
+    lbl_warning.setFont(new java.awt.Font("Verdana", 0, 12));
+    lbl_loading.setVisible(false);
+    lbl_loading.setFont(new java.awt.Font("Verdana", 0, 12));
+    lbl_blank.setVisible(true);
+    lbl_blank.setFont(new java.awt.Font("Verdana", 0, 12));
+
+    tbl_summary.setAutoCreateRowSorter(true);
+    tbl_summary.getTableHeader().setReorderingAllowed(false);
+    tbl_summary.addMouseListener(new MouseAdapter()
+    {
+      @Override
+      public void mouseClicked(MouseEvent e)
+      {
+        validateSelection();
+      }
+
+      @Override
+      public void mouseReleased(MouseEvent e)
+      {
+        validateSelection();
+      }
+    });
+    tbl_summary.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        validateSelection();
+        switch (evt.getKeyCode())
+        {
+        case KeyEvent.VK_ESCAPE: // escape key
+          btn_back_ActionPerformed();
+          break;
+        case KeyEvent.VK_ENTER: // enter key
+          if (btn_ok.isEnabled())
+          {
+            okAction();
+          }
+          evt.consume();
+          break;
+        case KeyEvent.VK_TAB: // tab key
+          if (evt.isShiftDown())
+          {
+            tabbedPane.requestFocus();
+          }
+          else
+          {
+            btn_back.requestFocus();
+          }
+          evt.consume();
+          break;
+        default:
+          return;
+        }
+      }
+    });
+
+    btn_back.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_back.setText(MessageManager.getString("action.back"));
+    btn_back.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        btn_back_ActionPerformed();
+      }
+    });
+    btn_back.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          btn_back_ActionPerformed();
+        }
+      }
+    });
+
+    btn_ok.setEnabled(false);
+    btn_ok.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_ok.setText(MessageManager.getString("action.ok"));
+    btn_ok.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        okAction();
+      }
+    });
+    btn_ok.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          okAction();
+        }
+      }
+    });
+    btn_next_page.setEnabled(false);
+    btn_next_page.setToolTipText(MessageManager
+            .getString("label.next_page_tooltip"));
+    btn_next_page.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_next_page.setText(MessageManager.getString("action.next_page"));
+    btn_next_page.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        nextPageAction();
+      }
+    });
+    btn_next_page.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          nextPageAction();
+        }
+      }
+    });
+
+    btn_prev_page.setEnabled(false);
+    btn_prev_page.setToolTipText(MessageManager
+            .getString("label.prev_page_tooltip"));
+    btn_prev_page.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_prev_page.setText(MessageManager.getString("action.prev_page"));
+    btn_prev_page.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        prevPageAction();
+      }
+    });
+    btn_prev_page.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          prevPageAction();
+        }
+      }
+    });
+
+    if (isPaginationEnabled())
+    {
+      btn_prev_page.setVisible(true);
+      btn_next_page.setVisible(true);
+    }
+    else
+    {
+      btn_prev_page.setVisible(false);
+      btn_next_page.setVisible(false);
+    }
+
+    btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_cancel.setText(MessageManager.getString("action.cancel"));
+    btn_cancel.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        btn_cancel_ActionPerformed();
+      }
+    });
+    btn_cancel.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          btn_cancel_ActionPerformed();
+        }
+      }
+    });
+    scrl_searchResult.setPreferredSize(new Dimension(width, height));
+
+    cmb_searchTarget.setFont(new java.awt.Font("Verdana", 0, 12));
+    cmb_searchTarget.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        String tooltipText;
+        if ("all".equalsIgnoreCase(getCmbSearchTarget().getSelectedItem()
+                .toString()))
+        {
+          tooltipText = MessageManager.getString("label.search_all");
+        }
+        else if ("pdb id".equalsIgnoreCase(getCmbSearchTarget()
+                .getSelectedItem().toString()))
+        {
+          tooltipText = MessageManager
+                  .getString("label.separate_multiple_accession_ids");
+        }
+        else
+        {
+          tooltipText = MessageManager.formatMessage(
+                  "label.separate_multiple_query_values",
+                  new Object[] { getCmbSearchTarget().getSelectedItem()
+                          .toString() });
+        }
+        txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
+                tooltipText));
+        searchAction(true);
+      }
+    });
+
+    populateCmbSearchTargetOptions();
+
+    txt_search.setFont(new java.awt.Font("Verdana", 0, 12));
+
+    txt_search.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent e)
+      {
+        if (e.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          if (txt_search.getText() == null
+                  || txt_search.getText().isEmpty())
+          {
+            return;
+          }
+          String primaryKeyName = getFTSRestClient().getPrimaryKeyColumn()
+                  .getName();
+          if (primaryKeyName.equalsIgnoreCase(getCmbSearchTarget()
+                  .getSelectedItem().toString()))
+          {
+            transferToSequenceFetcher(txt_search.getText());
+          }
+        }
+      }
+    });
+
+    final DeferredTextInputListener listener = new DeferredTextInputListener(
+            1500, new ActionListener()
+            {
+              @Override
+              public void actionPerformed(ActionEvent e)
+              {
+                if (!getTypedText().equalsIgnoreCase(lastSearchTerm))
+                {
+                  searchAction(true);
+                  paginatorCart.clear();
+                  lastSearchTerm = getTypedText();
+                }
+              }
+            }, false);
+    txt_search.getDocument().addDocumentListener(listener);
+    txt_search.addFocusListener(new FocusListener()
+    {
+      @Override
+      public void focusGained(FocusEvent e)
+      {
+        listener.start();
+      }
+
+      @Override
+      public void focusLost(FocusEvent e)
+      {
+        // listener.stop();
+      }
+    });
+
+    final String searchTabTitle = MessageManager
+            .getString("label.search_result");
+    final String configureCols = MessageManager
+            .getString("label.configure_displayed_columns");
+    ChangeListener changeListener = new ChangeListener()
+    {
+      @Override
+      public void stateChanged(ChangeEvent changeEvent)
+      {
+        JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
+                .getSource();
+        int index = sourceTabbedPane.getSelectedIndex();
+
+        btn_back.setVisible(true);
+        btn_cancel.setVisible(true);
+        btn_ok.setVisible(true);
+        if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
+        {
+          btn_back.setVisible(false);
+          btn_cancel.setVisible(false);
+          btn_ok.setVisible(false);
+          btn_back.setEnabled(false);
+          btn_cancel.setEnabled(false);
+          btn_ok.setEnabled(false);
+          btn_next_page.setEnabled(false);
+          btn_prev_page.setEnabled(false);
+          txt_search.setEnabled(false);
+          cmb_searchTarget.setEnabled(false);
+          previousWantedFields = getFTSRestClient()
+                  .getAllDefaultDisplayedFTSDataColumns().toArray(
+                          new Object[0]);
+        }
+        if (sourceTabbedPane.getTitleAt(index).equals(searchTabTitle))
+        {
+          btn_back.setEnabled(true);
+          btn_cancel.setEnabled(true);
+          refreshPaginatorState();
+          txt_search.setEnabled(true);
+          cmb_searchTarget.setEnabled(true);
+          if (wantedFieldsUpdated())
+          {
+            searchAction(true);
+            paginatorCart.clear();
+          }
+          else
+          {
+            validateSelection();
+          }
+        }
+      }
+    };
+    tabbedPane.addChangeListener(changeListener);
+    tabbedPane.setPreferredSize(new Dimension(width, height));
+    tabbedPane.add(searchTabTitle, scrl_searchResult);
+    tabbedPane.add(configureCols, new FTSDataColumnPreferences(
+            PreferenceSource.SEARCH_SUMMARY, getFTSRestClient()));
+
+    pnl_actions.add(btn_back);
+    pnl_actions.add(btn_ok);
+    pnl_actions.add(btn_cancel);
+
+    pnl_results.add(tabbedPane);
+    pnl_inputs.add(cmb_searchTarget);
+    pnl_inputs.add(txt_search);
+    pnl_inputs.add(lbl_loading);
+    pnl_inputs.add(lbl_warning);
+    pnl_inputs.add(lbl_blank);
+    pnl_inputs.add(btn_prev_page);
+    pnl_inputs.add(btn_next_page);
+
+    this.setLayout(mainLayout);
+    this.add(pnl_inputs, java.awt.BorderLayout.NORTH);
+    this.add(pnl_results, java.awt.BorderLayout.CENTER);
+    this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
+    mainFrame.setVisible(true);
+    mainFrame.setContentPane(this);
+    mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+    mainFrame
+            .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
+            {
+              @Override
+              public void internalFrameClosing(InternalFrameEvent e)
+              {
+                closeAction();
+              }
+            });
+    mainFrame.setVisible(true);
+    mainFrame.setContentPane(this);
+    mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+    Integer x = getTempUserPrefs().get("FTSPanel.x");
+    Integer y = getTempUserPrefs().get("FTSPanel.y");
+    if (x != null && y != null)
+    {
+      mainFrame.setLocation(x, y);
+    }
+    Desktop.addInternalFrame(mainFrame, getFTSFrameTitle(), width, height);
+  }
+
+  protected void closeAction()
+  {
+    // System.out.println(">>>>>>>>>> closing internal frame!!!");
+    // System.out.println("width : " + this.getWidth());
+    // System.out.println("heigh : " + this.getHeight());
+    // System.out.println("x : " + mainFrame.getX());
+    // System.out.println("y : " + mainFrame.getY());
+    getTempUserPrefs().put("FTSPanel.width", this.getWidth());
+    getTempUserPrefs().put("FTSPanel.height", pnl_results.getHeight());
+    getTempUserPrefs().put("FTSPanel.x", mainFrame.getX());
+    getTempUserPrefs().put("FTSPanel.y", mainFrame.getY());
+    mainFrame.dispose();
+  }
+
+  public class DeferredTextInputListener implements DocumentListener
+  {
+    private final Timer swingTimer;
+
+    public DeferredTextInputListener(int timeOut, ActionListener listener,
+            boolean repeats)
+    {
+      swingTimer = new Timer(timeOut, listener);
+      swingTimer.setRepeats(repeats);
+    }
+
+    public void start()
+    {
+      swingTimer.start();
+    }
+
+    public void stop()
+    {
+      swingTimer.stop();
+    }
+
+    @Override
+    public void insertUpdate(DocumentEvent e)
+    {
+      swingTimer.restart();
+    }
+
+    @Override
+    public void removeUpdate(DocumentEvent e)
+    {
+      swingTimer.restart();
+    }
+
+    @Override
+    public void changedUpdate(DocumentEvent e)
+    {
+      swingTimer.restart();
+    }
+
+  }
+
+  public boolean wantedFieldsUpdated()
+  {
+    if (previousWantedFields == null)
+    {
+      return true;
+    }
+
+    return Arrays.equals(getFTSRestClient()
+            .getAllDefaultDisplayedFTSDataColumns().toArray(new Object[0]),
+            previousWantedFields) ? false : true;
+
+  }
+
+  public void validateSelection()
+  {
+    if (tbl_summary.getSelectedRows().length > 0
+            || !paginatorCart.isEmpty())
+    {
+      btn_ok.setEnabled(true);
+    }
+    else
+    {
+      btn_ok.setEnabled(false);
+    }
+  }
+
+  public JComboBox<FTSDataColumnI> getCmbSearchTarget()
+  {
+    return cmb_searchTarget;
+  }
+
+  public JTextField getTxtSearch()
+  {
+    return txt_search;
+  }
+
+  public JInternalFrame getMainFrame()
+  {
+    return mainFrame;
+  }
+
+  protected void delayAndEnableActionButtons()
+  {
+    new Thread()
+    {
+      @Override
+      public void run()
+      {
+        try
+        {
+          Thread.sleep(1500);
+        } catch (InterruptedException e)
+        {
+          e.printStackTrace();
+        }
+        btn_ok.setEnabled(true);
+        btn_back.setEnabled(true);
+        btn_cancel.setEnabled(true);
+      }
+    }.start();
+  }
+
+  protected void checkForErrors()
+  {
+    lbl_warning.setVisible(false);
+    lbl_blank.setVisible(true);
+    if (errorWarning.length() > 0)
+    {
+      lbl_loading.setVisible(false);
+      lbl_blank.setVisible(false);
+      lbl_warning.setToolTipText(JvSwingUtils.wrapTooltip(true,
+              errorWarning.toString()));
+      lbl_warning.setVisible(true);
+    }
+  }
+
+  protected void btn_back_ActionPerformed()
+  {
+    closeAction();
+    new SequenceFetcher(progressIndicator);
+  }
+
+  protected void disableActionButtons()
+  {
+    btn_ok.setEnabled(false);
+    btn_back.setEnabled(false);
+    btn_cancel.setEnabled(false);
+  }
+
+  protected void btn_cancel_ActionPerformed()
+  {
+    closeAction();
+  }
+
+  /**
+   * Populates search target combo-box options
+   */
+  public void populateCmbSearchTargetOptions()
+  {
+    List<FTSDataColumnI> searchableTargets = new ArrayList<FTSDataColumnI>();
+    try
+    {
+      Collection<FTSDataColumnI> foundFTSTargets = getFTSRestClient()
+              .getSearchableDataColumns();
+      searchableTargets.addAll(foundFTSTargets);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+
+    Collections.sort(searchableTargets, new Comparator<FTSDataColumnI>()
+    {
+      @Override
+      public int compare(FTSDataColumnI o1, FTSDataColumnI o2)
+      {
+        return o1.getName().compareTo(o2.getName());
+      }
+    });
+
+    for (FTSDataColumnI searchTarget : searchableTargets)
+    {
+      cmb_searchTarget.addItem(searchTarget);
+    }
+  }
+
+  public void transferToSequenceFetcher(String ids)
+  {
+    // mainFrame.dispose();
+    seqFetcher.getTextArea().setText(ids);
+    Thread worker = new Thread(seqFetcher);
+    worker.start();
+  }
+
+  @Override
+  public String getTypedText()
+  {
+    return txt_search.getText().trim();
+  }
+
+  @Override
+  public JTable getResultTable()
+  {
+    return tbl_summary;
+  }
+
+  public void reset()
+  {
+    lbl_loading.setVisible(false);
+    errorWarning.setLength(0);
+    lbl_warning.setVisible(false);
+    lbl_blank.setVisible(true);
+    btn_ok.setEnabled(false);
+    mainFrame.setTitle(getFTSFrameTitle());
+    referesh();
+    tbl_summary.setModel(new DefaultTableModel());
+    tbl_summary.setVisible(false);
+  }
+
+  @Override
+  public void setPrevPageButtonEnabled(boolean isEnabled)
+  {
+    btn_prev_page.setEnabled(isEnabled);
+  }
+
+  @Override
+  public void setNextPageButtonEnabled(boolean isEnabled)
+  {
+    btn_next_page.setEnabled(isEnabled);
+  }
+
+  @Override
+  public void setErrorMessage(String message)
+  {
+    errorWarning.append(message);
+  }
+
+  @Override
+  public void updateSearchFrameTitle(String title)
+  {
+    mainFrame.setTitle(title);
+  }
+
+  @Override
+  public void setSearchInProgress(Boolean isSearchInProgress)
+  {
+    lbl_blank.setVisible(!isSearchInProgress);
+    lbl_loading.setVisible(isSearchInProgress);
+  }
+
+  @Override
+  public void prevPageAction()
+  {
+    updatePaginatorCart();
+    if (offSet >= pageLimit)
+    {
+      offSet = offSet - pageLimit;
+      searchAction(false);
+    }
+    else
+    {
+      refreshPaginatorState();
+    }
+  }
+
+  @Override
+  public void nextPageAction()
+  {
+    updatePaginatorCart();
+    offSet = offSet + pageLimit;
+    searchAction(false);
+  }
+
+  public void updatePaginatorCart()
+  {
+    int primaryKeyColIndex = 0;
+    JTable resultTable = getResultTable();
+    int totalRows = resultTable.getRowCount();
+    try
+    {
+      primaryKeyColIndex = getFTSRestClient().getPrimaryKeyColumIndex(
+              wantedFields, false);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+
+    for (int row = 0; row < totalRows; row++)
+    {
+      String id = (String) resultTable.getValueAt(row, primaryKeyColIndex);
+      if (paginatorCart.contains(id))
+      {
+        paginatorCart.remove(id);
+      }
+    }
+    int[] selectedRows = resultTable.getSelectedRows();
+    for (int summaryRow : selectedRows)
+    {
+      String idStr = resultTable.getValueAt(summaryRow, primaryKeyColIndex)
+              .toString();
+      paginatorCart.add(idStr);
+    }
+    // System.out.println("Paginator shopping cart size : "
+    // + paginatorCart.size());
+  }
+
+  public void updateSummaryTableSelections()
+  {
+    JTable resultTable = getResultTable();
+    if (paginatorCart.isEmpty())
+    {
+      return;
+    }
+    int primaryKeyColIndex = 0;
+    try
+    {
+      primaryKeyColIndex = getFTSRestClient().getPrimaryKeyColumIndex(
+              wantedFields, false);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    // System.out.println(">>>>>> got here : 1");
+    int totalRows = resultTable.getRowCount();
+    // resultTable.clearSelection();
+    for (int row = 0; row < totalRows; row++)
+    {
+      String id = (String) resultTable.getValueAt(row, primaryKeyColIndex);
+      if (paginatorCart.contains(id))
+      {
+        resultTable.addRowSelectionInterval(row, row);
+      }
+    }
+    validateSelection();
+  }
+
+  public void refreshPaginatorState()
+  {
+    // System.out.println("resultSet count : " + resultSetCount);
+    // System.out.println("offSet : " + offSet);
+    // System.out.println("page limit : " + pageLimit);
+    setPrevPageButtonEnabled(false);
+    setNextPageButtonEnabled(false);
+    if (resultSetCount == 0 && pageLimit == 0)
+    {
+      return;
+    }
+    if (resultSetCount >= pageLimit)
+    {
+      setNextPageButtonEnabled(true);
+    }
+    if (offSet >= pageLimit)
+    {
+      setPrevPageButtonEnabled(true);
+    }
+  }
+
+  public void referesh()
+  {
+    mainFrame.setTitle(getFTSFrameTitle());
+  }
+
+}
diff --git a/src/jalview/fts/service/pdb/PDBFTSPanel.java b/src/jalview/fts/service/pdb/PDBFTSPanel.java
new file mode 100644
index 0000000..21a66c0
--- /dev/null
+++ b/src/jalview/fts/service/pdb/PDBFTSPanel.java
@@ -0,0 +1,279 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.fts.service.pdb;
+
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.core.GFTSPanel;
+import jalview.gui.SequenceFetcher;
+import jalview.util.MessageManager;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+ at SuppressWarnings("serial")
+public class PDBFTSPanel extends GFTSPanel
+{
+  private static String defaultFTSFrameTitle = MessageManager
+          .getString("label.pdb_sequence_fetcher");
+
+  private String ftsFrameTitle = defaultFTSFrameTitle;
+
+  private static Map<String, Integer> tempUserPrefs = new HashMap<String, Integer>();
+
+  public PDBFTSPanel(SequenceFetcher seqFetcher)
+  {
+    super();
+    pageLimit = PDBFTSRestClient.getInstance().getDefaultResponsePageSize();
+    this.seqFetcher = seqFetcher;
+    this.progressIndicator = (seqFetcher == null) ? null : seqFetcher
+            .getProgressIndicator();
+  }
+
+  @Override
+  public void searchAction(boolean isFreshSearch)
+  {
+    if (isFreshSearch)
+    {
+      offSet = 0;
+    }
+    new Thread()
+    {
+      @Override
+      public void run()
+      {
+        ftsFrameTitle = defaultFTSFrameTitle;
+        reset();
+        boolean allowEmptySequence = false;
+        if (getTypedText().length() > 0)
+        {
+          setSearchInProgress(true);
+          long startTime = System.currentTimeMillis();
+
+          String searchTarget = ((FTSDataColumnI) cmb_searchTarget
+                  .getSelectedItem()).getCode();
+          wantedFields = PDBFTSRestClient.getInstance()
+                  .getAllDefaultDisplayedFTSDataColumns();
+          String searchTerm = decodeSearchTerm(txt_search.getText(),
+                  searchTarget);
+
+          FTSRestRequest request = new FTSRestRequest();
+          request.setAllowEmptySeq(allowEmptySequence);
+          request.setResponseSize(100);
+          request.setFieldToSearchBy("(" + searchTarget + ":");
+          request.setSearchTerm(searchTerm + ")");
+          request.setOffSet(offSet);
+          request.setWantedFields(wantedFields);
+          FTSRestClientI pdbRestCleint = PDBFTSRestClient.getInstance();
+          FTSRestResponse resultList;
+          try
+          {
+            resultList = pdbRestCleint.executeRequest(request);
+          } catch (Exception e)
+          {
+            setErrorMessage(e.getMessage());
+            checkForErrors();
+            return;
+          }
+
+          if (resultList.getSearchSummary() != null
+                  && resultList.getSearchSummary().size() > 0)
+          {
+            getResultTable().setModel(
+                    FTSRestResponse.getTableModel(request,
+                            resultList.getSearchSummary()));
+            FTSRestResponse.configureTableColumn(getResultTable(),
+                    wantedFields, tempUserPrefs);
+            getResultTable().setVisible(true);
+          }
+
+          long endTime = System.currentTimeMillis();
+          totalResultSetCount = resultList.getNumberOfItemsFound();
+          resultSetCount = resultList.getSearchSummary() == null ? 0
+                  : resultList.getSearchSummary().size();
+          String result = (resultSetCount > 0) ? MessageManager
+                  .getString("label.results") : MessageManager
+                  .getString("label.result");
+
+          if (isPaginationEnabled() && resultSetCount > 0)
+          {
+            updateSearchFrameTitle(defaultFTSFrameTitle
+                    + " - "
+                    + result
+                    + " "
+                    + totalNumberformatter.format((Number) (offSet + 1))
+                    + " to "
+                    + totalNumberformatter
+                            .format((Number) (offSet + resultSetCount))
+                    + " of "
+                    + totalNumberformatter
+                            .format((Number) totalResultSetCount) + " "
+                    + " (" + (endTime - startTime) + " milli secs)");
+          }
+          else
+          {
+            updateSearchFrameTitle(defaultFTSFrameTitle + " - "
+                    + resultSetCount + " " + result + " ("
+                    + (endTime - startTime) + " milli secs)");
+          }
+
+          setSearchInProgress(false);
+          refreshPaginatorState();
+          updateSummaryTableSelections();
+        }
+      }
+    }.start();
+  }
+
+  public static String decodeSearchTerm(String enteredText,
+          String targetField)
+  {
+    String foundSearchTerms = enteredText;
+    StringBuilder foundSearchTermsBuilder = new StringBuilder();
+    if (enteredText.contains(";"))
+    {
+      String[] searchTerms = enteredText.split(";");
+      for (String searchTerm : searchTerms)
+      {
+        if (searchTerm.contains(":"))
+        {
+          foundSearchTermsBuilder.append(targetField).append(":")
+                  .append(searchTerm.split(":")[0]).append(" OR ");
+        }
+        else
+        {
+          foundSearchTermsBuilder.append(targetField).append(":")
+                  .append(searchTerm).append(" OR ");
+        }
+      }
+      int endIndex = foundSearchTermsBuilder.lastIndexOf(" OR ");
+      foundSearchTerms = foundSearchTermsBuilder.toString();
+      if (foundSearchTerms.contains(" OR "))
+      {
+        foundSearchTerms = foundSearchTerms.substring(
+                targetField.length() + 1, endIndex);
+      }
+    }
+    else if (enteredText.contains(":"))
+    {
+      foundSearchTerms = foundSearchTerms.split(":")[0];
+    }
+    return foundSearchTerms;
+  }
+
+  @Override
+  public void okAction()
+  {
+    // mainFrame.dispose();
+    disableActionButtons();
+    StringBuilder selectedIds = new StringBuilder();
+    HashSet<String> selectedIdsSet = new HashSet<String>();
+    int primaryKeyColIndex = 0;
+    try
+    {
+      primaryKeyColIndex = getFTSRestClient().getPrimaryKeyColumIndex(
+              wantedFields, false);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    int[] selectedRows = getResultTable().getSelectedRows();
+    String searchTerm = txt_search.getText();
+    for (int summaryRow : selectedRows)
+    {
+      String idStr = getResultTable().getValueAt(summaryRow,
+              primaryKeyColIndex).toString();
+      selectedIdsSet.add(getPDBIdwithSpecifiedChain(idStr, searchTerm));
+    }
+
+    for (String idStr : paginatorCart)
+    {
+      selectedIdsSet.add(getPDBIdwithSpecifiedChain(idStr, searchTerm));
+    }
+
+    for (String selectedId : selectedIdsSet)
+    {
+      selectedIds.append(selectedId).append(";");
+    }
+
+    String ids = selectedIds.toString();
+    // System.out.println(">>>>>>>>>>>>>>>> selected Ids: " + ids);
+    seqFetcher.getTextArea().setText(ids);
+    Thread worker = new Thread(seqFetcher);
+    worker.start();
+    delayAndEnableActionButtons();
+  }
+
+  public static String getPDBIdwithSpecifiedChain(String pdbId,
+          String searchTerm)
+  {
+    String pdbIdWithChainCode = "";
+    if (searchTerm.contains(";"))
+    {
+      String[] foundTerms = searchTerm.split(";");
+      for (String foundTerm : foundTerms)
+      {
+        if (foundTerm.contains(pdbId))
+        {
+          pdbIdWithChainCode = foundTerm;
+        }
+      }
+    }
+    else if (searchTerm.contains(pdbId))
+    {
+      pdbIdWithChainCode = searchTerm;
+    }
+    else
+    {
+      pdbIdWithChainCode = pdbId;
+    }
+    return pdbIdWithChainCode;
+  }
+
+  @Override
+  public FTSRestClientI getFTSRestClient()
+  {
+    return PDBFTSRestClient.getInstance();
+  }
+
+  @Override
+  public String getFTSFrameTitle()
+  {
+    return ftsFrameTitle;
+  }
+
+  @Override
+  public boolean isPaginationEnabled()
+  {
+    return true;
+  }
+
+  @Override
+  public Map<String, Integer> getTempUserPrefs()
+  {
+    return tempUserPrefs;
+  }
+
+}
diff --git a/src/jalview/fts/service/pdb/PDBFTSRestClient.java b/src/jalview/fts/service/pdb/PDBFTSRestClient.java
new file mode 100644
index 0000000..bf347dd
--- /dev/null
+++ b/src/jalview/fts/service/pdb/PDBFTSRestClient.java
@@ -0,0 +1,426 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.fts.service.pdb;
+
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSRestClient;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.util.MessageManager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+import javax.ws.rs.core.MediaType;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+
+/**
+ * A rest client for querying the Search endpoint of the PDB API
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public class PDBFTSRestClient extends FTSRestClient
+{
+
+  private static FTSRestClientI instance = null;
+
+  public static final String PDB_SEARCH_ENDPOINT = "http://www.ebi.ac.uk/pdbe/search/pdb/select?";
+
+  protected PDBFTSRestClient()
+  {
+  }
+
+  /**
+   * Takes a PDBRestRequest object and returns a response upon execution
+   * 
+   * @param pdbRestRequest
+   *          the PDBRestRequest instance to be processed
+   * @return the pdbResponse object for the given request
+   * @throws Exception
+   */
+  @Override
+  public FTSRestResponse executeRequest(FTSRestRequest pdbRestRequest)
+          throws Exception
+  {
+    try
+    {
+      ClientConfig clientConfig = new DefaultClientConfig();
+      Client client = Client.create(clientConfig);
+
+      String wantedFields = getDataColumnsFieldsAsCommaDelimitedString(pdbRestRequest
+              .getWantedFields());
+      int responseSize = (pdbRestRequest.getResponseSize() == 0) ? getDefaultResponsePageSize()
+              : pdbRestRequest.getResponseSize();
+      int offSet = pdbRestRequest.getOffSet();
+      String sortParam = null;
+      if (pdbRestRequest.getFieldToSortBy() == null
+              || pdbRestRequest.getFieldToSortBy().trim().isEmpty())
+      {
+        sortParam = "";
+      }
+      else
+      {
+        if (pdbRestRequest.getFieldToSortBy()
+                .equalsIgnoreCase("Resolution"))
+        {
+          sortParam = pdbRestRequest.getFieldToSortBy()
+                  + (pdbRestRequest.isAscending() ? " asc" : " desc");
+        }
+        else
+        {
+          sortParam = pdbRestRequest.getFieldToSortBy()
+                  + (pdbRestRequest.isAscending() ? " desc" : " asc");
+        }
+      }
+
+      String facetPivot = (pdbRestRequest.getFacetPivot() == null || pdbRestRequest
+              .getFacetPivot().isEmpty()) ? "" : pdbRestRequest
+              .getFacetPivot();
+      String facetPivotMinCount = String.valueOf(pdbRestRequest
+              .getFacetPivotMinCount());
+
+      String query = pdbRestRequest.getFieldToSearchBy()
+              + pdbRestRequest.getSearchTerm()
+              + (pdbRestRequest.isAllowEmptySeq() ? ""
+                      : " AND molecule_sequence:['' TO *]")
+              + (pdbRestRequest.isAllowUnpublishedEntries() ? ""
+                      : " AND status:REL");
+
+      // Build request parameters for the REST Request
+      WebResource webResource = null;
+      if (pdbRestRequest.isFacet())
+      {
+        webResource = client.resource(PDB_SEARCH_ENDPOINT)
+                .queryParam("wt", "json").queryParam("fl", wantedFields)
+                .queryParam("rows", String.valueOf(responseSize))
+                .queryParam("q", query)
+                .queryParam("start", String.valueOf(offSet))
+                .queryParam("sort", sortParam).queryParam("facet", "true")
+                .queryParam("facet.pivot", facetPivot)
+                .queryParam("facet.pivot.mincount", facetPivotMinCount);
+      }
+      else
+      {
+        webResource = client.resource(PDB_SEARCH_ENDPOINT)
+                .queryParam("wt", "json").queryParam("fl", wantedFields)
+                .queryParam("rows", String.valueOf(responseSize))
+                .queryParam("start", String.valueOf(offSet))
+                .queryParam("q", query).queryParam("sort", sortParam);
+      }
+      // Execute the REST request
+      ClientResponse clientResponse = webResource.accept(
+              MediaType.APPLICATION_JSON).get(ClientResponse.class);
+
+      // Get the JSON string from the response object
+      String responseString = clientResponse.getEntity(String.class);
+      // System.out.println("query >>>>>>> " + pdbRestRequest.toString());
+
+      // Check the response status and report exception if one occurs
+      if (clientResponse.getStatus() != 200)
+      {
+        String errorMessage = "";
+        if (clientResponse.getStatus() == 400)
+        {
+          errorMessage = parseJsonExceptionString(responseString);
+          throw new Exception(errorMessage);
+        }
+        else
+        {
+          errorMessage = getMessageByHTTPStatusCode(
+                  clientResponse.getStatus(), "PDB");
+          throw new Exception(errorMessage);
+        }
+      }
+
+      // Make redundant objects eligible for garbage collection to conserve
+      // memory
+      clientResponse = null;
+      client = null;
+
+      // Process the response and return the result to the caller.
+      return parsePDBJsonResponse(responseString, pdbRestRequest);
+    } catch (Exception e)
+    {
+      String exceptionMsg = e.getMessage();
+      if (exceptionMsg.contains("SocketException"))
+      {
+        // No internet connection
+        throw new Exception(
+                MessageManager
+                        .getString("exception.unable_to_detect_internet_connection"));
+      }
+      else if (exceptionMsg.contains("UnknownHostException"))
+      {
+        // The server 'www.ebi.ac.uk' is unreachable
+        throw new Exception(MessageManager.formatMessage(
+                "exception.fts_server_unreachable", "PDB Solr"));
+      }
+      else
+      {
+        throw e;
+      }
+    }
+  }
+
+  /**
+   * Process error response from PDB server if/when one occurs.
+   * 
+   * @param jsonResponse
+   *          the JSON string containing error message from the server
+   * @return the processed error message from the JSON string
+   */
+  public static String parseJsonExceptionString(String jsonErrorResponse)
+  {
+    StringBuilder errorMessage = new StringBuilder(
+            "\n============= PDB Rest Client RunTime error =============\n");
+
+    try
+    {
+      JSONParser jsonParser = new JSONParser();
+      JSONObject jsonObj = (JSONObject) jsonParser.parse(jsonErrorResponse);
+      JSONObject errorResponse = (JSONObject) jsonObj.get("error");
+
+      JSONObject responseHeader = (JSONObject) jsonObj
+              .get("responseHeader");
+      JSONObject paramsObj = (JSONObject) responseHeader.get("params");
+      String status = responseHeader.get("status").toString();
+      String message = errorResponse.get("msg").toString();
+      String query = paramsObj.get("q").toString();
+      String fl = paramsObj.get("fl").toString();
+
+      errorMessage.append("Status: ").append(status).append("\n");
+      errorMessage.append("Message: ").append(message).append("\n");
+      errorMessage.append("query: ").append(query).append("\n");
+      errorMessage.append("fl: ").append(fl).append("\n");
+
+    } catch (ParseException e)
+    {
+      e.printStackTrace();
+    }
+    return errorMessage.toString();
+  }
+
+  /**
+   * Parses the JSON response string from PDB REST API. The response is dynamic
+   * hence, only fields specifically requested for in the 'wantedFields'
+   * parameter is fetched/processed
+   * 
+   * @param pdbJsonResponseString
+   *          the JSON string to be parsed
+   * @param pdbRestRequest
+   *          the request object which contains parameters used to process the
+   *          JSON string
+   * @return
+   */
+  @SuppressWarnings("unchecked")
+  public static FTSRestResponse parsePDBJsonResponse(
+          String pdbJsonResponseString, FTSRestRequest pdbRestRequest)
+  {
+    FTSRestResponse searchResult = new FTSRestResponse();
+    List<FTSData> result = null;
+    try
+    {
+      JSONParser jsonParser = new JSONParser();
+      JSONObject jsonObj = (JSONObject) jsonParser
+              .parse(pdbJsonResponseString);
+
+      JSONObject pdbResponse = (JSONObject) jsonObj.get("response");
+      String queryTime = ((JSONObject) jsonObj.get("responseHeader")).get(
+              "QTime").toString();
+      int numFound = Integer
+              .valueOf(pdbResponse.get("numFound").toString());
+      if (numFound > 0)
+      {
+        result = new ArrayList<FTSData>();
+        JSONArray docs = (JSONArray) pdbResponse.get("docs");
+        for (Iterator<JSONObject> docIter = docs.iterator(); docIter
+                .hasNext();)
+        {
+          JSONObject doc = docIter.next();
+          result.add(getFTSData(doc, pdbRestRequest));
+        }
+        searchResult.setNumberOfItemsFound(numFound);
+        searchResult.setResponseTime(queryTime);
+        searchResult.setSearchSummary(result);
+      }
+    } catch (ParseException e)
+    {
+      e.printStackTrace();
+    }
+    return searchResult;
+  }
+
+  public static FTSData getFTSData(JSONObject pdbJsonDoc,
+          FTSRestRequest request)
+  {
+
+    String primaryKey = null;
+
+    Object[] summaryRowData;
+
+    SequenceI associatedSequence;
+
+    Collection<FTSDataColumnI> diplayFields = request.getWantedFields();
+    SequenceI associatedSeq = request.getAssociatedSequence();
+    int colCounter = 0;
+    summaryRowData = new Object[(associatedSeq != null) ? diplayFields
+            .size() + 1 : diplayFields.size()];
+    if (associatedSeq != null)
+    {
+      associatedSequence = associatedSeq;
+      summaryRowData[0] = associatedSequence;
+      colCounter = 1;
+    }
+
+    for (FTSDataColumnI field : diplayFields)
+    {
+      String fieldData = (pdbJsonDoc.get(field.getCode()) == null) ? ""
+              : pdbJsonDoc.get(field.getCode()).toString();
+      if (field.isPrimaryKeyColumn())
+      {
+        primaryKey = fieldData;
+        summaryRowData[colCounter++] = primaryKey;
+      }
+      else if (fieldData == null || fieldData.isEmpty())
+      {
+        summaryRowData[colCounter++] = null;
+      }
+      else
+      {
+        try
+        {
+          summaryRowData[colCounter++] = (field.getDataType()
+                  .getDataTypeClass() == Integer.class) ? Integer
+                  .valueOf(fieldData) : (field.getDataType()
+                  .getDataTypeClass() == Double.class) ? Double
+                  .valueOf(fieldData) : sanitiseData(fieldData);
+        } catch (Exception e)
+        {
+          e.printStackTrace();
+          System.out.println("offending value:" + fieldData);
+        }
+      }
+    }
+
+    final String primaryKey1 = primaryKey;
+
+    final Object[] summaryRowData1 = summaryRowData;
+    return new FTSData()
+    {
+      @Override
+      public Object[] getSummaryData()
+      {
+        return summaryRowData1;
+      }
+
+      @Override
+      public Object getPrimaryKey()
+      {
+        return primaryKey1;
+      }
+
+      /**
+       * Returns a string representation of this object;
+       */
+      @Override
+      public String toString()
+      {
+        StringBuilder summaryFieldValues = new StringBuilder();
+        for (Object summaryField : summaryRowData1)
+        {
+          summaryFieldValues.append(
+                  summaryField == null ? " " : summaryField.toString())
+                  .append("\t");
+        }
+        return summaryFieldValues.toString();
+      }
+
+      /**
+       * Returns hash code value for this object
+       */
+      @Override
+      public int hashCode()
+      {
+        return Objects.hash(primaryKey1, this.toString());
+      }
+
+      @Override
+      public boolean equals(Object that)
+      {
+        return this.toString().equals(that.toString());
+      }
+    };
+  }
+
+  private static String sanitiseData(String data)
+  {
+    String cleanData = data.replaceAll("\\[\"", "").replaceAll("\\]\"", "")
+            .replaceAll("\\[", "").replaceAll("\\]", "")
+            .replaceAll("\",\"", ", ").replaceAll("\"", "");
+    return cleanData;
+  }
+
+  @Override
+  public String getColumnDataConfigFileName()
+  {
+    return "/fts/pdb_data_columns.txt";
+  }
+
+  public static FTSRestClientI getInstance()
+  {
+    if (instance == null)
+    {
+      instance = new PDBFTSRestClient();
+    }
+    return instance;
+  }
+
+  private Collection<FTSDataColumnI> allDefaultDisplayedStructureDataColumns;
+
+  public Collection<FTSDataColumnI> getAllDefaultDisplayedStructureDataColumns()
+  {
+    if (allDefaultDisplayedStructureDataColumns == null
+            || allDefaultDisplayedStructureDataColumns.isEmpty())
+    {
+      allDefaultDisplayedStructureDataColumns = new ArrayList<FTSDataColumnI>();
+      allDefaultDisplayedStructureDataColumns.addAll(super
+              .getAllDefaultDisplayedFTSDataColumns());
+    }
+    return allDefaultDisplayedStructureDataColumns;
+  }
+}
diff --git a/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java b/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java
new file mode 100644
index 0000000..58d92d3
--- /dev/null
+++ b/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java
@@ -0,0 +1,323 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.fts.service.uniprot;
+
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSRestClient;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.util.MessageManager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+import javax.ws.rs.core.MediaType;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+
+public class UniProtFTSRestClient extends FTSRestClient
+{
+  private static FTSRestClientI instance = null;
+
+  public static final String UNIPROT_SEARCH_ENDPOINT = "http://www.uniprot.org/uniprot/?";
+
+  @Override
+  public FTSRestResponse executeRequest(FTSRestRequest uniportRestRequest)
+          throws Exception
+  {
+    try
+    {
+      ClientConfig clientConfig = new DefaultClientConfig();
+      Client client = Client.create(clientConfig);
+
+      String wantedFields = getDataColumnsFieldsAsCommaDelimitedString(uniportRestRequest
+              .getWantedFields());
+      int responseSize = (uniportRestRequest.getResponseSize() == 0) ? getDefaultResponsePageSize()
+              : uniportRestRequest.getResponseSize();
+
+      int offSet = uniportRestRequest.getOffSet();
+      String query;
+      if (isAdvancedQuery(uniportRestRequest.getSearchTerm()))
+      {
+        query = uniportRestRequest.getSearchTerm();
+      }
+      else
+      {
+        query = uniportRestRequest.getFieldToSearchBy().equalsIgnoreCase(
+                "Search All") ? uniportRestRequest.getSearchTerm()
+                + " or mnemonic:" + uniportRestRequest.getSearchTerm()
+                : uniportRestRequest.getFieldToSearchBy() + ":"
+                        + uniportRestRequest.getSearchTerm();
+      }
+
+      WebResource webResource = null;
+      webResource = client.resource(UNIPROT_SEARCH_ENDPOINT)
+              .queryParam("format", "tab")
+              .queryParam("columns", wantedFields)
+              .queryParam("limit", String.valueOf(responseSize))
+              .queryParam("offset", String.valueOf(offSet))
+              .queryParam("sort", "score").queryParam("query", query);
+      // Execute the REST request
+      ClientResponse clientResponse = webResource.accept(
+              MediaType.TEXT_PLAIN).get(ClientResponse.class);
+      String uniProtTabDelimittedResponseString = clientResponse
+              .getEntity(String.class);
+      // Make redundant objects eligible for garbage collection to conserve
+      // memory
+      // System.out.println(">>>>> response : "
+      // + uniProtTabDelimittedResponseString);
+      if (clientResponse.getStatus() != 200)
+      {
+        String errorMessage = getMessageByHTTPStatusCode(
+                clientResponse.getStatus(), "Uniprot");
+        throw new Exception(errorMessage);
+
+      }
+      int xTotalResults = Integer.valueOf(clientResponse.getHeaders()
+              .get("X-Total-Results").get(0));
+      clientResponse = null;
+      client = null;
+      return parseUniprotResponse(uniProtTabDelimittedResponseString,
+              uniportRestRequest, xTotalResults);
+    } catch (Exception e)
+    {
+      String exceptionMsg = e.getMessage();
+      if (exceptionMsg.contains("SocketException"))
+      {
+        // No internet connection
+        throw new Exception(
+                MessageManager
+                        .getString("exception.unable_to_detect_internet_connection"));
+      }
+      else if (exceptionMsg.contains("UnknownHostException"))
+      {
+        // The server 'http://www.uniprot.org' is unreachable
+        throw new Exception(MessageManager.formatMessage(
+                "exception.fts_server_unreachable", "Uniprot"));
+      }
+      else
+      {
+        throw e;
+      }
+    }
+  }
+
+  public boolean isAdvancedQuery(String query)
+  {
+    if (query.contains(" AND ") || query.contains(" OR ")
+            || query.contains(" NOT ") || query.contains(" ! ")
+            || query.contains(" || ") || query.contains(" && ")
+            || query.contains(":") || query.contains("-"))
+    {
+      return true;
+    }
+    return false;
+  }
+
+  public FTSRestResponse parseUniprotResponse(
+          String uniProtTabDelimittedResponseString,
+          FTSRestRequest uniprotRestRequest, int xTotalResults)
+  {
+    FTSRestResponse searchResult = new FTSRestResponse();
+    List<FTSData> result = null;
+    if (uniProtTabDelimittedResponseString == null
+            || uniProtTabDelimittedResponseString.trim().isEmpty())
+    {
+      searchResult.setNumberOfItemsFound(0);
+      return searchResult;
+    }
+    String[] foundDataRow = uniProtTabDelimittedResponseString.split("\n");
+    if (foundDataRow != null && foundDataRow.length > 0)
+    {
+      result = new ArrayList<FTSData>();
+      String titleRow = getDataColumnsFieldsAsTabDelimitedString(uniprotRestRequest
+              .getWantedFields());
+      // System.out.println(">>>>Title row : " + titleRow);
+      for (String dataRow : foundDataRow)
+      {
+        if (dataRow.equalsIgnoreCase(titleRow))
+        {
+          // System.out.println(">>>>>>>>>> matched!!!");
+          continue;
+        }
+        // System.out.println(dataRow);
+        result.add(getFTSData(dataRow, uniprotRestRequest));
+      }
+      searchResult.setNumberOfItemsFound(xTotalResults);
+      searchResult.setSearchSummary(result);
+    }
+    return searchResult;
+  }
+
+  /**
+   * Takes a collection of FTSDataColumnI and converts its 'code' values into a
+   * tab delimited string.
+   * 
+   * @param dataColumnFields
+   *          the collection of FTSDataColumnI to process
+   * @return the generated comma delimited string from the supplied
+   *         FTSDataColumnI collection
+   */
+  private String getDataColumnsFieldsAsTabDelimitedString(
+          Collection<FTSDataColumnI> dataColumnFields)
+  {
+    String result = "";
+    if (dataColumnFields != null && !dataColumnFields.isEmpty())
+    {
+      StringBuilder returnedFields = new StringBuilder();
+      for (FTSDataColumnI field : dataColumnFields)
+      {
+        if (field.getName().equalsIgnoreCase("Uniprot Id"))
+        {
+          returnedFields.append("\t").append("Entry");
+        }
+        else
+        {
+          returnedFields.append("\t").append(field.getName());
+        }
+      }
+      returnedFields.deleteCharAt(0);
+      result = returnedFields.toString();
+    }
+    return result;
+  }
+
+  public static FTSData getFTSData(String tabDelimittedDataStr,
+          FTSRestRequest request)
+  {
+    String primaryKey = null;
+
+    Object[] summaryRowData;
+
+    Collection<FTSDataColumnI> diplayFields = request.getWantedFields();
+    int colCounter = 0;
+    summaryRowData = new Object[diplayFields.size()];
+    String[] columns = tabDelimittedDataStr.split("\t");
+    for (FTSDataColumnI field : diplayFields)
+    {
+      try
+      {
+        String fieldData = columns[colCounter];
+        if (field.isPrimaryKeyColumn())
+        {
+          primaryKey = fieldData;
+          summaryRowData[colCounter++] = primaryKey;
+        }
+        else if (fieldData == null || fieldData.isEmpty())
+        {
+          summaryRowData[colCounter++] = null;
+        }
+        else
+        {
+          try
+          {
+            summaryRowData[colCounter++] = (field.getDataType()
+                    .getDataTypeClass() == Integer.class) ? Integer
+                    .valueOf(fieldData.replace(",", ""))
+                    : (field.getDataType().getDataTypeClass() == Double.class) ? Double
+                            .valueOf(fieldData) : fieldData;
+          } catch (Exception e)
+          {
+            e.printStackTrace();
+            System.out.println("offending value:" + fieldData);
+          }
+        }
+      } catch (Exception e)
+      {
+        // e.printStackTrace();
+      }
+    }
+
+    final String primaryKey1 = primaryKey;
+
+    final Object[] summaryRowData1 = summaryRowData;
+    return new FTSData()
+    {
+      @Override
+      public Object[] getSummaryData()
+      {
+        return summaryRowData1;
+      }
+
+      @Override
+      public Object getPrimaryKey()
+      {
+        return primaryKey1;
+      }
+
+      /**
+       * Returns a string representation of this object;
+       */
+      @Override
+      public String toString()
+      {
+        StringBuilder summaryFieldValues = new StringBuilder();
+        for (Object summaryField : summaryRowData1)
+        {
+          summaryFieldValues.append(
+                  summaryField == null ? " " : summaryField.toString())
+                  .append("\t");
+        }
+        return summaryFieldValues.toString();
+      }
+
+      /**
+       * Returns hash code value for this object
+       */
+      @Override
+      public int hashCode()
+      {
+        return Objects.hash(primaryKey1, this.toString());
+      }
+
+      @Override
+      public boolean equals(Object that)
+      {
+        return this.toString().equals(that.toString());
+      }
+    };
+  }
+
+  public static FTSRestClientI getInstance()
+  {
+    if (instance == null)
+    {
+      instance = new UniProtFTSRestClient();
+    }
+    return instance;
+  }
+
+  @Override
+  public String getColumnDataConfigFileName()
+  {
+    return "/fts/uniprot_data_columns.txt";
+  }
+
+}
diff --git a/src/jalview/fts/service/uniprot/UniprotFTSPanel.java b/src/jalview/fts/service/uniprot/UniprotFTSPanel.java
new file mode 100644
index 0000000..381b685
--- /dev/null
+++ b/src/jalview/fts/service/uniprot/UniprotFTSPanel.java
@@ -0,0 +1,238 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+package jalview.fts.service.uniprot;
+
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.core.GFTSPanel;
+import jalview.gui.SequenceFetcher;
+import jalview.util.MessageManager;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+ at SuppressWarnings("serial")
+public class UniprotFTSPanel extends GFTSPanel
+{
+
+  private static String defaultFTSFrameTitle = MessageManager
+          .getString("label.uniprot_sequence_fetcher");
+
+  private String ftsFrameTitle = defaultFTSFrameTitle;
+
+  private static Map<String, Integer> tempUserPrefs = new HashMap<String, Integer>();
+
+  public UniprotFTSPanel(SequenceFetcher seqFetcher)
+  {
+    super();
+    pageLimit = UniProtFTSRestClient.getInstance()
+            .getDefaultResponsePageSize();
+    this.seqFetcher = seqFetcher;
+    this.progressIndicator = (seqFetcher == null) ? null : seqFetcher
+            .getProgressIndicator();
+  }
+
+  @Override
+  public void searchAction(boolean isFreshSearch)
+  {
+    if (isFreshSearch)
+    {
+      offSet = 0;
+    }
+    new Thread()
+    {
+      @Override
+      public void run()
+      {
+        ftsFrameTitle = defaultFTSFrameTitle;
+        reset();
+        if (getTypedText().length() > 0)
+        {
+          setSearchInProgress(true);
+          long startTime = System.currentTimeMillis();
+
+          String searchTarget = ((FTSDataColumnI) cmb_searchTarget
+                  .getSelectedItem()).getAltCode();
+
+          wantedFields = UniProtFTSRestClient.getInstance()
+                  .getAllDefaultDisplayedFTSDataColumns();
+          String searchTerm = decodeSearchTerm(txt_search.getText(),
+                  searchTarget);
+
+          FTSRestRequest request = new FTSRestRequest();
+          request.setFieldToSearchBy(searchTarget);
+          request.setSearchTerm(searchTerm);
+          request.setOffSet(offSet);
+          request.setWantedFields(wantedFields);
+          FTSRestClientI uniProtRestCleint = UniProtFTSRestClient
+                  .getInstance();
+          FTSRestResponse resultList;
+          try
+          {
+            resultList = uniProtRestCleint.executeRequest(request);
+          } catch (Exception e)
+          {
+            e.printStackTrace();
+            setErrorMessage(e.getMessage());
+            checkForErrors();
+            return;
+          }
+
+          if (resultList.getSearchSummary() != null
+                  && resultList.getSearchSummary().size() > 0)
+          {
+            getResultTable().setModel(
+                    FTSRestResponse.getTableModel(request,
+                            resultList.getSearchSummary()));
+            FTSRestResponse.configureTableColumn(getResultTable(),
+                    wantedFields, tempUserPrefs);
+            getResultTable().setVisible(true);
+          }
+
+          long endTime = System.currentTimeMillis();
+          totalResultSetCount = resultList.getNumberOfItemsFound();
+          resultSetCount = resultList.getSearchSummary() == null ? 0
+                  : resultList.getSearchSummary().size();
+          String result = (resultSetCount > 0) ? MessageManager
+                  .getString("label.results") : MessageManager
+                  .getString("label.result");
+          if (isPaginationEnabled() && resultSetCount > 0)
+          {
+            updateSearchFrameTitle(defaultFTSFrameTitle
+                    + " - "
+                    + result
+                    + " "
+                    + totalNumberformatter.format((Number) (offSet + 1))
+                    + " to "
+                    + totalNumberformatter
+                            .format((Number) (offSet + resultSetCount))
+                    + " of "
+                    + totalNumberformatter
+                            .format((Number) totalResultSetCount) + " "
+                    + " (" + (endTime - startTime) + " milli secs)");
+          }
+          else
+          {
+            updateSearchFrameTitle(defaultFTSFrameTitle + " - "
+                    + resultSetCount + " " + result + " ("
+                    + (endTime - startTime) + " milli secs)");
+          }
+          setSearchInProgress(false);
+          refreshPaginatorState();
+          updateSummaryTableSelections();
+        }
+      }
+    }.start();
+
+  }
+
+  public String decodeSearchTerm(String enteredText, String targetField)
+  {
+    int searchTargetLength = targetField.equalsIgnoreCase("Search All") ? 0
+            : targetField.length() + 1;
+    String searchTarget = targetField.equalsIgnoreCase("Search All") ? ""
+            : targetField + ":";
+    String foundSearchTerms = enteredText;
+    StringBuilder foundSearchTermsBuilder = new StringBuilder();
+    if (enteredText.contains(";"))
+    {
+      String[] searchTerms = enteredText.split(";");
+      for (String searchTerm : searchTerms)
+      {
+        foundSearchTermsBuilder.append(searchTarget).append(searchTerm)
+                .append(" OR ");
+      }
+      int endIndex = foundSearchTermsBuilder.lastIndexOf(" OR ");
+      foundSearchTerms = foundSearchTermsBuilder.toString();
+      if (foundSearchTerms.contains(" OR "))
+      {
+        foundSearchTerms = foundSearchTerms.substring(searchTargetLength,
+                endIndex);
+      }
+    }
+    return foundSearchTerms;
+  }
+
+  @Override
+  public boolean isPaginationEnabled()
+  {
+    return true;
+  }
+
+  @Override
+  public void okAction()
+  {
+    disableActionButtons();
+    StringBuilder selectedIds = new StringBuilder();
+    HashSet<String> selectedIdsSet = new HashSet<String>();
+    int primaryKeyColIndex = 0;
+    try
+    {
+      primaryKeyColIndex = getFTSRestClient().getPrimaryKeyColumIndex(
+              wantedFields, false);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    int[] selectedRows = getResultTable().getSelectedRows();
+    for (int summaryRow : selectedRows)
+    {
+      String idStr = getResultTable().getValueAt(summaryRow,
+              primaryKeyColIndex).toString();
+      selectedIdsSet.add(idStr);
+    }
+    selectedIdsSet.addAll(paginatorCart);
+    for (String selectedId : selectedIdsSet)
+    {
+      selectedIds.append(selectedId).append(";");
+    }
+
+    String ids = selectedIds.toString();
+    // System.out.println(">>>>>>>>>>>>>>>> selected Ids: " + ids);
+    seqFetcher.getTextArea().setText(ids);
+    Thread worker = new Thread(seqFetcher);
+    worker.start();
+    delayAndEnableActionButtons();
+  }
+
+  @Override
+  public FTSRestClientI getFTSRestClient()
+  {
+    return UniProtFTSRestClient.getInstance();
+  }
+
+  @Override
+  public String getFTSFrameTitle()
+  {
+    return ftsFrameTitle;
+  }
+
+  @Override
+  public Map<String, Integer> getTempUserPrefs()
+  {
+    return tempUserPrefs;
+  }
+
+}
diff --git a/src/jalview/gui/AlignExportSettings.java b/src/jalview/gui/AlignExportSettings.java
index 04849d8..32a1f4e 100644
--- a/src/jalview/gui/AlignExportSettings.java
+++ b/src/jalview/gui/AlignExportSettings.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java
index b484118..05f714f 100644
--- a/src/jalview/gui/AlignFrame.java
+++ b/src/jalview/gui/AlignFrame.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -71,6 +71,7 @@ import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
 import jalview.io.JnetAnnotationMaker;
 import jalview.io.NewickFile;
+import jalview.io.StructureFile;
 import jalview.io.TCoffeeScoreFile;
 import jalview.jbgui.GAlignFrame;
 import jalview.schemes.Blosum62ColourScheme;
@@ -91,9 +92,10 @@ import jalview.schemes.TaylorColourScheme;
 import jalview.schemes.TurnColourScheme;
 import jalview.schemes.UserColourScheme;
 import jalview.schemes.ZappoColourScheme;
-import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.ws.DBRefFetcher;
+import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
 import jalview.ws.jws1.Discoverer;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
@@ -114,6 +116,8 @@ import java.awt.dnd.DropTargetEvent;
 import java.awt.dnd.DropTargetListener;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.awt.event.KeyAdapter;
@@ -131,7 +135,6 @@ import java.util.Deque;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
-import java.util.Set;
 import java.util.Vector;
 
 import javax.swing.JCheckBoxMenuItem;
@@ -368,6 +371,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       setGUINucleotide(viewport.getAlignment().isNucleotide());
     }
 
+    this.alignPanel.av
+            .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
+
     setMenusFromViewport(viewport);
     buildSortByAnnotationScoresMenu();
     buildTreeMenu();
@@ -459,6 +465,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       formatMenu.add(vsel);
     }
+    addFocusListener(new FocusAdapter()
+    {
+      @Override
+      public void focusGained(FocusEvent e)
+      {
+        Jalview.setCurrentAlignFrame(AlignFrame.this);
+      }
+    });
 
   }
 
@@ -656,6 +670,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           toggleHiddenRegions(toggleSeqs, toggleCols);
           break;
         }
+        case KeyEvent.VK_B:
+        {
+          boolean toggleSel = evt.isControlDown() || evt.isMetaDown();
+          boolean modifyExisting = true; // always modify, don't clear
+                                         // evt.isShiftDown();
+          boolean invertHighlighted = evt.isAltDown();
+          avc.markHighlightedColumns(invertHighlighted, modifyExisting,
+                  toggleSel);
+          break;
+        }
         case KeyEvent.VK_PAGE_UP:
           if (viewport.getWrapAlignment())
           {
@@ -825,13 +849,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void setGUINucleotide(boolean nucleotide)
   {
     showTranslation.setVisible(nucleotide);
+    showReverse.setVisible(nucleotide);
+    showReverseComplement.setVisible(nucleotide);
     conservationMenuItem.setEnabled(!nucleotide);
     modifyConservation.setEnabled(!nucleotide);
     showGroupConservation.setEnabled(!nucleotide);
     rnahelicesColour.setEnabled(nucleotide);
     purinePyrimidineColour.setEnabled(nucleotide);
-    showComplementMenuItem.setText(MessageManager
-            .getString(nucleotide ? "label.protein" : "label.nucleotide"));
+    showComplementMenuItem.setText(nucleotide ? MessageManager
+            .getString("label.protein") : MessageManager
+            .getString("label.nucleotide"));
     setColourSelected(jalview.bin.Cache.getDefault(
             nucleotide ? Preferences.DEFAULT_COLOUR_NUC
                     : Preferences.DEFAULT_COLOUR_PROT, "None"));
@@ -842,6 +869,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * operation that affects the data in the current view (selection changed,
    * etc) to update the menus to reflect the new state.
    */
+  @Override
   public void setMenusForViewport()
   {
     setMenusFromViewport(viewport);
@@ -899,10 +927,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     rnahelicesColour.setEnabled(av.getAlignment().hasRNAStructure());
     rnahelicesColour
             .setSelected(av.getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
-    setShowProductsEnabled();
+
+    showProducts.setEnabled(canShowProducts());
+    setGroovyEnabled(Desktop.getGroovyConsole() != null);
+
     updateEditMenuBar();
   }
 
+  /**
+   * Set the enabled state of the 'Run Groovy' option in the Calculate menu
+   * 
+   * @param b
+   */
+  public void setGroovyEnabled(boolean b)
+  {
+    runGroovy.setEnabled(b);
+  }
+
   private IProgressIndicator progressBar;
 
   /*
@@ -955,7 +996,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void fetchSequence_actionPerformed(ActionEvent e)
   {
-    new SequenceFetcher(this);
+    new jalview.gui.SequenceFetcher(this);
   }
 
   @Override
@@ -1264,13 +1305,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     AlignmentI alignmentToExport = null;
     AlignExportSettingI settings = exportSettings;
     String[] omitHidden = null;
-    int[] alignmentStartEnd = new int[2];
 
     HiddenSequences hiddenSeqs = viewport.getAlignment()
             .getHiddenSequences();
 
     alignmentToExport = viewport.getAlignment();
-    alignmentStartEnd = new int[] { 0, alignmentToExport.getWidth() - 1 };
 
     boolean hasHiddenSeqs = hiddenSeqs.getSize() > 0;
     if (settings == null)
@@ -1282,9 +1321,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     if (viewport.hasHiddenColumns() && !settings.isExportHiddenColumns())
     {
-      omitHidden = viewport.getViewAsString(false);
+      omitHidden = viewport.getViewAsString(false,
+              settings.isExportHiddenSequences());
     }
 
+    int[] alignmentStartEnd = new int[2];
     if (hasHiddenSeqs && settings.isExportHiddenSequences())
     {
       alignmentToExport = hiddenSeqs.getFullAlignment();
@@ -1292,67 +1333,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     else
     {
       alignmentToExport = viewport.getAlignment();
-      alignmentStartEnd = getStartEnd(alignmentStartEnd, viewport
-              .getColumnSelection().getHiddenColumns());
     }
+    alignmentStartEnd = alignmentToExport
+            .getVisibleStartAndEndIndex(viewport.getColumnSelection()
+                    .getHiddenColumns());
     AlignmentExportData ed = new AlignmentExportData(alignmentToExport,
             omitHidden, alignmentStartEnd, settings);
     return ed;
   }
 
-  public static int[] getStartEnd(int[] aligmentStartEnd,
-          List<int[]> hiddenCols)
-  {
-    int startPos = aligmentStartEnd[0];
-    int endPos = aligmentStartEnd[1];
-
-    int[] lowestRange = new int[2];
-    int[] higestRange = new int[2];
-
-    for (int[] hiddenCol : hiddenCols)
-    {
-      // System.out.println("comparing : " + hiddenCol[0] + "-" + hiddenCol[1]);
-      lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
-      higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
-    }
-    // System.out.println("min : " + lowestRange[0] + "-" + lowestRange[1]);
-    // System.out.println("max : " + higestRange[0] + "-" + higestRange[1]);
-
-    if (lowestRange[0] == 0 && lowestRange[1] == 0)
-    {
-      startPos = aligmentStartEnd[0];
-    }
-    else
-    {
-      startPos = lowestRange[1] + 1;
-    }
-
-    if (higestRange[0] == 0 && higestRange[1] == 0)
-    {
-      endPos = aligmentStartEnd[1];
-    }
-    else
-    {
-      endPos = higestRange[0];
-    }
-
-    // System.out.println("Export range : " + minPos + " - " + maxPos);
-    return new int[] { startPos, endPos };
-  }
-
-  public static void main(String[] args)
-  {
-    ArrayList<int[]> hiddenCols = new ArrayList<int[]>();
-    hiddenCols.add(new int[] { 0, 4 });
-    hiddenCols.add(new int[] { 6, 9 });
-    hiddenCols.add(new int[] { 11, 12 });
-    hiddenCols.add(new int[] { 33, 33 });
-    hiddenCols.add(new int[] { 45, 50 });
-
-    int[] x = getStartEnd(new int[] { 0, 50 }, hiddenCols);
-    // System.out.println("Export range : " + x[0] + " - " + x[1]);
-  }
-
   /**
    * DOCUMENT ME!
    * 
@@ -1362,14 +1351,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void htmlMenuItem_actionPerformed(ActionEvent e)
   {
-    new HtmlSvgOutput(null, alignPanel);
+    HtmlSvgOutput htmlSVG = new HtmlSvgOutput(alignPanel);
+    htmlSVG.exportHTML(null);
   }
 
   @Override
   public void bioJSMenuItem_actionPerformed(ActionEvent e)
   {
     BioJsHTMLOutput bjs = new BioJsHTMLOutput(alignPanel);
-    bjs.exportJalviewAlignmentAsBioJsHtmlFile();
+    bjs.exportHTML(null);
   }
 
   public void createImageMap(File file, String image)
@@ -1401,6 +1391,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     alignPanel.makeEPS(f);
   }
 
+  @Override
   public void createSVG(File f)
   {
     alignPanel.makeSVG(f);
@@ -1574,6 +1565,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
+  @Override
   public void addHistoryItem(CommandI command)
   {
     if (command.getSize() > 0)
@@ -1977,7 +1969,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           return;
         }
 
-        format = new IdentifyFile().Identify(str, "Paste");
+        format = new IdentifyFile().identify(str, "Paste");
 
       } catch (OutOfMemoryError er)
       {
@@ -2372,20 +2364,24 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
      */
     if (sg.getSize() == viewport.getAlignment().getHeight())
     {
-      int confirm = JOptionPane.showConfirmDialog(this,
-              MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
-              MessageManager.getString("label.delete_all"), // $NON-NLS-1$
-              JOptionPane.OK_CANCEL_OPTION);
-
-      if (confirm == JOptionPane.CANCEL_OPTION
-              || confirm == JOptionPane.CLOSED_OPTION)
+      boolean isEntireAlignWidth = (((sg.getEndRes() - sg.getStartRes()) + 1) == viewport
+              .getAlignment().getWidth()) ? true : false;
+      if (isEntireAlignWidth)
       {
-        return;
+        int confirm = JOptionPane.showConfirmDialog(this,
+                MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
+                MessageManager.getString("label.delete_all"), // $NON-NLS-1$
+                JOptionPane.OK_CANCEL_OPTION);
+
+        if (confirm == JOptionPane.CANCEL_OPTION
+                || confirm == JOptionPane.CLOSED_OPTION)
+        {
+          return;
+        }
       }
       viewport.getColumnSelection().removeElements(sg.getStartRes(),
               sg.getEndRes() + 1);
     }
-
     SequenceI[] cut = sg.getSequences()
             .toArray(new SequenceI[sg.getSize()]);
 
@@ -2447,7 +2443,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     sg.setEndRes(viewport.getAlignment().getWidth() - 1);
     viewport.setSelectionGroup(sg);
     viewport.sendSelection();
-    alignPanel.paintAlignment(true);
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    alignPanel.paintAlignment(false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
   }
 
@@ -2470,7 +2469,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.setSelectionGroup(null);
     alignPanel.getSeqPanel().seqCanvas.highlightSearchResults(null);
     alignPanel.getIdPanel().getIdCanvas().searchResults = null;
-    alignPanel.paintAlignment(true);
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    alignPanel.paintAlignment(false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
     viewport.sendSelection();
   }
@@ -2497,6 +2499,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
     }
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
 
     alignPanel.paintAlignment(true);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
@@ -2540,7 +2545,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     ColumnSelection colSel = viewport.getColumnSelection();
     int column;
 
-    if (colSel.size() > 0)
+    if (!colSel.isEmpty())
     {
       if (trimLeft)
       {
@@ -2565,18 +2570,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       TrimRegionCommand trimRegion;
       if (trimLeft)
       {
-        trimRegion = new TrimRegionCommand("Remove Left",
-                TrimRegionCommand.TRIM_LEFT, seqs, column,
-                viewport.getAlignment(), viewport.getColumnSelection(),
-                viewport.getSelectionGroup());
+        trimRegion = new TrimRegionCommand("Remove Left", true, seqs,
+                column, viewport.getAlignment());
         viewport.setStartRes(0);
       }
       else
       {
-        trimRegion = new TrimRegionCommand("Remove Right",
-                TrimRegionCommand.TRIM_RIGHT, seqs, column,
-                viewport.getAlignment(), viewport.getColumnSelection(),
-                viewport.getSelectionGroup());
+        trimRegion = new TrimRegionCommand("Remove Right", false, seqs,
+                column, viewport.getAlignment());
       }
 
       statusBar.setText(MessageManager.formatMessage(
@@ -2848,7 +2849,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void expandViews_actionPerformed(ActionEvent e)
   {
-    Desktop.instance.explodeViews(this);
+    Desktop.explodeViews(this);
   }
 
   /**
@@ -2918,8 +2919,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.setFollowHighlight(state);
     if (state)
     {
-      alignPanel.scrollToPosition(
-              alignPanel.getSeqPanel().seqCanvas.searchResults, false);
+      alignPanel.scrollToPosition(viewport.getSearchResults(), false);
     }
   }
 
@@ -2963,6 +2963,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.showAllHiddenColumns();
     repaint();
+    viewport.sendSelection();
   }
 
   @Override
@@ -2988,9 +2989,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       // Hide everything by the current selection - this is a hack - we do the
       // invert and then hide
       // first check that there will be visible columns after the invert.
-      if ((viewport.getColumnSelection() != null
-              && viewport.getColumnSelection().getSelected() != null && viewport
-              .getColumnSelection().getSelected().size() > 0)
+      if (viewport.hasSelectedColumns()
               || (sg != null && sg.getSize() > 0 && sg.getStartRes() <= sg
                       .getEndRes()))
       {
@@ -3018,8 +3017,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         hideSelSequences_actionPerformed(null);
         hide = true;
       }
-      else if (!(toggleCols && viewport.getColumnSelection().getSelected()
-              .size() > 0))
+      else if (!(toggleCols && viewport.hasSelectedColumns()))
       {
         showAllSeqs_actionPerformed(null);
       }
@@ -3027,7 +3025,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     if (toggleCols)
     {
-      if (viewport.getColumnSelection().getSelected().size() > 0)
+      if (viewport.hasSelectedColumns())
       {
         hideSelColumns_actionPerformed(null);
         if (!toggleSeqs)
@@ -3053,6 +3051,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void hideAllButSelection_actionPerformed(ActionEvent e)
   {
     toggleHiddenRegions(false, false);
+    viewport.sendSelection();
   }
 
   /*
@@ -3070,6 +3069,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.hideAllSelectedSeqs();
     viewport.hideSelectedColumns();
     alignPanel.paintAlignment(true);
+    viewport.sendSelection();
   }
 
   /*
@@ -3085,6 +3085,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.showAllHiddenColumns();
     viewport.showAllHiddenSeqs();
     alignPanel.paintAlignment(true);
+    viewport.sendSelection();
   }
 
   @Override
@@ -3092,6 +3093,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     viewport.hideSelectedColumns();
     alignPanel.paintAlignment(true);
+    viewport.sendSelection();
   }
 
   @Override
@@ -3222,30 +3224,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * Set or clear 'Show Sequence Features'
-   * 
-   * @param evt
-   *          DOCUMENT ME!
-   */
-  @Override
-  public void showSeqFeaturesHeight_actionPerformed(ActionEvent evt)
-  {
-    viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
-            .isSelected());
-    if (viewport.isShowSequenceFeaturesHeight())
-    {
-      // ensure we're actually displaying features
-      viewport.setShowSequenceFeatures(true);
-      showSeqFeatures.setSelected(true);
-    }
-    alignPanel.paintAlignment(true);
-    if (alignPanel.getOverviewPanel() != null)
-    {
-      alignPanel.getOverviewPanel().updateOverviewImage();
-    }
-  }
-
-  /**
    * Action on toggle of the 'Show annotations' menu item. This shows or hides
    * the annotations panel as a whole.
    * 
@@ -3493,6 +3471,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param cs
    *          DOCUMENT ME!
    */
+  @Override
   public void changeColour(ColourSchemeI cs)
   {
     // TODO: pull up to controller method
@@ -3512,6 +3491,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel,
                 cs, "Background"));
       }
+      if (cs instanceof TCoffeeColourScheme)
+      {
+        tcoffeeColour.setEnabled(true);
+        tcoffeeColour.setSelected(true);
+      }
     }
 
     viewport.setGlobalColourScheme(cs);
@@ -3645,35 +3629,50 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           @Override
           public void mousePressed(MouseEvent evt)
           {
-            if (evt.isControlDown()
-                    || SwingUtilities.isRightMouseButton(evt))
+            if (evt.isPopupTrigger()) // Mac
             {
-              radioItem.removeActionListener(radioItem.getActionListeners()[0]);
+              offerRemoval(radioItem);
+            }
+          }
 
-              int option = JOptionPane.showInternalConfirmDialog(
-                      jalview.gui.Desktop.desktop,
-                      MessageManager
-                              .getString("label.remove_from_default_list"),
-                      MessageManager
-                              .getString("label.remove_user_defined_colour"),
-                      JOptionPane.YES_NO_OPTION);
-              if (option == JOptionPane.YES_OPTION)
-              {
-                jalview.gui.UserDefinedColours
-                        .removeColourFromDefaults(radioItem.getText());
-                colourMenu.remove(radioItem);
-              }
-              else
+          @Override
+          public void mouseReleased(MouseEvent evt)
+          {
+            if (evt.isPopupTrigger()) // Windows
+            {
+              offerRemoval(radioItem);
+            }
+          }
+
+          /**
+           * @param radioItem
+           */
+          void offerRemoval(final JRadioButtonMenuItem radioItem)
+          {
+            radioItem.removeActionListener(radioItem.getActionListeners()[0]);
+
+            int option = JOptionPane.showInternalConfirmDialog(
+                    jalview.gui.Desktop.desktop, MessageManager
+                            .getString("label.remove_from_default_list"),
+                    MessageManager
+                            .getString("label.remove_user_defined_colour"),
+                    JOptionPane.YES_NO_OPTION);
+            if (option == JOptionPane.YES_OPTION)
+            {
+              jalview.gui.UserDefinedColours
+                      .removeColourFromDefaults(radioItem.getText());
+              colourMenu.remove(radioItem);
+            }
+            else
+            {
+              radioItem.addActionListener(new ActionListener()
               {
-                radioItem.addActionListener(new ActionListener()
+                @Override
+                public void actionPerformed(ActionEvent evt)
                 {
-                  @Override
-                  public void actionPerformed(ActionEvent evt)
-                  {
-                    userDefinedColour_actionPerformed(evt);
-                  }
-                });
-              }
+                  userDefinedColour_actionPerformed(evt);
+                }
+              });
             }
           }
         });
@@ -4150,7 +4149,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         JMenuItem tm = new JMenuItem();
         ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
-        if (sm.isProtein() == !viewport.getAlignment().isNucleotide())
+        if (sm.isDNA() == viewport.getAlignment().isNucleotide()
+                || sm.isProtein() == !viewport.getAlignment()
+                        .isNucleotide())
         {
           String smn = MessageManager.getStringOrReturn(
                   "label.score_model_", sm.getName());
@@ -4463,22 +4464,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           // object broker mechanism.
           final Vector<JMenu> wsmenu = new Vector<JMenu>();
           final IProgressIndicator af = me;
+
+          /*
+           * do not i18n these strings - they are hard-coded in class
+           * compbio.data.msa.Category, Jws2Discoverer.isRecalculable() and
+           * SequenceAnnotationWSClient.initSequenceAnnotationWSClient()
+           */
           final JMenu msawsmenu = new JMenu("Alignment");
           final JMenu secstrmenu = new JMenu(
                   "Secondary Structure Prediction");
           final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
           final JMenu analymenu = new JMenu("Analysis");
           final JMenu dismenu = new JMenu("Protein Disorder");
-          // final JMenu msawsmenu = new
-          // JMenu(MessageManager.getString("label.alignment"));
-          // final JMenu secstrmenu = new
-          // JMenu(MessageManager.getString("label.secondary_structure_prediction"));
-          // final JMenu seqsrchmenu = new
-          // JMenu(MessageManager.getString("label.sequence_database_search"));
-          // final JMenu analymenu = new
-          // JMenu(MessageManager.getString("label.analysis"));
-          // final JMenu dismenu = new
-          // JMenu(MessageManager.getString("label.protein_disorder"));
           // JAL-940 - only show secondary structure prediction services from
           // the legacy server
           if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
@@ -4637,81 +4634,46 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
-  /*
-   * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
-   * chooser = new JalviewFileChooser(jalview.bin.Cache.
-   * getProperty("LAST_DIRECTORY"));
-   * 
-   * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
-   * to Vamsas file"); chooser.setToolTipText("Export");
-   * 
-   * int value = chooser.showSaveDialog(this);
-   * 
-   * if (value == JalviewFileChooser.APPROVE_OPTION) {
-   * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
-   * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
-   * chooser.getSelectedFile().getAbsolutePath(), this); } }
-   */
   /**
-   * prototype of an automatically enabled/disabled analysis function
+   * Searches the alignment sequences for xRefs and builds the Show
+   * Cross-References menu (formerly called Show Products), with database
+   * sources for which cross-references are found (protein sources for a
+   * nucleotide alignment and vice versa)
    * 
+   * @return true if Show Cross-references menu should be enabled
    */
-  protected void setShowProductsEnabled()
+  public boolean canShowProducts()
   {
-    SequenceI[] selection = viewport.getSequenceSelection();
-    if (canShowProducts(selection, viewport.getSelectionGroup() != null,
-            viewport.getAlignment().getDataset()))
-    {
-      showProducts.setEnabled(true);
+    SequenceI[] seqs = viewport.getAlignment().getSequencesArray();
+    AlignmentI dataset = viewport.getAlignment().getDataset();
 
-    }
-    else
+    showProducts.removeAll();
+    final boolean dna = viewport.getAlignment().isNucleotide();
+
+    if (seqs == null || seqs.length == 0)
     {
-      showProducts.setEnabled(false);
+      // nothing to see here.
+      return false;
     }
-  }
 
-  /**
-   * search selection for sequence xRef products and build the show products
-   * menu.
-   * 
-   * @param selection
-   * @param dataset
-   * @return true if showProducts menu should be enabled.
-   */
-  public boolean canShowProducts(SequenceI[] selection,
-          boolean isRegionSelection, Alignment dataset)
-  {
     boolean showp = false;
     try
     {
-      showProducts.removeAll();
-      final boolean dna = viewport.getAlignment().isNucleotide();
-      final Alignment ds = dataset;
-      String[] ptypes = (selection == null || selection.length == 0) ? null
-              : CrossRef.findSequenceXrefTypes(dna, selection, dataset);
-      // Object[] prods =
-      // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
-      // selection, dataset, true);
-      final SequenceI[] sel = selection;
-      for (int t = 0; ptypes != null && t < ptypes.length; t++)
+      List<String> ptypes = new CrossRef(seqs, dataset)
+              .findXrefSourcesForSequences(dna);
+
+      for (final String source : ptypes)
       {
         showp = true;
-        final boolean isRegSel = isRegionSelection;
         final AlignFrame af = this;
-        final String source = ptypes[t];
-        JMenuItem xtype = new JMenuItem(ptypes[t]);
+        JMenuItem xtype = new JMenuItem(source);
         xtype.addActionListener(new ActionListener()
         {
-
           @Override
           public void actionPerformed(ActionEvent e)
           {
-            // TODO: new thread for this call with vis-delay
-            af.showProductsFor(af.viewport.getSequenceSelection(),
-                    isRegSel, dna, source);
+            showProductsFor(af.viewport.getSequenceSelection(), dna, source);
           }
-
         });
         showProducts.add(xtype);
       }
@@ -4719,156 +4681,30 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       showProducts.setEnabled(showp);
     } catch (Exception e)
     {
-      jalview.bin.Cache.log
-              .warn("canTranslate threw an exception - please report to help at jalview.org",
+      Cache.log
+              .warn("canShowProducts threw an exception - please report to help at jalview.org",
                       e);
       return false;
     }
     return showp;
   }
 
+  /**
+   * Finds and displays cross-references for the selected sequences (protein
+   * products for nucleotide sequences, dna coding sequences for peptides).
+   * 
+   * @param sel
+   *          the sequences to show cross-references for
+   * @param dna
+   *          true if from a nucleotide alignment (so showing proteins)
+   * @param source
+   *          the database to show cross-references for
+   */
   protected void showProductsFor(final SequenceI[] sel,
-          final boolean isRegSel, final boolean dna, final String source)
+          final boolean _odna, final String source)
   {
-    Runnable foo = new Runnable()
-    {
-
-      @Override
-      public void run()
-      {
-        final long sttime = System.currentTimeMillis();
-        AlignFrame.this.setProgressBar(MessageManager.formatMessage(
-                "status.searching_for_sequences_from",
-                new Object[] { source }), sttime);
-        try
-        {
-          // update our local dataset reference
-          Alignment ds = AlignFrame.this.getViewport().getAlignment()
-                  .getDataset();
-          Alignment prods = CrossRef
-                  .findXrefSequences(sel, dna, source, ds);
-          if (prods != null)
-          {
-            SequenceI[] sprods = new SequenceI[prods.getHeight()];
-            for (int s = 0; s < sprods.length; s++)
-            {
-              sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
-              if (ds.getSequences() == null
-                      || !ds.getSequences().contains(
-                              sprods[s].getDatasetSequence()))
-              {
-                ds.addSequence(sprods[s].getDatasetSequence());
-              }
-              sprods[s].updatePDBIds();
-            }
-            Alignment al = new Alignment(sprods);
-            al.setDataset(ds);
-
-            /*
-             * Copy dna-to-protein mappings to new alignment
-             */
-            // TODO 1: no mappings are set up for EMBL product
-            // TODO 2: if they were, should add them to protein alignment, not
-            // dna
-            Set<AlignedCodonFrame> cf = prods.getCodonFrames();
-            for (AlignedCodonFrame acf : cf)
-            {
-              al.addCodonFrame(acf);
-            }
-            AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
-                    DEFAULT_HEIGHT);
-            String newtitle = "" + ((dna) ? "Proteins" : "Nucleotides")
-                    + " for " + ((isRegSel) ? "selected region of " : "")
-                    + getTitle();
-            naf.setTitle(newtitle);
-
-            // temporary flag until SplitFrame is released
-            boolean asSplitFrame = Cache.getDefault(
-                    Preferences.ENABLE_SPLIT_FRAME, true);
-            if (asSplitFrame)
-            {
-              /*
-               * Make a copy of this alignment (sharing the same dataset
-               * sequences). If we are DNA, drop introns and update mappings
-               */
-              AlignmentI copyAlignment = null;
-              final SequenceI[] sequenceSelection = AlignFrame.this.viewport
-                      .getSequenceSelection();
-              if (dna)
-              {
-                copyAlignment = AlignmentUtils.makeExonAlignment(
-                        sequenceSelection, cf);
-                al.getCodonFrames().clear();
-                al.getCodonFrames().addAll(cf);
-                final StructureSelectionManager ssm = StructureSelectionManager
-                        .getStructureSelectionManager(Desktop.instance);
-                ssm.registerMappings(cf);
-              }
-              else
-              {
-                copyAlignment = new Alignment(new Alignment(
-                        sequenceSelection));
-              }
-              AlignFrame copyThis = new AlignFrame(copyAlignment,
-                      AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
-              copyThis.setTitle(AlignFrame.this.getTitle());
-              // SplitFrame with dna above, protein below
-              SplitFrame sf = new SplitFrame(dna ? copyThis : naf,
-                      dna ? naf : copyThis);
-              naf.setVisible(true);
-              copyThis.setVisible(true);
-              String linkedTitle = MessageManager
-                      .getString("label.linked_view_title");
-              Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
-            }
-            else
-            {
-              Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
-                      DEFAULT_HEIGHT);
-            }
-          }
-          else
-          {
-            System.err.println("No Sequences generated for xRef type "
-                    + source);
-          }
-        } catch (Exception e)
-        {
-          jalview.bin.Cache.log.error(
-                  "Exception when finding crossreferences", e);
-        } catch (OutOfMemoryError e)
-        {
-          new OOMWarning("whilst fetching crossreferences", e);
-        } catch (Error e)
-        {
-          jalview.bin.Cache.log.error("Error when finding crossreferences",
-                  e);
-        }
-        AlignFrame.this.setProgressBar(MessageManager.formatMessage(
-                "status.finished_searching_for_sequences_from",
-                new Object[] { source }), sttime);
-      }
-
-    };
-    Thread frunner = new Thread(foo);
-    frunner.start();
-  }
-
-  public boolean canShowTranslationProducts(SequenceI[] selection,
-          AlignmentI alignment)
-  {
-    // old way
-    try
-    {
-      return (jalview.analysis.Dna.canTranslate(selection,
-              viewport.getViewAsVisibleContigs(true)));
-    } catch (Exception e)
-    {
-      jalview.bin.Cache.log
-              .warn("canTranslate threw an exception - please report to help at jalview.org",
-                      e);
-      return false;
-    }
+    new Thread(CrossRefAction.showProductsFor(sel, _odna, source, this))
+            .start();
   }
 
   /**
@@ -4892,7 +4728,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               .getString("label.error_when_translating_sequences_submit_bug_report");
       final String errorTitle = MessageManager
               .getString("label.implementation_error")
-              + MessageManager.getString("translation_failed");
+              + MessageManager.getString("label.translation_failed");
       JOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
               JOptionPane.ERROR_MESSAGE);
       return;
@@ -4990,50 +4826,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void drop(DropTargetDropEvent evt)
   {
+    // JAL-1552 - acceptDrop required before getTransferable call for
+    // Java's Transferable for native dnd
+    evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
     Transferable t = evt.getTransferable();
-    java.util.List files = null;
+    java.util.List<String> files = new ArrayList<String>(), protocols = new ArrayList<String>();
 
     try
     {
-      DataFlavor uriListFlavor = new DataFlavor(
-              "text/uri-list;class=java.lang.String");
-      if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
-      {
-        // Works on Windows and MacOSX
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        files = (java.util.List) t
-                .getTransferData(DataFlavor.javaFileListFlavor);
-      }
-      else if (t.isDataFlavorSupported(uriListFlavor))
-      {
-        // This is used by Unix drag system
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        String data = (String) t.getTransferData(uriListFlavor);
-        files = new java.util.ArrayList(1);
-        for (java.util.StringTokenizer st = new java.util.StringTokenizer(
-                data, "\r\n"); st.hasMoreTokens();)
-        {
-          String s = st.nextToken();
-          if (s.startsWith("#"))
-          {
-            // the line is a comment (as per the RFC 2483)
-            continue;
-          }
-
-          java.net.URI uri = new java.net.URI(s);
-          // check to see if we can handle this kind of URI
-          if (uri.getScheme().toLowerCase().startsWith("http"))
-          {
-            files.add(uri.toString());
-          }
-          else
-          {
-            // otherwise preserve old behaviour: catch all for file objects
-            java.io.File file = new java.io.File(uri);
-            files.add(file.toString());
-          }
-        }
-      }
+      Desktop.transferFromDropTarget(files, protocols, evt, t);
     } catch (Exception e)
     {
       e.printStackTrace();
@@ -5088,14 +4889,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               String type = null;
               try
               {
-                type = new IdentifyFile().Identify(file, protocol);
+                type = new IdentifyFile().identify(file, protocol);
               } catch (Exception ex)
               {
                 type = null;
               }
               if (type != null)
               {
-                if (type.equalsIgnoreCase("PDB"))
+                if (StructureFile.isStructureFile(type))
                 {
                   filesmatched.add(new Object[] { file, protocol, mtch });
                   continue;
@@ -5115,14 +4916,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                                   this,
                                   MessageManager
                                           .formatMessage(
-                                                  "label.automatically_associate_pdb_files_with_sequences_same_name",
+                                                  "label.automatically_associate_structure_files_with_sequences_same_name",
                                                   new Object[] { Integer
                                                           .valueOf(
                                                                   filesmatched
                                                                           .size())
                                                           .toString() }),
                                   MessageManager
-                                          .getString("label.automatically_associate_pdb_files_by_name"),
+                                          .getString("label.automatically_associate_structure_files_by_name"),
                                   JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
 
           {
@@ -5187,7 +4988,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
   /**
    * Attempt to load a "dropped" file or URL string: First by testing whether
-   * it's and Annotation file, then a JNet file, and finally a features file. If
+   * it's an Annotation file, then a JNet file, and finally a features file. If
    * all are false then the user may have dropped an alignment file onto this
    * AlignFrame.
    * 
@@ -5201,7 +5002,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       if (protocol == null)
       {
-        protocol = jalview.io.FormatAdapter.checkProtocol(file);
+        protocol = FormatAdapter.checkProtocol(file);
       }
       // if the file isn't identified, or not positively identified as some
       // other filetype (PFAM is default unidentified alignment file type) then
@@ -5262,7 +5063,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           // try to parse it as a features file
           if (format == null)
           {
-            format = new IdentifyFile().Identify(file, protocol);
+            format = new IdentifyFile().identify(file, protocol);
           }
           if (format.equalsIgnoreCase("JnetFile"))
           {
@@ -5278,42 +5079,17 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             viewport.setColumnSelection(cs);
             isAnnotation = true;
           }
-          else
+          else if (IdentifyFile.FeaturesFile.equals(format))
           {
-            /*
-             * if (format.equalsIgnoreCase("PDB")) {
-             * 
-             * String pdbfn = ""; // try to match up filename with sequence id
-             * try { if (protocol == jalview.io.FormatAdapter.FILE) { File fl =
-             * new File(file); pdbfn = fl.getName(); } else if (protocol ==
-             * jalview.io.FormatAdapter.URL) { URL url = new URL(file); pdbfn =
-             * url.getFile(); } } catch (Exception e) { } ; if (assocSeq ==
-             * null) { SequenceIdMatcher idm = new SequenceIdMatcher(viewport
-             * .getAlignment().getSequencesArray()); if (pdbfn.length() > 0) {
-             * // attempt to find a match in the alignment SequenceI mtch =
-             * idm.findIdMatch(pdbfn); int l = 0, c = pdbfn.indexOf("."); while
-             * (mtch == null && c != -1) { while ((c = pdbfn.indexOf(".", l)) >
-             * l) { l = c; } if (l > -1) { pdbfn = pdbfn.substring(0, l); } mtch
-             * = idm.findIdMatch(pdbfn); } if (mtch != null) { // try and
-             * associate // prompt ? PDBEntry pe = new AssociatePdbFileWithSeq()
-             * .associatePdbWithSeq(file, protocol, mtch, true); if (pe != null)
-             * { System.err.println("Associated file : " + file + " with " +
-             * mtch.getDisplayId(true)); alignPanel.paintAlignment(true); } } //
-             * TODO: maybe need to load as normal otherwise return; } }
-             */
-            // try to parse it as a features file
-            boolean isGroupsFile = parseFeaturesFile(file, protocol);
-            // if it wasn't a features file then we just treat it as a general
-            // alignment file to load into the current view.
-            if (!isGroupsFile)
-            {
-              new FileLoader().LoadFile(viewport, file, protocol, format);
-            }
-            else
+            if (parseFeaturesFile(file, protocol))
             {
               alignPanel.paintAlignment(true);
             }
           }
+          else
+          {
+            new FileLoader().LoadFile(viewport, file, protocol, format);
+          }
         }
       }
       if (isAnnotation)
@@ -5335,7 +5111,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       } catch (Exception x)
       {
       }
-      ;
       new OOMWarning(
               "loading data "
                       + (protocol != null ? (protocol.equals(FormatAdapter.PASTE) ? "from clipboard."
@@ -5385,7 +5160,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void tabbedPane_mousePressed(MouseEvent e)
   {
-    if (SwingUtilities.isRightMouseButton(e))
+    if (e.isPopupTrigger())
     {
       String msg = MessageManager.getString("label.enter_view_name");
       String reply = JOptionPane.showInternalInputDialog(this, msg, msg,
@@ -5518,13 +5293,23 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         new Thread(new Runnable()
         {
-
           @Override
           public void run()
           {
-            new jalview.ws.DBRefFetcher(alignPanel.av
-                    .getSequenceSelection(), alignPanel.alignFrame)
-                    .fetchDBRefs(false);
+            boolean isNucleotide = alignPanel.alignFrame.getViewport()
+                    .getAlignment().isNucleotide();
+            DBRefFetcher dbRefFetcher = new DBRefFetcher(alignPanel.av
+                    .getSequenceSelection(), alignPanel.alignFrame, null,
+                    alignPanel.alignFrame.featureSettings, isNucleotide);
+            dbRefFetcher.addListener(new FetchFinishedListenerI()
+            {
+              @Override
+              public void finished()
+              {
+                AlignFrame.this.setMenusForViewport();
+              }
+            });
+            dbRefFetcher.fetchDBRefs(false);
           }
         }).start();
 
@@ -5538,7 +5323,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       @Override
       public void run()
       {
-        final jalview.ws.SequenceFetcher sf = SequenceFetcher
+        final jalview.ws.SequenceFetcher sf = jalview.gui.SequenceFetcher
                 .getSequenceFetcherSingleton(me);
         javax.swing.SwingUtilities.invokeLater(new Runnable()
         {
@@ -5592,10 +5377,24 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       @Override
                       public void run()
                       {
-                        new jalview.ws.DBRefFetcher(alignPanel.av
-                                .getSequenceSelection(),
-                                alignPanel.alignFrame, dassource)
-                                .fetchDBRefs(false);
+                        boolean isNucleotide = alignPanel.alignFrame
+                                .getViewport().getAlignment()
+                                .isNucleotide();
+                        DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                                alignPanel.av.getSequenceSelection(),
+                                alignPanel.alignFrame, dassource,
+                                alignPanel.alignFrame.featureSettings,
+                                isNucleotide);
+                        dbRefFetcher
+                                .addListener(new FetchFinishedListenerI()
+                                {
+                                  @Override
+                                  public void finished()
+                                  {
+                                    AlignFrame.this.setMenusForViewport();
+                                  }
+                                });
+                        dbRefFetcher.fetchDBRefs(false);
                       }
                     }).start();
                   }
@@ -5628,10 +5427,24 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       @Override
                       public void run()
                       {
-                        new jalview.ws.DBRefFetcher(alignPanel.av
-                                .getSequenceSelection(),
-                                alignPanel.alignFrame, dassource)
-                                .fetchDBRefs(false);
+                        boolean isNucleotide = alignPanel.alignFrame
+                                .getViewport().getAlignment()
+                                .isNucleotide();
+                        DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                                alignPanel.av.getSequenceSelection(),
+                                alignPanel.alignFrame, dassource,
+                                alignPanel.alignFrame.featureSettings,
+                                isNucleotide);
+                        dbRefFetcher
+                                .addListener(new FetchFinishedListenerI()
+                                {
+                                  @Override
+                                  public void finished()
+                                  {
+                                    AlignFrame.this.setMenusForViewport();
+                                  }
+                                });
+                        dbRefFetcher.fetchDBRefs(false);
                       }
                     }).start();
                   }
@@ -5679,10 +5492,24 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                         @Override
                         public void run()
                         {
-                          new jalview.ws.DBRefFetcher(alignPanel.av
-                                  .getSequenceSelection(),
-                                  alignPanel.alignFrame, dassrc)
-                                  .fetchDBRefs(false);
+                          boolean isNucleotide = alignPanel.alignFrame
+                                  .getViewport().getAlignment()
+                                  .isNucleotide();
+                          DBRefFetcher dbRefFetcher = new DBRefFetcher(
+                                  alignPanel.av.getSequenceSelection(),
+                                  alignPanel.alignFrame, dassrc,
+                                  alignPanel.alignFrame.featureSettings,
+                                  isNucleotide);
+                          dbRefFetcher
+                                  .addListener(new FetchFinishedListenerI()
+                                  {
+                                    @Override
+                                    public void finished()
+                                    {
+                                      AlignFrame.this.setMenusForViewport();
+                                    }
+                                  });
+                          dbRefFetcher.fetchDBRefs(false);
                         }
                       }).start();
                     }
@@ -5746,6 +5573,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.firePropertyChange("alignment", null, al);
   }
 
+  @Override
   public void setShowSeqFeatures(boolean b)
   {
     showSeqFeatures.setSelected(b);
@@ -5922,8 +5750,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   protected void setAnnotationsVisibility(boolean visible,
           boolean forSequences, boolean forAlignment)
   {
-    for (AlignmentAnnotation aa : alignPanel.getAlignment()
-            .getAlignmentAnnotation())
+    AlignmentAnnotation[] anns = alignPanel.getAlignment()
+            .getAlignmentAnnotation();
+    if (anns == null)
+    {
+      return;
+    }
+    for (AlignmentAnnotation aa : anns)
     {
       /*
        * don't display non-positional annotations on an alignment
@@ -5972,7 +5805,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     // TODO no longer a menu action - refactor as required
     final AlignmentI alignment = getViewport().getAlignment();
-    Set<AlignedCodonFrame> mappings = alignment.getCodonFrames();
+    List<AlignedCodonFrame> mappings = alignment.getCodonFrames();
     if (mappings == null)
     {
       return;
@@ -6027,6 +5860,98 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       sf.setComplementVisible(this, show);
     }
   }
+
+  /**
+   * Generate the reverse (optionally complemented) of the selected sequences,
+   * and add them to the alignment
+   */
+  @Override
+  protected void showReverse_actionPerformed(boolean complement)
+  {
+    AlignmentI al = null;
+    try
+    {
+      Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
+      al = dna.reverseCdna(complement);
+      viewport.addAlignment(al, "");
+      addHistoryItem(new EditCommand(
+              MessageManager.getString("label.add_sequences"),
+              Action.PASTE, al.getSequencesArray(), 0, al.getWidth(),
+              viewport.getAlignment()));
+    } catch (Exception ex)
+    {
+      System.err.println(ex.getMessage());
+      return;
+    }
+  }
+
+  /**
+   * Try to run a script in the Groovy console, having first ensured that this
+   * AlignFrame is set as currentAlignFrame in Desktop, to allow the script to
+   * be targeted at this alignment.
+   */
+  @Override
+  protected void runGroovy_actionPerformed()
+  {
+    Jalview.setCurrentAlignFrame(this);
+    groovy.ui.Console console = Desktop.getGroovyConsole();
+    if (console != null)
+    {
+      try
+      {
+        console.runScript();
+      } catch (Exception ex)
+      {
+        System.err.println((ex.toString()));
+        JOptionPane
+                .showInternalMessageDialog(Desktop.desktop, MessageManager
+                        .getString("label.couldnt_run_groovy_script"),
+                        MessageManager
+                                .getString("label.groovy_support_failed"),
+                        JOptionPane.ERROR_MESSAGE);
+      }
+    }
+    else
+    {
+      System.err.println("Can't run Groovy script as console not found");
+    }
+  }
+
+  /**
+   * Hides columns containing (or not containing) a specified feature, provided
+   * that would not leave all columns hidden
+   * 
+   * @param featureType
+   * @param columnsContaining
+   * @return
+   */
+  public boolean hideFeatureColumns(String featureType,
+          boolean columnsContaining)
+  {
+    boolean notForHiding = avc.markColumnsContainingFeatures(
+            columnsContaining, false, false, featureType);
+    if (notForHiding)
+    {
+      if (avc.markColumnsContainingFeatures(!columnsContaining, false,
+              false, featureType))
+      {
+        getViewport().hideSelectedColumns();
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
+  protected void selectHighlightedColumns_actionPerformed(
+          ActionEvent actionEvent)
+  {
+    // include key modifier check in case user selects from menu
+    avc.markHighlightedColumns(
+            (actionEvent.getModifiers() & ActionEvent.ALT_MASK) != 0,
+            true,
+            (actionEvent.getModifiers() & (ActionEvent.META_MASK | ActionEvent.CTRL_MASK)) != 0);
+  }
 }
 
 class PrintThread extends Thread
diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java
index 8a95015..6576050 100644
--- a/src/jalview/gui/AlignViewport.java
+++ b/src/jalview/gui/AlignViewport.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,24 +18,6 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-/*
- * Jalview - A Sequence Alignment Editor and Viewer
- * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
- */
 package jalview.gui;
 
 import jalview.analysis.AlignmentUtils;
@@ -43,6 +25,9 @@ import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.NJTree;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureColourI;
+import jalview.api.FeatureSettingsModelI;
+import jalview.api.FeaturesDisplayedI;
 import jalview.api.ViewStyleI;
 import jalview.bin.Cache;
 import jalview.commands.CommandI;
@@ -52,6 +37,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -72,7 +58,6 @@ import java.awt.Rectangle;
 import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
-import java.util.Set;
 import java.util.Vector;
 
 import javax.swing.JInternalFrame;
@@ -417,10 +402,11 @@ public class AlignViewport extends AlignmentViewport implements
    * @param align
    *          DOCUMENT ME!
    */
+  @Override
   public void setAlignment(AlignmentI align)
   {
     replaceMappings(align);
-    this.alignment = align;
+    super.setAlignment(align);
   }
 
   /**
@@ -461,7 +447,7 @@ public class AlignViewport extends AlignmentViewport implements
     AlignmentI al = getAlignment();
     if (al != null)
     {
-      Set<AlignedCodonFrame> mappings = al.getCodonFrames();
+      List<AlignedCodonFrame> mappings = al.getCodonFrames();
       if (mappings != null)
       {
         StructureSelectionManager ssm = StructureSelectionManager
@@ -482,6 +468,7 @@ public class AlignViewport extends AlignmentViewport implements
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public char getGapCharacter()
   {
     return getAlignment().getGapCharacter();
@@ -504,16 +491,6 @@ public class AlignViewport extends AlignmentViewport implements
   /**
    * DOCUMENT ME!
    * 
-   * @return DOCUMENT ME!
-   */
-  public ColumnSelection getColumnSelection()
-  {
-    return colSel;
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
    * @param tree
    *          DOCUMENT ME!
    */
@@ -617,6 +594,7 @@ public class AlignViewport extends AlignmentViewport implements
   /**
    * Send the current selection to be broadcast to any selection listeners.
    */
+  @Override
   public void sendSelection()
   {
     jalview.structure.StructureSelectionManager
@@ -658,39 +636,6 @@ public class AlignViewport extends AlignmentViewport implements
   }
 
   /**
-   * synthesize a column selection if none exists so it covers the given
-   * selection group. if wholewidth is false, no column selection is made if the
-   * selection group covers the whole alignment width.
-   * 
-   * @param sg
-   * @param wholewidth
-   */
-  public void expandColSelection(SequenceGroup sg, boolean wholewidth)
-  {
-    int sgs, sge;
-    if (sg != null
-            && (sgs = sg.getStartRes()) >= 0
-            && sg.getStartRes() <= (sge = sg.getEndRes())
-            && (colSel == null || colSel.getSelected() == null || colSel
-                    .getSelected().size() == 0))
-    {
-      if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
-      {
-        // do nothing
-        return;
-      }
-      if (colSel == null)
-      {
-        colSel = new ColumnSelection();
-      }
-      for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
-      {
-        colSel.addElement(cspos);
-      }
-    }
-  }
-
-  /**
    * Returns the (Desktop) instance of the StructureSelectionManager
    */
   @Override
@@ -711,31 +656,49 @@ public class AlignViewport extends AlignmentViewport implements
     List<SequenceI[]> seqvectors = new ArrayList<SequenceI[]>();
     for (PDBEntry pdb : pdbEntries)
     {
-      List<SequenceI> seqs = new ArrayList<SequenceI>();
+      List<SequenceI> choosenSeqs = new ArrayList<SequenceI>();
       for (SequenceI sq : alignment.getSequences())
       {
-        Vector<PDBEntry> pdbs = sq.getDatasetSequence().getAllPDBEntries();
-        if (pdbs == null)
+        Vector<PDBEntry> pdbRefEntries = sq.getDatasetSequence()
+                .getAllPDBEntries();
+        if (pdbRefEntries == null)
         {
           continue;
         }
-        for (PDBEntry p1 : pdbs)
+        for (PDBEntry pdbRefEntry : pdbRefEntries)
         {
-          if (p1.getId().equals(pdb.getId()))
+          if (pdbRefEntry.getId().equals(pdb.getId()))
           {
-            if (!seqs.contains(sq))
+            if (pdbRefEntry.getChainCode() != null
+                    && pdb.getChainCode() != null)
             {
-              seqs.add(sq);
-              continue;
+              if (pdbRefEntry.getChainCode().equalsIgnoreCase(
+                      pdb.getChainCode())
+                      && !choosenSeqs.contains(sq))
+              {
+                choosenSeqs.add(sq);
+                continue;
+              }
             }
+            else
+            {
+              if (!choosenSeqs.contains(sq))
+              {
+                choosenSeqs.add(sq);
+                continue;
+              }
+            }
+
           }
         }
       }
-      seqvectors.add(seqs.toArray(new SequenceI[seqs.size()]));
+      seqvectors
+              .add(choosenSeqs.toArray(new SequenceI[choosenSeqs.size()]));
     }
     return seqvectors.toArray(new SequenceI[seqvectors.size()][]);
   }
 
+  @Override
   public boolean isNormaliseSequenceLogo()
   {
     return normaliseSequenceLogo;
@@ -750,6 +713,7 @@ public class AlignViewport extends AlignmentViewport implements
    * 
    * @return true if alignment characters should be displayed
    */
+  @Override
   public boolean isValidCharWidth()
   {
     return validCharWidth;
@@ -825,10 +789,10 @@ public class AlignViewport extends AlignmentViewport implements
    * may give the user the option to open a new frame, or split panel, with cDNA
    * and protein linked.
    * 
-   * @param al
+   * @param toAdd
    * @param title
    */
-  public void addAlignment(AlignmentI al, String title)
+  public void addAlignment(AlignmentI toAdd, String title)
   {
     // TODO: promote to AlignViewportI? applet CutAndPasteTransfer is different
 
@@ -841,25 +805,26 @@ public class AlignViewport extends AlignmentViewport implements
     // TODO: create undo object for this JAL-1101
 
     /*
-     * If any cDNA/protein mappings can be made between the alignments, offer to
-     * open a linked alignment with split frame option.
+     * Ensure datasets are created for the new alignment as
+     * mappings operate on dataset sequences
+     */
+    toAdd.setDataset(null);
+
+    /*
+     * Check if any added sequence could be the object of a mapping or
+     * cross-reference; if so, make the mapping explicit 
+     */
+    getAlignment().realiseMappings(toAdd.getSequences());
+
+    /*
+     * If any cDNA/protein mappings exist or can be made between the alignments, 
+     * offer to open a split frame with linked alignments
      */
     if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
     {
-      if (al.getDataset() == null)
+      if (AlignmentUtils.isMappable(toAdd, getAlignment()))
       {
-        // need to create ds seqs
-        for (SequenceI sq : al.getSequences())
-        {
-          if (sq.getDatasetSequence() == null)
-          {
-            sq.createDatasetSequence();
-          }
-        }
-      }
-      if (AlignmentUtils.isMappable(al, getAlignment()))
-      {
-        if (openLinkedAlignment(al, title))
+        if (openLinkedAlignment(toAdd, title))
         {
           return;
         }
@@ -872,9 +837,22 @@ public class AlignViewport extends AlignmentViewport implements
     // TODO: JAL-407 regardless of above - identical sequences (based on ID and
     // provenance) should share the same dataset sequence
 
-    for (int i = 0; i < al.getHeight(); i++)
+    AlignmentI al = getAlignment();
+    String gap = String.valueOf(al.getGapCharacter());
+    for (int i = 0; i < toAdd.getHeight(); i++)
     {
-      getAlignment().addSequence(al.getSequenceAt(i));
+      SequenceI seq = toAdd.getSequenceAt(i);
+      /*
+       * experimental!
+       * - 'align' any mapped sequences as per existing 
+       *    e.g. cdna to genome, domain hit to protein sequence
+       * very experimental! (need a separate menu option for this)
+       * - only add mapped sequences ('select targets from a dataset')
+       */
+      if (true /*AlignmentUtils.alignSequenceAs(seq, al, gap, true, true)*/)
+      {
+        al.addSequence(seq);
+      }
     }
 
     setEndSeq(getAlignment().getHeight());
@@ -926,7 +904,7 @@ public class AlignViewport extends AlignmentViewport implements
      * is a pre-requisite for building mappings.
      */
     al.setDataset(null);
-    AlignmentUtils.mapProteinToCdna(protein, cdna);
+    AlignmentUtils.mapProteinAlignmentToCdna(protein, cdna);
 
     /*
      * Create the AlignFrame for the added alignment. If it is protein, mappings
@@ -1065,14 +1043,14 @@ public class AlignViewport extends AlignmentViewport implements
      * there is no complement, or it is not following highlights, or no mapping
      * is found, the result will be empty.
      */
-    SearchResults sr = new SearchResults();
+    SearchResultsI sr = new SearchResults();
     int verticalOffset = findComplementScrollTarget(sr);
     if (!sr.isEmpty())
     {
       // TODO would like next line without cast but needs more refactoring...
       final AlignmentPanel complementPanel = ((AlignViewport) getCodingComplement())
               .getAlignPanel();
-      complementPanel.setFollowingComplementScroll(true);
+      complementPanel.setDontScrollComplement(true);
       complementPanel.scrollToCentre(sr, verticalOffset);
     }
   }
@@ -1107,4 +1085,68 @@ public class AlignViewport extends AlignmentViewport implements
     return true;
   }
 
+  /**
+   * Applies the supplied feature settings descriptor to currently known
+   * features. This supports an 'initial configuration' of feature colouring
+   * based on a preset or user favourite. This may then be modified in the usual
+   * way using the Feature Settings dialogue.
+   * 
+   * @param featureSettings
+   */
+  @Override
+  public void applyFeaturesStyle(FeatureSettingsModelI featureSettings)
+  {
+    if (featureSettings == null)
+    {
+      return;
+    }
+
+    FeatureRenderer fr = getAlignPanel().getSeqPanel().seqCanvas
+            .getFeatureRenderer();
+    fr.findAllFeatures(true);
+    List<String> renderOrder = fr.getRenderOrder();
+    FeaturesDisplayedI displayed = fr.getFeaturesDisplayed();
+    displayed.clear();
+    // TODO this clears displayed.featuresRegistered - do we care?
+
+    /*
+     * set feature colour if specified by feature settings
+     * set visibility of all features
+     */
+    for (String type : renderOrder)
+    {
+      FeatureColourI preferredColour = featureSettings
+              .getFeatureColour(type);
+      if (preferredColour != null)
+      {
+        fr.setColour(type, preferredColour);
+      }
+      if (featureSettings.isFeatureDisplayed(type))
+      {
+        displayed.setVisible(type);
+      }
+    }
+
+    /*
+     * set visibility of feature groups
+     */
+    for (String group : fr.getFeatureGroups())
+    {
+      fr.setGroupVisibility(group, featureSettings.isGroupDisplayed(group));
+    }
+
+    /*
+     * order the features
+     */
+    if (featureSettings.optimiseOrder())
+    {
+      // TODO not supported (yet?)
+    }
+    else
+    {
+      fr.orderFeatures(featureSettings);
+    }
+    fr.setTransparency(featureSettings.getTransparency());
+  }
+
 }
diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java
index 1906829..d74a0c6 100644
--- a/src/jalview/gui/AlignmentPanel.java
+++ b/src/jalview/gui/AlignmentPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -25,7 +25,7 @@ import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -99,7 +99,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * Flag set while scrolling to follow complementary cDNA/protein scroll. When
    * true, suppresses invoking the same method recursively.
    */
-  private boolean followingComplementScroll;
+  private boolean dontScrollComplement;
+
+  private PropertyChangeListener propertyChangeListener;
 
   /**
    * Creates a new AlignmentPanel object.
@@ -135,8 +137,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
     vscroll.addAdjustmentListener(this);
 
     final AlignmentPanel ap = this;
-    av.addPropertyChangeListener(new PropertyChangeListener()
+    propertyChangeListener = new PropertyChangeListener()
     {
+      @Override
       public void propertyChange(PropertyChangeEvent evt)
       {
         if (evt.getPropertyName().equals("alignment"))
@@ -145,7 +148,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
           alignmentChanged();
         }
       }
-    });
+    };
+    av.addPropertyChangeListener(propertyChangeListener);
     fontChanged();
     adjustAnnotationHeight();
     updateLayout();
@@ -293,7 +297,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * Highlight the given results on the alignment.
    * 
    */
-  public void highlightSearchResults(SearchResults results)
+  public void highlightSearchResults(SearchResultsI results)
   {
     scrollToPosition(results);
     getSeqPanel().seqCanvas.highlightSearchResults(results);
@@ -305,7 +309,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * 
    * @param results
    */
-  public boolean scrollToPosition(SearchResults results)
+  public boolean scrollToPosition(SearchResultsI results)
   {
     return scrollToPosition(results, 0, true, false);
   }
@@ -318,7 +322,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @param redrawOverview
    * @return
    */
-  public boolean scrollToPosition(SearchResults searchResults,
+  public boolean scrollToPosition(SearchResultsI searchResults,
           boolean redrawOverview)
   {
     return scrollToPosition(searchResults, 0, redrawOverview, false);
@@ -338,7 +342,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    *          if true, try to centre the search results horizontally in the view
    * @return false if results were not found
    */
-  public boolean scrollToPosition(SearchResults results,
+  public boolean scrollToPosition(SearchResultsI results,
           int verticalOffset, boolean redrawOverview, boolean centre)
   {
     int startv, endv, starts, ends;
@@ -509,6 +513,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * automatically adjust annotation panel height for new annotation whilst
    * ensuring the alignment is still visible.
    */
+  @Override
   public void adjustAnnotationHeight()
   {
     // TODO: display vertical annotation scrollbar if necessary
@@ -566,6 +571,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
     annotationScroller.setPreferredSize(new Dimension(annotationScroller
             .getWidth(), annotationHeight));
 
+    Dimension e = idPanel.getSize();
+    alabels.setSize(new Dimension(e.width, annotationHeight));
+
     annotationSpaceFillerHolder.setPreferredSize(new Dimension(
             annotationSpaceFillerHolder.getWidth(), annotationHeight));
     annotationScroller.validate();
@@ -729,7 +737,14 @@ public class AlignmentPanel extends GAlignmentPanel implements
       x = 0;
     }
 
+    /*
+     * each scroll adjustment triggers adjustmentValueChanged, which resets the
+     * 'do not scroll complement' flag; ensure it is the same for both
+     * operations
+     */
+    boolean flag = isDontScrollComplement();
     hscroll.setValues(x, hextent, 0, width);
+    setDontScrollComplement(flag);
     vscroll.setValues(y, vextent, 0, height);
   }
 
@@ -739,6 +754,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
     int oldX = av.getStartRes();
@@ -772,6 +788,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
           // as preference setting
           SwingUtilities.invokeLater(new Runnable()
           {
+            @Override
             public void run()
             {
               setScrollValues(av.getStartRes(), av.getStartSeq());
@@ -828,9 +845,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
      * If there is one, scroll the (Protein/cDNA) complementary alignment to
      * match, unless we are ourselves doing that.
      */
-    if (isFollowingComplementScroll())
+    if (isDontScrollComplement())
     {
-      setFollowingComplementScroll(false);
+      setDontScrollComplement(false);
     }
     else
     {
@@ -842,6 +859,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * Repaint the alignment including the annotations and overview panels (if
    * shown).
    */
+  @Override
   public void paintAlignment(boolean updateOverview)
   {
     final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),
@@ -868,6 +886,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @param g
    *          DOCUMENT ME!
    */
+  @Override
   public void paintComponent(Graphics g)
   {
     invalidate();
@@ -877,6 +896,12 @@ public class AlignmentPanel extends GAlignmentPanel implements
     hscrollFillerPanel.setPreferredSize(new Dimension(d.width, 12));
     validate();
 
+    /*
+     * set scroll bar positions; first suppress this being 'followed' in any
+     * complementary split pane
+     */
+    setDontScrollComplement(true);
+
     if (av.getWrapAlignment())
     {
       int maxwidth = av.getAlignment().getWidth();
@@ -920,6 +945,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @throws PrinterException
    *           DOCUMENT ME!
    */
+  @Override
   public int print(Graphics pg, PageFormat pf, int pi)
           throws PrinterException
   {
@@ -930,11 +956,11 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
     if (av.getWrapAlignment())
     {
-      return printWrappedAlignment(pg, pwidth, pheight, pi);
+      return printWrappedAlignment(pwidth, pheight, pi, pg);
     }
     else
     {
-      return printUnwrapped(pg, pwidth, pheight, pi);
+      return printUnwrapped(pwidth, pheight, pi, pg, pg);
     }
   }
 
@@ -955,84 +981,103 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @throws PrinterException
    *           DOCUMENT ME!
    */
-  public int printUnwrapped(Graphics pg, int pwidth, int pheight, int pi)
+  /**
+   * Draws the alignment image, including sequence ids, sequences, and
+   * annotation labels and annotations if shown, on either one or two Graphics
+   * context.
+   * 
+   * @param pageWidth
+   * @param pageHeight
+   * @param pi
+   * @param idGraphics
+   *          the graphics context for sequence ids and annotation labels
+   * @param alignmentGraphics
+   *          the graphics context for sequences and annotations (may or may not
+   *          be the same context as idGraphics)
+   * @return
+   * @throws PrinterException
+   */
+  public int printUnwrapped(int pageWidth, int pageHeight, int pi,
+          Graphics idGraphics, Graphics alignmentGraphics)
           throws PrinterException
   {
-    int idWidth = getVisibleIdWidth(false);
-    FontMetrics fm = getFontMetrics(av.getFont());
-    int scaleHeight = av.getCharHeight() + fm.getDescent();
+    final int idWidth = getVisibleIdWidth(false);
 
-    pg.setColor(Color.white);
-    pg.fillRect(0, 0, pwidth, pheight);
-    pg.setFont(av.getFont());
-
-    // //////////////////////////////////
-    // / How many sequences and residues can we fit on a printable page?
-    int totalRes = (pwidth - idWidth) / av.getCharWidth();
+    /*
+     * Get the horizontal offset to where we draw the sequences.
+     * This is idWidth if using a single Graphics context, else zero.
+     */
+    final int alignmentGraphicsOffset = idGraphics != alignmentGraphics ? 0 : idWidth;
 
-    int totalSeq = (pheight - scaleHeight) / av.getCharHeight() - 1;
+    FontMetrics fm = getFontMetrics(av.getFont());
+    int charHeight = av.getCharHeight();
+    int scaleHeight = charHeight + fm.getDescent();
 
-    int pagesWide = (av.getAlignment().getWidth() / totalRes) + 1;
+    idGraphics.setColor(Color.white);
+    idGraphics.fillRect(0, 0, pageWidth, pageHeight);
+    idGraphics.setFont(av.getFont());
 
-    // ///////////////////////////
-    // / Only print these sequences and residues on this page
-    int startRes;
+    /*
+     * How many sequences and residues can we fit on a printable page?
+     */
+    int totalRes = (pageWidth - idWidth) / av.getCharWidth();
 
-    // ///////////////////////////
-    // / Only print these sequences and residues on this page
-    int endRes;
+    int totalSeq = (pageHeight - scaleHeight) / charHeight - 1;
 
-    // ///////////////////////////
-    // / Only print these sequences and residues on this page
-    int startSeq;
+    int alignmentWidth = av.getAlignment().getWidth();
+    int pagesWide = (alignmentWidth / totalRes) + 1;
 
-    // ///////////////////////////
-    // / Only print these sequences and residues on this page
-    int endSeq;
-    startRes = (pi % pagesWide) * totalRes;
-    endRes = (startRes + totalRes) - 1;
+    final int startRes = (pi % pagesWide) * totalRes;
+    int endRes = (startRes + totalRes) - 1;
 
-    if (endRes > (av.getAlignment().getWidth() - 1))
+    if (endRes > (alignmentWidth - 1))
     {
-      endRes = av.getAlignment().getWidth() - 1;
+      endRes = alignmentWidth - 1;
     }
 
-    startSeq = (pi / pagesWide) * totalSeq;
-    endSeq = startSeq + totalSeq;
+    final int startSeq = (pi / pagesWide) * totalSeq;
+    int endSeq = startSeq + totalSeq;
 
-    if (endSeq > av.getAlignment().getHeight())
+    int alignmentHeight = av.getAlignment().getHeight();
+    if (endSeq > alignmentHeight)
     {
-      endSeq = av.getAlignment().getHeight();
+      endSeq = alignmentHeight;
     }
 
-    int pagesHigh = ((av.getAlignment().getHeight() / totalSeq) + 1)
-            * pheight;
+    int pagesHigh = ((alignmentHeight / totalSeq) + 1)
+            * pageHeight;
 
     if (av.isShowAnnotation())
     {
       pagesHigh += getAnnotationPanel().adjustPanelHeight() + 3;
     }
 
-    pagesHigh /= pheight;
+    pagesHigh /= pageHeight;
 
     if (pi >= (pagesWide * pagesHigh))
     {
       return Printable.NO_SUCH_PAGE;
     }
+    final int alignmentDrawnHeight = (endSeq - startSeq) * charHeight
+            + 3;
 
-    // draw Scale
-    pg.translate(idWidth, 0);
-    getScalePanel().drawScale(pg, startRes, endRes, pwidth - idWidth,
-            scaleHeight);
-    pg.translate(-idWidth, scaleHeight);
+    /*
+     * draw the Scale at horizontal offset, then reset to top left (0, 0)
+     */
+    alignmentGraphics.translate(alignmentGraphicsOffset, 0);
+    getScalePanel().drawScale(alignmentGraphics, startRes, endRes,
+            pageWidth - idWidth, scaleHeight);
+    alignmentGraphics.translate(-alignmentGraphicsOffset, 0);
 
-    // //////////////
-    // Draw the ids
+    /*
+     * Draw the sequence ids, offset for scale height,
+     * then reset to top left (0, 0)
+     */
+    idGraphics.translate(0, scaleHeight);
+    idGraphics.setFont(getIdPanel().getIdCanvas().getIdfont());
     Color currentColor = null;
     Color currentTextColor = null;
 
-    pg.setFont(getIdPanel().getIdCanvas().getIdfont());
-
     SequenceI seq;
     for (int i = startSeq; i < endSeq; i++)
     {
@@ -1040,6 +1085,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
       if ((av.getSelectionGroup() != null)
               && av.getSelectionGroup().getSequences(null).contains(seq))
       {
+        /*
+         * gray out ids of sequences in selection group (if any)
+         */
         currentColor = Color.gray;
         currentTextColor = Color.black;
       }
@@ -1049,45 +1097,58 @@ public class AlignmentPanel extends GAlignmentPanel implements
         currentTextColor = Color.black;
       }
 
-      pg.setColor(currentColor);
-      pg.fillRect(0, (i - startSeq) * av.getCharHeight(), idWidth,
-              av.getCharHeight());
+      idGraphics.setColor(currentColor);
+      idGraphics.fillRect(0, (i - startSeq) * charHeight, idWidth,
+              charHeight);
 
-      pg.setColor(currentTextColor);
+      idGraphics.setColor(currentTextColor);
 
       int xPos = 0;
+      String displayId = seq.getDisplayId(av.getShowJVSuffix());
       if (av.isRightAlignIds())
       {
-        fm = pg.getFontMetrics();
+        fm = idGraphics.getFontMetrics();
         xPos = idWidth
-                - fm.stringWidth(seq.getDisplayId(av.getShowJVSuffix()))
+                - fm.stringWidth(displayId)
                 - 4;
       }
 
-      pg.drawString(seq.getDisplayId(av.getShowJVSuffix()), xPos,
-              (((i - startSeq) * av.getCharHeight()) + av.getCharHeight())
-                      - (av.getCharHeight() / 5));
+      idGraphics.drawString(displayId, xPos,
+              (((i - startSeq) * charHeight) + charHeight)
+                      - (charHeight / 5));
     }
+    idGraphics.setFont(av.getFont());
+    idGraphics.translate(0, -scaleHeight);
 
-    pg.setFont(av.getFont());
+    /*
+     * draw the sequences, offset for scale height, and id width (if using a
+     * single graphics context), then reset to (0, scale height)
+     */
+    alignmentGraphics.translate(alignmentGraphicsOffset, scaleHeight);
+    getSeqPanel().seqCanvas.drawPanel(alignmentGraphics, startRes, endRes,
+            startSeq, endSeq, 0);
+    alignmentGraphics.translate(-alignmentGraphicsOffset, 0);
 
-    // draw main sequence panel
-    pg.translate(idWidth, 0);
-    getSeqPanel().seqCanvas.drawPanel(pg, startRes, endRes, startSeq,
-            endSeq, 0);
-
-    if (av.isShowAnnotation() && (endSeq == av.getAlignment().getHeight()))
-    {
-      // draw annotation - need to offset for current scroll position
-      int offset = -getAlabels().getScrollOffset();
-      pg.translate(0, offset);
-      pg.translate(-idWidth - 3, (endSeq - startSeq) * av.getCharHeight()
-              + 3);
-      getAlabels().drawComponent(pg, idWidth);
-      pg.translate(idWidth + 3, 0);
+    if (av.isShowAnnotation() && (endSeq == alignmentHeight))
+    {
+      /*
+       * draw annotation labels; drawComponent() translates by
+       * getScrollOffset(), so compensate for that first;
+       * then reset to (0, scale height)
+       */
+      int offset = getAlabels().getScrollOffset();
+      idGraphics.translate(0, -offset);
+      idGraphics.translate(0, alignmentDrawnHeight);
+      getAlabels().drawComponent(idGraphics, idWidth);
+      idGraphics.translate(0, -alignmentDrawnHeight);
+
+      /*
+       * draw the annotations starting at 
+       * (idOffset, alignmentHeight) from (0, scaleHeight)
+       */
+      alignmentGraphics.translate(alignmentGraphicsOffset, alignmentDrawnHeight);
       getAnnotationPanel().renderer.drawComponent(getAnnotationPanel(), av,
-              pg, -1, startRes, endRes + 1);
-      pg.translate(0, -offset);
+              alignmentGraphics, -1, startRes, endRes + 1);
     }
 
     return Printable.PAGE_EXISTS;
@@ -1110,8 +1171,8 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @throws PrinterException
    *           DOCUMENT ME!
    */
-  public int printWrappedAlignment(Graphics pg, int pwidth, int pheight,
-          int pi) throws PrinterException
+  public int printWrappedAlignment(int pwidth, int pheight, int pi,
+          Graphics pg) throws PrinterException
   {
     int annotationHeight = 0;
     AnnotationLabels labels = null;
@@ -1238,22 +1299,26 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (onscreen
             || (idwidth = Cache.getIntegerProperty("FIGURE_FIXEDIDWIDTH")) == null)
     {
-      return (getIdPanel().getWidth() > 0 ? getIdPanel().getWidth()
-              : calculateIdWidth().width + 4);
+      int w = getIdPanel().getWidth();
+      return (w > 0 ? w : calculateIdWidth().width + 4);
     }
     return idwidth.intValue() + 4;
   }
 
   void makeAlignmentImage(jalview.util.ImageMaker.TYPE type, File file)
   {
-    long progress = System.currentTimeMillis();
+    int boarderBottomOffset = 5;
+    long pSessionId = System.currentTimeMillis();
     headless = (System.getProperty("java.awt.headless") != null && System
             .getProperty("java.awt.headless").equals("true"));
     if (alignFrame != null && !headless)
     {
-      alignFrame.setProgressBar(MessageManager.formatMessage(
-              "status.saving_file", new Object[] { type.getLabel() }),
-              progress);
+      if (file != null)
+      {
+        alignFrame.setProgressBar(MessageManager.formatMessage(
+                "status.saving_file", new Object[] { type.getLabel() }),
+                pSessionId);
+      }
     }
     try
     {
@@ -1279,26 +1344,30 @@ public class AlignmentPanel extends GAlignmentPanel implements
         }
 
         im = new jalview.util.ImageMaker(this, type, imageAction,
-                aDimension.getWidth(), aDimension.getHeight(), file,
-                imageTitle);
+                aDimension.getWidth(), aDimension.getHeight()
+                        + boarderBottomOffset, file, imageTitle,
+                alignFrame, pSessionId, headless);
+        Graphics graphics = im.getGraphics();
         if (av.getWrapAlignment())
         {
-          if (im.getGraphics() != null)
+          if (graphics != null)
           {
-            printWrappedAlignment(im.getGraphics(), aDimension.getWidth(),
-                    aDimension.getHeight(), 0);
+            printWrappedAlignment(aDimension.getWidth(),
+                    aDimension.getHeight() + boarderBottomOffset, 0,
+                    graphics);
             im.writeImage();
           }
         }
         else
         {
-          if (im.getGraphics() != null)
+          if (graphics != null)
           {
-            printUnwrapped(im.getGraphics(), aDimension.getWidth(),
-                    aDimension.getHeight(), 0);
+            printUnwrapped(aDimension.getWidth(), aDimension.getHeight(),
+                    0, graphics, graphics);
             im.writeImage();
           }
         }
+
       } catch (OutOfMemoryError err)
       {
         // Be noisy here.
@@ -1312,12 +1381,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
     } finally
     {
-      if (alignFrame != null && !headless)
-      {
-        alignFrame.setProgressBar(
-                MessageManager.getString("status.export_complete"),
-                progress);
-      }
+
     }
   }
 
@@ -1384,7 +1448,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
   public void makePNGImageMap(File imgMapFile, String imageName)
   {
-    // /////ONLY WORKS WITH NONE WRAPPED ALIGNMENTS
+    // /////ONLY WORKS WITH NON WRAPPED ALIGNMENTS
     // ////////////////////////////////////////////
     int idWidth = getVisibleIdWidth(false);
     FontMetrics fm = getFontMetrics(av.getFont());
@@ -1398,7 +1462,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
       {
         int s, sSize = av.getAlignment().getHeight(), res, alwidth = av
                 .getAlignment().getWidth(), g, gSize, f, fSize, sy;
-        StringBuffer text = new StringBuffer();
         PrintWriter out = new PrintWriter(new FileWriter(imgMapFile));
         out.println(jalview.io.HTMLOutput.getImageMapHTML());
         out.println("<img src=\"" + imageName
@@ -1414,7 +1477,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
           SequenceGroup[] groups = av.getAlignment().findAllGroups(seq);
           for (res = 0; res < alwidth; res++)
           {
-            text = new StringBuffer();
+            StringBuilder text = new StringBuilder();
             String triplet = null;
             if (av.getAlignment().isNucleotide())
             {
@@ -1438,18 +1501,20 @@ public class AlignmentPanel extends GAlignmentPanel implements
             {
               if (text.length() < 1)
               {
-                text.append("<area shape=\"rect\" coords=\""
-                        + (idWidth + res * av.getCharWidth()) + "," + sy
-                        + "," + (idWidth + (res + 1) * av.getCharWidth())
-                        + "," + (av.getCharHeight() + sy) + "\""
-                        + " onMouseOver=\"toolTip('" + alIndex + " "
-                        + triplet);
+                text.append("<area shape=\"rect\" coords=\"")
+                        .append((idWidth + res * av.getCharWidth()))
+                        .append(",").append(sy).append(",")
+                        .append((idWidth + (res + 1) * av.getCharWidth()))
+                        .append(",").append((av.getCharHeight() + sy))
+                        .append("\"").append(" onMouseOver=\"toolTip('")
+                        .append(alIndex).append(" ").append(triplet);
               }
 
               if (groups[g].getStartRes() < res
                       && groups[g].getEndRes() > res)
               {
-                text.append("<br><em>" + groups[g].getName() + "</em>");
+                text.append("<br><em>").append(groups[g].getName())
+                        .append("</em>");
               }
             }
 
@@ -1457,12 +1522,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
             {
               if (text.length() < 1)
               {
-                text.append("<area shape=\"rect\" coords=\""
-                        + (idWidth + res * av.getCharWidth()) + "," + sy
-                        + "," + (idWidth + (res + 1) * av.getCharWidth())
-                        + "," + (av.getCharHeight() + sy) + "\""
-                        + " onMouseOver=\"toolTip('" + alIndex + " "
-                        + triplet);
+                text.append("<area shape=\"rect\" coords=\"")
+                        .append((idWidth + res * av.getCharWidth()))
+                        .append(",").append(sy).append(",")
+                        .append((idWidth + (res + 1) * av.getCharWidth()))
+                        .append(",").append((av.getCharHeight() + sy))
+                        .append("\"").append(" onMouseOver=\"toolTip('")
+                        .append(alIndex).append(" ").append(triplet);
               }
               fSize = features.length;
               for (f = 0; f < fSize; f++)
@@ -1471,15 +1537,15 @@ public class AlignmentPanel extends GAlignmentPanel implements
                 if ((features[f].getBegin() <= seq.findPosition(res))
                         && (features[f].getEnd() >= seq.findPosition(res)))
                 {
-                  if (features[f].getType().equals("disulfide bond"))
+                  if (features[f].isContactFeature())
                   {
                     if (features[f].getBegin() == seq.findPosition(res)
                             || features[f].getEnd() == seq
                                     .findPosition(res))
                     {
-                      text.append("<br>disulfide bond "
-                              + features[f].getBegin() + ":"
-                              + features[f].getEnd());
+                      text.append("<br>").append(features[f].getType())
+                              .append(" ").append(features[f].getBegin())
+                              .append(":").append(features[f].getEnd());
                     }
                   }
                   else
@@ -1490,13 +1556,13 @@ public class AlignmentPanel extends GAlignmentPanel implements
                             && !features[f].getType().equals(
                                     features[f].getDescription()))
                     {
-                      text.append(" " + features[f].getDescription());
+                      text.append(" ").append(features[f].getDescription());
                     }
 
                     if (features[f].getValue("status") != null)
                     {
-                      text.append(" (" + features[f].getValue("status")
-                              + ")");
+                      text.append(" (").append(features[f].getValue("status"))
+                              .append(")");
                     }
                   }
                 }
@@ -1571,8 +1637,18 @@ public class AlignmentPanel extends GAlignmentPanel implements
     PaintRefresher.RemoveComponent(getSeqPanel().seqCanvas);
     PaintRefresher.RemoveComponent(getIdPanel().getIdCanvas());
     PaintRefresher.RemoveComponent(this);
+
+    /*
+     * try to ensure references are nulled
+     */
+    if (annotationPanel != null)
+    {
+      annotationPanel.dispose();
+    }
+
     if (av != null)
     {
+      av.removePropertyChangeListener(propertyChangeListener);
       jalview.structure.StructureSelectionManager ssm = av
               .getStructureSelectionManager();
       ssm.removeStructureViewerListener(getSeqPanel(), null);
@@ -1580,7 +1656,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
       ssm.removeCommandListener(av);
       ssm.removeStructureViewerListener(getSeqPanel(), null);
       ssm.removeSelectionListener(getSeqPanel());
-      av.setAlignment(null);
+      av.dispose();
       av = null;
     }
     else
@@ -1751,14 +1827,14 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * @param verticalOffset
    *          the number of visible sequences to show above the mapped region
    */
-  public void scrollToCentre(SearchResults sr, int verticalOffset)
+  public void scrollToCentre(SearchResultsI sr, int verticalOffset)
   {
     /*
      * To avoid jumpy vertical scrolling (if some sequences are gapped or not
      * mapped), we can make the scroll-to location a sequence above the one
      * actually mapped.
      */
-    SequenceI mappedTo = sr.getResultSequence(0);
+    SequenceI mappedTo = sr.getResults().get(0).getSequence();
     List<SequenceI> seqs = av.getAlignment().getSequences();
 
     /*
@@ -1786,17 +1862,17 @@ public class AlignmentPanel extends GAlignmentPanel implements
   }
 
   /**
-   * Set a flag to say we are scrolling to follow a (cDNA/protein) complement.
+   * Set a flag to say do not scroll any (cDNA/protein) complement.
    * 
    * @param b
    */
-  protected void setFollowingComplementScroll(boolean b)
+  protected void setDontScrollComplement(boolean b)
   {
-    this.followingComplementScroll = b;
+    this.dontScrollComplement = b;
   }
 
-  protected boolean isFollowingComplementScroll()
+  protected boolean isDontScrollComplement()
   {
-    return this.followingComplementScroll;
+    return this.dontScrollComplement;
   }
 }
diff --git a/src/jalview/gui/AnnotationChooser.java b/src/jalview/gui/AnnotationChooser.java
index 35b6032..0017c26 100644
--- a/src/jalview/gui/AnnotationChooser.java
+++ b/src/jalview/gui/AnnotationChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/AnnotationColourChooser.java b/src/jalview/gui/AnnotationColourChooser.java
index adfb520..214359c 100644
--- a/src/jalview/gui/AnnotationColourChooser.java
+++ b/src/jalview/gui/AnnotationColourChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -141,7 +141,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       default:
         throw new Error(
                 MessageManager
-                        .getString("error.implementation_error_dont_know_about_thereshold_setting"));
+                        .getString("error.implementation_error_dont_know_about_threshold_setting"));
       }
       thresholdIsMin.setSelected(acg.thresholdIsMinMax);
       thresholdValue.setText("" + acg.getAnnotationThreshold());
@@ -210,7 +210,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        ok_actionPerformed(e);
+        ok_actionPerformed();
       }
     });
     cancel.setOpaque(false);
@@ -220,7 +220,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        cancel_actionPerformed(e);
+        cancel_actionPerformed();
       }
     });
     defColours.setOpaque(false);
@@ -233,7 +233,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       @Override
       public void actionPerformed(ActionEvent arg0)
       {
-        resetColours_actionPerformed(arg0);
+        resetColours_actionPerformed();
       }
     });
 
@@ -242,7 +242,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        annotations_actionPerformed(e);
+        annotations_actionPerformed();
       }
     });
     getThreshold().addActionListener(new ActionListener()
@@ -250,7 +250,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        threshold_actionPerformed(e);
+        threshold_actionPerformed();
       }
     });
     thresholdValue.addActionListener(new ActionListener()
@@ -258,7 +258,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        thresholdValue_actionPerformed(e);
+        thresholdValue_actionPerformed();
       }
     });
     slider.setPaintLabels(false);
@@ -278,7 +278,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        currentColours_actionPerformed(e);
+        currentColours_actionPerformed();
       }
     });
     thresholdIsMin.setBackground(Color.white);
@@ -290,7 +290,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
-        thresholdIsMin_actionPerformed(actionEvent);
+        thresholdIsMin_actionPerformed();
       }
     });
     seqAssociated.setBackground(Color.white);
@@ -303,7 +303,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
       @Override
       public void actionPerformed(ActionEvent arg0)
       {
-        seqAssociated_actionPerformed(arg0, annotations, seqAssociated);
+        seqAssociated_actionPerformed(annotations);
       }
     });
 
@@ -332,7 +332,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     this.validate();
   }
 
-  protected void resetColours_actionPerformed(ActionEvent arg0)
+  protected void resetColours_actionPerformed()
   {
     setDefaultMinMax();
     updateView();
@@ -372,6 +372,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     updateView();
   }
 
+  @Override
   public void reset()
   {
     av.setGlobalColourScheme(oldcs);
@@ -385,6 +386,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     }
   }
 
+  @Override
   public void valueChanged(boolean updateAllAnnotation)
   {
     if (slider.isEnabled())
@@ -411,7 +413,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter
     this.threshold = threshold;
   }
 
-  public void currentColours_actionPerformed(ActionEvent e)
+  public void currentColours_actionPerformed()
   {
     if (currentColours.isSelected())
     {
diff --git a/src/jalview/gui/AnnotationColumnChooser.java b/src/jalview/gui/AnnotationColumnChooser.java
index 6b66d9c..29a3c66 100644
--- a/src/jalview/gui/AnnotationColumnChooser.java
+++ b/src/jalview/gui/AnnotationColumnChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -178,7 +178,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        ok_actionPerformed(e);
+        ok_actionPerformed();
       }
     });
 
@@ -189,7 +189,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        cancel_actionPerformed(e);
+        cancel_actionPerformed();
       }
     });
 
@@ -201,7 +201,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        threshold_actionPerformed(e);
+        threshold_actionPerformed();
       }
     });
 
@@ -212,7 +212,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        thresholdValue_actionPerformed(e);
+        thresholdValue_actionPerformed();
       }
     });
 
@@ -288,14 +288,15 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
     String defaultTtip = MessageManager
             .getString("info.change_threshold_mode_to_enable");
 
-    String threshold = getThreshold().getSelectedItem().toString();
-    if (threshold.equalsIgnoreCase("No Threshold"))
+    String thresh = getThreshold().getSelectedItem().toString();
+    if (thresh.equalsIgnoreCase("No Threshold"))
     {
       thresholdValue.setToolTipText(defaultTtip);
       slider.setToolTipText(defaultTtip);
     }
   }
 
+  @Override
   public void reset()
   {
     if (this.getOldColumnSelection() != null)
@@ -324,6 +325,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements
 
   }
 
+  @Override
   public void valueChanged(boolean updateAllAnnotation)
   {
     if (slider.isEnabled())
diff --git a/src/jalview/gui/AnnotationExporter.java b/src/jalview/gui/AnnotationExporter.java
index f7b7c61..45aad52 100644
--- a/src/jalview/gui/AnnotationExporter.java
+++ b/src/jalview/gui/AnnotationExporter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,9 @@
  */
 package jalview.gui;
 
+import jalview.api.FeatureColourI;
 import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.SequenceI;
 import jalview.io.AnnotationFile;
 import jalview.io.FeaturesFile;
 import jalview.io.JalviewFileChooser;
@@ -32,6 +34,7 @@ import java.awt.Color;
 import java.awt.FlowLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.util.Map;
 
 import javax.swing.BorderFactory;
 import javax.swing.ButtonGroup;
@@ -153,17 +156,28 @@ public class AnnotationExporter extends JPanel
             .getString("label.no_features_on_alignment");
     if (features)
     {
+      Map<String, FeatureColourI> displayedFeatureColours = ap
+              .getFeatureRenderer().getDisplayedFeatureCols();
+      FeaturesFile formatter = new FeaturesFile();
+      SequenceI[] sequences = ap.av.getAlignment().getSequencesArray();
+      Map<String, FeatureColourI> featureColours = ap.getFeatureRenderer()
+              .getDisplayedFeatureCols();
+      boolean includeNonPositional = ap.av.isShowNPFeats();
       if (GFFFormat.isSelected())
       {
-        text = new FeaturesFile().printGFFFormat(ap.av.getAlignment()
-                .getDataset().getSequencesArray(), ap.getFeatureRenderer()
-                .getDisplayedFeatureCols(), true, ap.av.isShowNPFeats());// ap.av.featuresDisplayed//);
+        text = new FeaturesFile().printGffFormat(ap.av.getAlignment()
+                .getDataset().getSequencesArray(), displayedFeatureColours,
+                true, ap.av.isShowNPFeats());
+        text = formatter.printGffFormat(sequences, featureColours, true,
+                includeNonPositional);
       }
       else
       {
         text = new FeaturesFile().printJalviewFormat(ap.av.getAlignment()
-                .getDataset().getSequencesArray(), ap.getFeatureRenderer()
-                .getDisplayedFeatureCols(), true, ap.av.isShowNPFeats()); // ap.av.featuresDisplayed);
+                .getDataset().getSequencesArray(), displayedFeatureColours,
+                true, ap.av.isShowNPFeats()); // ap.av.featuresDisplayed);
+        text = formatter.printJalviewFormat(sequences, featureColours,
+                true, includeNonPositional);
       }
     }
     else
@@ -236,6 +250,7 @@ public class AnnotationExporter extends JPanel
     toFile.setText(MessageManager.getString("label.to_file"));
     toFile.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         toFile_actionPerformed(e);
@@ -244,6 +259,7 @@ public class AnnotationExporter extends JPanel
     toTextbox.setText(MessageManager.getString("label.to_textbox"));
     toTextbox.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         toTextbox_actionPerformed(e);
@@ -252,6 +268,7 @@ public class AnnotationExporter extends JPanel
     close.setText(MessageManager.getString("action.close"));
     close.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         close_actionPerformed(e);
diff --git a/src/jalview/gui/AnnotationLabels.java b/src/jalview/gui/AnnotationLabels.java
index 60324e5..d819843 100644
--- a/src/jalview/gui/AnnotationLabels.java
+++ b/src/jalview/gui/AnnotationLabels.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -213,11 +213,13 @@ public class AnnotationLabels extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     AlignmentAnnotation[] aa = ap.av.getAlignment()
             .getAlignmentAnnotation();
 
+    boolean fullRepaint = false;
     if (evt.getActionCommand().equals(ADDNEW))
     {
       AlignmentAnnotation newAnnotation = new AlignmentAnnotation(null,
@@ -230,11 +232,16 @@ public class AnnotationLabels extends JPanel implements MouseListener,
 
       ap.av.getAlignment().addAnnotation(newAnnotation);
       ap.av.getAlignment().setAnnotationIndex(newAnnotation, 0);
+      fullRepaint = true;
     }
     else if (evt.getActionCommand().equals(EDITNAME))
     {
+      String name = aa[selectedRow].label;
       editLabelDescription(aa[selectedRow]);
-      repaint();
+      if (!name.equalsIgnoreCase(aa[selectedRow].label))
+      {
+        fullRepaint = true;
+      }
     }
     else if (evt.getActionCommand().equals(HIDE))
     {
@@ -243,6 +250,8 @@ public class AnnotationLabels extends JPanel implements MouseListener,
     else if (evt.getActionCommand().equals(DELETE))
     {
       ap.av.getAlignment().deleteAnnotation(aa[selectedRow]);
+      ap.av.getCalcManager().removeWorkerForAnnotation(aa[selectedRow]);
+      fullRepaint = true;
     }
     else if (evt.getActionCommand().equals(SHOWALL))
     {
@@ -253,6 +262,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
           aa[i].visible = true;
         }
       }
+      fullRepaint = true;
     }
     else if (evt.getActionCommand().equals(OUTPUT_TEXT))
     {
@@ -281,18 +291,30 @@ public class AnnotationLabels extends JPanel implements MouseListener,
       aa[selectedRow].scaleColLabel = !aa[selectedRow].scaleColLabel;
     }
 
-    refresh();
+    refresh(fullRepaint);
 
   }
 
   /**
    * Redraw sensibly.
+   * 
+   * @adjustHeight if true, try to recalculate panel height for visible
+   *               annotations
    */
-  protected void refresh()
+  protected void refresh(boolean adjustHeight)
   {
-    ap.validateAnnotationDimensions(false);
+    ap.validateAnnotationDimensions(adjustHeight);
     ap.addNotify();
-    ap.repaint();
+    if (adjustHeight)
+    {
+      // sort, repaint, update overview
+      ap.paintAlignment(true);
+    }
+    else
+    {
+      // lightweight repaint
+      ap.repaint();
+    }
   }
 
   /**
@@ -303,6 +325,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
    */
   boolean editLabelDescription(AlignmentAnnotation annotation)
   {
+    // TODO i18n
     EditNameDialog dialog = new EditNameDialog(annotation.label,
             annotation.description, "       Annotation Name ",
             "Annotation Description ", "Edit Annotation Name/Description",
@@ -325,416 +348,152 @@ public class AnnotationLabels extends JPanel implements MouseListener,
     return true;
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param evt
-   *          DOCUMENT ME!
-   */
+  @Override
   public void mousePressed(MouseEvent evt)
   {
     getSelectedRow(evt.getY() - getScrollOffset());
     oldY = evt.getY();
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param evt
-   *          DOCUMENT ME!
-   */
-  public void mouseReleased(MouseEvent evt)
-  {
-    int start = selectedRow;
-    getSelectedRow(evt.getY() - getScrollOffset());
-    int end = selectedRow;
-
-    if (start != end)
+    if (evt.isPopupTrigger())
     {
-      // Swap these annotations
-      AlignmentAnnotation startAA = ap.av.getAlignment()
-              .getAlignmentAnnotation()[start];
-      if (end == -1)
-      {
-        end = ap.av.getAlignment().getAlignmentAnnotation().length - 1;
-      }
-      AlignmentAnnotation endAA = ap.av.getAlignment()
-              .getAlignmentAnnotation()[end];
-
-      ap.av.getAlignment().getAlignmentAnnotation()[end] = startAA;
-      ap.av.getAlignment().getAlignmentAnnotation()[start] = endAA;
+      showPopupMenu(evt);
     }
-
-    resizePanel = false;
-    dragEvent = null;
-    repaint();
-    ap.getAnnotationPanel().repaint();
   }
 
   /**
-   * DOCUMENT ME!
+   * Build and show the Pop-up menu at the right-click mouse position
    * 
    * @param evt
-   *          DOCUMENT ME!
    */
-  public void mouseEntered(MouseEvent evt)
+  void showPopupMenu(MouseEvent evt)
   {
-    if (evt.getY() < 10)
-    {
-      resizePanel = true;
-      repaint();
-    }
-  }
+    evt.consume();
+    final AlignmentAnnotation[] aa = ap.av.getAlignment()
+            .getAlignmentAnnotation();
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param evt
-   *          DOCUMENT ME!
-   */
-  public void mouseExited(MouseEvent evt)
-  {
-    if (dragEvent == null)
+    JPopupMenu pop = new JPopupMenu(
+            MessageManager.getString("label.annotations"));
+    JMenuItem item = new JMenuItem(ADDNEW);
+    item.addActionListener(this);
+    pop.add(item);
+    if (selectedRow < 0)
     {
-      resizePanel = false;
-      repaint();
+      if (hasHiddenRows)
+      { // let the user make everything visible again
+        item = new JMenuItem(SHOWALL);
+        item.addActionListener(this);
+        pop.add(item);
+      }
+      pop.show(this, evt.getX(), evt.getY());
+      return;
     }
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param evt
-   *          DOCUMENT ME!
-   */
-  public void mouseDragged(MouseEvent evt)
-  {
-    dragEvent = evt;
-
-    if (resizePanel)
+    item = new JMenuItem(EDITNAME);
+    item.addActionListener(this);
+    pop.add(item);
+    item = new JMenuItem(HIDE);
+    item.addActionListener(this);
+    pop.add(item);
+    // JAL-1264 hide all sequence-specific annotations of this type
+    if (selectedRow < aa.length)
     {
-      Dimension d = ap.annotationScroller.getPreferredSize();
-      int dif = evt.getY() - oldY;
-
-      dif /= ap.av.getCharHeight();
-      dif *= ap.av.getCharHeight();
-
-      if ((d.height - dif) > 20)
+      if (aa[selectedRow].sequenceRef != null)
       {
-        ap.annotationScroller.setPreferredSize(new Dimension(d.width,
-                d.height - dif));
-        d = ap.annotationSpaceFillerHolder.getPreferredSize();
-        ap.annotationSpaceFillerHolder.setPreferredSize(new Dimension(
-                d.width, d.height - dif));
-        ap.paintAlignment(true);
+        final String label = aa[selectedRow].label;
+        JMenuItem hideType = new JMenuItem();
+        String text = MessageManager.getString("label.hide_all") + " "
+                + label;
+        hideType.setText(text);
+        hideType.addActionListener(new ActionListener()
+        {
+          @Override
+          public void actionPerformed(ActionEvent e)
+          {
+            AlignmentUtils.showOrHideSequenceAnnotations(
+                    ap.av.getAlignment(), Collections.singleton(label),
+                    null, false, false);
+            // for (AlignmentAnnotation ann : ap.av.getAlignment()
+            // .getAlignmentAnnotation())
+            // {
+            // if (ann.sequenceRef != null && ann.label != null
+            // && ann.label.equals(label))
+            // {
+            // ann.visible = false;
+            // }
+            // }
+            refresh(true);
+          }
+        });
+        pop.add(hideType);
       }
-
-      ap.addNotify();
     }
-    else
+    item = new JMenuItem(DELETE);
+    item.addActionListener(this);
+    pop.add(item);
+    if (hasHiddenRows)
     {
-      repaint();
+      item = new JMenuItem(SHOWALL);
+      item.addActionListener(this);
+      pop.add(item);
     }
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param evt
-   *          DOCUMENT ME!
-   */
-  public void mouseMoved(MouseEvent evt)
-  {
-    resizePanel = evt.getY() < 10;
-
-    getSelectedRow(evt.getY() - getScrollOffset());
-
-    if (selectedRow > -1
-            && ap.av.getAlignment().getAlignmentAnnotation().length > selectedRow)
+    item = new JMenuItem(OUTPUT_TEXT);
+    item.addActionListener(this);
+    pop.add(item);
+    // TODO: annotation object should be typed for autocalculated/derived
+    // property methods
+    if (selectedRow < aa.length)
     {
-      AlignmentAnnotation aa = ap.av.getAlignment()
-              .getAlignmentAnnotation()[selectedRow];
-
-      StringBuffer desc = new StringBuffer();
-      if (aa.description != null
-              && !aa.description.equals("New description"))
+      final String label = aa[selectedRow].label;
+      if (!aa[selectedRow].autoCalculated)
       {
-        // TODO: we could refactor and merge this code with the code in
-        // jalview.gui.SeqPanel.mouseMoved(..) that formats sequence feature
-        // tooltips
-        desc.append(aa.getDescription(true).trim());
-        // check to see if the description is an html fragment.
-        if (desc.length() < 6
-                || (desc.substring(0, 6).toLowerCase().indexOf("<html>") < 0))
+        if (aa[selectedRow].graph == AlignmentAnnotation.NO_GRAPH)
         {
-          // clean the description ready for embedding in html
-          desc = new StringBuffer(LEFT_ANGLE_BRACKET_PATTERN.matcher(desc)
-                  .replaceAll("<"));
-          desc.insert(0, "<html>");
+          // display formatting settings for this row.
+          pop.addSeparator();
+          // av and sequencegroup need to implement same interface for
+          item = new JCheckBoxMenuItem(TOGGLE_LABELSCALE,
+                  aa[selectedRow].scaleColLabel);
+          item.addActionListener(this);
+          pop.add(item);
         }
-        else
+      }
+      else if (label.indexOf("Consensus") > -1)
+      {
+        pop.addSeparator();
+        // av and sequencegroup need to implement same interface for
+        final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
+                MessageManager.getString("label.ignore_gaps_consensus"),
+                (aa[selectedRow].groupRef != null) ? aa[selectedRow].groupRef
+                        .getIgnoreGapsConsensus() : ap.av
+                        .isIgnoreGapsConsensus());
+        final AlignmentAnnotation aaa = aa[selectedRow];
+        cbmi.addActionListener(new ActionListener()
         {
-          // remove terminating html if any
-          int i = desc.substring(desc.length() - 7).toLowerCase()
-                  .lastIndexOf("</html>");
-          if (i > -1)
+          @Override
+          public void actionPerformed(ActionEvent e)
           {
-            desc.setLength(desc.length() - 7 + i);
+            if (aaa.groupRef != null)
+            {
+              // TODO: pass on reference to ap so the view can be updated.
+              aaa.groupRef.setIgnoreGapsConsensus(cbmi.getState());
+              ap.getAnnotationPanel().paint(
+                      ap.getAnnotationPanel().getGraphics());
+            }
+            else
+            {
+              ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap);
+            }
+            ap.alignmentChanged();
           }
-        }
-        if (aa.hasScore())
-        {
-          desc.append("<br/>");
-        }
-        // if (aa.hasProperties())
-        // {
-        // desc.append("<table>");
-        // for (String prop : aa.getProperties())
-        // {
-        // desc.append("<tr><td>" + prop + "</td><td>"
-        // + aa.getProperty(prop) + "</td><tr>");
-        // }
-        // desc.append("</table>");
-        // }
-      }
-      else
-      {
-        // begin the tooltip's html fragment
-        desc.append("<html>");
-        if (aa.hasScore())
-        {
-          // TODO: limit precision of score to avoid noise from imprecise
-          // doubles
-          // (64.7 becomes 64.7+/some tiny value).
-          desc.append(" Score: " + aa.score);
-        }
-      }
-      if (desc.length() > 6)
-      {
-        desc.append("</html>");
-        this.setToolTipText(desc.toString());
-      }
-      else
-      {
-        this.setToolTipText(null);
-      }
-    }
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param evt
-   *          DOCUMENT ME!
-   */
-  public void mouseClicked(MouseEvent evt)
-  {
-    final AlignmentAnnotation[] aa = ap.av.getAlignment()
-            .getAlignmentAnnotation();
-    if (SwingUtilities.isLeftMouseButton(evt))
-    {
-      if (selectedRow > -1 && selectedRow < aa.length)
-      {
-        if (aa[selectedRow].groupRef != null)
-        {
-          if (evt.getClickCount() >= 2)
-          {
-            // todo: make the ap scroll to the selection - not necessary, first
-            // click highlights/scrolls, second selects
-            ap.getSeqPanel().ap.getIdPanel().highlightSearchResults(null);
-            ap.av.setSelectionGroup(// new SequenceGroup(
-            aa[selectedRow].groupRef); // );
-            ap.paintAlignment(false);
-            PaintRefresher.Refresh(ap, ap.av.getSequenceSetId());
-            ap.av.sendSelection();
-          }
-          else
-          {
-            ap.getSeqPanel().ap.getIdPanel().highlightSearchResults(
-                    aa[selectedRow].groupRef.getSequences(null));
-          }
-          return;
-        }
-        else if (aa[selectedRow].sequenceRef != null)
-        {
-          if (evt.getClickCount() == 1)
-          {
-            ap.getSeqPanel().ap
-                    .getIdPanel()
-                    .highlightSearchResults(
-                            Arrays.asList(new SequenceI[] { aa[selectedRow].sequenceRef }));
-          }
-          else if (evt.getClickCount() >= 2)
-          {
-            ap.getSeqPanel().ap.getIdPanel().highlightSearchResults(null);
-            SequenceGroup sg = ap.av.getSelectionGroup();
-            if (sg != null)
-            {
-              // we make a copy rather than edit the current selection if no
-              // modifiers pressed
-              // see Enhancement JAL-1557
-              if (!(evt.isControlDown() || evt.isShiftDown()))
-              {
-                sg = new SequenceGroup(sg);
-                sg.clear();
-                sg.addSequence(aa[selectedRow].sequenceRef, false);
-              }
-              else
-              {
-                if (evt.isControlDown())
-                {
-                  sg.addOrRemove(aa[selectedRow].sequenceRef, true);
-                }
-                else
-                {
-                  // notionally, we should also add intermediate sequences from
-                  // last added sequence ?
-                  sg.addSequence(aa[selectedRow].sequenceRef, true);
-                }
-              }
-            }
-            else
-            {
-              sg = new SequenceGroup();
-              sg.setStartRes(0);
-              sg.setEndRes(ap.av.getAlignment().getWidth() - 1);
-              sg.addSequence(aa[selectedRow].sequenceRef, false);
-            }
-            ap.av.setSelectionGroup(sg);
-            ap.av.sendSelection();
-            ap.paintAlignment(false);
-            PaintRefresher.Refresh(ap, ap.av.getSequenceSetId());
-          }
-
-        }
-      }
-    }
-    if (!SwingUtilities.isRightMouseButton(evt))
-    {
-      return;
-    }
-
-    JPopupMenu pop = new JPopupMenu(
-            MessageManager.getString("label.annotations"));
-    JMenuItem item = new JMenuItem(ADDNEW);
-    item.addActionListener(this);
-    pop.add(item);
-    if (selectedRow < 0)
-    {
-      if (hasHiddenRows)
-      { // let the user make everything visible again
-        item = new JMenuItem(SHOWALL);
-        item.addActionListener(this);
-        pop.add(item);
-      }
-      pop.show(this, evt.getX(), evt.getY());
-      return;
-    }
-    item = new JMenuItem(EDITNAME);
-    item.addActionListener(this);
-    pop.add(item);
-    item = new JMenuItem(HIDE);
-    item.addActionListener(this);
-    pop.add(item);
-    // JAL-1264 hide all sequence-specific annotations of this type
-    if (selectedRow < aa.length)
-    {
-      if (aa[selectedRow].sequenceRef != null)
-      {
-        final String label = aa[selectedRow].label;
-        JMenuItem hideType = new JMenuItem();
-        String text = MessageManager.getString("label.hide_all") + " "
-                + label;
-        hideType.setText(text);
-        hideType.addActionListener(new ActionListener()
-        {
-          @Override
-          public void actionPerformed(ActionEvent e)
-          {
-            AlignmentUtils.showOrHideSequenceAnnotations(
-                    ap.av.getAlignment(), Collections.singleton(label),
-                    null, false, false);
-            // for (AlignmentAnnotation ann : ap.av.getAlignment()
-            // .getAlignmentAnnotation())
-            // {
-            // if (ann.sequenceRef != null && ann.label != null
-            // && ann.label.equals(label))
-            // {
-            // ann.visible = false;
-            // }
-            // }
-            refresh();
-          }
-        });
-        pop.add(hideType);
-      }
-    }
-    item = new JMenuItem(DELETE);
-    item.addActionListener(this);
-    pop.add(item);
-    if (hasHiddenRows)
-    {
-      item = new JMenuItem(SHOWALL);
-      item.addActionListener(this);
-      pop.add(item);
-    }
-    item = new JMenuItem(OUTPUT_TEXT);
-    item.addActionListener(this);
-    pop.add(item);
-    // TODO: annotation object should be typed for autocalculated/derived
-    // property methods
-    if (selectedRow < aa.length)
-    {
-      final String label = aa[selectedRow].label;
-      if (!aa[selectedRow].autoCalculated)
-      {
-        if (aa[selectedRow].graph == AlignmentAnnotation.NO_GRAPH)
-        {
-          // display formatting settings for this row.
-          pop.addSeparator();
-          // av and sequencegroup need to implement same interface for
-          item = new JCheckBoxMenuItem(TOGGLE_LABELSCALE,
-                  aa[selectedRow].scaleColLabel);
-          item.addActionListener(this);
-          pop.add(item);
-        }
-      }
-      else if (label.indexOf("Consensus") > -1)
-      {
-        pop.addSeparator();
-        // av and sequencegroup need to implement same interface for
-        final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
-                MessageManager.getString("label.ignore_gaps_consensus"),
-                (aa[selectedRow].groupRef != null) ? aa[selectedRow].groupRef
-                        .getIgnoreGapsConsensus() : ap.av
-                        .isIgnoreGapsConsensus());
-        final AlignmentAnnotation aaa = aa[selectedRow];
-        cbmi.addActionListener(new ActionListener()
-        {
-          public void actionPerformed(ActionEvent e)
-          {
-            if (aaa.groupRef != null)
-            {
-              // TODO: pass on reference to ap so the view can be updated.
-              aaa.groupRef.setIgnoreGapsConsensus(cbmi.getState());
-              ap.getAnnotationPanel().paint(
-                      ap.getAnnotationPanel().getGraphics());
-            }
-            else
-            {
-              ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap);
-            }
-          }
-        });
-        pop.add(cbmi);
-        // av and sequencegroup need to implement same interface for
-        if (aaa.groupRef != null)
+        });
+        pop.add(cbmi);
+        // av and sequencegroup need to implement same interface for
+        if (aaa.groupRef != null)
         {
           final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
                   MessageManager.getString("label.show_group_histogram"),
                   aa[selectedRow].groupRef.isShowConsensusHistogram());
           chist.addActionListener(new ActionListener()
           {
+            @Override
             public void actionPerformed(ActionEvent e)
             {
               // TODO: pass on reference
@@ -754,6 +513,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
                   aa[selectedRow].groupRef.isShowSequenceLogo());
           cprofl.addActionListener(new ActionListener()
           {
+            @Override
             public void actionPerformed(ActionEvent e)
             {
               // TODO: pass on reference
@@ -773,6 +533,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
                   aa[selectedRow].groupRef.isNormaliseSequenceLogo());
           cproflnorm.addActionListener(new ActionListener()
           {
+            @Override
             public void actionPerformed(ActionEvent e)
             {
 
@@ -798,6 +559,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
                   av.isShowConsensusHistogram());
           chist.addActionListener(new ActionListener()
           {
+            @Override
             public void actionPerformed(ActionEvent e)
             {
               // TODO: pass on reference
@@ -818,6 +580,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
                   av.isShowSequenceLogo());
           cprof.addActionListener(new ActionListener()
           {
+            @Override
             public void actionPerformed(ActionEvent e)
             {
               // TODO: pass on reference
@@ -838,6 +601,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
                   av.isNormaliseSequenceLogo());
           cprofnorm.addActionListener(new ActionListener()
           {
+            @Override
             public void actionPerformed(ActionEvent e)
             {
               // TODO: pass on reference
@@ -864,6 +628,325 @@ public class AnnotationLabels extends JPanel implements MouseListener,
   }
 
   /**
+   * DOCUMENT ME!
+   * 
+   * @param evt
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void mouseReleased(MouseEvent evt)
+  {
+    if (evt.isPopupTrigger())
+    {
+      showPopupMenu(evt);
+      return;
+    }
+
+    int start = selectedRow;
+    getSelectedRow(evt.getY() - getScrollOffset());
+    int end = selectedRow;
+
+    if (start != end)
+    {
+      // Swap these annotations
+      AlignmentAnnotation startAA = ap.av.getAlignment()
+              .getAlignmentAnnotation()[start];
+      if (end == -1)
+      {
+        end = ap.av.getAlignment().getAlignmentAnnotation().length - 1;
+      }
+      AlignmentAnnotation endAA = ap.av.getAlignment()
+              .getAlignmentAnnotation()[end];
+
+      ap.av.getAlignment().getAlignmentAnnotation()[end] = startAA;
+      ap.av.getAlignment().getAlignmentAnnotation()[start] = endAA;
+    }
+
+    resizePanel = false;
+    dragEvent = null;
+    repaint();
+    ap.getAnnotationPanel().repaint();
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param evt
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void mouseEntered(MouseEvent evt)
+  {
+    if (evt.getY() < 10)
+    {
+      resizePanel = true;
+      repaint();
+    }
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param evt
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void mouseExited(MouseEvent evt)
+  {
+    if (dragEvent == null)
+    {
+      resizePanel = false;
+      repaint();
+    }
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param evt
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void mouseDragged(MouseEvent evt)
+  {
+    dragEvent = evt;
+
+    if (resizePanel)
+    {
+      Dimension d = ap.annotationScroller.getPreferredSize();
+      int dif = evt.getY() - oldY;
+
+      dif /= ap.av.getCharHeight();
+      dif *= ap.av.getCharHeight();
+
+      if ((d.height - dif) > 20)
+      {
+        ap.annotationScroller.setPreferredSize(new Dimension(d.width,
+                d.height - dif));
+        d = ap.annotationSpaceFillerHolder.getPreferredSize();
+        ap.annotationSpaceFillerHolder.setPreferredSize(new Dimension(
+                d.width, d.height - dif));
+        ap.paintAlignment(true);
+      }
+
+      ap.addNotify();
+    }
+    else
+    {
+      repaint();
+    }
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param evt
+   *          DOCUMENT ME!
+   */
+  @Override
+  public void mouseMoved(MouseEvent evt)
+  {
+    resizePanel = evt.getY() < 10;
+
+    getSelectedRow(evt.getY() - getScrollOffset());
+
+    if (selectedRow > -1
+            && ap.av.getAlignment().getAlignmentAnnotation().length > selectedRow)
+    {
+      AlignmentAnnotation aa = ap.av.getAlignment()
+              .getAlignmentAnnotation()[selectedRow];
+
+      StringBuffer desc = new StringBuffer();
+      if (aa.description != null
+              && !aa.description.equals("New description"))
+      {
+        // TODO: we could refactor and merge this code with the code in
+        // jalview.gui.SeqPanel.mouseMoved(..) that formats sequence feature
+        // tooltips
+        desc.append(aa.getDescription(true).trim());
+        // check to see if the description is an html fragment.
+        if (desc.length() < 6
+                || (desc.substring(0, 6).toLowerCase().indexOf("<html>") < 0))
+        {
+          // clean the description ready for embedding in html
+          desc = new StringBuffer(LEFT_ANGLE_BRACKET_PATTERN.matcher(desc)
+                  .replaceAll("<"));
+          desc.insert(0, "<html>");
+        }
+        else
+        {
+          // remove terminating html if any
+          int i = desc.substring(desc.length() - 7).toLowerCase()
+                  .lastIndexOf("</html>");
+          if (i > -1)
+          {
+            desc.setLength(desc.length() - 7 + i);
+          }
+        }
+        if (aa.hasScore())
+        {
+          desc.append("<br/>");
+        }
+        // if (aa.hasProperties())
+        // {
+        // desc.append("<table>");
+        // for (String prop : aa.getProperties())
+        // {
+        // desc.append("<tr><td>" + prop + "</td><td>"
+        // + aa.getProperty(prop) + "</td><tr>");
+        // }
+        // desc.append("</table>");
+        // }
+      }
+      else
+      {
+        // begin the tooltip's html fragment
+        desc.append("<html>");
+        if (aa.hasScore())
+        {
+          // TODO: limit precision of score to avoid noise from imprecise
+          // doubles
+          // (64.7 becomes 64.7+/some tiny value).
+          desc.append(" Score: " + aa.score);
+        }
+      }
+      if (desc.length() > 6)
+      {
+        desc.append("</html>");
+        this.setToolTipText(desc.toString());
+      }
+      else
+      {
+        this.setToolTipText(null);
+      }
+    }
+  }
+
+  @Override
+  public void mouseClicked(MouseEvent evt)
+  {
+    final AlignmentAnnotation[] aa = ap.av.getAlignment()
+            .getAlignmentAnnotation();
+    if (!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt))
+    {
+      if (selectedRow > -1 && selectedRow < aa.length)
+      {
+        if (aa[selectedRow].groupRef != null)
+        {
+          if (evt.getClickCount() >= 2)
+          {
+            // todo: make the ap scroll to the selection - not necessary, first
+            // click highlights/scrolls, second selects
+            ap.getSeqPanel().ap.getIdPanel().highlightSearchResults(null);
+            // process modifiers
+            SequenceGroup sg = ap.av.getSelectionGroup();
+            if (sg == null
+                    || sg == aa[selectedRow].groupRef
+                    || !(jalview.util.Platform.isControlDown(evt) || evt
+                            .isShiftDown()))
+            {
+              if (jalview.util.Platform.isControlDown(evt)
+                      || evt.isShiftDown())
+              {
+                // clone a new selection group from the associated group
+                ap.av.setSelectionGroup(new SequenceGroup(
+                        aa[selectedRow].groupRef));
+              }
+              else
+              {
+                // set selection to the associated group so it can be edited
+                ap.av.setSelectionGroup(aa[selectedRow].groupRef);
+              }
+            }
+            else
+            {
+              // modify current selection with associated group
+              int remainToAdd = aa[selectedRow].groupRef.getSize();
+              for (SequenceI sgs : aa[selectedRow].groupRef.getSequences())
+              {
+                if (jalview.util.Platform.isControlDown(evt))
+                {
+                  sg.addOrRemove(sgs, --remainToAdd == 0);
+                }
+                else
+                {
+                  // notionally, we should also add intermediate sequences from
+                  // last added sequence ?
+                  sg.addSequence(sgs, --remainToAdd == 0);
+                }
+              }
+            }
+
+            ap.paintAlignment(false);
+            PaintRefresher.Refresh(ap, ap.av.getSequenceSetId());
+            ap.av.sendSelection();
+          }
+          else
+          {
+            ap.getSeqPanel().ap.getIdPanel().highlightSearchResults(
+                    aa[selectedRow].groupRef.getSequences(null));
+          }
+          return;
+        }
+        else if (aa[selectedRow].sequenceRef != null)
+        {
+          if (evt.getClickCount() == 1)
+          {
+            ap.getSeqPanel().ap
+                    .getIdPanel()
+                    .highlightSearchResults(
+                            Arrays.asList(new SequenceI[] { aa[selectedRow].sequenceRef }));
+          }
+          else if (evt.getClickCount() >= 2)
+          {
+            ap.getSeqPanel().ap.getIdPanel().highlightSearchResults(null);
+            SequenceGroup sg = ap.av.getSelectionGroup();
+            if (sg != null)
+            {
+              // we make a copy rather than edit the current selection if no
+              // modifiers pressed
+              // see Enhancement JAL-1557
+              if (!(jalview.util.Platform.isControlDown(evt) || evt
+                      .isShiftDown()))
+              {
+                sg = new SequenceGroup(sg);
+                sg.clear();
+                sg.addSequence(aa[selectedRow].sequenceRef, false);
+              }
+              else
+              {
+                if (jalview.util.Platform.isControlDown(evt))
+                {
+                  sg.addOrRemove(aa[selectedRow].sequenceRef, true);
+                }
+                else
+                {
+                  // notionally, we should also add intermediate sequences from
+                  // last added sequence ?
+                  sg.addSequence(aa[selectedRow].sequenceRef, true);
+                }
+              }
+            }
+            else
+            {
+              sg = new SequenceGroup();
+              sg.setStartRes(0);
+              sg.setEndRes(ap.av.getAlignment().getWidth() - 1);
+              sg.addSequence(aa[selectedRow].sequenceRef, false);
+            }
+            ap.av.setSelectionGroup(sg);
+            ap.paintAlignment(false);
+            PaintRefresher.Refresh(ap, ap.av.getSequenceSetId());
+            ap.av.sendSelection();
+          }
+
+        }
+      }
+      return;
+    }
+  }
+
+  /**
    * do a single sequence copy to jalview and the system clipboard
    * 
    * @param sq
@@ -893,7 +976,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
     List<int[]> hiddenCols = av.getColumnSelection().getHiddenColumns();
     if (hiddenCols != null)
     {
-      alignmentStartEnd = AlignFrame.getStartEnd(alignmentStartEnd,
+      alignmentStartEnd = av.getAlignment().getVisibleStartAndEndIndex(
               hiddenCols);
     }
     String output = new FormatAdapter().formatSequences("Fasta", seqs,
@@ -926,6 +1009,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
    * @param g1
    *          DOCUMENT ME!
    */
+  @Override
   public void paintComponent(Graphics g)
   {
 
diff --git a/src/jalview/gui/AnnotationPanel.java b/src/jalview/gui/AnnotationPanel.java
index 3f4c92a..0ddf8b8 100644
--- a/src/jalview/gui/AnnotationPanel.java
+++ b/src/jalview/gui/AnnotationPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,6 +26,8 @@ import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.AnnotationRenderer;
 import jalview.renderer.AwtRenderPanelI;
+import jalview.schemes.ResidueProperties;
+import jalview.util.Comparison;
 import jalview.util.MessageManager;
 
 import java.awt.AlphaComposite;
@@ -47,6 +49,9 @@ import java.awt.event.MouseMotionListener;
 import java.awt.event.MouseWheelEvent;
 import java.awt.event.MouseWheelListener;
 import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 import javax.swing.JColorChooser;
 import javax.swing.JMenuItem;
@@ -54,7 +59,6 @@ import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.Scrollable;
-import javax.swing.SwingUtilities;
 import javax.swing.ToolTipManager;
 
 /**
@@ -119,8 +123,6 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 
   boolean mouseDragging = false;
 
-  boolean MAC = false;
-
   // for editing cursor
   int cursorX = 0;
 
@@ -138,9 +140,6 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
    */
   public AnnotationPanel(AlignmentPanel ap)
   {
-
-    MAC = jalview.util.Platform.isAMac();
-
     ToolTipManager.sharedInstance().registerComponent(this);
     ToolTipManager.sharedInstance().setInitialDelay(0);
     ToolTipManager.sharedInstance().setDismissDelay(10000);
@@ -287,16 +286,20 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       aa[activeRow].annotations = anot;
     }
 
-    if (evt.getActionCommand().equals(REMOVE))
+    String action = evt.getActionCommand();
+    if (action.equals(REMOVE))
     {
-      for (int i = 0; i < av.getColumnSelection().size(); i++)
+      for (int index : av.getColumnSelection().getSelected())
       {
-        anot[av.getColumnSelection().columnAt(i)] = null;
+        if (av.getColumnSelection().isVisible(index))
+        {
+          anot[index] = null;
+        }
       }
     }
-    else if (evt.getActionCommand().equals(LABEL))
+    else if (action.equals(LABEL))
     {
-      String exMesg = collectAnnotVals(anot, av.getColumnSelection(), LABEL);
+      String exMesg = collectAnnotVals(anot, LABEL);
       String label = JOptionPane.showInputDialog(this,
               MessageManager.getString("label.enter_label"), exMesg);
 
@@ -310,10 +313,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         aa[activeRow].hasText = true;
       }
 
-      for (int i = 0; i < av.getColumnSelection().size(); i++)
+      for (int index : av.getColumnSelection().getSelected())
       {
-        int index = av.getColumnSelection().columnAt(i);
-
         if (!av.getColumnSelection().isVisible(index))
         {
           continue;
@@ -321,10 +322,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 
         if (anot[index] == null)
         {
-          anot[index] = new Annotation(label, "", ' ', 0); // TODO: verify that
-          // null exceptions
-          // aren't raised
-          // elsewhere.
+          anot[index] = new Annotation(label, "", ' ', 0);
         }
         else
         {
@@ -332,16 +330,14 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         }
       }
     }
-    else if (evt.getActionCommand().equals(COLOUR))
+    else if (action.equals(COLOUR))
     {
       Color col = JColorChooser.showDialog(this,
               MessageManager.getString("label.select_foreground_colour"),
               Color.black);
 
-      for (int i = 0; i < av.getColumnSelection().size(); i++)
+      for (int index : av.getColumnSelection().getSelected())
       {
-        int index = av.getColumnSelection().columnAt(i);
-
         if (!av.getColumnSelection().isVisible(index))
         {
           continue;
@@ -356,26 +352,27 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       }
     }
     else
-    // HELIX OR SHEET
+    // HELIX, SHEET or STEM
     {
       char type = 0;
-      String symbol = "\u03B1";
+      String symbol = "\u03B1"; // alpha
 
-      if (evt.getActionCommand().equals(HELIX))
+      if (action.equals(HELIX))
       {
         type = 'H';
       }
-      else if (evt.getActionCommand().equals(SHEET))
+      else if (action.equals(SHEET))
       {
         type = 'E';
-        symbol = "\u03B2";
+        symbol = "\u03B2"; // beta
       }
 
       // Added by LML to color stems
-      else if (evt.getActionCommand().equals(STEM))
+      else if (action.equals(STEM))
       {
         type = 'S';
-        symbol = "\u03C3";
+        int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
+        symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
       }
 
       if (!aa[activeRow].hasIcons)
@@ -394,15 +391,13 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       if ((label.length() > 0) && !aa[activeRow].hasText)
       {
         aa[activeRow].hasText = true;
-        if (evt.getActionCommand().equals(STEM))
+        if (action.equals(STEM))
         {
           aa[activeRow].showAllColLabels = true;
         }
       }
-      for (int i = 0; i < av.getColumnSelection().size(); i++)
+      for (int index : av.getColumnSelection().getSelected())
       {
-        int index = av.getColumnSelection().columnAt(i);
-
         if (!av.getColumnSelection().isVisible(index))
         {
           continue;
@@ -419,47 +414,64 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 
       }
     }
+
     av.getAlignment().validateAnnotation(aa[activeRow]);
     ap.alignmentChanged();
-
+    ap.alignFrame.setMenusForViewport();
     adjustPanelHeight();
     repaint();
 
     return;
   }
 
-  private String collectAnnotVals(Annotation[] anot,
-          ColumnSelection columnSelection, String label2)
+  /**
+   * Returns any existing annotation concatenated as a string. For each
+   * annotation, takes the description, if any, else the secondary structure
+   * character (if type is HELIX, SHEET or STEM), else the display character (if
+   * type is LABEL).
+   * 
+   * @param anots
+   * @param type
+   * @return
+   */
+  private String collectAnnotVals(Annotation[] anots, String type)
   {
-    String collatedInput = "";
+    // TODO is this method wanted? why? 'last' is not used
+
+    StringBuilder collatedInput = new StringBuilder(64);
     String last = "";
     ColumnSelection viscols = av.getColumnSelection();
-    // TODO: refactor and save av.getColumnSelection for efficiency
-    for (int i = 0; i < columnSelection.size(); i++)
+
+    /*
+     * the selection list (read-only view) is in selection order, not
+     * column order; make a copy so we can sort it
+     */
+    List<Integer> selected = new ArrayList<Integer>(viscols.getSelected());
+    Collections.sort(selected);
+    for (int index : selected)
     {
-      int index = columnSelection.columnAt(i);
       // always check for current display state - just in case
       if (!viscols.isVisible(index))
       {
         continue;
       }
       String tlabel = null;
-      if (anot[index] != null)
+      if (anots[index] != null)
       { // LML added stem code
-        if (label2.equals(HELIX) || label2.equals(SHEET)
-                || label2.equals(STEM) || label2.equals(LABEL))
+        if (type.equals(HELIX) || type.equals(SHEET) || type.equals(STEM)
+                || type.equals(LABEL))
         {
-          tlabel = anot[index].description;
+          tlabel = anots[index].description;
           if (tlabel == null || tlabel.length() < 1)
           {
-            if (label2.equals(HELIX) || label2.equals(SHEET)
-                    || label2.equals(STEM))
+            if (type.equals(HELIX) || type.equals(SHEET)
+                    || type.equals(STEM))
             {
-              tlabel = "" + anot[index].secondaryStructure;
+              tlabel = "" + anots[index].secondaryStructure;
             }
             else
             {
-              tlabel = "" + anot[index].displayCharacter;
+              tlabel = "" + anots[index].displayCharacter;
             }
           }
         }
@@ -467,13 +479,13 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         {
           if (last.length() > 0)
           {
-            collatedInput += " ";
+            collatedInput.append(" ");
           }
-          collatedInput += tlabel;
+          collatedInput.append(tlabel);
         }
       }
     }
-    return collatedInput;
+    return collatedInput.toString();
   }
 
   /**
@@ -495,6 +507,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     int height = 0;
     activeRow = -1;
 
+    final int y = evt.getY();
     for (int i = 0; i < aa.length; i++)
     {
       if (aa[i].visible)
@@ -502,7 +515,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         height += aa[i].height;
       }
 
-      if (evt.getY() < height)
+      if (y < height)
       {
         if (aa[i].editable)
         {
@@ -512,62 +525,71 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         {
           // Stretch Graph
           graphStretch = i;
-          graphStretchY = evt.getY();
+          graphStretchY = y;
         }
 
         break;
       }
     }
 
-    if (SwingUtilities.isRightMouseButton(evt) && activeRow != -1)
+    /*
+     * isPopupTrigger fires in mousePressed on Mac,
+     * not until mouseRelease on Windows
+     */
+    if (evt.isPopupTrigger() && activeRow != -1)
     {
-      if (av.getColumnSelection() == null)
-      {
-        return;
-      }
+      showPopupMenu(y, evt.getX());
+      return;
+    }
 
-      JPopupMenu pop = new JPopupMenu(
-              MessageManager.getString("label.structure_type"));
-      JMenuItem item;
-      /*
-       * Just display the needed structure options
-       */
-      if (av.getAlignment().isNucleotide() == true)
-      {
-        item = new JMenuItem(STEM);
-        item.addActionListener(this);
-        pop.add(item);
-      }
-      else
-      {
-        item = new JMenuItem(HELIX);
-        item.addActionListener(this);
-        pop.add(item);
-        item = new JMenuItem(SHEET);
-        item.addActionListener(this);
-        pop.add(item);
-      }
-      item = new JMenuItem(LABEL);
+    ap.getScalePanel().mousePressed(evt);
+  }
+
+  /**
+   * Construct and display a context menu at the right-click position
+   * 
+   * @param y
+   * @param x
+   */
+  void showPopupMenu(final int y, int x)
+  {
+    if (av.getColumnSelection() == null
+            || av.getColumnSelection().isEmpty())
+    {
+      return;
+    }
+
+    JPopupMenu pop = new JPopupMenu(
+            MessageManager.getString("label.structure_type"));
+    JMenuItem item;
+    /*
+     * Just display the needed structure options
+     */
+    if (av.getAlignment().isNucleotide())
+    {
+      item = new JMenuItem(STEM);
       item.addActionListener(this);
       pop.add(item);
-      item = new JMenuItem(COLOUR);
+    }
+    else
+    {
+      item = new JMenuItem(HELIX);
       item.addActionListener(this);
       pop.add(item);
-      item = new JMenuItem(REMOVE);
+      item = new JMenuItem(SHEET);
       item.addActionListener(this);
       pop.add(item);
-      pop.show(this, evt.getX(), evt.getY());
-
-      return;
-    }
-
-    if (aa == null)
-    {
-      return;
     }
-
-    ap.getScalePanel().mousePressed(evt);
-
+    item = new JMenuItem(LABEL);
+    item.addActionListener(this);
+    pop.add(item);
+    item = new JMenuItem(COLOUR);
+    item.addActionListener(this);
+    pop.add(item);
+    item = new JMenuItem(REMOVE);
+    item.addActionListener(this);
+    pop.add(item);
+    pop.show(this, x, y);
   }
 
   /**
@@ -583,6 +605,16 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     graphStretchY = -1;
     mouseDragging = false;
     ap.getScalePanel().mouseReleased(evt);
+
+    /*
+     * isPopupTrigger is set in mouseReleased on Windows
+     * (in mousePressed on Mac)
+     */
+    if (evt.isPopupTrigger() && activeRow != -1)
+    {
+      showPopupMenu(evt.getY(), evt.getX());
+    }
+
   }
 
   /**
@@ -637,10 +669,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   }
 
   /**
-   * DOCUMENT ME!
+   * Constructs the tooltip, and constructs and displays a status message, for
+   * the current mouse position
    * 
    * @param evt
-   *          DOCUMENT ME!
    */
   @Override
   public void mouseMoved(MouseEvent evt)
@@ -666,7 +698,6 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       if (evt.getY() < height)
       {
         row = i;
-
         break;
       }
     }
@@ -677,64 +708,139 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       return;
     }
 
-    int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
+    int column = (evt.getX() / av.getCharWidth()) + av.getStartRes();
 
     if (av.hasHiddenColumns())
     {
-      res = av.getColumnSelection().adjustForHiddenColumns(res);
+      column = av.getColumnSelection().adjustForHiddenColumns(column);
     }
 
-    if (row > -1 && aa[row].annotations != null
-            && res < aa[row].annotations.length)
+    AlignmentAnnotation ann = aa[row];
+    if (row > -1 && ann.annotations != null
+            && column < ann.annotations.length)
+    {
+      buildToolTip(ann, column, aa);
+      setStatusMessage(column, ann);
+    }
+    else
     {
-      if (aa[row].graphGroup > -1)
+      this.setToolTipText(null);
+      ap.alignFrame.statusBar.setText(" ");
+    }
+  }
+
+  /**
+   * Builds a tooltip for the annotation at the current mouse position.
+   * 
+   * @param ann
+   * @param column
+   * @param anns
+   */
+  void buildToolTip(AlignmentAnnotation ann, int column,
+          AlignmentAnnotation[] anns)
+  {
+    if (ann.graphGroup > -1)
+    {
+      StringBuilder tip = new StringBuilder(32);
+      tip.append("<html>");
+      for (int i = 0; i < anns.length; i++)
       {
-        StringBuffer tip = new StringBuffer("<html>");
-        for (int gg = 0; gg < aa.length; gg++)
+        if (anns[i].graphGroup == ann.graphGroup
+                && anns[i].annotations[column] != null)
         {
-          if (aa[gg].graphGroup == aa[row].graphGroup
-                  && aa[gg].annotations[res] != null)
+          tip.append(anns[i].label);
+          String description = anns[i].annotations[column].description;
+          if (description != null && description.length() > 0)
           {
-            tip.append(aa[gg].label + " "
-                    + aa[gg].annotations[res].description + "<br>");
+            tip.append(" ").append(description);
           }
-        }
-        if (tip.length() != 6)
-        {
-          tip.setLength(tip.length() - 4);
-          this.setToolTipText(tip.toString() + "</html>");
+          tip.append("<br>");
         }
       }
-      else if (aa[row].annotations[res] != null
-              && aa[row].annotations[res].description != null
-              && aa[row].annotations[res].description.length() > 0)
+      if (tip.length() != 6)
       {
-        this.setToolTipText(JvSwingUtils.wrapTooltip(true,
-                aa[row].annotations[res].description));
+        tip.setLength(tip.length() - 4);
+        this.setToolTipText(tip.toString() + "</html>");
       }
-      else
+    }
+    else if (ann.annotations[column] != null)
+    {
+      String description = ann.annotations[column].description;
+      if (description != null && description.length() > 0)
       {
-        // clear the tooltip.
-        this.setToolTipText(null);
+        this.setToolTipText(JvSwingUtils.wrapTooltip(true, description));
       }
+    }
+    else
+    {
+      // clear the tooltip.
+      this.setToolTipText(null);
+    }
+  }
 
-      if (aa[row].annotations[res] != null)
+  /**
+   * Constructs and displays the status bar message
+   * 
+   * @param column
+   * @param ann
+   */
+  void setStatusMessage(int column, AlignmentAnnotation ann)
+  {
+    /*
+     * show alignment column and annotation description if any
+     */
+    StringBuilder text = new StringBuilder(32);
+    text.append(MessageManager.getString("label.column")).append(" ")
+            .append(column + 1);
+
+    if (ann.annotations[column] != null)
+    {
+      String description = ann.annotations[column].description;
+      if (description != null && description.trim().length() > 0)
       {
-        StringBuffer text = new StringBuffer("Sequence position "
-                + (res + 1));
+        text.append("  ").append(description);
+      }
+    }
 
-        if (aa[row].annotations[res].description != null)
+    /*
+     * if the annotation is sequence-specific, show the sequence number
+     * in the alignment, and (if not a gap) the residue and position
+     */
+    SequenceI seqref = ann.sequenceRef;
+    if (seqref != null)
+    {
+      int seqIndex = av.getAlignment().findIndex(seqref);
+      if (seqIndex != -1)
+      {
+        text.append(", ")
+                .append(MessageManager.getString("label.sequence"))
+                .append(" ").append(seqIndex + 1);
+        char residue = seqref.getCharAt(column);
+        if (!Comparison.isGap(residue))
         {
-          text.append("  " + aa[row].annotations[res].description);
+          text.append(" ");
+          String name;
+          if (av.getAlignment().isNucleotide())
+          {
+            name = ResidueProperties.nucleotideName.get(String
+                    .valueOf(residue));
+            text.append(" Nucleotide: ").append(
+                    name != null ? name : residue);
+          }
+          else
+          {
+            name = 'X' == residue ? "X" : ('*' == residue ? "STOP"
+                    : ResidueProperties.aa2Triplet.get(String
+                            .valueOf(residue)));
+            text.append(" Residue: ").append(name != null ? name : residue);
+          }
+          int residuePos = seqref.findPosition(column);
+          text.append(" (").append(residuePos).append(")");
         }
-
-        ap.alignFrame.statusBar.setText(text.toString());
       }
     }
-    else
-    {
-      this.setToolTipText(null);
-    }
+
+    ap.alignFrame.statusBar.setText(text.toString());
   }
 
   /**
@@ -1027,4 +1133,25 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       return null;
     }
   }
+
+  /**
+   * Try to ensure any references held are nulled
+   */
+  public void dispose()
+  {
+    av = null;
+    ap = null;
+    image = null;
+    fadedImage = null;
+    gg = null;
+    _mwl = null;
+
+    /*
+     * I created the renderer so I will dispose of it
+     */
+    if (renderer != null)
+    {
+      renderer.dispose();
+    }
+  }
 }
diff --git a/src/jalview/gui/AnnotationRowFilter.java b/src/jalview/gui/AnnotationRowFilter.java
index de6a9d2..a0a0ca9 100644
--- a/src/jalview/gui/AnnotationRowFilter.java
+++ b/src/jalview/gui/AnnotationRowFilter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,7 +26,6 @@ import jalview.datamodel.SequenceGroup;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.util.MessageManager;
 
-import java.awt.event.ActionEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.util.Vector;
@@ -52,7 +51,7 @@ public abstract class AnnotationRowFilter extends JPanel
 
   protected boolean enableSeqAss = false;
 
-  private jalview.datamodel.AlignmentAnnotation currentAnnotation;
+  private AlignmentAnnotation currentAnnotation;
 
   protected boolean adjusting = false;
 
@@ -161,11 +160,20 @@ public abstract class AnnotationRowFilter extends JPanel
         enableSeqAss = true;
       }
       String label = av.getAlignment().getAlignmentAnnotation()[i].label;
+      // add associated sequence ID if available
+      if (!isSeqAssociated
+              && av.getAlignment().getAlignmentAnnotation()[i].sequenceRef != null)
+      {
+        label = label
+                + "_"
+                + av.getAlignment().getAlignmentAnnotation()[i].sequenceRef
+                        .getName();
+      }
+      // make label unique
       if (!list.contains(label))
       {
         anmap[list.size()] = i;
         list.add(label);
-
       }
       else
       {
@@ -200,7 +208,7 @@ public abstract class AnnotationRowFilter extends JPanel
     seqAssociated.setEnabled(enableSeqAss);
   }
 
-  public void ok_actionPerformed(ActionEvent e)
+  public void ok_actionPerformed()
   {
     try
     {
@@ -210,7 +218,7 @@ public abstract class AnnotationRowFilter extends JPanel
     }
   }
 
-  public void cancel_actionPerformed(ActionEvent e)
+  public void cancel_actionPerformed()
   {
     reset();
     ap.paintAlignment(true);
@@ -222,22 +230,22 @@ public abstract class AnnotationRowFilter extends JPanel
     }
   }
 
-  public void thresholdCheck_actionPerformed(ActionEvent e)
+  public void thresholdCheck_actionPerformed()
   {
     updateView();
   }
 
-  public void annotations_actionPerformed(ActionEvent e)
+  public void annotations_actionPerformed()
   {
     updateView();
   }
 
-  public void threshold_actionPerformed(ActionEvent e)
+  public void threshold_actionPerformed()
   {
     updateView();
   }
 
-  public void thresholdValue_actionPerformed(ActionEvent e)
+  public void thresholdValue_actionPerformed()
   {
     try
     {
@@ -249,7 +257,7 @@ public abstract class AnnotationRowFilter extends JPanel
     }
   }
 
-  public void thresholdIsMin_actionPerformed(ActionEvent actionEvent)
+  public void thresholdIsMin_actionPerformed()
   {
     updateView();
   }
@@ -257,15 +265,14 @@ public abstract class AnnotationRowFilter extends JPanel
   protected void populateThresholdComboBox(JComboBox<String> threshold)
   {
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_no_thereshold"));
+            .getString("label.threshold_feature_no_threshold"));
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_above_thereshold"));
+            .getString("label.threshold_feature_above_threshold"));
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_below_thereshold"));
+            .getString("label.threshold_feature_below_threshold"));
   }
 
-  protected void seqAssociated_actionPerformed(ActionEvent arg0,
-          JComboBox<String> annotations, JCheckBox seqAssociated)
+  protected void seqAssociated_actionPerformed(JComboBox<String> annotations)
   {
     adjusting = true;
     String cursel = (String) annotations.getSelectedItem();
@@ -322,26 +329,25 @@ public abstract class AnnotationRowFilter extends JPanel
     }
   }
 
-  protected boolean colorAlignmContaining(
-          AlignmentAnnotation currentAnnotation, int selectedThresholdItem)
+  protected boolean colorAlignmContaining(AlignmentAnnotation currentAnn,
+          int selectedThresholdOption)
   {
 
     AnnotationColourGradient acg = null;
     if (currentColours.isSelected())
     {
-      acg = new AnnotationColourGradient(currentAnnotation,
-              av.getGlobalColourScheme(), selectedThresholdItem);
+      acg = new AnnotationColourGradient(currentAnn,
+              av.getGlobalColourScheme(), selectedThresholdOption);
     }
     else
     {
-      acg = new AnnotationColourGradient(currentAnnotation,
+      acg = new AnnotationColourGradient(currentAnn,
               minColour.getBackground(), maxColour.getBackground(),
-              selectedThresholdItem);
+              selectedThresholdOption);
     }
     acg.setSeqAssociated(seqAssociated.isSelected());
 
-    if (currentAnnotation.graphMin == 0f
-            && currentAnnotation.graphMax == 0f)
+    if (currentAnn.graphMin == 0f && currentAnn.graphMax == 0f)
     {
       acg.setPredefinedColours(true);
     }
@@ -362,17 +368,17 @@ public abstract class AnnotationRowFilter extends JPanel
 
         if (currentColours.isSelected())
         {
-          sg.cs = new AnnotationColourGradient(currentAnnotation, sg.cs,
-                  selectedThresholdItem);
+          sg.cs = new AnnotationColourGradient(currentAnn, sg.cs,
+                  selectedThresholdOption);
           ((AnnotationColourGradient) sg.cs).setSeqAssociated(seqAssociated
                   .isSelected());
 
         }
         else
         {
-          sg.cs = new AnnotationColourGradient(currentAnnotation,
+          sg.cs = new AnnotationColourGradient(currentAnn,
                   minColour.getBackground(), maxColour.getBackground(),
-                  selectedThresholdItem);
+                  selectedThresholdOption);
           ((AnnotationColourGradient) sg.cs).setSeqAssociated(seqAssociated
                   .isSelected());
         }
diff --git a/src/jalview/gui/AppJmol.java b/src/jalview/gui/AppJmol.java
index 55d5f8c..b3505b6 100644
--- a/src/jalview/gui/AppJmol.java
+++ b/src/jalview/gui/AppJmol.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -41,6 +41,7 @@ import jalview.schemes.ZappoColourScheme;
 import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.ws.dbsources.Pdb;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -49,7 +50,6 @@ import java.awt.Font;
 import java.awt.Graphics;
 import java.awt.Rectangle;
 import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.io.BufferedReader;
@@ -66,10 +66,10 @@ import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JColorChooser;
 import javax.swing.JInternalFrame;
 import javax.swing.JMenu;
-import javax.swing.JMenuItem;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JSplitPane;
+import javax.swing.SwingUtilities;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
 import javax.swing.event.MenuEvent;
@@ -77,6 +77,13 @@ import javax.swing.event.MenuListener;
 
 public class AppJmol extends StructureViewerBase
 {
+  // ms to wait for Jmol to load files
+  private static final int JMOL_LOAD_TIMEOUT = 20000;
+
+  private static final String SPACE = " ";
+
+  private static final String BACKSLASH = "\"";
+
   AppJmolBinding jmb;
 
   JPanel scriptWindow;
@@ -122,7 +129,7 @@ public class AppJmol extends StructureViewerBase
     // / TODO: check if protocol is needed to be set, and if chains are
     // autodiscovered.
     jmb = new AppJmolBinding(this, ap.getStructureSelectionManager(),
-            pdbentrys, seqs, null, null);
+            pdbentrys, seqs, null);
 
     jmb.setLoadingFromArchive(true);
     addAlignmentPanel(ap);
@@ -292,7 +299,7 @@ public class AppJmol extends StructureViewerBase
   {
     progressBar = ap.alignFrame;
     jmb = new AppJmolBinding(this, ap.getStructureSelectionManager(),
-            pdbentrys, seqs, null, null);
+            pdbentrys, seqs, null);
     addAlignmentPanel(ap);
     useAlignmentPanelForColourbyseq(ap);
     if (pdbentrys.length > 1)
@@ -303,12 +310,10 @@ public class AppJmol extends StructureViewerBase
     jmb.setColourBySequence(true);
     setSize(400, 400); // probably should be a configurable/dynamic default here
     initMenus();
-    worker = null;
-    {
-      addingStructures = false;
-      worker = new Thread(this);
-      worker.start();
-    }
+    addingStructures = false;
+    worker = new Thread(this);
+    worker.start();
+
     this.addInternalFrameListener(new InternalFrameAdapter()
     {
       @Override
@@ -375,66 +380,24 @@ public class AppJmol extends StructureViewerBase
       scriptWindow.setVisible(false);
     }
 
-    jmb.allocateViewer(renderPanel, true, "", null, null, "", scriptWindow,
-            null);
+    jmb.allocateViewer(renderPanel, true, "", null, null, "",
+            scriptWindow, null);
     // jmb.newJmolPopup("Jmol");
     if (command == null)
     {
       command = "";
     }
     jmb.evalStateCommand(command);
+    jmb.evalStateCommand("set hoverDelay=0.1");
     jmb.setFinishedInit(true);
   }
 
-  void setChainMenuItems(Vector<String> chains)
-  {
-    chainMenu.removeAll();
-    if (chains == null)
-    {
-      return;
-    }
-    JMenuItem menuItem = new JMenuItem(
-            MessageManager.getString("label.all"));
-    menuItem.addActionListener(new ActionListener()
-    {
-      public void actionPerformed(ActionEvent evt)
-      {
-        allChainsSelected = true;
-        for (int i = 0; i < chainMenu.getItemCount(); i++)
-        {
-          if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
-          {
-            ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true);
-          }
-        }
-        centerViewer();
-        allChainsSelected = false;
-      }
-    });
 
-    chainMenu.add(menuItem);
-
-    for (String chain : chains)
-    {
-      menuItem = new JCheckBoxMenuItem(chain, true);
-      menuItem.addItemListener(new ItemListener()
-      {
-        public void itemStateChanged(ItemEvent evt)
-        {
-          if (!allChainsSelected)
-          {
-            centerViewer();
-          }
-        }
-      });
-
-      chainMenu.add(menuItem);
-    }
-  }
 
   boolean allChainsSelected = false;
 
-  void centerViewer()
+  @Override
+  void showSelectedChains()
   {
     Vector<String> toshow = new Vector<String>();
     for (int i = 0; i < chainMenu.getItemCount(); i++)
@@ -451,6 +414,7 @@ public class AppJmol extends StructureViewerBase
     jmb.centerViewer(toshow);
   }
 
+  @Override
   public void closeViewer(boolean closeExternalViewer)
   {
     // Jmol does not use an external viewer
@@ -467,18 +431,172 @@ public class AppJmol extends StructureViewerBase
     jmb = null;
   }
 
+  @Override
   public void run()
   {
     _started = true;
+    try
+    {
+      List<String> files = fetchPdbFiles();
+      if (files.size() > 0)
+      {
+        showFilesInViewer(files);
+      }
+    } finally
+    {
+      _started = false;
+      worker = null;
+    }
+  }
+
+  /**
+   * Either adds the given files to a structure viewer or opens a new viewer to
+   * show them
+   * 
+   * @param files
+   *          list of absolute paths to structure files
+   */
+  void showFilesInViewer(List<String> files)
+  {
+    long lastnotify = jmb.getLoadNotifiesHandled();
+    StringBuilder fileList = new StringBuilder();
+    for (String s : files)
+    {
+      fileList.append(SPACE).append(BACKSLASH)
+              .append(Platform.escapeString(s)).append(BACKSLASH);
+    }
+    String filesString = fileList.toString();
+
+    if (!addingStructures)
+    {
+      try
+      {
+        initJmol("load FILES " + filesString);
+      } catch (OutOfMemoryError oomerror)
+      {
+        new OOMWarning("When trying to open the Jmol viewer!", oomerror);
+        Cache.log.debug("File locations are " + filesString);
+      } catch (Exception ex)
+      {
+        Cache.log.error("Couldn't open Jmol viewer!", ex);
+      }
+    }
+    else
+    {
+      StringBuilder cmd = new StringBuilder();
+      cmd.append("loadingJalviewdata=true\nload APPEND ");
+      cmd.append(filesString);
+      cmd.append("\nloadingJalviewdata=null");
+      final String command = cmd.toString();
+      lastnotify = jmb.getLoadNotifiesHandled();
+
+      try
+      {
+        jmb.evalStateCommand(command);
+      } catch (OutOfMemoryError oomerror)
+      {
+        new OOMWarning("When trying to add structures to the Jmol viewer!",
+                oomerror);
+        Cache.log.debug("File locations are " + filesString);
+      } catch (Exception ex)
+      {
+        Cache.log.error("Couldn't add files to Jmol viewer!", ex);
+      }
+    }
+
+    // need to wait around until script has finished
+    int waitMax = JMOL_LOAD_TIMEOUT;
+    int waitFor = 35;
+    int waitTotal = 0;
+    while (addingStructures ? lastnotify >= jmb.getLoadNotifiesHandled()
+            : !(jmb.isFinishedInit() && jmb.getPdbFile() != null && jmb
+                    .getPdbFile().length == files.size()))
+    {
+      try
+      {
+        Cache.log.debug("Waiting around for jmb notify.");
+        Thread.sleep(waitFor);
+        waitTotal += waitFor;
+      } catch (Exception e)
+      {
+      }
+      if (waitTotal > waitMax)
+      {
+        System.err
+                .println("Timed out waiting for Jmol to load files after "
+                        + waitTotal + "ms");
+//        System.err.println("finished: " + jmb.isFinishedInit()
+//                + "; loaded: " + Arrays.toString(jmb.getPdbFile())
+//                + "; files: " + files.toString());
+        jmb.getPdbFile();
+        break;
+      }
+    }
+
+    // refresh the sequence colours for the new structure(s)
+    for (AlignmentPanel ap : _colourwith)
+    {
+      jmb.updateColours(ap);
+    }
+    // do superposition if asked to
+    if (Cache.getDefault("AUTOSUPERIMPOSE", true) && alignAddedStructures)
+    {
+      alignAddedStructures();
+    }
+    addingStructures = false;
+  }
+
+  /**
+   * Queues a thread to align structures with Jalview alignments
+   */
+  void alignAddedStructures()
+  {
+    javax.swing.SwingUtilities.invokeLater(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        if (jmb.viewer.isScriptExecuting())
+        {
+          SwingUtilities.invokeLater(this);
+          try
+          {
+            Thread.sleep(5);
+          } catch (InterruptedException q)
+          {
+          }
+          return;
+        }
+        else
+        {
+          alignStructs_withAllAlignPanels();
+        }
+      }
+    });
+    alignAddedStructures = false;
+  }
+
+  /**
+   * Retrieves and saves as file any modelled PDB entries for which we do not
+   * already have a file saved. Returns a list of absolute paths to structure
+   * files which were either retrieved, or already stored but not modelled in
+   * the structure viewer (i.e. files to add to the viewer display).
+   * 
+   * @return
+   */
+  List<String> fetchPdbFiles()
+  {
+    // todo - record which pdbids were successfully imported.
+    StringBuilder errormsgs = new StringBuilder();
+
+    List<String> files = new ArrayList<String>();
     String pdbid = "";
-    // todo - record which pdbids were successfuly imported.
-    StringBuffer errormsgs = new StringBuffer(), files = new StringBuffer();
     try
     {
-      String[] curfiles = jmb.getPdbFile(); // files currently in viewer
+      String[] filesInViewer = jmb.getPdbFile();
       // TODO: replace with reference fetching/transfer code (validate PDBentry
       // as a DBRef?)
-      jalview.ws.dbsources.Pdb pdbclient = new jalview.ws.dbsources.Pdb();
+      Pdb pdbclient = new Pdb();
       for (int pi = 0; pi < jmb.getPdbCount(); pi++)
       {
         String file = jmb.getPdbEntry(pi).getFile();
@@ -502,12 +620,15 @@ public class AppJmol extends StructureViewerBase
           } catch (Exception ex)
           {
             ex.printStackTrace();
-            errormsgs.append("'" + pdbid + "'");
-          }
-          if (progressBar != null)
+            errormsgs.append("'").append(pdbid).append("'");
+          } finally
           {
-            progressBar.setProgressBar(
-                    MessageManager.getString("label.state_completed"), hdl);
+            if (progressBar != null)
+            {
+              progressBar.setProgressBar(
+                      MessageManager.getString("label.state_completed"),
+                      hdl);
+            }
           }
           if (pdbseq != null)
           {
@@ -516,22 +637,21 @@ public class AppJmol extends StructureViewerBase
             file = new File(pdbseq.getSequenceAt(0).getAllPDBEntries()
                     .elementAt(0).getFile()).getAbsolutePath();
             jmb.getPdbEntry(pi).setFile(file);
-
-            files.append(" \"" + Platform.escapeString(file) + "\"");
+            files.add(file);
           }
           else
           {
-            errormsgs.append("'" + pdbid + "' ");
+            errormsgs.append("'").append(pdbid).append("' ");
           }
         }
         else
         {
-          if (curfiles != null && curfiles.length > 0)
+          if (filesInViewer != null && filesInViewer.length > 0)
           {
             addingStructures = true; // already files loaded.
-            for (int c = 0; c < curfiles.length; c++)
+            for (int c = 0; c < filesInViewer.length; c++)
             {
-              if (curfiles[c].equals(file))
+              if (filesInViewer[c].equals(file))
               {
                 file = null;
                 break;
@@ -540,7 +660,7 @@ public class AppJmol extends StructureViewerBase
           }
           if (file != null)
           {
-            files.append(" \"" + Platform.escapeString(file) + "\"");
+            files.add(file);
           }
         }
       }
@@ -550,113 +670,18 @@ public class AppJmol extends StructureViewerBase
     } catch (Exception ex)
     {
       ex.printStackTrace();
-      errormsgs.append("When retrieving pdbfiles : current was: '" + pdbid
-              + "'");
+      errormsgs.append("When retrieving pdbfiles : current was: '")
+              .append(pdbid).append("'");
     }
     if (errormsgs.length() > 0)
     {
-
       JOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
               .formatMessage("label.pdb_entries_couldnt_be_retrieved",
                       new String[] { errormsgs.toString() }),
               MessageManager.getString("label.couldnt_load_file"),
               JOptionPane.ERROR_MESSAGE);
-
     }
-    long lastnotify = jmb.getLoadNotifiesHandled();
-    if (files.length() > 0)
-    {
-      if (!addingStructures)
-      {
-
-        try
-        {
-          initJmol("load FILES " + files.toString());
-        } catch (OutOfMemoryError oomerror)
-        {
-          new OOMWarning("When trying to open the Jmol viewer!", oomerror);
-          Cache.log.debug("File locations are " + files);
-        } catch (Exception ex)
-        {
-          Cache.log.error("Couldn't open Jmol viewer!", ex);
-        }
-      }
-      else
-      {
-        StringBuffer cmd = new StringBuffer();
-        cmd.append("loadingJalviewdata=true\nload APPEND ");
-        cmd.append(files.toString());
-        cmd.append("\nloadingJalviewdata=null");
-        final String command = cmd.toString();
-        cmd = null;
-        lastnotify = jmb.getLoadNotifiesHandled();
-
-        try
-        {
-          jmb.evalStateCommand(command);
-        } catch (OutOfMemoryError oomerror)
-        {
-          new OOMWarning(
-                  "When trying to add structures to the Jmol viewer!",
-                  oomerror);
-          Cache.log.debug("File locations are " + files);
-        } catch (Exception ex)
-        {
-          Cache.log.error("Couldn't add files to Jmol viewer!", ex);
-        }
-      }
-
-      // need to wait around until script has finished
-      while (addingStructures ? lastnotify >= jmb.getLoadNotifiesHandled()
-              : (!jmb.isFinishedInit() && jmb.getPdbFile() != null && jmb
-                      .getPdbFile().length != jmb.getPdbCount()))
-      {
-        try
-        {
-          Cache.log.debug("Waiting around for jmb notify.");
-          Thread.sleep(35);
-        } catch (Exception e)
-        {
-        }
-      }
-
-      // refresh the sequence colours for the new structure(s)
-      for (AlignmentPanel ap : _colourwith)
-      {
-        jmb.updateColours(ap);
-      }
-      // do superposition if asked to
-      if (Cache.getDefault("AUTOSUPERIMPOSE", true) && alignAddedStructures)
-      {
-        javax.swing.SwingUtilities.invokeLater(new Runnable()
-        {
-          public void run()
-          {
-            if (jmb.viewer.isScriptExecuting())
-            {
-              javax.swing.SwingUtilities.invokeLater(this);
-              try
-              {
-                Thread.sleep(5);
-              } catch (InterruptedException q)
-              {
-              }
-              ;
-              return;
-            }
-            else
-            {
-              alignStructs_withAllAlignPanels();
-            }
-          }
-        });
-        alignAddedStructures = false;
-      }
-      addingStructures = false;
-
-    }
-    _started = false;
-    worker = null;
+    return files;
   }
 
   @Override
@@ -752,20 +777,20 @@ public class AppJmol extends StructureViewerBase
     {
       im = new jalview.util.ImageMaker(this,
               jalview.util.ImageMaker.TYPE.PNG, "Make PNG image from view",
-              width, height, null, null);
+              width, height, null, null, null, 0, false);
     }
     else if (type == jalview.util.ImageMaker.TYPE.EPS)
     {
       im = new jalview.util.ImageMaker(this,
               jalview.util.ImageMaker.TYPE.EPS, "Make EPS file from view",
-              width, height, null, this.getTitle());
+              width, height, null, this.getTitle(), null, 0, false);
     }
     else
     {
 
       im = new jalview.util.ImageMaker(this,
               jalview.util.ImageMaker.TYPE.SVG, "Make SVG file from PCA",
-              width, height, null, this.getTitle());
+              width, height, null, this.getTitle(), null, 0, false);
     }
 
     if (im.getGraphics() != null)
@@ -1003,7 +1028,7 @@ public class AppJmol extends StructureViewerBase
       repaint();
       return;
     }
-    setChainMenuItems(jmb.chainNames);
+    setChainMenuItems(jmb.getChainNames());
 
     this.setTitle(jmb.getViewerTitle());
     if (jmb.getPdbFile().length > 1 && jmb.getSequence().length > 1)
@@ -1069,6 +1094,7 @@ public class AppJmol extends StructureViewerBase
 
   }
 
+  @Override
   public void setJalviewColourScheme(ColourSchemeI ucs)
   {
     jmb.setJalviewColourScheme(ucs);
diff --git a/src/jalview/gui/AppJmolBinding.java b/src/jalview/gui/AppJmolBinding.java
index eb15a65..86bf234 100644
--- a/src/jalview/gui/AppJmolBinding.java
+++ b/src/jalview/gui/AppJmolBinding.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -41,10 +41,9 @@ public class AppJmolBinding extends JalviewJmolBinding
   private FeatureRenderer fr = null;
 
   public AppJmolBinding(AppJmol appJmol, StructureSelectionManager sSm,
-          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
-          String protocol)
+          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String protocol)
   {
-    super(sSm, pdbentry, sequenceIs, chains, protocol);
+    super(sSm, pdbentry, sequenceIs, protocol);
     appJmolWindow = appJmol;
   }
 
@@ -113,6 +112,7 @@ public class AppJmolBinding extends JalviewJmolBinding
     // appJmolWindow.repaint();
     javax.swing.SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         appJmolWindow.updateTitleAndMenus();
@@ -121,6 +121,7 @@ public class AppJmolBinding extends JalviewJmolBinding
     });
   }
 
+  @Override
   public void updateColours(Object source)
   {
     AlignmentPanel ap = (AlignmentPanel) source;
@@ -144,6 +145,7 @@ public class AppJmolBinding extends JalviewJmolBinding
     // msWalltime);
   }
 
+  @Override
   public void showUrl(String url)
   {
     showUrl(url, "jmol");
diff --git a/src/jalview/gui/AppVarna.java b/src/jalview/gui/AppVarna.java
index 3742864..3e0f4cd 100644
--- a/src/jalview/gui/AppVarna.java
+++ b/src/jalview/gui/AppVarna.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/AppVarnaBinding.java b/src/jalview/gui/AppVarnaBinding.java
index 366dc06..b0f859c 100644
--- a/src/jalview/gui/AppVarnaBinding.java
+++ b/src/jalview/gui/AppVarnaBinding.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/AssociatePdbFileWithSeq.java b/src/jalview/gui/AssociatePdbFileWithSeq.java
index f67d6da..a28b9e6 100644
--- a/src/jalview/gui/AssociatePdbFileWithSeq.java
+++ b/src/jalview/gui/AssociatePdbFileWithSeq.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -23,6 +23,7 @@ package jalview.gui;
 import jalview.api.StructureSelectionManagerProvider;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.io.StructureFile;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 
@@ -48,7 +49,7 @@ public class AssociatePdbFileWithSeq
           StructureSelectionManagerProvider ssmp)
   {
     PDBEntry entry = new PDBEntry();
-    MCview.PDBfile pdbfile = null;
+    StructureFile pdbfile = null;
     pdbfile = StructureSelectionManager.getStructureSelectionManager(ssmp)
             .setMapping(false, new SequenceI[] { sequence }, null, choice,
                     protocol);
@@ -57,7 +58,7 @@ public class AssociatePdbFileWithSeq
       // stacktrace already thrown so just return
       return null;
     }
-    if (pdbfile.id == null)
+    if (pdbfile.getId() == null)
     {
       String reply = null;
 
@@ -78,7 +79,7 @@ public class AssociatePdbFileWithSeq
     }
     else
     {
-      entry.setId(pdbfile.id);
+      entry.setId(pdbfile.getId());
     }
     entry.setType(PDBEntry.Type.FILE);
 
diff --git a/src/jalview/gui/BlogReader.java b/src/jalview/gui/BlogReader.java
index 5baed16..cceddb2 100644
--- a/src/jalview/gui/BlogReader.java
+++ b/src/jalview/gui/BlogReader.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -41,6 +41,7 @@ import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collections;
+import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -285,6 +286,7 @@ public class BlogReader extends JPanel
   /**
    * check if the news panel's container is visible
    */
+  @Override
   public boolean isVisible()
   {
     if (parent == null)
@@ -312,6 +314,7 @@ public class BlogReader extends JPanel
           xf.setContentPane(me);
           xf.addWindowListener(new WindowAdapter()
           {
+            @Override
             public void windowClosing(WindowEvent e)
             {
               ActionEvent actionEvent = new ActionEvent(this,
@@ -320,6 +323,7 @@ public class BlogReader extends JPanel
               exitAction.actionPerformed(actionEvent);
             }
 
+            @Override
             public void windowOpened(WindowEvent e)
             {
             }
@@ -367,9 +371,9 @@ public class BlogReader extends JPanel
               + " and lastDate is " + lastDate);
       for (Item i : (List<Item>) chan.getItems())
       {
+        Date published = i.getPublishDate();
         boolean isread = lastDate == null ? false
-                : (i.getPublishDate() != null && !lastDate.before(i
-                        .getPublishDate()));
+                : (published != null && !lastDate.before(published));
 
         if (!updating || updateItems)
         {
@@ -379,11 +383,11 @@ public class BlogReader extends JPanel
         {
           i.setRead(isread);
         }
-        if (i.getPublishDate() != null && !i.isRead())
+        if (published != null && !i.isRead())
         {
-          if (earliest == null || earliest.after(i.getPublishDate()))
+          if (earliest == null || earliest.after(published))
           {
-            earliest = i.getPublishDate();
+            earliest = published;
           }
         }
       }
@@ -420,11 +424,9 @@ public class BlogReader extends JPanel
       }
       if (lastDate != null)
       {
-        jalview.bin.Cache.setDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED",
-                lastDate);
-        jalview.bin.Cache.log.debug("Saved last read date as "
-                + jalview.bin.Cache.date_format.format(lastDate));
-
+        String formatted = Cache.setDateProperty(
+                "JALVIEW_NEWS_RSS_LASTMODIFIED", lastDate);
+        Cache.log.debug("Saved last read date as " + formatted);
       }
     }
   }
@@ -472,6 +474,7 @@ public class BlogReader extends JPanel
 
     listItems.addMouseListener(new java.awt.event.MouseAdapter()
     {
+      @Override
       public void mouseClicked(MouseEvent e)
       {
         listItems_mouseClicked(e);
@@ -497,6 +500,7 @@ public class BlogReader extends JPanel
     }
     textDescription.addHyperlinkListener(new HyperlinkListener()
     {
+      @Override
       public void hyperlinkUpdate(HyperlinkEvent e)
       {
         if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
@@ -508,6 +512,7 @@ public class BlogReader extends JPanel
 
     listItems.addListSelectionListener(new ListSelectionListener()
     {
+      @Override
       public void valueChanged(ListSelectionEvent e)
       {
         if (e.getValueIsAdjusting() == false)
@@ -537,6 +542,7 @@ public class BlogReader extends JPanel
       _listItems = listItems;
     }
 
+    @Override
     public void actionPerformed(ActionEvent e)
     {
       Object o = _listItems.getSelectedValue();
@@ -550,6 +556,7 @@ public class BlogReader extends JPanel
       }
     }
 
+    @Override
     public void update(Object o)
     {
       setEnabled(true);
@@ -754,11 +761,10 @@ public class BlogReader extends JPanel
     lastread.set(1983, 01, 01);
     while (lastread.before(today))
     {
-      Cache.setDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED",
-              lastread.getTime());
+      String formattedDate = Cache.setDateProperty(
+              "JALVIEW_NEWS_RSS_LASTMODIFIED", lastread.getTime());
       BlogReader me = new BlogReader();
-      System.out.println("Set last date to "
-              + jalview.bin.Cache.date_format.format(lastread.getTime()));
+      System.out.println("Set last date to " + formattedDate);
       if (me.isNewsNew())
       {
         Cache.log.debug("There is news to read.");
@@ -810,6 +816,7 @@ class ChannelsRenderer extends DefaultListCellRenderer
   private final static Icon _icon = new ImageIcon(
           Main.class.getResource("image/ComposeMail16.gif"));
 
+  @Override
   public Component getListCellRendererComponent(JList list, Object value,
           int index, boolean isSelected, boolean cellHasFocus)
   {
@@ -837,6 +844,7 @@ class ItemsRenderer extends DefaultListCellRenderer
   private final static Icon _icon = new ImageIcon(
           Main.class.getResource("image/ComposeMail16.gif"));
 
+  @Override
   public Component getListCellRendererComponent(JList list, Object value,
           int index, boolean isSelected, boolean cellHasFocus)
   {
diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java
index c902093..9e11d87 100644
--- a/src/jalview/gui/ChimeraViewFrame.java
+++ b/src/jalview/gui/ChimeraViewFrame.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -31,6 +31,7 @@ import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
+import jalview.io.StructureFile;
 import jalview.schemes.BuriedColourScheme;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.HelixColourScheme;
@@ -46,7 +47,6 @@ import jalview.util.Platform;
 import jalview.ws.dbsources.Pdb;
 
 import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.io.BufferedReader;
@@ -59,16 +59,13 @@ import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.Random;
-import java.util.Set;
 import java.util.Vector;
 
 import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JColorChooser;
 import javax.swing.JInternalFrame;
 import javax.swing.JMenu;
-import javax.swing.JMenuItem;
 import javax.swing.JOptionPane;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
@@ -196,7 +193,7 @@ public class ChimeraViewFrame extends StructureViewerBase
   public ChimeraViewFrame(PDBEntry pdbentry, SequenceI[] seq,
           String[] chains, final AlignmentPanel ap)
   {
-    super();
+    this();
     String pdbId = pdbentry.getId();
 
     /*
@@ -249,10 +246,9 @@ public class ChimeraViewFrame extends StructureViewerBase
           SequenceI[][] seqs)
   {
     createProgressBar();
-    String[][] chains = extractChains(seqs);
+    // FIXME extractChains needs pdbentries to match IDs to PDBEntry(s) on seqs
     jmb = new JalviewChimeraBindingModel(this,
-            ap.getStructureSelectionManager(), pdbentrys, seqs, chains,
-            null);
+            ap.getStructureSelectionManager(), pdbentrys, seqs, null);
     addAlignmentPanel(ap);
     useAlignmentPanelForColourbyseq(ap);
     if (pdbentrys.length > 1)
@@ -270,6 +266,7 @@ public class ChimeraViewFrame extends StructureViewerBase
 
     this.addInternalFrameListener(new InternalFrameAdapter()
     {
+      @Override
       public void internalFrameClosing(InternalFrameEvent internalFrameEvent)
       {
         closeViewer(false);
@@ -278,38 +275,7 @@ public class ChimeraViewFrame extends StructureViewerBase
 
   }
 
-  /**
-   * Retrieve chains for sequences by inspecting their PDB refs. The hope is
-   * that the first will be to the sequence's own chain. Really need a more
-   * managed way of doing this.
-   * 
-   * @param seqs
-   * @return
-   */
-  protected String[][] extractChains(SequenceI[][] seqs)
-  {
-    String[][] chains = new String[seqs.length][];
-    for (int i = 0; i < seqs.length; i++)
-    {
-      chains[i] = new String[seqs[i].length];
-      int seqno = 0;
-      for (SequenceI seq : seqs[i])
-      {
-        String chain = null;
-        if (seq.getDatasetSequence() != null)
-        {
-          Vector<PDBEntry> pdbrefs = seq.getDatasetSequence()
-                  .getAllPDBEntries();
-          if (pdbrefs != null && pdbrefs.size() > 0)
-          {
-            chain = pdbrefs.get(0).getChainCode();
-          }
-        }
-        chains[i][seqno++] = chain;
-      }
-    }
-    return chains;
-  }
+
 
   /**
    * Create a new viewer from saved session state data including Chimera session
@@ -328,7 +294,7 @@ public class ChimeraViewFrame extends StructureViewerBase
           SequenceI[][] seqsArray, boolean colourByChimera,
           boolean colourBySequence, String newViewId)
   {
-    super();
+    this();
     setViewId(newViewId);
     this.chimeraSessionFile = chimeraSessionFile;
     openNewChimera(alignPanel, pdbArray, seqsArray);
@@ -357,31 +323,22 @@ public class ChimeraViewFrame extends StructureViewerBase
   public ChimeraViewFrame(PDBEntry[] pe, SequenceI[][] seqs,
           AlignmentPanel ap)
   {
-    super();
+    this();
     openNewChimera(ap, pe, seqs);
   }
 
-  public ChimeraViewFrame(Map<PDBEntry, List<SequenceI>> toView,
-          AlignmentPanel alignPanel)
+  /**
+   * Default constructor
+   */
+  public ChimeraViewFrame()
   {
     super();
 
     /*
-     * Convert the map of sequences per pdb entry into the tied arrays expected
-     * by openNewChimera
-     * 
-     * TODO pass the Map down to openNewChimera and its callees instead
+     * closeViewer will decide whether or not to close this frame
+     * depending on whether user chooses to Cancel or not
      */
-    final Set<PDBEntry> pdbEntries = toView.keySet();
-    PDBEntry[] pdbs = pdbEntries.toArray(new PDBEntry[pdbEntries.size()]);
-    SequenceI[][] seqsForPdbs = new SequenceI[pdbEntries.size()][];
-    for (int i = 0; i < pdbs.length; i++)
-    {
-      final List<SequenceI> seqsForPdb = toView.get(pdbs[i]);
-      seqsForPdbs[i] = seqsForPdb.toArray(new SequenceI[seqsForPdb.size()]);
-    }
-
-    openNewChimera(alignPanel, pdbs, seqsForPdbs);
+    setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE);
   }
 
   /**
@@ -443,61 +400,11 @@ public class ChimeraViewFrame extends StructureViewerBase
     jmb.startChimeraListener();
   }
 
-  /**
-   * If the list is not empty, add menu items for 'All' and each individual
-   * chain to the "View | Show Chain" sub-menu. Multiple selections are allowed.
-   * 
-   * @param chainNames
-   */
-  void setChainMenuItems(List<String> chainNames)
-  {
-    chainMenu.removeAll();
-    if (chainNames == null || chainNames.isEmpty())
-    {
-      return;
-    }
-    JMenuItem menuItem = new JMenuItem(
-            MessageManager.getString("label.all"));
-    menuItem.addActionListener(new ActionListener()
-    {
-      public void actionPerformed(ActionEvent evt)
-      {
-        allChainsSelected = true;
-        for (int i = 0; i < chainMenu.getItemCount(); i++)
-        {
-          if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
-          {
-            ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true);
-          }
-        }
-        showSelectedChains();
-        allChainsSelected = false;
-      }
-    });
-
-    chainMenu.add(menuItem);
-
-    for (String chainName : chainNames)
-    {
-      menuItem = new JCheckBoxMenuItem(chainName, true);
-      menuItem.addItemListener(new ItemListener()
-      {
-        public void itemStateChanged(ItemEvent evt)
-        {
-          if (!allChainsSelected)
-          {
-            showSelectedChains();
-          }
-        }
-      });
-
-      chainMenu.add(menuItem);
-    }
-  }
 
   /**
    * Show only the selected chain(s) in the viewer
    */
+  @Override
   void showSelectedChains()
   {
     List<String> toshow = new ArrayList<String>();
@@ -523,6 +430,7 @@ public class ChimeraViewFrame extends StructureViewerBase
    * @param closeChimera
    *          if true, close any linked Chimera process; if false, prompt first
    */
+  @Override
   public void closeViewer(boolean closeChimera)
   {
     if (jmb != null && jmb.isChimeraRunning())
@@ -535,7 +443,15 @@ public class ChimeraViewFrame extends StructureViewerBase
         prompt = JvSwingUtils.wrapTooltip(true, prompt);
         int confirm = JOptionPane.showConfirmDialog(this, prompt,
                 MessageManager.getString("label.close_viewer"),
-                JOptionPane.YES_NO_OPTION);
+                JOptionPane.YES_NO_CANCEL_OPTION);
+        /*
+         * abort closure if user hits escape or Cancel
+         */
+        if (confirm == JOptionPane.CANCEL_OPTION
+                || confirm == JOptionPane.CLOSED_OPTION)
+        {
+          return;
+        }
         closeChimera = confirm == JOptionPane.YES_OPTION;
       }
       jmb.closeViewer(closeChimera);
@@ -547,12 +463,14 @@ public class ChimeraViewFrame extends StructureViewerBase
     // TODO: check for memory leaks where instance isn't finalised because jmb
     // holds a reference to the window
     jmb = null;
+    dispose();
   }
 
   /**
    * Open any newly added PDB structures in Chimera, having first fetched data
    * from PDB (if not already saved).
    */
+  @Override
   public void run()
   {
     _started = true;
@@ -562,6 +480,7 @@ public class ChimeraViewFrame extends StructureViewerBase
     List<PDBEntry> filePDB = new ArrayList<PDBEntry>();
     List<Integer> filePDBpos = new ArrayList<Integer>();
     PDBEntry thePdbEntry = null;
+    StructureFile pdb = null;
     try
     {
       String[] curfiles = jmb.getPdbFile(); // files currently in viewer
@@ -651,7 +570,8 @@ public class ChimeraViewFrame extends StructureViewerBase
           {
             int pos = filePDBpos.get(num).intValue();
             long startTime = startProgressBar("Chimera "
-                    + MessageManager.getString("status.opening_file"));
+                    + MessageManager.getString("status.opening_file_for")
+                    + " " + pe.getId());
             jmb.openFile(pe);
             jmb.addSequence(pos, jmb.getSequence()[pos]);
             File fl = new File(pe.getFile());
@@ -669,8 +589,9 @@ public class ChimeraViewFrame extends StructureViewerBase
               stopProgressBar("", startTime);
             }
             // Explicitly map to the filename used by Chimera ;
-            jmb.getSsm().setMapping(jmb.getSequence()[pos],
+            pdb = jmb.getSsm().setMapping(jmb.getSequence()[pos],
                     jmb.getChains()[pos], pe.getFile(), protocol);
+            stashFoundChains(pdb, pe.getFile());
           } catch (OutOfMemoryError oomerror)
           {
             new OOMWarning(
@@ -686,6 +607,7 @@ public class ChimeraViewFrame extends StructureViewerBase
           }
         }
       }
+      jmb.refreshGUI();
       jmb.setFinishedInit(true);
       jmb.setLoadingFromArchive(false);
 
@@ -699,6 +621,7 @@ public class ChimeraViewFrame extends StructureViewerBase
       {
         new Thread(new Runnable()
         {
+          @Override
           public void run()
           {
             alignStructs_withAllAlignPanels();
@@ -720,8 +643,20 @@ public class ChimeraViewFrame extends StructureViewerBase
    * @return
    * @throws Exception
    */
+
+  private void stashFoundChains(StructureFile pdb, String file)
+  {
+    for (int i = 0; i < pdb.getChains().size(); i++)
+    {
+      String chid = new String(pdb.getId() + ":"
+              + pdb.getChains().elementAt(i).id);
+      jmb.getChainNames().add(chid);
+      jmb.getChainFile().put(chid, file);
+    }
+  }
   private String fetchPdbFile(PDBEntry processingEntry) throws Exception
   {
+    // FIXME: this is duplicated code with Jmol frame ?
     String filePath = null;
     Pdb pdbclient = new Pdb();
     AlignmentI pdbseq = null;
@@ -1091,6 +1026,7 @@ public class ChimeraViewFrame extends StructureViewerBase
     }
   }
 
+  @Override
   public void setJalviewColourScheme(ColourSchemeI ucs)
   {
     jmb.setJalviewColourScheme(ucs);
diff --git a/src/jalview/gui/Console.java b/src/jalview/gui/Console.java
index 4118f91..c23ce50 100644
--- a/src/jalview/gui/Console.java
+++ b/src/jalview/gui/Console.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/CrossRefAction.java b/src/jalview/gui/CrossRefAction.java
new file mode 100644
index 0000000..86ffd75
--- /dev/null
+++ b/src/jalview/gui/CrossRefAction.java
@@ -0,0 +1,312 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.gui;
+
+import jalview.analysis.AlignmentUtils;
+import jalview.analysis.CrossRef;
+import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureSettingsModelI;
+import jalview.bin.Cache;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.SequenceI;
+import jalview.io.gff.SequenceOntologyI;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
+import jalview.ws.SequenceFetcher;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+
+/**
+ * Factory constructor and runnable for discovering and displaying
+ * cross-references for a set of aligned sequences
+ * 
+ * @author jprocter
+ *
+ */
+public class CrossRefAction implements Runnable
+{
+  private AlignFrame alignFrame;
+
+  private SequenceI[] sel;
+
+  private boolean _odna;
+
+  private String source;
+
+  List<AlignmentViewPanel> xrefViews = new ArrayList<AlignmentViewPanel>();
+
+  public List<jalview.api.AlignmentViewPanel> getXrefViews()
+  {
+    return xrefViews;
+  }
+
+  @Override
+  public void run()
+  {
+    final long sttime = System.currentTimeMillis();
+    alignFrame.setProgressBar(
+            MessageManager.formatMessage(
+                    "status.searching_for_sequences_from",
+                    new Object[] { source }), sttime);
+    try
+    {
+      AlignmentI alignment = alignFrame.getViewport().getAlignment();
+      AlignmentI dataset = alignment.getDataset() == null ? alignment
+              : alignment.getDataset();
+      boolean dna = alignment.isNucleotide();
+      if (_odna != dna)
+      {
+        System.err
+                .println("Conflict: showProducts for alignment originally "
+                        + "thought to be " + (_odna ? "DNA" : "Protein")
+                        + " now searching for " + (dna ? "DNA" : "Protein")
+                        + " Context.");
+      }
+      AlignmentI xrefs = new CrossRef(sel, dataset).findXrefSequences(
+              source, dna);
+      if (xrefs == null)
+      {
+        return;
+      }
+      /*
+       * get display scheme (if any) to apply to features
+       */
+      FeatureSettingsModelI featureColourScheme = new SequenceFetcher()
+              .getFeatureColourScheme(source);
+
+      AlignmentI xrefsAlignment = makeCrossReferencesAlignment(dataset,
+              xrefs);
+      if (!dna)
+      {
+        xrefsAlignment = AlignmentUtils.makeCdsAlignment(
+                xrefsAlignment.getSequencesArray(), dataset, sel);
+        xrefsAlignment.alignAs(alignment);
+      }
+
+      /*
+       * If we are opening a splitframe, make a copy of this alignment (sharing the same dataset
+       * sequences). If we are DNA, drop introns and update mappings
+       */
+      AlignmentI copyAlignment = null;
+
+      if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
+      {
+        boolean copyAlignmentIsAligned = false;
+        if (dna)
+        {
+          copyAlignment = AlignmentUtils.makeCdsAlignment(sel, dataset,
+                  xrefsAlignment.getSequencesArray());
+          if (copyAlignment.getHeight() == 0)
+          {
+            JOptionPane.showMessageDialog(alignFrame,
+                    MessageManager.getString("label.cant_map_cds"),
+                    MessageManager.getString("label.operation_failed"),
+                    JOptionPane.OK_OPTION);
+            System.err.println("Failed to make CDS alignment");
+          }
+
+          /*
+           * pending getting Embl transcripts to 'align', 
+           * we are only doing this for Ensembl
+           */
+          // TODO proper criteria for 'can align as cdna'
+          if (DBRefSource.ENSEMBL.equalsIgnoreCase(source)
+                  || AlignmentUtils.looksLikeEnsembl(alignment))
+          {
+            copyAlignment.alignAs(alignment);
+            copyAlignmentIsAligned = true;
+          }
+        }
+        else
+        {
+          copyAlignment = AlignmentUtils.makeCopyAlignment(sel,
+                  xrefs.getSequencesArray(), dataset);
+        }
+        copyAlignment
+                .setGapCharacter(alignFrame.viewport.getGapCharacter());
+
+        StructureSelectionManager ssm = StructureSelectionManager
+                .getStructureSelectionManager(Desktop.instance);
+
+        /*
+         * register any new mappings for sequence mouseover etc
+         * (will not duplicate any previously registered mappings)
+         */
+        ssm.registerMappings(dataset.getCodonFrames());
+
+        if (copyAlignment.getHeight() <= 0)
+        {
+          System.err.println("No Sequences generated for xRef type "
+                  + source);
+          return;
+        }
+        /*
+         * align protein to dna
+         */
+        if (dna && copyAlignmentIsAligned)
+        {
+          xrefsAlignment.alignAs(copyAlignment);
+        }
+        else
+        {
+          /*
+           * align cdna to protein - currently only if 
+           * fetching and aligning Ensembl transcripts!
+           */
+          // TODO: generalise for other sources of locus/transcript/cds data
+          if (dna && DBRefSource.ENSEMBL.equalsIgnoreCase(source))
+          {
+            copyAlignment.alignAs(xrefsAlignment);
+          }
+        }
+      }
+      /*
+       * build AlignFrame(s) according to available alignment data
+       */
+      AlignFrame newFrame = new AlignFrame(xrefsAlignment,
+              AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+      if (Cache.getDefault("HIDE_INTRONS", true))
+      {
+        newFrame.hideFeatureColumns(SequenceOntologyI.EXON, false);
+      }
+      String newtitle = String.format("%s %s %s",
+              dna ? MessageManager.getString("label.proteins")
+                      : MessageManager.getString("label.nucleotides"),
+              MessageManager.getString("label.for"), alignFrame.getTitle());
+      newFrame.setTitle(newtitle);
+
+      if (copyAlignment == null)
+      {
+        /*
+         * split frame display is turned off in preferences file
+         */
+        Desktop.addInternalFrame(newFrame, newtitle,
+                AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+        xrefViews.add(newFrame.alignPanel);
+        return; // via finally clause
+      }
+      AlignFrame copyThis = new AlignFrame(copyAlignment,
+              AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+      copyThis.setTitle(alignFrame.getTitle());
+
+      boolean showSequenceFeatures = alignFrame.getViewport()
+              .isShowSequenceFeatures();
+      newFrame.setShowSeqFeatures(showSequenceFeatures);
+      copyThis.setShowSeqFeatures(showSequenceFeatures);
+      FeatureRenderer myFeatureStyling = alignFrame.alignPanel
+              .getSeqPanel().seqCanvas.getFeatureRenderer();
+
+      /*
+       * copy feature rendering settings to split frame
+       */
+      newFrame.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
+              .transferSettings(myFeatureStyling);
+      copyThis.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
+              .transferSettings(myFeatureStyling);
+
+      /*
+       * apply 'database source' feature configuration
+       * if any was found
+       */
+      // TODO is this the feature colouring for the original
+      // alignment or the fetched xrefs? either could be Ensembl
+      newFrame.getViewport().applyFeaturesStyle(featureColourScheme);
+      copyThis.getViewport().applyFeaturesStyle(featureColourScheme);
+
+      SplitFrame sf = new SplitFrame(dna ? copyThis : newFrame,
+              dna ? newFrame : copyThis);
+      newFrame.setVisible(true);
+      copyThis.setVisible(true);
+      String linkedTitle = MessageManager
+              .getString("label.linked_view_title");
+      Desktop.addInternalFrame(sf, linkedTitle, -1, -1);
+      sf.adjustDivider();
+
+      // finally add the top, then bottom frame to the view list
+      xrefViews.add(dna ? copyThis.alignPanel : newFrame.alignPanel);
+      xrefViews.add(!dna ? copyThis.alignPanel : newFrame.alignPanel);
+
+    } catch (OutOfMemoryError e)
+    {
+      new OOMWarning("whilst fetching crossreferences", e);
+    } catch (Throwable e)
+    {
+      Cache.log.error("Error when finding crossreferences", e);
+    } finally
+    {
+      alignFrame.setProgressBar(MessageManager.formatMessage(
+              "status.finished_searching_for_sequences_from",
+              new Object[] { source }), sttime);
+    }
+  }
+
+  /**
+   * Makes an alignment containing the given sequences, and adds them to the
+   * given dataset, which is also set as the dataset for the new alignment
+   * 
+   * TODO: refactor to DatasetI method
+   * 
+   * @param dataset
+   * @param seqs
+   * @return
+   */
+  protected AlignmentI makeCrossReferencesAlignment(AlignmentI dataset,
+          AlignmentI seqs)
+  {
+    SequenceI[] sprods = new SequenceI[seqs.getHeight()];
+    for (int s = 0; s < sprods.length; s++)
+    {
+      sprods[s] = (seqs.getSequenceAt(s)).deriveSequence();
+      if (dataset.getSequences() == null
+              || !dataset.getSequences().contains(
+                      sprods[s].getDatasetSequence()))
+      {
+        dataset.addSequence(sprods[s].getDatasetSequence());
+      }
+      sprods[s].updatePDBIds();
+    }
+    Alignment al = new Alignment(sprods);
+    al.setDataset(dataset);
+    return al;
+  }
+
+  public CrossRefAction(AlignFrame alignFrame, SequenceI[] sel,
+          boolean _odna, String source)
+  {
+    this.alignFrame = alignFrame;
+    this.sel = sel;
+    this._odna = _odna;
+    this.source = source;
+  }
+
+  public static CrossRefAction showProductsFor(final SequenceI[] sel,
+          final boolean _odna, final String source,
+          final AlignFrame alignFrame)
+  {
+    return new CrossRefAction(alignFrame, sel, _odna, source);
+  }
+
+}
diff --git a/src/jalview/gui/CutAndPasteHtmlTransfer.java b/src/jalview/gui/CutAndPasteHtmlTransfer.java
index 7fded36..a8d463c 100644
--- a/src/jalview/gui/CutAndPasteHtmlTransfer.java
+++ b/src/jalview/gui/CutAndPasteHtmlTransfer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -100,6 +100,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
     });
     SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         textarea.requestFocus();
@@ -143,6 +144,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
     textarea.setText(text);
   }
 
+  @Override
   public void save_actionPerformed(ActionEvent e)
   {
     JalviewFileChooser chooser = new JalviewFileChooser(
@@ -173,6 +175,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
     }
   }
 
+  @Override
   public void toggleHtml_actionPerformed(ActionEvent e)
   {
     String txt = textarea.getText();
@@ -187,6 +190,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void copyItem_actionPerformed(ActionEvent e)
   {
     Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
@@ -210,6 +214,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void cancel_actionPerformed(ActionEvent e)
   {
     try
@@ -220,9 +225,11 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
     }
   }
 
+  @Override
   public void textarea_mousePressed(MouseEvent e)
   {
-    if (SwingUtilities.isRightMouseButton(e))
+    // isPopupTrigger is on mousePressed (Mac) or mouseReleased (Windows)
+    if (e.isPopupTrigger())
     {
       JPopupMenu popup = new JPopupMenu(
               MessageManager.getString("action.edit"));
@@ -230,6 +237,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
               MessageManager.getString("action.copy"));
       item.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent e)
         {
           copyItem_actionPerformed(e);
diff --git a/src/jalview/gui/CutAndPasteTransfer.java b/src/jalview/gui/CutAndPasteTransfer.java
index 9c5651a..1c5063e 100644
--- a/src/jalview/gui/CutAndPasteTransfer.java
+++ b/src/jalview/gui/CutAndPasteTransfer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -23,7 +23,9 @@ package jalview.gui;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.ComplexAlignFile;
+import jalview.api.FeatureSettingsModelI;
 import jalview.api.FeaturesDisplayedI;
+import jalview.api.FeaturesSourceI;
 import jalview.bin.Jalview;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
@@ -35,6 +37,7 @@ import jalview.io.IdentifyFile;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
 import jalview.jbgui.GCutAndPasteTransfer;
+import jalview.json.binding.biojson.v1.ColourSchemeMapper;
 import jalview.schemes.ColourSchemeI;
 import jalview.util.MessageManager;
 
@@ -71,6 +74,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
   {
     SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         textarea.requestFocus();
@@ -123,6 +127,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
     textarea.append(text);
   }
 
+  @Override
   public void save_actionPerformed(ActionEvent e)
   {
     JalviewFileChooser chooser = new JalviewFileChooser(
@@ -159,6 +164,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void copyItem_actionPerformed(ActionEvent e)
   {
     textarea.getSelectedText();
@@ -172,6 +178,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void pasteMenu_actionPerformed(ActionEvent e)
   {
     Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
@@ -197,6 +204,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void ok_actionPerformed(ActionEvent e)
   {
     String text = getText();
@@ -205,7 +213,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
       return;
     }
 
-    String format = new IdentifyFile().Identify(text, "Paste");
+    String format = new IdentifyFile().identify(text, "Paste");
     if (format == null || format.equalsIgnoreCase("EMPTY DATA FILE"))
     {
       System.err.println(MessageManager
@@ -246,8 +254,19 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
     {
       String title = MessageManager.formatMessage(
               "label.input_cut_paste_params", new String[] { format });
-      if (viewport != null)
+      FeatureSettingsModelI proxyColourScheme = source
+              .getFeatureColourScheme();
+
+      /*
+       * if the view panel was closed its alignment is nulled
+       * and this is an orphaned cut and paste window
+       */
+      if (viewport != null && viewport.getAlignment() != null)
       {
+        if (proxyColourScheme != null)
+        {
+          viewport.applyFeaturesStyle(proxyColourScheme);
+        }
         ((AlignViewport) viewport).addAlignment(al, title);
       }
       else
@@ -262,21 +281,34 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
                   .getHiddenSequences();
           boolean showSeqFeatures = ((ComplexAlignFile) source)
                   .isShowSeqFeatures();
-          ColourSchemeI cs = ((ComplexAlignFile) source).getColourScheme();
+          String colourSchemeName = ((ComplexAlignFile) source)
+                  .getGlobalColourScheme();
           FeaturesDisplayedI fd = ((ComplexAlignFile) source)
                   .getDisplayedFeatures();
           af = new AlignFrame(al, hiddenSeqs, colSel,
                   AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
           af.getViewport().setShowSequenceFeatures(showSeqFeatures);
           af.getViewport().setFeaturesDisplayed(fd);
-          af.changeColour(cs);
+          ColourSchemeI cs = ColourSchemeMapper.getJalviewColourScheme(
+                  colourSchemeName, al);
+          if (cs != null)
+          {
+            af.changeColour(cs);
+          }
         }
         else
         {
           af = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
                   AlignFrame.DEFAULT_HEIGHT);
+          if (source instanceof FeaturesSourceI)
+          {
+            af.getViewport().setShowSequenceFeatures(true);
+          }
+        }
+        if (proxyColourScheme != null)
+        {
+          af.getViewport().applyFeaturesStyle(proxyColourScheme);
         }
-
         af.currentFileFormat = format;
         Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH,
                 AlignFrame.DEFAULT_HEIGHT);
@@ -312,6 +344,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void cancel_actionPerformed(ActionEvent e)
   {
     try
@@ -322,9 +355,14 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
     }
   }
 
+  @Override
   public void textarea_mousePressed(MouseEvent e)
   {
-    if (SwingUtilities.isRightMouseButton(e))
+    /*
+     * isPopupTrigger is checked in mousePressed on Mac,
+     * in mouseReleased on Windows
+     */
+    if (e.isPopupTrigger())
     {
       JPopupMenu popup = new JPopupMenu(
               MessageManager.getString("action.edit"));
@@ -332,6 +370,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
               MessageManager.getString("action.copy"));
       item.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent e)
         {
           copyItem_actionPerformed(e);
@@ -341,6 +380,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
       item = new JMenuItem(MessageManager.getString("action.paste"));
       item.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent e)
         {
           pasteMenu_actionPerformed(e);
diff --git a/src/jalview/gui/DasSourceBrowser.java b/src/jalview/gui/DasSourceBrowser.java
index c0fefd1..970d062 100644
--- a/src/jalview/gui/DasSourceBrowser.java
+++ b/src/jalview/gui/DasSourceBrowser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -81,6 +81,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     ListSelectionModel rowSM = table.getSelectionModel();
     rowSM.addListSelectionListener(new ListSelectionListener()
     {
+      @Override
       public void valueChanged(ListSelectionEvent e)
       {
         ListSelectionModel lsm = (ListSelectionModel) e.getSource();
@@ -94,10 +95,10 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
 
     table.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mouseClicked(MouseEvent evt)
       {
-        if (evt.getClickCount() == 2
-                || SwingUtilities.isRightMouseButton(evt))
+        if (evt.getClickCount() == 2 || evt.isPopupTrigger())
         {
           editRemoveLocalSource(evt);
         }
@@ -119,6 +120,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     this(null);
   }
 
+  @Override
   public void paintComponent(java.awt.Graphics g)
   {
     if (sourceRegistry == null)
@@ -145,6 +147,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
 
     javax.swing.SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         TableSorter sorter = (TableSorter) table.getModel();
@@ -282,6 +285,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     fullDetails.setText(text.toString());
     javax.swing.SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         fullDetailsScrollpane.getVerticalScrollBar().setValue(0);
@@ -289,6 +293,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     });
   }
 
+  @Override
   public void run()
   {
     loadingDasSources = true;
@@ -358,6 +363,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     return selected;
   }
 
+  @Override
   public void refresh_actionPerformed(ActionEvent e)
   {
     saveProperties(jalview.bin.Cache.applicationProperties);
@@ -411,6 +417,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
 
     javax.swing.SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         filter1.setSelectedIndex(0);
@@ -420,6 +427,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     });
   }
 
+  @Override
   public void amendLocal(boolean newSource)
   {
     String url = "http://localhost:8080/", nickname = "";
@@ -440,7 +448,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     seqs.setSelected(seqsrc);
     JPanel panel = new JPanel(new BorderLayout());
     JPanel pane12 = new JPanel(new BorderLayout());
-    pane12.add(new JLabel(MessageManager.getString("label.name")),
+    pane12.add(new JLabel(MessageManager.getString("label.name:")),
             BorderLayout.CENTER);
     pane12.add(nametf, BorderLayout.EAST);
     panel.add(pane12, BorderLayout.NORTH);
@@ -503,6 +511,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
 
     SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         scrollPane.getVerticalScrollBar().setValue(
@@ -571,6 +580,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
       refreshTableData(data);
       SwingUtilities.invokeLater(new Runnable()
       {
+        @Override
         public void run()
         {
           scrollPane.getVerticalScrollBar().setValue(
@@ -582,6 +592,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     }
   }
 
+  @Override
   public void valueChanged(ListSelectionEvent evt)
   {
     // Called when the MainTable selection changes
@@ -691,6 +702,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     }
   }
 
+  @Override
   public void reset_actionPerformed(ActionEvent e)
   {
     registryURL.setText(sourceRegistry.getDasRegistryURL());
@@ -743,21 +755,25 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
 
     private Object[][] data;
 
+    @Override
     public int getColumnCount()
     {
       return columnNames.length;
     }
 
+    @Override
     public int getRowCount()
     {
       return data.length;
     }
 
+    @Override
     public String getColumnName(int col)
     {
       return columnNames[col];
     }
 
+    @Override
     public Object getValueAt(int row, int col)
     {
       return data[row][col];
@@ -768,6 +784,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
      * each cell. If we didn't implement this method, then the last column would
      * contain text ("true"/"false"), rather than a check box.
      */
+    @Override
     public Class getColumnClass(int c)
     {
       return getValueAt(0, c).getClass();
@@ -776,6 +793,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     /*
      * Don't need to implement this method unless your table's editable.
      */
+    @Override
     public boolean isCellEditable(int row, int col)
     {
       // Note that the data/cell address is constant,
@@ -787,6 +805,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     /*
      * Don't need to implement this method unless your table's data can change.
      */
+    @Override
     public void setValueAt(Object value, int row, int col)
     {
       data[row][col] = value;
@@ -812,6 +831,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
 
     Thread thr = new Thread(new Runnable()
     {
+      @Override
       public void run()
       {
         // this actually initialises the das source list
diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java
index 5c45232..ed602e8 100644
--- a/src/jalview/gui/Desktop.java
+++ b/src/jalview/gui/Desktop.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,9 +20,13 @@
  */
 package jalview.gui;
 
+import static jalview.util.UrlConstants.EMBLEBI_STRING;
+import static jalview.util.UrlConstants.SEQUENCE_ID;
+
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.bin.Cache;
+import jalview.bin.Jalview;
 import jalview.io.FileLoader;
 import jalview.io.FormatAdapter;
 import jalview.io.IdentifyFile;
@@ -33,6 +37,7 @@ import jalview.jbgui.GStructureViewer;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.ImageMaker;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.ParamManager;
 
@@ -45,6 +50,7 @@ import java.awt.GridLayout;
 import java.awt.Point;
 import java.awt.Rectangle;
 import java.awt.Toolkit;
+import java.awt.Window;
 import java.awt.datatransfer.Clipboard;
 import java.awt.datatransfer.ClipboardOwner;
 import java.awt.datatransfer.DataFlavor;
@@ -58,31 +64,34 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.FocusEvent;
 import java.awt.event.FocusListener;
+import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
-import java.beans.PropertyVetoException;
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
-import java.lang.reflect.Constructor;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.StringTokenizer;
 import java.util.Vector;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Semaphore;
 
+import javax.swing.AbstractAction;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
 import javax.swing.DefaultDesktopManager;
 import javax.swing.DesktopManager;
 import javax.swing.JButton;
+import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JComponent;
 import javax.swing.JDesktopPane;
@@ -94,9 +103,12 @@ import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.JProgressBar;
+import javax.swing.KeyStroke;
 import javax.swing.SwingUtilities;
 import javax.swing.event.HyperlinkEvent;
 import javax.swing.event.HyperlinkEvent.EventType;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
 import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
 
@@ -167,8 +179,6 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
   static final int yOffset = 30;
 
-  private static AlignFrame currentAlignFrame;
-
   public static jalview.ws.jws1.Discoverer discoverer;
 
   public static Object[] jalviewClipboard;
@@ -187,6 +197,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       this.delegate = delegate;
     }
 
+    @Override
     public void activateFrame(JInternalFrame f)
     {
       try
@@ -199,31 +210,37 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       }
     }
 
+    @Override
     public void beginDraggingFrame(JComponent f)
     {
       delegate.beginDraggingFrame(f);
     }
 
+    @Override
     public void beginResizingFrame(JComponent f, int direction)
     {
       delegate.beginResizingFrame(f, direction);
     }
 
+    @Override
     public void closeFrame(JInternalFrame f)
     {
       delegate.closeFrame(f);
     }
 
+    @Override
     public void deactivateFrame(JInternalFrame f)
     {
       delegate.deactivateFrame(f);
     }
 
+    @Override
     public void deiconifyFrame(JInternalFrame f)
     {
       delegate.deiconifyFrame(f);
     }
 
+    @Override
     public void dragFrame(JComponent f, int newX, int newY)
     {
       if (newY < 0)
@@ -233,31 +250,37 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       delegate.dragFrame(f, newX, newY);
     }
 
+    @Override
     public void endDraggingFrame(JComponent f)
     {
       delegate.endDraggingFrame(f);
     }
 
+    @Override
     public void endResizingFrame(JComponent f)
     {
       delegate.endResizingFrame(f);
     }
 
+    @Override
     public void iconifyFrame(JInternalFrame f)
     {
       delegate.iconifyFrame(f);
     }
 
+    @Override
     public void maximizeFrame(JInternalFrame f)
     {
       delegate.maximizeFrame(f);
     }
 
+    @Override
     public void minimizeFrame(JInternalFrame f)
     {
       delegate.minimizeFrame(f);
     }
 
+    @Override
     public void openFrame(JInternalFrame f)
     {
       delegate.openFrame(f);
@@ -274,6 +297,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
     }
 
+    @Override
     public void setBoundsForFrame(JComponent f, int newX, int newY,
             int newWidth, int newHeight)
     {
@@ -296,7 +320,20 @@ public class Desktop extends jalview.jbgui.GDesktop implements
      */
     instance = this;
     doVamsasClientCheck();
-    doGroovyCheck();
+
+    groovyShell = new JMenuItem();
+    groovyShell.setText(MessageManager.getString("label.groovy_console"));
+    groovyShell.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        groovyShell_actionPerformed();
+      }
+    });
+    toolsMenu.add(groovyShell);
+    groovyShell.setVisible(true);
+
     doConfigureStructurePrefs();
     setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
@@ -305,6 +342,10 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     boolean showjconsole = jalview.bin.Cache.getDefault(
             "SHOW_JAVA_CONSOLE", false);
     desktop = new MyDesktopPane(selmemusage);
+    if (Platform.isAMac())
+    {
+      desktop.setDoubleBuffered(false);
+    }
     showMemusage.setSelected(selmemusage);
     desktop.setBackground(Color.white);
     getContentPane().setLayout(new BorderLayout());
@@ -348,8 +389,11 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
     showNews.setVisible(false);
 
+    checkURLLinks();
+
     this.addWindowListener(new WindowAdapter()
     {
+      @Override
       public void windowClosing(WindowEvent evt)
       {
         quit();
@@ -359,9 +403,19 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     MouseAdapter ma;
     this.addMouseListener(ma = new MouseAdapter()
     {
+      @Override
       public void mousePressed(MouseEvent evt)
       {
-        if (SwingUtilities.isRightMouseButton(evt))
+        if (evt.isPopupTrigger()) // Mac
+        {
+          showPasteMenu(evt.getX(), evt.getY());
+        }
+      }
+
+      @Override
+      public void mouseReleased(MouseEvent evt)
+      {
+        if (evt.isPopupTrigger()) // Windows
         {
           showPasteMenu(evt.getX(), evt.getY());
         }
@@ -391,6 +445,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     // Spawn a thread that shows the splashscreen
     SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         new SplashScreen();
@@ -402,6 +457,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     // takes to open it later on.
     new Thread(new Runnable()
     {
+      @Override
       public void run()
       {
         Cache.log.debug("Filechooser init thread started.");
@@ -573,16 +629,19 @@ public class Desktop extends jalview.jbgui.GDesktop implements
         // reselected again.
         boolean refresh = true;
 
+        @Override
         public void menuCanceled(MenuEvent e)
         {
           refresh = true;
         }
 
+        @Override
         public void menuDeselected(MenuEvent e)
         {
           refresh = true;
         }
 
+        @Override
         public void menuSelected(MenuEvent e)
         {
           if (refresh)
@@ -603,6 +662,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
             MessageManager.getString("label.paste_new_window"));
     item.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent evt)
       {
         paste();
@@ -625,7 +685,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
         String file = (String) contents
                 .getTransferData(DataFlavor.stringFlavor);
 
-        String format = new IdentifyFile().Identify(file,
+        String format = new IdentifyFile().identify(file,
                 FormatAdapter.PASTE);
 
         new FileLoader().LoadFile(file, FormatAdapter.PASTE, format);
@@ -750,38 +810,66 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     frame.setResizable(resizable);
     frame.setMaximizable(resizable);
     frame.setIconifiable(resizable);
-    frame.setFrameIcon(null);
-
+    if (Platform.isAMac())
+    {
+      frame.setIconifiable(false);
+      frame.setFrameIcon(null);
+      // frame.setDesktopIcon(null);
+      frame.setDoubleBuffered(false);
+    }
     if (frame.getX() < 1 && frame.getY() < 1)
     {
       frame.setLocation(xOffset * openFrameCount, yOffset
               * ((openFrameCount - 1) % 10) + yOffset);
     }
 
+    /*
+     * add an entry for the new frame in the Window menu 
+     * (and remove it when the frame is closed)
+     */
     final JMenuItem menuItem = new JMenuItem(title);
-    frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
+    frame.addInternalFrameListener(new InternalFrameAdapter()
     {
-      public void internalFrameActivated(
-              javax.swing.event.InternalFrameEvent evt)
+      @Override
+      public void internalFrameActivated(InternalFrameEvent evt)
       {
         JInternalFrame itf = desktop.getSelectedFrame();
         if (itf != null)
         {
           itf.requestFocus();
         }
-
       }
 
-      public void internalFrameClosed(
-              javax.swing.event.InternalFrameEvent evt)
+      @Override
+      public void internalFrameClosed(InternalFrameEvent evt)
       {
         PaintRefresher.RemoveComponent(frame);
-        openFrameCount--;
+
+        /*
+         * defensive check to prevent frames being
+         * added half off the window
+         */
+        if (openFrameCount > 0)
+        {
+          openFrameCount--;
+        }
+
+        /*
+         * ensure no reference to alignFrame retained by menu item listener
+         */
+        if (menuItem.getActionListeners().length > 0)
+        {
+          menuItem.removeActionListener(menuItem.getActionListeners()[0]);
+        }
         windowMenu.remove(menuItem);
         JInternalFrame itf = desktop.getSelectedFrame();
         if (itf != null)
         {
           itf.requestFocus();
+          if (itf instanceof AlignFrame)
+          {
+            Jalview.setCurrentAlignFrame((AlignFrame) itf);
+          }
         }
         System.gc();
       };
@@ -789,6 +877,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
     menuItem.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         try
@@ -801,47 +890,6 @@ public class Desktop extends jalview.jbgui.GDesktop implements
         }
       }
     });
-    menuItem.addMouseListener(new MouseListener()
-    {
-
-      @Override
-      public void mouseReleased(MouseEvent e)
-      {
-      }
-
-      @Override
-      public void mousePressed(MouseEvent e)
-      {
-      }
-
-      @Override
-      public void mouseExited(MouseEvent e)
-      {
-        try
-        {
-          frame.setSelected(false);
-        } catch (PropertyVetoException e1)
-        {
-        }
-      }
-
-      @Override
-      public void mouseEntered(MouseEvent e)
-      {
-        try
-        {
-          frame.setSelected(true);
-        } catch (PropertyVetoException e1)
-        {
-        }
-      }
-
-      @Override
-      public void mouseClicked(MouseEvent e)
-      {
-
-      }
-    });
 
     windowMenu.add(menuItem);
 
@@ -861,6 +909,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     }
   }
 
+  @Override
   public void lostOwnership(Clipboard clipboard, Transferable contents)
   {
     if (!internalCopy)
@@ -871,14 +920,17 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     internalCopy = false;
   }
 
+  @Override
   public void dragEnter(DropTargetDragEvent evt)
   {
   }
 
+  @Override
   public void dragExit(DropTargetEvent evt)
   {
   }
 
+  @Override
   public void dragOver(DropTargetDragEvent evt)
   {
   }
@@ -894,57 +946,23 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void drop(DropTargetDropEvent evt)
   {
     boolean success = true;
+    // JAL-1552 - acceptDrop required before getTransferable call for
+    // Java's Transferable for native dnd
+    evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
     Transferable t = evt.getTransferable();
-    java.util.List files = null;
-    java.util.List protocols = null;
+    java.util.List<String> files = new ArrayList<String>();
+    java.util.List<String> protocols = new ArrayList<String>();
 
     try
     {
-      DataFlavor uriListFlavor = new DataFlavor(
-              "text/uri-list;class=java.lang.String");
-      if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
-      {
-        // Works on Windows and MacOSX
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        files = (java.util.List) t
-                .getTransferData(DataFlavor.javaFileListFlavor);
-      }
-      else if (t.isDataFlavorSupported(uriListFlavor))
-      {
-        // This is used by Unix drag system
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        String data = (String) t.getTransferData(uriListFlavor);
-        files = new java.util.ArrayList(1);
-        protocols = new java.util.ArrayList(1);
-        for (java.util.StringTokenizer st = new java.util.StringTokenizer(
-                data, "\r\n"); st.hasMoreTokens();)
-        {
-          String s = st.nextToken();
-          if (s.startsWith("#"))
-          {
-            // the line is a comment (as per the RFC 2483)
-            continue;
-          }
-          java.net.URI uri = new java.net.URI(s);
-          if (uri.getScheme().toLowerCase().startsWith("http"))
-          {
-            protocols.add(FormatAdapter.URL);
-            files.add(uri.toString());
-          }
-          else
-          {
-            // otherwise preserve old behaviour: catch all for file objects
-            java.io.File file = new java.io.File(uri);
-            protocols.add(FormatAdapter.FILE);
-            files.add(file.toString());
-          }
-        }
-      }
+      Desktop.transferFromDropTarget(files, protocols, evt, t);
     } catch (Exception e)
     {
+      e.printStackTrace();
       success = false;
     }
 
@@ -966,7 +984,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
           }
           else
           {
-            format = new IdentifyFile().Identify(file, protocol);
+            format = new IdentifyFile().identify(file, protocol);
           }
 
           new FileLoader().LoadFile(file, protocol, format);
@@ -1017,20 +1035,13 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       }
       else
       {
-        format = new IdentifyFile().Identify(choice, FormatAdapter.FILE);
+        format = new IdentifyFile().identify(choice, FormatAdapter.FILE);
       }
 
       if (viewport != null)
       {
         new FileLoader().LoadFile(viewport, choice, FormatAdapter.FILE,
                 format);
-        // viewport.setShowSequenceFeatures(JSONFile.isSeqFeaturesEnabled());
-        // AlignFrame af = viewport.getAlignPanel().alignFrame;
-        // if (af != null)
-        // {
-        // af.changeColour(JSONFile.getColourScheme());
-        // af.setMenusForViewport();
-        // }
       }
       else
       {
@@ -1100,7 +1111,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     }
     else
     {
-      String format = new IdentifyFile().Identify(url, FormatAdapter.URL);
+      String format = new IdentifyFile().identify(url, FormatAdapter.URL);
 
       if (format.equals("URL NOT FOUND"))
       {
@@ -1171,6 +1182,13 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       dialogExecutor.shutdownNow();
     }
     closeAll_actionPerformed(null);
+
+    if (groovyConsole != null)
+    {
+      // suppress a possible repeat prompt to save script
+      groovyConsole.setDirty(false);
+      groovyConsole.exit();
+    }
     System.exit(0);
   }
 
@@ -1201,6 +1219,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     // message.toString(), "About Jalview", JOptionPane.INFORMATION_MESSAGE);
     new Thread(new Runnable()
     {
+      @Override
       public void run()
       {
         new SplashScreen(true);
@@ -1293,6 +1312,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
   @Override
   public void closeAll_actionPerformed(ActionEvent e)
   {
+    // TODO show a progress bar while closing?
     JInternalFrame[] frames = desktop.getAllFrames();
     for (int i = 0; i < frames.length; i++)
     {
@@ -1303,6 +1323,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       {
       }
     }
+    Jalview.setCurrentAlignFrame(null);
     System.out.println("ALL CLOSED");
     if (v_client != null)
     {
@@ -1319,6 +1340,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     {
       ssm.resetAll();
     }
+    System.gc();
   }
 
   @Override
@@ -1511,6 +1533,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
       new Thread(new Runnable()
       {
+        @Override
         public void run()
         {
           // TODO: refactor to Jalview desktop session controller action.
@@ -1582,6 +1605,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
               selectedFile.getParent());
       new Thread(new Runnable()
       {
+        @Override
         public void run()
         {
           setProgressBar(
@@ -1786,7 +1810,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    * 
    * @param af
    */
-  public void explodeViews(AlignFrame af)
+  public static void explodeViews(AlignFrame af)
   {
     int size = af.alignPanels.size();
     if (size < 2)
@@ -2101,6 +2125,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
           sessit.addActionListener(new ActionListener()
           {
 
+            @Override
             public void actionPerformed(ActionEvent e)
             {
               if (dsktp.v_client == null)
@@ -2108,6 +2133,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
                 Thread rthr = new Thread(new Runnable()
                 {
 
+                  @Override
                   public void run()
                   {
                     dsktp.v_client = new VamsasApplication(dsktp, mysesid);
@@ -2239,6 +2265,84 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     new Thread(jvq).start();
   }
 
+  public void checkURLLinks()
+  {
+    // Thread off the URL link checker
+    addDialogThread(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        if (Cache.getDefault("CHECKURLLINKS", true))
+        {
+          // check what the actual links are - if it's just the default don't
+          // bother with the warning
+          Vector<String> links = Preferences.sequenceURLLinks;
+
+          // only need to check links if there is one with a
+          // SEQUENCE_ID which is not the default EMBL_EBI link
+          ListIterator<String> li = links.listIterator();
+          boolean check = false;
+          List<JLabel> urls = new ArrayList<JLabel>();
+          while (li.hasNext())
+          {
+            String link = li.next();
+            if (link.contains(SEQUENCE_ID) && !link.equals(EMBLEBI_STRING))
+            {
+              check = true;
+              int barPos = link.indexOf("|");
+              String urlMsg = barPos == -1 ? link : link.substring(0,
+                      barPos) + ": " + link.substring(barPos + 1);
+              urls.add(new JLabel(urlMsg));
+            }
+          }
+          if (!check)
+          {
+            return;
+          }
+
+          // ask user to check in case URL links use old style tokens
+          // ($SEQUENCE_ID$ for sequence id _or_ accession id)
+          JPanel msgPanel = new JPanel();
+          msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
+          msgPanel.add(Box.createVerticalGlue());
+          JLabel msg = new JLabel(
+                  MessageManager
+                          .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
+          JLabel msg2 = new JLabel(
+                  MessageManager
+                          .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
+          msgPanel.add(msg);
+          for (JLabel url : urls)
+          {
+            msgPanel.add(url);
+          }
+          msgPanel.add(msg2);
+
+          final JCheckBox jcb = new JCheckBox(
+                  MessageManager.getString("label.do_not_display_again"));
+          jcb.addActionListener(new ActionListener()
+          {
+            @Override
+            public void actionPerformed(ActionEvent e)
+            {
+              // update Cache settings for "don't show this again"
+              boolean showWarningAgain = !jcb.isSelected();
+              Cache.setProperty("CHECKURLLINKS",
+                      Boolean.valueOf(showWarningAgain).toString());
+            }
+          });
+          msgPanel.add(jcb);
+
+          JOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
+                  MessageManager
+                          .getString("label.SEQUENCE_ID_no_longer_used"),
+                  JOptionPane.WARNING_MESSAGE);
+        }
+      }
+    });
+    }
+
   /**
    * Proxy class for JDesktopPane which optionally displays the current memory
    * usage and highlights the desktop area with a red bar if free memory runs
@@ -2280,6 +2384,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       return showMemoryUsage;
     }
 
+    @Override
     public void run()
     {
       df = java.text.NumberFormat.getNumberInstance();
@@ -2346,24 +2451,6 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
   protected JMenuItem groovyShell;
 
-  public void doGroovyCheck()
-  {
-    if (jalview.bin.Cache.groovyJarsPresent())
-    {
-      groovyShell = new JMenuItem();
-      groovyShell.setText(MessageManager.getString("label.groovy_console"));
-      groovyShell.addActionListener(new ActionListener()
-      {
-        public void actionPerformed(ActionEvent e)
-        {
-          groovyShell_actionPerformed();
-        }
-      });
-      toolsMenu.add(groovyShell);
-      groovyShell.setVisible(true);
-    }
-  }
-
   /**
    * Accessor method to quickly get all the AlignmentFrames loaded.
    * 
@@ -2371,6 +2458,12 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    */
   public static AlignFrame[] getAlignFrames()
   {
+    if (Jalview.isHeadlessMode())
+    {
+      // Desktop.desktop is null in headless mode
+      return new AlignFrame[] { Jalview.currentAlignFrame };
+    }
+
     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
 
     if (frames == null)
@@ -2445,24 +2538,9 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    */
   public void groovyShell_actionPerformed()
   {
-    // use reflection to avoid creating compilation dependency.
-    if (!jalview.bin.Cache.groovyJarsPresent())
-    {
-      throw new Error(
-              MessageManager
-                      .getString("error.implementation_error_cannot_create_groovyshell"));
-    }
     try
     {
-      Class<?> gcClass = Desktop.class.getClassLoader().loadClass(
-              "groovy.ui.Console");
-      Constructor<?> gccons = gcClass.getConstructor();
-      java.lang.reflect.Method setvar = gcClass.getMethod("setVariable",
-              new Class[] { String.class, Object.class });
-      java.lang.reflect.Method run = gcClass.getMethod("run");
-      Object gc = gccons.newInstance();
-      setvar.invoke(gc, new Object[] { "Jalview", this });
-      run.invoke(gc);
+      openGroovyConsole();
     } catch (Exception ex)
     {
       jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
@@ -2475,6 +2553,93 @@ public class Desktop extends jalview.jbgui.GDesktop implements
   }
 
   /**
+   * Open the Groovy console
+   */
+  void openGroovyConsole()
+  {
+    if (groovyConsole == null)
+    {
+      groovyConsole = new groovy.ui.Console();
+      groovyConsole.setVariable("Jalview", this);
+      groovyConsole.run();
+
+      /*
+       * We allow only one console at a time, so that AlignFrame menu option
+       * 'Calculate | Run Groovy script' is unambiguous.
+       * Disable 'Groovy Console', and enable 'Run script', when the console is 
+       * opened, and the reverse when it is closed
+       */
+      Window window = (Window) groovyConsole.getFrame();
+      window.addWindowListener(new WindowAdapter()
+      {
+        @Override
+        public void windowClosed(WindowEvent e)
+        {
+          /*
+           * rebind CMD-Q from Groovy Console to Jalview Quit
+           */
+          addQuitHandler();
+          enableExecuteGroovy(false);
+        }
+      });
+    }
+
+    /*
+     * show Groovy console window (after close and reopen)
+     */
+    ((Window) groovyConsole.getFrame()).setVisible(true);
+
+    /*
+     * if we got this far, enable 'Run Groovy' in AlignFrame menus
+     * and disable opening a second console
+     */
+    enableExecuteGroovy(true);
+  }
+
+  /**
+   * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
+   * binding when opened
+   */
+  protected void addQuitHandler()
+  {
+    getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
+            KeyStroke.getKeyStroke(KeyEvent.VK_Q, Toolkit
+                    .getDefaultToolkit().getMenuShortcutKeyMask()), "Quit");
+    getRootPane().getActionMap().put("Quit", new AbstractAction()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        quit();
+      }
+    });
+  }
+
+  /**
+   * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
+   * 
+   * @param enabled
+   *          true if Groovy console is open
+   */
+  public void enableExecuteGroovy(boolean enabled)
+  {
+    /*
+     * disable opening a second Groovy console
+     * (or re-enable when the console is closed)
+     */
+    groovyShell.setEnabled(!enabled);
+
+    AlignFrame[] alignFrames = getAlignFrames();
+    if (alignFrames != null)
+    {
+      for (AlignFrame af : alignFrames)
+      {
+        af.setGroovyEnabled(enabled);
+      }
+    }
+  }
+
+  /**
    * Progress bars managed by the IProgressIndicator method.
    */
   private Hashtable<Long, JPanel> progressBars;
@@ -2486,6 +2651,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    * 
    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
    */
+  @Override
   public void setProgressBar(String message, long id)
   {
     if (progressBars == null)
@@ -2515,6 +2681,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    * @see jalview.gui.IProgressIndicator#registerHandler(long,
    * jalview.gui.IProgressIndicatorHandler)
    */
+  @Override
   public void registerHandler(final long id,
           final IProgressIndicatorHandler handler)
   {
@@ -2535,6 +2702,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       cancel.addActionListener(new ActionListener()
       {
 
+        @Override
         public void actionPerformed(ActionEvent e)
         {
           handler.cancelActivity(id);
@@ -2693,6 +2861,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
             // only run if we aren't already displaying one of these.
             addDialogThread(serviceChangedDialog = new Runnable()
             {
+              @Override
               public void run()
               {
 
@@ -2776,6 +2945,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
   {
     new Thread(new Runnable()
     {
+      @Override
       public void run()
       {
         try
@@ -2865,6 +3035,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    */
   private java.util.concurrent.Semaphore block = new Semaphore(0);
 
+  private static groovy.ui.Console groovyConsole;
+
   /**
    * add another dialog thread to the queue
    * 
@@ -2874,6 +3046,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
   {
     dialogExecutor.submit(new Runnable()
     {
+      @Override
       public void run()
       {
         if (dialogPause)
@@ -2917,7 +3090,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     ImageMaker im = new jalview.util.ImageMaker(this, ImageMaker.TYPE.EPS,
             "View of Desktop", getWidth(), getHeight(), of = new File(
                     "Jalview_snapshot" + System.currentTimeMillis()
-                            + ".eps"), "View of desktop");
+                            + ".eps"), "View of desktop", null, 0, false);
     try
     {
       paintAll(im.getGraphics());
@@ -2933,7 +3106,11 @@ public class Desktop extends jalview.jbgui.GDesktop implements
   }
 
   /**
-   * Explode the views in the given frame into separate AlignFrame windows.
+   * Explode the views in the given SplitFrame into separate SplitFrame windows.
+   * This respects (remembers) any previous 'exploded geometry' i.e. the size
+   * and location last time the view was expanded (if any). However it does not
+   * remember the split pane divider location - this is set to match the
+   * 'exploding' frame.
    * 
    * @param sf
    */
@@ -2962,27 +3139,39 @@ public class Desktop extends jalview.jbgui.GDesktop implements
        * AlignmentPanel objects, including their AlignmentViewports, so the
        * cdna/protein relationships between the viewports is carried over to the
        * new split frames.
+       * 
+       * explodedGeometry holds the (x, y) position of the previously exploded
+       * SplitFrame, and the (width, height) of the AlignFrame component
        */
       AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
       AlignFrame newTopFrame = new AlignFrame(topPanel);
-      newTopFrame.setSize(new Dimension(AlignFrame.DEFAULT_WIDTH,
-              AlignFrame.DEFAULT_HEIGHT));
+      newTopFrame.setSize(oldTopFrame.getSize());
       newTopFrame.setVisible(true);
+      Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
+              .getExplodedGeometry();
+      if (geometry != null)
+      {
+        newTopFrame.setSize(geometry.getSize());
+      }
+
       AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
       AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
-      newBottomFrame.setSize(new Dimension(AlignFrame.DEFAULT_WIDTH,
-              AlignFrame.DEFAULT_HEIGHT));
+      newBottomFrame.setSize(oldBottomFrame.getSize());
       newBottomFrame.setVisible(true);
+      geometry = ((AlignViewport) bottomPanel.getAlignViewport())
+              .getExplodedGeometry();
+      if (geometry != null)
+      {
+        newBottomFrame.setSize(geometry.getSize());
+      }
+
       topPanel.av.setGatherViewsHere(false);
       bottomPanel.av.setGatherViewsHere(false);
       JInternalFrame splitFrame = new SplitFrame(newTopFrame,
               newBottomFrame);
-      // either panel may hold previous exploded frame geometry
-      Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
-              .getExplodedGeometry();
       if (geometry != null)
       {
-        splitFrame.setBounds(geometry);
+        splitFrame.setLocation(geometry.getLocation());
       }
       Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
     }
@@ -3005,10 +3194,18 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    */
   public void gatherViews(GSplitFrame source)
   {
+    /*
+     * special handling of explodedGeometry for a view within a SplitFrame: - it
+     * holds the (x, y) position of the enclosing SplitFrame, and the (width,
+     * height) of the AlignFrame component
+     */
     AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
     AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
-    myTopFrame.viewport.setExplodedGeometry(source.getBounds());
-    myBottomFrame.viewport.setExplodedGeometry(source.getBounds());
+    myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
+            source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
+    myBottomFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
+            source.getY(), myBottomFrame.getWidth(), myBottomFrame
+                    .getHeight()));
     myTopFrame.viewport.setGatherViewsHere(true);
     myBottomFrame.viewport.setGatherViewsHere(true);
     String topViewId = myTopFrame.viewport.getSequenceSetId();
@@ -3033,10 +3230,10 @@ public class Desktop extends jalview.jbgui.GDesktop implements
             gatherThis = true;
             topPanel.av.setGatherViewsHere(false);
             bottomPanel.av.setGatherViewsHere(false);
-            // both panels refer to the same split frame geometry
-            Rectangle position = sf.getBounds();
-            topPanel.av.setExplodedGeometry(position);
-            bottomPanel.av.setExplodedGeometry(position);
+            topPanel.av.setExplodedGeometry(new Rectangle(sf.getLocation(),
+                    topFrame.getSize()));
+            bottomPanel.av.setExplodedGeometry(new Rectangle(sf
+                    .getLocation(), bottomFrame.getSize()));
             myTopFrame.addAlignmentPanel(topPanel, false);
             myBottomFrame.addAlignmentPanel(bottomPanel, false);
           }
@@ -3055,17 +3252,106 @@ public class Desktop extends jalview.jbgui.GDesktop implements
      * The dust settles...give focus to the tab we did this from.
      */
     myTopFrame.setDisplayedView(myTopFrame.alignPanel);
-
   }
 
-  public static AlignFrame getCurrentAlignFrame()
+  public static groovy.ui.Console getGroovyConsole()
   {
-    return currentAlignFrame;
+    return groovyConsole;
   }
 
-  public static void setCurrentAlignFrame(AlignFrame currentAlignFrame)
+  public static void transferFromDropTarget(List<String> files,
+          List<String> protocols, DropTargetDropEvent evt, Transferable t)
+          throws Exception
   {
-    Desktop.currentAlignFrame = currentAlignFrame;
-  }
 
+    DataFlavor uriListFlavor = new DataFlavor(
+            "text/uri-list;class=java.lang.String");
+    if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
+    {
+      // Works on Windows and MacOSX
+      Cache.log.debug("Drop handled as javaFileListFlavor");
+      for (Object file : (List) t
+              .getTransferData(DataFlavor.javaFileListFlavor))
+      {
+        files.add(((File) file).toString());
+        protocols.add(FormatAdapter.FILE);
+      }
+    }
+    else
+    {
+      // Unix like behaviour
+      boolean added = false;
+      String data = null;
+      if (t.isDataFlavorSupported(uriListFlavor))
+      {
+        Cache.log.debug("Drop handled as uriListFlavor");
+        // This is used by Unix drag system
+        data = (String) t.getTransferData(uriListFlavor);
+      }
+      if (data == null)
+      {
+        // fallback to text: workaround - on OSX where there's a JVM bug
+        Cache.log.debug("standard URIListFlavor failed. Trying text");
+        // try text fallback
+        data = (String) t.getTransferData(new DataFlavor(
+                "text/plain;class=java.lang.String"));
+        if (Cache.log.isDebugEnabled())
+        {
+          Cache.log.debug("fallback returned " + data);
+        }
+      }
+      while (protocols.size() < files.size())
+      {
+        Cache.log.debug("Adding missing FILE protocol for "
+                + files.get(protocols.size()));
+        protocols.add(FormatAdapter.FILE);
+      }
+      for (java.util.StringTokenizer st = new java.util.StringTokenizer(
+              data, "\r\n"); st.hasMoreTokens();)
+      {
+        added = true;
+        String s = st.nextToken();
+        if (s.startsWith("#"))
+        {
+          // the line is a comment (as per the RFC 2483)
+          continue;
+        }
+        java.net.URI uri = new java.net.URI(s);
+        if (uri.getScheme().toLowerCase().startsWith("http"))
+        {
+          protocols.add(FormatAdapter.URL);
+          files.add(uri.toString());
+        }
+        else
+        {
+          // otherwise preserve old behaviour: catch all for file objects
+          java.io.File file = new java.io.File(uri);
+          protocols.add(FormatAdapter.FILE);
+          files.add(file.toString());
+        }
+      }
+      if (Cache.log.isDebugEnabled())
+      {
+        if (data == null || !added)
+        {
+          Cache.log
+                  .debug("Couldn't resolve drop data. Here are the supported flavors:");
+          for (DataFlavor fl : t.getTransferDataFlavors())
+          {
+            Cache.log.debug("Supported transfer dataflavor: "
+                    + fl.toString());
+            Object df = t.getTransferData(fl);
+            if (df != null)
+            {
+              Cache.log.debug("Retrieves: " + df);
+            }
+            else
+            {
+              Cache.log.debug("Retrieved nothing");
+            }
+          }
+        }
+      }
+    }
+  }
 }
diff --git a/src/jalview/gui/EPSOptions.java b/src/jalview/gui/EPSOptions.java
index 73abeaf..f5078b0 100644
--- a/src/jalview/gui/EPSOptions.java
+++ b/src/jalview/gui/EPSOptions.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/EditNameDialog.java b/src/jalview/gui/EditNameDialog.java
index 8c67f6e..07c1e53 100644
--- a/src/jalview/gui/EditNameDialog.java
+++ b/src/jalview/gui/EditNameDialog.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/FeatureColourChooser.java b/src/jalview/gui/FeatureColourChooser.java
index 25527dc..96f1e0b 100644
--- a/src/jalview/gui/FeatureColourChooser.java
+++ b/src/jalview/gui/FeatureColourChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,9 +20,9 @@
  */
 package jalview.gui;
 
+import jalview.api.FeatureColourI;
 import jalview.datamodel.GraphLine;
-import jalview.schemes.AnnotationColourGradient;
-import jalview.schemes.GraduatedColor;
+import jalview.schemes.FeatureColour;
 import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
@@ -33,7 +33,6 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
-import java.util.Hashtable;
 
 import javax.swing.BorderFactory;
 import javax.swing.JCheckBox;
@@ -52,16 +51,16 @@ public class FeatureColourChooser extends JalviewDialog
   // FeatureSettings fs;
   FeatureRenderer fr;
 
-  private GraduatedColor cs;
+  private FeatureColourI cs;
 
-  private Object oldcs;
+  private FeatureColourI oldcs;
 
   /**
    * 
    * @return the last colour setting selected by user - either oldcs (which may
    *         be a java.awt.Color) or the new GraduatedColor
    */
-  public Object getLastColour()
+  public FeatureColourI getLastColour()
   {
     if (cs == null)
     {
@@ -70,15 +69,15 @@ public class FeatureColourChooser extends JalviewDialog
     return cs;
   }
 
-  Hashtable oldgroupColours;
-
   AlignmentPanel ap;
 
   boolean adjusting = false;
 
-  private float min;
+  final private float min;
+
+  final private float max;
 
-  private float max;
+  final private float scaleFactor;
 
   String type = null;
 
@@ -93,25 +92,28 @@ public class FeatureColourChooser extends JalviewDialog
     this.fr = frender;
     this.type = type;
     ap = fr.ap;
-    initDialogFrame(this, true, block, "Graduated Feature Colour for "
-            + type, 480, 185);
+    String title = MessageManager.formatMessage(
+            "label.graduated_color_for_params", new String[] { type });
+    initDialogFrame(this, true, block, title, 480, 185);
     // frame.setLayer(JLayeredPane.PALETTE_LAYER);
     // Desktop.addInternalFrame(frame, "Graduated Feature Colour for "+type,
     // 480, 145);
 
     slider.addChangeListener(new ChangeListener()
     {
+      @Override
       public void stateChanged(ChangeEvent evt)
       {
         if (!adjusting)
         {
-          thresholdValue.setText(((float) slider.getValue() / 1000f) + "");
+          thresholdValue.setText((slider.getValue() / scaleFactor) + "");
           valueChanged();
         }
       }
     });
     slider.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mouseReleased(MouseEvent evt)
       {
         if (ap != null)
@@ -122,36 +124,44 @@ public class FeatureColourChooser extends JalviewDialog
       }
     });
 
-    float mm[] = ((float[][]) fr.getMinMax().get(type))[0];
+    float mm[] = fr.getMinMax().get(type)[0];
     min = mm[0];
     max = mm[1];
+
+    /*
+     * ensure scale factor allows a scaled range with
+     * 10 integer divisions ('ticks'); if we have got here,
+     * we should expect that max != min
+     */
+    scaleFactor = (max == min) ? 1f : 100f / (max - min);
+
     oldcs = fr.getFeatureColours().get(type);
-    if (oldcs instanceof GraduatedColor)
+    if (!oldcs.isSimpleColour())
     {
-      if (((GraduatedColor) oldcs).isAutoScale())
+      if (oldcs.isAutoScaled())
       {
         // update the scale
-        cs = new GraduatedColor((GraduatedColor) oldcs, min, max);
+        cs = new FeatureColour((FeatureColour) oldcs, min, max);
       }
       else
       {
-        cs = new GraduatedColor((GraduatedColor) oldcs);
+        cs = new FeatureColour((FeatureColour) oldcs);
       }
     }
     else
     {
       // promote original color to a graduated color
-      Color bl = Color.black;
-      if (oldcs instanceof Color)
+      Color bl = oldcs.getColour();
+      if (bl == null)
       {
-        bl = (Color) oldcs;
+        bl = Color.BLACK;
       }
       // original colour becomes the maximum colour
-      cs = new GraduatedColor(Color.white, bl, mm[0], mm[1]);
+      cs = new FeatureColour(Color.white, bl, mm[0], mm[1]);
       cs.setColourByLabel(false);
     }
-    minColour.setBackground(oldminColour = cs.getMinColor());
-    maxColour.setBackground(oldmaxColour = cs.getMaxColor());
+    minColour.setBackground(oldminColour = cs.getMinColour());
+    maxColour.setBackground(oldmaxColour = cs.getMaxColour());
     adjusting = true;
 
     try
@@ -161,18 +171,15 @@ public class FeatureColourChooser extends JalviewDialog
     {
     }
     // update the gui from threshold state
-    thresholdIsMin.setSelected(!cs.isAutoScale());
+    thresholdIsMin.setSelected(!cs.isAutoScaled());
     colourByLabel.setSelected(cs.isColourByLabel());
-    if (cs.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
+    if (cs.hasThreshold())
     {
       // initialise threshold slider and selector
-      threshold
-              .setSelectedIndex(cs.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD ? 1
-                      : 2);
+      threshold.setSelectedIndex(cs.isAboveThreshold() ? 1 : 2);
       slider.setEnabled(true);
       thresholdValue.setEnabled(true);
-      threshline = new jalview.datamodel.GraphLine((max - min) / 2f,
-              "Threshold", Color.black);
+      threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black);
 
     }
 
@@ -182,17 +189,6 @@ public class FeatureColourChooser extends JalviewDialog
     waitForInput();
   }
 
-  public FeatureColourChooser()
-  {
-    try
-    {
-      jbInit();
-    } catch (Exception ex)
-    {
-      ex.printStackTrace();
-    }
-  }
-
   private void jbInit() throws Exception
   {
 
@@ -202,6 +198,7 @@ public class FeatureColourChooser extends JalviewDialog
     minColour.setToolTipText(MessageManager.getString("label.min_colour"));
     minColour.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mousePressed(MouseEvent e)
       {
         if (minColour.isEnabled())
@@ -216,6 +213,7 @@ public class FeatureColourChooser extends JalviewDialog
     maxColour.setToolTipText(MessageManager.getString("label.max_colour"));
     maxColour.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mousePressed(MouseEvent e)
       {
         if (maxColour.isEnabled())
@@ -235,6 +233,7 @@ public class FeatureColourChooser extends JalviewDialog
     jPanel2.setBackground(Color.white);
     threshold.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         threshold_actionPerformed(e);
@@ -243,14 +242,15 @@ public class FeatureColourChooser extends JalviewDialog
     threshold.setToolTipText(MessageManager
             .getString("label.threshold_feature_display_by_score"));
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_no_thereshold")); // index 0
+            .getString("label.threshold_feature_no_threshold")); // index 0
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_above_thereshold")); // index 1
+            .getString("label.threshold_feature_above_threshold")); // index 1
     threshold.addItem(MessageManager
-            .getString("label.threshold_feature_below_thereshold")); // index 2
+            .getString("label.threshold_feature_below_threshold")); // index 2
     jPanel3.setLayout(flowLayout2);
     thresholdValue.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         thresholdValue_actionPerformed(e);
@@ -263,7 +263,7 @@ public class FeatureColourChooser extends JalviewDialog
     slider.setOpaque(false);
     slider.setPreferredSize(new Dimension(100, 32));
     slider.setToolTipText(MessageManager
-            .getString("label.adjust_thereshold"));
+            .getString("label.adjust_threshold"));
     thresholdValue.setEnabled(false);
     thresholdValue.setColumns(7);
     jPanel3.setBackground(Color.white);
@@ -274,6 +274,7 @@ public class FeatureColourChooser extends JalviewDialog
             .getString("label.toggle_absolute_relative_display_threshold"));
     thresholdIsMin.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         thresholdIsMin_actionPerformed(actionEvent);
@@ -287,6 +288,7 @@ public class FeatureColourChooser extends JalviewDialog
                     .getString("label.display_features_same_type_different_label_using_different_colour"));
     colourByLabel.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         colourByLabel_actionPerformed(actionEvent);
@@ -387,56 +389,56 @@ public class FeatureColourChooser extends JalviewDialog
       return;
     }
 
-    int aboveThreshold = AnnotationColourGradient.NO_THRESHOLD;
+    boolean aboveThreshold = false;
+    boolean belowThreshold = false;
     if (threshold.getSelectedIndex() == 1)
     {
-      aboveThreshold = AnnotationColourGradient.ABOVE_THRESHOLD;
+      aboveThreshold = true;
     }
     else if (threshold.getSelectedIndex() == 2)
     {
-      aboveThreshold = AnnotationColourGradient.BELOW_THRESHOLD;
+      belowThreshold = true;
     }
+    boolean hasThreshold = aboveThreshold || belowThreshold;
 
     slider.setEnabled(true);
     thresholdValue.setEnabled(true);
 
-    GraduatedColor acg;
+    FeatureColourI acg;
     if (cs.isColourByLabel())
     {
-      acg = new GraduatedColor(oldminColour, oldmaxColour, min, max);
+      acg = new FeatureColour(oldminColour, oldmaxColour, min, max);
     }
     else
     {
-      acg = new GraduatedColor(oldminColour = minColour.getBackground(),
+      acg = new FeatureColour(oldminColour = minColour.getBackground(),
               oldmaxColour = maxColour.getBackground(), min, max);
 
     }
 
-    if (aboveThreshold == AnnotationColourGradient.NO_THRESHOLD)
+    if (!hasThreshold)
     {
       slider.setEnabled(false);
       thresholdValue.setEnabled(false);
       thresholdValue.setText("");
       thresholdIsMin.setEnabled(false);
     }
-    else if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD
-            && threshline == null)
+    else if (threshline == null)
     {
       // todo visual indication of feature threshold
-      threshline = new jalview.datamodel.GraphLine((max - min) / 2f,
-              "Threshold", Color.black);
+      threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black);
     }
 
-    if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
+    if (hasThreshold)
     {
       adjusting = true;
-      acg.setThresh(threshline.value);
+      acg.setThreshold(threshline.value);
 
-      float range = max * 1000f - min * 1000f;
+      float range = (max - min) * scaleFactor;
 
-      slider.setMinimum((int) (min * 1000));
-      slider.setMaximum((int) (max * 1000));
-      slider.setValue((int) (threshline.value * 1000));
+      slider.setMinimum((int) (min * scaleFactor));
+      slider.setMaximum((int) (max * scaleFactor));
+      slider.setValue((int) (threshline.value * scaleFactor));
       thresholdValue.setText(threshline.value + "");
       slider.setMajorTickSpacing((int) (range / 10f));
       slider.setEnabled(true);
@@ -445,18 +447,18 @@ public class FeatureColourChooser extends JalviewDialog
       adjusting = false;
     }
 
-    acg.setThreshType(aboveThreshold);
-    if (thresholdIsMin.isSelected()
-            && aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
+    acg.setAboveThreshold(aboveThreshold);
+    acg.setBelowThreshold(belowThreshold);
+    if (thresholdIsMin.isSelected() && hasThreshold)
     {
       acg.setAutoScaled(false);
-      if (aboveThreshold == AnnotationColourGradient.ABOVE_THRESHOLD)
+      if (aboveThreshold)
       {
-        acg = new GraduatedColor(acg, threshline.value, max);
+        acg = new FeatureColour((FeatureColour) acg, threshline.value, max);
       }
       else
       {
-        acg = new GraduatedColor(acg, min, threshline.value);
+        acg = new FeatureColour((FeatureColour) acg, min, threshline.value);
       }
     }
     else
@@ -488,6 +490,7 @@ public class FeatureColourChooser extends JalviewDialog
     ap.paintAlignment(false);
   }
 
+  @Override
   protected void raiseClosed()
   {
     if (this.colourEditor != null)
@@ -496,11 +499,13 @@ public class FeatureColourChooser extends JalviewDialog
     }
   }
 
+  @Override
   public void okPressed()
   {
     changeColour();
   }
 
+  @Override
   public void cancelPressed()
   {
     reset();
@@ -533,7 +538,7 @@ public class FeatureColourChooser extends JalviewDialog
     try
     {
       float f = Float.parseFloat(thresholdValue.getText());
-      slider.setValue((int) (f * 1000));
+      slider.setValue((int) (f * scaleFactor));
       threshline.value = f;
     } catch (NumberFormatException ex)
     {
@@ -542,8 +547,8 @@ public class FeatureColourChooser extends JalviewDialog
 
   public void valueChanged()
   {
-    threshline.value = (float) slider.getValue() / 1000f;
-    cs.setThresh(threshline.value);
+    threshline.value = slider.getValue() / scaleFactor;
+    cs.setThreshold(threshline.value);
     changeColour();
     ap.paintAlignment(false);
   }
diff --git a/src/jalview/gui/FeatureRenderer.java b/src/jalview/gui/FeatureRenderer.java
index 89e94c8..cfff2d6 100644
--- a/src/jalview/gui/FeatureRenderer.java
+++ b/src/jalview/gui/FeatureRenderer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,10 +20,13 @@
  */
 package jalview.gui;
 
+import jalview.api.FeatureColourI;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
-import jalview.schemes.GraduatedColor;
+import jalview.schemes.FeatureColour;
+import jalview.schemes.UserColourScheme;
 import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
@@ -37,6 +40,8 @@ import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.util.Arrays;
+import java.util.Comparator;
 
 import javax.swing.JColorChooser;
 import javax.swing.JComboBox;
@@ -71,9 +76,8 @@ public class FeatureRenderer extends
    */
   public FeatureRenderer(AlignmentPanel ap)
   {
-    super();
+    super(ap.av);
     this.ap = ap;
-    this.av = ap.av;
     if (ap != null && ap.getSeqPanel() != null
             && ap.getSeqPanel().seqCanvas != null
             && ap.getSeqPanel().seqCanvas.fr != null)
@@ -92,7 +96,7 @@ public class FeatureRenderer extends
 
   static String lastDescriptionAdded;
 
-  Object oldcol, fcol;
+  FeatureColourI oldcol, fcol;
 
   int featureIndex = 0;
 
@@ -121,22 +125,22 @@ public class FeatureRenderer extends
     {
       FeatureColourChooser fcc = null;
 
+      @Override
       public void mousePressed(MouseEvent evt)
       {
-        if (fcol instanceof Color)
+        if (fcol.isSimpleColour())
         {
           Color col = JColorChooser.showDialog(Desktop.desktop,
                   MessageManager.getString("label.select_feature_colour"),
-                  ((Color) fcol));
+                  fcol.getColour());
           if (col != null)
           {
-            fcol = col;
-            updateColourButton(bigPanel, colour, col);
+            fcol = new FeatureColour(col);
+            updateColourButton(bigPanel, colour, new FeatureColour(col));
           }
         }
         else
         {
-
           if (fcc == null)
           {
             final String type = features[featureIndex].getType();
@@ -147,6 +151,7 @@ public class FeatureRenderer extends
             fcc.addActionListener(new ActionListener()
             {
 
+              @Override
               public void actionPerformed(ActionEvent e)
               {
                 fcol = fcc.getLastColour();
@@ -169,7 +174,8 @@ public class FeatureRenderer extends
     {
       panel = new JPanel(new GridLayout(4, 1));
       tmp = new JPanel();
-      tmp.add(new JLabel(MessageManager.getString("label.select_feature")));
+      tmp.add(new JLabel(MessageManager.getString("label.select_feature")
+              + ":"));
       overlaps = new JComboBox();
       for (int i = 0; i < features.length; i++)
       {
@@ -182,6 +188,7 @@ public class FeatureRenderer extends
 
       overlaps.addItemListener(new ItemListener()
       {
+        @Override
         public void itemStateChanged(ItemEvent e)
         {
           int index = overlaps.getSelectedIndex();
@@ -194,18 +201,18 @@ public class FeatureRenderer extends
             start.setValue(new Integer(features[index].getBegin()));
             end.setValue(new Integer(features[index].getEnd()));
 
-            SearchResults highlight = new SearchResults();
+            SearchResultsI highlight = new SearchResults();
             highlight.addResult(sequences[0], features[index].getBegin(),
                     features[index].getEnd());
 
             ap.getSeqPanel().seqCanvas.highlightSearchResults(highlight);
 
           }
-          Object col = getFeatureStyle(name.getText());
+          FeatureColourI col = getFeatureStyle(name.getText());
           if (col == null)
           {
-            col = new jalview.schemes.UserColourScheme()
-                    .createColourFromName(name.getText());
+            col = new FeatureColour(UserColourScheme
+                    .createColourFromName(name.getText()));
           }
           oldcol = fcol = col;
           updateColourButton(bigPanel, colour, col);
@@ -219,12 +226,13 @@ public class FeatureRenderer extends
 
     tmp = new JPanel();
     panel.add(tmp);
-    tmp.add(new JLabel(MessageManager.getString("label.name"), JLabel.RIGHT));
+    tmp.add(new JLabel(MessageManager.getString("label.name:"),
+            JLabel.RIGHT));
     tmp.add(name);
 
     tmp = new JPanel();
     panel.add(tmp);
-    tmp.add(new JLabel(MessageManager.getString("label.group") + ":",
+    tmp.add(new JLabel(MessageManager.getString("label.group:"),
             JLabel.RIGHT));
     tmp.add(source);
 
@@ -243,7 +251,7 @@ public class FeatureRenderer extends
     bigPanel.add(panel, BorderLayout.NORTH);
 
     panel = new JPanel();
-    panel.add(new JLabel(MessageManager.getString("label.description"),
+    panel.add(new JLabel(MessageManager.getString("label.description:"),
             JLabel.RIGHT));
     description.setFont(JvSwingUtils.getTextAreaFont());
     description.setLineWrap(true);
@@ -416,26 +424,36 @@ public class FeatureRenderer extends
    * 
    * @param bigPanel
    * @param col
-   * @param col2
+   * @param col
    */
   protected void updateColourButton(JPanel bigPanel, JLabel colour,
-          Object col2)
+          FeatureColourI col)
   {
     colour.removeAll();
     colour.setIcon(null);
     colour.setToolTipText(null);
     colour.setText("");
 
-    if (col2 instanceof Color)
+    if (col.isSimpleColour())
     {
-      colour.setBackground((Color) col2);
+      colour.setBackground(col.getColour());
     }
     else
     {
       colour.setBackground(bigPanel.getBackground());
       colour.setForeground(Color.black);
-      FeatureSettings.renderGraduatedColor(colour, (GraduatedColor) col2);
-      // colour.setForeground(colour.getBackground());
+      FeatureSettings.renderGraduatedColor(colour, col);
     }
   }
+
+  /**
+   * Orders features in render precedence (last in order is last to render, so
+   * displayed on top of other features)
+   * 
+   * @param order
+   */
+  public void orderFeatures(Comparator<String> order)
+  {
+    Arrays.sort(renderOrder, order);
+  }
 }
diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java
index a2d0559..0850c86 100644
--- a/src/jalview/gui/FeatureSettings.java
+++ b/src/jalview/gui/FeatureSettings.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,15 +20,19 @@
  */
 package jalview.gui;
 
+import jalview.api.FeatureColourI;
 import jalview.api.FeatureSettingsControllerI;
 import jalview.bin.Cache;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.Help.HelpId;
 import jalview.io.JalviewFileChooser;
-import jalview.schemes.AnnotationColourGradient;
-import jalview.schemes.GraduatedColor;
+import jalview.schemabinding.version2.JalviewUserColours;
+import jalview.schemes.FeatureColour;
+import jalview.util.Format;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.util.QuickSort;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.dbsources.das.api.jalviewSourceI;
 
@@ -54,9 +58,11 @@ import java.io.FileOutputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.Vector;
 
@@ -158,16 +164,17 @@ public class FeatureSettings extends JPanel implements
 
     table.setDefaultEditor(Color.class, new ColorEditor(this));
 
-    table.setDefaultEditor(GraduatedColor.class, new ColorEditor(this));
-    table.setDefaultRenderer(GraduatedColor.class, new ColorRenderer());
+    table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
+    table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
     table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 
     table.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mousePressed(MouseEvent evt)
       {
         selectedRow = table.rowAtPoint(evt.getPoint());
-        if (SwingUtilities.isRightMouseButton(evt))
+        if (evt.isPopupTrigger())
         {
           popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
                   table.getValueAt(selectedRow, 1), fr.getMinMax(),
@@ -175,14 +182,16 @@ public class FeatureSettings extends JPanel implements
         }
         else if (evt.getClickCount() == 2)
         {
+          boolean invertSelection = evt.isAltDown();
+          boolean toggleSelection = Platform.isControlDown(evt);
+          boolean extendSelection = evt.isShiftDown();
           fr.ap.alignFrame.avc.markColumnsContainingFeatures(
-                  evt.isAltDown(), evt.isShiftDown() || evt.isMetaDown(),
-                  evt.isMetaDown(),
+                  invertSelection, extendSelection, toggleSelection,
                   (String) table.getValueAt(selectedRow, 0));
         }
       }
 
-      // isPopupTrigger fires on mouseReleased on Mac
+      // isPopupTrigger fires on mouseReleased on Windows
       @Override
       public void mouseReleased(MouseEvent evt)
       {
@@ -198,24 +207,28 @@ public class FeatureSettings extends JPanel implements
 
     table.addMouseMotionListener(new MouseMotionAdapter()
     {
+      @Override
       public void mouseDragged(MouseEvent evt)
       {
         int newRow = table.rowAtPoint(evt.getPoint());
         if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
         {
-          Object[] temp = new Object[3];
-          temp[0] = table.getValueAt(selectedRow, 0);
-          temp[1] = table.getValueAt(selectedRow, 1);
-          temp[2] = table.getValueAt(selectedRow, 2);
-
-          table.setValueAt(table.getValueAt(newRow, 0), selectedRow, 0);
-          table.setValueAt(table.getValueAt(newRow, 1), selectedRow, 1);
-          table.setValueAt(table.getValueAt(newRow, 2), selectedRow, 2);
-
-          table.setValueAt(temp[0], newRow, 0);
-          table.setValueAt(temp[1], newRow, 1);
-          table.setValueAt(temp[2], newRow, 2);
-
+          /*
+           * reposition 'selectedRow' to 'newRow' (the dragged to location)
+           * this could be more than one row away for a very fast drag action
+           * so just swap it with adjacent rows until we get it there
+           */
+          Object[][] data = ((FeatureTableModel) table.getModel())
+                  .getData();
+          int direction = newRow < selectedRow ? -1 : 1;
+          for (int i = selectedRow; i != newRow; i += direction)
+          {
+            Object[] temp = data[i];
+            data[i] = data[i + direction];
+            data[i + direction] = temp;
+          }
+          updateFeatureRenderer(data);
+          table.repaint();
           selectedRow = newRow;
         }
       }
@@ -237,6 +250,7 @@ public class FeatureSettings extends JPanel implements
     final FeatureSettings fs = this;
     fr.addPropertyChangeListener(change = new PropertyChangeListener()
     {
+      @Override
       public void propertyChange(PropertyChangeEvent evt)
       {
         if (!fs.resettingTable && !fs.handlingUpdate)
@@ -252,7 +266,7 @@ public class FeatureSettings extends JPanel implements
 
     frame = new JInternalFrame();
     frame.setContentPane(this);
-    if (new jalview.util.Platform().isAMac())
+    if (Platform.isAMac())
     {
       Desktop.addInternalFrame(frame,
               MessageManager.getString("label.sequence_feature_settings"),
@@ -267,6 +281,7 @@ public class FeatureSettings extends JPanel implements
 
     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
     {
+      @Override
       public void internalFrameClosed(
               javax.swing.event.InternalFrameEvent evt)
       {
@@ -278,8 +293,11 @@ public class FeatureSettings extends JPanel implements
   }
 
   protected void popupSort(final int selectedRow, final String type,
-          final Object typeCol, final Hashtable minmax, int x, int y)
+          final Object typeCol, final Map<String, float[][]> minmax, int x,
+          int y)
   {
+    final FeatureColourI featureColour = (FeatureColourI) typeCol;
+
     JPopupMenu men = new JPopupMenu(MessageManager.formatMessage(
             "label.settings_for_param", new String[] { type }));
     JMenuItem scr = new JMenuItem(
@@ -289,9 +307,11 @@ public class FeatureSettings extends JPanel implements
     scr.addActionListener(new ActionListener()
     {
 
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        me.af.avc.sortAlignmentByFeatureScore(new String[] { type });
+        me.af.avc.sortAlignmentByFeatureScore(Arrays
+                .asList(new String[] { type }));
       }
 
     });
@@ -300,16 +320,18 @@ public class FeatureSettings extends JPanel implements
     dens.addActionListener(new ActionListener()
     {
 
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        me.af.avc.sortAlignmentByFeatureDensity(new String[] { type });
+        me.af.avc.sortAlignmentByFeatureDensity(Arrays
+                .asList(new String[] { type }));
       }
 
     });
     men.add(dens);
     if (minmax != null)
     {
-      final Object typeMinMax = minmax.get(type);
+      final float[][] typeMinMax = minmax.get(type);
       /*
        * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
        * this is broken at the moment and isn't that useful anyway!
@@ -324,24 +346,25 @@ public class FeatureSettings extends JPanel implements
        * 
        * men.add(chb);
        */
-      if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
+      if (typeMinMax != null && typeMinMax[0] != null)
       {
         // if (table.getValueAt(row, column));
         // graduated colourschemes for those where minmax exists for the
         // positional features
         final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
                 "Graduated Colour");
-        mxcol.setSelected(!(typeCol instanceof Color));
+        mxcol.setSelected(!featureColour.isSimpleColour());
         men.add(mxcol);
         mxcol.addActionListener(new ActionListener()
         {
           JColorChooser colorChooser;
 
+          @Override
           public void actionPerformed(ActionEvent e)
           {
             if (e.getSource() == mxcol)
             {
-              if (typeCol instanceof Color)
+              if (featureColour.isSimpleColour())
               {
                 FeatureColourChooser fc = new FeatureColourChooser(me.fr,
                         type);
@@ -355,8 +378,7 @@ public class FeatureSettings extends JPanel implements
                         "Select new Colour", true, // modal
                         colorChooser, this, // OK button handler
                         null); // no CANCEL button handler
-                colorChooser.setColor(((GraduatedColor) typeCol)
-                        .getMaxColor());
+                colorChooser.setColor(featureColour.getMaxColour());
                 dialog.setVisible(true);
               }
             }
@@ -372,7 +394,9 @@ public class FeatureSettings extends JPanel implements
               else
               {
                 // probably the color chooser!
-                table.setValueAt(colorChooser.getColor(), selectedRow, 1);
+                table.setValueAt(
+                        new FeatureColour(colorChooser.getColor()),
+                        selectedRow, 1);
                 table.validate();
                 me.updateFeatureRenderer(
                         ((FeatureTableModel) table.getModel()).getData(),
@@ -388,7 +412,6 @@ public class FeatureSettings extends JPanel implements
             MessageManager.getString("label.select_columns_containing"));
     selCols.addActionListener(new ActionListener()
     {
-
       @Override
       public void actionPerformed(ActionEvent arg0)
       {
@@ -400,7 +423,6 @@ public class FeatureSettings extends JPanel implements
             MessageManager.getString("label.select_columns_not_containing"));
     clearCols.addActionListener(new ActionListener()
     {
-
       @Override
       public void actionPerformed(ActionEvent arg0)
       {
@@ -408,8 +430,30 @@ public class FeatureSettings extends JPanel implements
                 false, type);
       }
     });
+    JMenuItem hideCols = new JMenuItem(
+            MessageManager.getString("label.hide_columns_containing"));
+    hideCols.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent arg0)
+      {
+        fr.ap.alignFrame.hideFeatureColumns(type, true);
+      }
+    });
+    JMenuItem hideOtherCols = new JMenuItem(
+            MessageManager.getString("label.hide_columns_not_containing"));
+    hideOtherCols.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent arg0)
+      {
+        fr.ap.alignFrame.hideFeatureColumns(type, false);
+      }
+    });
     men.add(selCols);
     men.add(clearCols);
+    men.add(hideCols);
+    men.add(hideOtherCols);
     men.show(table, x, y);
   }
 
@@ -421,13 +465,13 @@ public class FeatureSettings extends JPanel implements
   /**
    * contains a float[3] for each feature type string. created by setTableData
    */
-  Hashtable typeWidth = null;
+  Map<String, float[]> typeWidth = null;
 
   @Override
   synchronized public void discoverAllFeatureData()
   {
-    Vector allFeatures = new Vector();
-    Vector allGroups = new Vector();
+    Vector<String> allFeatures = new Vector<String>();
+    Vector<String> allGroups = new Vector<String>();
     SequenceFeature[] tmpfeatures;
     String group;
     for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
@@ -507,6 +551,7 @@ public class FeatureSettings extends JPanel implements
     check.setFont(new Font("Serif", Font.BOLD, 12));
     check.addItemListener(new ItemListener()
     {
+      @Override
       public void itemStateChanged(ItemEvent evt)
       {
         fr.setGroupVisibility(check.getText(), check.isSelected());
@@ -532,13 +577,13 @@ public class FeatureSettings extends JPanel implements
       return;
     }
     resettingTable = true;
-    typeWidth = new Hashtable();
+    typeWidth = new Hashtable<String, float[]>();
     // TODO: change avWidth calculation to 'per-sequence' average and use long
     // rather than float
     float[] avWidth = null;
     SequenceFeature[] tmpfeatures;
     String group = null, type;
-    Vector visibleChecks = new Vector();
+    Vector<String> visibleChecks = new Vector<String>();
 
     // Find out which features should be visible depending on which groups
     // are selected / deselected
@@ -579,7 +624,7 @@ public class FeatureSettings extends JPanel implements
         }
         else
         {
-          avWidth = (float[]) typeWidth.get(tmpfeatures[index].getType());
+          avWidth = typeWidth.get(tmpfeatures[index].getType());
         }
         avWidth[0]++;
         if (tmpfeatures[index].getBegin() > tmpfeatures[index].getEnd())
@@ -724,8 +769,7 @@ public class FeatureSettings extends JPanel implements
         InputStreamReader in = new InputStreamReader(new FileInputStream(
                 file), "UTF-8");
 
-        jalview.schemabinding.version2.JalviewUserColours jucs = new jalview.schemabinding.version2.JalviewUserColours();
-        jucs = jucs.unmarshal(in);
+        JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
 
         for (int i = jucs.getColourCount() - 1; i >= 0; i--)
         {
@@ -744,7 +788,7 @@ public class FeatureSettings extends JPanel implements
               Cache.log.warn("Couldn't parse out graduated feature color.",
                       e);
             }
-            GraduatedColor gcol = new GraduatedColor(mincol, maxcol,
+            FeatureColourI gcol = new FeatureColour(mincol, maxcol,
                     newcol.getMin(), newcol.getMax());
             if (newcol.hasAutoScale())
             {
@@ -756,31 +800,28 @@ public class FeatureSettings extends JPanel implements
             }
             if (newcol.hasThreshold())
             {
-              gcol.setThresh(newcol.getThreshold());
-              gcol.setThreshType(AnnotationColourGradient.NO_THRESHOLD); // default
+              gcol.setThreshold(newcol.getThreshold());
             }
             if (newcol.getThreshType().length() > 0)
             {
               String ttyp = newcol.getThreshType();
-              if (ttyp.equalsIgnoreCase("NONE"))
-              {
-                gcol.setThreshType(AnnotationColourGradient.NO_THRESHOLD);
-              }
               if (ttyp.equalsIgnoreCase("ABOVE"))
               {
-                gcol.setThreshType(AnnotationColourGradient.ABOVE_THRESHOLD);
+                gcol.setAboveThreshold(true);
               }
               if (ttyp.equalsIgnoreCase("BELOW"))
               {
-                gcol.setThreshType(AnnotationColourGradient.BELOW_THRESHOLD);
+                gcol.setBelowThreshold(true);
               }
             }
             fr.setColour(name = newcol.getName(), gcol);
           }
           else
           {
-            fr.setColour(name = jucs.getColour(i).getName(), new Color(
-                    Integer.parseInt(jucs.getColour(i).getRGB(), 16)));
+            Color color = new Color(Integer.parseInt(jucs.getColour(i)
+                    .getRGB(), 16));
+            fr.setColour(name = jucs.getColour(i).getName(),
+                    new FeatureColour(color));
           }
           fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
         }
@@ -803,8 +844,7 @@ public class FeatureSettings extends JPanel implements
   void save()
   {
     JalviewFileChooser chooser = new JalviewFileChooser(
-            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
-            new String[] { "fc" },
+            Cache.getProperty("LAST_DIRECTORY"), new String[] { "fc" },
             new String[] { "Sequence Feature Colours" },
             "Sequence Feature Colours");
     chooser.setFileView(new jalview.io.JalviewFileView());
@@ -824,50 +864,40 @@ public class FeatureSettings extends JPanel implements
         PrintWriter out = new PrintWriter(new OutputStreamWriter(
                 new FileOutputStream(choice), "UTF-8"));
 
-        Set fr_colours = fr.getAllFeatureColours();
-        Iterator e = fr_colours.iterator();
+        Set<String> fr_colours = fr.getAllFeatureColours();
+        Iterator<String> e = fr_colours.iterator();
         float[] sortOrder = new float[fr_colours.size()];
         String[] sortTypes = new String[fr_colours.size()];
         int i = 0;
         while (e.hasNext())
         {
-          sortTypes[i] = e.next().toString();
+          sortTypes[i] = e.next();
           sortOrder[i] = fr.getOrder(sortTypes[i]);
           i++;
         }
-        jalview.util.QuickSort.sort(sortOrder, sortTypes);
+        QuickSort.sort(sortOrder, sortTypes);
         sortOrder = null;
-        Object fcol;
-        GraduatedColor gcol;
         for (i = 0; i < sortTypes.length; i++)
         {
           jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
           col.setName(sortTypes[i]);
-          col.setRGB(jalview.util.Format.getHexString(fr.getColour(col
-                  .getName())));
-          fcol = fr.getFeatureStyle(sortTypes[i]);
-          if (fcol instanceof GraduatedColor)
+          FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
+          if (fcol.isSimpleColour())
           {
-            gcol = (GraduatedColor) fcol;
-            col.setMin(gcol.getMin());
-            col.setMax(gcol.getMax());
-            col.setMinRGB(jalview.util.Format.getHexString(gcol
-                    .getMinColor()));
-            col.setAutoScale(gcol.isAutoScale());
-            col.setThreshold(gcol.getThresh());
-            col.setColourByLabel(gcol.isColourByLabel());
-            switch (gcol.getThreshType())
-            {
-            case AnnotationColourGradient.NO_THRESHOLD:
-              col.setThreshType("NONE");
-              break;
-            case AnnotationColourGradient.ABOVE_THRESHOLD:
-              col.setThreshType("ABOVE");
-              break;
-            case AnnotationColourGradient.BELOW_THRESHOLD:
-              col.setThreshType("BELOW");
-              break;
-            }
+            col.setRGB(Format.getHexString(fcol.getColour()));
+          }
+          else
+          {
+            col.setRGB(Format.getHexString(fcol.getMaxColour()));
+            col.setMin(fcol.getMin());
+            col.setMax(fcol.getMax());
+            col.setMinRGB(jalview.util.Format.getHexString(fcol
+                    .getMinColour()));
+            col.setAutoScale(fcol.isAutoScaled());
+            col.setThreshold(fcol.getThreshold());
+            col.setColourByLabel(fcol.isColourByLabel());
+            col.setThreshType(fcol.isAboveThreshold() ? "ABOVE" : (fcol
+                    .isBelowThreshold() ? "BELOW" : "NONE"));
           }
           ucs.addColour(col);
         }
@@ -903,7 +933,7 @@ public class FeatureSettings extends JPanel implements
     int num = 0;
     for (int i = 0; i < data.length; i++)
     {
-      awidth = (float[]) typeWidth.get(data[i][0]);
+      awidth = typeWidth.get(data[i][0]);
       if (awidth[0] > 0)
       {
         width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
@@ -969,10 +999,19 @@ public class FeatureSettings extends JPanel implements
     updateFeatureRenderer(data, true);
   }
 
+  /**
+   * Update the priority order of features; only repaint if this changed the
+   * order of visible features
+   * 
+   * @param data
+   * @param visibleNew
+   */
   private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
   {
-    fr.setFeaturePriority(data, visibleNew);
-    af.alignPanel.paintAlignment(true);
+    if (fr.setFeaturePriority(data, visibleNew))
+    {
+      af.alignPanel.paintAlignment(true);
+    }
   }
 
   int selectedRow = -1;
@@ -1029,6 +1068,7 @@ public class FeatureSettings extends JPanel implements
     invert.setText(MessageManager.getString("label.invert_selection"));
     invert.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         invertSelection();
@@ -1038,6 +1078,7 @@ public class FeatureSettings extends JPanel implements
     optimizeOrder.setText(MessageManager.getString("label.optimise_order"));
     optimizeOrder.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         orderByAvWidth();
@@ -1048,6 +1089,7 @@ public class FeatureSettings extends JPanel implements
             .setText(MessageManager.getString("label.seq_sort_by_score"));
     sortByScore.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         af.avc.sortAlignmentByFeatureScore(null);
@@ -1058,6 +1100,7 @@ public class FeatureSettings extends JPanel implements
             .getString("label.sequence_sort_by_density"));
     sortByDens.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         af.avc.sortAlignmentByFeatureDensity(null);
@@ -1067,6 +1110,7 @@ public class FeatureSettings extends JPanel implements
     help.setText(MessageManager.getString("action.help"));
     help.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         try
@@ -1082,6 +1126,7 @@ public class FeatureSettings extends JPanel implements
     help.setText(MessageManager.getString("action.help"));
     help.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         try
@@ -1097,6 +1142,7 @@ public class FeatureSettings extends JPanel implements
     cancel.setText(MessageManager.getString("action.cancel"));
     cancel.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         fr.setTransparency(originalTransparency);
@@ -1108,6 +1154,7 @@ public class FeatureSettings extends JPanel implements
     ok.setText(MessageManager.getString("action.ok"));
     ok.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         close();
@@ -1117,6 +1164,7 @@ public class FeatureSettings extends JPanel implements
     loadColours.setText(MessageManager.getString("label.load_colours"));
     loadColours.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         load();
@@ -1126,6 +1174,7 @@ public class FeatureSettings extends JPanel implements
     saveColours.setText(MessageManager.getString("label.save_colours"));
     saveColours.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         save();
@@ -1133,6 +1182,7 @@ public class FeatureSettings extends JPanel implements
     });
     transparency.addChangeListener(new ChangeListener()
     {
+      @Override
       public void stateChanged(ChangeEvent evt)
       {
         fr.setTransparency((100 - transparency.getValue()) / 100f);
@@ -1146,6 +1196,7 @@ public class FeatureSettings extends JPanel implements
     fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
     fetchDAS.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         fetchDAS_actionPerformed(e);
@@ -1154,6 +1205,7 @@ public class FeatureSettings extends JPanel implements
     saveDAS.setText(MessageManager.getString("action.save_as_default"));
     saveDAS.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         saveDAS_actionPerformed(e);
@@ -1165,6 +1217,7 @@ public class FeatureSettings extends JPanel implements
     cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
     cancelDAS.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         cancelDAS_actionPerformed(e);
@@ -1203,7 +1256,8 @@ public class FeatureSettings extends JPanel implements
     fetchDAS.setEnabled(false);
     cancelDAS.setEnabled(true);
     dassourceBrowser.setGuiEnabled(false);
-    Vector selectedSources = dassourceBrowser.getSelectedSources();
+    Vector<jalviewSourceI> selectedSources = dassourceBrowser
+            .getSelectedSources();
     doDasFeatureFetch(selectedSources, true, true);
   }
 
@@ -1262,7 +1316,7 @@ public class FeatureSettings extends JPanel implements
    *          Vector of Strings to resolve to DAS source nicknames.
    * @return sources that are present in source list.
    */
-  public List<jalviewSourceI> resolveSourceNicknames(Vector sources)
+  public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
   {
     return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
   }
@@ -1273,7 +1327,7 @@ public class FeatureSettings extends JPanel implements
    * 
    * @return vector of selected das source nicknames
    */
-  public Vector getSelectedSources()
+  public Vector<jalviewSourceI> getSelectedSources()
   {
     return dassourceBrowser.getSelectedSources();
   }
@@ -1287,7 +1341,7 @@ public class FeatureSettings extends JPanel implements
    *          if true then runs in same thread, otherwise passes to the Swing
    *          executor
    */
-  public void fetchDasFeatures(Vector sources, boolean block)
+  public void fetchDasFeatures(Vector<String> sources, boolean block)
   {
     initDasSources();
     List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
@@ -1304,6 +1358,7 @@ public class FeatureSettings extends JPanel implements
       Runnable fetcher = new Runnable()
       {
 
+        @Override
         public void run()
         {
           doDasFeatureFetch(dassources, true, false);
@@ -1385,6 +1440,7 @@ public class FeatureSettings extends JPanel implements
       this.data = data;
     }
 
+    @Override
     public int getColumnCount()
     {
       return columnNames.length;
@@ -1395,31 +1451,37 @@ public class FeatureSettings extends JPanel implements
       return data[row];
     }
 
+    @Override
     public int getRowCount()
     {
       return data.length;
     }
 
+    @Override
     public String getColumnName(int col)
     {
       return columnNames[col];
     }
 
+    @Override
     public Object getValueAt(int row, int col)
     {
       return data[row][col];
     }
 
+    @Override
     public Class getColumnClass(int c)
     {
       return getValueAt(0, c).getClass();
     }
 
+    @Override
     public boolean isCellEditable(int row, int col)
     {
       return col == 0 ? false : true;
     }
 
+    @Override
     public void setValueAt(Object value, int row, int col)
     {
       data[row][col] = value;
@@ -1444,10 +1506,12 @@ public class FeatureSettings extends JPanel implements
       setVerticalTextPosition(SwingConstants.CENTER);
     }
 
-    public Component getTableCellRendererComponent(JTable table,
+    @Override
+    public Component getTableCellRendererComponent(JTable tbl,
             Object color, boolean isSelected, boolean hasFocus, int row,
             int column)
     {
+      FeatureColourI cellColour = (FeatureColourI) color;
       // JLabel comp = new JLabel();
       // comp.
       setOpaque(true);
@@ -1455,11 +1519,11 @@ public class FeatureSettings extends JPanel implements
       // setBounds(getBounds());
       Color newColor;
       setToolTipText(baseTT);
-      setBackground(table.getBackground());
-      if (color instanceof GraduatedColor)
+      setBackground(tbl.getBackground());
+      if (!cellColour.isSimpleColour())
       {
-        Rectangle cr = table.getCellRect(row, column, false);
-        FeatureSettings.renderGraduatedColor(this, (GraduatedColor) color,
+        Rectangle cr = tbl.getCellRect(row, column, false);
+        FeatureSettings.renderGraduatedColor(this, cellColour,
                 (int) cr.getWidth(), (int) cr.getHeight());
 
       }
@@ -1467,20 +1531,16 @@ public class FeatureSettings extends JPanel implements
       {
         this.setText("");
         this.setIcon(null);
-        newColor = (Color) color;
-        // comp.
+        newColor = cellColour.getColour();
         setBackground(newColor);
-        // comp.setToolTipText("RGB value: " + newColor.getRed() + ", "
-        // + newColor.getGreen() + ", " + newColor.getBlue());
       }
       if (isSelected)
       {
         if (selectedBorder == null)
         {
           selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
-                  table.getSelectionBackground());
+                  tbl.getSelectionBackground());
         }
-        // comp.
         setBorder(selectedBorder);
       }
       else
@@ -1488,9 +1548,8 @@ public class FeatureSettings extends JPanel implements
         if (unselectedBorder == null)
         {
           unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
-                  table.getBackground());
+                  tbl.getBackground());
         }
-        // comp.
         setBorder(unselectedBorder);
       }
 
@@ -1504,7 +1563,7 @@ public class FeatureSettings extends JPanel implements
    * @param comp
    * @param gcol
    */
-  public static void renderGraduatedColor(JLabel comp, GraduatedColor gcol)
+  public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
   {
     int w = comp.getWidth(), h = comp.getHeight();
     if (w < 20)
@@ -1520,23 +1579,23 @@ public class FeatureSettings extends JPanel implements
     renderGraduatedColor(comp, gcol, w, h);
   }
 
-  public static void renderGraduatedColor(JLabel comp, GraduatedColor gcol,
+  public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
           int w, int h)
   {
     boolean thr = false;
     String tt = "";
     String tx = "";
-    if (gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD)
+    if (gcol.isAboveThreshold())
     {
       thr = true;
       tx += ">";
-      tt += "Thresholded (Above " + gcol.getThresh() + ") ";
+      tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
     }
-    if (gcol.getThreshType() == AnnotationColourGradient.BELOW_THRESHOLD)
+    if (gcol.isBelowThreshold())
     {
       thr = true;
       tx += "<";
-      tt += "Thresholded (Below " + gcol.getThresh() + ") ";
+      tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
     }
     if (gcol.isColourByLabel())
     {
@@ -1550,7 +1609,7 @@ public class FeatureSettings extends JPanel implements
     }
     else
     {
-      Color newColor = gcol.getMaxColor();
+      Color newColor = gcol.getMaxColour();
       comp.setBackground(newColor);
       // System.err.println("Width is " + w / 2);
       Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
@@ -1578,7 +1637,7 @@ public class FeatureSettings extends JPanel implements
 
 class FeatureIcon implements Icon
 {
-  GraduatedColor gcol;
+  FeatureColourI gcol;
 
   Color backg;
 
@@ -1590,7 +1649,7 @@ class FeatureIcon implements Icon
 
   Color mpcolour = Color.white;
 
-  FeatureIcon(GraduatedColor gfc, Color bg, int w, int h, boolean mspace)
+  FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
   {
     gcol = gfc;
     backg = bg;
@@ -1609,16 +1668,19 @@ class FeatureIcon implements Icon
     }
   }
 
+  @Override
   public int getIconWidth()
   {
     return width;
   }
 
+  @Override
   public int getIconHeight()
   {
     return height;
   }
 
+  @Override
   public void paintIcon(Component c, Graphics g, int x, int y)
   {
 
@@ -1627,7 +1689,7 @@ class FeatureIcon implements Icon
       g.setColor(backg);
       g.fillRect(0, 0, width, height);
       // need an icon here.
-      g.setColor(gcol.getMaxColor());
+      g.setColor(gcol.getMaxColour());
 
       g.setFont(new Font("Verdana", Font.PLAIN, 9));
 
@@ -1641,7 +1703,7 @@ class FeatureIcon implements Icon
     }
     else
     {
-      Color minCol = gcol.getMinColor();
+      Color minCol = gcol.getMinColour();
       g.setColor(minCol);
       g.fillRect(0, 0, s1, height);
       if (midspace)
@@ -1649,7 +1711,7 @@ class FeatureIcon implements Icon
         g.setColor(Color.white);
         g.fillRect(s1, 0, e1 - s1, height);
       }
-      g.setColor(gcol.getMaxColor());
+      g.setColor(gcol.getMaxColour());
       g.fillRect(0, e1, width - e1, height);
     }
   }
@@ -1660,14 +1722,12 @@ class ColorEditor extends AbstractCellEditor implements TableCellEditor,
 {
   FeatureSettings me;
 
-  GraduatedColor currentGColor;
+  FeatureColourI currentColor;
 
   FeatureColourChooser chooser;
 
   String type;
 
-  Color currentColor;
-
   JButton button;
 
   JColorChooser colorChooser;
@@ -1699,6 +1759,7 @@ class ColorEditor extends AbstractCellEditor implements TableCellEditor,
   /**
    * Handles events from the editor button and from the dialog's OK button.
    */
+  @Override
   public void actionPerformed(ActionEvent e)
   {
 
@@ -1706,11 +1767,11 @@ class ColorEditor extends AbstractCellEditor implements TableCellEditor,
     {
       // The user has clicked the cell, so
       // bring up the dialog.
-      if (currentColor != null)
+      if (currentColor.isSimpleColour())
       {
         // bring up simple color chooser
-        button.setBackground(currentColor);
-        colorChooser.setColor(currentColor);
+        button.setBackground(currentColor.getColour());
+        colorChooser.setColor(currentColor.getColour());
         dialog.setVisible(true);
       }
       else
@@ -1727,15 +1788,13 @@ class ColorEditor extends AbstractCellEditor implements TableCellEditor,
     }
     else
     { // User pressed dialog's "OK" button.
-      if (currentColor != null)
+      if (currentColor.isSimpleColour())
       {
-        currentColor = colorChooser.getColor();
+        currentColor = new FeatureColour(colorChooser.getColor());
       }
       else
       {
-        // class cast exceptions may be raised if the chooser created on a
-        // non-graduated color
-        currentGColor = (GraduatedColor) chooser.getLastColour();
+        currentColor = chooser.getLastColour();
       }
       me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
       fireEditingStopped();
@@ -1744,31 +1803,27 @@ class ColorEditor extends AbstractCellEditor implements TableCellEditor,
   }
 
   // Implement the one CellEditor method that AbstractCellEditor doesn't.
+  @Override
   public Object getCellEditorValue()
   {
-    if (currentColor == null)
-    {
-      return currentGColor;
-    }
     return currentColor;
   }
 
   // Implement the one method defined by TableCellEditor.
+  @Override
   public Component getTableCellEditorComponent(JTable table, Object value,
           boolean isSelected, int row, int column)
   {
-    currentGColor = null;
-    currentColor = null;
+    currentColor = (FeatureColourI) value;
     this.selectedRow = row;
     type = me.table.getValueAt(row, 0).toString();
     button.setOpaque(true);
     button.setBackground(me.getBackground());
-    if (value instanceof GraduatedColor)
+    if (!currentColor.isSimpleColour())
     {
-      currentGColor = (GraduatedColor) value;
       JLabel btn = new JLabel();
       btn.setSize(button.getSize());
-      FeatureSettings.renderGraduatedColor(btn, currentGColor);
+      FeatureSettings.renderGraduatedColor(btn, currentColor);
       button.setBackground(btn.getBackground());
       button.setIcon(btn.getIcon());
       button.setText(btn.getText());
@@ -1777,8 +1832,7 @@ class ColorEditor extends AbstractCellEditor implements TableCellEditor,
     {
       button.setText("");
       button.setIcon(null);
-      currentColor = (Color) value;
-      button.setBackground(currentColor);
+      button.setBackground(currentColor.getColour());
     }
     return button;
   }
diff --git a/src/jalview/gui/Finder.java b/src/jalview/gui/Finder.java
index 1a7da1f..1ec4731 100644
--- a/src/jalview/gui/Finder.java
+++ b/src/jalview/gui/Finder.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,8 @@
  */
 package jalview.gui;
 
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultMatchI;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.jbgui.GFinder;
@@ -67,7 +68,7 @@ public class Finder extends GFinder
 
   int resIndex = -1;
 
-  SearchResults searchResults;
+  SearchResultsI searchResults;
 
   /**
    * Creates a new Finder object with no associated viewport or panel.
@@ -109,6 +110,7 @@ public class Finder extends GFinder
             KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel");
     getRootPane().getActionMap().put("Cancel", new AbstractAction()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         escapeActionPerformed();
@@ -130,6 +132,7 @@ public class Finder extends GFinder
    * 
    * @param e
    */
+  @Override
   public void findNext_actionPerformed(ActionEvent e)
   {
     if (getFocusedViewport())
@@ -143,6 +146,7 @@ public class Finder extends GFinder
    * 
    * @param e
    */
+  @Override
   public void findAll_actionPerformed(ActionEvent e)
   {
     if (getFocusedViewport())
@@ -198,19 +202,22 @@ public class Finder extends GFinder
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void createNewGroup_actionPerformed(ActionEvent e)
   {
     SequenceI[] seqs = new SequenceI[searchResults.getSize()];
     SequenceFeature[] features = new SequenceFeature[searchResults
             .getSize()];
 
-    for (int i = 0; i < searchResults.getSize(); i++)
+    int i = 0;
+    for (SearchResultMatchI match : searchResults.getResults())
     {
-      seqs[i] = searchResults.getResultSequence(i).getDatasetSequence();
+      seqs[i] = match.getSequence().getDatasetSequence();
 
       features[i] = new SequenceFeature(textfield.getText().trim(),
-              "Search Results", null, searchResults.getResultStart(i),
-              searchResults.getResultEnd(i), "Search Results");
+              "Search Results", null, match.getStart(), match.getEnd(),
+              "Search Results");
+      i++;
     }
 
     if (ap.getSeqPanel().seqCanvas.getFeatureRenderer().amendFeatures(seqs,
@@ -256,7 +263,7 @@ public class Finder extends GFinder
 
     searchResults = finder.getSearchResults(); // find(regex,
     // caseSensitive.isSelected(), )
-    Vector idMatch = finder.getIdMatch();
+    Vector<SequenceI> idMatch = finder.getIdMatch();
     boolean haveResults = false;
     // set or reset the GUI
     if ((idMatch.size() > 0))
diff --git a/src/jalview/gui/FontChooser.java b/src/jalview/gui/FontChooser.java
index df3bfc2..d3fca5b 100644
--- a/src/jalview/gui/FontChooser.java
+++ b/src/jalview/gui/FontChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -157,6 +157,7 @@ public class FontChooser extends GFontChooser
     init = false;
   }
 
+  @Override
   public void smoothFont_actionPerformed(ActionEvent e)
   {
     ap.av.antiAlias = smoothFont.isSelected();
@@ -170,6 +171,7 @@ public class FontChooser extends GFontChooser
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   protected void ok_actionPerformed(ActionEvent e)
   {
     try
@@ -194,6 +196,7 @@ public class FontChooser extends GFontChooser
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   protected void cancel_actionPerformed(ActionEvent e)
   {
     if (ap != null)
@@ -247,10 +250,10 @@ public class FontChooser extends GFontChooser
     double iw = iBounds.getWidth();
     if (mw < 1 || iw < 1)
     {
-      final String messageKey = iBounds.getHeight() < 1 ? "label.font_doesnt_have_letters_defined"
-              : "label.font_too_small";
-      JOptionPane.showInternalMessageDialog(this,
-              MessageManager.getString(messageKey),
+      String message = iBounds.getHeight() < 1 ? MessageManager
+              .getString("label.font_doesnt_have_letters_defined")
+              : MessageManager.getString("label.font_too_small");
+      JOptionPane.showInternalMessageDialog(this, message,
               MessageManager.getString("label.invalid_font"),
               JOptionPane.WARNING_MESSAGE);
       /*
@@ -301,6 +304,7 @@ public class FontChooser extends GFontChooser
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   protected void fontName_actionPerformed(ActionEvent e)
   {
     if (init)
@@ -317,6 +321,7 @@ public class FontChooser extends GFontChooser
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   protected void fontSize_actionPerformed(ActionEvent e)
   {
     if (init)
@@ -333,6 +338,7 @@ public class FontChooser extends GFontChooser
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   protected void fontStyle_actionPerformed(ActionEvent e)
   {
     if (init)
@@ -349,6 +355,7 @@ public class FontChooser extends GFontChooser
    * 
    * @param e
    */
+  @Override
   public void defaultButton_actionPerformed(ActionEvent e)
   {
     Cache.setProperty("FONT_NAME", fontName.getSelectedItem().toString());
diff --git a/src/jalview/gui/HTMLOptions.java b/src/jalview/gui/HTMLOptions.java
index 2b9f764..786247d 100644
--- a/src/jalview/gui/HTMLOptions.java
+++ b/src/jalview/gui/HTMLOptions.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/Help.java b/src/jalview/gui/Help.java
index ee91713..958b879 100644
--- a/src/jalview/gui/Help.java
+++ b/src/jalview/gui/Help.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -37,8 +37,8 @@ public class Help
 {
   public enum HelpId
   {
-    Home("home"), SequenceFeatureSettings("seqfeatures.settings"), StructureViewer(
-            "viewingpdbs");
+    Home("home"), SequenceFeatureSettings("seqfeatures.settings"),
+    StructureViewer("viewingpdbs");
 
     private String id;
 
diff --git a/src/jalview/gui/IProgressIndicator.java b/src/jalview/gui/IProgressIndicator.java
index 5bbf9f6..fac4c12 100644
--- a/src/jalview/gui/IProgressIndicator.java
+++ b/src/jalview/gui/IProgressIndicator.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/IProgressIndicatorHandler.java b/src/jalview/gui/IProgressIndicatorHandler.java
index 64d93b4..d06d2a6 100644
--- a/src/jalview/gui/IProgressIndicatorHandler.java
+++ b/src/jalview/gui/IProgressIndicatorHandler.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/IdCanvas.java b/src/jalview/gui/IdCanvas.java
index d7a8f00..96a1310 100644
--- a/src/jalview/gui/IdCanvas.java
+++ b/src/jalview/gui/IdCanvas.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -204,6 +204,7 @@ public class IdCanvas extends JPanel
    * @param g
    *          DOCUMENT ME!
    */
+  @Override
   public void paintComponent(Graphics g)
   {
     g.setColor(Color.white);
@@ -479,7 +480,7 @@ public class IdCanvas extends JPanel
     Font bold = new Font(av.getFont().getName(), Font.BOLD, av.getFont()
             .getSize());
 
-    if (av.isHiddenRepSequence(seq))
+    if (av.isReferenceSeq(seq) || av.isHiddenRepSequence(seq))
     {
       gg.setFont(bold);
     }
diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java
index b5569c3..02688c1 100644
--- a/src/jalview/gui/IdPanel.java
+++ b/src/jalview/gui/IdPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,6 +26,7 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.io.SequenceAnnotationReport;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.util.UrlLink;
 import jalview.viewmodel.AlignmentViewport;
 
@@ -108,12 +109,12 @@ public class IdPanel extends JPanel implements MouseListener,
     if (seq > -1 && seq < av.getAlignment().getHeight())
     {
       SequenceI sequence = av.getAlignment().getSequenceAt(seq);
-      StringBuffer tip = new StringBuffer(64);
-      seqAnnotReport.createSequenceAnnotationReport(tip, sequence,
+      StringBuilder tip = new StringBuilder(64);
+      seqAnnotReport.createTooltipAnnotationReport(tip, sequence,
               av.isShowDBRefs(), av.isShowNPFeats(),
               sp.seqCanvas.fr.getMinMax());
-      setToolTipText("<html>" + sequence.getDisplayId(true) + " "
-              + tip.toString() + "</html>");
+      setToolTipText(JvSwingUtils.wrapTooltip(true,
+              sequence.getDisplayId(true) + " " + tip.toString()));
     }
   }
 
@@ -193,6 +194,8 @@ public class IdPanel extends JPanel implements MouseListener,
      */
     if (e.getClickCount() < 2 || SwingUtilities.isRightMouseButton(e))
     {
+      // reinstate isRightMouseButton check to ignore mouse-related popup events
+      // note - this does nothing on default MacBookPro force-trackpad config!
       return;
     }
 
@@ -222,7 +225,14 @@ public class IdPanel extends JPanel implements MouseListener,
         url = null;
         continue;
       }
-      ;
+
+      if (urlLink.usesDBAccession())
+      {
+        // this URL requires an accession id, not the name of a sequence
+        url = null;
+        continue;
+      }
+
       if (!urlLink.isValid())
       {
         jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
@@ -314,38 +324,24 @@ public class IdPanel extends JPanel implements MouseListener,
       return;
     }
 
-    int seq = alignPanel.getSeqPanel().findSeq(e);
-
-    if (SwingUtilities.isRightMouseButton(e))
+    if (e.isPopupTrigger()) // Mac reports this in mousePressed
     {
-      Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq);
-      // build a new links menu based on the current links + any non-positional
-      // features
-      Vector nlinks = new Vector(Preferences.sequenceURLLinks);
-      SequenceFeature sf[] = sq == null ? null : sq.getSequenceFeatures();
-      for (int sl = 0; sf != null && sl < sf.length; sl++)
-      {
-        if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
-        {
-          if (sf[sl].links != null && sf[sl].links.size() > 0)
-          {
-            for (int l = 0, lSize = sf[sl].links.size(); l < lSize; l++)
-            {
-              nlinks.addElement(sf[sl].links.elementAt(l));
-            }
-          }
-        }
-      }
-
-      jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(alignPanel, sq,
-              nlinks, new Vector(Preferences.getGroupURLLinks()));
-      pop.show(this, e.getX(), e.getY());
+      showPopupMenu(e);
+      return;
+    }
 
+    /*
+     * defer right-mouse click handling to mouseReleased on Windows
+     * (where isPopupTrigger() will answer true)
+     * NB isRightMouseButton is also true for Cmd-click on Mac
+     */
+    if (SwingUtilities.isRightMouseButton(e) && !Platform.isAMac())
+    {
       return;
     }
 
     if ((av.getSelectionGroup() == null)
-            || ((!e.isControlDown() && !e.isShiftDown()) && av
+            || (!jalview.util.Platform.isControlDown(e) && !e.isShiftDown() && av
                     .getSelectionGroup() != null))
     {
       av.setSelectionGroup(new SequenceGroup());
@@ -353,6 +349,7 @@ public class IdPanel extends JPanel implements MouseListener,
       av.getSelectionGroup().setEndRes(av.getAlignment().getWidth() - 1);
     }
 
+    int seq = alignPanel.getSeqPanel().findSeq(e);
     if (e.isShiftDown() && (lastid != -1))
     {
       selectSeqs(lastid, seq);
@@ -361,13 +358,48 @@ public class IdPanel extends JPanel implements MouseListener,
     {
       selectSeq(seq);
     }
-    // TODO is this addition ok here?
+
     av.isSelectionGroupChanged(true);
 
     alignPanel.paintAlignment(true);
   }
 
   /**
+   * Build and show the popup-menu at the right-click mouse position
+   * 
+   * @param e
+   */
+  void showPopupMenu(MouseEvent e)
+  {
+    int seq2 = alignPanel.getSeqPanel().findSeq(e);
+    Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq2);
+    // build a new links menu based on the current links + any non-positional
+    // features
+    Vector<String> nlinks = new Vector<String>(Preferences.sequenceURLLinks);
+    SequenceFeature sfs[] = sq == null ? null : sq.getSequenceFeatures();
+    if (sfs != null)
+    {
+      for (SequenceFeature sf : sfs)
+      {
+        if (sf.begin == sf.end && sf.begin == 0)
+        {
+          if (sf.links != null && sf.links.size() > 0)
+          {
+            for (int l = 0, lSize = sf.links.size(); l < lSize; l++)
+            {
+              nlinks.addElement(sf.links.elementAt(l));
+            }
+          }
+        }
+      }
+    }
+
+    PopupMenu pop = new PopupMenu(alignPanel, sq, nlinks,
+            Preferences.getGroupURLLinks());
+    pop.show(this, e.getX(), e.getY());
+  }
+
+  /**
    * Toggle whether the sequence is part of the current selection group.
    * 
    * @param seq
@@ -434,6 +466,11 @@ public class IdPanel extends JPanel implements MouseListener,
     PaintRefresher.Refresh(this, av.getSequenceSetId());
     // always send selection message when mouse is released
     av.sendSelection();
+
+    if (e.isPopupTrigger()) // Windows reports this in mouseReleased
+    {
+      showPopupMenu(e);
+    }
   }
 
   /**
diff --git a/src/jalview/gui/IdwidthAdjuster.java b/src/jalview/gui/IdwidthAdjuster.java
index f26dfa7..56bc18a 100644
--- a/src/jalview/gui/IdwidthAdjuster.java
+++ b/src/jalview/gui/IdwidthAdjuster.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/JDatabaseTree.java b/src/jalview/gui/JDatabaseTree.java
index 0ee8008..765299a 100644
--- a/src/jalview/gui/JDatabaseTree.java
+++ b/src/jalview/gui/JDatabaseTree.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,7 +26,6 @@ import jalview.ws.seqfetcher.DbSourceProxy;
 
 import java.awt.BorderLayout;
 import java.awt.Component;
-import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
 import java.awt.GridLayout;
@@ -34,6 +33,8 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.KeyEvent;
 import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Hashtable;
@@ -46,6 +47,7 @@ import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JTree;
+import javax.swing.ToolTipManager;
 import javax.swing.event.TreeSelectionEvent;
 import javax.swing.event.TreeSelectionListener;
 import javax.swing.tree.DefaultMutableTreeNode;
@@ -72,7 +74,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
       @Override
       public void actionPerformed(ActionEvent arg0)
       {
-        showDialog(null);
+        showDialog();
       }
     });
     return viewdbs;
@@ -168,6 +170,19 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
         _setSelectionState();
       }
     });
+    dbviews.addMouseListener(new MouseAdapter()
+    {
+
+      @Override
+      public void mousePressed(MouseEvent e)
+      {
+        if (e.getClickCount() == 2)
+        {
+          okPressed();
+          closeDialog();
+        }
+      }
+    });
     JPanel jc = new JPanel(new BorderLayout()), j = new JPanel(
             new FlowLayout());
     jc.add(svp, BorderLayout.CENTER);
@@ -187,6 +202,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
     jc.validate();
     // j.setPreferredSize(new Dimension(300,50));
     add(jc, BorderLayout.CENTER);
+    ok.setEnabled(false);
     j.add(ok);
     j.add(cancel);
     add(j, BorderLayout.SOUTH);
@@ -244,6 +260,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
     public DbTreeRenderer(JDatabaseTree me)
     {
       us = me;
+      ToolTipManager.sharedInstance().registerComponent(dbviews);
     }
 
     private Component returnLabel(String txt)
@@ -265,19 +282,23 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
         value = vl.getUserObject();
         if (value instanceof DbSourceProxy)
         {
-          val = (((DbSourceProxy) value).getDbName());
+          val = ((DbSourceProxy) value).getDbName();
+          if (((DbSourceProxy) value).getDescription() != null)
+          { // getName()
+            this.setToolTipText(((DbSourceProxy) value).getDescription());
+          }
         }
         else
         {
           if (value instanceof String)
           {
-            val = ((String) value);
+            val = (String) value;
           }
         }
       }
       if (value == null)
       {
-        val = ("");
+        val = "";
       }
       return super.getTreeCellRendererComponent(tree, val, selected,
               expanded, leaf, row, hasFocus);
@@ -302,7 +323,6 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
   protected void okPressed()
   {
     _setSelectionState();
-    closeDialog();
   }
 
   @Override
@@ -314,7 +334,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
     closeDialog();
   }
 
-  private void showDialog(Container parent)
+  void showDialog()
   {
     oldselection = selection;
     oldtsel = tsel;
@@ -343,10 +363,12 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
     {
       return;
     }
+    ok.setEnabled(false);
     if (dbviews.getSelectionCount() == 0)
     {
       selection = null;
     }
+
     tsel = dbviews.getSelectionPaths();
     boolean forcedFirstChild = false;
     List<DbSourceProxy> srcs = new ArrayList<DbSourceProxy>();
@@ -358,6 +380,10 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
                 .getLastPathComponent();
         if (dmt.getUserObject() != null)
         {
+          /*
+           * enable OK button once a selection has been made
+           */
+          ok.setEnabled(true);
           if (dmt.getUserObject() instanceof DbSourceProxy)
           {
             srcs.add((DbSourceProxy) dmt.getUserObject());
@@ -413,6 +439,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
       }
     }
 
+    dbstatex.setText(" ");
     if (allowMultiSelections)
     {
       dbstatus.setText(MessageManager.formatMessage(
@@ -421,7 +448,6 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
                   (srcs.size() == 1 ? "" : "s"),
                   (srcs.size() > 0 ? " with " + x + " test quer"
                           + (x == 1 ? "y" : "ies") : ".") }));
-      dbstatex.setText(" ");
     }
     else
     {
@@ -434,10 +460,6 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
           dbstatex.setText(MessageManager.formatMessage(
                   "label.example_param", new String[] { qr }));
         }
-        else
-        {
-          dbstatex.setText(" ");
-        }
       }
       else
       {
@@ -549,6 +571,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
     {
       action = arg0.getKeyCode();
       okPressed();
+      closeDialog();
     }
     if (!arg0.isConsumed() && arg0.getKeyChar() == KeyEvent.VK_ESCAPE)
     {
diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java
index d920c7f..c9604a2 100644
--- a/src/jalview/gui/Jalview2XML.java
+++ b/src/jalview/gui/Jalview2XML.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import jalview.analysis.Conservation;
+import jalview.api.FeatureColourI;
 import jalview.api.ViewStyleI;
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
@@ -71,7 +73,7 @@ import jalview.schemabinding.version2.Viewport;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
-import jalview.schemes.GraduatedColor;
+import jalview.schemes.FeatureColour;
 import jalview.schemes.ResidueColourScheme;
 import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
@@ -79,6 +81,7 @@ import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.util.StringUtils;
 import jalview.util.jarInputStreamProvider;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
@@ -90,6 +93,7 @@ import jalview.ws.params.ArgumentI;
 import jalview.ws.params.AutoCalcSetting;
 import jalview.ws.params.WsParamSetI;
 
+import java.awt.Color;
 import java.awt.Rectangle;
 import java.io.BufferedReader;
 import java.io.DataInputStream;
@@ -105,6 +109,7 @@ import java.lang.reflect.InvocationTargetException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -116,7 +121,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.StringTokenizer;
 import java.util.Vector;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
@@ -163,7 +167,9 @@ public class Jalview2XML
    */
   Map<String, SequenceI> seqRefIds = null;
 
-  Vector frefedSequence = null;
+  Map<String, SequenceI> incompleteSeqs = null;
+
+  List<SeqFref> frefedSequence = null;
 
   boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
 
@@ -218,6 +224,10 @@ public class Jalview2XML
       {
         seqsToIds.clear();
       }
+      if (incompleteSeqs != null)
+      {
+        incompleteSeqs.clear();
+      }
       // seqRefIds = null;
       // seqsToIds = null;
     }
@@ -240,6 +250,14 @@ public class Jalview2XML
     {
       seqRefIds = new HashMap<String, SequenceI>();
     }
+    if (incompleteSeqs == null)
+    {
+      incompleteSeqs = new HashMap<String, SequenceI>();
+    }
+    if (frefedSequence == null)
+    {
+      frefedSequence = new ArrayList<SeqFref>();
+    }
   }
 
   public Jalview2XML()
@@ -251,78 +269,185 @@ public class Jalview2XML
     this.raiseGUI = raiseGUI;
   }
 
+  /**
+   * base class for resolving forward references to sequences by their ID
+   * 
+   * @author jprocter
+   *
+   */
+  abstract class SeqFref
+  {
+    String sref;
+
+    String type;
+
+    public SeqFref(String _sref, String type)
+    {
+      sref = _sref;
+      this.type = type;
+    }
+
+    public String getSref()
+    {
+      return sref;
+    }
+
+    public SequenceI getSrefSeq()
+    {
+      return seqRefIds.get(sref);
+    }
+
+    public boolean isResolvable()
+    {
+      return seqRefIds.get(sref) != null;
+    }
+
+    public SequenceI getSrefDatasetSeq()
+    {
+      SequenceI sq = seqRefIds.get(sref);
+      if (sq != null)
+      {
+        while (sq.getDatasetSequence() != null)
+        {
+          sq = sq.getDatasetSequence();
+        }
+      }
+      return sq;
+    }
+
+    /**
+     * @return true if the forward reference was fully resolved
+     */
+    abstract boolean resolve();
+
+    @Override
+    public String toString()
+    {
+      return type + " reference to " + sref;
+    }
+  }
+
+  /**
+   * create forward reference for a mapping
+   * 
+   * @param sref
+   * @param _jmap
+   * @return
+   */
+  public SeqFref newMappingRef(final String sref,
+          final jalview.datamodel.Mapping _jmap)
+  {
+    SeqFref fref = new SeqFref(sref, "Mapping")
+    {
+      public jalview.datamodel.Mapping jmap = _jmap;
+
+      @Override
+      boolean resolve()
+      {
+        SequenceI seq = getSrefDatasetSeq();
+        if (seq == null)
+        {
+          return false;
+        }
+        jmap.setTo(seq);
+        return true;
+      }
+    };
+    return fref;
+  }
+
+  public SeqFref newAlcodMapRef(final String sref,
+          final AlignedCodonFrame _cf, final jalview.datamodel.Mapping _jmap)
+  {
+
+    SeqFref fref = new SeqFref(sref, "Codon Frame")
+    {
+      AlignedCodonFrame cf = _cf;
+
+      public jalview.datamodel.Mapping mp = _jmap;
+
+      @Override
+      public boolean isResolvable()
+      {
+        return super.isResolvable() && mp.getTo() != null;
+      };
+
+      @Override
+      boolean resolve()
+      {
+        SequenceI seq = getSrefDatasetSeq();
+        if (seq == null)
+        {
+          return false;
+        }
+        cf.addMap(seq, mp.getTo(), mp.getMap());
+        return true;
+      }
+    };
+    return fref;
+  }
+
   public void resolveFrefedSequences()
   {
-    if (frefedSequence.size() > 0)
+    Iterator<SeqFref> nextFref = frefedSequence.iterator();
+    int toresolve = frefedSequence.size();
+    int unresolved = 0, failedtoresolve = 0;
+    while (nextFref.hasNext())
     {
-      int r = 0, rSize = frefedSequence.size();
-      while (r < rSize)
+      SeqFref ref = nextFref.next();
+      if (ref.isResolvable())
       {
-        Object[] ref = (Object[]) frefedSequence.elementAt(r);
-        if (ref != null)
+        try
         {
-          String sref = (String) ref[0];
-          if (seqRefIds.containsKey(sref))
+          if (ref.resolve())
           {
-            if (ref[1] instanceof jalview.datamodel.Mapping)
-            {
-              SequenceI seq = seqRefIds.get(sref);
-              while (seq.getDatasetSequence() != null)
-              {
-                seq = seq.getDatasetSequence();
-              }
-              ((jalview.datamodel.Mapping) ref[1]).setTo(seq);
-            }
-            else
-            {
-              if (ref[1] instanceof jalview.datamodel.AlignedCodonFrame)
-              {
-                SequenceI seq = seqRefIds.get(sref);
-                while (seq.getDatasetSequence() != null)
-                {
-                  seq = seq.getDatasetSequence();
-                }
-                if (ref[2] != null
-                        && ref[2] instanceof jalview.datamodel.Mapping)
-                {
-                  jalview.datamodel.Mapping mp = (jalview.datamodel.Mapping) ref[2];
-                  ((jalview.datamodel.AlignedCodonFrame) ref[1]).addMap(
-                          seq, mp.getTo(), mp.getMap());
-                }
-                else
-                {
-                  System.err
-                          .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for AlcodonFrames involving "
-                                  + ref[2].getClass() + " type objects.");
-                }
-              }
-              else
-              {
-                System.err
-                        .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for "
-                                + ref[1].getClass() + " type objects.");
-              }
-            }
-            frefedSequence.remove(r);
-            rSize--;
+            nextFref.remove();
           }
           else
           {
-            System.err
-                    .println("IMPLEMENTATION WARNING: Unresolved forward reference for hash string "
-                            + ref[0]
-                            + " with objecttype "
-                            + ref[1].getClass());
-            r++;
+            failedtoresolve++;
           }
+        } catch (Exception x)
+        {
+          System.err
+                  .println("IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "
+                          + ref.getSref());
+          x.printStackTrace();
+          failedtoresolve++;
         }
-        else
+      }
+      else
+      {
+        unresolved++;
+      }
+    }
+    if (unresolved > 0)
+    {
+      System.err.println("Jalview Project Import: There were " + unresolved
+              + " forward references left unresolved on the stack.");
+    }
+    if (failedtoresolve > 0)
+    {
+      System.err.println("SERIOUS! " + failedtoresolve
+              + " resolvable forward references failed to resolve.");
+    }
+    if (incompleteSeqs != null && incompleteSeqs.size() > 0)
+    {
+      System.err.println("Jalview Project Import: There are "
+              + incompleteSeqs.size()
+              + " sequences which may have incomplete metadata.");
+      if (incompleteSeqs.size() < 10)
+      {
+        for (SequenceI s : incompleteSeqs.values())
         {
-          // empty reference
-          frefedSequence.remove(r);
-          rSize--;
+          System.err.println(s.toString());
         }
       }
+      else
+      {
+        System.err
+                .println("Too many to report. Skipping output of incomplete sequences.");
+      }
     }
   }
 
@@ -394,7 +519,20 @@ public class Jalview2XML
     {
       return;
     }
+    saveAllFrames(Arrays.asList(frames), jout);
+  }
 
+  /**
+   * core method for storing state for a set of AlignFrames.
+   * 
+   * @param frames
+   *          - frames involving all data to be exported (including containing
+   *          splitframes)
+   * @param jout
+   *          - project output stream
+   */
+  private void saveAllFrames(List<AlignFrame> frames, JarOutputStream jout)
+  {
     Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
 
     /*
@@ -414,9 +552,9 @@ public class Jalview2XML
       List<String> viewIds = new ArrayList<String>();
 
       // REVERSE ORDER
-      for (int i = frames.length - 1; i > -1; i--)
+      for (int i = frames.size() - 1; i > -1; i--)
       {
-        AlignFrame af = frames[i];
+        AlignFrame af = frames.get(i);
         // skip ?
         if (skipList != null
                 && skipList
@@ -519,30 +657,20 @@ public class Jalview2XML
   {
     try
     {
-      int ap = 0;
-      int apSize = af.alignPanels.size();
       FileOutputStream fos = new FileOutputStream(jarFile);
       JarOutputStream jout = new JarOutputStream(fos);
-      Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
-      List<String> viewIds = new ArrayList<String>();
+      List<AlignFrame> frames = new ArrayList<AlignFrame>();
 
-      for (AlignmentPanel apanel : af.alignPanels)
+      // resolve splitframes
+      if (af.getViewport().getCodingComplement() != null)
       {
-        String jfileName = apSize == 1 ? fileName : fileName + ap;
-        ap++;
-        if (!jfileName.endsWith(".xml"))
-        {
-          jfileName = jfileName + ".xml";
-        }
-        saveState(apanel, jfileName, jout, viewIds);
-        String dssid = getDatasetIdRef(af.getViewport().getAlignment()
-                .getDataset());
-        if (!dsses.containsKey(dssid))
-        {
-          dsses.put(dssid, af);
-        }
+        frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames();
       }
-      writeDatasetFor(dsses, fileName, jout);
+      else
+      {
+        frames.add(af);
+      }
+      saveAllFrames(frames, jout);
       try
       {
         jout.flush();
@@ -633,11 +761,15 @@ public class Jalview2XML
     object.setVersion(jalview.bin.Cache.getDefault("VERSION",
             "Development Build"));
 
-    jalview.datamodel.AlignmentI jal = av.getAlignment();
+    /**
+     * rjal is full height alignment, jal is actual alignment with full metadata
+     * but excludes hidden sequences.
+     */
+    jalview.datamodel.AlignmentI rjal = av.getAlignment(), jal = rjal;
 
     if (av.hasHiddenRows())
     {
-      jal = jal.getHiddenSequences().getFullAlignment();
+      rjal = jal.getHiddenSequences().getFullAlignment();
     }
 
     SequenceSet vamsasSet = new SequenceSet();
@@ -654,6 +786,7 @@ public class Jalview2XML
       {
         // switch jal and the dataset
         jal = jal.getDataset();
+        rjal = jal;
       }
     }
     if (jal.getProperties() != null)
@@ -671,38 +804,42 @@ public class Jalview2XML
 
     JSeq jseq;
     Set<String> calcIdSet = new HashSet<String>();
-
+    // record the set of vamsas sequence XML POJO we create.
+    HashMap<String, Sequence> vamsasSetIds = new HashMap<String, Sequence>();
     // SAVE SEQUENCES
-    for (int i = 0; i < jal.getHeight(); i++)
+    for (final SequenceI jds : rjal.getSequences())
     {
-      final SequenceI jds = jal.getSequenceAt(i);
       final SequenceI jdatasq = jds.getDatasetSequence() == null ? jds
               : jds.getDatasetSequence();
       String id = seqHash(jds);
-
-      if (seqRefIds.get(id) != null)
-      {
-        // This happens for two reasons: 1. multiple views are being serialised.
-        // 2. the hashCode has collided with another sequence's code. This DOES
-        // HAPPEN! (PF00072.15.stk does this)
-        // JBPNote: Uncomment to debug writing out of files that do not read
-        // back in due to ArrayOutOfBoundExceptions.
-        // System.err.println("vamsasSeq backref: "+id+"");
-        // System.err.println(jds.getName()+"
-        // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
-        // System.err.println("Hashcode: "+seqHash(jds));
-        // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
-        // System.err.println(rsq.getName()+"
-        // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
-        // System.err.println("Hashcode: "+seqHash(rsq));
-      }
-      else
-      {
-        vamsasSeq = createVamsasSequence(id, jds);
-        vamsasSet.addSequence(vamsasSeq);
-        seqRefIds.put(id, jds);
+      if (vamsasSetIds.get(id) == null)
+      {
+        if (seqRefIds.get(id) != null && !storeDS)
+        {
+          // This happens for two reasons: 1. multiple views are being
+          // serialised.
+          // 2. the hashCode has collided with another sequence's code. This
+          // DOES
+          // HAPPEN! (PF00072.15.stk does this)
+          // JBPNote: Uncomment to debug writing out of files that do not read
+          // back in due to ArrayOutOfBoundExceptions.
+          // System.err.println("vamsasSeq backref: "+id+"");
+          // System.err.println(jds.getName()+"
+          // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
+          // System.err.println("Hashcode: "+seqHash(jds));
+          // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
+          // System.err.println(rsq.getName()+"
+          // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
+          // System.err.println("Hashcode: "+seqHash(rsq));
+        }
+        else
+        {
+          vamsasSeq = createVamsasSequence(id, jds);
+          vamsasSet.addSequence(vamsasSeq);
+          vamsasSetIds.put(id, vamsasSeq);
+          seqRefIds.put(id, jds);
+        }
       }
-
       jseq = new JSeq();
       jseq.setStart(jds.getStart());
       jseq.setEnd(jds.getEnd());
@@ -714,26 +851,33 @@ public class Jalview2XML
         // Store any sequences this sequence represents
         if (av.hasHiddenRows())
         {
+          // use rjal, contains the full height alignment
           jseq.setHidden(av.getAlignment().getHiddenSequences()
                   .isHidden(jds));
 
-          if (av.isHiddenRepSequence(jal.getSequenceAt(i)))
+          if (av.isHiddenRepSequence(jds))
           {
             jalview.datamodel.SequenceI[] reps = av
-                    .getRepresentedSequences(jal.getSequenceAt(i))
-                    .getSequencesInOrder(jal);
+                    .getRepresentedSequences(jds).getSequencesInOrder(rjal);
 
             for (int h = 0; h < reps.length; h++)
             {
-              if (reps[h] != jal.getSequenceAt(i))
+              if (reps[h] != jds)
               {
-                jseq.addHiddenSequences(jal.findIndex(reps[h]));
+                jseq.addHiddenSequences(rjal.findIndex(reps[h]));
               }
             }
           }
         }
+        // mark sequence as reference - if it is the reference for this view
+        if (jal.hasSeqrep())
+        {
+          jseq.setViewreference(jds == jal.getSeqrep());
+        }
       }
 
+      // TODO: omit sequence features from each alignment view's XML dump if we
+      // are storing dataset
       if (jds.getSequenceFeatures() != null)
       {
         jalview.datamodel.SequenceFeature[] sf = jds.getSequenceFeatures();
@@ -761,10 +905,11 @@ public class Jalview2XML
           if (sf[index].otherDetails != null)
           {
             String key;
-            Enumeration keys = sf[index].otherDetails.keys();
-            while (keys.hasMoreElements())
+            Iterator<String> keys = sf[index].otherDetails.keySet()
+                    .iterator();
+            while (keys.hasNext())
             {
-              key = keys.nextElement().toString();
+              key = keys.next();
               OtherData keyValue = new OtherData();
               keyValue.setKey(key);
               keyValue.setValue(sf[index].otherDetails.get(key).toString());
@@ -847,17 +992,16 @@ public class Jalview2XML
             }
           }
 
-          if (entry.getProperty() != null && !entry.getProperty().isEmpty())
+          Enumeration<String> props = entry.getProperties();
+          if (props.hasMoreElements())
           {
             PdbentryItem item = new PdbentryItem();
-            Hashtable properties = entry.getProperty();
-            Enumeration en2 = properties.keys();
-            while (en2.hasMoreElements())
+            while (props.hasMoreElements())
             {
               Property prop = new Property();
-              String key = en2.nextElement().toString();
+              String key = props.nextElement();
               prop.setName(key);
-              prop.setValue(properties.get(key).toString());
+              prop.setValue(entry.getProperty(key).toString());
               item.addProperty(prop);
             }
             pdb.addPdbentryItem(item);
@@ -877,16 +1021,17 @@ public class Jalview2XML
       jal = av.getAlignment();
     }
     // SAVE MAPPINGS
-    if (jal.getCodonFrames() != null)
+    // FOR DATASET
+    if (storeDS && jal.getCodonFrames() != null)
     {
-      Set<AlignedCodonFrame> jac = jal.getCodonFrames();
+      List<AlignedCodonFrame> jac = jal.getCodonFrames();
       for (AlignedCodonFrame acf : jac)
       {
         AlcodonFrame alc = new AlcodonFrame();
-        vamsasSet.addAlcodonFrame(alc);
         if (acf.getProtMappings() != null
                 && acf.getProtMappings().length > 0)
         {
+          boolean hasMap = false;
           SequenceI[] dnas = acf.getdnaSeqs();
           jalview.datamodel.Mapping[] pmaps = acf.getProtMappings();
           for (int m = 0; m < pmaps.length; m++)
@@ -896,9 +1041,14 @@ public class Jalview2XML
             alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
                     false));
             alc.addAlcodMap(alcmap);
+            hasMap = true;
+          }
+          if (hasMap)
+          {
+            vamsasSet.addAlcodonFrame(alc);
           }
         }
-
+        // TODO: delete this ? dead code from 2.8.3->2.9 ?
         // {
         // AlcodonFrame alc = new AlcodonFrame();
         // vamsasSet.addAlcodonFrame(alc);
@@ -1092,15 +1242,26 @@ public class Jalview2XML
       view.setViewName(av.viewName);
       view.setGatheredViews(av.isGatherViewsHere());
 
-      Rectangle position = ap.av.getExplodedGeometry();
-      if (position == null)
+      Rectangle size = ap.av.getExplodedGeometry();
+      Rectangle position = size;
+      if (size == null)
       {
-        position = ap.alignFrame.getBounds();
+        size = ap.alignFrame.getBounds();
+        if (av.getCodingComplement() != null)
+        {
+          position = ((SplitFrame) ap.alignFrame.getSplitViewContainer())
+                  .getBounds();
+        }
+        else
+        {
+          position = size;
+        }
       }
       view.setXpos(position.x);
       view.setYpos(position.y);
-      view.setWidth(position.width);
-      view.setHeight(position.height);
+
+      view.setWidth(size.width);
+      view.setHeight(size.height);
 
       view.setStartRes(av.startRes);
       view.setStartSeq(av.startSeq);
@@ -1182,82 +1343,53 @@ public class Jalview2XML
                 .getFeatureRenderer().getRenderOrder()
                 .toArray(new String[0]);
 
-        Vector settingsAdded = new Vector();
-        Object gstyle = null;
-        GraduatedColor gcol = null;
+        Vector<String> settingsAdded = new Vector<String>();
         if (renderOrder != null)
         {
-          for (int ro = 0; ro < renderOrder.length; ro++)
+          for (String featureType : renderOrder)
           {
-            gstyle = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                    .getFeatureStyle(renderOrder[ro]);
+            FeatureColourI fcol = ap.getSeqPanel().seqCanvas
+                    .getFeatureRenderer().getFeatureStyle(featureType);
             Setting setting = new Setting();
-            setting.setType(renderOrder[ro]);
-            if (gstyle instanceof GraduatedColor)
+            setting.setType(featureType);
+            if (!fcol.isSimpleColour())
             {
-              gcol = (GraduatedColor) gstyle;
-              setting.setColour(gcol.getMaxColor().getRGB());
-              setting.setMincolour(gcol.getMinColor().getRGB());
-              setting.setMin(gcol.getMin());
-              setting.setMax(gcol.getMax());
-              setting.setColourByLabel(gcol.isColourByLabel());
-              setting.setAutoScale(gcol.isAutoScale());
-              setting.setThreshold(gcol.getThresh());
-              setting.setThreshstate(gcol.getThreshType());
+              setting.setColour(fcol.getMaxColour().getRGB());
+              setting.setMincolour(fcol.getMinColour().getRGB());
+              setting.setMin(fcol.getMin());
+              setting.setMax(fcol.getMax());
+              setting.setColourByLabel(fcol.isColourByLabel());
+              setting.setAutoScale(fcol.isAutoScaled());
+              setting.setThreshold(fcol.getThreshold());
+              // -1 = No threshold, 0 = Below, 1 = Above
+              setting.setThreshstate(fcol.isAboveThreshold() ? 1 : (fcol
+                      .isBelowThreshold() ? 0 : -1));
             }
             else
             {
-              setting.setColour(ap.getSeqPanel().seqCanvas
-                      .getFeatureRenderer().getColour(renderOrder[ro])
-                      .getRGB());
+              setting.setColour(fcol.getColour().getRGB());
             }
 
             setting.setDisplay(av.getFeaturesDisplayed().isVisible(
-                    renderOrder[ro]));
+                    featureType));
             float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                    .getOrder(renderOrder[ro]);
+                    .getOrder(featureType);
             if (rorder > -1)
             {
               setting.setOrder(rorder);
             }
             fs.addSetting(setting);
-            settingsAdded.addElement(renderOrder[ro]);
+            settingsAdded.addElement(featureType);
           }
         }
 
-        // Make sure we save none displayed feature settings
-        Iterator en = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                .getFeatureColours().keySet().iterator();
-        while (en.hasNext())
-        {
-          String key = en.next().toString();
-          if (settingsAdded.contains(key))
-          {
-            continue;
-          }
-
-          Setting setting = new Setting();
-          setting.setType(key);
-          setting.setColour(ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                  .getColour(key).getRGB());
-
-          setting.setDisplay(false);
-          float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                  .getOrder(key);
-          if (rorder > -1)
-          {
-            setting.setOrder(rorder);
-          }
-          fs.addSetting(setting);
-          settingsAdded.addElement(key);
-        }
         // is groups actually supposed to be a map here ?
-        en = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                .getFeatureGroups().iterator();
-        Vector groupsAdded = new Vector();
+        Iterator<String> en = ap.getSeqPanel().seqCanvas
+                .getFeatureRenderer().getFeatureGroups().iterator();
+        Vector<String> groupsAdded = new Vector<String>();
         while (en.hasNext())
         {
-          String grp = en.next().toString();
+          String grp = en.next();
           if (groupsAdded.contains(grp))
           {
             continue;
@@ -1271,7 +1403,6 @@ public class Jalview2XML
           groupsAdded.addElement(grp);
         }
         jms.setFeatureSettings(fs);
-
       }
 
       if (av.hasHiddenColumns())
@@ -1937,16 +2068,17 @@ public class Jalview2XML
     if (jds.getDatasetSequence() != null)
     {
       vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
-      if (jds.getDatasetSequence().getDBRef() != null)
-      {
-        dbrefs = jds.getDatasetSequence().getDBRef();
-      }
     }
     else
     {
-      vamsasSeq.setDsseqid(id); // so we can tell which sequences really are
+      // seqId==dsseqid so we can tell which sequences really are
       // dataset sequences only
-      dbrefs = jds.getDBRef();
+      vamsasSeq.setDsseqid(id);
+      dbrefs = jds.getDBRefs();
+      if (parentseq == null)
+      {
+        parentseq = jds;
+      }
     }
     if (dbrefs != null)
     {
@@ -1998,38 +2130,32 @@ public class Jalview2XML
       if (jmp.getTo() != null)
       {
         MappingChoice mpc = new MappingChoice();
-        if (recurse
-                && (parentseq != jmp.getTo() || parentseq
-                        .getDatasetSequence() != jmp.getTo()))
+
+        // check/create ID for the sequence referenced by getTo()
+
+        String jmpid = "";
+        SequenceI ps = null;
+        if (parentseq != jmp.getTo()
+                && parentseq.getDatasetSequence() != jmp.getTo())
         {
-          mpc.setSequence(createVamsasSequence(false, seqHash(jmp.getTo()),
-                  jmp.getTo(), jds));
+          // chaining dbref rather than a handshaking one
+          jmpid = seqHash(ps = jmp.getTo());
         }
         else
         {
-          String jmpid = "";
-          SequenceI ps = null;
-          if (parentseq != jmp.getTo()
-                  && parentseq.getDatasetSequence() != jmp.getTo())
-          {
-            // chaining dbref rather than a handshaking one
-            jmpid = seqHash(ps = jmp.getTo());
-          }
-          else
-          {
-            jmpid = seqHash(ps = parentseq);
-          }
-          mpc.setDseqFor(jmpid);
-          if (!seqRefIds.containsKey(mpc.getDseqFor()))
-          {
-            jalview.bin.Cache.log.debug("creatign new DseqFor ID");
-            seqRefIds.put(mpc.getDseqFor(), ps);
-          }
-          else
-          {
-            jalview.bin.Cache.log.debug("reusing DseqFor ID");
-          }
+          jmpid = seqHash(ps = parentseq);
         }
+        mpc.setDseqFor(jmpid);
+        if (!seqRefIds.containsKey(mpc.getDseqFor()))
+        {
+          jalview.bin.Cache.log.debug("creatign new DseqFor ID");
+          seqRefIds.put(mpc.getDseqFor(), ps);
+        }
+        else
+        {
+          jalview.bin.Cache.log.debug("reusing DseqFor ID");
+        }
+
         mp.setMappingChoice(mpc);
       }
     }
@@ -2167,6 +2293,7 @@ public class Jalview2XML
       {
         SwingUtilities.invokeAndWait(new Runnable()
         {
+          @Override
           public void run()
           {
             setLoadingFinishedForNewStructureViewers();
@@ -2237,14 +2364,10 @@ public class Jalview2XML
     }
     if (seqRefIds == null)
     {
-      seqRefIds = new HashMap<String, SequenceI>();
-    }
-    if (frefedSequence == null)
-    {
-      frefedSequence = new Vector();
+      initSeqRefs();
     }
-
     AlignFrame af = null, _af = null;
+    IdentityHashMap<AlignmentI, AlignmentI> importedDatasets = new IdentityHashMap<AlignmentI, AlignmentI>();
     Map<String, AlignFrame> gatherToThisFrame = new HashMap<String, AlignFrame>();
     final String file = jprovider.getFilename();
     try
@@ -2272,13 +2395,24 @@ public class Jalview2XML
           if (true) // !skipViewport(object))
           {
             _af = loadFromObject(object, file, true, jprovider);
-            if (object.getJalviewModelSequence().getViewportCount() > 0)
+            if (_af != null
+                    && object.getJalviewModelSequence().getViewportCount() > 0)
             {
-              af = _af;
-              if (af.viewport.isGatherViewsHere())
+              if (af == null)
+              {
+                // store a reference to the first view
+                af = _af;
+              }
+              if (_af.viewport.isGatherViewsHere())
               {
-                gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
+                // if this is a gathered view, keep its reference since
+                // after gathering views, only this frame will remain
+                af = _af;
+                gatherToThisFrame.put(_af.viewport.getSequenceSetId(), _af);
               }
+              // Save dataset to register mappings once all resolved
+              importedDatasets.put(af.viewport.getAlignment().getDataset(),
+                      af.viewport.getAlignment().getDataset());
             }
           }
           entryCount++;
@@ -2334,11 +2468,6 @@ public class Jalview2XML
       e.printStackTrace();
     }
 
-    if (Desktop.instance != null)
-    {
-      Desktop.instance.stopLoading();
-    }
-
     /*
      * Regather multiple views (with the same sequence set id) to the frame (if
      * any) that is flagged as the one to gather to, i.e. convert them to tabbed
@@ -2352,11 +2481,24 @@ public class Jalview2XML
     }
 
     restoreSplitFrames();
-
+    for (AlignmentI ds : importedDatasets.keySet())
+    {
+      if (ds.getCodonFrames() != null)
+      {
+        StructureSelectionManager.getStructureSelectionManager(
+                Desktop.instance).registerMappings(ds.getCodonFrames());
+      }
+    }
     if (errorMessage != null)
     {
       reportErrors();
     }
+
+    if (Desktop.instance != null)
+    {
+      Desktop.instance.stopLoading();
+    }
+
     return af;
   }
 
@@ -2401,6 +2543,8 @@ public class Jalview2XML
           SplitFrame sf = createSplitFrame(dnaFrame, af);
           addedToSplitFrames.add(dnaFrame);
           addedToSplitFrames.add(af);
+          dnaFrame.setMenusForViewport();
+          af.setMenusForViewport();
           if (af.viewport.isGatherViewsHere())
           {
             gatherTo.add(sf);
@@ -2422,6 +2566,7 @@ public class Jalview2XML
         Viewport view = candidate.getKey();
         Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
                 view.getHeight());
+        af.setMenusForViewport();
         System.err.println("Failed to restore view " + view.getTitle()
                 + " to split frame");
       }
@@ -2453,6 +2598,11 @@ public class Jalview2XML
     int width = (int) dnaFrame.getBounds().getWidth();
     int height = (int) (dnaFrame.getBounds().getHeight()
             + proteinFrame.getBounds().getHeight() + 50);
+
+    /*
+     * SplitFrame location is saved to both enclosed frames
+     */
+    splitFrame.setLocation(dnaFrame.getX(), dnaFrame.getY());
     Desktop.addInternalFrame(splitFrame, title, width, height);
 
     /*
@@ -2520,14 +2670,16 @@ public class Jalview2XML
    * @param pdbId
    * @return
    */
-  String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
+  String loadPDBFile(jarInputStreamProvider jprovider, String pdbId,
+          String origFile)
   {
     if (alreadyLoadedPDB.containsKey(pdbId))
     {
       return alreadyLoadedPDB.get(pdbId).toString();
     }
 
-    String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb");
+    String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb",
+            origFile);
     if (tempFile != null)
     {
       alreadyLoadedPDB.put(pdbId, tempFile);
@@ -2544,14 +2696,26 @@ public class Jalview2XML
    * @param prefix
    *          a prefix for the temporary file name, must be at least three
    *          characters long
+   * @param origFile
+   *          null or original file - so new file can be given the same suffix
+   *          as the old one
    * @return
    */
   protected String copyJarEntry(jarInputStreamProvider jprovider,
-          String jarEntryName, String prefix)
+          String jarEntryName, String prefix, String origFile)
   {
     BufferedReader in = null;
     PrintWriter out = null;
-
+    String suffix = ".tmp";
+    if (origFile == null)
+    {
+      origFile = jarEntryName;
+    }
+    int sfpos = origFile.lastIndexOf(".");
+    if (sfpos > -1 && sfpos < (origFile.length() - 3))
+    {
+      suffix = "." + origFile.substring(sfpos + 1);
+    }
     try
     {
       JarInputStream jin = jprovider.getJarInputStream();
@@ -2569,7 +2733,7 @@ public class Jalview2XML
       if (entry != null)
       {
         in = new BufferedReader(new InputStreamReader(jin, UTF_8));
-        File outFile = File.createTempFile(prefix, ".tmp");
+        File outFile = File.createTempFile(prefix, suffix);
         outFile.deleteOnExit();
         out = new PrintWriter(new FileOutputStream(outFile));
         String data;
@@ -2657,36 +2821,71 @@ public class Jalview2XML
     // LOAD SEQUENCES
 
     List<SequenceI> hiddenSeqs = null;
-    jalview.datamodel.Sequence jseq;
 
     List<SequenceI> tmpseqs = new ArrayList<SequenceI>();
 
     boolean multipleView = false;
-
+    SequenceI referenceseqForView = null;
     JSeq[] jseqs = object.getJalviewModelSequence().getJSeq();
     int vi = 0; // counter in vamsasSeq array
     for (int i = 0; i < jseqs.length; i++)
     {
       String seqId = jseqs[i].getId();
 
-      if (seqRefIds.get(seqId) != null)
+      SequenceI tmpSeq = seqRefIds.get(seqId);
+      if (tmpSeq != null)
       {
-        tmpseqs.add(seqRefIds.get(seqId));
-        multipleView = true;
+        if (!incompleteSeqs.containsKey(seqId))
+        {
+          // may not need this check, but keep it for at least 2.9,1 release
+          if (tmpSeq.getStart() != jseqs[i].getStart()
+                  || tmpSeq.getEnd() != jseqs[i].getEnd())
+          {
+            System.err
+                    .println("Warning JAL-2154 regression: updating start/end for sequence "
+                            + tmpSeq.toString() + " to " + jseqs[i]);
+          }
+        }
+        else
+        {
+          incompleteSeqs.remove(seqId);
+        }
+        if (vamsasSeq.length > vi && vamsasSeq[vi].getId().equals(seqId))
+        {
+          // most likely we are reading a dataset XML document so
+          // update from vamsasSeq section of XML for this sequence
+          tmpSeq.setName(vamsasSeq[vi].getName());
+          tmpSeq.setDescription(vamsasSeq[vi].getDescription());
+          tmpSeq.setSequence(vamsasSeq[vi].getSequence());
+          vi++;
+        }
+        else
+        {
+          // reading multiple views, so vamsasSeq set is a subset of JSeq
+          multipleView = true;
+        }
+        tmpSeq.setStart(jseqs[i].getStart());
+        tmpSeq.setEnd(jseqs[i].getEnd());
+        tmpseqs.add(tmpSeq);
       }
       else
       {
-        jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
+        tmpSeq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
                 vamsasSeq[vi].getSequence());
-        jseq.setDescription(vamsasSeq[vi].getDescription());
-        jseq.setStart(jseqs[i].getStart());
-        jseq.setEnd(jseqs[i].getEnd());
-        jseq.setVamsasId(uniqueSetSuffix + seqId);
-        seqRefIds.put(vamsasSeq[vi].getId(), jseq);
-        tmpseqs.add(jseq);
+        tmpSeq.setDescription(vamsasSeq[vi].getDescription());
+        tmpSeq.setStart(jseqs[i].getStart());
+        tmpSeq.setEnd(jseqs[i].getEnd());
+        tmpSeq.setVamsasId(uniqueSetSuffix + seqId);
+        seqRefIds.put(vamsasSeq[vi].getId(), tmpSeq);
+        tmpseqs.add(tmpSeq);
         vi++;
       }
 
+      if (jseqs[i].hasViewreference() && jseqs[i].getViewreference())
+      {
+        referenceseqForView = tmpseqs.get(tmpseqs.size() - 1);
+      }
+
       if (jseqs[i].getHidden())
       {
         if (hiddenSeqs == null)
@@ -2694,9 +2893,8 @@ public class Jalview2XML
           hiddenSeqs = new ArrayList<SequenceI>();
         }
 
-        hiddenSeqs.add(seqRefIds.get(seqId));
+        hiddenSeqs.add(tmpSeq);
       }
-
     }
 
     // /
@@ -2705,31 +2903,51 @@ public class Jalview2XML
     SequenceI[] orderedSeqs = tmpseqs
             .toArray(new SequenceI[tmpseqs.size()]);
 
-    Alignment al = new Alignment(orderedSeqs);
-
-    // / Add the alignment properties
-    for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
-    {
-      SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
-      al.setProperty(ssp.getKey(), ssp.getValue());
-    }
-
-    // /
-    // SequenceFeatures are added to the DatasetSequence,
-    // so we must create or recover the dataset before loading features
+    AlignmentI al = null;
+    // so we must create or recover the dataset alignment before going further
     // ///////////////////////////////
     if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
     {
-      // older jalview projects do not have a dataset id.
+      // older jalview projects do not have a dataset - so creat alignment and
+      // dataset
+      al = new Alignment(orderedSeqs);
       al.setDataset(null);
     }
     else
     {
-      // recover dataset - passing on flag indicating if this a 'viewless'
-      // sequence set (a.k.a. a stored dataset for the project)
-      recoverDatasetFor(vamsasSet, al, object.getJalviewModelSequence()
-              .getViewportCount() == 0);
+      boolean isdsal = object.getJalviewModelSequence().getViewportCount() == 0;
+      if (isdsal)
+      {
+        // we are importing a dataset record, so
+        // recover reference to an alignment already materialsed as dataset
+        al = getDatasetFor(vamsasSet.getDatasetId());
+      }
+      if (al == null)
+      {
+        // materialse the alignment
+        al = new Alignment(orderedSeqs);
+      }
+      if (isdsal)
+      {
+        addDatasetRef(vamsasSet.getDatasetId(), al);
+      }
+
+      // finally, verify all data in vamsasSet is actually present in al
+      // passing on flag indicating if it is actually a stored dataset
+      recoverDatasetFor(vamsasSet, al, isdsal);
+    }
+
+    if (referenceseqForView != null)
+    {
+      al.setSeqrep(referenceseqForView);
+    }
+    // / Add the alignment properties
+    for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
+    {
+      SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
+      al.setProperty(ssp.getKey(), ssp.getValue());
     }
+
     // ///////////////////////////////
 
     Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
@@ -2737,6 +2955,12 @@ public class Jalview2XML
     {
       // load sequence features, database references and any associated PDB
       // structures for the alignment
+      //
+      // prior to 2.10, this part would only be executed the first time a
+      // sequence was encountered, but not afterwards.
+      // now, for 2.10 projects, this is also done if the xml doc includes
+      // dataset sequences not actually present in any particular view.
+      //
       for (int i = 0; i < vamsasSeq.length; i++)
       {
         if (jseqs[i].getFeaturesCount() > 0)
@@ -2763,13 +2987,17 @@ public class Jalview2XML
               }
 
             }
-
-            al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
+            // adds feature to datasequence's feature set (since Jalview 2.10)
+            al.getSequenceAt(i).addSequenceFeature(sf);
           }
         }
         if (vamsasSeq[i].getDBRefCount() > 0)
         {
-          addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
+          // adds dbrefs to datasequence's set (since Jalview 2.10)
+          addDBRefs(
+                  al.getSequenceAt(i).getDatasetSequence() == null ? al.getSequenceAt(i)
+                          : al.getSequenceAt(i).getDatasetSequence(),
+                  vamsasSeq[i]);
         }
         if (jseqs[i].getPdbidsCount() > 0)
         {
@@ -2780,29 +3008,49 @@ public class Jalview2XML
             entry.setId(ids[p].getId());
             if (ids[p].getType() != null)
             {
-              if (ids[p].getType().equalsIgnoreCase("PDB"))
+              if (PDBEntry.Type.getType(ids[p].getType()) != null)
               {
-                entry.setType(PDBEntry.Type.PDB);
+                entry.setType(PDBEntry.Type.getType(ids[p].getType()));
               }
               else
               {
                 entry.setType(PDBEntry.Type.FILE);
               }
             }
-            if (ids[p].getFile() != null)
+            // jprovider is null when executing 'New View'
+            if (ids[p].getFile() != null && jprovider != null)
             {
               if (!pdbloaded.containsKey(ids[p].getFile()))
               {
-                entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
+                entry.setFile(loadPDBFile(jprovider, ids[p].getId(),
+                        ids[p].getFile()));
               }
               else
               {
                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
               }
             }
+            if (ids[p].getPdbentryItem() != null)
+            {
+              for (PdbentryItem item : ids[p].getPdbentryItem())
+              {
+                for (Property pr : item.getProperty())
+                {
+                  entry.setProperty(pr.getName(), pr.getValue());
+                }
+              }
+            }
             StructureSelectionManager.getStructureSelectionManager(
                     Desktop.instance).registerPDBEntry(entry);
-            al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
+            // adds PDBEntry to datasequence's set (since Jalview 2.10)
+            if (al.getSequenceAt(i).getDatasetSequence() != null)
+            {
+              al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
+            }
+            else
+            {
+              al.getSequenceAt(i).addPDBId(entry);
+            }
           }
         }
       }
@@ -2831,20 +3079,20 @@ public class Jalview2XML
             if (maps[m].getMapping() != null)
             {
               mapping = addMapping(maps[m].getMapping());
-            }
-            if (dnaseq != null)
-            {
-              cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
-            }
-            else
-            {
-              // defer to later
-              frefedSequence.add(new Object[] { maps[m].getDnasq(), cf,
-                  mapping });
+              if (dnaseq != null && mapping.getTo() != null)
+              {
+                cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
+              }
+              else
+              {
+                // defer to later
+                frefedSequence.add(newAlcodMapRef(maps[m].getDnasq(), cf,
+                        mapping));
+              }
             }
           }
+          al.addCodonFrame(cf);
         }
-        al.addCodonFrame(cf);
       }
     }
 
@@ -3153,9 +3401,8 @@ public class Jalview2XML
         }
         if (jGroup.getConsThreshold() != 0)
         {
-          jalview.analysis.Conservation c = new jalview.analysis.Conservation(
-                  "All", ResidueProperties.propHash, 3,
-                  sg.getSequences(null), 0, sg.getWidth() - 1);
+          Conservation c = new Conservation("All", sg.getSequences(null),
+                  0, sg.getWidth() - 1);
           c.calculate();
           c.verdict(false, 25);
           sg.cs.setConservation(c);
@@ -3250,8 +3497,8 @@ public class Jalview2XML
      * indicate that annotation colours are applied across all groups (pre
      * Jalview 2.8.1 behaviour)
      */
-    boolean doGroupAnnColour = isVersionStringLaterThan("2.8.1",
-            object.getVersion());
+    boolean doGroupAnnColour = Jalview2XML.isVersionStringLaterThan(
+            "2.8.1", object.getVersion());
 
     AlignmentPanel ap = null;
     boolean isnewview = true;
@@ -3344,7 +3591,7 @@ public class Jalview2XML
           String rnaTitle = ss.getTitle();
           String sessionState = ss.getViewerState();
           String tempStateFile = copyJarEntry(jprovider, sessionState,
-                  "varna");
+                  "varna", null);
           RnaModel rna = new RnaModel(rnaTitle, ann, seq, null, gapped);
           appVarna.addModelSession(rna, rnaTitle, tempStateFile);
         }
@@ -3519,7 +3766,8 @@ public class Jalview2XML
             // Originally : ids[p].getFile()
             // : TODO: verify external PDB file recovery still works in normal
             // jalview project load
-            jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
+            jpdb.setFile(loadPDBFile(jprovider, ids[p].getId(),
+                    ids[p].getFile()));
             jpdb.setId(ids[p].getId());
 
             int x = structureState.getXpos();
@@ -3530,7 +3778,8 @@ public class Jalview2XML
             // Probably don't need to do this anymore...
             // Desktop.desktop.getComponentAt(x, y);
             // TODO: NOW: check that this recovers the PDB file correctly.
-            String pdbFile = loadPDBFile(jprovider, ids[p].getId());
+            String pdbFile = loadPDBFile(jprovider, ids[p].getId(),
+                    ids[p].getFile());
             jalview.datamodel.SequenceI seq = seqRefIds.get(jseqs[i]
                     .getId() + "");
             if (sviewid == null)
@@ -3690,7 +3939,7 @@ public class Jalview2XML
      */
     String viewerJarEntryName = getViewerJarEntryName(data.getViewId());
     chimeraSessionFile = copyJarEntry(jprovider, viewerJarEntryName,
-            "chimera");
+            "chimera", null);
 
     Set<Entry<File, StructureData>> fileData = data.getFileData()
             .entrySet();
@@ -3771,6 +4020,11 @@ public class Jalview2XML
         // filename
         // translation differently.
         StructureData filedat = oldFiles.get(new File(oldfilenam));
+        if (filedat == null)
+        {
+          String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\");
+          filedat = oldFiles.get(new File(reformatedOldFilename));
+        }
         newFileLoc.append(Platform.escapeString(filedat.getFilePath()));
         pdbfilenames.add(filedat.getFilePath());
         pdbids.add(filedat.getPdbId());
@@ -4029,18 +4283,22 @@ public class Jalview2XML
   }
 
   /**
+   * Answers true if 'version' is equal to or later than 'supported', where each
+   * is formatted as major/minor versions like "2.8.3" or "2.3.4b1" for bugfix
+   * changes. Development and test values for 'version' are leniently treated
+   * i.e. answer true.
    * 
    * @param supported
    *          - minimum version we are comparing against
    * @param version
-   *          - version of data being processsed.
-   * @return true if version is development/null or evaluates to the same or
-   *         later X.Y.Z (where X,Y,Z are like [0-9]+b?[0-9]*)
+   *          - version of data being processsed
+   * @return
    */
-  protected boolean isVersionStringLaterThan(String supported,
+  public static boolean isVersionStringLaterThan(String supported,
           String version)
   {
-    if (version == null || version.equalsIgnoreCase("DEVELOPMENT BUILD")
+    if (supported == null || version == null
+            || version.equalsIgnoreCase("DEVELOPMENT BUILD")
             || version.equalsIgnoreCase("Test")
             || version.equalsIgnoreCase("AUTOMATED BUILD"))
     {
@@ -4051,38 +4309,8 @@ public class Jalview2XML
     }
     else
     {
-      StringTokenizer currentV = new StringTokenizer(supported, "."), fileV = new StringTokenizer(
-              version, ".");
-      while (currentV.hasMoreTokens() && fileV.hasMoreTokens())
-      {
-        // convert b to decimal to catch bugfix releases within a series
-        String curT = currentV.nextToken().toLowerCase().replace('b', '.');
-        String fileT = fileV.nextToken().toLowerCase().replace('b', '.');
-        try
-        {
-          if (Float.valueOf(curT) > Float.valueOf(fileT))
-          {
-            // current version is newer than the version that wrote the file
-            return false;
-          }
-        } catch (NumberFormatException nfe)
-        {
-          System.err
-                  .println("** WARNING: Version comparison failed for tokens ("
-                          + curT
-                          + ") and ("
-                          + fileT
-                          + ")\n** Current: '"
-                          + supported + "' and Version: '" + version + "'");
-        }
-      }
-      if (currentV.hasMoreElements())
-      {
-        // fileV has no minor version but identical series to current
-        return false;
-      }
+      return StringUtils.compareVersions(version, supported, "b") >= 0;
     }
-    return true;
   }
 
   Vector<JalviewStructureDisplayI> newStructureViewers = null;
@@ -4110,7 +4338,7 @@ public class Jalview2XML
   }
 
   AlignFrame loadViewport(String file, JSeq[] JSEQ,
-          List<SequenceI> hiddenSeqs, Alignment al,
+          List<SequenceI> hiddenSeqs, AlignmentI al,
           JalviewModelSequence jms, Viewport view, String uniqueSeqSetId,
           String viewId, List<JvAnnotRow> autoAlan)
   {
@@ -4126,6 +4354,12 @@ public class Jalview2XML
               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
     }
 
+    if (al.hasSeqrep())
+    {
+      af.getViewport().setColourByReferenceSeq(true);
+      af.getViewport().setDisplayReferenceSeq(true);
+    }
+
     af.viewport.setGatherViewsHere(view.getGatheredViews());
 
     if (view.getSequenceSetId() != null)
@@ -4152,25 +4386,25 @@ public class Jalview2XML
     {
       for (int s = 0; s < JSEQ.length; s++)
       {
-        jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
-
+        SequenceGroup hidden = new SequenceGroup();
+        boolean isRepresentative = false;
         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
         {
-          hidden.addSequence(
-                  al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
+          isRepresentative = true;
+          SequenceI sequenceToHide = al.getSequenceAt(JSEQ[s]
+                  .getHiddenSequences(r));
+          hidden.addSequence(sequenceToHide, false);
+          // remove from hiddenSeqs list so we don't try to hide it twice
+          hiddenSeqs.remove(sequenceToHide);
+        }
+        if (isRepresentative)
+        {
+          SequenceI representativeSequence = al.getSequenceAt(s);
+          hidden.addSequence(representativeSequence, false);
+          af.viewport.hideRepSequences(representativeSequence, hidden);
         }
-        af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
       }
 
-      // jalview.datamodel.SequenceI[] hseqs = new
-      // jalview.datamodel.SequenceI[hiddenSeqs
-      // .size()];
-      //
-      // for (int s = 0; s < hiddenSeqs.size(); s++)
-      // {
-      // hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
-      // }
-
       SequenceI[] hseqs = hiddenSeqs.toArray(new SequenceI[hiddenSeqs
               .size()]);
       af.viewport.hideSequence(hseqs);
@@ -4329,25 +4563,33 @@ public class Jalview2XML
       af.viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
       String[] renderOrder = new String[jms.getFeatureSettings()
               .getSettingCount()];
-      Hashtable featureGroups = new Hashtable();
-      Hashtable featureColours = new Hashtable();
-      Hashtable featureOrder = new Hashtable();
+      Map<String, FeatureColourI> featureColours = new Hashtable<String, FeatureColourI>();
+      Map<String, Float> featureOrder = new Hashtable<String, Float>();
 
       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
       {
         Setting setting = jms.getFeatureSettings().getSetting(fs);
         if (setting.hasMincolour())
         {
-          GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
-                  new java.awt.Color(setting.getMincolour()),
-                  new java.awt.Color(setting.getColour()),
-                  setting.getMin(), setting.getMax()) : new GraduatedColor(
-                  new java.awt.Color(setting.getMincolour()),
-                  new java.awt.Color(setting.getColour()), 0, 1);
+          FeatureColourI gc = setting.hasMin() ? new FeatureColour(
+                  new Color(setting.getMincolour()), new Color(
+                          setting.getColour()), setting.getMin(),
+                  setting.getMax()) : new FeatureColour(new Color(
+                  setting.getMincolour()), new Color(setting.getColour()),
+                  0, 1);
           if (setting.hasThreshold())
           {
-            gc.setThresh(setting.getThreshold());
-            gc.setThreshType(setting.getThreshstate());
+            gc.setThreshold(setting.getThreshold());
+            int threshstate = setting.getThreshstate();
+            // -1 = None, 0 = Below, 1 = Above threshold
+            if (threshstate == 0)
+            {
+              gc.setBelowThreshold(true);
+            }
+            else if (threshstate == 1)
+            {
+              gc.setAboveThreshold(true);
+            }
           }
           gc.setAutoScaled(true); // default
           if (setting.hasAutoScale())
@@ -4363,8 +4605,8 @@ public class Jalview2XML
         }
         else
         {
-          featureColours.put(setting.getType(),
-                  new java.awt.Color(setting.getColour()));
+          featureColours.put(setting.getType(), new FeatureColour(
+                  new Color(setting.getColour())));
         }
         renderOrder[fs] = setting.getType();
         if (setting.hasOrder())
@@ -4381,7 +4623,7 @@ public class Jalview2XML
           fdi.setVisible(setting.getType());
         }
       }
-      Hashtable fgtable = new Hashtable();
+      Map<String, Boolean> fgtable = new Hashtable<String, Boolean>();
       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
       {
         Group grp = jms.getFeatureSettings().getGroup(gs);
@@ -4424,7 +4666,7 @@ public class Jalview2XML
       }
     }
     af.setMenusFromViewport(af.viewport);
-
+    af.setTitle(view.getTitle());
     // TODO: we don't need to do this if the viewport is aready visible.
     /*
      * Add the AlignFrame to the desktop (it may be 'gathered' later), unless it
@@ -4449,7 +4691,7 @@ public class Jalview2XML
   }
 
   private ColourSchemeI constructAnnotationColour(
-          AnnotationColours viewAnnColour, AlignFrame af, Alignment al,
+          AnnotationColours viewAnnColour, AlignFrame af, AlignmentI al,
           JalviewModelSequence jms, boolean checkGroupAnnColour)
   {
     boolean propagateAnnColour = false;
@@ -4573,7 +4815,7 @@ public class Jalview2XML
     return cs;
   }
 
-  private void reorderAutoannotation(AlignFrame af, Alignment al,
+  private void reorderAutoannotation(AlignFrame af, AlignmentI al,
           List<JvAnnotRow> autoAlan)
   {
     // copy over visualization settings for autocalculated annotation in the
@@ -4728,10 +4970,11 @@ public class Jalview2XML
     }
   }
 
-  private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al,
+  private void recoverDatasetFor(SequenceSet vamsasSet, AlignmentI al,
           boolean ignoreUnrefed)
   {
-    jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
+    jalview.datamodel.AlignmentI ds = getDatasetFor(vamsasSet
+            .getDatasetId());
     Vector dseqs = null;
     if (ds == null)
     {
@@ -4741,7 +4984,7 @@ public class Jalview2XML
     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
     {
       Sequence vamsasSeq = vamsasSet.getSequence(i);
-      ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed);
+      ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed, i);
     }
     // create a new dataset
     if (ds == null)
@@ -4768,18 +5011,29 @@ public class Jalview2XML
    *          dataset alignment
    * @param dseqs
    *          vector to add new dataset sequence to
+   * @param ignoreUnrefed
+   *          - when true, don't create new sequences from vamsasSeq if it's id
+   *          doesn't already have an asssociated Jalview sequence.
+   * @param vseqpos
+   *          - used to reorder the sequence in the alignment according to the
+   *          vamsasSeq array ordering, to preserve ordering of dataset
    */
   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
-          AlignmentI ds, Vector dseqs, boolean ignoreUnrefed)
+          AlignmentI ds, Vector dseqs, boolean ignoreUnrefed, int vseqpos)
   {
     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
     // xRef Codon Maps
     SequenceI sq = seqRefIds.get(vamsasSeq.getId());
+    boolean reorder = false;
     SequenceI dsq = null;
     if (sq != null && sq.getDatasetSequence() != null)
     {
       dsq = sq.getDatasetSequence();
     }
+    else
+    {
+      reorder = true;
+    }
     if (sq == null && ignoreUnrefed)
     {
       return;
@@ -4875,21 +5129,50 @@ public class Jalview2XML
         // + (post ? "appended" : ""));
       }
     }
+    else
+    {
+      // sequence refs are identical. We may need to update the existing dataset
+      // alignment with this one, though.
+      if (ds != null && dseqs == null)
+      {
+        int opos = ds.findIndex(dsq);
+        SequenceI tseq = null;
+        if (opos != -1 && vseqpos != opos)
+        {
+          // remove from old position
+          ds.deleteSequence(dsq);
+        }
+        if (vseqpos < ds.getHeight())
+        {
+          if (vseqpos != opos)
+          {
+            // save sequence at destination position
+            tseq = ds.getSequenceAt(vseqpos);
+            ds.replaceSequenceAt(vseqpos, dsq);
+            ds.addSequence(tseq);
+          }
+        }
+        else
+        {
+          ds.addSequence(dsq);
+        }
+      }
+    }
   }
 
   /*
    * TODO use AlignmentI here and in related methods - needs
    * AlignmentI.getDataset() changed to return AlignmentI instead of Alignment
    */
-  Hashtable<String, Alignment> datasetIds = null;
+  Hashtable<String, AlignmentI> datasetIds = null;
 
-  IdentityHashMap<Alignment, String> dataset2Ids = null;
+  IdentityHashMap<AlignmentI, String> dataset2Ids = null;
 
-  private Alignment getDatasetFor(String datasetId)
+  private AlignmentI getDatasetFor(String datasetId)
   {
     if (datasetIds == null)
     {
-      datasetIds = new Hashtable<String, Alignment>();
+      datasetIds = new Hashtable<String, AlignmentI>();
       return null;
     }
     if (datasetIds.containsKey(datasetId))
@@ -4899,11 +5182,11 @@ public class Jalview2XML
     return null;
   }
 
-  private void addDatasetRef(String datasetId, Alignment dataset)
+  private void addDatasetRef(String datasetId, AlignmentI dataset)
   {
     if (datasetIds == null)
     {
-      datasetIds = new Hashtable<String, Alignment>();
+      datasetIds = new Hashtable<String, AlignmentI>();
     }
     datasetIds.put(datasetId, dataset);
   }
@@ -4914,7 +5197,7 @@ public class Jalview2XML
    * @param dataset
    * @return
    */
-  private String getDatasetIdRef(Alignment dataset)
+  private String getDatasetIdRef(AlignmentI dataset)
   {
     if (dataset.getDataset() != null)
     {
@@ -4926,7 +5209,7 @@ public class Jalview2XML
       // make a new datasetId and record it
       if (dataset2Ids == null)
       {
-        dataset2Ids = new IdentityHashMap<Alignment, String>();
+        dataset2Ids = new IdentityHashMap<AlignmentI, String>();
       }
       else
       {
@@ -4994,7 +5277,7 @@ public class Jalview2XML
         }
         else
         {
-          frefedSequence.add(new Object[] { dsfor, jmap });
+          frefedSequence.add(newMappingRef(dsfor, jmap));
         }
       }
       else
@@ -5032,6 +5315,7 @@ public class Jalview2XML
           djs.setEnd(jmap.getMap().getToHighest());
           djs.setVamsasId(uniqueSetSuffix + sqid);
           jmap.setTo(djs);
+          incompleteSeqs.put(sqid, djs);
           seqRefIds.put(sqid, djs);
 
         }
diff --git a/src/jalview/gui/Jalview2XML_V1.java b/src/jalview/gui/Jalview2XML_V1.java
index b9d4391..ee659f2 100644
--- a/src/jalview/gui/Jalview2XML_V1.java
+++ b/src/jalview/gui/Jalview2XML_V1.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,7 @@
  */
 package jalview.gui;
 
+import jalview.analysis.Conservation;
 import jalview.binding.Annotation;
 import jalview.binding.AnnotationElement;
 import jalview.binding.Features;
@@ -37,13 +38,14 @@ import jalview.binding.Viewport;
 import jalview.datamodel.PDBEntry;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
-import jalview.schemes.ResidueProperties;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 import jalview.util.jarInputStreamProvider;
 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
 
 import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.UnknownHostException;
 import java.util.Hashtable;
 import java.util.Vector;
 import java.util.jar.JarEntry;
@@ -51,8 +53,6 @@ import java.util.jar.JarInputStream;
 
 import javax.swing.JOptionPane;
 
-import org.exolab.castor.xml.IDResolver;
-
 /**
  * DOCUMENT ME!
  * 
@@ -127,33 +127,29 @@ public class Jalview2XML_V1
           jarentry = jin.getNextJarEntry();
         }
 
-        class NoDescIDResolver implements IDResolver
-        {
-          public Object resolve(String idref)
-          {
-            System.out.println(idref + " used");
-            return null;
-          }
-        }
-
         if (jarentry != null)
         {
-          InputStreamReader in = new InputStreamReader(jin, "UTF-8");
-          JalviewModel object = new JalviewModel();
+          entryCount++;
+          if (jarentry.getName().endsWith(".xml"))
+          {
+            Reader in = new InputStreamReader(jin, "UTF-8");
+            JalviewModel object = new JalviewModel();
 
-          object = object.unmarshal(in);
+            object = object.unmarshal(in);
 
-          af = LoadFromObject(object, file);
-          entryCount++;
+            af = LoadFromObject(object, file);
+          }
         }
+        jin.close();
       } while (jarentry != null);
-    } catch (final java.net.UnknownHostException ex)
+    } catch (final UnknownHostException ex)
     {
       ex.printStackTrace();
       if (raiseGUI)
       {
         javax.swing.SwingUtilities.invokeLater(new Runnable()
         {
+          @Override
           public void run()
           {
 
@@ -176,6 +172,7 @@ public class Jalview2XML_V1
       {
         javax.swing.SwingUtilities.invokeLater(new Runnable()
         {
+          @Override
           public void run()
           {
 
@@ -362,9 +359,8 @@ public class Jalview2XML_V1
 
         if (groups[i].getConsThreshold() != 0)
         {
-          jalview.analysis.Conservation c = new jalview.analysis.Conservation(
-                  "All", ResidueProperties.propHash, 3,
-                  sg.getSequences(null), 0, sg.getWidth() - 1);
+          Conservation c = new Conservation("All", sg.getSequences(null),
+                  0, sg.getWidth() - 1);
           c.calculate();
           c.verdict(false, 25);
           sg.cs.setConservation(c);
diff --git a/src/jalview/gui/JalviewAppender.java b/src/jalview/gui/JalviewAppender.java
index 99aa5b8..df30d38 100644
--- a/src/jalview/gui/JalviewAppender.java
+++ b/src/jalview/gui/JalviewAppender.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/JalviewChangeSupport.java b/src/jalview/gui/JalviewChangeSupport.java
index 63cb46c..6ef1375 100644
--- a/src/jalview/gui/JalviewChangeSupport.java
+++ b/src/jalview/gui/JalviewChangeSupport.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/JalviewChimeraBindingModel.java b/src/jalview/gui/JalviewChimeraBindingModel.java
index 4637f07..7f4cd02 100644
--- a/src/jalview/gui/JalviewChimeraBindingModel.java
+++ b/src/jalview/gui/JalviewChimeraBindingModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -32,11 +32,12 @@ public class JalviewChimeraBindingModel extends JalviewChimeraBinding
 
   private FeatureRenderer fr = null;
 
+
   public JalviewChimeraBindingModel(ChimeraViewFrame chimeraViewFrame,
           StructureSelectionManager ssm, PDBEntry[] pdbentry,
-          SequenceI[][] sequenceIs, String[][] chains, String protocol)
+          SequenceI[][] sequenceIs, String protocol)
   {
-    super(ssm, pdbentry, sequenceIs, chains, protocol);
+    super(ssm, pdbentry, sequenceIs, protocol);
     cvf = chimeraViewFrame;
   }
 
@@ -72,6 +73,7 @@ public class JalviewChimeraBindingModel extends JalviewChimeraBinding
   {
     javax.swing.SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         cvf.updateTitleAndMenus();
@@ -80,6 +82,7 @@ public class JalviewChimeraBindingModel extends JalviewChimeraBinding
     });
   }
 
+  @Override
   public void updateColours(Object source)
   {
     AlignmentPanel ap = (AlignmentPanel) source;
@@ -113,6 +116,7 @@ public class JalviewChimeraBindingModel extends JalviewChimeraBinding
    * Send an asynchronous command to Chimera, in a new thread, optionally with
    * an 'in progress' message in a progress bar somewhere
    */
+  @Override
   protected void sendAsynchronousCommand(final String command,
           final String progressMsg)
   {
@@ -135,4 +139,6 @@ public class JalviewChimeraBindingModel extends JalviewChimeraBinding
     thread.start();
 
   }
+
+
 }
diff --git a/src/jalview/gui/JalviewDialog.java b/src/jalview/gui/JalviewDialog.java
index 895abcb..d455c3b 100644
--- a/src/jalview/gui/JalviewDialog.java
+++ b/src/jalview/gui/JalviewDialog.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -59,6 +59,7 @@ public abstract class JalviewDialog extends JPanel
       new Thread(new Runnable()
       {
 
+        @Override
         public void run()
         {
           frame.setVisible(true);
@@ -95,6 +96,7 @@ public abstract class JalviewDialog extends JPanel
     ok.setText(MessageManager.getString("action.ok"));
     ok.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         okPressed();
@@ -105,6 +107,7 @@ public abstract class JalviewDialog extends JPanel
     cancel.setText(MessageManager.getString("action.cancel"));
     cancel.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         cancelPressed();
diff --git a/src/jalview/gui/JvSwingUtils.java b/src/jalview/gui/JvSwingUtils.java
index 7392488..ea7bf2a 100644
--- a/src/jalview/gui/JvSwingUtils.java
+++ b/src/jalview/gui/JvSwingUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/MenuChooser.java b/src/jalview/gui/MenuChooser.java
index 4810279..8e63eaa 100644
--- a/src/jalview/gui/MenuChooser.java
+++ b/src/jalview/gui/MenuChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/OOMWarning.java b/src/jalview/gui/OOMWarning.java
index e37dd07..ddfa9a2 100644
--- a/src/jalview/gui/OOMWarning.java
+++ b/src/jalview/gui/OOMWarning.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/OptsAndParamsPage.java b/src/jalview/gui/OptsAndParamsPage.java
index d2efc7e..2217718 100644
--- a/src/jalview/gui/OptsAndParamsPage.java
+++ b/src/jalview/gui/OptsAndParamsPage.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -114,7 +114,7 @@ public class OptsAndParamsPage
                 .wrapTooltip(
                         true,
                         ((desc == null || desc.trim().length() == 0) ? MessageManager
-                                .getString("label.opt_and_params_further_details ")
+                                .getString("label.opt_and_params_further_details")
                                 : desc)
                                 + "<br><img src=\"" + linkImageURL + "\"/>"));
         enabled.addMouseListener(this);
@@ -130,9 +130,9 @@ public class OptsAndParamsPage
       add(enabled, BorderLayout.NORTH);
       for (Object str : opt.getPossibleValues())
       {
-        val.addItem((String) str);
+        val.addItem(str);
       }
-      val.setSelectedItem((String) opt.getValue());
+      val.setSelectedItem(opt.getValue());
       if (opt.getPossibleValues().size() > 1)
       {
         setLayout(new GridLayout(1, 2));
@@ -145,6 +145,7 @@ public class OptsAndParamsPage
       setInitialValue();
     }
 
+    @Override
     public void actionPerformed(ActionEvent e)
     {
       if (e.getSource() != enabled)
@@ -206,36 +207,41 @@ public class OptsAndParamsPage
       return opt;
     }
 
+    @Override
     public void mouseClicked(MouseEvent e)
     {
-      if (javax.swing.SwingUtilities.isRightMouseButton(e))
+      if (e.isPopupTrigger()) // for Windows
       {
         showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
       }
     }
 
+    @Override
     public void mouseEntered(MouseEvent e)
     {
       // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void mouseExited(MouseEvent e)
     {
       // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void mousePressed(MouseEvent e)
     {
-      // TODO Auto-generated method stub
-
+      if (e.isPopupTrigger()) // Mac
+      {
+        showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
+      }
     }
 
+    @Override
     public void mouseReleased(MouseEvent e)
     {
-      // TODO Auto-generated method stub
-
     }
 
     public void resetToDefault(boolean setDefaultParams)
@@ -351,7 +357,7 @@ public class OptsAndParamsPage
                                         + linkImageURL
                                         + "\"/>"
                                         + MessageManager
-                                                .getString("label.opt_and_params_further_detail")
+                                                .getString("label.opt_and_params_further_details")
                                         : "")));
       }
 
@@ -412,6 +418,7 @@ public class OptsAndParamsPage
         showDesc.addActionListener(new ActionListener()
         {
 
+          @Override
           public void actionPerformed(ActionEvent e)
           {
             descisvisible = !descisvisible;
@@ -451,6 +458,7 @@ public class OptsAndParamsPage
       validate();
     }
 
+    @Override
     public void actionPerformed(ActionEvent e)
     {
       if (adjusting)
@@ -526,45 +534,53 @@ public class OptsAndParamsPage
       lastVal = null;
     }
 
+    @Override
     public void mouseClicked(MouseEvent e)
     {
-      if (javax.swing.SwingUtilities.isRightMouseButton(e))
+      if (e.isPopupTrigger()) // for Windows
       {
         showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
       }
     }
 
+    @Override
     public void mouseEntered(MouseEvent e)
     {
       // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void mouseExited(MouseEvent e)
     {
       // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void mousePressed(MouseEvent e)
     {
-      // TODO Auto-generated method stub
-
+      if (e.isPopupTrigger()) // for Mac
+      {
+        showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
+      }
     }
 
+    @Override
     public void mouseReleased(MouseEvent e)
     {
       // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void stateChanged(ChangeEvent e)
     {
       if (!adjusting)
       {
         valueField.setText(""
-                + ((integ) ? ("" + (int) slider.getValue())
-                        : ("" + (float) (slider.getValue() / 1000f))));
+                + ((integ) ? ("" + slider.getValue()) : ("" + slider
+                        .getValue() / 1000f)));
         checkIfModified();
       }
 
diff --git a/src/jalview/gui/OptsParametersContainerI.java b/src/jalview/gui/OptsParametersContainerI.java
index 16b57e6..ce47a41 100644
--- a/src/jalview/gui/OptsParametersContainerI.java
+++ b/src/jalview/gui/OptsParametersContainerI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/OverviewPanel.java b/src/jalview/gui/OverviewPanel.java
index 9397ceb..0fdb560 100644
--- a/src/jalview/gui/OverviewPanel.java
+++ b/src/jalview/gui/OverviewPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -295,14 +295,25 @@ public class OverviewPanel extends JPanel implements Runnable
     final boolean hasHiddenRows = av.hasHiddenRows(), hasHiddenCols = av
             .hasHiddenColumns();
     boolean hiddenRow = false;
+    // get hidden row and hidden column map once at beginning.
+    // clone featureRenderer settings to avoid race conditions... if state is
+    // updated just need to refresh again
     for (row = 0; row < sequencesHeight; row++)
     {
+      if (resizeAgain)
+      {
+        break;
+      }
       if ((int) (row * sampleRow) == lastrow)
       {
         // No need to recalculate the colours,
         // Just copy from the row above
         for (col = 0; col < width; col++)
         {
+          if (resizeAgain)
+          {
+            break;
+          }
           miniMe.setRGB(col, row, miniMe.getRGB(col, row - 1));
         }
         continue;
@@ -340,6 +351,10 @@ public class OverviewPanel extends JPanel implements Runnable
 
       for (col = 0; col < width; col++)
       {
+        if (resizeAgain)
+        {
+          break;
+        }
         if ((int) (col * sampleCol) == lastcol
                 && (int) (row * sampleRow) == lastrow)
         {
@@ -380,6 +395,10 @@ public class OverviewPanel extends JPanel implements Runnable
       renderer.updateFromAlignViewport(av);
       for (col = 0; col < width; col++)
       {
+        if (resizeAgain)
+        {
+          break;
+        }
         lastcol = (int) (col * sampleCol);
         {
           mg.translate(col, sequencesHeight);
@@ -395,13 +414,17 @@ public class OverviewPanel extends JPanel implements Runnable
 
     resizing = false;
 
-    setBoxPosition();
-
     if (resizeAgain)
     {
       resizeAgain = false;
       updateOverviewImage();
     }
+    else
+    {
+      lastMiniMe = miniMe;
+    }
+
+    setBoxPosition();
   }
 
   /**
@@ -456,6 +479,8 @@ public class OverviewPanel extends JPanel implements Runnable
     repaint();
   }
 
+  private BufferedImage lastMiniMe = null;
+
   /**
    * DOCUMENT ME!
    * 
@@ -465,19 +490,32 @@ public class OverviewPanel extends JPanel implements Runnable
   @Override
   public void paintComponent(Graphics g)
   {
-    if (resizing)
+    if (resizing || resizeAgain)
     {
-      g.setColor(Color.white);
+      if (lastMiniMe == null)
+      {
+        g.setColor(Color.white);
+        g.fillRect(0, 0, getWidth(), getHeight());
+      }
+      else
+      {
+        g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this);
+      }
+      g.setColor(new Color(100, 100, 100, 25));
       g.fillRect(0, 0, getWidth(), getHeight());
     }
-    else if (miniMe != null)
+    else if (lastMiniMe != null)
     {
-      g.drawImage(miniMe, 0, 0, this);
+      g.drawImage(lastMiniMe, 0, 0, this);
+      if (lastMiniMe != miniMe)
+      {
+        g.setColor(new Color(100, 100, 100, 25));
+        g.fillRect(0, 0, getWidth(), getHeight());
+      }
     }
-
+    // TODO: render selected regions
     g.setColor(Color.red);
     g.drawRect(boxX, boxY, boxWidth, boxHeight);
     g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2);
-
   }
 }
diff --git a/src/jalview/gui/PCAPanel.java b/src/jalview/gui/PCAPanel.java
index 57a14a0..b9d0a2e 100644
--- a/src/jalview/gui/PCAPanel.java
+++ b/src/jalview/gui/PCAPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,6 +21,7 @@
 package jalview.gui;
 
 import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SeqCigar;
@@ -139,8 +140,8 @@ public class PCAPanel extends GPCAPanel implements Runnable,
       {
         // create an entry for this score matrix for use in PCA
         JCheckBoxMenuItem jm = new JCheckBoxMenuItem();
-        jm.setText(MessageManager
-                .getStringOrReturn("label.score_model", sm));
+        jm.setText(MessageManager.getStringOrReturn("label.score_model_",
+                sm));
         jm.setSelected(pcaModel.getScore_matrix().equals(sm));
         if ((ResidueProperties.scoreMatrices.get(sm).isDNA() && ResidueProperties.scoreMatrices
                 .get(sm).isProtein())
@@ -167,6 +168,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
     }
   }
 
+  @Override
   public void bgcolour_actionPerformed(ActionEvent e)
   {
     Color col = JColorChooser.showDialog(this,
@@ -183,6 +185,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
   /**
    * DOCUMENT ME!
    */
+  @Override
   public void run()
   {
     long progId = System.currentTimeMillis();
@@ -288,6 +291,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   protected void xCombobox_actionPerformed(ActionEvent e)
   {
     doDimensionChange();
@@ -299,6 +303,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   protected void yCombobox_actionPerformed(ActionEvent e)
   {
     doDimensionChange();
@@ -310,11 +315,13 @@ public class PCAPanel extends GPCAPanel implements Runnable,
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   protected void zCombobox_actionPerformed(ActionEvent e)
   {
     doDimensionChange();
   }
 
+  @Override
   public void outputValues_actionPerformed(ActionEvent e)
   {
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
@@ -330,17 +337,20 @@ public class PCAPanel extends GPCAPanel implements Runnable,
     }
   }
 
+  @Override
   public void showLabels_actionPerformed(ActionEvent e)
   {
     rc.showLabels(showLabels.getState());
   }
 
+  @Override
   public void print_actionPerformed(ActionEvent e)
   {
     PCAPrinter printer = new PCAPrinter();
     printer.start();
   }
 
+  @Override
   public void originalSeqData_actionPerformed(ActionEvent e)
   {
     // this was cut'n'pasted from the equivalent TreePanel method - we should
@@ -374,8 +384,8 @@ public class PCAPanel extends GPCAPanel implements Runnable,
     {
       // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
 
-      Alignment al = new Alignment((SequenceI[]) alAndColsel[0]);
-      Alignment dataset = (av != null && av.getAlignment() != null) ? av
+      AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
+      AlignmentI dataset = (av != null && av.getAlignment() != null) ? av
               .getAlignment().getDataset() : null;
       if (dataset != null)
       {
@@ -414,6 +424,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
 
   class PCAPrinter extends Thread implements Printable
   {
+    @Override
     public void run()
     {
       PrinterJob printJob = PrinterJob.getPrinterJob();
@@ -433,6 +444,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
       }
     }
 
+    @Override
     public int print(Graphics pg, PageFormat pf, int pi)
             throws PrinterException
     {
@@ -462,6 +474,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void eps_actionPerformed(ActionEvent e)
   {
     makePCAImage(jalview.util.ImageMaker.TYPE.EPS);
@@ -473,6 +486,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void png_actionPerformed(ActionEvent e)
   {
     makePCAImage(jalview.util.ImageMaker.TYPE.PNG);
@@ -489,19 +503,19 @@ public class PCAPanel extends GPCAPanel implements Runnable,
     {
       im = new jalview.util.ImageMaker(this,
               jalview.util.ImageMaker.TYPE.PNG, "Make PNG image from PCA",
-              width, height, null, null);
+              width, height, null, null, null, 0, false);
     }
     else if (type == jalview.util.ImageMaker.TYPE.EPS)
     {
       im = new jalview.util.ImageMaker(this,
               jalview.util.ImageMaker.TYPE.EPS, "Make EPS file from PCA",
-              width, height, null, this.getTitle());
+              width, height, null, this.getTitle(), null, 0, false);
     }
     else
     {
       im = new jalview.util.ImageMaker(this,
               jalview.util.ImageMaker.TYPE.SVG, "Make SVG file from PCA",
-              width, height, null, this.getTitle());
+              width, height, null, this.getTitle(), null, 0, false);
 
     }
 
@@ -517,6 +531,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
     }
   }
 
+  @Override
   public void viewMenu_menuSelected()
   {
     buildAssociatedViewMenu();
@@ -552,6 +567,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
       buttonGroup.add(item);
       item.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent evt)
         {
           rc.applyToAllViews = false;
@@ -571,6 +587,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
     itemf.setSelected(rc.applyToAllViews);
     itemf.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent evt)
       {
         rc.applyToAllViews = itemf.isSelected();
@@ -587,6 +604,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
    * jalview.jbgui.GPCAPanel#outputPoints_actionPerformed(java.awt.event.ActionEvent
    * )
    */
+  @Override
   protected void outputPoints_actionPerformed(ActionEvent e)
   {
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
@@ -612,6 +630,7 @@ public class PCAPanel extends GPCAPanel implements Runnable,
    * jalview.jbgui.GPCAPanel#outputProjPoints_actionPerformed(java.awt.event
    * .ActionEvent)
    */
+  @Override
   protected void outputProjPoints_actionPerformed(ActionEvent e)
   {
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
diff --git a/src/jalview/gui/PDBSearchPanel.java b/src/jalview/gui/PDBSearchPanel.java
deleted file mode 100644
index 4c89f12..0000000
--- a/src/jalview/gui/PDBSearchPanel.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-
-package jalview.gui;
-
-import jalview.jbgui.GPDBSearchPanel;
-import jalview.jbgui.PDBDocFieldPreferences;
-import jalview.util.MessageManager;
-import jalview.ws.dbsources.PDBRestClient;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
-import jalview.ws.uimodel.PDBRestRequest;
-import jalview.ws.uimodel.PDBRestResponse;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-
-import javax.swing.table.DefaultTableModel;
-
- at SuppressWarnings("serial")
-public class PDBSearchPanel extends GPDBSearchPanel
-{
-  private SequenceFetcher seqFetcher;
-
-  private IProgressIndicator progressIdicator;
-
-  private Collection<PDBDocField> wantedFields;
-
-  public PDBSearchPanel(SequenceFetcher seqFetcher)
-  {
-    this.seqFetcher = seqFetcher;
-    this.progressIdicator = (seqFetcher == null) ? null : seqFetcher
-            .getProgressIndicator();
-  }
-
-  /**
-   * Action performed when an input is detected on txt_search field.
-   */
-  @Override
-  public void txt_search_ActionPerformed()
-  {
-    new Thread()
-    {
-      public void run()
-      {
-        lbl_loading.setVisible(false);
-        errorWarning.setLength(0);
-        lbl_warning.setVisible(false);
-        btn_ok.setEnabled(false);
-        boolean allowEmptySequence = false;
-        mainFrame.setTitle(MessageManager
-                .getString("label.pdb_sequence_getcher"));
-        tbl_summary.setModel(new DefaultTableModel());
-        if (txt_search.getText().trim().length() > 0)
-        {
-          lbl_loading.setVisible(true);
-          long startTime = System.currentTimeMillis();
-
-          String searchTarget = ((PDBDocField) cmb_searchTarget
-                  .getSelectedItem()).getCode();
-
-          wantedFields = PDBDocFieldPreferences.getSearchSummaryFields();
-
-          String searchTerm = decodeSearchTerm(txt_search.getText(),
-                  searchTarget);
-
-          PDBRestRequest request = new PDBRestRequest();
-          request.setAllowEmptySeq(allowEmptySequence);
-          request.setResponseSize(100);
-          request.setFieldToSearchBy("(" + searchTarget + ":");
-          request.setSearchTerm(searchTerm + ")");
-          request.setWantedFields(wantedFields);
-          // System.out.println(">>>>>>>>>>>>>> " + request.getQuery());
-          PDBRestClient pdbRestCleint = new PDBRestClient();
-          PDBRestResponse resultList;
-          try
-          {
-            resultList = pdbRestCleint.executeRequest(request);
-          } catch (Exception e)
-          {
-            // e.printStackTrace();
-            errorWarning.append(e.getMessage());
-            checkForErrors();
-            return;
-          }
-
-          if (resultList.getSearchSummary() != null)
-          {
-            tbl_summary.setModel(PDBRestResponse.getTableModel(request,
-                    resultList.getSearchSummary()));
-          }
-
-          long endTime = System.currentTimeMillis();
-          int resultSetCount = resultList.getNumberOfItemsFound();
-          String result = (resultSetCount > 1) ? MessageManager
-                  .getString("label.results") : MessageManager
-                  .getString("label.result");
-          mainFrame.setTitle(frameTitle + " - " + resultSetCount + " "
-                  + result + " (" + (endTime - startTime) + " milli secs)");
-          lbl_loading.setVisible(false);
-        }
-      }
-    }.start();
-  }
-
-  public static String decodeSearchTerm(String enteredText,
-          String targetField)
-  {
-    String foundSearchTerms = enteredText;
-    StringBuilder foundSearchTermsBuilder = new StringBuilder();
-    if (enteredText.contains(";"))
-    {
-      String[] searchTerms = enteredText.split(";");
-      for (String searchTerm : searchTerms)
-      {
-        if (searchTerm.contains(":"))
-        {
-          foundSearchTermsBuilder.append(targetField).append(":")
-                  .append(searchTerm.split(":")[0]).append(" OR ");
-        }
-        else
-        {
-          foundSearchTermsBuilder.append(targetField).append(":")
-                  .append(searchTerm).append(" OR ");
-        }
-      }
-      int endIndex = foundSearchTermsBuilder.lastIndexOf(" OR ");
-      foundSearchTerms = foundSearchTermsBuilder.toString();
-      if (foundSearchTerms.contains(" OR "))
-      {
-        foundSearchTerms = foundSearchTerms.substring(
-                targetField.length() + 1, endIndex);
-      }
-    }
-    else if (enteredText.contains(":"))
-    {
-      foundSearchTerms = foundSearchTerms.split(":")[0];
-    }
-    return foundSearchTerms;
-  }
-
-  @Override
-  public void btn_ok_ActionPerformed()
-  {
-    loadSelectedPDBSequencesToAlignment();
-  }
-
-  @Override
-  public void btn_back_ActionPerformed()
-  {
-    mainFrame.dispose();
-    new SequenceFetcher(progressIdicator);
-  }
-
-  @Override
-  public void btn_cancel_ActionPerformed()
-  {
-    mainFrame.dispose();
-  }
-
-  public void transferToSequenceFetcher(String ids)
-  {
-    // mainFrame.dispose();
-    seqFetcher.textArea.setText(ids);
-    Thread worker = new Thread(seqFetcher);
-    worker.start();
-  }
-
-  /**
-   * Add the discovered/selected sequences to a target alignment window
-   */
-  public void loadSelectedPDBSequencesToAlignment()
-  {
-    // mainFrame.dispose();
-    disableActionButtons();
-    StringBuilder selectedIds = new StringBuilder();
-    HashSet<String> selectedIdsSet = new HashSet<String>();
-    int pdbIdCol = PDBRestClient.getPDBIdColumIndex(wantedFields, false);
-    int[] selectedRows = tbl_summary.getSelectedRows();
-    for (int summaryRow : selectedRows)
-    {
-      String pdbIdStr = tbl_summary.getValueAt(summaryRow, pdbIdCol)
-              .toString();
-      String searchTerm = txt_search.getText();
-      selectedIdsSet.add(getPDBIdwithSpecifiedChain(pdbIdStr, searchTerm));
-    }
-
-    for (String selectedId : selectedIdsSet)
-    {
-      selectedIds.append(selectedId).append(";");
-    }
-
-    String ids = selectedIds.toString();
-    // System.out.println(">>>>>>>>>>>>>>>> selected Ids: " + ids);
-    seqFetcher.textArea.setText(ids);
-    Thread worker = new Thread(seqFetcher);
-    worker.start();
-    delayAndEnableActionButtons();
-
-  }
-
-  private void disableActionButtons()
-  {
-    btn_ok.setEnabled(false);
-    btn_back.setEnabled(false);
-    btn_cancel.setEnabled(false);
-  }
-
-  private void delayAndEnableActionButtons()
-  {
-    new Thread()
-    {
-      public void run()
-      {
-        try
-        {
-          Thread.sleep(1500);
-        } catch (InterruptedException e)
-        {
-          e.printStackTrace();
-        }
-        btn_ok.setEnabled(true);
-        btn_back.setEnabled(true);
-        btn_cancel.setEnabled(true);
-      }
-    }.start();
-  }
-
-  public static String getPDBIdwithSpecifiedChain(String pdbId,
-          String searchTerm)
-  {
-    String pdbIdWithChainCode = "";
-    if (searchTerm.contains(";"))
-    {
-      String[] foundTerms = searchTerm.split(";");
-      for (String foundTerm : foundTerms)
-      {
-        if (foundTerm.contains(pdbId))
-        {
-          pdbIdWithChainCode = foundTerm;
-        }
-      }
-    }
-    else if (searchTerm.contains(pdbId))
-    {
-      pdbIdWithChainCode = searchTerm;
-    }
-    else
-    {
-      pdbIdWithChainCode = pdbId;
-    }
-    return pdbIdWithChainCode;
-  }
-
-  /**
-   * Populates search target combo-box options
-   */
-  public void populateCmbSearchTargetOptions()
-  {
-    List<PDBDocField> searchableTargets = new ArrayList<PDBDocField>();
-    searchableTargets.add(PDBDocField.PDB_ID);
-    searchableTargets.add(PDBDocField.PFAM_ACCESSION);
-    searchableTargets.add(PDBDocField.MOLECULE_TYPE);
-    searchableTargets.add(PDBDocField.MOLECULE_NAME);
-    searchableTargets.add(PDBDocField.UNIPROT_ACCESSION);
-    searchableTargets.add(PDBDocField.GENE_NAME);
-    searchableTargets.add(PDBDocField.GENUS);
-    searchableTargets.add(PDBDocField.ALL);
-
-    Collections.sort(searchableTargets, new Comparator<PDBDocField>()
-    {
-      @Override
-      public int compare(PDBDocField o1, PDBDocField o2)
-      {
-        return o1.getName().compareTo(o2.getName());
-      }
-    });
-
-    for (PDBDocField searchTarget : searchableTargets)
-    {
-      cmb_searchTarget.addItem(searchTarget);
-    }
-  }
-
-  public void checkForErrors()
-  {
-    lbl_warning.setVisible(false);
-    if (errorWarning.length() > 0)
-    {
-      lbl_loading.setVisible(false);
-      lbl_warning.setToolTipText(JvSwingUtils.wrapTooltip(true,
-              errorWarning.toString()));
-      lbl_warning.setVisible(true);
-    }
-  }
-}
diff --git a/src/jalview/gui/PaintRefresher.java b/src/jalview/gui/PaintRefresher.java
index 1b90d82..f800737 100644
--- a/src/jalview/gui/PaintRefresher.java
+++ b/src/jalview/gui/PaintRefresher.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/PairwiseAlignPanel.java b/src/jalview/gui/PairwiseAlignPanel.java
index 294bb9d..a1d08db 100644
--- a/src/jalview/gui/PairwiseAlignPanel.java
+++ b/src/jalview/gui/PairwiseAlignPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java
index d344c0c..44a7178 100644
--- a/src/jalview/gui/PopupMenu.java
+++ b/src/jalview/gui/PopupMenu.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -24,6 +24,7 @@ import jalview.analysis.AAFrequency;
 import jalview.analysis.AlignmentAnnotationUtils;
 import jalview.analysis.AlignmentUtils;
 import jalview.analysis.Conservation;
+import jalview.bin.Cache;
 import jalview.commands.ChangeCaseCommand;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
@@ -48,7 +49,6 @@ import jalview.schemes.HydrophobicColourScheme;
 import jalview.schemes.NucleotideColourScheme;
 import jalview.schemes.PIDColourScheme;
 import jalview.schemes.PurinePyrimidineColourScheme;
-import jalview.schemes.ResidueProperties;
 import jalview.schemes.StrandColourScheme;
 import jalview.schemes.TaylorColourScheme;
 import jalview.schemes.TurnColourScheme;
@@ -63,6 +63,7 @@ import java.awt.Color;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Hashtable;
 import java.util.LinkedHashMap;
@@ -88,10 +89,6 @@ import javax.swing.JRadioButtonMenuItem;
  */
 public class PopupMenu extends JPopupMenu
 {
-  private static final String ALL_ANNOTATIONS = "All";
-
-  private static final String COMMA = ",";
-
   JMenu groupMenu = new JMenu();
 
   JMenuItem groupName = new JMenuItem();
@@ -176,12 +173,6 @@ public class PopupMenu extends JPopupMenu
 
   JMenu pdbMenu = new JMenu();
 
-  JMenuItem pdbFromFile = new JMenuItem();
-
-  JMenuItem enterPDB = new JMenuItem();
-
-  JMenuItem discoverPDB = new JMenuItem();
-
   JMenu outputMenu = new JMenu();
 
   JMenu seqShowAnnotationsMenu = new JMenu();
@@ -222,7 +213,7 @@ public class PopupMenu extends JPopupMenu
    * @param seq
    *          DOCUMENT ME!
    */
-  public PopupMenu(final AlignmentPanel ap, Sequence seq, Vector links)
+  public PopupMenu(final AlignmentPanel ap, Sequence seq, List<String> links)
   {
     this(ap, seq, links, null);
   }
@@ -235,7 +226,7 @@ public class PopupMenu extends JPopupMenu
    * @param groupLinks
    */
   public PopupMenu(final AlignmentPanel ap, final SequenceI seq,
-          Vector links, Vector groupLinks)
+          List<String> links, List<String> groupLinks)
   {
     // /////////////////////////////////////////////////////////
     // If this is activated from the sequence panel, the user may want to
@@ -483,8 +474,6 @@ public class PopupMenu extends JPopupMenu
 
     if (sg != null && sg.getSize() > 0)
     {
-      groupName.setText(MessageManager.formatMessage("label.name_param",
-              new Object[] { sg.getName() }));
       groupName.setText(MessageManager
               .getString("label.edit_name_and_description_current_group"));
 
@@ -614,126 +603,67 @@ public class PopupMenu extends JPopupMenu
 
     if (links != null && links.size() > 0)
     {
+      addFeatureLinks(seq, links);
+    }
+  }
 
-      JMenu linkMenu = new JMenu(MessageManager.getString("action.link"));
-      Vector linkset = new Vector();
-      for (int i = 0; i < links.size(); i++)
-      {
-        String link = links.elementAt(i).toString();
-        UrlLink urlLink = null;
-        try
-        {
-          urlLink = new UrlLink(link);
-        } catch (Exception foo)
-        {
-          jalview.bin.Cache.log.error("Exception for URLLink '" + link
-                  + "'", foo);
-          continue;
-        }
-        ;
-        if (!urlLink.isValid())
-        {
-          jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
-          continue;
-        }
-        final String label = urlLink.getLabel();
-        if (seq != null && urlLink.isDynamic())
-        {
-
-          // collect matching db-refs
-          DBRefEntry[] dbr = jalview.util.DBRefUtils.selectRefs(
-                  seq.getDBRef(), new String[] { urlLink.getTarget() });
-          // collect id string too
-          String id = seq.getName();
-          String descr = seq.getDescription();
-          if (descr != null && descr.length() < 1)
-          {
-            descr = null;
-          }
+  /**
+   * Adds a 'Link' menu item with a sub-menu item for each hyperlink provided.
+   * 
+   * @param seq
+   * @param links
+   */
+  void addFeatureLinks(final SequenceI seq, List<String> links)
+  {
+    JMenu linkMenu = new JMenu(MessageManager.getString("action.link"));
+    Map<String, List<String>> linkset = new LinkedHashMap<String, List<String>>();
 
-          if (dbr != null)
-          {
-            for (int r = 0; r < dbr.length; r++)
-            {
-              if (id != null && dbr[r].getAccessionId().equals(id))
-              {
-                // suppress duplicate link creation for the bare sequence ID
-                // string with this link
-                id = null;
-              }
-              // create Bare ID link for this RUL
-              String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(),
-                      true);
-              if (urls != null)
-              {
-                for (int u = 0; u < urls.length; u += 2)
-                {
-                  if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
-                  {
-                    linkset.addElement(urls[u] + "|" + urls[u + 1]);
-                    addshowLink(linkMenu, label + "|" + urls[u],
-                            urls[u + 1]);
-                  }
-                }
-              }
-            }
-          }
-          if (id != null)
-          {
-            // create Bare ID link for this RUL
-            String[] urls = urlLink.makeUrls(id, true);
-            if (urls != null)
-            {
-              for (int u = 0; u < urls.length; u += 2)
-              {
-                if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
-                {
-                  linkset.addElement(urls[u] + "|" + urls[u + 1]);
-                  addshowLink(linkMenu, label, urls[u + 1]);
-                }
-              }
-            }
-          }
-          // Create urls from description but only for URL links which are regex
-          // links
-          if (descr != null && urlLink.getRegexReplace() != null)
-          {
-            // create link for this URL from description where regex matches
-            String[] urls = urlLink.makeUrls(descr, true);
-            if (urls != null)
-            {
-              for (int u = 0; u < urls.length; u += 2)
-              {
-                if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
-                {
-                  linkset.addElement(urls[u] + "|" + urls[u + 1]);
-                  addshowLink(linkMenu, label, urls[u + 1]);
-                }
-              }
-            }
-          }
-        }
-        else
-        {
-          if (!linkset.contains(label + "|" + urlLink.getUrl_prefix()))
-          {
-            linkset.addElement(label + "|" + urlLink.getUrl_prefix());
-            // Add a non-dynamic link
-            addshowLink(linkMenu, label, urlLink.getUrl_prefix());
-          }
-        }
-      }
-      if (sequence != null)
+    for (String link : links)
+    {
+      UrlLink urlLink = null;
+      try
+      {
+        urlLink = new UrlLink(link);
+      } catch (Exception foo)
       {
-        sequenceMenu.add(linkMenu);
+        Cache.log.error("Exception for URLLink '" + link + "'", foo);
+        continue;
       }
-      else
+
+      if (!urlLink.isValid())
       {
-        add(linkMenu);
+        Cache.log.error(urlLink.getInvalidMessage());
+        continue;
       }
+
+      urlLink.createLinksFromSeq(seq, linkset);
+    }
+
+    addshowLinks(linkMenu, linkset.values());
+
+    // disable link menu if there are no valid entries
+    if (linkMenu.getItemCount() > 0)
+    {
+      linkMenu.setEnabled(true);
+    }
+    else
+    {
+      linkMenu.setEnabled(false);
     }
+
+    if (sequence != null)
+    {
+      sequenceMenu.add(linkMenu);
+    }
+    else
+    {
+      add(linkMenu);
+    }
+
   }
 
+
+
   /**
    * Add annotation types to 'Show annotations' and/or 'Hide annotations' menus.
    * "All" is added first, followed by a separator. Then add any annotation
@@ -755,7 +685,8 @@ public class PopupMenu extends JPopupMenu
     showMenu.removeAll();
     hideMenu.removeAll();
 
-    final List<String> all = Arrays.asList(ALL_ANNOTATIONS);
+    final List<String> all = Arrays.asList(new String[] { MessageManager
+            .getString("label.all") });
     addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true, true);
     addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
             false);
@@ -861,7 +792,7 @@ public class PopupMenu extends JPopupMenu
     showOrHideMenu.add(item);
   }
 
-  private void buildGroupURLMenu(SequenceGroup sg, Vector groupLinks)
+  private void buildGroupURLMenu(SequenceGroup sg, List<String> groupLinks)
   {
 
     // TODO: usability: thread off the generation of group url content so root
@@ -870,19 +801,15 @@ public class PopupMenu extends JPopupMenu
     // ID/regex match URLs
     groupLinksMenu = new JMenu(
             MessageManager.getString("action.group_link"));
+    // three types of url that might be created.
     JMenu[] linkMenus = new JMenu[] { null,
         new JMenu(MessageManager.getString("action.ids")),
         new JMenu(MessageManager.getString("action.sequences")),
-        new JMenu(MessageManager.getString("action.ids_sequences")) }; // three
-                                                                       // types
-                                                                       // of url
-                                                                       // that
-                                                                       // might
-                                                                       // be
-    // created.
+        new JMenu(MessageManager.getString("action.ids_sequences")) };
+
     SequenceI[] seqs = ap.av.getSelectionAsNewSequence();
     String[][] idandseqs = GroupUrlLink.formStrings(seqs);
-    Hashtable commonDbrefs = new Hashtable();
+    Hashtable<String, Object[]> commonDbrefs = new Hashtable<String, Object[]>();
     for (int sq = 0; sq < seqs.length; sq++)
     {
 
@@ -896,13 +823,13 @@ public class PopupMenu extends JPopupMenu
       {
         sqi = sqi.getDatasetSequence();
       }
-      DBRefEntry[] dbr = sqi.getDBRef();
+      DBRefEntry[] dbr = sqi.getDBRefs();
       if (dbr != null && dbr.length > 0)
       {
         for (int d = 0; d < dbr.length; d++)
         {
           String src = dbr[d].getSource(); // jalview.util.DBRefUtils.getCanonicalName(dbr[d].getSource()).toUpperCase();
-          Object[] sarray = (Object[]) commonDbrefs.get(src);
+          Object[] sarray = commonDbrefs.get(src);
           if (sarray == null)
           {
             sarray = new Object[2];
@@ -927,30 +854,28 @@ public class PopupMenu extends JPopupMenu
     // now create group links for all distinct ID/sequence sets.
     boolean addMenu = false; // indicates if there are any group links to give
                              // to user
-    for (int i = 0; i < groupLinks.size(); i++)
+    for (String link : groupLinks)
     {
-      String link = groupLinks.elementAt(i).toString();
       GroupUrlLink urlLink = null;
       try
       {
         urlLink = new GroupUrlLink(link);
       } catch (Exception foo)
       {
-        jalview.bin.Cache.log.error("Exception for GroupURLLink '" + link
-                + "'", foo);
+        Cache.log.error("Exception for GroupURLLink '" + link + "'", foo);
         continue;
       }
       ;
       if (!urlLink.isValid())
       {
-        jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
+        Cache.log.error(urlLink.getInvalidMessage());
         continue;
       }
       final String label = urlLink.getLabel();
       boolean usingNames = false;
       // Now see which parts of the group apply for this URL
       String ltarget = urlLink.getTarget(); // jalview.util.DBRefUtils.getCanonicalName(urlLink.getTarget());
-      Object[] idset = (Object[]) commonDbrefs.get(ltarget.toUpperCase());
+      Object[] idset = commonDbrefs.get(ltarget.toUpperCase());
       String[] seqstr, ids; // input to makeUrl
       if (idset != null)
       {
@@ -1013,6 +938,15 @@ public class PopupMenu extends JPopupMenu
     }
   }
 
+  private void addshowLinks(JMenu linkMenu, Collection<List<String>> linkset)
+  {
+    for (List<String> linkstrset : linkset)
+    {
+      // split linkstr into label and url
+      addshowLink(linkMenu, linkstrset.get(1), linkstrset.get(3));
+    }
+  }
+
   /**
    * add a show URL menu item to the given linkMenu
    * 
@@ -1068,7 +1002,7 @@ public class PopupMenu extends JPopupMenu
             new Object[] { urlgenerator.getUrl_prefix(),
                 urlgenerator.getNumberInvolved(urlstub) }));
     // TODO: put in info about what is being sent.
-    item.addActionListener(new java.awt.event.ActionListener()
+    item.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1082,7 +1016,7 @@ public class PopupMenu extends JPopupMenu
             try
             {
               showLink(urlgenerator.constructFrom(urlstub));
-            } catch (UrlStringTooLongException e)
+            } catch (UrlStringTooLongException e2)
             {
             }
           }
@@ -1102,7 +1036,6 @@ public class PopupMenu extends JPopupMenu
    */
   private void jbInit() throws Exception
   {
-    groupMenu.setText(MessageManager.getString("label.group"));
     groupMenu.setText(MessageManager.getString("label.selection"));
     groupName.setText(MessageManager.getString("label.name"));
     groupName.addActionListener(new java.awt.event.ActionListener()
@@ -1284,36 +1217,6 @@ public class PopupMenu extends JPopupMenu
         changeCase(e);
       }
     });
-    pdbMenu.setText(MessageManager
-            .getString("label.associate_structure_with_sequence"));
-    pdbFromFile.setText(MessageManager.getString("label.from_file"));
-    pdbFromFile.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        pdbFromFile_actionPerformed();
-      }
-    });
-
-    enterPDB.setText(MessageManager.getString("label.enter_pdb_id"));
-    enterPDB.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        enterPDB_actionPerformed();
-      }
-    });
-    discoverPDB.setText(MessageManager.getString("label.discover_pdb_ids"));
-    discoverPDB.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        discoverPDB_actionPerformed();
-      }
-    });
     outputMenu.setText(MessageManager.getString("label.out_to_textbox")
             + "...");
     seqShowAnnotationsMenu.setText(MessageManager
@@ -1483,13 +1386,10 @@ public class PopupMenu extends JPopupMenu
     editMenu.add(upperCase);
     editMenu.add(lowerCase);
     editMenu.add(toggle);
-    pdbMenu.add(pdbFromFile);
     // JBPNote: These shouldn't be added here - should appear in a generic
     // 'apply web service to this sequence menu'
     // pdbMenu.add(RNAFold);
     // pdbMenu.add(ContraFold);
-    pdbMenu.add(enterPDB);
-    pdbMenu.add(discoverPDB);
     jMenu1.add(groupName);
     jMenu1.add(colourMenu);
     jMenu1.add(showBoxes);
@@ -1777,7 +1677,7 @@ public class PopupMenu extends JPopupMenu
   public void createSequenceDetailsReport(SequenceI[] sequences)
   {
     CutAndPasteHtmlTransfer cap = new CutAndPasteHtmlTransfer();
-    StringBuffer contents = new StringBuffer();
+    StringBuilder contents = new StringBuilder(128);
     for (SequenceI seq : sequences)
     {
       contents.append("<p><h2>"
@@ -1792,7 +1692,6 @@ public class PopupMenu extends JPopupMenu
                       seq,
                       true,
                       true,
-                      false,
                       (ap.getSeqPanel().seqCanvas.fr != null) ? ap
                               .getSeqPanel().seqCanvas.fr.getMinMax()
                               : null);
@@ -2080,9 +1979,8 @@ public class PopupMenu extends JPopupMenu
     if (conservationMenuItem.isSelected())
     {
       // JBPNote: Conservation name shouldn't be i18n translated
-      Conservation c = new Conservation("Group",
-              ResidueProperties.propHash, 3, sg.getSequences(ap.av
-                      .getHiddenRepSequences()), sg.getStartRes(),
+      Conservation c = new Conservation("Group", sg.getSequences(ap.av
+              .getHiddenRepSequences()), sg.getStartRes(),
               sg.getEndRes() + 1);
 
       c.calculate();
@@ -2307,28 +2205,7 @@ public class PopupMenu extends JPopupMenu
 
   void hideSequences(boolean representGroup)
   {
-    SequenceGroup sg = ap.av.getSelectionGroup();
-    if (sg == null || sg.getSize() < 1)
-    {
-      ap.av.hideSequence(new SequenceI[] { sequence });
-      return;
-    }
-
-    ap.av.setSelectionGroup(null);
-
-    if (representGroup)
-    {
-      ap.av.hideRepSequences(sequence, sg);
-
-      return;
-    }
-
-    int gsize = sg.getSize();
-    SequenceI[] hseqs = sg.getSequences().toArray(new SequenceI[gsize]);
-
-    ap.av.hideSequence(hseqs);
-    // refresh(); TODO: ? needed ?
-    ap.av.sendSelection();
+    ap.av.hideSequences(sequence, representGroup);
   }
 
   public void copy_actionPerformed()
@@ -2400,66 +2277,6 @@ public class PopupMenu extends JPopupMenu
             ap, true));
   }
 
-  public void pdbFromFile_actionPerformed()
-  {
-    jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
-            jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
-    chooser.setFileView(new jalview.io.JalviewFileView());
-    chooser.setDialogTitle(MessageManager.formatMessage(
-            "label.select_pdb_file_for",
-            new Object[] { sequence.getDisplayId(false) }));
-    chooser.setToolTipText(MessageManager.formatMessage(
-            "label.load_pdb_file_associate_with_sequence",
-            new Object[] { sequence.getDisplayId(false) }));
-
-    int value = chooser.showOpenDialog(null);
-
-    if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
-    {
-      String choice = chooser.getSelectedFile().getPath();
-      jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
-      new AssociatePdbFileWithSeq().associatePdbWithSeq(choice,
-              jalview.io.AppletFormatAdapter.FILE, sequence, true,
-              Desktop.instance);
-    }
-
-  }
-
-  public void enterPDB_actionPerformed()
-  {
-    String id = JOptionPane.showInternalInputDialog(Desktop.desktop,
-            MessageManager.getString("label.enter_pdb_id"),
-            MessageManager.getString("label.enter_pdb_id"),
-            JOptionPane.QUESTION_MESSAGE);
-
-    if (id != null && id.length() > 0)
-    {
-      PDBEntry entry = new PDBEntry();
-      entry.setId(id.toUpperCase());
-      sequence.getDatasetSequence().addPDBId(entry);
-    }
-  }
-
-  public void discoverPDB_actionPerformed()
-  {
-
-    final SequenceI[] sequences = ((ap.av.getSelectionGroup() == null) ? new SequenceI[]
-    { sequence }
-            : ap.av.getSequenceSelection());
-    Thread discpdb = new Thread(new Runnable()
-    {
-      @Override
-      public void run()
-      {
-
-        new jalview.ws.DBRefFetcher(sequences, ap.alignFrame)
-                .fetchDBRefs(false);
-      }
-
-    });
-    discpdb.start();
-  }
-
   public void sequenceFeature_actionPerformed()
   {
     SequenceGroup sg = ap.av.getSelectionGroup();
diff --git a/src/jalview/gui/Preferences.java b/src/jalview/gui/Preferences.java
index 78a21c8..2bde3d5 100644
--- a/src/jalview/gui/Preferences.java
+++ b/src/jalview/gui/Preferences.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,11 @@
  */
 package jalview.gui;
 
+import static jalview.util.UrlConstants.DB_ACCESSION;
+import static jalview.util.UrlConstants.EMBLEBI_STRING;
+import static jalview.util.UrlConstants.SEQUENCE_ID;
+import static jalview.util.UrlConstants.SRS_STRING;
+
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.bin.Cache;
 import jalview.gui.Help.HelpId;
@@ -30,6 +35,8 @@ import jalview.jbgui.GPreferences;
 import jalview.jbgui.GSequenceLink;
 import jalview.schemes.ColourSchemeProperty;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.ws.sifts.SiftsSettings;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -39,7 +46,7 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
 import java.io.File;
-import java.util.Collection;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.StringTokenizer;
 import java.util.Vector;
@@ -95,7 +102,7 @@ public class Preferences extends GPreferences
    * Holds name and link separated with | character. Sequence ID must be
    * $SEQUENCE_ID$ or $SEQUENCE_ID=/.possible | chars ./=$
    */
-  public static Vector sequenceURLLinks;
+  public static Vector<String> sequenceURLLinks;
 
   /**
    * Holds name and link separated with | character. Sequence IDS and Sequences
@@ -105,14 +112,11 @@ public class Preferences extends GPreferences
    * (TODO: proper escape for using | to separate ids or sequences
    */
 
-  public static Vector groupURLLinks;
+  public static List<String> groupURLLinks;
   static
   {
-    String string = Cache
-            .getDefault(
-                    "SEQUENCE_LINKS",
-                    "EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$");
-    sequenceURLLinks = new Vector();
+    String string = Cache.getDefault("SEQUENCE_LINKS", EMBLEBI_STRING);
+    sequenceURLLinks = new Vector<String>();
 
     try
     {
@@ -122,7 +126,11 @@ public class Preferences extends GPreferences
         String name = st.nextToken();
         String url = st.nextToken();
         // check for '|' within a regex
-        int rxstart = url.indexOf("$SEQUENCE_ID$");
+        int rxstart = url.indexOf("$" + DB_ACCESSION + "$");
+        if (rxstart == -1)
+        {
+          rxstart = url.indexOf("$" + SEQUENCE_ID + "$");
+        }
         while (rxstart == -1 && url.indexOf("/=$") == -1)
         {
           url = url + "|" + st.nextToken();
@@ -135,14 +143,10 @@ public class Preferences extends GPreferences
     }
     {
       // upgrade old SRS link
-      int srsPos = sequenceURLLinks
-              .indexOf("SRS|http://srs.ebi.ac.uk/srsbin/cgi-bin/wgetz?-newId+(([uniprot-all:$SEQUENCE_ID$]))+-view+SwissEntry");
+      int srsPos = sequenceURLLinks.indexOf(SRS_STRING);
       if (srsPos > -1)
       {
-        sequenceURLLinks
-                .setElementAt(
-                        "EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$",
-                        srsPos);
+        sequenceURLLinks.setElementAt(EMBLEBI_STRING, srsPos);
       }
     }
 
@@ -151,10 +155,10 @@ public class Preferences extends GPreferences
      * .properties file as '|' separated strings
      */
 
-    groupURLLinks = new Vector();
+    groupURLLinks = new ArrayList<String>();
   }
 
-  Vector nameLinks, urlLinks;
+  Vector<String> nameLinks, urlLinks;
 
   JInternalFrame frame;
 
@@ -162,6 +166,16 @@ public class Preferences extends GPreferences
 
   private WsPreferences wsPrefs;
 
+  private OptionsParam promptEachTimeOpt = new OptionsParam(
+          MessageManager.getString("label.prompt_each_time"),
+          "Prompt each time");
+
+  private OptionsParam lineArtOpt = new OptionsParam(
+          MessageManager.getString("label.lineart"), "Lineart");
+
+  private OptionsParam textOpt = new OptionsParam(
+          MessageManager.getString("action.text"), "Text");
+
   /**
    * Creates a new Preferences object.
    */
@@ -175,7 +189,8 @@ public class Preferences extends GPreferences
     wsPrefs = new WsPreferences();
     wsTab.add(wsPrefs, BorderLayout.CENTER);
     int width = 500, height = 450;
-    if (new jalview.util.Platform().isAMac())
+    new jalview.util.Platform();
+    if (Platform.isAMac())
     {
       width = 570;
       height = 480;
@@ -313,11 +328,23 @@ public class Preferences extends GPreferences
       }
     });
 
+    if (Cache.getDefault("MAP_WITH_SIFTS", false))
+    {
+      siftsMapping.setSelected(true);
+    }
+    else
+    {
+      nwMapping.setSelected(true);
+    }
+
+    SiftsSettings
+            .setMapWithSifts(Cache.getDefault("MAP_WITH_SIFTS", false));
+
     /*
      * Set Connections tab defaults
      */
-    nameLinks = new Vector();
-    urlLinks = new Vector();
+    nameLinks = new Vector<String>();
+    urlLinks = new Vector<String>();
     for (int i = 0; i < sequenceURLLinks.size(); i++)
     {
       String link = sequenceURLLinks.elementAt(i).toString();
@@ -344,12 +371,23 @@ public class Preferences extends GPreferences
     /*
      * Set Output tab defaults
      */
-    epsRendering
-            .addItem(MessageManager.getString("label.prompt_each_time"));
-    epsRendering.addItem(MessageManager.getString("label.lineart"));
-    epsRendering.addItem(MessageManager.getString("action.text"));
-    epsRendering.setSelectedItem(Cache.getDefault("EPS_RENDERING",
-            "Prompt each time"));
+    epsRendering.addItem(promptEachTimeOpt);
+    epsRendering.addItem(lineArtOpt);
+    epsRendering.addItem(textOpt);
+    String defaultEPS = Cache.getDefault("EPS_RENDERING",
+            "Prompt each time");
+    if (defaultEPS.equalsIgnoreCase("Text"))
+    {
+      epsRendering.setSelectedItem(textOpt);
+    }
+    else if (defaultEPS.equalsIgnoreCase("Lineart"))
+    {
+      epsRendering.setSelectedItem(lineArtOpt);
+    }
+    else
+    {
+      epsRendering.setSelectedItem(promptEachTimeOpt);
+    }
     autoIdWidth.setSelected(Cache.getDefault("FIGURE_AUTOIDWIDTH", false));
     userIdWidth.setEnabled(!autoIdWidth.isSelected());
     userIdWidthlabel.setEnabled(!autoIdWidth.isSelected());
@@ -384,6 +422,7 @@ public class Preferences extends GPreferences
    * 
    * @param e
    */
+  @Override
   public void ok_actionPerformed(ActionEvent e)
   {
     if (!validateSettings())
@@ -492,19 +531,15 @@ public class Preferences extends GPreferences
     Cache.applicationProperties.setProperty(STRUCTURE_DISPLAY, structViewer
             .getSelectedItem().toString());
     Cache.setOrRemove(CHIMERA_PATH, chimeraPath.getText());
+    Cache.applicationProperties.setProperty("MAP_WITH_SIFTS",
+            Boolean.toString(siftsMapping.isSelected()));
+    SiftsSettings.setMapWithSifts(siftsMapping.isSelected());
 
     /*
      * Save Output settings
      */
-    if (epsRendering.getSelectedItem().equals("Prompt each time"))
-    {
-      Cache.applicationProperties.remove("EPS_RENDERING");
-    }
-    else
-    {
-      Cache.applicationProperties.setProperty("EPS_RENDERING", epsRendering
-              .getSelectedItem().toString());
-    }
+    Cache.applicationProperties.setProperty("EPS_RENDERING",
+            ((OptionsParam) epsRendering.getSelectedItem()).getCode());
 
     /*
      * Save Connections settings
@@ -516,7 +551,7 @@ public class Preferences extends GPreferences
     if (nameLinks.size() > 0)
     {
       StringBuffer links = new StringBuffer();
-      sequenceURLLinks = new Vector();
+      sequenceURLLinks = new Vector<String>();
       for (int i = 0; i < nameLinks.size(); i++)
       {
         sequenceURLLinks.addElement(nameLinks.elementAt(i) + "|"
@@ -532,6 +567,7 @@ public class Preferences extends GPreferences
     else
     {
       Cache.applicationProperties.remove("SEQUENCE_LINKS");
+      sequenceURLLinks.clear();
     }
 
     Cache.applicationProperties.setProperty("USE_PROXY",
@@ -647,6 +683,7 @@ public class Preferences extends GPreferences
   /**
    * DOCUMENT ME!
    */
+  @Override
   public void startupFileTextfield_mouseClicked()
   {
     JalviewFileChooser chooser = new JalviewFileChooser(
@@ -676,6 +713,7 @@ public class Preferences extends GPreferences
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void cancel_actionPerformed(ActionEvent e)
   {
     try
@@ -694,6 +732,7 @@ public class Preferences extends GPreferences
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void annotations_actionPerformed(ActionEvent e)
   {
     conservation.setEnabled(annotations.isSelected());
@@ -707,6 +746,7 @@ public class Preferences extends GPreferences
             && (identity.isSelected() || showGroupConsensus.isSelected()));
   }
 
+  @Override
   public void newLink_actionPerformed(ActionEvent e)
   {
 
@@ -733,6 +773,7 @@ public class Preferences extends GPreferences
     }
   }
 
+  @Override
   public void editLink_actionPerformed(ActionEvent e)
   {
     GSequenceLink link = new GSequenceLink();
@@ -774,6 +815,7 @@ public class Preferences extends GPreferences
     }
   }
 
+  @Override
   public void deleteLink_actionPerformed(ActionEvent e)
   {
     int index = linkNameList.getSelectedIndex();
@@ -796,6 +838,7 @@ public class Preferences extends GPreferences
     linkURLList.setListData(urlLinks);
   }
 
+  @Override
   public void defaultBrowser_mouseClicked(MouseEvent e)
   {
     JFileChooser chooser = new JFileChooser(".");
@@ -818,13 +861,14 @@ public class Preferences extends GPreferences
    * jalview.jbgui.GPreferences#showunconserved_actionPerformed(java.awt.event
    * .ActionEvent)
    */
+  @Override
   protected void showunconserved_actionPerformed(ActionEvent e)
   {
     // TODO Auto-generated method stub
     super.showunconserved_actionPerformed(e);
   }
 
-  public static Collection getGroupURLLinks()
+  public static List<String> getGroupURLLinks()
   {
     return groupURLLinks;
   }
@@ -956,4 +1000,58 @@ public class Preferences extends GPreferences
     }
   }
 
+  public class OptionsParam
+  {
+    private String name;
+
+    private String code;
+
+    public OptionsParam(String name, String code)
+    {
+      this.name = name;
+      this.code = code;
+    }
+
+    public String getName()
+    {
+      return name;
+    }
+
+    public void setName(String name)
+    {
+      this.name = name;
+    }
+
+    public String getCode()
+    {
+      return code;
+    }
+
+    public void setCode(String code)
+    {
+      this.code = code;
+    }
+
+    @Override
+    public String toString()
+    {
+      return name;
+    }
+
+    @Override
+    public boolean equals(Object that)
+    {
+      if (!(that instanceof OptionsParam))
+      {
+        return false;
+      }
+      return this.code.equalsIgnoreCase(((OptionsParam) that).code);
+    }
+
+    @Override
+    public int hashCode()
+    {
+      return name.hashCode() + code.hashCode();
+    }
+  }
 }
diff --git a/src/jalview/gui/ProgressBar.java b/src/jalview/gui/ProgressBar.java
index f6710bc..56db1f3 100644
--- a/src/jalview/gui/ProgressBar.java
+++ b/src/jalview/gui/ProgressBar.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/PromptUserConfig.java b/src/jalview/gui/PromptUserConfig.java
index f802149..c0857b2 100644
--- a/src/jalview/gui/PromptUserConfig.java
+++ b/src/jalview/gui/PromptUserConfig.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/RedundancyPanel.java b/src/jalview/gui/RedundancyPanel.java
index 4998bcc..241bed1 100644
--- a/src/jalview/gui/RedundancyPanel.java
+++ b/src/jalview/gui/RedundancyPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -172,7 +172,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
     progress = null;
 
     label.setText(MessageManager
-            .getString("label.enter_redundancy_thereshold"));
+            .getString("label.enter_redundancy_threshold"));
     slider.setVisible(true);
     applyButton.setEnabled(true);
     valueField.setVisible(true);
diff --git a/src/jalview/gui/RestInputParamEditDialog.java b/src/jalview/gui/RestInputParamEditDialog.java
index 80dc8fb..fcfccd5 100644
--- a/src/jalview/gui/RestInputParamEditDialog.java
+++ b/src/jalview/gui/RestInputParamEditDialog.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/RestServiceEditorPane.java b/src/jalview/gui/RestServiceEditorPane.java
index 4e42bf0..554aab8 100644
--- a/src/jalview/gui/RestServiceEditorPane.java
+++ b/src/jalview/gui/RestServiceEditorPane.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/RotatableCanvas.java b/src/jalview/gui/RotatableCanvas.java
index 441fa5f..adf474f 100644
--- a/src/jalview/gui/RotatableCanvas.java
+++ b/src/jalview/gui/RotatableCanvas.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/SVGOptions.java b/src/jalview/gui/SVGOptions.java
index 21319eb..0659fdb 100644
--- a/src/jalview/gui/SVGOptions.java
+++ b/src/jalview/gui/SVGOptions.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/ScalePanel.java b/src/jalview/gui/ScalePanel.java
index 21db011..d0a2fa7 100644
--- a/src/jalview/gui/ScalePanel.java
+++ b/src/jalview/gui/ScalePanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -23,7 +23,10 @@ package jalview.gui;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.renderer.ScaleRenderer;
+import jalview.renderer.ScaleRenderer.ScaleMark;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 
 import java.awt.Color;
 import java.awt.FontMetrics;
@@ -35,6 +38,7 @@ import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
+import java.util.List;
 
 import javax.swing.JMenuItem;
 import javax.swing.JPanel;
@@ -43,17 +47,14 @@ import javax.swing.SwingUtilities;
 import javax.swing.ToolTipManager;
 
 /**
- * DOCUMENT ME!
- * 
- * @author $author$
- * @version $Revision$
+ * The panel containing the sequence ruler (when not in wrapped mode), and
+ * supports a range of mouse operations to select, hide or reveal columns.
  */
 public class ScalePanel extends JPanel implements MouseMotionListener,
         MouseListener
 {
   protected int offy = 4;
 
-  /** DOCUMENT ME!! */
   public int width;
 
   protected AlignViewport av;
@@ -62,13 +63,26 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
 
   boolean stretchingGroup = false;
 
-  int min; // used by mouseDragged to see if user
+  /*
+   * min, max hold the extent of a mouse drag action
+   */
+  int min;
 
-  int max; // used by mouseDragged to see if user
+  int max;
 
   boolean mouseDragging = false;
 
-  // wants to delete columns
+  /*
+   * holds a hidden column range when the mouse is over an adjacent column
+   */
+  int[] reveal;
+
+  /**
+   * Constructor
+   * 
+   * @param av
+   * @param ap
+   */
   public ScalePanel(AlignViewport av, AlignmentPanel ap)
   {
     this.av = av;
@@ -84,6 +98,7 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mousePressed(MouseEvent evt)
   {
     int x = (evt.getX() / av.getCharWidth()) + av.getStartRes();
@@ -106,111 +121,152 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
     min = res;
     max = res;
 
-    if (SwingUtilities.isRightMouseButton(evt))
+    if (evt.isPopupTrigger()) // Mac: mousePressed
     {
-      JPopupMenu pop = new JPopupMenu();
-      if (reveal != null)
+      rightMouseButtonPressed(evt, res);
+    }
+    else if (SwingUtilities.isRightMouseButton(evt) && !Platform.isAMac())
+    {
+      /*
+       * defer right-mouse click handling to mouse up on Windows
+       * (where isPopupTrigger() will answer true)
+       * but accept Cmd-click on Mac which passes isRightMouseButton
+       */
+      return;
+    }
+    else
+    {
+      leftMouseButtonPressed(evt, res);
+    }
+  }
+
+  /**
+   * Handles right mouse button press. If pressed in a selected column, opens
+   * context menu for 'Hide Columns'. If pressed on a hidden columns marker,
+   * opens context menu for 'Reveal / Reveal All'. Else does nothing.
+   * 
+   * @param evt
+   * @param res
+   */
+  protected void rightMouseButtonPressed(MouseEvent evt, final int res)
+  {
+    JPopupMenu pop = new JPopupMenu();
+    if (reveal != null)
+    {
+      JMenuItem item = new JMenuItem(
+              MessageManager.getString("label.reveal"));
+      item.addActionListener(new ActionListener()
       {
-        JMenuItem item = new JMenuItem(
-                MessageManager.getString("label.reveal"));
-        item.addActionListener(new ActionListener()
+        @Override
+        public void actionPerformed(ActionEvent e)
         {
-          public void actionPerformed(ActionEvent e)
+          av.showColumn(reveal[0]);
+          reveal = null;
+          ap.paintAlignment(true);
+          if (ap.overviewPanel != null)
           {
-            av.showColumn(reveal[0]);
-            reveal = null;
-            ap.paintAlignment(true);
-            if (ap.overviewPanel != null)
-            {
-              ap.overviewPanel.updateOverviewImage();
-            }
+            ap.overviewPanel.updateOverviewImage();
           }
-        });
-        pop.add(item);
-
-        if (av.getColumnSelection().hasHiddenColumns())
-        {
-          item = new JMenuItem(
-                  MessageManager.getString("action.reveal_all"));
-          item.addActionListener(new ActionListener()
-          {
-            public void actionPerformed(ActionEvent e)
-            {
-              av.showAllHiddenColumns();
-              reveal = null;
-              ap.paintAlignment(true);
-              if (ap.overviewPanel != null)
-              {
-                ap.overviewPanel.updateOverviewImage();
-              }
-            }
-          });
-          pop.add(item);
+          av.sendSelection();
         }
-        pop.show(this, evt.getX(), evt.getY());
-      }
-      else if (av.getColumnSelection().contains(res))
+      });
+      pop.add(item);
+
+      if (av.getColumnSelection().hasHiddenColumns())
       {
-        JMenuItem item = new JMenuItem(
-                MessageManager.getString("label.hide_columns"));
+        item = new JMenuItem(MessageManager.getString("action.reveal_all"));
         item.addActionListener(new ActionListener()
         {
+          @Override
           public void actionPerformed(ActionEvent e)
           {
-            av.hideColumns(res, res);
-            if (av.getSelectionGroup() != null
-                    && av.getSelectionGroup().getSize() == av
-                            .getAlignment().getHeight())
-            {
-              av.setSelectionGroup(null);
-            }
-
+            av.showAllHiddenColumns();
+            reveal = null;
             ap.paintAlignment(true);
             if (ap.overviewPanel != null)
             {
               ap.overviewPanel.updateOverviewImage();
             }
+            av.sendSelection();
           }
         });
         pop.add(item);
-        pop.show(this, evt.getX(), evt.getY());
       }
+      pop.show(this, evt.getX(), evt.getY());
     }
-    else
-    // LEFT MOUSE TO SELECT
+    else if (av.getColumnSelection().contains(res))
     {
-      if (!evt.isControlDown() && !evt.isShiftDown())
+      JMenuItem item = new JMenuItem(
+              MessageManager.getString("label.hide_columns"));
+      item.addActionListener(new ActionListener()
       {
-        av.getColumnSelection().clear();
-      }
+        @Override
+        public void actionPerformed(ActionEvent e)
+        {
+          av.hideColumns(res, res);
+          if (av.getSelectionGroup() != null
+                  && av.getSelectionGroup().getSize() == av.getAlignment()
+                          .getHeight())
+          {
+            av.setSelectionGroup(null);
+          }
 
-      av.getColumnSelection().addElement(res);
-      SequenceGroup sg = new SequenceGroup();
-      // try to be as quick as possible
-      SequenceI[] iVec = av.getAlignment().getSequencesArray();
-      for (int i = 0; i < iVec.length; i++)
-      {
-        sg.addSequence(iVec[i], false);
-        iVec[i] = null;
-      }
-      iVec = null;
-      sg.setStartRes(res);
-      sg.setEndRes(res);
+          ap.paintAlignment(true);
+          if (ap.overviewPanel != null)
+          {
+            ap.overviewPanel.updateOverviewImage();
+          }
+          av.sendSelection();
+        }
+      });
+      pop.add(item);
+      pop.show(this, evt.getX(), evt.getY());
+    }
+  }
+
+  /**
+   * Handles left mouse button press
+   * 
+   * @param evt
+   * @param res
+   */
+  protected void leftMouseButtonPressed(MouseEvent evt, final int res)
+  {
+    /*
+     * Ctrl-click/Cmd-click adds to the selection
+     * Shift-click extends the selection
+     */
+    // TODO Problem: right-click on Windows not reported until mouseReleased?!?
+    if (!Platform.isControlDown(evt) && !evt.isShiftDown())
+    {
+      av.getColumnSelection().clear();
+    }
+
+    av.getColumnSelection().addElement(res);
+    SequenceGroup sg = new SequenceGroup();
+    // try to be as quick as possible
+    SequenceI[] iVec = av.getAlignment().getSequencesArray();
+    for (int i = 0; i < iVec.length; i++)
+    {
+      sg.addSequence(iVec[i], false);
+      iVec[i] = null;
+    }
+    iVec = null;
+    sg.setStartRes(res);
+    sg.setEndRes(res);
 
-      if (evt.isShiftDown())
+    if (evt.isShiftDown())
+    {
+      int min = Math.min(av.getColumnSelection().getMin(), res);
+      int max = Math.max(av.getColumnSelection().getMax(), res);
+      for (int i = min; i < max; i++)
       {
-        int min = Math.min(av.getColumnSelection().getMin(), res);
-        int max = Math.max(av.getColumnSelection().getMax(), res);
-        for (int i = min; i < max; i++)
-        {
-          av.getColumnSelection().addElement(i);
-        }
-        sg.setStartRes(min);
-        sg.setEndRes(max);
+        av.getColumnSelection().addElement(i);
       }
-      av.setSelectionGroup(sg);
+      sg.setStartRes(min);
+      sg.setEndRes(max);
     }
-
+    av.setSelectionGroup(sg);
     ap.paintAlignment(false);
     av.sendSelection();
   }
@@ -221,6 +277,7 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     mouseDragging = false;
@@ -239,8 +296,14 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
 
     if (!stretchingGroup)
     {
-      ap.paintAlignment(false);
-
+      if (evt.isPopupTrigger()) // Windows: mouseReleased
+      {
+        rightMouseButtonPressed(evt, res);
+      }
+      else
+      {
+        ap.paintAlignment(false);
+      }
       return;
     }
 
@@ -268,6 +331,7 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mouseDragged(MouseEvent evt)
   {
     mouseDragging = true;
@@ -337,6 +401,7 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
     }
   }
 
+  @Override
   public void mouseEntered(MouseEvent evt)
   {
     if (mouseDragging)
@@ -345,6 +410,7 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
     }
   }
 
+  @Override
   public void mouseExited(MouseEvent evt)
   {
     if (mouseDragging)
@@ -353,12 +419,16 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
     }
   }
 
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseMoved(MouseEvent evt)
   {
+    this.setToolTipText(null);
+    reveal = null;
     if (!av.hasHiddenColumns())
     {
       return;
@@ -368,7 +438,6 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
 
     res = av.getColumnSelection().adjustForHiddenColumns(res);
 
-    reveal = null;
     if (av.getColumnSelection().getHiddenColumns() != null)
     {
       for (int[] region : av.getColumnSelection().getHiddenColumns())
@@ -381,23 +450,18 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
                   .getString("label.reveal_hidden_columns"));
           break;
         }
-        else
-        {
-          this.setToolTipText(null);
-        }
       }
     }
     repaint();
   }
 
-  int[] reveal;
-
   /**
    * DOCUMENT ME!
    * 
    * @param g
    *          DOCUMENT ME!
    */
+  @Override
   public void paintComponent(Graphics g)
   {
     drawScale(g, av.getStartRes(), av.getEndRes(), getWidth(), getHeight());
@@ -425,14 +489,15 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
     ColumnSelection cs = av.getColumnSelection();
     int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight();
 
-    int s;
     if (cs != null)
     {
       gg.setColor(new Color(220, 0, 0));
 
-      for (int i = 0; i < cs.size(); i++)
+      for (int sel : cs.getSelected())
       {
-        int sel = cs.columnAt(i);
+        // TODO: JAL-2001 - provide a fast method to list visible selected in a
+        // given range
+
         if (av.hasHiddenColumns())
         {
           if (cs.isVisible(sel))
@@ -452,49 +517,16 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
         }
       }
     }
-    // Draw the scale numbers
-    gg.setColor(Color.black);
 
-    int scalestartx = (startx / 10) * 10;
+    int widthx = 1 + endx - startx;
 
     FontMetrics fm = gg.getFontMetrics(av.getFont());
-    int y = avCharHeight - fm.getDescent();
-
-    if ((scalestartx % 10) == 0)
-    {
-      scalestartx += 5;
-    }
-
-    String string;
-    int maxX = 0;
-
-    for (int i = scalestartx; i < endx; i += 5)
-    {
-      if ((i % 10) == 0)
-      {
-        string = String.valueOf(av.getColumnSelection()
-                .adjustForHiddenColumns(i));
-        if ((i - startx - 1) * avCharWidth > maxX)
-        {
-          gg.drawString(string, (i - startx - 1) * avCharWidth, y);
-          maxX = (i - startx + 1) * avCharWidth + fm.stringWidth(string);
-        }
-
-        gg.drawLine(((i - startx - 1) * avCharWidth) + (avCharWidth / 2),
-                y + 2,
-                ((i - startx - 1) * avCharWidth) + (avCharWidth / 2), y
-                        + (fm.getDescent() * 2));
-      }
-      else
-      {
-        gg.drawLine(((i - startx - 1) * avCharWidth) + (avCharWidth / 2), y
-                + fm.getDescent(), ((i - startx - 1) * avCharWidth)
-                + (avCharWidth / 2), y + (fm.getDescent() * 2));
-      }
-    }
-
+    int y = avCharHeight;
+    int yOf = fm.getDescent();
+    y -= yOf;
     if (av.hasHiddenColumns())
     {
+      // draw any hidden column markers
       gg.setColor(Color.blue);
       int res;
       if (av.getShowHiddenMarkers()
@@ -503,29 +535,52 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
         for (int i = 0; i < av.getColumnSelection().getHiddenColumns()
                 .size(); i++)
         {
-
           res = av.getColumnSelection().findHiddenRegionPosition(i)
                   - startx;
 
-          if (res < 0 || res > endx - scalestartx)
+          if (res < 0 || res > widthx)
           {
             continue;
           }
 
-          gg.fillPolygon(new int[] { res * avCharWidth - avCharHeight / 4,
-              res * avCharWidth + avCharHeight / 4, res * avCharWidth },
-                  new int[] { y - avCharHeight / 2, y - avCharHeight / 2,
-                      y + 8 }, 3);
-
+          gg.fillPolygon(new int[] {
+              -1 + res * avCharWidth - avCharHeight / 4,
+              -1 + res * avCharWidth + avCharHeight / 4,
+              -1 + res * avCharWidth }, new int[] { y, y, y + 2 * yOf }, 3);
         }
       }
+    }
+    // Draw the scale numbers
+    gg.setColor(Color.black);
+
+    int maxX = 0;
+    List<ScaleMark> marks = new ScaleRenderer().calculateMarks(av, startx,
+            endx);
 
-      if (reveal != null && reveal[0] > startx && reveal[0] < endx)
+    for (ScaleMark mark : marks)
+    {
+      boolean major = mark.major;
+      int mpos = mark.column; // (i - startx - 1)
+      String mstring = mark.text;
+      if (mstring != null)
       {
-        gg.drawString(MessageManager.getString("label.reveal_columns"),
-                reveal[0] * avCharWidth, 0);
+        if (mpos * avCharWidth > maxX)
+        {
+          gg.drawString(mstring, mpos * avCharWidth, y);
+          maxX = (mpos + 2) * avCharWidth + fm.stringWidth(mstring);
+        }
+      }
+      if (major)
+      {
+        gg.drawLine((mpos * avCharWidth) + (avCharWidth / 2), y + 2,
+                (mpos * avCharWidth) + (avCharWidth / 2), y + (yOf * 2));
+      }
+      else
+      {
+        gg.drawLine((mpos * avCharWidth) + (avCharWidth / 2), y + yOf,
+                (mpos * avCharWidth) + (avCharWidth / 2), y + (yOf * 2));
       }
     }
-
   }
+
 }
diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java
index 76dd53a..4db404d 100644
--- a/src/jalview/gui/SeqCanvas.java
+++ b/src/jalview/gui/SeqCanvas.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,9 +21,11 @@
 package jalview.gui;
 
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.renderer.ScaleRenderer;
+import jalview.renderer.ScaleRenderer.ScaleMark;
 
 import java.awt.BasicStroke;
 import java.awt.BorderLayout;
@@ -60,8 +62,6 @@ public class SeqCanvas extends JComponent
 
   AlignViewport av;
 
-  SearchResults searchResults = null;
-
   boolean fastPaint = false;
 
   int LABEL_WEST;
@@ -122,24 +122,26 @@ public class SeqCanvas extends JComponent
   private void drawNorthScale(Graphics g, int startx, int endx, int ypos)
   {
     updateViewport();
-    int scalestartx = startx - (startx % 10) + 10;
-
-    g.setColor(Color.black);
-    // NORTH SCALE
-    for (int i = scalestartx; i < endx; i += 10)
+    for (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx,
+            endx))
     {
-      int value = i;
-      if (av.hasHiddenColumns())
+      int mpos = mark.column; // (i - startx - 1)
+      if (mpos < 0)
       {
-        value = av.getColumnSelection().adjustForHiddenColumns(value);
+        continue;
       }
+      String mstring = mark.text;
 
-      g.drawString(String.valueOf(value), (i - startx - 1) * charWidth,
-              ypos - (charHeight / 2));
-
-      g.drawLine(((i - startx - 1) * charWidth) + (charWidth / 2),
-              (ypos + 2) - (charHeight / 2), ((i - startx - 1) * charWidth)
-                      + (charWidth / 2), ypos - 2);
+      if (mark.major)
+      {
+        if (mstring != null)
+        {
+          g.drawString(mstring, mpos * charWidth, ypos - (charHeight / 2));
+        }
+        g.drawLine((mpos * charWidth) + (charWidth / 2), (ypos + 2)
+                - (charHeight / 2), (mpos * charWidth) + (charWidth / 2),
+                ypos - 2);
+      }
     }
   }
 
@@ -335,6 +337,7 @@ public class SeqCanvas extends JComponent
    */
 
   // Set this to false to force a full panel paint
+  @Override
   public void paintComponent(Graphics g)
   {
     updateViewport();
@@ -682,10 +685,17 @@ public class SeqCanvas extends JComponent
         g1.translate(-screenY * charWidth, 0);
         screenY += blockEnd - blockStart + 1;
         blockStart = hideEnd + 1;
+
+        if (screenY > (endRes - startRes))
+        {
+          // already rendered last block
+          return;
+        }
       }
 
       if (screenY <= (endRes - startRes))
       {
+        // remaining visible region to render
         blockEnd = blockStart + (endRes - startRes) - screenY;
         g1.translate(screenY * charWidth, 0);
         draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
@@ -728,10 +738,10 @@ public class SeqCanvas extends JComponent
 
       // / Highlight search Results once all sequences have been drawn
       // ////////////////////////////////////////////////////////
-      if (searchResults != null)
+      if (av.hasSearchResults())
       {
-        int[] visibleResults = searchResults.getResults(nextSeq, startRes,
-                endRes);
+        int[] visibleResults = av.getSearchResults().getResults(nextSeq,
+                startRes, endRes);
         if (visibleResults != null)
         {
           for (int r = 0; r < visibleResults.length; r += 2)
@@ -953,11 +963,11 @@ public class SeqCanvas extends JComponent
    * @param results
    *          DOCUMENT ME!
    */
-  public void highlightSearchResults(SearchResults results)
+  public void highlightSearchResults(SearchResultsI results)
   {
     img = null;
 
-    searchResults = results;
+    av.setSearchResults(results);
 
     repaint();
   }
diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java
index e922a0d..c6e5ba6 100644
--- a/src/jalview/gui/SeqPanel.java
+++ b/src/jalview/gui/SeqPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,13 +21,15 @@
 package jalview.gui;
 
 import jalview.api.AlignViewportI;
+import jalview.bin.Cache;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
 import jalview.commands.EditCommand.Edit;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
-import jalview.datamodel.SearchResults.Match;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
@@ -42,6 +44,7 @@ import jalview.structure.VamsasSource;
 import jalview.util.Comparison;
 import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.BorderLayout;
@@ -54,11 +57,12 @@ import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.awt.event.MouseWheelEvent;
 import java.awt.event.MouseWheelListener;
+import java.util.ArrayList;
 import java.util.List;
-import java.util.Vector;
 
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
 import javax.swing.ToolTipManager;
 
 /**
@@ -121,7 +125,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
   private final SequenceAnnotationReport seqARep;
 
-  StringBuffer tooltipText = new StringBuffer();
+  StringBuilder tooltipText = new StringBuilder();
 
   String tmpString;
 
@@ -129,7 +133,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
   StructureSelectionManager ssm;
 
-  SearchResults lastSearchResults;
+  SearchResultsI lastSearchResults;
 
   /**
    * Creates a new SeqPanel object.
@@ -509,6 +513,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
   void insertNucAtCursor(boolean group, String nuc)
   {
+    // TODO not called - delete?
     groupEditing = group;
     startseq = seqCanvas.cursorY;
     lastres = seqCanvas.cursorX;
@@ -579,6 +584,13 @@ public class SeqPanel extends JPanel implements MouseListener,
     mouseDragging = false;
     mouseWheelPressed = false;
 
+    if (evt.isPopupTrigger()) // Windows: mouseReleased
+    {
+      showPopupMenu(evt);
+      evt.consume();
+      return;
+    }
+
     if (!editingSeqs)
     {
       doMouseReleasedDefineMode(evt);
@@ -599,19 +611,20 @@ public class SeqPanel extends JPanel implements MouseListener,
   {
     lastMousePress = evt.getPoint();
 
-    if (javax.swing.SwingUtilities.isMiddleMouseButton(evt))
+    if (SwingUtilities.isMiddleMouseButton(evt))
     {
       mouseWheelPressed = true;
       return;
     }
 
-    if (evt.isShiftDown() || evt.isAltDown() || evt.isControlDown())
+    boolean isControlDown = Platform.isControlDown(evt);
+    if (evt.isShiftDown() || isControlDown)
     {
-      if (evt.isAltDown() || evt.isControlDown())
+      editingSeqs = true;
+      if (isControlDown)
       {
         groupEditing = true;
       }
-      editingSeqs = true;
     }
     else
     {
@@ -664,7 +677,7 @@ public class SeqPanel extends JPanel implements MouseListener,
    * the start of the highlighted region.
    */
   @Override
-  public void highlightSequence(SearchResults results)
+  public void highlightSequence(SearchResultsI results)
   {
     if (results == null || results.equals(lastSearchResults))
     {
@@ -680,7 +693,7 @@ public class SeqPanel extends JPanel implements MouseListener,
        * 
        * @see AlignmentPanel#adjustmentValueChanged
        */
-      ap.setFollowingComplementScroll(true);
+      ap.setDontScrollComplement(true);
       if (ap.scrollToPosition(results, false))
       {
         seqCanvas.revalidate();
@@ -773,7 +786,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       seqARep.appendFeatures(tooltipText, rpos, features,
               this.ap.getSeqPanel().seqCanvas.fr.getMinMax());
     }
-    if (tooltipText.length() == 6) // <html></html>
+    if (tooltipText.length() == 6) // <html>
     {
       setToolTipText(null);
       lastTooltip = null;
@@ -801,6 +814,7 @@ public class SeqPanel extends JPanel implements MouseListener,
    * 
    * @see javax.swing.JComponent#getToolTipLocation(java.awt.event.MouseEvent)
    */
+  @Override
   public Point getToolTipLocation(MouseEvent event)
   {
     int x = event.getX(), w = getWidth();
@@ -821,6 +835,21 @@ public class SeqPanel extends JPanel implements MouseListener,
   String lastTooltip;
 
   /**
+   * set when the current UI interaction has resulted in a change that requires
+   * overview shading to be recalculated. this could be changed to something
+   * more expressive that indicates what actually has changed, so selective
+   * redraws can be applied
+   */
+  private boolean needOverviewUpdate = false; // TODO: refactor to avcontroller
+
+  /**
+   * set if av.getSelectionGroup() refers to a group that is defined on the
+   * alignment view, rather than a transient selection
+   */
+  // private boolean editingDefinedGroup = false; // TODO: refactor to
+  // avcontroller or viewModel
+
+  /**
    * Set status message in alignment panel
    * 
    * @param sequence
@@ -839,7 +868,8 @@ public class SeqPanel extends JPanel implements MouseListener,
      * Sequence number (if known), and sequence name.
      */
     String seqno = seq == -1 ? "" : " " + (seq + 1);
-    text.append("Sequence" + seqno + " ID: " + sequence.getName());
+    text.append("Sequence").append(seqno).append(" ID: ")
+            .append(sequence.getName());
 
     String residue = null;
     /*
@@ -881,7 +911,7 @@ public class SeqPanel extends JPanel implements MouseListener,
    * 
    * @param results
    */
-  private void setStatusMessage(SearchResults results)
+  private void setStatusMessage(SearchResultsI results)
   {
     AlignmentI al = this.av.getAlignment();
     int sequenceIndex = al.findIndex(results);
@@ -890,7 +920,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       return;
     }
     SequenceI ds = al.getSequenceAt(sequenceIndex).getDatasetSequence();
-    for (Match m : results.getResults())
+    for (SearchResultMatchI m : results.getResults())
     {
       SequenceI seq = m.getSequence();
       if (seq.getDatasetSequence() != null)
@@ -904,7 +934,7 @@ public class SeqPanel extends JPanel implements MouseListener,
          * Convert position in sequence (base 1) to sequence character array
          * index (base 0)
          */
-        int start = m.getStart() - 1;
+        int start = m.getStart() - m.getSequence().getStart();
         setStatusMessage(seq, start, sequenceIndex);
         return;
       }
@@ -1170,8 +1200,7 @@ public class SeqPanel extends JPanel implements MouseListener,
           {
             for (int j = 0; j < startres - lastres; j++)
             {
-              if (!jalview.util.Comparison.isGap(groupSeqs[g]
-                      .getCharAt(fixedRight - j)))
+              if (!Comparison.isGap(groupSeqs[g].getCharAt(fixedRight - j)))
               {
                 blank = false;
                 break;
@@ -1233,7 +1262,7 @@ public class SeqPanel extends JPanel implements MouseListener,
               continue;
             }
 
-            if (!jalview.util.Comparison.isGap(groupSeqs[g].getCharAt(j)))
+            if (!Comparison.isGap(groupSeqs[g].getCharAt(j)))
             {
               // Not a gap, block edit not valid
               endEditing();
@@ -1365,7 +1394,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
       for (blankColumn = fixedColumn; blankColumn > j; blankColumn--)
       {
-        if (jalview.util.Comparison.isGap(seq[s].getCharAt(blankColumn)))
+        if (Comparison.isGap(seq[s].getCharAt(blankColumn)))
         {
           // Theres a space, so break and insert the gap
           break;
@@ -1473,7 +1502,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
       if (features != null && features.size() > 0)
       {
-        SearchResults highlight = new SearchResults();
+        SearchResultsI highlight = new SearchResults();
         highlight.addResult(sequence, features.get(0).getBegin(), features
                 .get(0).getEnd());
         seqCanvas.highlightSearchResults(highlight);
@@ -1528,9 +1557,10 @@ public class SeqPanel extends JPanel implements MouseListener,
    */
   public void doMousePressedDefineMode(MouseEvent evt)
   {
-    int res = findRes(evt);
-    int seq = findSeq(evt);
+    final int res = findRes(evt);
+    final int seq = findSeq(evt);
     oldSeq = seq;
+    needOverviewUpdate = false;
 
     startWrapBlock = wrappedBlock;
 
@@ -1593,28 +1623,21 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
 
       av.setSelectionGroup(stretchGroup);
-
     }
 
-    if (javax.swing.SwingUtilities.isRightMouseButton(evt))
+    if (evt.isPopupTrigger()) // Mac: mousePressed
     {
-      List<SequenceFeature> allFeatures = ap.getFeatureRenderer()
-              .findFeaturesAtRes(sequence.getDatasetSequence(),
-                      sequence.findPosition(res));
-      Vector links = new Vector();
-      for (SequenceFeature sf : allFeatures)
-      {
-        if (sf.links != null)
-        {
-          for (int j = 0; j < sf.links.size(); j++)
-          {
-            links.addElement(sf.links.elementAt(j));
-          }
-        }
-      }
+      showPopupMenu(evt);
+      return;
+    }
 
-      jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(ap, null, links);
-      pop.show(this, evt.getX(), evt.getY());
+    /*
+     * defer right-mouse click handling to mouseReleased on Windows
+     * (where isPopupTrigger() will answer true)
+     * NB isRightMouseButton is also true for Cmd-click on Mac
+     */
+    if (SwingUtilities.isRightMouseButton(evt) && !Platform.isAMac())
+    {
       return;
     }
 
@@ -1636,7 +1659,6 @@ public class SeqPanel extends JPanel implements MouseListener,
       sg.setEndRes(res);
       sg.addSequence(sequence, false);
       av.setSelectionGroup(sg);
-
       stretchGroup = sg;
 
       if (av.getConservationSelected())
@@ -1668,6 +1690,37 @@ public class SeqPanel extends JPanel implements MouseListener,
   }
 
   /**
+   * Build and show a pop-up menu at the right-click mouse position
+   * 
+   * @param evt
+   * @param res
+   * @param sequence
+   */
+  void showPopupMenu(MouseEvent evt)
+  {
+    final int res = findRes(evt);
+    final int seq = findSeq(evt);
+    SequenceI sequence = av.getAlignment().getSequenceAt(seq);
+    List<SequenceFeature> allFeatures = ap.getFeatureRenderer()
+            .findFeaturesAtRes(sequence.getDatasetSequence(),
+                    sequence.findPosition(res));
+    List<String> links = new ArrayList<String>();
+    for (SequenceFeature sf : allFeatures)
+    {
+      if (sf.links != null)
+      {
+        for (String link : sf.links)
+        {
+          links.add(link);
+        }
+      }
+    }
+
+    PopupMenu pop = new PopupMenu(ap, null, links);
+    pop.show(this, evt.getX(), evt.getY());
+  }
+
+  /**
    * DOCUMENT ME!
    * 
    * @param evt
@@ -1679,9 +1732,10 @@ public class SeqPanel extends JPanel implements MouseListener,
     {
       return;
     }
-
-    stretchGroup.recalcConservation(); // always do this - annotation has own
-                                       // state
+    // always do this - annotation has own state
+    // but defer colourscheme update until hidden sequences are passed in
+    boolean vischange = stretchGroup.recalcConservation(true);
+    needOverviewUpdate |= vischange && av.isSelectionDefinedGroup();
     if (stretchGroup.cs != null)
     {
       stretchGroup.cs.alignmentChanged(stretchGroup,
@@ -1699,8 +1753,8 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
     }
     PaintRefresher.Refresh(this, av.getSequenceSetId());
-    ap.paintAlignment(true);
-
+    ap.paintAlignment(needOverviewUpdate);
+    needOverviewUpdate = false;
     changeEndRes = false;
     changeStartRes = false;
     stretchGroup = null;
@@ -1754,6 +1808,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       if (res > (stretchGroup.getStartRes() - 1))
       {
         stretchGroup.setEndRes(res);
+        needOverviewUpdate |= av.isSelectionDefinedGroup();
       }
     }
     else if (changeStartRes)
@@ -1761,6 +1816,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       if (res < (stretchGroup.getEndRes() + 1))
       {
         stretchGroup.setStartRes(res);
+        needOverviewUpdate |= av.isSelectionDefinedGroup();
       }
     }
 
@@ -1794,6 +1850,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       if (stretchGroup.getSequences(null).contains(nextSeq))
       {
         stretchGroup.deleteSequence(seq, false);
+        needOverviewUpdate |= av.isSelectionDefinedGroup();
       }
       else
       {
@@ -1803,6 +1860,7 @@ public class SeqPanel extends JPanel implements MouseListener,
         }
 
         stretchGroup.addSequence(nextSeq, false);
+        needOverviewUpdate |= av.isSelectionDefinedGroup();
       }
     }
 
@@ -1944,28 +2002,28 @@ public class SeqPanel extends JPanel implements MouseListener,
 
     // do we want to thread this ? (contention with seqsel and colsel locks, I
     // suspect)
-    // rules are: colsel is copied if there is a real intersection between
-    // sequence selection
+    /*
+     * only copy colsel if there is a real intersection between
+     * sequence selection and this panel's alignment
+     */
     boolean repaint = false;
-    boolean copycolsel = true;
+    boolean copycolsel = false;
 
     SequenceGroup sgroup = null;
     if (seqsel != null && seqsel.getSize() > 0)
     {
       if (av.getAlignment() == null)
       {
-        jalview.bin.Cache.log.warn("alignviewport av SeqSetId="
-                + av.getSequenceSetId() + " ViewId=" + av.getViewId()
+        Cache.log.warn("alignviewport av SeqSetId=" + av.getSequenceSetId()
+                + " ViewId=" + av.getViewId()
                 + " 's alignment is NULL! returning immediately.");
         return;
       }
       sgroup = seqsel.intersect(av.getAlignment(),
               (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
-      if ((sgroup == null || sgroup.getSize() == 0)
-              || (colsel == null || colsel.size() == 0))
+      if ((sgroup != null && sgroup.getSize() > 0))
       {
-        // don't copy columns if the region didn't intersect.
-        copycolsel = false;
+        copycolsel = true;
       }
     }
     if (sgroup != null && sgroup.getSize() > 0)
@@ -1983,7 +2041,7 @@ public class SeqPanel extends JPanel implements MouseListener,
     {
       // the current selection is unset or from a previous message
       // so import the new colsel.
-      if (colsel == null || colsel.size() == 0)
+      if (colsel == null || colsel.isEmpty())
       {
         if (av.getColumnSelection() != null)
         {
@@ -2058,7 +2116,6 @@ public class SeqPanel extends JPanel implements MouseListener,
     ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
             av);
     av.setColumnSelection(cs);
-    av.isColSelChanged(true);
 
     PaintRefresher.Refresh(this, av.getSequenceSetId());
 
diff --git a/src/jalview/gui/SequenceFetcher.java b/src/jalview/gui/SequenceFetcher.java
index 9f92c42..0d87496 100644
--- a/src/jalview/gui/SequenceFetcher.java
+++ b/src/jalview/gui/SequenceFetcher.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,15 +20,18 @@
  */
 package jalview.gui;
 
+import jalview.api.FeatureSettingsModelI;
+import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
-import jalview.io.FormatAdapter;
-import jalview.io.IdentifyFile;
+import jalview.fts.service.pdb.PDBFTSPanel;
+import jalview.fts.service.uniprot.UniprotFTSPanel;
+import jalview.io.gff.SequenceOntologyI;
 import jalview.util.DBRefUtils;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.ws.dbsources.das.api.DasSourceRegistryI;
 import jalview.ws.seqfetcher.DbSourceProxy;
 
@@ -40,6 +43,7 @@ import java.awt.event.KeyAdapter;
 import java.awt.event.KeyEvent;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -54,8 +58,6 @@ import javax.swing.JTextArea;
 import javax.swing.SwingConstants;
 import javax.swing.tree.DefaultMutableTreeNode;
 
-import com.stevesoft.pat.Regex;
-
 public class SequenceFetcher extends JPanel implements Runnable
 {
   JLabel dbeg = new JLabel();
@@ -114,7 +116,10 @@ public class SequenceFetcher extends JPanel implements Runnable
 
   private static Thread initingThread = null;
 
-  int debounceTrap = 0;
+  public JTextArea getTextArea()
+  {
+    return textArea;
+  }
 
   /**
    * Blocking method that initialises and returns the shared instance of the
@@ -160,11 +165,10 @@ public class SequenceFetcher extends JPanel implements Runnable
       }
     }
     if (sfetch == null
-            || dasRegistry != jalview.bin.Cache.getDasSourceRegistry()
-            || lastDasSourceRegistry != (jalview.bin.Cache
-                    .getDasSourceRegistry().getDasRegistryURL() + jalview.bin.Cache
-                    .getDasSourceRegistry().getLocalSourceString())
-                    .hashCode())
+            || dasRegistry != Cache.getDasSourceRegistry()
+            || lastDasSourceRegistry != (Cache.getDasSourceRegistry()
+                    .getDasRegistryURL() + Cache.getDasSourceRegistry()
+                    .getLocalSourceString()).hashCode())
     {
       _initingFetcher = true;
       initingThread = Thread.currentThread();
@@ -177,7 +181,7 @@ public class SequenceFetcher extends JPanel implements Runnable
                 .getString("status.init_sequence_database_fetchers"),
                 Thread.currentThread().hashCode());
       }
-      dasRegistry = jalview.bin.Cache.getDasSourceRegistry();
+      dasRegistry = Cache.getDasSourceRegistry();
       dasRegistry.refreshSources();
 
       jalview.ws.SequenceFetcher sf = new jalview.ws.SequenceFetcher();
@@ -196,8 +200,19 @@ public class SequenceFetcher extends JPanel implements Runnable
 
   private IProgressIndicator progressIndicator;
 
+  private volatile boolean _isConstructing = false;
+
+  private List<AlignFrame> newAlframes = null;
+
   public SequenceFetcher(IProgressIndicator guiIndic)
   {
+    this(guiIndic, null, null);
+  }
+
+  public SequenceFetcher(IProgressIndicator guiIndic,
+          final String selectedDb, final String queryString)
+  {
+    this._isConstructing = true;
     this.progressIndicator = guiIndic;
     final SequenceFetcher us = this;
     // launch initialiser thread
@@ -209,7 +224,8 @@ public class SequenceFetcher extends JPanel implements Runnable
       {
         if (getSequenceFetcherSingleton(progressIndicator) != null)
         {
-          us.initGui(progressIndicator);
+          us.initGui(progressIndicator, selectedDb, queryString);
+          us._isConstructing = false;
         }
         else
         {
@@ -236,6 +252,32 @@ public class SequenceFetcher extends JPanel implements Runnable
     sf.start();
   }
 
+  /**
+   * blocking call which creates a new sequence fetcher panel, configures it and
+   * presses the OK button with the given database and query.
+   * 
+   * @param database
+   * @param query
+   */
+  public static List<AlignFrame> fetchAndShow(String database, String query)
+  {
+    final SequenceFetcher sf = new SequenceFetcher(Desktop.instance,
+            database, query);
+    while (sf._isConstructing)
+    {
+      try
+      {
+        Thread.sleep(50);
+      } catch (Exception q)
+      {
+        return Collections.emptyList();
+      }
+    }
+    sf.newAlframes = new ArrayList<AlignFrame>();
+    sf.run();
+    return sf.newAlframes;
+  }
+
   private class DatabaseAuthority extends DefaultMutableTreeNode
   {
 
@@ -247,11 +289,57 @@ public class SequenceFetcher extends JPanel implements Runnable
   };
 
   /**
+   * initialise the database and query for this fetcher panel
+   * 
+   * @param selectedDb
+   *          - string that should correspond to a sequence fetcher
+   * @param queryString
+   *          - string that will be entered in the query dialog
+   * @return true if UI was configured with valid database and query string
+   */
+  protected boolean setInitialQuery(String selectedDb, String queryString)
+  {
+    if (selectedDb == null || selectedDb.trim().length() == 0)
+    {
+      return false;
+    }
+    try
+    {
+      List<DbSourceProxy> sp = sfetch.getSourceProxy(selectedDb);
+      for (DbSourceProxy sourcep : sp)
+      {
+        if (sourcep.getTier() == 0)
+        {
+          database.selection = Arrays
+                  .asList(new DbSourceProxy[] { sourcep });
+          break;
+        }
+      }
+      if (database.selection == null || database.selection.size() == 0)
+      {
+        System.err.println("Ignoring fetch parameter db='" + selectedDb
+                + "'");
+        return false;
+      }
+      textArea.setText(queryString);
+    } catch (Exception q)
+    {
+      System.err.println("Ignoring fetch parameter db='" + selectedDb
+              + "' and query='" + queryString + "'");
+      return false;
+    }
+    return true;
+  }
+
+  /**
    * called by thread spawned by constructor
    * 
    * @param guiWindow
+   * @param queryString
+   * @param selectedDb
    */
-  private void initGui(IProgressIndicator guiWindow)
+  private void initGui(IProgressIndicator guiWindow, String selectedDb,
+          String queryString)
   {
     this.guiWindow = guiWindow;
     if (guiWindow instanceof AlignFrame)
@@ -262,6 +350,16 @@ public class SequenceFetcher extends JPanel implements Runnable
     try
     {
       jbInit();
+      /*
+       * configure the UI with any query parameters we were called with
+       */
+      if (!setInitialQuery(selectedDb, queryString))
+      {
+        /*
+         * none provided, so show the database chooser
+         */
+        database.waitForInput();
+      }
     } catch (Exception ex)
     {
       ex.printStackTrace();
@@ -269,13 +367,13 @@ public class SequenceFetcher extends JPanel implements Runnable
 
     frame = new JInternalFrame();
     frame.setContentPane(this);
-    if (new jalview.util.Platform().isAMac())
+    if (Platform.isAMac())
     {
-      Desktop.addInternalFrame(frame, getFrameTitle(), 400, 240);
+      Desktop.addInternalFrame(frame, getFrameTitle(), false, 400, 240);
     }
     else
     {
-      Desktop.addInternalFrame(frame, getFrameTitle(), 400, 180);
+      Desktop.addInternalFrame(frame, getFrameTitle(), false, 400, 180);
     }
   }
 
@@ -358,26 +456,46 @@ public class SequenceFetcher extends JPanel implements Runnable
     jPanel1.add(example);
     jPanel1.add(clear);
     jPanel1.add(close);
-    jPanel3.add(jPanel2, java.awt.BorderLayout.CENTER);
     jPanel2.setLayout(borderLayout3);
-    databaseButt = database.getDatabaseSelectorButton();
+    databaseButt = /*database.getDatabaseSelectorButton();
+                   final JButton viewdbs =*/new JButton(
+            MessageManager.getString("action.select_ddbb"));
+    databaseButt.addActionListener(new ActionListener()
+    {
+
+      @Override
+      public void actionPerformed(ActionEvent arg0)
+      {
+        hidePanel();
+        database.showDialog();
+      }
+    });
     databaseButt.setFont(JvSwingUtils.getLabelFont());
     database.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        debounceTrap++;
         String currentSelection = database.getSelectedItem();
-        if (!currentSelection.equalsIgnoreCase("pdb"))
+        if (currentSelection == null)
         {
-          otherSourceAction();
+          close_actionPerformed(null);
         }
-        if (currentSelection.equalsIgnoreCase("pdb")
-                && (database.action == KeyEvent.VK_ENTER || ((debounceTrap % 2) == 0)))
+
+        showPanel();
+
+        if ("pdb".equalsIgnoreCase(currentSelection))
         {
           pdbSourceAction();
         }
+        else if ("uniprot".equalsIgnoreCase(currentSelection))
+        {
+          uniprotSourceAction();
+        }
+        else
+        {
+          otherSourceAction();
+        }
         database.action = -1;
       }
     });
@@ -395,13 +513,19 @@ public class SequenceFetcher extends JPanel implements Runnable
     this.add(jPanel3, java.awt.BorderLayout.CENTER);
     this.add(jPanel2, java.awt.BorderLayout.NORTH);
     jScrollPane1.getViewport().add(textArea);
-
   }
 
   private void pdbSourceAction()
   {
     databaseButt.setText(database.getSelectedItem());
-    new PDBSearchPanel(this);
+    new PDBFTSPanel(this);
+    frame.dispose();
+  }
+
+  private void uniprotSourceAction()
+  {
+    databaseButt.setText(database.getSelectedItem());
+    new UniprotFTSPanel(this);
     frame.dispose();
   }
 
@@ -518,30 +642,34 @@ public class SequenceFetcher extends JPanel implements Runnable
       resetDialog();
       return;
     }
+    // TODO: Refactor to GUI independent code and write tests.
     // indicate if successive sources should be merged into one alignment.
     boolean addToLast = false;
-    ArrayList<String> aresultq = new ArrayList<String>(), presultTitle = new ArrayList<String>();
-    ArrayList<AlignmentI> presult = new ArrayList<AlignmentI>(), aresult = new ArrayList<AlignmentI>();
+    List<String> aresultq = new ArrayList<String>();
+    List<String> presultTitle = new ArrayList<String>();
+    List<AlignmentI> presult = new ArrayList<AlignmentI>();
+    List<AlignmentI> aresult = new ArrayList<AlignmentI>();
     Iterator<DbSourceProxy> proxies = database.getSelectedSources()
             .iterator();
     String[] qries;
-    List<String> nextfetch = Arrays.asList(qries = textArea.getText()
+    List<String> nextFetch = Arrays.asList(qries = textArea.getText()
             .split(";"));
     Iterator<String> en = Arrays.asList(new String[0]).iterator();
     int nqueries = qries.length;
-    while (proxies.hasNext() && (en.hasNext() || nextfetch.size() > 0))
+
+    FeatureSettingsModelI preferredFeatureColours = null;
+    while (proxies.hasNext() && (en.hasNext() || nextFetch.size() > 0))
     {
-      if (!en.hasNext() && nextfetch.size() > 0)
+      if (!en.hasNext() && nextFetch.size() > 0)
       {
-        en = nextfetch.iterator();
-        nqueries = nextfetch.size();
+        en = nextFetch.iterator();
+        nqueries = nextFetch.size();
         // save the remaining queries in the original array
-        qries = nextfetch.toArray(new String[nqueries]);
-        nextfetch = new ArrayList<String>();
+        qries = nextFetch.toArray(new String[nqueries]);
+        nextFetch = new ArrayList<String>();
       }
 
       DbSourceProxy proxy = proxies.next();
-      boolean isAliSource = false;
       try
       {
         // update status
@@ -552,136 +680,27 @@ public class SequenceFetcher extends JPanel implements Runnable
                             Integer.valueOf(nqueries).toString(),
                             proxy.getDbName() }), Thread.currentThread()
                         .hashCode());
-        isAliSource = proxy.isA(DBRefSource.ALIGNMENTDB);
-        if (proxy.getAccessionSeparator() == null)
+        if (proxy.getMaximumQueryCount() == 1)
         {
+          /*
+           * proxy only handles one accession id at a time
+           */
           while (en.hasNext())
           {
-            String item = en.next();
-            try
-            {
-              if (aresult != null)
-              {
-                try
-                {
-                  // give the server a chance to breathe
-                  Thread.sleep(5);
-                } catch (Exception e)
-                {
-                  //
-                }
-
-              }
-
-              AlignmentI indres = null;
-              try
-              {
-                indres = proxy.getSequenceRecords(item);
-              } catch (OutOfMemoryError oome)
-              {
-                new OOMWarning("fetching " + item + " from "
-                        + proxy.getDbName(), oome, this);
-              }
-              if (indres != null)
-              {
-                aresultq.add(item);
-                aresult.add(indres);
-              }
-              else
-              {
-                nextfetch.add(item);
-              }
-            } catch (Exception e)
+            String acc = en.next();
+            if (!fetchSingleAccession(proxy, acc, aresultq, aresult))
             {
-              jalview.bin.Cache.log.info("Error retrieving " + item
-                      + " from " + proxy.getDbName(), e);
-              nextfetch.add(item);
+              nextFetch.add(acc);
             }
           }
         }
         else
         {
-          StringBuffer multiacc = new StringBuffer();
-          ArrayList<String> tosend = new ArrayList<String>();
-          while (en.hasNext())
-          {
-            String nel = en.next();
-            tosend.add(nel);
-            multiacc.append(nel);
-            if (en.hasNext())
-            {
-              multiacc.append(proxy.getAccessionSeparator());
-            }
-          }
-          try
-          {
-            AlignmentI rslt;
-            SequenceI[] rs;
-            List<String> nores = new ArrayList<String>();
-            rslt = proxy.getSequenceRecords(multiacc.toString());
-            if (rslt == null || rslt.getHeight() == 0)
-            {
-              // no results - pass on all queries to next source
-              nextfetch.addAll(tosend);
-            }
-            else
-            {
-              aresultq.add(multiacc.toString());
-              aresult.add(rslt);
-
-              rs = rslt.getSequencesArray();
-              // search for each query in the dbrefs associated with each
-              // sequence
-              // returned.
-              // ones we do not find will be used to query next source (if any)
-              for (String q : tosend)
-              {
-                DBRefEntry dbr = new DBRefEntry(), found[] = null;
-                dbr.setSource(proxy.getDbSource());
-                dbr.setVersion(null);
-                if (proxy.getAccessionValidator() != null)
-                {
-                  Regex vgr = proxy.getAccessionValidator();
-                  vgr.search(q);
-                  if (vgr.numSubs() > 0)
-                  {
-                    dbr.setAccessionId(vgr.stringMatched(1));
-                  }
-                  else
-                  {
-                    dbr.setAccessionId(vgr.stringMatched());
-                  }
-                }
-                else
-                {
-                  dbr.setAccessionId(q);
-                }
-                boolean rfound = false;
-                for (int r = 0; r < rs.length; r++)
-                {
-                  if (rs[r] != null
-                          && (found = DBRefUtils.searchRefs(
-                                  rs[r].getDBRef(), dbr)) != null
-                          && found.length > 0)
-                  {
-                    rfound = true;
-                    rs[r] = null;
-                    continue;
-                  }
-                }
-                if (!rfound)
-                {
-                  nextfetch.add(q);
-                }
-              }
-            }
-          } catch (OutOfMemoryError oome)
-          {
-            new OOMWarning("fetching " + multiacc + " from "
-                    + database.getSelectedItem(), oome, this);
-          }
+          /*
+           * proxy can fetch multiple accessions at one time
+           */
+          fetchMultipleAccessions(proxy, en, aresultq, aresult, nextFetch);
         }
-
       } catch (Exception e)
       {
         showErrorMessage("Error retrieving " + textArea.getText()
@@ -694,7 +713,6 @@ public class SequenceFetcher extends JPanel implements Runnable
         e.printStackTrace();
       } catch (OutOfMemoryError e)
       {
-        // resets dialog box - so we don't use OOMwarning here.
         showErrorMessage("Out of Memory when retrieving "
                 + textArea.getText()
                 + " from "
@@ -707,11 +725,19 @@ public class SequenceFetcher extends JPanel implements Runnable
                 + " from " + database.getSelectedItem());
         e.printStackTrace();
       }
+
       // Stack results ready for opening in alignment windows
       if (aresult != null && aresult.size() > 0)
       {
+        FeatureSettingsModelI proxyColourScheme = proxy
+                .getFeatureColourScheme();
+        if (proxyColourScheme != null)
+        {
+          preferredFeatureColours = proxyColourScheme;
+        }
+
         AlignmentI ar = null;
-        if (isAliSource)
+        if (proxy.isAlignmentSource())
         {
           addToLast = false;
           // new window for each result
@@ -741,7 +767,6 @@ public class SequenceFetcher extends JPanel implements Runnable
             {
               ar.append(aresult.remove(0));
             }
-            ;
           }
           addToLast = true;
           presult.add(ar);
@@ -760,18 +785,19 @@ public class SequenceFetcher extends JPanel implements Runnable
     // process results
     while (presult.size() > 0)
     {
-      parseResult(presult.remove(0), presultTitle.remove(0), null);
+      parseResult(presult.remove(0), presultTitle.remove(0), null,
+              preferredFeatureColours);
     }
     // only remove visual delay after we finished parsing.
     guiWindow.setProgressBar(null, Thread.currentThread().hashCode());
-    if (nextfetch.size() > 0)
+    if (nextFetch.size() > 0)
     {
       StringBuffer sb = new StringBuffer();
       sb.append("Didn't retrieve the following "
-              + (nextfetch.size() == 1 ? "query" : nextfetch.size()
+              + (nextFetch.size() == 1 ? "query" : nextFetch.size()
                       + " queries") + ": \n");
       int l = sb.length(), lr = 0;
-      for (String s : nextfetch)
+      for (String s : nextFetch)
       {
         if (l != sb.length())
         {
@@ -788,33 +814,157 @@ public class SequenceFetcher extends JPanel implements Runnable
     resetDialog();
   }
 
-  AlignmentI parseResult(String result, String title)
+  /**
+   * Tries to fetch one or more accession ids from the database proxy
+   * 
+   * @param proxy
+   * @param accessions
+   *          the queries to fetch
+   * @param aresultq
+   *          a successful queries list to add to
+   * @param aresult
+   *          a list of retrieved alignments to add to
+   * @param nextFetch
+   *          failed queries are added to this list
+   * @throws Exception
+   */
+  void fetchMultipleAccessions(DbSourceProxy proxy,
+          Iterator<String> accessions, List<String> aresultq,
+          List<AlignmentI> aresult, List<String> nextFetch)
+          throws Exception
   {
-    String format = new IdentifyFile().Identify(result, "Paste");
-    AlignmentI sequences = null;
-    if (FormatAdapter.isValidFormat(format))
+    StringBuilder multiacc = new StringBuilder();
+    List<String> tosend = new ArrayList<String>();
+    while (accessions.hasNext())
     {
-      sequences = null;
-      try
-      {
-        sequences = new FormatAdapter().readFile(result.toString(),
-                "Paste", format);
-      } catch (Exception ex)
+      String nel = accessions.next();
+      tosend.add(nel);
+      multiacc.append(nel);
+      if (accessions.hasNext())
       {
+        multiacc.append(proxy.getAccessionSeparator());
       }
+    }
 
-      if (sequences != null)
+    try
+    {
+      String query = multiacc.toString();
+      AlignmentI rslt = proxy.getSequenceRecords(query);
+      if (rslt == null || rslt.getHeight() == 0)
       {
-        return parseResult(sequences, title, format);
+        // no results - pass on all queries to next source
+        nextFetch.addAll(tosend);
       }
+      else
+      {
+        aresultq.add(query);
+        aresult.add(rslt);
+        if (tosend.size() > 1)
+        {
+          checkResultForQueries(rslt, tosend, nextFetch, proxy);
+        }
+      }
+    } catch (OutOfMemoryError oome)
+    {
+      new OOMWarning("fetching " + multiacc + " from "
+              + database.getSelectedItem(), oome, this);
     }
-    else
+  }
+
+  /**
+   * Query for a single accession id via the database proxy
+   * 
+   * @param proxy
+   * @param accession
+   * @param aresultq
+   *          a list of successful queries to add to
+   * @param aresult
+   *          a list of retrieved alignments to add to
+   * @return true if the fetch was successful, else false
+   */
+  boolean fetchSingleAccession(DbSourceProxy proxy, String accession,
+          List<String> aresultq, List<AlignmentI> aresult)
+  {
+    boolean success = false;
+    try
     {
-      showErrorMessage("Error retrieving " + textArea.getText() + " from "
-              + database.getSelectedItem());
+      if (aresult != null)
+      {
+        try
+        {
+          // give the server a chance to breathe
+          Thread.sleep(5);
+        } catch (Exception e)
+        {
+          //
+        }
+      }
+
+      AlignmentI indres = null;
+      try
+      {
+        indres = proxy.getSequenceRecords(accession);
+      } catch (OutOfMemoryError oome)
+      {
+        new OOMWarning("fetching " + accession + " from "
+                + proxy.getDbName(), oome, this);
+      }
+      if (indres != null)
+      {
+        aresultq.add(accession);
+        aresult.add(indres);
+        success = true;
+      }
+    } catch (Exception e)
+    {
+      Cache.log.info(
+              "Error retrieving " + accession + " from "
+                      + proxy.getDbName(), e);
     }
+    return success;
+  }
 
-    return null;
+  /**
+   * Checks which of the queries were successfully retrieved by searching the
+   * DBRefs of the retrieved sequences for a match. Any not found are added to
+   * the 'nextFetch' list.
+   * 
+   * @param rslt
+   * @param queries
+   * @param nextFetch
+   * @param proxy
+   */
+  void checkResultForQueries(AlignmentI rslt, List<String> queries,
+          List<String> nextFetch, DbSourceProxy proxy)
+  {
+    SequenceI[] rs = rslt.getSequencesArray();
+
+    for (String q : queries)
+    {
+      DBRefEntry dbr = new DBRefEntry();
+      dbr.setSource(proxy.getDbSource());
+      dbr.setVersion(null);
+      String accId = proxy.getAccessionIdFromQuery(q);
+      dbr.setAccessionId(accId);
+      boolean rfound = false;
+      for (int r = 0; r < rs.length; r++)
+      {
+        if (rs[r] != null)
+        {
+          List<DBRefEntry> found = DBRefUtils.searchRefs(rs[r].getDBRefs(),
+                  accId);
+          if (!found.isEmpty())
+          {
+            rfound = true;
+            break;
+          }
+        }
+      }
+      if (!rfound)
+      {
+        nextFetch.add(q);
+      }
+    }
   }
 
   /**
@@ -828,7 +978,8 @@ public class SequenceFetcher extends JPanel implements Runnable
   }
 
   AlignmentI parseResult(AlignmentI al, String title,
-          String currentFileFormat)
+          String currentFileFormat,
+          FeatureSettingsModelI preferredFeatureColours)
   {
 
     if (al != null && al.getHeight() > 0)
@@ -866,6 +1017,19 @@ public class SequenceFetcher extends JPanel implements Runnable
 
           }
         }
+
+        if (preferredFeatureColours != null)
+        {
+          af.getViewport().applyFeaturesStyle(preferredFeatureColours);
+        }
+        if (Cache.getDefault("HIDE_INTRONS", true))
+        {
+          af.hideFeatureColumns(SequenceOntologyI.EXON, false);
+        }
+        if (newAlframes != null)
+        {
+          newAlframes.add(af);
+        }
         Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH,
                 AlignFrame.DEFAULT_HEIGHT);
 
@@ -874,8 +1038,7 @@ public class SequenceFetcher extends JPanel implements Runnable
 
         try
         {
-          af.setMaximum(jalview.bin.Cache.getDefault("SHOW_FULLSCREEN",
-                  false));
+          af.setMaximum(Cache.getDefault("SHOW_FULLSCREEN", false));
         } catch (Exception ex)
         {
         }
@@ -912,4 +1075,22 @@ public class SequenceFetcher extends JPanel implements Runnable
   {
     this.progressIndicator = progressIndicator;
   }
+
+  /**
+   * Make this panel visible (after a selection has been made in the database
+   * chooser)
+   */
+  void showPanel()
+  {
+    frame.setVisible(true);
+  }
+
+  /**
+   * Hide this panel (on clicking the database button to open the database
+   * chooser)
+   */
+  void hidePanel()
+  {
+    frame.setVisible(false);
+  }
 }
diff --git a/src/jalview/gui/SequenceRenderer.java b/src/jalview/gui/SequenceRenderer.java
index 8c4ead8..18f0d4a 100644
--- a/src/jalview/gui/SequenceRenderer.java
+++ b/src/jalview/gui/SequenceRenderer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,12 +29,6 @@ import java.awt.Color;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
 
-/**
- * DOCUMENT ME!
- * 
- * @author $author$
- * @version $Revision$
- */
 public class SequenceRenderer implements jalview.api.SequenceRenderer
 {
   final static int CHAR_TO_UPPER = 'A' - 'a';
@@ -91,6 +85,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   @Override
   public Color getResidueBoxColour(SequenceI seq, int i)
   {
+    // rate limiting step when rendering overview for lots of groups
     allGroups = av.getAlignment().findAllGroups(seq);
 
     if (inCurrentSequenceGroup(i))
@@ -417,7 +412,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
           }
           if (!isarep && av.getShowUnconserved())
           {
-            s = getDisplayChar(srep, i, s, '.', currentSequenceGroup);
+            s = getDisplayChar(srep, i, s, '.', null);
 
           }
 
@@ -447,12 +442,17 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   {
     // TODO - use currentSequenceGroup rather than alignment
     // currentSequenceGroup.getConsensus()
-    char conschar = (usesrep) ? (currentGroup == null ? av.getAlignment()
+    char conschar = (usesrep) ? (currentGroup == null
+            || position < currentGroup.getStartRes()
+            || position > currentGroup.getEndRes() ? av.getAlignment()
             .getSeqrep().getCharAt(position)
             : (currentGroup.getSeqrep() != null ? currentGroup.getSeqrep()
                     .getCharAt(position) : av.getAlignment().getSeqrep()
                     .getCharAt(position)))
-            : (currentGroup != null && currentGroup.getConsensus() != null) ? currentGroup
+            : (currentGroup != null && currentGroup.getConsensus() != null
+                    && position >= currentGroup.getStartRes()
+                    && position <= currentGroup.getEndRes() && currentGroup
+                    .getConsensus().annotations.length > position) ? currentGroup
                     .getConsensus().annotations[position].displayCharacter
                     .charAt(0)
                     : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter
diff --git a/src/jalview/gui/SliderPanel.java b/src/jalview/gui/SliderPanel.java
index 02a8ed7..0476347 100644
--- a/src/jalview/gui/SliderPanel.java
+++ b/src/jalview/gui/SliderPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -219,8 +219,9 @@ public class SliderPanel extends GSliderPanel
       pid.cs = cs;
     }
 
-    PIDSlider.setTitle(MessageManager
-            .formatMessage("label.percentage_identity_thereshold",
+    PIDSlider
+            .setTitle(MessageManager.formatMessage(
+                    "label.percentage_identity_threshold",
                     new String[] { source }));
 
     if (ap.av.getAlignment().getGroups() != null)
diff --git a/src/jalview/gui/SplashScreen.java b/src/jalview/gui/SplashScreen.java
index 9c810d0..3d41eb7 100644
--- a/src/jalview/gui/SplashScreen.java
+++ b/src/jalview/gui/SplashScreen.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/SplitFrame.java b/src/jalview/gui/SplitFrame.java
index 4e2187e..9dd018c 100644
--- a/src/jalview/gui/SplitFrame.java
+++ b/src/jalview/gui/SplitFrame.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,6 +26,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.jbgui.GAlignFrame;
 import jalview.jbgui.GSplitFrame;
 import jalview.structure.StructureSelectionManager;
+import jalview.util.Platform;
 import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Component;
@@ -36,6 +37,8 @@ import java.awt.event.KeyAdapter;
 import java.awt.event.KeyEvent;
 import java.awt.event.KeyListener;
 import java.beans.PropertyVetoException;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Map.Entry;
 
 import javax.swing.AbstractAction;
@@ -60,6 +63,16 @@ import javax.swing.event.InternalFrameEvent;
  */
 public class SplitFrame extends GSplitFrame implements SplitContainerI
 {
+  private static final int WINDOWS_INSETS_WIDTH = 28; // tbc
+
+  private static final int MAC_INSETS_WIDTH = 28;
+
+  private static final int WINDOWS_INSETS_HEIGHT = 50; // tbc
+
+  private static final int MAC_INSETS_HEIGHT = 50;
+
+  private static final int DESKTOP_DECORATORS_HEIGHT = 65;
+
   private static final long serialVersionUID = 1L;
 
   public SplitFrame(GAlignFrame top, GAlignFrame bottom)
@@ -81,12 +94,19 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
     ((AlignFrame) getTopFrame()).getViewport().setCodingComplement(
             ((AlignFrame) getBottomFrame()).getViewport());
 
-    int width = ((AlignFrame) getTopFrame()).getWidth();
-    // about 50 pixels for the SplitFrame's title bar etc
+    /*
+     * estimate width and height of SplitFrame; this.getInsets() doesn't seem to
+     * give the full additional size (a few pixels short)
+     */
+    int widthFudge = Platform.isAMac() ? MAC_INSETS_WIDTH
+            : WINDOWS_INSETS_WIDTH;
+    int heightFudge = Platform.isAMac() ? MAC_INSETS_HEIGHT
+            : WINDOWS_INSETS_HEIGHT;
+    int width = ((AlignFrame) getTopFrame()).getWidth() + widthFudge;
     int height = ((AlignFrame) getTopFrame()).getHeight()
-            + ((AlignFrame) getBottomFrame()).getHeight() + 50;
-    // about 65 pixels for Desktop decorators on Windows
-    height = Math.min(height, Desktop.instance.getHeight() - 65);
+            + ((AlignFrame) getBottomFrame()).getHeight() + DIVIDER_SIZE
+            + heightFudge;
+    height = fitHeightToDesktop(height);
     setSize(width, height);
 
     adjustLayout();
@@ -101,6 +121,28 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
   }
 
   /**
+   * Reduce the height if too large to fit in the Desktop. Also adjust the
+   * divider location in proportion.
+   * 
+   * @param height
+   *          in pixels
+   * @return original or reduced height
+   */
+  public int fitHeightToDesktop(int height)
+  {
+    // allow about 65 pixels for Desktop decorators on Windows
+
+    int newHeight = Math.min(height, Desktop.instance.getHeight()
+            - DESKTOP_DECORATORS_HEIGHT);
+    if (newHeight != height)
+    {
+      int oldDividerLocation = getDividerLocation();
+      setDividerLocation(oldDividerLocation * newHeight / height);
+    }
+    return newHeight;
+  }
+
+  /**
    * Set the top and bottom frames to listen to each others Commands (e.g. Edit,
    * Order).
    */
@@ -155,6 +197,46 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
   }
 
   /**
+   * Adjust the divider for a sensible split of the real estate (for example,
+   * when many transcripts are shown with a single protein). This should only be
+   * called after the split pane has been laid out (made visible) so it has a
+   * height.
+   */
+  protected void adjustDivider()
+  {
+    final AlignViewport topViewport = ((AlignFrame) getTopFrame()).viewport;
+    final AlignViewport bottomViewport = ((AlignFrame) getBottomFrame()).viewport;
+    final AlignmentI topAlignment = topViewport.getAlignment();
+    final AlignmentI bottomAlignment = bottomViewport.getAlignment();
+    boolean topAnnotations = topViewport.isShowAnnotation();
+    boolean bottomAnnotations = bottomViewport.isShowAnnotation();
+    // TODO need number of visible sequences here, not #sequences - how?
+    int topCount = topAlignment.getHeight();
+    int bottomCount = bottomAlignment.getHeight();
+    int topCharHeight = topViewport.getViewStyle().getCharHeight();
+    int bottomCharHeight = bottomViewport.getViewStyle().getCharHeight();
+
+    /*
+     * estimate ratio of (topFrameContent / bottomFrameContent)
+     */
+    int insets = Platform.isAMac() ? MAC_INSETS_HEIGHT
+            : WINDOWS_INSETS_HEIGHT;
+    // allow 3 'rows' for scale, scrollbar, status bar
+    int topHeight = insets + (3 + topCount) * topCharHeight
+            + (topAnnotations ? topViewport.calcPanelHeight() : 0);
+    int bottomHeight = insets + (3 + bottomCount) * bottomCharHeight
+            + (bottomAnnotations ? bottomViewport.calcPanelHeight() : 0);
+    double ratio = ((double) topHeight) / (topHeight + bottomHeight);
+
+    /*
+     * limit to 0.2 <= ratio <= 0.8 to avoid concealing all sequences
+     */
+    ratio = Math.min(ratio, 0.8d);
+    ratio = Math.max(ratio, 0.2d);
+    setRelativeDividerLocation(ratio);
+  }
+
+  /**
    * Add a listener to tidy up when the frame is closed.
    */
   protected void addCloseFrameListener()
@@ -256,6 +338,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
         actioned = true;
         e.consume();
       }
+      break;
     default:
     }
     return actioned;
@@ -632,6 +715,18 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
   }
 
   /**
+   * return the AlignFrames held by this container
+   * 
+   * @return { Top alignFrame (Usually CDS), Bottom AlignFrame (Usually
+   *         Protein)}
+   */
+  public List<AlignFrame> getAlignFrames()
+  {
+    return Arrays.asList(new AlignFrame[] { (AlignFrame) getTopFrame(),
+        (AlignFrame) getBottomFrame() });
+  }
+
+  /**
    * Replace Cmd-F Find action with our version. This is necessary because the
    * 'default' Finder searches in the first AlignFrame it finds. We need it to
    * search in the half of the SplitFrame that has the mouse.
diff --git a/src/jalview/gui/StructureChooser.java b/src/jalview/gui/StructureChooser.java
index 347c1b2..2ed2f43 100644
--- a/src/jalview/gui/StructureChooser.java
+++ b/src/jalview/gui/StructureChooser.java
@@ -1,7 +1,6 @@
 /*
-
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,18 +21,23 @@
 
 package jalview.gui;
 
+import jalview.bin.Jalview;
 import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.service.pdb.PDBFTSRestClient;
 import jalview.jbgui.GStructureChooser;
-import jalview.jbgui.PDBDocFieldPreferences;
+import jalview.structure.StructureMapping;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
-import jalview.ws.dbsources.PDBRestClient;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
-import jalview.ws.uimodel.PDBRestRequest;
-import jalview.ws.uimodel.PDBRestResponse;
-import jalview.ws.uimodel.PDBRestResponse.PDBResponseSummary;
+import jalview.ws.DBRefFetcher;
+import jalview.ws.sifts.SiftsSettings;
 
 import java.awt.event.ItemEvent;
 import java.util.ArrayList;
@@ -41,6 +45,9 @@ import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.Vector;
 
 import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
@@ -55,26 +62,29 @@ import javax.swing.table.AbstractTableModel;
  *
  */
 @SuppressWarnings("serial")
-public class StructureChooser extends GStructureChooser
+public class StructureChooser extends GStructureChooser implements
+        IProgressIndicator
 {
-  private boolean structuresDiscovered = false;
-
   private SequenceI selectedSequence;
 
   private SequenceI[] selectedSequences;
 
   private IProgressIndicator progressIndicator;
 
-  private Collection<PDBResponseSummary> discoveredStructuresSet;
+  private Collection<FTSData> discoveredStructuresSet;
 
-  private PDBRestRequest lastPdbRequest;
+  private FTSRestRequest lastPdbRequest;
 
-  private PDBRestClient pdbRestCleint;
+  private FTSRestClientI pdbRestCleint;
 
   private String selectedPdbFileName;
 
   private boolean isValidPBDEntry;
 
+  private boolean cachedPDBExists;
+
+  private static int MAX_QLENGHT = 7820;
+
   public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
           AlignmentPanel ap)
   {
@@ -90,6 +100,13 @@ public class StructureChooser extends GStructureChooser
    */
   public void init()
   {
+    if (!Jalview.isHeadlessMode())
+    {
+      progressBar = new ProgressBar(this.statusPanel, this.statusBar);
+    }
+
+    // ensure a filter option is in force for search
+    populateFilterComboBox(true, cachedPDBExists);
     Thread discoverPDBStructuresThread = new Thread(new Runnable()
     {
       @Override
@@ -104,7 +121,8 @@ public class StructureChooser extends GStructureChooser
                 .getString("status.searching_for_pdb_structures"),
                 startTime);
         fetchStructuresMetaData();
-        populateFilterComboBox();
+        // revise filter options if no results were found
+        populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
         updateProgressIndicator(null, startTime);
         mainFrame.setVisible(true);
         updateCurrentView();
@@ -136,22 +154,26 @@ public class StructureChooser extends GStructureChooser
   public void fetchStructuresMetaData()
   {
     long startTime = System.currentTimeMillis();
-    Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
+    pdbRestCleint = PDBFTSRestClient.getInstance();
+    Collection<FTSDataColumnI> wantedFields = pdbDocFieldPrefs
             .getStructureSummaryFields();
 
-    discoveredStructuresSet = new LinkedHashSet<PDBResponseSummary>();
+    discoveredStructuresSet = new LinkedHashSet<FTSData>();
     HashSet<String> errors = new HashSet<String>();
     for (SequenceI seq : selectedSequences)
     {
-      PDBRestRequest pdbRequest = new PDBRestRequest();
+      FTSRestRequest pdbRequest = new FTSRestRequest();
       pdbRequest.setAllowEmptySeq(false);
       pdbRequest.setResponseSize(500);
-      pdbRequest.setFieldToSearchBy("(text:");
+      pdbRequest.setFieldToSearchBy("(");
+      FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
+              .getSelectedItem());
+      pdbRequest.setFieldToSortBy(selectedFilterOpt.getValue(),
+              !chk_invertFilter.isSelected());
       pdbRequest.setWantedFields(wantedFields);
       pdbRequest.setSearchTerm(buildQuery(seq) + ")");
       pdbRequest.setAssociatedSequence(seq);
-      pdbRestCleint = new PDBRestClient();
-      PDBRestResponse resultList;
+      FTSRestResponse resultList;
       try
       {
         resultList = pdbRestCleint.executeRequest(pdbRequest);
@@ -175,9 +197,9 @@ public class StructureChooser extends GStructureChooser
     if (discoveredStructuresSet != null
             && !discoveredStructuresSet.isEmpty())
     {
-      tbl_summary.setModel(PDBRestResponse.getTableModel(lastPdbRequest,
-              discoveredStructuresSet));
-      structuresDiscovered = true;
+      getResultTable().setModel(
+              FTSRestResponse.getTableModel(lastPdbRequest,
+                      discoveredStructuresSet));
       noOfStructuresFound = discoveredStructuresSet.size();
       mainFrame.setTitle(MessageManager.formatMessage(
               "label.structure_chooser_no_of_structures",
@@ -219,7 +241,7 @@ public class StructureChooser extends GStructureChooser
         }
       }
     }
-
+    cachedPDBExists = !entries.isEmpty();
     PDBEntryTableModel tableModelx = new PDBEntryTableModel(entries);
     tbl_local_pdb.setModel(tableModelx);
   }
@@ -234,63 +256,100 @@ public class StructureChooser extends GStructureChooser
 
   public static String buildQuery(SequenceI seq)
   {
-    HashSet<String> seqRefs = new LinkedHashSet<String>();
-    String seqName = seq.getName();
-    String[] names = seqName.toLowerCase().split("\\|");
-    for (String name : names)
-    {
-      // System.out.println("Found name : " + name);
-      name.trim();
-      if (isValidSeqName(name))
-      {
-        seqRefs.add(name);
-      }
-    }
+    boolean isPDBRefsFound = false;
+    boolean isUniProtRefsFound = false;
+    StringBuilder queryBuilder = new StringBuilder();
+    Set<String> seqRefs = new LinkedHashSet<String>();
 
-    if (seq.getAllPDBEntries() != null)
+    if (seq.getAllPDBEntries() != null
+            && queryBuilder.length() < MAX_QLENGHT)
     {
       for (PDBEntry entry : seq.getAllPDBEntries())
       {
         if (isValidSeqName(entry.getId()))
         {
-          seqRefs.add(entry.getId());
+          queryBuilder.append("pdb_id:")
+                  .append(entry.getId().toLowerCase()).append(" OR ");
+          isPDBRefsFound = true;
         }
       }
     }
 
-    if (seq.getDBRef() != null && seq.getDBRef().length != 0)
+    if (seq.getDBRefs() != null && seq.getDBRefs().length != 0)
     {
-      int count = 0;
-      for (DBRefEntry dbRef : seq.getDBRef())
+      for (DBRefEntry dbRef : seq.getDBRefs())
       {
-        if (isValidSeqName(getDBRefId(dbRef)))
-        {
-          seqRefs.add(getDBRefId(dbRef));
-        }
-        ++count;
-        if (count > 10)
+        if (isValidSeqName(getDBRefId(dbRef))
+                && queryBuilder.length() < MAX_QLENGHT)
         {
-          break;
+          if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
+          {
+            queryBuilder.append("uniprot_accession:")
+                    .append(getDBRefId(dbRef)).append(" OR ");
+            queryBuilder.append("uniprot_id:").append(getDBRefId(dbRef))
+                    .append(" OR ");
+            isUniProtRefsFound = true;
+          }
+          else if (dbRef.getSource().equalsIgnoreCase(DBRefSource.PDB))
+          {
+
+            queryBuilder.append("pdb_id:")
+                    .append(getDBRefId(dbRef).toLowerCase()).append(" OR ");
+            isPDBRefsFound = true;
+          }
+          else
+          {
+            seqRefs.add(getDBRefId(dbRef));
+          }
         }
       }
     }
 
-    StringBuilder queryBuilder = new StringBuilder();
-    for (String seqRef : seqRefs)
+    if (!isPDBRefsFound && !isUniProtRefsFound)
     {
-      queryBuilder.append("text:").append(seqRef).append(" OR ");
+      String seqName = seq.getName();
+      seqName = sanitizeSeqName(seqName);
+      String[] names = seqName.toLowerCase().split("\\|");
+      for (String name : names)
+      {
+        // System.out.println("Found name : " + name);
+        name.trim();
+        if (isValidSeqName(name))
+        {
+          seqRefs.add(name);
+        }
+      }
+
+      for (String seqRef : seqRefs)
+      {
+        queryBuilder.append("text:").append(seqRef).append(" OR ");
+      }
     }
-    int endIndex = queryBuilder.lastIndexOf(" OR ");
 
+    int endIndex = queryBuilder.lastIndexOf(" OR ");
     if (queryBuilder.toString().length() < 6)
     {
       return null;
     }
-    String query = queryBuilder.toString().substring(5, endIndex);
+    String query = queryBuilder.toString().substring(0, endIndex);
     return query;
   }
 
   /**
+   * Remove the following special characters from input string +, -, &, !, (, ),
+   * {, }, [, ], ^, ", ~, *, ?, :, \
+   * 
+   * @param seqName
+   * @return
+   */
+  static String sanitizeSeqName(String seqName)
+  {
+    Objects.requireNonNull(seqName);
+    return seqName.replaceAll("\\[\\d*\\]", "")
+            .replaceAll("[^\\dA-Za-z|_]", "").replaceAll("\\s+", "+");
+  }
+
+  /**
    * Ensures sequence ref names are not less than 3 characters and does not
    * contain a database name
    * 
@@ -340,24 +399,40 @@ public class StructureChooser extends GStructureChooser
       public void run()
       {
         long startTime = System.currentTimeMillis();
+        pdbRestCleint = PDBFTSRestClient.getInstance();
         lbl_loading.setVisible(true);
-        Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
+        Collection<FTSDataColumnI> wantedFields = pdbDocFieldPrefs
                 .getStructureSummaryFields();
-        Collection<PDBResponseSummary> filteredResponse = new HashSet<PDBResponseSummary>();
+        Collection<FTSData> filteredResponse = new HashSet<FTSData>();
         HashSet<String> errors = new HashSet<String>();
+
         for (SequenceI seq : selectedSequences)
         {
-          PDBRestRequest pdbRequest = new PDBRestRequest();
-          pdbRequest.setAllowEmptySeq(false);
-          pdbRequest.setResponseSize(1);
-          pdbRequest.setFieldToSearchBy("(text:");
-          pdbRequest.setFieldToSortBy(fieldToFilterBy,
-                  !chk_invertFilter.isSelected());
-          pdbRequest.setSearchTerm(buildQuery(seq) + ")");
-          pdbRequest.setWantedFields(wantedFields);
-          pdbRequest.setAssociatedSequence(seq);
-          pdbRestCleint = new PDBRestClient();
-          PDBRestResponse resultList;
+          FTSRestRequest pdbRequest = new FTSRestRequest();
+          if (fieldToFilterBy.equalsIgnoreCase("uniprot_coverage"))
+          {
+            pdbRequest.setAllowEmptySeq(false);
+            pdbRequest.setResponseSize(1);
+            pdbRequest.setFieldToSearchBy("(");
+            pdbRequest.setSearchTerm(buildQuery(seq) + ")");
+            pdbRequest.setWantedFields(wantedFields);
+            pdbRequest.setAssociatedSequence(seq);
+            pdbRequest.setFacet(true);
+            pdbRequest.setFacetPivot(fieldToFilterBy + ",entry_entity");
+            pdbRequest.setFacetPivotMinCount(1);
+          }
+          else
+          {
+            pdbRequest.setAllowEmptySeq(false);
+            pdbRequest.setResponseSize(1);
+            pdbRequest.setFieldToSearchBy("(");
+            pdbRequest.setFieldToSortBy(fieldToFilterBy,
+                    !chk_invertFilter.isSelected());
+            pdbRequest.setSearchTerm(buildQuery(seq) + ")");
+            pdbRequest.setWantedFields(wantedFields);
+            pdbRequest.setAssociatedSequence(seq);
+          }
+          FTSRestResponse resultList;
           try
           {
             resultList = pdbRestCleint.executeRequest(pdbRequest);
@@ -380,14 +455,21 @@ public class StructureChooser extends GStructureChooser
         if (!filteredResponse.isEmpty())
         {
           final int filterResponseCount = filteredResponse.size();
-          Collection<PDBResponseSummary> reorderedStructuresSet = new LinkedHashSet<PDBResponseSummary>();
+          Collection<FTSData> reorderedStructuresSet = new LinkedHashSet<FTSData>();
           reorderedStructuresSet.addAll(filteredResponse);
           reorderedStructuresSet.addAll(discoveredStructuresSet);
-          tbl_summary.setModel(PDBRestResponse.getTableModel(
-                  lastPdbRequest, reorderedStructuresSet));
-
+          getResultTable().setModel(
+                  FTSRestResponse.getTableModel(lastPdbRequest,
+                          reorderedStructuresSet));
+
+          FTSRestResponse.configureTableColumn(getResultTable(),
+                  wantedFields, tempUserPrefs);
+          getResultTable().getColumn("Ref Sequence").setPreferredWidth(120);
+          getResultTable().getColumn("Ref Sequence").setMinWidth(100);
+          getResultTable().getColumn("Ref Sequence").setMaxWidth(200);
           // Update table selection model here
-          tbl_summary.addRowSelectionInterval(0, filterResponseCount - 1);
+          getResultTable().addRowSelectionInterval(0,
+                  filterResponseCount - 1);
           mainFrame.setTitle(MessageManager.formatMessage(
                   "label.structure_chooser_filter_time", totalTime));
         }
@@ -421,6 +503,7 @@ public class StructureChooser extends GStructureChooser
   /**
    * Handles action event for btn_pdbFromFile
    */
+  @Override
   public void pdbFromFile_actionPerformed()
   {
     jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
@@ -446,29 +529,42 @@ public class StructureChooser extends GStructureChooser
    * Populates the filter combo-box options dynamically depending on discovered
    * structures
    */
-  protected void populateFilterComboBox()
+  protected void populateFilterComboBox(boolean haveData,
+          boolean cachedPDBExists)
   {
-    if (isStructuresDiscovered())
+    /*
+     * temporarily suspend the change listener behaviour
+     */
+    cmb_filterOption.removeItemListener(this);
+
+    cmb_filterOption.removeAllItems();
+    if (haveData)
     {
       cmb_filterOption.addItem(new FilterOption("Best Quality",
-              PDBDocField.OVERALL_QUALITY.getCode(), VIEWS_FILTER));
-      cmb_filterOption.addItem(new FilterOption("Best UniProt Coverage",
-              PDBDocField.UNIPROT_COVERAGE.getCode(), VIEWS_FILTER));
-      cmb_filterOption.addItem(new FilterOption("Highest Resolution",
-              PDBDocField.RESOLUTION.getCode(), VIEWS_FILTER));
-      cmb_filterOption.addItem(new FilterOption("Highest Protein Chain",
-              PDBDocField.PROTEIN_CHAIN_COUNT.getCode(), VIEWS_FILTER));
-      cmb_filterOption.addItem(new FilterOption("Highest Bound Molecules",
-              PDBDocField.BOUND_MOLECULE_COUNT.getCode(), VIEWS_FILTER));
-      cmb_filterOption.addItem(new FilterOption("Highest Polymer Residues",
-              PDBDocField.POLYMER_RESIDUE_COUNT.getCode(), VIEWS_FILTER));
+              "overall_quality", VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Best Resolution",
+              "resolution", VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Most Protein Chain",
+              "number_of_protein_chains", VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Most Bound Molecules",
+              "number_of_bound_molecules", VIEWS_FILTER));
+      cmb_filterOption.addItem(new FilterOption("Most Polymer Residues",
+              "number_of_polymer_residues", VIEWS_FILTER));
     }
     cmb_filterOption.addItem(new FilterOption("Enter PDB Id", "-",
             VIEWS_ENTER_ID));
     cmb_filterOption.addItem(new FilterOption("From File", "-",
             VIEWS_FROM_FILE));
-    cmb_filterOption.addItem(new FilterOption("Cached PDB Entries", "-",
-            VIEWS_LOCAL_PDB));
+    FilterOption cachedOption = new FilterOption("Cached PDB Entries", "-",
+            VIEWS_LOCAL_PDB);
+    cmb_filterOption.addItem(cachedOption);
+
+    if (/*!haveData &&*/cachedPDBExists)
+    {
+      cmb_filterOption.setSelectedItem(cachedOption);
+    }
+
+    cmb_filterOption.addItemListener(this);
   }
 
   /**
@@ -504,6 +600,7 @@ public class StructureChooser extends GStructureChooser
    * Validates user selection and activates the view button if all parameters
    * are correct
    */
+  @Override
   public void validateSelections()
   {
     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
@@ -512,7 +609,7 @@ public class StructureChooser extends GStructureChooser
     String currentView = selectedFilterOpt.getView();
     if (currentView == VIEWS_FILTER)
     {
-      if (tbl_summary.getSelectedRows().length > 0)
+      if (getResultTable().getSelectedRows().length > 0)
       {
         btn_view.setEnabled(true);
       }
@@ -636,139 +733,228 @@ public class StructureChooser extends GStructureChooser
   @Override
   public void ok_ActionPerformed()
   {
-    FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
-            .getSelectedItem());
-    String currentView = selectedFilterOpt.getView();
-    if (currentView == VIEWS_FILTER)
+    final long progressSessionId = System.currentTimeMillis();
+    final StructureSelectionManager ssm = ap.getStructureSelectionManager();
+    final int preferredHeight = pnl_filter.getHeight();
+    ssm.setProgressIndicator(this);
+    ssm.setProgressSessionId(progressSessionId);
+    new Thread(new Runnable()
     {
-      int pdbIdColIndex = tbl_summary.getColumn(
-              PDBRestClient.PDBDocField.PDB_ID.getName()).getModelIndex();
-      int refSeqColIndex = tbl_summary.getColumn("Ref Sequence")
-              .getModelIndex();
-      int[] selectedRows = tbl_summary.getSelectedRows();
-      PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
-      int count = 0;
-      ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
-      for (int row : selectedRows)
+      @Override
+      public void run()
       {
-        String pdbIdStr = tbl_summary.getValueAt(row, pdbIdColIndex)
-                .toString();
-        SequenceI selectedSeq = (SequenceI) tbl_summary.getValueAt(row,
-                refSeqColIndex);
-        selectedSeqsToView.add(selectedSeq);
-        PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
-        if (pdbEntry == null)
+        FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
+                .getSelectedItem());
+        String currentView = selectedFilterOpt.getView();
+        if (currentView == VIEWS_FILTER)
         {
-          pdbEntry = new PDBEntry();
-          pdbEntry.setId(pdbIdStr);
-          pdbEntry.setType(PDBEntry.Type.PDB);
-          selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
+          int pdbIdColIndex = getResultTable().getColumn("PDB Id")
+                  .getModelIndex();
+          int refSeqColIndex = getResultTable().getColumn("Ref Sequence")
+                  .getModelIndex();
+          int[] selectedRows = getResultTable().getSelectedRows();
+          PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
+          int count = 0;
+          ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
+          for (int row : selectedRows)
+          {
+            String pdbIdStr = getResultTable().getValueAt(row,
+                    pdbIdColIndex).toString();
+            SequenceI selectedSeq = (SequenceI) getResultTable()
+                    .getValueAt(row, refSeqColIndex);
+            selectedSeqsToView.add(selectedSeq);
+            PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
+            if (pdbEntry == null)
+            {
+              pdbEntry = getFindEntry(pdbIdStr,
+                      selectedSeq.getAllPDBEntries());
+            }
+            if (pdbEntry == null)
+            {
+              pdbEntry = new PDBEntry();
+              pdbEntry.setId(pdbIdStr);
+              pdbEntry.setType(PDBEntry.Type.PDB);
+              selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
+            }
+            pdbEntriesToView[count++] = pdbEntry;
+          }
+          SequenceI[] selectedSeqs = selectedSeqsToView
+                  .toArray(new SequenceI[selectedSeqsToView.size()]);
+          launchStructureViewer(ssm, pdbEntriesToView, ap, selectedSeqs);
         }
-        pdbEntriesToView[count++] = pdbEntry;
-      }
-      SequenceI[] selectedSeqs = selectedSeqsToView
-              .toArray(new SequenceI[selectedSeqsToView.size()]);
-      launchStructureViewer(ap.getStructureSelectionManager(),
-              pdbEntriesToView, ap, selectedSeqs);
-    }
-    else if (currentView == VIEWS_LOCAL_PDB)
-    {
-      int[] selectedRows = tbl_local_pdb.getSelectedRows();
-      PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
-      int count = 0;
-      int pdbIdColIndex = tbl_local_pdb.getColumn(
-              PDBRestClient.PDBDocField.PDB_ID.getName()).getModelIndex();
-      int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence")
-              .getModelIndex();
-      ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
-      for (int row : selectedRows)
-      {
-        PDBEntry pdbEntry = (PDBEntry) tbl_local_pdb.getValueAt(row,
-                pdbIdColIndex);
-        pdbEntriesToView[count++] = pdbEntry;
-        SequenceI selectedSeq = (SequenceI) tbl_local_pdb.getValueAt(row,
-                refSeqColIndex);
-        selectedSeqsToView.add(selectedSeq);
-      }
-      SequenceI[] selectedSeqs = selectedSeqsToView
-              .toArray(new SequenceI[selectedSeqsToView.size()]);
-      launchStructureViewer(ap.getStructureSelectionManager(),
-              pdbEntriesToView, ap, selectedSeqs);
-    }
-    else if (currentView == VIEWS_ENTER_ID)
-    {
-      SequenceI userSelectedSeq = ((AssociateSeqOptions) idInputAssSeqPanel
-              .getCmb_assSeq().getSelectedItem()).getSequence();
-      if (userSelectedSeq != null)
-      {
-        selectedSequence = userSelectedSeq;
-      }
+        else if (currentView == VIEWS_LOCAL_PDB)
+        {
+          int[] selectedRows = tbl_local_pdb.getSelectedRows();
+          PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
+          int count = 0;
+          int pdbIdColIndex = tbl_local_pdb.getColumn("PDB Id")
+                  .getModelIndex();
+          int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence")
+                  .getModelIndex();
+          ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
+          for (int row : selectedRows)
+          {
+            PDBEntry pdbEntry = (PDBEntry) tbl_local_pdb.getValueAt(row,
+                    pdbIdColIndex);
+            pdbEntriesToView[count++] = pdbEntry;
+            SequenceI selectedSeq = (SequenceI) tbl_local_pdb.getValueAt(
+                    row, refSeqColIndex);
+            selectedSeqsToView.add(selectedSeq);
+          }
+          SequenceI[] selectedSeqs = selectedSeqsToView
+                  .toArray(new SequenceI[selectedSeqsToView.size()]);
+          launchStructureViewer(ssm, pdbEntriesToView, ap, selectedSeqs);
+        }
+        else if (currentView == VIEWS_ENTER_ID)
+        {
+          SequenceI userSelectedSeq = ((AssociateSeqOptions) idInputAssSeqPanel
+                  .getCmb_assSeq().getSelectedItem()).getSequence();
+          if (userSelectedSeq != null)
+          {
+            selectedSequence = userSelectedSeq;
+          }
 
-      String pdbIdStr = txt_search.getText();
-      PDBEntry pdbEntry = selectedSequence.getPDBEntry(pdbIdStr);
-      if (pdbEntry == null)
-      {
-        pdbEntry = new PDBEntry();
-        pdbEntry.setId(pdbIdStr);
-        pdbEntry.setType(PDBEntry.Type.PDB);
-        selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
+          String pdbIdStr = txt_search.getText();
+          PDBEntry pdbEntry = selectedSequence.getPDBEntry(pdbIdStr);
+          if (pdbEntry == null)
+          {
+            pdbEntry = new PDBEntry();
+            if (pdbIdStr.split(":").length > 1)
+            {
+              pdbEntry.setId(pdbIdStr.split(":")[0]);
+              pdbEntry.setChainCode(pdbIdStr.split(":")[1].toUpperCase());
+            }
+            else
+            {
+              pdbEntry.setId(pdbIdStr);
+            }
+            pdbEntry.setType(PDBEntry.Type.PDB);
+            selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
+          }
+
+          PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
+          launchStructureViewer(ssm, pdbEntriesToView, ap,
+                  new SequenceI[] { selectedSequence });
+        }
+        else if (currentView == VIEWS_FROM_FILE)
+        {
+          SequenceI userSelectedSeq = ((AssociateSeqOptions) fileChooserAssSeqPanel
+                  .getCmb_assSeq().getSelectedItem()).getSequence();
+          if (userSelectedSeq != null)
+          {
+            selectedSequence = userSelectedSeq;
+          }
+          PDBEntry fileEntry = new AssociatePdbFileWithSeq()
+                  .associatePdbWithSeq(selectedPdbFileName,
+                          jalview.io.AppletFormatAdapter.FILE,
+                          selectedSequence, true, Desktop.instance);
+
+          launchStructureViewer(ssm, new PDBEntry[] { fileEntry }, ap,
+                  new SequenceI[] { selectedSequence });
+        }
+        closeAction(preferredHeight);
       }
+    }).start();
+  }
 
-      PDBEntry[] pdbEntriesToView = new PDBEntry[] { pdbEntry };
-      launchStructureViewer(ap.getStructureSelectionManager(),
-              pdbEntriesToView, ap, new SequenceI[] { selectedSequence });
-    }
-    else if (currentView == VIEWS_FROM_FILE)
+  private PDBEntry getFindEntry(String id, Vector<PDBEntry> pdbEntries)
+  {
+    Objects.requireNonNull(id);
+    Objects.requireNonNull(pdbEntries);
+    PDBEntry foundEntry = null;
+    for (PDBEntry entry : pdbEntries)
     {
-      SequenceI userSelectedSeq = ((AssociateSeqOptions) fileChooserAssSeqPanel
-              .getCmb_assSeq().getSelectedItem()).getSequence();
-      if (userSelectedSeq != null)
+      if (entry.getId().equalsIgnoreCase(id))
       {
-        selectedSequence = userSelectedSeq;
+        return entry;
       }
-      PDBEntry fileEntry = new AssociatePdbFileWithSeq()
-              .associatePdbWithSeq(selectedPdbFileName,
-                      jalview.io.AppletFormatAdapter.FILE,
-                      selectedSequence, true, Desktop.instance);
-
-      launchStructureViewer(ap.getStructureSelectionManager(),
-              new PDBEntry[] { fileEntry }, ap,
-              new SequenceI[] { selectedSequence });
     }
-    mainFrame.dispose();
+    return foundEntry;
   }
 
-  private void launchStructureViewer(final StructureSelectionManager ssm,
+  private void launchStructureViewer(StructureSelectionManager ssm,
           final PDBEntry[] pdbEntriesToView,
-          final AlignmentPanel alignPanel, final SequenceI[] sequences)
+          final AlignmentPanel alignPanel, SequenceI[] sequences)
   {
+    ssm.setProgressBar(MessageManager
+            .getString("status.launching_3d_structure_viewer"));
     final StructureViewer sViewer = new StructureViewer(ssm);
-    new Thread(new Runnable()
+
+    if (SiftsSettings.isMapWithSifts())
     {
-      public void run()
+      List<SequenceI> seqsWithoutSourceDBRef = new ArrayList<SequenceI>();
+      int p = 0;
+      // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a
+      // real PDB ID. For moment, we can also safely do this if there is already
+      // a known mapping between the PDBEntry and the sequence.
+      for (SequenceI seq : sequences)
       {
-        if (pdbEntriesToView.length > 1)
+        PDBEntry pdbe = pdbEntriesToView[p++];
+        if (pdbe != null && pdbe.getFile() != null)
         {
-          ArrayList<SequenceI[]> seqsMap = new ArrayList<SequenceI[]>();
-          for (SequenceI seq : sequences)
+          StructureMapping[] smm = ssm.getMapping(pdbe.getFile());
+          if (smm != null && smm.length > 0)
           {
-            seqsMap.add(new SequenceI[] { seq });
+            for (StructureMapping sm : smm)
+            {
+              if (sm.getSequence() == seq)
+              {
+                continue;
+              }
+            }
           }
-          SequenceI[][] collatedSeqs = seqsMap.toArray(new SequenceI[0][0]);
-          sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel);
         }
-        else
+        if (seq.getPrimaryDBRefs().size() == 0)
         {
-          sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
+          seqsWithoutSourceDBRef.add(seq);
+          continue;
         }
       }
-    }).start();
+      if (!seqsWithoutSourceDBRef.isEmpty())
+      {
+        int y = seqsWithoutSourceDBRef.size();
+        ssm.setProgressBar(null);
+        ssm.setProgressBar(MessageManager.formatMessage(
+                "status.fetching_dbrefs_for_sequences_without_valid_refs",
+                y));
+        SequenceI[] seqWithoutSrcDBRef = new SequenceI[y];
+        int x = 0;
+        for (SequenceI fSeq : seqsWithoutSourceDBRef)
+        {
+          seqWithoutSrcDBRef[x++] = fSeq;
+        }
+        DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef);
+        dbRefFetcher.fetchDBRefs(true);
+      }
+    }
+    if (pdbEntriesToView.length > 1)
+    {
+      ArrayList<SequenceI[]> seqsMap = new ArrayList<SequenceI[]>();
+      for (SequenceI seq : sequences)
+      {
+        seqsMap.add(new SequenceI[] { seq });
+      }
+      SequenceI[][] collatedSeqs = seqsMap.toArray(new SequenceI[0][0]);
+      ssm.setProgressBar(null);
+      ssm.setProgressBar(MessageManager
+              .getString("status.fetching_3d_structures_for_selected_entries"));
+      sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel);
+    }
+    else
+    {
+      ssm.setProgressBar(null);
+      ssm.setProgressBar(MessageManager.formatMessage(
+              "status.fetching_3d_structures_for",
+              pdbEntriesToView[0].getId()));
+      sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
+    }
   }
 
   /**
    * Populates the combo-box used in associating manually fetched structures to
    * a unique sequence when more than one sequence selection is made.
    */
+  @Override
   public void populateCmbAssociateSeqOptions(
           JComboBox<AssociateSeqOptions> cmb_assSeq, JLabel lbl_associateSeq)
   {
@@ -795,15 +981,11 @@ public class StructureChooser extends GStructureChooser
 
   public boolean isStructuresDiscovered()
   {
-    return structuresDiscovered;
+    return discoveredStructuresSet != null
+            && !discoveredStructuresSet.isEmpty();
   }
 
-  public void setStructuresDiscovered(boolean structuresDiscovered)
-  {
-    this.structuresDiscovered = structuresDiscovered;
-  }
-
-  public Collection<PDBResponseSummary> getDiscoveredStructuresSet()
+  public Collection<FTSData> getDiscoveredStructuresSet()
   {
     return discoveredStructuresSet;
   }
@@ -813,23 +995,27 @@ public class StructureChooser extends GStructureChooser
   {
     new Thread()
     {
+      @Override
       public void run()
       {
         errorWarning.setLength(0);
         isValidPBDEntry = false;
         if (txt_search.getText().length() > 0)
         {
-          List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
-          wantedFields.add(PDBDocField.PDB_ID);
-          PDBRestRequest pdbRequest = new PDBRestRequest();
+          String searchTerm = txt_search.getText().toLowerCase();
+          searchTerm = searchTerm.split(":")[0];
+          // System.out.println(">>>>> search term : " + searchTerm);
+          List<FTSDataColumnI> wantedFields = new ArrayList<FTSDataColumnI>();
+          FTSRestRequest pdbRequest = new FTSRestRequest();
           pdbRequest.setAllowEmptySeq(false);
           pdbRequest.setResponseSize(1);
           pdbRequest.setFieldToSearchBy("(pdb_id:");
           pdbRequest.setWantedFields(wantedFields);
-          pdbRequest.setSearchTerm(txt_search.getText() + ")");
+          pdbRequest.setSearchTerm(searchTerm + ")");
           pdbRequest.setAssociatedSequence(selectedSequence);
-          pdbRestCleint = new PDBRestClient();
-          PDBRestResponse resultList;
+          pdbRestCleint = PDBFTSRestClient.getInstance();
+          wantedFields.add(pdbRestCleint.getPrimaryKeyColumn());
+          FTSRestResponse resultList;
           try
           {
             resultList = pdbRestCleint.executeRequest(pdbRequest);
@@ -969,4 +1155,24 @@ public class StructureChooser extends GStructureChooser
     }
 
   }
+
+  private IProgressIndicator progressBar;
+
+  @Override
+  public void setProgressBar(String message, long id)
+  {
+    progressBar.setProgressBar(message, id);
+  }
+
+  @Override
+  public void registerHandler(long id, IProgressIndicatorHandler handler)
+  {
+    progressBar.registerHandler(id, handler);
+  }
+
+  @Override
+  public boolean operationInProgress()
+  {
+    return progressBar.operationInProgress();
+  }
 }
diff --git a/src/jalview/gui/StructureViewer.java b/src/jalview/gui/StructureViewer.java
index df5a6c4..724822d 100644
--- a/src/jalview/gui/StructureViewer.java
+++ b/src/jalview/gui/StructureViewer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -136,14 +136,16 @@ public class StructureViewer
   protected JalviewStructureDisplayI viewStructures(ViewerType viewerType,
           PDBEntry[] pdbs, SequenceI[][] seqsForPdbs, AlignmentPanel ap)
   {
+    PDBEntry[] pdbsForFile = getUniquePdbFiles(pdbs);
     JalviewStructureDisplayI sview = null;
     if (viewerType.equals(ViewerType.JMOL))
     {
-      sview = new AppJmol(ap, pdbs, ap.av.collateForPDB(pdbs));
+      sview = new AppJmol(ap, pdbsForFile, ap.av.collateForPDB(pdbsForFile));
     }
     else if (viewerType.equals(ViewerType.CHIMERA))
     {
-      sview = new ChimeraViewFrame(pdbs, ap.av.collateForPDB(pdbs), ap);
+      sview = new ChimeraViewFrame(pdbsForFile,
+              ap.av.collateForPDB(pdbsForFile), ap);
     }
     else
     {
@@ -153,6 +155,36 @@ public class StructureViewer
     return sview;
   }
 
+  /**
+   * Convert the array of PDBEntry into an array with no filename repeated
+   * 
+   * @param pdbs
+   * @return
+   */
+  static PDBEntry[] getUniquePdbFiles(PDBEntry[] pdbs)
+  {
+    if (pdbs == null)
+    {
+      return null;
+    }
+    List<PDBEntry> uniques = new ArrayList<PDBEntry>();
+    List<String> filesSeen = new ArrayList<String>();
+    for (PDBEntry entry : pdbs)
+    {
+      String file = entry.getFile();
+      if (file == null)
+      {
+        uniques.add(entry);
+      }
+      else if (!filesSeen.contains(file))
+      {
+        uniques.add(entry);
+        filesSeen.add(file);
+      }
+    }
+    return uniques.toArray(new PDBEntry[uniques.size()]);
+  }
+
   protected JalviewStructureDisplayI viewStructures(ViewerType viewerType,
           PDBEntry pdb, SequenceI[] seqsForPdb, AlignmentPanel ap)
   {
diff --git a/src/jalview/gui/StructureViewerBase.java b/src/jalview/gui/StructureViewerBase.java
index 8146237..6d8ba81 100644
--- a/src/jalview/gui/StructureViewerBase.java
+++ b/src/jalview/gui/StructureViewerBase.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -30,10 +30,15 @@ import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.MessageManager;
 
 import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Vector;
 
+import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JMenuItem;
 import javax.swing.JOptionPane;
 
@@ -76,6 +81,8 @@ public abstract class StructureViewerBase extends GStructureViewer
 
   protected Thread worker = null;
 
+  protected boolean allChainsSelected = false;
+
   /**
    * 
    * @param ap2
@@ -152,6 +159,7 @@ public abstract class StructureViewerBase extends GStructureViewer
     this.ap = alp;
   }
 
+  @Override
   public AlignmentPanel[] getAllAlignmentPanels()
   {
     AlignmentPanel[] t, list = new AlignmentPanel[0];
@@ -291,6 +299,7 @@ public abstract class StructureViewerBase extends GStructureViewer
         // queue.
         new Thread(new Runnable()
         {
+          @Override
           public void run()
           {
             while (worker != null && worker.isAlive() && _started)
@@ -492,4 +501,55 @@ public abstract class StructureViewerBase extends GStructureViewer
     }
     return finished;
   }
+
+  void setChainMenuItems(List<String> chainNames)
+  {
+    chainMenu.removeAll();
+    if (chainNames == null || chainNames.isEmpty())
+    {
+      return;
+    }
+    JMenuItem menuItem = new JMenuItem(
+            MessageManager.getString("label.all"));
+    menuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent evt)
+      {
+        allChainsSelected = true;
+        for (int i = 0; i < chainMenu.getItemCount(); i++)
+        {
+          if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
+          {
+            ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true);
+          }
+        }
+        showSelectedChains();
+        allChainsSelected = false;
+      }
+    });
+
+    chainMenu.add(menuItem);
+
+    for (String chain : chainNames)
+    {
+      menuItem = new JCheckBoxMenuItem(chain, true);
+      menuItem.addItemListener(new ItemListener()
+      {
+        @Override
+        public void itemStateChanged(ItemEvent evt)
+        {
+          if (!allChainsSelected)
+          {
+            showSelectedChains();
+          }
+        }
+      });
+
+      chainMenu.add(menuItem);
+    }
+  }
+
+  abstract void showSelectedChains();
+
 }
diff --git a/src/jalview/gui/TextColourChooser.java b/src/jalview/gui/TextColourChooser.java
index fc01404..d792d46 100644
--- a/src/jalview/gui/TextColourChooser.java
+++ b/src/jalview/gui/TextColourChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -72,7 +72,7 @@ public class TextColourChooser
     final JPanel col2 = new JPanel();
     col2.setPreferredSize(new Dimension(40, 20));
     col2.setBorder(BorderFactory.createEtchedBorder());
-    col2.setToolTipText(MessageManager.getString("label.ligth_colour"));
+    col2.setToolTipText(MessageManager.getString("label.light_colour"));
     col2.setBackground(new Color(original2));
     final JPanel bigpanel = new JPanel(new BorderLayout());
     JPanel panel = new JPanel();
@@ -81,7 +81,7 @@ public class TextColourChooser
             new JLabel(
                     "<html>"
                             + MessageManager
-                                    .getString("label.select_dark_light_set_thereshold")
+                                    .getString("label.select_dark_light_set_threshold")
                             + "</html>"), BorderLayout.NORTH);
     panel.add(col1);
     panel.add(slider);
@@ -89,6 +89,7 @@ public class TextColourChooser
 
     col1.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mousePressed(MouseEvent e)
       {
         Color col = JColorChooser.showDialog(bigpanel,
@@ -104,6 +105,7 @@ public class TextColourChooser
 
     col2.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mousePressed(MouseEvent e)
       {
         Color col = JColorChooser.showDialog(bigpanel,
@@ -119,6 +121,7 @@ public class TextColourChooser
 
     slider.addChangeListener(new ChangeListener()
     {
+      @Override
       public void stateChanged(ChangeEvent evt)
       {
         thresholdChanged(slider.getValue());
@@ -130,7 +133,7 @@ public class TextColourChooser
                     ap,
                     bigpanel,
                     MessageManager
-                            .getString("label.adjunst_foreground_text_colour_thereshold"),
+                            .getString("label.adjunst_foreground_text_colour_threshold"),
                     JOptionPane.OK_CANCEL_OPTION,
                     JOptionPane.QUESTION_MESSAGE, null, null, null);
 
diff --git a/src/jalview/gui/TreeCanvas.java b/src/jalview/gui/TreeCanvas.java
index f590c2e..8f4ac71 100644
--- a/src/jalview/gui/TreeCanvas.java
+++ b/src/jalview/gui/TreeCanvas.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,7 +29,6 @@ import jalview.datamodel.SequenceI;
 import jalview.datamodel.SequenceNode;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
-import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
 import jalview.structure.SelectionSource;
 import jalview.util.Format;
@@ -175,13 +174,13 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
     tree.findHeight(tree.getTopNode());
 
     // Now have to calculate longest name based on the leaves
-    Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector());
+    Vector<SequenceNode> leaves = tree.findLeaves(tree.getTopNode());
     boolean has_placeholders = false;
     longestName = "";
 
     for (int i = 0; i < leaves.size(); i++)
     {
-      SequenceNode lf = (SequenceNode) leaves.elementAt(i);
+      SequenceNode lf = leaves.elementAt(i);
 
       if (lf.isPlaceholder())
       {
@@ -546,6 +545,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
   }
 
   // put printing in a thread to avoid painting problems
+  @Override
   public void run()
   {
     PrinterJob printJob = PrinterJob.getPrinterJob();
@@ -580,6 +580,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
    * @throws PrinterException
    *           DOCUMENT ME!
    */
+  @Override
   public int print(Graphics pg, PageFormat pf, int pi)
           throws PrinterException
   {
@@ -634,6 +635,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
    * @param g
    *          DOCUMENT ME!
    */
+  @Override
   public void paintComponent(Graphics g)
   {
     super.paintComponent(g);
@@ -677,6 +679,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
    * @param fontSize
    *          DOCUMENT ME!
    */
+  @Override
   public void setFont(Font font)
   {
     this.font = font;
@@ -744,80 +747,99 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
   }
 
   /**
-   * DOCUMENT ME!
+   * Empty method to satisfy the MouseListener interface
    * 
    * @param e
-   *          DOCUMENT ME!
    */
+  @Override
   public void mouseReleased(MouseEvent e)
   {
+    /*
+     * isPopupTrigger is set on mouseReleased on Windows
+     */
+    if (e.isPopupTrigger())
+    {
+      chooseSubtreeColour();
+      e.consume(); // prevent mouseClicked happening
+    }
   }
 
   /**
-   * DOCUMENT ME!
+   * Empty method to satisfy the MouseListener interface
    * 
    * @param e
-   *          DOCUMENT ME!
    */
+  @Override
   public void mouseEntered(MouseEvent e)
   {
   }
 
   /**
-   * DOCUMENT ME!
+   * Empty method to satisfy the MouseListener interface
    * 
    * @param e
-   *          DOCUMENT ME!
    */
+  @Override
   public void mouseExited(MouseEvent e)
   {
   }
 
   /**
-   * DOCUMENT ME!
+   * Handles a mouse click on a tree node (clicks elsewhere are handled in
+   * mousePressed). Click selects the sub-tree, double-click swaps leaf nodes
+   * order, right-click opens a dialogue to choose colour for the sub-tree.
    * 
    * @param e
-   *          DOCUMENT ME!
    */
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
-    if (highlightNode != null)
+    if (highlightNode == null)
     {
-      if (SwingUtilities.isRightMouseButton(evt))
-      {
-        Color col = JColorChooser.showDialog(this,
-                MessageManager.getString("label.select_subtree_colour"),
-                highlightNode.color);
-        if (col != null)
-        {
-          setColor(highlightNode, col);
-        }
-      }
-      else if (evt.getClickCount() > 1)
+      return;
+    }
+
+    if (evt.getClickCount() > 1)
+    {
+      tree.swapNodes(highlightNode);
+      tree.reCount(tree.getTopNode());
+      tree.findHeight(tree.getTopNode());
+    }
+    else
+    {
+      Vector<SequenceNode> leaves = tree.findLeaves(highlightNode);
+
+      for (int i = 0; i < leaves.size(); i++)
       {
-        tree.swapNodes(highlightNode);
-        tree.reCount(tree.getTopNode());
-        tree.findHeight(tree.getTopNode());
+        SequenceI seq = (SequenceI) leaves.elementAt(i).element();
+        treeSelectionChanged(seq);
       }
-      else
-      {
-        Vector leaves = new Vector();
-        tree.findLeaves(highlightNode, leaves);
+      av.sendSelection();
+    }
 
-        for (int i = 0; i < leaves.size(); i++)
-        {
-          SequenceI seq = (SequenceI) ((SequenceNode) leaves.elementAt(i))
-                  .element();
-          treeSelectionChanged(seq);
-        }
-        av.sendSelection();
-      }
+    PaintRefresher.Refresh(tp, av.getSequenceSetId());
+    repaint();
+  }
 
-      PaintRefresher.Refresh(tp, av.getSequenceSetId());
+  /**
+   * Offer the user the option to choose a colour for the highlighted node and
+   * its children; this colour is also applied to the corresponding sequence ids
+   * in the alignment
+   */
+  void chooseSubtreeColour()
+  {
+    Color col = JColorChooser.showDialog(this,
+            MessageManager.getString("label.select_subtree_colour"),
+            highlightNode.color);
+    if (col != null)
+    {
+      setColor(highlightNode, col);
+      PaintRefresher.Refresh(tp, ap.av.getSequenceSetId());
       repaint();
     }
   }
 
+  @Override
   public void mouseMoved(MouseEvent evt)
   {
     av.setCurrentTree(tree);
@@ -843,20 +865,48 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
     }
   }
 
+  @Override
   public void mouseDragged(MouseEvent ect)
   {
   }
 
   /**
-   * DOCUMENT ME!
+   * Handles a mouse press on a sequence name or the tree background canvas
+   * (click on a node is handled in mouseClicked). The action is to create
+   * groups by partitioning the tree at the mouse position. Colours for the
+   * groups (and sequence names) are generated randomly.
    * 
    * @param e
-   *          DOCUMENT ME!
    */
+  @Override
   public void mousePressed(MouseEvent e)
   {
     av.setCurrentTree(tree);
 
+    /*
+     * isPopupTrigger is set for mousePressed (Mac)
+     * or mouseReleased (Windows)
+     */
+    if (e.isPopupTrigger())
+    {
+      if (highlightNode != null)
+      {
+        chooseSubtreeColour();
+      }
+      return;
+    }
+
+    /*
+     * defer right-click handling on Windows to
+     * mouseClicked; note isRightMouseButton
+     * also matches Cmd-click on Mac which should do
+     * nothing here
+     */
+    if (SwingUtilities.isRightMouseButton(e))
+    {
+      return;
+    }
+
     int x = e.getX();
     int y = e.getY();
 
@@ -891,12 +941,13 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
           aps[a].av.setSelectionGroup(null);
           aps[a].av.getAlignment().deleteAllGroups();
           aps[a].av.clearSequenceColours();
-        }
-        if (av.getCodingComplement() != null)
-        {
-          av.getCodingComplement().setSelectionGroup(null);
-          av.getCodingComplement().getAlignment().deleteAllGroups();
-          av.getCodingComplement().clearSequenceColours();
+          if (aps[a].av.getCodingComplement() != null)
+          {
+            aps[a].av.getCodingComplement().setSelectionGroup(null);
+            aps[a].av.getCodingComplement().getAlignment()
+                    .deleteAllGroups();
+            aps[a].av.getCodingComplement().clearSequenceColours();
+          }
         }
         colourGroups();
       }
@@ -914,17 +965,16 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
     {
       Color col = new Color((int) (Math.random() * 255),
               (int) (Math.random() * 255), (int) (Math.random() * 255));
-      setColor((SequenceNode) tree.getGroups().elementAt(i), col.brighter());
+      setColor(tree.getGroups().elementAt(i), col.brighter());
 
-      Vector l = tree.findLeaves(
-              (SequenceNode) tree.getGroups().elementAt(i), new Vector());
+      Vector<SequenceNode> l = tree.findLeaves(tree.getGroups()
+              .elementAt(i));
 
-      Vector sequences = new Vector();
+      Vector<SequenceI> sequences = new Vector<SequenceI>();
 
       for (int j = 0; j < l.size(); j++)
       {
-        SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j))
-                .element();
+        SequenceI s1 = (SequenceI) l.elementAt(j).element();
 
         if (!sequences.contains(s1))
         {
@@ -967,51 +1017,46 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
         if (aps[a].av.getGlobalColourScheme() != null
                 && aps[a].av.getGlobalColourScheme().conservationApplied())
         {
-          Conservation c = new Conservation("Group",
-                  ResidueProperties.propHash, 3, sg.getSequences(null),
+          Conservation c = new Conservation("Group", sg.getSequences(null),
                   sg.getStartRes(), sg.getEndRes());
-
           c.calculate();
           c.verdict(false, aps[a].av.getConsPercGaps());
           sg.cs.setConservation(c);
         }
 
         aps[a].av.getAlignment().addGroup(new SequenceGroup(sg));
-      }
-
-      // TODO can we push all of the below into AlignViewportI?
-      av.getAlignment().addGroup(sg);
-      final AlignViewportI codingComplement = av.getCodingComplement();
-      if (codingComplement != null)
-      {
-        SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av,
-                codingComplement);
-        if (mappedGroup.getSequences().size() > 0)
+        // TODO can we push all of the below into AlignViewportI?
+        final AlignViewportI codingComplement = aps[a].av
+                .getCodingComplement();
+        if (codingComplement != null)
         {
-          codingComplement.getAlignment().addGroup(mappedGroup);
-          for (SequenceI seq : mappedGroup.getSequences())
+          SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av,
+                  codingComplement);
+          if (mappedGroup.getSequences().size() > 0)
           {
-            codingComplement.setSequenceColour(seq, col.brighter());
+            codingComplement.getAlignment().addGroup(mappedGroup);
+            for (SequenceI seq : mappedGroup.getSequences())
+            {
+              codingComplement.setSequenceColour(seq, col.brighter());
+            }
           }
         }
       }
     }
 
-    // notify the panel to redo any group specific stuff.
+    // notify the panel(s) to redo any group specific stuff.
     for (int a = 0; a < aps.length; a++)
     {
       aps[a].updateAnnotation();
       // TODO: JAL-868 - need to ensure view colour change message is broadcast
       // to any Jmols listening in
-    }
-
-    if (av.getCodingComplement() != null)
-    {
-      ((AlignViewport) av.getCodingComplement()).getAlignPanel()
-              .updateAnnotation();
-      /*
-       * idPanel. repaint ()
-       */
+      final AlignViewportI codingComplement = aps[a].av
+              .getCodingComplement();
+      if (codingComplement != null)
+      {
+        ((AlignViewport) codingComplement).getAlignPanel()
+                .updateAnnotation();
+      }
     }
   }
 
diff --git a/src/jalview/gui/TreePanel.java b/src/jalview/gui/TreePanel.java
index c0b0c68..13bcde0 100644
--- a/src/jalview/gui/TreePanel.java
+++ b/src/jalview/gui/TreePanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -161,6 +161,7 @@ public class TreePanel extends GTreePanel
 
     av.addPropertyChangeListener(new java.beans.PropertyChangeListener()
     {
+      @Override
       public void propertyChange(PropertyChangeEvent evt)
       {
         if (evt.getPropertyName().equals("alignment"))
@@ -196,6 +197,7 @@ public class TreePanel extends GTreePanel
 
   }
 
+  @Override
   public void viewMenu_menuSelected()
   {
     buildAssociatedViewMenu();
@@ -231,6 +233,7 @@ public class TreePanel extends GTreePanel
       buttonGroup.add(item);
       item.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent evt)
         {
           treeCanvas.applyToAllViews = false;
@@ -249,6 +252,7 @@ public class TreePanel extends GTreePanel
     itemf.setSelected(treeCanvas.applyToAllViews);
     itemf.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent evt)
       {
         treeCanvas.applyToAllViews = itemf.isSelected();
@@ -276,6 +280,7 @@ public class TreePanel extends GTreePanel
       }
     }
 
+    @Override
     public void run()
     {
 
@@ -389,6 +394,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void textbox_actionPerformed(ActionEvent e)
   {
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
@@ -434,6 +440,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void saveAsNewick_actionPerformed(ActionEvent e)
   {
     JalviewFileChooser chooser = new JalviewFileChooser(
@@ -474,12 +481,14 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void printMenu_actionPerformed(ActionEvent e)
   {
     // Putting in a thread avoids Swing painting problems
     treeCanvas.startPrinting();
   }
 
+  @Override
   public void originalSeqData_actionPerformed(ActionEvent e)
   {
     if (!tree.hasOriginalSequenceData())
@@ -511,8 +520,8 @@ public class TreePanel extends GTreePanel
     {
       // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
 
-      Alignment al = new Alignment((SequenceI[]) alAndColsel[0]);
-      Alignment dataset = (av != null && av.getAlignment() != null) ? av
+      AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
+      AlignmentI dataset = (av != null && av.getAlignment() != null) ? av
               .getAlignment().getDataset() : null;
       if (dataset != null)
       {
@@ -547,6 +556,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void fitToWindow_actionPerformed(ActionEvent e)
   {
     treeCanvas.fitToWindow = fitToWindow.isSelected();
@@ -637,6 +647,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void font_actionPerformed(ActionEvent e)
   {
     if (treeCanvas == null)
@@ -666,6 +677,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void distanceMenu_actionPerformed(ActionEvent e)
   {
     treeCanvas.setShowDistances(distanceMenu.isSelected());
@@ -677,6 +689,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void bootstrapMenu_actionPerformed(ActionEvent e)
   {
     treeCanvas.setShowBootstrap(bootstrapMenu.isSelected());
@@ -688,6 +701,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void placeholdersMenu_actionPerformed(ActionEvent e)
   {
     treeCanvas.setMarkPlaceholders(placeholdersMenu.isSelected());
@@ -699,6 +713,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void epsTree_actionPerformed(ActionEvent e)
   {
     boolean accurateText = true;
@@ -772,6 +787,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void pngTree_actionPerformed(ActionEvent e)
   {
     int width = treeCanvas.getWidth();
@@ -828,6 +844,7 @@ public class TreePanel extends GTreePanel
     tree.applyToNodes(new NodeTransformI()
     {
 
+      @Override
       public void transform(BinaryNode node)
       {
         if (node instanceof SequenceNode
@@ -840,7 +857,7 @@ public class TreePanel extends GTreePanel
           {
             // search dbrefs, features and annotation
             DBRefEntry[] refs = jalview.util.DBRefUtils.selectRefs(
-                    sq.getDBRef(),
+                    sq.getDBRefs(),
                     new String[] { labelClass.toUpperCase() });
             if (refs != null)
             {
diff --git a/src/jalview/gui/UserDefinedColours.java b/src/jalview/gui/UserDefinedColours.java
index 1e85e55..274c23b 100644
--- a/src/jalview/gui/UserDefinedColours.java
+++ b/src/jalview/gui/UserDefinedColours.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/UserQuestionnaireCheck.java b/src/jalview/gui/UserQuestionnaireCheck.java
index c286935..0193f99 100644
--- a/src/jalview/gui/UserQuestionnaireCheck.java
+++ b/src/jalview/gui/UserQuestionnaireCheck.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/VamsasApplication.java b/src/jalview/gui/VamsasApplication.java
index 4630fd1..8409725 100644
--- a/src/jalview/gui/VamsasApplication.java
+++ b/src/jalview/gui/VamsasApplication.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -321,6 +321,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
     Thread udthread = new Thread(new Runnable()
     {
 
+      @Override
       public void run()
       {
         Cache.log.info("Jalview updating to the Vamsas Session.");
@@ -639,6 +640,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
     final VamsasApplication client = this;
     vclient.addDocumentUpdateHandler(new PropertyChangeListener()
     {
+      @Override
       public void propertyChange(PropertyChangeEvent evt)
       {
         Cache.log.debug("Dealing with document update event.");
@@ -656,6 +658,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
             uk.ac.vamsas.client.Events.DOCUMENT_REQUESTTOCLOSE,
             new PropertyChangeListener()
             {
+              @Override
               public void propertyChange(PropertyChangeEvent evt)
               {
                 if (client.promptUser)
@@ -774,6 +777,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
         {
           String last = null;
 
+          @Override
           public void handleMessage(Message message)
           {
             if (vobj2jv == null)
@@ -998,6 +1002,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource
         selecter = new SelectionListener()
         {
 
+          @Override
           public void selection(SequenceGroup seqsel,
                   ColumnSelection colsel, SelectionSource source)
           {
@@ -1065,11 +1070,10 @@ public class VamsasApplication implements SelectionSource, VamsasSource
                   {
                     // gather selected columns outwith the sequence positions
                     // too
-                    for (Object obj : colsel.getSelected())
+                    for (Integer ival : colsel.getSelected())
                     {
-                      int ival = ((Integer) obj).intValue();
                       Pos p = new Pos();
-                      p.setI(ival + 1);
+                      p.setI(ival.intValue() + 1);
                       range.addPos(p);
                     }
                   }
diff --git a/src/jalview/gui/ViewSelectionMenu.java b/src/jalview/gui/ViewSelectionMenu.java
index d906c6d..f92f2d6 100644
--- a/src/jalview/gui/ViewSelectionMenu.java
+++ b/src/jalview/gui/ViewSelectionMenu.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/WebserviceInfo.java b/src/jalview/gui/WebserviceInfo.java
index 82257ff..0b88059 100644
--- a/src/jalview/gui/WebserviceInfo.java
+++ b/src/jalview/gui/WebserviceInfo.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/WsJobParameters.java b/src/jalview/gui/WsJobParameters.java
index b3e40c1..f100aca 100644
--- a/src/jalview/gui/WsJobParameters.java
+++ b/src/jalview/gui/WsJobParameters.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/WsParamSetManager.java b/src/jalview/gui/WsParamSetManager.java
index 687c9f3..939cdd9 100644
--- a/src/jalview/gui/WsParamSetManager.java
+++ b/src/jalview/gui/WsParamSetManager.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/gui/WsPreferences.java b/src/jalview/gui/WsPreferences.java
index fa29078..dbf887f 100644
--- a/src/jalview/gui/WsPreferences.java
+++ b/src/jalview/gui/WsPreferences.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/httpserver/AbstractRequestHandler.java b/src/jalview/httpserver/AbstractRequestHandler.java
index 64ac2d1..e1dcfd9 100644
--- a/src/jalview/httpserver/AbstractRequestHandler.java
+++ b/src/jalview/httpserver/AbstractRequestHandler.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/httpserver/HttpServer.java b/src/jalview/httpserver/HttpServer.java
index 1f00df4..9943f39 100644
--- a/src/jalview/httpserver/HttpServer.java
+++ b/src/jalview/httpserver/HttpServer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/AMSAFile.java b/src/jalview/io/AMSAFile.java
index d8cb64f..cac17b2 100644
--- a/src/jalview/io/AMSAFile.java
+++ b/src/jalview/io/AMSAFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/AlignFile.java b/src/jalview/io/AlignFile.java
index e5c8e04..03f3313 100644
--- a/src/jalview/io/AlignFile.java
+++ b/src/jalview/io/AlignFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -47,7 +47,8 @@ public abstract class AlignFile extends FileParse
   int maxLength = 0;
 
   /**
-   * Sequences to be added to form a new alignment.
+   * Sequences to be added to form a new alignment. TODO: remove vector in this
+   * class
    */
   protected Vector<SequenceI> seqs;
 
@@ -88,14 +89,14 @@ public abstract class AlignFile extends FileParse
   /**
    * Constructor which parses the data from a file of some specified type.
    * 
-   * @param inFile
-   *          Filename to read from.
+   * @param dataObject
+   *          Filename, URL or Pasted String to read from.
    * @param type
-   *          What type of file to read from (File, URL)
+   *          What type of file to read from (File, URL, Pasted String)
    */
-  public AlignFile(String inFile, String type) throws IOException
+  public AlignFile(String dataObject, String type) throws IOException
   {
-    this(true, inFile, type);
+    this(true, dataObject, type);
   }
 
   /**
@@ -104,16 +105,16 @@ public abstract class AlignFile extends FileParse
    * 
    * @param parseImmediately
    *          if false, need to call 'doParse()' to begin parsing data
-   * @param inFile
-   *          Filename to read from.
+   * @param dataObject
+   *          Filename, URL or Pasted String to read from.
    * @param type
    *          What type of file to read from (File, URL)
    * @throws IOException
    */
-  public AlignFile(boolean parseImmediately, String inFile, String type)
+  public AlignFile(boolean parseImmediately, String dataObject, String type)
           throws IOException
   {
-    super(inFile, type);
+    super(dataObject, type);
     initData();
     if (parseImmediately)
     {
@@ -351,7 +352,15 @@ public abstract class AlignFile extends FileParse
     if (space > -1)
     {
       seq = new Sequence(id.substring(0, space), "");
-      seq.setDescription(id.substring(space + 1));
+      String desc = id.substring(space + 1);
+      seq.setDescription(desc);
+
+      /*
+       * it is tempting to parse Ensembl style gene description e.g.
+       * chromosome:GRCh38:7:140696688:140721955:1 and set the
+       * start position of the sequence, but this causes much confusion
+       * for reverse strand feature locations
+       */
     }
     else
     {
diff --git a/src/jalview/io/AlignmentProperties.java b/src/jalview/io/AlignmentProperties.java
index bef3795..a5fc767 100644
--- a/src/jalview/io/AlignmentProperties.java
+++ b/src/jalview/io/AlignmentProperties.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/AnnotationFile.java b/src/jalview/io/AnnotationFile.java
index 2872a77..cf148f0 100644
--- a/src/jalview/io/AnnotationFile.java
+++ b/src/jalview/io/AnnotationFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -32,7 +32,6 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
-import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
 
 import java.io.BufferedReader;
@@ -1638,8 +1637,7 @@ public class AnnotationFile
         else if (key.equalsIgnoreCase("consThreshold"))
         {
           sg.cs.setConservationInc(Integer.parseInt(value));
-          Conservation c = new Conservation("Group",
-                  ResidueProperties.propHash, 3, sg.getSequences(null),
+          Conservation c = new Conservation("Group", sg.getSequences(null),
                   sg.getStartRes(), sg.getEndRes() + 1);
 
           c.calculate();
@@ -1760,6 +1758,10 @@ public class AnnotationFile
    */
   public String printCSVAnnotations(AlignmentAnnotation[] annotations)
   {
+    if (annotations == null)
+    {
+      return "";
+    }
     StringBuffer sp = new StringBuffer();
     for (int i = 0; i < annotations.length; i++)
     {
diff --git a/src/jalview/io/AppletFormatAdapter.java b/src/jalview/io/AppletFormatAdapter.java
index 00d6d2f..6bc06ac 100644
--- a/src/jalview/io/AppletFormatAdapter.java
+++ b/src/jalview/io/AppletFormatAdapter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  *
  * This file is part of Jalview.
  *
@@ -26,6 +26,9 @@ import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
+import jalview.datamodel.PDBEntry.Type;
+import jalview.ext.jmol.JmolParser;
+import jalview.structure.StructureImportSettings;
 import jalview.util.MessageManager;
 
 import java.io.File;
@@ -86,7 +89,7 @@ public class AppletFormatAdapter
   public static final String[] READABLE_FORMATS = new String[] { "BLC",
       "CLUSTAL", "FASTA", "MSF", "PileUp", "PIR", "PFAM", "STH", "PDB",
       "JnetFile", "RNAML", PhylipFile.FILE_DESC, JSONFile.FILE_DESC,
-      IdentifyFile.GFF3File, "HTML" };
+      IdentifyFile.FeaturesFile, "HTML", "mmCIF" };
 
   /**
    * List of readable format file extensions by application in order
@@ -95,7 +98,7 @@ public class AppletFormatAdapter
   public static final String[] READABLE_EXTENSIONS = new String[] {
       "fa, fasta, mfa, fastq", "aln", "pfam", "msf", "pir", "blc", "amsa",
       "sto,stk", "xml,rnaml", PhylipFile.FILE_EXT, JSONFile.FILE_EXT,
-      ".gff2,gff3", "jar,jvp", HtmlFile.FILE_EXT };
+      ".gff2,gff3", "jar,jvp", HtmlFile.FILE_EXT, "cif" };
 
   /**
    * List of readable formats by application in order corresponding to
@@ -103,8 +106,8 @@ public class AppletFormatAdapter
    */
   public static final String[] READABLE_FNAMES = new String[] { "Fasta",
       "Clustal", "PFAM", "MSF", "PIR", "BLC", "AMSA", "Stockholm", "RNAML",
-      PhylipFile.FILE_DESC, JSONFile.FILE_DESC, IdentifyFile.GFF3File,
-      "Jalview", HtmlFile.FILE_DESC };
+      PhylipFile.FILE_DESC, JSONFile.FILE_DESC, IdentifyFile.FeaturesFile,
+      "Jalview", HtmlFile.FILE_DESC, "mmCIF" };
 
   /**
    * List of valid format strings for use by callers of the formatSequences
@@ -277,10 +280,35 @@ public class AppletFormatAdapter
       }
       else if (format.equals("PDB"))
       {
-        alignFile = new MCview.PDBfile(annotFromStructure,
-                localSecondaryStruct, serviceSecondaryStruct, inFile, type);
-        // Uncomment to test Jmol data based PDB processing: JAL-1213
-        // afile = new jalview.ext.jmol.PDBFileWithJmol(inFile, type);
+        // TODO obtain config value from preference settings.
+        // Set value to 'true' to test PDB processing with Jmol: JAL-1213
+        boolean isParseWithJMOL = StructureImportSettings
+                .getDefaultPDBFileParser().equalsIgnoreCase(
+                        StructureImportSettings.StructureParser.JMOL_PARSER
+                                .toString());
+        if (isParseWithJMOL)
+        {
+          StructureImportSettings.addSettings(annotFromStructure,
+                  localSecondaryStruct, serviceSecondaryStruct);
+          alignFile = new jalview.ext.jmol.JmolParser(inFile, type);
+        }
+        else
+        {
+          StructureImportSettings.addSettings(annotFromStructure,
+                  localSecondaryStruct, serviceSecondaryStruct);
+          StructureImportSettings.setShowSeqFeatures(true);
+          alignFile = new MCview.PDBfile(annotFromStructure,
+                  localSecondaryStruct, serviceSecondaryStruct, inFile,
+                  type);
+        }
+        ((StructureFile) alignFile).setDbRefType(format);
+      }
+      else if (format.equalsIgnoreCase("mmCIF"))
+      {
+        StructureImportSettings.addSettings(annotFromStructure,
+                localSecondaryStruct, serviceSecondaryStruct);
+        alignFile = new jalview.ext.jmol.JmolParser(inFile, type);
+        ((StructureFile) alignFile).setDbRefType(format);
       }
       else if (format.equals("STH"))
       {
@@ -306,9 +334,9 @@ public class AppletFormatAdapter
       {
         alignFile = new RnamlFile(inFile, type);
       }
-      else if (format.equals(IdentifyFile.GFF3File))
+      else if (format.equals(IdentifyFile.FeaturesFile))
       {
-        alignFile = new Gff3File(inFile, type);
+        alignFile = new FeaturesFile(true, inFile, type);
       }
       return buildAlignmentFrom(alignFile);
     } catch (Exception e)
@@ -407,8 +435,28 @@ public class AppletFormatAdapter
       }
       else if (format.equals("PDB"))
       {
-        alignFile = new MCview.PDBfile(annotFromStructure,
-                localSecondaryStruct, serviceSecondaryStruct, source);
+        // TODO obtain config value from preference settings
+        boolean isParseWithJMOL = false;
+        if (isParseWithJMOL)
+        {
+          StructureImportSettings.addSettings(annotFromStructure,
+                  localSecondaryStruct, serviceSecondaryStruct);
+          alignFile = new JmolParser(source);
+        }
+        else
+        {
+          StructureImportSettings.setShowSeqFeatures(true);
+          alignFile = new MCview.PDBfile(annotFromStructure,
+                  localSecondaryStruct, serviceSecondaryStruct, source);
+        }
+        ((StructureFile) alignFile).setDbRefType(Type.PDB);
+      }
+      else if (format.equalsIgnoreCase("mmCIF"))
+      {
+        StructureImportSettings.addSettings(annotFromStructure,
+                localSecondaryStruct, serviceSecondaryStruct);
+        alignFile = new JmolParser(source);
+        ((StructureFile) alignFile).setDbRefType(Type.MMCIF);
       }
       else if (format.equals("STH"))
       {
@@ -426,9 +474,9 @@ public class AppletFormatAdapter
       {
         alignFile = new PhylipFile(source);
       }
-      else if (format.equals(IdentifyFile.GFF3File))
+      else if (format.equals(IdentifyFile.FeaturesFile))
       {
-        alignFile = new Gff3File(inFile, type);
+        alignFile = new FeaturesFile(inFile, type);
       }
       else if (format.equals(JSONFile.FILE_DESC))
       {
@@ -669,7 +717,7 @@ public class AppletFormatAdapter
           long memf = -r.totalMemory() + r.freeMemory();
           long t1 = -System.currentTimeMillis();
           AlignmentI al = afa.readFile(args[i], FILE,
-                  new IdentifyFile().Identify(args[i], FILE));
+                  new IdentifyFile().identify(args[i], FILE));
           t1 += System.currentTimeMillis();
           System.gc();
           memf += r.totalMemory() - r.freeMemory();
@@ -835,7 +883,7 @@ public class AppletFormatAdapter
     {
       try
       {
-        String idformat = new jalview.io.IdentifyFile().Identify(file,
+        String idformat = new jalview.io.IdentifyFile().identify(file,
                 protocol);
         if (idformat == null)
         {
diff --git a/src/jalview/io/BLCFile.java b/src/jalview/io/BLCFile.java
index 071e814..c65bf93 100644
--- a/src/jalview/io/BLCFile.java
+++ b/src/jalview/io/BLCFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/BioJsHTMLOutput.java b/src/jalview/io/BioJsHTMLOutput.java
index 0d4dc87..c1749b7 100644
--- a/src/jalview/io/BioJsHTMLOutput.java
+++ b/src/jalview/io/BioJsHTMLOutput.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,10 +20,9 @@
  */
 package jalview.io;
 
-import jalview.api.AlignExportSettingI;
-import jalview.api.AlignmentViewPanel;
-import jalview.datamodel.AlignmentExportData;
 import jalview.exceptions.NoFileSelectedException;
+import jalview.gui.AlignmentPanel;
+import jalview.gui.OOMWarning;
 import jalview.json.binding.biojs.BioJSReleasePojo;
 import jalview.json.binding.biojs.BioJSRepositoryPojo;
 import jalview.util.MessageManager;
@@ -40,9 +39,8 @@ import java.net.URL;
 import java.util.Objects;
 import java.util.TreeMap;
 
-public class BioJsHTMLOutput
+public class BioJsHTMLOutput extends HTMLOutput
 {
-  private AlignmentViewPanel ap;
 
   private static File currentBJSTemplateFile;
 
@@ -59,161 +57,41 @@ public class BioJsHTMLOutput
                   "biojs_template_git_repo",
                   "https://raw.githubusercontent.com/jalview/exporter-templates/master/biojs/package.json");
 
-  public BioJsHTMLOutput(AlignmentViewPanel ap)
+  public BioJsHTMLOutput(AlignmentPanel ap)
   {
-    if (ap != null)
-    {
-      this.ap = ap;
-    }
+    super(ap);
   }
 
-  public void exportJalviewAlignmentAsBioJsHtmlFile()
+  @Override
+  public void exportHTML(String outputFile)
   {
+    exportStarted();
     try
     {
-      String outputFile = getOutputFile();
-      // String jalviewAlignmentJson = JSONFile.getJSONData(ap);
-      AlignExportSettingI exportSettings = new AlignExportSettingI()
-      {
-        @Override
-        public boolean isExportHiddenSequences()
-        {
-          return true;
-        }
-
-        @Override
-        public boolean isExportHiddenColumns()
-        {
-          return true;
-        }
-
-        @Override
-        public boolean isExportAnnotations()
-        {
-          return true;
-        }
-
-        @Override
-        public boolean isExportFeatures()
-        {
-          return true;
-        }
-
-        @Override
-        public boolean isExportGroups()
-        {
-          return true;
-        }
-
-        @Override
-        public boolean isCancelled()
-        {
-          return false;
-        }
-
-      };
-      AlignmentExportData exportData = jalview.gui.AlignFrame
-              .getAlignmentForExport(JSONFile.FILE_DESC,
-                      ap.getAlignViewport(), exportSettings);
-      if (exportData.getSettings().isCancelled())
+      if (outputFile == null)
       {
-        return;
+        outputFile = getOutputFile();
       }
-      String jalviewAlignmentJson = new FormatAdapter(ap,
-              exportData.getSettings()).formatSequences(JSONFile.FILE_DESC,
-              exportData.getAlignment(), exportData.getOmitHidden(),
-              exportData.getStartEndPostions(), ap.getAlignViewport()
-                      .getColumnSelection());
-
-      String bioJSTemplateString = getBioJsTemplateAsString();
-      String generatedBioJsWithJalviewAlignmentAsJson = bioJSTemplateString
-              .replaceAll("#sequenceData#", jalviewAlignmentJson)
-              .toString();
-
-      PrintWriter out = new java.io.PrintWriter(new java.io.FileWriter(
-              outputFile));
-      out.print(generatedBioJsWithJalviewAlignmentAsJson);
-      out.flush();
-      out.close();
-      jalview.util.BrowserLauncher.openURL("file:///" + outputFile);
-    } catch (NoFileSelectedException ex)
+      generatedFile = new File(outputFile);
+    } catch (NoFileSelectedException e)
     {
-      // do noting if no file was selected
+      setProgressMessage(MessageManager.formatMessage(
+              "status.cancelled_image_export_operation", "BioJS MSA"));
+      return;
     } catch (Exception e)
     {
+      setProgressMessage(MessageManager.formatMessage(
+              "info.error_creating_file", "BioJS MSA"));
       e.printStackTrace();
+      return;
     }
-  }
+    new Thread(this).start();
 
-  public String getOutputFile() throws NoFileSelectedException
-  {
-    String selectedFile = null;
-    JalviewFileChooser jvFileChooser = new JalviewFileChooser(
-            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
-            new String[] { "html" }, new String[] { "HTML files" },
-            "HTML files");
-    jvFileChooser.setFileView(new JalviewFileView());
-
-    jvFileChooser.setDialogTitle(MessageManager
-            .getString("label.save_as_biojs_html"));
-    jvFileChooser.setToolTipText(MessageManager.getString("action.save"));
-
-    int fileChooserOpt = jvFileChooser.showSaveDialog(null);
-    if (fileChooserOpt == JalviewFileChooser.APPROVE_OPTION)
-    {
-      jalview.bin.Cache.setProperty("LAST_DIRECTORY", jvFileChooser
-              .getSelectedFile().getParent());
-      selectedFile = jvFileChooser.getSelectedFile().getPath();
-    }
-    else
-    {
-      throw new NoFileSelectedException("No file was selected.");
-    }
-    return selectedFile;
   }
 
-  public static String getBioJsTemplateAsString() throws IOException
-  {
-    InputStreamReader isReader = null;
-    BufferedReader buffReader = null;
-    StringBuilder sb = new StringBuilder();
-    Objects.requireNonNull(getCurrentBJSTemplateFile(),
-            "BioJsTemplate File not initialized!");
-    @SuppressWarnings("deprecation")
-    URL url = getCurrentBJSTemplateFile().toURL();
-    if (url != null)
-    {
-      try
-      {
-        isReader = new InputStreamReader(url.openStream());
-        buffReader = new BufferedReader(isReader);
-        String line;
-        String lineSeparator = System.getProperty("line.separator");
-        while ((line = buffReader.readLine()) != null)
-        {
-          sb.append(line).append(lineSeparator);
-        }
-
-      } catch (Exception ex)
-      {
-        ex.printStackTrace();
-      } finally
-      {
-        if (isReader != null)
-        {
-          isReader.close();
-        }
 
-        if (buffReader != null)
-        {
-          buffReader.close();
-        }
-      }
-    }
-    return sb.toString();
-  }
 
-  public static void refreshBioJSVersionsInfo(String dirName)
+  public static void refreshVersionInfo(String dirName)
           throws URISyntaxException
   {
     File directory = new File(BJS_TEMPLATES_LOCAL_DIRECTORY);
@@ -250,6 +128,7 @@ public class BioJsHTMLOutput
   {
     Thread updateThread = new Thread()
     {
+      @Override
       public void run()
       {
         try
@@ -260,7 +139,7 @@ public class BioJsHTMLOutput
             BioJSRepositoryPojo release = new BioJSRepositoryPojo(
                     gitRepoPkgJson);
             syncUpdates(BJS_TEMPLATES_LOCAL_DIRECTORY, release);
-            refreshBioJSVersionsInfo(BJS_TEMPLATES_LOCAL_DIRECTORY);
+            refreshVersionInfo(BJS_TEMPLATES_LOCAL_DIRECTORY);
           }
         } catch (URISyntaxException e)
         {
@@ -381,4 +260,56 @@ public class BioJsHTMLOutput
     BioJsHTMLOutput.bioJsMSAVersions = bioJsMSAVersions;
   }
 
+  @Override
+  public boolean isEmbedData()
+  {
+    return true;
+  }
+
+  @Override
+  public boolean isLaunchInBrowserAfterExport()
+  {
+    return true;
+  }
+
+  @Override
+  public File getExportedFile()
+  {
+    return generatedFile;
+  }
+
+  @Override
+  public void run()
+  {
+    try
+    {
+      String bioJSON = getBioJSONData();
+      String bioJSTemplateString = HTMLOutput
+              .readFileAsString(getCurrentBJSTemplateFile());
+      String generatedBioJsWithJalviewAlignmentAsJson = bioJSTemplateString
+              .replaceAll("#sequenceData#", bioJSON).toString();
+
+      PrintWriter out = new java.io.PrintWriter(new java.io.FileWriter(
+              generatedFile));
+      out.print(generatedBioJsWithJalviewAlignmentAsJson);
+      out.flush();
+      out.close();
+      setProgressMessage(MessageManager.formatMessage(
+              "status.export_complete", "BioJS"));
+      exportCompleted();
+
+    } catch (OutOfMemoryError err)
+    {
+      System.out.println("########################\n" + "OUT OF MEMORY "
+              + generatedFile + "\n" + "########################");
+      new OOMWarning("Creating Image for " + generatedFile, err);
+    } catch (Exception e)
+    {
+      setProgressMessage(MessageManager.formatMessage(
+              "info.error_creating_file", "HTML"));
+      e.printStackTrace();
+    }
+
+  }
+
 }
diff --git a/src/jalview/io/ClansFile.java b/src/jalview/io/ClansFile.java
index 7e87c1c..a253fa0 100644
--- a/src/jalview/io/ClansFile.java
+++ b/src/jalview/io/ClansFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/ClustalFile.java b/src/jalview/io/ClustalFile.java
index 5dd0e40..6050afd 100644
--- a/src/jalview/io/ClustalFile.java
+++ b/src/jalview/io/ClustalFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -47,11 +47,13 @@ public class ClustalFile extends AlignFile
     super(source);
   }
 
+  @Override
   public void initData()
   {
     super.initData();
   }
 
+  @Override
   public void parse() throws IOException
   {
     int i = 0;
@@ -193,6 +195,7 @@ public class ClustalFile extends AlignFile
     }
   }
 
+  @Override
   public String print()
   {
     return print(getSeqsAsArray());
@@ -233,7 +236,7 @@ public class ClustalFile extends AlignFile
     maxid++;
 
     int len = 60;
-    int nochunks = (max / len) + 1;
+    int nochunks = (max / len) + (max % len > 0 ? 1 : 0);
 
     for (i = 0; i < nochunks; i++)
     {
diff --git a/src/jalview/io/DBRefFile.java b/src/jalview/io/DBRefFile.java
index 6aa5994..f218f2a 100644
--- a/src/jalview/io/DBRefFile.java
+++ b/src/jalview/io/DBRefFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/FastaFile.java b/src/jalview/io/FastaFile.java
index 2801ac4..ecce9eb 100644
--- a/src/jalview/io/FastaFile.java
+++ b/src/jalview/io/FastaFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -77,6 +77,7 @@ public class FastaFile extends AlignFile
    * @throws IOException
    *           DOCUMENT ME!
    */
+  @Override
   public void parse() throws IOException
   {
     StringBuffer sb = new StringBuffer();
@@ -173,8 +174,7 @@ public class FastaFile extends AlignFile
     addProperties(al);
     for (int i = 0; i < annotations.size(); i++)
     {
-      AlignmentAnnotation aa = (AlignmentAnnotation) annotations
-              .elementAt(i);
+      AlignmentAnnotation aa = annotations.elementAt(i);
       aa.setPadGaps(true, al.getGapCharacter());
       al.addAnnotation(aa);
     }
@@ -209,7 +209,8 @@ public class FastaFile extends AlignFile
 
       out.append(newline);
 
-      int nochunks = (s[i].getLength() / len) + 1;
+      int nochunks = (s[i].getLength() / len)
+              + (s[i].getLength() % len > 0 ? 1 : 0);
 
       for (int j = 0; j < nochunks; j++)
       {
@@ -238,6 +239,7 @@ public class FastaFile extends AlignFile
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public String print()
   {
     return print(getSeqsAsArray());
diff --git a/src/jalview/io/FeaturesFile.java b/src/jalview/io/FeaturesFile.java
index f316c5e..b3e4544 100644
--- a/src/jalview/io/FeaturesFile.java
+++ b/src/jalview/io/FeaturesFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,47 +20,68 @@
  */
 package jalview.io;
 
+import jalview.analysis.AlignmentUtils;
 import jalview.analysis.SequenceIdMatcher;
+import jalview.api.AlignViewportI;
+import jalview.api.FeatureColourI;
+import jalview.api.FeaturesSourceI;
 import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceDummy;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
-import jalview.schemes.AnnotationColourGradient;
-import jalview.schemes.GraduatedColor;
+import jalview.io.gff.GffHelperBase;
+import jalview.io.gff.GffHelperFactory;
+import jalview.io.gff.GffHelperI;
+import jalview.schemes.FeatureColour;
 import jalview.schemes.UserColourScheme;
-import jalview.util.Format;
 import jalview.util.MapList;
+import jalview.util.ParseHtmlBodyAndLinks;
+import jalview.util.StringUtils;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.StringTokenizer;
-import java.util.Vector;
+import java.util.Map.Entry;
 
 /**
- * Parse and create Jalview Features files Detects GFF format features files and
- * parses. Does not implement standard print() - call specific printFeatures or
- * printGFF. Uses AlignmentI.findSequence(String id) to find the sequence object
- * for the features annotation - this normally works on an exact match.
+ * Parses and writes features files, which may be in Jalview, GFF2 or GFF3
+ * format. These are tab-delimited formats but with differences in the use of
+ * columns.
+ * 
+ * A Jalview feature file may define feature colours and then declare that the
+ * remainder of the file is in GFF format with the line 'GFF'.
+ * 
+ * GFF3 files may include alignment mappings for features, which Jalview will
+ * attempt to model, and may include sequence data following a ##FASTA line.
+ * 
  * 
  * @author AMW
- * @version $Revision$
+ * @author jbprocter
+ * @author gmcarstairs
  */
-public class FeaturesFile extends AlignFile
+public class FeaturesFile extends AlignFile implements FeaturesSourceI
 {
-  /**
-   * work around for GFF interpretation bug where source string becomes
-   * description rather than a group
-   */
-  private boolean doGffSource = true;
+  private static final String ID_NOT_SPECIFIED = "ID_NOT_SPECIFIED";
+
+  private static final String NOTE = "Note";
 
-  private int gffversion;
+  protected static final String TAB = "\t";
+
+  protected static final String GFF_VERSION = "##gff-version";
+
+  private AlignmentI lastmatchedAl = null;
+
+  private SequenceIdMatcher matcher = null;
+
+  protected AlignmentI dataset;
+
+  protected int gffVersion;
 
   /**
    * Creates a new FeaturesFile object.
@@ -70,13 +91,15 @@ public class FeaturesFile extends AlignFile
   }
 
   /**
+   * Constructor which does not parse the file immediately
+   * 
    * @param inFile
    * @param type
    * @throws IOException
    */
   public FeaturesFile(String inFile, String type) throws IOException
   {
-    super(inFile, type);
+    super(false, inFile, type);
   }
 
   /**
@@ -89,17 +112,8 @@ public class FeaturesFile extends AlignFile
   }
 
   /**
-   * @param parseImmediately
-   * @param source
-   * @throws IOException
-   */
-  public FeaturesFile(boolean parseImmediately, FileParse source)
-          throws IOException
-  {
-    super(parseImmediately, source);
-  }
-
-  /**
+   * Constructor that optionally parses the file immediately
+   * 
    * @param parseImmediately
    * @param inFile
    * @param type
@@ -123,566 +137,128 @@ public class FeaturesFile extends AlignFile
    *          - process html strings into plain text
    * @return true if features were added
    */
-  public boolean parse(AlignmentI align, Hashtable colours,
-          boolean removeHTML)
-  {
-    return parse(align, colours, null, removeHTML, false);
-  }
-
-  /**
-   * Parse GFF or sequence features file optionally using case-independent
-   * matching, discarding URLs
-   * 
-   * @param align
-   *          - alignment/dataset containing sequences that are to be annotated
-   * @param colours
-   *          - hashtable to store feature colour definitions
-   * @param removeHTML
-   *          - process html strings into plain text
-   * @param relaxedIdmatching
-   *          - when true, ID matches to compound sequence IDs are allowed
-   * @return true if features were added
-   */
-  public boolean parse(AlignmentI align, Map colours, boolean removeHTML,
-          boolean relaxedIdMatching)
+  public boolean parse(AlignmentI align,
+          Map<String, FeatureColourI> colours, boolean removeHTML)
   {
-    return parse(align, colours, null, removeHTML, relaxedIdMatching);
+    return parse(align, colours, removeHTML, false);
   }
 
   /**
-   * Parse GFF or sequence features file optionally using case-independent
-   * matching
-   * 
-   * @param align
-   *          - alignment/dataset containing sequences that are to be annotated
-   * @param colours
-   *          - hashtable to store feature colour definitions
-   * @param featureLink
-   *          - hashtable to store associated URLs
-   * @param removeHTML
-   *          - process html strings into plain text
-   * @return true if features were added
+   * Extends the default addProperties by also adding peptide-to-cDNA mappings
+   * (if any) derived while parsing a GFF file
    */
-  public boolean parse(AlignmentI align, Map colours, Map featureLink,
-          boolean removeHTML)
-  {
-    return parse(align, colours, featureLink, removeHTML, false);
-  }
-
-  @Override
-  public void addAnnotations(AlignmentI al)
-  {
-    // TODO Auto-generated method stub
-    super.addAnnotations(al);
-  }
-
   @Override
   public void addProperties(AlignmentI al)
   {
-    // TODO Auto-generated method stub
     super.addProperties(al);
-  }
-
-  @Override
-  public void addSeqGroups(AlignmentI al)
-  {
-    // TODO Auto-generated method stub
-    super.addSeqGroups(al);
+    if (dataset != null && dataset.getCodonFrames() != null)
+    {
+      AlignmentI ds = (al.getDataset() == null) ? al : al.getDataset();
+      for (AlignedCodonFrame codons : dataset.getCodonFrames())
+      {
+        ds.addCodonFrame(codons);
+      }
+    }
   }
 
   /**
-   * Parse GFF or sequence features file
+   * Parse GFF or Jalview format sequence features file
    * 
    * @param align
    *          - alignment/dataset containing sequences that are to be annotated
    * @param colours
    *          - hashtable to store feature colour definitions
-   * @param featureLink
-   *          - hashtable to store associated URLs
    * @param removeHTML
    *          - process html strings into plain text
    * @param relaxedIdmatching
    *          - when true, ID matches to compound sequence IDs are allowed
    * @return true if features were added
    */
-  public boolean parse(AlignmentI align, Map colours, Map featureLink,
-          boolean removeHTML, boolean relaxedIdmatching)
+  public boolean parse(AlignmentI align,
+          Map<String, FeatureColourI> colours, boolean removeHTML,
+          boolean relaxedIdmatching)
   {
+    Map<String, String> gffProps = new HashMap<String, String>();
+    /*
+     * keep track of any sequences we try to create from the data
+     */
+    List<SequenceI> newseqs = new ArrayList<SequenceI>();
 
     String line = null;
     try
     {
-      SequenceI seq = null;
-      /**
-       * keep track of any sequences we try to create from the data if it is a
-       * GFF3 file
-       */
-      ArrayList<SequenceI> newseqs = new ArrayList<SequenceI>();
-      String type, desc, token = null;
-
-      int index, start, end;
-      float score;
-      StringTokenizer st;
-      SequenceFeature sf;
-      String featureGroup = null, groupLink = null;
-      Map typeLink = new Hashtable();
-      /**
-       * when true, assume GFF style features rather than Jalview style.
-       */
-      boolean GFFFile = true;
-      Map<String, String> gffProps = new HashMap<String, String>();
+      String[] gffColumns;
+      String featureGroup = null;
+
       while ((line = nextLine()) != null)
       {
         // skip comments/process pragmas
-        if (line.startsWith("#"))
+        if (line.length() == 0 || line.startsWith("#"))
         {
-          if (line.startsWith("##"))
+          if (line.toLowerCase().startsWith("##"))
           {
-            // possibly GFF2/3 version and metadata header
             processGffPragma(line, gffProps, align, newseqs);
-            line = "";
           }
           continue;
         }
 
-        st = new StringTokenizer(line, "\t");
-        if (st.countTokens() == 1)
+        gffColumns = line.split("\\t"); // tab as regex
+        if (gffColumns.length == 1)
         {
           if (line.trim().equalsIgnoreCase("GFF"))
           {
-            // Start parsing file as if it might be GFF again.
-            GFFFile = true;
+            /*
+             * Jalview features file with appended GFF
+             * assume GFF2 (though it may declare ##gff-version 3)
+             */
+            gffVersion = 2;
             continue;
           }
         }
-        if (st.countTokens() > 1 && st.countTokens() < 4)
+
+        if (gffColumns.length > 1 && gffColumns.length < 4)
         {
-          GFFFile = false;
-          type = st.nextToken();
-          if (type.equalsIgnoreCase("startgroup"))
+          /*
+           * if 2 or 3 tokens, we anticipate either 'startgroup', 'endgroup' or
+           * a feature type colour specification
+           */
+          String ft = gffColumns[0];
+          if (ft.equalsIgnoreCase("startgroup"))
           {
-            featureGroup = st.nextToken();
-            if (st.hasMoreElements())
-            {
-              groupLink = st.nextToken();
-              featureLink.put(featureGroup, groupLink);
-            }
+            featureGroup = gffColumns[1];
           }
-          else if (type.equalsIgnoreCase("endgroup"))
+          else if (ft.equalsIgnoreCase("endgroup"))
           {
             // We should check whether this is the current group,
-            // but at present theres no way of showing more than 1 group
-            st.nextToken();
+            // but at present there's no way of showing more than 1 group
             featureGroup = null;
-            groupLink = null;
           }
           else
           {
-            Object colour = null;
-            String colscheme = st.nextToken();
-            if (colscheme.indexOf("|") > -1
-                    || colscheme.trim().equalsIgnoreCase("label"))
-            {
-              // Parse '|' separated graduated colourscheme fields:
-              // [label|][mincolour|maxcolour|[absolute|]minvalue|maxvalue|thresholdtype|thresholdvalue]
-              // can either provide 'label' only, first is optional, next two
-              // colors are required (but may be
-              // left blank), next is optional, nxt two min/max are required.
-              // first is either 'label'
-              // first/second and third are both hexadecimal or word equivalent
-              // colour.
-              // next two are values parsed as floats.
-              // fifth is either 'above','below', or 'none'.
-              // sixth is a float value and only required when fifth is either
-              // 'above' or 'below'.
-              StringTokenizer gcol = new StringTokenizer(colscheme, "|",
-                      true);
-              // set defaults
-              int threshtype = AnnotationColourGradient.NO_THRESHOLD;
-              float min = Float.MIN_VALUE, max = Float.MAX_VALUE, threshval = Float.NaN;
-              boolean labelCol = false;
-              // Parse spec line
-              String mincol = gcol.nextToken();
-              if (mincol == "|")
-              {
-                System.err
-                        .println("Expected either 'label' or a colour specification in the line: "
-                                + line);
-                continue;
-              }
-              String maxcol = null;
-              if (mincol.toLowerCase().indexOf("label") == 0)
-              {
-                labelCol = true;
-                mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null); // skip
-                                                                           // '|'
-                mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
-              }
-              String abso = null, minval, maxval;
-              if (mincol != null)
-              {
-                // at least four more tokens
-                if (mincol.equals("|"))
-                {
-                  mincol = "";
-                }
-                else
-                {
-                  gcol.nextToken(); // skip next '|'
-                }
-                // continue parsing rest of line
-                maxcol = gcol.nextToken();
-                if (maxcol.equals("|"))
-                {
-                  maxcol = "";
-                }
-                else
-                {
-                  gcol.nextToken(); // skip next '|'
-                }
-                abso = gcol.nextToken();
-                gcol.nextToken(); // skip next '|'
-                if (abso.toLowerCase().indexOf("abso") != 0)
-                {
-                  minval = abso;
-                  abso = null;
-                }
-                else
-                {
-                  minval = gcol.nextToken();
-                  gcol.nextToken(); // skip next '|'
-                }
-                maxval = gcol.nextToken();
-                if (gcol.hasMoreTokens())
-                {
-                  gcol.nextToken(); // skip next '|'
-                }
-                try
-                {
-                  if (minval.length() > 0)
-                  {
-                    min = new Float(minval).floatValue();
-                  }
-                } catch (Exception e)
-                {
-                  System.err
-                          .println("Couldn't parse the minimum value for graduated colour for type ("
-                                  + colscheme
-                                  + ") - did you misspell 'auto' for the optional automatic colour switch ?");
-                  e.printStackTrace();
-                }
-                try
-                {
-                  if (maxval.length() > 0)
-                  {
-                    max = new Float(maxval).floatValue();
-                  }
-                } catch (Exception e)
-                {
-                  System.err
-                          .println("Couldn't parse the maximum value for graduated colour for type ("
-                                  + colscheme + ")");
-                  e.printStackTrace();
-                }
-              }
-              else
-              {
-                // add in some dummy min/max colours for the label-only
-                // colourscheme.
-                mincol = "FFFFFF";
-                maxcol = "000000";
-              }
-              try
-              {
-                colour = new jalview.schemes.GraduatedColor(
-                        new UserColourScheme(mincol).findColour('A'),
-                        new UserColourScheme(maxcol).findColour('A'), min,
-                        max);
-              } catch (Exception e)
-              {
-                System.err
-                        .println("Couldn't parse the graduated colour scheme ("
-                                + colscheme + ")");
-                e.printStackTrace();
-              }
-              if (colour != null)
-              {
-                ((jalview.schemes.GraduatedColor) colour)
-                        .setColourByLabel(labelCol);
-                ((jalview.schemes.GraduatedColor) colour)
-                        .setAutoScaled(abso == null);
-                // add in any additional parameters
-                String ttype = null, tval = null;
-                if (gcol.hasMoreTokens())
-                {
-                  // threshold type and possibly a threshold value
-                  ttype = gcol.nextToken();
-                  if (ttype.toLowerCase().startsWith("below"))
-                  {
-                    ((jalview.schemes.GraduatedColor) colour)
-                            .setThreshType(AnnotationColourGradient.BELOW_THRESHOLD);
-                  }
-                  else if (ttype.toLowerCase().startsWith("above"))
-                  {
-                    ((jalview.schemes.GraduatedColor) colour)
-                            .setThreshType(AnnotationColourGradient.ABOVE_THRESHOLD);
-                  }
-                  else
-                  {
-                    ((jalview.schemes.GraduatedColor) colour)
-                            .setThreshType(AnnotationColourGradient.NO_THRESHOLD);
-                    if (!ttype.toLowerCase().startsWith("no"))
-                    {
-                      System.err
-                              .println("Ignoring unrecognised threshold type : "
-                                      + ttype);
-                    }
-                  }
-                }
-                if (((GraduatedColor) colour).getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
-                {
-                  try
-                  {
-                    gcol.nextToken();
-                    tval = gcol.nextToken();
-                    ((jalview.schemes.GraduatedColor) colour)
-                            .setThresh(new Float(tval).floatValue());
-                  } catch (Exception e)
-                  {
-                    System.err
-                            .println("Couldn't parse threshold value as a float: ("
-                                    + tval + ")");
-                    e.printStackTrace();
-                  }
-                }
-                // parse the thresh-is-min token ?
-                if (gcol.hasMoreTokens())
-                {
-                  System.err
-                          .println("Ignoring additional tokens in parameters in graduated colour specification\n");
-                  while (gcol.hasMoreTokens())
-                  {
-                    System.err.println("|" + gcol.nextToken());
-                  }
-                  System.err.println("\n");
-                }
-              }
-            }
-            else
-            {
-              UserColourScheme ucs = new UserColourScheme(colscheme);
-              colour = ucs.findColour('A');
-            }
+            String colscheme = gffColumns[1];
+            FeatureColourI colour = FeatureColour
+                    .parseJalviewFeatureColour(colscheme);
             if (colour != null)
             {
-              colours.put(type, colour);
-            }
-            if (st.hasMoreElements())
-            {
-              String link = st.nextToken();
-              typeLink.put(type, link);
-              if (featureLink == null)
-              {
-                featureLink = new Hashtable();
-              }
-              featureLink.put(type, link);
+              colours.put(ft, colour);
             }
           }
           continue;
         }
-        String seqId = "";
-        while (st.hasMoreElements())
-        {
-
-          if (GFFFile)
-          {
-            // Still possible this is an old Jalview file,
-            // which does not have type colours at the beginning
-            seqId = token = st.nextToken();
-            seq = findName(align, seqId, relaxedIdmatching, newseqs);
-            if (seq != null)
-            {
-              desc = st.nextToken();
-              String group = null;
-              if (doGffSource && desc.indexOf(' ') == -1)
-              {
-                // could also be a source term rather than description line
-                group = new String(desc);
-              }
-              type = st.nextToken();
-              try
-              {
-                String stt = st.nextToken();
-                if (stt.length() == 0 || stt.equals("-"))
-                {
-                  start = 0;
-                }
-                else
-                {
-                  start = Integer.parseInt(stt);
-                }
-              } catch (NumberFormatException ex)
-              {
-                start = 0;
-              }
-              try
-              {
-                String stt = st.nextToken();
-                if (stt.length() == 0 || stt.equals("-"))
-                {
-                  end = 0;
-                }
-                else
-                {
-                  end = Integer.parseInt(stt);
-                }
-              } catch (NumberFormatException ex)
-              {
-                end = 0;
-              }
-              // TODO: decide if non positional feature assertion for input data
-              // where end==0 is generally valid
-              if (end == 0)
-              {
-                // treat as non-positional feature, regardless.
-                start = 0;
-              }
-              try
-              {
-                score = new Float(st.nextToken()).floatValue();
-              } catch (NumberFormatException ex)
-              {
-                score = 0;
-              }
-
-              sf = new SequenceFeature(type, desc, start, end, score, group);
-
-              try
-              {
-                sf.setValue("STRAND", st.nextToken());
-                sf.setValue("FRAME", st.nextToken());
-              } catch (Exception ex)
-              {
-              }
-
-              if (st.hasMoreTokens())
-              {
-                StringBuffer attributes = new StringBuffer();
-                boolean sep = false;
-                while (st.hasMoreTokens())
-                {
-                  attributes.append((sep ? "\t" : "") + st.nextElement());
-                  sep = true;
-                }
-                // TODO validate and split GFF2 attributes field ? parse out
-                // ([A-Za-z][A-Za-z0-9_]*) <value> ; and add as
-                // sf.setValue(attrib, val);
-                sf.setValue("ATTRIBUTES", attributes.toString());
-              }
-
-              if (processOrAddSeqFeature(align, newseqs, seq, sf, GFFFile,
-                      relaxedIdmatching))
-              {
-                // check whether we should add the sequence feature to any other
-                // sequences in the alignment with the same or similar
-                while ((seq = align.findName(seq, seqId, true)) != null)
-                {
-                  seq.addSequenceFeature(new SequenceFeature(sf));
-                }
-              }
-              break;
-            }
-          }
-
-          if (GFFFile && seq == null)
-          {
-            desc = token;
-          }
-          else
-          {
-            desc = st.nextToken();
-          }
-          if (!st.hasMoreTokens())
-          {
-            System.err
-                    .println("DEBUG: Run out of tokens when trying to identify the destination for the feature.. giving up.");
-            // in all probability, this isn't a file we understand, so bail
-            // quietly.
-            return false;
-          }
-
-          token = st.nextToken();
-
-          if (!token.equals("ID_NOT_SPECIFIED"))
-          {
-            seq = findName(align, seqId = token, relaxedIdmatching, null);
-            st.nextToken();
-          }
-          else
-          {
-            seqId = null;
-            try
-            {
-              index = Integer.parseInt(st.nextToken());
-              seq = align.getSequenceAt(index);
-            } catch (NumberFormatException ex)
-            {
-              seq = null;
-            }
-          }
-
-          if (seq == null)
-          {
-            System.out.println("Sequence not found: " + line);
-            break;
-          }
-
-          start = Integer.parseInt(st.nextToken());
-          end = Integer.parseInt(st.nextToken());
-
-          type = st.nextToken();
-
-          if (!colours.containsKey(type))
-          {
-            // Probably the old style groups file
-            UserColourScheme ucs = new UserColourScheme(type);
-            colours.put(type, ucs.findColour('A'));
-          }
-          sf = new SequenceFeature(type, desc, "", start, end, featureGroup);
-          if (st.hasMoreTokens())
-          {
-            try
-            {
-              score = new Float(st.nextToken()).floatValue();
-              // update colourgradient bounds if allowed to
-            } catch (NumberFormatException ex)
-            {
-              score = 0;
-            }
-            sf.setScore(score);
-          }
-          if (groupLink != null && removeHTML)
-          {
-            sf.addLink(groupLink);
-            sf.description += "%LINK%";
-          }
-          if (typeLink.containsKey(type) && removeHTML)
-          {
-            sf.addLink(typeLink.get(type).toString());
-            sf.description += "%LINK%";
-          }
-
-          parseDescriptionHTML(sf, removeHTML);
 
-          seq.addSequenceFeature(sf);
-
-          while (seqId != null
-                  && (seq = align.findName(seq, seqId, false)) != null)
-          {
-            seq.addSequenceFeature(new SequenceFeature(sf));
-          }
-          // If we got here, its not a GFFFile
-          GFFFile = false;
+        /*
+         * if not a comment, GFF pragma, startgroup, endgroup or feature
+         * colour specification, that just leaves a feature details line
+         * in either Jalview or GFF format
+         */
+        if (gffVersion == 0)
+        {
+          parseJalviewFeature(line, gffColumns, align, colours, removeHTML,
+                  relaxedIdmatching, featureGroup);
+        }
+        else
+        {
+          parseGff(gffColumns, align, relaxedIdmatching, newseqs);
         }
       }
       resetMatcher();
@@ -697,428 +273,158 @@ public class FeaturesFile extends AlignFile
       return false;
     }
 
-    return true;
-  }
-
-  private enum GffPragmas
-  {
-    gff_version, sequence_region, feature_ontology, attribute_ontology, source_ontology, species_build, fasta, hash
-  };
-
-  private static Map<String, GffPragmas> GFFPRAGMA;
-  static
-  {
-    GFFPRAGMA = new HashMap<String, GffPragmas>();
-    GFFPRAGMA.put("sequence-region", GffPragmas.sequence_region);
-    GFFPRAGMA.put("feature-ontology", GffPragmas.feature_ontology);
-    GFFPRAGMA.put("#", GffPragmas.hash);
-    GFFPRAGMA.put("fasta", GffPragmas.fasta);
-    GFFPRAGMA.put("species-build", GffPragmas.species_build);
-    GFFPRAGMA.put("source-ontology", GffPragmas.source_ontology);
-    GFFPRAGMA.put("attribute-ontology", GffPragmas.attribute_ontology);
-  }
-
-  private void processGffPragma(String line, Map<String, String> gffProps,
-          AlignmentI align, ArrayList<SequenceI> newseqs)
-          throws IOException
-  {
-    // line starts with ##
-    int spacepos = line.indexOf(' ');
-    String pragma = spacepos == -1 ? line.substring(2).trim() : line
-            .substring(2, spacepos);
-    GffPragmas gffpragma = GFFPRAGMA.get(pragma.toLowerCase());
-    if (gffpragma == null)
-    {
-      return;
-    }
-    switch (gffpragma)
+    /*
+     * experimental - add any dummy sequences with features to the alignment
+     * - we need them for Ensembl feature extraction - though maybe not otherwise
+     */
+    for (SequenceI newseq : newseqs)
     {
-    case gff_version:
-      try
-      {
-        gffversion = Integer.parseInt(line.substring(spacepos + 1));
-      } finally
+      if (newseq.getSequenceFeatures() != null)
       {
-
+        align.addSequence(newseq);
       }
-      break;
-    case feature_ontology:
-      // resolve against specific feature ontology
-      break;
-    case attribute_ontology:
-      // resolve against specific attribute ontology
-      break;
-    case source_ontology:
-      // resolve against specific source ontology
-      break;
-    case species_build:
-      // resolve against specific NCBI taxon version
-      break;
-    case hash:
-      // close off any open feature hierarchies
-      break;
-    case fasta:
-      // process the rest of the file as a fasta file and replace any dummy
-      // sequence IDs
-      process_as_fasta(align, newseqs);
-      break;
-    default:
-      // we do nothing ?
-      System.err.println("Ignoring unknown pragma:\n" + line);
     }
-  }
-
-  private void process_as_fasta(AlignmentI align, List<SequenceI> newseqs)
-          throws IOException
-  {
-    try
-    {
-      mark();
-    } catch (IOException q)
-    {
-    }
-    FastaFile parser = new FastaFile(this);
-    List<SequenceI> includedseqs = parser.getSeqs();
-    SequenceIdMatcher smatcher = new SequenceIdMatcher(newseqs);
-    // iterate over includedseqs, and replacing matching ones with newseqs
-    // sequences. Generic iterator not used here because we modify includedseqs
-    // as we go
-    for (int p = 0, pSize = includedseqs.size(); p < pSize; p++)
-    {
-      // search for any dummy seqs that this sequence can be used to update
-      SequenceI dummyseq = smatcher.findIdMatch(includedseqs.get(p));
-      if (dummyseq != null)
-      {
-        // dummyseq was created so it could be annotated and referred to in
-        // alignments/codon mappings
-
-        SequenceI mseq = includedseqs.get(p);
-        // mseq is the 'template' imported from the FASTA file which we'll use
-        // to coomplete dummyseq
-        if (dummyseq instanceof SequenceDummy)
-        {
-          // probably have the pattern wrong
-          // idea is that a flyweight proxy for a sequence ID can be created for
-          // 1. stable reference creation
-          // 2. addition of annotation
-          // 3. future replacement by a real sequence
-          // current pattern is to create SequenceDummy objects - a convenience
-          // constructor for a Sequence.
-          // problem is that when promoted to a real sequence, all references
-          // need
-          // to be updated somehow.
-          ((SequenceDummy) dummyseq).become(mseq);
-          includedseqs.set(p, dummyseq); // template is no longer needed
-        }
-      }
-    }
-    // finally add sequences to the dataset
-    for (SequenceI seq : includedseqs)
-    {
-      align.addSequence(seq);
-    }
-  }
-
-  /**
-   * take a sequence feature and examine its attributes to decide how it should
-   * be added to a sequence
-   * 
-   * @param seq
-   *          - the destination sequence constructed or discovered in the
-   *          current context
-   * @param sf
-   *          - the base feature with ATTRIBUTES property containing any
-   *          additional attributes
-   * @param gFFFile
-   *          - true if we are processing a GFF annotation file
-   * @return true if sf was actually added to the sequence, false if it was
-   *         processed in another way
-   */
-  public boolean processOrAddSeqFeature(AlignmentI align,
-          List<SequenceI> newseqs, SequenceI seq, SequenceFeature sf,
-          boolean gFFFile, boolean relaxedIdMatching)
-  {
-    String attr = (String) sf.getValue("ATTRIBUTES");
-    boolean add = true;
-    if (gFFFile && attr != null)
-    {
-      int nattr = 8;
-
-      for (String attset : attr.split("\t"))
-      {
-        if (attset == null || attset.trim().length() == 0)
-        {
-          continue;
-        }
-        nattr++;
-        Map<String, List<String>> set = new HashMap<String, List<String>>();
-        // normally, only expect one column - 9 - in this field
-        // the attributes (Gff3) or groups (gff2) field
-        for (String pair : attset.trim().split(";"))
-        {
-          pair = pair.trim();
-          if (pair.length() == 0)
-          {
-            continue;
-          }
-
-          // expect either space seperated (gff2) or '=' separated (gff3)
-          // key/value pairs here
-
-          int eqpos = pair.indexOf('='), sppos = pair.indexOf(' ');
-          String key = null, value = null;
-
-          if (sppos > -1 && (eqpos == -1 || sppos < eqpos))
-          {
-            key = pair.substring(0, sppos);
-            value = pair.substring(sppos + 1);
-          }
-          else
-          {
-            if (eqpos > -1 && (sppos == -1 || eqpos < sppos))
-            {
-              key = pair.substring(0, eqpos);
-              value = pair.substring(eqpos + 1);
-            }
-            else
-            {
-              key = pair;
-            }
-          }
-          if (key != null)
-          {
-            List<String> vals = set.get(key);
-            if (vals == null)
-            {
-              vals = new ArrayList<String>();
-              set.put(key, vals);
-            }
-            if (value != null)
-            {
-              vals.add(value.trim());
-            }
-          }
-        }
-        try
-        {
-          add &= processGffKey(set, nattr, seq, sf, align, newseqs,
-                  relaxedIdMatching); // process decides if
-                                      // feature is actually
-                                      // added
-        } catch (InvalidGFF3FieldException ivfe)
-        {
-          System.err.println(ivfe);
-        }
-      }
-    }
-    if (add)
-    {
-      seq.addSequenceFeature(sf);
-    }
-    return add;
-  }
-
-  public class InvalidGFF3FieldException extends Exception
-  {
-    String field, value;
-
-    public InvalidGFF3FieldException(String field,
-            Map<String, List<String>> set, String message)
-    {
-      super(message + " (Field was " + field + " and value was "
-              + set.get(field).toString());
-      this.field = field;
-      this.value = set.get(field).toString();
-    }
-
+    return true;
   }
 
   /**
-   * take a set of keys for a feature and interpret them
+   * Try to parse a Jalview format feature specification and add it as a
+   * sequence feature to any matching sequences in the alignment. Returns true
+   * if successful (a feature was added), or false if not.
    * 
-   * @param set
-   * @param nattr
-   * @param seq
-   * @param sf
-   * @return
+   * @param line
+   * @param gffColumns
+   * @param alignment
+   * @param featureColours
+   * @param removeHTML
+   * @param relaxedIdmatching
+   * @param featureGroup
    */
-  public boolean processGffKey(Map<String, List<String>> set, int nattr,
-          SequenceI seq, SequenceFeature sf, AlignmentI align,
-          List<SequenceI> newseqs, boolean relaxedIdMatching)
-          throws InvalidGFF3FieldException
+  protected boolean parseJalviewFeature(String line, String[] gffColumns,
+          AlignmentI alignment, Map<String, FeatureColourI> featureColours,
+          boolean removeHTML, boolean relaxedIdMatching, String featureGroup)
   {
-    String attr;
-    // decide how to interpret according to type
-    if (sf.getType().equals("similarity"))
+    /*
+     * tokens: description seqid seqIndex start end type [score]
+     */
+    if (gffColumns.length < 6)
     {
-      int strand = sf.getStrand();
-      // exonerate cdna/protein map
-      // look for fields
-      List<SequenceI> querySeq = findNames(align, newseqs,
-              relaxedIdMatching, set.get(attr = "Query"));
-      if (querySeq == null || querySeq.size() != 1)
-      {
-        throw new InvalidGFF3FieldException(attr, set,
-                "Expecting exactly one sequence in Query field (got "
-                        + set.get(attr) + ")");
-      }
-      if (set.containsKey(attr = "Align"))
-      {
-        // process the align maps and create cdna/protein maps
-        // ideally, the query sequences are in the alignment, but maybe not...
-
-        AlignedCodonFrame alco = new AlignedCodonFrame();
-        MapList codonmapping = constructCodonMappingFromAlign(set, attr,
-                strand);
-
-        // add codon mapping, and hope!
-        alco.addMap(seq, querySeq.get(0), codonmapping);
-        align.addCodonFrame(alco);
-        // everything that's needed to be done is done
-        // no features to create here !
-        return false;
-      }
-
+      System.err.println("Ignoring feature line '" + line
+              + "' with too few columns (" + gffColumns.length + ")");
+      return false;
     }
-    return true;
-  }
+    String desc = gffColumns[0];
+    String seqId = gffColumns[1];
+    SequenceI seq = findSequence(seqId, alignment, null, relaxedIdMatching);
 
-  private MapList constructCodonMappingFromAlign(
-          Map<String, List<String>> set, String attr, int strand)
-          throws InvalidGFF3FieldException
-  {
-    if (strand == 0)
+    if (!ID_NOT_SPECIFIED.equals(seqId))
     {
-      throw new InvalidGFF3FieldException(attr, set,
-              "Invalid strand for a codon mapping (cannot be 0)");
+      seq = findSequence(seqId, alignment, null, relaxedIdMatching);
     }
-    List<Integer> fromrange = new ArrayList<Integer>(), torange = new ArrayList<Integer>();
-    int lastppos = 0, lastpframe = 0;
-    for (String range : set.get(attr))
+    else
     {
-      List<Integer> ints = new ArrayList<Integer>();
-      StringTokenizer st = new StringTokenizer(range, " ");
-      while (st.hasMoreTokens())
-      {
-        String num = st.nextToken();
-        try
-        {
-          ints.add(new Integer(num));
-        } catch (NumberFormatException nfe)
-        {
-          throw new InvalidGFF3FieldException(attr, set,
-                  "Invalid number in field " + num);
-        }
-      }
-      // Align positionInRef positionInQuery LengthInRef
-      // contig_1146 exonerate:protein2genome:local similarity 8534 11269
-      // 3652 - . alignment_id 0 ;
-      // Query DDB_G0269124
-      // Align 11270 143 120
-      // corresponds to : 120 bases align at pos 143 in protein to 11270 on
-      // dna in strand direction
-      // Align 11150 187 282
-      // corresponds to : 282 bases align at pos 187 in protein to 11150 on
-      // dna in strand direction
-      //
-      // Align 10865 281 888
-      // Align 9977 578 1068
-      // Align 8909 935 375
-      //
-      if (ints.size() != 3)
-      {
-        throw new InvalidGFF3FieldException(attr, set,
-                "Invalid number of fields for this attribute ("
-                        + ints.size() + ")");
-      }
-      fromrange.add(new Integer(ints.get(0).intValue()));
-      fromrange.add(new Integer(ints.get(0).intValue() + strand
-              * ints.get(2).intValue()));
-      // how are intron/exon boundaries that do not align in codons
-      // represented
-      if (ints.get(1).equals(lastppos) && lastpframe > 0)
+      seqId = null;
+      seq = null;
+      String seqIndex = gffColumns[2];
+      try
       {
-        // extend existing to map
-        lastppos += ints.get(2) / 3;
-        lastpframe = ints.get(2) % 3;
-        torange.set(torange.size() - 1, new Integer(lastppos));
-      }
-      else
+        int idx = Integer.parseInt(seqIndex);
+        seq = alignment.getSequenceAt(idx);
+      } catch (NumberFormatException ex)
       {
-        // new to map range
-        torange.add(ints.get(1));
-        lastppos = ints.get(1) + ints.get(2) / 3;
-        lastpframe = ints.get(2) % 3;
-        torange.add(new Integer(lastppos));
+        System.err.println("Invalid sequence index: " + seqIndex);
       }
     }
-    // from and to ranges must end up being a series of start/end intervals
-    if (fromrange.size() % 2 == 1)
-    {
-      throw new InvalidGFF3FieldException(attr, set,
-              "Couldn't parse the DNA alignment range correctly");
-    }
-    if (torange.size() % 2 == 1)
+
+    if (seq == null)
     {
-      throw new InvalidGFF3FieldException(attr, set,
-              "Couldn't parse the protein alignment range correctly");
+      System.out.println("Sequence not found: " + line);
+      return false;
     }
-    // finally, build the map
-    int[] frommap = new int[fromrange.size()], tomap = new int[torange
-            .size()];
-    int p = 0;
-    for (Integer ip : fromrange)
+
+    int startPos = Integer.parseInt(gffColumns[3]);
+    int endPos = Integer.parseInt(gffColumns[4]);
+
+    String ft = gffColumns[5];
+
+    if (!featureColours.containsKey(ft))
     {
-      frommap[p++] = ip.intValue();
+      /* 
+       * Perhaps an old style groups file with no colours -
+       * synthesize a colour from the feature type
+       */
+      UserColourScheme ucs = new UserColourScheme(ft);
+      featureColours.put(ft, new FeatureColour(ucs.findColour('A')));
     }
-    p = 0;
-    for (Integer ip : torange)
+    SequenceFeature sf = new SequenceFeature(ft, desc, "", startPos,
+            endPos, featureGroup);
+    if (gffColumns.length > 6)
     {
-      tomap[p++] = ip.intValue();
+      float score = Float.NaN;
+      try
+      {
+        score = new Float(gffColumns[6]).floatValue();
+        // update colourgradient bounds if allowed to
+      } catch (NumberFormatException ex)
+      {
+        // leave as NaN
+      }
+      sf.setScore(score);
     }
 
-    return new MapList(frommap, tomap, 3, 1);
-  }
+    parseDescriptionHTML(sf, removeHTML);
 
-  private List<SequenceI> findNames(AlignmentI align,
-          List<SequenceI> newseqs, boolean relaxedIdMatching,
-          List<String> list)
-  {
-    List<SequenceI> found = new ArrayList<SequenceI>();
-    for (String seqId : list)
+    seq.addSequenceFeature(sf);
+
+    while (seqId != null
+            && (seq = alignment.findName(seq, seqId, false)) != null)
     {
-      SequenceI seq = findName(align, seqId, relaxedIdMatching, newseqs);
-      if (seq != null)
-      {
-        found.add(seq);
-      }
+      seq.addSequenceFeature(new SequenceFeature(sf));
     }
-    return found;
+    return true;
   }
 
-  private AlignmentI lastmatchedAl = null;
-
-  private SequenceIdMatcher matcher = null;
-
   /**
    * clear any temporary handles used to speed up ID matching
    */
-  private void resetMatcher()
+  protected void resetMatcher()
   {
     lastmatchedAl = null;
     matcher = null;
   }
 
-  private SequenceI findName(AlignmentI align, String seqId,
-          boolean relaxedIdMatching, List<SequenceI> newseqs)
+  /**
+   * Returns a sequence matching the given id, as follows
+   * <ul>
+   * <li>strict matching is on exact sequence name</li>
+   * <li>relaxed matching allows matching on a token within the sequence name,
+   * or a dbxref</li>
+   * <li>first tries to find a match in the alignment sequences</li>
+   * <li>else tries to find a match in the new sequences already generated while
+   * parsing the features file</li>
+   * <li>else creates a new placeholder sequence, adds it to the new sequences
+   * list, and returns it</li>
+   * </ul>
+   * 
+   * @param seqId
+   * @param align
+   * @param newseqs
+   * @param relaxedIdMatching
+   * 
+   * @return
+   */
+  protected SequenceI findSequence(String seqId, AlignmentI align,
+          List<SequenceI> newseqs, boolean relaxedIdMatching)
   {
+    // TODO encapsulate in SequenceIdMatcher, share the matcher
+    // with the GffHelper (removing code duplication)
     SequenceI match = null;
     if (relaxedIdMatching)
     {
       if (lastmatchedAl != align)
       {
-        matcher = new SequenceIdMatcher(
-                (lastmatchedAl = align).getSequencesArray());
+        lastmatchedAl = align;
+        matcher = new SequenceIdMatcher(align.getSequencesArray());
         if (newseqs != null)
         {
           matcher.addAll(newseqs);
@@ -1160,7 +466,7 @@ public class FeaturesFile extends AlignFile
     {
       return;
     }
-    jalview.util.ParseHtmlBodyAndLinks parsed = new jalview.util.ParseHtmlBodyAndLinks(
+    ParseHtmlBodyAndLinks parsed = new ParseHtmlBodyAndLinks(
             sf.getDescription(), removeHTML, newline);
 
     sf.description = (removeHTML) ? parsed.getNonHtmlContent()
@@ -1175,22 +481,22 @@ public class FeaturesFile extends AlignFile
   /**
    * generate a features file for seqs includes non-pos features by default.
    * 
-   * @param seqs
+   * @param sequences
    *          source of sequence features
    * @param visible
    *          hash of feature types and colours
    * @return features file contents
    */
-  public String printJalviewFormat(SequenceI[] seqs,
-          Map<String, Object> visible)
+  public String printJalviewFormat(SequenceI[] sequences,
+          Map<String, FeatureColourI> visible)
   {
-    return printJalviewFormat(seqs, visible, true, true);
+    return printJalviewFormat(sequences, visible, true, true);
   }
 
   /**
    * generate a features file for seqs with colours from visible (if any)
    * 
-   * @param seqs
+   * @param sequences
    *          source of features
    * @param visible
    *          hash of Colours for each feature type
@@ -1201,11 +507,11 @@ public class FeaturesFile extends AlignFile
    *          of group or type)
    * @return features file contents
    */
-  public String printJalviewFormat(SequenceI[] seqs, Map visible,
-          boolean visOnly, boolean nonpos)
+  public String printJalviewFormat(SequenceI[] sequences,
+          Map<String, FeatureColourI> visible, boolean visOnly,
+          boolean nonpos)
   {
-    StringBuffer out = new StringBuffer();
-    SequenceFeature[] next;
+    StringBuilder out = new StringBuilder(256);
     boolean featuresGen = false;
     if (visOnly && !nonpos && (visible == null || visible.size() < 1))
     {
@@ -1218,83 +524,40 @@ public class FeaturesFile extends AlignFile
       // write feature colours only if we're given them and we are generating
       // viewed features
       // TODO: decide if feature links should also be written here ?
-      Iterator en = visible.keySet().iterator();
-      String type, color;
+      Iterator<String> en = visible.keySet().iterator();
       while (en.hasNext())
       {
-        type = en.next().toString();
-
-        if (visible.get(type) instanceof GraduatedColor)
-        {
-          GraduatedColor gc = (GraduatedColor) visible.get(type);
-          color = (gc.isColourByLabel() ? "label|" : "")
-                  + Format.getHexString(gc.getMinColor()) + "|"
-                  + Format.getHexString(gc.getMaxColor())
-                  + (gc.isAutoScale() ? "|" : "|abso|") + gc.getMin() + "|"
-                  + gc.getMax() + "|";
-          if (gc.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
-          {
-            if (gc.getThreshType() == AnnotationColourGradient.BELOW_THRESHOLD)
-            {
-              color += "below";
-            }
-            else
-            {
-              if (gc.getThreshType() != AnnotationColourGradient.ABOVE_THRESHOLD)
-              {
-                System.err.println("WARNING: Unsupported threshold type ("
-                        + gc.getThreshType() + ") : Assuming 'above'");
-              }
-              color += "above";
-            }
-            // add the value
-            color += "|" + gc.getThresh();
-          }
-          else
-          {
-            color += "none";
-          }
-        }
-        else if (visible.get(type) instanceof java.awt.Color)
-        {
-          color = Format.getHexString((java.awt.Color) visible.get(type));
-        }
-        else
-        {
-          // legacy support for integer objects containing colour triplet values
-          color = Format.getHexString(new java.awt.Color(Integer
-                  .parseInt(visible.get(type).toString())));
-        }
-        out.append(type);
-        out.append("\t");
-        out.append(color);
-        out.append(newline);
+        String featureType = en.next().toString();
+        FeatureColourI colour = visible.get(featureType);
+        out.append(colour.toJalviewFormat(featureType)).append(newline);
       }
     }
+
     // Work out which groups are both present and visible
-    Vector groups = new Vector();
+    List<String> groups = new ArrayList<String>();
     int groupIndex = 0;
     boolean isnonpos = false;
 
-    for (int i = 0; i < seqs.length; i++)
+    SequenceFeature[] features;
+    for (int i = 0; i < sequences.length; i++)
     {
-      next = seqs[i].getSequenceFeatures();
-      if (next != null)
+      features = sequences[i].getSequenceFeatures();
+      if (features != null)
       {
-        for (int j = 0; j < next.length; j++)
+        for (int j = 0; j < features.length; j++)
         {
-          isnonpos = next[j].begin == 0 && next[j].end == 0;
+          isnonpos = features[j].begin == 0 && features[j].end == 0;
           if ((!nonpos && isnonpos)
                   || (!isnonpos && visOnly && !visible
-                          .containsKey(next[j].type)))
+                          .containsKey(features[j].type)))
           {
             continue;
           }
 
-          if (next[j].featureGroup != null
-                  && !groups.contains(next[j].featureGroup))
+          if (features[j].featureGroup != null
+                  && !groups.contains(features[j].featureGroup))
           {
-            groups.addElement(next[j].featureGroup);
+            groups.add(features[j].featureGroup);
           }
         }
       }
@@ -1303,12 +566,11 @@ public class FeaturesFile extends AlignFile
     String group = null;
     do
     {
-
       if (groups.size() > 0 && groupIndex < groups.size())
       {
-        group = groups.elementAt(groupIndex).toString();
+        group = groups.get(groupIndex);
         out.append(newline);
-        out.append("STARTGROUP\t");
+        out.append("STARTGROUP").append(TAB);
         out.append(group);
         out.append(newline);
       }
@@ -1317,17 +579,18 @@ public class FeaturesFile extends AlignFile
         group = null;
       }
 
-      for (int i = 0; i < seqs.length; i++)
+      for (int i = 0; i < sequences.length; i++)
       {
-        next = seqs[i].getSequenceFeatures();
-        if (next != null)
+        features = sequences[i].getSequenceFeatures();
+        if (features != null)
         {
-          for (int j = 0; j < next.length; j++)
+          for (SequenceFeature sequenceFeature : features)
           {
-            isnonpos = next[j].begin == 0 && next[j].end == 0;
+            isnonpos = sequenceFeature.begin == 0
+                    && sequenceFeature.end == 0;
             if ((!nonpos && isnonpos)
                     || (!isnonpos && visOnly && !visible
-                            .containsKey(next[j].type)))
+                            .containsKey(sequenceFeature.type)))
             {
               // skip if feature is nonpos and we ignore them or if we only
               // output visible and it isn't non-pos and it's not visible
@@ -1335,65 +598,66 @@ public class FeaturesFile extends AlignFile
             }
 
             if (group != null
-                    && (next[j].featureGroup == null || !next[j].featureGroup
+                    && (sequenceFeature.featureGroup == null || !sequenceFeature.featureGroup
                             .equals(group)))
             {
               continue;
             }
 
-            if (group == null && next[j].featureGroup != null)
+            if (group == null && sequenceFeature.featureGroup != null)
             {
               continue;
             }
             // we have features to output
             featuresGen = true;
-            if (next[j].description == null
-                    || next[j].description.equals(""))
+            if (sequenceFeature.description == null
+                    || sequenceFeature.description.equals(""))
             {
-              out.append(next[j].type + "\t");
+              out.append(sequenceFeature.type).append(TAB);
             }
             else
             {
-              if (next[j].links != null
-                      && next[j].getDescription().indexOf("<html>") == -1)
+              if (sequenceFeature.links != null
+                      && sequenceFeature.getDescription().indexOf("<html>") == -1)
               {
                 out.append("<html>");
               }
 
-              out.append(next[j].description + " ");
-              if (next[j].links != null)
+              out.append(sequenceFeature.description);
+              if (sequenceFeature.links != null)
               {
-                for (int l = 0; l < next[j].links.size(); l++)
+                for (int l = 0; l < sequenceFeature.links.size(); l++)
                 {
-                  String label = next[j].links.elementAt(l).toString();
+                  String label = sequenceFeature.links.elementAt(l);
                   String href = label.substring(label.indexOf("|") + 1);
                   label = label.substring(0, label.indexOf("|"));
 
-                  if (next[j].description.indexOf(href) == -1)
+                  if (sequenceFeature.description.indexOf(href) == -1)
                   {
-                    out.append("<a href=\"" + href + "\">" + label + "</a>");
+                    out.append(" <a href=\"" + href + "\">" + label
+                            + "</a>");
                   }
                 }
 
-                if (next[j].getDescription().indexOf("</html>") == -1)
+                if (sequenceFeature.getDescription().indexOf("</html>") == -1)
                 {
                   out.append("</html>");
                 }
               }
 
-              out.append("\t");
+              out.append(TAB);
             }
-            out.append(seqs[i].getName());
+            out.append(sequences[i].getName());
             out.append("\t-1\t");
-            out.append(next[j].begin);
-            out.append("\t");
-            out.append(next[j].end);
-            out.append("\t");
-            out.append(next[j].type);
-            if (!Float.isNaN(next[j].score))
+            out.append(sequenceFeature.begin);
+            out.append(TAB);
+            out.append(sequenceFeature.end);
+            out.append(TAB);
+            out.append(sequenceFeature.type);
+            if (!Float.isNaN(sequenceFeature.score))
             {
-              out.append("\t");
-              out.append(next[j].score);
+              out.append(TAB);
+              out.append(sequenceFeature.score);
             }
             out.append(newline);
           }
@@ -1402,7 +666,7 @@ public class FeaturesFile extends AlignFile
 
       if (group != null)
       {
-        out.append("ENDGROUP\t");
+        out.append("ENDGROUP").append(TAB);
         out.append(group);
         out.append(newline);
         groupIndex++;
@@ -1423,110 +687,493 @@ public class FeaturesFile extends AlignFile
   }
 
   /**
-   * generate a gff file for sequence features includes non-pos features by
-   * default.
+   * Parse method that is called when a GFF file is dragged to the desktop
+   */
+  @Override
+  public void parse()
+  {
+    AlignViewportI av = getViewport();
+    if (av != null)
+    {
+      if (av.getAlignment() != null)
+      {
+        dataset = av.getAlignment().getDataset();
+      }
+      if (dataset == null)
+      {
+        // working in the applet context ?
+        dataset = av.getAlignment();
+      }
+    }
+    else
+    {
+      dataset = new Alignment(new SequenceI[] {});
+    }
+
+    boolean parseResult = parse(dataset, null, false, true);
+    if (!parseResult)
+    {
+      // pass error up somehow
+    }
+    if (av != null)
+    {
+      // update viewport with the dataset data ?
+    }
+    else
+    {
+      setSeqs(dataset.getSequencesArray());
+    }
+  }
+
+  /**
+   * Implementation of unused abstract method
+   * 
+   * @return error message
+   */
+  @Override
+  public String print()
+  {
+    return "Use printGffFormat() or printJalviewFormat()";
+  }
+
+  /**
+   * Returns features output in GFF2 format, including hidden and non-positional
+   * features
    * 
-   * @param seqs
+   * @param sequences
+   *          the sequences whose features are to be output
    * @param visible
+   *          a map whose keys are the type names of visible features
    * @return
    */
-  public String printGFFFormat(SequenceI[] seqs, Map<String, Object> visible)
+  public String printGffFormat(SequenceI[] sequences,
+          Map<String, FeatureColourI> visible)
   {
-    return printGFFFormat(seqs, visible, true, true);
+    return printGffFormat(sequences, visible, true, true);
   }
 
-  public String printGFFFormat(SequenceI[] seqs,
-          Map<String, Object> visible, boolean visOnly, boolean nonpos)
+  /**
+   * Returns features output in GFF2 format
+   * 
+   * @param sequences
+   *          the sequences whose features are to be output
+   * @param visible
+   *          a map whose keys are the type names of visible features
+   * @param outputVisibleOnly
+   * @param includeNonPositionalFeatures
+   * @return
+   */
+  public String printGffFormat(SequenceI[] sequences,
+          Map<String, FeatureColourI> visible, boolean outputVisibleOnly,
+          boolean includeNonPositionalFeatures)
   {
-    StringBuffer out = new StringBuffer();
-    SequenceFeature[] next;
+    StringBuilder out = new StringBuilder(256);
+    int version = gffVersion == 0 ? 2 : gffVersion;
+    out.append(String.format("%s %d\n", GFF_VERSION, version));
     String source;
     boolean isnonpos;
-    for (int i = 0; i < seqs.length; i++)
+    for (SequenceI seq : sequences)
     {
-      if (seqs[i].getSequenceFeatures() != null)
+      SequenceFeature[] features = seq.getSequenceFeatures();
+      if (features != null)
       {
-        next = seqs[i].getSequenceFeatures();
-        for (int j = 0; j < next.length; j++)
+        for (SequenceFeature sf : features)
         {
-          isnonpos = next[j].begin == 0 && next[j].end == 0;
-          if ((!nonpos && isnonpos)
-                  || (!isnonpos && visOnly && !visible
-                          .containsKey(next[j].type)))
+          isnonpos = sf.begin == 0 && sf.end == 0;
+          if (!includeNonPositionalFeatures && isnonpos)
+          {
+            /*
+             * ignore non-positional features if not wanted
+             */
+            continue;
+          }
+          // TODO why the test !isnonpos here?
+          // what about not visible non-positional features?
+          if (!isnonpos && outputVisibleOnly
+                  && !visible.containsKey(sf.type))
           {
+            /*
+             * ignore not visible features if not wanted
+             */
             continue;
           }
 
-          source = next[j].featureGroup;
+          source = sf.featureGroup;
           if (source == null)
           {
-            source = next[j].getDescription();
+            source = sf.getDescription();
           }
 
-          out.append(seqs[i].getName());
-          out.append("\t");
+          out.append(seq.getName());
+          out.append(TAB);
           out.append(source);
-          out.append("\t");
-          out.append(next[j].type);
-          out.append("\t");
-          out.append(next[j].begin);
-          out.append("\t");
-          out.append(next[j].end);
-          out.append("\t");
-          out.append(next[j].score);
-          out.append("\t");
-
-          if (next[j].getValue("STRAND") != null)
-          {
-            out.append(next[j].getValue("STRAND"));
-            out.append("\t");
-          }
-          else
+          out.append(TAB);
+          out.append(sf.type);
+          out.append(TAB);
+          out.append(sf.begin);
+          out.append(TAB);
+          out.append(sf.end);
+          out.append(TAB);
+          out.append(sf.score);
+          out.append(TAB);
+
+          int strand = sf.getStrand();
+          out.append(strand == 1 ? "+" : (strand == -1 ? "-" : "."));
+          out.append(TAB);
+
+          String phase = sf.getPhase();
+          out.append(phase == null ? "." : phase);
+
+          // miscellaneous key-values (GFF column 9)
+          String attributes = sf.getAttributes();
+          if (attributes != null)
           {
-            out.append(".\t");
+            out.append(TAB).append(attributes);
           }
 
-          if (next[j].getValue("FRAME") != null)
-          {
-            out.append(next[j].getValue("FRAME"));
-          }
-          else
-          {
-            out.append(".");
-          }
-          // TODO: verify/check GFF - should there be a /t here before attribute
-          // output ?
+          out.append(newline);
+        }
+      }
+    }
 
-          if (next[j].getValue("ATTRIBUTES") != null)
-          {
-            out.append(next[j].getValue("ATTRIBUTES"));
-          }
+    return out.toString();
+  }
 
-          out.append(newline);
+  /**
+   * Returns a mapping given list of one or more Align descriptors (exonerate
+   * format)
+   * 
+   * @param alignedRegions
+   *          a list of "Align fromStart toStart fromCount"
+   * @param mapIsFromCdna
+   *          if true, 'from' is dna, else 'from' is protein
+   * @param strand
+   *          either 1 (forward) or -1 (reverse)
+   * @return
+   * @throws IOException
+   */
+  protected MapList constructCodonMappingFromAlign(
+          List<String> alignedRegions, boolean mapIsFromCdna, int strand)
+          throws IOException
+  {
+    if (strand == 0)
+    {
+      throw new IOException(
+              "Invalid strand for a codon mapping (cannot be 0)");
+    }
+    int regions = alignedRegions.size();
+    // arrays to hold [start, end] for each aligned region
+    int[] fromRanges = new int[regions * 2]; // from dna
+    int[] toRanges = new int[regions * 2]; // to protein
+    int fromRangesIndex = 0;
+    int toRangesIndex = 0;
+
+    for (String range : alignedRegions)
+    {
+      /* 
+       * Align mapFromStart mapToStart mapFromCount
+       * e.g. if mapIsFromCdna
+       *     Align 11270 143 120
+       * means:
+       *     120 bases from pos 11270 align to pos 143 in peptide
+       * if !mapIsFromCdna this would instead be
+       *     Align 143 11270 40 
+       */
+      String[] tokens = range.split(" ");
+      if (tokens.length != 3)
+      {
+        throw new IOException("Wrong number of fields for Align");
+      }
+      int fromStart = 0;
+      int toStart = 0;
+      int fromCount = 0;
+      try
+      {
+        fromStart = Integer.parseInt(tokens[0]);
+        toStart = Integer.parseInt(tokens[1]);
+        fromCount = Integer.parseInt(tokens[2]);
+      } catch (NumberFormatException nfe)
+      {
+        throw new IOException("Invalid number in Align field: "
+                + nfe.getMessage());
+      }
+
+      /*
+       * Jalview always models from dna to protein, so adjust values if the
+       * GFF mapping is from protein to dna
+       */
+      if (!mapIsFromCdna)
+      {
+        fromCount *= 3;
+        int temp = fromStart;
+        fromStart = toStart;
+        toStart = temp;
+      }
+      fromRanges[fromRangesIndex++] = fromStart;
+      fromRanges[fromRangesIndex++] = fromStart + strand * (fromCount - 1);
+
+      /*
+       * If a codon has an intron gap, there will be contiguous 'toRanges';
+       * this is handled for us by the MapList constructor. 
+       * (It is not clear that exonerate ever generates this case)  
+       */
+      toRanges[toRangesIndex++] = toStart;
+      toRanges[toRangesIndex++] = toStart + (fromCount - 1) / 3;
+    }
+
+    return new MapList(fromRanges, toRanges, 3, 1);
+  }
+
+  /**
+   * Parse a GFF format feature. This may include creating a 'dummy' sequence to
+   * hold the feature, or for its mapped sequence, or both, to be resolved
+   * either later in the GFF file (##FASTA section), or when the user loads
+   * additional sequences.
+   * 
+   * @param gffColumns
+   * @param alignment
+   * @param relaxedIdMatching
+   * @param newseqs
+   * @return
+   */
+  protected SequenceI parseGff(String[] gffColumns, AlignmentI alignment,
+          boolean relaxedIdMatching, List<SequenceI> newseqs)
+  {
+    /*
+     * GFF: seqid source type start end score strand phase [attributes]
+     */
+    if (gffColumns.length < 5)
+    {
+      System.err.println("Ignoring GFF feature line with too few columns ("
+              + gffColumns.length + ")");
+      return null;
+    }
 
+    /*
+     * locate referenced sequence in alignment _or_ 
+     * as a forward or external reference (SequenceDummy)
+     */
+    String seqId = gffColumns[0];
+    SequenceI seq = findSequence(seqId, alignment, newseqs,
+            relaxedIdMatching);
+
+    SequenceFeature sf = null;
+    GffHelperI helper = GffHelperFactory.getHelper(gffColumns);
+    if (helper != null)
+    {
+      try
+      {
+        sf = helper.processGff(seq, gffColumns, alignment, newseqs,
+                relaxedIdMatching);
+        if (sf != null)
+        {
+          seq.addSequenceFeature(sf);
+          while ((seq = alignment.findName(seq, seqId, true)) != null)
+          {
+            seq.addSequenceFeature(new SequenceFeature(sf));
+          }
         }
+      } catch (IOException e)
+      {
+        System.err.println("GFF parsing failed with: " + e.getMessage());
+        return null;
       }
     }
 
-    return out.toString();
+    return seq;
   }
 
   /**
-   * this is only for the benefit of object polymorphism - method does nothing.
+   * Process the 'column 9' data of the GFF file. This is less formally defined,
+   * and its interpretation will vary depending on the tool that has generated
+   * it.
+   * 
+   * @param attributes
+   * @param sf
    */
-  public void parse()
+  protected void processGffColumnNine(String attributes, SequenceFeature sf)
   {
-    // IGNORED
+    sf.setAttributes(attributes);
+
+    /*
+     * Parse attributes in column 9 and add them to the sequence feature's 
+     * 'otherData' table; use Note as a best proxy for description
+     */
+    char nameValueSeparator = gffVersion == 3 ? '=' : ' ';
+    // TODO check we don't break GFF2 values which include commas here
+    Map<String, List<String>> nameValues = GffHelperBase
+            .parseNameValuePairs(attributes, ";", nameValueSeparator, ",");
+    for (Entry<String, List<String>> attr : nameValues.entrySet())
+    {
+      String values = StringUtils.listToDelimitedString(attr.getValue(),
+              "; ");
+      sf.setValue(attr.getKey(), values);
+      if (NOTE.equals(attr.getKey()))
+      {
+        sf.setDescription(values);
+      }
+    }
   }
 
   /**
-   * this is only for the benefit of object polymorphism - method does nothing.
+   * After encountering ##fasta in a GFF3 file, process the remainder of the
+   * file as FAST sequence data. Any placeholder sequences created during
+   * feature parsing are updated with the actual sequences.
    * 
-   * @return error message
+   * @param align
+   * @param newseqs
+   * @throws IOException
    */
-  public String print()
+  protected void processAsFasta(AlignmentI align, List<SequenceI> newseqs)
+          throws IOException
   {
-    return "USE printGFFFormat() or printJalviewFormat()";
+    try
+    {
+      mark();
+    } catch (IOException q)
+    {
+    }
+    FastaFile parser = new FastaFile(this);
+    List<SequenceI> includedseqs = parser.getSeqs();
+
+    SequenceIdMatcher smatcher = new SequenceIdMatcher(newseqs);
+
+    /*
+     * iterate over includedseqs, and replacing matching ones with newseqs
+     * sequences. Generic iterator not used here because we modify
+     * includedseqs as we go
+     */
+    for (int p = 0, pSize = includedseqs.size(); p < pSize; p++)
+    {
+      // search for any dummy seqs that this sequence can be used to update
+      SequenceI includedSeq = includedseqs.get(p);
+      SequenceI dummyseq = smatcher.findIdMatch(includedSeq);
+      if (dummyseq != null && dummyseq instanceof SequenceDummy)
+      {
+        // probably have the pattern wrong
+        // idea is that a flyweight proxy for a sequence ID can be created for
+        // 1. stable reference creation
+        // 2. addition of annotation
+        // 3. future replacement by a real sequence
+        // current pattern is to create SequenceDummy objects - a convenience
+        // constructor for a Sequence.
+        // problem is that when promoted to a real sequence, all references
+        // need to be updated somehow. We avoid that by keeping the same object.
+        ((SequenceDummy) dummyseq).become(includedSeq);
+        dummyseq.createDatasetSequence();
+
+        /*
+         * Update mappings so they are now to the dataset sequence
+         */
+        for (AlignedCodonFrame mapping : align.getCodonFrames())
+        {
+          mapping.updateToDataset(dummyseq);
+        }
+
+        /*
+         * replace parsed sequence with the realised forward reference
+         */
+        includedseqs.set(p, dummyseq);
+
+        /*
+         * and remove from the newseqs list
+         */
+        newseqs.remove(dummyseq);
+      }
+    }
+
+    /*
+     * finally add sequences to the dataset
+     */
+    for (SequenceI seq : includedseqs)
+    {
+      // experimental: mapping-based 'alignment' to query sequence
+      AlignmentUtils.alignSequenceAs(seq, align,
+              String.valueOf(align.getGapCharacter()), false, true);
+
+      // rename sequences if GFF handler requested this
+      // TODO a more elegant way e.g. gffHelper.postProcess(newseqs) ?
+      SequenceFeature[] sfs = seq.getSequenceFeatures();
+      if (sfs != null)
+      {
+        String newName = (String) sfs[0].getValue(GffHelperI.RENAME_TOKEN);
+        if (newName != null)
+        {
+          seq.setName(newName);
+        }
+      }
+      align.addSequence(seq);
+    }
   }
 
+  /**
+   * Process a ## directive
+   * 
+   * @param line
+   * @param gffProps
+   * @param align
+   * @param newseqs
+   * @throws IOException
+   */
+  protected void processGffPragma(String line,
+          Map<String, String> gffProps, AlignmentI align,
+          List<SequenceI> newseqs) throws IOException
+  {
+    line = line.trim();
+    if ("###".equals(line))
+    {
+      // close off any open 'forward references'
+      return;
+    }
+
+    String[] tokens = line.substring(2).split(" ");
+    String pragma = tokens[0];
+    String value = tokens.length == 1 ? null : tokens[1];
+
+    if ("gff-version".equalsIgnoreCase(pragma))
+    {
+      if (value != null)
+      {
+        try
+        {
+          // value may be e.g. "3.1.2"
+          gffVersion = Integer.parseInt(value.split("\\.")[0]);
+        } catch (NumberFormatException e)
+        {
+          // ignore
+        }
+      }
+    }
+    else if ("sequence-region".equalsIgnoreCase(pragma))
+    {
+      // could capture <seqid start end> if wanted here
+    }
+    else if ("feature-ontology".equalsIgnoreCase(pragma))
+    {
+      // should resolve against the specified feature ontology URI
+    }
+    else if ("attribute-ontology".equalsIgnoreCase(pragma))
+    {
+      // URI of attribute ontology - not currently used in GFF3
+    }
+    else if ("source-ontology".equalsIgnoreCase(pragma))
+    {
+      // URI of source ontology - not currently used in GFF3
+    }
+    else if ("species-build".equalsIgnoreCase(pragma))
+    {
+      // save URI of specific NCBI taxon version of annotations
+      gffProps.put("species-build", value);
+    }
+    else if ("fasta".equalsIgnoreCase(pragma))
+    {
+      // process the rest of the file as a fasta file and replace any dummy
+      // sequence IDs
+      processAsFasta(align, newseqs);
+    }
+    else
+    {
+      System.err.println("Ignoring unknown pragma: " + line);
+    }
+  }
 }
diff --git a/src/jalview/io/FileLoader.java b/src/jalview/io/FileLoader.java
index 9fbf9ee..f6fd21d 100644
--- a/src/jalview/io/FileLoader.java
+++ b/src/jalview/io/FileLoader.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,7 +21,9 @@
 package jalview.io;
 
 import jalview.api.ComplexAlignFile;
+import jalview.api.FeatureSettingsModelI;
 import jalview.api.FeaturesDisplayedI;
+import jalview.api.FeaturesSourceI;
 import jalview.bin.Jalview;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
@@ -31,6 +33,7 @@ import jalview.gui.AlignFrame;
 import jalview.gui.AlignViewport;
 import jalview.gui.Desktop;
 import jalview.gui.Jalview2XML;
+import jalview.json.binding.biojson.v1.ColourSchemeMapper;
 import jalview.schemes.ColourSchemeI;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
@@ -99,6 +102,7 @@ public class FileLoader implements Runnable
 
     SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         loader.start();
@@ -233,6 +237,7 @@ public class FileLoader implements Runnable
     }
   }
 
+  @Override
   public void run()
   {
     String title = protocol.equals(AppletFormatAdapter.PASTE) ? "Copied From Clipboard"
@@ -249,14 +254,14 @@ public class FileLoader implements Runnable
         // just in case the caller didn't identify the file for us
         if (source != null)
         {
-          format = new IdentifyFile().Identify(source, false); // identify
+          format = new IdentifyFile().identify(source, false); // identify
           // stream and
           // rewind rather
           // than close
         }
         else
         {
-          format = new IdentifyFile().Identify(file, protocol);
+          format = new IdentifyFile().identify(file, protocol);
         }
 
       }
@@ -358,8 +363,14 @@ public class FileLoader implements Runnable
             }
           }
 
+          FeatureSettingsModelI proxyColourScheme = source
+                  .getFeatureColourScheme();
           if (viewport != null)
           {
+            if (proxyColourScheme != null)
+            {
+              viewport.applyFeaturesStyle(proxyColourScheme);
+            }
             // append to existing alignment
             viewport.addAlignment(al, title);
           }
@@ -373,31 +384,41 @@ public class FileLoader implements Runnable
                       .getColumnSelection();
               SequenceI[] hiddenSeqs = ((ComplexAlignFile) source)
                       .getHiddenSequences();
-              boolean showSeqFeatures = ((ComplexAlignFile) source)
-                      .isShowSeqFeatures();
-              ColourSchemeI cs = ((ComplexAlignFile) source)
-                      .getColourScheme();
+              String colourSchemeName = ((ComplexAlignFile) source)
+                      .getGlobalColourScheme();
               FeaturesDisplayedI fd = ((ComplexAlignFile) source)
                       .getDisplayedFeatures();
               alignFrame = new AlignFrame(al, hiddenSeqs, colSel,
                       AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
-
-              alignFrame.getViewport().setShowSequenceFeatures(
-                      showSeqFeatures);
               alignFrame.getViewport().setFeaturesDisplayed(fd);
-              alignFrame.changeColour(cs);
+              alignFrame.getViewport().setShowSequenceFeatures(
+                      ((ComplexAlignFile) source).isShowSeqFeatures());
+              ColourSchemeI cs = ColourSchemeMapper.getJalviewColourScheme(
+                      colourSchemeName, al);
+              if (cs != null)
+              {
+                alignFrame.changeColour(cs);
+              }
             }
             else
             {
               alignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
                       AlignFrame.DEFAULT_HEIGHT);
+              if (source instanceof FeaturesSourceI)
+              {
+                alignFrame.getViewport().setShowSequenceFeatures(true);
+              }
             }
             // add metadata and update ui
             if (!protocol.equals(AppletFormatAdapter.PASTE))
             {
               alignFrame.setFileName(file, format);
             }
-
+            if (proxyColourScheme != null)
+            {
+              alignFrame.getViewport()
+                      .applyFeaturesStyle(proxyColourScheme);
+            }
             alignFrame.statusBar.setText(MessageManager.formatMessage(
                     "label.successfully_loaded_file",
                     new String[] { title }));
@@ -440,6 +461,7 @@ public class FileLoader implements Runnable
           {
             javax.swing.SwingUtilities.invokeLater(new Runnable()
             {
+              @Override
               public void run()
               {
                 JOptionPane.showInternalMessageDialog(Desktop.desktop,
@@ -466,6 +488,7 @@ public class FileLoader implements Runnable
       {
         javax.swing.SwingUtilities.invokeLater(new Runnable()
         {
+          @Override
           public void run()
           {
             javax.swing.JOptionPane.showInternalMessageDialog(
@@ -487,6 +510,7 @@ public class FileLoader implements Runnable
       {
         javax.swing.SwingUtilities.invokeLater(new Runnable()
         {
+          @Override
           public void run()
           {
             javax.swing.JOptionPane.showInternalMessageDialog(
@@ -545,6 +569,7 @@ public class FileLoader implements Runnable
    * 
    * @see java.lang.Object#finalize()
    */
+  @Override
   protected void finalize() throws Throwable
   {
     source = null;
diff --git a/src/jalview/io/FileParse.java b/src/jalview/io/FileParse.java
index 4526eba..52619a7 100644
--- a/src/jalview/io/FileParse.java
+++ b/src/jalview/io/FileParse.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -23,6 +23,7 @@ package jalview.io;
 import jalview.api.AlignExportSettingI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureSettingsModelI;
 import jalview.util.MessageManager;
 
 import java.io.BufferedReader;
@@ -274,6 +275,30 @@ public class FileParse
   }
 
   /**
+   * not for general use, creates a fileParse object for an existing reader with
+   * configurable values for the origin and the type of the source
+   */
+  public FileParse(BufferedReader source, String originString,
+          String typeString)
+  {
+    type = typeString;
+    error = false;
+    inFile = null;
+    dataName = originString;
+    dataIn = source;
+    try
+    {
+      if (dataIn.markSupported())
+      {
+        dataIn.mark(READAHEAD_LIMIT);
+      }
+    } catch (IOException q)
+    {
+
+    }
+  }
+
+  /**
    * Create a datasource for input to Jalview. See AppletFormatAdapter for the
    * types of sources that are handled.
    * 
@@ -457,11 +482,19 @@ public class FileParse
   }
 
   /**
-   * rewinds the datasource the beginning.
+   * Rewinds the datasource to the marked point if possible
+   * 
+   * @param bytesRead
    * 
    */
-  public void reset() throws IOException
+  public void reset(int bytesRead) throws IOException
   {
+    if (bytesRead >= READAHEAD_LIMIT)
+    {
+      System.err.println(String.format(
+              "File reset error: read %d bytes but reset limit is %d",
+              bytesRead, READAHEAD_LIMIT));
+    }
     if (dataIn != null && !error)
     {
       dataIn.reset();
@@ -581,4 +614,15 @@ public class FileParse
     }
     // could also set export/import settings
   }
+
+  /**
+   * Returns the preferred feature colour configuration if there is one, else
+   * null
+   * 
+   * @return
+   */
+  public FeatureSettingsModelI getFeatureColourScheme()
+  {
+    return null;
+  }
 }
diff --git a/src/jalview/io/FormatAdapter.java b/src/jalview/io/FormatAdapter.java
index 1b39d0a..57d4112 100644
--- a/src/jalview/io/FormatAdapter.java
+++ b/src/jalview/io/FormatAdapter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -107,7 +107,6 @@ public class FormatAdapter extends AppletFormatAdapter
       {
         startRes = seqs[i].getStart();
         endRes = seqs[i].getEnd();
-
         if (startEnd != null)
         {
           startIndex = startEnd[0];
@@ -127,9 +126,7 @@ public class FormatAdapter extends AppletFormatAdapter
           }
 
           startRes = seqs[i].findPosition(startIndex);
-          startRes = seqs[i].getStart() > 1 ? startRes - seqs[i].getStart()
-                  : startRes;
-          endRes = seqs[i].findPosition(endIndex) - seqs[i].getStart();
+          endRes = seqs[i].findPosition(endIndex);
         }
 
         tmp[i] = new Sequence(seqs[i].getName(), omitHiddenColumns[i],
@@ -308,20 +305,6 @@ public class FormatAdapter extends AppletFormatAdapter
     return this.formatSequences(format, alignment, suffix);
   }
 
-  public AlignmentI readFile(String inFile, String type, String format)
-          throws java.io.IOException
-  {
-    AlignmentI al = super.readFile(inFile, type, format);
-    return al;
-  }
-
-  public AlignmentI readFromFile(FileParse source, String format)
-          throws java.io.IOException
-  {
-    AlignmentI al = super.readFromFile(source, format);
-    return al;
-  }
-
   /**
    * validate format is valid for IO in Application. This is basically the
    * AppletFormatAdapter.isValidFormat call with additional checks for
diff --git a/src/jalview/io/Gff3File.java b/src/jalview/io/Gff3File.java
deleted file mode 100644
index 4d8ea94..0000000
--- a/src/jalview/io/Gff3File.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.io;
-
-import jalview.api.AlignViewportI;
-import jalview.datamodel.AlignedCodonFrame;
-import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SequenceI;
-
-import java.io.IOException;
-import java.util.List;
-
-/**
- * A GFF3 File parsing wrapper for the tangled mess that is FeaturesFile.
- * 
- * This class implements the methods relied on by FileLoader/FormatAdapter in
- * order to allow them to load alignments directly from GFF2 and GFF3 files that
- * contain sequence data and alignment information.
- * 
- * Major issues:
- * 
- * 1. GFF3 files commonly include mappings between DNA, RNA and Protein - so
- * this class needs a dataset AlignmentI context to create alignment codon
- * mappings.
- * 
- * 2. A single GFF3 file can generate many distinct alignments. Support will be
- * needed to allow several AlignmentI instances to be generated from a single
- * file.
- * 
- * 
- * @author jprocter
- *
- */
-public class Gff3File extends FeaturesFile
-{
-
-  /**
-   * 
-   */
-  public Gff3File()
-  {
-    super();
-  }
-
-  /**
-   * @param source
-   * @throws IOException
-   */
-  public Gff3File(FileParse source) throws IOException
-  {
-    super(source);
-  }
-
-  /**
-   * @param inFile
-   * @param type
-   * @throws IOException
-   */
-  public Gff3File(String inFile, String type) throws IOException
-  {
-    super(inFile, type);
-  }
-
-  /**
-   * @param parseImmediately
-   * @param source
-   * @throws IOException
-   */
-  public Gff3File(boolean parseImmediately, FileParse source)
-          throws IOException
-  {
-    super(parseImmediately, source);
-  }
-
-  /**
-   * @param parseImmediately
-   * @param inFile
-   * @param type
-   * @throws IOException
-   */
-  public Gff3File(boolean parseImmediately, String inFile, String type)
-          throws IOException
-  {
-    super(parseImmediately, inFile, type);
-  }
-
-  /*
-   * (non-Javadoc)
-   * 
-   * @see jalview.io.FeaturesFile#print()
-   */
-  @Override
-  public String print()
-  {
-    // TODO GFF3 writer with sensible defaults for writing alignment data
-
-    // return super.printGFFFormat(seqs, visible);
-    return ("Not yet implemented.");
-  }
-
-  AlignmentI dataset;
-
-  List<AlignmentI> alignments;
-
-  @Override
-  public void parse()
-  {
-    AlignViewportI av = getViewport();
-    if (av != null)
-    {
-      if (av.getAlignment() != null)
-      {
-        dataset = av.getAlignment().getDataset();
-      }
-      if (dataset == null)
-      {
-        // working in the applet context ?
-        dataset = av.getAlignment();
-      }
-    }
-    else
-    {
-      dataset = new Alignment(new SequenceI[] {});
-    }
-
-    boolean parseResult = parse(dataset, null, null, false, true);
-    if (!parseResult)
-    {
-      // pass error up somehow
-    }
-    if (av != null)
-    {
-      // update viewport with the dataset data ?
-    }
-    else
-    {
-      setSeqs(dataset.getSequencesArray());
-    }
-
-  }
-
-  @Override
-  public void addProperties(AlignmentI al)
-  {
-    super.addProperties(al);
-    if (dataset.getCodonFrames() != null)
-    {
-      AlignmentI ds = (al.getDataset() == null) ? al : al.getDataset();
-      for (AlignedCodonFrame codons : dataset.getCodonFrames())
-      {
-        ds.addCodonFrame(codons);
-      }
-    }
-  }
-}
diff --git a/src/jalview/io/HTMLOutput.java b/src/jalview/io/HTMLOutput.java
index 95864c3..81fa8a5 100644
--- a/src/jalview/io/HTMLOutput.java
+++ b/src/jalview/io/HTMLOutput.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,283 +20,149 @@
  */
 package jalview.io;
 
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignViewport;
+import jalview.api.AlignExportSettingI;
+import jalview.datamodel.AlignmentExportData;
+import jalview.exceptions.NoFileSelectedException;
 import jalview.gui.AlignmentPanel;
-import jalview.gui.FeatureRenderer;
-import jalview.gui.SequenceRenderer;
+import jalview.gui.IProgressIndicator;
 import jalview.util.MessageManager;
 
-import java.awt.Color;
-import java.awt.Font;
-import java.io.PrintWriter;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Objects;
 
-public class HTMLOutput
+
+public abstract class HTMLOutput implements Runnable
 {
-  AlignViewport av;
+  protected AlignmentPanel ap;
 
-  SequenceRenderer sr;
+  protected long pSessionId;
 
-  jalview.renderer.seqfeatures.FeatureRenderer fr;
+  protected IProgressIndicator pIndicator;
 
-  Color color;
+  protected File generatedFile;
 
-  public HTMLOutput(AlignmentPanel ap, SequenceRenderer sr,
-          FeatureRenderer fr1)
+  public HTMLOutput(AlignmentPanel ap)
   {
-    this.av = ap.av;
-    this.sr = sr;
-
-    fr = new FeatureRenderer(ap);
-    fr.transferSettings(fr1);
-
-    JalviewFileChooser chooser = new JalviewFileChooser(
-            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
-            new String[] { "html" }, new String[] { "HTML files" },
-            "HTML files");
-
-    chooser.setFileView(new JalviewFileView());
-    chooser.setDialogTitle(MessageManager.getString("label.save_as_html"));
-    chooser.setToolTipText(MessageManager.getString("action.save"));
-
-    int value = chooser.showSaveDialog(null);
-
-    if (value == JalviewFileChooser.APPROVE_OPTION)
+    if (ap != null)
     {
-      String choice = chooser.getSelectedFile().getPath();
-      jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
-              .getSelectedFile().getParent());
-
-      try
-      {
-        PrintWriter out = new java.io.PrintWriter(new java.io.FileWriter(
-                choice));
-        out.println("<HTML>");
-        out.println("<style type=\"text/css\">");
-        out.println("<!--");
-        out.print("td {font-family: \"" + av.getFont().getFamily()
-                + "\", \"" + av.getFont().getName() + "\", mono; "
-                + "font-size: " + av.getFont().getSize() + "px; ");
-
-        if (av.getFont().getStyle() == Font.BOLD)
-        {
-          out.print("font-weight: BOLD; ");
-        }
-
-        if (av.getFont().getStyle() == Font.ITALIC)
-        {
-          out.print("font-style: italic; ");
-        }
-
-        out.println("text-align: center; }");
-
-        out.println("-->");
-        out.println("</style>");
-        out.println("<BODY>");
-
-        if (av.getWrapAlignment())
-        {
-          drawWrappedAlignment(out);
-        }
-        else
-        {
-          drawUnwrappedAlignment(out);
-        }
-
-        out.println("\n</body>\n</html>");
-        out.close();
-        jalview.util.BrowserLauncher.openURL("file:///" + choice);
-      } catch (Exception ex)
-      {
-        ex.printStackTrace();
-      }
+      this.ap = ap;
+      this.pIndicator = ap.alignFrame;
     }
   }
 
-  void drawUnwrappedAlignment(PrintWriter out)
+  public String getBioJSONData()
   {
-    out.println("<table border=\"1\"><tr><td>\n");
-    out.println("<table border=\"0\"  cellpadding=\"0\" cellspacing=\"0\">\n");
-
-    // ////////////
-    SequenceI seq;
-    AlignmentI alignment = av.getAlignment();
-
-    // draws the top row, the measure rule
-    out.println("<tr><td colspan=\"6\"></td>");
-
-    int i = 0;
+    return getBioJSONData(null);
+  }
 
-    for (i = 10; i < (alignment.getWidth() - 10); i += 10)
+  public String getBioJSONData(AlignExportSettingI exportSettings)
+  {
+    if (!isEmbedData())
     {
-      out.println("<td colspan=\"9\">" + i + "<br>|</td><td></td>");
+      return null;
     }
-
-    out.println("<td colspan=\"3\"></td><td colspan=\"3\">" + i
-            + "<br>|</td>");
-    out.println("</tr>");
-
-    for (i = 0; i < alignment.getHeight(); i++)
+    if (exportSettings == null)
     {
-      seq = alignment.getSequenceAt(i);
-
-      String id = seq.getDisplayId(av.getShowJVSuffix());
-
-      out.println("<tr><td nowrap>" + id + "  </td>");
-
-      for (int res = 0; res < seq.getLength(); res++)
+      exportSettings = new AlignExportSettingI()
       {
-        if (!jalview.util.Comparison.isGap(seq.getCharAt(res)))
+        @Override
+        public boolean isExportHiddenSequences()
         {
-          color = sr.getResidueBoxColour(seq, res);
+          return true;
+        }
 
-          color = fr.findFeatureColour(color, seq, res);
+        @Override
+        public boolean isExportHiddenColumns()
+        {
+          return true;
         }
-        else
+
+        @Override
+        public boolean isExportAnnotations()
         {
-          color = Color.white;
+          return true;
         }
 
-        if (color.getRGB() < -1)
+        @Override
+        public boolean isExportFeatures()
         {
-          out.println("<td bgcolor=\"#"
-                  + jalview.util.Format.getHexString(color) + "\">"
-                  + seq.getCharAt(res) + "</td>");
+          return true;
         }
-        else
+
+        @Override
+        public boolean isExportGroups()
         {
-          out.println("<td>" + seq.getCharAt(res) + "</td>");
+          return true;
         }
-      }
 
-      out.println("</tr>");
+        @Override
+        public boolean isCancelled()
+        {
+          return false;
+        }
+      };
     }
-
-    // ////////////
-    out.println("</table>");
-    out.println("</td></tr></table>");
+    AlignmentExportData exportData = jalview.gui.AlignFrame
+            .getAlignmentForExport(JSONFile.FILE_DESC,
+                    ap.getAlignViewport(), exportSettings);
+    String bioJSON = new FormatAdapter(ap, exportData.getSettings())
+            .formatSequences(JSONFile.FILE_DESC, exportData.getAlignment(),
+                    exportData.getOmitHidden(), exportData
+                            .getStartEndPostions(), ap.getAlignViewport()
+                            .getColumnSelection());
+    return bioJSON;
   }
 
-  void drawWrappedAlignment(PrintWriter out)
+  /**
+   * Read a template file content as string
+   * 
+   * @param file
+   *          - the file to be read
+   * @return File content as String
+   * @throws IOException
+   */
+  public static String readFileAsString(File file) throws IOException
   {
-    // //////////////////////////////////
-    // / How many sequences and residues can we fit on a printable page?
-    AlignmentI al = av.getAlignment();
-    SequenceI seq;
-    String r;
-    String g;
-    String b;
-
-    out.println("<table border=\"1\"><tr><td>\n");
-    out.println("<table border=\"0\"  cellpadding=\"0\" cellspacing=\"0\">\n");
-
-    for (int startRes = 0; startRes < al.getWidth(); startRes += av
-            .getWrappedWidth())
+    InputStreamReader isReader = null;
+    BufferedReader buffReader = null;
+    StringBuilder sb = new StringBuilder();
+    Objects.requireNonNull(file, "File must not be null!");
+    @SuppressWarnings("deprecation")
+    URL url = file.toURL();
+    if (url != null)
     {
-      int endRes = startRes + av.getWrappedWidth();
-
-      if (endRes > al.getWidth())
-      {
-        endRes = al.getWidth();
-      }
-
-      if (av.getScaleAboveWrapped())
+      try
       {
-        out.println("<tr>");
-
-        if (av.getScaleLeftWrapped())
-        {
-          out.println("<td colspan=\"7\"> </td>");
-        }
-        else
+        isReader = new InputStreamReader(url.openStream());
+        buffReader = new BufferedReader(isReader);
+        String line;
+        String lineSeparator = System.getProperty("line.separator");
+        while ((line = buffReader.readLine()) != null)
         {
-          out.println("<td colspan=\"6\"> </td>");
+          sb.append(line).append(lineSeparator);
         }
-
-        for (int i = startRes + 10; i < endRes; i += 10)
-        {
-          out.println("<td colspan=\"9\">" + i + "<br>|</td><td></td>");
-        }
-
-        out.println("</tr>");
-      }
-
-      int startPos, endPos;
-      for (int s = 0; s < al.getHeight(); s++)
+  
+      } catch (Exception ex)
       {
-        out.println("<tr>");
-        seq = al.getSequenceAt(s);
-
-        startPos = seq.findPosition(startRes);
-        endPos = seq.findPosition(endRes) - 1;
-
-        String id = seq.getDisplayId(av.getShowJVSuffix());
-
-        out.println("<td nowrap>" + id + "  </td>");
-
-        if (av.getScaleLeftWrapped())
-        {
-          if (startPos > seq.getEnd() || endPos == 0)
-          {
-            out.println("<td nowrap> </td>");
-          }
-          else
-          {
-            out.println("<td nowrap>" + startPos + "  </td>");
-          }
-        }
-
-        for (int res = startRes; res < endRes; res++)
-        {
-          if (!jalview.util.Comparison.isGap(seq.getCharAt(res)))
-          {
-            color = sr.getResidueBoxColour(seq, res);
-
-            color = fr.findFeatureColour(color, seq, res);
-          }
-          else
-          {
-            color = Color.white;
-          }
-
-          if (color.getRGB() < -1)
-          {
-            out.println("<td bgcolor=\"#"
-                    + jalview.util.Format.getHexString(color) + "\">"
-                    + seq.getCharAt(res) + "</td>");
-          }
-          else
-          {
-            out.println("<td>" + seq.getCharAt(res) + "</td>");
-          }
-
-        }
-
-        if (av.getScaleRightWrapped()
-                && endRes < startRes + av.getWrappedWidth())
+        ex.printStackTrace();
+      } finally
+      {
+        if (isReader != null)
         {
-          out.println("<td colspan=\""
-                  + (startRes + av.getWrappedWidth() - endRes) + "\">"
-                  + "  </td>");
+          isReader.close();
         }
-
-        if (av.getScaleRightWrapped() && startPos < endPos)
+  
+        if (buffReader != null)
         {
-          out.println("<td nowrap> " + endPos + "  </td>");
+          buffReader.close();
         }
-
-        out.println("</tr>");
-      }
-
-      if (endRes < al.getWidth())
-      {
-        out.println("<tr><td height=\"5\"></td></tr>");
       }
     }
-
-    out.println("</table>");
-    out.println("</table>");
+    return sb.toString();
   }
 
   public static String getImageMapHTML()
@@ -386,4 +252,122 @@ public class HTMLOutput
                     + "initToolTips(); //--></script>\n");
 
   }
+
+  public String getOutputFile() throws NoFileSelectedException
+  {
+    String selectedFile = null;
+    if (pIndicator != null && !isHeadless())
+    {
+      pIndicator.setProgressBar(MessageManager.formatMessage(
+              "status.waiting_for_user_to_select_output_file", "HTML"),
+              pSessionId);
+    }
+
+    JalviewFileChooser jvFileChooser = new JalviewFileChooser(
+            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
+            new String[] { "html" }, new String[] { "HTML files" },
+            "HTML files");
+    jvFileChooser.setFileView(new JalviewFileView());
+
+    jvFileChooser.setDialogTitle(MessageManager
+            .getString("label.save_as_html"));
+    jvFileChooser.setToolTipText(MessageManager.getString("action.save"));
+
+    int fileChooserOpt = jvFileChooser.showSaveDialog(null);
+    if (fileChooserOpt == JalviewFileChooser.APPROVE_OPTION)
+    {
+      jalview.bin.Cache.setProperty("LAST_DIRECTORY", jvFileChooser
+              .getSelectedFile().getParent());
+      selectedFile = jvFileChooser.getSelectedFile().getPath();
+    }
+    else
+    {
+      throw new NoFileSelectedException("No file was selected.");
+    }
+    return selectedFile;
+  }
+
+  protected void setProgressMessage(String message)
+  {
+    if (pIndicator != null && !isHeadless())
+    {
+      pIndicator.setProgressBar(message, pSessionId);
+    }
+    else
+    {
+      System.out.println(message);
+    }
+  }
+
+  /**
+   * Answers true if HTML export is invoke in headless mode or false otherwise
+   * 
+   * @return
+   */
+  protected boolean isHeadless()
+  {
+    return System.getProperty("java.awt.headless") != null
+            && System.getProperty("java.awt.headless").equals("true");
+  }
+
+  /**
+   * This method provides implementation of consistent behaviour which should
+   * occur before a HTML file export. It MUST be called at the start of the
+   * exportHTML() method implementation.
+   */
+  protected void exportStarted()
+  {
+    pSessionId = System.currentTimeMillis();
+  }
+
+  /**
+   * This method provides implementation of consistent behaviour which should
+   * occur after a HTML file export. It MUST be called at the end of the
+   * exportHTML() method implementation.
+   */
+  protected void exportCompleted()
+  {
+    if (isLaunchInBrowserAfterExport() && !isHeadless())
+    {
+      try
+      {
+        jalview.util.BrowserLauncher
+                .openURL("file:///" + getExportedFile());
+      } catch (IOException e)
+      {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  /**
+   * if this answers true then BioJSON data will be embedded to the exported
+   * HTML file otherwise it won't be embedded.
+   * 
+   * @return
+   */
+  public abstract boolean isEmbedData();
+
+  /**
+   * if this answers true then the generated HTML file is opened for viewing in
+   * a browser after its generation otherwise it won't be opened in a browser
+   * 
+   * @return
+   */
+  public abstract boolean isLaunchInBrowserAfterExport();
+
+  /**
+   * handle to the generated HTML file
+   * 
+   * @return
+   */
+  public abstract File getExportedFile();
+
+  /**
+   * This is the main method to handle the HTML generation.
+   * 
+   * @param outputFile
+   *          the file path of the generated HTML
+   */
+  public abstract void exportHTML(String outputFile);
 }
diff --git a/src/jalview/io/HtmlFile.java b/src/jalview/io/HtmlFile.java
index 0d0fae9..d48253a 100644
--- a/src/jalview/io/HtmlFile.java
+++ b/src/jalview/io/HtmlFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,10 +22,10 @@
 package jalview.io;
 
 import jalview.api.ComplexAlignFile;
+import jalview.api.FeatureSettingsModelI;
 import jalview.api.FeaturesDisplayedI;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.SequenceI;
-import jalview.schemes.ColourSchemeI;
 
 import java.io.IOException;
 import java.io.StringReader;
@@ -40,7 +40,7 @@ public class HtmlFile extends AlignFile implements ComplexAlignFile
 
   public static final String FILE_DESC = "HTML";
 
-  private ColourSchemeI colourScheme;
+  private String globalColourScheme;
 
   private boolean showSeqFeatures;
 
@@ -108,7 +108,7 @@ public class HtmlFile extends AlignFile implements ComplexAlignFile
       this.seqGroups = jsonFile.getSeqGroups();
       this.annotations = jsonFile.getAnnotations();
       this.showSeqFeatures = jsonFile.isShowSeqFeatures();
-      this.colourScheme = jsonFile.getColourScheme();
+      this.globalColourScheme = jsonFile.getGlobalColourScheme();
       this.hiddenSequences = jsonFile.getHiddenSequences();
       this.columnSelection = jsonFile.getColumnSelection();
       this.displayedFeatures = jsonFile.getDisplayedFeatures();
@@ -125,6 +125,7 @@ public class HtmlFile extends AlignFile implements ComplexAlignFile
             "Print method of HtmlFile is not supported!");
   }
 
+  @Override
   public boolean isShowSeqFeatures()
   {
     return showSeqFeatures;
@@ -135,16 +136,18 @@ public class HtmlFile extends AlignFile implements ComplexAlignFile
     this.showSeqFeatures = showSeqFeatures;
   }
 
-  public ColourSchemeI getColourScheme()
+  @Override
+  public String getGlobalColourScheme()
   {
-    return colourScheme;
+    return globalColourScheme;
   }
 
-  public void setColourScheme(ColourSchemeI colourScheme)
+  public void setColourScheme(String globalColourScheme)
   {
-    this.colourScheme = colourScheme;
+    this.globalColourScheme = globalColourScheme;
   }
 
+  @Override
   public ColumnSelection getColumnSelection()
   {
     return columnSelection;
@@ -155,6 +158,7 @@ public class HtmlFile extends AlignFile implements ComplexAlignFile
     this.columnSelection = columnSelection;
   }
 
+  @Override
   public SequenceI[] getHiddenSequences()
   {
     return hiddenSequences;
@@ -171,4 +175,19 @@ public class HtmlFile extends AlignFile implements ComplexAlignFile
     return displayedFeatures;
   }
 
+  /**
+   * Returns a descriptor for suitable feature display settings with
+   * <ul>
+   * <li>ResNums or insertions features visible</li>
+   * <li>insertions features coloured red</li>
+   * <li>ResNum features coloured by label</li>
+   * <li>Insertions displayed above (on top of) ResNums</li>
+   * </ul>
+   */
+  @Override
+  public FeatureSettingsModelI getFeatureColourScheme()
+  {
+    return new PDBFeatureSettings();
+  }
+
 }
diff --git a/src/jalview/io/HtmlSvgOutput.java b/src/jalview/io/HtmlSvgOutput.java
index 7cbde64..0d46a8d 100644
--- a/src/jalview/io/HtmlSvgOutput.java
+++ b/src/jalview/io/HtmlSvgOutput.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,170 +20,58 @@
  */
 package jalview.io;
 
-import jalview.api.AlignExportSettingI;
-import jalview.api.FeatureRenderer;
-import jalview.datamodel.AlignmentExportData;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignViewport;
+import jalview.exceptions.NoFileSelectedException;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.HTMLOptions;
+import jalview.gui.OOMWarning;
 import jalview.math.AlignmentDimension;
 import jalview.util.MessageManager;
 
-import java.awt.Color;
-import java.awt.FontMetrics;
 import java.awt.Graphics;
-import java.awt.print.Printable;
 import java.awt.print.PrinterException;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.IOException;
 
 import org.jfree.graphics2d.svg.SVGGraphics2D;
 import org.jfree.graphics2d.svg.SVGHints;
 
-public class HtmlSvgOutput
+public class HtmlSvgOutput extends HTMLOutput
 {
-  AlignViewport av;
 
-  FeatureRenderer fr;
 
-  AlignmentPanel ap;
-
-  public HtmlSvgOutput(File file, AlignmentPanel ap)
+  public HtmlSvgOutput(AlignmentPanel ap)
   {
-    this.av = ap.av;
-    this.ap = ap;
-    fr = ap.cloneFeatureRenderer();
-    generateHtmlSvgOutput(file);
+    super(ap);
   }
 
-  public void generateHtmlSvgOutput(File file)
+  @Override
+  public void exportHTML(String outputFile)
   {
+    exportStarted();
     try
     {
-      if (file == null)
-      {
-
-        JalviewFileChooser chooser = getHTMLChooser();
-        chooser.setFileView(new jalview.io.JalviewFileView());
-        chooser.setDialogTitle(ap.alignFrame.getTitle());
-        chooser.setToolTipText(MessageManager.getString("action.save"));
-        int value = chooser.showSaveDialog(ap.alignFrame);
-
-        if (value == jalview.io.JalviewFileChooser.APPROVE_OPTION)
-        {
-          jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
-                  .getSelectedFile().getParent());
-          file = chooser.getSelectedFile();
-        }
-      }
-
-      AlignmentDimension aDimension = ap.getAlignmentDimension();
-      SVGGraphics2D g1 = new SVGGraphics2D(aDimension.getWidth(),
-              aDimension.getHeight());
-      SVGGraphics2D g2 = new SVGGraphics2D(aDimension.getWidth(),
-              aDimension.getHeight());
-
-      String renderStyle = jalview.bin.Cache.getDefault("HTML_RENDERING",
-              "Prompt each time");
-
-      // If we need to prompt, and if the GUI is visible then
-      // Prompt for rendering style
-      if (renderStyle.equalsIgnoreCase("Prompt each time")
-              && !(System.getProperty("java.awt.headless") != null && System
-                      .getProperty("java.awt.headless").equals("true")))
-      {
-        HTMLOptions svgOption = new HTMLOptions();
-        renderStyle = svgOption.getValue();
-
-        if (renderStyle == null || svgOption.cancelled)
-        {
-          return;
-        }
-      }
-
-      if (renderStyle.equalsIgnoreCase("lineart"))
-      {
-        g1.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
-                SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
-        g2.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
-                SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
-      }
-      printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0, g1,
-              g2);
-
-      String titleSvgData = g1.getSVGDocument();
-      String alignSvgData = g2.getSVGDocument();
-      String jsonData = null;
-      boolean isEmbbedBioJSON = Boolean.valueOf(jalview.bin.Cache
-              .getDefault("EXPORT_EMBBED_BIOJSON", "true"));
-      if (isEmbbedBioJSON)
-      {
-        AlignExportSettingI exportSettings = new AlignExportSettingI()
-        {
-          @Override
-          public boolean isExportHiddenSequences()
-          {
-            return true;
-          }
-
-          @Override
-          public boolean isExportHiddenColumns()
-          {
-            return true;
-          }
-
-          @Override
-          public boolean isExportAnnotations()
-          {
-            return true;
-          }
-
-          @Override
-          public boolean isExportFeatures()
-          {
-            return true;
-          }
-
-          @Override
-          public boolean isExportGroups()
-          {
-            return true;
-          }
-
-          @Override
-          public boolean isCancelled()
-          {
-            return false;
-          }
-
-        };
-        AlignmentExportData exportData = jalview.gui.AlignFrame
-                .getAlignmentForExport(JSONFile.FILE_DESC, av,
-                        exportSettings);
-        jsonData = new FormatAdapter(ap, exportData.getSettings())
-                .formatSequences(JSONFile.FILE_DESC,
-                        exportData.getAlignment(),
-                        exportData.getOmitHidden(),
-                        exportData.getStartEndPostions(),
-                        av.getColumnSelection());
-      }
-      String htmlData = getHtml(titleSvgData, alignSvgData, jsonData);
-      FileOutputStream out = new FileOutputStream(file);
-      out.write(htmlData.getBytes());
-      out.flush();
-      out.close();
-      if (!(System.getProperty("java.awt.headless") != null && System
-              .getProperty("java.awt.headless").equals("true")))
+      if (outputFile == null)
       {
-        jalview.util.BrowserLauncher.openURL("file:///" + file);
+        outputFile = getOutputFile();
       }
+      generatedFile = new File(outputFile);
+    } catch (NoFileSelectedException e)
+    {
+      setProgressMessage(MessageManager.formatMessage(
+              "status.cancelled_image_export_operation", "HTML"));
+      return;
     } catch (Exception e)
     {
+      setProgressMessage(MessageManager.formatMessage(
+              "info.error_creating_file", "HTML"));
       e.printStackTrace();
+      return;
     }
+    new Thread(this).start();
   }
 
+
   static JalviewFileChooser getHTMLChooser()
   {
     return new jalview.io.JalviewFileChooser(
@@ -193,139 +81,22 @@ public class HtmlSvgOutput
             "Hypertext Markup Language");
   }
 
-  public int printUnwrapped(int pwidth, int pheight, int pi, Graphics... pg)
+  public int printUnwrapped(int pwidth, int pheight, int pi,
+          Graphics idGraphics, Graphics alignmentGraphics)
           throws PrinterException
   {
-    int idWidth = ap.getVisibleIdWidth(false);
-    FontMetrics fm = ap.getFontMetrics(av.getFont());
-    int scaleHeight = av.getCharHeight() + fm.getDescent();
-
-    pg[0].setColor(Color.white);
-    pg[0].fillRect(0, 0, pwidth, pheight);
-    pg[0].setFont(av.getFont());
-
-    // //////////////////////////////////
-    // / How many sequences and residues can we fit on a printable page?
-    int totalRes = (pwidth - idWidth) / av.getCharWidth();
-    int totalSeq = (pheight - scaleHeight) / av.getCharHeight() - 1;
-    int pagesWide = (av.getAlignment().getWidth() / totalRes) + 1;
-
-    // ///////////////////////////
-    // / Only print these sequences and residues on this page
-    int startRes;
-
-    // ///////////////////////////
-    // / Only print these sequences and residues on this page
-    int endRes;
-
-    // ///////////////////////////
-    // / Only print these sequences and residues on this page
-    int startSeq;
-
-    // ///////////////////////////
-    // / Only print these sequences and residues on this page
-    int endSeq;
-    startRes = (pi % pagesWide) * totalRes;
-    endRes = (startRes + totalRes) - 1;
-
-    if (endRes > (av.getAlignment().getWidth() - 1))
-    {
-      endRes = av.getAlignment().getWidth() - 1;
-    }
-    startSeq = (pi / pagesWide) * totalSeq;
-    endSeq = startSeq + totalSeq;
-    if (endSeq > av.getAlignment().getHeight())
-    {
-      endSeq = av.getAlignment().getHeight();
-    }
-    int pagesHigh = ((av.getAlignment().getHeight() / totalSeq) + 1)
-            * pheight;
-    if (av.isShowAnnotation())
-    {
-      pagesHigh += ap.getAnnotationPanel().adjustPanelHeight() + 3;
-    }
-    pagesHigh /= pheight;
-    if (pi >= (pagesWide * pagesHigh))
-    {
-      return Printable.NO_SUCH_PAGE;
-    }
-
-    // draw Scale
-    pg[1].translate(0, 0);
-    ap.getScalePanel().drawScale(pg[1], startRes, endRes, pwidth - idWidth,
-            scaleHeight);
-    pg[1].translate(-idWidth, scaleHeight);
-
-    // //////////////
-    // Draw the ids
-    Color currentColor = null;
-    Color currentTextColor = null;
-    pg[0].translate(0, scaleHeight);
-    pg[0].setFont(ap.getIdPanel().getIdCanvas().getIdfont());
-    SequenceI seq;
-    for (int i = startSeq; i < endSeq; i++)
-    {
-      seq = av.getAlignment().getSequenceAt(i);
-      if ((av.getSelectionGroup() != null)
-              && av.getSelectionGroup().getSequences(null).contains(seq))
-      {
-        currentColor = Color.gray;
-        currentTextColor = Color.black;
-      }
-      else
-      {
-        currentColor = av.getSequenceColour(seq);
-        currentTextColor = Color.black;
-      }
-      pg[0].setColor(currentColor);
-      pg[0].fillRect(0, (i - startSeq) * av.getCharHeight(), idWidth,
-              av.getCharHeight());
-      pg[0].setColor(currentTextColor);
-      int xPos = 0;
-      if (av.isRightAlignIds())
-      {
-        fm = pg[0].getFontMetrics();
-        xPos = idWidth
-                - fm.stringWidth(seq.getDisplayId(av.getShowJVSuffix()))
-                - 4;
-      }
-      pg[0].drawString(seq.getDisplayId(av.getShowJVSuffix()), xPos,
-              (((i - startSeq) * av.getCharHeight()) + av.getCharHeight())
-                      - (av.getCharHeight() / 5));
-    }
-    pg[0].setFont(av.getFont());
-    pg[0].translate(idWidth, 0);
-
-    // draw main sequence panel
-    pg[1].translate(idWidth, 0);
-    ap.getSeqPanel().seqCanvas.drawPanel(pg[1], startRes, endRes, startSeq,
-            endSeq, 0);
-    if (av.isShowAnnotation() && (endSeq == av.getAlignment().getHeight()))
-    {
-      // draw annotation label - need to offset for current scroll position
-      int offset = -ap.getAlabels().getScrollOffset();
-      pg[0].translate(0, offset);
-      pg[0].translate(-idWidth - 3,
-              (endSeq - startSeq) * av.getCharHeight() + 3);
-      ap.getAlabels().drawComponent(pg[0], idWidth);
-      pg[0].translate(idWidth + 3, 0);
-      pg[0].translate(0, -offset);
-
-      // draw annotation - need to offset for current scroll position
-      pg[1].translate(0, offset);
-      pg[1].translate(-idWidth - 3,
-              (endSeq - startSeq) * av.getCharHeight() + 3);
-      pg[1].translate(idWidth + 3, 0);
-      ap.getAnnotationPanel().renderer.drawComponent(
-              ap.getAnnotationPanel(), av, pg[1], -1, startRes, endRes + 1);
-      pg[1].translate(0, -offset);
-    }
+    return ap.printUnwrapped(pwidth, pheight, pi, idGraphics,
+            alignmentGraphics);
+  }
 
-    return Printable.PAGE_EXISTS;
+  public int printWrapped(int pwidth, int pheight, int pi, Graphics... pg)
+          throws PrinterException
+  {
+    return ap.printWrappedAlignment(pwidth, pheight, pi, pg[0]);
   }
 
   private String getHtml(String titleSvg, String alignmentSvg,
-          String jsonData)
+          String jsonData, boolean wrapped)
   {
     StringBuilder htmlSvg = new StringBuilder();
     htmlSvg.append("<html>\n");
@@ -365,8 +136,9 @@ public class HtmlSvgOutput
               + ".facebox_hide { z-index:-100; }\n"
               + ".facebox_overlayBG { background-color: #000;  z-index: 99;  }");
     }
-
     htmlSvg.append("</style>");
+    if (!wrapped)
+    {
     htmlSvg.append("<div class=\"main-container\" \n>");
     htmlSvg.append("<div class=\"titlex\">\n");
     htmlSvg.append("<div class=\"sub-category-container\"> \n");
@@ -387,9 +159,16 @@ public class HtmlSvgOutput
             + "subCatContainer.scrollTop($(this).scrollTop());\n});\n");
 
     htmlSvg.append("</script>\n");
+    }
+    else
+    {
+      htmlSvg.append("<div>\n")
+              .append(alignmentSvg).append("</div>");
+      htmlSvg.append("<script language=\"JavaScript\" type=\"text/javascript\" src=\"http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js\"></script>\n"
+              + "<script language=\"JavaScript\" type=\"text/javascript\"  src=\"http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js\"></script>\n");
+    }
 
     // javascript for launching file in Jalview
-
     htmlSvg.append("<script language=\"JavaScript\">\n");
     htmlSvg.append("function openJalviewUsingCurrentUrl(){\n");
     htmlSvg.append("    var json = JSON.parse(document.getElementById(\"seqData\").innerHTML);\n");
@@ -414,269 +193,115 @@ public class HtmlSvgOutput
     htmlSvg.append("    document.body.removeChild(myForm);\n");
     htmlSvg.append("}\n");
 
-    // jquery facebox for displaying raw BioJSON data");
     if (jsonData != null)
     {
-      htmlSvg.append("/* Facebox (for jQuery)\n");
-      htmlSvg.append("* version: 1.3\n");
-      htmlSvg.append(" * @requires jQuery v1.2 or later\n");
-      htmlSvg.append(" * @homepage https://github.com/defunkt/facebox\n");
-      htmlSvg.append(" * Licensed under the MIT:\n");
-      htmlSvg.append(" *   http://www.opensource.org/licenses/mit-license.php\n");
-      htmlSvg.append(" * Copyright Forever Chris Wanstrath, Kyle Neath\n");
-      htmlSvg.append(" * Usage:\n");
-      htmlSvg.append(" *  jQuery(document).ready(function() {\n");
-      htmlSvg.append(" *    jQuery('a[rel*=facebox]').facebox()\n");
-      htmlSvg.append(" *  })\n");
-      htmlSvg.append(" *  <a href=\"#terms\" rel=\"facebox\">Terms</a>\n");
-      htmlSvg.append(" *    Loads the #terms div in the box\n");
-      htmlSvg.append(" *  <a href=\"terms.html\" rel=\"facebox\">Terms</a>\n");
-      htmlSvg.append(" *    Loads the terms.html page in the box\n");
-      htmlSvg.append(" *  <a href=\"terms.png\" rel=\"facebox\">Terms</a>\n");
-      htmlSvg.append(" *    Loads the terms.png image in the box\n");
-      htmlSvg.append(" *  You can also use it programmatically:\n");
-      htmlSvg.append(" *    jQuery.facebox('some html')\n");
-      htmlSvg.append(" *    jQuery.facebox('some html', 'my-groovy-style')\n");
-      htmlSvg.append(" *  The above will open a facebox with \"some html\" as the content.\n");
-      htmlSvg.append(" *    jQuery.facebox(function($) {\n");
-      htmlSvg.append(" *      $.get('blah.html', function(data) { $.facebox(data) })\n");
-      htmlSvg.append(" *    })\n");
-      htmlSvg.append(" *  The above will show a loading screen before the passed function is called,\n");
-      htmlSvg.append(" *  allowing for a better ajaxy experience.\n");
-      htmlSvg.append(" *  The facebox function can also display an ajax page, an image, or the contents of a div:\n");
-      htmlSvg.append(" *    jQuery.facebox({ ajax: 'remote.html' })\n");
-      htmlSvg.append(" *    jQuery.facebox({ ajax: 'remote.html' }, 'my-groovy-style')\n");
-      htmlSvg.append(" *    jQuery.facebox({ image: 'stairs.jpg' })\n");
-      htmlSvg.append(" *    jQuery.facebox({ image: 'stairs.jpg' }, 'my-groovy-style')\n");
-      htmlSvg.append(" *    jQuery.facebox({ div: '#box' })\n");
-      htmlSvg.append(" *    jQuery.facebox({ div: '#box' }, 'my-groovy-style')\n");
-      htmlSvg.append(" *    Want to close the facebox?  Trigger the 'close.facebox' document event:\n");
-      htmlSvg.append(" *    jQuery(document).trigger('close.facebox')\n");
-      htmlSvg.append(" *  Facebox also has a bunch of other hooks:\n");
-      htmlSvg.append(" *    loading.facebox\n");
-      htmlSvg.append(" *    beforeReveal.facebox\n");
-      htmlSvg.append(" *    reveal.facebox (aliased as 'afterReveal.facebox')\n");
-      htmlSvg.append(" *    init.facebox\n");
-      htmlSvg.append(" *    afterClose.facebox\n");
-      htmlSvg.append(" *  Simply bind a function to any of these hooks:\n");
-      htmlSvg.append(" *   $(document).bind('reveal.facebox', function() { ...stuff to do after the facebox and contents are revealed... })\n");
-      htmlSvg.append(" *\n");
-      htmlSvg.append(" */\n");
-      htmlSvg.append("(function($) {\n");
-      htmlSvg.append("  $.facebox = function(data, klass) {\n");
-      htmlSvg.append("    $.facebox.loading()\n");
-      htmlSvg.append("    if (data.ajax) fillFaceboxFromAjax(data.ajax, klass)\n");
-      htmlSvg.append("    else if (data.image) fillFaceboxFromImage(data.image, klass)\n");
-      htmlSvg.append("    else if (data.div) fillFaceboxFromHref(data.div, klass)\n");
-      htmlSvg.append("    else if ($.isFunction(data)) data.call($)\n");
-      htmlSvg.append("    else $.facebox.reveal(data, klass)\n");
-      htmlSvg.append("  }\n");
-
-      htmlSvg.append("  $.extend($.facebox, {\n");
-      htmlSvg.append("    settings: {\n");
-      htmlSvg.append("      opacity      : 0.2,\n");
-      htmlSvg.append("      overlay      : true,\n");
-      htmlSvg.append("      loadingImage : 'https://raw.githubusercontent.com/jalview/biojson/gh-pages/images/loading.gif',\n");
-      htmlSvg.append("      closeImage   : 'https://raw.githubusercontent.com/jalview/biojson/gh-pages/images/cancel.png',\n");
-      htmlSvg.append("      imageTypes   : [ 'png', 'jpg', 'jpeg', 'gif' ],\n");
-      htmlSvg.append("      faceboxHtml  : '<div  id=\"facebox\" style=\"display:none; width: 95%; height: 85%; overflow: auto;\"> ");
-      htmlSvg.append("      <div class=\"popup\"> ");
-      htmlSvg.append("        <div class=\"content\"> ");
-      htmlSvg.append("        </div> ");
-      htmlSvg.append("        <a href=\"#\" class=\"close\"></a> ");
-      htmlSvg.append("      </div> ");
-      htmlSvg.append("    </div>'\n");
-      htmlSvg.append("    },      \n");
-      htmlSvg.append("    loading: function() {\n");
-      htmlSvg.append("      init()\n");
-      htmlSvg.append("      if ($('#facebox .loading').length == 1) return true\n");
-      htmlSvg.append("      showOverlay()      \n");
-      htmlSvg.append("      $('#facebox .content').empty().\n");
-      htmlSvg.append("        append('<div class=\"loading\"><img src=\"'+$.facebox.settings.loadingImage+'\"/></div>')\n");
-      htmlSvg.append("      $('#facebox').show().css({\n");
-      htmlSvg.append("        top:    getPageScroll()[1] + (getPageHeight() / 10),\n");
-      htmlSvg.append("        left:    $(window).width() / 2 - ($('#facebox .popup').outerWidth() / 2)\n");
-      htmlSvg.append("      })      \n");
-      htmlSvg.append("      $(document).bind('keydown.facebox', function(e) {\n");
-      htmlSvg.append("       if (e.keyCode == 27) $.facebox.close()\n");
-      htmlSvg.append("        return true\n");
-      htmlSvg.append("      })\n");
-      htmlSvg.append("      $(document).trigger('loading.facebox')\n");
-      htmlSvg.append("    },\n");
-      htmlSvg.append("    reveal: function(data, klass) {\n");
-      htmlSvg.append("      $(document).trigger('beforeReveal.facebox')\n");
-      htmlSvg.append("      if (klass) $('#facebox .content').addClass(klass)\n");
-      htmlSvg.append("      $('#facebox .content').empty().append('<pre><code>'+JSON.stringify(JSON.parse(data),null,4)+'</pre></code>')\n");
-      htmlSvg.append("      $('#facebox .popup').children().fadeIn('normal')\n");
-      htmlSvg.append("      $('#facebox').css('left', $(window).width() / 2 - ($('#facebox .popup').outerWidth() / 2))\n");
-      htmlSvg.append("      $(document).trigger('reveal.facebox').trigger('afterReveal.facebox')\n");
-      htmlSvg.append("    },      \n");
-      htmlSvg.append("    close: function() {\n");
-      htmlSvg.append("      $(document).trigger('close.facebox')\n");
-      htmlSvg.append("      return false\n");
-      htmlSvg.append("    }\n");
-      htmlSvg.append("  })\n");
-      htmlSvg.append("  $.fn.facebox = function(settings) {\n");
-      htmlSvg.append("    if ($(this).length == 0) return    \n");
-      htmlSvg.append("    init(settings)      \n");
-      htmlSvg.append("    function clickHandler() {\n");
-      htmlSvg.append("      $.facebox.loading(true)      \n");
-      htmlSvg.append("      // support for rel=\"facebox.inline_popup\" syntax, to add a class\n");
-      htmlSvg.append("      // also supports deprecated \"facebox[.inline_popup]\" syntax\n");
-      htmlSvg.append("      var klass = this.rel.match(/facebox\\[?\\.(\\w+)\\]?/)\n");
-      htmlSvg.append("      if (klass) klass = klass[1]\n");
-      htmlSvg.append("      fillFaceboxFromHref(this.href, klass)\n");
-      htmlSvg.append("      return false\n");
-      htmlSvg.append("    }      \n");
-      htmlSvg.append("    return this.bind('click.facebox', clickHandler)\n");
-      htmlSvg.append("  }\n");
-      htmlSvg.append("  // called one time to setup facebox on this page\n");
-      htmlSvg.append("  function init(settings) {\n");
-      htmlSvg.append("    if ($.facebox.settings.inited) return true\n");
-      htmlSvg.append("    else $.facebox.settings.inited = true\n");
-      htmlSvg.append("    $(document).trigger('init.facebox')\n");
-      htmlSvg.append("    makeCompatible()\n");
-      htmlSvg.append("    var imageTypes = $.facebox.settings.imageTypes.join('|')\n");
-      htmlSvg.append("    $.facebox.settings.imageTypesRegexp = new RegExp('\\\\.(' + imageTypes + ')(\\\\?.*)?$', 'i')\n");
-
-      htmlSvg.append("    if (settings) $.extend($.facebox.settings, settings)\n");
-      htmlSvg.append("    $('body').append($.facebox.settings.faceboxHtml)\n");
-
-      htmlSvg.append("    var preload = [ new Image(), new Image() ]\n");
-      htmlSvg.append("    preload[0].src = $.facebox.settings.closeImage\n");
-      htmlSvg.append("    preload[1].src = $.facebox.settings.loadingImage\n");
-
-      htmlSvg.append("    $('#facebox').find('.b:first, .bl').each(function() {\n");
-      htmlSvg.append("      preload.push(new Image())\n");
-      htmlSvg.append("      preload.slice(-1).src = $(this).css('background-image').replace(/url\\((.+)\\)/, '$1')\n");
-      htmlSvg.append("    })\n");
-
-      htmlSvg.append("    $('#facebox .close')\n");
-      htmlSvg.append("      .click($.facebox.close)\n");
-      htmlSvg.append("      .append('<img src=\"'\n");
-      htmlSvg.append("              + $.facebox.settings.closeImage\n");
-      htmlSvg.append("              + '\" class=\"close_image\" title=\"close\">')\n");
-      htmlSvg.append("  }\n");
-
-      htmlSvg.append("  // getPageScroll() by quirksmode.com\n");
-      htmlSvg.append("  function getPageScroll() {\n");
-      htmlSvg.append("    var xScroll, yScroll;\n");
-      htmlSvg.append("    if (self.pageYOffset) {\n");
-      htmlSvg.append("      yScroll = self.pageYOffset;\n");
-      htmlSvg.append("      xScroll = self.pageXOffset;\n");
-      htmlSvg.append("    } else if (document.documentElement && document.documentElement.scrollTop) {     // Explorer 6 Strict\n");
-      htmlSvg.append("      yScroll = document.documentElement.scrollTop;\n");
-      htmlSvg.append("      xScroll = document.documentElement.scrollLeft;\n");
-      htmlSvg.append("    } else if (document.body) {// all other Explorers\n");
-      htmlSvg.append("      yScroll = document.body.scrollTop;\n");
-      htmlSvg.append("      xScroll = document.body.scrollLeft;\n");
-      htmlSvg.append("    }\n");
-      htmlSvg.append("    return new Array(xScroll,yScroll)\n");
-      htmlSvg.append("  }\n");
-
-      // Adapted from getPageSize() by quirksmode.com");
-      htmlSvg.append("  function getPageHeight() {\n");
-      htmlSvg.append("    var windowHeight\n");
-      htmlSvg.append("    if (self.innerHeight) {    // all except Explorer\n");
-      htmlSvg.append("      windowHeight = self.innerHeight;\n");
-      htmlSvg.append("    } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode\n");
-      htmlSvg.append("      windowHeight = document.documentElement.clientHeight;\n");
-      htmlSvg.append("    } else if (document.body) { // other Explorers\n");
-      htmlSvg.append("      windowHeight = document.body.clientHeight;\n");
-      htmlSvg.append("    }\n");
-      htmlSvg.append("    return windowHeight\n");
-      htmlSvg.append("  }\n");
-
-      htmlSvg.append("  // Backwards compatibility\n");
-      htmlSvg.append("  function makeCompatible() {\n");
-      htmlSvg.append("    var $s = $.facebox.settings      \n");
-      htmlSvg.append("    $s.loadingImage = $s.loading_image || $s.loadingImage\n");
-      htmlSvg.append("    $s.closeImage = $s.close_image || $s.closeImage\n");
-      htmlSvg.append("    $s.imageTypes = $s.image_types || $s.imageTypes\n");
-      htmlSvg.append("    $s.faceboxHtml = $s.facebox_html || $s.faceboxHtml\n");
-      htmlSvg.append("  }\n");
-
-      htmlSvg.append("  // Figures out what you want to display and displays it\n");
-      htmlSvg.append("  // formats are:\n");
-      htmlSvg.append("  //     div: #id\n");
-      htmlSvg.append("  //   image: blah.extension\n");
-      htmlSvg.append("  //    ajax: anything else\n");
-      htmlSvg.append("  function fillFaceboxFromHref(href, klass) {\n");
-      htmlSvg.append("    // div\n");
-      htmlSvg.append("    if (href.match(/#/)) {\n");
-      htmlSvg.append("      var url    = window.location.href.split('#')[0]\n");
-      htmlSvg.append("      var target = href.replace(url,'')\n");
-      htmlSvg.append("      if (target == '#') return\n");
-      htmlSvg.append("      $.facebox.reveal($(target).html(), klass)\n");
-
-      htmlSvg.append("    // image\n");
-      htmlSvg.append("    } else if (href.match($.facebox.settings.imageTypesRegexp)) {\n");
-      htmlSvg.append("      fillFaceboxFromImage(href, klass)\n");
-      htmlSvg.append("    // ajax\n");
-      htmlSvg.append("    } else {\n");
-      htmlSvg.append("      fillFaceboxFromAjax(href, klass)\n");
-      htmlSvg.append("    }\n");
-      htmlSvg.append("  }\n");
+      // JQuery FaceBox for displaying raw BioJSON data");
+      File faceBoxJsFile = new File("examples/javascript/facebox-1.3.js");
+      try
+      {
+        htmlSvg.append(HTMLOutput.readFileAsString(faceBoxJsFile));
+      } catch (IOException e)
+      {
+        e.printStackTrace();
+      }
+    }
 
-      htmlSvg.append("  function fillFaceboxFromImage(href, klass) {\n");
-      htmlSvg.append("    var image = new Image()\n");
-      htmlSvg.append("    image.onload = function() {\n");
-      htmlSvg.append("      $.facebox.reveal('<div class=\"image\"><img src=\"' + image.src + '\" /></div>', klass)\n");
-      htmlSvg.append("    }\n");
-      htmlSvg.append("    image.src = href\n");
-      htmlSvg.append("   }\n");
+    htmlSvg.append("</script>\n");
+    htmlSvg.append("</html>");
+    return htmlSvg.toString();
+  }
 
-      htmlSvg.append("  function fillFaceboxFromAjax(href, klass) {\n");
-      htmlSvg.append("    $.facebox.jqxhr = $.get(href, function(data) { $.facebox.reveal(data, klass) })\n");
-      htmlSvg.append("  }\n");
+  @Override
+  public boolean isEmbedData()
+  {
+    return Boolean.valueOf(jalview.bin.Cache.getDefault(
+            "EXPORT_EMBBED_BIOJSON", "true"));
+  }
 
-      htmlSvg.append("  function skipOverlay() {\n");
-      htmlSvg.append("    return $.facebox.settings.overlay == false || $.facebox.settings.opacity === null\n");
-      htmlSvg.append("  }\n");
+  @Override
+  public boolean isLaunchInBrowserAfterExport()
+  {
+    return true;
+  }
 
-      htmlSvg.append("  function showOverlay() {\n");
-      htmlSvg.append("    if (skipOverlay()) return\n");
+  @Override
+  public File getExportedFile()
+  {
+    return generatedFile;
+  }
 
-      htmlSvg.append("    if ($('#facebox_overlay').length == 0)\n");
-      htmlSvg.append("      $(\"body\").append('<div id=\"facebox_overlay\" class=\"facebox_hide\"></div>')\n");
+  @Override
+  public void run()
+  {
+    try
+    {
+      setProgressMessage(null);
+      setProgressMessage(MessageManager.formatMessage(
+              "status.exporting_alignment_as_x_file", "HTML"));
+      AlignmentDimension aDimension = ap.getAlignmentDimension();
+      SVGGraphics2D idPanelGraphics = new SVGGraphics2D(
+              aDimension.getWidth(), aDimension.getHeight());
+      SVGGraphics2D alignPanelGraphics = new SVGGraphics2D(
+              aDimension.getWidth(), aDimension.getHeight());
 
-      htmlSvg.append("    $('#facebox_overlay').hide().addClass(\"facebox_overlayBG\")\n");
-      htmlSvg.append("      .css('opacity', $.facebox.settings.opacity)\n");
-      htmlSvg.append("      .click(function() { $(document).trigger('close.facebox') })\n");
-      htmlSvg.append("       .fadeIn(200)\n");
-      htmlSvg.append("    return false\n");
-      htmlSvg.append("  }\n");
+      String renderStyle = jalview.bin.Cache.getDefault("HTML_RENDERING",
+              "Prompt each time");
 
-      htmlSvg.append("  function hideOverlay() {\n");
-      htmlSvg.append("    if (skipOverlay()) return      \n");
-      htmlSvg.append("    $('#facebox_overlay').fadeOut(200, function(){\n");
-      htmlSvg.append("      $(\"#facebox_overlay\").removeClass(\"facebox_overlayBG\")\n");
-      htmlSvg.append("      $(\"#facebox_overlay\").addClass(\"facebox_hide\")\n");
-      htmlSvg.append("      $(\"#facebox_overlay\").remove()\n");
-      htmlSvg.append("    })      \n");
-      htmlSvg.append("    return false\n");
-      htmlSvg.append("  }\n");
+      // If we need to prompt, and if the GUI is visible then
+      // Prompt for rendering style
+      if (renderStyle.equalsIgnoreCase("Prompt each time") && !isHeadless())
+      {
+        HTMLOptions svgOption = new HTMLOptions();
+        renderStyle = svgOption.getValue();
 
-      htmlSvg.append("  $(document).bind('close.facebox', function() {\n");
-      htmlSvg.append("    if ($.facebox.jqxhr) {\n");
-      htmlSvg.append("      $.facebox.jqxhr.abort()\n");
-      htmlSvg.append("      $.facebox.jqxhr = null\n");
-      htmlSvg.append("    }\n");
-      htmlSvg.append("    $(document).unbind('keydown.facebox')\n");
-      htmlSvg.append("    $('#facebox').fadeOut(function() {\n");
-      htmlSvg.append("      $('#facebox .content').removeClass().addClass('content')\n");
-      htmlSvg.append("      $('#facebox .loading').remove()\n");
-      htmlSvg.append("      $(document).trigger('afterClose.facebox')\n");
-      htmlSvg.append("    })\n");
-      htmlSvg.append("    hideOverlay()\n");
-      htmlSvg.append("  })\n");
+        if (renderStyle == null || svgOption.cancelled)
+        {
+          setProgressMessage(MessageManager.formatMessage(
+                  "status.cancelled_image_export_operation", "HTML"));
+          return;
+        }
+      }
 
-      htmlSvg.append("})(jQuery);\n");
+      if (renderStyle.equalsIgnoreCase("Lineart"))
+      {
+        idPanelGraphics.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
+                SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
+        alignPanelGraphics.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
+                SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
+      }
+      if (ap.av.getWrapAlignment())
+      {
+        printWrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
+                alignPanelGraphics);
+      }
+      else
+      {
+        printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
+                idPanelGraphics, alignPanelGraphics);
+      }
 
+      String idPanelSvgData = idPanelGraphics.getSVGDocument();
+      String alignPanelSvgData = alignPanelGraphics.getSVGDocument();
+      String jsonData = getBioJSONData();
+      String htmlData = getHtml(idPanelSvgData, alignPanelSvgData,
+              jsonData, ap.av.getWrapAlignment());
+      FileOutputStream out = new FileOutputStream(generatedFile);
+      out.write(htmlData.getBytes());
+      out.flush();
+      out.close();
+      setProgressMessage(MessageManager.formatMessage(
+              "status.export_complete", "HTML"));
+      exportCompleted();
+    } catch (OutOfMemoryError err)
+    {
+      System.out.println("########################\n" + "OUT OF MEMORY "
+              + generatedFile + "\n" + "########################");
+      new OOMWarning("Creating Image for " + generatedFile, err);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      setProgressMessage(MessageManager.formatMessage(
+              "info.error_creating_file", "HTML"));
     }
-
-    htmlSvg.append("</script>\n");
-    htmlSvg.append("</html>");
-    return htmlSvg.toString();
   }
 }
diff --git a/src/jalview/io/IdentifyFile.java b/src/jalview/io/IdentifyFile.java
index a241e0e..ac5a808 100644
--- a/src/jalview/io/IdentifyFile.java
+++ b/src/jalview/io/IdentifyFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  *
  * This file is part of Jalview.
  *
@@ -30,7 +30,7 @@ import java.io.IOException;
  */
 public class IdentifyFile
 {
-  public static final String GFF3File = "GFF v2 or v3";
+  public static final String FeaturesFile = "GFF or Jalview features";
 
   /**
    * Identify a datasource's file content.
@@ -44,7 +44,7 @@ public class IdentifyFile
    *          DOCUMENT ME!
    * @return ID String
    */
-  public String Identify(String file, String protocol)
+  public String identify(String file, String protocol)
   {
     String emessage = "UNIDENTIFIED FILE PARSING ERROR";
     FileParse parser = null;
@@ -53,7 +53,7 @@ public class IdentifyFile
       parser = new FileParse(file, protocol);
       if (parser.isValid())
       {
-        return Identify(parser);
+        return identify(parser);
       }
     } catch (Exception e)
     {
@@ -68,9 +68,9 @@ public class IdentifyFile
     return emessage;
   }
 
-  public String Identify(FileParse source)
+  public String identify(FileParse source)
   {
-    return Identify(source, true); // preserves original behaviour prior to
+    return identify(source, true); // preserves original behaviour prior to
     // version 2.3
   }
 
@@ -82,11 +82,12 @@ public class IdentifyFile
    * @param closeSource
    * @return filetype string
    */
-  public String Identify(FileParse source, boolean closeSource)
+  public String identify(FileParse source, boolean closeSource)
   {
     String reply = "PFAM";
     String data;
-    int length = 0;
+    int bytesRead = 0;
+    int trimmedLength = 0;
     boolean lineswereskipped = false;
     boolean isBinary = false; // true if length is non-zero and non-printable
     // characters are encountered
@@ -98,7 +99,8 @@ public class IdentifyFile
       }
       while ((data = source.nextLine()) != null)
       {
-        length += data.trim().length();
+        bytesRead += data.length();
+        trimmedLength += data.trim().length();
         if (!lineswereskipped)
         {
           for (int i = 0; !isBinary && i < data.length(); i++)
@@ -134,7 +136,13 @@ public class IdentifyFile
 
         if (data.startsWith("##GFF-VERSION"))
         {
-          reply = GFF3File;
+          // GFF - possibly embedded in a Jalview features file!
+          reply = FeaturesFile;
+          break;
+        }
+        if (looksLikeFeatureData(data))
+        {
+          reply = FeaturesFile;
           break;
         }
         if (data.indexOf("# STOCKHOLM") > -1)
@@ -142,6 +150,13 @@ public class IdentifyFile
           reply = "STH";
           break;
         }
+        if (data.indexOf("_ENTRY.ID") > -1
+                || data.indexOf("_AUDIT_AUTHOR.NAME") > -1
+                || data.indexOf("_ATOM_SITE.") > -1)
+        {
+          reply = "mmCIF";
+          break;
+        }
         // if (data.indexOf(">") > -1)
         if (data.startsWith(">"))
         {
@@ -215,7 +230,6 @@ public class IdentifyFile
                 } catch (IOException ex)
                 {
                 }
-                ;
                 if (dta != null && dta.indexOf("*") > -1)
                 {
                   starterm = true;
@@ -235,29 +249,19 @@ public class IdentifyFile
           // read as a FASTA (probably)
           break;
         }
-        if ((data.indexOf("<") > -1)) // possible Markup Language data i.e HTML,
-                                      // RNAML, XML
+        int lessThan = data.indexOf("<");
+        if ((lessThan > -1)) // possible Markup Language data i.e HTML,
+                             // RNAML, XML
         {
-          boolean identified = false;
-          do
+          String upper = data.toUpperCase();
+          if (upper.substring(lessThan).startsWith("<HTML"))
           {
-            if (data.matches("<(?i)html(\"[^\"]*\"|'[^']*'|[^'\">])*>"))
-            {
-              reply = HtmlFile.FILE_DESC;
-              identified = true;
-              break;
-            }
-
-            if (data.matches("<(?i)rnaml (\"[^\"]*\"|'[^']*'|[^'\">])*>"))
-            {
-              reply = "RNAML";
-              identified = true;
-              break;
-            }
-          } while ((data = source.nextLine()) != null);
-
-          if (identified)
+            reply = HtmlFile.FILE_DESC;
+            break;
+          }
+          if (upper.substring(lessThan).startsWith("<RNAML"))
           {
+            reply = "RNAML";
             break;
           }
         }
@@ -305,23 +309,13 @@ public class IdentifyFile
           reply = PhylipFile.FILE_DESC;
           break;
         }
-
-        /*
-         * // TODO comment out SimpleBLAST identification for Jalview 2.4.1 else
-         * if (!lineswereskipped && data.indexOf("BLAST")<4) { reply =
-         * "SimpleBLAST"; break;
-         * 
-         * } // end comments for Jalview 2.4.1
-         */
-        else if (!lineswereskipped && data.charAt(0) != '*'
-                && data.charAt(0) != ' '
-                && data.indexOf(":") < data.indexOf(",")) // &&
-        // data.indexOf(",")<data.indexOf(",",
-        // data.indexOf(",")))
+        else
         {
-          // file looks like a concise JNet file
-          reply = "JnetFile";
-          break;
+          if (!lineswereskipped && looksLikeJnetData(data))
+          {
+            reply = "JnetFile";
+            break;
+          }
         }
 
         lineswereskipped = true; // this means there was some junk before any
@@ -333,14 +327,14 @@ public class IdentifyFile
       }
       else
       {
-        source.reset(); // so the file can be parsed from the beginning again.
+        source.reset(bytesRead); // so the file can be parsed from the mark
       }
     } catch (Exception ex)
     {
       System.err.println("File Identification failed!\n" + ex);
       return source.errormessage;
     }
-    if (length == 0)
+    if (trimmedLength == 0)
     {
       System.err
               .println("File Identification failed! - Empty file was read.");
@@ -349,13 +343,61 @@ public class IdentifyFile
     return reply;
   }
 
+  /**
+   * Returns true if the data appears to be Jnet concise annotation format
+   * 
+   * @param data
+   * @return
+   */
+  protected boolean looksLikeJnetData(String data)
+  {
+    char firstChar = data.charAt(0);
+    int colonPos = data.indexOf(":");
+    int commaPos = data.indexOf(",");
+    boolean isJnet = firstChar != '*' && firstChar != ' ' && colonPos > -1
+            && commaPos > -1 && colonPos < commaPos;
+    // && data.indexOf(",")<data.indexOf(",", data.indexOf(","))) / ??
+    return isJnet;
+  }
+
+  /**
+   * Returns true if the data has at least 6 tab-delimited fields _and_ fields 4
+   * and 5 are integer (start/end)
+   * 
+   * @param data
+   * @return
+   */
+  protected boolean looksLikeFeatureData(String data)
+  {
+    if (data == null)
+    {
+      return false;
+    }
+    String[] columns = data.split("\t");
+    if (columns.length < 6)
+    {
+      return false;
+    }
+    for (int col = 3; col < 5; col++)
+    {
+      try
+      {
+        Integer.parseInt(columns[col]);
+      } catch (NumberFormatException e)
+      {
+        return false;
+      }
+    }
+    return true;
+  }
+
   public static void main(String[] args)
   {
 
     for (int i = 0; args != null && i < args.length; i++)
     {
       IdentifyFile ider = new IdentifyFile();
-      String type = ider.Identify(args[i], AppletFormatAdapter.FILE);
+      String type = ider.identify(args[i], AppletFormatAdapter.FILE);
       System.out.println("Type of " + args[i] + " is " + type);
     }
     if (args == null || args.length == 0)
diff --git a/src/jalview/io/InputStreamParser.java b/src/jalview/io/InputStreamParser.java
index c12d2a3..7e53d2f 100644
--- a/src/jalview/io/InputStreamParser.java
+++ b/src/jalview/io/InputStreamParser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/JPredFile.java b/src/jalview/io/JPredFile.java
index 5bc615e..50663ed 100644
--- a/src/jalview/io/JPredFile.java
+++ b/src/jalview/io/JPredFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/JSONFile.java b/src/jalview/io/JSONFile.java
index 9b15eb5..e137428 100644
--- a/src/jalview/io/JSONFile.java
+++ b/src/jalview/io/JSONFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,6 +26,7 @@ import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.ComplexAlignFile;
 import jalview.api.FeatureRenderer;
+import jalview.api.FeatureSettingsModelI;
 import jalview.api.FeaturesDisplayedI;
 import jalview.bin.BuildDetails;
 import jalview.datamodel.AlignmentAnnotation;
@@ -39,13 +40,14 @@ import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.json.binding.biojson.v1.AlignmentAnnotationPojo;
 import jalview.json.binding.biojson.v1.AlignmentPojo;
+import jalview.json.binding.biojson.v1.AnnotationDisplaySettingPojo;
 import jalview.json.binding.biojson.v1.AnnotationPojo;
-import jalview.json.binding.biojson.v1.JalviewBioJsColorSchemeMapper;
+import jalview.json.binding.biojson.v1.ColourSchemeMapper;
 import jalview.json.binding.biojson.v1.SequenceFeaturesPojo;
 import jalview.json.binding.biojson.v1.SequenceGrpPojo;
 import jalview.json.binding.biojson.v1.SequencePojo;
-import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.UserColourScheme;
 import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
 
 import java.awt.Color;
@@ -63,8 +65,6 @@ import org.json.simple.parser.JSONParser;
 
 public class JSONFile extends AlignFile implements ComplexAlignFile
 {
-  private ColourSchemeI colourScheme;
-
   private static String version = new BuildDetails().getVersion();
 
   private String webstartUrl = "http://www.jalview.org/services/launchApp";
@@ -75,7 +75,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
 
   public static final String FILE_DESC = "JSON";
 
-  private String globalColorScheme;
+  private String globalColourScheme;
 
   private boolean showSeqFeatures;
 
@@ -93,6 +93,8 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
 
   private ArrayList<SequenceI> hiddenSequences;
 
+  private final static String TCOFFEE_SCORE = "TCoffeeScore";
+
   public JSONFile()
   {
     super();
@@ -183,7 +185,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
         jsonSeqPojo.setSeq(seq.getSequenceAsString());
         jsonAlignmentPojo.getSeqs().add(jsonSeqPojo);
       }
-      jsonAlignmentPojo.setGlobalColorScheme(globalColorScheme);
+      jsonAlignmentPojo.setGlobalColorScheme(globalColourScheme);
       jsonAlignmentPojo.getAppSettings().put("application", application);
       jsonAlignmentPojo.getAppSettings().put("version", version);
       jsonAlignmentPojo.getAppSettings().put("webStartUrl", webstartUrl);
@@ -212,6 +214,16 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
         jsonAlignmentPojo
                 .setAlignAnnotation(annotationToJsonPojo(annotations));
       }
+      else
+      {
+        // These color schemes require annotation, disable them if annotations
+        // are not exported
+        if (globalColourScheme.equalsIgnoreCase("RNA Helices")
+                || globalColourScheme.equalsIgnoreCase("T-COFFEE SCORES"))
+        {
+          jsonAlignmentPojo.setGlobalColorScheme("None");
+        }
+      }
 
       if (exportSettings.isExportFeatures())
       {
@@ -363,6 +375,26 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
       AlignmentAnnotationPojo alignAnnotPojo = new AlignmentAnnotationPojo();
       alignAnnotPojo.setDescription(annot.description);
       alignAnnotPojo.setLabel(annot.label);
+      if (!Double.isNaN(annot.score))
+      {
+        alignAnnotPojo.setScore(annot.score);
+      }
+      alignAnnotPojo.setCalcId(annot.getCalcId());
+      alignAnnotPojo.setGraphType(annot.graph);
+
+      AnnotationDisplaySettingPojo annotSetting = new AnnotationDisplaySettingPojo();
+      annotSetting.setBelowAlignment(annot.belowAlignment);
+      annotSetting.setCentreColLabels(annot.centreColLabels);
+      annotSetting.setScaleColLabel(annot.scaleColLabel);
+      annotSetting.setShowAllColLabels(annot.showAllColLabels);
+      annotSetting.setVisible(annot.visible);
+      annotSetting.setHasIcon(annot.hasIcons);
+      alignAnnotPojo.setAnnotationSettings(annotSetting);
+      SequenceI refSeq = annot.sequenceRef;
+      if (refSeq != null)
+      {
+        alignAnnotPojo.setSequenceRef(String.valueOf(refSeq.hashCode()));
+      }
       for (Annotation annotation : annot.annotations)
       {
         AnnotationPojo annotationPojo = new AnnotationPojo();
@@ -372,12 +404,28 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
           annotationPojo.setValue(annotation.value);
           annotationPojo
                   .setSecondaryStructure(annotation.secondaryStructure);
-          annotationPojo.setDisplayCharacter(annotation.displayCharacter);
+          String displayChar = annotation.displayCharacter == null ? null
+                  : annotation.displayCharacter;
+          // System.out.println("--------------------->[" + displayChar + "]");
+          annotationPojo.setDisplayCharacter(displayChar);
+          if (annotation.colour != null)
+          {
+            annotationPojo.setColour(jalview.util.Format
+                    .getHexString(annotation.colour));
+          }
           alignAnnotPojo.getAnnotations().add(annotationPojo);
         }
         else
         {
-          alignAnnotPojo.getAnnotations().add(annotationPojo);
+          if (annot.getCalcId() != null
+                  && annot.getCalcId().equalsIgnoreCase(TCOFFEE_SCORE))
+          {
+            // do nothing
+          }
+          else
+          {
+            alignAnnotPojo.getAnnotations().add(annotationPojo);
+          }
         }
       }
       jsonAnnotations.add(alignAnnotPojo);
@@ -405,11 +453,10 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
 
       if (jvSettingsJsonObj != null)
       {
-        String jsColourScheme = (String) jvSettingsJsonObj
+        globalColourScheme = (String) jvSettingsJsonObj
                 .get("globalColorScheme");
         Boolean showFeatures = Boolean.valueOf(jvSettingsJsonObj.get(
                 "showSeqFeatures").toString());
-        setColourScheme(getJalviewColorScheme(jsColourScheme));
         setShowSeqFeatures(showFeatures);
         parseHiddenSeqRefsAsList(jvSettingsJsonObj);
         parseHiddenCols(jvSettingsJsonObj);
@@ -435,6 +482,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
         seqs.add(seq);
         seqMap.put(seqUniqueId, seq);
       }
+
       parseFeatures(jsonSeqArray);
 
       for (Iterator<JSONObject> seqGrpIter = seqGrpJsonArray.iterator(); seqGrpIter
@@ -472,10 +520,10 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
             }
           }
         }
-        ColourSchemeI grpColourScheme = getJalviewColorScheme(colourScheme);
-        SequenceGroup seqGrp = new SequenceGroup(grpSeqs, grpName,
-                grpColourScheme, displayBoxes, displayText, colourText,
-                startRes, endRes);
+        SequenceGroup seqGrp = new SequenceGroup(grpSeqs, grpName, null,
+                displayBoxes, displayText, colourText, startRes, endRes);
+        seqGrp.cs = ColourSchemeMapper.getJalviewColourScheme(colourScheme,
+                seqGrp);
         seqGrp.setShowNonconserved(showNonconserved);
         seqGrp.setDescription(description);
         this.seqGroups.add(seqGrp);
@@ -503,13 +551,20 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
                     .valueOf(annot.get("value").toString());
             String desc = annot.get("description") == null ? null : annot
                     .get("description").toString();
-
-            char ss = annot.get("secondaryStructure") == null ? ' ' : annot
+            char ss = annot.get("secondaryStructure") == null
+                    || annot.get("secondaryStructure").toString()
+                            .equalsIgnoreCase("u0000") ? ' ' : annot
                     .get("secondaryStructure").toString().charAt(0);
             String displayChar = annot.get("displayCharacter") == null ? ""
                     : annot.get("displayCharacter").toString();
 
             annotations[count] = new Annotation(displayChar, desc, ss, val);
+            if (annot.get("colour") != null)
+            {
+              Color color = UserColourScheme.getColourFromString(annot.get(
+                      "colour").toString());
+              annotations[count].colour = color;
+            }
           }
           ++count;
         }
@@ -517,9 +572,65 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
         AlignmentAnnotation alignAnnot = new AlignmentAnnotation(alAnnot
                 .get("label").toString(), alAnnot.get("description")
                 .toString(), annotations);
+        alignAnnot.graph = (alAnnot.get("graphType") == null) ? 0 : Integer
+                .valueOf(alAnnot.get("graphType").toString());
+
+        JSONObject diplaySettings = (JSONObject) alAnnot
+                .get("annotationSettings");
+        if (diplaySettings != null)
+        {
+
+          alignAnnot.scaleColLabel = (diplaySettings.get("scaleColLabel") == null) ? false
+                  : Boolean.valueOf(diplaySettings.get("scaleColLabel")
+                          .toString());
+          alignAnnot.showAllColLabels = (diplaySettings
+                  .get("showAllColLabels") == null) ? true : Boolean
+                  .valueOf(diplaySettings.get("showAllColLabels")
+                          .toString());
+          alignAnnot.centreColLabels = (diplaySettings
+                  .get("centreColLabels") == null) ? true
+                  : Boolean.valueOf(diplaySettings.get("centreColLabels")
+                          .toString());
+          alignAnnot.belowAlignment = (diplaySettings.get("belowAlignment") == null) ? false
+                  : Boolean.valueOf(diplaySettings.get("belowAlignment")
+                          .toString());
+          alignAnnot.visible = (diplaySettings.get("visible") == null) ? true
+                  : Boolean.valueOf(diplaySettings.get("visible")
+                          .toString());
+          alignAnnot.hasIcons = (diplaySettings.get("hasIcon") == null) ? true
+                  : Boolean.valueOf(diplaySettings.get("hasIcon")
+                          .toString());
+
+        }
+        if (alAnnot.get("score") != null)
+        {
+          alignAnnot.score = Double
+                  .valueOf(alAnnot.get("score").toString());
+        }
+
+        String calcId = (alAnnot.get("calcId") == null) ? "" : alAnnot.get(
+                "calcId").toString();
+        alignAnnot.setCalcId(calcId);
+        String seqHash = (alAnnot.get("sequenceRef") != null) ? alAnnot
+                .get("sequenceRef").toString() : null;
+
+        Sequence sequence = (seqHash != null) ? seqMap.get(seqHash) : null;
+        if (sequence != null)
+        {
+          alignAnnot.sequenceRef = sequence;
+          sequence.addAlignmentAnnotation(alignAnnot);
+          if (alignAnnot.label.equalsIgnoreCase("T-COFFEE"))
+          {
+            alignAnnot.createSequenceMapping(sequence, sequence.getStart(),
+                    false);
+            sequence.addAlignmentAnnotation(alignAnnot);
+            alignAnnot.adjustForAlignment();
+          }
+        }
+        alignAnnot.validateRangeAndDisplay();
         this.annotations.add(alignAnnot);
-      }
 
+      }
     } catch (Exception e)
     {
       e.printStackTrace();
@@ -599,40 +710,15 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
     }
   }
 
-  public static ColourSchemeI getJalviewColorScheme(
-          String bioJsColourSchemeName)
-  {
-    ColourSchemeI jalviewColor = null;
-    for (JalviewBioJsColorSchemeMapper cs : JalviewBioJsColorSchemeMapper
-            .values())
-    {
-      if (cs.getBioJsName().equalsIgnoreCase(bioJsColourSchemeName))
-      {
-        jalviewColor = cs.getJvColourScheme();
-        break;
-      }
-    }
-    return jalviewColor;
-  }
-
-  public String getGlobalColorScheme()
-  {
-    return globalColorScheme;
-  }
-
-  public void setGlobalColorScheme(String globalColorScheme)
-  {
-    this.globalColorScheme = globalColorScheme;
-  }
-
-  public ColourSchemeI getColourScheme()
+  @Override
+  public String getGlobalColourScheme()
   {
-    return colourScheme;
+    return globalColourScheme;
   }
 
-  public void setColourScheme(ColourSchemeI colourScheme)
+  public void setGlobalColorScheme(String globalColourScheme)
   {
-    this.colourScheme = colourScheme;
+    this.globalColourScheme = globalColourScheme;
   }
 
   @Override
@@ -646,8 +732,13 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
     this.displayedFeatures = displayedFeatures;
   }
 
+  @Override
   public void configureForView(AlignmentViewPanel avpanel)
   {
+    if (avpanel == null)
+    {
+      return;
+    }
     super.configureForView(avpanel);
     AlignViewportI viewport = avpanel.getAlignViewport();
     AlignmentI alignment = viewport.getAlignment();
@@ -657,24 +748,24 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
     fr = avpanel.cloneFeatureRenderer();
 
     // Add non auto calculated annotation to AlignFile
-    for (AlignmentAnnotation annot : annots)
+    if (annots != null)
     {
-      if (annot != null && !annot.autoCalculated)
+      for (AlignmentAnnotation annot : annots)
       {
-        if (!annot.visible)
+        if (annot != null && !annot.autoCalculated)
         {
-          continue;
+          annotations.add(annot);
         }
-        annotations.add(annot);
       }
     }
-    globalColorScheme = ColourSchemeProperty.getColourName(viewport
+    globalColourScheme = ColourSchemeProperty.getColourName(viewport
             .getGlobalColourScheme());
     setDisplayedFeatures(viewport.getFeaturesDisplayed());
     showSeqFeatures = viewport.isShowSequenceFeatures();
 
   }
 
+  @Override
   public boolean isShowSeqFeatures()
   {
     return showSeqFeatures;
@@ -695,6 +786,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
     return hiddenColumns;
   }
 
+  @Override
   public ColumnSelection getColumnSelection()
   {
     return columnSelection;
@@ -705,6 +797,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
     this.columnSelection = columnSelection;
   }
 
+  @Override
   public SequenceI[] getHiddenSequences()
   {
     if (hiddenSequences == null || hiddenSequences.isEmpty())
@@ -784,4 +877,19 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
       this.exportJalviewSettings = exportJalviewSettings;
     }
   }
+
+  /**
+   * Returns a descriptor for suitable feature display settings with
+   * <ul>
+   * <li>ResNums or insertions features visible</li>
+   * <li>insertions features coloured red</li>
+   * <li>ResNum features coloured by label</li>
+   * <li>Insertions displayed above (on top of) ResNums</li>
+   * </ul>
+   */
+  @Override
+  public FeatureSettingsModelI getFeatureColourScheme()
+  {
+    return new PDBFeatureSettings();
+  }
 }
diff --git a/src/jalview/io/JalviewFileChooser.java b/src/jalview/io/JalviewFileChooser.java
index 951b998..414738b 100644
--- a/src/jalview/io/JalviewFileChooser.java
+++ b/src/jalview/io/JalviewFileChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  *
  * This file is part of Jalview.
  *
diff --git a/src/jalview/io/JalviewFileFilter.java b/src/jalview/io/JalviewFileFilter.java
index 8d6038b..fb76b95 100644
--- a/src/jalview/io/JalviewFileFilter.java
+++ b/src/jalview/io/JalviewFileFilter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/JalviewFileView.java b/src/jalview/io/JalviewFileView.java
index b7f3201..fa30ba8 100644
--- a/src/jalview/io/JalviewFileView.java
+++ b/src/jalview/io/JalviewFileView.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/JnetAnnotationMaker.java b/src/jalview/io/JnetAnnotationMaker.java
index 5192454..e921451 100644
--- a/src/jalview/io/JnetAnnotationMaker.java
+++ b/src/jalview/io/JnetAnnotationMaker.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -192,13 +192,13 @@ public class JnetAnnotationMaker
           if (id.equals("JNETCONF"))
           {
             annot = new AlignmentAnnotation(preds[i].getName(),
-                    "JNet Output", annotations, 0f, 10f,
+                    "JPred Output", annotations, 0f, 10f,
                     AlignmentAnnotation.BAR_GRAPH);
           }
           else
           {
             annot = new AlignmentAnnotation(preds[i].getName(),
-                    "JNet Output", annotations);
+                    "JPred Output", annotations);
           }
 
           if (seqRef != null)
diff --git a/src/jalview/io/MSFfile.java b/src/jalview/io/MSFfile.java
index 67ace7c..0fb03c0 100644
--- a/src/jalview/io/MSFfile.java
+++ b/src/jalview/io/MSFfile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,12 +22,14 @@ package jalview.io;
 
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
+import jalview.util.Comparison;
 import jalview.util.Format;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.StringTokenizer;
-import java.util.Vector;
 
 /**
  * DOCUMENT ME!
@@ -67,23 +69,23 @@ public class MSFfile extends AlignFile
   }
 
   /**
-   * DOCUMENT ME!
+   * Read and parse MSF sequence data
    */
+  @Override
   public void parse() throws IOException
   {
-    int i = 0;
     boolean seqFlag = false;
-    String key = new String();
-    Vector headers = new Vector();
-    Hashtable seqhash = new Hashtable();
-    String line;
+    List<String> headers = new ArrayList<String>();
+    Hashtable<String, StringBuilder> seqhash = new Hashtable<String, StringBuilder>();
 
     try
     {
+      String line;
       while ((line = nextLine()) != null)
       {
         StringTokenizer str = new StringTokenizer(line);
 
+        String key = null;
         while (str.hasMoreTokens())
         {
           String inStr = str.nextToken();
@@ -92,31 +94,31 @@ public class MSFfile extends AlignFile
           if (inStr.indexOf("Name:") != -1)
           {
             key = str.nextToken();
-            headers.addElement(key);
+            headers.add(key);
           }
 
-          // if line has // set SeqFlag to 1 so we know sequences are coming
+          // if line has // set SeqFlag so we know sequences are coming
           if (inStr.indexOf("//") != -1)
           {
             seqFlag = true;
           }
 
           // Process lines as sequence lines if seqFlag is set
-          if ((inStr.indexOf("//") == -1) && (seqFlag == true))
+          if ((inStr.indexOf("//") == -1) && seqFlag)
           {
-            // seqeunce id is the first field
+            // sequence id is the first field
             key = inStr;
 
-            StringBuffer tempseq;
+            StringBuilder tempseq;
 
             // Get sequence from hash if it exists
             if (seqhash.containsKey(key))
             {
-              tempseq = (StringBuffer) seqhash.get(key);
+              tempseq = seqhash.get(key);
             }
             else
             {
-              tempseq = new StringBuffer();
+              tempseq = new StringBuilder(64);
               seqhash.put(key, tempseq);
             }
 
@@ -124,7 +126,8 @@ public class MSFfile extends AlignFile
             while (str.hasMoreTokens())
             {
               // append the word to the sequence
-              tempseq.append(str.nextToken());
+              String sequenceBlock = str.nextToken();
+              tempseq.append(sequenceBlock);
             }
           }
         }
@@ -138,11 +141,11 @@ public class MSFfile extends AlignFile
     this.noSeqs = headers.size();
 
     // Add sequences to the hash
-    for (i = 0; i < headers.size(); i++)
+    for (int i = 0; i < headers.size(); i++)
     {
-      if (seqhash.get(headers.elementAt(i)) != null)
+      if (seqhash.get(headers.get(i)) != null)
       {
-        String head = headers.elementAt(i).toString();
+        String head = headers.get(i);
         String seq = seqhash.get(head).toString();
 
         if (maxLength < head.length())
@@ -150,8 +153,11 @@ public class MSFfile extends AlignFile
           maxLength = head.length();
         }
 
-        // Replace ~ with a sensible gap character
-        seq = seq.replace('~', '-');
+        /*
+         * replace ~ (leading/trailing positions) with the gap character;
+         * use '.' as this is the internal gap character required by MSF
+         */
+        seq = seq.replace('~', '.');
 
         Sequence newSeq = parseId(head);
 
@@ -162,7 +168,7 @@ public class MSFfile extends AlignFile
       else
       {
         System.err.println("MSFFile Parser: Can't find sequence for "
-                + headers.elementAt(i));
+                + headers.get(i));
       }
     }
   }
@@ -210,15 +216,16 @@ public class MSFfile extends AlignFile
    * 
    * @return DOCUMENT ME!
    */
-  public String print(SequenceI[] seqs)
+  public String print(SequenceI[] sqs)
   {
 
-    boolean is_NA = jalview.util.Comparison.isNucleotide(seqs);
+    boolean is_NA = Comparison.isNucleotide(sqs);
 
-    SequenceI[] s = new SequenceI[seqs.length];
+    SequenceI[] s = new SequenceI[sqs.length];
 
-    StringBuffer out = new StringBuffer("!!" + (is_NA ? "NA" : "AA")
-            + "_MULTIPLE_ALIGNMENT 1.0");
+    StringBuilder out = new StringBuilder(256);
+    out.append("!!").append(is_NA ? "NA" : "AA")
+            .append("_MULTIPLE_ALIGNMENT 1.0");
     // TODO: JBPNote : Jalview doesn't remember NA or AA yet.
     out.append(newline);
     out.append(newline);
@@ -226,14 +233,15 @@ public class MSFfile extends AlignFile
     int maxid = 0;
     int i = 0;
 
-    while ((i < seqs.length) && (seqs[i] != null))
+    while ((i < sqs.length) && (sqs[i] != null))
     {
-      // Replace all internal gaps with . and external spaces with ~
-      s[i] = new Sequence(seqs[i].getName(), seqs[i].getSequenceAsString()
-              .replace('-', '.'), seqs[i].getStart(), seqs[i].getEnd());
+      /*
+       * modify to MSF format: uses '.' for internal gaps, 
+       * and '~' for leading or trailing gaps
+       */
+      String seqString = sqs[i].getSequenceAsString().replace('-', '.');
 
-      StringBuffer sb = new StringBuffer();
-      sb.append(s[i].getSequence());
+      StringBuilder sb = new StringBuilder(seqString);
 
       for (int ii = 0; ii < sb.length(); ii++)
       {
@@ -258,12 +266,12 @@ public class MSFfile extends AlignFile
           break;
         }
       }
+      s[i] = new Sequence(sqs[i].getName(), sb.toString(),
+              sqs[i].getStart(), sqs[i].getEnd());
 
-      s[i].setSequence(sb.toString());
-
-      if (s[i].getSequence().length > max)
+      if (sb.length() > max)
       {
-        max = s[i].getSequence().length;
+        max = sb.length();
       }
 
       i++;
@@ -343,12 +351,7 @@ public class MSFfile extends AlignFile
     out.append(newline);
     int len = 50;
 
-    int nochunks = (max / len) + 1;
-
-    if ((max % len) == 0)
-    {
-      nochunks--;
-    }
+    int nochunks = (max / len) + (max % len > 0 ? 1 : 0);
 
     for (i = 0; i < nochunks; i++)
     {
@@ -410,6 +413,7 @@ public class MSFfile extends AlignFile
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public String print()
   {
     return print(getSeqsAsArray());
diff --git a/src/jalview/io/MatrixFile.java b/src/jalview/io/MatrixFile.java
index b8f46f2..54e2886 100644
--- a/src/jalview/io/MatrixFile.java
+++ b/src/jalview/io/MatrixFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/ModellerDescription.java b/src/jalview/io/ModellerDescription.java
index a7a1efa..7784c1e 100644
--- a/src/jalview/io/ModellerDescription.java
+++ b/src/jalview/io/ModellerDescription.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -268,10 +268,10 @@ public class ModellerDescription
       // sets the local reference field
       int t = 0; // sequence
       if (seq.getDatasetSequence() != null
-              && seq.getDatasetSequence().getDBRef() != null)
+              && seq.getDatasetSequence().getDBRefs() != null)
       {
         jalview.datamodel.DBRefEntry[] dbr = seq.getDatasetSequence()
-                .getDBRef();
+                .getDBRefs();
         int i, j;
         for (i = 0, j = dbr.length; i < j; i++)
         {
diff --git a/src/jalview/io/NewickFile.java b/src/jalview/io/NewickFile.java
index 800d51a..03d568d 100644
--- a/src/jalview/io/NewickFile.java
+++ b/src/jalview/io/NewickFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/PDBFeatureSettings.java b/src/jalview/io/PDBFeatureSettings.java
new file mode 100644
index 0000000..ecf9fe5
--- /dev/null
+++ b/src/jalview/io/PDBFeatureSettings.java
@@ -0,0 +1,85 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io;
+
+import jalview.api.FeatureColourI;
+import jalview.schemes.FeatureColour;
+import jalview.schemes.FeatureSettingsAdapter;
+
+import java.awt.Color;
+
+public class PDBFeatureSettings extends FeatureSettingsAdapter
+{
+  // TODO find one central place to define feature names
+  private static final String FEATURE_INSERTION = "INSERTION";
+
+  private static final String FEATURE_RES_NUM = "RESNUM";
+
+  @Override
+  public boolean isFeatureDisplayed(String type)
+  {
+    return type.equalsIgnoreCase(FEATURE_INSERTION)
+            || type.equalsIgnoreCase(FEATURE_RES_NUM);
+  }
+
+  @Override
+  public FeatureColourI getFeatureColour(String type)
+  {
+    if (type.equalsIgnoreCase(FEATURE_INSERTION))
+    {
+      return new FeatureColour()
+      {
+
+        @Override
+        public Color getColour()
+        {
+          return Color.RED;
+        }
+      };
+    }
+    return null;
+  }
+
+  /**
+   * Order to render insertion after ResNum
+   */
+  @Override
+  public int compare(String feature1, String feature2)
+  {
+    if (feature1.equalsIgnoreCase(FEATURE_INSERTION))
+    {
+      return +1;
+    }
+    if (feature2.equalsIgnoreCase(FEATURE_INSERTION))
+    {
+      return -1;
+    }
+    if (feature1.equalsIgnoreCase(FEATURE_RES_NUM))
+    {
+      return +1;
+    }
+    if (feature2.equalsIgnoreCase(FEATURE_RES_NUM))
+    {
+      return -1;
+    }
+    return 0;
+  }
+}
diff --git a/src/jalview/io/PIRFile.java b/src/jalview/io/PIRFile.java
index 4b3d02e..521c734 100644
--- a/src/jalview/io/PIRFile.java
+++ b/src/jalview/io/PIRFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -46,6 +46,7 @@ public class PIRFile extends AlignFile
     super(source);
   }
 
+  @Override
   public void parse() throws IOException
   {
     StringBuffer sequence;
@@ -100,6 +101,7 @@ public class PIRFile extends AlignFile
     }
   }
 
+  @Override
   public String print()
   {
     return print(getSeqsAsArray());
@@ -174,7 +176,8 @@ public class PIRFile extends AlignFile
           }
         }
       }
-      int nochunks = (seq.length() / len) + 1;
+      int nochunks = (seq.length() / len)
+              + (seq.length() % len > 0 ? 1 : 0);
 
       for (int j = 0; j < nochunks; j++)
       {
diff --git a/src/jalview/io/PfamFile.java b/src/jalview/io/PfamFile.java
index e696609..7979646 100644
--- a/src/jalview/io/PfamFile.java
+++ b/src/jalview/io/PfamFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,9 +26,8 @@ import jalview.util.Format;
 import jalview.util.MessageManager;
 
 import java.io.IOException;
-import java.util.Hashtable;
-import java.util.StringTokenizer;
-import java.util.Vector;
+import java.util.ArrayList;
+import java.util.HashMap;
 
 public class PfamFile extends AlignFile
 {
@@ -47,57 +46,70 @@ public class PfamFile extends AlignFile
     super(source);
   }
 
+  @Override
   public void initData()
   {
     super.initData();
   }
 
+  @Override
   public void parse() throws IOException
   {
     int i = 0;
     String line;
 
-    Hashtable seqhash = new Hashtable();
-    Vector headers = new Vector();
-
+    HashMap<String, StringBuffer> seqhash = new HashMap<String, StringBuffer>();
+    ArrayList<String> headers = new ArrayList<String>();
+    boolean useTabs = false;
+    int spces;
     while ((line = nextLine()) != null)
     {
-      if (line.indexOf(" ") != 0)
+      if (line.indexOf("#") == 0)
+      {
+        // skip comment lines
+        continue;
+      }
+      // locate first space or (if already checked), tab
+      if (useTabs)
+      {
+        spces = line.indexOf("\t");
+      }
+      else
       {
-        if (line.indexOf("#") != 0)
+        spces = line.indexOf(" ");
+        // check to see if we ought to split on tabs instead.
+        if (!useTabs && spces == -1)
         {
-          // TODO: verify pfam format requires spaces and not tab characters -
-          // if not upgrade to use stevesoft regex and look for whitespace.
-          StringTokenizer str = new StringTokenizer(line, " ");
-          String id = "";
-
-          if (str.hasMoreTokens())
-          {
-            id = str.nextToken();
-
-            StringBuffer tempseq;
-
-            if (seqhash.containsKey(id))
-            {
-              tempseq = (StringBuffer) seqhash.get(id);
-            }
-            else
-            {
-              tempseq = new StringBuffer();
-              seqhash.put(id, tempseq);
-            }
-
-            if (!(headers.contains(id)))
-            {
-              headers.addElement(id);
-            }
-            if (str.hasMoreTokens())
-            {
-              tempseq.append(str.nextToken());
-            }
-          }
+          useTabs = true;
+          spces = line.indexOf("\t");
         }
       }
+      if (spces <= 0)
+      {
+        // no sequence data to split on
+        continue;
+      }
+      String id = line.substring(0, spces);
+      StringBuffer tempseq;
+
+      if (seqhash.containsKey(id))
+      {
+        tempseq = seqhash.get(id);
+      }
+      else
+      {
+        tempseq = new StringBuffer();
+        seqhash.put(id, tempseq);
+      }
+
+      if (!(headers.contains(id)))
+      {
+        headers.add(id);
+      }
+      if (spces + 1 < line.length())
+      {
+        tempseq.append(line.substring(spces + 1).trim());
+      }
     }
 
     this.noSeqs = headers.size();
@@ -110,23 +122,22 @@ public class PfamFile extends AlignFile
 
     for (i = 0; i < headers.size(); i++)
     {
-      if (seqhash.get(headers.elementAt(i)) != null)
+      if (seqhash.get(headers.get(i)) != null)
       {
-        if (maxLength < seqhash.get(headers.elementAt(i)).toString()
-                .length())
+        if (maxLength < seqhash.get(headers.get(i)).toString().length())
         {
-          maxLength = seqhash.get(headers.elementAt(i)).toString().length();
+          maxLength = seqhash.get(headers.get(i)).toString().length();
         }
 
-        Sequence newSeq = parseId(headers.elementAt(i).toString());
-        newSeq.setSequence(seqhash.get(headers.elementAt(i).toString())
+        Sequence newSeq = parseId(headers.get(i).toString());
+        newSeq.setSequence(seqhash.get(headers.get(i).toString())
                 .toString());
         seqs.addElement(newSeq);
       }
       else
       {
         System.err.println("PFAM File reader: Can't find sequence for "
-                + headers.elementAt(i));
+                + headers.get(i));
       }
     }
   }
@@ -178,6 +189,7 @@ public class PfamFile extends AlignFile
     return out.toString();
   }
 
+  @Override
   public String print()
   {
     return print(getSeqsAsArray());
diff --git a/src/jalview/io/PhylipFile.java b/src/jalview/io/PhylipFile.java
index 656e103..2cf8216 100644
--- a/src/jalview/io/PhylipFile.java
+++ b/src/jalview/io/PhylipFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/PileUpfile.java b/src/jalview/io/PileUpfile.java
index e826687..e7338b4 100644
--- a/src/jalview/io/PileUpfile.java
+++ b/src/jalview/io/PileUpfile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -76,11 +76,13 @@ public class PileUpfile extends MSFfile
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public String print()
   {
     return print(getSeqsAsArray());
   }
 
+  @Override
   public String print(SequenceI[] s)
   {
     StringBuffer out = new StringBuffer("PileUp");
@@ -141,12 +143,7 @@ public class PileUpfile extends MSFfile
 
     int len = 50;
 
-    int nochunks = (max / len) + 1;
-
-    if ((max % len) == 0)
-    {
-      nochunks--;
-    }
+    int nochunks = (max / len) + (max % len > 0 ? 1 : 0);
 
     for (i = 0; i < nochunks; i++)
     {
diff --git a/src/jalview/io/RnamlFile.java b/src/jalview/io/RnamlFile.java
index 2948c92..11de5ce 100644
--- a/src/jalview/io/RnamlFile.java
+++ b/src/jalview/io/RnamlFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,7 @@
  */
 package jalview.io;
 
-import jalview.analysis.SecStrConsensus.SimpleBP;
+import jalview.analysis.Rna;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.Sequence;
@@ -32,6 +32,7 @@ import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 
 import com.stevesoft.pat.Regex;
 
@@ -79,6 +80,7 @@ public class RnamlFile extends AlignFile
    * 
    * @see jalview.io.AlignFile#parse()
    */
+  @Override
   public void parse() throws IOException
   {
     if (System.getProperty("java.version").indexOf("1.6") > -1
@@ -134,10 +136,10 @@ public class RnamlFile extends AlignFile
 
     result = RNAFactory.loadSecStrRNAML(getReader());
 
-    ArrayList<ArrayList> allarray = new ArrayList();
-    ArrayList<ArrayList<SimpleBP>> BP = new ArrayList();
-    ArrayList strucinarray = new ArrayList();
-    SequenceI[] seqs = new SequenceI[result.size()];
+    // ArrayList<ArrayList> allarray = new ArrayList();
+    // ArrayList<ArrayList<SimpleBP>> BP = new ArrayList();
+    // ArrayList strucinarray = new ArrayList();
+    SequenceI[] sqs = new SequenceI[result.size()];
 
     for (int i = 0; i < result.size(); i++)
     {
@@ -157,9 +159,9 @@ public class RnamlFile extends AlignFile
           id += "." + i;
         }
       }
-      seqs[i] = new Sequence(id, seq, begin, end);
+      sqs[i] = new Sequence(id, seq, begin, end);
 
-      seqs[i].setEnd(seqs[i].findPosition(seqs[i].getLength()));
+      sqs[i].setEnd(sqs[i].findPosition(sqs[i].getLength()));
       String[] annot = new String[rna.length()];
       Annotation[] ann = new Annotation[rna.length()];
 
@@ -170,9 +172,8 @@ public class RnamlFile extends AlignFile
       }
       for (int k = 0; k < rna.length(); k++)
       {
-        ann[k] = new Annotation(annot[k], "",
-                jalview.schemes.ResidueProperties.getRNASecStrucState(
-                        annot[k]).charAt(0), 0f);
+        ann[k] = new Annotation(annot[k], "", Rna.getRNASecStrucState(
+                annot[k]).charAt(0), 0f);
       }
 
       AlignmentAnnotation align = new AlignmentAnnotation(
@@ -181,17 +182,17 @@ public class RnamlFile extends AlignFile
                       + current.getID()
                       : "", ann);
 
-      seqs[i].addAlignmentAnnotation(align);
-      seqs[i].setRNA(result.get(i));
+      sqs[i].addAlignmentAnnotation(align);
+      sqs[i].setRNA(result.get(i));
 
-      allarray.add(strucinarray);
+      // allarray.add(strucinarray);
 
       annotations.addElement(align);
-      BP.add(align.bps);
+      // BP.add(align.bps);
 
     }
 
-    setSeqs(seqs);
+    setSeqs(sqs);
   }
 
   public static String print(SequenceI[] s)
@@ -199,13 +200,14 @@ public class RnamlFile extends AlignFile
     return "not yet implemented";
   }
 
+  @Override
   public String print()
   {
     System.out.print("print :");
     return print(getSeqsAsArray());
   }
 
-  public ArrayList getRNA()
+  public List<RNA> getRNA()
   {
     return result;
   }
diff --git a/src/jalview/io/SequenceAnnotationReport.java b/src/jalview/io/SequenceAnnotationReport.java
index 8c4a332..52cf777 100644
--- a/src/jalview/io/SequenceAnnotationReport.java
+++ b/src/jalview/io/SequenceAnnotationReport.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,13 +21,19 @@
 package jalview.io;
 
 import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.io.gff.GffConstants;
+import jalview.util.MessageManager;
 import jalview.util.UrlLink;
 
-import java.util.ArrayList;
-import java.util.Hashtable;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * generate HTML reports for a sequence
@@ -36,309 +42,313 @@ import java.util.List;
  */
 public class SequenceAnnotationReport
 {
+  private static final String COMMA = ",";
+
+  private static final String ELLIPSIS = "...";
+
+  private static final int MAX_REFS_PER_SOURCE = 4;
+
+  private static final int MAX_SOURCES = 40;
+
+  private static final String[][] PRIMARY_SOURCES = new String[][] {
+      DBRefSource.CODINGDBS, DBRefSource.DNACODINGDBS,
+      DBRefSource.PROTEINDBS };
+
   final String linkImageURL;
 
+  /*
+   * Comparator to order DBRefEntry by Source + accession id (case-insensitive)
+   */
+  private static Comparator<DBRefEntry> comparator = new Comparator<DBRefEntry>()
+  {
+
+    @Override
+    public int compare(DBRefEntry ref1, DBRefEntry ref2)
+    {
+      String s1 = ref1.getSource();
+      String s2 = ref2.getSource();
+      boolean s1Primary = isPrimarySource(s1);
+      boolean s2Primary = isPrimarySource(s2);
+      if (s1Primary && !s2Primary)
+      {
+        return -1;
+      }
+      if (!s1Primary && s2Primary)
+      {
+        return 1;
+      }
+      int comp = s1 == null ? -1 : (s2 == null ? 1 : s1
+              .compareToIgnoreCase(s2));
+      if (comp == 0)
+      {
+        String a1 = ref1.getAccessionId();
+        String a2 = ref2.getAccessionId();
+        comp = a1 == null ? -1 : (a2 == null ? 1 : a1
+                .compareToIgnoreCase(a2));
+      }
+      return comp;
+    }
+
+    private boolean isPrimarySource(String source)
+    {
+      for (String[] primary : PRIMARY_SOURCES)
+      {
+        for (String s : primary)
+        {
+          if (source.equals(s))
+          {
+            return true;
+          }
+        }
+      }
+      return false;
+    }
+  };
+
   public SequenceAnnotationReport(String linkImageURL)
   {
     this.linkImageURL = linkImageURL;
   }
 
   /**
-   * appends the features at rpos to the given stringbuffer ready for display in
-   * a tooltip
+   * Append text for the list of features to the tooltip
    * 
-   * @param tooltipText2
-   * @param linkImageURL
+   * @param sb
    * @param rpos
    * @param features
-   *          TODO refactor to Jalview 'utilities' somehow.
+   * @param minmax
    */
-  public void appendFeatures(final StringBuffer tooltipText2, int rpos,
-          List<SequenceFeature> features)
+  public void appendFeatures(final StringBuilder sb, int rpos,
+          List<SequenceFeature> features, Map<String, float[][]> minmax)
   {
-    appendFeatures(tooltipText2, rpos, features, null);
+    if (features != null)
+    {
+      for (SequenceFeature feature : features)
+      {
+        appendFeature(sb, rpos, minmax, feature);
+      }
+    }
   }
 
-  public void appendFeatures(final StringBuffer tooltipText2, int rpos,
-          List<SequenceFeature> features, Hashtable minmax)
+  /**
+   * Appends the feature at rpos to the given buffer
+   * 
+   * @param sb
+   * @param rpos
+   * @param minmax
+   * @param feature
+   */
+  void appendFeature(final StringBuilder sb, int rpos,
+          Map<String, float[][]> minmax, SequenceFeature feature)
   {
-    String tmpString;
-    if (features != null)
+    if (feature.isContactFeature())
     {
-      for (SequenceFeature feature : features)
+      if (feature.getBegin() == rpos || feature.getEnd() == rpos)
       {
-        if (feature.getType().equals("disulfide bond"))
+        if (sb.length() > 6)
         {
-          if (feature.getBegin() == rpos || feature.getEnd() == rpos)
-          {
-            if (tooltipText2.length() > 6)
-            {
-              tooltipText2.append("<br>");
-            }
-            tooltipText2.append("disulfide bond " + feature.getBegin()
-                    + ":" + feature.getEnd());
-          }
+          sb.append("<br>");
         }
-        else
+        sb.append(feature.getType()).append(" ").append(feature.getBegin())
+                .append(":")
+                .append(feature.getEnd());
+      }
+    }
+    else
+    {
+      if (sb.length() > 6)
+      {
+        sb.append("<br>");
+      }
+      // TODO: remove this hack to display link only features
+      boolean linkOnly = feature.getValue("linkonly") != null;
+      if (!linkOnly)
+      {
+        sb.append(feature.getType()).append(" ");
+        if (rpos != 0)
+        {
+          // we are marking a positional feature
+          sb.append(feature.begin);
+        }
+        if (feature.begin != feature.end)
         {
-          if (tooltipText2.length() > 6)
+          sb.append(" ").append(feature.end);
+        }
+
+        if (feature.getDescription() != null
+                && !feature.description.equals(feature.getType()))
+        {
+          String tmpString = feature.getDescription();
+          String tmp2up = tmpString.toUpperCase();
+          int startTag = tmp2up.indexOf("<HTML>");
+          if (startTag > -1)
           {
-            tooltipText2.append("<br>");
+            tmpString = tmpString.substring(startTag + 6);
+            tmp2up = tmp2up.substring(startTag + 6);
           }
-          // TODO: remove this hack to display link only features
-          boolean linkOnly = feature.getValue("linkonly") != null;
-          if (!linkOnly)
+          int endTag = tmp2up.indexOf("</BODY>");
+          if (endTag > -1)
           {
-            tooltipText2.append(feature.getType() + " ");
-            if (rpos != 0)
-            {
-              // we are marking a positional feature
-              tooltipText2.append(feature.begin);
-            }
-            if (feature.begin != feature.end)
-            {
-              tooltipText2.append(" " + feature.end);
-            }
+            tmpString = tmpString.substring(0, endTag);
+            tmp2up = tmp2up.substring(0, endTag);
+          }
+          endTag = tmp2up.indexOf("</HTML>");
+          if (endTag > -1)
+          {
+            tmpString = tmpString.substring(0, endTag);
+          }
 
-            if (feature.getDescription() != null
-                    && !feature.description.equals(feature.getType()))
+          if (startTag > -1)
+          {
+            sb.append("; ").append(tmpString);
+          }
+          else
+          {
+            if (tmpString.indexOf("<") > -1 || tmpString.indexOf(">") > -1)
             {
-              tmpString = feature.getDescription();
-              String tmp2up = tmpString.toUpperCase();
-              int startTag = tmp2up.indexOf("<HTML>");
-              if (startTag > -1)
-              {
-                tmpString = tmpString.substring(startTag + 6);
-                tmp2up = tmp2up.substring(startTag + 6);
-              }
-              int endTag = tmp2up.indexOf("</BODY>");
-              if (endTag > -1)
-              {
-                tmpString = tmpString.substring(0, endTag);
-                tmp2up = tmp2up.substring(0, endTag);
-              }
-              endTag = tmp2up.indexOf("</HTML>");
-              if (endTag > -1)
-              {
-                tmpString = tmpString.substring(0, endTag);
-              }
-
-              if (startTag > -1)
-              {
-                tooltipText2.append("; " + tmpString);
-              }
-              else
-              {
-                if (tmpString.indexOf("<") > -1
-                        || tmpString.indexOf(">") > -1)
-                {
-                  // The description does not specify html is to
-                  // be used, so we must remove < > symbols
-                  tmpString = tmpString.replaceAll("<", "<");
-                  tmpString = tmpString.replaceAll(">", ">");
-
-                  tooltipText2.append("; ");
-                  tooltipText2.append(tmpString);
+              // The description does not specify html is to
+              // be used, so we must remove < > symbols
+              tmpString = tmpString.replaceAll("<", "<");
+              tmpString = tmpString.replaceAll(">", ">");
 
-                }
-                else
-                {
-                  tooltipText2.append("; " + tmpString);
-                }
-              }
+              sb.append("; ");
+              sb.append(tmpString);
             }
-            // check score should be shown
-            if (!Float.isNaN(feature.getScore()))
+            else
             {
-              float[][] rng = (minmax == null) ? null : ((float[][]) minmax
-                      .get(feature.getType()));
-              if (rng != null && rng[0] != null && rng[0][0] != rng[0][1])
-              {
-                tooltipText2.append(" Score=" + feature.getScore());
-              }
-            }
-            if (feature.getValue("status") != null)
-            {
-              String status = feature.getValue("status").toString();
-              if (status.length() > 0)
-              {
-                tooltipText2.append("; (" + feature.getValue("status")
-                        + ")");
-              }
+              sb.append("; ").append(tmpString);
             }
           }
         }
-        if (feature.links != null)
+        // check score should be shown
+        if (!Float.isNaN(feature.getScore()))
         {
-          if (linkImageURL != null)
+          float[][] rng = (minmax == null) ? null : minmax.get(feature
+                  .getType());
+          if (rng != null && rng[0] != null && rng[0][0] != rng[0][1])
           {
-            tooltipText2.append(" <img src=\"" + linkImageURL + "\">");
+            sb.append(" Score=").append(String.valueOf(feature.getScore()));
           }
-          else
-          {
-            for (String urlstring : feature.links)
-            {
-              try
-              {
-                for (String[] urllink : createLinksFrom(null, urlstring))
-                {
-                  tooltipText2.append("<br/> <a href=\""
-                          + urllink[3]
-                          + "\" target=\""
-                          + urllink[0]
-                          + "\">"
-                          + (urllink[0].toLowerCase().equals(
-                                  urllink[1].toLowerCase()) ? urllink[0]
-                                  : (urllink[0] + ":" + urllink[1]))
-                          + "</a></br>");
-                }
-              } catch (Exception x)
-              {
-                System.err.println("problem when creating links from "
-                        + urlstring);
-                x.printStackTrace();
-              }
-            }
-          }
-
+        }
+        String status = (String) feature.getValue("status");
+        if (status != null && status.length() > 0)
+        {
+          sb.append("; (").append(status).append(")");
+        }
+        String clinSig = (String) feature
+                .getValue(GffConstants.CLINICAL_SIGNIFICANCE);
+        if (clinSig != null)
+        {
+          sb.append("; ").append(clinSig);
         }
       }
     }
   }
 
   /**
+   * Format and appends any hyperlinks for the sequence feature to the string
+   * buffer
    * 
-   * @param seq
-   * @param link
-   * @return String[][] { String[] { link target, link label, dynamic component
-   *         inserted (if any), url }}
+   * @param sb
+   * @param feature
    */
-  public String[][] createLinksFrom(SequenceI seq, String link)
+  void appendLinks(final StringBuffer sb, SequenceFeature feature)
   {
-    ArrayList<String[]> urlSets = new ArrayList<String[]>();
-    ArrayList<String> uniques = new ArrayList<String>();
-    UrlLink urlLink = new UrlLink(link);
-    if (!urlLink.isValid())
+    if (feature.links != null)
     {
-      System.err.println(urlLink.getInvalidMessage());
-      return null;
-    }
-    final String target = urlLink.getTarget(); // link.substring(0,
-    // link.indexOf("|"));
-    final String label = urlLink.getLabel();
-    if (seq != null && urlLink.isDynamic())
-    {
-
-      // collect matching db-refs
-      DBRefEntry[] dbr = jalview.util.DBRefUtils.selectRefs(seq.getDBRef(),
-              new String[] { target });
-      // collect id string too
-      String id = seq.getName();
-      String descr = seq.getDescription();
-      if (descr != null && descr.length() < 1)
+      if (linkImageURL != null)
       {
-        descr = null;
-      }
-      if (dbr != null)
-      {
-        for (int r = 0; r < dbr.length; r++)
-        {
-          if (id != null && dbr[r].getAccessionId().equals(id))
-          {
-            // suppress duplicate link creation for the bare sequence ID
-            // string with this link
-            id = null;
-          }
-          // create Bare ID link for this RUL
-          String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(), true);
-          if (urls != null)
-          {
-            for (int u = 0; u < urls.length; u += 2)
-            {
-              String unq = urls[u] + "|" + urls[u + 1];
-              if (!uniques.contains(unq))
-              {
-                urlSets.add(new String[] { target, label, urls[u],
-                    urls[u + 1] });
-                uniques.add(unq);
-              }
-            }
-          }
-        }
+        sb.append(" <img src=\"" + linkImageURL + "\">");
       }
-      if (id != null)
+      else
       {
-        // create Bare ID link for this RUL
-        String[] urls = urlLink.makeUrls(id, true);
-        if (urls != null)
+        for (String urlstring : feature.links)
         {
-          for (int u = 0; u < urls.length; u += 2)
+          try
           {
-            String unq = urls[u] + "|" + urls[u + 1];
-            if (!uniques.contains(unq))
+            for (List<String> urllink : createLinksFrom(null, urlstring))
             {
-              urlSets.add(new String[] { target, label, urls[u],
-                  urls[u + 1] });
-              uniques.add(unq);
+              sb.append("<br/> <a href=\""
+                      + urllink.get(3)
+                      + "\" target=\""
+                      + urllink.get(0)
+                      + "\">"
+                      + (urllink.get(0).toLowerCase()
+                              .equals(urllink.get(1).toLowerCase()) ? urllink
+                              .get(0) : (urllink.get(0) + ":" + urllink
+                              .get(1)))
+                      + "</a></br>");
             }
-          }
-        }
-      }
-      if (descr != null && urlLink.getRegexReplace() != null)
-      {
-        // create link for this URL from description only if regex matches
-        String[] urls = urlLink.makeUrls(descr, true);
-        if (urls != null)
-        {
-          for (int u = 0; u < urls.length; u += 2)
+          } catch (Exception x)
           {
-            String unq = urls[u] + "|" + urls[u + 1];
-            if (!uniques.contains(unq))
-            {
-              urlSets.add(new String[] { target, label, urls[u],
-                  urls[u + 1] });
-              uniques.add(unq);
-            }
+            System.err.println("problem when creating links from "
+                    + urlstring);
+            x.printStackTrace();
           }
         }
       }
 
     }
-    else
+  }
+
+  /**
+   * 
+   * @param seq
+   * @param link
+   * @return Collection< List<String> > { List<String> { link target, link
+   *         label, dynamic component inserted (if any), url }}
+   */
+  Collection<List<String>> createLinksFrom(SequenceI seq, String link)
+  {
+    Map<String, List<String>> urlSets = new LinkedHashMap<String, List<String>>();
+    UrlLink urlLink = new UrlLink(link);
+    if (!urlLink.isValid())
     {
-      String unq = label + "|" + urlLink.getUrl_prefix();
-      if (!uniques.contains(unq))
-      {
-        uniques.add(unq);
-        // Add a non-dynamic link
-        urlSets.add(new String[] { target, label, null,
-            urlLink.getUrl_prefix() });
-      }
+      System.err.println(urlLink.getInvalidMessage());
+      return null;
     }
 
-    return urlSets.toArray(new String[][] {});
+    urlLink.createLinksFromSeq(seq, urlSets);
+
+    return urlSets.values();
   }
 
-  public void createSequenceAnnotationReport(final StringBuffer tip,
+  public void createSequenceAnnotationReport(final StringBuilder tip,
           SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
-          Hashtable minmax)
+          Map<String, float[][]> minmax)
   {
     createSequenceAnnotationReport(tip, sequence, showDbRefs, showNpFeats,
-            true, minmax);
+            minmax, false);
   }
 
-  public void createSequenceAnnotationReport(final StringBuffer tip,
+  /**
+   * Builds an html formatted report of sequence details and appends it to the
+   * provided buffer.
+   * 
+   * @param sb
+   *          buffer to append report to
+   * @param sequence
+   *          the sequence the report is for
+   * @param showDbRefs
+   *          whether to include database references for the sequence
+   * @param showNpFeats
+   *          whether to include non-positional sequence features
+   * @param minmax
+   * @param summary
+   * @return
+   */
+  int createSequenceAnnotationReport(final StringBuilder sb,
           SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
-          boolean tableWrap, Hashtable minmax)
+          Map<String, float[][]> minmax, boolean summary)
   {
     String tmp;
-    tip.append("<i>");
+    sb.append("<i>");
 
     int maxWidth = 0;
     if (sequence.getDescription() != null)
     {
       tmp = sequence.getDescription();
-      tip.append("<br>" + tmp);
+      sb.append("<br>").append(tmp);
       maxWidth = Math.max(maxWidth, tmp.length());
     }
     SequenceI ds = sequence;
@@ -346,19 +356,84 @@ public class SequenceAnnotationReport
     {
       ds = ds.getDatasetSequence();
     }
-    DBRefEntry[] dbrefs = ds.getDBRef();
+    DBRefEntry[] dbrefs = ds.getDBRefs();
     if (showDbRefs && dbrefs != null)
     {
-      for (int i = 0; i < dbrefs.length; i++)
+      // note this sorts the refs held on the sequence!
+      Arrays.sort(dbrefs, comparator);
+      boolean ellipsis = false;
+      String source = null;
+      String lastSource = null;
+      int countForSource = 0;
+      int sourceCount = 0;
+      boolean moreSources = false;
+      int lineLength = 0;
+
+      for (DBRefEntry ref : dbrefs)
       {
-        tip.append("<br>");
-        tmp = dbrefs[i].getSource() + " " + dbrefs[i].getAccessionId();
-        tip.append(tmp);
-        maxWidth = Math.max(maxWidth, tmp.length());
+        source = ref.getSource();
+        if (source == null)
+        {
+          // shouldn't happen
+          continue;
+        }
+        boolean sourceChanged = !source.equals(lastSource);
+        if (sourceChanged)
+        {
+          lineLength = 0;
+          countForSource = 0;
+          sourceCount++;
+        }
+        if (sourceCount > MAX_SOURCES && summary)
+        {
+          ellipsis = true;
+          moreSources = true;
+          break;
+        }
+        lastSource = source;
+        countForSource++;
+        if (countForSource == 1 || !summary)
+        {
+          sb.append("<br>");
+        }
+        if (countForSource <= MAX_REFS_PER_SOURCE || !summary)
+        {
+          String accessionId = ref.getAccessionId();
+          lineLength += accessionId.length() + 1;
+          if (countForSource > 1 && summary)
+          {
+            sb.append(", ").append(accessionId);
+            lineLength++;
+          }
+          else
+          {
+            sb.append(source).append(" ").append(accessionId);
+            lineLength += source.length();
+          }
+          maxWidth = Math.max(maxWidth, lineLength);
+        }
+        if (countForSource == MAX_REFS_PER_SOURCE && summary)
+        {
+          sb.append(COMMA).append(ELLIPSIS);
+          ellipsis = true;
+        }
+      }
+      if (moreSources)
+      {
+        sb.append("<br>").append(ELLIPSIS).append(COMMA).append(source)
+                .append(COMMA).append(ELLIPSIS);
+      }
+      if (ellipsis)
+      {
+        sb.append("<br>(");
+        sb.append(MessageManager.getString("label.output_seq_details"));
+        sb.append(")");
       }
     }
 
-    // ADD NON POSITIONAL SEQUENCE INFO
+    /*
+     * add non-positional features if wanted
+     */
     SequenceFeature[] features = sequence.getSequenceFeatures();
     if (showNpFeats && features != null)
     {
@@ -366,21 +441,29 @@ public class SequenceAnnotationReport
       {
         if (features[i].begin == 0 && features[i].end == 0)
         {
-          int sz = -tip.length();
-          List<SequenceFeature> tfeat = new ArrayList<SequenceFeature>();
-          tfeat.add(features[i]);
-          appendFeatures(tip, 0, tfeat, minmax);
-          sz += tip.length();
+          int sz = -sb.length();
+          appendFeature(sb, 0, minmax, features[i]);
+          sz += sb.length();
           maxWidth = Math.max(maxWidth, sz);
         }
       }
     }
+    sb.append("</i>");
+    return maxWidth;
+  }
 
-    if (tableWrap && maxWidth > 60)
+  public void createTooltipAnnotationReport(final StringBuilder tip,
+          SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
+          Map<String, float[][]> minmax)
+  {
+    int maxWidth = createSequenceAnnotationReport(tip, sequence,
+            showDbRefs, showNpFeats, minmax, true);
+
+    if (maxWidth > 60)
     {
-      tip.insert(0, "<table width=350 border=0><tr><td><i>");
-      tip.append("</i></td></tr></table>");
+      // ? not sure this serves any useful purpose
+      // tip.insert(0, "<table width=350 border=0><tr><td>");
+      // tip.append("</td></tr></table>");
     }
-
   }
 }
diff --git a/src/jalview/io/SimpleBlastFile.java b/src/jalview/io/SimpleBlastFile.java
index db5f8a9..5d9e29a 100644
--- a/src/jalview/io/SimpleBlastFile.java
+++ b/src/jalview/io/SimpleBlastFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/StockholmFile.java b/src/jalview/io/StockholmFile.java
index 0274abc..2440948 100644
--- a/src/jalview/io/StockholmFile.java
+++ b/src/jalview/io/StockholmFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -23,6 +23,7 @@
  */
 package jalview.io;
 
+import jalview.analysis.Rna;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
@@ -31,6 +32,7 @@ import jalview.datamodel.Mapping;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.schemes.ResidueProperties;
 import jalview.util.Format;
 import jalview.util.MessageManager;
 
@@ -72,8 +74,12 @@ import fr.orsay.lri.varna.models.rna.RNA;
  */
 public class StockholmFile extends AlignFile
 {
-  // static Logger logger = Logger.getLogger("jalview.io.StockholmFile");
-  protected ArrayList<RNA> result;
+  private static final Regex OPEN_PAREN = new Regex("(<|\\[)", "(");
+
+  private static final Regex CLOSE_PAREN = new Regex("(>|\\])", ")");
+
+  private static final Regex DETECT_BRACKETS = new Regex(
+          "(<|>|\\[|\\]|\\(|\\))");
 
   StringBuffer out; // output buffer
 
@@ -101,6 +107,7 @@ public class StockholmFile extends AlignFile
     super(source);
   }
 
+  @Override
   public void initData()
   {
     super.initData();
@@ -118,7 +125,7 @@ public class StockholmFile extends AlignFile
     fr = new FileReader(inFile);
 
     BufferedReader r = new BufferedReader(fr);
-    result = null;
+    List<RNA> result = null;
     try
     {
       result = RNAFactory.loadSecStrStockholm(r);
@@ -155,9 +162,8 @@ public class StockholmFile extends AlignFile
 
       for (int k = 0; k < rna.length(); k++)
       {
-        ann[k] = new Annotation(annot[k], "",
-                jalview.schemes.ResidueProperties.getRNASecStrucState(
-                        annot[k]).charAt(0), 0f);
+        ann[k] = new Annotation(annot[k], "", Rna.getRNASecStrucState(
+                annot[k]).charAt(0), 0f);
 
       }
       AlignmentAnnotation align = new AlignmentAnnotation("Sec. str.",
@@ -178,6 +184,7 @@ public class StockholmFile extends AlignFile
    * @throws IOException
    *           If there is an error with the input file
    */
+  @Override
   public void parse() throws IOException
   {
     StringBuffer treeString = new StringBuffer();
@@ -533,8 +540,7 @@ public class StockholmFile extends AlignFile
           }
           else
           {
-            // throw new IOException(MessageManager.formatMessage(
-            // "exception.error_parsing_line", new String[] { line }));
+            // throw new IOException("Error parsing " + line);
             System.err.println(">> missing annotation: " + line);
           }
         }
@@ -788,19 +794,13 @@ public class StockholmFile extends AlignFile
   }
 
   protected static AlignmentAnnotation parseAnnotationRow(
-          Vector annotation, String label, String annots)
+          Vector<AlignmentAnnotation> annotation, String label,
+          String annots)
   {
     String convert1, convert2 = null;
 
-    // Convert all bracket types to parentheses
-    Regex openparen = new Regex("(<|\\[)", "(");
-    Regex closeparen = new Regex("(>|\\])", ")");
-
-    // Detect if file is RNA by looking for bracket types
-    Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))");
-
-    convert1 = openparen.replaceAll(annots);
-    convert2 = closeparen.replaceAll(convert1);
+    convert1 = OPEN_PAREN.replaceAll(annots);
+    convert2 = CLOSE_PAREN.replaceAll(convert1);
     annots = convert2;
 
     String type = label;
@@ -827,15 +827,14 @@ public class StockholmFile extends AlignFile
       {
         // if (" .-_".indexOf(pos) == -1)
         {
-          if (detectbrackets.search(pos))
+          if (DETECT_BRACKETS.search(pos))
           {
-            ann.secondaryStructure = jalview.schemes.ResidueProperties
-                    .getRNASecStrucState(pos).charAt(0);
+            ann.secondaryStructure = Rna.getRNASecStrucState(pos).charAt(0);
           }
           else
           {
-            ann.secondaryStructure = jalview.schemes.ResidueProperties
-                    .getDssp3state(pos).charAt(0);
+            ann.secondaryStructure = ResidueProperties.getDssp3state(pos)
+                    .charAt(0);
           }
 
           if (ann.secondaryStructure == pos.charAt(0))
@@ -853,10 +852,10 @@ public class StockholmFile extends AlignFile
       els[i] = ann;
     }
     AlignmentAnnotation annot = null;
-    Enumeration e = annotation.elements();
+    Enumeration<AlignmentAnnotation> e = annotation.elements();
     while (e.hasMoreElements())
     {
-      annot = (AlignmentAnnotation) e.nextElement();
+      annot = e.nextElement();
       if (annot.label.equals(type))
       {
         break;
@@ -900,18 +899,18 @@ public class StockholmFile extends AlignFile
       {
         maxid = tmp.length();
       }
-      if (s[in].getDBRef() != null)
+      if (s[in].getDBRefs() != null)
       {
-        for (int idb = 0; idb < s[in].getDBRef().length; idb++)
+        for (int idb = 0; idb < s[in].getDBRefs().length; idb++)
         {
           if (dataRef == null)
           {
             dataRef = new Hashtable();
           }
 
-          String datAs1 = s[in].getDBRef()[idb].getSource().toString()
+          String datAs1 = s[in].getDBRefs()[idb].getSource().toString()
                   + " ; "
-                  + s[in].getDBRef()[idb].getAccessionId().toString();
+                  + s[in].getDBRefs()[idb].getAccessionId().toString();
           dataRef.put(tmp, datAs1);
         }
       }
@@ -1106,6 +1105,7 @@ public class StockholmFile extends AlignFile
     return seq;
   }
 
+  @Override
   public String print()
   {
     out = new StringBuffer();
@@ -1119,6 +1119,7 @@ public class StockholmFile extends AlignFile
   }
 
   private static Hashtable typeIds = null;
+
   static
   {
     if (typeIds == null)
diff --git a/src/jalview/io/StructureFile.java b/src/jalview/io/StructureFile.java
new file mode 100644
index 0000000..eee8e63
--- /dev/null
+++ b/src/jalview/io/StructureFile.java
@@ -0,0 +1,508 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io;
+
+import jalview.analysis.AlignSeq;
+import jalview.api.FeatureSettingsModelI;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.PDBEntry.Type;
+import jalview.datamodel.SequenceI;
+import jalview.structure.StructureImportSettings;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.List;
+import java.util.Vector;
+
+import MCview.PDBChain;
+
+public abstract class StructureFile extends AlignFile
+{
+
+  private String id;
+
+  public enum StructureFileType
+  {
+    PDB, MMCIF, MMTF
+  };
+
+  private PDBEntry.Type dbRefType;
+
+  /**
+   * set to true to add derived sequence annotations (temp factor read from
+   * file, or computed secondary structure) to the alignment
+   */
+  protected boolean visibleChainAnnotation = false;
+
+  /**
+   * Set true to predict secondary structure (using JMol for protein, Annotate3D
+   * for RNA)
+   */
+  protected boolean predictSecondaryStructure = false;
+
+  /**
+   * Set true (with predictSecondaryStructure=true) to predict secondary
+   * structure using an external service (currently Annotate3D for RNA only)
+   */
+  protected boolean externalSecondaryStructure = false;
+
+  private Vector<PDBChain> chains;
+
+  private boolean pdbIdAvailable;
+
+  public StructureFile(String inFile, String type) throws IOException
+  {
+    super(inFile, type);
+  }
+
+  public StructureFile(FileParse fp) throws IOException
+  {
+    super(fp);
+  }
+
+  public void addSettings(boolean addAlignmentAnnotations,
+          boolean predictSecondaryStructure, boolean externalSecStr)
+  {
+    this.visibleChainAnnotation = addAlignmentAnnotations;
+    this.predictSecondaryStructure = predictSecondaryStructure;
+    this.externalSecondaryStructure = externalSecStr;
+  }
+
+  public void xferSettings()
+  {
+    this.visibleChainAnnotation = StructureImportSettings
+            .isVisibleChainAnnotation();
+    this.predictSecondaryStructure = StructureImportSettings
+            .isProcessSecondaryStructure();
+    this.externalSecondaryStructure = StructureImportSettings
+            .isExternalSecondaryStructure();
+
+  }
+
+  public StructureFile(boolean parseImmediately, String dataObject,
+          String type) throws IOException
+  {
+    super(parseImmediately, dataObject, type);
+  }
+
+  public StructureFile(boolean a, FileParse fp) throws IOException
+  {
+    super(a, fp);
+  }
+
+  public StructureFile()
+  {
+  }
+
+  protected SequenceI postProcessChain(PDBChain chain)
+  {
+    SequenceI pdbSequence = chain.sequence;
+    pdbSequence.setName(getId() + "|" + pdbSequence.getName());
+    PDBEntry entry = new PDBEntry();
+    entry.setId(getId());
+    entry.setType(getStructureFileType());
+    if (chain.id != null)
+    {
+      entry.setChainCode(chain.id);
+    }
+    if (inFile != null)
+    {
+      entry.setFile(inFile.getAbsolutePath());
+    }
+    else
+    {
+      entry.setFile(getDataName());
+    }
+
+    DBRefEntry sourceDBRef = new DBRefEntry();
+    sourceDBRef.setAccessionId(getId());
+    sourceDBRef.setSource(DBRefSource.PDB);
+    // TODO: specify version for 'PDB' database ref if it is read from a file.
+    // TODO: decide if jalview.io should be creating primary refs!
+    sourceDBRef.setVersion("");
+    pdbSequence.addPDBId(entry);
+    pdbSequence.addDBRef(sourceDBRef);
+    SequenceI chainseq = pdbSequence;
+    seqs.addElement(chainseq);
+    AlignmentAnnotation[] chainannot = chainseq.getAnnotation();
+
+    if (chainannot != null && visibleChainAnnotation)
+    {
+      for (int ai = 0; ai < chainannot.length; ai++)
+      {
+        chainannot[ai].visible = visibleChainAnnotation;
+        annotations.addElement(chainannot[ai]);
+      }
+    }
+    return chainseq;
+  }
+
+  /**
+   * filetype of structure file - default is PDB
+   */
+  String structureFileType = PDBEntry.Type.PDB.toString();
+
+  protected void setStructureFileType(String structureFileType)
+  {
+    this.structureFileType = structureFileType;
+  }
+
+  /**
+   * filetype of last file processed
+   * 
+   * @return
+   */
+  public String getStructureFileType()
+  {
+    return structureFileType;
+  }
+
+  @SuppressWarnings({ "unchecked", "rawtypes" })
+  protected void processPdbFileWithAnnotate3d(List<SequenceI> rna)
+          throws Exception
+  {
+    // System.out.println("this is a PDB format and RNA sequence");
+    // note: we use reflection here so that the applet can compile and run
+    // without the HTTPClient bits and pieces needed for accessing Annotate3D
+    // web service
+    try
+    {
+      Class cl = Class.forName("jalview.ws.jws1.Annotate3D");
+      if (cl != null)
+      {
+        // TODO: use the PDB ID of the structure if one is available, to save
+        // bandwidth and avoid uploading the whole structure to the service
+        Object annotate3d = cl.getConstructor(new Class[] {}).newInstance(
+                new Object[] {});
+        AlignmentI al = ((AlignmentI) cl.getMethod("getRNAMLFor",
+                new Class[] { FileParse.class }).invoke(annotate3d,
+                new Object[] { new FileParse(getDataName(), type) }));
+        for (SequenceI sq : al.getSequences())
+        {
+          if (sq.getDatasetSequence() != null)
+          {
+            if (sq.getDatasetSequence().getAllPDBEntries() != null)
+            {
+              sq.getDatasetSequence().getAllPDBEntries().clear();
+            }
+          }
+          else
+          {
+            if (sq.getAllPDBEntries() != null)
+            {
+              sq.getAllPDBEntries().clear();
+            }
+          }
+        }
+        replaceAndUpdateChains(rna, al, AlignSeq.DNA, false);
+      }
+    } catch (ClassNotFoundException x)
+    {
+      // ignore classnotfounds - occurs in applet
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  protected void replaceAndUpdateChains(List<SequenceI> prot,
+          AlignmentI al, String pep, boolean b)
+  {
+    List<List<? extends Object>> replaced = AlignSeq
+            .replaceMatchingSeqsWith(seqs, annotations, prot, al, pep,
+                    false);
+    for (PDBChain ch : getChains())
+    {
+      int p = 0;
+      for (SequenceI sq : (List<SequenceI>) replaced.get(0))
+      {
+        p++;
+        if (sq == ch.sequence || sq.getDatasetSequence() == ch.sequence)
+        {
+          p = -p;
+          break;
+        }
+      }
+      if (p < 0)
+      {
+        p = -p - 1;
+        // set shadow entry for chains
+        ch.shadow = (SequenceI) replaced.get(1).get(p);
+        ch.shadowMap = ((AlignSeq) replaced.get(2).get(p))
+                .getMappingFromS1(false);
+      }
+    }
+  }
+
+  /**
+   * Predict secondary structure for RNA and/or protein sequences and add as
+   * annotations
+   * 
+   * @param rnaSequences
+   * @param proteinSequences
+   */
+  protected void addSecondaryStructure(List<SequenceI> rnaSequences,
+          List<SequenceI> proteinSequences)
+  {
+    /*
+     * Currently using Annotate3D for RNA, but only if the 'use external
+     * prediction' flag is set
+     */
+    if (externalSecondaryStructure && rnaSequences.size() > 0)
+    {
+      try
+      {
+        processPdbFileWithAnnotate3d(rnaSequences);
+      } catch (Exception x)
+      {
+        System.err.println("Exceptions when dealing with RNA in pdb file");
+        x.printStackTrace();
+
+      }
+    }
+
+    /*
+     * Currently using JMol PDB parser for peptide
+     */
+    if (proteinSequences.size() > 0)
+    {
+      try
+      {
+        processWithJmolParser(proteinSequences);
+      } catch (Exception x)
+      {
+        System.err
+                .println("Exceptions from Jmol when processing data in pdb file");
+        x.printStackTrace();
+      }
+    }
+  }
+
+  @SuppressWarnings({ "unchecked", "rawtypes" })
+  private void processWithJmolParser(List<SequenceI> prot) throws Exception
+  {
+    try
+    {
+
+      Class cl = Class.forName("jalview.ext.jmol.JmolParser");
+      if (cl != null)
+      {
+        final Constructor constructor = cl
+                .getConstructor(new Class[] { FileParse.class });
+        final Object[] args = new Object[] { new FileParse(getDataName(),
+                type) };
+
+        StructureImportSettings.setShowSeqFeatures(false);
+        StructureImportSettings.setVisibleChainAnnotation(false);
+        StructureImportSettings
+                .setProcessSecondaryStructure(predictSecondaryStructure);
+        StructureImportSettings
+                .setExternalSecondaryStructure(externalSecondaryStructure);
+        Object jmf = constructor.newInstance(args);
+        AlignmentI al = new Alignment((SequenceI[]) cl.getMethod(
+                "getSeqsAsArray", new Class[] {}).invoke(jmf));
+        cl.getMethod("addAnnotations", new Class[] { AlignmentI.class })
+                .invoke(jmf, al);
+        for (SequenceI sq : al.getSequences())
+        {
+          if (sq.getDatasetSequence() != null)
+          {
+            sq.getDatasetSequence().getAllPDBEntries().clear();
+          }
+          else
+          {
+            sq.getAllPDBEntries().clear();
+          }
+        }
+        replaceAndUpdateChains(prot, al, AlignSeq.PEP, false);
+      }
+    } catch (ClassNotFoundException q)
+    {
+    }
+    StructureImportSettings.setShowSeqFeatures(true);
+  }
+
+  public PDBChain findChain(String id) throws Exception
+  {
+    for (PDBChain chain : getChains())
+    {
+      if (chain.id.equals(id))
+      {
+        return chain;
+      }
+    }
+    throw new Exception("PDB chain not Found!");
+  }
+
+  public void makeResidueList()
+  {
+    for (PDBChain chain : getChains())
+    {
+      chain.makeResidueList(visibleChainAnnotation);
+    }
+  }
+
+  public void makeCaBondList()
+  {
+    for (PDBChain chain : getChains())
+    {
+      chain.makeCaBondList();
+    }
+  }
+
+  public void setChargeColours()
+  {
+    for (PDBChain chain : getChains())
+    {
+      chain.setChargeColours();
+    }
+  }
+
+  public void setColours(jalview.schemes.ColourSchemeI cs)
+  {
+    for (PDBChain chain : getChains())
+    {
+      chain.setChainColours(cs);
+    }
+  }
+
+  public void setChainColours()
+  {
+    int i = 0;
+    for (PDBChain chain : getChains())
+    {
+      chain.setChainColours(Color.getHSBColor(1.0f / i++, .4f, 1.0f));
+    }
+  }
+
+  public static boolean isRNA(SequenceI seq)
+  {
+    for (char c : seq.getSequence())
+    {
+      if ((c != 'A') && (c != 'C') && (c != 'G') && (c != 'U'))
+      {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * make a friendly ID string.
+   * 
+   * @param dataName
+   * @return truncated dataName to after last '/'
+   */
+  protected String safeName(String dataName)
+  {
+    int p = 0;
+    while ((p = dataName.indexOf("/")) > -1 && p < dataName.length())
+    {
+      dataName = dataName.substring(p + 1);
+    }
+    return dataName;
+  }
+
+  public String getId()
+  {
+    return id;
+  }
+
+  public void setId(String id)
+  {
+    this.id = id;
+  }
+
+  public Vector<PDBChain> getChains()
+  {
+    return chains;
+  }
+
+  public void setChains(Vector<PDBChain> chains)
+  {
+    this.chains = chains;
+  }
+
+  public Type getDbRefType()
+  {
+    return dbRefType;
+  }
+
+  public void setDbRefType(String dbRefType)
+  {
+    this.dbRefType = Type.getType(dbRefType);
+  }
+
+  public void setDbRefType(Type dbRefType)
+  {
+    this.dbRefType = dbRefType;
+  }
+
+  /**
+   * Returns a descriptor for suitable feature display settings with
+   * <ul>
+   * <li>ResNums or insertions features visible</li>
+   * <li>insertions features coloured red</li>
+   * <li>ResNum features coloured by label</li>
+   * <li>Insertions displayed above (on top of) ResNums</li>
+   * </ul>
+   */
+  @Override
+  public FeatureSettingsModelI getFeatureColourScheme()
+  {
+    return new PDBFeatureSettings();
+  }
+
+  /**
+   * Answers true if the structure file has a PDBId
+   * 
+   * @return
+   */
+  public boolean isPPDBIdAvailable()
+  {
+    return pdbIdAvailable;
+  }
+
+  public void setPDBIdAvailable(boolean pdbIdAvailable)
+  {
+    this.pdbIdAvailable = pdbIdAvailable;
+  }
+
+  public static boolean isStructureFile(String fileType)
+  {
+    if (fileType == null)
+    {
+      return false;
+    }
+    for (StructureFileType sfType : StructureFileType.values())
+    {
+      if (sfType.name().equalsIgnoreCase(fileType))
+      {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/src/jalview/io/TCoffeeScoreFile.java b/src/jalview/io/TCoffeeScoreFile.java
index 72aa99b..ce1ac32 100644
--- a/src/jalview/io/TCoffeeScoreFile.java
+++ b/src/jalview/io/TCoffeeScoreFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/VamsasAppDatastore.java b/src/jalview/io/VamsasAppDatastore.java
index d580129..70b3b38 100644
--- a/src/jalview/io/VamsasAppDatastore.java
+++ b/src/jalview/io/VamsasAppDatastore.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -43,7 +43,6 @@ import java.util.Hashtable;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
 import java.util.Vector;
 import java.util.jar.JarInputStream;
 import java.util.jar.JarOutputStream;
@@ -1433,7 +1432,7 @@ public class VamsasAppDatastore
         // to the align frames.
         boolean gathered = false;
         String newviewid = null;
-        Set<AlignedCodonFrame> mappings = av.getAlignment()
+        List<AlignedCodonFrame> mappings = av.getAlignment()
                 .getCodonFrames();
         for (int i = 0; i < views.length; i++)
         {
@@ -2732,7 +2731,7 @@ public class VamsasAppDatastore
 
       }
       // Store any sequence mappings.
-      Set<AlignedCodonFrame> cframes = av.getAlignment().getCodonFrames();
+      List<AlignedCodonFrame> cframes = av.getAlignment().getCodonFrames();
       if (cframes != null)
       {
         for (AlignedCodonFrame acf : cframes)
diff --git a/src/jalview/io/WSWUBlastClient.java b/src/jalview/io/WSWUBlastClient.java
index 1e7cc40..9761b60 100644
--- a/src/jalview/io/WSWUBlastClient.java
+++ b/src/jalview/io/WSWUBlastClient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -176,7 +176,7 @@ public class WSWUBlastClient
           }
         }
 
-        DBRefEntry[] entries = oldseq.getDBRef();
+        DBRefEntry[] entries = oldseq.getDBRefs();
         if (entries != null)
         {
           oldseq.addDBRef(new jalview.datamodel.DBRefEntry(
diff --git a/src/jalview/io/gff/ExonerateHelper.java b/src/jalview/io/gff/ExonerateHelper.java
new file mode 100644
index 0000000..5e53979
--- /dev/null
+++ b/src/jalview/io/gff/ExonerateHelper.java
@@ -0,0 +1,365 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io.gff;
+
+import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.MappingType;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.util.MapList;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A handler to parse GFF in the format generated by the exonerate tool
+ */
+public class ExonerateHelper extends Gff2Helper
+{
+  private static final String SIMILARITY = "similarity";
+
+  private static final String GENOME2GENOME = "genome2genome";
+
+  private static final String CDNA2GENOME = "cdna2genome";
+
+  private static final String CODING2GENOME = "coding2genome";
+
+  private static final String CODING2CODING = "coding2coding";
+
+  private static final String PROTEIN2GENOME = "protein2genome";
+
+  private static final String PROTEIN2DNA = "protein2dna";
+
+  private static final String ALIGN = "Align";
+
+  private static final String QUERY = "Query";
+
+  private static final String TARGET = "Target";
+
+  /**
+   * Process one GFF feature line (as modelled by SequenceFeature)
+   * 
+   * @param seq
+   *          the sequence with which this feature is associated
+   * @param gffColumns
+   *          the sequence feature with ATTRIBUTES property containing any
+   *          additional attributes
+   * @param align
+   *          the alignment we are adding GFF to
+   * @param newseqs
+   *          any new sequences referenced by the GFF
+   * @param relaxedIdMatching
+   *          if true, match word tokens in sequence names
+   * @return true if the sequence feature should be added to the sequence, else
+   *         false (i.e. it has been processed in another way e.g. to generate a
+   *         mapping)
+   */
+  @Override
+  public SequenceFeature processGff(SequenceI seq, String[] gffColumns,
+          AlignmentI align, List<SequenceI> newseqs,
+          boolean relaxedIdMatching)
+  {
+    String attr = gffColumns[ATTRIBUTES_COL];
+    Map<String, List<String>> set = parseNameValuePairs(attr);
+
+    try
+    {
+      processGffSimilarity(set, seq, gffColumns, align, newseqs,
+              relaxedIdMatching);
+    } catch (IOException ivfe)
+    {
+      System.err.println(ivfe);
+    }
+
+    /*
+     * return null to indicate we don't want to add a sequence feature for
+     * similarity (only process it to create mappings)
+     */
+    return null;
+  }
+
+  /**
+   * Processes the 'Query' (or 'Target') and 'Align' properties associated with
+   * an exonerate GFF similarity feature; these properties define the mapping of
+   * the annotated range to a related sequence.
+   * 
+   * @param set
+   *          parsed GFF column 9 key/value(s)
+   * @param seq
+   *          the sequence the GFF feature is on
+   * @param gff
+   *          the GFF column data
+   * @param align
+   *          the alignment the sequence belongs to, where any new mappings
+   *          should be added
+   * @param newseqs
+   *          a list of new 'virtual sequences' generated while parsing GFF
+   * @param relaxedIdMatching
+   *          if true allow fuzzy search for a matching target sequence
+   * @throws IOException
+   */
+  protected void processGffSimilarity(Map<String, List<String>> set,
+          SequenceI seq, String[] gff, AlignmentI align,
+          List<SequenceI> newseqs, boolean relaxedIdMatching)
+          throws IOException
+  {
+    /*
+     * exonerate may be run with
+     * --showquerygff - outputs 'features on the query' e.g. (protein2genome)  
+     *     Target <dnaseqid> ; Align proteinStartPos dnaStartPos proteinCount  
+     * --showtargetgff - outputs 'features on the target' e.g. (protein2genome)
+     *     Query <proteinseqid> ; Align dnaStartPos proteinStartPos nucleotideCount
+     * where the Align spec may repeat 
+     */
+    // TODO handle coding2coding and similar as well
+    boolean featureIsOnTarget = true;
+    List<String> mapTo = set.get(QUERY);
+    if (mapTo == null)
+    {
+      mapTo = set.get(TARGET);
+      featureIsOnTarget = false;
+    }
+    MappingType type = getMappingType(gff[SOURCE_COL]);
+
+    if (type == null)
+    {
+      throw new IOException("Sorry, I don't handle " + gff[SOURCE_COL]);
+    }
+
+    if (mapTo == null || mapTo.size() != 1)
+    {
+      throw new IOException(
+              "Expecting exactly one sequence in Query or Target field (got "
+                      + mapTo + ")");
+    }
+
+    /*
+     * locate the mapped sequence in the alignment or 'new' (GFF file) sequences; 
+     */
+    SequenceI mappedSequence = findSequence(mapTo.get(0), align, newseqs,
+            relaxedIdMatching);
+
+    /*
+     * If mapping is from protein to dna, we store it as dna to protein instead
+     */
+    SequenceI mapFromSequence = seq;
+    SequenceI mapToSequence = mappedSequence;
+    if ((type == MappingType.NucleotideToPeptide && featureIsOnTarget)
+            || (type == MappingType.PeptideToNucleotide && !featureIsOnTarget))
+    {
+      mapFromSequence = mappedSequence;
+      mapToSequence = seq;
+    }
+
+    /*
+     * Process the Align maps and create mappings.
+     * These may be cdna-genome, cdna-protein, genome-protein.
+     * The mapped sequences may or may not be in the alignment
+     * (they may be included later in the GFF file).
+     */
+
+    /*
+     * get any existing mapping for these sequences (or start one),
+     * and add this mapped range
+     */
+    AlignedCodonFrame acf = getMapping(align, mapFromSequence,
+            mapToSequence);
+
+    /*
+     * exonerate GFF has the strand of the target in column 7
+     * (differs from GFF3 which has it in the Target descriptor)
+     */
+    String strand = gff[STRAND_COL];
+    boolean forwardStrand = true;
+    if ("-".equals(strand))
+    {
+      forwardStrand = false;
+    }
+    else if (!"+".equals(strand))
+    {
+      System.err.println("Strand must be specified for alignment");
+      return;
+    }
+
+    List<String> alignedRegions = set.get(ALIGN);
+    for (String region : alignedRegions)
+    {
+      MapList mapping = buildMapping(region, type, forwardStrand,
+              featureIsOnTarget, gff);
+
+      if (mapping == null)
+      {
+        continue;
+      }
+
+      acf.addMap(mapFromSequence, mapToSequence, mapping);
+    }
+    align.addCodonFrame(acf);
+  }
+
+  /**
+   * Construct the mapping
+   * 
+   * @param region
+   * @param type
+   * @param forwardStrand
+   * @param featureIsOnTarget
+   * @param gff
+   * @return
+   */
+  protected MapList buildMapping(String region, MappingType type,
+          boolean forwardStrand, boolean featureIsOnTarget, String[] gff)
+  {
+    /*
+     * process one "fromStart toStart fromCount" descriptor
+     */
+    String[] tokens = region.split(" ");
+    if (tokens.length != 3)
+    {
+      System.err.println("Malformed Align descriptor: " + region);
+      return null;
+    }
+
+    /*
+     * get start/end of from/to mappings
+     * if feature is on the target sequence we have to invert the sense
+     */
+    int alignFromStart;
+    int alignToStart;
+    int alignCount;
+    try
+    {
+      alignFromStart = Integer.parseInt(tokens[0]);
+      alignToStart = Integer.parseInt(tokens[1]);
+      alignCount = Integer.parseInt(tokens[2]);
+    } catch (NumberFormatException nfe)
+    {
+      System.err.println(nfe.toString());
+      return null;
+    }
+
+    int fromStart;
+    int fromEnd;
+    int toStart;
+    int toEnd;
+
+    if (featureIsOnTarget)
+    {
+      fromStart = alignToStart;
+      toStart = alignFromStart;
+      toEnd = forwardStrand ? toStart + alignCount - 1 : toStart
+              - (alignCount - 1);
+      int toLength = Math.abs(toEnd - toStart) + 1;
+      int fromLength = toLength * type.getFromRatio() / type.getToRatio();
+      fromEnd = fromStart + fromLength - 1;
+    }
+    else
+    {
+      // we use the 'Align' values here not the feature start/end
+      // not clear why they may differ but it seems they can
+      fromStart = alignFromStart;
+      fromEnd = alignFromStart + alignCount - 1;
+      int fromLength = fromEnd - fromStart + 1;
+      int toLength = fromLength * type.getToRatio() / type.getFromRatio();
+      toStart = alignToStart;
+      if (forwardStrand)
+      {
+        toEnd = toStart + toLength - 1;
+      }
+      else
+      {
+        toEnd = toStart - (toLength - 1);
+      }
+    }
+
+    MapList codonmapping = constructMappingFromAlign(fromStart, fromEnd,
+            toStart, toEnd, type);
+    return codonmapping;
+  }
+
+  /**
+   * Returns a MappingType depending on the exonerate 'model' value.
+   * 
+   * @param model
+   * @return
+   */
+  protected static MappingType getMappingType(String model)
+  {
+    MappingType result = null;
+
+    if (model.contains(PROTEIN2DNA) || model.contains(PROTEIN2GENOME))
+    {
+      result = MappingType.PeptideToNucleotide;
+    }
+    else if (model.contains(CODING2CODING) || model.contains(CODING2GENOME)
+            || model.contains(CDNA2GENOME) || model.contains(GENOME2GENOME))
+    {
+      result = MappingType.NucleotideToNucleotide;
+    }
+    return result;
+  }
+
+  /**
+   * Tests whether the GFF data looks like it was generated by exonerate, and is
+   * a format we are willing to handle
+   * 
+   * @param columns
+   * @return
+   */
+  public static boolean recognises(String[] columns)
+  {
+    if (!SIMILARITY.equalsIgnoreCase(columns[TYPE_COL]))
+    {
+      return false;
+    }
+
+    /*
+     * inspect alignment model
+     */
+    String model = columns[SOURCE_COL];
+    // e.g. exonerate:protein2genome:local
+    if (model != null)
+    {
+      String mdl = model.toLowerCase();
+      if (mdl.contains(PROTEIN2DNA) || mdl.contains(PROTEIN2GENOME)
+              || mdl.contains(CODING2CODING) || mdl.contains(CODING2GENOME)
+              || mdl.contains(CDNA2GENOME) || mdl.contains(GENOME2GENOME))
+      {
+        return true;
+      }
+    }
+    System.err.println("Sorry, I don't handle exonerate model " + model);
+    return false;
+  }
+
+  @Override
+  protected SequenceFeature buildSequenceFeature(String[] gff,
+          Map<String, List<String>> set)
+  {
+    SequenceFeature sf = super.buildSequenceFeature(gff, set);
+    sf.setFeatureGroup("exonerate");
+
+    return sf;
+  }
+
+}
diff --git a/src/jalview/io/gff/Gff2Helper.java b/src/jalview/io/gff/Gff2Helper.java
new file mode 100644
index 0000000..2861378
--- /dev/null
+++ b/src/jalview/io/gff/Gff2Helper.java
@@ -0,0 +1,71 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io.gff;
+
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+public class Gff2Helper extends GffHelperBase
+{
+  /**
+   * GFF2 uses space character to delimit name/value pairs on column 9
+   * 
+   * @param text
+   * @return
+   */
+  public static Map<String, List<String>> parseNameValuePairs(String text)
+  {
+    // TODO: can a value include a comma? if so it will be broken by this
+    return parseNameValuePairs(text, ";", ' ', ",");
+  }
+
+  /**
+   * Return ' ' as the name-value separator used in column 9 attributes.
+   */
+  @Override
+  protected char getNameValueSeparator()
+  {
+    return ' ';
+  }
+
+  /**
+   * Default processing if not overridden is just to construct a sequence
+   * feature
+   */
+  @Override
+  public SequenceFeature processGff(SequenceI seq, String[] gff,
+          AlignmentI align, List<SequenceI> newseqs,
+          boolean relaxedIdMatching) throws IOException
+  {
+    Map<String, List<String>> attributes = null;
+    if (gff.length > ATTRIBUTES_COL)
+    {
+      attributes = parseNameValuePairs(gff[ATTRIBUTES_COL]);
+    }
+    return buildSequenceFeature(gff, attributes);
+  }
+
+}
diff --git a/src/jalview/io/gff/Gff3Helper.java b/src/jalview/io/gff/Gff3Helper.java
new file mode 100644
index 0000000..92be772
--- /dev/null
+++ b/src/jalview/io/gff/Gff3Helper.java
@@ -0,0 +1,425 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io.gff;
+
+import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.MappingType;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.util.MapList;
+import jalview.util.StringUtils;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class with generic / common functionality for processing GFF3 data.
+ * Override this as required for any specialisations resulting from
+ * peculiarities of GFF3 generated by particular tools.
+ */
+public class Gff3Helper extends GffHelperBase
+{
+  protected static final String TARGET = "Target";
+
+  protected static final String ID = "ID";
+
+  private static final String NAME = "Name";
+
+  /**
+   * GFF3 uses '=' to delimit name/value pairs in column 9, and comma to
+   * separate multiple values for a name
+   * 
+   * @param text
+   * @return
+   */
+  public static Map<String, List<String>> parseNameValuePairs(String text)
+  {
+    return parseNameValuePairs(text, ";", '=', ",");
+  }
+
+  /**
+   * Process one GFF feature line (as modelled by SequenceFeature)
+   * 
+   * @param seq
+   *          the sequence with which this feature is associated
+   * @param sf
+   *          the sequence feature with ATTRIBUTES property containing any
+   *          additional attributes
+   * @param align
+   *          the alignment we are adding GFF to
+   * @param newseqs
+   *          any new sequences referenced by the GFF
+   * @param relaxedIdMatching
+   *          if true, match word tokens in sequence names
+   * @return true if the sequence feature should be added to the sequence, else
+   *         false (i.e. it has been processed in another way e.g. to generate a
+   *         mapping)
+   * @throws IOException
+   */
+  @Override
+  public SequenceFeature processGff(SequenceI seq, String[] gff,
+          AlignmentI align, List<SequenceI> newseqs,
+          boolean relaxedIdMatching) throws IOException
+  {
+    SequenceFeature sf = null;
+
+    if (gff.length == 9)
+    {
+      String soTerm = gff[TYPE_COL];
+      String atts = gff[ATTRIBUTES_COL];
+      Map<String, List<String>> attributes = parseNameValuePairs(atts);
+
+      SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+      if (so.isA(soTerm, SequenceOntologyI.PROTEIN_MATCH))
+      {
+        sf = processProteinMatch(attributes, seq, gff, align, newseqs,
+                relaxedIdMatching);
+      }
+      else if (so.isA(soTerm, SequenceOntologyI.NUCLEOTIDE_MATCH))
+      {
+        sf = processNucleotideMatch(attributes, seq, gff, align, newseqs,
+                relaxedIdMatching);
+      }
+      else
+      {
+        sf = buildSequenceFeature(gff, attributes);
+      }
+    }
+    else
+    {
+      /*
+       * fall back on generating a sequence feature with no special processing
+       */
+      sf = buildSequenceFeature(gff, null);
+    }
+
+    return sf;
+  }
+
+  /**
+   * Processes one GFF3 nucleotide (e.g. cDNA to genome) match.
+   * 
+   * @param attributes
+   *          parsed GFF column 9 key/value(s)
+   * @param seq
+   *          the sequence the GFF feature is on
+   * @param gffColumns
+   *          the GFF column data
+   * @param align
+   *          the alignment the sequence belongs to, where any new mappings
+   *          should be added
+   * @param newseqs
+   *          a list of new 'virtual sequences' generated while parsing GFF
+   * @param relaxedIdMatching
+   *          if true allow fuzzy search for a matching target sequence
+   * @return a sequence feature, if one should be added to the sequence, else
+   *         null
+   * @throws IOException
+   */
+  protected SequenceFeature processNucleotideMatch(
+          Map<String, List<String>> attributes, SequenceI seq,
+          String[] gffColumns, AlignmentI align, List<SequenceI> newseqs,
+          boolean relaxedIdMatching) throws IOException
+  {
+    String strand = gffColumns[STRAND_COL];
+
+    /*
+     * (For now) we don't process mappings from reverse complement ; to do
+     * this would require (a) creating a virtual sequence placeholder for
+     * the reverse complement (b) resolving the sequence by its id from some
+     * source (GFF ##FASTA or other) (c) creating the reverse complement
+     * sequence (d) updating the mapping to be to the reverse complement
+     */
+    if ("-".equals(strand))
+    {
+      System.err
+              .println("Skipping mapping from reverse complement as not yet supported");
+      return null;
+    }
+
+    List<String> targets = attributes.get(TARGET);
+    if (targets == null)
+    {
+      System.err.println("'Target' missing in GFF");
+      return null;
+    }
+
+    /*
+     * Typically we only expect one Target per GFF line, but this can handle
+     * multiple matches, to the same or different sequences (e.g. dna variants)
+     */
+    for (String target : targets)
+    {
+      /*
+       * Process "seqid start end [strand]"
+       */
+      String[] tokens = target.split(" ");
+      if (tokens.length < 3)
+      {
+        System.err.println("Incomplete Target: " + target);
+        continue;
+      }
+
+      /*
+       * Locate the mapped sequence in the alignment, or as a 
+       * (new or existing) virtual sequence in the newseqs list 
+       */
+      String targetId = findTargetId(tokens[0], attributes);
+      SequenceI mappedSequence1 = findSequence(targetId, align, newseqs,
+              relaxedIdMatching);
+      SequenceI mappedSequence = mappedSequence1;
+      if (mappedSequence == null)
+      {
+        continue;
+      }
+
+      /*
+       * get any existing mapping for these sequences (or start one),
+       * and add this mapped range
+       */
+      AlignedCodonFrame acf = getMapping(align, seq, mappedSequence);
+
+      try
+      {
+        int toStart = Integer.parseInt(tokens[1]);
+        int toEnd = Integer.parseInt(tokens[2]);
+        if (tokens.length > 3 && "-".equals(tokens[3]))
+        {
+          // mapping to reverse strand - swap start/end
+          int temp = toStart;
+          toStart = toEnd;
+          toEnd = temp;
+        }
+
+        int fromStart = Integer.parseInt(gffColumns[START_COL]);
+        int fromEnd = Integer.parseInt(gffColumns[END_COL]);
+        MapList mapping = constructMappingFromAlign(fromStart, fromEnd,
+                toStart, toEnd, MappingType.NucleotideToNucleotide);
+
+        if (mapping != null)
+        {
+          acf.addMap(seq, mappedSequence, mapping);
+          align.addCodonFrame(acf);
+        }
+      } catch (NumberFormatException nfe)
+      {
+        System.err.println("Invalid start or end in Target " + target);
+      }
+    }
+
+    SequenceFeature sf = buildSequenceFeature(gffColumns, attributes);
+    return sf;
+  }
+
+  /**
+   * Returns the target sequence id extracted from the GFF name/value pairs.
+   * Default (standard behaviour) is the first token for "Target". This may be
+   * overridden where tools report this in a non-standard way.
+   * 
+   * @param target
+   *          first token of a "Target" value from GFF column 9, typically
+   *          "seqid start end"
+   * @param set
+   *          a map with all parsed column 9 attributes
+   * @return
+   */
+  @SuppressWarnings("unused")
+  protected String findTargetId(String target, Map<String, List<String>> set)
+  {
+    return target;
+  }
+
+  /**
+   * Processes one GFF 'protein_match'; fields of interest are
+   * <ul>
+   * <li>feature group - the database reporting a match e.g. Pfam</li>
+   * <li>Name - the matched entry's accession id in the database</li>
+   * <li>ID - a sequence identifier for the matched region (which may be
+   * appended as FASTA in the GFF file)</li>
+   * </ul>
+   * 
+   * @param set
+   *          parsed GFF column 9 key/value(s)
+   * @param seq
+   *          the sequence the GFF feature is on
+   * @param gffColumns
+   *          the sequence feature holding GFF data
+   * @param align
+   *          the alignment the sequence belongs to, where any new mappings
+   *          should be added
+   * @param newseqs
+   *          a list of new 'virtual sequences' generated while parsing GFF
+   * @param relaxedIdMatching
+   *          if true allow fuzzy search for a matching target sequence
+   * @return the (real or virtual) sequence(s) mapped to by this match
+   * @throws IOException
+   */
+  protected SequenceFeature processProteinMatch(
+          Map<String, List<String>> set, SequenceI seq,
+          String[] gffColumns, AlignmentI align, List<SequenceI> newseqs,
+          boolean relaxedIdMatching)
+  {
+    // This is currently tailored to InterProScan GFF output:
+    // ID holds the ID of the matched sequence, Target references the
+    // query sequence; this looks wrong, as ID should just be the GFF internal
+    // ID of the GFF feature, while Target would normally reference the matched
+    // sequence.
+    // TODO refactor as needed if other protein-protein GFF varies
+
+    SequenceFeature sf = buildSequenceFeature(gffColumns, set);
+
+    /*
+     * locate the mapped sequence in the alignment, or as a 
+     * (new or existing) virtual sequence in the newseqs list 
+     */
+    List<String> targets = set.get(TARGET);
+    if (targets != null)
+    {
+      for (String target : targets)
+      {
+
+        SequenceI mappedSequence1 = findSequence(findTargetId(target, set),
+                align, newseqs, relaxedIdMatching);
+        SequenceI mappedSequence = mappedSequence1;
+        if (mappedSequence == null)
+        {
+          continue;
+        }
+
+        /*
+         * give the mapped sequence a copy of the sequence feature, with 
+         * start/end range adjusted 
+         */
+        SequenceFeature sf2 = new SequenceFeature(sf);
+        sf2.setBegin(1);
+        int sequenceFeatureLength = 1 + sf.getEnd() - sf.getBegin();
+        sf2.setEnd(sequenceFeatureLength);
+        mappedSequence.addSequenceFeature(sf2);
+
+        /*
+         * add a property to the mapped sequence so that it can eventually be
+         * renamed with its qualified accession id; renaming has to wait until
+         * all sequence reference resolution is complete
+         */
+        String accessionId = StringUtils.listToDelimitedString(
+                set.get(NAME), ",");
+        if (accessionId.length() > 0)
+        {
+          String database = sf.getType(); // TODO InterProScan only??
+          String qualifiedAccId = database + "|" + accessionId;
+          sf2.setValue(RENAME_TOKEN, qualifiedAccId);
+        }
+
+        /*
+         * get any existing mapping for these sequences (or start one),
+         * and add this mapped range
+         */
+        AlignedCodonFrame alco = getMapping(align, seq, mappedSequence);
+        int[] from = new int[] { sf.getBegin(), sf.getEnd() };
+        int[] to = new int[] { 1, sequenceFeatureLength };
+        MapList mapping = new MapList(from, to, 1, 1);
+
+        alco.addMap(seq, mappedSequence, mapping);
+        align.addCodonFrame(alco);
+      }
+    }
+
+    return sf;
+  }
+
+  /**
+   * Return '=' as the name-value separator used in column 9 attributes.
+   */
+  @Override
+  protected char getNameValueSeparator()
+  {
+    return '=';
+  }
+
+  /**
+   * Modifies the default SequenceFeature in order to set the Target sequence id
+   * as the description
+   */
+  @Override
+  protected SequenceFeature buildSequenceFeature(String[] gff,
+          Map<String, List<String>> attributes)
+  {
+    SequenceFeature sf = super.buildSequenceFeature(gff, attributes);
+    String desc = getDescription(sf, attributes);
+    if (desc != null)
+    {
+      sf.setDescription(desc);
+    }
+    return sf;
+  }
+
+  /**
+   * Apply heuristic rules to try to get the most useful feature description
+   * 
+   * @param sf
+   * @param attributes
+   * @return
+   */
+  protected String getDescription(SequenceFeature sf,
+          Map<String, List<String>> attributes)
+  {
+    String desc = null;
+    String target = (String) sf.getValue(TARGET);
+    if (target != null)
+    {
+      desc = target.split(" ")[0];
+    }
+
+    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    String type = sf.getType();
+    if (so.isA(type, SequenceOntologyI.SEQUENCE_VARIANT))
+    {
+      /*
+       * Ensembl returns dna variants as 'alleles'
+       */
+      desc = StringUtils.listToDelimitedString(attributes.get("alleles"),
+              ",");
+    }
+
+    /*
+     * extract 'Name' for a transcript (to show gene name)
+     * or an exon (so 'colour by label' shows exon boundaries) 
+     */
+    if (SequenceOntologyI.NMD_TRANSCRIPT_VARIANT.equals(type)
+            || so.isA(type, SequenceOntologyI.TRANSCRIPT)
+            || so.isA(type, SequenceOntologyI.EXON))
+    {
+      desc = StringUtils.listToDelimitedString(attributes.get("Name"), ",");
+    }
+
+    /*
+     * if the above fails, try ID
+     */
+    if (desc == null)
+    {
+      desc = (String) sf.getValue(ID);
+    }
+
+    return desc;
+  }
+}
diff --git a/src/jalview/datamodel/xdb/embl/EmblError.java b/src/jalview/io/gff/GffConstants.java
similarity index 59%
copy from src/jalview/datamodel/xdb/embl/EmblError.java
copy to src/jalview/io/gff/GffConstants.java
index 232d15e..e1e4262 100644
--- a/src/jalview/datamodel/xdb/embl/EmblError.java
+++ b/src/jalview/io/gff/GffConstants.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,31 +18,26 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.datamodel.xdb.embl;
+package jalview.io.gff;
 
 /**
- * Data model mapped from any <error> elements returned from an EMBL query
- * 
- * @see embl_mapping.xml
+ * A class to hold constants shared by creators and consumers of GFF or feature
+ * data
  */
-public class EmblError
+public class GffConstants
 {
-  String accession;
+  // SequenceOntology terms are to be found in SequenceOntologyI
 
-  /**
-   * @return the accession
+  /*
+   * clinical_significance may be an attribute of 
+   * sequence_variant data from Ensembl
    */
-  public String getAccession()
-  {
-    return accession;
-  }
+  public static final String CLINICAL_SIGNIFICANCE = "clinical_significance";
 
-  /**
-   * @param accession
-   *          the accession to set
+  /*
+   * not instantiable
    */
-  public void setAccession(String accession)
+  private GffConstants()
   {
-    this.accession = accession;
   }
 }
diff --git a/src/jalview/io/gff/GffHelperBase.java b/src/jalview/io/gff/GffHelperBase.java
new file mode 100644
index 0000000..8b10c2a
--- /dev/null
+++ b/src/jalview/io/gff/GffHelperBase.java
@@ -0,0 +1,426 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io.gff;
+
+import jalview.analysis.SequenceIdMatcher;
+import jalview.datamodel.AlignedCodonFrame;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.MappingType;
+import jalview.datamodel.SequenceDummy;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.util.MapList;
+import jalview.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Base class with common functionality for flavours of GFF handler (GFF2 or
+ * GFF3)
+ */
+public abstract class GffHelperBase implements GffHelperI
+{
+  private static final String NOTE = "Note";
+
+  /*
+   * GFF columns 1-9 (zero-indexed):
+   */
+  protected static final int SEQID_COL = 0;
+
+  protected static final int SOURCE_COL = 1;
+
+  protected static final int TYPE_COL = 2;
+
+  protected static final int START_COL = 3;
+
+  protected static final int END_COL = 4;
+
+  protected static final int SCORE_COL = 5;
+
+  protected static final int STRAND_COL = 6;
+
+  protected static final int PHASE_COL = 7;
+
+  protected static final int ATTRIBUTES_COL = 8;
+
+  private AlignmentI lastmatchedAl = null;
+
+  private SequenceIdMatcher matcher = null;
+
+  /**
+   * Constructs and returns a mapping, or null if data appear invalid
+   * 
+   * @param fromStart
+   * @param fromEnd
+   * @param toStart
+   * @param toEnd
+   * @param mappingType
+   *          type of mapping (e.g. protein to nucleotide)
+   * @return
+   */
+  protected MapList constructMappingFromAlign(int fromStart, int fromEnd,
+          int toStart, int toEnd, MappingType mappingType)
+  {
+    int[] from = new int[] { fromStart, fromEnd };
+    int[] to = new int[] { toStart, toEnd };
+
+    /*
+     * Jalview always models from dna to protein, so switch values if the
+     * GFF mapping is from protein to dna
+     */
+    if (mappingType == MappingType.PeptideToNucleotide)
+    {
+      int[] temp = from;
+      from = to;
+      to = temp;
+      mappingType = mappingType.getInverse();
+    }
+
+    int fromRatio = mappingType.getFromRatio();
+    int toRatio = mappingType.getToRatio();
+
+    /*
+     * sanity check that mapped residue counts match
+     * TODO understand why PASA generates such cases...
+     */
+    if (!trimMapping(from, to, fromRatio, toRatio))
+    {
+      System.err.println("Ignoring mapping from " + Arrays.toString(from)
+              + " to " + Arrays.toString(to) + " as counts don't match!");
+      return null;
+    }
+
+    /*
+     * If a codon has an intron gap, there will be contiguous 'toRanges';
+     * this is handled for us by the MapList constructor. 
+     * (It is not clear that exonerate ever generates this case)  
+     */
+
+    return new MapList(from, to, fromRatio, toRatio);
+  }
+
+  /**
+   * Checks that the 'from' and 'to' ranges have equivalent lengths. If not,
+   * tries to trim the end of the longer so they do. Returns true if the
+   * mappings could be made equivalent, else false. Note the range array values
+   * may be modified by this method.
+   * 
+   * @param from
+   * @param to
+   * @param fromRatio
+   * @param toRatio
+   * @return
+   */
+  protected static boolean trimMapping(int[] from, int[] to, int fromRatio,
+          int toRatio)
+  {
+    int fromLength = Math.abs(from[1] - from[0]) + 1;
+    int toLength = Math.abs(to[1] - to[0]) + 1;
+    int fromOverlap = fromLength * toRatio - toLength * fromRatio;
+    if (fromOverlap == 0)
+    {
+      return true;
+    }
+    if (fromOverlap > 0 && fromOverlap % toRatio == 0)
+    {
+      /*
+       * restrict from range to make them match up
+       * it's kind of arbitrary which end we truncate - here it is the end
+       */
+      System.err.print("Truncating mapping from " + Arrays.toString(from)
+              + " to ");
+      if (from[1] > from[0])
+      {
+        from[1] -= fromOverlap / toRatio;
+      }
+      else
+      {
+        from[1] += fromOverlap / toRatio;
+      }
+      System.err.println(Arrays.toString(from));
+      return true;
+    }
+    else if (fromOverlap < 0 && fromOverlap % fromRatio == 0)
+    {
+      fromOverlap = -fromOverlap; // > 0
+      /*
+       * restrict to range to make them match up
+       */
+      System.err.print("Truncating mapping to " + Arrays.toString(to)
+              + " to ");
+      if (to[1] > to[0])
+      {
+        to[1] -= fromOverlap / fromRatio;
+      }
+      else
+      {
+        to[1] += fromOverlap / fromRatio;
+      }
+      System.err.println(Arrays.toString(to));
+      return true;
+    }
+
+    /*
+     * Couldn't truncate to an exact match..
+     */
+    return false;
+  }
+
+  /**
+   * Returns a sequence matching the given id, as follows
+   * <ul>
+   * <li>strict matching is on exact sequence name</li>
+   * <li>relaxed matching allows matching on a token within the sequence name,
+   * or a dbxref</li>
+   * <li>first tries to find a match in the alignment sequences</li>
+   * <li>else tries to find a match in the new sequences already generated while
+   * parsing the features file</li>
+   * <li>else creates a new placeholder sequence, adds it to the new sequences
+   * list, and returns it</li>
+   * </ul>
+   * 
+   * @param seqId
+   * @param align
+   * @param newseqs
+   * @param relaxedIdMatching
+   * 
+   * @return
+   */
+  protected SequenceI findSequence(String seqId, AlignmentI align,
+          List<SequenceI> newseqs, boolean relaxedIdMatching)
+  {
+    if (seqId == null)
+    {
+      return null;
+    }
+    SequenceI match = null;
+    if (relaxedIdMatching)
+    {
+      if (lastmatchedAl != align)
+      {
+        lastmatchedAl = align;
+        matcher = new SequenceIdMatcher(align.getSequencesArray());
+        if (newseqs != null)
+        {
+          matcher.addAll(newseqs);
+        }
+      }
+      match = matcher.findIdMatch(seqId);
+    }
+    else
+    {
+      match = align.findName(seqId, true);
+      if (match == null && newseqs != null)
+      {
+        for (SequenceI m : newseqs)
+        {
+          if (seqId.equals(m.getName()))
+          {
+            return m;
+          }
+        }
+      }
+
+    }
+    if (match == null && newseqs != null)
+    {
+      match = new SequenceDummy(seqId);
+      if (relaxedIdMatching)
+      {
+        matcher.addAll(Arrays.asList(new SequenceI[] { match }));
+      }
+      // add dummy sequence to the newseqs list
+      newseqs.add(match);
+    }
+    return match;
+  }
+
+  /**
+   * Parses the input line to a map of name / value(s) pairs. For example the
+   * line <br>
+   * Notes=Fe-S;Method=manual curation, prediction; source = Pfam; Notes = Metal <br>
+   * if parsed with delimiter=";" and separators {' ', '='} <br>
+   * would return a map with { Notes={Fe=S, Metal}, Method={manual curation,
+   * prediction}, source={Pfam}} <br>
+   * 
+   * This method supports parsing of either GFF2 format (which uses space ' ' as
+   * the name/value delimiter, and allows multiple occurrences of the same
+   * name), or GFF3 format (which uses '=' as the name/value delimiter, and
+   * strictly does not allow repeat occurrences of the same name - but does
+   * allow a comma-separated list of values).
+   * 
+   * @param text
+   * @param namesDelimiter
+   *          the major delimiter between name-value pairs
+   * @param nameValueSeparator
+   *          one or more separators used between name and value
+   * @param valuesDelimiter
+   *          delimits a list of more than one value
+   * @return the name-values map (which may be empty but never null)
+   */
+  public static Map<String, List<String>> parseNameValuePairs(String text,
+          String namesDelimiter, char nameValueSeparator,
+          String valuesDelimiter)
+  {
+    Map<String, List<String>> map = new HashMap<String, List<String>>();
+    if (text == null || text.trim().length() == 0)
+    {
+      return map;
+    }
+
+    for (String pair : text.trim().split(namesDelimiter))
+    {
+      pair = pair.trim();
+      if (pair.length() == 0)
+      {
+        continue;
+      }
+
+      int sepPos = pair.indexOf(nameValueSeparator);
+      if (sepPos == -1)
+      {
+        // no name=value present
+        continue;
+      }
+
+      String key = pair.substring(0, sepPos).trim();
+      String values = pair.substring(sepPos + 1).trim();
+      if (values.length() > 0)
+      {
+        List<String> vals = map.get(key);
+        if (vals == null)
+        {
+          vals = new ArrayList<String>();
+          map.put(key, vals);
+        }
+        for (String val : values.split(valuesDelimiter))
+        {
+          vals.add(val);
+        }
+      }
+    }
+    return map;
+  }
+
+  /**
+   * Constructs a SequenceFeature from the GFF column data. Subclasses may wish
+   * to call this method then adjust the SequenceFeature depending on the
+   * particular usage of different tools that generate GFF.
+   * 
+   * @param gff
+   * @param attributes
+   * @return
+   */
+  protected SequenceFeature buildSequenceFeature(String[] gff,
+          Map<String, List<String>> attributes)
+  {
+    try
+    {
+      int start = Integer.parseInt(gff[START_COL]);
+      int end = Integer.parseInt(gff[END_COL]);
+
+      /*
+       * default 'score' is 0 rather than Float.NaN as the latter currently
+       * disables the 'graduated colour => colour by label' option
+       */
+      float score = 0f;
+      try
+      {
+        score = Float.parseFloat(gff[SCORE_COL]);
+      } catch (NumberFormatException nfe)
+      {
+        // e.g. '.' - leave as zero
+      }
+
+      SequenceFeature sf = new SequenceFeature(gff[TYPE_COL],
+              gff[SOURCE_COL], start, end, score, gff[SOURCE_COL]);
+
+      sf.setStrand(gff[STRAND_COL]);
+
+      sf.setPhase(gff[PHASE_COL]);
+
+      if (attributes != null)
+      {
+        /*
+         * save 'raw' column 9 to allow roundtrip output as input
+         */
+        sf.setAttributes(gff[ATTRIBUTES_COL]);
+
+        /*
+         * Add attributes in column 9 to the sequence feature's 
+         * 'otherData' table; use Note as a best proxy for description
+         */
+        for (Entry<String, List<String>> attr : attributes.entrySet())
+        {
+          String values = StringUtils.listToDelimitedString(
+                  attr.getValue(), ",");
+          sf.setValue(attr.getKey(), values);
+          if (NOTE.equals(attr.getKey()))
+          {
+            sf.setDescription(values);
+          }
+        }
+      }
+
+      return sf;
+    } catch (NumberFormatException nfe)
+    {
+      System.err.println("Invalid number in gff: " + nfe.getMessage());
+      return null;
+    }
+  }
+
+  /**
+   * Returns the character used to separate attributes names from values in GFF
+   * column 9. This is space for GFF2, '=' for GFF3.
+   * 
+   * @return
+   */
+  protected abstract char getNameValueSeparator();
+
+  /**
+   * Returns any existing mapping held on the alignment between the given
+   * dataset sequences, or a new one if none found. This is a convenience method
+   * to facilitate processing multiple GFF lines that make up a single 'spliced'
+   * mapping, by extending the first mapping as the others are read.
+   * 
+   * @param align
+   * @param fromSeq
+   * @param toSeq
+   * @return
+   */
+  protected AlignedCodonFrame getMapping(AlignmentI align,
+          SequenceI fromSeq, SequenceI toSeq)
+  {
+    AlignedCodonFrame acf = align.getMapping(fromSeq, toSeq);
+    if (acf == null)
+    {
+      acf = new AlignedCodonFrame();
+    }
+    return acf;
+  }
+
+}
diff --git a/src/jalview/io/gff/GffHelperFactory.java b/src/jalview/io/gff/GffHelperFactory.java
new file mode 100644
index 0000000..80b2d5f
--- /dev/null
+++ b/src/jalview/io/gff/GffHelperFactory.java
@@ -0,0 +1,89 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io.gff;
+
+/**
+ * A factory to serve instances of GFF helper classes
+ */
+public class GffHelperFactory
+{
+
+  /**
+   * Returns a class to process the GFF line based on inspecting its column
+   * data. This may return a general-purpose GFF2 or GFF3 helper, or a
+   * specialisation for a flavour of GFF generated by a particular tool.
+   * 
+   * @param gff
+   * @return
+   */
+  public static GffHelperI getHelper(String[] gff)
+  {
+    if (gff == null || gff.length < 6)
+    {
+      return null;
+    }
+
+    GffHelperI result = null;
+    if (ExonerateHelper.recognises(gff))
+    {
+      result = new ExonerateHelper();
+    }
+    else if (InterProScanHelper.recognises(gff))
+    {
+      result = new InterProScanHelper();
+    }
+    else if (looksLikeGff3(gff))
+    {
+      result = new Gff3Helper();
+    }
+    else
+    {
+      result = new Gff2Helper();
+    }
+
+    return result;
+  }
+
+  /**
+   * Heuristic rule: if column 9 seems to have Name=Value entries, assume this
+   * is GFF3. GFF3 uses '=' as name-value separator, GFF2 uses space ' '.
+   * 
+   * @param gff
+   * @return
+   */
+  protected static boolean looksLikeGff3(String[] gff)
+  {
+    if (gff.length >= 9)
+    {
+      String attributes = gff[8].trim();
+      int pos1 = attributes.indexOf(';');
+      int pos2 = attributes.indexOf('=');
+      if (pos2 != -1 && (pos1 == -1 || pos2 < pos1))
+      {
+        // there is an '=' before the first ';' (if any)
+        // not foolproof as theoretically GFF2 could be like "Name Value=123;"
+        return true;
+      }
+    }
+    return false;
+  }
+
+}
diff --git a/src/jalview/io/gff/GffHelperI.java b/src/jalview/io/gff/GffHelperI.java
new file mode 100644
index 0000000..286cca4
--- /dev/null
+++ b/src/jalview/io/gff/GffHelperI.java
@@ -0,0 +1,63 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io.gff;
+
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * An interface to described common functionality of different flavours of GFF
+ * 
+ * @author gmcarstairs
+ *
+ */
+public interface GffHelperI
+{
+
+  final String RENAME_TOKEN = "$RENAME_TO$";
+
+  /**
+   * Process one GFF feature line
+   * 
+   * @param seq
+   *          the sequence with which this feature is associated
+   * @param gffColumns
+   *          the GFF column data
+   * @param align
+   *          the alignment we are adding GFF to
+   * @param newseqs
+   *          any new sequences referenced by the GFF
+   * @param relaxedIdMatching
+   *          if true, match word tokens in sequence names
+   * @return a SequenceFeature if one should be created, else null
+   * @throws IOException
+   */
+  SequenceFeature processGff(SequenceI seq, String[] gffColumns,
+          AlignmentI align, List<SequenceI> newseqs,
+          boolean relaxedIdMatching) throws IOException;
+
+  // java 8 will allow static methods in interfaces:
+  // static boolean recognises(String [] columns);
+}
diff --git a/src/jalview/io/gff/InterProScanHelper.java b/src/jalview/io/gff/InterProScanHelper.java
new file mode 100644
index 0000000..4f436b4
--- /dev/null
+++ b/src/jalview/io/gff/InterProScanHelper.java
@@ -0,0 +1,138 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io.gff;
+
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.util.StringUtils;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A handler to parse GFF in the format generated by InterProScan
+ */
+public class InterProScanHelper extends Gff3Helper
+{
+  private static final String INTER_PRO_SCAN = "InterProScan";
+
+  private static final String SIGNATURE_DESC = "signature_desc";
+
+  /**
+   * Process one GFF feature line (as modelled by SequenceFeature)
+   * 
+   * @param seq
+   *          the sequence with which this feature is associated
+   * @param gff
+   *          the gff column data
+   * @param align
+   *          the alignment we are adding GFF to
+   * @param newseqs
+   *          any new sequences referenced by the GFF
+   * @param relaxedIdMatching
+   *          if true, match word tokens in sequence names
+   * @return a sequence feature if one should be added to the sequence, else
+   *         null (i.e. it has been processed in another way e.g. to generate a
+   *         mapping)
+   * @throws IOException
+   */
+  @Override
+  public SequenceFeature processGff(SequenceI seq, String[] gff,
+          AlignmentI align, List<SequenceI> newseqs,
+          boolean relaxedIdMatching) throws IOException
+  {
+    /*
+     * ignore the 'polypeptide' match of the whole sequence
+     */
+    if (".".equals(gff[SOURCE_COL]))
+    {
+      return null;
+    }
+
+    return super.processGff(seq, gff, align, newseqs, relaxedIdMatching);
+  }
+
+  /**
+ * 
+ */
+  @Override
+  protected SequenceFeature buildSequenceFeature(String[] gff,
+          Map<String, List<String>> attributes)
+  {
+    SequenceFeature sf = super.buildSequenceFeature(gff, attributes);
+
+    /*
+     * signature_desc is a more informative source of description
+     */
+    List<String> desc = attributes.get(SIGNATURE_DESC);
+    String description = StringUtils.listToDelimitedString(desc, ", ");
+    if (description.length() > 0)
+    {
+      sf.setDescription(description);
+    }
+
+    /*
+     * Set sequence feature group as 'InterProScan', and type as the source
+     * database for this match (e.g. 'Pfam')
+     */
+    sf.setType(gff[SOURCE_COL]);
+    sf.setFeatureGroup(INTER_PRO_SCAN);
+
+    return sf;
+  }
+
+  /**
+   * Tests whether the GFF data looks like it was generated by InterProScan
+   * 
+   * @param columns
+   * @return
+   */
+  public static boolean recognises(String[] columns)
+  {
+    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
+    String type = columns[TYPE_COL];
+    if (so.isA(type, SequenceOntologyI.PROTEIN_MATCH)
+            || (".".equals(columns[SOURCE_COL]) && so.isA(type,
+                    SequenceOntologyI.POLYPEPTIDE)))
+    {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Overriden method, because InterProScan GFF has the target sequence id in
+   * GFF field 'ID' rather than the usual 'Target' :-O
+   */
+  @Override
+  protected String findTargetId(String target, Map<String, List<String>> set)
+  {
+    List<String> ids = set.get(ID);
+    if (ids == null || ids.size() != 1)
+    {
+      return null;
+    }
+    return ids.get(0);
+  }
+
+}
diff --git a/src/jalview/datamodel/IncompleteCodonException.java b/src/jalview/io/gff/SequenceOntologyFactory.java
similarity index 54%
copy from src/jalview/datamodel/IncompleteCodonException.java
copy to src/jalview/io/gff/SequenceOntologyFactory.java
index f2b5a50..17fb063 100644
--- a/src/jalview/datamodel/IncompleteCodonException.java
+++ b/src/jalview/io/gff/SequenceOntologyFactory.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,16 +18,31 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.datamodel;
+package jalview.io.gff;
 
 /**
- * An exception to indicate that less than 3 nucleotide bases are available when
- * trying to form a codon.
+ * A factory class that returns a model of the Sequence Ontology. By default a
+ * hard-coded subset is used (for the applet, or testing), or setInstance() can
+ * be used to set full Ontology data.
  * 
  * @author gmcarstairs
  *
  */
-public class IncompleteCodonException extends RuntimeException
+public class SequenceOntologyFactory
 {
+  private static SequenceOntologyI instance;
 
+  public static synchronized SequenceOntologyI getInstance()
+  {
+    if (instance == null)
+    {
+      instance = new SequenceOntologyLite();
+    }
+    return instance;
+  }
+
+  public static void setInstance(SequenceOntologyI so)
+  {
+    instance = so;
+  }
 }
diff --git a/src/jalview/io/gff/SequenceOntologyI.java b/src/jalview/io/gff/SequenceOntologyI.java
new file mode 100644
index 0000000..aeae28a
--- /dev/null
+++ b/src/jalview/io/gff/SequenceOntologyI.java
@@ -0,0 +1,80 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io.gff;
+
+import java.util.List;
+
+public interface SequenceOntologyI
+{
+  /*
+   * selected commonly used values for quick reference
+   */
+  // SO:0000104
+  public static final String POLYPEPTIDE = "polypeptide";
+
+  // SO:0000349
+  public static final String PROTEIN_MATCH = "protein_match";
+
+  // SO:0000347
+  public static final String NUCLEOTIDE_MATCH = "nucleotide_match";
+
+  // SO:0000316
+  public static final String CDS = "CDS";
+
+  // SO:0001060
+  public static final String SEQUENCE_VARIANT = "sequence_variant";
+
+  // SO:0000147
+  public static final String EXON = "exon";
+
+  // SO:0000673
+  public static final String TRANSCRIPT = "transcript";
+
+  // SO:0001621 isA sequence_variant but used in Ensembl as a transcript
+  public static final String NMD_TRANSCRIPT_VARIANT = "NMD_transcript_variant";
+
+  // SO:0000704
+  public static final String GENE = "gene";
+
+  public boolean isA(String childTerm, String parentTerm);
+
+  /**
+   * Returns a sorted list of all valid terms queried for (i.e. terms processed
+   * which were valid in the SO), using the friendly description.
+   * 
+   * This can be used to check that any hard-coded stand-in for the full SO
+   * includes all the terms needed for correct processing.
+   * 
+   * @return
+   */
+  public List<String> termsFound();
+
+  /**
+   * Returns a sorted list of all invalid terms queried for (i.e. terms
+   * processed which were not found in the SO), using the friendly description.
+   * 
+   * This can be used to report any 'non-compliance' in data, and/or to report
+   * valid terms missing from any hard-coded stand-in for the full SO.
+   * 
+   * @return
+   */
+  public List<String> termsNotFound();
+}
diff --git a/src/jalview/io/gff/SequenceOntologyLite.java b/src/jalview/io/gff/SequenceOntologyLite.java
new file mode 100644
index 0000000..dd3658f
--- /dev/null
+++ b/src/jalview/io/gff/SequenceOntologyLite.java
@@ -0,0 +1,240 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.io.gff;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An implementation of SequenceOntologyI that hard codes terms of interest.
+ *
+ * Use this in unit testing by calling SequenceOntology.setInstance(new
+ * SequenceOntologyLite()).
+ * 
+ * May also become a stand-in for SequenceOntology in the applet if we want to
+ * avoid the additional jars needed for parsing the full SO.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class SequenceOntologyLite implements SequenceOntologyI
+{
+  /*
+   * initial selection of types of interest when processing Ensembl features
+   * NB unlike the full SequenceOntology we don't traverse indirect
+   * child-parent relationships here so e.g. need to list every sub-type
+   * of gene (direct or indirect) that is of interest
+   */
+  // @formatter:off
+  private final String[][] TERMS = new String[][] {
+
+    /*
+     * gene sub-types:
+     */
+    { "gene", "gene" }, 
+    { "ncRNA_gene", "gene" }, 
+    { "snRNA_gene", "gene" },
+    { "miRNA_gene", "gene" },
+    { "lincRNA_gene", "gene" },
+    { "rRNA_gene", "gene" },
+    
+    /*
+     * transcript sub-types:
+     */
+    { "transcript", "transcript" }, 
+    { "mature_transcript", "transcript" }, 
+    { "processed_transcript", "transcript" }, 
+    { "aberrant_processed_transcript", "transcript" },
+    { "ncRNA", "transcript" },
+    { "snRNA", "transcript" },
+    { "miRNA", "transcript" },
+    { "lincRNA", "transcript" },
+    { "rRNA", "transcript" },
+    { "mRNA", "transcript" },
+    // there are many more sub-types of ncRNA...
+    
+    /*
+     * sequence_variant sub-types:
+     */
+    { "sequence_variant", "sequence_variant" },
+    { "feature_variant", "sequence_variant" },
+    { "gene_variant", "sequence_variant" },
+    // NB Ensembl uses NMD_transcript_variant as if a 'transcript'
+    // but we model it here correctly as per the SO
+    { "NMD_transcript_variant", "sequence_variant" },
+    { "transcript_variant", "sequence_variant" },
+    { "structural_variant", "sequence_variant" },
+    
+    /*
+     * no sub-types of exon or CDS yet seen in Ensembl
+     * some added here for testing purposes
+     */
+    { "exon", "exon" },
+    { "coding_exon", "exon" },
+    { "CDS", "CDS" },
+    { "CDS_predicted", "CDS" },
+    
+    /*
+     * terms used in exonerate or PASA GFF
+     */
+    { "protein_match", "protein_match"},
+    { "nucleotide_match", "nucleotide_match"},
+    { "cDNA_match", "nucleotide_match"},
+    
+    /*
+     * used in InterProScan GFF
+     */
+    { "polypeptide", "polypeptide" }
+  };
+  // @formatter:on
+
+  /*
+   * hard-coded list of any parents (direct or indirect) 
+   * that we care about for a term
+   */
+  private Map<String, List<String>> parents;
+
+  private List<String> termsFound;
+
+  private List<String> termsNotFound;
+
+  public SequenceOntologyLite()
+  {
+    termsFound = new ArrayList<String>();
+    termsNotFound = new ArrayList<String>();
+    loadStaticData();
+  }
+
+  /**
+   * Loads hard-coded data into a lookup table of {term, {list_of_parents}}
+   */
+  private void loadStaticData()
+  {
+    parents = new HashMap<String, List<String>>();
+    for (String[] pair : TERMS)
+    {
+      List<String> p = parents.get(pair[0]);
+      if (p == null)
+      {
+        p = new ArrayList<String>();
+        parents.put(pair[0], p);
+      }
+      p.add(pair[1]);
+    }
+  }
+
+  /**
+   * Answers true if 'child' isA 'parent' (including equality). In this
+   * implementation, based only on hard-coded values.
+   */
+  @Override
+  public boolean isA(String child, String parent)
+  {
+    if (child == null || parent == null)
+    {
+      return false;
+    }
+    if (child.equals(parent))
+    {
+      termFound(child);
+      return true;
+    }
+
+    List<String> p = parents.get(child);
+    if (p == null)
+    {
+      termNotFound(child);
+      return false;
+    }
+    termFound(child);
+    if (p.contains(parent))
+    {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Records a valid term queried for, for reporting purposes
+   * 
+   * @param term
+   */
+  private void termFound(String term)
+  {
+    if (!termsFound.contains(term))
+    {
+      synchronized (termsFound)
+      {
+        termsFound.add(term);
+      }
+    }
+  }
+
+  /**
+   * Records an invalid term queried for, for reporting purposes
+   * 
+   * @param term
+   */
+  private void termNotFound(String term)
+  {
+    synchronized (termsNotFound)
+    {
+      if (!termsNotFound.contains(term))
+      {
+        // suppress logging here as it reports Uniprot sequence features
+        // (which do not use SO terms) when auto-configuring feature colours
+        // System.out.println("SO term " + term
+        // + " not known - add to model if needed in "
+        // + getClass().getName());
+        termsNotFound.add(term);
+      }
+    }
+  }
+
+  /**
+   * Sorts (case-insensitive) and returns the list of valid terms queried for
+   */
+  @Override
+  public List<String> termsFound()
+  {
+    synchronized (termsFound)
+    {
+      Collections.sort(termsFound, String.CASE_INSENSITIVE_ORDER);
+      return termsFound;
+    }
+  }
+
+  /**
+   * Sorts (case-insensitive) and returns the list of invalid terms queried for
+   */
+  @Override
+  public List<String> termsNotFound()
+  {
+    synchronized (termsNotFound)
+    {
+      Collections.sort(termsNotFound, String.CASE_INSENSITIVE_ORDER);
+      return termsNotFound;
+    }
+  }
+}
diff --git a/src/jalview/io/packed/DataProvider.java b/src/jalview/io/packed/DataProvider.java
index 65a796b..754ee96 100644
--- a/src/jalview/io/packed/DataProvider.java
+++ b/src/jalview/io/packed/DataProvider.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/packed/JalviewDataset.java b/src/jalview/io/packed/JalviewDataset.java
index 1906fff..e8a8866 100644
--- a/src/jalview/io/packed/JalviewDataset.java
+++ b/src/jalview/io/packed/JalviewDataset.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,13 +20,16 @@
  */
 package jalview.io.packed;
 
+import jalview.api.FeatureColourI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
 import jalview.io.NewickFile;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 
 public class JalviewDataset
 {
@@ -55,7 +58,7 @@ public class JalviewDataset
   /**
    * @return the featureColours
    */
-  public Hashtable getFeatureColours()
+  public Map<String, FeatureColourI> getFeatureColours()
   {
     return featureColours;
   }
@@ -64,7 +67,7 @@ public class JalviewDataset
    * @param featureColours
    *          the featureColours to set
    */
-  public void setFeatureColours(Hashtable featureColours)
+  public void setFeatureColours(Map<String, FeatureColourI> featureColours)
   {
     this.featureColours = featureColours;
   }
@@ -185,7 +188,7 @@ public class JalviewDataset
   /**
    * current set of feature colours
    */
-  Hashtable featureColours;
+  Map<String, FeatureColourI> featureColours;
 
   /**
    * original identity of each sequence in results
@@ -199,7 +202,7 @@ public class JalviewDataset
     seqDetails = new Hashtable();
     al = new ArrayList<AlignmentSet>();
     parentDataset = null;
-    featureColours = new Hashtable();
+    featureColours = new HashMap<String, FeatureColourI>();
   }
 
   /**
@@ -207,9 +210,10 @@ public class JalviewDataset
    * 
    * @param parentAlignment
    */
-  public JalviewDataset(AlignmentI aldataset, Hashtable fc,
-          Hashtable seqDets)
+  public JalviewDataset(AlignmentI aldataset,
+          Map<String, FeatureColourI> fc, Hashtable seqDets)
   {
+    // TODO not used - remove?
     this(aldataset, fc, seqDets, null);
   }
 
@@ -228,8 +232,9 @@ public class JalviewDataset
    *          (may be null) alignment to associate new annotation and trees
    *          with.
    */
-  public JalviewDataset(AlignmentI aldataset, Hashtable fc,
-          Hashtable seqDets, AlignmentI parentAlignment)
+  public JalviewDataset(AlignmentI aldataset,
+          Map<String, FeatureColourI> fc, Hashtable seqDets,
+          AlignmentI parentAlignment)
   {
     this();
     parentDataset = aldataset;
diff --git a/src/jalview/io/packed/ParsePackedSet.java b/src/jalview/io/packed/ParsePackedSet.java
index 4de8a66..41631f9 100644
--- a/src/jalview/io/packed/ParsePackedSet.java
+++ b/src/jalview/io/packed/ParsePackedSet.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,7 @@
  */
 package jalview.io.packed;
 
+import jalview.api.FeatureColourI;
 import jalview.datamodel.AlignmentI;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.FileParse;
@@ -30,7 +31,7 @@ import jalview.io.packed.DataProvider.JvDataType;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Hashtable;
+import java.util.HashMap;
 import java.util.List;
 
 public class ParsePackedSet
@@ -66,7 +67,7 @@ public class ParsePackedSet
         String fmt = null;
         try
         {
-          fmt = new IdentifyFile().Identify(src, false);
+          fmt = new IdentifyFile().identify(src, false);
         } catch (Exception ex)
         {
           exerror = ex;
@@ -157,7 +158,7 @@ public class ParsePackedSet
         // if not, create one.
         if (context.featureColours == null)
         {
-          context.featureColours = new Hashtable();
+          context.featureColours = new HashMap<String, FeatureColourI>();
         }
         try
         {
diff --git a/src/jalview/io/packed/SimpleDataProvider.java b/src/jalview/io/packed/SimpleDataProvider.java
index ac036ca..ff219bf 100644
--- a/src/jalview/io/packed/SimpleDataProvider.java
+++ b/src/jalview/io/packed/SimpleDataProvider.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/vamsas/Datasetsequence.java b/src/jalview/io/vamsas/Datasetsequence.java
index 297b854..370bd2e 100644
--- a/src/jalview/io/vamsas/Datasetsequence.java
+++ b/src/jalview/io/vamsas/Datasetsequence.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -175,10 +175,10 @@ public class Datasetsequence extends DatastoreItem
     boolean modifiedthedoc = false;
     SequenceI sq = (SequenceI) jvobj;
 
-    if (sq.getDatasetSequence() == null && sq.getDBRef() != null)
+    if (sq.getDatasetSequence() == null && sq.getDBRefs() != null)
     {
       // only sync database references for dataset sequences
-      DBRefEntry[] entries = sq.getDBRef();
+      DBRefEntry[] entries = sq.getDBRefs();
       // jalview.datamodel.DBRefEntry dbentry;
       for (int db = 0; db < entries.length; db++)
       {
diff --git a/src/jalview/io/vamsas/DatastoreItem.java b/src/jalview/io/vamsas/DatastoreItem.java
index 215f2d5..2675e10 100644
--- a/src/jalview/io/vamsas/DatastoreItem.java
+++ b/src/jalview/io/vamsas/DatastoreItem.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/vamsas/DatastoreRegistry.java b/src/jalview/io/vamsas/DatastoreRegistry.java
index 9ecd36c..ce3a2a1 100644
--- a/src/jalview/io/vamsas/DatastoreRegistry.java
+++ b/src/jalview/io/vamsas/DatastoreRegistry.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -152,7 +152,8 @@ public class DatastoreRegistry
     return dsregitem;
   }
 
-  protected void finalize()
+  @Override
+  protected void finalize() throws Throwable
   {
     if (dsObjReg != null)
     {
@@ -171,5 +172,6 @@ public class DatastoreRegistry
     {
       dsItemReg.clear();
     }
+    super.finalize();
   }
 }
diff --git a/src/jalview/io/vamsas/Dbref.java b/src/jalview/io/vamsas/Dbref.java
index e7f6b3c..ec2b523 100644
--- a/src/jalview/io/vamsas/Dbref.java
+++ b/src/jalview/io/vamsas/Dbref.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/vamsas/LocalDocSyncObject.java b/src/jalview/io/vamsas/LocalDocSyncObject.java
index 67511dc..2a73ce4 100644
--- a/src/jalview/io/vamsas/LocalDocSyncObject.java
+++ b/src/jalview/io/vamsas/LocalDocSyncObject.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/vamsas/Rangetype.java b/src/jalview/io/vamsas/Rangetype.java
index d94d86c..bfb0c7a 100644
--- a/src/jalview/io/vamsas/Rangetype.java
+++ b/src/jalview/io/vamsas/Rangetype.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/io/vamsas/Sequencefeature.java b/src/jalview/io/vamsas/Sequencefeature.java
index 0f9a664..99f67af 100644
--- a/src/jalview/io/vamsas/Sequencefeature.java
+++ b/src/jalview/io/vamsas/Sequencefeature.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -27,6 +27,7 @@ import jalview.io.VamsasAppDatastore;
 import jalview.util.UrlLink;
 
 import java.util.Enumeration;
+import java.util.Iterator;
 import java.util.Vector;
 
 import uk.ac.vamsas.objects.core.DataSetAnnotations;
@@ -71,6 +72,7 @@ public class Sequencefeature extends Rangetype
     doJvUpdate();
   }
 
+  @Override
   public void addToDocument()
   {
     DataSetAnnotations dsa = (DataSetAnnotations) vobj;
@@ -89,6 +91,7 @@ public class Sequencefeature extends Rangetype
     dataset.addDataSetAnnotations(dsa);
   }
 
+  @Override
   public void addFromDocument()
   {
     DataSetAnnotations dsa = (DataSetAnnotations) vobj;
@@ -106,6 +109,7 @@ public class Sequencefeature extends Rangetype
     bindjvvobj(sf, dsa);
   }
 
+  @Override
   public void conflict()
   {
     log.warn("Untested sequencefeature conflict code");
@@ -118,6 +122,7 @@ public class Sequencefeature extends Rangetype
     addToDocument(); // and create a new feature in the document
   }
 
+  @Override
   public void updateToDoc()
   {
     DataSetAnnotations dsa = (DataSetAnnotations) vobj;
@@ -144,6 +149,7 @@ public class Sequencefeature extends Rangetype
 
   }
 
+  @Override
   public void updateFromDoc()
   {
     DataSetAnnotations dsa = (DataSetAnnotations) vobj;
@@ -229,11 +235,11 @@ public class Sequencefeature extends Rangetype
     }
     if (feature.otherDetails != null)
     {
-      Enumeration iter = feature.otherDetails.keys();
+      Iterator<String> iter = feature.otherDetails.keySet().iterator();
       Vector props = dsa.getPropertyAsReference();
-      while (iter.hasMoreElements())
+      while (iter.hasNext())
       {
-        String key = (String) iter.nextElement();
+        String key = iter.next();
         if (!key.equalsIgnoreCase("score")
                 && !key.equalsIgnoreCase("status"))
         {
diff --git a/src/jalview/io/vamsas/Sequencemapping.java b/src/jalview/io/vamsas/Sequencemapping.java
index 6dbbd6c..686c73d 100644
--- a/src/jalview/io/vamsas/Sequencemapping.java
+++ b/src/jalview/io/vamsas/Sequencemapping.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -386,7 +386,7 @@ public class Sequencemapping extends Rangetype
   private void matchConjugateDBRefs(SequenceI from, SequenceI to,
           jalview.util.MapList smap)
   {
-    if (from.getDBRef() == null && to.getDBRef() == null)
+    if (from.getDBRefs() == null && to.getDBRefs() == null)
     {
       if (jalview.bin.Cache.log.isDebugEnabled())
       {
@@ -400,11 +400,11 @@ public class Sequencemapping extends Rangetype
       jalview.bin.Cache.log.debug("Matching conjugate refs for "
               + from.getName() + " and " + to.getName());
     }
-    jalview.datamodel.DBRefEntry[] fdb = from.getDBRef();
+    jalview.datamodel.DBRefEntry[] fdb = from.getDBRefs();
     jalview.datamodel.DBRefEntry[] tdb = new jalview.datamodel.DBRefEntry[to
-            .getDBRef().length];
-    int tdblen = to.getDBRef().length;
-    System.arraycopy(to.getDBRef(), 0, tdb, 0, tdblen);
+            .getDBRefs().length];
+    int tdblen = to.getDBRefs().length;
+    System.arraycopy(to.getDBRefs(), 0, tdb, 0, tdblen);
     Vector matched = new Vector();
     jalview.util.MapList smapI = smap.getInverse();
     for (int f = 0; f < fdb.length; f++)
diff --git a/src/jalview/io/vamsas/Tree.java b/src/jalview/io/vamsas/Tree.java
index 4d232ee..adcc143 100644
--- a/src/jalview/io/vamsas/Tree.java
+++ b/src/jalview/io/vamsas/Tree.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -291,8 +291,10 @@ public class Tree extends DatastoreItem
       }
     }
     if (alsq.size() < sequences.length)
+    {
       Cache.log
               .warn("Not recovered all alignment sequences for given set of input sequence CIGARS");
+    }
     return alsq;
   }
 
@@ -306,15 +308,18 @@ public class Tree extends DatastoreItem
   public void UpdateSequenceTreeMap(TreePanel tp)
   {
     if (tp == null || tree == null)
+    {
       return;
-    Vector leaves = new Vector();
+    }
+
     if (tp.getTree() == null)
     {
       Cache.log.warn("Not updating SequenceTreeMap for "
               + tree.getVorbaId());
       return;
     }
-    tp.getTree().findLeaves(tp.getTree().getTopNode(), leaves);
+    Vector<SequenceNode> leaves = tp.getTree().findLeaves(
+            tp.getTree().getTopNode());
     Treenode[] tn = tree.getTreenode(); // todo: select nodes for this
     // particular tree
     int sz = tn.length;
@@ -371,8 +376,7 @@ public class Tree extends DatastoreItem
    */
   public Treenode[] makeTreeNodes(NJTree ntree, Newick newick)
   {
-    Vector leaves = new Vector();
-    ntree.findLeaves(ntree.getTopNode(), leaves);
+    Vector<SequenceNode> leaves = ntree.findLeaves(ntree.getTopNode());
     Vector tnv = new Vector();
     Enumeration l = leaves.elements();
     Hashtable nodespecs = new Hashtable();
@@ -473,7 +477,9 @@ public class Tree extends DatastoreItem
         --occurence;
       }
       else
+      {
         bn = null;
+      }
     }
     return bn;
   }
diff --git a/src/jalview/javascript/JSFunctionExec.java b/src/jalview/javascript/JSFunctionExec.java
index 6202cba..1414854 100644
--- a/src/jalview/javascript/JSFunctionExec.java
+++ b/src/jalview/javascript/JSFunctionExec.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -39,7 +39,8 @@ public class JSFunctionExec implements Runnable
     jvlite.setExecutor(this);
   }
 
-  public void finalize()
+  @Override
+  protected void finalize() throws Throwable
   {
     jvlite = null;
     executor = null;
@@ -48,6 +49,7 @@ public class JSFunctionExec implements Runnable
       jsExecQueue.clear();
     }
     jsExecQueue = null;
+    super.finalize();
   }
 
   private Vector jsExecQueue;
@@ -82,6 +84,7 @@ public class JSFunctionExec implements Runnable
     executor = null;
   }
 
+  @Override
   public void run()
   {
     while (jsExecQueue != null)
@@ -164,6 +167,7 @@ public class JSFunctionExec implements Runnable
     final Exception[] jsex = new Exception[1];
     Runnable exec = new Runnable()
     {
+      @Override
       public void run()
       {
         try
@@ -196,7 +200,7 @@ public class JSFunctionExec implements Runnable
             if (jex instanceof netscape.javascript.JSException
                     && jvlite.jsfallbackEnabled)
             {
-              jsex[0] = (netscape.javascript.JSException) jex;
+              jsex[0] = jex;
               if (jvlite.debug)
               {
                 System.err.println("Falling back to javascript: url call");
diff --git a/src/jalview/javascript/JalviewLiteJsApi.java b/src/jalview/javascript/JalviewLiteJsApi.java
index 1de9303..f585621 100644
--- a/src/jalview/javascript/JalviewLiteJsApi.java
+++ b/src/jalview/javascript/JalviewLiteJsApi.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/javascript/JsCallBack.java b/src/jalview/javascript/JsCallBack.java
index 505f80d..655c99c 100644
--- a/src/jalview/javascript/JsCallBack.java
+++ b/src/jalview/javascript/JsCallBack.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/javascript/JsSelectionSender.java b/src/jalview/javascript/JsSelectionSender.java
index cc293b1..902439c 100644
--- a/src/jalview/javascript/JsSelectionSender.java
+++ b/src/jalview/javascript/JsSelectionSender.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -50,7 +50,6 @@ public class JsSelectionSender extends JSFunctionExec implements
     try
     {
       String setid = "";
-      String viewid = "";
       AlignFrame src = _af;
       if (source != null)
       {
@@ -82,19 +81,16 @@ public class JsSelectionSender extends JSFunctionExec implements
           end = seqsel.getEndRes();
         }
       }
-      if (colsel != null && colsel.size() > 0)
+      if (colsel != null && !colsel.isEmpty())
       {
         if (end == -1)
         {
           end = colsel.getMax() + 1;
         }
         cols = new String[colsel.getSelected().size()];
-        int d = 0, r = -1;
         for (int i = 0; i < cols.length; i++)
         {
-          cols[i] = ""
-                  + (1 + ((Integer) colsel.getSelected().elementAt(i))
-                          .intValue());
+          cols[i] = "" + (1 + colsel.getSelected().get(i).intValue());
         }
       }
       else
diff --git a/src/jalview/javascript/MouseOverListener.java b/src/jalview/javascript/MouseOverListener.java
index 26d582e..fcc5040 100644
--- a/src/jalview/javascript/MouseOverListener.java
+++ b/src/jalview/javascript/MouseOverListener.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/javascript/MouseOverStructureListener.java b/src/jalview/javascript/MouseOverStructureListener.java
index 78f383a..1c6eafd 100644
--- a/src/jalview/javascript/MouseOverStructureListener.java
+++ b/src/jalview/javascript/MouseOverStructureListener.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -32,6 +32,7 @@ import jalview.structure.StructureListener;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureMappingcommandSet;
 import jalview.structure.StructureSelectionManager;
+import jalview.util.HttpUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -87,47 +88,43 @@ public class MouseOverStructureListener extends JSFunctionExec implements
     {
       for (int i = 0; i < modelSet.length; i++)
       {
-        // resolve a real filename
-        try
-        {
-          if (new java.net.URL(modelSet[i]).openConnection() != null)
-          {
-            continue;
-          }
-        } catch (Exception x)
-        {
-        }
-        ;
-        try
-        {
-          String db = jvlite.getDocumentBase().toString();
-          db = db.substring(0, db.lastIndexOf("/"));
-          if (new java.net.URL(db + "/" + modelSet[i]).openConnection() != null)
-          {
-            modelSet[i] = db + "/" + modelSet[i];
-            continue;
-          }
-        } catch (Exception x)
-        {
-        }
-        ;
-        try
-        {
-          if (new java.net.URL(jvlite.getCodeBase() + modelSet[i])
-                  .openConnection() != null)
-          {
-            modelSet[i] = jvlite.getCodeBase() + modelSet[i];
-            continue;
-          }
-        } catch (Exception x)
-        {
-        }
-        ;
-
+        modelSet[i] = resolveModelFile(modelSet[i]);
       }
     }
   }
 
+  /**
+   * Returns the first out of: file, file prefixed by document base, or file
+   * prefixed by codebase which can be resolved to a valid URL. If none can,
+   * returns the input parameter value.
+   * 
+   * @param file
+   */
+  public String resolveModelFile(String file)
+  {
+    // TODO reuse JalviewLite.LoadingThread.addProtocol instead
+    if (HttpUtils.isValidUrl(file))
+    {
+      return file;
+    }
+
+    String db = jvlite.getDocumentBase().toString();
+    db = db.substring(0, db.lastIndexOf("/"));
+    String docBaseFile = db + "/" + file;
+    if (HttpUtils.isValidUrl(docBaseFile))
+    {
+      return docBaseFile;
+    }
+
+    String cb = jvlite.getCodeBase() + file;
+    if (HttpUtils.isValidUrl(cb))
+    {
+      return cb;
+    }
+
+    return file;
+  }
+
   @Override
   public String[] getPdbFile()
   {
@@ -301,7 +298,7 @@ public class MouseOverStructureListener extends JSFunctionExec implements
     return _listenerfn;
   }
 
-  public void finalise()
+  public void finalize() throws Throwable
   {
     jvlite = null;
     super.finalize();
diff --git a/src/jalview/jbgui/GAlignExportSettings.java b/src/jalview/jbgui/GAlignExportSettings.java
index fcc38fb..f7a79e0 100644
--- a/src/jalview/jbgui/GAlignExportSettings.java
+++ b/src/jalview/jbgui/GAlignExportSettings.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GAlignFrame.java b/src/jalview/jbgui/GAlignFrame.java
index f2d3249..8a3206b 100644
--- a/src/jalview/jbgui/GAlignFrame.java
+++ b/src/jalview/jbgui/GAlignFrame.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -55,7 +55,6 @@ import javax.swing.JPanel;
 import javax.swing.JRadioButtonMenuItem;
 import javax.swing.JTabbedPane;
 import javax.swing.KeyStroke;
-import javax.swing.SwingUtilities;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
@@ -137,8 +136,6 @@ public class GAlignFrame extends JInternalFrame
 
   public JCheckBoxMenuItem showSeqFeatures = new JCheckBoxMenuItem();
 
-  public JCheckBoxMenuItem showSeqFeaturesHeight = new JCheckBoxMenuItem();
-
   JMenuItem copy = new JMenuItem();
 
   JMenuItem cut = new JMenuItem();
@@ -173,8 +170,14 @@ public class GAlignFrame extends JInternalFrame
 
   protected JMenuItem showTranslation = new JMenuItem();
 
+  protected JMenuItem showReverse = new JMenuItem();
+
+  protected JMenuItem showReverseComplement = new JMenuItem();
+
   protected JMenu showProducts = new JMenu();
 
+  protected JMenuItem runGroovy = new JMenuItem();
+
   protected JMenuItem rnahelicesColour = new JMenuItem();
 
   protected JCheckBoxMenuItem autoCalculate = new JCheckBoxMenuItem();
@@ -289,35 +292,50 @@ public class GAlignFrame extends JInternalFrame
           @Override
           public void mousePressed(MouseEvent evt)
           {
-            if (evt.isControlDown()
-                    || SwingUtilities.isRightMouseButton(evt))
+            if (evt.isPopupTrigger()) // Mac
             {
-              radioItem.removeActionListener(radioItem.getActionListeners()[0]);
-
-              int option = JOptionPane.showInternalConfirmDialog(
-                      jalview.gui.Desktop.desktop,
-                      MessageManager
-                              .getString("label.remove_from_default_list"),
-                      MessageManager
-                              .getString("label.remove_user_defined_colour"),
-                      JOptionPane.YES_NO_OPTION);
-              if (option == JOptionPane.YES_OPTION)
-              {
-                jalview.gui.UserDefinedColours
-                        .removeColourFromDefaults(radioItem.getText());
-                colourMenu.remove(radioItem);
-              }
-              else
+              offerRemoval(radioItem);
+            }
+          }
+
+          @Override
+          public void mouseReleased(MouseEvent evt)
+          {
+            if (evt.isPopupTrigger()) // Windows
+            {
+              offerRemoval(radioItem);
+            }
+          }
+
+          /**
+           * @param radioItem
+           */
+          void offerRemoval(final JRadioButtonMenuItem radioItem)
+          {
+            radioItem.removeActionListener(radioItem.getActionListeners()[0]);
+
+            int option = JOptionPane.showInternalConfirmDialog(
+                    jalview.gui.Desktop.desktop, MessageManager
+                            .getString("label.remove_from_default_list"),
+                    MessageManager
+                            .getString("label.remove_user_defined_colour"),
+                    JOptionPane.YES_NO_OPTION);
+            if (option == JOptionPane.YES_OPTION)
+            {
+              jalview.gui.UserDefinedColours
+                      .removeColourFromDefaults(radioItem.getText());
+              colourMenu.remove(radioItem);
+            }
+            else
+            {
+              radioItem.addActionListener(new ActionListener()
               {
-                radioItem.addActionListener(new ActionListener()
+                @Override
+                public void actionPerformed(ActionEvent evt)
                 {
-                  @Override
-                  public void actionPerformed(ActionEvent evt)
-                  {
-                    userDefinedColour_actionPerformed(evt);
-                  }
-                });
-              }
+                  userDefinedColour_actionPerformed(evt);
+                }
+              });
             }
           }
         });
@@ -1295,8 +1313,10 @@ public class GAlignFrame extends JInternalFrame
             MessageManager.getString("label.show_last"));
     buttonGroup.add(showAutoFirst);
     buttonGroup.add(showAutoLast);
-    showAutoFirst.setSelected(Cache.getDefault(
-            Preferences.SHOW_AUTOCALC_ABOVE, false));
+    final boolean autoFirst = Cache.getDefault(
+            Preferences.SHOW_AUTOCALC_ABOVE, false);
+    showAutoFirst.setSelected(autoFirst);
+    setShowAutoCalculatedAbove(autoFirst);
     showAutoFirst.addActionListener(new ActionListener()
     {
       @Override
@@ -1585,7 +1605,7 @@ public class GAlignFrame extends JInternalFrame
     });
 
     JMenuItem modifyPID = new JMenuItem(
-            MessageManager.getString("label.modify_identity_thereshold"));
+            MessageManager.getString("label.modify_identity_threshold"));
     modifyPID.addActionListener(new ActionListener()
     {
       @Override
@@ -1595,7 +1615,7 @@ public class GAlignFrame extends JInternalFrame
       }
     });
     modifyConservation.setText(MessageManager
-            .getString("label.modify_conservation_thereshold"));
+            .getString("label.modify_conservation_threshold"));
     modifyConservation.addActionListener(new ActionListener()
     {
       @Override
@@ -1684,6 +1704,25 @@ public class GAlignFrame extends JInternalFrame
         showTranslation_actionPerformed(e);
       }
     });
+    showReverse.setText(MessageManager.getString("label.reverse"));
+    showReverse.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        showReverse_actionPerformed(false);
+      }
+    });
+    showReverseComplement.setText(MessageManager
+            .getString("label.reverse_complement"));
+    showReverseComplement.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        showReverse_actionPerformed(true);
+      }
+    });
 
     JMenuItem extractScores = new JMenuItem(
             MessageManager.getString("label.extract_scores"));
@@ -1701,6 +1740,18 @@ public class GAlignFrame extends JInternalFrame
     // for show products actions see AlignFrame.canShowProducts
     showProducts.setText(MessageManager.getString("label.get_cross_refs"));
 
+    runGroovy.setText(MessageManager.getString("label.run_groovy"));
+    runGroovy.setToolTipText(MessageManager
+            .getString("label.run_groovy_tip"));
+    runGroovy.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        runGroovy_actionPerformed();
+      }
+    });
+
     JMenuItem openFeatureSettings = new JMenuItem(
             MessageManager.getString("action.feature_settings"));
     openFeatureSettings.addActionListener(new ActionListener()
@@ -1996,7 +2047,19 @@ public class GAlignFrame extends JInternalFrame
       @Override
       public void mousePressed(MouseEvent e)
       {
-        tabbedPane_mousePressed(e);
+        if (e.isPopupTrigger()) // Mac
+        {
+          tabbedPane_mousePressed(e);
+        }
+      }
+
+      @Override
+      public void mouseReleased(MouseEvent e)
+      {
+        if (e.isPopupTrigger()) // Windows
+        {
+          tabbedPane_mousePressed(e);
+        }
       }
     });
     tabbedPane.addFocusListener(new FocusAdapter()
@@ -2118,6 +2181,19 @@ public class GAlignFrame extends JInternalFrame
         alignmentProperties();
       }
     });
+    JMenuItem selectHighlighted = new JMenuItem(
+            MessageManager.getString("action.select_highlighted_columns"));
+    selectHighlighted.setToolTipText(MessageManager
+            .getString("tooltip.select_highlighted_columns"));
+    al = new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent actionEvent)
+      {
+        selectHighlightedColumns_actionPerformed(actionEvent);
+      }
+    };
+    selectHighlighted.addActionListener(al);
     JMenu tooltipSettingsMenu = new JMenu(
             MessageManager.getString("label.sequence_id_tooltip"));
     JMenu autoAnnMenu = new JMenu(
@@ -2135,6 +2211,7 @@ public class GAlignFrame extends JInternalFrame
     alignFrameMenuBar.add(colourMenu);
     alignFrameMenuBar.add(calculateMenu);
     alignFrameMenuBar.add(webService);
+
     fileMenu.add(fetchSequence);
     fileMenu.add(addSequenceMenu);
     fileMenu.add(reload);
@@ -2153,6 +2230,9 @@ public class GAlignFrame extends JInternalFrame
     fileMenu.add(associatedData);
     fileMenu.addSeparator();
     fileMenu.add(closeMenuItem);
+
+    pasteMenu.add(pasteNew);
+    pasteMenu.add(pasteThis);
     editMenu.add(undoMenuItem);
     editMenu.add(redoMenuItem);
     editMenu.add(cut);
@@ -2173,6 +2253,13 @@ public class GAlignFrame extends JInternalFrame
     // editMenu.addSeparator();
     editMenu.add(padGapsMenuitem);
 
+    showMenu.add(showAllColumns);
+    showMenu.add(showAllSeqs);
+    showMenu.add(showAllhidden);
+    hideMenu.add(hideSelColumns);
+    hideMenu.add(hideSelSequences);
+    hideMenu.add(hideAllSelection);
+    hideMenu.add(hideAllButSelection);
     viewMenu.add(newView);
     viewMenu.add(expandViews);
     viewMenu.add(gatherViews);
@@ -2243,6 +2330,12 @@ public class GAlignFrame extends JInternalFrame
     colourMenu.add(modifyPID);
     colourMenu.add(annotationColour);
     colourMenu.add(rnahelicesColour);
+
+    sort.add(sortIDMenuItem);
+    sort.add(sortLengthMenuItem);
+    sort.add(sortGroupMenuItem);
+    sort.add(sortPairwiseMenuItem);
+    sort.add(sortByTreeMenu);
     calculateMenu.add(sort);
     calculateMenu.add(calculateTree);
     calculateMenu.addSeparator();
@@ -2250,21 +2343,20 @@ public class GAlignFrame extends JInternalFrame
     calculateMenu.add(PCAMenuItem);
     calculateMenu.addSeparator();
     calculateMenu.add(showTranslation);
+    calculateMenu.add(showReverse);
+    calculateMenu.add(showReverseComplement);
     calculateMenu.add(showProducts);
     calculateMenu.add(autoCalculate);
     calculateMenu.add(sortByTree);
     calculateMenu.addSeparator();
+    calculateMenu.add(expandAlignment);
     calculateMenu.add(extractScores);
+    calculateMenu.addSeparator();
+    calculateMenu.add(runGroovy);
+
     webServiceNoServices = new JMenuItem(
             MessageManager.getString("label.no_services"));
     webService.add(webServiceNoServices);
-    pasteMenu.add(pasteNew);
-    pasteMenu.add(pasteThis);
-    sort.add(sortIDMenuItem);
-    sort.add(sortLengthMenuItem);
-    sort.add(sortGroupMenuItem);
-    sort.add(sortPairwiseMenuItem);
-    sort.add(sortByTreeMenu);
     exportImageMenu.add(htmlMenuItem);
     exportImageMenu.add(epsFile);
     exportImageMenu.add(createPNG);
@@ -2276,13 +2368,6 @@ public class GAlignFrame extends JInternalFrame
     this.getContentPane().add(statusPanel, java.awt.BorderLayout.SOUTH);
     statusPanel.add(statusBar, null);
     this.getContentPane().add(tabbedPane, java.awt.BorderLayout.CENTER);
-    showMenu.add(showAllColumns);
-    showMenu.add(showAllSeqs);
-    showMenu.add(showAllhidden);
-    hideMenu.add(hideSelColumns);
-    hideMenu.add(hideSelSequences);
-    hideMenu.add(hideAllSelection);
-    hideMenu.add(hideAllButSelection);
 
     formatMenu.add(font);
     formatMenu.addSeparator();
@@ -2310,13 +2395,39 @@ public class GAlignFrame extends JInternalFrame
     selectMenu.add(grpsFromSelection);
     selectMenu.add(deleteGroups);
     selectMenu.add(annotationColumn);
-    calculateMenu.add(expandAlignment);
+    selectMenu.add(selectHighlighted);
     // TODO - determine if the listenToViewSelections button is needed : see bug
     // JAL-574
     // selectMenu.addSeparator();
     // selectMenu.add(listenToViewSelections);
   }
 
+  protected void selectHighlightedColumns_actionPerformed(
+          ActionEvent actionEvent)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  /**
+   * Generate the reverse sequence (or reverse complement if the flag is true)
+   * and add it to the alignment
+   * 
+   * @param complement
+   */
+  protected void showReverse_actionPerformed(boolean complement)
+  {
+  }
+
+  /**
+   * Try to run script in a Groovy console, having first ensured that this
+   * alignframe is set as currentAlignFrame in Desktop
+   */
+  protected void runGroovy_actionPerformed()
+  {
+
+  }
+
   /**
    * Adds the given action listener and key accelerator to the given menu item.
    * Also saves in a lookup table to support lookup of action by key stroke.
@@ -2458,13 +2569,6 @@ public class GAlignFrame extends JInternalFrame
 
   }
 
-  protected void showSeqFeaturesHeight_actionPerformed(
-          ActionEvent actionEvent)
-  {
-    // TODO Auto-generated method stub
-
-  }
-
   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
   {
     // TODO Auto-generated method stub
@@ -3105,7 +3209,7 @@ public class GAlignFrame extends JInternalFrame
     return this.splitFrame;
   }
 
-  protected void showComplement_actionPerformed(boolean state)
+  protected void showComplement_actionPerformed(boolean complement)
   {
   }
 }
diff --git a/src/jalview/jbgui/GAlignmentPanel.java b/src/jalview/jbgui/GAlignmentPanel.java
index efbc6af..592cbde 100644
--- a/src/jalview/jbgui/GAlignmentPanel.java
+++ b/src/jalview/jbgui/GAlignmentPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GCutAndPasteHtmlTransfer.java b/src/jalview/jbgui/GCutAndPasteHtmlTransfer.java
index ce704fe..0343801 100644
--- a/src/jalview/jbgui/GCutAndPasteHtmlTransfer.java
+++ b/src/jalview/jbgui/GCutAndPasteHtmlTransfer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -106,6 +106,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
     ok.setText(MessageManager.getString("label.new_window"));
     ok.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         ok_actionPerformed(e);
@@ -114,6 +115,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
     cancel.setText(MessageManager.getString("action.close"));
     cancel.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         cancel_actionPerformed(e);
@@ -123,6 +125,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
     close.setText(MessageManager.getString("action.close"));
     close.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         cancel_actionPerformed(e);
@@ -137,6 +140,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
                     .getMenuShortcutKeyMask(), false));
     selectAll.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         selectAll_actionPerformed(e);
@@ -149,6 +153,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
                     .getMenuShortcutKeyMask(), false));
     save.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         save_actionPerformed(e);
@@ -163,15 +168,23 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
     textarea.setFont(new java.awt.Font("Monospaced", Font.PLAIN, 12));
     textarea.addMouseListener(new java.awt.event.MouseAdapter()
     {
+      @Override
       public void mousePressed(MouseEvent e)
       {
         textarea_mousePressed(e);
       }
+
+      @Override
+      public void mouseReleased(MouseEvent e)
+      {
+        textarea_mousePressed(e);
+      }
     });
     editMenu.setText(MessageManager.getString("action.edit"));
     copyItem.setText(MessageManager.getString("action.copy"));
     copyItem.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         copyItem_actionPerformed(e);
diff --git a/src/jalview/jbgui/GCutAndPasteTransfer.java b/src/jalview/jbgui/GCutAndPasteTransfer.java
index 10f4166..c7e90a6 100644
--- a/src/jalview/jbgui/GCutAndPasteTransfer.java
+++ b/src/jalview/jbgui/GCutAndPasteTransfer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -103,6 +103,7 @@ public class GCutAndPasteTransfer extends JInternalFrame
     ok.setText(MessageManager.getString("label.new_window"));
     ok.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         ok_actionPerformed(e);
@@ -111,6 +112,7 @@ public class GCutAndPasteTransfer extends JInternalFrame
     cancel.setText(MessageManager.getString("action.close"));
     cancel.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         cancel_actionPerformed(e);
@@ -124,6 +126,7 @@ public class GCutAndPasteTransfer extends JInternalFrame
                     .getMenuShortcutKeyMask(), false));
     selectAll.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         selectAll_actionPerformed(e);
@@ -136,6 +139,7 @@ public class GCutAndPasteTransfer extends JInternalFrame
                     .getMenuShortcutKeyMask(), false));
     save.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         save_actionPerformed(e);
@@ -152,15 +156,23 @@ public class GCutAndPasteTransfer extends JInternalFrame
     textarea.setFont(new java.awt.Font("Monospaced", Font.PLAIN, 12));
     textarea.addMouseListener(new java.awt.event.MouseAdapter()
     {
+      @Override
       public void mousePressed(MouseEvent e)
       {
-        textarea_mousePressed(e);
+        textarea_mousePressed(e); // on Mac
+      }
+
+      @Override
+      public void mouseReleased(MouseEvent e)
+      {
+        textarea_mousePressed(e); // on Windows
       }
     });
     editMenu.setText(MessageManager.getString("action.edit"));
     pasteMenu.setText(MessageManager.getString("action.paste"));
     pasteMenu.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         pasteMenu_actionPerformed(e);
@@ -169,6 +181,7 @@ public class GCutAndPasteTransfer extends JInternalFrame
     copyItem.setText(MessageManager.getString("action.copy"));
     copyItem.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         copyItem_actionPerformed(e);
diff --git a/src/jalview/jbgui/GDasSourceBrowser.java b/src/jalview/jbgui/GDasSourceBrowser.java
index bbff0ab..ef79988 100644
--- a/src/jalview/jbgui/GDasSourceBrowser.java
+++ b/src/jalview/jbgui/GDasSourceBrowser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GDesktop.java b/src/jalview/jbgui/GDesktop.java
index 8fd5921..10e0822 100644
--- a/src/jalview/jbgui/GDesktop.java
+++ b/src/jalview/jbgui/GDesktop.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GFinder.java b/src/jalview/jbgui/GFinder.java
index af1760f..57b6d07 100644
--- a/src/jalview/jbgui/GFinder.java
+++ b/src/jalview/jbgui/GFinder.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GFontChooser.java b/src/jalview/jbgui/GFontChooser.java
index d4b8906..63c4695 100644
--- a/src/jalview/jbgui/GFontChooser.java
+++ b/src/jalview/jbgui/GFontChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GPCAPanel.java b/src/jalview/jbgui/GPCAPanel.java
index f57845d..8fa2f9f 100644
--- a/src/jalview/jbgui/GPCAPanel.java
+++ b/src/jalview/jbgui/GPCAPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GPDBSearchPanel.java b/src/jalview/jbgui/GPDBSearchPanel.java
deleted file mode 100644
index 64095d0..0000000
--- a/src/jalview/jbgui/GPDBSearchPanel.java
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-
-package jalview.jbgui;
-
-import jalview.gui.Desktop;
-import jalview.gui.JvSwingUtils;
-import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
-import jalview.util.MessageManager;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
-
-import java.awt.BorderLayout;
-import java.awt.Dimension;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.util.Arrays;
-
-import javax.swing.ImageIcon;
-import javax.swing.JButton;
-import javax.swing.JComboBox;
-import javax.swing.JFrame;
-import javax.swing.JInternalFrame;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTabbedPane;
-import javax.swing.JTable;
-import javax.swing.JTextField;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-
-/**
- * GUI layout for PDB Fetch Panel
- * 
- * @author tcnofoegbu
- *
- */
- at SuppressWarnings("serial")
-public abstract class GPDBSearchPanel extends JPanel
-{
-  protected String frameTitle = MessageManager
-          .getString("label.pdb_sequence_getcher");
-
-  protected JInternalFrame mainFrame = new JInternalFrame(frameTitle);
-
-  protected JComboBox<PDBDocField> cmb_searchTarget = new JComboBox<PDBDocField>();
-
-  protected JButton btn_ok = new JButton();
-
-  protected JButton btn_back = new JButton();
-
-  protected JButton btn_cancel = new JButton();
-
-  protected JTextField txt_search = new JTextField(20);
-
-  protected JTable tbl_summary = new JTable()
-  {
-    public String getToolTipText(MouseEvent evt)
-    {
-      String toolTipText = null;
-      java.awt.Point pnt = evt.getPoint();
-      int rowIndex = rowAtPoint(pnt);
-      int colIndex = columnAtPoint(pnt);
-
-      try
-      {
-        toolTipText = getValueAt(rowIndex, colIndex).toString();
-      } catch (Exception e)
-      {
-        e.printStackTrace();
-      }
-      toolTipText = (toolTipText == null ? null
-              : (toolTipText.length() > 500 ? JvSwingUtils.wrapTooltip(
-                      true, toolTipText.subSequence(0, 500) + "...")
-                      : JvSwingUtils.wrapTooltip(true, toolTipText)));
-
-      return toolTipText;
-    }
-  };
-
-  protected StringBuilder errorWarning = new StringBuilder();
-
-  protected JScrollPane scrl_searchResult = new JScrollPane(tbl_summary);
-
-  protected ImageIcon warningImage = new ImageIcon(getClass().getResource(
-          "/images/warning.gif"));
-
-  protected ImageIcon loadingImage = new ImageIcon(getClass().getResource(
-          "/images/loading.gif"));
-
-  protected JLabel lbl_warning = new JLabel(warningImage);
-
-  protected JLabel lbl_loading = new JLabel(loadingImage);
-
-  private JTabbedPane tabbedPane = new JTabbedPane();
-
-  private PDBDocFieldPreferences pdbDocFieldPrefs = new PDBDocFieldPreferences(
-          PreferenceSource.SEARCH_SUMMARY);
-
-  private JPanel pnl_actions = new JPanel();
-
-  private JPanel pnl_results = new JPanel();
-
-  private JPanel pnl_inputs = new JPanel();
-
-  private BorderLayout mainLayout = new BorderLayout();
-
-  protected PDBDocField[] previousWantedFields;
-
-  public GPDBSearchPanel()
-  {
-    try
-    {
-      jbInit();
-      mainFrame.invalidate();
-      mainFrame.pack();
-    } catch (Exception e)
-    {
-      e.printStackTrace();
-    }
-  }
-
-  /**
-   * Initializes the GUI default properties
-   * 
-   * @throws Exception
-   */
-  private void jbInit() throws Exception
-  {
-    lbl_warning.setVisible(false);
-    lbl_warning.setFont(new java.awt.Font("Verdana", 0, 12));
-    lbl_loading.setVisible(false);
-    lbl_loading.setFont(new java.awt.Font("Verdana", 0, 12));
-
-    tbl_summary.setAutoCreateRowSorter(true);
-    tbl_summary.getTableHeader().setReorderingAllowed(false);
-    tbl_summary.addMouseListener(new MouseAdapter()
-    {
-      public void mouseClicked(MouseEvent e)
-      {
-        validateSelection();
-      }
-
-      public void mouseReleased(MouseEvent e)
-      {
-        validateSelection();
-      }
-    });
-
-    btn_back.setFont(new java.awt.Font("Verdana", 0, 12));
-    btn_back.setText(MessageManager.getString("action.back"));
-    btn_back.addActionListener(new java.awt.event.ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        btn_back_ActionPerformed();
-      }
-    });
-
-    btn_ok.setEnabled(false);
-    btn_ok.setFont(new java.awt.Font("Verdana", 0, 12));
-    btn_ok.setText(MessageManager.getString("action.ok"));
-    btn_ok.addActionListener(new java.awt.event.ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        btn_ok_ActionPerformed();
-      }
-    });
-    btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
-    btn_cancel.setText(MessageManager.getString("action.cancel"));
-    btn_cancel.addActionListener(new java.awt.event.ActionListener()
-    {
-      public void actionPerformed(ActionEvent e)
-      {
-        btn_cancel_ActionPerformed();
-      }
-    });
-
-    scrl_searchResult.setPreferredSize(new Dimension(500, 300));
-    scrl_searchResult
-            .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
-
-    cmb_searchTarget.setFont(new java.awt.Font("Verdana", 0, 12));
-    cmb_searchTarget.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        String tooltipText;
-        if ("all".equalsIgnoreCase(getCmbSearchTarget().getSelectedItem()
-                .toString()))
-        {
-          tooltipText = MessageManager.getString("label.search_all");
-        }
-        else if ("pdb id".equalsIgnoreCase(getCmbSearchTarget()
-                .getSelectedItem().toString()))
-        {
-          tooltipText = MessageManager
-                  .getString("label.separate_multiple_accession_ids");
-        }
-        else
-        {
-          tooltipText = MessageManager.formatMessage(
-                  "label.separate_multiple_query_values",
-                  new Object[] { getCmbSearchTarget().getSelectedItem()
-                          .toString() });
-        }
-        txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
-                tooltipText));
-        txt_search_ActionPerformed();
-      }
-    });
-
-    populateCmbSearchTargetOptions();
-
-    txt_search.setFont(new java.awt.Font("Verdana", 0, 12));
-
-    txt_search.addKeyListener(new KeyAdapter()
-    {
-      @Override
-      public void keyPressed(KeyEvent e)
-      {
-        if (e.getKeyCode() == KeyEvent.VK_ENTER)
-        {
-          if (txt_search.getText() == null
-                  || txt_search.getText().isEmpty())
-          {
-            return;
-          }
-          if ("pdb id".equalsIgnoreCase(getCmbSearchTarget()
-                  .getSelectedItem().toString()))
-          {
-            transferToSequenceFetcher(txt_search.getText());
-          }
-        }
-      }
-    });
-
-    txt_search.getDocument().addDocumentListener(new DocumentListener()
-    {
-      @Override
-      public void insertUpdate(DocumentEvent e)
-      {
-        txt_search_ActionPerformed();
-      }
-
-      @Override
-      public void removeUpdate(DocumentEvent e)
-      {
-        txt_search_ActionPerformed();
-      }
-
-      @Override
-      public void changedUpdate(DocumentEvent e)
-      {
-        txt_search_ActionPerformed();
-      }
-    });
-
-    final String searchTabTitle = MessageManager
-            .getString("label.search_result");
-    final String configureCols = MessageManager
-            .getString("label.configure_displayed_columns");
-    ChangeListener changeListener = new ChangeListener()
-    {
-      public void stateChanged(ChangeEvent changeEvent)
-      {
-        JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
-                .getSource();
-        int index = sourceTabbedPane.getSelectedIndex();
-        if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
-        {
-          btn_back.setEnabled(false);
-          btn_cancel.setEnabled(false);
-          btn_ok.setEnabled(false);
-          previousWantedFields = PDBDocFieldPreferences
-                  .getSearchSummaryFields().toArray(new PDBDocField[0]);
-        }
-        if (sourceTabbedPane.getTitleAt(index).equals(searchTabTitle))
-        {
-          btn_back.setEnabled(true);
-          btn_cancel.setEnabled(true);
-          if (wantedFieldsUpdated())
-          {
-            txt_search_ActionPerformed();
-          }
-          else
-          {
-            validateSelection();
-          }
-        }
-      }
-    };
-    tabbedPane.addChangeListener(changeListener);
-    tabbedPane.setPreferredSize(new Dimension(500, 300));
-    tabbedPane.add(searchTabTitle, scrl_searchResult);
-    tabbedPane.add(configureCols, pdbDocFieldPrefs);
-
-    pnl_actions.add(btn_back);
-    pnl_actions.add(btn_ok);
-    pnl_actions.add(btn_cancel);
-
-    pnl_results.add(tabbedPane);
-    pnl_inputs.add(cmb_searchTarget);
-    pnl_inputs.add(txt_search);
-    pnl_inputs.add(lbl_loading);
-    pnl_inputs.add(lbl_warning);
-
-    this.setLayout(mainLayout);
-    this.add(pnl_inputs, java.awt.BorderLayout.NORTH);
-    this.add(pnl_results, java.awt.BorderLayout.CENTER);
-    this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
-    mainFrame.setVisible(true);
-    mainFrame.setContentPane(this);
-    mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
-    Desktop.addInternalFrame(mainFrame, frameTitle, 800, 400);
-  }
-
-  public boolean wantedFieldsUpdated()
-  {
-    if (previousWantedFields == null)
-    {
-      return true;
-    }
-
-    return Arrays.equals(PDBDocFieldPreferences.getSearchSummaryFields()
-            .toArray(new PDBDocField[0]), previousWantedFields) ? false
-            : true;
-
-  }
-
-  public void validateSelection()
-  {
-    if (tbl_summary.getSelectedRows().length > 0)
-    {
-      btn_ok.setEnabled(true);
-    }
-    else
-    {
-      btn_ok.setEnabled(false);
-    }
-  }
-
-  public JComboBox<PDBDocField> getCmbSearchTarget()
-  {
-    return cmb_searchTarget;
-  }
-
-  public JTextField getTxtSearch()
-  {
-    return txt_search;
-  }
-
-  public JInternalFrame getMainFrame()
-  {
-    return mainFrame;
-  }
-
-  public abstract void transferToSequenceFetcher(String ids);
-
-  public abstract void txt_search_ActionPerformed();
-
-  public abstract void btn_ok_ActionPerformed();
-
-  public abstract void btn_back_ActionPerformed();
-
-  public abstract void btn_cancel_ActionPerformed();
-
-  public abstract void populateCmbSearchTargetOptions();
-
-}
diff --git a/src/jalview/jbgui/GPairwiseAlignPanel.java b/src/jalview/jbgui/GPairwiseAlignPanel.java
index e23060f..a1d2092 100644
--- a/src/jalview/jbgui/GPairwiseAlignPanel.java
+++ b/src/jalview/jbgui/GPairwiseAlignPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GPreferences.java b/src/jalview/jbgui/GPreferences.java
index 3458347..830bed7 100644
--- a/src/jalview/jbgui/GPreferences.java
+++ b/src/jalview/jbgui/GPreferences.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,9 +20,11 @@
  */
 package jalview.jbgui;
 
+import jalview.fts.core.FTSDataColumnPreferences;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.fts.service.pdb.PDBFTSRestClient;
 import jalview.gui.JvSwingUtils;
 import jalview.gui.StructureViewer.ViewerType;
-import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
 import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
@@ -44,6 +46,7 @@ import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 
 import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
 import javax.swing.DefaultListCellRenderer;
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
@@ -52,6 +55,7 @@ import javax.swing.JFileChooser;
 import javax.swing.JLabel;
 import javax.swing.JList;
 import javax.swing.JPanel;
+import javax.swing.JRadioButton;
 import javax.swing.JScrollPane;
 import javax.swing.JTabbedPane;
 import javax.swing.JTextField;
@@ -156,6 +160,12 @@ public class GPreferences extends JPanel
 
   protected JTextField chimeraPath = new JTextField();
 
+  protected ButtonGroup mappingMethod = new ButtonGroup();
+
+  protected JRadioButton siftsMapping = new JRadioButton();
+
+  protected JRadioButton nwMapping = new JRadioButton();
+
   /*
    * Colours tab components
    */
@@ -191,7 +201,7 @@ public class GPreferences extends JPanel
   /*
    * Output tab components
    */
-  protected JComboBox<String> epsRendering = new JComboBox<String>();
+  protected JComboBox<Object> epsRendering = new JComboBox<Object>();
 
   protected JLabel userIdWidthlabel = new JLabel();
 
@@ -509,6 +519,7 @@ public class GPreferences extends JPanel
     newLink.setText(MessageManager.getString("action.new"));
     newLink.addActionListener(new java.awt.event.ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         newLink_actionPerformed(e);
@@ -518,6 +529,7 @@ public class GPreferences extends JPanel
     editLink.setText(MessageManager.getString("action.edit"));
     editLink.addActionListener(new java.awt.event.ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         editLink_actionPerformed(e);
@@ -527,6 +539,7 @@ public class GPreferences extends JPanel
     deleteLink.setText(MessageManager.getString("action.delete"));
     deleteLink.addActionListener(new java.awt.event.ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         deleteLink_actionPerformed(e);
@@ -535,6 +548,7 @@ public class GPreferences extends JPanel
 
     linkURLList.addListSelectionListener(new ListSelectionListener()
     {
+      @Override
       public void valueChanged(ListSelectionEvent e)
       {
         int index = linkURLList.getSelectedIndex();
@@ -544,6 +558,7 @@ public class GPreferences extends JPanel
 
     linkNameList.addListSelectionListener(new ListSelectionListener()
     {
+      @Override
       public void valueChanged(ListSelectionEvent e)
       {
         int index = linkNameList.getSelectedIndex();
@@ -571,6 +586,7 @@ public class GPreferences extends JPanel
 
     defaultBrowser.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mouseClicked(MouseEvent e)
       {
         if (e.getClickCount() > 1)
@@ -585,6 +601,7 @@ public class GPreferences extends JPanel
     useProxy.setText(MessageManager.getString("label.use_proxy_server"));
     useProxy.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         useProxy_actionPerformed();
@@ -653,6 +670,7 @@ public class GPreferences extends JPanel
     ok.setText(MessageManager.getString("action.ok"));
     ok.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         ok_actionPerformed(e);
@@ -662,6 +680,7 @@ public class GPreferences extends JPanel
     cancel.setText(MessageManager.getString("action.cancel"));
     cancel.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         cancel_actionPerformed(e);
@@ -693,6 +712,7 @@ public class GPreferences extends JPanel
     minColour.setPreferredSize(new Dimension(40, 20));
     minColour.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mousePressed(MouseEvent e)
       {
         minColour_actionPerformed(minColour);
@@ -707,6 +727,7 @@ public class GPreferences extends JPanel
     maxColour.setPreferredSize(new Dimension(40, 20));
     maxColour.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mousePressed(MouseEvent e)
       {
         maxColour_actionPerformed(maxColour);
@@ -764,7 +785,7 @@ public class GPreferences extends JPanel
     final int width = 400;
     final int height = 22;
     final int lineSpacing = 25;
-    int ypos = 30;
+    int ypos = 15;
 
     structFromPdb.setFont(LABEL_FONT);
     structFromPdb
@@ -859,13 +880,30 @@ public class GPreferences extends JPanel
     structureTab.add(chimeraPath);
 
     ypos += lineSpacing;
-    // scrl_pdbDocFieldConfig.setPreferredSize(new Dimension(450, 100));
-    // scrl_pdbDocFieldConfig
-    // .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
-    // scrl_pdbDocFieldConfig.setBounds();
-    PDBDocFieldPreferences docFieldPref = new PDBDocFieldPreferences(
-            PreferenceSource.PREFERENCES);
-    docFieldPref.setBounds(new Rectangle(10, ypos + 5, 450, 120));
+    nwMapping.setFont(LABEL_FONT);
+    nwMapping.setText(MessageManager.getString("label.nw_mapping"));
+    siftsMapping.setFont(LABEL_FONT);
+    siftsMapping.setText(MessageManager.getString("label.sifts_mapping"));
+    mappingMethod.add(nwMapping);
+    mappingMethod.add(siftsMapping);
+    JPanel mappingPanel = new JPanel();
+    mappingPanel.setFont(LABEL_FONT);
+    TitledBorder mmTitledBorder = new TitledBorder(
+            MessageManager.getString("label.mapping_method"));
+    mmTitledBorder.setTitleFont(LABEL_FONT);
+    mappingPanel.setBorder(mmTitledBorder);
+    mappingPanel.setBounds(new Rectangle(10, ypos, 452, 45));
+    // GridLayout mappingLayout = new GridLayout();
+    mappingPanel.setLayout(new GridLayout());
+    mappingPanel.add(nwMapping);
+    mappingPanel.add(siftsMapping);
+    structureTab.add(mappingPanel);
+
+    ypos += lineSpacing;
+    ypos += lineSpacing;
+    FTSDataColumnPreferences docFieldPref = new FTSDataColumnPreferences(
+            PreferenceSource.PREFERENCES, PDBFTSRestClient.getInstance());
+    docFieldPref.setBounds(new Rectangle(10, ypos, 450, 120));
     structureTab.add(docFieldPref);
 
     return structureTab;
@@ -1018,6 +1056,7 @@ public class GPreferences extends JPanel
     annotations.setBounds(new Rectangle(169, 12, 200, 23));
     annotations.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         annotations_actionPerformed(e);
@@ -1025,6 +1064,7 @@ public class GPreferences extends JPanel
     });
     identity.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         annotations_actionPerformed(e);
@@ -1032,6 +1072,7 @@ public class GPreferences extends JPanel
     });
     showGroupConsensus.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         annotations_actionPerformed(e);
@@ -1045,6 +1086,7 @@ public class GPreferences extends JPanel
             .getString("action.show_unconserved"));
     showUnconserved.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         showunconserved_actionPerformed(e);
@@ -1112,6 +1154,7 @@ public class GPreferences extends JPanel
     startupFileTextfield.setBounds(new Rectangle(172, 310, 330, 20));
     startupFileTextfield.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mouseClicked(MouseEvent e)
       {
         if (e.getClickCount() > 1)
@@ -1193,7 +1236,7 @@ public class GPreferences extends JPanel
             .getString("label.open_overview"));
     openoverv.setHorizontalAlignment(SwingConstants.RIGHT);
     openoverv.setHorizontalTextPosition(SwingConstants.LEFT);
-    openoverv.setText(MessageManager.getString(("label.open_overview")));
+    openoverv.setText(MessageManager.getString("label.open_overview"));
     JPanel jPanel2 = new JPanel();
     jPanel2.setBounds(new Rectangle(7, 17, 158, 310));
     jPanel2.setLayout(new GridLayout(14, 1));
diff --git a/src/jalview/jbgui/GRestInputParamEditDialog.java b/src/jalview/jbgui/GRestInputParamEditDialog.java
index b0f0480..d6a995e 100644
--- a/src/jalview/jbgui/GRestInputParamEditDialog.java
+++ b/src/jalview/jbgui/GRestInputParamEditDialog.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GRestServiceEditorPane.java b/src/jalview/jbgui/GRestServiceEditorPane.java
index c233f63..71b9382 100644
--- a/src/jalview/jbgui/GRestServiceEditorPane.java
+++ b/src/jalview/jbgui/GRestServiceEditorPane.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GRnaStructureViewer.java b/src/jalview/jbgui/GRnaStructureViewer.java
index ad84901..25bf473 100644
--- a/src/jalview/jbgui/GRnaStructureViewer.java
+++ b/src/jalview/jbgui/GRnaStructureViewer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GSequenceLink.java b/src/jalview/jbgui/GSequenceLink.java
index bdd9908..2b88adc 100644
--- a/src/jalview/jbgui/GSequenceLink.java
+++ b/src/jalview/jbgui/GSequenceLink.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -60,16 +60,18 @@ public class GSequenceLink extends Panel
     nameTB.setBounds(new Rectangle(77, 10, 310, 23));
     nameTB.addKeyListener(new KeyAdapter()
     {
+      @Override
       public void keyTyped(KeyEvent e)
       {
         nameTB_keyTyped(e);
       }
     });
     urlTB.setFont(JvSwingUtils.getLabelFont());
-    urlTB.setText("http://www.");
+    urlTB.setText("http://");
     urlTB.setBounds(new Rectangle(78, 40, 309, 23));
     urlTB.addKeyListener(new KeyAdapter()
     {
+      @Override
       public void keyTyped(KeyEvent e)
       {
         urlTB_keyTyped(e);
@@ -88,7 +90,20 @@ public class GSequenceLink extends Panel
     jLabel3.setBounds(new Rectangle(21, 72, 351, 15));
     jLabel4.setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));
     jLabel4.setText(MessageManager.getString("label.use_sequence_id_2"));
-    jLabel4.setBounds(new Rectangle(21, 93, 351, 15));
+    jLabel4.setBounds(new Rectangle(21, 88, 351, 15));
+    jLabel5.setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));
+    jLabel5.setText(MessageManager.getString("label.use_sequence_id_3"));
+    jLabel5.setBounds(new Rectangle(21, 106, 351, 15));
+
+    String lastLabel = MessageManager.getString("label.use_sequence_id_4");
+    if (lastLabel.length() > 0)
+    {
+      // e.g. Spanish version has longer text
+      jLabel6.setFont(new java.awt.Font("Verdana", Font.ITALIC, 11));
+      jLabel6.setText(lastLabel);
+      jLabel6.setBounds(new Rectangle(21, 122, 351, 15));
+    }
+
     jPanel1.setBorder(BorderFactory.createEtchedBorder());
     jPanel1.setLayout(null);
     jPanel1.add(jLabel1);
@@ -97,11 +112,21 @@ public class GSequenceLink extends Panel
     jPanel1.add(jLabel2);
     jPanel1.add(jLabel3);
     jPanel1.add(jLabel4);
+    jPanel1.add(jLabel5);
+
+    int height = 130;
+    if (lastLabel.length() > 0)
+    {
+      jPanel1.add(jLabel6);
+      height = 146;
+    }
+
     this.add(jPanel1, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0,
             GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
-                    5, 4, 6, 5), 390, 130));
+                    5, 4, 6, 5), 390, height));
   }
 
+  @Override
   public void setName(String name)
   {
     nameTB.setText(name);
@@ -112,6 +137,7 @@ public class GSequenceLink extends Panel
     urlTB.setText(url);
   }
 
+  @Override
   public String getName()
   {
     return nameTB.getText();
@@ -149,6 +175,10 @@ public class GSequenceLink extends Panel
 
   JLabel jLabel4 = new JLabel();
 
+  JLabel jLabel5 = new JLabel();
+
+  JLabel jLabel6 = new JLabel();
+
   JPanel jPanel1 = new JPanel();
 
   GridBagLayout gridBagLayout1 = new GridBagLayout();
diff --git a/src/jalview/jbgui/GSliderPanel.java b/src/jalview/jbgui/GSliderPanel.java
index 588f0cf..cc06175 100644
--- a/src/jalview/jbgui/GSliderPanel.java
+++ b/src/jalview/jbgui/GSliderPanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GSplitFrame.java b/src/jalview/jbgui/GSplitFrame.java
index 39b47ff..25288d8 100644
--- a/src/jalview/jbgui/GSplitFrame.java
+++ b/src/jalview/jbgui/GSplitFrame.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -33,6 +33,8 @@ import javax.swing.plaf.basic.BasicInternalFrameUI;
 
 public class GSplitFrame extends JInternalFrame
 {
+  protected static final int DIVIDER_SIZE = 5;
+
   private static final long serialVersionUID = 1L;
 
   private GAlignFrame topFrame;
@@ -41,6 +43,12 @@ public class GSplitFrame extends JInternalFrame
 
   private JSplitPane splitPane;
 
+  /*
+   * proportional position of split divider; saving this allows it to be
+   * restored after hiding one half and resizing
+   */
+  private double dividerRatio;
+
   /**
    * Constructor
    * 
@@ -65,12 +73,23 @@ public class GSplitFrame extends JInternalFrame
     splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topFrame,
             bottomFrame);
     splitPane.setVisible(true);
-    final double ratio = bottomFrame.getHeight() == 0 ? 0.5d : topFrame
-            .getHeight()
-            / (double) (topFrame.getHeight() + bottomFrame.getHeight());
-    splitPane.setDividerLocation(ratio);
-    splitPane.setResizeWeight(ratio);
-    splitPane.setDividerSize(5);
+
+    /*
+     * set divider split at 50:50, or restore saved split if loading from
+     * project
+     */
+    int topFrameHeight = topFrame.getHeight();
+    splitPane.setDividerSize(DIVIDER_SIZE);
+    if (topFrameHeight == 0)
+    {
+      setRelativeDividerLocation(0.5d); // as a proportion
+    }
+    else
+    {
+      int dividerPosition = topFrameHeight + DIVIDER_SIZE / 2;
+      splitPane.setDividerLocation(dividerPosition); // absolute position
+    }
+    splitPane.setResizeWeight(0.5d);
     add(splitPane);
   }
 
@@ -139,11 +158,25 @@ public class GSplitFrame extends JInternalFrame
   }
 
   /**
-   * Make the complement of the specified split component visible or hidden,
-   * adjusting the position of the split divide.
+   * Makes the complement of the specified split component visible or hidden,
+   * restoring or saving the position of the split divide.
    */
   public void setComplementVisible(Object alignFrame, boolean show)
   {
+    /*
+     * save divider ratio on hide, restore on show
+     */
+    if (show)
+    {
+      setRelativeDividerLocation(dividerRatio);
+    }
+    else
+    {
+      this.dividerRatio = splitPane.getDividerLocation()
+              / (double) (splitPane.getHeight() - splitPane
+                      .getDividerSize());
+    }
+
     if (alignFrame == this.topFrame)
     {
       this.bottomFrame.setVisible(show);
@@ -152,12 +185,40 @@ public class GSplitFrame extends JInternalFrame
     {
       this.topFrame.setVisible(show);
     }
-    if (show)
-    {
-      // SplitPane needs nudging to restore 50-50 split
-      // TODO save/restore other ratios
-      splitPane.setDividerLocation(0.5d);
-    }
+
     validate();
   }
+
+  /**
+   * Set the divider location as a proportion (0 <= r <= 1) of the height <br>
+   * Warning: this overloads setDividerLocation(int), and getDividerLocation()
+   * returns the int (pixel count) value
+   * 
+   * @param r
+   */
+  public void setRelativeDividerLocation(double r)
+  {
+    this.dividerRatio = r;
+    splitPane.setDividerLocation(r);
+  }
+
+  /**
+   * Sets the divider location (in pixels from top)
+   * 
+   * @return
+   */
+  protected void setDividerLocation(int p)
+  {
+    splitPane.setDividerLocation(p);
+  }
+
+  /**
+   * Returns the divider location (in pixels from top)
+   * 
+   * @return
+   */
+  protected int getDividerLocation()
+  {
+    return splitPane.getDividerLocation();
+  }
 }
diff --git a/src/jalview/jbgui/GStructureChooser.java b/src/jalview/jbgui/GStructureChooser.java
index c4043c4..3a064d2 100644
--- a/src/jalview/jbgui/GStructureChooser.java
+++ b/src/jalview/jbgui/GStructureChooser.java
@@ -22,24 +22,30 @@
 package jalview.jbgui;
 
 import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.core.FTSDataColumnPreferences;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.fts.service.pdb.PDBFTSRestClient;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.Desktop;
 import jalview.gui.JvSwingUtils;
-import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
 import jalview.util.MessageManager;
-import jalview.ws.dbsources.PDBRestClient;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
 
 import java.awt.BorderLayout;
 import java.awt.CardLayout;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
+import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.swing.ImageIcon;
 import javax.swing.JButton;
@@ -57,6 +63,8 @@ import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.table.TableColumn;
 
 @SuppressWarnings("serial")
 /**
@@ -67,6 +75,12 @@ import javax.swing.event.DocumentListener;
 public abstract class GStructureChooser extends JPanel implements
         ItemListener
 {
+  protected JPanel statusPanel = new JPanel();
+
+  public JLabel statusBar = new JLabel();
+
+  private JPanel pnl_actionsAndStatus = new JPanel(new BorderLayout());
+
   protected String frameTitle = MessageManager
           .getString("label.structure_chooser");
 
@@ -148,8 +162,70 @@ public abstract class GStructureChooser extends JPanel implements
 
   protected static final String VIEWS_LOCAL_PDB = "VIEWS_LOCAL_PDB";
 
-  protected JTable tbl_summary = new JTable()
+  protected JTable tbl_local_pdb = new JTable();
+
+  protected JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
+
+  protected JTabbedPane pnl_filter = new JTabbedPane();
+
+  protected FTSDataColumnPreferences pdbDocFieldPrefs = new FTSDataColumnPreferences(
+          PreferenceSource.STRUCTURE_CHOOSER,
+          PDBFTSRestClient.getInstance());
+
+  protected FTSDataColumnI[] previousWantedFields;
+
+  protected static Map<String, Integer> tempUserPrefs = new HashMap<String, Integer>();
+
+  private JTable tbl_summary = new JTable()
   {
+    private boolean inLayout;
+
+    @Override
+    public boolean getScrollableTracksViewportWidth()
+    {
+      return hasExcessWidth();
+
+    }
+
+    @Override
+    public void doLayout()
+    {
+      if (hasExcessWidth())
+      {
+        autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
+      }
+      inLayout = true;
+      super.doLayout();
+      inLayout = false;
+      autoResizeMode = AUTO_RESIZE_OFF;
+    }
+
+    protected boolean hasExcessWidth()
+    {
+      return getPreferredSize().width < getParent().getWidth();
+    }
+
+    @Override
+    public void columnMarginChanged(ChangeEvent e)
+    {
+      if (isEditing())
+      {
+        removeEditor();
+      }
+      TableColumn resizingColumn = getTableHeader().getResizingColumn();
+      // Need to do this here, before the parent's
+      // layout manager calls getPreferredSize().
+      if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF
+              && !inLayout)
+      {
+        resizingColumn.setPreferredWidth(resizingColumn.getWidth());
+        String colHeader = resizingColumn.getHeaderValue().toString();
+        tempUserPrefs.put(colHeader, resizingColumn.getWidth());
+      }
+      resizeAndRepaint();
+    }
+
+    @Override
     public String getToolTipText(MouseEvent evt)
     {
       String toolTipText = null;
@@ -159,10 +235,14 @@ public abstract class GStructureChooser extends JPanel implements
 
       try
       {
+        if (getValueAt(rowIndex, colIndex) == null)
+        {
+          return null;
+        }
         toolTipText = getValueAt(rowIndex, colIndex).toString();
       } catch (Exception e)
       {
-        e.printStackTrace();
+        // e.printStackTrace();
       }
       toolTipText = (toolTipText == null ? null
               : (toolTipText.length() > 500 ? JvSwingUtils.wrapTooltip(
@@ -175,17 +255,6 @@ public abstract class GStructureChooser extends JPanel implements
 
   protected JScrollPane scrl_foundStructures = new JScrollPane(tbl_summary);
 
-  protected JTable tbl_local_pdb = new JTable();
-
-  protected JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
-
-  private JTabbedPane pnl_filter = new JTabbedPane();
-
-  private PDBDocFieldPreferences pdbDocFieldPrefs = new PDBDocFieldPreferences(
-          PreferenceSource.STRUCTURE_CHOOSER);
-
-  protected PDBDocField[] previousWantedFields;
-
   public GStructureChooser()
   {
     try
@@ -207,39 +276,156 @@ public abstract class GStructureChooser extends JPanel implements
    */
   private void jbInit() throws Exception
   {
+    Integer width = tempUserPrefs.get("structureChooser.width") == null ? 800
+            : tempUserPrefs.get("structureChooser.width");
+    Integer height = tempUserPrefs.get("structureChooser.height") == null ? 400
+            : tempUserPrefs.get("structureChooser.height");
     tbl_summary.setAutoCreateRowSorter(true);
     tbl_summary.getTableHeader().setReorderingAllowed(false);
+    tbl_summary.addMouseListener(new MouseAdapter()
+    {
+      @Override
+      public void mouseClicked(MouseEvent e)
+      {
+        validateSelections();
+      }
+
+      @Override
+      public void mouseReleased(MouseEvent e)
+      {
+        validateSelections();
+      }
+    });
+    tbl_summary.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        validateSelections();
+        switch (evt.getKeyCode())
+        {
+        case KeyEvent.VK_ESCAPE: // escape key
+          mainFrame.dispose();
+          break;
+        case KeyEvent.VK_ENTER: // enter key
+          if (btn_view.isEnabled())
+          {
+            ok_ActionPerformed();
+          }
+          break;
+        case KeyEvent.VK_TAB: // tab key
+          if (evt.isShiftDown())
+          {
+            pnl_filter.requestFocus();
+          }
+          else
+          {
+            btn_view.requestFocus();
+          }
+          evt.consume();
+          break;
+        default:
+          return;
+        }
+      }
+    });
     tbl_local_pdb.setAutoCreateRowSorter(true);
     tbl_local_pdb.getTableHeader().setReorderingAllowed(false);
     tbl_local_pdb.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mouseClicked(MouseEvent e)
       {
-        updateCurrentView();
+        validateSelections();
       }
 
+      @Override
       public void mouseReleased(MouseEvent e)
       {
-        updateCurrentView();
+        validateSelections();
+      }
+    });
+    tbl_local_pdb.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        validateSelections();
+        switch (evt.getKeyCode())
+        {
+        case KeyEvent.VK_ESCAPE: // escape key
+          mainFrame.dispose();
+          break;
+        case KeyEvent.VK_ENTER: // enter key
+          if (btn_view.isEnabled())
+          {
+            ok_ActionPerformed();
+          }
+          break;
+        case KeyEvent.VK_TAB: // tab key
+          if (evt.isShiftDown())
+          {
+            cmb_filterOption.requestFocus();
+          }
+          else
+          {
+            if (btn_view.isEnabled())
+            {
+              btn_view.requestFocus();
+            }
+            else
+            {
+              btn_cancel.requestFocus();
+            }
+          }
+          evt.consume();
+          break;
+        default:
+          return;
+        }
       }
     });
-
     btn_view.setFont(new java.awt.Font("Verdana", 0, 12));
     btn_view.setText(MessageManager.getString("action.view"));
     btn_view.addActionListener(new java.awt.event.ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         ok_ActionPerformed();
       }
     });
+    btn_view.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          ok_ActionPerformed();
+        }
+      }
+    });
+
     btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
     btn_cancel.setText(MessageManager.getString("action.cancel"));
     btn_cancel.addActionListener(new java.awt.event.ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        mainFrame.dispose();
+        closeAction(pnl_filter.getHeight());
+      }
+    });
+    btn_cancel.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          closeAction(pnl_filter.getHeight());
+        }
       }
     });
 
@@ -248,17 +434,27 @@ public abstract class GStructureChooser extends JPanel implements
     btn_pdbFromFile.setText(btn_title + "              ");
     btn_pdbFromFile.addActionListener(new java.awt.event.ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         pdbFromFile_actionPerformed();
       }
     });
+    btn_pdbFromFile.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          pdbFromFile_actionPerformed();
+        }
+      }
+    });
 
-    scrl_foundStructures.setPreferredSize(new Dimension(500, 300));
-    scrl_foundStructures
-            .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+    scrl_foundStructures.setPreferredSize(new Dimension(width, height));
 
-    scrl_localPDB.setPreferredSize(new Dimension(500, 300));
+    scrl_localPDB.setPreferredSize(new Dimension(width, height));
     scrl_localPDB
             .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
 
@@ -266,9 +462,8 @@ public abstract class GStructureChooser extends JPanel implements
     chk_invertFilter.setFont(new java.awt.Font("Verdana", 0, 12));
     chk_rememberSettings.setFont(new java.awt.Font("Verdana", 0, 12));
     chk_rememberSettings.setVisible(false);
-
-    txt_search.setToolTipText(MessageManager
-            .getString("label.enter_pdb_id"));
+    txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
+            MessageManager.getString("label.enter_pdb_id")));
     cmb_filterOption.setToolTipText(MessageManager
             .getString("info.select_filter_option"));
     txt_search.getDocument().addDocumentListener(new DocumentListener()
@@ -321,18 +516,23 @@ public abstract class GStructureChooser extends JPanel implements
             .getString("label.configure_displayed_columns");
     ChangeListener changeListener = new ChangeListener()
     {
+      @Override
       public void stateChanged(ChangeEvent changeEvent)
       {
         JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
                 .getSource();
         int index = sourceTabbedPane.getSelectedIndex();
+        btn_view.setVisible(true);
+        btn_cancel.setVisible(true);
         if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
         {
           btn_view.setEnabled(false);
           btn_cancel.setEnabled(false);
-          previousWantedFields = PDBDocFieldPreferences
+          btn_view.setVisible(false);
+          btn_cancel.setVisible(false);
+          previousWantedFields = pdbDocFieldPrefs
                   .getStructureSummaryFields().toArray(
-                          new PDBRestClient.PDBDocField[0]);
+                          new FTSDataColumnI[0]);
         }
         if (sourceTabbedPane.getTitleAt(index)
                 .equals(foundStructureSummary))
@@ -350,7 +550,7 @@ public abstract class GStructureChooser extends JPanel implements
       }
     };
     pnl_filter.addChangeListener(changeListener);
-    pnl_filter.setPreferredSize(new Dimension(500, 300));
+    pnl_filter.setPreferredSize(new Dimension(width, height));
     pnl_filter.add(foundStructureSummary, scrl_foundStructures);
     pnl_filter.add(configureCols, pdbDocFieldPrefs);
 
@@ -364,12 +564,46 @@ public abstract class GStructureChooser extends JPanel implements
     this.setLayout(mainLayout);
     this.add(pnl_main, java.awt.BorderLayout.NORTH);
     this.add(pnl_switchableViews, java.awt.BorderLayout.CENTER);
-    this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
-
+    // this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
+    statusPanel.setLayout(new GridLayout());
+    pnl_actionsAndStatus.add(pnl_actions, BorderLayout.CENTER);
+    pnl_actionsAndStatus.add(statusPanel, BorderLayout.SOUTH);
+    statusPanel.add(statusBar, null);
+    this.add(pnl_actionsAndStatus, java.awt.BorderLayout.SOUTH);
+
+    mainFrame
+            .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
+            {
+              @Override
+              public void internalFrameClosing(InternalFrameEvent e)
+              {
+                closeAction(pnl_filter.getHeight());
+              }
+            });
     mainFrame.setVisible(true);
     mainFrame.setContentPane(this);
     mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
-    Desktop.addInternalFrame(mainFrame, frameTitle, 800, 400);
+    Integer x = tempUserPrefs.get("structureChooser.x");
+    Integer y = tempUserPrefs.get("structureChooser.y");
+    if (x != null && y != null)
+    {
+      mainFrame.setLocation(x, y);
+    }
+    Desktop.addInternalFrame(mainFrame, frameTitle, width, height);
+  }
+
+  protected void closeAction(int preferredHeight)
+  {
+    // System.out.println(">>>>>>>>>> closing internal frame!!!");
+    // System.out.println("width : " + mainFrame.getWidth());
+    // System.out.println("heigh : " + mainFrame.getHeight());
+    // System.out.println("x : " + mainFrame.getX());
+    // System.out.println("y : " + mainFrame.getY());
+    tempUserPrefs.put("structureChooser.width", pnl_filter.getWidth());
+    tempUserPrefs.put("structureChooser.height", preferredHeight);
+    tempUserPrefs.put("structureChooser.x", mainFrame.getX());
+    tempUserPrefs.put("structureChooser.y", mainFrame.getY());
+    mainFrame.dispose();
   }
 
   public boolean wantedFieldsUpdated()
@@ -379,9 +613,10 @@ public abstract class GStructureChooser extends JPanel implements
       return true;
     }
 
-    return Arrays.equals(PDBDocFieldPreferences.getStructureSummaryFields()
-            .toArray(new PDBRestClient.PDBDocField[0]),
-            previousWantedFields) ? false : true;
+    FTSDataColumnI[] currentWantedFields = pdbDocFieldPrefs
+            .getStructureSummaryFields().toArray(new FTSDataColumnI[0]);
+    return Arrays.equals(currentWantedFields, previousWantedFields) ? false
+            : true;
 
   }
 
@@ -445,6 +680,7 @@ public abstract class GStructureChooser extends JPanel implements
       this.view = view;
     }
 
+    @Override
     public String toString()
     {
       return this.name;
@@ -477,6 +713,7 @@ public abstract class GStructureChooser extends JPanel implements
       this.sequence = seq;
     }
 
+    @Override
     public String toString()
     {
       return name;
@@ -553,6 +790,11 @@ public abstract class GStructureChooser extends JPanel implements
     }
   }
 
+  public JTable getResultTable()
+  {
+    return tbl_summary;
+  }
+
   public JComboBox<FilterOption> getCmbFilterOption()
   {
     return cmb_filterOption;
@@ -560,10 +802,6 @@ public abstract class GStructureChooser extends JPanel implements
 
   protected abstract void stateChanged(ItemEvent e);
 
-  protected abstract void updateCurrentView();
-
-  protected abstract void populateFilterComboBox();
-
   protected abstract void ok_ActionPerformed();
 
   protected abstract void pdbFromFile_actionPerformed();
diff --git a/src/jalview/jbgui/GStructureViewer.java b/src/jalview/jbgui/GStructureViewer.java
index ac2202c..bd5344c 100644
--- a/src/jalview/jbgui/GStructureViewer.java
+++ b/src/jalview/jbgui/GStructureViewer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -114,6 +114,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     pdbFile.setText(MessageManager.getString("label.pdb_file"));
     pdbFile.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         pdbFile_actionPerformed(actionEvent);
@@ -124,6 +125,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     png.setText("PNG");
     png.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         png_actionPerformed(actionEvent);
@@ -134,6 +136,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     eps.setText("EPS");
     eps.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         eps_actionPerformed(actionEvent);
@@ -144,6 +147,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     viewMapping.setText(MessageManager.getString("label.view_mapping"));
     viewMapping.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         viewMapping_actionPerformed(actionEvent);
@@ -156,6 +160,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     fitToWindow.setText(MessageManager.getString("label.fit_to_window"));
     fitToWindow.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         fitToWindow_actionPerformed();
@@ -170,6 +175,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
             .setText(MessageManager.getString("action.background_colour"));
     backGround.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         backGround_actionPerformed(actionEvent);
@@ -179,6 +185,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     seqColour.setText(MessageManager.getString("action.by_sequence"));
     seqColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         seqColour_actionPerformed(actionEvent);
@@ -187,6 +194,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     chainColour.setText(MessageManager.getString("action.by_chain"));
     chainColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         chainColour_actionPerformed(actionEvent);
@@ -195,6 +203,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     chargeColour.setText(MessageManager.getString("label.charge_cysteine"));
     chargeColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         chargeColour_actionPerformed(actionEvent);
@@ -203,6 +212,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     zappoColour.setText(MessageManager.getString("label.zappo"));
     zappoColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         zappoColour_actionPerformed(actionEvent);
@@ -211,6 +221,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     taylorColour.setText(MessageManager.getString("label.taylor"));
     taylorColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         taylorColour_actionPerformed(actionEvent);
@@ -219,6 +230,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     hydroColour.setText(MessageManager.getString("label.hydrophobicity"));
     hydroColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         hydroColour_actionPerformed(actionEvent);
@@ -228,6 +240,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
             .getString("label.strand_propensity"));
     strandColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         strandColour_actionPerformed(actionEvent);
@@ -236,6 +249,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     helixColour.setText(MessageManager.getString("label.helix_propensity"));
     helixColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         helixColour_actionPerformed(actionEvent);
@@ -244,6 +258,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     turnColour.setText(MessageManager.getString("label.turn_propensity"));
     turnColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         turnColour_actionPerformed(actionEvent);
@@ -252,6 +267,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     buriedColour.setText(MessageManager.getString("label.buried_index"));
     buriedColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         buriedColour_actionPerformed(actionEvent);
@@ -261,6 +277,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
             .getString("label.purine_pyrimidine"));
     purinePyrimidineColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         purinePyrimidineColour_actionPerformed(actionEvent);
@@ -270,6 +287,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     userColour.setText(MessageManager.getString("action.user_defined"));
     userColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         userColour_actionPerformed(actionEvent);
@@ -282,6 +300,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
             .getString("label.let_jmol_manage_structure_colours"));
     viewerColour.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         viewerColour_actionPerformed(actionEvent);
@@ -293,6 +312,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
     helpItem.setText(MessageManager.getString("label.jmol_help"));
     helpItem.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         showHelp_actionPerformed(actionEvent);
@@ -302,6 +322,7 @@ public abstract class GStructureViewer extends JInternalFrame implements
             .setText(MessageManager.getString("label.align_structures"));
     alignStructs.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
         alignStructs_actionPerformed(actionEvent);
@@ -364,6 +385,10 @@ public abstract class GStructureViewer extends JInternalFrame implements
   {
   }
 
+  protected void highlightSelection_actionPerformed()
+  {
+  }
+
   protected void viewerColour_actionPerformed(ActionEvent actionEvent)
   {
   }
diff --git a/src/jalview/jbgui/GTreePanel.java b/src/jalview/jbgui/GTreePanel.java
index 6130204..6845e50 100644
--- a/src/jalview/jbgui/GTreePanel.java
+++ b/src/jalview/jbgui/GTreePanel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GUserDefinedColours.java b/src/jalview/jbgui/GUserDefinedColours.java
index d54e98a..5d3a6c4 100644
--- a/src/jalview/jbgui/GUserDefinedColours.java
+++ b/src/jalview/jbgui/GUserDefinedColours.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GWebserviceInfo.java b/src/jalview/jbgui/GWebserviceInfo.java
index f64568a..9a3dc9d 100644
--- a/src/jalview/jbgui/GWebserviceInfo.java
+++ b/src/jalview/jbgui/GWebserviceInfo.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/GWsPreferences.java b/src/jalview/jbgui/GWsPreferences.java
index 98b6c73..295487d 100644
--- a/src/jalview/jbgui/GWsPreferences.java
+++ b/src/jalview/jbgui/GWsPreferences.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/jbgui/PDBDocFieldPreferences.java b/src/jalview/jbgui/PDBDocFieldPreferences.java
deleted file mode 100644
index 8c3aab6..0000000
--- a/src/jalview/jbgui/PDBDocFieldPreferences.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.jbgui;
-
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-
-import javax.swing.JScrollPane;
-import javax.swing.JTable;
-import javax.swing.table.AbstractTableModel;
-
- at SuppressWarnings("serial")
-public class PDBDocFieldPreferences extends JScrollPane
-{
-  protected JTable tbl_pdbDocFieldConfig = new JTable();
-
-  protected JScrollPane scrl_pdbDocFieldConfig = new JScrollPane(
-          tbl_pdbDocFieldConfig);
-
-  private HashMap<String, PDBDocField> map = new HashMap<String, PDBDocField>();
-
-  private static Collection<PDBDocField> searchSummaryFields = new LinkedHashSet<PDBDocField>();
-
-  private static Collection<PDBDocField> structureSummaryFields = new LinkedHashSet<PDBDocField>();
-
-  public enum PreferenceSource
-  {
-    SEARCH_SUMMARY, STRUCTURE_CHOOSER, PREFERENCES;
-  }
-
-  private PreferenceSource currentSource;
-
-  static
-  {
-    searchSummaryFields.add(PDBDocField.PDB_ID);
-    searchSummaryFields.add(PDBDocField.TITLE);
-
-    structureSummaryFields.add(PDBDocField.PDB_ID);
-    structureSummaryFields.add(PDBDocField.TITLE);
-  }
-
-  public PDBDocFieldPreferences(PreferenceSource source)
-  {
-    tbl_pdbDocFieldConfig.setAutoCreateRowSorter(true);
-    this.getViewport().add(tbl_pdbDocFieldConfig);
-    this.currentSource = source;
-
-    String[] columnNames = null;
-    switch (source)
-    {
-    case SEARCH_SUMMARY:
-      columnNames = new String[] { "PDB Field", "Show in search summary" };
-      break;
-    case STRUCTURE_CHOOSER:
-      columnNames = new String[] { "PDB Field", "Show in structure summary" };
-      break;
-    case PREFERENCES:
-      columnNames = new String[] { "PDB Field", "Show in search summary",
-          "Show in structure summary" };
-      break;
-    default:
-      break;
-    }
-
-    Object[][] data = new Object[PDBDocField.values().length - 1][3];
-    int x = 0;
-    for (PDBDocField field : PDBDocField.values())
-    {
-      if (field.getName().equalsIgnoreCase("all"))
-      {
-        continue;
-      }
-
-      switch (source)
-      {
-      case SEARCH_SUMMARY:
-        data[x++] = new Object[] { field.getName(),
-            searchSummaryFields.contains(field) };
-        break;
-      case STRUCTURE_CHOOSER:
-        data[x++] = new Object[] { field.getName(),
-            structureSummaryFields.contains(field) };
-        break;
-      case PREFERENCES:
-        data[x++] = new Object[] { field.getName(),
-            searchSummaryFields.contains(field),
-            structureSummaryFields.contains(field) };
-        break;
-      default:
-        break;
-      }
-      map.put(field.getName(), field);
-    }
-
-    PDBFieldTableModel model = new PDBFieldTableModel(columnNames, data);
-    tbl_pdbDocFieldConfig.setModel(model);
-  }
-
-  public static Collection<PDBDocField> getSearchSummaryFields()
-  {
-    return searchSummaryFields;
-  }
-
-  public static void setSearchSummaryFields(
-          Collection<PDBDocField> searchSummaryFields)
-  {
-    PDBDocFieldPreferences.searchSummaryFields = searchSummaryFields;
-  }
-
-  public static Collection<PDBDocField> getStructureSummaryFields()
-  {
-    return structureSummaryFields;
-  }
-
-  public static void setStructureSummaryFields(
-          Collection<PDBDocField> structureSummaryFields)
-  {
-    PDBDocFieldPreferences.structureSummaryFields = structureSummaryFields;
-  }
-
-  class PDBFieldTableModel extends AbstractTableModel
-  {
-
-    public PDBFieldTableModel(String[] columnNames, Object[][] data)
-    {
-      this.data = data;
-      this.columnNames = columnNames;
-    }
-
-    private Object[][] data;
-
-    private String[] columnNames;
-
-    public int getColumnCount()
-    {
-      return columnNames.length;
-    }
-
-    public int getRowCount()
-    {
-      return data.length;
-    }
-
-    public String getColumnName(int col)
-    {
-      return columnNames[col];
-    }
-
-    public Object getValueAt(int row, int col)
-    {
-      return data[row][col];
-    }
-
-    /*
-     * JTable uses this method to determine the default renderer/ editor for
-     * each cell. If we didn't implement this method, then the last column would
-     * contain text ("true"/"false"), rather than a check box.
-     */
-    public Class getColumnClass(int c)
-    {
-      return getValueAt(0, c).getClass();
-    }
-
-    /*
-     * Don't need to implement this method unless your table's editable.
-     */
-    public boolean isCellEditable(int row, int col)
-    {
-      // Note that the data/cell address is constant,
-      // no matter where the cell appears onscreen.
-      // !isPDBID(row, col) ensures the PDB_Id cell is never editable as it
-      // serves as a unique id for each row.
-      return (col == 1 || col == 2) && !isPDBID(row, col);
-
-    }
-
-    /**
-     * Determines whether the data in a given cell is a PDB ID.
-     * 
-     * @param row
-     * @param col
-     * @return
-     */
-
-    public boolean isPDBID(int row, int col)
-    {
-      boolean matched = false;
-      String name = getValueAt(row, 0).toString();
-      PDBDocField pdbField = map.get(name);
-      if (pdbField == PDBDocField.PDB_ID)
-      {
-        matched = true;
-      }
-      return matched;
-    }
-
-    /*
-     * Don't need to implement this method unless your table's data can change.
-     */
-    public void setValueAt(Object value, int row, int col)
-    {
-      data[row][col] = value;
-      fireTableCellUpdated(row, col);
-
-      String name = getValueAt(row, 0).toString();
-      boolean selected = ((Boolean) value).booleanValue();
-
-      PDBDocField pdbField = map.get(name);
-
-      if (currentSource == PreferenceSource.SEARCH_SUMMARY)
-      {
-        updatePrefs(searchSummaryFields, pdbField, selected);
-      }
-      else if (currentSource == PreferenceSource.STRUCTURE_CHOOSER)
-      {
-        updatePrefs(structureSummaryFields, pdbField, selected);
-      }
-      else if (currentSource == PreferenceSource.PREFERENCES)
-      {
-        if (col == 1)
-        {
-          updatePrefs(searchSummaryFields, pdbField, selected);
-        }
-        else if (col == 2)
-        {
-          updatePrefs(structureSummaryFields, pdbField, selected);
-        }
-      }
-    }
-
-    private void updatePrefs(Collection<PDBDocField> prefConfig,
-            PDBDocField pdbField, boolean selected)
-    {
-      if (prefConfig.contains(pdbField) && !selected)
-      {
-        prefConfig.remove(pdbField);
-      }
-
-      if (!prefConfig.contains(pdbField) && selected)
-      {
-        prefConfig.add(pdbField);
-      }
-    }
-
-  }
-}
diff --git a/src/jalview/json/binding/biojs/BioJSReleasePojo.java b/src/jalview/json/binding/biojs/BioJSReleasePojo.java
index c60cea2..3ece1b1 100644
--- a/src/jalview/json/binding/biojs/BioJSReleasePojo.java
+++ b/src/jalview/json/binding/biojs/BioJSReleasePojo.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/json/binding/biojs/BioJSRepositoryPojo.java b/src/jalview/json/binding/biojs/BioJSRepositoryPojo.java
index cafe762..b549625 100644
--- a/src/jalview/json/binding/biojs/BioJSRepositoryPojo.java
+++ b/src/jalview/json/binding/biojs/BioJSRepositoryPojo.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/json/binding/biojson/v1/AlignmentAnnotationPojo.java b/src/jalview/json/binding/biojson/v1/AlignmentAnnotationPojo.java
index ab350eb..bfbbce8 100644
--- a/src/jalview/json/binding/biojson/v1/AlignmentAnnotationPojo.java
+++ b/src/jalview/json/binding/biojson/v1/AlignmentAnnotationPojo.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -30,17 +30,41 @@ public class AlignmentAnnotationPojo
 
   @Attributes(
     required = false,
-    description = "Label for the Alignment Annotation")
+    description = "Label for the alignment annotation")
   private String label;
 
   @Attributes(
     required = false,
-    description = "Description for the Alignment Annotation")
+    description = "Description for the alignment annotation")
   private String description;
 
   @Attributes(required = false)
   private List<AnnotationPojo> annotations = new ArrayList<AnnotationPojo>();
 
+  @Attributes(
+    required = false,
+    enums = { "0", "1", "2" },
+    description = "Determines the rendering for the annotation<br><ul><li>0 - No graph</li><li>1 - Bar Graph</li><li>2 - Line graph</li></ul>")
+  private int graphType;
+
+  @Attributes(
+    required = false,
+    description = "Reference to the sequence in the alignment<br> if per-sequence annotation")
+  private String sequenceRef;
+
+  @Attributes(
+    required = false,
+    description = "Stores display settings for an annotation")
+  private AnnotationDisplaySettingPojo annotationSettings;
+
+  @Attributes(required = false, description = "Score of the annotation")
+  private double score;
+
+  @Attributes(
+    required = false,
+    description = "The annotation generation source")
+  private String calcId;
+
   public String getLabel()
   {
     return label;
@@ -71,4 +95,55 @@ public class AlignmentAnnotationPojo
     this.annotations = annotations;
   }
 
+  public String getSequenceRef()
+  {
+    return sequenceRef;
+  }
+
+  public void setSequenceRef(String sequenceRef)
+  {
+    this.sequenceRef = sequenceRef;
+  }
+
+  public int getGraphType()
+  {
+    return graphType;
+  }
+
+  public void setGraphType(int graphType)
+  {
+    this.graphType = graphType;
+  }
+
+  public AnnotationDisplaySettingPojo getAnnotationSettings()
+  {
+    return annotationSettings;
+  }
+
+  public void setAnnotationSettings(
+          AnnotationDisplaySettingPojo annotationSettings)
+  {
+    this.annotationSettings = annotationSettings;
+  }
+
+  public double getScore()
+  {
+    return score;
+  }
+
+  public void setScore(double score)
+  {
+    this.score = score;
+  }
+
+  public String getCalcId()
+  {
+    return calcId;
+  }
+
+  public void setCalcId(String calcId)
+  {
+    this.calcId = calcId;
+  }
+
 }
diff --git a/src/jalview/json/binding/biojson/v1/AlignmentPojo.java b/src/jalview/json/binding/biojson/v1/AlignmentPojo.java
index 7b78313..d797e47 100644
--- a/src/jalview/json/binding/biojson/v1/AlignmentPojo.java
+++ b/src/jalview/json/binding/biojson/v1/AlignmentPojo.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/json/binding/biojson/v1/AnnotationDisplaySettingPojo.java b/src/jalview/json/binding/biojson/v1/AnnotationDisplaySettingPojo.java
new file mode 100644
index 0000000..7b33dbf
--- /dev/null
+++ b/src/jalview/json/binding/biojson/v1/AnnotationDisplaySettingPojo.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ ******************************************************************************/
+package jalview.json.binding.biojson.v1;
+
+import com.github.reinert.jjschema.Attributes;
+
+public class AnnotationDisplaySettingPojo
+{
+
+  @Attributes(
+    required = false,
+    description = "Indicates if column label is scaled to fit within the <br>alignment column")
+  private boolean scaleColLabel;
+
+  @Attributes(
+    required = false,
+    description = "Indicates if every column label is displayed.")
+  private boolean showAllColLabels;
+
+  @Attributes(
+    required = false,
+    description = "Indicates if column labels is centred relative to the <br>alignment column")
+  private boolean centreColLabels;
+
+  @Attributes(
+    required = false,
+    description = "Indicates if the Annotation is shown below the alignment")
+  private boolean belowAlignment;
+
+  @Attributes(
+    required = false,
+    description = "Indicates if the annotation row is visible")
+  private boolean visible;
+
+  @Attributes(
+    required = false,
+    description = "Indicates if annotation has a graphical symbol track")
+  private boolean hasIcon;
+
+  public boolean isScaleColLabel()
+  {
+    return scaleColLabel;
+  }
+
+  public void setScaleColLabel(boolean scaleColLabel)
+  {
+    this.scaleColLabel = scaleColLabel;
+  }
+
+  public boolean isShowAllColLabels()
+  {
+    return showAllColLabels;
+  }
+
+  public void setShowAllColLabels(boolean showAllColLabels)
+  {
+    this.showAllColLabels = showAllColLabels;
+  }
+
+  public boolean isCentreColLabels()
+  {
+    return centreColLabels;
+  }
+
+  public void setCentreColLabels(boolean centreColLabels)
+  {
+    this.centreColLabels = centreColLabels;
+  }
+
+  public boolean isBelowAlignment()
+  {
+    return belowAlignment;
+  }
+
+  public void setBelowAlignment(boolean belowAlignment)
+  {
+    this.belowAlignment = belowAlignment;
+  }
+
+  public boolean isVisible()
+  {
+    return visible;
+  }
+
+  public void setVisible(boolean visible)
+  {
+    this.visible = visible;
+  }
+
+  public boolean isHasIcon()
+  {
+    return hasIcon;
+  }
+
+  public void setHasIcon(boolean hasIcon)
+  {
+    this.hasIcon = hasIcon;
+  }
+
+}
diff --git a/src/jalview/json/binding/biojson/v1/AnnotationPojo.java b/src/jalview/json/binding/biojson/v1/AnnotationPojo.java
index d1cb71c..a0bf6cc 100644
--- a/src/jalview/json/binding/biojson/v1/AnnotationPojo.java
+++ b/src/jalview/json/binding/biojson/v1/AnnotationPojo.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -43,6 +43,11 @@ public class AnnotationPojo
   @Attributes(required = false, description = "Value of the annotation")
   private float value;
 
+  @Attributes(
+    required = false,
+    description = "Colour of the annotation position in hex string.")
+  private String colour;
+
   public String getDisplayCharacter()
   {
     return displayCharacter;
@@ -83,4 +88,14 @@ public class AnnotationPojo
     this.value = value;
   }
 
+  public String getColour()
+  {
+    return colour;
+  }
+
+  public void setColour(String colour)
+  {
+    this.colour = colour;
+  }
+
 }
diff --git a/src/jalview/json/binding/biojson/v1/ColourSchemeMapper.java b/src/jalview/json/binding/biojson/v1/ColourSchemeMapper.java
new file mode 100644
index 0000000..25134f7
--- /dev/null
+++ b/src/jalview/json/binding/biojson/v1/ColourSchemeMapper.java
@@ -0,0 +1,111 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.json.binding.biojson.v1;
+
+import jalview.datamodel.AnnotatedCollectionI;
+import jalview.schemes.Blosum62ColourScheme;
+import jalview.schemes.BuriedColourScheme;
+import jalview.schemes.ClustalxColourScheme;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.HelixColourScheme;
+import jalview.schemes.HydrophobicColourScheme;
+import jalview.schemes.NucleotideColourScheme;
+import jalview.schemes.PIDColourScheme;
+import jalview.schemes.PurinePyrimidineColourScheme;
+import jalview.schemes.RNAHelicesColour;
+import jalview.schemes.RNAInteractionColourScheme;
+import jalview.schemes.StrandColourScheme;
+import jalview.schemes.TCoffeeColourScheme;
+import jalview.schemes.TaylorColourScheme;
+import jalview.schemes.TurnColourScheme;
+import jalview.schemes.ZappoColourScheme;
+
+public class ColourSchemeMapper
+{
+  private static ColourSchemeI csZappo, csTaylor, csNucleotide, csPurine,
+          csHelix, csTurn, csStrand, csBuried, csHydro,
+          csRNAInteractionType, csPID, csBlosum62 = null;
+  static
+  {
+    csZappo = new ZappoColourScheme();
+    csTaylor = new TaylorColourScheme();
+    csNucleotide = new NucleotideColourScheme();
+    csPurine = new PurinePyrimidineColourScheme();
+    csHelix = new HelixColourScheme();
+    csTurn = new TurnColourScheme();
+    csStrand = new StrandColourScheme();
+    csBuried = new BuriedColourScheme();
+    csHydro = new HydrophobicColourScheme();
+    csRNAInteractionType = new RNAInteractionColourScheme();
+    csPID = new PIDColourScheme();
+    csBlosum62 = new Blosum62ColourScheme();
+  }
+
+  public static ColourSchemeI getJalviewColourScheme(
+          String colourSchemeName, AnnotatedCollectionI annotCol)
+  {
+    switch (colourSchemeName.toUpperCase())
+    {
+    case "ZAPPO":
+      return csZappo;
+    case "TAYLOR":
+      return csTaylor;
+    case "NUCLEOTIDE":
+      return csNucleotide;
+    case "PURINE":
+    case "PURINE/PYRIMIDINE":
+      return csPurine;
+    case "HELIX":
+    case "HELIX PROPENSITY":
+      return csHelix;
+    case "TURN":
+    case "TURN PROPENSITY":
+      return csTurn;
+    case "STRAND":
+    case "STRAND PROPENSITY":
+      return csStrand;
+    case "BURIED":
+    case "BURIED INDEX":
+      return csBuried;
+    case "HYDRO":
+    case "HYDROPHOBIC":
+      return csHydro;
+    case "RNA INTERACTION TYPE":
+      return csRNAInteractionType;
+    case "PID":
+    case "% IDENTITY":
+      return csPID;
+    case "BLOSUM62":
+      return csBlosum62;
+    case "T-COFFEE SCORES":
+      return (annotCol != null) ? new TCoffeeColourScheme(annotCol) : null;
+    case "RNA HELICES":
+      return (annotCol != null) ? new RNAHelicesColour(annotCol) : null;
+    case "CLUSTAL":
+      return (annotCol != null) ? new ClustalxColourScheme(annotCol, null)
+              : null;
+    case "USER DEFINED":
+      return null;
+    default:
+      return null;
+    }
+  }
+}
diff --git a/src/jalview/json/binding/biojson/v1/JalviewBioJsColorSchemeMapper.java b/src/jalview/json/binding/biojson/v1/JalviewBioJsColorSchemeMapper.java
deleted file mode 100644
index 4d9f8ec..0000000
--- a/src/jalview/json/binding/biojson/v1/JalviewBioJsColorSchemeMapper.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.json.binding.biojson.v1;
-
-import jalview.schemes.Blosum62ColourScheme;
-import jalview.schemes.BuriedColourScheme;
-import jalview.schemes.ColourSchemeI;
-import jalview.schemes.HelixColourScheme;
-import jalview.schemes.HydrophobicColourScheme;
-import jalview.schemes.NucleotideColourScheme;
-import jalview.schemes.PIDColourScheme;
-import jalview.schemes.PurinePyrimidineColourScheme;
-import jalview.schemes.RNAInteractionColourScheme;
-import jalview.schemes.StrandColourScheme;
-import jalview.schemes.TaylorColourScheme;
-import jalview.schemes.TurnColourScheme;
-import jalview.schemes.ZappoColourScheme;
-
-public enum JalviewBioJsColorSchemeMapper
-{
-
-  USER_DEFINED("User Defined", "user defined", null), NONE("None", "foo",
-          null), CLUSTAL("Clustal", "clustal", null), ZAPPO("Zappo",
-          "zappo", new ZappoColourScheme()), TAYLOR("Taylor", "taylor",
-          new TaylorColourScheme()), NUCLEOTIDE("Nucleotide", "nucleotide",
-          new NucleotideColourScheme()), PURINE_PYRIMIDINE(
-          "Purine/Pyrimidine", "purine", new PurinePyrimidineColourScheme()), HELIX_PROPENSITY(
-          "Helix Propensity", "helix", new HelixColourScheme()), TURN_PROPENSITY(
-          "Turn Propensity", "turn", new TurnColourScheme()), STRAND_PROPENSITY(
-          "Strand Propensity", "strand", new StrandColourScheme()), BURIED_INDEX(
-          "Buried Index", "buried", new BuriedColourScheme()), HYDROPHOBIC(
-          "Hydrophobic", "hydro", new HydrophobicColourScheme()),
-
-  // The color types below are not yet supported by BioJs MSA viewer
-  T_COFFE_SCORES("T-Coffee Scores", "T-Coffee Scores", null), RNA_INT_TYPE(
-          "RNA Interaction type", "RNA Interaction type",
-          new RNAInteractionColourScheme()), BLOSUM62("Blosum62",
-          "Blosum62", new Blosum62ColourScheme()), RNA_HELICES(
-          "RNA Helices", "RNA Helices", null), PERCENTAGE_IDENTITY(
-          "% Identity", "pid", new PIDColourScheme());
-
-  private String jalviewName;
-
-  private String bioJsName;
-
-  private ColourSchemeI jvColourScheme;
-
-  private JalviewBioJsColorSchemeMapper(String jalviewName,
-          String bioJsName, ColourSchemeI jvColourScheme)
-  {
-    this.jalviewName = jalviewName;
-    this.bioJsName = bioJsName;
-    this.setJvColourScheme(jvColourScheme);
-  }
-
-  public String getJalviewName()
-  {
-    return jalviewName;
-  }
-
-  public String getBioJsName()
-  {
-    return bioJsName;
-  }
-
-  public ColourSchemeI getJvColourScheme()
-  {
-    return jvColourScheme;
-  }
-
-  public void setJvColourScheme(ColourSchemeI jvColourScheme)
-  {
-    this.jvColourScheme = jvColourScheme;
-  }
-
-}
diff --git a/src/jalview/json/binding/biojson/v1/SequenceFeaturesPojo.java b/src/jalview/json/binding/biojson/v1/SequenceFeaturesPojo.java
index 318eeac..bba9c0f 100644
--- a/src/jalview/json/binding/biojson/v1/SequenceFeaturesPojo.java
+++ b/src/jalview/json/binding/biojson/v1/SequenceFeaturesPojo.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/json/binding/biojson/v1/SequenceGrpPojo.java b/src/jalview/json/binding/biojson/v1/SequenceGrpPojo.java
index f64ec92..b5b8efe 100644
--- a/src/jalview/json/binding/biojson/v1/SequenceGrpPojo.java
+++ b/src/jalview/json/binding/biojson/v1/SequenceGrpPojo.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/json/binding/biojson/v1/SequencePojo.java b/src/jalview/json/binding/biojson/v1/SequencePojo.java
index 112a54b..9439e3a 100644
--- a/src/jalview/json/binding/biojson/v1/SequencePojo.java
+++ b/src/jalview/json/binding/biojson/v1/SequencePojo.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,7 +28,7 @@ public class SequencePojo
     required = true,
     minLength = 3,
     maxLength = 2147483647,
-    description = "Sequence residue characters. An aligned sequence may contain <br>one of the following gap characters “.�?, “-�? or “ �?")
+    description = "Sequence residue characters. An aligned sequence may contain <br>one of the following gap characters &#x201c;.&#x201d;, &#x201c;-&#x201d; or &#x201c; &#x201d;")
   private String seq;
 
   @Attributes(required = true, description = "Sequence name")
diff --git a/src/jalview/math/AlignmentDimension.java b/src/jalview/math/AlignmentDimension.java
index f6198b4..55049ad 100644
--- a/src/jalview/math/AlignmentDimension.java
+++ b/src/jalview/math/AlignmentDimension.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/math/Matrix.java b/src/jalview/math/Matrix.java
index 189ec51..005eda0 100644
--- a/src/jalview/math/Matrix.java
+++ b/src/jalview/math/Matrix.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/math/RotatableMatrix.java b/src/jalview/math/RotatableMatrix.java
index 075ac0e..17026b1 100644
--- a/src/jalview/math/RotatableMatrix.java
+++ b/src/jalview/math/RotatableMatrix.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/renderer/AnnotationRenderer.java b/src/jalview/renderer/AnnotationRenderer.java
index 9610a79..77fb638 100644
--- a/src/jalview/renderer/AnnotationRenderer.java
+++ b/src/jalview/renderer/AnnotationRenderer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,13 +22,16 @@ package jalview.renderer;
 
 import jalview.analysis.AAFrequency;
 import jalview.analysis.CodingUtils;
+import jalview.analysis.Rna;
 import jalview.analysis.StructureFrequency;
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.ProfilesI;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ResidueProperties;
+import jalview.util.Platform;
 
 import java.awt.BasicStroke;
 import java.awt.Color;
@@ -43,8 +46,6 @@ import java.awt.image.ImageObserver;
 import java.util.BitSet;
 import java.util.Hashtable;
 
-import com.stevesoft.pat.Regex;
-
 public class AnnotationRenderer
 {
   private static final int UPPER_TO_LOWER = 'a' - 'A'; // 32
@@ -58,6 +59,70 @@ public class AnnotationRenderer
    */
   private final boolean debugRedraw;
 
+  private int charWidth, endRes, charHeight;
+
+  private boolean validCharWidth, hasHiddenColumns;
+
+  private FontMetrics fm;
+
+  private final boolean MAC = Platform.isAMac();
+
+  boolean av_renderHistogram = true, av_renderProfile = true,
+          av_normaliseProfile = false;
+
+  ColourSchemeI profcolour = null;
+
+  private ColumnSelection columnSelection;
+
+  private ProfilesI hconsensus;
+
+  private Hashtable[] complementConsensus;
+
+  private Hashtable[] hStrucConsensus;
+
+  private boolean av_ignoreGapsConsensus;
+
+  /**
+   * attributes set from AwtRenderPanelI
+   */
+  /**
+   * old image used when data is currently being calculated and cannot be
+   * rendered
+   */
+  private Image fadedImage;
+
+  /**
+   * panel being rendered into
+   */
+  private ImageObserver annotationPanel;
+
+  /**
+   * width of image to render in panel
+   */
+  private int imgWidth;
+
+  /**
+   * offset to beginning of visible area
+   */
+  private int sOffset;
+
+  /**
+   * offset to end of visible area
+   */
+  private int visHeight;
+
+  /**
+   * indicate if the renderer should only render the visible portion of the
+   * annotation given the current view settings
+   */
+  private boolean useClip = true;
+
+  /**
+   * master flag indicating if renderer should ever try to clip. not enabled for
+   * jalview 2.8.1
+   */
+  private boolean canClip = false;
+
   public AnnotationRenderer()
   {
     this(false);
@@ -75,15 +140,26 @@ public class AnnotationRenderer
     this.debugRedraw = debugRedraw;
   }
 
-  public void drawStemAnnot(Graphics g, Annotation[] row_annotations,
-          int lastSSX, int x, int y, int iconOffset, int startRes,
-          int column, boolean validRes, boolean validEnd)
+  /**
+   * Remove any references and resources when this object is no longer required
+   */
+  public void dispose()
+  {
+    hconsensus = null;
+    complementConsensus = null;
+    hStrucConsensus = null;
+    fadedImage = null;
+    annotationPanel = null;
+  }
+
+  void drawStemAnnot(Graphics g, Annotation[] row_annotations, int lastSSX,
+          int x, int y, int iconOffset, int startRes, int column,
+          boolean validRes, boolean validEnd)
   {
     g.setColor(STEM_COLOUR);
     int sCol = (lastSSX / charWidth) + startRes;
     int x1 = lastSSX;
     int x2 = (x * charWidth);
-    Regex closeparen = new Regex("(\\))");
 
     char dc = (column == 0 || row_annotations[column - 1] == null) ? ' '
             : row_annotations[column - 1].secondaryStructure;
@@ -93,15 +169,17 @@ public class AnnotationRenderer
     boolean diffdownstream = !validRes || !validEnd
             || row_annotations[column] == null
             || dc != row_annotations[column].secondaryStructure;
-    // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
-    // If a closing base pair half of the stem, display a backward arrow
-    if (column > 0 && ResidueProperties.isCloseParenRNA(dc))
-    {
 
+    if (column > 0 && Rna.isClosingParenthesis(dc))
+    {
       if (diffupstream)
       // if (validRes && column>1 && row_annotations[column-2]!=null &&
       // dc.equals(row_annotations[column-2].displayCharacter))
       {
+        /*
+         * if new annotation with a closing base pair half of the stem, 
+         * display a backward arrow
+         */
         g.fillPolygon(new int[] { lastSSX + 5, lastSSX + 5, lastSSX },
                 new int[] { y + iconOffset, y + 14 + iconOffset,
                     y + 8 + iconOffset }, 3);
@@ -114,10 +192,13 @@ public class AnnotationRenderer
     }
     else
     {
-
       // display a forward arrow
       if (diffdownstream)
       {
+        /*
+         * if annotation ending with an opeing base pair half of the stem, 
+         * display a forward arrow
+         */
         g.fillPolygon(new int[] { x2 - 5, x2 - 5, x2 }, new int[] {
             y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
         x2 -= 5;
@@ -131,71 +212,7 @@ public class AnnotationRenderer
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
   }
 
-  private int charWidth, endRes, charHeight;
-
-  private boolean validCharWidth, hasHiddenColumns;
-
-  private FontMetrics fm;
-
-  private final boolean MAC = jalview.util.Platform.isAMac();
-
-  boolean av_renderHistogram = true, av_renderProfile = true,
-          av_normaliseProfile = false;
-
-  ColourSchemeI profcolour = null;
-
-  private ColumnSelection columnSelection;
-
-  private Hashtable[] hconsensus;
-
-  private Hashtable[] complementConsensus;
-
-  private Hashtable[] hStrucConsensus;
-
-  private boolean av_ignoreGapsConsensus;
-
-  /**
-   * attributes set from AwtRenderPanelI
-   */
-  /**
-   * old image used when data is currently being calculated and cannot be
-   * rendered
-   */
-  private Image fadedImage;
-
-  /**
-   * panel being rendered into
-   */
-  private ImageObserver annotationPanel;
-
-  /**
-   * width of image to render in panel
-   */
-  private int imgWidth;
-
-  /**
-   * offset to beginning of visible area
-   */
-  private int sOffset;
-
-  /**
-   * offset to end of visible area
-   */
-  private int visHeight;
-
-  /**
-   * indicate if the renderer should only render the visible portion of the
-   * annotation given the current view settings
-   */
-  private boolean useClip = true;
-
-  /**
-   * master flag indicating if renderer should ever try to clip. not enabled for
-   * jalview 2.8.1
-   */
-  private boolean canClip = false;
-
-  public void drawNotCanonicalAnnot(Graphics g, Color nonCanColor,
+  void drawNotCanonicalAnnot(Graphics g, Color nonCanColor,
           Annotation[] row_annotations, int lastSSX, int x, int y,
           int iconOffset, int startRes, int column, boolean validRes,
           boolean validEnd)
@@ -206,7 +223,6 @@ public class AnnotationRenderer
     int sCol = (lastSSX / charWidth) + startRes;
     int x1 = lastSSX;
     int x2 = (x * charWidth);
-    Regex closeparen = new Regex("}|]|<|[a-z]");
 
     String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
             : row_annotations[column - 1].displayCharacter;
@@ -218,8 +234,7 @@ public class AnnotationRenderer
             || !dc.equals(row_annotations[column].displayCharacter);
     // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
     // If a closing base pair half of the stem, display a backward arrow
-    if (column > 0 && closeparen.search(dc))// closeletter_b.search(dc)||closeletter_c.search(dc)||closeletter_d.search(dc)||closecrochet.search(dc))
-                                            // )
+    if (column > 0 && Rna.isClosingParenthesis(dc))
     {
 
       if (diffupstream)
@@ -321,7 +336,7 @@ public class AnnotationRenderer
    * @param column
    * @return
    */
-  public int[] getProfileFor(AlignmentAnnotation aa, int column)
+  int[] getProfileFor(AlignmentAnnotation aa, int column)
   {
     // TODO : consider refactoring the global alignment calculation
     // properties/rendering attributes as a global 'alignment group' which holds
@@ -337,7 +352,7 @@ public class AnnotationRenderer
       {
         // TODO? group consensus for cDNA complement
         return AAFrequency.extractProfile(
-                aa.groupRef.consensusData[column],
+                aa.groupRef.consensusData.get(column),
                 aa.groupRef.getIgnoreGapsConsensus());
       }
       // TODO extend annotation row to enable dynamic and static profile data to
@@ -351,7 +366,8 @@ public class AnnotationRenderer
         }
         else
         {
-          return AAFrequency.extractProfile(hconsensus[column],
+          return AAFrequency.extractProfile(
+hconsensus.get(column),
                   av_ignoreGapsConsensus);
         }
       }
@@ -598,14 +614,9 @@ public class AnnotationRenderer
 
               if (columnSelection != null)
               {
-                for (int n = 0; n < columnSelection.size(); n++)
+                if (columnSelection.contains(column))
                 {
-                  int v = columnSelection.columnAt(n);
-
-                  if (v == column)
-                  {
-                    g.fillRect(x * charWidth, y, charWidth, charHeight);
-                  }
+                  g.fillRect(x * charWidth, y, charWidth, charHeight);
                 }
               }
             }
@@ -755,7 +766,7 @@ public class AnnotationRenderer
                             validEnd);
                     break;
                   }
-
+                  // no break if isRNA - falls through to drawNotCanonicalAnnot!
                 case 'E':
                   if (!isRNA)
                   {
@@ -764,6 +775,7 @@ public class AnnotationRenderer
                             validEnd);
                     break;
                   }
+                  // no break if isRNA - fall through to drawNotCanonicalAnnot!
 
                 case '{':
                 case '}':
@@ -871,7 +883,6 @@ public class AnnotationRenderer
         {
           validRes = true;
         }
-
         // x ++;
 
         if (row.hasIcons)
@@ -886,6 +897,7 @@ public class AnnotationRenderer
                       startRes, column, validRes, validEnd);
               break;
             }
+            // no break if isRNA - fall through to drawNotCanonicalAnnot!
 
           case 'E':
             if (!isRNA)
@@ -894,6 +906,7 @@ public class AnnotationRenderer
                       startRes, column, validRes, validEnd);
               break;
             }
+            // no break if isRNA - fall through to drawNotCanonicalAnnot!
 
           case '(':
           case ')': // Stem case for RNA secondary structure
@@ -1074,15 +1087,15 @@ public class AnnotationRenderer
 
   private Color sdNOTCANONICAL_COLOUR;
 
-  public void drawGlyphLine(Graphics g, Annotation[] row, int lastSSX,
-          int x, int y, int iconOffset, int startRes, int column,
+  void drawGlyphLine(Graphics g, Annotation[] row, int lastSSX, int x,
+          int y, int iconOffset, int startRes, int column,
           boolean validRes, boolean validEnd)
   {
     g.setColor(GLYPHLINE_COLOR);
     g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
   }
 
-  public void drawSheetAnnot(Graphics g, Annotation[] row,
+  void drawSheetAnnot(Graphics g, Annotation[] row,
 
   int lastSSX, int x, int y, int iconOffset, int startRes, int column,
           boolean validRes, boolean validEnd)
@@ -1106,8 +1119,8 @@ public class AnnotationRenderer
 
   }
 
-  public void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX,
-          int x, int y, int iconOffset, int startRes, int column,
+  void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX, int x,
+          int y, int iconOffset, int startRes, int column,
           boolean validRes, boolean validEnd)
   {
     g.setColor(HELIX_COLOUR);
@@ -1166,7 +1179,7 @@ public class AnnotationRenderer
     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
   }
 
-  public void drawLineGraph(Graphics g, AlignmentAnnotation _aa,
+  void drawLineGraph(Graphics g, AlignmentAnnotation _aa,
           Annotation[] aa_annotations, int sRes, int eRes, int y,
           float min, float max, int graphHeight)
   {
@@ -1259,7 +1272,7 @@ public class AnnotationRenderer
     }
   }
 
-  public void drawBarGraph(Graphics g, AlignmentAnnotation _aa,
+  void drawBarGraph(Graphics g, AlignmentAnnotation _aa,
           Annotation[] aa_annotations, int sRes, int eRes, float min,
           float max, int y, boolean renderHistogram, boolean renderProfile,
           boolean normaliseProfile)
@@ -1390,8 +1403,9 @@ public class AnnotationRenderer
             scl = htn * scale * profl[c++];
             lm = ofont.getLineMetrics(dc, 0, 1, g.getFontMetrics()
                     .getFontRenderContext());
-            g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(
-                    wdth, scl / lm.getAscent())));
+            Font font = ofont.deriveFont(AffineTransform.getScaleInstance(
+                    wdth, scl / lm.getAscent()));
+            g.setFont(font);
             lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g);
 
             // Debug - render boxes around characters
diff --git a/src/jalview/renderer/AwtRenderPanelI.java b/src/jalview/renderer/AwtRenderPanelI.java
index dd18bd6..f3a580b 100644
--- a/src/jalview/renderer/AwtRenderPanelI.java
+++ b/src/jalview/renderer/AwtRenderPanelI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/renderer/ScaleRenderer.java b/src/jalview/renderer/ScaleRenderer.java
new file mode 100644
index 0000000..f09a74d
--- /dev/null
+++ b/src/jalview/renderer/ScaleRenderer.java
@@ -0,0 +1,132 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.renderer;
+
+import jalview.api.AlignViewportI;
+import jalview.datamodel.SequenceI;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Calculate and display alignment rulers
+ * 
+ * @author jprocter
+ *
+ */
+public class ScaleRenderer
+{
+  public final class ScaleMark
+  {
+    public final boolean major;
+
+    public final int column;
+
+    public final String text;
+
+    ScaleMark(boolean isMajor, int col, String txt)
+    {
+      major = isMajor;
+      column = col;
+      text = txt;
+    }
+  }
+
+  /**
+   * calculate positions markers on the alignment ruler
+   * 
+   * @param av
+   * @param startx
+   *          left-most column in visible view
+   * @param endx
+   *          - right-most column in visible view
+   * @return List of ScaleMark holding boolean: true/false for major/minor mark,
+   *         marker position in alignment column coords, a String to be rendered
+   *         at the position (or null)
+   */
+  public List<ScaleMark> calculateMarks(AlignViewportI av, int startx,
+          int endx)
+  {
+    int scalestartx = (startx / 10) * 10;
+
+    SequenceI refSeq = av.getAlignment().getSeqrep();
+    int refSp = 0, refStartI = 0, refEndI = -1;
+    if (refSeq != null)
+    {
+      // find bounds and set origin appopriately
+      // locate first visible position for this sequence
+      int[] refbounds = av.getColumnSelection()
+              .locateVisibleBoundsOfSequence(refSeq);
+
+      refSp = refbounds[0];
+      refStartI = refbounds[4];
+      refEndI = refbounds[5];
+      scalestartx = refSp + ((scalestartx - refSp) / 10) * 10;
+    }
+
+    if (refSeq == null && scalestartx % 10 == 0)
+    {
+      scalestartx += 5;
+    }
+    List<ScaleMark> marks = new ArrayList<ScaleMark>();
+    String string;
+    int refN, iadj;
+    // todo: add a 'reference origin column' to set column number relative to
+    for (int i = scalestartx; i < endx; i += 5)
+    {
+      if (((i - refSp) % 10) == 0)
+      {
+        if (refSeq == null)
+        {
+          iadj = av.getColumnSelection().adjustForHiddenColumns(i - 1) + 1;
+          string = String.valueOf(iadj);
+        }
+        else
+        {
+          iadj = av.getColumnSelection().adjustForHiddenColumns(i - 1);
+          refN = refSeq.findPosition(iadj);
+          // TODO show bounds if position is a gap
+          // - ie L--R -> "1L|2R" for
+          // marker
+          if (iadj < refStartI)
+          {
+            string = String.valueOf(iadj - refStartI);
+          }
+          else if (iadj > refEndI)
+          {
+            string = "+" + String.valueOf(iadj - refEndI);
+          }
+          else
+          {
+            string = String.valueOf(refN) + refSeq.getCharAt(iadj);
+          }
+        }
+        marks.add(new ScaleMark(true, i - startx - 1, string));
+      }
+      else
+      {
+        marks.add(new ScaleMark(false, i - startx - 1, null));
+      }
+    }
+    return marks;
+  }
+
+}
diff --git a/src/jalview/renderer/seqfeatures/FeatureRenderer.java b/src/jalview/renderer/seqfeatures/FeatureRenderer.java
index d05d158..ad8e1ad 100644
--- a/src/jalview/renderer/seqfeatures/FeatureRenderer.java
+++ b/src/jalview/renderer/seqfeatures/FeatureRenderer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,8 +20,10 @@
  */
 package jalview.renderer.seqfeatures;
 
+import jalview.api.AlignViewportI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
 
 import java.awt.AlphaComposite;
 import java.awt.Color;
@@ -30,8 +32,7 @@ import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 
-public class FeatureRenderer extends
-        jalview.viewmodel.seqfeatures.FeatureRendererModel
+public class FeatureRenderer extends FeatureRendererModel
 {
 
   FontMetrics fm;
@@ -50,6 +51,18 @@ public class FeatureRenderer extends
 
   boolean av_validCharWidth, av_isShowSeqFeatureHeight;
 
+  private Integer currentColour;
+
+  /**
+   * Constructor given a viewport
+   * 
+   * @param viewport
+   */
+  public FeatureRenderer(AlignViewportI viewport)
+  {
+    this.av = viewport;
+  }
+
   protected void updateAvConfig()
   {
     av_charHeight = av.getCharHeight();
@@ -168,21 +181,27 @@ public class FeatureRenderer extends
 
   BufferedImage offscreenImage;
 
+  @Override
   public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
   {
     return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
   }
 
   /**
-   * This is used by the Molecule Viewer and Overview to get the accurate
-   * colourof the rendered sequence
+   * This is used by Structure Viewers and the Overview Window to get the
+   * feature colour of the rendered sequence, returned as an RGB value
+   * 
+   * @param defaultColour
+   * @param seq
+   * @param column
+   * @return
    */
-  public synchronized int findFeatureColour(int initialCol,
+  public synchronized int findFeatureColour(int defaultColour,
           final SequenceI seq, int column)
   {
     if (!av.isShowSequenceFeatures())
     {
-      return initialCol;
+      return defaultColour;
     }
 
     SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
@@ -209,7 +228,7 @@ public class FeatureRenderer extends
 
     if (lastSequenceFeatures == null || sfSize == 0)
     {
-      return initialCol;
+      return defaultColour;
     }
 
     if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
@@ -230,7 +249,7 @@ public class FeatureRenderer extends
 
     if (offscreenImage != null)
     {
-      offscreenImage.setRGB(0, 0, initialCol);
+      offscreenImage.setRGB(0, 0, defaultColour);
       drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
 
       return offscreenImage.getRGB(0, 0);
@@ -241,11 +260,11 @@ public class FeatureRenderer extends
 
       if (currentColour == null)
       {
-        return initialCol;
+        return defaultColour;
       }
       else
       {
-        return ((Integer) currentColour).intValue();
+        return currentColour.intValue();
       }
     }
 
@@ -261,6 +280,19 @@ public class FeatureRenderer extends
 
   int epos;
 
+  /**
+   * Draws the sequence on the graphics context, or just determines the colour
+   * that would be drawn (if flag offscreenrender is true).
+   * 
+   * @param g
+   * @param seq
+   * @param start
+   *          start column (or sequence position in offscreenrender mode)
+   * @param end
+   *          end column (not used in offscreenrender mode)
+   * @param y1
+   *          vertical offset at which to draw on the graphics
+   */
   public synchronized void drawSequence(Graphics g, final SequenceI seq,
           int start, int end, int y1)
   {
@@ -298,12 +330,10 @@ public class FeatureRenderer extends
     }
 
     sfSize = lastSequenceFeatures.length;
-    String type;
     for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
     {
-      type = renderOrder[renderIndex];
-
-      if (type == null || !showFeatureOfType(type))
+      String type = renderOrder[renderIndex];
+      if (!showFeatureOfType(type))
       {
         continue;
       }
@@ -318,16 +348,16 @@ public class FeatureRenderer extends
           continue;
         }
 
-        if (featureGroups != null
-                && sequenceFeature.featureGroup != null
-                && sequenceFeature.featureGroup.length() != 0
-                && featureGroups.containsKey(sequenceFeature.featureGroup)
-                && !featureGroups.get(sequenceFeature.featureGroup)
-                        .booleanValue())
+        if (featureGroupNotShown(sequenceFeature))
         {
           continue;
         }
 
+        /*
+         * check feature overlaps the visible part of the alignment, 
+         * unless doing offscreenRender (to the Overview window or a 
+         * structure viewer) which is not limited 
+         */
         if (!offscreenRender
                 && (sequenceFeature.getBegin() > epos || sequenceFeature
                         .getEnd() < spos))
@@ -335,35 +365,43 @@ public class FeatureRenderer extends
           continue;
         }
 
+        Color featureColour = getColour(sequenceFeature);
+        boolean isContactFeature = sequenceFeature.isContactFeature();
+
         if (offscreenRender && offscreenImage == null)
         {
-          if (sequenceFeature.begin <= start
-                  && sequenceFeature.end >= start)
+          /*
+           * offscreen mode with no image (image is only needed if transparency 
+           * is applied to feature colours) - just check feature is rendered at 
+           * the requested position (start == sequence position in this mode)
+           */
+          boolean featureIsAtPosition = sequenceFeature.begin <= start
+                  && sequenceFeature.end >= start;
+          if (isContactFeature)
+          {
+            featureIsAtPosition = sequenceFeature.begin == start
+                    || sequenceFeature.end == start;
+          }
+          if (featureIsAtPosition)
           {
             // this is passed out to the overview and other sequence renderers
             // (e.g. molecule viewer) to get displayed colour for rendered
             // sequence
-            currentColour = new Integer(getColour(sequenceFeature).getRGB());
+            currentColour = new Integer(featureColour.getRGB());
             // used to be retreived from av.featuresDisplayed
             // currentColour = av.featuresDisplayed
             // .get(sequenceFeatures[sfindex].type);
 
           }
         }
-        else if (sequenceFeature.type.equals("disulfide bond"))
+        else if (isContactFeature)
         {
           renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
-                  seq.findIndex(sequenceFeature.begin) - 1,
-                  getColour(sequenceFeature)
-                  // new Color(((Integer) av.featuresDisplayed
-                  // .get(sequenceFeatures[sfindex].type)).intValue())
-                  , start, end, y1);
+                  seq.findIndex(sequenceFeature.begin) - 1, featureColour,
+                  start, end, y1);
           renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1,
-                  seq.findIndex(sequenceFeature.end) - 1,
-                  getColour(sequenceFeature)
-                  // new Color(((Integer) av.featuresDisplayed
-                  // .get(sequenceFeatures[sfindex].type)).intValue())
-                  , start, end, y1);
+                  seq.findIndex(sequenceFeature.end) - 1, featureColour,
+                  start, end, y1);
 
         }
         else if (showFeature(sequenceFeature))
@@ -374,22 +412,20 @@ public class FeatureRenderer extends
             renderScoreFeature(g, seq,
                     seq.findIndex(sequenceFeature.begin) - 1,
                     seq.findIndex(sequenceFeature.end) - 1,
-                    getColour(sequenceFeature), start, end, y1,
+                    featureColour, start, end, y1,
                     normaliseScore(sequenceFeature));
           }
           else
           {
             renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
                     seq.findIndex(sequenceFeature.end) - 1,
-                    getColour(sequenceFeature), start, end, y1);
+                    featureColour, start, end, y1);
           }
         }
-
       }
-
     }
 
-    if (transparency != 1.0f && g != null && transparencyAvailable)
+    if (transparency != 1.0f && g != null)
     {
       Graphics2D g2 = (Graphics2D) g;
       g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
@@ -397,17 +433,22 @@ public class FeatureRenderer extends
     }
   }
 
-  boolean transparencyAvailable = true;
-
-  protected void setTransparencyAvailable(boolean isTransparencyAvailable)
-  {
-    transparencyAvailable = isTransparencyAvailable;
-  }
-
-  @Override
-  public boolean isTransparencyAvailable()
+  /**
+   * Answers true if the feature belongs to a feature group which is not
+   * currently displayed, else false
+   * 
+   * @param sequenceFeature
+   * @return
+   */
+  protected boolean featureGroupNotShown(
+          final SequenceFeature sequenceFeature)
   {
-    return transparencyAvailable;
+    return featureGroups != null
+            && sequenceFeature.featureGroup != null
+            && sequenceFeature.featureGroup.length() != 0
+            && featureGroups.containsKey(sequenceFeature.featureGroup)
+            && !featureGroups.get(sequenceFeature.featureGroup)
+                    .booleanValue();
   }
 
   /**
@@ -415,6 +456,7 @@ public class FeatureRenderer extends
    * discover and display.
    * 
    */
+  @Override
   public void featuresAdded()
   {
     lastSeq = null;
diff --git a/src/jalview/rest/RestHandler.java b/src/jalview/rest/RestHandler.java
index 76cf184..3ca0419 100644
--- a/src/jalview/rest/RestHandler.java
+++ b/src/jalview/rest/RestHandler.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemabinding/version2/.castor.cdr b/src/jalview/schemabinding/version2/.castor.cdr
deleted file mode 100644
index 361fb7c..0000000
--- a/src/jalview/schemabinding/version2/.castor.cdr
+++ /dev/null
@@ -1,50 +0,0 @@
-#Thu Sep 03 10:55:37 BST 2015
-jalview.schemabinding.version2.ThresholdLine=jalview.schemabinding.version2.descriptors.ThresholdLineDescriptor
-jalview.schemabinding.version2.SequenceSetProperties=jalview.schemabinding.version2.descriptors.SequenceSetPropertiesDescriptor
-jalview.schemabinding.version2.StructureState=jalview.schemabinding.version2.descriptors.StructureStateDescriptor
-jalview.schemabinding.version2.CalcIdParam=jalview.schemabinding.version2.descriptors.CalcIdParamDescriptor
-jalview.schemabinding.version2.Tree=jalview.schemabinding.version2.descriptors.TreeDescriptor
-jalview.schemabinding.version2.JalviewModel=jalview.schemabinding.version2.descriptors.JalviewModelDescriptor
-jalview.schemabinding.version2.SequenceType=jalview.schemabinding.version2.descriptors.SequenceTypeDescriptor
-jalview.schemabinding.version2.OtherData=jalview.schemabinding.version2.descriptors.OtherDataDescriptor
-jalview.schemabinding.version2.Setting=jalview.schemabinding.version2.descriptors.SettingDescriptor
-jalview.schemabinding.version2.AlcodonFrame=jalview.schemabinding.version2.descriptors.AlcodonFrameDescriptor
-jalview.schemabinding.version2.AnnotationElement=jalview.schemabinding.version2.descriptors.AnnotationElementDescriptor
-jalview.schemabinding.version2.SecondaryStructure=jalview.schemabinding.version2.descriptors.SecondaryStructureDescriptor
-jalview.schemabinding.version2.SequenceSet=jalview.schemabinding.version2.descriptors.SequenceSetDescriptor
-jalview.schemabinding.version2.Viewport=jalview.schemabinding.version2.descriptors.ViewportDescriptor
-jalview.schemabinding.version2.RnaViewer=jalview.schemabinding.version2.descriptors.RnaViewerDescriptor
-jalview.schemabinding.version2.MapListType=jalview.schemabinding.version2.descriptors.MapListTypeDescriptor
-jalview.schemabinding.version2.Property=jalview.schemabinding.version2.descriptors.PropertyDescriptor
-jalview.schemabinding.version2.UserColourScheme=jalview.schemabinding.version2.descriptors.UserColourSchemeDescriptor
-jalview.schemabinding.version2.DBRef=jalview.schemabinding.version2.descriptors.DBRefDescriptor
-jalview.schemabinding.version2.AlcodMap=jalview.schemabinding.version2.descriptors.AlcodMapDescriptor
-jalview.schemabinding.version2.Annotation=jalview.schemabinding.version2.descriptors.AnnotationDescriptor
-jalview.schemabinding.version2.Wsparameters=jalview.schemabinding.version2.descriptors.WsparametersDescriptor
-jalview.schemabinding.version2.JSeq=jalview.schemabinding.version2.descriptors.JSeqDescriptor
-jalview.schemabinding.version2.Sequence=jalview.schemabinding.version2.descriptors.SequenceDescriptor
-jalview.schemabinding.version2.WebServiceParameterSet=jalview.schemabinding.version2.descriptors.WebServiceParameterSetDescriptor
-jalview.schemabinding.version2.Alcodon=jalview.schemabinding.version2.descriptors.AlcodonDescriptor
-jalview.schemabinding.version2.AnnotationColours=jalview.schemabinding.version2.descriptors.AnnotationColoursDescriptor
-jalview.schemabinding.version2.Pdbids=jalview.schemabinding.version2.descriptors.PdbidsDescriptor
-jalview.schemabinding.version2.AnnotationColourScheme=jalview.schemabinding.version2.descriptors.AnnotationColourSchemeDescriptor
-jalview.schemabinding.version2.Mapping=jalview.schemabinding.version2.descriptors.MappingDescriptor
-jalview.schemabinding.version2.MappingChoice=jalview.schemabinding.version2.descriptors.MappingChoiceDescriptor
-jalview.schemabinding.version2.Group=jalview.schemabinding.version2.descriptors.GroupDescriptor
-jalview.schemabinding.version2.Feature=jalview.schemabinding.version2.descriptors.FeatureDescriptor
-jalview.schemabinding.version2.JalviewModelSequence=jalview.schemabinding.version2.descriptors.JalviewModelSequenceDescriptor
-jalview.schemabinding.version2.UserColours=jalview.schemabinding.version2.descriptors.UserColoursDescriptor
-jalview.schemabinding.version2.Colour=jalview.schemabinding.version2.descriptors.ColourDescriptor
-jalview.schemabinding.version2.MapListFrom=jalview.schemabinding.version2.descriptors.MapListFromDescriptor
-jalview.schemabinding.version2.PdbentryItem=jalview.schemabinding.version2.descriptors.PdbentryItemDescriptor
-jalview.schemabinding.version2.JGroup=jalview.schemabinding.version2.descriptors.JGroupDescriptor
-jalview.schemabinding.version2.FeatureSettings=jalview.schemabinding.version2.descriptors.FeatureSettingsDescriptor
-jalview.schemabinding.version2.VamsasModel=jalview.schemabinding.version2.descriptors.VamsasModelDescriptor
-jalview.schemabinding.version2.JalviewUserColours=jalview.schemabinding.version2.descriptors.JalviewUserColoursDescriptor
-jalview.schemabinding.version2.MapListTo=jalview.schemabinding.version2.descriptors.MapListToDescriptor
-jalview.schemabinding.version2.Pdbentry=jalview.schemabinding.version2.descriptors.PdbentryDescriptor
-jalview.schemabinding.version2.HiddenColumns=jalview.schemabinding.version2.descriptors.HiddenColumnsDescriptor
-jalview.schemabinding.version2.Features=jalview.schemabinding.version2.descriptors.FeaturesDescriptor
-jalview.schemabinding.version2.DseqFor=jalview.schemabinding.version2.descriptors.DseqForDescriptor
-jalview.schemabinding.version2.VAMSAS=jalview.schemabinding.version2.descriptors.VAMSASDescriptor
-jalview.schemabinding.version2.MappingChoiceItem=jalview.schemabinding.version2.descriptors.MappingChoiceItemDescriptor
diff --git a/src/jalview/schemabinding/version2/JSeq.java b/src/jalview/schemabinding/version2/JSeq.java
index 9ca6708..7c6308e 100644
--- a/src/jalview/schemabinding/version2/JSeq.java
+++ b/src/jalview/schemabinding/version2/JSeq.java
@@ -72,6 +72,16 @@ public class JSeq implements java.io.Serializable
   private boolean _has_hidden;
 
   /**
+   * Field _viewreference.
+   */
+  private boolean _viewreference;
+
+  /**
+   * keeps track of state for field: _viewreference
+   */
+  private boolean _has_viewreference;
+
+  /**
    * Field _featuresList.
    */
   private java.util.Vector _featuresList;
@@ -256,6 +266,13 @@ public class JSeq implements java.io.Serializable
   }
 
   /**
+     */
+  public void deleteViewreference()
+  {
+    this._has_viewreference = false;
+  }
+
+  /**
    * Method enumerateFeatures.
    * 
    * @return an Enumeration over all jalview.schemabinding.version2.Features
@@ -549,6 +566,16 @@ public class JSeq implements java.io.Serializable
   }
 
   /**
+   * Returns the value of field 'viewreference'.
+   * 
+   * @return the value of field 'Viewreference'.
+   */
+  public boolean getViewreference()
+  {
+    return this._viewreference;
+  }
+
+  /**
    * Method hasColour.
    * 
    * @return true if at least one Colour has been added
@@ -589,6 +616,16 @@ public class JSeq implements java.io.Serializable
   }
 
   /**
+   * Method hasViewreference.
+   * 
+   * @return true if at least one Viewreference has been added
+   */
+  public boolean hasViewreference()
+  {
+    return this._has_viewreference;
+  }
+
+  /**
    * Returns the value of field 'hidden'.
    * 
    * @return the value of field 'Hidden'.
@@ -616,6 +653,16 @@ public class JSeq implements java.io.Serializable
   }
 
   /**
+   * Returns the value of field 'viewreference'.
+   * 
+   * @return the value of field 'Viewreference'.
+   */
+  public boolean isViewreference()
+  {
+    return this._viewreference;
+  }
+
+  /**
    * 
    * 
    * @param out
@@ -1004,6 +1051,18 @@ public class JSeq implements java.io.Serializable
   }
 
   /**
+   * Sets the value of field 'viewreference'.
+   * 
+   * @param viewreference
+   *          the value of field 'viewreference'.
+   */
+  public void setViewreference(final boolean viewreference)
+  {
+    this._viewreference = viewreference;
+    this._has_viewreference = true;
+  }
+
+  /**
    * Method unmarshal.
    * 
    * @param reader
diff --git a/src/jalview/schemabinding/version2/descriptors/AnnotationColoursDescriptor.java b/src/jalview/schemabinding/version2/descriptors/AnnotationColoursDescriptor.java
index f269bdf..5739d90 100644
--- a/src/jalview/schemabinding/version2/descriptors/AnnotationColoursDescriptor.java
+++ b/src/jalview/schemabinding/version2/descriptors/AnnotationColoursDescriptor.java
@@ -11,6 +11,7 @@ package jalview.schemabinding.version2.descriptors;
 //- Imported classes and packages -/
 //---------------------------------/
 
+import jalview.schemabinding.version2.AnnotationColours;
 
 /**
  * Class AnnotationColoursDescriptor.
diff --git a/src/jalview/schemabinding/version2/descriptors/FeaturesDescriptor.java b/src/jalview/schemabinding/version2/descriptors/FeaturesDescriptor.java
index 5a5b4a9..107c06d 100644
--- a/src/jalview/schemabinding/version2/descriptors/FeaturesDescriptor.java
+++ b/src/jalview/schemabinding/version2/descriptors/FeaturesDescriptor.java
@@ -11,6 +11,7 @@ package jalview.schemabinding.version2.descriptors;
 //- Imported classes and packages -/
 //---------------------------------/
 
+import jalview.schemabinding.version2.Features;
 
 /**
  * Class FeaturesDescriptor.
diff --git a/src/jalview/schemabinding/version2/descriptors/JSeqDescriptor.java b/src/jalview/schemabinding/version2/descriptors/JSeqDescriptor.java
index 0f000bb..28f23b2 100644
--- a/src/jalview/schemabinding/version2/descriptors/JSeqDescriptor.java
+++ b/src/jalview/schemabinding/version2/descriptors/JSeqDescriptor.java
@@ -334,6 +334,61 @@ public class JSeqDescriptor extends
       fieldValidator.setValidator(typeValidator);
     }
     desc.setValidator(fieldValidator);
+    // -- _viewreference
+    desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl(
+            java.lang.Boolean.TYPE, "_viewreference", "viewreference",
+            org.exolab.castor.xml.NodeType.Attribute);
+    handler = new org.exolab.castor.xml.XMLFieldHandler()
+    {
+      public java.lang.Object getValue(java.lang.Object object)
+              throws IllegalStateException
+      {
+        JSeq target = (JSeq) object;
+        if (!target.hasViewreference())
+        {
+          return null;
+        }
+        return (target.getViewreference() ? java.lang.Boolean.TRUE
+                : java.lang.Boolean.FALSE);
+      }
+
+      public void setValue(java.lang.Object object, java.lang.Object value)
+              throws IllegalStateException, IllegalArgumentException
+      {
+        try
+        {
+          JSeq target = (JSeq) object;
+          // if null, use delete method for optional primitives
+          if (value == null)
+          {
+            target.deleteViewreference();
+            return;
+          }
+          target.setViewreference(((java.lang.Boolean) value)
+                  .booleanValue());
+        } catch (java.lang.Exception ex)
+        {
+          throw new IllegalStateException(ex.toString());
+        }
+      }
+
+      public java.lang.Object newInstance(java.lang.Object parent)
+      {
+        return null;
+      }
+    };
+    desc.setHandler(handler);
+    desc.setMultivalued(false);
+    addFieldDescriptor(desc);
+
+    // -- validation code for: _viewreference
+    fieldValidator = new org.exolab.castor.xml.FieldValidator();
+    { // -- local scope
+      org.exolab.castor.xml.validators.BooleanValidator typeValidator;
+      typeValidator = new org.exolab.castor.xml.validators.BooleanValidator();
+      fieldValidator.setValidator(typeValidator);
+    }
+    desc.setValidator(fieldValidator);
     // -- initialize element descriptors
 
     // -- _featuresList
diff --git a/src/jalview/schemabinding/version2/descriptors/JalviewUserColoursDescriptor.java b/src/jalview/schemabinding/version2/descriptors/JalviewUserColoursDescriptor.java
index 77efa7e..d65de13 100644
--- a/src/jalview/schemabinding/version2/descriptors/JalviewUserColoursDescriptor.java
+++ b/src/jalview/schemabinding/version2/descriptors/JalviewUserColoursDescriptor.java
@@ -72,6 +72,7 @@ public class JalviewUserColoursDescriptor extends
     desc.setImmutable(true);
     handler = new org.exolab.castor.xml.XMLFieldHandler()
     {
+      @Override
       public java.lang.Object getValue(java.lang.Object object)
               throws IllegalStateException
       {
@@ -79,6 +80,7 @@ public class JalviewUserColoursDescriptor extends
         return target.getSchemeName();
       }
 
+      @Override
       public void setValue(java.lang.Object object, java.lang.Object value)
               throws IllegalStateException, IllegalArgumentException
       {
@@ -92,6 +94,7 @@ public class JalviewUserColoursDescriptor extends
         }
       }
 
+      @Override
       public java.lang.Object newInstance(java.lang.Object parent)
       {
         return null;
@@ -119,6 +122,7 @@ public class JalviewUserColoursDescriptor extends
     desc.setImmutable(true);
     handler = new org.exolab.castor.xml.XMLFieldHandler()
     {
+      @Override
       public java.lang.Object getValue(java.lang.Object object)
               throws IllegalStateException
       {
@@ -126,6 +130,7 @@ public class JalviewUserColoursDescriptor extends
         return target.getVersion();
       }
 
+      @Override
       public void setValue(java.lang.Object object, java.lang.Object value)
               throws IllegalStateException, IllegalArgumentException
       {
@@ -139,6 +144,7 @@ public class JalviewUserColoursDescriptor extends
         }
       }
 
+      @Override
       public java.lang.Object newInstance(java.lang.Object parent)
       {
         return null;
@@ -163,6 +169,7 @@ public class JalviewUserColoursDescriptor extends
             org.exolab.castor.xml.NodeType.Element);
     handler = new org.exolab.castor.xml.XMLFieldHandler()
     {
+      @Override
       public java.lang.Object getValue(java.lang.Object object)
               throws IllegalStateException
       {
@@ -170,6 +177,7 @@ public class JalviewUserColoursDescriptor extends
         return target.getColour();
       }
 
+      @Override
       public void setValue(java.lang.Object object, java.lang.Object value)
               throws IllegalStateException, IllegalArgumentException
       {
@@ -183,6 +191,7 @@ public class JalviewUserColoursDescriptor extends
         }
       }
 
+      @Override
       public void resetValue(Object object) throws IllegalStateException,
               IllegalArgumentException
       {
@@ -196,6 +205,7 @@ public class JalviewUserColoursDescriptor extends
         }
       }
 
+      @Override
       public java.lang.Object newInstance(java.lang.Object parent)
       {
         return new Colour();
@@ -222,6 +232,7 @@ public class JalviewUserColoursDescriptor extends
    * 
    * @return the access mode specified for this class.
    */
+  @Override
   public org.exolab.castor.mapping.AccessMode getAccessMode()
   {
     return null;
@@ -232,6 +243,7 @@ public class JalviewUserColoursDescriptor extends
    * 
    * @return the identity field, null if this class has no identity.
    */
+  @Override
   public org.exolab.castor.mapping.FieldDescriptor getIdentity()
   {
     return super.getIdentity();
@@ -242,6 +254,7 @@ public class JalviewUserColoursDescriptor extends
    * 
    * @return the Java class represented by this descriptor.
    */
+  @Override
   public java.lang.Class getJavaClass()
   {
     return jalview.schemabinding.version2.JalviewUserColours.class;
@@ -252,6 +265,7 @@ public class JalviewUserColoursDescriptor extends
    * 
    * @return the namespace prefix to use when marshaling as XML.
    */
+  @Override
   public java.lang.String getNameSpacePrefix()
   {
     return _nsPrefix;
@@ -262,6 +276,7 @@ public class JalviewUserColoursDescriptor extends
    * 
    * @return the namespace URI used when marshaling and unmarshaling as XML.
    */
+  @Override
   public java.lang.String getNameSpaceURI()
   {
     return _nsURI;
@@ -273,6 +288,7 @@ public class JalviewUserColoursDescriptor extends
    * @return a specific validator for the class described by this
    *         ClassDescriptor.
    */
+  @Override
   public org.exolab.castor.xml.TypeValidator getValidator()
   {
     return this;
@@ -283,6 +299,7 @@ public class JalviewUserColoursDescriptor extends
    * 
    * @return the XML Name for the Class being described.
    */
+  @Override
   public java.lang.String getXMLName()
   {
     return _xmlName;
@@ -294,6 +311,7 @@ public class JalviewUserColoursDescriptor extends
    * @return true if XML schema definition of this Class is that of a global
    *         element or element with anonymous type definition.
    */
+  @Override
   public boolean isElementDefinition()
   {
     return _elementDefinition;
diff --git a/src/jalview/schemabinding/version2/descriptors/UserColourSchemeDescriptor.java b/src/jalview/schemabinding/version2/descriptors/UserColourSchemeDescriptor.java
index 527b16b..df9ab07 100644
--- a/src/jalview/schemabinding/version2/descriptors/UserColourSchemeDescriptor.java
+++ b/src/jalview/schemabinding/version2/descriptors/UserColourSchemeDescriptor.java
@@ -11,6 +11,7 @@ package jalview.schemabinding.version2.descriptors;
 //- Imported classes and packages -/
 //---------------------------------/
 
+import jalview.schemabinding.version2.UserColourScheme;
 
 /**
  * Class UserColourSchemeDescriptor.
diff --git a/src/jalview/schemabinding/version2/descriptors/VamsasModelDescriptor.java b/src/jalview/schemabinding/version2/descriptors/VamsasModelDescriptor.java
index fc7f9ba..3e26611 100644
--- a/src/jalview/schemabinding/version2/descriptors/VamsasModelDescriptor.java
+++ b/src/jalview/schemabinding/version2/descriptors/VamsasModelDescriptor.java
@@ -11,6 +11,7 @@ package jalview.schemabinding.version2.descriptors;
 //- Imported classes and packages -/
 //---------------------------------/
 
+import jalview.schemabinding.version2.VamsasModel;
 
 /**
  * Class VamsasModelDescriptor.
diff --git a/src/jalview/schemes/AnnotationColourGradient.java b/src/jalview/schemes/AnnotationColourGradient.java
index ed8077f..e9578c1 100644
--- a/src/jalview/schemes/AnnotationColourGradient.java
+++ b/src/jalview/schemes/AnnotationColourGradient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/Blosum62ColourScheme.java b/src/jalview/schemes/Blosum62ColourScheme.java
index 9e226de..7047025 100644
--- a/src/jalview/schemes/Blosum62ColourScheme.java
+++ b/src/jalview/schemes/Blosum62ColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,16 +20,19 @@
  */
 package jalview.schemes;
 
-import jalview.analysis.AAFrequency;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
+import jalview.util.Comparison;
 
 import java.awt.Color;
 import java.util.Map;
 
 public class Blosum62ColourScheme extends ResidueColourScheme
 {
+  private static final Color LIGHT_BLUE = new Color(204, 204, 255);
+  private static final Color DARK_BLUE = new Color(154, 154, 255);
+
   public Blosum62ColourScheme()
   {
     super();
@@ -44,7 +47,7 @@ public class Blosum62ColourScheme extends ResidueColourScheme
       res -= ('a' - 'A');
     }
 
-    if (consensus == null || j >= consensus.length || consensus[j] == null
+    if (consensus == null || consensus.get(j) == null
             || (threshold != 0 && !aboveThreshold(res, j)))
     {
       return Color.white;
@@ -52,14 +55,16 @@ public class Blosum62ColourScheme extends ResidueColourScheme
 
     Color currentColour;
 
-    if (!jalview.util.Comparison.isGap(res))
+    if (!Comparison.isGap(res))
     {
-      String max = (String) consensus[j].get(AAFrequency.MAXRESIDUE);
+      /*
+       * test if this is the consensus (or joint consensus) residue
+       */
+      String max = consensus.get(j).getModalResidue();
 
       if (max.indexOf(res) > -1)
       {
-        // TODO use a constant here?
-        currentColour = new Color(154, 154, 255);
+        currentColour = DARK_BLUE;
       }
       else
       {
@@ -74,8 +79,7 @@ public class Blosum62ColourScheme extends ResidueColourScheme
 
         if (c > 0)
         {
-          // TODO use a constant here?
-          currentColour = new Color(204, 204, 255);
+          currentColour = LIGHT_BLUE;
         }
         else
         {
diff --git a/src/jalview/schemes/BuriedColourScheme.java b/src/jalview/schemes/BuriedColourScheme.java
index f2859d3..4940e10 100644
--- a/src/jalview/schemes/BuriedColourScheme.java
+++ b/src/jalview/schemes/BuriedColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/ClustalxColourScheme.java b/src/jalview/schemes/ClustalxColourScheme.java
index 2ba06dc..d8b9761 100644
--- a/src/jalview/schemes/ClustalxColourScheme.java
+++ b/src/jalview/schemes/ClustalxColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/ColourSchemeI.java b/src/jalview/schemes/ColourSchemeI.java
index 0c393cd..a72ae80 100644
--- a/src/jalview/schemes/ColourSchemeI.java
+++ b/src/jalview/schemes/ColourSchemeI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,6 +21,7 @@
 package jalview.schemes;
 
 import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.ProfilesI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
 
@@ -52,7 +53,7 @@ public interface ColourSchemeI
   /**
    * assign the given consensus profile for the colourscheme
    */
-  public void setConsensus(java.util.Hashtable[] h);
+  public void setConsensus(ProfilesI hconsensus);
 
   /**
    * assign the given conservation to the colourscheme
diff --git a/src/jalview/schemes/ColourSchemeProperty.java b/src/jalview/schemes/ColourSchemeProperty.java
index 500047b..707e480 100644
--- a/src/jalview/schemes/ColourSchemeProperty.java
+++ b/src/jalview/schemes/ColourSchemeProperty.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/Consensus.java b/src/jalview/schemes/Consensus.java
index 337672c..36d9899 100644
--- a/src/jalview/schemes/Consensus.java
+++ b/src/jalview/schemes/Consensus.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/CovariationColourScheme.java b/src/jalview/schemes/CovariationColourScheme.java
index c88393a..e342eba 100644
--- a/src/jalview/schemes/CovariationColourScheme.java
+++ b/src/jalview/schemes/CovariationColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/FeatureColour.java b/src/jalview/schemes/FeatureColour.java
new file mode 100644
index 0000000..b3cd379
--- /dev/null
+++ b/src/jalview/schemes/FeatureColour.java
@@ -0,0 +1,696 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.schemes;
+
+import jalview.api.FeatureColourI;
+import jalview.datamodel.SequenceFeature;
+import jalview.util.Format;
+
+import java.awt.Color;
+import java.util.StringTokenizer;
+
+/**
+ * A class that wraps either a simple colour or a graduated colour
+ */
+public class FeatureColour implements FeatureColourI
+{
+  private static final String BAR = "|";
+
+  final private Color colour;
+
+  final private Color minColour;
+
+  final private Color maxColour;
+
+  private boolean graduatedColour;
+
+  private boolean colourByLabel;
+
+  private float threshold;
+
+  private float base;
+
+  private float range;
+
+  private boolean belowThreshold;
+
+  private boolean aboveThreshold;
+
+  private boolean thresholdIsMinOrMax;
+
+  private boolean isHighToLow;
+
+  private boolean autoScaled;
+
+  final private float minRed;
+
+  final private float minGreen;
+
+  final private float minBlue;
+
+  final private float deltaRed;
+
+  final private float deltaGreen;
+
+  final private float deltaBlue;
+
+  /**
+   * Parses a Jalview features file format colour descriptor
+   * [label|][mincolour|maxcolour
+   * |[absolute|]minvalue|maxvalue|thresholdtype|thresholdvalue] Examples:
+   * <ul>
+   * <li>red</li>
+   * <li>a28bbb</li>
+   * <li>25,125,213</li>
+   * <li>label</li>
+   * <li>label|||0.0|0.0|above|12.5</li>
+   * <li>label|||0.0|0.0|below|12.5</li>
+   * <li>red|green|12.0|26.0|none</li>
+   * <li>a28bbb|3eb555|12.0|26.0|above|12.5</li>
+   * <li>a28bbb|3eb555|abso|12.0|26.0|below|12.5</li>
+   * </ul>
+   * 
+   * @param descriptor
+   * @return
+   * @throws IllegalArgumentException
+   *           if not parseable
+   */
+  public static FeatureColour parseJalviewFeatureColour(String descriptor)
+  {
+    StringTokenizer gcol = new StringTokenizer(descriptor, "|", true);
+    float min = Float.MIN_VALUE;
+    float max = Float.MAX_VALUE;
+    boolean labelColour = false;
+
+    String mincol = gcol.nextToken();
+    if (mincol == "|")
+    {
+      throw new IllegalArgumentException(
+              "Expected either 'label' or a colour specification in the line: "
+                      + descriptor);
+    }
+    String maxcol = null;
+    if (mincol.toLowerCase().indexOf("label") == 0)
+    {
+      labelColour = true;
+      mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
+      // skip '|'
+      mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
+    }
+
+    if (!labelColour && !gcol.hasMoreTokens())
+    {
+      /*
+       * only a simple colour specification - parse it
+       */
+      Color colour = UserColourScheme.getColourFromString(descriptor);
+      if (colour == null)
+      {
+        throw new IllegalArgumentException("Invalid colour descriptor: "
+                + descriptor);
+      }
+      return new FeatureColour(colour);
+    }
+
+    /*
+     * autoScaled == true: colours range over actual score range
+     * autoScaled == false ('abso'): colours range over min/max range
+     */
+    boolean autoScaled = true;
+    String tok = null, minval, maxval;
+    if (mincol != null)
+    {
+      // at least four more tokens
+      if (mincol.equals("|"))
+      {
+        mincol = "";
+      }
+      else
+      {
+        gcol.nextToken(); // skip next '|'
+      }
+      maxcol = gcol.nextToken();
+      if (maxcol.equals("|"))
+      {
+        maxcol = "";
+      }
+      else
+      {
+        gcol.nextToken(); // skip next '|'
+      }
+      tok = gcol.nextToken();
+      gcol.nextToken(); // skip next '|'
+      if (tok.toLowerCase().startsWith("abso"))
+      {
+        minval = gcol.nextToken();
+        gcol.nextToken(); // skip next '|'
+        autoScaled = false;
+      }
+      else
+      {
+        minval = tok;
+      }
+      maxval = gcol.nextToken();
+      if (gcol.hasMoreTokens())
+      {
+        gcol.nextToken(); // skip next '|'
+      }
+      try
+      {
+        if (minval.length() > 0)
+        {
+          min = new Float(minval).floatValue();
+        }
+      } catch (Exception e)
+      {
+        throw new IllegalArgumentException(
+                "Couldn't parse the minimum value for graduated colour ("
+                        + descriptor + ")");
+      }
+      try
+      {
+        if (maxval.length() > 0)
+        {
+          max = new Float(maxval).floatValue();
+        }
+      } catch (Exception e)
+      {
+        throw new IllegalArgumentException(
+                "Couldn't parse the maximum value for graduated colour ("
+                        + descriptor + ")");
+      }
+    }
+    else
+    {
+      // add in some dummy min/max colours for the label-only
+      // colourscheme.
+      mincol = "FFFFFF";
+      maxcol = "000000";
+    }
+
+    /*
+     * construct the FeatureColour
+     */
+    FeatureColour featureColour;
+    try
+    {
+      featureColour = new FeatureColour(
+              new UserColourScheme(mincol).findColour('A'),
+              new UserColourScheme(maxcol).findColour('A'), min, max);
+      featureColour.setColourByLabel(labelColour);
+      featureColour.setAutoScaled(autoScaled);
+      // add in any additional parameters
+      String ttype = null, tval = null;
+      if (gcol.hasMoreTokens())
+      {
+        // threshold type and possibly a threshold value
+        ttype = gcol.nextToken();
+        if (ttype.toLowerCase().startsWith("below"))
+        {
+          featureColour.setBelowThreshold(true);
+        }
+        else if (ttype.toLowerCase().startsWith("above"))
+        {
+          featureColour.setAboveThreshold(true);
+        }
+        else
+        {
+          if (!ttype.toLowerCase().startsWith("no"))
+          {
+            System.err.println("Ignoring unrecognised threshold type : "
+                    + ttype);
+          }
+        }
+      }
+      if (featureColour.hasThreshold())
+      {
+        try
+        {
+          gcol.nextToken();
+          tval = gcol.nextToken();
+          featureColour.setThreshold(new Float(tval).floatValue());
+        } catch (Exception e)
+        {
+          System.err.println("Couldn't parse threshold value as a float: ("
+                  + tval + ")");
+        }
+      }
+      if (gcol.hasMoreTokens())
+      {
+        System.err
+                .println("Ignoring additional tokens in parameters in graduated colour specification\n");
+        while (gcol.hasMoreTokens())
+        {
+          System.err.println("|" + gcol.nextToken());
+        }
+        System.err.println("\n");
+      }
+      return featureColour;
+    } catch (Exception e)
+    {
+      throw new IllegalArgumentException(e.getMessage());
+    }
+  }
+
+  /**
+   * Default constructor
+   */
+  public FeatureColour()
+  {
+    this((Color) null);
+  }
+
+  /**
+   * Constructor given a simple colour
+   * 
+   * @param c
+   */
+  public FeatureColour(Color c)
+  {
+    minColour = Color.WHITE;
+    maxColour = Color.BLACK;
+    minRed = 0f;
+    minGreen = 0f;
+    minBlue = 0f;
+    deltaRed = 0f;
+    deltaGreen = 0f;
+    deltaBlue = 0f;
+    colour = c;
+  }
+
+  /**
+   * Constructor given a colour range and a score range
+   * 
+   * @param low
+   * @param high
+   * @param min
+   * @param max
+   */
+  public FeatureColour(Color low, Color high, float min, float max)
+  {
+    graduatedColour = true;
+    colour = null;
+    minColour = low;
+    maxColour = high;
+    threshold = Float.NaN;
+    isHighToLow = min >= max;
+    minRed = low.getRed() / 255f;
+    minGreen = low.getGreen() / 255f;
+    minBlue = low.getBlue() / 255f;
+    deltaRed = (high.getRed() / 255f) - minRed;
+    deltaGreen = (high.getGreen() / 255f) - minGreen;
+    deltaBlue = (high.getBlue() / 255f) - minBlue;
+    if (isHighToLow)
+    {
+      base = max;
+      range = min - max;
+    }
+    else
+    {
+      base = min;
+      range = max - min;
+    }
+  }
+
+  /**
+   * Copy constructor
+   * 
+   * @param fc
+   */
+  public FeatureColour(FeatureColour fc)
+  {
+    graduatedColour = fc.graduatedColour;
+    colour = fc.colour;
+    minColour = fc.minColour;
+    maxColour = fc.maxColour;
+    minRed = fc.minRed;
+    minGreen = fc.minGreen;
+    minBlue = fc.minBlue;
+    deltaRed = fc.deltaRed;
+    deltaGreen = fc.deltaGreen;
+    deltaBlue = fc.deltaBlue;
+    base = fc.base;
+    range = fc.range;
+    isHighToLow = fc.isHighToLow;
+    setAboveThreshold(fc.isAboveThreshold());
+    setBelowThreshold(fc.isBelowThreshold());
+    setThreshold(fc.getThreshold());
+    setAutoScaled(fc.isAutoScaled());
+    setColourByLabel(fc.isColourByLabel());
+  }
+
+  /**
+   * Copy constructor with new min/max ranges
+   * 
+   * @param fc
+   * @param min
+   * @param max
+   */
+  public FeatureColour(FeatureColour fc, float min, float max)
+  {
+    this(fc);
+    graduatedColour = true;
+    updateBounds(min, max);
+  }
+
+  @Override
+  public boolean isGraduatedColour()
+  {
+    return graduatedColour;
+  }
+
+  /**
+   * Sets the 'graduated colour' flag. If true, also sets 'colour by label' to
+   * false.
+   */
+  void setGraduatedColour(boolean b)
+  {
+    graduatedColour = b;
+    if (b)
+    {
+      setColourByLabel(false);
+    }
+  }
+
+  @Override
+  public Color getColour()
+  {
+    return colour;
+  }
+
+  @Override
+  public Color getMinColour()
+  {
+    return minColour;
+  }
+
+  @Override
+  public Color getMaxColour()
+  {
+    return maxColour;
+  }
+
+  @Override
+  public boolean isColourByLabel()
+  {
+    return colourByLabel;
+  }
+
+  /**
+   * Sets the 'colour by label' flag. If true, also sets 'graduated colour' to
+   * false.
+   */
+  @Override
+  public void setColourByLabel(boolean b)
+  {
+    colourByLabel = b;
+    if (b)
+    {
+      setGraduatedColour(false);
+    }
+  }
+
+  @Override
+  public boolean isBelowThreshold()
+  {
+    return belowThreshold;
+  }
+
+  @Override
+  public void setBelowThreshold(boolean b)
+  {
+    belowThreshold = b;
+    if (b)
+    {
+      setAboveThreshold(false);
+    }
+  }
+
+  @Override
+  public boolean isAboveThreshold()
+  {
+    return aboveThreshold;
+  }
+
+  @Override
+  public void setAboveThreshold(boolean b)
+  {
+    aboveThreshold = b;
+    if (b)
+    {
+      setBelowThreshold(false);
+    }
+  }
+
+  @Override
+  public boolean isThresholdMinMax()
+  {
+    return thresholdIsMinOrMax;
+  }
+
+  @Override
+  public void setThresholdMinMax(boolean b)
+  {
+    thresholdIsMinOrMax = b;
+  }
+
+  @Override
+  public float getThreshold()
+  {
+    return threshold;
+  }
+
+  @Override
+  public void setThreshold(float f)
+  {
+    threshold = f;
+  }
+
+  @Override
+  public boolean isAutoScaled()
+  {
+    return autoScaled;
+  }
+
+  @Override
+  public void setAutoScaled(boolean b)
+  {
+    this.autoScaled = b;
+  }
+
+  /**
+   * Updates the base and range appropriately for the given minmax range
+   * 
+   * @param min
+   * @param max
+   */
+  @Override
+  public void updateBounds(float min, float max)
+  {
+    if (max < min)
+    {
+      base = max;
+      range = min - max;
+      isHighToLow = true;
+    }
+    else
+    {
+      base = min;
+      range = max - min;
+      isHighToLow = false;
+    }
+  }
+
+  /**
+   * Returns the colour for the given instance of the feature. This may be a
+   * simple colour, a colour generated from the feature description (if
+   * isColourByLabel()), or a colour derived from the feature score (if
+   * isGraduatedColour()).
+   * 
+   * @param feature
+   * @return
+   */
+  @Override
+  public Color getColor(SequenceFeature feature)
+  {
+    if (isColourByLabel())
+    {
+      return UserColourScheme
+              .createColourFromName(feature.getDescription());
+    }
+
+    if (!isGraduatedColour())
+    {
+      return getColour();
+    }
+
+    // todo should we check for above/below threshold here?
+    if (range == 0.0)
+    {
+      return getMaxColour();
+    }
+    float scr = feature.getScore();
+    if (Float.isNaN(scr))
+    {
+      return getMinColour();
+    }
+    float scl = (scr - base) / range;
+    if (isHighToLow)
+    {
+      scl = -scl;
+    }
+    if (scl < 0f)
+    {
+      scl = 0f;
+    }
+    if (scl > 1f)
+    {
+      scl = 1f;
+    }
+    return new Color(minRed + scl * deltaRed, minGreen + scl * deltaGreen,
+            minBlue + scl * deltaBlue);
+  }
+
+  /**
+   * Returns the maximum score of the graduated colour range
+   * 
+   * @return
+   */
+  @Override
+  public float getMax()
+  {
+    // regenerate the original values passed in to the constructor
+    return (isHighToLow) ? base : (base + range);
+  }
+
+  /**
+   * Returns the minimum score of the graduated colour range
+   * 
+   * @return
+   */
+  @Override
+  public float getMin()
+  {
+    // regenerate the original value passed in to the constructor
+    return (isHighToLow) ? (base + range) : base;
+  }
+
+  /**
+   * Answers true if the feature has a simple colour, or is coloured by label,
+   * or has a graduated colour and the score of this feature instance is within
+   * the range to render (if any), i.e. does not lie below or above any
+   * threshold set.
+   * 
+   * @param feature
+   * @return
+   */
+  @Override
+  public boolean isColored(SequenceFeature feature)
+  {
+    if (isColourByLabel() || !isGraduatedColour())
+    {
+      return true;
+    }
+
+    float val = feature.getScore();
+    if (Float.isNaN(val))
+    {
+      return true;
+    }
+    if (Float.isNaN(this.threshold))
+    {
+      return true;
+    }
+
+    if (isAboveThreshold() && val <= threshold)
+    {
+      return false;
+    }
+    if (isBelowThreshold() && val >= threshold)
+    {
+      return false;
+    }
+    return true;
+  }
+
+  @Override
+  public boolean isSimpleColour()
+  {
+    return (!isColourByLabel() && !isGraduatedColour());
+  }
+
+  @Override
+  public boolean hasThreshold()
+  {
+    return isAboveThreshold() || isBelowThreshold();
+  }
+
+  @Override
+  public String toJalviewFormat(String featureType)
+  {
+    String colourString = null;
+    if (isSimpleColour())
+    {
+      colourString = Format.getHexString(getColour());
+    }
+    else
+    {
+      StringBuilder sb = new StringBuilder(32);
+      if (isColourByLabel())
+      {
+        sb.append("label");
+        if (hasThreshold())
+        {
+          sb.append(BAR).append(BAR).append(BAR);
+        }
+      }
+      if (isGraduatedColour())
+      {
+        sb.append(Format.getHexString(getMinColour())).append(BAR);
+        sb.append(Format.getHexString(getMaxColour())).append(BAR);
+        if (!isAutoScaled())
+        {
+          sb.append("abso").append(BAR);
+        }
+      }
+      if (hasThreshold() || isGraduatedColour())
+      {
+        sb.append(getMin()).append(BAR);
+        sb.append(getMax()).append(BAR);
+        if (isBelowThreshold())
+        {
+          sb.append("below").append(BAR).append(getThreshold());
+        }
+        else if (isAboveThreshold())
+        {
+          sb.append("above").append(BAR).append(getThreshold());
+        }
+        else
+        {
+          sb.append("none");
+        }
+      }
+      colourString = sb.toString();
+    }
+    return String.format("%s\t%s", featureType, colourString);
+  }
+
+}
diff --git a/src/jalview/util/ReverseListIterator.java b/src/jalview/schemes/FeatureSettingsAdapter.java
similarity index 51%
copy from src/jalview/util/ReverseListIterator.java
copy to src/jalview/schemes/FeatureSettingsAdapter.java
index 1145b66..176a37c 100644
--- a/src/jalview/util/ReverseListIterator.java
+++ b/src/jalview/schemes/FeatureSettingsAdapter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,46 +18,51 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.util;
+package jalview.schemes;
 
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
+import jalview.api.FeatureColourI;
+import jalview.api.FeatureSettingsModelI;
 
 /**
- * An iterator that traverses a list backwards.
- * 
- * @author gmcarstairs (and checked against
- *         org.codehaus.groovey.runtime.ReverseListIterator)
- *
- * @param <E>
+ * An adapter class that may be extended to instantiate feature colour schemes
  */
-public class ReverseListIterator<E> implements Iterator<E>
+public class FeatureSettingsAdapter implements FeatureSettingsModelI
 {
 
-  private ListIterator<E> iterator;
+  @Override
+  public boolean isFeatureDisplayed(String type)
+  {
+    return false;
+  }
 
-  public ReverseListIterator(List<E> stuff)
+  @Override
+  public boolean isGroupDisplayed(String group)
+  {
+    return true;
+  }
+
+  @Override
+  public FeatureColourI getFeatureColour(String type)
   {
-    this.iterator = stuff.listIterator(stuff.size());
+    return null;
   }
 
   @Override
-  public boolean hasNext()
+  public float getTransparency()
   {
-    return iterator.hasPrevious();
+    return 1f;
   }
 
   @Override
-  public E next()
+  public int compare(String feature1, String feature2)
   {
-    return iterator.previous();
+    return 0;
   }
 
   @Override
-  public void remove()
+  public boolean optimiseOrder()
   {
-    iterator.remove();
+    return false;
   }
 
 }
diff --git a/src/jalview/schemes/FollowerColourScheme.java b/src/jalview/schemes/FollowerColourScheme.java
index 163bcff..78f7565 100644
--- a/src/jalview/schemes/FollowerColourScheme.java
+++ b/src/jalview/schemes/FollowerColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,8 +21,7 @@
 package jalview.schemes;
 
 import jalview.analysis.Conservation;
-
-import java.util.Hashtable;
+import jalview.datamodel.ProfilesI;
 
 /**
  * Colourscheme that takes its colours from some other colourscheme
@@ -41,7 +40,7 @@ public class FollowerColourScheme extends ResidueColourScheme
   }
 
   @Override
-  public void setConsensus(Hashtable[] consensus)
+  public void setConsensus(ProfilesI consensus)
   {
     if (colourScheme != null)
     {
diff --git a/src/jalview/schemes/GraduatedColor.java b/src/jalview/schemes/GraduatedColor.java
deleted file mode 100644
index 3de2ab5..0000000
--- a/src/jalview/schemes/GraduatedColor.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.schemes;
-
-import jalview.datamodel.SequenceFeature;
-
-import java.awt.Color;
-
-/**
- * Value and/or thresholded colour scale used for colouring by annotation and
- * feature score
- * 
- * @author JimP
- * 
- */
-public class GraduatedColor
-{
-  int thresholdState = AnnotationColourGradient.NO_THRESHOLD; // or
-                                                              // ABOVE_THRESHOLD
-                                                              // or
-                                                              // BELOW_THRESHOLD
-
-  float lr, lg, lb, dr, dg, db;
-
-  /**
-   * linear scaling parameters, base, minimum colour threshold, range of linear
-   * scale from lower to upper
-   */
-  float base, range, thrsh;
-
-  /**
-   * when true, colour from u to u-d rather than u to u+d
-   */
-  boolean tolow = false;
-
-  /**
-   * when false, min/max range has been manually set so should not be
-   * dynamically adjusted.
-   */
-  boolean autoScale = true;
-
-  /**
-   * construct a graduatedColor object from simple parameters
-   * 
-   * @param low
-   * @param high
-   * @param min
-   * @param max
-   *          color low->high from min->max
-   */
-  public GraduatedColor(Color low, Color high, float min, float max)
-  {
-    thrsh = Float.NaN;
-    tolow = min >= max;
-    lr = low.getRed() / 255f;
-    lg = low.getGreen() / 255f;
-    lb = low.getBlue() / 255f;
-    dr = (high.getRed() / 255f) - lr;
-    dg = (high.getGreen() / 255f) - lg;
-    db = (high.getBlue() / 255f) - lb;
-    if (tolow)
-    {
-      base = max;
-      range = min - max;
-    }
-    else
-    {
-      base = min;
-      range = max - min;
-    }
-  }
-
-  public GraduatedColor(GraduatedColor oldcs)
-  {
-    lr = oldcs.lr;
-    lg = oldcs.lg;
-    lb = oldcs.lb;
-    dr = oldcs.dr;
-    dg = oldcs.dg;
-    db = oldcs.db;
-    base = oldcs.base;
-    range = oldcs.range;
-    tolow = oldcs.tolow;
-    thresholdState = oldcs.thresholdState;
-    thrsh = oldcs.thrsh;
-    autoScale = oldcs.autoScale;
-    colourByLabel = oldcs.colourByLabel;
-  }
-
-  /**
-   * make a new gradient from an old one with a different scale range
-   * 
-   * @param oldcs
-   * @param min
-   * @param max
-   */
-  public GraduatedColor(GraduatedColor oldcs, float min, float max)
-  {
-    this(oldcs);
-    updateBounds(min, max);
-  }
-
-  public Color getMinColor()
-  {
-    return new Color(lr, lg, lb);
-  }
-
-  public Color getMaxColor()
-  {
-    return new Color(lr + dr, lg + dg, lb + db);
-  }
-
-  /**
-   * 
-   * @return true if original min/max scale was from high to low
-   */
-  public boolean getTolow()
-  {
-    return tolow;
-  }
-
-  public void setTolow(boolean tolower)
-  {
-    tolow = tolower;
-  }
-
-  public boolean isColored(SequenceFeature feature)
-  {
-    float val = feature.getScore();
-    if (Float.isNaN(val))
-    {
-      return true;
-    }
-    if (this.thresholdState == AnnotationColourGradient.NO_THRESHOLD)
-    {
-      return true;
-    }
-    if (Float.isNaN(this.thrsh))
-    {
-      return true;
-    }
-    boolean rtn = thresholdState == AnnotationColourGradient.ABOVE_THRESHOLD;
-    if (val <= thrsh)
-    {
-      return !rtn; // ? !tolow : tolow;
-    }
-    else
-    {
-      return rtn; // ? tolow : !tolow;
-    }
-  }
-
-  /**
-   * default implementor of a getColourFromString method. TODO: abstract an
-   * interface enabling pluggable colour from string
-   */
-  private UserColourScheme ucs = null;
-
-  private boolean colourByLabel = false;
-
-  /**
-   * 
-   * @return true if colourByLabel style is set
-   */
-  public boolean isColourByLabel()
-  {
-    return colourByLabel;
-  }
-
-  /**
-   * @param colourByLabel
-   *          the colourByLabel to set
-   */
-  public void setColourByLabel(boolean colourByLabel)
-  {
-    this.colourByLabel = colourByLabel;
-  }
-
-  public Color findColor(SequenceFeature feature)
-  {
-    if (colourByLabel)
-    {
-      // TODO: allow user defined feature label colourschemes. Colour space is
-      // {type,regex,%anytype%}x{description string, regex, keyword}
-      if (ucs == null)
-      {
-        ucs = new UserColourScheme();
-      }
-      return ucs.createColourFromName(feature.getDescription());
-    }
-    if (range == 0.0)
-    {
-      return getMaxColor();
-    }
-    float scr = feature.getScore();
-    if (Float.isNaN(scr))
-    {
-      return getMinColor();
-    }
-    float scl = (scr - base) / range;
-    if (tolow)
-    {
-      scl = -scl;
-    }
-    if (scl < 0f)
-    {
-      scl = 0f;
-    }
-    if (scl > 1f)
-    {
-      scl = 1f;
-    }
-    return new Color(lr + scl * dr, lg + scl * dg, lb + scl * db);
-  }
-
-  public void setThresh(float value)
-  {
-    thrsh = value;
-  }
-
-  public float getThresh()
-  {
-    return thrsh;
-  }
-
-  public void setThreshType(int aboveThreshold)
-  {
-    thresholdState = aboveThreshold;
-  }
-
-  public int getThreshType()
-  {
-    return thresholdState;
-  }
-
-  public float getMax()
-  {
-    // regenerate the original values passed in to the constructor
-    return (tolow) ? base : (base + range);
-  }
-
-  public float getMin()
-  {
-    // regenerate the original value passed in to the constructor
-    return (tolow) ? (base + range) : base;
-  }
-
-  public boolean isAutoScale()
-  {
-    return autoScale;
-  }
-
-  public void setAutoScaled(boolean autoscale)
-  {
-    autoScale = autoscale;
-  }
-
-  /**
-   * update the base and range appropriatly for the given minmax range
-   * 
-   * @param a
-   *          float[] {min,max} array containing minmax range for the associated
-   *          score values
-   */
-  public void updateBounds(float min, float max)
-  {
-    if (max < min)
-    {
-      base = max;
-      range = min - max;
-      tolow = true;
-    }
-    else
-    {
-      base = min;
-      range = max - min;
-      tolow = false;
-    }
-  }
-}
diff --git a/src/jalview/schemes/HelixColourScheme.java b/src/jalview/schemes/HelixColourScheme.java
index a89bc47..1e44c59 100644
--- a/src/jalview/schemes/HelixColourScheme.java
+++ b/src/jalview/schemes/HelixColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/HydrophobicColourScheme.java b/src/jalview/schemes/HydrophobicColourScheme.java
index 56e0106..cf379be 100644
--- a/src/jalview/schemes/HydrophobicColourScheme.java
+++ b/src/jalview/schemes/HydrophobicColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/NucleotideColourScheme.java b/src/jalview/schemes/NucleotideColourScheme.java
index dd99f8a..c724f32 100644
--- a/src/jalview/schemes/NucleotideColourScheme.java
+++ b/src/jalview/schemes/NucleotideColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/PIDColourScheme.java b/src/jalview/schemes/PIDColourScheme.java
index d21c30f..9d31e72 100644
--- a/src/jalview/schemes/PIDColourScheme.java
+++ b/src/jalview/schemes/PIDColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,9 +20,10 @@
  */
 package jalview.schemes;
 
-import jalview.analysis.AAFrequency;
+import jalview.datamodel.ProfileI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.util.Comparison;
 
 import java.awt.Color;
 
@@ -48,7 +49,7 @@ public class PIDColourScheme extends ResidueColourScheme
       c -= ('a' - 'A');
     }
 
-    if (consensus == null || j >= consensus.length || consensus[j] == null)
+    if (consensus == null || consensus.get(j) == null)
     {
       return Color.white;
     }
@@ -62,25 +63,24 @@ public class PIDColourScheme extends ResidueColourScheme
 
     double sc = 0;
 
-    if (consensus.length <= j)
-    {
-      return Color.white;
-    }
 
-    if ((Integer
-            .parseInt(consensus[j].get(AAFrequency.MAXCOUNT).toString()) != -1)
-            && consensus[j].contains(String.valueOf(c)))
+    /*
+     * test whether this is the consensus (or joint consensus) residue
+     */
+    ProfileI profile = consensus.get(j);
+    boolean matchesConsensus = profile.getModalResidue().contains(
+            String.valueOf(c));
+    if (matchesConsensus)
     {
-      sc = ((Float) consensus[j].get(ignoreGaps)).floatValue();
+      sc = profile.getPercentageIdentity(ignoreGaps);
 
-      if (!jalview.util.Comparison.isGap(c))
+      if (!Comparison.isGap(c))
       {
         for (int i = 0; i < thresholds.length; i++)
         {
           if (sc > thresholds[i])
           {
             currentColour = pidColours[i];
-
             break;
           }
         }
diff --git a/src/jalview/schemes/PurinePyrimidineColourScheme.java b/src/jalview/schemes/PurinePyrimidineColourScheme.java
index 441a117..63117c3 100644
--- a/src/jalview/schemes/PurinePyrimidineColourScheme.java
+++ b/src/jalview/schemes/PurinePyrimidineColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/RNAHelicesColour.java b/src/jalview/schemes/RNAHelicesColour.java
index 9c2ec3e..2cee151 100644
--- a/src/jalview/schemes/RNAHelicesColour.java
+++ b/src/jalview/schemes/RNAHelicesColour.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -91,6 +91,10 @@ public class RNAHelicesColour extends ResidueColourScheme
     // This loop will find the first rna structure annotation by which to colour
     // the sequences.
     AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation();
+    if (annotations == null)
+    {
+      return;
+    }
     for (int i = 0; i < annotations.length; i++)
     {
 
diff --git a/src/jalview/schemes/RNAHelicesColourChooser.java b/src/jalview/schemes/RNAHelicesColourChooser.java
index 5a09a34..9422327 100644
--- a/src/jalview/schemes/RNAHelicesColourChooser.java
+++ b/src/jalview/schemes/RNAHelicesColourChooser.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,6 +22,7 @@ package jalview.schemes;
 
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.SequenceGroup;
 
 import java.awt.event.ActionEvent;
@@ -77,16 +78,20 @@ public class RNAHelicesColourChooser
     adjusting = true;
     Vector list = new Vector();
     int index = 1;
-    for (int i = 0; i < av.getAlignment().getAlignmentAnnotation().length; i++)
+    AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation();
+    if (anns != null)
     {
-      String label = av.getAlignment().getAlignmentAnnotation()[i].label;
-      if (!list.contains(label))
+      for (int i = 0; i < anns.length; i++)
       {
-        list.addElement(label);
-      }
-      else
-      {
-        list.addElement(label + "_" + (index++));
+        String label = anns[i].label;
+        if (!list.contains(label))
+        {
+          list.addElement(label);
+        }
+        else
+        {
+          list.addElement(label + "_" + (index++));
+        }
       }
     }
 
diff --git a/src/jalview/schemes/RNAInteractionColourScheme.java b/src/jalview/schemes/RNAInteractionColourScheme.java
index e23a497..127456e 100644
--- a/src/jalview/schemes/RNAInteractionColourScheme.java
+++ b/src/jalview/schemes/RNAInteractionColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/ResidueColourScheme.java b/src/jalview/schemes/ResidueColourScheme.java
index 3164748..0a821f5 100644
--- a/src/jalview/schemes/ResidueColourScheme.java
+++ b/src/jalview/schemes/ResidueColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,15 +20,17 @@
  */
 package jalview.schemes;
 
-import jalview.analysis.AAFrequency;
 import jalview.analysis.Conservation;
 import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.ProfileI;
+import jalview.datamodel.ProfilesI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
+import jalview.util.ColorUtils;
+import jalview.util.Comparison;
 import jalview.util.MessageManager;
 
 import java.awt.Color;
-import java.util.Hashtable;
 import java.util.Map;
 
 /**
@@ -48,17 +50,21 @@ public class ResidueColourScheme implements ColourSchemeI
   int threshold = 0;
 
   /* Set when threshold colouring to either pid_gaps or pid_nogaps */
-  protected String ignoreGaps = AAFrequency.PID_GAPS;
+  protected boolean ignoreGaps = false;
 
-  /** Consenus as a hashtable array */
-  Hashtable[] consensus;
+  /*
+   * Consensus data indexed by column
+   */
+  ProfilesI consensus;
 
-  /** Conservation string as a char array */
+  /*
+   * Conservation string as a char array 
+   */
   char[] conservation;
 
-  int conservationLength = 0;
-
-  /** DOCUMENT ME!! */
+  /*
+   * The conservation slider percentage setting 
+   */
   int inc = 30;
 
   /**
@@ -100,6 +106,7 @@ public class ResidueColourScheme implements ColourSchemeI
   /**
    * Find a colour without an index in a sequence
    */
+  @Override
   public Color findColour(char c)
   {
     return colors == null ? Color.white : colors[symbolIndex[c]];
@@ -133,58 +140,63 @@ public class ResidueColourScheme implements ColourSchemeI
    * 
    * @return Returns the percentage threshold
    */
+  @Override
   public int getThreshold()
   {
     return threshold;
   }
 
   /**
-   * DOCUMENT ME!
+   * Sets the percentage consensus threshold value, and whether gaps are ignored
+   * in percentage identity calculation
    * 
-   * @param ct
-   *          DOCUMENT ME!
+   * @param consensusThreshold
+   * @param ignoreGaps
    */
-  public void setThreshold(int ct, boolean ignoreGaps)
+  @Override
+  public void setThreshold(int consensusThreshold, boolean ignoreGaps)
   {
-    threshold = ct;
-    if (ignoreGaps)
-    {
-      this.ignoreGaps = AAFrequency.PID_NOGAPS;
-    }
-    else
-    {
-      this.ignoreGaps = AAFrequency.PID_GAPS;
-    }
+    threshold = consensusThreshold;
+    this.ignoreGaps = ignoreGaps;
   }
 
   /**
-   * DOCUMENT ME!
+   * Answers true if there is a consensus profile for the specified column, and
+   * the given residue matches the consensus (or joint consensus) residue for
+   * the column, and the percentage identity for the profile is equal to or
+   * greater than the current threshold; else answers false. The percentage
+   * calculation depends on whether or not we are ignoring gapped sequences.
    * 
-   * @param s
-   *          DOCUMENT ME!
-   * @param j
-   *          DOCUMENT ME!
+   * @param residue
+   * @param column
+   *          (index into consensus profiles)
    * 
-   * @return DOCUMENT ME!
+   * @return
+   * @see #setThreshold(int, boolean)
    */
-  public boolean aboveThreshold(char c, int j)
+  public boolean aboveThreshold(char residue, int column)
   {
-    if ('a' <= c && c <= 'z')
+    if ('a' <= residue && residue <= 'z')
     {
       // TO UPPERCASE !!!
       // Faster than toUpperCase
-      c -= ('a' - 'A');
+      residue -= ('a' - 'A');
     }
 
-    if (consensus == null || consensus.length < j || consensus[j] == null)
+    if (consensus == null)
     {
       return false;
     }
 
-    if ((((Integer) consensus[j].get(AAFrequency.MAXCOUNT)).intValue() != -1)
-            && consensus[j].contains(String.valueOf(c)))
+    ProfileI profile = consensus.get(column);
+
+    /*
+     * test whether this is the consensus (or joint consensus) residue
+     */
+    if (profile != null
+            && profile.getModalResidue().contains(String.valueOf(residue)))
     {
-      if (((Float) consensus[j].get(ignoreGaps)).floatValue() >= threshold)
+      if (profile.getPercentageIdentity(ignoreGaps) >= threshold)
       {
         return true;
       }
@@ -193,6 +205,7 @@ public class ResidueColourScheme implements ColourSchemeI
     return false;
   }
 
+  @Override
   public boolean conservationApplied()
   {
     return conservationColouring;
@@ -204,11 +217,13 @@ public class ResidueColourScheme implements ColourSchemeI
     conservationColouring = conservationApplied;
   }
 
+  @Override
   public void setConservationInc(int i)
   {
     inc = i;
   }
 
+  @Override
   public int getConservationInc()
   {
     return inc;
@@ -220,7 +235,8 @@ public class ResidueColourScheme implements ColourSchemeI
    * @param consensus
    *          DOCUMENT ME!
    */
-  public void setConsensus(Hashtable[] consensus)
+  @Override
+  public void setConsensus(ProfilesI consensus)
   {
     if (consensus == null)
     {
@@ -230,6 +246,7 @@ public class ResidueColourScheme implements ColourSchemeI
     this.consensus = consensus;
   }
 
+  @Override
   public void setConservation(Conservation cons)
   {
     if (cons == null)
@@ -240,73 +257,70 @@ public class ResidueColourScheme implements ColourSchemeI
     else
     {
       conservationColouring = true;
-      int i, iSize = cons.getConsSequence().getLength();
+      int iSize = cons.getConsSequence().getLength();
       conservation = new char[iSize];
-      for (i = 0; i < iSize; i++)
+      for (int i = 0; i < iSize; i++)
       {
         conservation[i] = cons.getConsSequence().getCharAt(i);
       }
-      conservationLength = conservation.length;
     }
 
   }
 
   /**
-   * DOCUMENT ME!
+   * Applies a combination of column conservation score, and conservation
+   * percentage slider, to 'bleach' out the residue colours towards white.
+   * <p>
+   * If a column is fully conserved (identical residues, conservation score 11,
+   * shown as *), or all 10 physico-chemical properties are conserved
+   * (conservation score 10, shown as +), then the colour is left unchanged.
+   * <p>
+   * Otherwise a 'bleaching' factor is computed and applied to the colour. This
+   * is designed to fade colours for scores of 0-9 completely to white at slider
+   * positions ranging from 18% - 100% respectively.
    * 
-   * @param s
-   *          DOCUMENT ME!
-   * @param i
-   *          DOCUMENT ME!
+   * @param currentColour
+   * @param column
    * 
-   * @return DOCUMENT ME!
+   * @return bleached (or unmodified) colour
    */
-
-  Color applyConservation(Color currentColour, int i)
+  Color applyConservation(Color currentColour, int column)
   {
+    if (conservation == null || conservation.length <= column)
+    {
+      return currentColour;
+    }
+    char conservationScore = conservation[column];
+
+    /*
+     * if residues are fully conserved (* or 11), or all properties
+     * are conserved (+ or 10), leave colour unchanged
+     */
+    if (conservationScore == '*' || conservationScore == '+'
+            || conservationScore == (char) 10
+            || conservationScore == (char) 11)
+    {
+      return currentColour;
+    }
 
-    if ((conservationLength > i) && (conservation[i] != '*')
-            && (conservation[i] != '+'))
+    if (Comparison.isGap(conservationScore))
     {
-      if (jalview.util.Comparison.isGap(conservation[i]))
-      {
-        currentColour = Color.white;
-      }
-      else
-      {
-        float t = 11 - (conservation[i] - '0');
-        if (t == 0)
-        {
-          return Color.white;
-        }
-
-        int red = currentColour.getRed();
-        int green = currentColour.getGreen();
-        int blue = currentColour.getBlue();
-
-        int dr = 255 - red;
-        int dg = 255 - green;
-        int db = 255 - blue;
-
-        dr *= t / 10f;
-        dg *= t / 10f;
-        db *= t / 10f;
-
-        red += (inc / 20f) * dr;
-        green += (inc / 20f) * dg;
-        blue += (inc / 20f) * db;
-
-        if (red > 255 || green > 255 || blue > 255)
-        {
-          currentColour = Color.white;
-        }
-        else
-        {
-          currentColour = new Color(red, green, blue);
-        }
-      }
+      return Color.white;
     }
-    return currentColour;
+
+    /*
+     * convert score 0-9 to a bleaching factor 1.1 - 0.2
+     */
+    float bleachFactor = (11 - (conservationScore - '0')) / 10f;
+
+    /*
+     * scale this up by 0-5 (percentage slider / 20)
+     * as a result, scores of:         0  1  2  3  4  5  6  7  8  9
+     * fade to white at slider value: 18 20 22 25 29 33 40 50 67 100%
+     */
+    bleachFactor *= (inc / 20f);
+
+    return ColorUtils.bleachColour(currentColour, bleachFactor);
   }
 
   @Override
diff --git a/src/jalview/schemes/ResidueProperties.java b/src/jalview/schemes/ResidueProperties.java
index 092971d..60fb238 100644
--- a/src/jalview/schemes/ResidueProperties.java
+++ b/src/jalview/schemes/ResidueProperties.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,6 +26,7 @@ import jalview.api.analysis.ScoreModelI;
 
 import java.awt.Color;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Hashtable;
@@ -35,7 +36,7 @@ import java.util.Vector;
 
 public class ResidueProperties
 {
-  public static Hashtable<String, ScoreModelI> scoreMatrices = new Hashtable();
+  public static Hashtable<String, ScoreModelI> scoreMatrices = new Hashtable<String, ScoreModelI>();
 
   // Stores residue codes/names and colours and other things
   public static final int[] aaIndex; // aaHash version 2.1.1 and below
@@ -221,37 +222,42 @@ public class ResidueProperties
     purinepyrimidineIndex['n'] = 2;
   }
 
+  private static final Integer ONE = Integer.valueOf(1);
+
+  private static final Integer ZERO = Integer.valueOf(0);
+
   static
   {
-    aa3Hash.put("ALA", new Integer(0));
-    aa3Hash.put("ARG", new Integer(1));
-    aa3Hash.put("ASN", new Integer(2));
-    aa3Hash.put("ASP", new Integer(3)); // D
-    aa3Hash.put("CYS", new Integer(4));
-    aa3Hash.put("GLN", new Integer(5)); // Q
-    aa3Hash.put("GLU", new Integer(6)); // E
-    aa3Hash.put("GLY", new Integer(7));
-    aa3Hash.put("HIS", new Integer(8));
-    aa3Hash.put("ILE", new Integer(9));
-    aa3Hash.put("LEU", new Integer(10));
-    aa3Hash.put("LYS", new Integer(11));
-    aa3Hash.put("MET", new Integer(12));
-    aa3Hash.put("PHE", new Integer(13));
-    aa3Hash.put("PRO", new Integer(14));
-    aa3Hash.put("SER", new Integer(15));
-    aa3Hash.put("THR", new Integer(16));
-    aa3Hash.put("TRP", new Integer(17));
-    aa3Hash.put("TYR", new Integer(18));
-    aa3Hash.put("VAL", new Integer(19));
+    aa3Hash.put("ALA", ZERO);
+    aa3Hash.put("ARG", ONE);
+    aa3Hash.put("ASN", Integer.valueOf(2));
+    aa3Hash.put("ASP", Integer.valueOf(3)); // D
+    aa3Hash.put("CYS", Integer.valueOf(4));
+    aa3Hash.put("GLN", Integer.valueOf(5)); // Q
+    aa3Hash.put("GLU", Integer.valueOf(6)); // E
+    aa3Hash.put("GLY", Integer.valueOf(7));
+    aa3Hash.put("HIS", Integer.valueOf(8));
+    aa3Hash.put("ILE", Integer.valueOf(9));
+    aa3Hash.put("LEU", Integer.valueOf(10));
+    aa3Hash.put("LYS", Integer.valueOf(11));
+    aa3Hash.put("MET", Integer.valueOf(12));
+    aa3Hash.put("PHE", Integer.valueOf(13));
+    aa3Hash.put("PRO", Integer.valueOf(14));
+    aa3Hash.put("SER", Integer.valueOf(15));
+    aa3Hash.put("THR", Integer.valueOf(16));
+    aa3Hash.put("TRP", Integer.valueOf(17));
+    aa3Hash.put("TYR", Integer.valueOf(18));
+    aa3Hash.put("VAL", Integer.valueOf(19));
     // IUB Nomenclature for ambiguous peptides
-    aa3Hash.put("ASX", new Integer(20)); // "B";
-    aa3Hash.put("GLX", new Integer(21)); // X
-    aa3Hash.put("XAA", new Integer(22)); // X unknown
-    aa3Hash.put("-", new Integer(23));
-    aa3Hash.put("*", new Integer(23));
-    aa3Hash.put(".", new Integer(23));
-    aa3Hash.put(" ", new Integer(23));
-    aa3Hash.put("Gap", new Integer(23));
+    aa3Hash.put("ASX", Integer.valueOf(20)); // "B";
+    aa3Hash.put("GLX", Integer.valueOf(21)); // Z
+    aa3Hash.put("XAA", Integer.valueOf(22)); // X unknown
+    aa3Hash.put("-", Integer.valueOf(23));
+    aa3Hash.put("*", Integer.valueOf(23));
+    aa3Hash.put(".", Integer.valueOf(23));
+    aa3Hash.put(" ", Integer.valueOf(23));
+    aa3Hash.put("Gap", Integer.valueOf(23));
+    aa3Hash.put("UR3", Integer.valueOf(24));
   }
 
   static
@@ -300,28 +306,28 @@ public class ResidueProperties
 
   public static final String[] aa = { "A", "R", "N", "D", "C", "Q", "E",
       "G", "H", "I", "L", "K", "M", "F", "P", "S", "T", "W", "Y", "V", "B",
-      "Z", "X", "_", "*", ".", " " };
+      "Z", "X", "_", "*", ".", " ", "U" };
 
   public static final Color midBlue = new Color(100, 100, 255);
 
-  public static final Vector scaleColours = new Vector();
-
-  static
-  {
-    scaleColours.addElement(new Color(114, 0, 147));
-    scaleColours.addElement(new Color(156, 0, 98));
-    scaleColours.addElement(new Color(190, 0, 0));
-    scaleColours.addElement(Color.red);
-    scaleColours.addElement(new Color(255, 125, 0));
-    scaleColours.addElement(Color.orange);
-    scaleColours.addElement(new Color(255, 194, 85));
-    scaleColours.addElement(Color.yellow);
-    scaleColours.addElement(new Color(255, 255, 181));
-    scaleColours.addElement(Color.white);
-  }
-
-  public static final Color[] taylor = { new Color(204, 255, 0), // A
-                                                                 // Greenish-yellowy-yellow
+  // not currently in use
+  // public static final Vector<Color> scaleColours = new Vector<Color>();
+  // static
+  // {
+  // scaleColours.addElement(new Color(114, 0, 147));
+  // scaleColours.addElement(new Color(156, 0, 98));
+  // scaleColours.addElement(new Color(190, 0, 0));
+  // scaleColours.addElement(Color.red);
+  // scaleColours.addElement(new Color(255, 125, 0));
+  // scaleColours.addElement(Color.orange);
+  // scaleColours.addElement(new Color(255, 194, 85));
+  // scaleColours.addElement(Color.yellow);
+  // scaleColours.addElement(new Color(255, 255, 181));
+  // scaleColours.addElement(Color.white);
+  // }
+
+  public static final Color[] taylor = { new Color(204, 255, 0),
+      // A Greenish-yellowy-yellow
       new Color(0, 0, 255), // R Blueish-bluey-blue
       new Color(204, 0, 255), // N Blueish-reddy-blue
       new Color(255, 0, 0), // D Reddish-reddy-red
@@ -571,21 +577,21 @@ public class ResidueProperties
       { -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
           -8, -8, -8, -8, -8, -8, 1 }, };
 
-  public static final Hashtable ssHash = new Hashtable(); // stores the number
-  // value of the aa
-
-  static
-  {
-    ssHash.put("H", Color.magenta);
-    ssHash.put("E", Color.yellow);
-    ssHash.put("-", Color.white);
-    ssHash.put(".", Color.white);
-    ssHash.put("S", Color.cyan);
-    ssHash.put("T", Color.blue);
-    ssHash.put("G", Color.pink);
-    ssHash.put("I", Color.pink);
-    ssHash.put("B", Color.yellow);
-  }
+  // not currently used
+  // public static final Map<String, Color> ssHash = new Hashtable<String,
+  // Color>();
+  // static
+  // {
+  // ssHash.put("H", Color.magenta);
+  // ssHash.put("E", Color.yellow);
+  // ssHash.put("-", Color.white);
+  // ssHash.put(".", Color.white);
+  // ssHash.put("S", Color.cyan);
+  // ssHash.put("T", Color.blue);
+  // ssHash.put("G", Color.pink);
+  // ssHash.put("I", Color.pink);
+  // ssHash.put("B", Color.yellow);
+  // }
 
   /*
    * new Color(60, 136, 238), // U Color.white, // I Color.white, // X
@@ -617,7 +623,6 @@ public class ResidueProperties
     scoreMatrices.put("BLOSUM62", new ScoreMatrix("BLOSUM62", BLOSUM62, 0));
     scoreMatrices.put("PAM250", new ScoreMatrix("PAM250", PAM250, 0));
     scoreMatrices.put("DNA", new ScoreMatrix("DNA", DNA, 1));
-
   }
 
   public static final Color[] pidColours = { midBlue,
@@ -627,77 +632,10 @@ public class ResidueProperties
 
   public static final float[] pidThresholds = { 80, 60, 40, };
 
-  public static Map<String, List<String>> codonHash = new HashMap<String, List<String>>();
-
-  private static List<String> Lys = new ArrayList<String>();
-
-  private static List<String> Asn = new ArrayList<String>();
-
-  private static List<String> Gln = new ArrayList<String>();
-
-  private static List<String> His = new ArrayList<String>();
-
-  private static List<String> Glu = new ArrayList<String>();
-
-  private static List<String> Asp = new ArrayList<String>();
-
-  private static List<String> Tyr = new ArrayList<String>();
-
-  private static List<String> Thr = new ArrayList<String>();
-
-  private static List<String> Pro = new ArrayList<String>();
-
-  private static List<String> Ala = new ArrayList<String>();
-
-  private static List<String> Ser = new ArrayList<String>();
-
-  private static List<String> Arg = new ArrayList<String>();
-
-  private static List<String> Gly = new ArrayList<String>();
-
-  private static List<String> Trp = new ArrayList<String>();
-
-  private static List<String> Cys = new ArrayList<String>();
-
-  private static List<String> Ile = new ArrayList<String>();
-
-  private static List<String> Met = new ArrayList<String>();
-
-  private static List<String> Leu = new ArrayList<String>();
-
-  private static List<String> Val = new ArrayList<String>();
-
-  private static List<String> Phe = new ArrayList<String>();
-
-  public static List<String> STOP = new ArrayList<String>();
+  public static List<String> STOP = Arrays.asList("TGA", "TAA", "TAG");
 
   public static String START = "ATG";
 
-  static
-  {
-    codonHash.put("K", Lys);
-    codonHash.put("N", Asn);
-    codonHash.put("Q", Gln);
-    codonHash.put("H", His);
-    codonHash.put("E", Glu);
-    codonHash.put("D", Asp);
-    codonHash.put("Y", Tyr);
-    codonHash.put("T", Thr);
-    codonHash.put("P", Pro);
-    codonHash.put("A", Ala);
-    codonHash.put("S", Ser);
-    codonHash.put("R", Arg);
-    codonHash.put("G", Gly);
-    codonHash.put("W", Trp);
-    codonHash.put("C", Cys);
-    codonHash.put("I", Ile);
-    codonHash.put("M", Met);
-    codonHash.put("L", Leu);
-    codonHash.put("V", Val);
-    codonHash.put("F", Phe);
-    codonHash.put("STOP", STOP);
-  }
-
   /**
    * Nucleotide Ambiguity Codes
    */
@@ -884,7 +822,6 @@ public class ResidueProperties
         // make all codons for this combination
         char allres[][] = new char[tpos.length][];
         String _acodon = "";
-        char _anuc;
         for (ipos = 0; ipos < tpos.length; ipos++)
         {
           if (acodon[ipos].length == 0 || tpos[ipos] < 0)
@@ -950,379 +887,294 @@ public class ResidueProperties
         }
       }
     }
-
-  }
-
-  static
-  {
-    Lys.add("AAA");
-    Lys.add("AAG");
-    Asn.add("AAC");
-    Asn.add("AAT");
-
-    Gln.add("CAA");
-    Gln.add("CAG");
-    His.add("CAC");
-    His.add("CAT");
-
-    Glu.add("GAA");
-    Glu.add("GAG");
-    Asp.add("GAC");
-    Asp.add("GAT");
-
-    Tyr.add("TAC");
-    Tyr.add("TAT");
-
-    Thr.add("ACA");
-    Thr.add("ACG");
-    Thr.add("ACC");
-    Thr.add("ACT");
-
-    Pro.add("CCA");
-    Pro.add("CCG");
-    Pro.add("CCC");
-    Pro.add("CCT");
-
-    Ala.add("GCA");
-    Ala.add("GCG");
-    Ala.add("GCC");
-    Ala.add("GCT");
-
-    Ser.add("TCA");
-    Ser.add("TCG");
-    Ser.add("TCC");
-    Ser.add("TCT");
-    Ser.add("AGC");
-    Ser.add("AGT");
-
-    Arg.add("AGA");
-    Arg.add("AGG");
-    Arg.add("CGA");
-    Arg.add("CGG");
-    Arg.add("CGC");
-    Arg.add("CGT");
-
-    Gly.add("GGA");
-    Gly.add("GGG");
-    Gly.add("GGC");
-    Gly.add("GGT");
-
-    STOP.add("TGA");
-    STOP.add("TAA");
-    STOP.add("TAG");
-
-    Trp.add("TGG");
-
-    Cys.add("TGC");
-    Cys.add("TGT");
-
-    Ile.add("ATA");
-    Ile.add("ATC");
-    Ile.add("ATT");
-
-    Met.add("ATG");
-
-    Leu.add("CTA");
-    Leu.add("CTG");
-    Leu.add("CTC");
-    Leu.add("CTT");
-    Leu.add("TTA");
-    Leu.add("TTG");
-
-    Val.add("GTA");
-    Val.add("GTG");
-    Val.add("GTC");
-    Val.add("GTT");
-
-    Phe.add("TTC");
-    Phe.add("TTT");
   }
 
   // Stores residue codes/names and colours and other things
-  public static Hashtable propHash = new Hashtable();
+  public static Map<String, Map<String, Integer>> propHash = new Hashtable<String, Map<String, Integer>>();
 
-  public static Hashtable hydrophobic = new Hashtable();
+  public static Map<String, Integer> hydrophobic = new Hashtable<String, Integer>();
 
-  public static Hashtable polar = new Hashtable();
+  public static Map<String, Integer> polar = new Hashtable<String, Integer>();
 
-  public static Hashtable small = new Hashtable();
+  public static Map<String, Integer> small = new Hashtable<String, Integer>();
 
-  public static Hashtable positive = new Hashtable();
+  public static Map<String, Integer> positive = new Hashtable<String, Integer>();
 
-  public static Hashtable negative = new Hashtable();
+  public static Map<String, Integer> negative = new Hashtable<String, Integer>();
 
-  public static Hashtable charged = new Hashtable();
+  public static Map<String, Integer> charged = new Hashtable<String, Integer>();
 
-  public static Hashtable aromatic = new Hashtable();
+  public static Map<String, Integer> aromatic = new Hashtable<String, Integer>();
 
-  public static Hashtable aliphatic = new Hashtable();
+  public static Map<String, Integer> aliphatic = new Hashtable<String, Integer>();
 
-  public static Hashtable tiny = new Hashtable();
+  public static Map<String, Integer> tiny = new Hashtable<String, Integer>();
 
-  public static Hashtable proline = new Hashtable();
+  public static Map<String, Integer> proline = new Hashtable<String, Integer>();
 
   static
   {
-    hydrophobic.put("I", new Integer(1));
-    hydrophobic.put("L", new Integer(1));
-    hydrophobic.put("V", new Integer(1));
-    hydrophobic.put("C", new Integer(1));
-    hydrophobic.put("A", new Integer(1));
-    hydrophobic.put("G", new Integer(1));
-    hydrophobic.put("M", new Integer(1));
-    hydrophobic.put("F", new Integer(1));
-    hydrophobic.put("Y", new Integer(1));
-    hydrophobic.put("W", new Integer(1));
-    hydrophobic.put("H", new Integer(1));
-    hydrophobic.put("K", new Integer(1));
-    hydrophobic.put("X", new Integer(1));
-    hydrophobic.put("-", new Integer(1));
-    hydrophobic.put("*", new Integer(1));
-    hydrophobic.put("R", new Integer(0));
-    hydrophobic.put("E", new Integer(0));
-    hydrophobic.put("Q", new Integer(0));
-    hydrophobic.put("D", new Integer(0));
-    hydrophobic.put("N", new Integer(0));
-    hydrophobic.put("S", new Integer(0));
-    hydrophobic.put("T", new Integer(0));
-    hydrophobic.put("P", new Integer(0));
+    hydrophobic.put("I", ONE);
+    hydrophobic.put("L", ONE);
+    hydrophobic.put("V", ONE);
+    hydrophobic.put("C", ONE);
+    hydrophobic.put("A", ONE);
+    hydrophobic.put("G", ONE);
+    hydrophobic.put("M", ONE);
+    hydrophobic.put("F", ONE);
+    hydrophobic.put("Y", ONE);
+    hydrophobic.put("W", ONE);
+    hydrophobic.put("H", ONE);
+    hydrophobic.put("K", ONE);
+    hydrophobic.put("X", ONE);
+    hydrophobic.put("-", ONE);
+    hydrophobic.put("*", ONE);
+    hydrophobic.put("R", ZERO);
+    hydrophobic.put("E", ZERO);
+    hydrophobic.put("Q", ZERO);
+    hydrophobic.put("D", ZERO);
+    hydrophobic.put("N", ZERO);
+    hydrophobic.put("S", ZERO);
+    hydrophobic.put("T", ONE);
+    hydrophobic.put("P", ZERO);
   }
 
   static
   {
-    polar.put("Y", new Integer(1));
-    polar.put("W", new Integer(1));
-    polar.put("H", new Integer(1));
-    polar.put("K", new Integer(1));
-    polar.put("R", new Integer(1));
-    polar.put("E", new Integer(1));
-    polar.put("Q", new Integer(1));
-    polar.put("D", new Integer(1));
-    polar.put("N", new Integer(1));
-    polar.put("S", new Integer(1));
-    polar.put("T", new Integer(1));
-    polar.put("X", new Integer(1));
-    polar.put("-", new Integer(1));
-    polar.put("*", new Integer(1));
-    polar.put("I", new Integer(0));
-    polar.put("L", new Integer(0));
-    polar.put("V", new Integer(0));
-    polar.put("C", new Integer(0));
-    polar.put("A", new Integer(0));
-    polar.put("G", new Integer(0));
-    polar.put("M", new Integer(0));
-    polar.put("F", new Integer(0));
-    polar.put("P", new Integer(0));
+    polar.put("Y", ONE);
+    polar.put("W", ONE);
+    polar.put("H", ONE);
+    polar.put("K", ONE);
+    polar.put("R", ONE);
+    polar.put("E", ONE);
+    polar.put("Q", ONE);
+    polar.put("D", ONE);
+    polar.put("N", ONE);
+    polar.put("S", ONE);
+    polar.put("T", ONE);
+    polar.put("X", ONE);
+    polar.put("-", ONE);
+    polar.put("*", ONE);
+    polar.put("I", ZERO);
+    polar.put("L", ZERO);
+    polar.put("V", ZERO);
+    polar.put("C", ZERO);
+    polar.put("A", ZERO);
+    polar.put("G", ZERO);
+    polar.put("M", ZERO);
+    polar.put("F", ZERO);
+    polar.put("P", ZERO);
   }
 
   static
   {
-    small.put("I", new Integer(0));
-    small.put("L", new Integer(0));
-    small.put("V", new Integer(1));
-    small.put("C", new Integer(1));
-    small.put("A", new Integer(1));
-    small.put("G", new Integer(1));
-    small.put("M", new Integer(0));
-    small.put("F", new Integer(0));
-    small.put("Y", new Integer(0));
-    small.put("W", new Integer(0));
-    small.put("H", new Integer(0));
-    small.put("K", new Integer(0));
-    small.put("R", new Integer(0));
-    small.put("E", new Integer(0));
-    small.put("Q", new Integer(0));
-    small.put("D", new Integer(1));
-    small.put("N", new Integer(1));
-    small.put("S", new Integer(1));
-    small.put("T", new Integer(1));
-    small.put("P", new Integer(1));
-    small.put("-", new Integer(1));
-    small.put("*", new Integer(1));
+    small.put("I", ZERO);
+    small.put("L", ZERO);
+    small.put("V", ONE);
+    small.put("C", ONE);
+    small.put("A", ONE);
+    small.put("G", ONE);
+    small.put("M", ZERO);
+    small.put("F", ZERO);
+    small.put("Y", ZERO);
+    small.put("W", ZERO);
+    small.put("H", ZERO);
+    small.put("K", ZERO);
+    small.put("R", ZERO);
+    small.put("E", ZERO);
+    small.put("Q", ZERO);
+    small.put("D", ONE);
+    small.put("N", ONE);
+    small.put("S", ONE);
+    small.put("T", ONE);
+    small.put("P", ONE);
+    small.put("-", ONE);
+    small.put("*", ONE);
   }
 
   static
   {
-    positive.put("I", new Integer(0));
-    positive.put("L", new Integer(0));
-    positive.put("V", new Integer(0));
-    positive.put("C", new Integer(0));
-    positive.put("A", new Integer(0));
-    positive.put("G", new Integer(0));
-    positive.put("M", new Integer(0));
-    positive.put("F", new Integer(0));
-    positive.put("Y", new Integer(0));
-    positive.put("W", new Integer(0));
-    positive.put("H", new Integer(1));
-    positive.put("K", new Integer(1));
-    positive.put("R", new Integer(1));
-    positive.put("E", new Integer(0));
-    positive.put("Q", new Integer(0));
-    positive.put("D", new Integer(0));
-    positive.put("N", new Integer(0));
-    positive.put("S", new Integer(0));
-    positive.put("T", new Integer(0));
-    positive.put("P", new Integer(0));
-    positive.put("-", new Integer(1));
-    positive.put("*", new Integer(1));
+    positive.put("I", ZERO);
+    positive.put("L", ZERO);
+    positive.put("V", ZERO);
+    positive.put("C", ZERO);
+    positive.put("A", ZERO);
+    positive.put("G", ZERO);
+    positive.put("M", ZERO);
+    positive.put("F", ZERO);
+    positive.put("Y", ZERO);
+    positive.put("W", ZERO);
+    positive.put("H", ONE);
+    positive.put("K", ONE);
+    positive.put("R", ONE);
+    positive.put("E", ZERO);
+    positive.put("Q", ZERO);
+    positive.put("D", ZERO);
+    positive.put("N", ZERO);
+    positive.put("S", ZERO);
+    positive.put("T", ZERO);
+    positive.put("P", ZERO);
+    positive.put("-", ONE);
+    positive.put("*", ONE);
   }
 
   static
   {
-    negative.put("I", new Integer(0));
-    negative.put("L", new Integer(0));
-    negative.put("V", new Integer(0));
-    negative.put("C", new Integer(0));
-    negative.put("A", new Integer(0));
-    negative.put("G", new Integer(0));
-    negative.put("M", new Integer(0));
-    negative.put("F", new Integer(0));
-    negative.put("Y", new Integer(0));
-    negative.put("W", new Integer(0));
-    negative.put("H", new Integer(0));
-    negative.put("K", new Integer(0));
-    negative.put("R", new Integer(0));
-    negative.put("E", new Integer(1));
-    negative.put("Q", new Integer(0));
-    negative.put("D", new Integer(1));
-    negative.put("N", new Integer(0));
-    negative.put("S", new Integer(0));
-    negative.put("T", new Integer(0));
-    negative.put("P", new Integer(0));
-    negative.put("-", new Integer(1));
-    negative.put("*", new Integer(1));
+    negative.put("I", ZERO);
+    negative.put("L", ZERO);
+    negative.put("V", ZERO);
+    negative.put("C", ZERO);
+    negative.put("A", ZERO);
+    negative.put("G", ZERO);
+    negative.put("M", ZERO);
+    negative.put("F", ZERO);
+    negative.put("Y", ZERO);
+    negative.put("W", ZERO);
+    negative.put("H", ZERO);
+    negative.put("K", ZERO);
+    negative.put("R", ZERO);
+    negative.put("E", ONE);
+    negative.put("Q", ZERO);
+    negative.put("D", ONE);
+    negative.put("N", ZERO);
+    negative.put("S", ZERO);
+    negative.put("T", ZERO);
+    negative.put("P", ZERO);
+    negative.put("-", ONE);
+    negative.put("*", ONE);
   }
 
   static
   {
-    charged.put("I", new Integer(0));
-    charged.put("L", new Integer(0));
-    charged.put("V", new Integer(0));
-    charged.put("C", new Integer(0));
-    charged.put("A", new Integer(0));
-    charged.put("G", new Integer(0));
-    charged.put("M", new Integer(0));
-    charged.put("F", new Integer(0));
-    charged.put("Y", new Integer(0));
-    charged.put("W", new Integer(0));
-    charged.put("H", new Integer(1));
-    charged.put("K", new Integer(1));
-    charged.put("R", new Integer(1));
-    charged.put("E", new Integer(1));
-    charged.put("Q", new Integer(0));
-    charged.put("D", new Integer(1));
-    charged.put("N", new Integer(0)); // Asparagine is polar but not charged.
-                                      // Alternative would be charged and
-                                      // negative (in basic form)?
-    charged.put("S", new Integer(0));
-    charged.put("T", new Integer(0));
-    charged.put("P", new Integer(0));
-    charged.put("-", new Integer(1));
-    charged.put("*", new Integer(1));
+    charged.put("I", ZERO);
+    charged.put("L", ZERO);
+    charged.put("V", ZERO);
+    charged.put("C", ZERO);
+    charged.put("A", ZERO);
+    charged.put("G", ZERO);
+    charged.put("M", ZERO);
+    charged.put("F", ZERO);
+    charged.put("Y", ZERO);
+    charged.put("W", ZERO);
+    charged.put("H", ONE);
+    charged.put("K", ONE);
+    charged.put("R", ONE);
+    charged.put("E", ONE);
+    charged.put("Q", ZERO);
+    charged.put("D", ONE);
+    charged.put("N", ZERO); // Asparagine is polar but not
+                                          // charged.
+    // Alternative would be charged and
+    // negative (in basic form)?
+    charged.put("S", ZERO);
+    charged.put("T", ZERO);
+    charged.put("P", ZERO);
+    charged.put("-", ONE);
+    charged.put("*", ONE);
   }
 
   static
   {
-    aromatic.put("I", new Integer(0));
-    aromatic.put("L", new Integer(0));
-    aromatic.put("V", new Integer(0));
-    aromatic.put("C", new Integer(0));
-    aromatic.put("A", new Integer(0));
-    aromatic.put("G", new Integer(0));
-    aromatic.put("M", new Integer(0));
-    aromatic.put("F", new Integer(1));
-    aromatic.put("Y", new Integer(1));
-    aromatic.put("W", new Integer(1));
-    aromatic.put("H", new Integer(1));
-    aromatic.put("K", new Integer(0));
-    aromatic.put("R", new Integer(0));
-    aromatic.put("E", new Integer(0));
-    aromatic.put("Q", new Integer(0));
-    aromatic.put("D", new Integer(0));
-    aromatic.put("N", new Integer(0));
-    aromatic.put("S", new Integer(0));
-    aromatic.put("T", new Integer(0));
-    aromatic.put("P", new Integer(0));
-    aromatic.put("-", new Integer(1));
-    aromatic.put("*", new Integer(1));
+    aromatic.put("I", ZERO);
+    aromatic.put("L", ZERO);
+    aromatic.put("V", ZERO);
+    aromatic.put("C", ZERO);
+    aromatic.put("A", ZERO);
+    aromatic.put("G", ZERO);
+    aromatic.put("M", ZERO);
+    aromatic.put("F", ONE);
+    aromatic.put("Y", ONE);
+    aromatic.put("W", ONE);
+    aromatic.put("H", ONE);
+    aromatic.put("K", ZERO);
+    aromatic.put("R", ZERO);
+    aromatic.put("E", ZERO);
+    aromatic.put("Q", ZERO);
+    aromatic.put("D", ZERO);
+    aromatic.put("N", ZERO);
+    aromatic.put("S", ZERO);
+    aromatic.put("T", ZERO);
+    aromatic.put("P", ZERO);
+    aromatic.put("-", ONE);
+    aromatic.put("*", ONE);
   }
 
   static
   {
-    aliphatic.put("I", new Integer(1));
-    aliphatic.put("L", new Integer(1));
-    aliphatic.put("V", new Integer(1));
-    aliphatic.put("C", new Integer(0));
-    aliphatic.put("A", new Integer(0));
-    aliphatic.put("G", new Integer(0));
-    aliphatic.put("M", new Integer(0));
-    aliphatic.put("F", new Integer(0));
-    aliphatic.put("Y", new Integer(0));
-    aliphatic.put("W", new Integer(0));
-    aliphatic.put("H", new Integer(0));
-    aliphatic.put("K", new Integer(0));
-    aliphatic.put("R", new Integer(0));
-    aliphatic.put("E", new Integer(0));
-    aliphatic.put("Q", new Integer(0));
-    aliphatic.put("D", new Integer(0));
-    aliphatic.put("N", new Integer(0));
-    aliphatic.put("S", new Integer(0));
-    aliphatic.put("T", new Integer(0));
-    aliphatic.put("P", new Integer(0));
-    aliphatic.put("-", new Integer(1));
-    aliphatic.put("*", new Integer(1));
+    aliphatic.put("I", ONE);
+    aliphatic.put("L", ONE);
+    aliphatic.put("V", ONE);
+    aliphatic.put("C", ZERO);
+    aliphatic.put("A", ZERO);
+    aliphatic.put("G", ZERO);
+    aliphatic.put("M", ZERO);
+    aliphatic.put("F", ZERO);
+    aliphatic.put("Y", ZERO);
+    aliphatic.put("W", ZERO);
+    aliphatic.put("H", ZERO);
+    aliphatic.put("K", ZERO);
+    aliphatic.put("R", ZERO);
+    aliphatic.put("E", ZERO);
+    aliphatic.put("Q", ZERO);
+    aliphatic.put("D", ZERO);
+    aliphatic.put("N", ZERO);
+    aliphatic.put("S", ZERO);
+    aliphatic.put("T", ZERO);
+    aliphatic.put("P", ZERO);
+    aliphatic.put("-", ONE);
+    aliphatic.put("*", ONE);
   }
 
   static
   {
-    tiny.put("I", new Integer(0));
-    tiny.put("L", new Integer(0));
-    tiny.put("V", new Integer(0));
-    tiny.put("C", new Integer(0));
-    tiny.put("A", new Integer(1));
-    tiny.put("G", new Integer(1));
-    tiny.put("M", new Integer(0));
-    tiny.put("F", new Integer(0));
-    tiny.put("Y", new Integer(0));
-    tiny.put("W", new Integer(0));
-    tiny.put("H", new Integer(0));
-    tiny.put("K", new Integer(0));
-    tiny.put("R", new Integer(0));
-    tiny.put("E", new Integer(0));
-    tiny.put("Q", new Integer(0));
-    tiny.put("D", new Integer(0));
-    tiny.put("N", new Integer(0));
-    tiny.put("S", new Integer(1));
-    tiny.put("T", new Integer(0));
-    tiny.put("P", new Integer(0));
-    tiny.put("-", new Integer(1));
-    tiny.put("*", new Integer(1));
+    tiny.put("I", ZERO);
+    tiny.put("L", ZERO);
+    tiny.put("V", ZERO);
+    tiny.put("C", ZERO);
+    tiny.put("A", ONE);
+    tiny.put("G", ONE);
+    tiny.put("M", ZERO);
+    tiny.put("F", ZERO);
+    tiny.put("Y", ZERO);
+    tiny.put("W", ZERO);
+    tiny.put("H", ZERO);
+    tiny.put("K", ZERO);
+    tiny.put("R", ZERO);
+    tiny.put("E", ZERO);
+    tiny.put("Q", ZERO);
+    tiny.put("D", ZERO);
+    tiny.put("N", ZERO);
+    tiny.put("S", ONE);
+    tiny.put("T", ZERO);
+    tiny.put("P", ZERO);
+    tiny.put("-", ONE);
+    tiny.put("*", ONE);
   }
 
   static
   {
-    proline.put("I", new Integer(0));
-    proline.put("L", new Integer(0));
-    proline.put("V", new Integer(0));
-    proline.put("C", new Integer(0));
-    proline.put("A", new Integer(0));
-    proline.put("G", new Integer(0));
-    proline.put("M", new Integer(0));
-    proline.put("F", new Integer(0));
-    proline.put("Y", new Integer(0));
-    proline.put("W", new Integer(0));
-    proline.put("H", new Integer(0));
-    proline.put("K", new Integer(0));
-    proline.put("R", new Integer(0));
-    proline.put("E", new Integer(0));
-    proline.put("Q", new Integer(0));
-    proline.put("D", new Integer(0));
-    proline.put("N", new Integer(0));
-    proline.put("S", new Integer(0));
-    proline.put("T", new Integer(0));
-    proline.put("P", new Integer(1));
-    proline.put("-", new Integer(1));
-    proline.put("*", new Integer(1));
+    proline.put("I", ZERO);
+    proline.put("L", ZERO);
+    proline.put("V", ZERO);
+    proline.put("C", ZERO);
+    proline.put("A", ZERO);
+    proline.put("G", ZERO);
+    proline.put("M", ZERO);
+    proline.put("F", ZERO);
+    proline.put("Y", ZERO);
+    proline.put("W", ZERO);
+    proline.put("H", ZERO);
+    proline.put("K", ZERO);
+    proline.put("R", ZERO);
+    proline.put("E", ZERO);
+    proline.put("Q", ZERO);
+    proline.put("D", ZERO);
+    proline.put("N", ZERO);
+    proline.put("S", ZERO);
+    proline.put("T", ZERO);
+    proline.put("P", ONE);
+    proline.put("-", ONE);
+    proline.put("*", ONE);
   }
 
   static
@@ -1367,11 +1219,9 @@ public class ResidueProperties
         propMatrixF[i][j] = 0;
         propMatrixPos[i][j] = 0;
         propMatrixEpos[i][j] = 0;
-        for (Enumeration<String> en = propHash.keys(); en.hasMoreElements();)
+        for (String ph : propHash.keySet())
         {
-          String ph = en.nextElement();
-          Map<String, Integer> pph = (Map<String, Integer>) propHash
-                  .get(ph);
+          Map<String, Integer> pph = propHash.get(ph);
           if (pph.get(ic) != null && pph.get(jc) != null)
           {
             int icp = pph.get(ic).intValue(), jcp = pph.get(jc).intValue();
@@ -1469,22 +1319,8 @@ public class ResidueProperties
     return pog;
   }
 
-  public static Vector getCodons(String res)
-  {
-    if (codonHash.containsKey(res))
-    {
-      return (Vector) codonHash.get(res);
-    }
-
-    return null;
-  }
-
   public static String codonTranslate(String lccodon)
   {
-    if (false)
-    {
-      return _codonTranslate(lccodon);
-    }
     String cdn = codonHash2.get(lccodon.toUpperCase());
     if ("*".equals(cdn))
     {
@@ -1493,25 +1329,6 @@ public class ResidueProperties
     return cdn;
   }
 
-  public static String _codonTranslate(String lccodon)
-  {
-    String codon = lccodon.toUpperCase();
-    // all base ambiguity codes yield an 'X' amino acid residue
-    if (codon.indexOf('X') > -1 || codon.indexOf('N') > -1)
-    {
-      return "X";
-    }
-    for (String key : codonHash.keySet())
-    {
-      if (codonHash.get(key).contains(codon))
-      {
-        return key;
-      }
-    }
-
-    return null;
-  }
-
   public static int[][] getDefaultPeptideMatrix()
   {
     return ResidueProperties.getBLOSUM62();
@@ -1559,10 +1376,10 @@ public class ResidueProperties
     return pog;
   }
 
-  public static Hashtable toDssp3State;
+  public static Hashtable<String, String> toDssp3State;
   static
   {
-    toDssp3State = new Hashtable();
+    toDssp3State = new Hashtable<String, String>();
     toDssp3State.put("H", "H");
     toDssp3State.put("E", "E");
     toDssp3State.put("C", " ");
@@ -1592,7 +1409,7 @@ public class ResidueProperties
       String ssc = ssstring.substring(i, i + 1);
       if (toDssp3State.containsKey(ssc))
       {
-        ss.append((String) toDssp3State.get(ssc));
+        ss.append(toDssp3State.get(ssc));
       }
       else
       {
@@ -1602,87 +1419,6 @@ public class ResidueProperties
     return ss.toString();
   }
 
-  /**
-   * Used by getRNASecStrucState
-   * 
-   */
-  public static Hashtable<String, String> toRNAssState;
-
-  public static boolean RNAcloseParen[] = new boolean[255];
-  static
-  {
-    toRNAssState = new Hashtable<String, String>();
-    toRNAssState.put(")", "(");
-    toRNAssState.put("(", "(");
-    toRNAssState.put("]", "[");
-    toRNAssState.put("[", "[");
-    toRNAssState.put("{", "{");
-    toRNAssState.put("}", "{");
-    toRNAssState.put(">", ">");
-    toRNAssState.put("<", ">");
-    toRNAssState.put("A", "A");
-    toRNAssState.put("a", "A");
-    toRNAssState.put("B", "B");
-    toRNAssState.put("b", "B");
-    toRNAssState.put("C", "C");
-    toRNAssState.put("c", "C");
-    toRNAssState.put("D", "D");
-    toRNAssState.put("d", "D");
-    toRNAssState.put("E", "E");
-    toRNAssState.put("e", "E");
-    toRNAssState.put("F", "F");
-    toRNAssState.put("f", "F");
-    toRNAssState.put("G", "G");
-    toRNAssState.put("g", "G");
-    toRNAssState.put("H", "H");
-    toRNAssState.put("h", "H");
-    toRNAssState.put("I", "I");
-    toRNAssState.put("i", "I");
-    toRNAssState.put("J", "J");
-    toRNAssState.put("j", "J");
-    toRNAssState.put("K", "K");
-    toRNAssState.put("k", "K");
-    toRNAssState.put("L", "L");
-    toRNAssState.put("l", "L");
-    toRNAssState.put("M", "M");
-    toRNAssState.put("m", "M");
-    toRNAssState.put("N", "N");
-    toRNAssState.put("n", "N");
-    toRNAssState.put("O", "O");
-    toRNAssState.put("o", "O");
-    toRNAssState.put("P", "P");
-    toRNAssState.put("p", "P");
-    toRNAssState.put("Q", "Q");
-    toRNAssState.put("q", "Q");
-    toRNAssState.put("R", "R");
-    toRNAssState.put("r", "R");
-    toRNAssState.put("S", "S");
-    toRNAssState.put("s", "S");
-    toRNAssState.put("T", "T");
-    toRNAssState.put("t", "T");
-    toRNAssState.put("U", "U");
-    toRNAssState.put("u", "U");
-    toRNAssState.put("V", "V");
-    toRNAssState.put("v", "V");
-    toRNAssState.put("W", "W");
-    toRNAssState.put("w", "W");
-    toRNAssState.put("X", "X");
-    toRNAssState.put("x", "X");
-    toRNAssState.put("Y", "Y");
-    toRNAssState.put("y", "Y");
-    toRNAssState.put("Z", "Z");
-    toRNAssState.put("z", "Z");
-    for (int p = 0; p < RNAcloseParen.length; p++)
-    {
-      RNAcloseParen[p] = false;
-    }
-    for (String k : toRNAssState.keySet())
-    {
-      RNAcloseParen[k.charAt(0)] = k.charAt(0) != toRNAssState.get(k)
-              .charAt(0);
-    }
-  }
-
   static
   {
     modifications.put("MSE", "MET"); // Selenomethionine
@@ -1695,87 +1431,1353 @@ public class ResidueProperties
     // modifications.put("5HP", "GLU"); // Pyroglutamic acid; 5-hydroxyproline
     // modifications.put("PCA", "GLU"); // Pyroglutamic acid
     // modifications.put("LYZ", "LYS"); // 5-hydroxylysine
-  }
 
-  public static String getCanonicalAminoAcid(String aa)
-  {
-    String canonical = modifications.get(aa);
-    return canonical == null ? aa : canonical;
-  }
+    // Additional protein alphabets used in the SCOP database and PDB files
+    // source:
+    // https://github.com/biopython/biopython/blob/master/Bio/Data/SCOPData.py
+    modifications.put("00C", "CYS");
+    modifications.put("01W", "XAA");
+    modifications.put("02K", "ALA");
+    modifications.put("03Y", "CYS");
+    modifications.put("07O", "CYS");
+    modifications.put("08P", "CYS");
+    modifications.put("0A0", "ASP");
+    modifications.put("0A1", "TYR");
+    modifications.put("0A2", "LYS");
+    modifications.put("0A8", "CYS");
+    modifications.put("0AA", "VAL");
+    modifications.put("0AB", "VAL");
+    modifications.put("0AC", "GLY");
+    modifications.put("0AD", "GLY");
+    modifications.put("0AF", "TRP");
+    modifications.put("0AG", "LEU");
+    modifications.put("0AH", "SER");
+    modifications.put("0AK", "ASP");
+    modifications.put("0AM", "ALA");
+    modifications.put("0AP", "CYS");
+    modifications.put("0AU", "UR3");
+    modifications.put("0AV", "ALA");
+    modifications.put("0AZ", "PRO");
+    modifications.put("0BN", "PHE");
+    modifications.put("0C ", "CYS");
+    modifications.put("0CS", "ALA");
+    modifications.put("0DC", "CYS");
+    modifications.put("0DG", "GLY");
+    modifications.put("0DT", "THR");
+    modifications.put("0FL", "ALA");
+    modifications.put("0G ", "GLY");
+    modifications.put("0NC", "ALA");
+    modifications.put("0SP", "ALA");
+    modifications.put("0U ", "UR3");
+    modifications.put("0YG", "YG");
+    modifications.put("10C", "CYS");
+    modifications.put("125", "UR3");
+    modifications.put("126", "UR3");
+    modifications.put("127", "UR3");
+    modifications.put("128", "ASN");
+    modifications.put("12A", "ALA");
+    modifications.put("143", "CYS");
+    modifications.put("175", "ASG");
+    modifications.put("193", "XAA");
+    modifications.put("1AP", "ALA");
+    modifications.put("1MA", "ALA");
+    modifications.put("1MG", "GLY");
+    modifications.put("1PA", "PHE");
+    modifications.put("1PI", "ALA");
+    modifications.put("1PR", "ASN");
+    modifications.put("1SC", "CYS");
+    modifications.put("1TQ", "TRP");
+    modifications.put("1TY", "TYR");
+    modifications.put("1X6", "SER");
+    modifications.put("200", "PHE");
+    modifications.put("23F", "PHE");
+    modifications.put("23S", "XAA");
+    modifications.put("26B", "THR");
+    modifications.put("2AD", "XAA");
+    modifications.put("2AG", "ALA");
+    modifications.put("2AO", "XAA");
+    modifications.put("2AR", "ALA");
+    modifications.put("2AS", "XAA");
+    modifications.put("2AT", "THR");
+    modifications.put("2AU", "UR3");
+    modifications.put("2BD", "ILE");
+    modifications.put("2BT", "THR");
+    modifications.put("2BU", "ALA");
+    modifications.put("2CO", "CYS");
+    modifications.put("2DA", "ALA");
+    modifications.put("2DF", "ASN");
+    modifications.put("2DM", "ASN");
+    modifications.put("2DO", "XAA");
+    modifications.put("2DT", "THR");
+    modifications.put("2EG", "GLY");
+    modifications.put("2FE", "ASN");
+    modifications.put("2FI", "ASN");
+    modifications.put("2FM", "MET");
+    modifications.put("2GT", "THR");
+    modifications.put("2HF", "HIS");
+    modifications.put("2LU", "LEU");
+    modifications.put("2MA", "ALA");
+    modifications.put("2MG", "GLY");
+    modifications.put("2ML", "LEU");
+    modifications.put("2MR", "ARG");
+    modifications.put("2MT", "PRO");
+    modifications.put("2MU", "UR3");
+    modifications.put("2NT", "THR");
+    modifications.put("2OM", "UR3");
+    modifications.put("2OT", "THR");
+    modifications.put("2PI", "XAA");
+    modifications.put("2PR", "GLY");
+    modifications.put("2SA", "ASN");
+    modifications.put("2SI", "XAA");
+    modifications.put("2ST", "THR");
+    modifications.put("2TL", "THR");
+    modifications.put("2TY", "TYR");
+    modifications.put("2VA", "VAL");
+    modifications.put("2XA", "CYS");
+    modifications.put("32S", "XAA");
+    modifications.put("32T", "XAA");
+    modifications.put("3AH", "HIS");
+    modifications.put("3AR", "XAA");
+    modifications.put("3CF", "PHE");
+    modifications.put("3DA", "ALA");
+    modifications.put("3DR", "ASN");
+    modifications.put("3GA", "ALA");
+    modifications.put("3MD", "ASP");
+    modifications.put("3ME", "UR3");
+    modifications.put("3NF", "TYR");
+    modifications.put("3QN", "LYS");
+    modifications.put("3TY", "XAA");
+    modifications.put("3XH", "GLY");
+    modifications.put("4AC", "ASN");
+    modifications.put("4BF", "TYR");
+    modifications.put("4CF", "PHE");
+    modifications.put("4CY", "MET");
+    modifications.put("4DP", "TRP");
+    modifications.put("4F3", "GYG");
+    modifications.put("4FB", "PRO");
+    modifications.put("4FW", "TRP");
+    modifications.put("4HT", "TRP");
+    modifications.put("4IN", "TRP");
+    modifications.put("4MF", "ASN");
+    modifications.put("4MM", "XAA");
+    modifications.put("4OC", "CYS");
+    modifications.put("4PC", "CYS");
+    modifications.put("4PD", "CYS");
+    modifications.put("4PE", "CYS");
+    modifications.put("4PH", "PHE");
+    modifications.put("4SC", "CYS");
+    modifications.put("4SU", "UR3");
+    modifications.put("4TA", "ASN");
+    modifications.put("4U7", "ALA");
+    modifications.put("56A", "HIS");
+    modifications.put("5AA", "ALA");
+    modifications.put("5AB", "ALA");
+    modifications.put("5AT", "THR");
+    modifications.put("5BU", "UR3");
+    modifications.put("5CG", "GLY");
+    modifications.put("5CM", "CYS");
+    modifications.put("5CS", "CYS");
+    modifications.put("5FA", "ALA");
+    modifications.put("5FC", "CYS");
+    modifications.put("5FU", "UR3");
+    modifications.put("5HP", "GLU");
+    modifications.put("5HT", "THR");
+    modifications.put("5HU", "UR3");
+    modifications.put("5IC", "CYS");
+    modifications.put("5IT", "THR");
+    modifications.put("5IU", "UR3");
+    modifications.put("5MC", "CYS");
+    modifications.put("5MD", "ASN");
+    modifications.put("5MU", "UR3");
+    modifications.put("5NC", "CYS");
+    modifications.put("5PC", "CYS");
+    modifications.put("5PY", "THR");
+    modifications.put("5SE", "UR3");
+    modifications.put("5ZA", "TWG");
+    modifications.put("64T", "THR");
+    modifications.put("6CL", "LYS");
+    modifications.put("6CT", "THR");
+    modifications.put("6CW", "TRP");
+    modifications.put("6HA", "ALA");
+    modifications.put("6HC", "CYS");
+    modifications.put("6HG", "GLY");
+    modifications.put("6HN", "LYS");
+    modifications.put("6HT", "THR");
+    modifications.put("6IA", "ALA");
+    modifications.put("6MA", "ALA");
+    modifications.put("6MC", "ALA");
+    modifications.put("6MI", "ASN");
+    modifications.put("6MT", "ALA");
+    modifications.put("6MZ", "ASN");
+    modifications.put("6OG", "GLY");
+    modifications.put("70U", "UR3");
+    modifications.put("7DA", "ALA");
+    modifications.put("7GU", "GLY");
+    modifications.put("7JA", "ILE");
+    modifications.put("7MG", "GLY");
+    modifications.put("8AN", "ALA");
+    modifications.put("8FG", "GLY");
+    modifications.put("8MG", "GLY");
+    modifications.put("8OG", "GLY");
+    modifications.put("9NE", "GLU");
+    modifications.put("9NF", "PHE");
+    modifications.put("9NR", "ARG");
+    modifications.put("9NV", "VAL");
+    modifications.put("A  ", "ALA");
+    modifications.put("A1P", "ASN");
+    modifications.put("A23", "ALA");
+    modifications.put("A2L", "ALA");
+    modifications.put("A2M", "ALA");
+    modifications.put("A34", "ALA");
+    modifications.put("A35", "ALA");
+    modifications.put("A38", "ALA");
+    modifications.put("A39", "ALA");
+    modifications.put("A3A", "ALA");
+    modifications.put("A3P", "ALA");
+    modifications.put("A40", "ALA");
+    modifications.put("A43", "ALA");
+    modifications.put("A44", "ALA");
+    modifications.put("A47", "ALA");
+    modifications.put("A5L", "ALA");
+    modifications.put("A5M", "CYS");
+    modifications.put("A5N", "ASN");
+    modifications.put("A5O", "ALA");
+    modifications.put("A66", "XAA");
+    modifications.put("AA3", "ALA");
+    modifications.put("AA4", "ALA");
+    modifications.put("AAR", "ARG");
+    modifications.put("AB7", "XAA");
+    modifications.put("ABA", "ALA");
+    modifications.put("ABR", "ALA");
+    modifications.put("ABS", "ALA");
+    modifications.put("ABT", "ASN");
+    modifications.put("ACB", "ASP");
+    modifications.put("ACL", "ARG");
+    modifications.put("AD2", "ALA");
+    modifications.put("ADD", "XAA");
+    modifications.put("ADX", "ASN");
+    modifications.put("AEA", "XAA");
+    modifications.put("AEI", "ASP");
+    modifications.put("AET", "ALA");
+    modifications.put("AFA", "ASN");
+    modifications.put("AFF", "ASN");
+    modifications.put("AFG", "GLY");
+    modifications.put("AGM", "ARG");
+    modifications.put("AGT", "CYS");
+    modifications.put("AHB", "ASN");
+    modifications.put("AHH", "XAA");
+    modifications.put("AHO", "ALA");
+    modifications.put("AHP", "ALA");
+    modifications.put("AHS", "XAA");
+    modifications.put("AHT", "XAA");
+    modifications.put("AIB", "ALA");
+    modifications.put("AKL", "ASP");
+    modifications.put("AKZ", "ASP");
+    modifications.put("ALA", "ALA");
+    modifications.put("ALC", "ALA");
+    modifications.put("ALM", "ALA");
+    modifications.put("ALN", "ALA");
+    modifications.put("ALO", "THR");
+    modifications.put("ALQ", "XAA");
+    modifications.put("ALS", "ALA");
+    modifications.put("ALT", "ALA");
+    modifications.put("ALV", "ALA");
+    modifications.put("ALY", "LYS");
+    modifications.put("AN8", "ALA");
+    modifications.put("AP7", "ALA");
+    modifications.put("APE", "XAA");
+    modifications.put("APH", "ALA");
+    modifications.put("API", "LYS");
+    modifications.put("APK", "LYS");
+    modifications.put("APM", "XAA");
+    modifications.put("APP", "XAA");
+    modifications.put("AR2", "ARG");
+    modifications.put("AR4", "GLU");
+    modifications.put("AR7", "ARG");
+    modifications.put("ARG", "ARG");
+    modifications.put("ARM", "ARG");
+    modifications.put("ARO", "ARG");
+    modifications.put("ARV", "XAA");
+    modifications.put("AS ", "ALA");
+    modifications.put("AS2", "ASP");
+    modifications.put("AS9", "XAA");
+    modifications.put("ASA", "ASP");
+    modifications.put("ASB", "ASP");
+    modifications.put("ASI", "ASP");
+    modifications.put("ASK", "ASP");
+    modifications.put("ASL", "ASP");
+    modifications.put("ASM", "XAA");
+    modifications.put("ASN", "ASN");
+    modifications.put("ASP", "ASP");
+    modifications.put("ASQ", "ASP");
+    modifications.put("ASU", "ASN");
+    modifications.put("ASX", "ASX");
+    modifications.put("ATD", "THR");
+    modifications.put("ATL", "THR");
+    modifications.put("ATM", "THR");
+    modifications.put("AVC", "ALA");
+    modifications.put("AVN", "XAA");
+    modifications.put("AYA", "ALA");
+    modifications.put("AYG", "AYG");
+    modifications.put("AZK", "LYS");
+    modifications.put("AZS", "SER");
+    modifications.put("AZY", "TYR");
+    modifications.put("B1F", "PHE");
+    modifications.put("B1P", "ASN");
+    modifications.put("B2A", "ALA");
+    modifications.put("B2F", "PHE");
+    modifications.put("B2I", "ILE");
+    modifications.put("B2V", "VAL");
+    modifications.put("B3A", "ALA");
+    modifications.put("B3D", "ASP");
+    modifications.put("B3E", "GLU");
+    modifications.put("B3K", "LYS");
+    modifications.put("B3L", "XAA");
+    modifications.put("B3M", "XAA");
+    modifications.put("B3Q", "XAA");
+    modifications.put("B3S", "SER");
+    modifications.put("B3T", "XAA");
+    modifications.put("B3U", "HIS");
+    modifications.put("B3X", "ASN");
+    modifications.put("B3Y", "TYR");
+    modifications.put("BB6", "CYS");
+    modifications.put("BB7", "CYS");
+    modifications.put("BB8", "PHE");
+    modifications.put("BB9", "CYS");
+    modifications.put("BBC", "CYS");
+    modifications.put("BCS", "CYS");
+    modifications.put("BE2", "XAA");
+    modifications.put("BFD", "ASP");
+    modifications.put("BG1", "SER");
+    modifications.put("BGM", "GLY");
+    modifications.put("BH2", "ASP");
+    modifications.put("BHD", "ASP");
+    modifications.put("BIF", "PHE");
+    modifications.put("BIL", "XAA");
+    modifications.put("BIU", "ILE");
+    modifications.put("BJH", "XAA");
+    modifications.put("BLE", "LEU");
+    modifications.put("BLY", "LYS");
+    modifications.put("BMP", "ASN");
+    modifications.put("BMT", "THR");
+    modifications.put("BNN", "PHE");
+    modifications.put("BNO", "XAA");
+    modifications.put("BOE", "THR");
+    modifications.put("BOR", "ARG");
+    modifications.put("BPE", "CYS");
+    modifications.put("BRU", "UR3");
+    modifications.put("BSE", "SER");
+    modifications.put("BT5", "ASN");
+    modifications.put("BTA", "LEU");
+    modifications.put("BTC", "CYS");
+    modifications.put("BTR", "TRP");
+    modifications.put("BUC", "CYS");
+    modifications.put("BUG", "VAL");
+    modifications.put("BVP", "UR3");
+    modifications.put("BZG", "ASN");
+    modifications.put("C  ", "CYS");
+    modifications.put("C12", "TYG");
+    modifications.put("C1X", "LYS");
+    modifications.put("C25", "CYS");
+    modifications.put("C2L", "CYS");
+    modifications.put("C2S", "CYS");
+    modifications.put("C31", "CYS");
+    modifications.put("C32", "CYS");
+    modifications.put("C34", "CYS");
+    modifications.put("C36", "CYS");
+    modifications.put("C37", "CYS");
+    modifications.put("C38", "CYS");
+    modifications.put("C3Y", "CYS");
+    modifications.put("C42", "CYS");
+    modifications.put("C43", "CYS");
+    modifications.put("C45", "CYS");
+    modifications.put("C46", "CYS");
+    modifications.put("C49", "CYS");
+    modifications.put("C4R", "CYS");
+    modifications.put("C4S", "CYS");
+    modifications.put("C5C", "CYS");
+    modifications.put("C66", "XAA");
+    modifications.put("C6C", "CYS");
+    modifications.put("C99", "TFG");
+    modifications.put("CAF", "CYS");
+    modifications.put("CAL", "XAA");
+    modifications.put("CAR", "CYS");
+    modifications.put("CAS", "CYS");
+    modifications.put("CAV", "XAA");
+    modifications.put("CAY", "CYS");
+    modifications.put("CB2", "CYS");
+    modifications.put("CBR", "CYS");
+    modifications.put("CBV", "CYS");
+    modifications.put("CCC", "CYS");
+    modifications.put("CCL", "LYS");
+    modifications.put("CCS", "CYS");
+    modifications.put("CCY", "CYG");
+    modifications.put("CDE", "XAA");
+    modifications.put("CDV", "XAA");
+    modifications.put("CDW", "CYS");
+    modifications.put("CEA", "CYS");
+    modifications.put("CFL", "CYS");
+    modifications.put("CFY", "FCYG"); // check
+    modifications.put("CG1", "GLY");
+    modifications.put("CGA", "GLU");
+    modifications.put("CGU", "GLU");
+    modifications.put("CH ", "CYS");
+    modifications.put("CH6", "MYG");
+    modifications.put("CH7", "KYG");
+    modifications.put("CHF", "XAA");
+    modifications.put("CHG", "XAA");
+    modifications.put("CHP", "GLY");
+    modifications.put("CHS", "XAA");
+    modifications.put("CIR", "ARG");
+    modifications.put("CJO", "GYG");
+    modifications.put("CLE", "LEU");
+    modifications.put("CLG", "LYS");
+    modifications.put("CLH", "LYS");
+    modifications.put("CLV", "AFG");
+    modifications.put("CM0", "ASN");
+    modifications.put("CME", "CYS");
+    modifications.put("CMH", "CYS");
+    modifications.put("CML", "CYS");
+    modifications.put("CMR", "CYS");
+    modifications.put("CMT", "CYS");
+    modifications.put("CNU", "UR3");
+    modifications.put("CP1", "CYS");
+    modifications.put("CPC", "XAA");
+    modifications.put("CPI", "XAA");
+    modifications.put("CQR", "GYG");
+    modifications.put("CR0", "TLG");
+    modifications.put("CR2", "GYG");
+    modifications.put("CR5", "GLY");
+    modifications.put("CR7", "KYG");
+    modifications.put("CR8", "HYG");
+    modifications.put("CRF", "TWG");
+    modifications.put("CRG", "THG");
+    modifications.put("CRK", "MYG");
+    modifications.put("CRO", "GYG");
+    modifications.put("CRQ", "QYG");
+    modifications.put("CRU", "EYG");
+    modifications.put("CRW", "ASG");
+    modifications.put("CRX", "ASG");
+    modifications.put("CS0", "CYS");
+    modifications.put("CS1", "CYS");
+    modifications.put("CS3", "CYS");
+    modifications.put("CS4", "CYS");
+    modifications.put("CS8", "ASN");
+    modifications.put("CSA", "CYS");
+    modifications.put("CSB", "CYS");
+    modifications.put("CSD", "CYS");
+    modifications.put("CSE", "CYS");
+    modifications.put("CSF", "CYS");
+    modifications.put("CSH", "SHG");
+    modifications.put("CSI", "GLY");
+    modifications.put("CSJ", "CYS");
+    modifications.put("CSL", "CYS");
+    modifications.put("CSO", "CYS");
+    modifications.put("CSP", "CYS");
+    modifications.put("CSR", "CYS");
+    modifications.put("CSS", "CYS");
+    modifications.put("CSU", "CYS");
+    modifications.put("CSW", "CYS");
+    modifications.put("CSX", "CYS");
+    modifications.put("CSY", "SYG");
+    modifications.put("CSZ", "CYS");
+    modifications.put("CTE", "TRP");
+    modifications.put("CTG", "THR");
+    modifications.put("CTH", "THR");
+    modifications.put("CUC", "XAA");
+    modifications.put("CWR", "SER");
+    modifications.put("CXM", "MET");
+    modifications.put("CY0", "CYS");
+    modifications.put("CY1", "CYS");
+    modifications.put("CY3", "CYS");
+    modifications.put("CY4", "CYS");
+    modifications.put("CYA", "CYS");
+    modifications.put("CYD", "CYS");
+    modifications.put("CYF", "CYS");
+    modifications.put("CYG", "CYS");
+    modifications.put("CYJ", "XAA");
+    modifications.put("CYM", "CYS");
+    modifications.put("CYQ", "CYS");
+    modifications.put("CYR", "CYS");
+    modifications.put("CYS", "CYS");
+    modifications.put("CZ2", "CYS");
+    modifications.put("CZO", "GYG");
+    modifications.put("CZZ", "CYS");
+    modifications.put("D11", "THR");
+    modifications.put("D1P", "ASN");
+    modifications.put("D3 ", "ASN");
+    modifications.put("D33", "ASN");
+    modifications.put("D3P", "GLY");
+    modifications.put("D3T", "THR");
+    modifications.put("D4M", "THR");
+    modifications.put("D4P", "XAA");
+    modifications.put("DA ", "ALA");
+    modifications.put("DA2", "XAA");
+    modifications.put("DAB", "ALA");
+    modifications.put("DAH", "PHE");
+    modifications.put("DAL", "ALA");
+    modifications.put("DAR", "ARG");
+    modifications.put("DAS", "ASP");
+    modifications.put("DBB", "THR");
+    modifications.put("DBM", "ASN");
+    modifications.put("DBS", "SER");
+    modifications.put("DBU", "THR");
+    modifications.put("DBY", "TYR");
+    modifications.put("DBZ", "ALA");
+    modifications.put("DC ", "CYS");
+    modifications.put("DC2", "CYS");
+    modifications.put("DCG", "GLY");
+    modifications.put("DCI", "XAA");
+    modifications.put("DCL", "XAA");
+    modifications.put("DCT", "CYS");
+    modifications.put("DCY", "CYS");
+    modifications.put("DDE", "HIS");
+    modifications.put("DDG", "GLY");
+    modifications.put("DDN", "UR3");
+    modifications.put("DDX", "ASN");
+    modifications.put("DFC", "CYS");
+    modifications.put("DFG", "GLY");
+    modifications.put("DFI", "XAA");
+    modifications.put("DFO", "XAA");
+    modifications.put("DFT", "ASN");
+    modifications.put("DG ", "GLY");
+    modifications.put("DGH", "GLY");
+    modifications.put("DGI", "GLY");
+    modifications.put("DGL", "GLU");
+    modifications.put("DGN", "GLN");
+    modifications.put("DHA", "SER");
+    modifications.put("DHI", "HIS");
+    modifications.put("DHL", "XAA");
+    modifications.put("DHN", "VAL");
+    modifications.put("DHP", "XAA");
+    modifications.put("DHU", "UR3");
+    modifications.put("DHV", "VAL");
+    modifications.put("DI ", "ILE");
+    modifications.put("DIL", "ILE");
+    modifications.put("DIR", "ARG");
+    modifications.put("DIV", "VAL");
+    modifications.put("DLE", "LEU");
+    modifications.put("DLS", "LYS");
+    modifications.put("DLY", "LYS");
+    modifications.put("DM0", "LYS");
+    modifications.put("DMH", "ASN");
+    modifications.put("DMK", "ASP");
+    modifications.put("DMT", "XAA");
+    modifications.put("DN ", "ASN");
+    modifications.put("DNE", "LEU");
+    modifications.put("DNG", "LEU");
+    modifications.put("DNL", "LYS");
+    modifications.put("DNM", "LEU");
+    modifications.put("DNP", "ALA");
+    modifications.put("DNR", "CYS");
+    modifications.put("DNS", "LYS");
+    modifications.put("DOA", "XAA");
+    modifications.put("DOC", "CYS");
+    modifications.put("DOH", "ASP");
+    modifications.put("DON", "LEU");
+    modifications.put("DPB", "THR");
+    modifications.put("DPH", "PHE");
+    modifications.put("DPL", "PRO");
+    modifications.put("DPP", "ALA");
+    modifications.put("DPQ", "TYR");
+    modifications.put("DPR", "PRO");
+    modifications.put("DPY", "ASN");
+    modifications.put("DRM", "UR3");
+    modifications.put("DRP", "ASN");
+    modifications.put("DRT", "THR");
+    modifications.put("DRZ", "ASN");
+    modifications.put("DSE", "SER");
+    modifications.put("DSG", "ASN");
+    modifications.put("DSN", "SER");
+    modifications.put("DSP", "ASP");
+    modifications.put("DT ", "THR");
+    modifications.put("DTH", "THR");
+    modifications.put("DTR", "TRP");
+    modifications.put("DTY", "TYR");
+    modifications.put("DU ", "UR3");
+    modifications.put("DVA", "VAL");
+    modifications.put("DXD", "ASN");
+    modifications.put("DXN", "ASN");
+    modifications.put("DYG", "DYG");
+    modifications.put("DYS", "CYS");
+    modifications.put("DZM", "ALA");
+    modifications.put("E  ", "ALA");
+    modifications.put("E1X", "ALA");
+    modifications.put("ECC", "GLN");
+    modifications.put("EDA", "ALA");
+    modifications.put("EFC", "CYS");
+    modifications.put("EHP", "PHE");
+    modifications.put("EIT", "THR");
+    modifications.put("ENP", "ASN");
+    modifications.put("ESB", "TYR");
+    modifications.put("ESC", "MET");
+    modifications.put("EXB", "XAA");
+    modifications.put("EXY", "LEU");
+    modifications.put("EY5", "ASN");
+    modifications.put("EYS", "XAA");
+    modifications.put("F2F", "PHE");
+    modifications.put("FA2", "ALA");
+    modifications.put("FA5", "ASN");
+    modifications.put("FAG", "ASN");
+    modifications.put("FAI", "ASN");
+    modifications.put("FB5", "ALA");
+    modifications.put("FB6", "ALA");
+    modifications.put("FCL", "PHE");
+    modifications.put("FFD", "ASN");
+    modifications.put("FGA", "GLU");
+    modifications.put("FGL", "GLY");
+    modifications.put("FGP", "SER");
+    modifications.put("FHL", "XAA");
+    modifications.put("FHO", "LYS");
+    modifications.put("FHU", "UR3");
+    modifications.put("FLA", "ALA");
+    modifications.put("FLE", "LEU");
+    modifications.put("FLT", "TYR");
+    modifications.put("FME", "MET");
+    modifications.put("FMG", "GLY");
+    modifications.put("FMU", "ASN");
+    modifications.put("FOE", "CYS");
+    modifications.put("FOX", "GLY");
+    modifications.put("FP9", "PRO");
+    modifications.put("FPA", "PHE");
+    modifications.put("FRD", "XAA");
+    modifications.put("FT6", "TRP");
+    modifications.put("FTR", "TRP");
+    modifications.put("FTY", "TYR");
+    modifications.put("FVA", "VAL");
+    modifications.put("FZN", "LYS");
+    modifications.put("G  ", "GLY");
+    modifications.put("G25", "GLY");
+    modifications.put("G2L", "GLY");
+    modifications.put("G2S", "GLY");
+    modifications.put("G31", "GLY");
+    modifications.put("G32", "GLY");
+    modifications.put("G33", "GLY");
+    modifications.put("G36", "GLY");
+    modifications.put("G38", "GLY");
+    modifications.put("G42", "GLY");
+    modifications.put("G46", "GLY");
+    modifications.put("G47", "GLY");
+    modifications.put("G48", "GLY");
+    modifications.put("G49", "GLY");
+    modifications.put("G4P", "ASN");
+    modifications.put("G7M", "GLY");
+    modifications.put("GAO", "GLY");
+    modifications.put("GAU", "GLU");
+    modifications.put("GCK", "CYS");
+    modifications.put("GCM", "XAA");
+    modifications.put("GDP", "GLY");
+    modifications.put("GDR", "GLY");
+    modifications.put("GFL", "GLY");
+    modifications.put("GGL", "GLU");
+    modifications.put("GH3", "GLY");
+    modifications.put("GHG", "GLN");
+    modifications.put("GHP", "GLY");
+    modifications.put("GL3", "GLY");
+    modifications.put("GLH", "GLN");
+    modifications.put("GLJ", "GLU");
+    modifications.put("GLK", "GLU");
+    modifications.put("GLM", "XAA");
+    modifications.put("GLN", "GLN");
+    modifications.put("GLQ", "GLU");
+    modifications.put("GLU", "GLU");
+    modifications.put("GLX", "GLX");
+    modifications.put("GLY", "GLY");
+    modifications.put("GLZ", "GLY");
+    modifications.put("GMA", "GLU");
+    modifications.put("GMS", "GLY");
+    modifications.put("GMU", "UR3");
+    modifications.put("GN7", "GLY");
+    modifications.put("GND", "XAA");
+    modifications.put("GNE", "ASN");
+    modifications.put("GOM", "GLY");
+    modifications.put("GPL", "LYS");
+    modifications.put("GS ", "GLY");
+    modifications.put("GSC", "GLY");
+    modifications.put("GSR", "GLY");
+    modifications.put("GSS", "GLY");
+    modifications.put("GSU", "GLU");
+    modifications.put("GT9", "CYS");
+    modifications.put("GTP", "GLY");
+    modifications.put("GVL", "XAA");
+    modifications.put("GYC", "CYG");
+    modifications.put("GYS", "SYG");
+    modifications.put("H2U", "UR3");
+    modifications.put("H5M", "PRO");
+    modifications.put("HAC", "ALA");
+    modifications.put("HAR", "ARG");
+    modifications.put("HBN", "HIS");
+    modifications.put("HCS", "XAA");
+    modifications.put("HDP", "UR3");
+    modifications.put("HEU", "UR3");
+    modifications.put("HFA", "XAA");
+    modifications.put("HGL", "XAA");
+    modifications.put("HHI", "HIS");
+    modifications.put("HHK", "AK"); // check
+    modifications.put("HIA", "HIS");
+    modifications.put("HIC", "HIS");
+    modifications.put("HIP", "HIS");
+    modifications.put("HIQ", "HIS");
+    modifications.put("HIS", "HIS");
+    modifications.put("HL2", "LEU");
+    modifications.put("HLU", "LEU");
+    modifications.put("HMR", "ARG");
+    modifications.put("HOL", "ASN");
+    modifications.put("HPC", "PHE");
+    modifications.put("HPE", "PHE");
+    modifications.put("HPH", "PHE");
+    modifications.put("HPQ", "PHE");
+    modifications.put("HQA", "ALA");
+    modifications.put("HRG", "ARG");
+    modifications.put("HRP", "TRP");
+    modifications.put("HS8", "HIS");
+    modifications.put("HS9", "HIS");
+    modifications.put("HSE", "SER");
+    modifications.put("HSL", "SER");
+    modifications.put("HSO", "HIS");
+    modifications.put("HTI", "CYS");
+    modifications.put("HTN", "ASN");
+    modifications.put("HTR", "TRP");
+    modifications.put("HV5", "ALA");
+    modifications.put("HVA", "VAL");
+    modifications.put("HY3", "PRO");
+    modifications.put("HYP", "PRO");
+    modifications.put("HZP", "PRO");
+    modifications.put("I  ", "ILE");
+    modifications.put("I2M", "ILE");
+    modifications.put("I58", "LYS");
+    modifications.put("I5C", "CYS");
+    modifications.put("IAM", "ALA");
+    modifications.put("IAR", "ARG");
+    modifications.put("IAS", "ASP");
+    modifications.put("IC ", "CYS");
+    modifications.put("IEL", "LYS");
+    modifications.put("IEY", "HYG");
+    modifications.put("IG ", "GLY");
+    modifications.put("IGL", "GLY");
+    modifications.put("IGU", "GLY");
+    modifications.put("IIC", "SHG");
+    modifications.put("IIL", "ILE");
+    modifications.put("ILE", "ILE");
+    modifications.put("ILG", "GLU");
+    modifications.put("ILX", "ILE");
+    modifications.put("IMC", "CYS");
+    modifications.put("IML", "ILE");
+    modifications.put("IOY", "PHE");
+    modifications.put("IPG", "GLY");
+    modifications.put("IPN", "ASN");
+    modifications.put("IRN", "ASN");
+    modifications.put("IT1", "LYS");
+    modifications.put("IU ", "UR3");
+    modifications.put("IYR", "TYR");
+    modifications.put("IYT", "THR");
+    modifications.put("IZO", "MET");
+    modifications.put("JJJ", "CYS");
+    modifications.put("JJK", "CYS");
+    modifications.put("JJL", "CYS");
+    modifications.put("JW5", "ASN");
+    modifications.put("K1R", "CYS");
+    modifications.put("KAG", "GLY");
+    modifications.put("KCX", "LYS");
+    modifications.put("KGC", "LYS");
+    modifications.put("KNB", "ALA");
+    modifications.put("KOR", "MET");
+    modifications.put("KPI", "LYS");
+    modifications.put("KST", "LYS");
+    modifications.put("KYQ", "LYS");
+    modifications.put("L2A", "XAA");
+    modifications.put("LA2", "LYS");
+    modifications.put("LAA", "ASP");
+    modifications.put("LAL", "ALA");
+    modifications.put("LBY", "LYS");
+    modifications.put("LC ", "CYS");
+    modifications.put("LCA", "ALA");
+    modifications.put("LCC", "ASN");
+    modifications.put("LCG", "GLY");
+    modifications.put("LCH", "ASN");
+    modifications.put("LCK", "LYS");
+    modifications.put("LCX", "LYS");
+    modifications.put("LDH", "LYS");
+    modifications.put("LED", "LEU");
+    modifications.put("LEF", "LEU");
+    modifications.put("LEH", "LEU");
+    modifications.put("LEI", "VAL");
+    modifications.put("LEM", "LEU");
+    modifications.put("LEN", "LEU");
+    modifications.put("LET", "XAA");
+    modifications.put("LEU", "LEU");
+    modifications.put("LEX", "LEU");
+    modifications.put("LG ", "GLY");
+    modifications.put("LGP", "GLY");
+    modifications.put("LHC", "XAA");
+    modifications.put("LHU", "UR3");
+    modifications.put("LKC", "ASN");
+    modifications.put("LLP", "LYS");
+    modifications.put("LLY", "LYS");
+    modifications.put("LME", "GLU");
+    modifications.put("LMF", "LYS");
+    modifications.put("LMQ", "GLN");
+    modifications.put("LMS", "ASN");
+    modifications.put("LP6", "LYS");
+    modifications.put("LPD", "PRO");
+    modifications.put("LPG", "GLY");
+    modifications.put("LPL", "XAA");
+    modifications.put("LPS", "SER");
+    modifications.put("LSO", "XAA");
+    modifications.put("LTA", "XAA");
+    modifications.put("LTR", "TRP");
+    modifications.put("LVG", "GLY");
+    modifications.put("LVN", "VAL");
+    modifications.put("LYF", "LYS");
+    modifications.put("LYK", "LYS");
+    modifications.put("LYM", "LYS");
+    modifications.put("LYN", "LYS");
+    modifications.put("LYR", "LYS");
+    modifications.put("LYS", "LYS");
+    modifications.put("LYX", "LYS");
+    modifications.put("LYZ", "LYS");
+    modifications.put("M0H", "CYS");
+    modifications.put("M1G", "GLY");
+    modifications.put("M2G", "GLY");
+    modifications.put("M2L", "LYS");
+    modifications.put("M2S", "MET");
+    modifications.put("M30", "GLY");
+    modifications.put("M3L", "LYS");
+    modifications.put("M5M", "CYS");
+    modifications.put("MA ", "ALA");
+    modifications.put("MA6", "ALA");
+    modifications.put("MA7", "ALA");
+    modifications.put("MAA", "ALA");
+    modifications.put("MAD", "ALA");
+    modifications.put("MAI", "ARG");
+    modifications.put("MBQ", "TYR");
+    modifications.put("MBZ", "ASN");
+    modifications.put("MC1", "SER");
+    modifications.put("MCG", "XAA");
+    modifications.put("MCL", "LYS");
+    modifications.put("MCS", "CYS");
+    modifications.put("MCY", "CYS");
+    modifications.put("MD3", "CYS");
+    modifications.put("MD6", "GLY");
+    modifications.put("MDH", "XAA");
+    modifications.put("MDO", "ASG");
+    modifications.put("MDR", "ASN");
+    modifications.put("MEA", "PHE");
+    modifications.put("MED", "MET");
+    modifications.put("MEG", "GLU");
+    modifications.put("MEN", "ASN");
+    modifications.put("MEP", "UR3");
+    modifications.put("MEQ", "GLN");
+    modifications.put("MET", "MET");
+    modifications.put("MEU", "GLY");
+    modifications.put("MF3", "XAA");
+    modifications.put("MFC", "GYG");
+    modifications.put("MG1", "GLY");
+    modifications.put("MGG", "ARG");
+    modifications.put("MGN", "GLN");
+    modifications.put("MGQ", "ALA");
+    modifications.put("MGV", "GLY");
+    modifications.put("MGY", "GLY");
+    modifications.put("MHL", "LEU");
+    modifications.put("MHO", "MET");
+    modifications.put("MHS", "HIS");
+    modifications.put("MIA", "ALA");
+    modifications.put("MIS", "SER");
+    modifications.put("MK8", "LEU");
+    modifications.put("ML3", "LYS");
+    modifications.put("MLE", "LEU");
+    modifications.put("MLL", "LEU");
+    modifications.put("MLY", "LYS");
+    modifications.put("MLZ", "LYS");
+    modifications.put("MME", "MET");
+    modifications.put("MMO", "ARG");
+    modifications.put("MMT", "THR");
+    modifications.put("MND", "ASN");
+    modifications.put("MNL", "LEU");
+    modifications.put("MNU", "UR3");
+    modifications.put("MNV", "VAL");
+    modifications.put("MOD", "XAA");
+    modifications.put("MP8", "PRO");
+    modifications.put("MPH", "XAA");
+    modifications.put("MPJ", "XAA");
+    modifications.put("MPQ", "GLY");
+    modifications.put("MRG", "GLY");
+    modifications.put("MSA", "GLY");
+    modifications.put("MSE", "MET");
+    modifications.put("MSL", "MET");
+    modifications.put("MSO", "MET");
+    modifications.put("MSP", "XAA");
+    modifications.put("MT2", "MET");
+    modifications.put("MTR", "THR");
+    modifications.put("MTU", "ALA");
+    modifications.put("MTY", "TYR");
+    modifications.put("MVA", "VAL");
+    modifications.put("N  ", "ASN");
+    modifications.put("N10", "SER");
+    modifications.put("N2C", "XAA");
+    modifications.put("N5I", "ASN");
+    modifications.put("N5M", "CYS");
+    modifications.put("N6G", "GLY");
+    modifications.put("N7P", "PRO");
+    modifications.put("NA8", "ALA");
+    modifications.put("NAL", "ALA");
+    modifications.put("NAM", "ALA");
+    modifications.put("NB8", "ASN");
+    modifications.put("NBQ", "TYR");
+    modifications.put("NC1", "SER");
+    modifications.put("NCB", "ALA");
+    modifications.put("NCX", "ASN");
+    modifications.put("NCY", "XAA");
+    modifications.put("NDF", "PHE");
+    modifications.put("NDN", "UR3");
+    modifications.put("NEM", "HIS");
+    modifications.put("NEP", "HIS");
+    modifications.put("NF2", "ASN");
+    modifications.put("NFA", "PHE");
+    modifications.put("NHL", "GLU");
+    modifications.put("NIT", "XAA");
+    modifications.put("NIY", "TYR");
+    modifications.put("NLE", "LEU");
+    modifications.put("NLN", "LEU");
+    modifications.put("NLO", "LEU");
+    modifications.put("NLP", "LEU");
+    modifications.put("NLQ", "GLN");
+    modifications.put("NMC", "GLY");
+    modifications.put("NMM", "ARG");
+    modifications.put("NMS", "THR");
+    modifications.put("NMT", "THR");
+    modifications.put("NNH", "ARG");
+    modifications.put("NP3", "ASN");
+    modifications.put("NPH", "CYS");
+    modifications.put("NPI", "ALA");
+    modifications.put("NRP", "LYG");
+    modifications.put("NRQ", "MYG");
+    modifications.put("NSK", "XAA");
+    modifications.put("NTY", "TYR");
+    modifications.put("NVA", "VAL");
+    modifications.put("NYC", "TWG");
+    modifications.put("NYG", "NYG");
+    modifications.put("NYM", "ASN");
+    modifications.put("NYS", "CYS");
+    modifications.put("NZH", "HIS");
+    modifications.put("O12", "XAA");
+    modifications.put("O2C", "ASN");
+    modifications.put("O2G", "GLY");
+    modifications.put("OAD", "ASN");
+    modifications.put("OAS", "SER");
+    modifications.put("OBF", "XAA");
+    modifications.put("OBS", "XAA");
+    modifications.put("OCS", "CYS");
+    modifications.put("OCY", "CYS");
+    modifications.put("ODP", "ASN");
+    modifications.put("OHI", "HIS");
+    modifications.put("OHS", "ASP");
+    modifications.put("OIC", "XAA");
+    modifications.put("OIP", "ILE");
+    modifications.put("OLE", "XAA");
+    modifications.put("OLT", "THR");
+    modifications.put("OLZ", "SER");
+    modifications.put("OMC", "CYS");
+    modifications.put("OMG", "GLY");
+    modifications.put("OMT", "MET");
+    modifications.put("OMU", "UR3");
+    modifications.put("ONE", "UR3");
+    modifications.put("ONH", "ALA");
+    modifications.put("ONL", "XAA");
+    modifications.put("OPR", "ARG");
+    modifications.put("ORN", "ALA");
+    modifications.put("ORQ", "ARG");
+    modifications.put("OSE", "SER");
+    modifications.put("OTB", "XAA");
+    modifications.put("OTH", "THR");
+    modifications.put("OTY", "TYR");
+    modifications.put("OXX", "ASP");
+    modifications.put("P  ", "GLY");
+    modifications.put("P1L", "CYS");
+    modifications.put("P1P", "ASN");
+    modifications.put("P2T", "THR");
+    modifications.put("P2U", "UR3");
+    modifications.put("P2Y", "PRO");
+    modifications.put("P5P", "ALA");
+    modifications.put("PAQ", "TYR");
+    modifications.put("PAS", "ASP");
+    modifications.put("PAT", "TRP");
+    modifications.put("PAU", "ALA");
+    modifications.put("PBB", "CYS");
+    modifications.put("PBF", "PHE");
+    modifications.put("PBT", "ASN");
+    modifications.put("PCA", "GLU");
+    modifications.put("PCC", "PRO");
+    modifications.put("PCE", "XAA");
+    modifications.put("PCS", "PHE");
+    modifications.put("PDL", "XAA");
+    modifications.put("PDU", "UR3");
+    modifications.put("PEC", "CYS");
+    modifications.put("PF5", "PHE");
+    modifications.put("PFF", "PHE");
+    modifications.put("PFX", "XAA");
+    modifications.put("PG1", "SER");
+    modifications.put("PG7", "GLY");
+    modifications.put("PG9", "GLY");
+    modifications.put("PGL", "XAA");
+    modifications.put("PGN", "GLY");
+    modifications.put("PGP", "GLY");
+    modifications.put("PGY", "GLY");
+    modifications.put("PHA", "PHE");
+    modifications.put("PHD", "ASP");
+    modifications.put("PHE", "PHE");
+    modifications.put("PHI", "PHE");
+    modifications.put("PHL", "PHE");
+    modifications.put("PHM", "PHE");
+    modifications.put("PIA", "AYG");
+    modifications.put("PIV", "XAA");
+    modifications.put("PLE", "LEU");
+    modifications.put("PM3", "PHE");
+    modifications.put("PMT", "CYS");
+    modifications.put("POM", "PRO");
+    modifications.put("PPN", "PHE");
+    modifications.put("PPU", "ALA");
+    modifications.put("PPW", "GLY");
+    modifications.put("PQ1", "ASN");
+    modifications.put("PR3", "CYS");
+    modifications.put("PR5", "ALA");
+    modifications.put("PR9", "PRO");
+    modifications.put("PRN", "ALA");
+    modifications.put("PRO", "PRO");
+    modifications.put("PRS", "PRO");
+    modifications.put("PSA", "PHE");
+    modifications.put("PSH", "HIS");
+    modifications.put("PST", "THR");
+    modifications.put("PSU", "UR3");
+    modifications.put("PSW", "CYS");
+    modifications.put("PTA", "XAA");
+    modifications.put("PTH", "TYR");
+    modifications.put("PTM", "TYR");
+    modifications.put("PTR", "TYR");
+    modifications.put("PU ", "ALA");
+    modifications.put("PUY", "ASN");
+    modifications.put("PVH", "HIS");
+    modifications.put("PVL", "XAA");
+    modifications.put("PYA", "ALA");
+    modifications.put("PYO", "UR3");
+    modifications.put("PYX", "CYS");
+    modifications.put("PYY", "ASN");
+    modifications.put("QLG", "QLG");
+    modifications.put("QMM", "GLN");
+    modifications.put("QPA", "CYS");
+    modifications.put("QPH", "PHE");
+    modifications.put("QUO", "GLY");
+    modifications.put("R  ", "ALA");
+    modifications.put("R1A", "CYS");
+    modifications.put("R4K", "TRP");
+    modifications.put("RC7", "HYG");
+    modifications.put("RE0", "TRP");
+    modifications.put("RE3", "TRP");
+    modifications.put("RIA", "ALA");
+    modifications.put("RMP", "ALA");
+    modifications.put("RON", "XAA");
+    modifications.put("RT ", "THR");
+    modifications.put("RTP", "ASN");
+    modifications.put("S1H", "SER");
+    modifications.put("S2C", "CYS");
+    modifications.put("S2D", "ALA");
+    modifications.put("S2M", "THR");
+    modifications.put("S2P", "ALA");
+    modifications.put("S4A", "ALA");
+    modifications.put("S4C", "CYS");
+    modifications.put("S4G", "GLY");
+    modifications.put("S4U", "UR3");
+    modifications.put("S6G", "GLY");
+    modifications.put("SAC", "SER");
+    modifications.put("SAH", "CYS");
+    modifications.put("SAR", "GLY");
+    modifications.put("SBL", "SER");
+    modifications.put("SC ", "CYS");
+    modifications.put("SCH", "CYS");
+    modifications.put("SCS", "CYS");
+    modifications.put("SCY", "CYS");
+    modifications.put("SD2", "XAA");
+    modifications.put("SDG", "GLY");
+    modifications.put("SDP", "SER");
+    modifications.put("SEB", "SER");
+    modifications.put("SEC", "ALA");
+    modifications.put("SEG", "ALA");
+    modifications.put("SEL", "SER");
+    modifications.put("SEM", "SER");
+    modifications.put("SEN", "SER");
+    modifications.put("SEP", "SER");
+    modifications.put("SER", "SER");
+    modifications.put("SET", "SER");
+    modifications.put("SGB", "SER");
+    modifications.put("SHC", "CYS");
+    modifications.put("SHP", "GLY");
+    modifications.put("SHR", "LYS");
+    modifications.put("SIB", "CYS");
+    modifications.put("SIC", "DC"); // check
+    modifications.put("SLA", "PRO");
+    modifications.put("SLR", "PRO");
+    modifications.put("SLZ", "LYS");
+    modifications.put("SMC", "CYS");
+    modifications.put("SME", "MET");
+    modifications.put("SMF", "PHE");
+    modifications.put("SMP", "ALA");
+    modifications.put("SMT", "THR");
+    modifications.put("SNC", "CYS");
+    modifications.put("SNN", "ASN");
+    modifications.put("SOC", "CYS");
+    modifications.put("SOS", "ASN");
+    modifications.put("SOY", "SER");
+    modifications.put("SPT", "THR");
+    modifications.put("SRA", "ALA");
+    modifications.put("SSU", "UR3");
+    modifications.put("STY", "TYR");
+    modifications.put("SUB", "XAA");
+    modifications.put("SUI", "DG");
+    modifications.put("SUN", "SER");
+    modifications.put("SUR", "UR3");
+    modifications.put("SVA", "SER");
+    modifications.put("SVV", "SER");
+    modifications.put("SVW", "SER");
+    modifications.put("SVX", "SER");
+    modifications.put("SVY", "SER");
+    modifications.put("SVZ", "XAA");
+    modifications.put("SWG", "SWG");
+    modifications.put("SYS", "CYS");
+    modifications.put("T  ", "THR");
+    modifications.put("T11", "PHE");
+    modifications.put("T23", "THR");
+    modifications.put("T2S", "THR");
+    modifications.put("T2T", "ASN");
+    modifications.put("T31", "UR3");
+    modifications.put("T32", "THR");
+    modifications.put("T36", "THR");
+    modifications.put("T37", "THR");
+    modifications.put("T38", "THR");
+    modifications.put("T39", "THR");
+    modifications.put("T3P", "THR");
+    modifications.put("T41", "THR");
+    modifications.put("T48", "THR");
+    modifications.put("T49", "THR");
+    modifications.put("T4S", "THR");
+    modifications.put("T5O", "UR3");
+    modifications.put("T5S", "THR");
+    modifications.put("T66", "XAA");
+    modifications.put("T6A", "ALA");
+    modifications.put("TA3", "THR");
+    modifications.put("TA4", "XAA");
+    modifications.put("TAF", "THR");
+    modifications.put("TAL", "ASN");
+    modifications.put("TAV", "ASP");
+    modifications.put("TBG", "VAL");
+    modifications.put("TBM", "THR");
+    modifications.put("TC1", "CYS");
+    modifications.put("TCP", "THR");
+    modifications.put("TCQ", "TYR");
+    modifications.put("TCR", "TRP");
+    modifications.put("TCY", "ALA");
+    modifications.put("TDD", "LEU");
+    modifications.put("TDY", "THR");
+    modifications.put("TFE", "THR");
+    modifications.put("TFO", "ALA");
+    modifications.put("TFQ", "PHE");
+    modifications.put("TFT", "THR");
+    modifications.put("TGP", "GLY");
+    modifications.put("TH6", "THR");
+    modifications.put("THC", "THR");
+    modifications.put("THO", "XAA");
+    modifications.put("THR", "THR");
+    modifications.put("THX", "ASN");
+    modifications.put("THZ", "ARG");
+    modifications.put("TIH", "ALA");
+    modifications.put("TLB", "ASN");
+    modifications.put("TLC", "THR");
+    modifications.put("TLN", "UR3");
+    modifications.put("TMB", "THR");
+    modifications.put("TMD", "THR");
+    modifications.put("TNB", "CYS");
+    modifications.put("TNR", "SER");
+    modifications.put("TOX", "TRP");
+    modifications.put("TP1", "THR");
+    modifications.put("TPC", "CYS");
+    modifications.put("TPG", "GLY");
+    modifications.put("TPH", "XAA");
+    modifications.put("TPL", "TRP");
+    modifications.put("TPO", "THR");
+    modifications.put("TPQ", "TYR");
+    modifications.put("TQI", "TRP");
+    modifications.put("TQQ", "TRP");
+    modifications.put("TRF", "TRP");
+    modifications.put("TRG", "LYS");
+    modifications.put("TRN", "TRP");
+    modifications.put("TRO", "TRP");
+    modifications.put("TRP", "TRP");
+    modifications.put("TRQ", "TRP");
+    modifications.put("TRW", "TRP");
+    modifications.put("TRX", "TRP");
+    modifications.put("TS ", "ASN");
+    modifications.put("TST", "XAA");
+    modifications.put("TT ", "ASN");
+    modifications.put("TTD", "THR");
+    modifications.put("TTI", "UR3");
+    modifications.put("TTM", "THR");
+    modifications.put("TTQ", "TRP");
+    modifications.put("TTS", "TYR");
+    modifications.put("TY1", "TYR");
+    modifications.put("TY2", "TYR");
+    modifications.put("TY3", "TYR");
+    modifications.put("TY5", "TYR");
+    modifications.put("TYB", "TYR");
+    modifications.put("TYI", "TYR");
+    modifications.put("TYJ", "TYR");
+    modifications.put("TYN", "TYR");
+    modifications.put("TYO", "TYR");
+    modifications.put("TYQ", "TYR");
+    modifications.put("TYR", "TYR");
+    modifications.put("TYS", "TYR");
+    modifications.put("TYT", "TYR");
+    modifications.put("TYU", "ASN");
+    modifications.put("TYW", "TYR");
+    modifications.put("TYX", "XAA");
+    modifications.put("TYY", "TYR");
+    modifications.put("TZB", "XAA");
+    modifications.put("TZO", "XAA");
+    modifications.put("U  ", "UR3");
+    modifications.put("U25", "UR3");
+    modifications.put("U2L", "UR3");
+    modifications.put("U2N", "UR3");
+    modifications.put("U2P", "UR3");
+    modifications.put("U31", "UR3");
+    modifications.put("U33", "UR3");
+    modifications.put("U34", "UR3");
+    modifications.put("U36", "UR3");
+    modifications.put("U37", "UR3");
+    modifications.put("U8U", "UR3");
+    modifications.put("UAR", "UR3");
+    modifications.put("UCL", "UR3");
+    modifications.put("UD5", "UR3");
+    modifications.put("UDP", "ASN");
+    modifications.put("UFP", "ASN");
+    modifications.put("UFR", "UR3");
+    modifications.put("UFT", "UR3");
+    modifications.put("UMA", "ALA");
+    modifications.put("UMP", "UR3");
+    modifications.put("UMS", "UR3");
+    modifications.put("UN1", "XAA");
+    modifications.put("UN2", "XAA");
+    modifications.put("UNK", "XAA");
+    modifications.put("UR3", "UR3");
+    modifications.put("URD", "UR3");
+    modifications.put("US1", "UR3");
+    modifications.put("US2", "UR3");
+    modifications.put("US3", "THR");
+    modifications.put("US5", "UR3");
+    modifications.put("USM", "UR3");
+    modifications.put("VAD", "VAL");
+    modifications.put("VAF", "VAL");
+    modifications.put("VAL", "VAL");
+    modifications.put("VB1", "LYS");
+    modifications.put("VDL", "XAA");
+    modifications.put("VLL", "XAA");
+    modifications.put("VLM", "XAA");
+    modifications.put("VMS", "XAA");
+    modifications.put("VOL", "XAA");
+    modifications.put("WCR", "GYG");
+    modifications.put("X  ", "GLY");
+    modifications.put("X2W", "GLU");
+    modifications.put("X4A", "ASN");
+    modifications.put("X9Q", "AFG");
+    modifications.put("XAD", "ALA");
+    modifications.put("XAE", "ASN");
+    modifications.put("XAL", "ALA");
+    modifications.put("XAR", "ASN");
+    modifications.put("XCL", "CYS");
+    modifications.put("XCN", "CYS");
+    modifications.put("XCP", "XAA");
+    modifications.put("XCR", "CYS");
+    modifications.put("XCS", "ASN");
+    modifications.put("XCT", "CYS");
+    modifications.put("XCY", "CYS");
+    modifications.put("XGA", "ASN");
+    modifications.put("XGL", "GLY");
+    modifications.put("XGR", "GLY");
+    modifications.put("XGU", "GLY");
+    modifications.put("XPR", "PRO");
+    modifications.put("XSN", "ASN");
+    modifications.put("XTH", "THR");
+    modifications.put("XTL", "THR");
+    modifications.put("XTR", "THR");
+    modifications.put("XTS", "GLY");
+    modifications.put("XTY", "ASN");
+    modifications.put("XUA", "ALA");
+    modifications.put("XUG", "GLY");
+    modifications.put("XX1", "LYS");
+    modifications.put("XXY", "THG");
+    modifications.put("XYG", "DYG");
+    modifications.put("Y  ", "ALA");
+    modifications.put("YCM", "CYS");
+    modifications.put("YG ", "GLY");
+    modifications.put("YOF", "TYR");
+    modifications.put("YRR", "ASN");
+    modifications.put("YYG", "GLY");
+    modifications.put("Z  ", "CYS");
+    modifications.put("Z01", "ALA");
+    modifications.put("ZAD", "ALA");
+    modifications.put("ZAL", "ALA");
+    modifications.put("ZBC", "CYS");
+    modifications.put("ZBU", "UR3");
+    modifications.put("ZCL", "PHE");
+    modifications.put("ZCY", "CYS");
+    modifications.put("ZDU", "UR3");
+    modifications.put("ZFB", "XAA");
+    modifications.put("ZGU", "GLY");
+    modifications.put("ZHP", "ASN");
+    modifications.put("ZTH", "THR");
+    modifications.put("ZU0", "THR");
+    modifications.put("ZZJ", "ALA");
 
-  /**
-   * translate to RNA secondary structure representation
-   * 
-   * @param ssstring
-   * @return ssstring as a RNA-state secondary structure assignment.
-   */
-  public static String getRNASecStrucState(String ssstring)
-  {
-    if (ssstring == null)
-    {
-      return null;
-    }
-    StringBuffer ss = new StringBuffer();
-    for (int i = 0; i < ssstring.length(); i++)
-    {
-      String ssc = ssstring.substring(i, i + 1);
-      if (toRNAssState.containsKey(ssc))
-      {
-        // valid ss character - so return it
-        ss.append(ssc); // (String) toRNAssState.get(ssc));
-      }
-      else
-      {
-        ss.append(" ");
-      }
-    }
-    return ss.toString();
   }
 
-  public static boolean isCloseParenRNA(char dc)
+  public static String getCanonicalAminoAcid(String aA)
   {
-    return RNAcloseParen[dc];
+    String canonical = modifications.get(aA);
+    return canonical == null ? aA : canonical;
   }
 
   // main method generates perl representation of residue property hash
   // / cut here
   public static void main(String[] args)
   {
-    Hashtable aa = new Hashtable();
+    Hashtable<String, Vector<String>> aaProps = new Hashtable<String, Vector<String>>();
     System.out.println("my %aa = {");
     // invert property hashes
-    Enumeration prop = propHash.keys();
-    while (prop.hasMoreElements())
+    for (String pname : propHash.keySet())
     {
-      String pname = (String) prop.nextElement();
-      Hashtable phash = (Hashtable) propHash.get(pname);
-      Enumeration res = phash.keys();
-      while (res.hasMoreElements())
+      Map<String, Integer> phash = propHash.get(pname);
+      for (String rname : phash.keySet())
       {
-        String rname = (String) res.nextElement();
-        Vector aprops = (Vector) aa.get(rname);
+        Vector<String> aprops = aaProps.get(rname);
         if (aprops == null)
         {
-          aprops = new Vector();
-          aa.put(rname, aprops);
+          aprops = new Vector<String>();
+          aaProps.put(rname, aprops);
         }
-        Integer hasprop = (Integer) phash.get(rname);
+        Integer hasprop = phash.get(rname);
         if (hasprop.intValue() == 1)
         {
           aprops.addElement(pname);
         }
       }
     }
-    Enumeration res = aa.keys();
+    Enumeration<String> res = aaProps.keys();
     while (res.hasMoreElements())
     {
-      String rname = (String) res.nextElement();
+      String rname = res.nextElement();
 
       System.out.print("'" + rname + "' => [");
-      Enumeration props = ((Vector) aa.get(rname)).elements();
+      Enumeration<String> props = aaProps.get(rname).elements();
       while (props.hasMoreElements())
       {
-        System.out.print("'" + (String) props.nextElement() + "'");
+        System.out.print("'" + props.nextElement() + "'");
         if (props.hasMoreElements())
         {
           System.out.println(", ");
@@ -1791,15 +2793,15 @@ public class ResidueProperties
   /**
    * Returns a list of residue characters for the specified inputs
    * 
-   * @param nucleotide
+   * @param forNucleotide
    * @param includeAmbiguous
    * @return
    */
-  public static List<String> getResidues(boolean nucleotide,
+  public static List<String> getResidues(boolean forNucleotide,
           boolean includeAmbiguous)
   {
     List<String> result = new ArrayList<String>();
-    if (nucleotide)
+    if (forNucleotide)
     {
       for (String nuc : nucleotideName.keySet())
       {
diff --git a/src/jalview/schemes/ScoreColourScheme.java b/src/jalview/schemes/ScoreColourScheme.java
index fba1188..058d831 100644
--- a/src/jalview/schemes/ScoreColourScheme.java
+++ b/src/jalview/schemes/ScoreColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/ScoreMatrix.java b/src/jalview/schemes/ScoreMatrix.java
index e2aa232..b7541f9 100644
--- a/src/jalview/schemes/ScoreMatrix.java
+++ b/src/jalview/schemes/ScoreMatrix.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/StrandColourScheme.java b/src/jalview/schemes/StrandColourScheme.java
index 6fe4013..88112bc 100644
--- a/src/jalview/schemes/StrandColourScheme.java
+++ b/src/jalview/schemes/StrandColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/TCoffeeColourScheme.java b/src/jalview/schemes/TCoffeeColourScheme.java
index bc56281..e42cdad 100644
--- a/src/jalview/schemes/TCoffeeColourScheme.java
+++ b/src/jalview/schemes/TCoffeeColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -86,6 +86,10 @@ public class TCoffeeColourScheme extends ResidueColourScheme
     seqMap = new IdentityHashMap<SequenceI, Color[]>();
     AnnotatedCollectionI alcontext = alignment instanceof AlignmentI ? alignment
             : alignment.getContext();
+    if (alcontext == null)
+    {
+      return;
+    }
     int w = 0;
     for (AlignmentAnnotation al : alcontext
             .findAnnotation(TCoffeeScoreFile.TCOFFEE_SCORE))
diff --git a/src/jalview/schemes/TaylorColourScheme.java b/src/jalview/schemes/TaylorColourScheme.java
index da7d265..bac555b 100644
--- a/src/jalview/schemes/TaylorColourScheme.java
+++ b/src/jalview/schemes/TaylorColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/TurnColourScheme.java b/src/jalview/schemes/TurnColourScheme.java
index c0a4e3c..5c5c0f5 100644
--- a/src/jalview/schemes/TurnColourScheme.java
+++ b/src/jalview/schemes/TurnColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/schemes/UserColourScheme.java b/src/jalview/schemes/UserColourScheme.java
index 89f3eed..88c89be 100644
--- a/src/jalview/schemes/UserColourScheme.java
+++ b/src/jalview/schemes/UserColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -67,7 +67,7 @@ public class UserColourScheme extends ResidueColourScheme
 
     if (col == null)
     {
-      System.out.println("Unknown colour!! " + colour);
+      System.out.println("Making colour from name: " + colour);
       col = createColourFromName(colour);
     }
 
@@ -99,8 +99,12 @@ public class UserColourScheme extends ResidueColourScheme
     return schemeName;
   }
 
-  public Color getColourFromString(String colour)
+  public static Color getColourFromString(String colour)
   {
+    if (colour == null)
+    {
+      return null;
+    }
     colour = colour.trim();
 
     Color col = null;
@@ -136,7 +140,7 @@ public class UserColourScheme extends ResidueColourScheme
 
   }
 
-  public Color createColourFromName(String name)
+  public static Color createColourFromName(String name)
   {
     int r, g, b;
 
diff --git a/src/jalview/schemes/ZappoColourScheme.java b/src/jalview/schemes/ZappoColourScheme.java
index a313767..3603b63 100644
--- a/src/jalview/schemes/ZappoColourScheme.java
+++ b/src/jalview/schemes/ZappoColourScheme.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/structure/AlignmentViewPanelListener.java b/src/jalview/structure/AlignmentViewPanelListener.java
index 1c4babd..6958d59 100644
--- a/src/jalview/structure/AlignmentViewPanelListener.java
+++ b/src/jalview/structure/AlignmentViewPanelListener.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/structure/AtomSpec.java b/src/jalview/structure/AtomSpec.java
index 6dbc557..3b78d71 100644
--- a/src/jalview/structure/AtomSpec.java
+++ b/src/jalview/structure/AtomSpec.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/structure/CommandListener.java b/src/jalview/structure/CommandListener.java
index a8df3c8..5dbbcb5 100644
--- a/src/jalview/structure/CommandListener.java
+++ b/src/jalview/structure/CommandListener.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/structure/SecondaryStructureListener.java b/src/jalview/structure/SecondaryStructureListener.java
index c8be34c..e8f6f94 100644
--- a/src/jalview/structure/SecondaryStructureListener.java
+++ b/src/jalview/structure/SecondaryStructureListener.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/structure/SelectionListener.java b/src/jalview/structure/SelectionListener.java
index 582cfd8..b5c0961 100644
--- a/src/jalview/structure/SelectionListener.java
+++ b/src/jalview/structure/SelectionListener.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/structure/SelectionSource.java b/src/jalview/structure/SelectionSource.java
index b1bf009..e18fd4b 100644
--- a/src/jalview/structure/SelectionSource.java
+++ b/src/jalview/structure/SelectionSource.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/structure/SequenceListener.java b/src/jalview/structure/SequenceListener.java
index c44aa0b..ea296d3 100644
--- a/src/jalview/structure/SequenceListener.java
+++ b/src/jalview/structure/SequenceListener.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,7 @@
  */
 package jalview.structure;
 
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceI;
 
 public interface SequenceListener
@@ -27,7 +28,7 @@ public interface SequenceListener
   // TODO remove this? never called on SequenceListener type
   public void mouseOverSequence(SequenceI sequence, int index, int pos);
 
-  public void highlightSequence(jalview.datamodel.SearchResults results);
+  public void highlightSequence(SearchResultsI results);
 
   // TODO remove this? never called
   public void updateColours(SequenceI sequence, int index);
diff --git a/src/jalview/structure/StructureImportSettings.java b/src/jalview/structure/StructureImportSettings.java
new file mode 100644
index 0000000..a53bf9d
--- /dev/null
+++ b/src/jalview/structure/StructureImportSettings.java
@@ -0,0 +1,153 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.structure;
+
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.PDBEntry.Type;
+
+/**
+ * bean holding settings for structure IO. TODO: tests for validation of values
+ * TODO: tests for race conditions (all fields are static, is that correct ?)
+ * 
+ * @author tcofoegbu
+ *
+ */
+public class StructureImportSettings
+{
+  /**
+   * set to true to add derived sequence annotations (temp factor read from
+   * file, or computed secondary structure) to the alignment
+   */
+  private static boolean visibleChainAnnotation = false;
+
+  /**
+   * Set true to predict secondary structure (using JMol for protein, Annotate3D
+   * for RNA)
+   */
+  private static boolean processSecStr = false;
+
+  /**
+   * Set true (with predictSecondaryStructure=true) to predict secondary
+   * structure using an external service (currently Annotate3D for RNA only)
+   */
+  private static boolean externalSecondaryStructure = false;
+
+  private static boolean showSeqFeatures = true;
+
+  public enum StructureParser
+  {
+    JMOL_PARSER, JALVIEW_PARSER
+  }
+
+  /**
+   * Determines the default file format for structure files to be downloaded
+   * from the PDB sequence fetcher. Possible options include: PDB|mmCIF
+   */
+  private static PDBEntry.Type defaultStructureFileFormat = Type.PDB;
+
+  /**
+   * Determines the parser used for parsing PDB format file. Possible options
+   * are : JMolParser|JalveiwParser
+   */
+  private static StructureParser defaultPDBFileParser = StructureParser.JMOL_PARSER;
+
+  public static void addSettings(boolean addAlignmentAnnotations,
+          boolean processSecStr, boolean externalSecStr)
+  {
+    StructureImportSettings.visibleChainAnnotation = addAlignmentAnnotations;
+    StructureImportSettings.processSecStr = processSecStr;
+    StructureImportSettings.externalSecondaryStructure = externalSecStr;
+    StructureImportSettings.showSeqFeatures = true;
+  }
+
+  public static boolean isVisibleChainAnnotation()
+  {
+    return visibleChainAnnotation;
+  }
+
+  public static void setVisibleChainAnnotation(
+          boolean visibleChainAnnotation)
+  {
+    StructureImportSettings.visibleChainAnnotation = visibleChainAnnotation;
+  }
+
+  public static boolean isProcessSecondaryStructure()
+  {
+    return processSecStr;
+  }
+
+  public static void setProcessSecondaryStructure(
+          boolean processSecondaryStructure)
+  {
+    StructureImportSettings.processSecStr = processSecondaryStructure;
+  }
+
+  public static boolean isExternalSecondaryStructure()
+  {
+    return externalSecondaryStructure;
+  }
+
+  public static void setExternalSecondaryStructure(
+          boolean externalSecondaryStructure)
+  {
+    StructureImportSettings.externalSecondaryStructure = externalSecondaryStructure;
+  }
+
+  public static boolean isShowSeqFeatures()
+  {
+    return showSeqFeatures;
+  }
+
+  public static void setShowSeqFeatures(boolean showSeqFeatures)
+  {
+    StructureImportSettings.showSeqFeatures = showSeqFeatures;
+  }
+
+  public static String getDefaultStructureFileFormat()
+  {
+    return defaultStructureFileFormat.toString();
+  }
+
+  public static void setDefaultStructureFileFormat(
+          String defaultStructureFileFormat)
+  {
+    StructureImportSettings.defaultStructureFileFormat = PDBEntry.Type
+            .valueOf(defaultStructureFileFormat.toUpperCase());
+  }
+
+  public static String getDefaultPDBFileParser()
+  {
+    return defaultPDBFileParser.toString();
+  }
+
+  public static void setDefaultPDBFileParser(
+          StructureParser defaultPDBFileParser)
+  {
+    StructureImportSettings.defaultPDBFileParser = defaultPDBFileParser;
+  }
+
+  public static void setDefaultPDBFileParser(String defaultPDBFileParser)
+  {
+    StructureImportSettings.defaultPDBFileParser = StructureParser
+            .valueOf(defaultPDBFileParser.toUpperCase());
+  }
+
+}
diff --git a/src/jalview/structure/StructureListener.java b/src/jalview/structure/StructureListener.java
index 755fd0c..ac62f6b 100644
--- a/src/jalview/structure/StructureListener.java
+++ b/src/jalview/structure/StructureListener.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/structure/StructureMapping.java b/src/jalview/structure/StructureMapping.java
index 147cc07..e0fbfd5 100644
--- a/src/jalview/structure/StructureMapping.java
+++ b/src/jalview/structure/StructureMapping.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -23,6 +23,8 @@ package jalview.structure;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.SequenceI;
 
+import java.util.HashMap;
+
 public class StructureMapping
 {
   String mappingDetails;
@@ -35,11 +37,19 @@ public class StructureMapping
 
   String pdbchain;
 
-  // Mapping index 0 is resNum, index 1 is atomNo
-  int[][] mapping;
+  public static final int UNASSIGNED_VALUE = -1;
+
+  private static final int PDB_RES_NUM_INDEX = 0;
+
+  private static final int PDB_ATOM_NUM_INDEX = 1;
+
+  // Mapping key is residue index while value is an array containing PDB resNum,
+  // and atomNo
+  HashMap<Integer, int[]> mapping;
 
   public StructureMapping(SequenceI seq, String pdbfile, String pdbid,
-          String chain, int[][] mapping, String mappingDetails)
+          String chain, HashMap<Integer, int[]> mapping,
+          String mappingDetails)
   {
     sequence = seq;
     this.pdbfile = pdbfile;
@@ -71,13 +81,14 @@ public class StructureMapping
    */
   public int getAtomNum(int seqpos)
   {
-    if (mapping.length > seqpos)
+    int[] resNumAtomMap = mapping.get(seqpos);
+    if (resNumAtomMap != null)
     {
-      return mapping[seqpos][1];
+      return resNumAtomMap[PDB_ATOM_NUM_INDEX];
     }
     else
     {
-      return 0;
+      return UNASSIGNED_VALUE;
     }
   }
 
@@ -88,13 +99,14 @@ public class StructureMapping
    */
   public int getPDBResNum(int seqpos)
   {
-    if (mapping.length > seqpos)
+    int[] resNumAtomMap = mapping.get(seqpos);
+    if (resNumAtomMap != null)
     {
-      return mapping[seqpos][0];
+      return resNumAtomMap[PDB_RES_NUM_INDEX];
     }
     else
     {
-      return 0;
+      return UNASSIGNED_VALUE;
     }
   }
 
@@ -105,14 +117,14 @@ public class StructureMapping
    */
   public int getSeqPos(int pdbResNum)
   {
-    for (int i = 0; i < mapping.length; i++)
+    for (Integer seqPos : mapping.keySet())
     {
-      if (mapping[i][0] == pdbResNum)
+      if (pdbResNum == getPDBResNum(seqPos))
       {
-        return i;
+        return seqPos;
       }
     }
-    return -1;
+    return UNASSIGNED_VALUE;
   }
 
   /**
@@ -147,4 +159,14 @@ public class StructureMapping
     }
     return ala_copy;
   }
+
+  public String getMappingDetailsOutput()
+  {
+    return mappingDetails;
+  }
+
+  public HashMap<Integer, int[]> getMapping()
+  {
+    return mapping;
+  }
 }
diff --git a/src/jalview/structure/StructureMappingcommandSet.java b/src/jalview/structure/StructureMappingcommandSet.java
index c61d90a..086db18 100644
--- a/src/jalview/structure/StructureMappingcommandSet.java
+++ b/src/jalview/structure/StructureMappingcommandSet.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java
index c30908c..ee2cb04 100644
--- a/src/jalview/structure/StructureSelectionManager.java
+++ b/src/jalview/structure/StructureSelectionManager.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -31,10 +31,17 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceI;
+import jalview.ext.jmol.JmolParser;
+import jalview.gui.IProgressIndicator;
 import jalview.io.AppletFormatAdapter;
+import jalview.io.StructureFile;
 import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
+import jalview.ws.sifts.SiftsClient;
+import jalview.ws.sifts.SiftsException;
+import jalview.ws.sifts.SiftsSettings;
 
 import java.io.PrintStream;
 import java.util.ArrayList;
@@ -43,10 +50,8 @@ import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.Vector;
 
 import MCview.Atom;
@@ -67,10 +72,16 @@ public class StructureSelectionManager
 
   private boolean addTempFacAnnot = false;
 
+  private IProgressIndicator progressIndicator;
+
+  private SiftsClient siftsClient = null;
+
+  private long progressSessionId;
+
   /*
    * Set of any registered mappings between (dataset) sequences.
    */
-  public Set<AlignedCodonFrame> seqmappings = new LinkedHashSet<AlignedCodonFrame>();
+  private List<AlignedCodonFrame> seqmappings = new ArrayList<AlignedCodonFrame>();
 
   private List<CommandListener> commandListeners = new ArrayList<CommandListener>();
 
@@ -311,7 +322,7 @@ public class StructureSelectionManager
    *          - how to resolve data from resource
    * @return null or the structure data parsed as a pdb file
    */
-  synchronized public PDBfile setMapping(SequenceI[] sequence,
+  synchronized public StructureFile setMapping(SequenceI[] sequence,
           String[] targetChains, String pdbFile, String protocol)
   {
     return setMapping(true, sequence, targetChains, pdbFile, protocol);
@@ -324,9 +335,9 @@ public class StructureSelectionManager
    * @param forStructureView
    *          when true, record the mapping for use in mouseOvers
    * 
-   * @param sequence
+   * @param sequenceArray
    *          - one or more sequences to be mapped to pdbFile
-   * @param targetChains
+   * @param targetChainIds
    *          - optional chain specification for mapping each sequence to pdb
    *          (may be nill, individual elements may be nill)
    * @param pdbFile
@@ -335,9 +346,9 @@ public class StructureSelectionManager
    *          - how to resolve data from resource
    * @return null or the structure data parsed as a pdb file
    */
-  synchronized public PDBfile setMapping(boolean forStructureView,
-          SequenceI[] sequence, String[] targetChains, String pdbFile,
-          String protocol)
+  synchronized public StructureFile setMapping(boolean forStructureView,
+          SequenceI[] sequenceArray, String[] targetChainIds,
+          String pdbFile, String protocol)
   {
     /*
      * There will be better ways of doing this in the future, for now we'll use
@@ -346,7 +357,7 @@ public class StructureSelectionManager
     boolean parseSecStr = processSecondaryStructure;
     if (isPDBFileRegistered(pdbFile))
     {
-      for (SequenceI sq : sequence)
+      for (SequenceI sq : sequenceArray)
       {
         SequenceI ds = sq;
         while (ds.getDatasetSequence() != null)
@@ -369,52 +380,74 @@ public class StructureSelectionManager
         }
       }
     }
-    PDBfile pdb = null;
+    StructureFile pdb = null;
+    boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
     try
     {
-      pdb = new PDBfile(addTempFacAnnot, parseSecStr, secStructServices,
-              pdbFile, protocol);
-      if (pdb.id != null && pdb.id.trim().length() > 0
+      pdb = new JmolParser(pdbFile, protocol);
+
+      if (pdb.getId() != null && pdb.getId().trim().length() > 0
               && AppletFormatAdapter.FILE.equals(protocol))
       {
-        registerPDBFile(pdb.id.trim(), pdbFile);
+        registerPDBFile(pdb.getId().trim(), pdbFile);
       }
+      // if PDBId is unavailable then skip SIFTS mapping execution path
+      isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable();
+
     } catch (Exception ex)
     {
       ex.printStackTrace();
       return null;
     }
 
-    String targetChain;
-    for (int s = 0; s < sequence.length; s++)
+    try
+    {
+      if (isMapUsingSIFTs)
+      {
+        siftsClient = new SiftsClient(pdb);
+      }
+    } catch (SiftsException e)
+    {
+      isMapUsingSIFTs = false;
+      e.printStackTrace();
+    }
+
+    String targetChainId;
+    for (int s = 0; s < sequenceArray.length; s++)
     {
       boolean infChain = true;
-      final SequenceI seq = sequence[s];
-      if (targetChains != null && targetChains[s] != null)
+      final SequenceI seq = sequenceArray[s];
+      SequenceI ds = seq;
+      while (ds.getDatasetSequence() != null)
+      {
+        ds = ds.getDatasetSequence();
+      }
+
+      if (targetChainIds != null && targetChainIds[s] != null)
       {
         infChain = false;
-        targetChain = targetChains[s];
+        targetChainId = targetChainIds[s];
       }
       else if (seq.getName().indexOf("|") > -1)
       {
-        targetChain = seq.getName().substring(
+        targetChainId = seq.getName().substring(
                 seq.getName().lastIndexOf("|") + 1);
-        if (targetChain.length() > 1)
+        if (targetChainId.length() > 1)
         {
-          if (targetChain.trim().length() == 0)
+          if (targetChainId.trim().length() == 0)
           {
-            targetChain = " ";
+            targetChainId = " ";
           }
           else
           {
             // not a valid chain identifier
-            targetChain = "";
+            targetChainId = "";
           }
         }
       }
       else
       {
-        targetChain = "";
+        targetChainId = "";
       }
 
       /*
@@ -426,9 +459,9 @@ public class StructureSelectionManager
       String maxChainId = " ";
       PDBChain maxChain = null;
       boolean first = true;
-      for (PDBChain chain : pdb.chains)
+      for (PDBChain chain : pdb.getChains())
       {
-        if (targetChain.length() > 0 && !targetChain.equals(chain.id)
+        if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
                 && !infChain)
         {
           continue; // don't try to map chains don't match.
@@ -444,7 +477,7 @@ public class StructureSelectionManager
         // as.traceAlignment();
 
         if (first || as.maxscore > max
-                || (as.maxscore == max && chain.id.equals(targetChain)))
+                || (as.maxscore == max && chain.id.equals(targetChainId)))
         {
           first = false;
           maxChain = chain;
@@ -457,88 +490,223 @@ public class StructureSelectionManager
       {
         continue;
       }
-      final StringBuilder mappingDetails = new StringBuilder(128);
-      mappingDetails.append(NEWLINE).append("PDB Sequence is :")
-              .append(NEWLINE).append("Sequence = ")
-              .append(maxChain.sequence.getSequenceAsString());
-      mappingDetails.append(NEWLINE).append("No of residues = ")
-              .append(maxChain.residues.size()).append(NEWLINE)
-              .append(NEWLINE);
-      PrintStream ps = new PrintStream(System.out)
+
+      if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
       {
-        @Override
-        public void print(String x)
-        {
-          mappingDetails.append(x);
-        }
+        pdbFile = "INLINE" + pdb.getId();
+      }
 
-        @Override
-        public void println()
+      ArrayList<StructureMapping> seqToStrucMapping = new ArrayList<StructureMapping>();
+      if (isMapUsingSIFTs && seq.isProtein())
+      {
+        setProgressBar(null);
+        setProgressBar(MessageManager
+                .getString("status.obtaining_mapping_with_sifts"));
+        jalview.datamodel.Mapping sqmpping = maxAlignseq
+                .getMappingFromS1(false);
+        if (targetChainId != null && !targetChainId.trim().isEmpty())
         {
-          mappingDetails.append(NEWLINE);
+          StructureMapping siftsMapping;
+          try
+          {
+            siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
+                    pdb, maxChain, sqmpping, maxAlignseq);
+            seqToStrucMapping.add(siftsMapping);
+            maxChain.makeExactMapping(maxAlignseq, seq);
+            maxChain.transferRESNUMFeatures(seq, null);// FIXME: is this
+                                                       // "IEA:SIFTS" ?
+            maxChain.transferResidueAnnotation(siftsMapping, sqmpping);
+            ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
+
+          } catch (SiftsException e)
+          {
+            // fall back to NW alignment
+            System.err.println(e.getMessage());
+            StructureMapping nwMapping = getNWMappings(seq, pdbFile,
+                    targetChainId, maxChain, pdb, maxAlignseq);
+            seqToStrucMapping.add(nwMapping);
+            maxChain.makeExactMapping(maxAlignseq, seq);
+            maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
+                                                        // "IEA:Jalview" ?
+            maxChain.transferResidueAnnotation(nwMapping, sqmpping);
+            ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
+          }
         }
-      };
-
-      maxAlignseq.printAlignment(ps);
-
-      mappingDetails.append(NEWLINE).append("PDB start/end ");
-      mappingDetails.append(String.valueOf(maxAlignseq.seq2start)).append(
-              " ");
-      mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
-
-      mappingDetails.append(NEWLINE).append("SEQ start/end ");
-      mappingDetails.append(
-              String.valueOf(maxAlignseq.seq1start + seq.getStart() - 1))
-              .append(" ");
-      mappingDetails.append(String.valueOf(maxAlignseq.seq1end
-              + seq.getEnd() - 1));
-
-      maxChain.makeExactMapping(maxAlignseq, seq);
-      jalview.datamodel.Mapping sqmpping = maxAlignseq
-              .getMappingFromS1(false);
-      jalview.datamodel.Mapping omap = new jalview.datamodel.Mapping(
-              sqmpping.getMap().getInverse());
-      maxChain.transferRESNUMFeatures(seq, null);
-
-      // allocate enough slots to store the mapping from positions in
-      // sequence[s] to the associated chain
-      int[][] mapping = new int[seq.findPosition(seq.getLength()) + 2][2];
-      int resNum = -10000;
-      int index = 0;
-
-      do
-      {
-        Atom tmp = maxChain.atoms.elementAt(index);
-        if (resNum != tmp.resNumber && tmp.alignmentMapping != -1)
+        else
         {
-          resNum = tmp.resNumber;
-          if (tmp.alignmentMapping >= -1)
+          ArrayList<StructureMapping> foundSiftsMappings = new ArrayList<StructureMapping>();
+          for (PDBChain chain : pdb.getChains())
+          {
+            try
+            {
+              StructureMapping siftsMapping = getStructureMapping(seq,
+                      pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq);
+              foundSiftsMappings.add(siftsMapping);
+            } catch (SiftsException e)
+            {
+              System.err.println(e.getMessage());
+            }
+          }
+          if (!foundSiftsMappings.isEmpty())
           {
-            // TODO (JAL-1836) address root cause: negative residue no in PDB
-            // file
-            mapping[tmp.alignmentMapping + 1][0] = tmp.resNumber;
-            mapping[tmp.alignmentMapping + 1][1] = tmp.atomIndex;
+            seqToStrucMapping.addAll(foundSiftsMappings);
+            maxChain.makeExactMapping(maxAlignseq, seq);
+            maxChain.transferRESNUMFeatures(seq, null);// FIXME: is this
+                                                       // "IEA:SIFTS" ?
+            maxChain.transferResidueAnnotation(foundSiftsMappings.get(0),
+                    sqmpping);
+            ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
+          }
+          else
+          {
+            StructureMapping nwMapping = getNWMappings(seq, pdbFile,
+                    maxChainId, maxChain, pdb, maxAlignseq);
+            seqToStrucMapping.add(nwMapping);
+            maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
+                                                        // "IEA:Jalview" ?
+            maxChain.transferResidueAnnotation(nwMapping, sqmpping);
+            ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
           }
         }
+      }
+      else
+      {
+        setProgressBar(null);
+        setProgressBar(MessageManager
+                .getString("status.obtaining_mapping_with_nw_alignment"));
+        StructureMapping nwMapping = getNWMappings(seq, pdbFile,
+                maxChainId, maxChain, pdb, maxAlignseq);
+        seqToStrucMapping.add(nwMapping);
+        ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
 
-        index++;
-      } while (index < maxChain.atoms.size());
+      }
 
-      if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
+      if (forStructureView)
       {
-        pdbFile = "INLINE" + pdb.id;
+        mappings.addAll(seqToStrucMapping);
       }
-      StructureMapping newMapping = new StructureMapping(seq, pdbFile,
-              pdb.id, maxChainId, mapping, mappingDetails.toString());
-      if (forStructureView)
+    }
+    return pdb;
+  }
+
+  private boolean isCIFFile(String filename)
+  {
+    String fileExt = filename.substring(filename.lastIndexOf(".") + 1,
+            filename.length());
+    return "cif".equalsIgnoreCase(fileExt);
+  }
+
+  /**
+   * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
+   * uniprot or PDB
+   * 
+   * @param seq
+   * @param pdbFile
+   * @param targetChainId
+   * @param pdb
+   * @param maxChain
+   * @param sqmpping
+   * @param maxAlignseq
+   * @return
+   * @throws SiftsException
+   */
+  private StructureMapping getStructureMapping(SequenceI seq,
+          String pdbFile, String targetChainId, StructureFile pdb,
+          PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
+          AlignSeq maxAlignseq) throws SiftsException
+  {
+    StructureMapping curChainMapping = siftsClient
+            .getSiftsStructureMapping(seq, pdbFile, targetChainId);
+    try
+    {
+      PDBChain chain = pdb.findChain(targetChainId);
+      if (chain != null)
       {
-        mappings.add(newMapping);
+        chain.transferResidueAnnotation(curChainMapping, sqmpping);
       }
-      maxChain.transferResidueAnnotation(newMapping, sqmpping);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
     }
-    // ///////
+    return curChainMapping;
+  }
 
-    return pdb;
+  private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
+          String maxChainId, PDBChain maxChain, StructureFile pdb,
+          AlignSeq maxAlignseq)
+  {
+    final StringBuilder mappingDetails = new StringBuilder(128);
+    mappingDetails.append(NEWLINE).append(
+            "Sequence \u27f7 Structure mapping details");
+    mappingDetails.append(NEWLINE);
+    mappingDetails
+            .append("Method: inferred with Needleman & Wunsch alignment");
+    mappingDetails.append(NEWLINE).append("PDB Sequence is :")
+            .append(NEWLINE).append("Sequence = ")
+            .append(maxChain.sequence.getSequenceAsString());
+    mappingDetails.append(NEWLINE).append("No of residues = ")
+            .append(maxChain.residues.size()).append(NEWLINE)
+            .append(NEWLINE);
+    PrintStream ps = new PrintStream(System.out)
+    {
+      @Override
+      public void print(String x)
+      {
+        mappingDetails.append(x);
+      }
+
+      @Override
+      public void println()
+      {
+        mappingDetails.append(NEWLINE);
+      }
+    };
+
+    maxAlignseq.printAlignment(ps);
+
+    mappingDetails.append(NEWLINE).append("PDB start/end ");
+    mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
+            .append(" ");
+    mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
+    mappingDetails.append(NEWLINE).append("SEQ start/end ");
+    mappingDetails.append(
+            String.valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
+            .append(" ");
+    mappingDetails.append(String.valueOf(maxAlignseq.seq1end
+            + (seq.getStart() - 1)));
+    mappingDetails.append(NEWLINE);
+    maxChain.makeExactMapping(maxAlignseq, seq);
+    jalview.datamodel.Mapping sqmpping = maxAlignseq
+            .getMappingFromS1(false);
+    maxChain.transferRESNUMFeatures(seq, null);
+
+    HashMap<Integer, int[]> mapping = new HashMap<Integer, int[]>();
+    int resNum = -10000;
+    int index = 0;
+    char insCode = ' ';
+
+    do
+    {
+      Atom tmp = maxChain.atoms.elementAt(index);
+      if ((resNum != tmp.resNumber || insCode != tmp.insCode)
+              && tmp.alignmentMapping != -1)
+      {
+        resNum = tmp.resNumber;
+        insCode = tmp.insCode;
+        if (tmp.alignmentMapping >= -1)
+        {
+          mapping.put(tmp.alignmentMapping + 1, new int[] { tmp.resNumber,
+              tmp.atomIndex });
+        }
+      }
+
+      index++;
+    } while (index < maxChain.atoms.size());
+
+    StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
+            pdb.getId(), maxChainId, mapping, mappingDetails.toString());
+    maxChain.transferResidueAnnotation(nwMapping, sqmpping);
+    return nwMapping;
   }
 
   public void removeStructureViewerListener(Object svl, String[] pdbfiles)
@@ -638,7 +806,7 @@ public class StructureSelectionManager
       return;
     }
 
-    SearchResults results = new SearchResults();
+    SearchResultsI results = new SearchResults();
     for (AtomSpec atom : atoms)
     {
       SequenceI lastseq = null;
@@ -679,19 +847,19 @@ public class StructureSelectionManager
    *          the sequence that the mouse over occurred on
    * @param indexpos
    *          the absolute position being mouseovered in seq (0 to seq.length())
-   * @param index
+   * @param seqPos
    *          the sequence position (if -1, seq.findPosition is called to
    *          resolve the residue number)
    */
-  public void mouseOverSequence(SequenceI seq, int indexpos, int index,
+  public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
           VamsasSource source)
   {
     boolean hasSequenceListeners = handlingVamsasMo
             || !seqmappings.isEmpty();
-    SearchResults results = null;
-    if (index == -1)
+    SearchResultsI results = null;
+    if (seqPos == -1)
     {
-      index = seq.findPosition(indexpos);
+      seqPos = seq.findPosition(indexpos);
     }
     for (int i = 0; i < listeners.size(); i++)
     {
@@ -704,7 +872,7 @@ public class StructureSelectionManager
       }
       if (listener instanceof StructureListener)
       {
-        highlightStructure((StructureListener) listener, seq, index);
+        highlightStructure((StructureListener) listener, seq, seqPos);
       }
       else
       {
@@ -718,12 +886,12 @@ public class StructureSelectionManager
             {
               if (results == null)
               {
-                results = MappingUtils.buildSearchResults(seq, index,
+                results = MappingUtils.buildSearchResults(seq, seqPos,
                         seqmappings);
               }
               if (handlingVamsasMo)
               {
-                results.addResult(seq, index, index);
+                results.addResult(seq, seqPos, seqPos);
 
               }
               if (!results.isEmpty())
@@ -741,7 +909,7 @@ public class StructureSelectionManager
         else if (listener instanceof SecondaryStructureListener)
         {
           ((SecondaryStructureListener) listener).mouseOverSequence(seq,
-                  indexpos, index);
+                  indexpos, seqPos);
         }
       }
     }
@@ -749,14 +917,14 @@ public class StructureSelectionManager
 
   /**
    * Send suitable messages to a StructureListener to highlight atoms
-   * corresponding to the given sequence position.
+   * corresponding to the given sequence position(s)
    * 
    * @param sl
    * @param seq
-   * @param index
+   * @param positions
    */
-  protected void highlightStructure(StructureListener sl, SequenceI seq,
-          int index)
+  public void highlightStructure(StructureListener sl, SequenceI seq,
+          int... positions)
   {
     if (!sl.isListeningFor(seq))
     {
@@ -766,14 +934,20 @@ public class StructureSelectionManager
     List<AtomSpec> atoms = new ArrayList<AtomSpec>();
     for (StructureMapping sm : mappings)
     {
-      if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence())
+      if (sm.sequence == seq
+              || sm.sequence == seq.getDatasetSequence()
+              || (sm.sequence.getDatasetSequence() != null && sm.sequence
+                      .getDatasetSequence() == seq.getDatasetSequence()))
       {
-        atomNo = sm.getAtomNum(index);
-
-        if (atomNo > 0)
+        for (int index : positions)
         {
-          atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, sm
-                  .getPDBResNum(index), atomNo));
+          atomNo = sm.getAtomNum(index);
+
+          if (atomNo > 0)
+          {
+            atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, sm
+                    .getPDBResNum(index), atomNo));
+          }
         }
       }
     }
@@ -929,13 +1103,13 @@ public class StructureSelectionManager
   /**
    * Add each of the given codonFrames to the stored set, if not aready present.
    * 
-   * @param set
+   * @param mappings
    */
-  public void registerMappings(Set<AlignedCodonFrame> set)
+  public void registerMappings(List<AlignedCodonFrame> mappings)
   {
-    if (set != null)
+    if (mappings != null)
     {
-      for (AlignedCodonFrame acf : set)
+      for (AlignedCodonFrame acf : mappings)
       {
         registerMapping(acf);
       }
@@ -1147,4 +1321,39 @@ public class StructureSelectionManager
     }
     return null;
   }
+
+  public IProgressIndicator getProgressIndicator()
+  {
+    return progressIndicator;
+  }
+
+  public void setProgressIndicator(IProgressIndicator progressIndicator)
+  {
+    this.progressIndicator = progressIndicator;
+  }
+
+  public long getProgressSessionId()
+  {
+    return progressSessionId;
+  }
+
+  public void setProgressSessionId(long progressSessionId)
+  {
+    this.progressSessionId = progressSessionId;
+  }
+
+  public void setProgressBar(String message)
+  {
+    if (progressIndicator == null)
+    {
+      return;
+    }
+    progressIndicator.setProgressBar(message, progressSessionId);
+  }
+
+  public List<AlignedCodonFrame> getSequenceMappings()
+  {
+    return seqmappings;
+  }
+
 }
diff --git a/src/jalview/structure/VamsasListener.java b/src/jalview/structure/VamsasListener.java
index dbcccd9..77a1d9b 100644
--- a/src/jalview/structure/VamsasListener.java
+++ b/src/jalview/structure/VamsasListener.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/structure/VamsasSource.java b/src/jalview/structure/VamsasSource.java
index b9841f6..500efe5 100644
--- a/src/jalview/structure/VamsasSource.java
+++ b/src/jalview/structure/VamsasSource.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/structures/models/AAStructureBindingModel.java b/src/jalview/structures/models/AAStructureBindingModel.java
index 74fee63..1ebd93f 100644
--- a/src/jalview/structures/models/AAStructureBindingModel.java
+++ b/src/jalview/structures/models/AAStructureBindingModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -51,6 +51,10 @@ public abstract class AAStructureBindingModel extends
 
   private StructureSelectionManager ssm;
 
+  /*
+   * distinct PDB entries (pdb files) associated
+   * with sequences
+   */
   private PDBEntry[] pdbEntry;
 
   /*
@@ -75,6 +79,11 @@ public abstract class AAStructureBindingModel extends
   private boolean finishedInit = false;
 
   /**
+   * current set of model filenames loaded in the Jmol instance
+   */
+  protected String[] modelFileNames = null;
+
+  /**
    * Data bean class to simplify parameterisation in superposeStructures
    */
   protected class SuperposeData
@@ -126,19 +135,14 @@ public abstract class AAStructureBindingModel extends
    * @param protocol
    */
   public AAStructureBindingModel(StructureSelectionManager ssm,
-          PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains,
+          PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
           String protocol)
   {
     this.ssm = ssm;
     this.sequence = sequenceIs;
     this.nucleotide = Comparison.isNucleotide(sequenceIs);
-    this.chains = chains;
     this.pdbEntry = pdbentry;
     this.protocol = protocol;
-    if (chains == null)
-    {
-      this.chains = new String[pdbentry.length][];
-    }
   }
 
   public StructureSelectionManager getSsm()
@@ -239,24 +243,21 @@ public abstract class AAStructureBindingModel extends
     // TODO: give a more informative title when multiple structures are
     // displayed.
     StringBuilder title = new StringBuilder(64);
-    final PDBEntry pdbEntry = getPdbEntry(0);
+    final PDBEntry pdbe = getPdbEntry(0);
     title.append(viewerName + " view for " + getSequence()[0][0].getName()
-            + ":" + pdbEntry.getId());
+            + ":" + pdbe.getId());
 
     if (verbose)
     {
-      if (pdbEntry.getProperty() != null)
+      String method = (String) pdbe.getProperty("method");
+      if (method != null)
       {
-        if (pdbEntry.getProperty().get("method") != null)
-        {
-          title.append(" Method: ");
-          title.append(pdbEntry.getProperty().get("method"));
-        }
-        if (pdbEntry.getProperty().get("chains") != null)
-        {
-          title.append(" Chain:");
-          title.append(pdbEntry.getProperty().get("chains"));
-        }
+        title.append(" Method: ").append(method);
+      }
+      String chain = (String) pdbe.getProperty("chains");
+      if (chain != null)
+      {
+        title.append(" Chain:").append(chain);
       }
     }
     return title.toString();
@@ -521,6 +522,10 @@ public abstract class AAStructureBindingModel extends
   {
     int refStructure = -1;
     String[] files = getPdbFile();
+    if (files == null)
+    {
+      return -1;
+    }
     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
     {
       StructureMapping[] mappings = getSsm().getMapping(files[pdbfnum]);
@@ -565,7 +570,11 @@ public abstract class AAStructureBindingModel extends
             }
             structures[pdbfnum].pdbId = mapping.getPdbId();
             structures[pdbfnum].isRna = theSequence.getRNA() != null;
-            // move on to next pdb file
+
+            /*
+             * move on to next pdb file (ignore sequences for other chains
+             * for the same structure)
+             */
             s = seqCountForPdbFile;
             break;
           }
@@ -598,6 +607,10 @@ public abstract class AAStructureBindingModel extends
       for (String file : files)
       {
         notLoaded = file;
+        if (file == null)
+        {
+          continue;
+        }
         try
         {
           StructureMapping[] sm = getSsm().getMapping(file);
@@ -633,7 +646,10 @@ public abstract class AAStructureBindingModel extends
         {
           for (SequenceI s : seqs)
           {
-            if (s == seq)
+            if (s == seq
+                    || (s.getDatasetSequence() != null && s
+                            .getDatasetSequence() == seq
+                            .getDatasetSequence()))
             {
               return true;
             }
@@ -653,4 +669,12 @@ public abstract class AAStructureBindingModel extends
   {
     this.finishedInit = fi;
   }
+
+  /**
+   * Returns a list of chains mapped in this viewer.
+   * 
+   * @return
+   */
+  public abstract List<String> getChainNames();
+
 }
diff --git a/src/jalview/structures/models/SequenceStructureBindingModel.java b/src/jalview/structures/models/SequenceStructureBindingModel.java
index 54c1a53..5d8f272 100644
--- a/src/jalview/structures/models/SequenceStructureBindingModel.java
+++ b/src/jalview/structures/models/SequenceStructureBindingModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/util/AWTConsole.java b/src/jalview/util/AWTConsole.java
index 9d4f72a..4dacc0a 100644
--- a/src/jalview/util/AWTConsole.java
+++ b/src/jalview/util/AWTConsole.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/MCview/Residue.java b/src/jalview/util/ArrayUtils.java
similarity index 55%
copy from src/MCview/Residue.java
copy to src/jalview/util/ArrayUtils.java
index 2b0159e..ac51a0b 100644
--- a/src/MCview/Residue.java
+++ b/src/jalview/util/ArrayUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,35 +18,30 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package MCview;
+package jalview.util;
 
-import java.util.Vector;
-
-public class Residue
+public class ArrayUtils
 {
-  Vector<Atom> atoms;
-
-  int number;
-
-  int count;
-
-  public Residue(Vector<Atom> resAtoms, int number, int count)
+  /**
+   * Reverse the given array 'in situ'
+   * 
+   * @param arr
+   */
+  public static void reverseIntArray(int[] arr)
   {
-    this.atoms = resAtoms;
-    this.number = number;
-    this.count = count;
-  }
-
-  public Atom findAtom(String name)
-  {
-    for (Atom atom : atoms)
+    if (arr != null)
     {
-      if (atom.name.equals(name))
+      /*
+       * swap [k] with [end-k] up to the half way point in the array
+       * if length is odd, the middle entry is left untouched by the excitement
+       */
+      int last = arr.length - 1;
+      for (int k = 0; k < arr.length / 2; k++)
       {
-        return atom;
+        int temp = arr[k];
+        arr[k] = arr[last - k];
+        arr[last - k] = temp;
       }
     }
-
-    return null;
   }
 }
diff --git a/src/jalview/util/BrowserLauncher.java b/src/jalview/util/BrowserLauncher.java
index 52a3cdd..e53f436 100644
--- a/src/jalview/util/BrowserLauncher.java
+++ b/src/jalview/util/BrowserLauncher.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/util/CaseInsensitiveString.java b/src/jalview/util/CaseInsensitiveString.java
new file mode 100644
index 0000000..9b717c0
--- /dev/null
+++ b/src/jalview/util/CaseInsensitiveString.java
@@ -0,0 +1,77 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.util;
+
+/**
+ * A class to wrap a case insensitive string. For use in collections where we
+ * want to preserve case, but do not want to duplicate upper and lower case
+ * variants
+ */
+public final class CaseInsensitiveString
+{
+  String value;
+
+  public CaseInsensitiveString(String s)
+  {
+    this.value = s;
+  }
+
+  @Override
+  public String toString()
+  {
+    return value;
+  }
+
+  /**
+   * Answers true if the object compared to is a CaseInsensitiveString wrapping
+   * the same string value (ignoring case), or if both wrap a null value, else
+   * false
+   */
+  @Override
+  public boolean equals(Object o)
+  {
+    if (o == null)
+    {
+      return false;
+    }
+    if (!(o instanceof CaseInsensitiveString))
+    {
+      return false;
+    }
+    CaseInsensitiveString obj = (CaseInsensitiveString) o;
+    if (value == null)
+    {
+      return obj.value == null;
+    }
+    return value.equalsIgnoreCase(obj.value);
+  }
+
+  /**
+   * hashCode overriden to guarantee that 'equal' objects have the same hash
+   * code
+   */
+  @Override
+  public int hashCode()
+  {
+    return value == null ? super.hashCode() : value.toUpperCase()
+            .hashCode();
+  }
+}
diff --git a/src/jalview/util/ColorUtils.java b/src/jalview/util/ColorUtils.java
index c2f54f7..003dfe9 100644
--- a/src/jalview/util/ColorUtils.java
+++ b/src/jalview/util/ColorUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -103,4 +103,92 @@ public class ColorUtils
     return col == null ? null : col.brighter().brighter().brighter();
   }
 
+  /**
+   * Returns a color between minColour and maxColour; the RGB values are in
+   * proportion to where 'value' lies between minValue and maxValue
+   * 
+   * @param value
+   * @param minValue
+   * @param minColour
+   * @param maxValue
+   * @param maxColour
+   * @return
+   */
+  public static Color getGraduatedColour(float value, float minValue,
+          Color minColour, float maxValue, Color maxColour)
+  {
+    if (minValue == maxValue)
+    {
+      return minColour;
+    }
+    if (value < minValue)
+    {
+      value = minValue;
+    }
+    if (value > maxValue)
+    {
+      value = maxValue;
+    }
+
+    /*
+     * prop = proportion of the way value is from minValue to maxValue
+     */
+    float prop = (value - minValue) / (maxValue - minValue);
+    float r = minColour.getRed() + prop
+            * (maxColour.getRed() - minColour.getRed());
+    float g = minColour.getGreen() + prop
+            * (maxColour.getGreen() - minColour.getGreen());
+    float b = minColour.getBlue() + prop
+            * (maxColour.getBlue() - minColour.getBlue());
+    return new Color(r / 255, g / 255, b / 255);
+  }
+
+  /**
+   * 'Fades' the given colour towards white by the specified proportion. A
+   * factor of 1 or more results in White, a factor of 0 leaves the colour
+   * unchanged, and a factor between 0 and 1 results in a proportionate change
+   * of RGB values towards (255, 255, 255).
+   * <p>
+   * A negative bleachFactor can be specified to darken the colour towards Black
+   * (0, 0, 0).
+   * 
+   * @param colour
+   * @param bleachFactor
+   * @return
+   */
+  public static Color bleachColour(Color colour, float bleachFactor)
+  {
+    if (bleachFactor >= 1f)
+    {
+      return Color.WHITE;
+    }
+    if (bleachFactor <= -1f)
+    {
+      return Color.BLACK;
+    }
+    if (bleachFactor == 0f)
+    {
+      return colour;
+    }
+
+    int red = colour.getRed();
+    int green = colour.getGreen();
+    int blue = colour.getBlue();
+
+    if (bleachFactor > 0)
+    {
+      red += (255 - red) * bleachFactor;
+      green += (255 - green) * bleachFactor;
+      blue += (255 - blue) * bleachFactor;
+      return new Color(red, green, blue);
+    }
+    else
+    {
+      float factor = 1 + bleachFactor;
+      red *= factor;
+      green *= factor;
+      blue *= factor;
+      return new Color(red, green, blue);
+    }
+  }
 }
diff --git a/src/jalview/util/Comparison.java b/src/jalview/util/Comparison.java
index c491be4..c8bf606 100644
--- a/src/jalview/util/Comparison.java
+++ b/src/jalview/util/Comparison.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -249,6 +249,18 @@ public class Comparison
   }
 
   /**
+   * Overloaded method signature to test whether a single sequence is nucleotide
+   * (that is, more than 85% CGTA)
+   * 
+   * @param seq
+   * @return
+   */
+  public static final boolean isNucleotide(SequenceI seq)
+  {
+    return isNucleotide(new SequenceI[] { seq });
+  }
+
+  /**
    * Answers true if more than 85% of the sequence residues (ignoring gaps) are
    * A, G, C, T or U, else false. This is just a heuristic guess and may give a
    * wrong answer (as AGCT are also amino acid codes).
@@ -262,9 +274,35 @@ public class Comparison
     {
       return false;
     }
+    char[][] letters = new char[seqs.length][];
+    for (int i = 0; i < seqs.length; i++)
+    {
+      if (seqs[i] != null)
+      {
+        char[] sequence = seqs[i].getSequence();
+        if (sequence != null)
+        {
+          letters[i] = sequence;
+        }
+      }
+    }
+
+    return areNucleotide(letters);
+  }
+
+  /**
+   * Answers true if more than 85% of the sequence residues (ignoring gaps) are
+   * A, G, C, T or U, else false. This is just a heuristic guess and may give a
+   * wrong answer (as AGCT are also amino acid codes).
+   * 
+   * @param letters
+   * @return
+   */
+  static final boolean areNucleotide(char[][] letters)
+  {
     int ntCount = 0;
     int aaCount = 0;
-    for (SequenceI seq : seqs)
+    for (char[] seq : letters)
     {
       if (seq == null)
       {
@@ -272,18 +310,13 @@ public class Comparison
       }
       // TODO could possibly make an informed guess just from the first sequence
       // to save a lengthy calculation
-      for (char c : seq.getSequence())
+      for (char c : seq)
       {
-        if ('a' <= c && c <= 'z')
-        {
-          c -= TO_UPPER_CASE;
-        }
-
-        if (c == 'A' || c == 'G' || c == 'C' || c == 'T' || c == 'U')
+        if (isNucleotide(c))
         {
           ntCount++;
         }
-        else if (!Comparison.isGap(c))
+        else if (!isGap(c))
         {
           aaCount++;
         }
@@ -306,6 +339,59 @@ public class Comparison
   }
 
   /**
+   * Answers true if the character is one of aAcCgGtTuU
+   * 
+   * @param c
+   * @return
+   */
+  public static boolean isNucleotide(char c)
+  {
+    if ('a' <= c && c <= 'z')
+    {
+      c -= TO_UPPER_CASE;
+    }
+
+    switch (c)
+    {
+    case 'A':
+    case 'C':
+    case 'G':
+    case 'T':
+    case 'U':
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Answers true if every character in the string is one of aAcCgGtTuU, or
+   * (optionally) a gap character (dot, dash, space), else false
+   * 
+   * @param s
+   * @param allowGaps
+   * @return
+   */
+  public static boolean isNucleotideSequence(String s, boolean allowGaps)
+  {
+    if (s == null)
+    {
+      return false;
+    }
+    for (int i = 0; i < s.length(); i++)
+    {
+      char c = s.charAt(i);
+      if (!isNucleotide(c))
+      {
+        if (!allowGaps || !isGap(c))
+        {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  /**
    * Convenience overload of isNucleotide
    * 
    * @param seqs
@@ -329,4 +415,29 @@ public class Comparison
             .size()]);
     return isNucleotide(oneDArray);
   }
+
+  /**
+   * Compares two residues either case sensitively or case insensitively
+   * depending on the caseSensitive flag
+   * 
+   * @param c1
+   *          first char
+   * @param c2
+   *          second char to compare with
+   * @param caseSensitive
+   *          if true comparison will be case sensitive otherwise its not
+   * @return
+   */
+  public static boolean isSameResidue(char c1, char c2,
+          boolean caseSensitive)
+  {
+    if (caseSensitive)
+    {
+      return (c1 == c2);
+    }
+    else
+    {
+      return Character.toUpperCase(c1) == Character.toUpperCase(c2);
+    }
+  }
 }
diff --git a/src/jalview/util/DBRefUtils.java b/src/jalview/util/DBRefUtils.java
index 32589f7..503743f 100644
--- a/src/jalview/util/DBRefUtils.java
+++ b/src/jalview/util/DBRefUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,15 +26,23 @@ import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
-import java.util.Hashtable;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import com.stevesoft.pat.Regex;
 
+/**
+ * Utilities for handling DBRef objects and their collections.
+ */
 public class DBRefUtils
 {
+  /*
+   * lookup from lower-case form of a name to its canonical (standardised) form
+   */
   private static Map<String, String> canonicalSourceNameLookup = new HashMap<String, String>();
 
   private static Map<String, String> dasCoordinateSystemsLookup = new HashMap<String, String>();
@@ -45,7 +53,25 @@ public class DBRefUtils
     canonicalSourceNameLookup.put("uniprotkb/swiss-prot",
             DBRefSource.UNIPROT);
     canonicalSourceNameLookup.put("uniprotkb/trembl", DBRefSource.UNIPROT);
+
+    // Ensembl values for dbname in xref REST service:
+    canonicalSourceNameLookup.put("uniprot/sptrembl", DBRefSource.UNIPROT);
+    canonicalSourceNameLookup.put("uniprot/swissprot", DBRefSource.UNIPROT);
+
     canonicalSourceNameLookup.put("pdb", DBRefSource.PDB);
+    canonicalSourceNameLookup.put("ensembl", DBRefSource.ENSEMBL);
+    // Ensembl Gn and Tr are for Ensembl genomic and transcript IDs as served
+    // from ENA.
+    canonicalSourceNameLookup.put("ensembl-tr", DBRefSource.ENSEMBL);
+    canonicalSourceNameLookup.put("ensembl-gn", DBRefSource.ENSEMBL);
+
+    // Make sure we have lowercase entries for all canonical string lookups
+    Set<String> keys = canonicalSourceNameLookup.keySet();
+    for (String k : keys)
+    {
+      canonicalSourceNameLookup.put(k.toLowerCase(),
+              canonicalSourceNameLookup.get(k));
+    }
 
     dasCoordinateSystemsLookup.put("pdbresnum", DBRefSource.PDB);
     dasCoordinateSystemsLookup.put("uniprot", DBRefSource.UNIPROT);
@@ -54,39 +80,36 @@ public class DBRefUtils
   }
 
   /**
-   * Utilities for handling DBRef objects and their collections.
-   */
-  /**
+   * Returns those DBRefEntry objects whose source identifier (once converted to
+   * Jalview's canonical form) is in the list of sources to search for. Returns
+   * null if no matches found.
    * 
    * @param dbrefs
-   *          Vector of DBRef objects to search
+   *          DBRefEntry objects to search
    * @param sources
-   *          String[] array of source DBRef IDs to retrieve
-   * @return Vector
+   *          array of sources to select
+   * @return
    */
   public static DBRefEntry[] selectRefs(DBRefEntry[] dbrefs,
           String[] sources)
   {
-    if (dbrefs == null)
-    {
-      return null;
-    }
-    if (sources == null)
+    if (dbrefs == null || sources == null)
     {
       return dbrefs;
     }
-    Map<String, Integer> srcs = new HashMap<String, Integer>();
-    ArrayList<DBRefEntry> res = new ArrayList<DBRefEntry>();
-
-    for (int i = 0; i < sources.length; i++)
+    HashSet<String> srcs = new HashSet<String>();
+    for (String src : sources)
     {
-      srcs.put(new String(sources[i]), new Integer(i));
+      srcs.add(src.toUpperCase());
     }
-    for (int i = 0, j = dbrefs.length; i < j; i++)
+
+    List<DBRefEntry> res = new ArrayList<DBRefEntry>();
+    for (DBRefEntry dbr : dbrefs)
     {
-      if (srcs.containsKey(dbrefs[i].getSource()))
+      String source = getCanonicalName(dbr.getSource());
+      if (srcs.contains(source.toUpperCase()))
       {
-        res.add(dbrefs[i]);
+        res.add(dbr);
       }
     }
 
@@ -95,8 +118,6 @@ public class DBRefUtils
       DBRefEntry[] reply = new DBRefEntry[res.size()];
       return res.toArray(reply);
     }
-    res = null;
-    // there are probable memory leaks in the hashtable!
     return null;
   }
 
@@ -143,8 +164,8 @@ public class DBRefUtils
   }
 
   /**
-   * Returns an array of those references that match the given entry, or null if
-   * no matches. Currently uses a comparator which matches if
+   * Returns a (possibly empty) list of those references that match the given
+   * entry. Currently uses a comparator which matches if
    * <ul>
    * <li>database sources are the same</li>
    * <li>accession ids are the same</li>
@@ -157,15 +178,35 @@ public class DBRefUtils
    *          pattern to match
    * @return
    */
-  public static DBRefEntry[] searchRefs(DBRefEntry[] ref, DBRefEntry entry)
+  public static List<DBRefEntry> searchRefs(DBRefEntry[] ref,
+          DBRefEntry entry)
   {
     return searchRefs(ref, entry,
             matchDbAndIdAndEitherMapOrEquivalentMapList);
   }
 
   /**
-   * Returns an array of those references that match the given entry, according
-   * to the given comparator. Returns null if no matches.
+   * Returns a list of those references that match the given accession id
+   * <ul>
+   * <li>database sources are the same</li>
+   * <li>accession ids are the same</li>
+   * <li>both have no mapping, or the mappings are the same</li>
+   * </ul>
+   * 
+   * @param refs
+   *          Set of references to search
+   * @param accId
+   *          accession id to match
+   * @return
+   */
+  public static List<DBRefEntry> searchRefs(DBRefEntry[] refs, String accId)
+  {
+    return searchRefs(refs, new DBRefEntry("", "", accId), matchId);
+  }
+
+  /**
+   * Returns a (possibly empty) list of those references that match the given
+   * entry, according to the given comparator.
    * 
    * @param refs
    *          an array of database references to search
@@ -174,14 +215,14 @@ public class DBRefUtils
    * @param comparator
    * @return
    */
-  static DBRefEntry[] searchRefs(DBRefEntry[] refs, DBRefEntry entry,
+  static List<DBRefEntry> searchRefs(DBRefEntry[] refs, DBRefEntry entry,
           DbRefComp comparator)
   {
+    List<DBRefEntry> rfs = new ArrayList<DBRefEntry>();
     if (refs == null || entry == null)
     {
-      return null;
+      return rfs;
     }
-    List<DBRefEntry> rfs = new ArrayList<DBRefEntry>();
     for (int i = 0; i < refs.length; i++)
     {
       if (comparator.matches(entry, refs[i]))
@@ -189,7 +230,7 @@ public class DBRefUtils
         rfs.add(refs[i]);
       }
     }
-    return rfs.size() == 0 ? null : rfs.toArray(new DBRefEntry[rfs.size()]);
+    return rfs;
   }
 
   interface DbRefComp
@@ -200,12 +241,15 @@ public class DBRefUtils
   /**
    * match on all non-null fields in refa
    */
+  // TODO unused - remove?
   public static DbRefComp matchNonNullonA = new DbRefComp()
   {
+    @Override
     public boolean matches(DBRefEntry refa, DBRefEntry refb)
     {
       if (refa.getSource() == null
-              || refb.getSource().equals(refa.getSource()))
+              || DBRefUtils.getCanonicalName(refb.getSource()).equals(
+                      DBRefUtils.getCanonicalName(refa.getSource())))
       {
         if (refa.getVersion() == null
                 || refb.getVersion().equals(refa.getVersion()))
@@ -230,27 +274,18 @@ public class DBRefUtils
    * either field is null or field matches for all of source, version, accession
    * id and map.
    */
+  // TODO unused - remove?
   public static DbRefComp matchEitherNonNull = new DbRefComp()
   {
+    @Override
     public boolean matches(DBRefEntry refa, DBRefEntry refb)
     {
-      if ((refa.getSource() == null || refb.getSource() == null)
-              || refb.getSource().equals(refa.getSource()))
+      if (nullOrEqualSource(refa.getSource(), refb.getSource())
+              && nullOrEqual(refa.getVersion(), refb.getVersion())
+              && nullOrEqual(refa.getAccessionId(), refb.getAccessionId())
+              && nullOrEqual(refa.getMap(), refb.getMap()))
       {
-        if ((refa.getVersion() == null || refb.getVersion() == null)
-                || refb.getVersion().equals(refa.getVersion()))
-        {
-          if ((refa.getAccessionId() == null || refb.getAccessionId() == null)
-                  || refb.getAccessionId().equals(refa.getAccessionId()))
-          {
-            if ((refa.getMap() == null || refb.getMap() == null)
-                    || (refb.getMap() != null && refb.getMap().equals(
-                            refa.getMap())))
-            {
-              return true;
-            }
-          }
-        }
+        return true;
       }
       return false;
     }
@@ -260,18 +295,20 @@ public class DBRefUtils
    * accession ID and DB must be identical. Version is ignored. Map is either
    * not defined or is a match (or is compatible?)
    */
+  // TODO unused - remove?
   public static DbRefComp matchDbAndIdAndEitherMap = new DbRefComp()
   {
+    @Override
     public boolean matches(DBRefEntry refa, DBRefEntry refb)
     {
-      if (refa.getSource() != null && refb.getSource() != null
-              && refb.getSource().equals(refa.getSource()))
+      if (refa.getSource() != null
+              && refb.getSource() != null
+              && DBRefUtils.getCanonicalName(refb.getSource()).equals(
+                      DBRefUtils.getCanonicalName(refa.getSource())))
       {
         // We dont care about version
-        // if ((refa.getVersion()==null || refb.getVersion()==null)
-        // || refb.getVersion().equals(refa.getVersion()))
-        // {
         if (refa.getAccessionId() != null && refb.getAccessionId() != null
+        // FIXME should be && not || here?
                 || refb.getAccessionId().equals(refa.getAccessionId()))
         {
           if ((refa.getMap() == null || refb.getMap() == null)
@@ -291,17 +328,18 @@ public class DBRefUtils
    * or map but no maplist on either or maplist of map on a is the complement of
    * maplist of map on b.
    */
+  // TODO unused - remove?
   public static DbRefComp matchDbAndIdAndComplementaryMapList = new DbRefComp()
   {
+    @Override
     public boolean matches(DBRefEntry refa, DBRefEntry refb)
     {
-      if (refa.getSource() != null && refb.getSource() != null
-              && refb.getSource().equals(refa.getSource()))
+      if (refa.getSource() != null
+              && refb.getSource() != null
+              && DBRefUtils.getCanonicalName(refb.getSource()).equals(
+                      DBRefUtils.getCanonicalName(refa.getSource())))
       {
         // We dont care about version
-        // if ((refa.getVersion()==null || refb.getVersion()==null)
-        // || refb.getVersion().equals(refa.getVersion()))
-        // {
         if (refa.getAccessionId() != null && refb.getAccessionId() != null
                 || refb.getAccessionId().equals(refa.getAccessionId()))
         {
@@ -328,12 +366,16 @@ public class DBRefUtils
    * or or map but no maplist on either or maplist of map on a is equivalent to
    * the maplist of map on b.
    */
+  // TODO unused - remove?
   public static DbRefComp matchDbAndIdAndEquivalentMapList = new DbRefComp()
   {
+    @Override
     public boolean matches(DBRefEntry refa, DBRefEntry refb)
     {
-      if (refa.getSource() != null && refb.getSource() != null
-              && refb.getSource().equals(refa.getSource()))
+      if (refa.getSource() != null
+              && refb.getSource() != null
+              && DBRefUtils.getCanonicalName(refb.getSource()).equals(
+                      DBRefUtils.getCanonicalName(refa.getSource())))
       {
         // We dont care about version
         // if ((refa.getVersion()==null || refb.getVersion()==null)
@@ -362,25 +404,24 @@ public class DBRefUtils
   };
 
   /**
-   * accession ID and DB must be identical. Version is ignored. No map on either
-   * or map but no maplist on either or maplist of map on a is equivalent to the
-   * maplist of map on b.
+   * accession ID and DB must be identical, or null on a. Version is ignored. No
+   * map on either or map but no maplist on either or maplist of map on a is
+   * equivalent to the maplist of map on b.
    */
   public static DbRefComp matchDbAndIdAndEitherMapOrEquivalentMapList = new DbRefComp()
   {
+    @Override
     public boolean matches(DBRefEntry refa, DBRefEntry refb)
     {
-      // System.err.println("Comparing A: "+refa.getSrcAccString()+(refa.hasMap()?" has map.":"."));
-      // System.err.println("Comparing B: "+refb.getSrcAccString()+(refb.hasMap()?" has map.":"."));
-      if (refa.getSource() != null && refb.getSource() != null
-              && refb.getSource().equals(refa.getSource()))
+      if (refa.getSource() != null
+              && refb.getSource() != null
+              && DBRefUtils.getCanonicalName(refb.getSource()).equals(
+                      DBRefUtils.getCanonicalName(refa.getSource())))
       {
         // We dont care about version
-        // if ((refa.getVersion()==null || refb.getVersion()==null)
-        // || refb.getVersion().equals(refa.getVersion()))
-        // {
-        if (refa.getAccessionId() != null && refb.getAccessionId() != null
-                && refb.getAccessionId().equals(refa.getAccessionId()))
+
+        if (refa.getAccessionId() == null
+                || refa.getAccessionId().equals(refb.getAccessionId()))
         {
           if (refa.getMap() == null || refb.getMap() == null)
           {
@@ -392,7 +433,7 @@ public class DBRefUtils
                   || (refb.getMap().getMap() != null
                           && refa.getMap().getMap() != null && (refb
                           .getMap().getMap().equals(refa.getMap().getMap()))))
-          { // getMap().getMap().containsEither(false,refa.getMap().getMap())
+          {
             return true;
           }
         }
@@ -402,6 +443,23 @@ public class DBRefUtils
   };
 
   /**
+   * accession ID only must be identical.
+   */
+  public static DbRefComp matchId = new DbRefComp()
+  {
+    @Override
+    public boolean matches(DBRefEntry refa, DBRefEntry refb)
+    {
+      if (refa.getAccessionId() != null && refb.getAccessionId() != null
+              && refb.getAccessionId().equals(refa.getAccessionId()))
+      {
+        return true;
+      }
+      return false;
+    }
+  };
+
+  /**
    * Parses a DBRefEntry and adds it to the sequence, also a PDBEntry if the
    * database is PDB.
    * <p>
@@ -449,9 +507,7 @@ public class DBRefUtils
           PDBEntry pdbr = new PDBEntry();
           pdbr.setId(pdbid);
           pdbr.setType(PDBEntry.Type.PDB);
-          pdbr.setProperty(new Hashtable());
           pdbr.setChainCode(chaincode);
-          // pdbr.getProperty().put("CHAIN", chaincode);
           seq.addPDBId(pdbr);
         }
         else
@@ -472,4 +528,209 @@ public class DBRefUtils
     return ref;
   }
 
+  /**
+   * Returns true if either object is null, or they are equal
+   * 
+   * @param o1
+   * @param o2
+   * @return
+   */
+  public static boolean nullOrEqual(Object o1, Object o2)
+  {
+    if (o1 == null || o2 == null)
+    {
+      return true;
+    }
+    return o1.equals(o2);
+  }
+
+  /**
+   * canonicalise source string before comparing. null is always wildcard
+   * 
+   * @param o1
+   *          - null or source string to compare
+   * @param o2
+   *          - null or source string to compare
+   * @return true if either o1 or o2 are null, or o1 equals o2 under
+   *         DBRefUtils.getCanonicalName
+   *         (o1).equals(DBRefUtils.getCanonicalName(o2))
+   */
+  public static boolean nullOrEqualSource(String o1, String o2)
+  {
+    if (o1 == null || o2 == null)
+    {
+      return true;
+    }
+    return DBRefUtils.getCanonicalName(o1).equals(
+            DBRefUtils.getCanonicalName(o2));
+  }
+
+  /**
+   * Selects just the DNA or protein references from a set of references
+   * 
+   * @param selectDna
+   *          if true, select references to 'standard' DNA databases, else to
+   *          'standard' peptide databases
+   * @param refs
+   *          a set of references to select from
+   * @return
+   */
+  public static DBRefEntry[] selectDbRefs(boolean selectDna,
+          DBRefEntry[] refs)
+  {
+    return selectRefs(refs, selectDna ? DBRefSource.DNACODINGDBS
+            : DBRefSource.PROTEINDBS);
+    // could attempt to find other cross
+    // refs here - ie PDB xrefs
+    // (not dna, not protein seq)
+  }
+
+  /**
+   * Returns the (possibly empty) list of those supplied dbrefs which have the
+   * specified source database, with a case-insensitive match of source name
+   * 
+   * @param dbRefs
+   * @param source
+   * @return
+   */
+  public static List<DBRefEntry> searchRefsForSource(DBRefEntry[] dbRefs,
+          String source)
+  {
+    List<DBRefEntry> matches = new ArrayList<DBRefEntry>();
+    if (dbRefs != null && source != null)
+    {
+      for (DBRefEntry dbref : dbRefs)
+      {
+        if (source.equalsIgnoreCase(dbref.getSource()))
+        {
+          matches.add(dbref);
+        }
+      }
+    }
+    return matches;
+  }
+
+  /**
+   * promote direct database references to primary for nucleotide or protein
+   * sequences if they have an appropriate primary ref
+   * <table>
+   * <tr>
+   * <th>Seq Type</th>
+   * <th>Primary DB</th>
+   * <th>Direct which will be promoted</th>
+   * </tr>
+   * <tr align=center>
+   * <td>peptides</td>
+   * <td>Ensembl</td>
+   * <td>Uniprot</td>
+   * </tr>
+   * <tr align=center>
+   * <td>peptides</td>
+   * <td>Ensembl</td>
+   * <td>Uniprot</td>
+   * </tr>
+   * <tr align=center>
+   * <td>dna</td>
+   * <td>Ensembl</td>
+   * <td>ENA</td>
+   * </tr>
+   * </table>
+   * 
+   * @param sequence
+   */
+  public static void ensurePrimaries(SequenceI sequence)
+  {
+    List<DBRefEntry> pr = sequence.getPrimaryDBRefs();
+    if (pr.size() == 0)
+    {
+      // nothing to do
+      return;
+    }
+    List<DBRefEntry> selfs = new ArrayList<DBRefEntry>();
+    {
+      DBRefEntry[] selfArray = selectDbRefs(!sequence.isProtein(),
+              sequence.getDBRefs());
+      if (selfArray == null || selfArray.length == 0)
+      {
+        // nothing to do
+        return;
+      }
+      selfs.addAll(Arrays.asList(selfArray));
+    }
+
+    // filter non-primary refs
+    for (DBRefEntry p : pr)
+    {
+      while (selfs.contains(p))
+      {
+        selfs.remove(p);
+      }
+    }
+    List<DBRefEntry> toPromote = new ArrayList<DBRefEntry>();
+
+    for (DBRefEntry p : pr)
+    {
+      List<String> promType = new ArrayList<String>();
+      if (sequence.isProtein())
+      {
+        switch (getCanonicalName(p.getSource()))
+        {
+        case DBRefSource.UNIPROT:
+          // case DBRefSource.UNIPROTKB:
+          // case DBRefSource.UP_NAME:
+          // search for and promote ensembl
+          promType.add(DBRefSource.ENSEMBL);
+          break;
+        case DBRefSource.ENSEMBL:
+          // search for and promote Uniprot
+          promType.add(DBRefSource.UNIPROT);
+          break;
+        }
+      }
+      else
+      {
+        // TODO: promote transcript refs
+      }
+
+      // collate candidates and promote them
+      DBRefEntry[] candidates = selectRefs(
+              selfs.toArray(new DBRefEntry[0]),
+              promType.toArray(new String[0]));
+      if (candidates != null)
+      {
+        for (DBRefEntry cand : candidates)
+        {
+          if (cand.hasMap())
+          {
+            if (cand.getMap().getTo() != null
+                    && cand.getMap().getTo() != sequence)
+            {
+              // can't promote refs with mappings to other sequences
+              continue;
+            }
+            if (cand.getMap().getMap().getFromLowest() != sequence
+                    .getStart()
+                    && cand.getMap().getMap().getFromHighest() != sequence
+                            .getEnd())
+            {
+              // can't promote refs with mappings from a region of this sequence
+              // - eg CDS
+              continue;
+            }
+          }
+          // and promote
+          cand.setVersion(p.getVersion() + " (promoted)");
+          selfs.remove(cand);
+          toPromote.add(cand);
+          if (!cand.isPrimaryCandidate())
+          {
+            System.out.println("Warning: Couldn't promote dbref "
+                    + cand.toString() + " for sequence "
+                    + sequence.toString());
+          }
+        }
+      }
+    }
+  }
+
 }
diff --git a/src/jalview/util/DnaUtils.java b/src/jalview/util/DnaUtils.java
new file mode 100644
index 0000000..1b6e774
--- /dev/null
+++ b/src/jalview/util/DnaUtils.java
@@ -0,0 +1,153 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.util;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class DnaUtils
+{
+
+  /**
+   * Parses an ENA/GenBank format location specifier and returns a list of
+   * [start, end] ranges. Throws an exception if not able to parse.
+   * <p>
+   * Currently we do not parse "order()" specifiers, or indeterminate ranges of
+   * the format "<start..end" or "start..>end" or "start.end" or
+   * "start^end"
+   * 
+   * @param location
+   * @return
+   * @throws ParseException
+   *           if unable to parse the location (the exception message is the
+   *           location specifier being parsed); we use ParseException in
+   *           preference to the unchecked IllegalArgumentException
+   * @see http://www.insdc.org/files/feature_table.html#3.4
+   */
+  public static List<int[]> parseLocation(String location)
+          throws ParseException
+  {
+    if (location.startsWith("join("))
+    {
+      return parseJoin(location);
+    }
+    else if (location.startsWith("complement("))
+    {
+      return parseComplement(location);
+    }
+    if (location.startsWith("order("))
+    {
+      throw new ParseException(location, 0);
+    }
+
+    /*
+     * try to parse m..n (or simply m)
+     */
+    String[] range = location.split("\\.\\.");
+    if (range.length == 1 || range.length == 2)
+    {
+      try
+      {
+        int start = Integer.valueOf(range[0]);
+        int end = range.length == 1 ? start : Integer.valueOf(range[1]);
+        return Collections.singletonList(new int[] { start, end });
+      } catch (NumberFormatException e)
+      {
+        /*
+         * could be a location like <1..888 or 1..>888
+         */
+        throw new ParseException(location, 0);
+      }
+    }
+    else
+    {
+      /*
+       * could be a location like 102.110 or 123^124
+       */
+      throw new ParseException(location, 0);
+    }
+  }
+
+  /**
+   * Parses a complement(locationSpec) into a list of start-end ranges
+   * 
+   * @param location
+   * @return
+   * @throws ParseException
+   */
+  static List<int[]> parseComplement(String location) throws ParseException
+  {
+    /*
+     * take what is inside complement()
+     */
+    if (!location.endsWith(")"))
+    {
+      throw new ParseException(location, 0);
+    }
+    String toComplement = location.substring("complement(".length(),
+            location.length() - 1);
+    List<int[]> ranges = parseLocation(toComplement);
+
+    /*
+     * reverse the order and direction of ranges
+     */
+    Collections.reverse(ranges);
+    for (int[] range : ranges)
+    {
+      int temp = range[0];
+      range[0] = range[1];
+      range[1] = temp;
+    }
+    return ranges;
+  }
+
+  /**
+   * Parses a join(loc1,loc2,...,locn) into a list of start-end ranges
+   * 
+   * @param location
+   * @return
+   * @throws ParseException
+   */
+  static List<int[]> parseJoin(String location) throws ParseException
+  {
+    List<int[]> ranges = new ArrayList<int[]>();
+
+    /*
+     * take what is inside join()
+     */
+    if (!location.endsWith(")"))
+    {
+      throw new ParseException(location, 0);
+    }
+    String joinedLocs = location.substring("join(".length(),
+            location.length() - 1);
+    String[] locations = joinedLocs.split(",");
+    for (String loc : locations)
+    {
+      List<int[]> range = parseLocation(loc);
+      ranges.addAll(range);
+    }
+    return ranges;
+  }
+
+}
diff --git a/src/jalview/util/Format.java b/src/jalview/util/Format.java
index ffc80d3..8515a76 100644
--- a/src/jalview/util/Format.java
+++ b/src/jalview/util/Format.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,6 +26,8 @@
  */
 package jalview.util;
 
+import java.util.Arrays;
+
 /**
  * DOCUMENT ME!
  * 
@@ -664,30 +666,22 @@ public class Format
   }
 
   /**
-   * DOCUMENT ME!
+   * Returns a string consisting of n repeats of character c
    * 
    * @param c
-   *          DOCUMENT ME!
    * @param n
-   *          DOCUMENT ME!
    * 
-   * @return DOCUMENT ME!
+   * @return
    */
-  private static String repeat(char c, int n)
+  static String repeat(char c, int n)
   {
     if (n <= 0)
     {
       return "";
     }
-
-    StringBuffer s = new StringBuffer(n);
-
-    for (int i = 0; i < n; i++)
-    {
-      s.append(c);
-    }
-
-    return s.toString();
+    char[] chars = new char[n];
+    Arrays.fill(chars, c);
+    return new String(chars);
   }
 
   /**
@@ -947,4 +941,49 @@ public class Format
   {
     return formatString;
   }
+
+  /**
+   * Bespoke method to format percentage float value to the specified number of
+   * decimal places. Avoids use of general-purpose format parsers as a
+   * processing hotspot.
+   * 
+   * @param sb
+   * @param value
+   * @param dp
+   */
+  public static void appendPercentage(StringBuilder sb, float value, int dp)
+  {
+    /*
+     * rounding first
+     */
+    double d = value;
+    long factor = 1L;
+    for (int i = 0; i < dp; i++)
+    {
+      factor *= 10;
+    }
+    d *= factor;
+    d += 0.5;
+
+    /*
+     * integer part
+     */
+    value = (float) (d / factor);
+    sb.append((long) value);
+
+    /*
+     * decimal places
+     */
+    if (dp > 0)
+    {
+      sb.append(".");
+      while (dp > 0)
+      {
+        value = value - (int) value;
+        value *= 10;
+        sb.append((int) value);
+        dp--;
+      }
+    }
+  }
 }
diff --git a/src/jalview/util/GroupUrlLink.java b/src/jalview/util/GroupUrlLink.java
index 1acf94b..4eae6d6 100644
--- a/src/jalview/util/GroupUrlLink.java
+++ b/src/jalview/util/GroupUrlLink.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/datamodel/UniprotSequence.java b/src/jalview/util/HttpUtils.java
similarity index 50%
copy from src/jalview/datamodel/UniprotSequence.java
copy to src/jalview/util/HttpUtils.java
index c9b84fa..d8d4433 100644
--- a/src/jalview/datamodel/UniprotSequence.java
+++ b/src/jalview/util/HttpUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,41 +18,50 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.datamodel;
+package jalview.util;
 
-/**
- * Data model for the sequence returned by a Uniprot query
- * 
- * @see uniprot_mapping.xml
- */
-public class UniprotSequence
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+public class HttpUtils
 {
-  private String _content = "";
 
   /**
-   * Sets the content string, omitting any space characters
+   * Returns true if it is possible to open an input stream at the given URL,
+   * else false. The input stream is closed.
    * 
-   * @param seq
+   * @param url
+   * @return
    */
-  public void setContent(String seq)
+  public static boolean isValidUrl(String url)
   {
-    if (seq != null)
+    InputStream is = null;
+    try
     {
-      StringBuilder sb = new StringBuilder(seq.length());
-      for (int i = 0; i < seq.length(); i++)
+      is = new URL(url).openStream();
+      if (is != null)
       {
-        if (seq.charAt(i) != ' ')
+        return true;
+      }
+    } catch (IOException x)
+    {
+      // MalformedURLException, FileNotFoundException
+      return false;
+    } finally
+    {
+      if (is != null)
+      {
+        try
+        {
+          is.close();
+        } catch (IOException e)
         {
-          sb.append(seq.charAt(i));
+          // ignore
         }
       }
-      _content = sb.toString();
     }
-  }
-
-  public String getContent()
-  {
-    return _content;
+    return false;
   }
 
 }
diff --git a/src/jalview/util/ImageMaker.java b/src/jalview/util/ImageMaker.java
index 3c7ac8c..69cb26f 100644
--- a/src/jalview/util/ImageMaker.java
+++ b/src/jalview/util/ImageMaker.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,6 +22,7 @@ package jalview.util;
 
 import jalview.bin.Jalview;
 import jalview.gui.EPSOptions;
+import jalview.gui.IProgressIndicator;
 import jalview.gui.SVGOptions;
 import jalview.io.JalviewFileChooser;
 
@@ -53,11 +54,17 @@ public class ImageMaker
 
   TYPE type;
 
+  private IProgressIndicator pIndicator;
+
+  private long pSessionId;
+
+  private boolean headless;
+
   public enum TYPE
   {
-    EPS("EPS", MessageManager.getString("label.eps_file"), getEPSChooser()), PNG(
-            "PNG", MessageManager.getString("label.png_image"),
-            getPNGChooser()), SVG("SVG", "SVG", getSVGChooser());
+    EPS("EPS", MessageManager.getString("label.eps_file"), getEPSChooser()),
+    PNG("PNG", MessageManager.getString("label.png_image"), getPNGChooser()),
+    SVG("SVG", "SVG", getSVGChooser());
 
     private JalviewFileChooser chooser;
 
@@ -90,12 +97,17 @@ public class ImageMaker
   }
 
   public ImageMaker(Component parent, TYPE type, String title, int width,
-          int height, File file, String fileTitle)
+          int height, File file, String fileTitle,
+          IProgressIndicator pIndicator, long pSessionId, boolean headless)
   {
+    this.pIndicator = pIndicator;
     this.type = type;
-
+    this.pSessionId = pSessionId;
+    this.headless = headless;
     if (file == null)
     {
+      setProgressMessage(MessageManager.formatMessage(
+              "status.waiting_for_user_to_select_output_file", type.name));
       JalviewFileChooser chooser;
       chooser = type.getChooser();
       chooser.setFileView(new jalview.io.JalviewFileView());
@@ -109,6 +121,11 @@ public class ImageMaker
                 .getSelectedFile().getParent());
         file = chooser.getSelectedFile();
       }
+      else
+      {
+        setProgressMessage(MessageManager.formatMessage(
+                "status.cancelled_image_export_operation", type.name));
+      }
     }
 
     if (file != null)
@@ -116,6 +133,9 @@ public class ImageMaker
       try
       {
         out = new FileOutputStream(file);
+        setProgressMessage(null);
+        setProgressMessage(MessageManager.formatMessage(
+                "status.exporting_alignment_as_x_file", type.getName()));
         if (type == TYPE.SVG)
         {
           setupSVG(width, height, fileTitle);
@@ -132,6 +152,9 @@ public class ImageMaker
       } catch (Exception ex)
       {
         System.out.println("Error creating " + type.getName() + " file.");
+
+        setProgressMessage(MessageManager.formatMessage(
+                "info.error_creating_file", type.getName()));
       }
     }
   }
@@ -187,6 +210,8 @@ public class ImageMaker
 
       if (renderStyle == null || eps.cancelled)
       {
+        setProgressMessage(MessageManager.formatMessage(
+                "status.cancelled_image_export_operation", "EPS"));
         return;
       }
     }
@@ -206,6 +231,8 @@ public class ImageMaker
       pg.setAccurateTextMode(accurateText);
 
       graphics = pg;
+      setProgressMessage(MessageManager.formatMessage(
+              "status.export_complete", type.getName()));
     } catch (Exception ex)
     {
     }
@@ -218,6 +245,8 @@ public class ImageMaker
     Graphics2D ig2 = (Graphics2D) graphics;
     ig2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
             RenderingHints.VALUE_ANTIALIAS_ON);
+    setProgressMessage(MessageManager.formatMessage(
+            "status.export_complete", type.getName()));
 
   }
 
@@ -241,16 +270,20 @@ public class ImageMaker
 
       if (renderStyle == null || svgOption.cancelled)
       {
+        setProgressMessage(MessageManager.formatMessage(
+                "status.cancelled_image_export_operation", "SVG"));
         return;
       }
     }
 
-    if (renderStyle.equalsIgnoreCase("lineart"))
+    if (renderStyle.equalsIgnoreCase("Lineart"))
     {
       ig2.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
               SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
     }
 
+    setProgressMessage(MessageManager.formatMessage(
+            "status.export_complete", type.getName()));
     graphics = g2;
   }
 
@@ -280,6 +313,14 @@ public class ImageMaker
             "Encapsulated Postscript");
   }
 
+  private void setProgressMessage(String message)
+  {
+    if (pIndicator != null && !headless)
+    {
+      pIndicator.setProgressBar(message, pSessionId);
+    }
+  }
+
   static JalviewFileChooser getSVGChooser()
   {
     if (Jalview.isHeadlessMode())
diff --git a/src/jalview/util/LinkedIdentityHashSet.java b/src/jalview/util/LinkedIdentityHashSet.java
new file mode 100644
index 0000000..ec08a0b
--- /dev/null
+++ b/src/jalview/util/LinkedIdentityHashSet.java
@@ -0,0 +1,143 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.util;
+
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+
+/**
+ * Order preserving Set based on System.identityHashCode() for an object, which
+ * also supports Object->index lookup.
+ * 
+ * @author Jim Procter (2016) based on Evgeniy Dorofeev's response: via
+ *         https://stackoverflow.com/questions/17276658/linkedidentityhashset
+ * 
+ */
+public class LinkedIdentityHashSet<E> extends AbstractSet<E>
+{
+  LinkedHashMap<IdentityWrapper, IdentityWrapper> set = new LinkedHashMap<IdentityWrapper, IdentityWrapper>();
+
+  static class IdentityWrapper
+  {
+    Object obj;
+
+    public int p;
+
+    IdentityWrapper(Object obj, int p)
+    {
+      this.obj = obj;
+      this.p = p;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+      return this.obj == obj;
+    }
+
+    @Override
+    public int hashCode()
+    {
+      return System.identityHashCode(obj);
+    }
+  }
+
+  @Override
+  public boolean add(E e)
+  {
+    IdentityWrapper el = (new IdentityWrapper(e, set.size()));
+    // Map.putIfAbsent() from Java 8
+    // return set.putIfAbsent(el, el) == null;
+    return putIfAbsent(el, el) == null;
+  }
+
+  /**
+   * If the specified key is not already associated with a value (or is mapped
+   * to null) associates it with the given value and returns null, else returns
+   * the current value.
+   * 
+   * Method added for Java 7 (can remove for Java 8)
+   * 
+   * @param key
+   * @param value
+   * @return
+   * @see https
+   *      ://docs.oracle.com/javase/8/docs/api/java/util/Map.html#putIfAbsent
+   *      -K-V-
+   */
+  private IdentityWrapper putIfAbsent(IdentityWrapper key,
+          IdentityWrapper value)
+  {
+    IdentityWrapper v = set.get(key);
+    if (v == null)
+    {
+      v = set.put(key, value);
+    }
+    return v;
+  }
+
+  @Override
+  public Iterator<E> iterator()
+  {
+    return new Iterator<E>()
+    {
+      final Iterator<IdentityWrapper> se = set.keySet().iterator();
+
+      @Override
+      public boolean hasNext()
+      {
+        return se.hasNext();
+      }
+
+      @SuppressWarnings("unchecked")
+      @Override
+      public E next()
+      {
+        return (E) se.next().obj;
+      }
+
+      @Override
+      public void remove()
+      {
+        // Java 8 default behaviour
+        throw new UnsupportedOperationException();
+      }
+    };
+  }
+
+  @Override
+  public int size()
+  {
+    return set.size();
+  }
+
+  /**
+   * Lookup the index for e in the set
+   * 
+   * @param e
+   * @return position of e in the set when it was added.
+   */
+  public int indexOf(E e)
+  {
+    return set.get(e).p;
+  }
+}
diff --git a/src/jalview/util/MapList.java b/src/jalview/util/MapList.java
index a20a340..7c45398 100644
--- a/src/jalview/util/MapList.java
+++ b/src/jalview/util/MapList.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -41,12 +41,12 @@ public class MapList
   /*
    * Subregions (base 1) described as { [start1, end1], [start2, end2], ...}
    */
-  private List<int[]> fromShifts = new ArrayList<int[]>();
+  private List<int[]> fromShifts;
 
   /*
    * Same format as fromShifts, for the 'mapped to' sequence
    */
-  private List<int[]> toShifts = new ArrayList<int[]>();
+  private List<int[]> toShifts;
 
   /*
    * number of steps in fromShifts to one toRatio unit
@@ -73,14 +73,21 @@ public class MapList
   private int toHighest;
 
   /**
+   * Constructor
+   */
+  public MapList()
+  {
+    fromShifts = new ArrayList<int[]>();
+    toShifts = new ArrayList<int[]>();
+  }
+
+  /**
    * Two MapList objects are equal if they are the same object, or they both
    * have populated shift ranges and all values are the same.
    */
   @Override
   public boolean equals(Object o)
   {
-    // TODO should also override hashCode to ensure equal objects have equal
-    // hashcodes
     if (o == null || !(o instanceof MapList))
     {
       return false;
@@ -103,6 +110,19 @@ public class MapList
   }
 
   /**
+   * Returns a hashcode made from the fromRatio, toRatio, and from/to ranges
+   */
+  @Override
+  public int hashCode()
+  {
+    int hashCode = 31 * fromRatio;
+    hashCode = 31 * hashCode + toRatio;
+    hashCode = 31 * hashCode + fromShifts.toArray().hashCode();
+    hashCode = 31 * hashCode + toShifts.toArray().hashCode();
+    return hashCode;
+  }
+
+  /**
    * Returns the 'from' ranges as {[start1, end1], [start2, end2], ...}
    * 
    * @return
@@ -180,7 +200,9 @@ public class MapList
   }
 
   /**
-   * Constructor.
+   * Constructor given from and to ranges as [start1, end1, start2, end2,...].
+   * If any end is equal to the next start, the ranges will be merged. There is
+   * no validation check that the ranges do not overlap each other.
    * 
    * @param from
    *          contiguous regions as [start1, end1, start2, end2, ...]
@@ -193,25 +215,51 @@ public class MapList
    */
   public MapList(int from[], int to[], int fromRatio, int toRatio)
   {
+    this();
     this.fromRatio = fromRatio;
     this.toRatio = toRatio;
-    fromLowest = from[0];
-    fromHighest = from[1];
+    fromLowest = Integer.MAX_VALUE;
+    fromHighest = Integer.MIN_VALUE;
+    int added = 0;
+
     for (int i = 0; i < from.length; i += 2)
     {
-      fromLowest = Math.min(fromLowest, from[i]);
-      fromHighest = Math.max(fromHighest, from[i + 1]);
-
-      fromShifts.add(new int[] { from[i], from[i + 1] });
+      /*
+       * note lowest and highest values - bearing in mind the
+       * direction may be reversed
+       */
+      fromLowest = Math.min(fromLowest, Math.min(from[i], from[i + 1]));
+      fromHighest = Math.max(fromHighest, Math.max(from[i], from[i + 1]));
+      if (added > 0 && from[i] == fromShifts.get(added - 1)[1])
+      {
+        /*
+         * this range starts where the last ended - just extend it
+         */
+        fromShifts.get(added - 1)[1] = from[i + 1];
+      }
+      else
+      {
+        fromShifts.add(new int[] { from[i], from[i + 1] });
+        added++;
+      }
     }
 
-    toLowest = to[0];
-    toHighest = to[1];
+    toLowest = Integer.MAX_VALUE;
+    toHighest = Integer.MIN_VALUE;
+    added = 0;
     for (int i = 0; i < to.length; i += 2)
     {
-      toLowest = Math.min(toLowest, to[i]);
-      toHighest = Math.max(toHighest, to[i + 1]);
-      toShifts.add(new int[] { to[i], to[i + 1] });
+      toLowest = Math.min(toLowest, Math.min(to[i], to[i + 1]));
+      toHighest = Math.max(toHighest, Math.max(to[i], to[i + 1]));
+      if (added > 0 && to[i] == toShifts.get(added - 1)[1])
+      {
+        toShifts.get(added - 1)[1] = to[i + 1];
+      }
+      else
+      {
+        toShifts.add(new int[] { to[i], to[i + 1] });
+        added++;
+      }
     }
   }
 
@@ -222,6 +270,7 @@ public class MapList
    */
   public MapList(MapList map)
   {
+    this();
     // TODO not used - remove?
     this.fromLowest = map.fromLowest;
     this.fromHighest = map.fromHighest;
@@ -247,7 +296,8 @@ public class MapList
   }
 
   /**
-   * Constructor given ranges as lists of [start, end] positions
+   * Constructor given ranges as lists of [start, end] positions. There is no
+   * validation check that the ranges do not overlap each other.
    * 
    * @param fromRange
    * @param toRange
@@ -257,29 +307,110 @@ public class MapList
   public MapList(List<int[]> fromRange, List<int[]> toRange, int fromRatio,
           int toRatio)
   {
+    this();
+    fromRange = coalesceRanges(fromRange);
+    toRange = coalesceRanges(toRange);
     this.fromShifts = fromRange;
     this.toShifts = toRange;
     this.fromRatio = fromRatio;
     this.toRatio = toRatio;
 
     fromLowest = Integer.MAX_VALUE;
-    fromHighest = 0;
+    fromHighest = Integer.MIN_VALUE;
     for (int[] range : fromRange)
     {
-      fromLowest = Math.min(fromLowest, range[0]);
-      fromHighest = Math.max(fromHighest, range[1]);
+      fromLowest = Math.min(fromLowest, Math.min(range[0], range[1]));
+      fromHighest = Math.max(fromHighest, Math.max(range[0], range[1]));
     }
 
     toLowest = Integer.MAX_VALUE;
-    toHighest = 0;
+    toHighest = Integer.MIN_VALUE;
     for (int[] range : toRange)
     {
-      toLowest = Math.min(toLowest, range[0]);
-      toHighest = Math.max(toHighest, range[1]);
+      toLowest = Math.min(toLowest, Math.min(range[0], range[1]));
+      toHighest = Math.max(toHighest, Math.max(range[0], range[1]));
     }
   }
 
   /**
+   * Consolidates a list of ranges so that any contiguous ranges are merged.
+   * This assumes the ranges are already in start order (does not sort them).
+   * 
+   * @param ranges
+   * @return the same list (if unchanged), else a new merged list, leaving the
+   *         input list unchanged
+   */
+  public static List<int[]> coalesceRanges(final List<int[]> ranges)
+  {
+    if (ranges == null || ranges.size() < 2)
+    {
+      return ranges;
+    }
+
+    boolean changed = false;
+    List<int[]> merged = new ArrayList<int[]>();
+    int[] lastRange = ranges.get(0);
+    int lastDirection = lastRange[1] >= lastRange[0] ? 1 : -1;
+    lastRange = new int[] { lastRange[0], lastRange[1] };
+    merged.add(lastRange);
+    boolean first = true;
+
+    for (final int[] range : ranges)
+    {
+      if (first)
+      {
+        first = false;
+        continue;
+      }
+      if (range[0] == lastRange[0] && range[1] == lastRange[1])
+      {
+        // drop duplicate range
+        changed = true;
+        continue;
+      }
+
+      /*
+       * drop this range if it lies within the last range
+       */
+      if ((lastDirection == 1 && range[0] >= lastRange[0]
+              && range[0] <= lastRange[1] && range[1] >= lastRange[0] && range[1] <= lastRange[1])
+              || (lastDirection == -1 && range[0] <= lastRange[0]
+                      && range[0] >= lastRange[1]
+                      && range[1] <= lastRange[0] && range[1] >= lastRange[1]))
+      {
+        changed = true;
+        continue;
+      }
+
+      int direction = range[1] >= range[0] ? 1 : -1;
+
+      /*
+       * if next range is in the same direction as last and contiguous,
+       * just update the end position of the last range
+       */
+      boolean sameDirection = range[1] == range[0]
+              || direction == lastDirection;
+      boolean extending = range[0] == lastRange[1] + lastDirection;
+      boolean overlapping = (lastDirection == 1 && range[0] >= lastRange[0] && range[0] <= lastRange[1])
+              || (lastDirection == -1 && range[0] <= lastRange[0] && range[0] >= lastRange[1]);
+      if (sameDirection && (overlapping || extending))
+      {
+        lastRange[1] = range[1];
+        changed = true;
+      }
+      else
+      {
+        lastRange = new int[] { range[0], range[1] };
+        merged.add(lastRange);
+        // careful: merging [5, 5] after [7, 6] should keep negative direction
+        lastDirection = (range[1] == range[0]) ? lastDirection : direction;
+      }
+    }
+
+    return changed ? merged : ranges;
+  }
+
+  /**
    * get all mapped positions from 'from' to 'to'
    * 
    * @return int[][] { int[] { fromStart, fromFinish, toStart, toFinish }, int
@@ -849,13 +980,14 @@ public class MapList
   public String toString()
   {
     StringBuilder sb = new StringBuilder(64);
-    sb.append("From (").append(fromRatio).append(":").append(toRatio)
-            .append(") [");
+    sb.append("[");
     for (int[] shift : fromShifts)
     {
       sb.append(" ").append(Arrays.toString(shift));
     }
-    sb.append(" ] To [");
+    sb.append(" ] ");
+    sb.append(fromRatio).append(":").append(toRatio);
+    sb.append(" to [");
     for (int[] shift : toShifts)
     {
       sb.append(" ").append(Arrays.toString(shift));
@@ -863,4 +995,124 @@ public class MapList
     sb.append(" ]");
     return sb.toString();
   }
+
+  /**
+   * Extend this map list by adding the given map's ranges. There is no
+   * validation check that the ranges do not overlap existing ranges (or each
+   * other), but contiguous ranges are merged.
+   * 
+   * @param map
+   */
+  public void addMapList(MapList map)
+  {
+    if (this.equals(map))
+    {
+      return;
+    }
+    this.fromLowest = Math.min(fromLowest, map.fromLowest);
+    this.toLowest = Math.min(toLowest, map.toLowest);
+    this.fromHighest = Math.max(fromHighest, map.fromHighest);
+    this.toHighest = Math.max(toHighest, map.toHighest);
+
+    for (int[] range : map.getFromRanges())
+    {
+      addRange(range, fromShifts);
+    }
+    for (int[] range : map.getToRanges())
+    {
+      addRange(range, toShifts);
+    }
+  }
+
+  /**
+   * Adds the given range to a list of ranges. If the new range just extends
+   * existing ranges, the current endpoint is updated instead.
+   * 
+   * @param range
+   * @param addTo
+   */
+  static void addRange(int[] range, List<int[]> addTo)
+  {
+    /*
+     * list is empty - add to it!
+     */
+    if (addTo.size() == 0)
+    {
+      addTo.add(range);
+      return;
+    }
+
+    int[] last = addTo.get(addTo.size() - 1);
+    boolean lastForward = last[1] >= last[0];
+    boolean newForward = range[1] >= range[0];
+
+    /*
+     * contiguous range in the same direction - just update endpoint
+     */
+    if (lastForward == newForward && last[1] == range[0])
+    {
+      last[1] = range[1];
+      return;
+    }
+
+    /*
+     * next range starts at +1 in forward sense - update endpoint
+     */
+    if (lastForward && newForward && range[0] == last[1] + 1)
+    {
+      last[1] = range[1];
+      return;
+    }
+
+    /*
+     * next range starts at -1 in reverse sense - update endpoint
+     */
+    if (!lastForward && !newForward && range[0] == last[1] - 1)
+    {
+      last[1] = range[1];
+      return;
+    }
+
+    /*
+     * just add the new range
+     */
+    addTo.add(range);
+  }
+
+  /**
+   * Returns true if mapping is from forward strand, false if from reverse
+   * strand. Result is just based on the first 'from' range that is not a single
+   * position. Default is true unless proven to be false. Behaviour is not well
+   * defined if the mapping has a mixture of forward and reverse ranges.
+   * 
+   * @return
+   */
+  public boolean isFromForwardStrand()
+  {
+    boolean forwardStrand = true;
+    for (int[] range : getFromRanges())
+    {
+      if (range[1] > range[0])
+      {
+        break; // forward strand confirmed
+      }
+      else if (range[1] < range[0])
+      {
+        forwardStrand = false;
+        break; // reverse strand confirmed
+      }
+    }
+    return forwardStrand;
+  }
+
+  /**
+   * 
+   * @return true if from, or to is a three to 1 mapping
+   */
+  public boolean isTripletMap()
+  {
+    return (toRatio == 3 && fromRatio == 1)
+            || (fromRatio == 3 && toRatio == 1);
+  }
+
 }
diff --git a/src/jalview/util/MappingUtils.java b/src/jalview/util/MappingUtils.java
index 0933b97..26a9bba 100644
--- a/src/jalview/util/MappingUtils.java
+++ b/src/jalview/util/MappingUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -31,19 +31,19 @@ import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
-import jalview.datamodel.SearchResults.Match;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 
 import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * Helper methods for manipulations involving sequence mappings.
@@ -69,7 +69,7 @@ public final class MappingUtils
    */
   protected static void mapCutOrPaste(Edit edit, boolean undo,
           List<SequenceI> targetSeqs, EditCommand result,
-          Set<AlignedCodonFrame> mappings)
+          List<AlignedCodonFrame> mappings)
   {
     Action action = edit.getAction();
     if (undo)
@@ -93,7 +93,7 @@ public final class MappingUtils
    */
   public static EditCommand mapEditCommand(EditCommand command,
           boolean undo, final AlignmentI mapTo, char gapChar,
-          Set<AlignedCodonFrame> mappings)
+          List<AlignedCodonFrame> mappings)
   {
     /*
      * For now, only support mapping from protein edits to cDna
@@ -165,7 +165,7 @@ public final class MappingUtils
           Map<SequenceI, SequenceI> originalSequences,
           final List<SequenceI> targetSeqs,
           Map<SequenceI, SequenceI> targetCopies, char gapChar,
-          EditCommand result, Set<AlignedCodonFrame> mappings)
+          EditCommand result, List<AlignedCodonFrame> mappings)
   {
     Action action = edit.getAction();
 
@@ -195,7 +195,7 @@ public final class MappingUtils
       /*
        * Determine all mappings from this position to mapped sequences.
        */
-      SearchResults sr = buildSearchResults(seq, seqpos, mappings);
+      SearchResultsI sr = buildSearchResults(seq, seqpos, mappings);
 
       if (!sr.isEmpty())
       {
@@ -267,10 +267,10 @@ public final class MappingUtils
    * @param seqmappings
    * @return
    */
-  public static SearchResults buildSearchResults(SequenceI seq, int index,
-          Set<AlignedCodonFrame> seqmappings)
+  public static SearchResultsI buildSearchResults(SequenceI seq, int index,
+          List<AlignedCodonFrame> seqmappings)
   {
-    SearchResults results = new SearchResults();
+    SearchResultsI results = new SearchResults();
     addSearchResults(results, seq, index, seqmappings);
     return results;
   }
@@ -284,8 +284,8 @@ public final class MappingUtils
    * @param index
    * @param seqmappings
    */
-  public static void addSearchResults(SearchResults results, SequenceI seq,
-          int index, Set<AlignedCodonFrame> seqmappings)
+  public static void addSearchResults(SearchResultsI results, SequenceI seq,
+          int index, List<AlignedCodonFrame> seqmappings)
   {
     if (index >= seq.getStart() && index <= seq.getEnd())
     {
@@ -314,7 +314,7 @@ public final class MappingUtils
      */
     boolean targetIsNucleotide = mapTo.isNucleotide();
     AlignViewportI protein = targetIsNucleotide ? mapFrom : mapTo;
-    Set<AlignedCodonFrame> codonFrames = protein.getAlignment()
+    List<AlignedCodonFrame> codonFrames = protein.getAlignment()
             .getCodonFrames();
     /*
      * Copy group name, colours etc, but not sequences or sequence colour scheme
@@ -375,16 +375,17 @@ public final class MappingUtils
               /*
                * Found a sequence mapping. Locate the start/end mapped residues.
                */
-              SearchResults sr = buildSearchResults(selected,
-                      startResiduePos, Collections.singleton(acf));
-              for (Match m : sr.getResults())
+              List<AlignedCodonFrame> mapping = Arrays
+                      .asList(new AlignedCodonFrame[] { acf });
+              SearchResultsI sr = buildSearchResults(selected,
+                      startResiduePos, mapping);
+              for (SearchResultMatchI m : sr.getResults())
               {
                 mappedStartResidue = m.getStart();
                 mappedEndResidue = m.getEnd();
               }
-              sr = buildSearchResults(selected, endResiduePos,
-                      Collections.singleton(acf));
-              for (Match m : sr.getResults())
+              sr = buildSearchResults(selected, endResiduePos, mapping);
+              for (SearchResultMatchI m : sr.getResults())
               {
                 mappedStartResidue = Math.min(mappedStartResidue,
                         m.getStart());
@@ -428,7 +429,7 @@ public final class MappingUtils
    * @return
    */
   public static CommandI mapOrderCommand(OrderCommand command,
-          boolean undo, AlignmentI mapTo, Set<AlignedCodonFrame> mappings)
+          boolean undo, AlignmentI mapTo, List<AlignedCodonFrame> mappings)
   {
     SequenceI[] sortOrder = command.getSequenceOrder(undo);
     List<SequenceI> mappedOrder = new ArrayList<SequenceI>();
@@ -512,7 +513,7 @@ public final class MappingUtils
   {
     boolean targetIsNucleotide = mapTo.isNucleotide();
     AlignViewportI protein = targetIsNucleotide ? mapFrom : mapTo;
-    Set<AlignedCodonFrame> codonFrames = protein.getAlignment()
+    List<AlignedCodonFrame> codonFrames = protein.getAlignment()
             .getCodonFrames();
     ColumnSelection mappedColumns = new ColumnSelection();
 
@@ -523,79 +524,162 @@ public final class MappingUtils
 
     char fromGapChar = mapFrom.getAlignment().getGapCharacter();
 
-    // FIXME allow for hidden columns
-
     /*
      * For each mapped column, find the range of columns that residues in that
      * column map to.
      */
-    for (Object obj : colsel.getSelected())
+    List<SequenceI> fromSequences = mapFrom.getAlignment().getSequences();
+    List<SequenceI> toSequences = mapTo.getAlignment().getSequences();
+
+    for (Integer sel : colsel.getSelected())
+    {
+      mapColumn(sel.intValue(), codonFrames, mappedColumns, fromSequences,
+              toSequences, fromGapChar);
+    }
+
+    for (int[] hidden : colsel.getHiddenColumns())
+    {
+      mapHiddenColumns(hidden, codonFrames, mappedColumns, fromSequences,
+              toSequences, fromGapChar);
+    }
+    return mappedColumns;
+  }
+
+  /**
+   * Helper method that maps a [start, end] hidden column range to its mapped
+   * equivalent
+   * 
+   * @param hidden
+   * @param mappings
+   * @param mappedColumns
+   * @param fromSequences
+   * @param toSequences
+   * @param fromGapChar
+   */
+  protected static void mapHiddenColumns(int[] hidden,
+          List<AlignedCodonFrame> mappings, ColumnSelection mappedColumns,
+          List<SequenceI> fromSequences, List<SequenceI> toSequences,
+          char fromGapChar)
+  {
+    for (int col = hidden[0]; col <= hidden[1]; col++)
     {
-      int col = ((Integer) obj).intValue();
-      int mappedToMin = Integer.MAX_VALUE;
-      int mappedToMax = Integer.MIN_VALUE;
+      int[] mappedTo = findMappedColumns(col, mappings, fromSequences,
+              toSequences, fromGapChar);
 
       /*
-       * For each sequence in the 'from' alignment
+       * Add the range of hidden columns to the mapped selection (converting
+       * base 1 to base 0).
        */
-      for (SequenceI fromSeq : mapFrom.getAlignment().getSequences())
+      if (mappedTo != null)
       {
-        /*
-         * Ignore gaps (unmapped anyway)
-         */
-        if (fromSeq.getCharAt(col) == fromGapChar)
-        {
-          continue;
-        }
+        mappedColumns.hideColumns(mappedTo[0] - 1, mappedTo[1] - 1);
+      }
+    }
+  }
+
+  /**
+   * Helper method to map one column selection
+   * 
+   * @param col
+   *          the column number (base 0)
+   * @param mappings
+   *          the sequence mappings
+   * @param mappedColumns
+   *          the mapped column selections to add to
+   * @param fromSequences
+   * @param toSequences
+   * @param fromGapChar
+   */
+  protected static void mapColumn(int col,
+          List<AlignedCodonFrame> mappings, ColumnSelection mappedColumns,
+          List<SequenceI> fromSequences, List<SequenceI> toSequences,
+          char fromGapChar)
+  {
+    int[] mappedTo = findMappedColumns(col, mappings, fromSequences,
+            toSequences, fromGapChar);
+
+    /*
+     * Add the range of mapped columns to the mapped selection (converting
+     * base 1 to base 0). Note that this may include intron-only regions which
+     * lie between the start and end ranges of the selection.
+     */
+    if (mappedTo != null)
+    {
+      for (int i = mappedTo[0]; i <= mappedTo[1]; i++)
+      {
+        mappedColumns.addElement(i - 1);
+      }
+    }
+  }
+
+  /**
+   * Helper method to find the range of columns mapped to from one column.
+   * Returns the maximal range of columns mapped to from all sequences in the
+   * source column, or null if no mappings were found.
+   * 
+   * @param col
+   * @param mappings
+   * @param fromSequences
+   * @param toSequences
+   * @param fromGapChar
+   * @return
+   */
+  protected static int[] findMappedColumns(int col,
+          List<AlignedCodonFrame> mappings, List<SequenceI> fromSequences,
+          List<SequenceI> toSequences, char fromGapChar)
+  {
+    int[] mappedTo = new int[] { Integer.MAX_VALUE, Integer.MIN_VALUE };
+    boolean found = false;
+
+    /*
+     * For each sequence in the 'from' alignment
+     */
+    for (SequenceI fromSeq : fromSequences)
+    {
+      /*
+       * Ignore gaps (unmapped anyway)
+       */
+      if (fromSeq.getCharAt(col) == fromGapChar)
+      {
+        continue;
+      }
+
+      /*
+       * Get the residue position and find the mapped position.
+       */
+      int residuePos = fromSeq.findPosition(col);
+      SearchResultsI sr = buildSearchResults(fromSeq, residuePos, mappings);
+      for (SearchResultMatchI m : sr.getResults())
+      {
+        int mappedStartResidue = m.getStart();
+        int mappedEndResidue = m.getEnd();
+        SequenceI mappedSeq = m.getSequence();
 
         /*
-         * Get the residue position and find the mapped position.
+         * Locate the aligned sequence whose dataset is mappedSeq. TODO a
+         * datamodel that can do this efficiently.
          */
-        int residuePos = fromSeq.findPosition(col);
-        SearchResults sr = buildSearchResults(fromSeq, residuePos,
-                codonFrames);
-        for (Match m : sr.getResults())
+        for (SequenceI toSeq : toSequences)
         {
-          int mappedStartResidue = m.getStart();
-          int mappedEndResidue = m.getEnd();
-          SequenceI mappedSeq = m.getSequence();
-
-          /*
-           * Locate the aligned sequence whose dataset is mappedSeq. TODO a
-           * datamodel that can do this efficiently.
-           */
-          for (SequenceI toSeq : mapTo.getAlignment().getSequences())
+          if (toSeq.getDatasetSequence() == mappedSeq)
           {
-            if (toSeq.getDatasetSequence() == mappedSeq)
-            {
-              int mappedStartCol = toSeq.findIndex(mappedStartResidue);
-              int mappedEndCol = toSeq.findIndex(mappedEndResidue);
-              mappedToMin = Math.min(mappedToMin, mappedStartCol);
-              mappedToMax = Math.max(mappedToMax, mappedEndCol);
-              // System.out.println(fromSeq.getName() + " mapped to cols "
-              // + mappedStartCol + ":" + mappedEndCol);
-              break;
-              // note: remove break if we ever want to map one to many sequences
-            }
+            int mappedStartCol = toSeq.findIndex(mappedStartResidue);
+            int mappedEndCol = toSeq.findIndex(mappedEndResidue);
+            mappedTo[0] = Math.min(mappedTo[0], mappedStartCol);
+            mappedTo[1] = Math.max(mappedTo[1], mappedEndCol);
+            found = true;
+            break;
+            // note: remove break if we ever want to map one to many sequences
           }
         }
       }
-      /*
-       * Add the range of mapped columns to the mapped selection (converting
-       * base 1 to base 0). Note that this may include intron-only regions which
-       * lie between the start and end ranges of the selection.
-       */
-      for (int i = mappedToMin; i <= mappedToMax; i++)
-      {
-        mappedColumns.addElement(i - 1);
-      }
     }
-    return mappedColumns;
+    return found ? mappedTo : null;
   }
 
   /**
-   * Returns the mapped codon for a given aligned sequence column position (base
-   * 0).
+   * Returns the mapped codon or codons for a given aligned sequence column
+   * position (base 0).
    * 
    * @param seq
    *          an aligned peptide sequence
@@ -603,26 +687,32 @@ public final class MappingUtils
    *          an aligned column position (base 0)
    * @param mappings
    *          a set of codon mappings
-   * @return the bases of the mapped codon in the cDNA dataset sequence, or null
-   *         if not found
+   * @return the bases of the mapped codon(s) in the cDNA dataset sequence(s),
+   *         or an empty list if none found
    */
-  public static char[] findCodonFor(SequenceI seq, int col,
-          Set<AlignedCodonFrame> mappings)
+  public static List<char[]> findCodonsFor(SequenceI seq, int col,
+          List<AlignedCodonFrame> mappings)
   {
+    List<char[]> result = new ArrayList<char[]>();
     int dsPos = seq.findPosition(col);
     for (AlignedCodonFrame mapping : mappings)
     {
       if (mapping.involvesSequence(seq))
       {
-        return mapping.getMappedCodon(seq.getDatasetSequence(), dsPos);
+        List<char[]> codons = mapping.getMappedCodons(
+                seq.getDatasetSequence(), dsPos);
+        if (codons != null)
+        {
+          result.addAll(codons);
+        }
       }
     }
-    return null;
+    return result;
   }
 
   /**
-   * Converts a series of [start, end] ranges into an array of individual
-   * positions.
+   * Converts a series of [start, end] range pairs into an array of individual
+   * positions. This also caters for 'reverse strand' (start > end) cases.
    * 
    * @param ranges
    * @return
@@ -635,17 +725,21 @@ public final class MappingUtils
     int count = 0;
     for (int i = 0; i < ranges.length - 1; i += 2)
     {
-      count += ranges[i + 1] - ranges[i] + 1;
+      count += Math.abs(ranges[i + 1] - ranges[i]) + 1;
     }
 
     int[] result = new int[count];
     int k = 0;
     for (int i = 0; i < ranges.length - 1; i += 2)
     {
-      for (int j = ranges[i]; j <= ranges[i + 1]; j++)
+      int from = ranges[i];
+      final int to = ranges[i + 1];
+      int step = from <= to ? 1 : -1;
+      do
       {
-        result[k++] = j;
-      }
+        result[k++] = from;
+        from += step;
+      } while (from != to + step);
     }
     return result;
   }
@@ -659,7 +753,24 @@ public final class MappingUtils
    * @return
    */
   public static List<AlignedCodonFrame> findMappingsForSequence(
-          SequenceI sequence, Set<AlignedCodonFrame> mappings)
+          SequenceI sequence, List<AlignedCodonFrame> mappings)
+  {
+    return findMappingsForSequenceAndOthers(sequence, mappings, null);
+  }
+
+  /**
+   * Returns a list of any mappings that are from or to the given (aligned or
+   * dataset) sequence, optionally limited to mappings involving one of a given
+   * list of sequences.
+   * 
+   * @param sequence
+   * @param mappings
+   * @param filterList
+   * @return
+   */
+  public static List<AlignedCodonFrame> findMappingsForSequenceAndOthers(
+          SequenceI sequence, List<AlignedCodonFrame> mappings,
+          List<SequenceI> filterList)
   {
     List<AlignedCodonFrame> result = new ArrayList<AlignedCodonFrame>();
     if (sequence == null || mappings == null)
@@ -670,9 +781,157 @@ public final class MappingUtils
     {
       if (mapping.involvesSequence(sequence))
       {
-        result.add(mapping);
+        if (filterList != null)
+        {
+          for (SequenceI otherseq : filterList)
+          {
+            SequenceI otherDataset = otherseq.getDatasetSequence();
+            if (otherseq == sequence
+                    || otherseq == sequence.getDatasetSequence()
+                    || (otherDataset != null && (otherDataset == sequence || otherDataset == sequence
+                            .getDatasetSequence())))
+            {
+              // skip sequences in subset which directly relate to sequence
+              continue;
+            }
+            if (mapping.involvesSequence(otherseq))
+            {
+              // selected a mapping contained in subselect alignment
+              result.add(mapping);
+              break;
+            }
+          }
+        }
+        else
+        {
+          result.add(mapping);
+        }
       }
     }
     return result;
   }
+
+  /**
+   * Returns the total length of the supplied ranges, which may be as single
+   * [start, end] or multiple [start, end, start, end ...]
+   * 
+   * @param ranges
+   * @return
+   */
+  public static int getLength(List<int[]> ranges)
+  {
+    if (ranges == null)
+    {
+      return 0;
+    }
+    int length = 0;
+    for (int[] range : ranges)
+    {
+      if (range.length % 2 != 0)
+      {
+        System.err.println("Error unbalance start/end ranges: "
+                + ranges.toString());
+        return 0;
+      }
+      for (int i = 0; i < range.length - 1; i += 2)
+      {
+        length += Math.abs(range[i + 1] - range[i]) + 1;
+      }
+    }
+    return length;
+  }
+
+  /**
+   * Answers true if any range includes the given value
+   * 
+   * @param ranges
+   * @param value
+   * @return
+   */
+  public static boolean contains(List<int[]> ranges, int value)
+  {
+    if (ranges == null)
+    {
+      return false;
+    }
+    for (int[] range : ranges)
+    {
+      if (range[1] >= range[0] && value >= range[0] && value <= range[1])
+      {
+        /*
+         * value within ascending range
+         */
+        return true;
+      }
+      if (range[1] < range[0] && value <= range[0] && value >= range[1])
+      {
+        /*
+         * value within descending range
+         */
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Removes a specified number of positions from the start of a ranges list.
+   * For example, could be used to adjust cds ranges to allow for an incomplete
+   * start codon. Subranges are removed completely, or their start positions
+   * adjusted, until the required number of positions has been removed from the
+   * range. Reverse strand ranges are supported. The input array is not
+   * modified.
+   * 
+   * @param removeCount
+   * @param ranges
+   *          an array of [start, end, start, end...] positions
+   * @return a new array with the first removeCount positions removed
+   */
+  public static int[] removeStartPositions(int removeCount,
+          final int[] ranges)
+  {
+    if (removeCount <= 0)
+    {
+      return ranges;
+    }
+
+    int[] copy = Arrays.copyOf(ranges, ranges.length);
+    int sxpos = -1;
+    int cdspos = 0;
+    for (int x = 0; x < copy.length && sxpos == -1; x += 2)
+    {
+      cdspos += Math.abs(copy[x + 1] - copy[x]) + 1;
+      if (removeCount < cdspos)
+      {
+        /*
+         * we have removed enough, time to finish
+         */
+        sxpos = x;
+
+        /*
+         * increment start of first exon, or decrement if reverse strand
+         */
+        if (copy[x] <= copy[x + 1])
+        {
+          copy[x] = copy[x + 1] - cdspos + removeCount + 1;
+        }
+        else
+        {
+          copy[x] = copy[x + 1] + cdspos - removeCount - 1;
+        }
+        break;
+      }
+    }
+
+    if (sxpos > 0)
+    {
+      /*
+       * we dropped at least one entire sub-range - compact the array
+       */
+      int[] nxon = new int[copy.length - sxpos];
+      System.arraycopy(copy, sxpos, nxon, 0, copy.length - sxpos);
+      return nxon;
+    }
+    return copy;
+  }
 }
diff --git a/src/jalview/util/MessageManager.java b/src/jalview/util/MessageManager.java
index 51f23bb..096f056 100644
--- a/src/jalview/util/MessageManager.java
+++ b/src/jalview/util/MessageManager.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/util/ParseHtmlBodyAndLinks.java b/src/jalview/util/ParseHtmlBodyAndLinks.java
index fef31b2..b2fe944 100644
--- a/src/jalview/util/ParseHtmlBodyAndLinks.java
+++ b/src/jalview/util/ParseHtmlBodyAndLinks.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,6 +21,7 @@
 package jalview.util;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.StringTokenizer;
 import java.util.regex.Pattern;
 
@@ -52,9 +53,9 @@ public class ParseHtmlBodyAndLinks
     return htmlContent;
   }
 
-  ArrayList<String> links = new ArrayList<String>();
+  List<String> links = new ArrayList<String>();
 
-  StringBuffer sb = new StringBuffer();
+  String content;
 
   /**
    * result of parsing description - with or without HTML tags
@@ -64,7 +65,7 @@ public class ParseHtmlBodyAndLinks
   public String getContent()
   {
 
-    return sb.toString();
+    return content;
   }
 
   /**
@@ -72,12 +73,19 @@ public class ParseHtmlBodyAndLinks
    * 
    * @return
    */
-  public ArrayList<String> getLinks()
+  public List<String> getLinks()
   {
     return links;
   }
 
   /**
+   * Parses the given html and
+   * <ul>
+   * <li>extracts any 'href' links to a list of "displayName|url" strings,
+   * retrievable by #getLinks</li>
+   * <li>extracts the remaining text (with %LINK% placeholders replacing hrefs),
+   * retrievable by #getContent</li>
+   * </ul>
    * 
    * @param description
    *          - html or text content to be parsed
@@ -89,6 +97,7 @@ public class ParseHtmlBodyAndLinks
   public ParseHtmlBodyAndLinks(String description, boolean removeHTML,
           String newline)
   {
+    StringBuilder sb = new StringBuilder(description.length());
     if (description == null || description.length() == 0)
     {
       htmlContent = false;
@@ -105,7 +114,7 @@ public class ParseHtmlBodyAndLinks
     String tag = null;
     while (st.hasMoreElements())
     {
-      token = st.nextToken("&>");
+      token = st.nextToken(">");
       if (token.equalsIgnoreCase("html") || token.startsWith("/"))
       {
         continue;
@@ -135,18 +144,6 @@ public class ParseHtmlBodyAndLinks
       {
         sb.append(newline);
       }
-      else if (token.startsWith("lt;"))
-      {
-        sb.append("<" + token.substring(3));
-      }
-      else if (token.startsWith("gt;"))
-      {
-        sb.append(">" + token.substring(3));
-      }
-      else if (token.startsWith("amp;"))
-      {
-        sb.append("&" + token.substring(4));
-      }
       else
       {
         sb.append(token);
@@ -156,11 +153,18 @@ public class ParseHtmlBodyAndLinks
     {
       // instead of parsing the html into plaintext
       // clean the description ready for embedding in html
-      sb = new StringBuffer(LEFT_ANGLE_BRACKET_PATTERN.matcher(description)
-              .replaceAll("<"));
-
+      sb = new StringBuilder(LEFT_ANGLE_BRACKET_PATTERN
+              .matcher(description).replaceAll("<"));
     }
+    content = translateEntities(sb.toString());
+  }
 
+  private String translateEntities(String s)
+  {
+    s = s.replaceAll("&", "&");
+    s = s.replaceAll("<", "<");
+    s = s.replaceAll(">", ">");
+    return s;
   }
 
   /**
@@ -171,7 +175,7 @@ public class ParseHtmlBodyAndLinks
    */
   public String getNonHtmlContent()
   {
-    return isHtmlContent() ? sb.toString() : orig;
+    return isHtmlContent() ? content : orig;
   }
 
 }
diff --git a/src/jalview/util/Platform.java b/src/jalview/util/Platform.java
index ef860d4..c09fd22 100644
--- a/src/jalview/util/Platform.java
+++ b/src/jalview/util/Platform.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,9 @@
  */
 package jalview.util;
 
+import java.awt.Toolkit;
+import java.awt.event.MouseEvent;
+
 /**
  * System platform information used by Applet and Application
  * 
@@ -27,6 +30,10 @@ package jalview.util;
  */
 public class Platform
 {
+  private static Boolean isAMac = null;
+
+  private static Boolean isHeadless = null;
+
   /**
    * sorry folks - Macs really are different
    * 
@@ -34,15 +41,21 @@ public class Platform
    */
   public static boolean isAMac()
   {
-    return java.lang.System.getProperty("os.name").indexOf("Mac") > -1;
+    if (isAMac == null)
+    {
+      isAMac = System.getProperty("os.name").indexOf("Mac") > -1;
+    }
+    return isAMac.booleanValue();
 
   }
 
   public static boolean isHeadless()
   {
-    String hdls = java.lang.System.getProperty("java.awt.headless");
-
-    return hdls != null && hdls.equals("true");
+    if (isHeadless == null)
+    {
+      isHeadless = "true".equals(System.getProperty("java.awt.headless"));
+    }
+    return isHeadless;
   }
 
   /**
@@ -74,4 +87,44 @@ public class Platform
     f.append(file.substring(lastp));
     return f.toString();
   }
+
+  /**
+   * Answers true if the mouse event has Meta-down (Command key on Mac) or
+   * Ctrl-down (on other o/s). Note this answers _false_ if the Ctrl key is
+   * pressed instead of the Meta/Cmd key on Mac. To test for Ctrl-click on Mac,
+   * you can use e.isPopupTrigger().
+   * 
+   * @param e
+   * @return
+   */
+  public static boolean isControlDown(MouseEvent e)
+  {
+    boolean aMac = isAMac();
+    return isControlDown(e, aMac);
+  }
+
+  /**
+   * Overloaded version of method (to allow unit testing)
+   * 
+   * @param e
+   * @param aMac
+   * @return
+   */
+  protected static boolean isControlDown(MouseEvent e, boolean aMac)
+  {
+    if (aMac)
+    {
+      /*
+       * answer false for right mouse button
+       */
+      if (e.isPopupTrigger())
+      {
+        return false;
+      }
+      return (Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() & e
+              .getModifiers()) != 0;
+      // could we use e.isMetaDown() here?
+    }
+    return e.isControlDown();
+  }
 }
diff --git a/src/jalview/util/QuickSort.java b/src/jalview/util/QuickSort.java
index 19ef730..4593ff9 100644
--- a/src/jalview/util/QuickSort.java
+++ b/src/jalview/util/QuickSort.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -32,40 +32,106 @@ import java.util.Comparator;
  */
 public class QuickSort
 {
+  /**
+   * A comparator that compares two integers by comparing their respective
+   * indexed values in an array of floats
+   */
   static class FloatComparator implements Comparator<Integer>
   {
-
     private final float[] values;
 
-    FloatComparator(float[] v)
+    private boolean ascending;
+
+    FloatComparator(float[] v, boolean asc)
     {
       values = v;
+      ascending = asc;
     }
 
     @Override
     public int compare(Integer o1, Integer o2)
     {
-      return Float.compare(values[o1], values[o2]);
+      return ascending ? Float.compare(values[o1], values[o2]) : Float
+              .compare(values[o2], values[o1]);
+    }
+  }
+
+  /**
+   * A comparator that compares two integers by comparing their respective
+   * indexed values in an array of doubles
+   */
+  static class DoubleComparator implements Comparator<Integer>
+  {
+    private final double[] values;
+
+    private boolean ascending;
+
+    DoubleComparator(double[] v, boolean asc)
+    {
+      values = v;
+      ascending = asc;
     }
 
+    @Override
+    public int compare(Integer o1, Integer o2)
+    {
+      if (ascending)
+      {
+        return Double.compare(values[o1], values[o2]);
+      }
+      else
+      {
+        return Double.compare(values[o2], values[o1]);
+      }
+    }
   }
 
+  /**
+   * A comparator that compares two integers by comparing their respective
+   * indexed values in an array of ints
+   */
   static class IntComparator implements Comparator<Integer>
   {
-
     private final int[] values;
 
-    IntComparator(int[] v)
+    private boolean ascending;
+
+    IntComparator(int[] v, boolean asc)
     {
       values = v;
+      ascending = asc;
     }
 
     @Override
     public int compare(Integer o1, Integer o2)
     {
-      return Integer.compare(values[o1], values[o2]);
+      return ascending ? Integer.compare(values[o1], values[o2]) : Integer
+              .compare(values[o2], values[o1]);
+    }
+  }
+
+  /**
+   * A comparator that compares two integers by comparing their respective
+   * indexed values in an array of comparable objects.
+   */
+  static class ExternalComparator implements Comparator<Integer>
+  {
+    private final Comparable[] values;
+
+    private boolean ascending;
+
+    ExternalComparator(Comparable[] v, boolean asc)
+    {
+      values = v;
+      ascending = asc;
     }
 
+    @Override
+    public int compare(Integer o1, Integer o2)
+    {
+      return ascending ? values[o1].compareTo(values[o2]) : values[o2]
+              .compareTo(values[o1]);
+    }
   }
 
   /**
@@ -106,7 +172,7 @@ public class QuickSort
 
   /**
    * Sorts both arrays with respect to descending order of the items in the
-   * first array.
+   * first array. The sorting is case-sensitive.
    * 
    * @param arr
    * @param s
@@ -340,8 +406,10 @@ public class QuickSort
   }
 
   /**
-   * Sorts both arrays to give ascending order in the first array, by first
-   * partitioning into zero and non-zero values before sorting the latter.
+   * Sorts both arrays to give ascending order by the first array, by first
+   * partitioning into zero and non-zero values before sorting the latter. This
+   * is faster than a direct call to charSortByFloat in the case where most of
+   * the array to be sorted is zero.
    * 
    * @param arr
    * @param s
@@ -349,71 +417,87 @@ public class QuickSort
   public static void sort(float[] arr, char[] s)
   {
     /*
-     * Sort all zero values to the front
+     * Move all zero values to the front, non-zero to the back, while counting
+     * negative values
      */
     float[] f1 = new float[arr.length];
     char[] s1 = new char[s.length];
-    int nextZeroValue = 0;
+    int negativeCount = 0;
+    int zerosCount = 0;
     int nextNonZeroValue = arr.length - 1;
     for (int i = 0; i < arr.length; i++)
     {
       float val = arr[i];
-      if (val > 0f)
+      if (val != 0f)
       {
         f1[nextNonZeroValue] = val;
         s1[nextNonZeroValue] = s[i];
         nextNonZeroValue--;
+        if (val < 0f)
+        {
+          negativeCount++;
+        }
       }
       else
       {
-        f1[nextZeroValue] = val;
-        s1[nextZeroValue] = s[i];
-        nextZeroValue++;
+        f1[zerosCount] = val;
+        s1[zerosCount] = s[i];
+        zerosCount++;
       }
     }
+    int positiveCount = arr.length - zerosCount - negativeCount;
 
-    /*
-     * Copy zero values back to original arrays
-     */
-    System.arraycopy(f1, 0, arr, 0, nextZeroValue);
-    System.arraycopy(s1, 0, s, 0, nextZeroValue);
-
-    if (nextZeroValue == arr.length)
+    if (zerosCount == arr.length)
     {
       return; // all zero
     }
+
+    /*
+     * sort the non-zero values
+     */
+    float[] nonZeroFloats = Arrays.copyOfRange(f1, zerosCount, f1.length);
+    char[] nonZeroChars = Arrays.copyOfRange(s1, zerosCount, s1.length);
+    charSortByFloat(nonZeroFloats, nonZeroChars, true);
+
     /*
-     * Sort the non-zero values
+     * Backfill zero values to original arrays, after the space reserved for
+     * negatives
      */
-    float[] nonZeroFloats = Arrays
-            .copyOfRange(f1, nextZeroValue, f1.length);
-    char[] nonZeroChars = Arrays.copyOfRange(s1, nextZeroValue, s1.length);
-    externalSort(nonZeroFloats, nonZeroChars);
-    // sort(nonZeroFloats, 0, nonZeroFloats.length - 1, nonZeroChars);
+    System.arraycopy(f1, 0, arr, negativeCount, zerosCount);
+    System.arraycopy(s1, 0, s, negativeCount, zerosCount);
 
     /*
-     * Assemble sorted non-zero results
+     * Copy sorted negative values to the front of arr, s
      */
-    System.arraycopy(nonZeroFloats, 0, arr, nextZeroValue,
-            nonZeroFloats.length);
-    System.arraycopy(nonZeroChars, 0, s, nextZeroValue, nonZeroChars.length);
+    System.arraycopy(nonZeroFloats, 0, arr, 0, negativeCount);
+    System.arraycopy(nonZeroChars, 0, s, 0, negativeCount);
+
+    /*
+     * Copy sorted positive values after the negatives and zeros
+     */
+    System.arraycopy(nonZeroFloats, negativeCount, arr, negativeCount
+            + zerosCount, positiveCount);
+    System.arraycopy(nonZeroChars, negativeCount, s, negativeCount
+            + zerosCount, positiveCount);
   }
 
   /**
-   * Sort by making an array of indices, and sorting it using a comparator that
-   * refers to the float values.
+   * Sorts arrays of float and char by the float values, by making an array of
+   * indices, and sorting it using a comparator that refers to the float values.
    * 
    * @see http
    *      ://stackoverflow.com/questions/4859261/get-the-indices-of-an-array-
    *      after-sorting
    * @param arr
    * @param s
+   * @param ascending
    */
-  protected static void externalSort(float[] arr, char[] s)
+  public static void charSortByFloat(float[] arr, char[] s,
+          boolean ascending)
   {
     final int length = arr.length;
     Integer[] indices = makeIndexArray(length);
-    Arrays.sort(indices, new FloatComparator(arr));
+    Arrays.sort(indices, new FloatComparator(arr, ascending));
 
     /*
      * Copy the array values as per the sorted indices
@@ -462,77 +546,95 @@ public class QuickSort
 
   /**
    * Sorts both arrays to give ascending order in the first array, by first
-   * partitioning into zero and non-zero values before sorting the latter.
+   * partitioning into zero and non-zero values before sorting the latter. This
+   * is faster than a direct call to charSortByInt in the case where most of the
+   * array to be sorted is zero.
    * 
    * @param arr
    * @param s
    */
   public static void sort(int[] arr, char[] s)
-  {
-    /*
-     * Sort all zero values to the front
+  { /*
+     * Move all zero values to the front, non-zero to the back, while counting
+     * negative values
      */
     int[] f1 = new int[arr.length];
     char[] s1 = new char[s.length];
-    int nextZeroValue = 0;
+    int negativeCount = 0;
+    int zerosCount = 0;
     int nextNonZeroValue = arr.length - 1;
     for (int i = 0; i < arr.length; i++)
     {
       int val = arr[i];
-      if (val > 0f)
+      if (val != 0f)
       {
         f1[nextNonZeroValue] = val;
         s1[nextNonZeroValue] = s[i];
         nextNonZeroValue--;
+        if (val < 0)
+        {
+          negativeCount++;
+        }
       }
       else
       {
-        f1[nextZeroValue] = val;
-        s1[nextZeroValue] = s[i];
-        nextZeroValue++;
+        f1[zerosCount] = val;
+        s1[zerosCount] = s[i];
+        zerosCount++;
       }
     }
+    int positiveCount = arr.length - zerosCount - negativeCount;
 
-    /*
-     * Copy zero values back to original arrays
-     */
-    System.arraycopy(f1, 0, arr, 0, nextZeroValue);
-    System.arraycopy(s1, 0, s, 0, nextZeroValue);
-
-    if (nextZeroValue == arr.length)
+    if (zerosCount == arr.length)
     {
       return; // all zero
     }
+
     /*
-     * Sort the non-zero values
+     * sort the non-zero values
      */
-    int[] nonZeroInts = Arrays.copyOfRange(f1, nextZeroValue, f1.length);
-    char[] nonZeroChars = Arrays.copyOfRange(s1, nextZeroValue, s1.length);
-    externalSort(nonZeroInts, nonZeroChars);
-    // sort(nonZeroFloats, 0, nonZeroFloats.length - 1, nonZeroChars);
+    int[] nonZeroInts = Arrays.copyOfRange(f1, zerosCount, f1.length);
+    char[] nonZeroChars = Arrays.copyOfRange(s1, zerosCount, s1.length);
+    charSortByInt(nonZeroInts, nonZeroChars, true);
 
     /*
-     * Assemble sorted non-zero results
+     * Backfill zero values to original arrays, after the space reserved for
+     * negatives
      */
-    System.arraycopy(nonZeroInts, 0, arr, nextZeroValue, nonZeroInts.length);
-    System.arraycopy(nonZeroChars, 0, s, nextZeroValue, nonZeroChars.length);
+    System.arraycopy(f1, 0, arr, negativeCount, zerosCount);
+    System.arraycopy(s1, 0, s, negativeCount, zerosCount);
+
+    /*
+     * Copy sorted negative values to the front of arr, s
+     */
+    System.arraycopy(nonZeroInts, 0, arr, 0, negativeCount);
+    System.arraycopy(nonZeroChars, 0, s, 0, negativeCount);
+
+    /*
+     * Copy sorted positive values after the negatives and zeros
+     */
+    System.arraycopy(nonZeroInts, negativeCount, arr, negativeCount
+            + zerosCount, positiveCount);
+    System.arraycopy(nonZeroChars, negativeCount, s, negativeCount
+            + zerosCount, positiveCount);
   }
 
   /**
-   * Sort by making an array of indices, and sorting it using a comparator that
-   * refers to the float values.
+   * Sorts arrays of int and char, by making an array of indices, and sorting it
+   * using a comparator that refers to the int values.
    * 
    * @see http
    *      ://stackoverflow.com/questions/4859261/get-the-indices-of-an-array-
    *      after-sorting
    * @param arr
    * @param s
+   * @param ascending
    */
-  protected static void externalSort(int[] arr, char[] s)
+  public static void charSortByInt(int[] arr, char[] s, boolean ascending)
   {
     final int length = arr.length;
     Integer[] indices = makeIndexArray(length);
-    Arrays.sort(indices, new IntComparator(arr));
+    Arrays.sort(indices, new IntComparator(arr, ascending));
 
     /*
      * Copy the array values as per the sorted indices
@@ -551,4 +653,112 @@ public class QuickSort
     System.arraycopy(sortedInts, 0, arr, 0, length);
     System.arraycopy(sortedChars, 0, s, 0, s.length);
   }
+
+  /**
+   * Sorts arrays of int and Object, by making an array of indices, and sorting
+   * it using a comparator that refers to the int values.
+   * 
+   * @see http
+   *      ://stackoverflow.com/questions/4859261/get-the-indices-of-an-array-
+   *      after-sorting
+   * @param arr
+   * @param s
+   * @param ascending
+   */
+  public static void sortByInt(int[] arr, Object[] s, boolean ascending)
+  {
+    final int length = arr.length;
+    Integer[] indices = makeIndexArray(length);
+    Arrays.sort(indices, new IntComparator(arr, ascending));
+
+    /*
+     * Copy the array values as per the sorted indices
+     */
+    int[] sortedInts = new int[length];
+    Object[] sortedObjects = new Object[s.length];
+    for (int i = 0; i < length; i++)
+    {
+      sortedInts[i] = arr[indices[i]];
+      sortedObjects[i] = s[indices[i]];
+    }
+
+    /*
+     * And copy the sorted values back into the arrays
+     */
+    System.arraycopy(sortedInts, 0, arr, 0, length);
+    System.arraycopy(sortedObjects, 0, s, 0, s.length);
+  }
+
+  /**
+   * Sorts arrays of String and Object, by making an array of indices, and
+   * sorting it using a comparator that refers to the String values. Both arrays
+   * are sorted by case-sensitive order of the string array values.
+   * 
+   * @see http
+   *      ://stackoverflow.com/questions/4859261/get-the-indices-of-an-array-
+   *      after-sorting
+   * @param arr
+   * @param s
+   * @param ascending
+   */
+  public static void sortByString(String[] arr, Object[] s,
+          boolean ascending)
+  {
+    final int length = arr.length;
+    Integer[] indices = makeIndexArray(length);
+    Arrays.sort(indices, new ExternalComparator(arr, ascending));
+
+    /*
+     * Copy the array values as per the sorted indices
+     */
+    String[] sortedStrings = new String[length];
+    Object[] sortedObjects = new Object[s.length];
+    for (int i = 0; i < length; i++)
+    {
+      sortedStrings[i] = arr[indices[i]];
+      sortedObjects[i] = s[indices[i]];
+    }
+
+    /*
+     * And copy the sorted values back into the arrays
+     */
+    System.arraycopy(sortedStrings, 0, arr, 0, length);
+    System.arraycopy(sortedObjects, 0, s, 0, s.length);
+  }
+
+  /**
+   * Sorts arrays of double and Object, by making an array of indices, and
+   * sorting it using a comparator that refers to the double values.
+   * 
+   * @see http
+   *      ://stackoverflow.com/questions/4859261/get-the-indices-of-an-array-
+   *      after-sorting
+   * @param arr
+   * @param s
+   * @param ascending
+   */
+  public static void sortByDouble(double[] arr, Object[] s,
+          boolean ascending)
+  {
+    final int length = arr.length;
+    Integer[] indices = makeIndexArray(length);
+    Arrays.sort(indices, new DoubleComparator(arr, ascending));
+
+    /*
+     * Copy the array values as per the sorted indices
+     */
+    double[] sortedDoubles = new double[length];
+    Object[] sortedObjects = new Object[s.length];
+    for (int i = 0; i < length; i++)
+    {
+      sortedDoubles[i] = arr[indices[i]];
+      sortedObjects[i] = s[indices[i]];
+    }
+
+    /*
+     * And copy the sorted values back into the arrays
+     */
+    System.arraycopy(sortedDoubles, 0, arr, 0, length);
+    System.arraycopy(sortedObjects, 0, s, 0, s.length);
+  }
 }
diff --git a/src/jalview/util/ReverseListIterator.java b/src/jalview/util/ReverseListIterator.java
index 1145b66..ccaaf5b 100644
--- a/src/jalview/util/ReverseListIterator.java
+++ b/src/jalview/util/ReverseListIterator.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/util/ShiftList.java b/src/jalview/util/ShiftList.java
index 860a700..0500de1 100644
--- a/src/jalview/util/ShiftList.java
+++ b/src/jalview/util/ShiftList.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/util/SparseCount.java b/src/jalview/util/SparseCount.java
new file mode 100644
index 0000000..b5e4899
--- /dev/null
+++ b/src/jalview/util/SparseCount.java
@@ -0,0 +1,174 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.util;
+
+import jalview.ext.android.SparseIntArray;
+import jalview.ext.android.SparseShortArray;
+
+/**
+ * A class to count occurrences of characters with minimal memory footprint.
+ * Sparse arrays of short values are used to hold the counts, with automatic
+ * promotion to arrays of int if any count exceeds the maximum value for a
+ * short.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class SparseCount
+{
+  private static final int DEFAULT_PROFILE_SIZE = 2;
+
+  /*
+   * array of keys (chars) and values (counts)
+   * held either as shorts or (if shorts overflow) as ints 
+   */
+  private SparseShortArray shortProfile;
+
+  private SparseIntArray intProfile;
+
+  /*
+   * flag is set true after short overflow occurs
+   */
+  private boolean useInts;
+
+  /**
+   * Constructor which initially creates a new sparse array of short values to
+   * hold counts.
+   * 
+   * @param profileSize
+   */
+  public SparseCount(int profileSize)
+  {
+    this.shortProfile = new SparseShortArray(profileSize);
+  }
+
+  /**
+   * Constructor which allocates an initial count array for only two distinct
+   * values (the array will grow if needed)
+   */
+  public SparseCount()
+  {
+    this(DEFAULT_PROFILE_SIZE);
+  }
+
+  /**
+   * Adds the given value for the given key (or sets the initial value), and
+   * returns the new value
+   * 
+   * @param key
+   * @param value
+   */
+  public int add(int key, int value)
+  {
+    int newValue = 0;
+    if (useInts)
+    {
+      newValue = intProfile.add(key, value);
+    }
+    else
+    {
+      try {
+        newValue = shortProfile.add(key, value);
+      } catch (ArithmeticException e) {
+        handleOverflow();
+        newValue = intProfile.add(key, value);
+      }
+    }
+    return newValue;
+  }
+
+  /**
+   * Switch from counting shorts to counting ints
+   */
+  synchronized void handleOverflow()
+  {
+    int size = shortProfile.size();
+    intProfile = new SparseIntArray(size);
+    for (int i = 0; i < size; i++)
+    {
+      short key = shortProfile.keyAt(i);
+      short value = shortProfile.valueAt(i);
+      intProfile.put(key, value);
+    }
+    shortProfile = null;
+    useInts = true;
+  }
+
+  /**
+   * Returns the size of the profile (number of distinct items counted)
+   * 
+   * @return
+   */
+  public int size()
+  {
+    return useInts ? intProfile.size() : shortProfile.size();
+  }
+
+  /**
+   * Returns the value for the key (zero if no such key)
+   * 
+   * @param key
+   * @return
+   */
+  public int get(int key)
+  {
+    return useInts ? intProfile.get(key) : shortProfile.get(key);
+  }
+
+  /**
+   * Sets the value for the given key
+   * 
+   * @param key
+   * @param value
+   */
+  public void put(int key, int value)
+  {
+    if (useInts)
+    {
+      intProfile.put(key, value);
+    }
+    else
+    {
+      shortProfile.put(key, value);
+    }
+  }
+
+  public int keyAt(int k)
+  {
+    return useInts ? intProfile.keyAt(k) : shortProfile.keyAt(k);
+  }
+
+  public int valueAt(int k)
+  {
+    return useInts ? intProfile.valueAt(k) : shortProfile.valueAt(k);
+  }
+
+  /**
+   * Answers true if this object wraps arrays of int values, false if using
+   * short values
+   * 
+   * @return
+   */
+  boolean isUsingInt()
+  {
+    return useInts;
+  }
+}
diff --git a/src/jalview/util/StringUtils.java b/src/jalview/util/StringUtils.java
index b499e6e..ba10c00 100644
--- a/src/jalview/util/StringUtils.java
+++ b/src/jalview/util/StringUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -248,4 +248,159 @@ public class StringUtils
     }
     return "" + separator;
   }
+
+  /**
+   * Converts a list to a string with a delimiter before each term except the
+   * first. Returns an empty string given a null or zero-length argument. This
+   * can be replaced with StringJoiner in Java 8.
+   * 
+   * @param terms
+   * @param delim
+   * @return
+   */
+  public static String listToDelimitedString(List<String> terms,
+          String delim)
+  {
+    StringBuilder sb = new StringBuilder(32);
+    if (terms != null && !terms.isEmpty())
+    {
+      boolean appended = false;
+      for (String term : terms)
+      {
+        if (appended)
+        {
+          sb.append(delim);
+        }
+        appended = true;
+        sb.append(term);
+      }
+    }
+    return sb.toString();
+  }
+
+  /**
+   * Convenience method to parse a string to an integer, returning 0 if the
+   * input is null or not a valid integer
+   * 
+   * @param s
+   * @return
+   */
+  public static int parseInt(String s)
+  {
+    int result = 0;
+    if (s != null && s.length() > 0)
+    {
+      try
+      {
+        result = Integer.parseInt(s);
+      } catch (NumberFormatException ex)
+      {
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Compares two versions formatted as e.g. "3.4.5" and returns -1, 0 or 1 as
+   * the first version precedes, is equal to, or follows the second
+   * 
+   * @param v1
+   * @param v2
+   * @return
+   */
+  public static int compareVersions(String v1, String v2)
+  {
+    return compareVersions(v1, v2, null);
+  }
+
+  /**
+   * Compares two versions formatted as e.g. "3.4.5b1" and returns -1, 0 or 1 as
+   * the first version precedes, is equal to, or follows the second
+   * 
+   * @param v1
+   * @param v2
+   * @param pointSeparator
+   *          a string used to delimit point increments in sub-tokens of the
+   *          version
+   * @return
+   */
+  public static int compareVersions(String v1, String v2,
+          String pointSeparator)
+  {
+    if (v1 == null || v2 == null)
+    {
+      return 0;
+    }
+    String[] toks1 = v1.split("\\.");
+    String[] toks2 = v2.split("\\.");
+    int i = 0;
+    for (; i < toks1.length; i++)
+    {
+      if (i >= toks2.length)
+      {
+        /*
+         * extra tokens in v1
+         */
+        return 1;
+      }
+      String tok1 = toks1[i];
+      String tok2 = toks2[i];
+      if (pointSeparator != null)
+      {
+        /*
+         * convert e.g. 5b2 into decimal 5.2 for comparison purposes
+         */
+        tok1 = tok1.replace(pointSeparator, ".");
+        tok2 = tok2.replace(pointSeparator, ".");
+      }
+      try
+      {
+        float f1 = Float.valueOf(tok1);
+        float f2 = Float.valueOf(tok2);
+        int comp = Float.compare(f1, f2);
+        if (comp != 0)
+        {
+          return comp;
+        }
+      } catch (NumberFormatException e)
+      {
+        System.err.println("Invalid version format found: "
+                + e.getMessage());
+        return 0;
+      }
+    }
+
+    if (i < toks2.length)
+    {
+      /*
+       * extra tokens in v2 
+       */
+      return -1;
+    }
+
+    /*
+     * same length, all tokens match
+     */
+    return 0;
+  }
+
+  /**
+   * Converts the string to all lower-case except the first character which is
+   * upper-cased
+   * 
+   * @param s
+   * @return
+   */
+  public static String toSentenceCase(String s)
+  {
+    if (s == null)
+    {
+      return s;
+    }
+    if (s.length() <= 1)
+    {
+      return s.toUpperCase();
+    }
+    return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
+  }
 }
diff --git a/src/jalview/util/TableSorter.java b/src/jalview/util/TableSorter.java
index 77e0577..fd5a673 100644
--- a/src/jalview/util/TableSorter.java
+++ b/src/jalview/util/TableSorter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/util/UrlConstants.java b/src/jalview/util/UrlConstants.java
new file mode 100644
index 0000000..9d5c2bd
--- /dev/null
+++ b/src/jalview/util/UrlConstants.java
@@ -0,0 +1,55 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.util;
+
+/**
+ * A class to hold constants relating to Url links used in Jalview
+ */
+public class UrlConstants
+{
+
+  /*
+   * Sequence ID string
+   */
+  public static final String DB_ACCESSION = "DB_ACCESSION";
+
+  /*
+   * Sequence Name string
+   */
+  public static final String SEQUENCE_ID = "SEQUENCE_ID";
+
+  /*
+   * Default sequence URL link string for EMBL-EBI search
+   */
+  public static final String EMBLEBI_STRING = "EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$";
+
+  /*
+   * Default sequence URL link string for SRS 
+   */
+  public static final String SRS_STRING = "SRS|http://srs.ebi.ac.uk/srsbin/cgi-bin/wgetz?-newId+(([uniprot-all:$SEQUENCE_ID$]))+-view+SwissEntry";
+
+  /*
+   * not instantiable
+   */
+  private UrlConstants()
+  {
+  }
+}
diff --git a/src/jalview/util/UrlLink.java b/src/jalview/util/UrlLink.java
index 85a4bc8..74b5b40 100644
--- a/src/jalview/util/UrlLink.java
+++ b/src/jalview/util/UrlLink.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,15 @@
  */
 package jalview.util;
 
+import static jalview.util.UrlConstants.DB_ACCESSION;
+import static jalview.util.UrlConstants.SEQUENCE_ID;
+
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.SequenceI;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
 import java.util.Vector;
 
 public class UrlLink
@@ -30,17 +39,33 @@ public class UrlLink
    * Jalview 2.4 extension allows regular expressions to be used to parse ID
    * strings and replace the result in the URL. Regex's operate on the whole ID
    * string given to the matchURL method, if no regex is supplied, then only
-   * text following the first pipe symbol will be susbstituted. Usage
+   * text following the first pipe symbol will be substituted. Usage
    * documentation todo.
    */
-  private String url_suffix, url_prefix, target, label, regexReplace;
+
+  // Internal constants
+  private static final String SEP = "|";
+
+  private static final String DELIM = "$";
+
+  private String urlSuffix;
+
+  private String urlPrefix;
+
+  private String target;
+
+  private String label;
+
+  private String regexReplace;
 
   private boolean dynamic = false;
 
+  private boolean usesDBaccession = false;
+
   private String invalidMessage = null;
 
   /**
-   * parse the given linkString of the form '<label>|<url>' into parts url may
+   * parse the given linkString of the form '<label>SEP<url>' into parts url may
    * contain a string $SEQUENCE_ID<=optional regex=>$ where <=optional regex=>
    * must be of the form =/<perl style regex>/=$
    * 
@@ -48,81 +73,37 @@ public class UrlLink
    */
   public UrlLink(String link)
   {
-    int sep = link.indexOf("|"), psqid = link.indexOf("$SEQUENCE_ID");
+    int sep = link.indexOf(SEP);
+    int psqid = link.indexOf(DELIM + DB_ACCESSION);
+    int nsqid = link.indexOf(DELIM + SEQUENCE_ID);
     if (psqid > -1)
     {
       dynamic = true;
-      int p = sep;
-      do
-      {
-        sep = p;
-        p = link.indexOf("|", sep + 1);
-      } while (p > sep && p < psqid);
-      // Assuming that the URL itself does not contain any '|' symbols
-      // sep now contains last pipe symbol position prior to any regex symbols
-      label = link.substring(0, sep);
-      if (label.indexOf("|") > -1)
-      {
-        // | terminated database name / www target at start of Label
-        target = label.substring(0, label.indexOf("|"));
-      }
-      else if (label.indexOf(" ") > 2)
-      {
-        // space separated Label - matches database name
-        target = label.substring(0, label.indexOf(" "));
-      }
-      else
-      {
-        target = label;
-      }
-      // Parse URL : Whole URL string first
-      url_prefix = link.substring(sep + 1, psqid);
-      if (link.indexOf("$SEQUENCE_ID=/") == psqid
-              && (p = link.indexOf("/=$", psqid + 14)) > psqid + 14)
-      {
-        // Extract Regex and suffix
-        url_suffix = link.substring(p + 3);
-        regexReplace = link.substring(psqid + 14, p);
-        try
-        {
-          com.stevesoft.pat.Regex rg = com.stevesoft.pat.Regex.perlCode("/"
-                  + regexReplace + "/");
-          if (rg == null)
-          {
-            invalidMessage = "Invalid Regular Expression : '"
-                    + regexReplace + "'\n";
-          }
-        } catch (Exception e)
-        {
-          invalidMessage = "Invalid Regular Expression : '" + regexReplace
-                  + "'\n";
-        }
-      }
-      else
-      {
-        regexReplace = null;
-        // verify format is really correct.
-        if (link.indexOf("$SEQUENCE_ID$") == psqid)
-        {
-          url_suffix = link.substring(psqid + 13);
-          regexReplace = null;
-        }
-        else
-        {
-          invalidMessage = "Warning: invalid regex structure for URL link : "
-                  + link;
-        }
-      }
+      usesDBaccession = true;
+
+      sep = parseTargetAndLabel(sep, psqid, link);
+
+      parseUrl(link, DB_ACCESSION, psqid, sep);
+    }
+    else if (nsqid > -1)
+    {
+      dynamic = true;
+      sep = parseTargetAndLabel(sep, nsqid, link);
+
+      parseUrl(link, SEQUENCE_ID, nsqid, sep);
     }
     else
     {
       target = link.substring(0, sep);
-      label = link.substring(0, sep = link.lastIndexOf("|"));
-      url_prefix = link.substring(sep + 1);
+      sep = link.lastIndexOf(SEP);
+      label = link.substring(0, sep);
+      urlPrefix = link.substring(sep + 1).trim();
       regexReplace = null; // implies we trim any prefix if necessary //
-      // regexReplace=".*\\|?(.*)";
-      url_suffix = null;
+      urlSuffix = null;
     }
+
+    label = label.trim();
+    target = target.trim();
   }
 
   /**
@@ -130,7 +111,7 @@ public class UrlLink
    */
   public String getUrl_suffix()
   {
-    return url_suffix;
+    return urlSuffix;
   }
 
   /**
@@ -138,7 +119,7 @@ public class UrlLink
    */
   public String getUrl_prefix()
   {
-    return url_prefix;
+    return urlPrefix;
   }
 
   /**
@@ -185,6 +166,34 @@ public class UrlLink
   }
 
   /**
+   * 
+   * @return whether link is dynamic
+   */
+  public boolean isDynamic()
+  {
+    return dynamic;
+  }
+
+  /**
+   * 
+   * @return whether link uses DB Accession id
+   */
+  public boolean usesDBAccession()
+  {
+    return usesDBaccession;
+  }
+
+  /**
+   * Set the label
+   * 
+   * @param newlabel
+   */
+  public void setLabel(String newlabel)
+  {
+    this.label = newlabel;
+  }
+
+  /**
    * return one or more URL strings by applying regex to the given idstring
    * 
    * @param idstring
@@ -209,7 +218,7 @@ public class UrlLink
           {
             // take whole regex
             return new String[] { rg.stringMatched(),
-                url_prefix + rg.stringMatched() + url_suffix };
+                urlPrefix + rg.stringMatched() + urlSuffix };
           } /*
              * else if (ns==1) { // take only subgroup match return new String[]
              * { rg.stringMatched(1), url_prefix+rg.stringMatched(1)+url_suffix
@@ -250,7 +259,7 @@ public class UrlLink
                 if (mtch.length() > 0)
                 {
                   subs.addElement(mtch);
-                  subs.addElement(url_prefix + mtch + url_suffix);
+                  subs.addElement(urlPrefix + mtch + urlSuffix);
                 }
                 s = r;
               }
@@ -259,8 +268,8 @@ public class UrlLink
                 if (rg.matchedFrom(s) > -1)
                 {
                   subs.addElement(rg.stringMatched(s));
-                  subs.addElement(url_prefix + rg.stringMatched(s)
-                          + url_suffix);
+                  subs.addElement(urlPrefix + rg.stringMatched(s)
+                          + urlSuffix);
                 }
                 s++;
               }
@@ -281,29 +290,254 @@ public class UrlLink
         }
       }
       /* Otherwise - trim off any 'prefix' - pre 2.4 Jalview behaviour */
-      if (idstring.indexOf("|") > -1)
+      if (idstring.indexOf(SEP) > -1)
       {
-        idstring = idstring.substring(idstring.lastIndexOf("|") + 1);
+        idstring = idstring.substring(idstring.lastIndexOf(SEP) + 1);
       }
 
       // just return simple url substitution.
-      return new String[] { idstring, url_prefix + idstring + url_suffix };
+      return new String[] { idstring, urlPrefix + idstring + urlSuffix };
     }
     else
     {
-      return new String[] { "", url_prefix };
+      return new String[] { "", urlPrefix };
     }
   }
 
+  @Override
   public String toString()
   {
+    String var = (usesDBaccession ? DB_ACCESSION : SEQUENCE_ID);
+
     return label
-            + "|"
-            + url_prefix
-            + (dynamic ? ("$SEQUENCE_ID" + ((regexReplace != null) ? "="
-                    + regexReplace + "=$" : "$")) : "")
-            + ((url_suffix == null) ? "" : url_suffix);
+            + SEP
+            + urlPrefix
+            + (dynamic ? (DELIM + var + ((regexReplace != null) ? "="
+                    + regexReplace + "=" + DELIM : DELIM)) : "")
+            + ((urlSuffix == null) ? "" : urlSuffix);
+  }
+
+  /**
+   * 
+   * @param firstSep
+   *          Location of first occurrence of separator in link string
+   * @param psqid
+   *          Position of sequence id or name in link string
+   * @param link
+   *          Link string containing database name and url
+   * @return Position of last separator symbol prior to any regex symbols
+   */
+  protected int parseTargetAndLabel(int firstSep, int psqid, String link)
+  {
+    int p = firstSep;
+    int sep = firstSep;
+    do
+    {
+      sep = p;
+      p = link.indexOf(SEP, sep + 1);
+    } while (p > sep && p < psqid);
+    // Assuming that the URL itself does not contain any SEP symbols
+    // sep now contains last pipe symbol position prior to any regex symbols
+    label = link.substring(0, sep);
+    if (label.indexOf(SEP) > -1)
+    {
+      // SEP terminated database name / www target at start of Label
+      target = label.substring(0, label.indexOf(SEP));
+    }
+    else if (label.indexOf(" ") > 2)
+    {
+      // space separated Label - matches database name
+      target = label.substring(0, label.indexOf(" "));
+    }
+    else
+    {
+      target = label;
+    }
+    return sep;
+  }
+
+  /**
+   * Parse the URL part of the link string
+   * 
+   * @param link
+   *          Link string containing database name and url
+   * @param varName
+   *          Name of variable in url string (e.g. SEQUENCE_ID, SEQUENCE_NAME)
+   * @param sqidPos
+   *          Position of id or name in link string
+   * @param sep
+   *          Position of separator in link string
+   */
+  protected void parseUrl(String link, String varName, int sqidPos, int sep)
+  {
+    urlPrefix = link.substring(sep + 1, sqidPos).trim();
+
+    // delimiter at start of regex: e.g. $SEQUENCE_ID=/
+    String startDelimiter = DELIM + varName + "=/";
+
+    // delimiter at end of regex: /=$
+    String endDelimiter = "/=" + DELIM;
+
+    int startLength = startDelimiter.length();
+
+    // Parse URL : Whole URL string first
+    int p = link.indexOf(endDelimiter, sqidPos + startLength);
+
+    if (link.indexOf(startDelimiter) == sqidPos
+            && (p > sqidPos + startLength))
+    {
+      // Extract Regex and suffix
+      urlSuffix = link.substring(p + endDelimiter.length());
+      regexReplace = link.substring(sqidPos + startLength, p);
+      try
+      {
+        com.stevesoft.pat.Regex rg = com.stevesoft.pat.Regex.perlCode("/"
+                + regexReplace + "/");
+        if (rg == null)
+        {
+          invalidMessage = "Invalid Regular Expression : '" + regexReplace
+                  + "'\n";
+        }
+      } catch (Exception e)
+      {
+        invalidMessage = "Invalid Regular Expression : '" + regexReplace
+                + "'\n";
+      }
+    }
+    else
+    {
+      // no regex
+      regexReplace = null;
+      // verify format is really correct.
+      if (link.indexOf(DELIM + varName + DELIM) == sqidPos)
+      {
+        urlSuffix = link.substring(sqidPos + startLength - 1);
+        regexReplace = null;
+      }
+      else
+      {
+        invalidMessage = "Warning: invalid regex structure for URL link : "
+                + link;
+      }
+    }
+  }
+
+  /**
+   * Create a set of URL links for a sequence
+   * 
+   * @param seq
+   *          The sequence to create links for
+   * @param linkset
+   *          Map of links: key = id + SEP + link, value = [target, label, id,
+   *          link]
+   */
+  public void createLinksFromSeq(final SequenceI seq,
+          Map<String, List<String>> linkset)
+  {
+    if (seq != null && dynamic)
+    {
+      createDynamicLinks(seq, linkset);
+    }
+    else
+    {
+      createStaticLink(linkset);
+    }
+  }
+
+  /**
+   * Create a static URL link
+   * 
+   * @param linkset
+   *          Map of links: key = id + SEP + link, value = [target, label, id,
+   *          link]
+   */
+  protected void createStaticLink(Map<String, List<String>> linkset)
+  {
+    if (!linkset.containsKey(label + SEP + getUrl_prefix()))
+    {
+      // Add a non-dynamic link
+      linkset.put(label + SEP + getUrl_prefix(),
+              Arrays.asList(target, label, null, getUrl_prefix()));
+    }
+  }
+
+  /**
+   * Create dynamic URL links
+   * 
+   * @param seq
+   *          The sequence to create links for
+   * @param linkset
+   *          Map of links: key = id + SEP + link, value = [target, label, id,
+   *          link]
+   */
+  protected void createDynamicLinks(final SequenceI seq,
+          Map<String, List<String>> linkset)
+  {
+    // collect id string too
+    String id = seq.getName();
+    String descr = seq.getDescription();
+    if (descr != null && descr.length() < 1)
+    {
+      descr = null;
+    }
+
+    if (usesDBAccession()) // link is ID
+    {
+      // collect matching db-refs
+      DBRefEntry[] dbr = DBRefUtils.selectRefs(seq.getDBRefs(),
+              new String[] { target });
+
+      // if there are any dbrefs which match up with the link
+      if (dbr != null)
+      {
+        for (int r = 0; r < dbr.length; r++)
+        {
+          // create Bare ID link for this URL
+          createBareURLLink(dbr[r].getAccessionId(), true, linkset);
+        }
+      }
+    }
+    else if (!usesDBAccession() && id != null) // link is name
+    {
+      // create Bare ID link for this URL
+      createBareURLLink(id, false, linkset);
+    }
+
+    // Create urls from description but only for URL links which are regex
+    // links
+    if (descr != null && getRegexReplace() != null)
+    {
+      // create link for this URL from description where regex matches
+      createBareURLLink(descr, false, linkset);
+    }
+  }
 
+  /*
+   * Create a bare URL Link
+   * Returns map where key = id + SEP + link, and value = [target, label, id, link]
+   */
+  protected void createBareURLLink(String id, Boolean combineLabel,
+          Map<String, List<String>> linkset)
+  {
+    String[] urls = makeUrls(id, true);
+    if (urls != null)
+    {
+      for (int u = 0; u < urls.length; u += 2)
+      {
+        if (!linkset.containsKey(urls[u] + SEP + urls[u + 1]))
+        {
+          String thisLabel = label;
+          if (combineLabel)
+          {
+            // incorporate label with idstring
+            thisLabel = label + SEP + urls[u];
+          }
+
+          linkset.put(urls[u] + SEP + urls[u + 1],
+                  Arrays.asList(target, thisLabel, urls[u], urls[u + 1]));
+        }
+      }
+    }
   }
 
   private static void testUrls(UrlLink ul, String idstring, String[] urls)
@@ -341,7 +575,8 @@ public class UrlLink
      * "PF3|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/PFAM:(.+)/=$"
      * , "NOTFER|http://notfer.org/$SEQUENCE_ID=/(?<!\\s)(.+)/=$",
      */
-    "NESTED|http://nested/$SEQUENCE_ID=/^(?:Label:)?(?:(?:gi\\|(\\d+))|([^:]+))/=$/nested" };
+    "NESTED|http://nested/$" + DB_ACCESSION
+            + "=/^(?:Label:)?(?:(?:gi\\|(\\d+))|([^:]+))/=$/nested" };
     String[] idstrings = new String[] {
     /*
      * //"LGUL_human", //"QWIQW_123123", "uniprot|why_do+_12313_foo",
@@ -382,15 +617,4 @@ public class UrlLink
       }
     }
   }
-
-  public boolean isDynamic()
-  {
-    // TODO Auto-generated method stub
-    return dynamic;
-  }
-
-  public void setLabel(String newlabel)
-  {
-    this.label = newlabel;
-  }
 }
diff --git a/src/jalview/util/jarInputStreamProvider.java b/src/jalview/util/jarInputStreamProvider.java
index 17bb015..57e452d 100644
--- a/src/jalview/util/jarInputStreamProvider.java
+++ b/src/jalview/util/jarInputStreamProvider.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java
index a15e9a6..19fe300 100644
--- a/src/jalview/viewmodel/AlignmentViewport.java
+++ b/src/jalview/viewmodel/AlignmentViewport.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -36,7 +36,8 @@ import jalview.datamodel.Annotation;
 import jalview.datamodel.CigarArray;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.HiddenSequences;
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.ProfilesI;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceGroup;
@@ -44,11 +45,11 @@ import jalview.datamodel.SequenceI;
 import jalview.schemes.Blosum62ColourScheme;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.PIDColourScheme;
-import jalview.schemes.ResidueProperties;
 import jalview.structure.CommandListener;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
 import jalview.util.Comparison;
+import jalview.util.MapList;
 import jalview.util.MappingUtils;
 import jalview.viewmodel.styles.ViewStyle;
 import jalview.workers.AlignCalcManager;
@@ -57,6 +58,7 @@ import jalview.workers.ConsensusThread;
 import jalview.workers.StrucConsensusThread;
 
 import java.awt.Color;
+import java.beans.PropertyChangeSupport;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.BitSet;
@@ -65,7 +67,6 @@ import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * base class holding visualization and analysis attributes and common logic for
@@ -95,6 +96,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param name
    * @see jalview.api.ViewStyleI#setFontName(java.lang.String)
    */
+  @Override
   public void setFontName(String name)
   {
     viewStyle.setFontName(name);
@@ -104,6 +106,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param style
    * @see jalview.api.ViewStyleI#setFontStyle(int)
    */
+  @Override
   public void setFontStyle(int style)
   {
     viewStyle.setFontStyle(style);
@@ -113,6 +116,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param size
    * @see jalview.api.ViewStyleI#setFontSize(int)
    */
+  @Override
   public void setFontSize(int size)
   {
     viewStyle.setFontSize(size);
@@ -122,6 +126,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getFontStyle()
    */
+  @Override
   public int getFontStyle()
   {
     return viewStyle.getFontStyle();
@@ -131,6 +136,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getFontName()
    */
+  @Override
   public String getFontName()
   {
     return viewStyle.getFontName();
@@ -140,6 +146,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getFontSize()
    */
+  @Override
   public int getFontSize()
   {
     return viewStyle.getFontSize();
@@ -149,6 +156,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param upperCasebold
    * @see jalview.api.ViewStyleI#setUpperCasebold(boolean)
    */
+  @Override
   public void setUpperCasebold(boolean upperCasebold)
   {
     viewStyle.setUpperCasebold(upperCasebold);
@@ -158,6 +166,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#isUpperCasebold()
    */
+  @Override
   public boolean isUpperCasebold()
   {
     return viewStyle.isUpperCasebold();
@@ -167,6 +176,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#isSeqNameItalics()
    */
+  @Override
   public boolean isSeqNameItalics()
   {
     return viewStyle.isSeqNameItalics();
@@ -176,6 +186,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param colourByReferenceSeq
    * @see jalview.api.ViewStyleI#setColourByReferenceSeq(boolean)
    */
+  @Override
   public void setColourByReferenceSeq(boolean colourByReferenceSeq)
   {
     viewStyle.setColourByReferenceSeq(colourByReferenceSeq);
@@ -185,6 +196,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param b
    * @see jalview.api.ViewStyleI#setColourAppliesToAllGroups(boolean)
    */
+  @Override
   public void setColourAppliesToAllGroups(boolean b)
   {
     viewStyle.setColourAppliesToAllGroups(b);
@@ -194,6 +206,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getColourAppliesToAllGroups()
    */
+  @Override
   public boolean getColourAppliesToAllGroups()
   {
     return viewStyle.getColourAppliesToAllGroups();
@@ -203,6 +216,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getAbovePIDThreshold()
    */
+  @Override
   public boolean getAbovePIDThreshold()
   {
     return viewStyle.getAbovePIDThreshold();
@@ -212,6 +226,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param inc
    * @see jalview.api.ViewStyleI#setIncrement(int)
    */
+  @Override
   public void setIncrement(int inc)
   {
     viewStyle.setIncrement(inc);
@@ -221,6 +236,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getIncrement()
    */
+  @Override
   public int getIncrement()
   {
     return viewStyle.getIncrement();
@@ -230,6 +246,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param b
    * @see jalview.api.ViewStyleI#setConservationSelected(boolean)
    */
+  @Override
   public void setConservationSelected(boolean b)
   {
     viewStyle.setConservationSelected(b);
@@ -239,6 +256,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param show
    * @see jalview.api.ViewStyleI#setShowHiddenMarkers(boolean)
    */
+  @Override
   public void setShowHiddenMarkers(boolean show)
   {
     viewStyle.setShowHiddenMarkers(show);
@@ -248,6 +266,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getShowHiddenMarkers()
    */
+  @Override
   public boolean getShowHiddenMarkers()
   {
     return viewStyle.getShowHiddenMarkers();
@@ -257,6 +276,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param b
    * @see jalview.api.ViewStyleI#setScaleRightWrapped(boolean)
    */
+  @Override
   public void setScaleRightWrapped(boolean b)
   {
     viewStyle.setScaleRightWrapped(b);
@@ -266,6 +286,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param b
    * @see jalview.api.ViewStyleI#setScaleLeftWrapped(boolean)
    */
+  @Override
   public void setScaleLeftWrapped(boolean b)
   {
     viewStyle.setScaleLeftWrapped(b);
@@ -275,6 +296,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param b
    * @see jalview.api.ViewStyleI#setScaleAboveWrapped(boolean)
    */
+  @Override
   public void setScaleAboveWrapped(boolean b)
   {
     viewStyle.setScaleAboveWrapped(b);
@@ -284,6 +306,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getScaleLeftWrapped()
    */
+  @Override
   public boolean getScaleLeftWrapped()
   {
     return viewStyle.getScaleLeftWrapped();
@@ -293,6 +316,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getScaleAboveWrapped()
    */
+  @Override
   public boolean getScaleAboveWrapped()
   {
     return viewStyle.getScaleAboveWrapped();
@@ -302,6 +326,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getScaleRightWrapped()
    */
+  @Override
   public boolean getScaleRightWrapped()
   {
     return viewStyle.getScaleRightWrapped();
@@ -311,6 +336,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param b
    * @see jalview.api.ViewStyleI#setAbovePIDThreshold(boolean)
    */
+  @Override
   public void setAbovePIDThreshold(boolean b)
   {
     viewStyle.setAbovePIDThreshold(b);
@@ -320,6 +346,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param thresh
    * @see jalview.api.ViewStyleI#setThreshold(int)
    */
+  @Override
   public void setThreshold(int thresh)
   {
     viewStyle.setThreshold(thresh);
@@ -329,6 +356,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getThreshold()
    */
+  @Override
   public int getThreshold()
   {
     return viewStyle.getThreshold();
@@ -338,6 +366,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getShowJVSuffix()
    */
+  @Override
   public boolean getShowJVSuffix()
   {
     return viewStyle.getShowJVSuffix();
@@ -347,6 +376,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param b
    * @see jalview.api.ViewStyleI#setShowJVSuffix(boolean)
    */
+  @Override
   public void setShowJVSuffix(boolean b)
   {
     viewStyle.setShowJVSuffix(b);
@@ -356,6 +386,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param state
    * @see jalview.api.ViewStyleI#setWrapAlignment(boolean)
    */
+  @Override
   public void setWrapAlignment(boolean state)
   {
     viewStyle.setWrapAlignment(state);
@@ -365,6 +396,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param state
    * @see jalview.api.ViewStyleI#setShowText(boolean)
    */
+  @Override
   public void setShowText(boolean state)
   {
     viewStyle.setShowText(state);
@@ -374,6 +406,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param state
    * @see jalview.api.ViewStyleI#setRenderGaps(boolean)
    */
+  @Override
   public void setRenderGaps(boolean state)
   {
     viewStyle.setRenderGaps(state);
@@ -383,6 +416,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getColourText()
    */
+  @Override
   public boolean getColourText()
   {
     return viewStyle.getColourText();
@@ -392,6 +426,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param state
    * @see jalview.api.ViewStyleI#setColourText(boolean)
    */
+  @Override
   public void setColourText(boolean state)
   {
     viewStyle.setColourText(state);
@@ -401,6 +436,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getWrapAlignment()
    */
+  @Override
   public boolean getWrapAlignment()
   {
     return viewStyle.getWrapAlignment();
@@ -410,6 +446,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getShowText()
    */
+  @Override
   public boolean getShowText()
   {
     return viewStyle.getShowText();
@@ -419,6 +456,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getWrappedWidth()
    */
+  @Override
   public int getWrappedWidth()
   {
     return viewStyle.getWrappedWidth();
@@ -428,6 +466,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param w
    * @see jalview.api.ViewStyleI#setWrappedWidth(int)
    */
+  @Override
   public void setWrappedWidth(int w)
   {
     viewStyle.setWrappedWidth(w);
@@ -437,6 +476,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getCharHeight()
    */
+  @Override
   public int getCharHeight()
   {
     return viewStyle.getCharHeight();
@@ -446,6 +486,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param h
    * @see jalview.api.ViewStyleI#setCharHeight(int)
    */
+  @Override
   public void setCharHeight(int h)
   {
     viewStyle.setCharHeight(h);
@@ -455,6 +496,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getCharWidth()
    */
+  @Override
   public int getCharWidth()
   {
     return viewStyle.getCharWidth();
@@ -464,6 +506,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param w
    * @see jalview.api.ViewStyleI#setCharWidth(int)
    */
+  @Override
   public void setCharWidth(int w)
   {
     viewStyle.setCharWidth(w);
@@ -473,6 +516,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getShowBoxes()
    */
+  @Override
   public boolean getShowBoxes()
   {
     return viewStyle.getShowBoxes();
@@ -482,6 +526,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getShowUnconserved()
    */
+  @Override
   public boolean getShowUnconserved()
   {
     return viewStyle.getShowUnconserved();
@@ -491,6 +536,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param showunconserved
    * @see jalview.api.ViewStyleI#setShowUnconserved(boolean)
    */
+  @Override
   public void setShowUnconserved(boolean showunconserved)
   {
     viewStyle.setShowUnconserved(showunconserved);
@@ -500,6 +546,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param default1
    * @see jalview.api.ViewStyleI#setSeqNameItalics(boolean)
    */
+  @Override
   public void setSeqNameItalics(boolean default1)
   {
     viewStyle.setSeqNameItalics(default1);
@@ -566,7 +613,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     boolean recalc = false;
     if (cs != null)
     {
-      cs.setConservationApplied(recalc = getConservationSelected());
+      recalc = getConservationSelected();
       if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
               || cs instanceof Blosum62ColourScheme)
       {
@@ -583,6 +630,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
         cs.setConsensus(hconsensus);
         cs.setConservation(hconservation);
       }
+      cs.setConservationApplied(getConservationSelected());
       cs.alignmentChanged(alignment, hiddenRepSequences);
     }
     if (getColourAppliesToAllGroups())
@@ -653,7 +701,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   /**
    * results of alignment consensus analysis for visible portion of view
    */
-  protected Hashtable[] hconsensus = null;
+  protected ProfilesI hconsensus = null;
 
   /**
    * results of cDNA complement consensus visible portion of view
@@ -687,7 +735,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
-  public void setSequenceConsensusHash(Hashtable[] hconsensus)
+  public void setSequenceConsensusHash(ProfilesI hconsensus)
   {
     this.hconsensus = hconsensus;
   }
@@ -699,7 +747,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
-  public Hashtable[] getSequenceConsensusHash()
+  public ProfilesI getSequenceConsensusHash()
   {
     return hconsensus;
   }
@@ -761,7 +809,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void updateConservation(final AlignmentViewPanel ap)
   {
     // see note in mantis : issue number 8585
-    if (alignment.isNucleotide() || conservation == null
+    if (alignment.isNucleotide()
+            || (conservation == null && quality == null)
             || !autoCalculateConsensus)
     {
       return;
@@ -791,15 +840,36 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
     /*
      * A separate thread to compute cDNA consensus for a protein alignment
+     * which has mapping to cDNA
      */
     final AlignmentI al = this.getAlignment();
     if (!al.isNucleotide() && al.getCodonFrames() != null
             && !al.getCodonFrames().isEmpty())
     {
-      if (calculator
-              .getRegisteredWorkersOfClass(ComplementConsensusThread.class) == null)
+      /*
+       * fudge - check first for protein-to-nucleotide mappings
+       * (we don't want to do this for protein-to-protein)
+       */
+      boolean doConsensus = false;
+      for (AlignedCodonFrame mapping : al.getCodonFrames())
       {
-        calculator.registerWorker(new ComplementConsensusThread(this, ap));
+        // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
+        MapList[] mapLists = mapping.getdnaToProt();
+        // mapLists can be empty if project load has not finished resolving seqs
+        if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
+        {
+          doConsensus = true;
+          break;
+        }
+      }
+      if (doConsensus)
+      {
+        if (calculator
+                .getRegisteredWorkersOfClass(ComplementConsensusThread.class) == null)
+        {
+          calculator
+                  .registerWorker(new ComplementConsensusThread(this, ap));
+        }
       }
     }
   }
@@ -846,6 +916,37 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return false;
   }
 
+  public void setAlignment(AlignmentI align)
+  {
+    this.alignment = align;
+  }
+
+  /**
+   * Clean up references when this viewport is closed
+   */
+  @Override
+  public void dispose()
+  {
+    /*
+     * defensively null out references to large objects in case
+     * this object is not garbage collected (as if!)
+     */
+    consensus = null;
+    complementConsensus = null;
+    strucConsensus = null;
+    conservation = null;
+    quality = null;
+    groupConsensus = null;
+    groupConservation = null;
+    hconsensus = null;
+    hcomplementConsensus = null;
+    // colour scheme may hold reference to consensus
+    globalColourScheme = null;
+    // TODO remove listeners from changeSupport?
+    changeSupport = null;
+    setAlignment(null);
+  }
+
   @Override
   public boolean isClosed()
   {
@@ -1019,6 +1120,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     {
       updateHiddenColumns();
     }
+    isColSelChanged(true);
   }
 
   /**
@@ -1039,6 +1141,13 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
+  public boolean hasSelectedColumns()
+  {
+    ColumnSelection columnSelection = getColumnSelection();
+    return columnSelection != null && columnSelection.hasSelectedColumns();
+  }
+
+  @Override
   public boolean hasHiddenColumns()
   {
     return colSel != null && colSel.hasHiddenColumns();
@@ -1147,8 +1256,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   public boolean isColSelChanged(boolean b)
   {
-    int hc = (colSel == null || colSel.size() == 0) ? -1 : colSel
-            .hashCode();
+    int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
     if (hc != -1 && hc != colselhash)
     {
       if (b)
@@ -1166,10 +1274,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return ignoreGapsInConsensusCalculation;
   }
 
-  // / property change stuff
-
+  // property change stuff
   // JBPNote Prolly only need this in the applet version.
-  private final java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
+  private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
           this);
 
   protected boolean showConservation = true;
@@ -1242,14 +1349,14 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   public void hideSelectedColumns()
   {
-    if (colSel.size() < 1)
+    if (colSel.isEmpty())
     {
       return;
     }
 
     colSel.hideSelectedColumns();
     setSelectionGroup(null);
-
+    isColSelChanged(true);
   }
 
   public void hideColumns(int start, int end)
@@ -1262,17 +1369,19 @@ public abstract class AlignmentViewport implements AlignViewportI,
     {
       colSel.hideColumns(start, end);
     }
+    isColSelChanged(true);
   }
 
   public void showColumn(int col)
   {
     colSel.revealHiddenColumns(col);
-
+    isColSelChanged(true);
   }
 
   public void showAllHiddenColumns()
   {
     colSel.revealAllHiddenColumns();
+    isColSelChanged(true);
   }
 
   // common hide/show seq stuff
@@ -1352,6 +1461,39 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   /**
+   * Hides the specified sequence, or the sequences it represents
+   * 
+   * @param sequence
+   *          the sequence to hide, or keep as representative
+   * @param representGroup
+   *          if true, hide the current selection group except for the
+   *          representative sequence
+   */
+  public void hideSequences(SequenceI sequence, boolean representGroup)
+  {
+    if (selectionGroup == null || selectionGroup.getSize() < 1)
+    {
+      hideSequence(new SequenceI[] { sequence });
+      return;
+    }
+
+    if (representGroup)
+    {
+      hideRepSequences(sequence, selectionGroup);
+      setSelectionGroup(null);
+      return;
+    }
+
+    int gsize = selectionGroup.getSize();
+    SequenceI[] hseqs = selectionGroup.getSequences().toArray(
+            new SequenceI[gsize]);
+
+    hideSequence(hseqs);
+    setSelectionGroup(null);
+    sendSelection();
+  }
+
+  /**
    * Set visibility for any annotations for the given sequence.
    * 
    * @param sequenceI
@@ -1359,11 +1501,15 @@ public abstract class AlignmentViewport implements AlignViewportI,
   protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
           boolean visible)
   {
-    for (AlignmentAnnotation ann : alignment.getAlignmentAnnotation())
+    AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
+    if (anns != null)
     {
-      if (ann.sequenceRef == sequenceI)
+      for (AlignmentAnnotation ann : anns)
       {
-        ann.visible = visible;
+        if (ann.sequenceRef == sequenceI)
+        {
+          ann.visible = visible;
+        }
       }
     }
   }
@@ -1378,7 +1524,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
     if (hiddenRepSequences == null)
     {
-      hiddenRepSequences = new Hashtable();
+      hiddenRepSequences = new Hashtable<SequenceI, SequenceCollectionI>();
     }
 
     hiddenRepSequences.put(repSequence, sg);
@@ -1404,13 +1550,42 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   }
 
+  /**
+   * 
+   * @return null or the current reference sequence
+   */
+  public SequenceI getReferenceSeq()
+  {
+    return alignment.getSeqrep();
+  }
+
+  /**
+   * @param seq
+   * @return true iff seq is the reference for the alignment
+   */
+  public boolean isReferenceSeq(SequenceI seq)
+  {
+    return alignment.getSeqrep() == seq;
+  }
+
+  /**
+   * 
+   * @param seq
+   * @return true if there are sequences represented by this sequence that are
+   *         currently hidden
+   */
   public boolean isHiddenRepSequence(SequenceI seq)
   {
-    return alignment.getSeqrep() == seq
-            || (hiddenRepSequences != null && hiddenRepSequences
-                    .containsKey(seq));
+    return (hiddenRepSequences != null && hiddenRepSequences
+            .containsKey(seq));
   }
 
+  /**
+   * 
+   * @param seq
+   * @return null or a sequence group containing the sequences that seq
+   *         represents
+   */
   public SequenceGroup getRepresentedSequences(SequenceI seq)
   {
     return (SequenceGroup) (hiddenRepSequences == null ? null
@@ -1498,6 +1673,13 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public String[] getViewAsString(boolean selectedRegionOnly)
   {
+    return getViewAsString(selectedRegionOnly, true);
+  }
+
+  @Override
+  public String[] getViewAsString(boolean selectedRegionOnly,
+          boolean exportHiddenSeqs)
+  {
     String[] selection = null;
     SequenceI[] seqs = null;
     int i, iSize;
@@ -1511,9 +1693,20 @@ public abstract class AlignmentViewport implements AlignViewportI,
     }
     else
     {
-      iSize = alignment.getHeight();
-      seqs = alignment.getSequencesArray();
-      end = alignment.getWidth();
+      if (hasHiddenRows() && exportHiddenSeqs)
+      {
+        AlignmentI fullAlignment = alignment.getHiddenSequences()
+                .getFullAlignment();
+        iSize = fullAlignment.getHeight();
+        seqs = fullAlignment.getSequencesArray();
+        end = fullAlignment.getWidth();
+      }
+      else
+      {
+        iSize = alignment.getHeight();
+        seqs = alignment.getSequencesArray();
+        end = alignment.getWidth();
+      }
     }
 
     selection = new String[iSize];
@@ -1675,8 +1868,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
       if (cs.conservationApplied())
       {
         cs.setConservation(Conservation.calculateConservation("All",
-                ResidueProperties.propHash, 3, alignment.getSequences(), 0,
-                alignment.getWidth(), false, getConsPercGaps(), false));
+                alignment.getSequences(), 0, alignment.getWidth(), false,
+                getConsPercGaps(), false));
       }
     }
 
@@ -1723,14 +1916,30 @@ public abstract class AlignmentViewport implements AlignViewportI,
   {
     if (!alignment.isNucleotide())
     {
-      final Set<AlignedCodonFrame> codonMappings = alignment
+      final List<AlignedCodonFrame> codonMappings = alignment
               .getCodonFrames();
       if (codonMappings != null && !codonMappings.isEmpty())
       {
-        complementConsensus = new AlignmentAnnotation("cDNA Consensus",
-                "PID for cDNA", new Annotation[1], 0f, 100f,
-                AlignmentAnnotation.BAR_GRAPH);
-        initConsensus(complementConsensus);
+        boolean doConsensus = false;
+        for (AlignedCodonFrame mapping : codonMappings)
+        {
+          // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
+          MapList[] mapLists = mapping.getdnaToProt();
+          // mapLists can be empty if project load has not finished resolving
+          // seqs
+          if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
+          {
+            doConsensus = true;
+            break;
+          }
+        }
+        if (doConsensus)
+        {
+          complementConsensus = new AlignmentAnnotation("cDNA Consensus",
+                  "PID for cDNA", new Annotation[1], 0f, 100f,
+                  AlignmentAnnotation.BAR_GRAPH);
+          initConsensus(complementConsensus);
+        }
       }
     }
   }
@@ -2042,7 +2251,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public boolean areFeaturesDisplayed()
   {
     return featuresDisplayed != null
-            && featuresDisplayed.getRegisterdFeaturesCount() > 0;
+            && featuresDisplayed.getRegisteredFeaturesCount() > 0;
   }
 
   /**
@@ -2115,6 +2324,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getTextColour()
    */
+  @Override
   public Color getTextColour()
   {
     return viewStyle.getTextColour();
@@ -2124,6 +2334,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getTextColour2()
    */
+  @Override
   public Color getTextColour2()
   {
     return viewStyle.getTextColour2();
@@ -2133,6 +2344,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getThresholdTextColour()
    */
+  @Override
   public int getThresholdTextColour()
   {
     return viewStyle.getThresholdTextColour();
@@ -2142,6 +2354,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#isConservationColourSelected()
    */
+  @Override
   public boolean isConservationColourSelected()
   {
     return viewStyle.isConservationColourSelected();
@@ -2151,6 +2364,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#isRenderGaps()
    */
+  @Override
   public boolean isRenderGaps()
   {
     return viewStyle.isRenderGaps();
@@ -2160,6 +2374,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#isShowColourText()
    */
+  @Override
   public boolean isShowColourText()
   {
     return viewStyle.isShowColourText();
@@ -2169,6 +2384,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param conservationColourSelected
    * @see jalview.api.ViewStyleI#setConservationColourSelected(boolean)
    */
+  @Override
   public void setConservationColourSelected(
           boolean conservationColourSelected)
   {
@@ -2179,6 +2395,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param showColourText
    * @see jalview.api.ViewStyleI#setShowColourText(boolean)
    */
+  @Override
   public void setShowColourText(boolean showColourText)
   {
     viewStyle.setShowColourText(showColourText);
@@ -2188,6 +2405,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param textColour
    * @see jalview.api.ViewStyleI#setTextColour(java.awt.Color)
    */
+  @Override
   public void setTextColour(Color textColour)
   {
     viewStyle.setTextColour(textColour);
@@ -2197,6 +2415,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param thresholdTextColour
    * @see jalview.api.ViewStyleI#setThresholdTextColour(int)
    */
+  @Override
   public void setThresholdTextColour(int thresholdTextColour)
   {
     viewStyle.setThresholdTextColour(thresholdTextColour);
@@ -2206,6 +2425,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param textColour2
    * @see jalview.api.ViewStyleI#setTextColour2(java.awt.Color)
    */
+  @Override
   public void setTextColour2(Color textColour2)
   {
     viewStyle.setTextColour2(textColour2);
@@ -2233,6 +2453,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#getIdWidth()
    */
+  @Override
   public int getIdWidth()
   {
     return viewStyle.getIdWidth();
@@ -2242,6 +2463,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param i
    * @see jalview.api.ViewStyleI#setIdWidth(int)
    */
+  @Override
   public void setIdWidth(int i)
   {
     viewStyle.setIdWidth(i);
@@ -2251,6 +2473,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#isCentreColumnLabels()
    */
+  @Override
   public boolean isCentreColumnLabels()
   {
     return viewStyle.isCentreColumnLabels();
@@ -2260,6 +2483,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param centreColumnLabels
    * @see jalview.api.ViewStyleI#setCentreColumnLabels(boolean)
    */
+  @Override
   public void setCentreColumnLabels(boolean centreColumnLabels)
   {
     viewStyle.setCentreColumnLabels(centreColumnLabels);
@@ -2269,6 +2493,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param showdbrefs
    * @see jalview.api.ViewStyleI#setShowDBRefs(boolean)
    */
+  @Override
   public void setShowDBRefs(boolean showdbrefs)
   {
     viewStyle.setShowDBRefs(showdbrefs);
@@ -2278,6 +2503,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#isShowDBRefs()
    */
+  @Override
   public boolean isShowDBRefs()
   {
     return viewStyle.isShowDBRefs();
@@ -2287,6 +2513,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @return
    * @see jalview.api.ViewStyleI#isShowNPFeats()
    */
+  @Override
   public boolean isShowNPFeats()
   {
     return viewStyle.isShowNPFeats();
@@ -2296,6 +2523,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    * @param shownpfeats
    * @see jalview.api.ViewStyleI#setShowNPFeats(boolean)
    */
+  @Override
   public void setShowNPFeats(boolean shownpfeats)
   {
     viewStyle.setShowNPFeats(shownpfeats);
@@ -2428,6 +2656,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return startRes;
   }
 
+  @Override
   public int getEndRes()
   {
     return endRes;
@@ -2489,7 +2718,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    *          the SearchResults to add to
    * @return the offset (below top of visible region) of the matched sequence
    */
-  protected int findComplementScrollTarget(SearchResults sr)
+  protected int findComplementScrollTarget(SearchResultsI sr)
   {
     final AlignViewportI complement = getCodingComplement();
     if (complement == null || !complement.isFollowHighlight())
@@ -2503,7 +2732,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     {
       return 0;
     }
-    final Set<AlignedCodonFrame> mappings = proteinAlignment
+    final List<AlignedCodonFrame> mappings = proteinAlignment
             .getCodonFrames();
 
     /*
@@ -2527,6 +2756,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
      * all gapped visible regions
      */
     int lastSeq = alignment.getHeight() - 1;
+    List<AlignedCodonFrame> seqMappings = null;
     for (int seqNo = getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++)
     {
       sequence = getAlignment().getSequenceAt(seqNo);
@@ -2538,15 +2768,16 @@ public abstract class AlignmentViewport implements AlignViewportI,
       {
         continue;
       }
-      List<AlignedCodonFrame> seqMappings = MappingUtils
-              .findMappingsForSequence(sequence, mappings);
+      seqMappings = MappingUtils
+              .findMappingsForSequenceAndOthers(sequence, mappings,
+                      getCodingComplement().getAlignment().getSequences());
       if (!seqMappings.isEmpty())
       {
         break;
       }
     }
 
-    if (sequence == null)
+    if (sequence == null || seqMappings == null || seqMappings.isEmpty())
     {
       /*
        * No ungapped mapped sequence in middle column - do nothing
@@ -2554,7 +2785,91 @@ public abstract class AlignmentViewport implements AlignViewportI,
       return 0;
     }
     MappingUtils.addSearchResults(sr, sequence,
-            sequence.findPosition(middleColumn), mappings);
+            sequence.findPosition(middleColumn), seqMappings);
     return seqOffset;
   }
+
+  /**
+   * synthesize a column selection if none exists so it covers the given
+   * selection group. if wholewidth is false, no column selection is made if the
+   * selection group covers the whole alignment width.
+   * 
+   * @param sg
+   * @param wholewidth
+   */
+  public void expandColSelection(SequenceGroup sg, boolean wholewidth)
+  {
+    int sgs, sge;
+    if (sg != null && (sgs = sg.getStartRes()) >= 0
+            && sg.getStartRes() <= (sge = sg.getEndRes())
+            && !this.hasSelectedColumns())
+    {
+      if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
+      {
+        // do nothing
+        return;
+      }
+      if (colSel == null)
+      {
+        colSel = new ColumnSelection();
+      }
+      for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
+      {
+        colSel.addElement(cspos);
+      }
+    }
+  }
+
+  /**
+   * hold status of current selection group - defined on alignment or not.
+   */
+  private boolean selectionIsDefinedGroup = false;
+
+
+  @Override
+  public boolean isSelectionDefinedGroup()
+  {
+    if (selectionGroup == null)
+    {
+      return false;
+    }
+    if (isSelectionGroupChanged(true))
+    {
+      selectionIsDefinedGroup = false;
+      List<SequenceGroup> gps = alignment.getGroups();
+      if (gps == null || gps.size() == 0)
+      {
+        selectionIsDefinedGroup = false;
+      }
+      else
+      {
+        selectionIsDefinedGroup = gps.contains(selectionGroup);
+      }
+    }
+    return selectionGroup.getContext() == alignment
+            || selectionIsDefinedGroup;
+  }
+
+  /**
+   * null, or currently highlighted results on this view
+   */
+  private SearchResultsI searchResults = null;
+
+  @Override
+  public boolean hasSearchResults()
+  {
+    return searchResults != null;
+  }
+
+  @Override
+  public void setSearchResults(SearchResultsI results)
+  {
+    searchResults = results;
+  }
+
+  @Override
+  public SearchResultsI getSearchResults()
+  {
+    return searchResults;
+  }
 }
diff --git a/src/jalview/viewmodel/PCAModel.java b/src/jalview/viewmodel/PCAModel.java
index 54a4925..b857f8c 100644
--- a/src/jalview/viewmodel/PCAModel.java
+++ b/src/jalview/viewmodel/PCAModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/viewmodel/annotationfilter/AnnotationFilterParameter.java b/src/jalview/viewmodel/annotationfilter/AnnotationFilterParameter.java
index 68c5ba8..a6e76bc 100644
--- a/src/jalview/viewmodel/annotationfilter/AnnotationFilterParameter.java
+++ b/src/jalview/viewmodel/annotationfilter/AnnotationFilterParameter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
index fdd40e4..c5f3570 100644
--- a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
+++ b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,19 +21,21 @@
 package jalview.viewmodel.seqfeatures;
 
 import jalview.api.AlignViewportI;
+import jalview.api.FeatureColourI;
 import jalview.api.FeaturesDisplayedI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.seqfeatures.FeatureRenderer;
-import jalview.schemes.GraduatedColor;
-import jalview.viewmodel.AlignmentViewport;
+import jalview.schemes.FeatureColour;
+import jalview.schemes.UserColourScheme;
 
 import java.awt.Color;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
@@ -50,19 +52,20 @@ public abstract class FeatureRendererModel implements
    */
   protected float transparency = 1.0f;
 
-  protected Map<String, Object> featureColours = new ConcurrentHashMap<String, Object>();
+  protected Map<String, FeatureColourI> featureColours = new ConcurrentHashMap<String, FeatureColourI>();
 
   protected Map<String, Boolean> featureGroups = new ConcurrentHashMap<String, Boolean>();
 
-  protected Object currentColour;
-
   protected String[] renderOrder;
 
+  Map<String, Float> featureOrder = null;
+
   protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
           this);
 
-  protected AlignmentViewport av;
+  protected AlignViewportI av;
 
+  @Override
   public AlignViewportI getViewport()
   {
     return av;
@@ -188,9 +191,9 @@ public abstract class FeatureRendererModel implements
     renderOrder = neworder;
   }
 
-  protected Hashtable minmax = new Hashtable();
+  protected Map<String, float[][]> minmax = new Hashtable<String, float[][]>();
 
-  public Hashtable getMinMax()
+  public Map<String, float[][]> getMinMax()
   {
     return minmax;
   }
@@ -204,7 +207,7 @@ public abstract class FeatureRendererModel implements
    */
   protected final byte[] normaliseScore(SequenceFeature sequenceFeature)
   {
-    float[] mm = ((float[][]) minmax.get(sequenceFeature.type))[0];
+    float[] mm = minmax.get(sequenceFeature.type)[0];
     final byte[] r = new byte[] { 0, (byte) 255 };
     if (mm != null)
     {
@@ -285,8 +288,12 @@ public abstract class FeatureRendererModel implements
           continue;
         }
 
-        if ((features[i].getBegin() <= res)
-                && (features[i].getEnd() >= res))
+        // check if start/end are at res, and if not a contact feature, that res
+        // lies between start and end
+        if ((features[i].getBegin() == res || features[i].getEnd() == res)
+                || (!features[i].isContactFeature()
+                        && (features[i].getBegin() < res) && (features[i]
+                        .getEnd() >= res)))
         {
           tmp.add(features[i]);
         }
@@ -335,7 +342,7 @@ public abstract class FeatureRendererModel implements
     }
     if (minmax == null)
     {
-      minmax = new Hashtable();
+      minmax = new Hashtable<String, float[][]>();
     }
     AlignmentI alignment = av.getAlignment();
     for (int i = 0; i < alignment.getHeight(); i++)
@@ -390,7 +397,7 @@ public abstract class FeatureRendererModel implements
         if (!Float.isNaN(features[index].score))
         {
           int nonpos = features[index].getBegin() >= 1 ? 0 : 1;
-          float[][] mm = (float[][]) minmax.get(features[index].getType());
+          float[][] mm = minmax.get(features[index].getType());
           if (mm == null)
           {
             mm = new float[][] { null, null };
@@ -439,7 +446,6 @@ public abstract class FeatureRendererModel implements
     List<String> allfeatures = new ArrayList<String>(allFeatures);
     String[] oldRender = renderOrder;
     renderOrder = new String[allfeatures.size()];
-    Object mmrange, fc = null;
     boolean initOrders = (featureOrder == null);
     int opos = 0;
     if (oldRender != null && oldRender.length > 0)
@@ -459,16 +465,13 @@ public abstract class FeatureRendererModel implements
             allfeatures.remove(oldRender[j]);
             if (minmax != null)
             {
-              mmrange = minmax.get(oldRender[j]);
+              float[][] mmrange = minmax.get(oldRender[j]);
               if (mmrange != null)
               {
-                fc = featureColours.get(oldRender[j]);
-                if (fc != null && fc instanceof GraduatedColor
-                        && ((GraduatedColor) fc).isAutoScale())
+                FeatureColourI fc = featureColours.get(oldRender[j]);
+                if (fc != null && !fc.isSimpleColour() && fc.isAutoScaled())
                 {
-                  ((GraduatedColor) fc).updateBounds(
-                          ((float[][]) mmrange)[0][0],
-                          ((float[][]) mmrange)[0][1]);
+                  fc.updateBounds(mmrange[0][0], mmrange[0][1]);
                 }
               }
             }
@@ -492,15 +495,13 @@ public abstract class FeatureRendererModel implements
       if (minmax != null)
       {
         // update from new features minmax if necessary
-        mmrange = minmax.get(newf[i]);
+        float[][] mmrange = minmax.get(newf[i]);
         if (mmrange != null)
         {
-          fc = featureColours.get(newf[i]);
-          if (fc != null && fc instanceof GraduatedColor
-                  && ((GraduatedColor) fc).isAutoScale())
+          FeatureColourI fc = featureColours.get(newf[i]);
+          if (fc != null && !fc.isSimpleColour() && fc.isAutoScaled())
           {
-            ((GraduatedColor) fc).updateBounds(((float[][]) mmrange)[0][0],
-                    ((float[][]) mmrange)[0][1]);
+            fc.updateBounds(mmrange[0][0], mmrange[0][1]);
           }
         }
       }
@@ -512,7 +513,7 @@ public abstract class FeatureRendererModel implements
         setOrder(newf[i], i / (float) denom);
       }
       // set order from newly found feature from persisted ordering.
-      sortOrder[i] = 2 - ((Float) featureOrder.get(newf[i])).floatValue();
+      sortOrder[i] = 2 - featureOrder.get(newf[i]).floatValue();
       if (i < iSize)
       {
         // only sort if we need to
@@ -530,51 +531,25 @@ public abstract class FeatureRendererModel implements
 
   /**
    * get a feature style object for the given type string. Creates a
-   * java.awt.Color for a featureType with no existing colourscheme. TODO:
-   * replace return type with object implementing standard abstract colour/style
-   * interface
+   * java.awt.Color for a featureType with no existing colourscheme.
    * 
    * @param featureType
-   * @return java.awt.Color or GraduatedColor
+   * @return
    */
-  public Object getFeatureStyle(String featureType)
+  @Override
+  public FeatureColourI getFeatureStyle(String featureType)
   {
-    Object fc = featureColours.get(featureType);
+    FeatureColourI fc = featureColours.get(featureType);
     if (fc == null)
     {
-      jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
-      Color col = ucs.createColourFromName(featureType);
-      featureColours.put(featureType, fc = col);
+      Color col = UserColourScheme.createColourFromName(featureType);
+      fc = new FeatureColour(col);
+      featureColours.put(featureType, fc);
     }
     return fc;
   }
 
   /**
-   * return a nominal colour for this feature
-   * 
-   * @param featureType
-   * @return standard color, or maximum colour for graduated colourscheme
-   */
-  public Color getColour(String featureType)
-  {
-    Object fc = getFeatureStyle(featureType);
-
-    if (fc instanceof Color)
-    {
-      return (Color) fc;
-    }
-    else
-    {
-      if (fc instanceof GraduatedColor)
-      {
-        return ((GraduatedColor) fc).getMaxColor();
-      }
-    }
-    throw new Error("Implementation Error: Unrecognised render object "
-            + fc.getClass() + " for features of type " + featureType);
-  }
-
-  /**
    * calculate the render colour for a specific feature using current feature
    * settings.
    * 
@@ -583,51 +558,32 @@ public abstract class FeatureRendererModel implements
    */
   public Color getColour(SequenceFeature feature)
   {
-    Object fc = getFeatureStyle(feature.getType());
-    if (fc instanceof Color)
-    {
-      return (Color) fc;
-    }
-    else
-    {
-      if (fc instanceof GraduatedColor)
-      {
-        return ((GraduatedColor) fc).findColor(feature);
-      }
-    }
-    throw new Error("Implementation Error: Unrecognised render object "
-            + fc.getClass() + " for features of type " + feature.getType());
+    FeatureColourI fc = getFeatureStyle(feature.getType());
+    return fc.getColor(feature);
   }
 
   protected boolean showFeature(SequenceFeature sequenceFeature)
   {
-    Object fc = getFeatureStyle(sequenceFeature.type);
-    if (fc instanceof GraduatedColor)
-    {
-      return ((GraduatedColor) fc).isColored(sequenceFeature);
-    }
-    else
-    {
-      return true;
-    }
+    FeatureColourI fc = getFeatureStyle(sequenceFeature.type);
+    return fc.isColored(sequenceFeature);
   }
 
+  /**
+   * Answers true if the feature type is currently selected to be displayed,
+   * else false
+   * 
+   * @param type
+   * @return
+   */
   protected boolean showFeatureOfType(String type)
   {
-    return av.getFeaturesDisplayed().isVisible(type);
+    return type == null ? false : av.getFeaturesDisplayed().isVisible(type);
   }
 
-  public void setColour(String featureType, Object col)
+  @Override
+  public void setColour(String featureType, FeatureColourI col)
   {
-    // overwrite
-    // Color _col = (col instanceof Color) ? ((Color) col) : (col instanceof
-    // GraduatedColor) ? ((GraduatedColor) col).getMaxColor() : null;
-    // Object c = featureColours.get(featureType);
-    // if (c == null || c instanceof Color || (c instanceof GraduatedColor &&
-    // !((GraduatedColor)c).getMaxColor().equals(_col)))
-    {
-      featureColours.put(featureType, col);
-    }
+    featureColours.put(featureType, col);
   }
 
   public void setTransparency(float value)
@@ -640,8 +596,6 @@ public abstract class FeatureRendererModel implements
     return transparency;
   }
 
-  Map featureOrder = null;
-
   /**
    * analogous to colour - store a normalized ordering for all feature types in
    * this rendering context.
@@ -656,7 +610,7 @@ public abstract class FeatureRendererModel implements
   {
     if (featureOrder == null)
     {
-      featureOrder = new Hashtable();
+      featureOrder = new Hashtable<String, Float>();
     }
     featureOrder.put(type, new Float(position));
     return position;
@@ -674,16 +628,16 @@ public abstract class FeatureRendererModel implements
     {
       if (featureOrder.containsKey(type))
       {
-        return ((Float) featureOrder.get(type)).floatValue();
+        return featureOrder.get(type).floatValue();
       }
     }
     return -1;
   }
 
   @Override
-  public Map<String, Object> getFeatureColours()
+  public Map<String, FeatureColourI> getFeatureColours()
   {
-    return new ConcurrentHashMap<String, Object>(featureColours);
+    return featureColours;
   }
 
   /**
@@ -691,21 +645,32 @@ public abstract class FeatureRendererModel implements
    * 
    * @param data
    *          { String(Type), Colour(Type), Boolean(Displayed) }
+   * @return true if any visible features have been reordered, else false
    */
-  public void setFeaturePriority(Object[][] data)
+  public boolean setFeaturePriority(Object[][] data)
   {
-    setFeaturePriority(data, true);
+    return setFeaturePriority(data, true);
   }
 
   /**
+   * Sets the priority order for features
    * 
    * @param data
    *          { String(Type), Colour(Type), Boolean(Displayed) }
    * @param visibleNew
    *          when true current featureDisplay list will be cleared
+   * @return true if any visible features have been reordered or recoloured,
+   *         else false (i.e. no need to repaint)
    */
-  public void setFeaturePriority(Object[][] data, boolean visibleNew)
+  public boolean setFeaturePriority(Object[][] data, boolean visibleNew)
   {
+    /*
+     * note visible feature ordering and colours before update
+     */
+    List<String> visibleFeatures = getDisplayedFeatureTypes();
+    Map<String, FeatureColourI> visibleColours = new HashMap<String, FeatureColourI>(
+            getFeatureColours());
+
     FeaturesDisplayedI av_featuresdisplayed = null;
     if (visibleNew)
     {
@@ -724,10 +689,10 @@ public abstract class FeatureRendererModel implements
     }
     if (data == null)
     {
-      return;
+      return false;
     }
     // The feature table will display high priority
-    // features at the top, but theses are the ones
+    // features at the top, but these are the ones
     // we need to render last, so invert the data
     renderOrder = new String[data.length];
 
@@ -736,8 +701,7 @@ public abstract class FeatureRendererModel implements
       for (int i = 0; i < data.length; i++)
       {
         String type = data[i][0].toString();
-        setColour(type, data[i][1]); // todo : typesafety - feature color
-        // interface object
+        setColour(type, (FeatureColourI) data[i][1]);
         if (((Boolean) data[i][2]).booleanValue())
         {
           av_featuresdisplayed.setVisible(type);
@@ -747,6 +711,30 @@ public abstract class FeatureRendererModel implements
       }
     }
 
+    /*
+     * get the new visible ordering and return true if it has changed
+     * order or any colour has changed
+     */
+    List<String> reorderedVisibleFeatures = getDisplayedFeatureTypes();
+    if (!visibleFeatures.equals(reorderedVisibleFeatures))
+    {
+      /*
+       * the list of ordered visible features has changed
+       */
+      return true;
+    }
+
+    /*
+     * return true if any feature colour has changed
+     */
+    for (String feature : visibleFeatures)
+    {
+      if (visibleColours.get(feature) != getFeatureStyle(feature))
+      {
+        return true;
+      }
+    }
+    return false;
   }
 
   /**
@@ -767,7 +755,7 @@ public abstract class FeatureRendererModel implements
     changeSupport.removePropertyChangeListener(listener);
   }
 
-  public Set getAllFeatureColours()
+  public Set<String> getAllFeatureColours()
   {
     return featureColours.keySet();
   }
@@ -782,6 +770,9 @@ public abstract class FeatureRendererModel implements
     return renderOrder != null;
   }
 
+  /**
+   * Returns feature types in ordering of rendering, where last means on top
+   */
   public List<String> getRenderOrder()
   {
     if (renderOrder == null)
@@ -835,9 +826,9 @@ public abstract class FeatureRendererModel implements
   {
     if (featureGroups != null)
     {
-      ArrayList gp = new ArrayList();
+      List<String> gp = new ArrayList<String>();
 
-      for (Object grp : featureGroups.keySet())
+      for (String grp : featureGroups.keySet())
       {
         Boolean state = featureGroups.get(grp);
         if (state.booleanValue() == visible)
@@ -879,19 +870,19 @@ public abstract class FeatureRendererModel implements
   }
 
   @Override
-  public Hashtable getDisplayedFeatureCols()
+  public Map<String, FeatureColourI> getDisplayedFeatureCols()
   {
-    Hashtable fcols = new Hashtable();
+    Map<String, FeatureColourI> fcols = new Hashtable<String, FeatureColourI>();
     if (getViewport().getFeaturesDisplayed() == null)
     {
       return fcols;
     }
-    Iterator<String> en = getViewport().getFeaturesDisplayed()
+    Iterator<String> features = getViewport().getFeaturesDisplayed()
             .getVisibleFeatures();
-    while (en.hasNext())
+    while (features.hasNext())
     {
-      String col = en.next();
-      fcols.put(col, getColour(col));
+      String feature = features.next();
+      fcols.put(feature, getFeatureStyle(feature));
     }
     return fcols;
   }
@@ -902,39 +893,39 @@ public abstract class FeatureRendererModel implements
     return av.getFeaturesDisplayed();
   }
 
+  /**
+   * Returns a (possibly empty) list of visible feature types, in render order
+   * (last is on top)
+   */
   @Override
-  public String[] getDisplayedFeatureTypes()
+  public List<String> getDisplayedFeatureTypes()
   {
-    String[] typ = null;
-    typ = getRenderOrder().toArray(new String[0]);
+    List<String> typ = getRenderOrder();
+    List<String> displayed = new ArrayList<String>();
     FeaturesDisplayedI feature_disp = av.getFeaturesDisplayed();
     if (feature_disp != null)
     {
       synchronized (feature_disp)
       {
-        for (int i = 0; i < typ.length; i++)
+        for (String type : typ)
         {
-          if (!feature_disp.isVisible(typ[i]))
+          if (feature_disp.isVisible(type))
           {
-            typ[i] = null;
+            displayed.add(type);
           }
         }
       }
     }
-    return typ;
+    return displayed;
   }
 
   @Override
-  public String[] getDisplayedFeatureGroups()
+  public List<String> getDisplayedFeatureGroups()
   {
-    String[] gps = null;
-    ArrayList<String> _gps = new ArrayList<String>();
-    Iterator en = getFeatureGroups().iterator();
-    int g = 0;
+    List<String> _gps = new ArrayList<String>();
     boolean valid = false;
-    while (en.hasNext())
+    for (String gp : getFeatureGroups())
     {
-      String gp = (String) en.next();
       if (checkGroupVisibility(gp, false))
       {
         valid = true;
@@ -946,11 +937,11 @@ public abstract class FeatureRendererModel implements
       }
       else
       {
-        gps = new String[_gps.size()];
-        _gps.toArray(gps);
+        // gps = new String[_gps.size()];
+        // _gps.toArray(gps);
       }
     }
-    return gps;
+    return _gps;
   }
 
 }
diff --git a/src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java b/src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java
index 93884f5..964b6ca 100644
--- a/src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java
+++ b/src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,10 +20,10 @@
  */
 package jalview.viewmodel.seqfeatures;
 
-import jalview.schemes.GraduatedColor;
+import jalview.api.FeatureColourI;
+import jalview.schemes.FeatureColour;
 
 import java.util.Arrays;
-import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -32,24 +32,33 @@ public class FeatureRendererSettings implements Cloneable
 {
   String[] renderOrder;
 
-  Map featureGroups;
+  /*
+   * map of {groupName, isDisplayed}
+   */
+  Map<String, Boolean> featureGroups;
 
-  Map featureColours;
+  /*
+   * map of {featureType, colourScheme}
+   */
+  Map<String, FeatureColourI> featureColours;
 
   float transparency;
 
-  Map featureOrder;
+  Map<String, Float> featureOrder;
 
   public FeatureRendererSettings(String[] renderOrder,
-          Hashtable featureGroups, Hashtable featureColours,
-          float transparency, Hashtable featureOrder)
+          Map<String, Boolean> featureGroups,
+          Map<String, FeatureColourI> featureColours, float transparency,
+          Map<String, Float> featureOrder)
   {
     super();
     this.renderOrder = Arrays.copyOf(renderOrder, renderOrder.length);
-    this.featureGroups = new ConcurrentHashMap(featureGroups);
-    this.featureColours = new ConcurrentHashMap(featureColours);
+    this.featureGroups = new ConcurrentHashMap<String, Boolean>(
+            featureGroups);
+    this.featureColours = new ConcurrentHashMap<String, FeatureColourI>(
+            featureColours);
     this.transparency = transparency;
-    this.featureOrder = new ConcurrentHashMap(featureOrder);
+    this.featureOrder = new ConcurrentHashMap<String, Float>(featureOrder);
   }
 
   /**
@@ -61,9 +70,9 @@ public class FeatureRendererSettings implements Cloneable
           jalview.viewmodel.seqfeatures.FeatureRendererModel fr)
   {
     renderOrder = null;
-    featureGroups = new ConcurrentHashMap();
-    featureColours = new ConcurrentHashMap();
-    featureOrder = new ConcurrentHashMap();
+    featureGroups = new ConcurrentHashMap<String, Boolean>();
+    featureColours = new ConcurrentHashMap<String, FeatureColourI>();
+    featureOrder = new ConcurrentHashMap<String, Float>();
     if (fr.renderOrder != null)
     {
       this.renderOrder = new String[fr.renderOrder.length];
@@ -72,26 +81,30 @@ public class FeatureRendererSettings implements Cloneable
     }
     if (fr.featureGroups != null)
     {
-      this.featureGroups = new ConcurrentHashMap(fr.featureGroups);
+      this.featureGroups = new ConcurrentHashMap<String, Boolean>(
+              fr.featureGroups);
     }
     if (fr.featureColours != null)
     {
-      this.featureColours = new ConcurrentHashMap(fr.featureColours);
+      this.featureColours = new ConcurrentHashMap<String, FeatureColourI>(
+              fr.featureColours);
     }
-    Iterator en = fr.featureColours.keySet().iterator();
+    Iterator<String> en = fr.featureColours.keySet().iterator();
     while (en.hasNext())
     {
-      Object next = en.next();
-      Object val = featureColours.get(next);
-      if (val instanceof GraduatedColor)
+      String next = en.next();
+      FeatureColourI val = featureColours.get(next);
+      // if (val instanceof GraduatedColor)
+      if (val.isGraduatedColour() || val.isColourByLabel()) // why this test?
       {
-        featureColours.put(next, new GraduatedColor((GraduatedColor) val));
+        featureColours.put(next, new FeatureColour((FeatureColour) val));
       }
     }
     this.transparency = fr.transparency;
     if (fr.featureOrder != null)
     {
-      this.featureOrder = new ConcurrentHashMap(fr.featureOrder);
+      this.featureOrder = new ConcurrentHashMap<String, Float>(
+              fr.featureOrder);
     }
   }
 }
diff --git a/src/jalview/viewmodel/seqfeatures/FeatureSettingsModel.java b/src/jalview/viewmodel/seqfeatures/FeatureSettingsModel.java
index a311bc5..48df144 100644
--- a/src/jalview/viewmodel/seqfeatures/FeatureSettingsModel.java
+++ b/src/jalview/viewmodel/seqfeatures/FeatureSettingsModel.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java b/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java
index 81a06a3..925a4cf 100644
--- a/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java
+++ b/src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -62,7 +62,7 @@ public class FeaturesDisplayed implements FeaturesDisplayedI
   }
 
   @Override
-  public boolean areVisible(Collection featureTypes)
+  public boolean areVisible(Collection<String> featureTypes)
   {
     return featuresDisplayed.containsAll(featureTypes);
   }
@@ -75,7 +75,7 @@ public class FeaturesDisplayed implements FeaturesDisplayedI
   }
 
   @Override
-  public void setAllVisible(Collection makeVisible)
+  public void setAllVisible(Collection<String> makeVisible)
   {
     featuresDisplayed.addAll(makeVisible);
     featuresRegistered.addAll(makeVisible);
@@ -107,7 +107,7 @@ public class FeaturesDisplayed implements FeaturesDisplayedI
   }
 
   @Override
-  public int getRegisterdFeaturesCount()
+  public int getRegisteredFeaturesCount()
   {
     return featuresRegistered.size();
   }
diff --git a/src/jalview/viewmodel/styles/ViewStyle.java b/src/jalview/viewmodel/styles/ViewStyle.java
index a0ba633..036b987 100644
--- a/src/jalview/viewmodel/styles/ViewStyle.java
+++ b/src/jalview/viewmodel/styles/ViewStyle.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/workers/AlignCalcManager.java b/src/jalview/workers/AlignCalcManager.java
index 2fa0553..66be7cd 100644
--- a/src/jalview/workers/AlignCalcManager.java
+++ b/src/jalview/workers/AlignCalcManager.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -34,23 +34,48 @@ import java.util.Set;
 
 public class AlignCalcManager implements AlignCalcManagerI
 {
-  private volatile List<AlignCalcWorkerI> restartable = Collections
-          .synchronizedList(new ArrayList<AlignCalcWorkerI>());
+  /*
+   * list of registered workers
+   */
+  private volatile List<AlignCalcWorkerI> restartable;
 
-  private volatile List<Class> blackList = Collections
-          .synchronizedList(new ArrayList<Class>());
+  /*
+   * types of worker _not_ to run (for example, because they have
+   * previously thrown errors)
+   */
+  private volatile List<Class<? extends AlignCalcWorkerI>> blackList;
 
-  /**
+  /*
    * global record of calculations in progress
    */
-  private volatile Map<Class, AlignCalcWorkerI> inProgress = Collections
-          .synchronizedMap(new Hashtable<Class, AlignCalcWorkerI>());
+  private volatile List<AlignCalcWorkerI> inProgress;
 
-  /**
+  /*
    * record of calculations pending or in progress in the current context
    */
-  private volatile Map<Class, List<AlignCalcWorkerI>> updating = Collections
-          .synchronizedMap(new Hashtable<Class, List<AlignCalcWorkerI>>());
+  private volatile Map<Class<? extends AlignCalcWorkerI>, List<AlignCalcWorkerI>> updating;
+
+  /*
+   * workers that have run to completion so are candidates for visual-only 
+   * update of their results
+   */
+  private HashSet<AlignCalcWorkerI> canUpdate;
+
+  /**
+   * Constructor
+   */
+  public AlignCalcManager()
+  {
+    restartable = Collections
+            .synchronizedList(new ArrayList<AlignCalcWorkerI>());
+    blackList = Collections
+            .synchronizedList(new ArrayList<Class<? extends AlignCalcWorkerI>>());
+    inProgress = Collections
+            .synchronizedList(new ArrayList<AlignCalcWorkerI>());
+    updating = Collections
+            .synchronizedMap(new Hashtable<Class<? extends AlignCalcWorkerI>, List<AlignCalcWorkerI>>());
+    canUpdate = new HashSet<AlignCalcWorkerI>();
+  }
 
   @Override
   public void notifyStart(AlignCalcWorkerI worker)
@@ -72,15 +97,6 @@ public class AlignCalcManager implements AlignCalcManagerI
     }
   }
 
-  @Override
-  public boolean alreadyDoing(AlignCalcWorkerI worker)
-  {
-    synchronized (inProgress)
-    {
-      return inProgress.containsKey(worker.getClass());
-    }
-  }
-
   /*
    * (non-Javadoc)
    * 
@@ -108,52 +124,30 @@ public class AlignCalcManager implements AlignCalcManagerI
     }
   }
 
-  // TODO make into api method if needed ?
-  public int numberLive(AlignCalcWorkerI worker)
-  {
-    synchronized (updating)
-    {
-      List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
-      if (upd == null)
-      {
-        return 0;
-      }
-      ;
-      return upd.size();
-    }
-  }
-
   @Override
   public boolean notifyWorking(AlignCalcWorkerI worker)
   {
     synchronized (inProgress)
     {
-      // TODO: decide if we should throw exceptions here if multiple workers
-      // start to work
-      if (inProgress.get(worker.getClass()) != null)
+      if (inProgress.contains(worker))
       {
-        if (false)
-        {
-          System.err
-                  .println("Warning: Multiple workers are running of type "
-                          + worker.getClass());
-        }
-        return false;
+        return false; // worker is already working, so ask caller to wait around
+      }
+      else
+      {
+        inProgress.add(worker);
       }
-      inProgress.put(worker.getClass(), worker);
     }
     return true;
   }
 
-  private final HashSet<AlignCalcWorkerI> canUpdate = new HashSet<AlignCalcWorkerI>();
-
   @Override
   public void workerComplete(AlignCalcWorkerI worker)
   {
     synchronized (inProgress)
     {
-      // System.err.println("Worker "+worker.getClass()+" marked as complete.");
-      inProgress.remove(worker.getClass());
+      // System.err.println("Worker " + worker + " marked as complete.");
+      inProgress.remove(worker);
       List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
       if (upd != null)
       {
@@ -167,7 +161,7 @@ public class AlignCalcManager implements AlignCalcManagerI
   }
 
   @Override
-  public void workerCannotRun(AlignCalcWorkerI worker)
+  public void disableWorker(AlignCalcWorkerI worker)
   {
     synchronized (blackList)
     {
@@ -175,22 +169,24 @@ public class AlignCalcManager implements AlignCalcManagerI
     }
   }
 
-  public boolean isBlackListed(Class workerType)
+  @Override
+  public boolean isDisabled(AlignCalcWorkerI worker)
   {
     synchronized (blackList)
     {
-      return blackList.contains(workerType);
+      return blackList.contains(worker.getClass());
     }
   }
 
   @Override
   public void startWorker(AlignCalcWorkerI worker)
   {
-    // System.err.println("Starting "+worker.getClass());
-    // new Exception("").printStackTrace();
-    Thread tw = new Thread(worker);
-    tw.setName(worker.getClass().toString());
-    tw.start();
+    if (!isDisabled(worker))
+    {
+      Thread tw = new Thread(worker);
+      tw.setName(worker.getClass().toString());
+      tw.start();
+    }
   }
 
   @Override
@@ -199,7 +195,7 @@ public class AlignCalcManager implements AlignCalcManagerI
     synchronized (inProgress)
     {// System.err.println("isWorking : worker "+(worker!=null ?
      // worker.getClass():"null")+ " "+hashCode());
-      return worker != null && inProgress.get(worker.getClass()) == worker;
+      return worker != null && inProgress.contains(worker);
     }
   }
 
@@ -243,7 +239,7 @@ public class AlignCalcManager implements AlignCalcManagerI
   {
     synchronized (inProgress)
     {
-      for (AlignCalcWorkerI worker : inProgress.values())
+      for (AlignCalcWorkerI worker : inProgress)
       {
         if (worker.involves(alignmentAnnotation))
         {
@@ -268,7 +264,8 @@ public class AlignCalcManager implements AlignCalcManagerI
   }
 
   @Override
-  public void updateAnnotationFor(Class workerClass)
+  public void updateAnnotationFor(
+          Class<? extends AlignCalcWorkerI> workerClass)
   {
 
     AlignCalcWorkerI[] workers;
@@ -287,62 +284,35 @@ public class AlignCalcManager implements AlignCalcManagerI
 
   @Override
   public List<AlignCalcWorkerI> getRegisteredWorkersOfClass(
-          Class workerClass)
+          Class<? extends AlignCalcWorkerI> workerClass)
   {
     List<AlignCalcWorkerI> workingClass = new ArrayList<AlignCalcWorkerI>();
-    AlignCalcWorkerI[] workers;
     synchronized (canUpdate)
     {
-      workers = canUpdate.toArray(new AlignCalcWorkerI[canUpdate.size()]);
-    }
-    for (AlignCalcWorkerI worker : workers)
-    {
-      if (workerClass.equals(worker.getClass()))
+      for (AlignCalcWorkerI worker : canUpdate)
       {
-        workingClass.add(worker);
+        if (workerClass.equals(worker.getClass()))
+        {
+          workingClass.add(worker);
+        }
       }
     }
     return (workingClass.size() == 0) ? null : workingClass;
   }
 
   @Override
-  public boolean startRegisteredWorkersOfClass(Class workerClass)
-  {
-    List<AlignCalcWorkerI> workers = getRegisteredWorkersOfClass(workerClass);
-    if (workers == null)
-    {
-      return false;
-    }
-    for (AlignCalcWorkerI worker : workers)
-    {
-      if (!isPending(worker))
-      {
-        startWorker(worker);
-      }
-      else
-      {
-        System.err.println("Pending exists for " + workerClass);
-      }
-    }
-    return true;
-  }
-
-  @Override
-  public void workerMayRun(AlignCalcWorkerI worker)
+  public void enableWorker(AlignCalcWorkerI worker)
   {
     synchronized (blackList)
     {
-      if (blackList.contains(worker.getClass()))
-      {
-        blackList.remove(worker.getClass());
-      }
+      blackList.remove(worker.getClass());
     }
   }
 
   @Override
-  public void removeRegisteredWorkersOfClass(Class typeToRemove)
+  public void removeRegisteredWorkersOfClass(
+          Class<? extends AlignCalcWorkerI> typeToRemove)
   {
-    List<AlignCalcWorkerI> workers = getRegisteredWorkersOfClass(typeToRemove);
     List<AlignCalcWorkerI> removable = new ArrayList<AlignCalcWorkerI>();
     Set<AlignCalcWorkerI> toremovannot = new HashSet<AlignCalcWorkerI>();
     synchronized (restartable)
@@ -382,4 +352,48 @@ public class AlignCalcManager implements AlignCalcManagerI
      * else { System.err.println("Pending exists for " + workerClass); } }
      */
   }
+
+  /**
+   * Deletes the worker that update the given annotation, provided it is marked
+   * as deletable.
+   */
+  @Override
+  public void removeWorkerForAnnotation(AlignmentAnnotation ann)
+  {
+    /*
+     * first just find those to remove (to avoid
+     * ConcurrentModificationException)
+     */
+    List<AlignCalcWorkerI> toRemove = new ArrayList<AlignCalcWorkerI>();
+    for (AlignCalcWorkerI worker : restartable)
+    {
+      if (worker.involves(ann))
+      {
+        if (worker.isDeletable())
+        {
+          toRemove.add(worker);
+        }
+      }
+    }
+
+    /*
+     * remove all references to deleted workers so any references 
+     * they hold to annotation data can be garbage collected 
+     */
+    for (AlignCalcWorkerI worker : toRemove)
+    {
+      restartable.remove(worker);
+      blackList.remove(worker.getClass());
+      inProgress.remove(worker);
+      canUpdate.remove(worker);
+      synchronized (updating)
+      {
+        List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
+        if (upd != null)
+        {
+          upd.remove(worker);
+        }
+      }
+    }
+  }
 }
diff --git a/src/jalview/workers/AlignCalcWorker.java b/src/jalview/workers/AlignCalcWorker.java
index 6bdf1c2..9a1acfb 100644
--- a/src/jalview/workers/AlignCalcWorker.java
+++ b/src/jalview/workers/AlignCalcWorker.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -26,6 +26,7 @@ import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
 
 import java.util.List;
 
@@ -46,7 +47,7 @@ public abstract class AlignCalcWorker implements AlignCalcWorkerI
 
   protected AlignmentViewPanel ap;
 
-  protected List<AlignmentAnnotation> ourAnnots = null;
+  protected List<AlignmentAnnotation> ourAnnots;
 
   public AlignCalcWorker(AlignViewportI alignViewport,
           AlignmentViewPanel alignPanel)
@@ -68,17 +69,18 @@ public abstract class AlignCalcWorker implements AlignCalcWorkerI
 
   }
 
+  @Override
   public boolean involves(AlignmentAnnotation i)
   {
     return ourAnnots != null && ourAnnots.contains(i);
   }
 
   /**
-   * permanently remove from the alignment all annotation rows managed by this
+   * Permanently removes from the alignment all annotation rows managed by this
    * worker
    */
   @Override
-  public void removeOurAnnotation()
+  public void removeAnnotation()
   {
     if (ourAnnots != null && alignViewport != null)
     {
@@ -90,9 +92,48 @@ public abstract class AlignCalcWorker implements AlignCalcWorkerI
           alignment.deleteAnnotation(aa, true);
         }
       }
+      ourAnnots.clear();
     }
   }
+
   // TODO: allow GUI to query workers associated with annotation to add items to
   // annotation label panel popup menu
 
+  @Override
+  public boolean isDeletable()
+  {
+    return false;
+  }
+
+  /**
+   * Calculate min and max values of annotations and set as graphMin, graphMax
+   * on the AlignmentAnnotation. This is needed because otherwise, well, bad
+   * things happen.
+   * 
+   * @param ann
+   * @param anns
+   */
+  protected void setGraphMinMax(AlignmentAnnotation ann, Annotation[] anns)
+  {
+    // TODO feels like this belongs inside AlignmentAnnotation!
+    float max = Float.MIN_VALUE;
+    float min = Float.MAX_VALUE;
+    boolean set = false;
+    for (Annotation a : anns)
+    {
+      if (a != null)
+      {
+        set = true;
+        float val = a.value;
+        max = Math.max(max, val);
+        min = Math.min(min, val);
+      }
+    }
+    if (set)
+    {
+      ann.graphMin = min;
+      ann.graphMax = max;
+    }
+  }
+
 }
diff --git a/src/jalview/workers/AlignmentAnnotationFactory.java b/src/jalview/workers/AlignmentAnnotationFactory.java
new file mode 100644
index 0000000..ac61f9f
--- /dev/null
+++ b/src/jalview/workers/AlignmentAnnotationFactory.java
@@ -0,0 +1,148 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.workers;
+
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.bin.Jalview;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Annotation;
+import jalview.gui.AlignFrame;
+
+import java.awt.Color;
+
+/**
+ * Factory class with methods which allow clients (including external scripts
+ * such as Groovy) to 'register and forget' an alignment annotation calculator. <br>
+ * Currently supports two flavours of calculator:
+ * <ul>
+ * <li>a simple 'feature counter' which counts any desired score derivable from
+ * residue value and any sequence features at each position of the alignment</li>
+ * <li>a 'general purpose' calculator which computes one or more complete
+ * AlignmentAnnotation objects</li>
+ * </ul>
+ */
+public class AlignmentAnnotationFactory
+{
+  /**
+   * Constructs and registers a new alignment annotation worker
+   * 
+   * @param counter
+   *          provider of feature counts per alignment position
+   */
+  public static void newCalculator(FeatureCounterI counter)
+  {
+    // TODO need an interface for AlignFrame by which to access
+    // its AlignViewportI and AlignmentViewPanel
+    AlignmentViewPanel currentAlignFrame = Jalview.getCurrentAlignFrame().alignPanel;
+    if (currentAlignFrame != null)
+    {
+      newCalculator(currentAlignFrame.getAlignViewport(),
+              currentAlignFrame, counter);
+    }
+    else
+    {
+      System.err
+              .println("Can't register calculator as no alignment window has focus");
+    }
+  }
+
+  /**
+   * Constructs and registers a new alignment annotation worker
+   * 
+   * @param viewport
+   * @param panel
+   * @param counter
+   *          provider of feature counts per alignment position
+   */
+  public static void newCalculator(AlignViewportI viewport,
+          AlignmentViewPanel panel, FeatureCounterI counter)
+  {
+    new ColumnCounterWorker(viewport, panel, counter);
+  }
+
+  /**
+   * Constructs and registers a new alignment annotation worker
+   * 
+   * @param calculator
+   *          provider of AlignmentAnnotation for the alignment
+   */
+  public static void newCalculator(AnnotationProviderI calculator)
+  {
+    // TODO need an interface for AlignFrame by which to access
+    // its AlignViewportI and AlignmentViewPanel
+    AlignFrame currentAlignFrame = Jalview.getCurrentAlignFrame();
+    if (currentAlignFrame != null)
+    {
+      newCalculator(currentAlignFrame.getViewport(), currentAlignFrame
+              .getAlignPanels().get(0), calculator);
+    }
+    else
+    {
+      System.err
+              .println("Can't register calculator as no alignment window has focus");
+    }
+  }
+
+  /**
+   * Constructs and registers a new alignment annotation worker
+   * 
+   * @param viewport
+   * @param panel
+   * @param calculator
+   *          provider of AlignmentAnnotation for the alignment
+   */
+  public static void newCalculator(AlignViewportI viewport,
+          AlignmentViewPanel panel, AnnotationProviderI calculator)
+  {
+    new AnnotationWorker(viewport, panel, calculator);
+  }
+
+  /**
+   * Factory method to construct an Annotation object
+   * 
+   * @param displayChar
+   * @param desc
+   * @param secondaryStructure
+   * @param val
+   * @param color
+   * @return
+   */
+  public static Annotation newAnnotation(String displayChar, String desc,
+          char secondaryStructure, float val, Color color)
+  {
+    return new Annotation(displayChar, desc, secondaryStructure, val, color);
+  }
+
+  /**
+   * Factory method to construct an AlignmentAnnotation object
+   * 
+   * @param name
+   * @param desc
+   * @param anns
+   * @return
+   */
+  public static AlignmentAnnotation newAlignmentAnnotation(String name,
+          String desc, Annotation[] anns)
+  {
+    return new AlignmentAnnotation(name, desc, anns);
+  }
+}
diff --git a/src/jalview/api/AlignCalcWorkerI.java b/src/jalview/workers/AnnotationProviderI.java
similarity index 63%
copy from src/jalview/api/AlignCalcWorkerI.java
copy to src/jalview/workers/AnnotationProviderI.java
index 3145382..f8c4555 100644
--- a/src/jalview/api/AlignCalcWorkerI.java
+++ b/src/jalview/workers/AnnotationProviderI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,16 +18,20 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.api;
+package jalview.workers;
 
+import jalview.api.FeatureRenderer;
 import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
 
-public interface AlignCalcWorkerI extends Runnable
-{
-
-  public boolean involves(AlignmentAnnotation annot);
-
-  public void updateAnnotation();
+import java.util.List;
 
-  void removeOurAnnotation();
+/**
+ * Interface to be satisfied by any class which computes one or more alignment
+ * annotations
+ */
+public interface AnnotationProviderI
+{
+  List<AlignmentAnnotation> calculateAnnotation(AlignmentI al,
+          FeatureRenderer fr);
 }
diff --git a/src/jalview/workers/AnnotationWorker.java b/src/jalview/workers/AnnotationWorker.java
new file mode 100644
index 0000000..4bfc352
--- /dev/null
+++ b/src/jalview/workers/AnnotationWorker.java
@@ -0,0 +1,144 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.workers;
+
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.renderer.seqfeatures.FeatureRenderer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class to create and update one or more alignment annotations, given a
+ * 'calculator'. Intended to support a 'plug-in' annotation worker which
+ * implements the AnnotationProviderI interface.
+ */
+class AnnotationWorker extends AlignCalcWorker
+{
+  /*
+   * the provider of the annotation calculations
+   */
+  AnnotationProviderI counter;
+
+  /**
+   * Constructor
+   * 
+   * @param af
+   * @param counter
+   */
+  public AnnotationWorker(AlignViewportI viewport,
+          AlignmentViewPanel panel, AnnotationProviderI counter)
+  {
+    super(viewport, panel);
+    ourAnnots = new ArrayList<AlignmentAnnotation>();
+    this.counter = counter;
+    calcMan.registerWorker(this);
+  }
+
+  @Override
+  public void run()
+  {
+    try
+    {
+      calcMan.notifyStart(this);
+
+      while (!calcMan.notifyWorking(this))
+      {
+        try
+        {
+          Thread.sleep(200);
+        } catch (InterruptedException ex)
+        {
+          ex.printStackTrace();
+        }
+      }
+      if (alignViewport.isClosed())
+      {
+        abortAndDestroy();
+        return;
+      }
+
+      // removeAnnotation();
+      AlignmentI alignment = alignViewport.getAlignment();
+      if (alignment != null)
+      {
+        try
+        {
+          List<AlignmentAnnotation> anns = counter.calculateAnnotation(
+                  alignment, new FeatureRenderer(alignViewport));
+          for (AlignmentAnnotation ann : anns)
+          {
+            AlignmentAnnotation theAnn = alignment.findOrCreateAnnotation(
+                    ann.label, ann.description, false, null, null);
+            theAnn.showAllColLabels = true;
+            theAnn.graph = AlignmentAnnotation.BAR_GRAPH;
+            theAnn.scaleColLabel = true;
+            theAnn.annotations = ann.annotations;
+            setGraphMinMax(theAnn, theAnn.annotations);
+            theAnn.validateRangeAndDisplay();
+            if (!ourAnnots.contains(theAnn))
+            {
+              ourAnnots.add(theAnn);
+            }
+            // alignment.addAnnotation(ann);
+          }
+        } catch (IndexOutOfBoundsException x)
+        {
+          // probable race condition. just finish and return without any fuss.
+          return;
+        }
+      }
+    } catch (OutOfMemoryError error)
+    {
+      ap.raiseOOMWarning("calculating annotations", error);
+      calcMan.disableWorker(this);
+    } finally
+    {
+      calcMan.workerComplete(this);
+    }
+
+    if (ap != null)
+    {
+      ap.adjustAnnotationHeight();
+      ap.paintAlignment(true);
+    }
+  }
+
+  @Override
+  public void updateAnnotation()
+  {
+    // do nothing
+  }
+
+  /**
+   * Answers true to indicate that if this worker's annotation is deleted from
+   * the display, the worker should also be removed. This prevents it running
+   * and recreating the annotation when the alignment changes.
+   */
+  @Override
+  public boolean isDeletable()
+  {
+    return true;
+  }
+}
diff --git a/src/jalview/workers/ColumnCounterWorker.java b/src/jalview/workers/ColumnCounterWorker.java
new file mode 100644
index 0000000..71f2598
--- /dev/null
+++ b/src/jalview/workers/ColumnCounterWorker.java
@@ -0,0 +1,244 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.workers;
+
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.renderer.seqfeatures.FeatureRenderer;
+import jalview.util.ColorUtils;
+import jalview.util.Comparison;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class to compute an alignment annotation with column counts of any
+ * properties of interest of positions in an alignment. <br>
+ * This is designed to be extensible, by supplying to the constructor an object
+ * that computes a count for each residue position, based on the residue value
+ * and any sequence features at that position.
+ * 
+ */
+class ColumnCounterWorker extends AlignCalcWorker
+{
+  FeatureCounterI counter;
+
+  /**
+   * Constructor registers the annotation for the given alignment frame
+   * 
+   * @param af
+   * @param counter
+   */
+  public ColumnCounterWorker(AlignViewportI viewport,
+          AlignmentViewPanel panel, FeatureCounterI counter)
+  {
+    super(viewport, panel);
+    ourAnnots = new ArrayList<AlignmentAnnotation>();
+    this.counter = counter;
+    calcMan.registerWorker(this);
+  }
+
+  /**
+   * method called under control of AlignCalcManager to recompute the annotation
+   * when the alignment changes
+   */
+  @Override
+  public void run()
+  {
+    try
+    {
+      calcMan.notifyStart(this);
+
+      while (!calcMan.notifyWorking(this))
+      {
+        try
+        {
+          Thread.sleep(200);
+        } catch (InterruptedException ex)
+        {
+          ex.printStackTrace();
+        }
+      }
+      if (alignViewport.isClosed())
+      {
+        abortAndDestroy();
+        return;
+      }
+
+      if (alignViewport.getAlignment() != null)
+      {
+        try
+        {
+          computeAnnotations();
+        } catch (IndexOutOfBoundsException x)
+        {
+          // probable race condition. just finish and return without any fuss.
+          return;
+        }
+      }
+    } catch (OutOfMemoryError error)
+    {
+      ap.raiseOOMWarning("calculating feature counts", error);
+      calcMan.disableWorker(this);
+    } finally
+    {
+      calcMan.workerComplete(this);
+    }
+
+    if (ap != null)
+    {
+      ap.adjustAnnotationHeight();
+      ap.paintAlignment(true);
+    }
+
+  }
+
+  /**
+   * Scan each column of the alignment to calculate a count by feature type. Set
+   * the count as the value of the alignment annotation for that feature type.
+   */
+  void computeAnnotations()
+  {
+    FeatureRenderer fr = new FeatureRenderer(alignViewport);
+    // TODO use the commented out code once JAL-2075 is fixed
+    // to get adequate performance on genomic length sequence
+    AlignmentI alignment = alignViewport.getAlignment();
+    // AlignmentView alignmentView = alignViewport.getAlignmentView(false);
+    // AlignmentI alignment = alignmentView.getVisibleAlignment(' ');
+
+    // int width = alignmentView.getWidth();
+    int width = alignment.getWidth();
+    int height = alignment.getHeight();
+    int[] counts = new int[width];
+    int max = 0;
+
+    for (int col = 0; col < width; col++)
+    {
+      int count = 0;
+      for (int row = 0; row < height; row++)
+      {
+        count += countFeaturesAt(alignment, col, row, fr);
+      }
+      counts[col] = count;
+      max = Math.max(count, max);
+    }
+
+    Annotation[] anns = new Annotation[width];
+    /*
+     * add non-zero counts as annotations
+     */
+    for (int i = 0; i < counts.length; i++)
+    {
+      int count = counts[i];
+      if (count > 0)
+      {
+        Color color = ColorUtils.getGraduatedColour(count, 0, Color.cyan,
+                max, Color.blue);
+        String str = String.valueOf(count);
+        anns[i] = new Annotation(str, str, '0', count, color);
+      }
+    }
+
+    /*
+     * construct or update the annotation
+     */
+    AlignmentAnnotation ann = alignViewport.getAlignment()
+            .findOrCreateAnnotation(counter.getName(),
+                    counter.getDescription(), false, null, null);
+    ann.description = counter.getDescription();
+    ann.showAllColLabels = true;
+    ann.scaleColLabel = true;
+    ann.graph = AlignmentAnnotation.BAR_GRAPH;
+    ann.annotations = anns;
+    setGraphMinMax(ann, anns);
+    ann.validateRangeAndDisplay();
+    if (!ourAnnots.contains(ann))
+    {
+      ourAnnots.add(ann);
+    }
+  }
+
+  /**
+   * Returns a count of any feature types present at the specified position of
+   * the alignment
+   * 
+   * @param alignment
+   * @param col
+   * @param row
+   * @param fr
+   */
+  int countFeaturesAt(AlignmentI alignment, int col, int row,
+          FeatureRenderer fr)
+  {
+    SequenceI seq = alignment.getSequenceAt(row);
+    if (seq == null)
+    {
+      return 0;
+    }
+    if (col >= seq.getLength())
+    {
+      return 0;// sequence doesn't extend this far
+    }
+    char res = seq.getCharAt(col);
+    if (Comparison.isGap(res))
+    {
+      return 0;
+    }
+    int pos = seq.findPosition(col);
+
+    /*
+     * compute a count for any displayed features at residue
+     */
+    // NB have to adjust pos if using AlignmentView.getVisibleAlignment
+    // see JAL-2075
+    List<SequenceFeature> features = fr.findFeaturesAtRes(seq, pos);
+    int count = this.counter.count(String.valueOf(res), features);
+    return count;
+  }
+
+  /**
+   * Method called when the user changes display options that may affect how the
+   * annotation is rendered, but do not change its values. Currently no such
+   * options affect user-defined annotation, so this method does nothing.
+   */
+  @Override
+  public void updateAnnotation()
+  {
+    // do nothing
+  }
+
+  /**
+   * Answers true to indicate that if this worker's annotation is deleted from
+   * the display, the worker should also be removed. This prevents it running
+   * and recreating the annotation when the alignment changes.
+   */
+  @Override
+  public boolean isDeletable()
+  {
+    return true;
+  }
+}
diff --git a/src/jalview/workers/ComplementConsensusThread.java b/src/jalview/workers/ComplementConsensusThread.java
index 13e9790..047c472 100644
--- a/src/jalview/workers/ComplementConsensusThread.java
+++ b/src/jalview/workers/ComplementConsensusThread.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -96,7 +96,6 @@ public class ComplementConsensusThread extends ConsensusThread
    * @param consensusData
    *          the computed consensus data
    */
-  @Override
   protected void deriveConsensus(AlignmentAnnotation consensusAnnotation,
           Hashtable[] consensusData)
   {
@@ -104,4 +103,16 @@ public class ComplementConsensusThread extends ConsensusThread
             alignViewport.isShowSequenceLogo(), getSequences().length);
   }
 
+  @Override
+  public void updateResultAnnotation(boolean immediate)
+  {
+    AlignmentAnnotation consensus = getConsensusAnnotation();
+    Hashtable[] hconsensus = getViewportConsensus();
+    if (immediate || !calcMan.isWorking(this) && consensus != null
+            && hconsensus != null)
+    {
+      deriveConsensus(consensus, hconsensus);
+    }
+  }
+
 }
diff --git a/src/jalview/workers/ConsensusThread.java b/src/jalview/workers/ConsensusThread.java
index ff7e7ce..fa6912d 100644
--- a/src/jalview/workers/ConsensusThread.java
+++ b/src/jalview/workers/ConsensusThread.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,19 +21,16 @@
 package jalview.workers;
 
 import jalview.analysis.AAFrequency;
-import jalview.api.AlignCalcWorkerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.ProfilesI;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ColourSchemeI;
 
-import java.util.Hashtable;
-
-public class ConsensusThread extends AlignCalcWorker implements
-        AlignCalcWorkerI
+public class ConsensusThread extends AlignCalcWorker
 {
   public ConsensusThread(AlignViewportI alignViewport,
           AlignmentViewPanel alignPanel)
@@ -98,7 +95,7 @@ public class ConsensusThread extends AlignCalcWorker implements
       }
     } catch (OutOfMemoryError error)
     {
-      calcMan.workerCannotRun(this);
+      calcMan.disableWorker(this);
       ap.raiseOOMWarning("calculating consensus", error);
     } finally
     {
@@ -127,10 +124,11 @@ public class ConsensusThread extends AlignCalcWorker implements
    */
   protected void computeConsensus(AlignmentI alignment)
   {
-    Hashtable[] hconsensus = new Hashtable[alignment.getWidth()];
 
     SequenceI[] aseqs = getSequences();
-    AAFrequency.calculate(aseqs, 0, alignment.getWidth(), hconsensus, true);
+    int width = alignment.getWidth();
+    ProfilesI hconsensus = AAFrequency.calculate(aseqs, width, 0,
+            width, true);
 
     alignViewport.setSequenceConsensusHash(hconsensus);
     setColourSchemeConsensus(hconsensus);
@@ -147,7 +145,7 @@ public class ConsensusThread extends AlignCalcWorker implements
   /**
    * @param hconsensus
    */
-  protected void setColourSchemeConsensus(Hashtable[] hconsensus)
+  protected void setColourSchemeConsensus(ProfilesI hconsensus)
   {
     ColourSchemeI globalColourScheme = alignViewport
             .getGlobalColourScheme();
@@ -180,7 +178,7 @@ public class ConsensusThread extends AlignCalcWorker implements
   public void updateResultAnnotation(boolean immediate)
   {
     AlignmentAnnotation consensus = getConsensusAnnotation();
-    Hashtable[] hconsensus = getViewportConsensus();
+    ProfilesI hconsensus = (ProfilesI) getViewportConsensus();
     if (immediate || !calcMan.isWorking(this) && consensus != null
             && hconsensus != null)
     {
@@ -194,15 +192,18 @@ public class ConsensusThread extends AlignCalcWorker implements
    * 
    * @param consensusAnnotation
    *          the annotation to be populated
-   * @param consensusData
+   * @param hconsensus
    *          the computed consensus data
    */
   protected void deriveConsensus(AlignmentAnnotation consensusAnnotation,
-          Hashtable[] consensusData)
+          ProfilesI hconsensus)
   {
+
     long nseq = getSequences().length;
-    AAFrequency.completeConsensus(consensusAnnotation, consensusData, 0,
-            consensusData.length, alignViewport.isIgnoreGapsConsensus(),
+    AAFrequency.completeConsensus(consensusAnnotation, hconsensus,
+            hconsensus.getStartColumn(),
+            hconsensus.getEndColumn() + 1,
+            alignViewport.isIgnoreGapsConsensus(),
             alignViewport.isShowSequenceLogo(), nseq);
   }
 
@@ -211,8 +212,9 @@ public class ConsensusThread extends AlignCalcWorker implements
    * 
    * @return
    */
-  protected Hashtable[] getViewportConsensus()
+  protected Object getViewportConsensus()
   {
+    // TODO convert ComplementConsensusThread to use Profile
     return alignViewport.getSequenceConsensusHash();
   }
 }
diff --git a/src/jalview/workers/ConservationThread.java b/src/jalview/workers/ConservationThread.java
index 990f724..0efd22d 100644
--- a/src/jalview/workers/ConservationThread.java
+++ b/src/jalview/workers/ConservationThread.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,7 +21,6 @@
 package jalview.workers;
 
 import jalview.analysis.Conservation;
-import jalview.api.AlignCalcWorkerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.datamodel.AlignmentAnnotation;
@@ -30,8 +29,7 @@ import jalview.datamodel.AlignmentI;
 import java.util.ArrayList;
 import java.util.List;
 
-public class ConservationThread extends AlignCalcWorker implements
-        AlignCalcWorkerI
+public class ConservationThread extends AlignCalcWorker
 {
 
   private int ConsPercGaps = 25; // JBPNote : This should be a configurable
@@ -97,7 +95,6 @@ public class ConservationThread extends AlignCalcWorker implements
       try
       {
         cons = Conservation.calculateConservation("All",
-                jalview.schemes.ResidueProperties.propHash, 3,
                 alignment.getSequences(), 0, alWidth - 1, false,
                 ConsPercGaps, quality != null);
       } catch (IndexOutOfBoundsException x)
@@ -110,7 +107,7 @@ public class ConservationThread extends AlignCalcWorker implements
     } catch (OutOfMemoryError error)
     {
       ap.raiseOOMWarning("calculating conservation", error);
-      calcMan.workerCannotRun(this);
+      calcMan.disableWorker(this);
       // alignViewport.conservation = null;
       // this.alignViewport.quality = null;
 
diff --git a/src/jalview/workers/FeatureCounterI.java b/src/jalview/workers/FeatureCounterI.java
new file mode 100644
index 0000000..856dc26
--- /dev/null
+++ b/src/jalview/workers/FeatureCounterI.java
@@ -0,0 +1,83 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.workers;
+
+import jalview.datamodel.SequenceFeature;
+
+import java.util.List;
+
+/**
+ * An interface for a type that returns counts of any value of interest at a
+ * sequence position that can be determined from the sequence character and any
+ * features present at that position
+ * 
+ */
+public interface FeatureCounterI
+{
+  /**
+   * Returns a count of some property of interest, for example
+   * <ul>
+   * <li>the number of variant features at the position</li>
+   * <li>the number of Cath features of status 'True Positive'</li>
+   * <li>1 if the residue is hydrophobic, else 0</li>
+   * <li>etc</li>
+   * </ul>
+   * 
+   * @param residue
+   *          the residue (or gap) at the position
+   * @param a
+   *          list of any sequence features which include the position
+   */
+  int count(String residue, List<SequenceFeature> features);
+
+  /**
+   * Returns a name for the annotation that this is counting, for use as the
+   * displayed label
+   * 
+   * @return
+   */
+  String getName();
+
+  /**
+   * Returns a description for the annotation, for display as a tooltip
+   * 
+   * @return
+   */
+  String getDescription();
+
+  /**
+   * Returns the colour (as [red, green, blue] values in the range 0-255) to use
+   * for the minimum value on histogram bars. If this is different to
+   * getMaxColour(), then bars will have a graduated colour.
+   * 
+   * @return
+   */
+  int[] getMinColour();
+
+  /**
+   * Returns the colour (as [red, green, blue] values in the range 0-255) to use
+   * for the maximum value on histogram bars. If this is the same as
+   * getMinColour(), then bars will have a single colour (not graduated).
+   * 
+   * @return
+   */
+  int[] getMaxColour();
+}
diff --git a/src/jalview/workers/StrucConsensusThread.java b/src/jalview/workers/StrucConsensusThread.java
index a7f64e7..5bb5956 100644
--- a/src/jalview/workers/StrucConsensusThread.java
+++ b/src/jalview/workers/StrucConsensusThread.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,7 +21,6 @@
 package jalview.workers;
 
 import jalview.analysis.StructureFrequency;
-import jalview.api.AlignCalcWorkerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.datamodel.AlignmentAnnotation;
@@ -31,8 +30,7 @@ import jalview.datamodel.SequenceI;
 
 import java.util.Hashtable;
 
-public class StrucConsensusThread extends AlignCalcWorker implements
-        AlignCalcWorkerI
+public class StrucConsensusThread extends AlignCalcWorker
 {
   public StrucConsensusThread(AlignViewportI alignViewport,
           AlignmentViewPanel alignPanel)
@@ -96,12 +94,15 @@ public class StrucConsensusThread extends AlignCalcWorker implements
               .getAlignmentAnnotation();
       AlignmentAnnotation rnaStruc = null;
       // select rna struct to use for calculation
-      for (int i = 0; i < aa.length; i++)
+      if (aa != null)
       {
-        if (aa[i].visible && aa[i].isRNA() && aa[i].isValidStruc())
+        for (int i = 0; i < aa.length; i++)
         {
-          rnaStruc = aa[i];
-          break;
+          if (aa[i].visible && aa[i].isRNA() && aa[i].isValidStruc())
+          {
+            rnaStruc = aa[i];
+            break;
+          }
         }
       }
       // check to see if its valid
@@ -128,7 +129,7 @@ public class StrucConsensusThread extends AlignCalcWorker implements
       updateResultAnnotation(true);
     } catch (OutOfMemoryError error)
     {
-      calcMan.workerCannotRun(this);
+      calcMan.disableWorker(this);
 
       // consensus = null;
       // hconsensus = null;
diff --git a/src/jalview/ws/AWSThread.java b/src/jalview/ws/AWSThread.java
index 156bd84..8db7ddc 100644
--- a/src/jalview/ws/AWSThread.java
+++ b/src/jalview/ws/AWSThread.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -31,8 +31,8 @@ import jalview.gui.WebserviceInfo;
 import jalview.util.MessageManager;
 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
 
-import java.util.LinkedHashSet;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
 
 public abstract class AWSThread extends Thread
 {
@@ -60,7 +60,7 @@ public abstract class AWSThread extends Thread
   /**
    * dataset sequence relationships to be propagated onto new results
    */
-  protected Set<AlignedCodonFrame> codonframe = null;
+  protected List<AlignedCodonFrame> codonframe = null;
 
   /**
    * are there jobs still running in this thread.
@@ -98,6 +98,7 @@ public abstract class AWSThread extends Thread
   /**
    * generic web service job/subjob poll loop
    */
+  @Override
   public void run()
   {
     JobStateSummary jstate = null;
@@ -378,11 +379,11 @@ public abstract class AWSThread extends Thread
     WsUrl = wsurl2;
     if (alframe != null)
     {
-      Set<AlignedCodonFrame> cf = alframe.getViewport().getAlignment()
+      List<AlignedCodonFrame> cf = alframe.getViewport().getAlignment()
               .getCodonFrames();
       if (cf != null)
       {
-        codonframe = new LinkedHashSet<AlignedCodonFrame>();
+        codonframe = new ArrayList<AlignedCodonFrame>();
         codonframe.addAll(cf);
       }
     }
diff --git a/src/jalview/ws/AWsJob.java b/src/jalview/ws/AWsJob.java
index 3bd7e65..06a1db4 100644
--- a/src/jalview/ws/AWsJob.java
+++ b/src/jalview/ws/AWsJob.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/DBRefFetcher.java b/src/jalview/ws/DBRefFetcher.java
index 7693142..2d31787 100644
--- a/src/jalview/ws/DBRefFetcher.java
+++ b/src/jalview/ws/DBRefFetcher.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -28,16 +28,20 @@ import jalview.datamodel.DBRefSource;
 import jalview.datamodel.Mapping;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
 import jalview.gui.CutAndPasteTransfer;
+import jalview.gui.DasSourceBrowser;
 import jalview.gui.Desktop;
+import jalview.gui.FeatureSettings;
 import jalview.gui.IProgressIndicator;
 import jalview.gui.OOMWarning;
+import jalview.util.DBRefUtils;
 import jalview.util.MessageManager;
 import jalview.ws.dbsources.das.api.jalviewSourceI;
+import jalview.ws.dbsources.das.datamodel.DasSequenceSource;
 import jalview.ws.seqfetcher.DbSourceProxy;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
@@ -45,6 +49,7 @@ import java.util.StringTokenizer;
 import java.util.Vector;
 
 import uk.ac.ebi.picr.model.UPEntry;
+import uk.ac.ebi.www.picr.AccessionMappingService.AccessionMapperServiceLocator;
 
 /**
  * Implements a runnable for validating a sequence against external databases
@@ -55,14 +60,19 @@ import uk.ac.ebi.picr.model.UPEntry;
  */
 public class DBRefFetcher implements Runnable
 {
+  private static final String NEWLINE = System.lineSeparator();
+
+  public interface FetchFinishedListenerI
+  {
+    void finished();
+  }
+
   SequenceI[] dataset;
 
-  IProgressIndicator af;
+  IProgressIndicator progressWindow;
 
   CutAndPasteTransfer output = new CutAndPasteTransfer();
 
-  StringBuffer sbuffer = new StringBuffer();
-
   boolean running = false;
 
   /**
@@ -70,123 +80,157 @@ public class DBRefFetcher implements Runnable
    */
   uk.ac.ebi.www.picr.AccessionMappingService.AccessionMapperInterface picrClient = null;
 
-  // /This will be a collection of Vectors of sequenceI refs.
+  // This will be a collection of Vectors of sequenceI refs.
   // The key will be the seq name or accession id of the seq
-  Hashtable seqRefs;
+  Hashtable<String, Vector<SequenceI>> seqRefs;
 
   DbSourceProxy[] dbSources;
 
   SequenceFetcher sfetcher;
 
+  private List<FetchFinishedListenerI> listeners;
+
   private SequenceI[] alseqs;
 
-  /**
+  /*
    * when true - retrieved sequences will be trimmed to cover longest derived
    * alignment sequence
    */
   private boolean trimDsSeqs = true;
 
-  public DBRefFetcher()
-  {
-  }
-
   /**
-   * Creates a new SequenceFeatureFetcher object and fetches from the currently
-   * selected set of databases.
+   * Creates a new DBRefFetcher object and fetches from the currently selected
+   * set of databases, if this is null then it fetches based on feature settings
    * 
    * @param seqs
-   *          fetch references for these sequences
-   * @param af
-   *          the parent alignframe for progress bar monitoring.
-   */
-  public DBRefFetcher(SequenceI[] seqs, AlignFrame af)
-  {
-    this(seqs, af, null);
-  }
-
-  /**
-   * Creates a new SequenceFeatureFetcher object and fetches from the currently
-   * selected set of databases.
-   * 
-   * @param seqs
-   *          fetch references for these sequences
-   * @param af
-   *          the parent alignframe for progress bar monitoring.
+   *          fetch references for these SequenceI array
+   * @param progressIndicatorFrame
+   *          the frame for progress bar monitoring
    * @param sources
-   *          array of database source strings to query references from
+   *          array of DbSourceProxy to query references form
+   * @param featureSettings
+   *          FeatureSettings to get alternative DbSourceProxy from
+   * @param isNucleotide
+   *          indicates if the array of SequenceI are Nucleotides or not
    */
-  public DBRefFetcher(SequenceI[] seqs, AlignFrame af,
-          DbSourceProxy[] sources)
+  public DBRefFetcher(SequenceI[] seqs,
+          IProgressIndicator progressIndicatorFrame,
+          DbSourceProxy[] sources, FeatureSettings featureSettings,
+          boolean isNucleotide)
   {
-    this.af = af;
+    listeners = new ArrayList<FetchFinishedListenerI>();
+    this.progressWindow = progressIndicatorFrame;
     alseqs = new SequenceI[seqs.length];
     SequenceI[] ds = new SequenceI[seqs.length];
     for (int i = 0; i < seqs.length; i++)
     {
       alseqs[i] = seqs[i];
       if (seqs[i].getDatasetSequence() != null)
+      {
         ds[i] = seqs[i].getDatasetSequence();
+      }
       else
+      {
         ds[i] = seqs[i];
+      }
     }
     this.dataset = ds;
     // TODO Jalview 2.5 lots of this code should be in the gui package!
-    sfetcher = jalview.gui.SequenceFetcher.getSequenceFetcherSingleton(af);
+    sfetcher = jalview.gui.SequenceFetcher
+            .getSequenceFetcherSingleton(progressIndicatorFrame);
     // set default behaviour for transferring excess sequence data to the
     // dataset
     trimDsSeqs = Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true);
     if (sources == null)
     {
-      // af.featureSettings_actionPerformed(null);
-      String[] defdb = null, otherdb = sfetcher
-              .getDbInstances(jalview.ws.dbsources.das.datamodel.DasSequenceSource.class);
-      List<DbSourceProxy> selsources = new ArrayList<DbSourceProxy>();
-      Vector dasselsrc = (af.featureSettings != null) ? af.featureSettings
-              .getSelectedSources() : new jalview.gui.DasSourceBrowser()
-              .getSelectedSources();
-      Enumeration<jalviewSourceI> en = dasselsrc.elements();
-      while (en.hasMoreElements())
+      setDatabaseSources(featureSettings, isNucleotide);
+    }
+    else
+    {
+      // we assume the caller knows what they're doing and ensured that all the
+      // db source names are valid
+      dbSources = sources;
+    }
+  }
+
+  /**
+   * Helper method to configure the list of database sources to query
+   * 
+   * @param featureSettings
+   * @param forNucleotide
+   */
+  void setDatabaseSources(FeatureSettings featureSettings,
+          boolean forNucleotide)
+  {
+    // af.featureSettings_actionPerformed(null);
+    String[] defdb = null;
+    List<DbSourceProxy> selsources = new ArrayList<DbSourceProxy>();
+    Vector<jalviewSourceI> dasselsrc = (featureSettings != null) ? featureSettings
+            .getSelectedSources() : new DasSourceBrowser()
+            .getSelectedSources();
+
+    for (jalviewSourceI src : dasselsrc)
+    {
+      List<DbSourceProxy> sp = src.getSequenceSourceProxies();
+      if (sp != null)
       {
-        jalviewSourceI src = en.nextElement();
-        List<DbSourceProxy> sp = src.getSequenceSourceProxies();
-        if (sp != null)
+        selsources.addAll(sp);
+        if (sp.size() > 1)
         {
-          selsources.addAll(sp);
-          if (sp.size() > 1)
-          {
-            Cache.log.debug("Added many Db Sources for :" + src.getTitle());
-          }
+          Cache.log.debug("Added many Db Sources for :" + src.getTitle());
         }
       }
-      // select appropriate databases based on alignFrame context.
-      if (af.getViewport().getAlignment().isNucleotide())
-      {
-        defdb = DBRefSource.DNACODINGDBS;
-      }
-      else
-      {
-        defdb = DBRefSource.PROTEINDBS;
-      }
-      List<DbSourceProxy> srces = new ArrayList<DbSourceProxy>();
-      for (String ddb : defdb)
+    }
+    // select appropriate databases based on alignFrame context.
+    if (forNucleotide)
+    {
+      defdb = DBRefSource.DNACODINGDBS;
+    }
+    else
+    {
+      defdb = DBRefSource.PROTEINDBS;
+    }
+    List<DbSourceProxy> srces = new ArrayList<DbSourceProxy>();
+    for (String ddb : defdb)
+    {
+      List<DbSourceProxy> srcesfordb = sfetcher.getSourceProxy(ddb);
+      if (srcesfordb != null)
       {
-        List<DbSourceProxy> srcesfordb = sfetcher.getSourceProxy(ddb);
-        if (srcesfordb != null)
+        for (DbSourceProxy src : srcesfordb)
         {
-          srces.addAll(srcesfordb);
+          if (!srces.contains(src))
+          {
+            srces.addAll(srcesfordb);
+          }
         }
       }
-
-      // append the selected sequence sources to the default dbs
-      srces.addAll(selsources);
-      dbSources = srces.toArray(new DbSourceProxy[0]);
-    }
-    else
-    {
-      // we assume the caller knows what they're doing and ensured that all the
-      // db source names are valid
-      dbSources = sources;
     }
+    // append the PDB data source, since it is 'special', catering for both
+    // nucleotide and protein
+    // srces.addAll(sfetcher.getSourceProxy(DBRefSource.PDB));
+
+    srces.addAll(selsources);
+    dbSources = srces.toArray(new DbSourceProxy[srces.size()]);
+  }
+
+  /**
+   * Constructor with only sequences provided
+   * 
+   * @param sequences
+   */
+  public DBRefFetcher(SequenceI[] sequences)
+  {
+    this(sequences, null, null, null, false);
+  }
+
+  /**
+   * Add a listener to be notified when sequence fetching is complete
+   * 
+   * @param l
+   */
+  public void addListener(FetchFinishedListenerI l)
+  {
+    listeners.add(l);
   }
 
   /**
@@ -201,7 +245,7 @@ public class DBRefFetcher implements Runnable
     }
     // append additional sources
     DbSourceProxy[] otherdb = sfetcher
-            .getDbSourceProxyInstances(jalview.ws.dbsources.das.datamodel.DasSequenceSource.class);
+            .getDbSourceProxyInstances(DasSequenceSource.class);
     if (otherdb != null && otherdb.length > 0)
     {
       DbSourceProxy[] newsrc = new DbSourceProxy[dbSources.length
@@ -220,6 +264,9 @@ public class DBRefFetcher implements Runnable
    */
   public void fetchDBRefs(boolean waitTillFinished)
   {
+    // TODO can we not simply write
+    // if (waitTillFinished) { run(); } else { new Thread(this).start(); }
+
     Thread thread = new Thread(this);
     thread.start();
     running = true;
@@ -251,10 +298,10 @@ public class DBRefFetcher implements Runnable
   {
     key = key.toUpperCase();
 
-    Vector seqs;
+    Vector<SequenceI> seqs;
     if (seqRefs.containsKey(key))
     {
-      seqs = (Vector) seqRefs.get(key);
+      seqs = seqRefs.get(key);
 
       if (seqs != null && !seqs.contains(seq))
       {
@@ -262,14 +309,14 @@ public class DBRefFetcher implements Runnable
       }
       else if (seqs == null)
       {
-        seqs = new Vector();
+        seqs = new Vector<SequenceI>();
         seqs.addElement(seq);
       }
 
     }
     else
     {
-      seqs = new Vector();
+      seqs = new Vector<SequenceI>();
       seqs.addElement(seq);
     }
 
@@ -279,6 +326,7 @@ public class DBRefFetcher implements Runnable
   /**
    * DOCUMENT ME!
    */
+  @Override
   public void run()
   {
     if (dbSources == null)
@@ -289,13 +337,17 @@ public class DBRefFetcher implements Runnable
     }
     running = true;
     long startTime = System.currentTimeMillis();
-    af.setProgressBar(MessageManager.getString("status.fetching_db_refs"),
-            startTime);
+    if (progressWindow != null)
+    {
+      progressWindow.setProgressBar(
+              MessageManager.getString("status.fetching_db_refs"),
+              startTime);
+    }
     try
     {
       if (Cache.getDefault("DBREFFETCH_USEPICR", false))
       {
-        picrClient = new uk.ac.ebi.www.picr.AccessionMappingService.AccessionMapperServiceLocator()
+        picrClient = new AccessionMapperServiceLocator()
                 .getAccessionMapperPort();
       }
     } catch (Exception e)
@@ -303,157 +355,145 @@ public class DBRefFetcher implements Runnable
       System.err.println("Couldn't locate PICR service instance.\n");
       e.printStackTrace();
     }
+
+    Vector<SequenceI> sdataset = new Vector<SequenceI>(
+            Arrays.asList(dataset));
+    List<String> warningMessages = new ArrayList<String>();
+
     int db = 0;
-    Vector sdataset = new Vector();
-    for (int s = 0; s < dataset.length; s++)
-    {
-      sdataset.addElement(dataset[s]);
-    }
     while (sdataset.size() > 0 && db < dbSources.length)
     {
-      int maxqlen = 1; // default number of queries made to at one time
-      System.err.println("Verifying against " + dbSources[db].getDbName());
-      boolean dn = false;
+      int maxqlen = 1; // default number of queries made at one time
+      System.out.println("Verifying against " + dbSources[db].getDbName());
 
       // iterate through db for each remaining un-verified sequence
       SequenceI[] currSeqs = new SequenceI[sdataset.size()];
       sdataset.copyInto(currSeqs);// seqs that are to be validated against
       // dbSources[db]
-      Vector queries = new Vector(); // generated queries curSeq
-      seqRefs = new Hashtable();
+      Vector<String> queries = new Vector<String>(); // generated queries curSeq
+      seqRefs = new Hashtable<String, Vector<SequenceI>>();
 
       int seqIndex = 0;
 
-      jalview.ws.seqfetcher.DbSourceProxy dbsource = dbSources[db];
+      DbSourceProxy dbsource = dbSources[db];
+      // for moment, we dumbly iterate over all retrieval sources for a
+      // particular database
+      // TODO: introduce multithread multisource queries and logic to remove a
+      // query from other sources if any source for a database returns a
+      // record
+      maxqlen = dbsource.getMaximumQueryCount();
+
+      while (queries.size() > 0 || seqIndex < currSeqs.length)
       {
-        // for moment, we dumbly iterate over all retrieval sources for a
-        // particular database
-        // TODO: introduce multithread multisource queries and logic to remove a
-        // query from other sources if any source for a database returns a
-        // record
-        if (dbsource.getDbSourceProperties().containsKey(
-                DBRefSource.MULTIACC))
-        {
-          maxqlen = ((Integer) dbsource.getDbSourceProperties().get(
-                  DBRefSource.MULTIACC)).intValue();
-        }
-        else
-        {
-          maxqlen = 1;
-        }
-        while (queries.size() > 0 || seqIndex < currSeqs.length)
+        if (queries.size() > 0)
         {
-          if (queries.size() > 0)
-          {
-            // Still queries to make for current seqIndex
-            StringBuffer queryString = new StringBuffer("");
-            int numq = 0, nqSize = (maxqlen > queries.size()) ? queries
-                    .size() : maxqlen;
+          // Still queries to make for current seqIndex
+          StringBuffer queryString = new StringBuffer("");
+          int numq = 0;
+          int nqSize = (maxqlen > queries.size()) ? queries.size()
+                  : maxqlen;
 
-            while (queries.size() > 0 && numq < nqSize)
-            {
-              String query = (String) queries.elementAt(0);
-              if (dbsource.isValidReference(query))
-              {
-                queryString.append((numq == 0) ? "" : dbsource
-                        .getAccessionSeparator());
-                queryString.append(query);
-                numq++;
-              }
-              // remove the extracted query string
-              queries.removeElementAt(0);
-            }
-            // make the queries and process the response
-            AlignmentI retrieved = null;
-            try
-            {
-              if (jalview.bin.Cache.log.isDebugEnabled())
-              {
-                jalview.bin.Cache.log.debug("Querying "
-                        + dbsource.getDbName() + " with : '"
-                        + queryString.toString() + "'");
-              }
-              retrieved = dbsource.getSequenceRecords(queryString
-                      .toString());
-            } catch (Exception ex)
-            {
-              ex.printStackTrace();
-            } catch (OutOfMemoryError err)
+          while (queries.size() > 0 && numq < nqSize)
+          {
+            String query = queries.elementAt(0);
+            if (dbsource.isValidReference(query))
             {
-              new OOMWarning("retrieving database references ("
-                      + queryString.toString() + ")", err);
+              queryString.append((numq == 0) ? "" : dbsource
+                      .getAccessionSeparator());
+              queryString.append(query);
+              numq++;
             }
-            if (retrieved != null)
+            // remove the extracted query string
+            queries.removeElementAt(0);
+          }
+          // make the queries and process the response
+          AlignmentI retrieved = null;
+          try
+          {
+            if (Cache.log.isDebugEnabled())
             {
-              transferReferences(sdataset, dbsource.getDbSource(),
-                      retrieved, trimDsSeqs);
+              Cache.log.debug("Querying " + dbsource.getDbName()
+                      + " with : '" + queryString.toString() + "'");
             }
+            retrieved = dbsource.getSequenceRecords(queryString.toString());
+          } catch (Exception ex)
+          {
+            ex.printStackTrace();
+          } catch (OutOfMemoryError err)
+          {
+            new OOMWarning("retrieving database references ("
+                    + queryString.toString() + ")", err);
           }
-          else
+          if (retrieved != null)
+          {
+            transferReferences(sdataset, dbsource.getDbSource(), retrieved,
+                    trimDsSeqs, warningMessages);
+          }
+        }
+        else
+        {
+          // make some more strings for use as queries
+          for (int i = 0; (seqIndex < dataset.length) && (i < 50); seqIndex++, i++)
           {
-            // make some more strings for use as queries
-            for (int i = 0; (seqIndex < dataset.length) && (i < 50); seqIndex++, i++)
+            SequenceI sequence = dataset[seqIndex];
+            DBRefEntry[] uprefs = DBRefUtils.selectRefs(
+                    sequence.getDBRefs(),
+                    new String[] { dbsource.getDbSource() }); // jalview.datamodel.DBRefSource.UNIPROT
+            // });
+            // check for existing dbrefs to use
+            if (uprefs != null && uprefs.length > 0)
             {
-              SequenceI sequence = dataset[seqIndex];
-              DBRefEntry[] uprefs = jalview.util.DBRefUtils.selectRefs(
-                      sequence.getDBRef(),
-                      new String[] { dbsource.getDbSource() }); // jalview.datamodel.DBRefSource.UNIPROT
-              // });
-              // check for existing dbrefs to use
-              if (uprefs != null && uprefs.length > 0)
+              for (int j = 0; j < uprefs.length; j++)
               {
-                for (int j = 0; j < uprefs.length; j++)
-                {
-                  addSeqId(sequence, uprefs[j].getAccessionId());
-                  queries.addElement(uprefs[j].getAccessionId()
-                          .toUpperCase());
-                }
+                addSeqId(sequence, uprefs[j].getAccessionId());
+                queries.addElement(uprefs[j].getAccessionId().toUpperCase());
               }
-              else
+            }
+            else
+            {
+              // generate queries from sequence ID string
+              StringTokenizer st = new StringTokenizer(sequence.getName(),
+                      "|");
+              while (st.hasMoreTokens())
               {
-                // generate queries from sequence ID string
-                StringTokenizer st = new StringTokenizer(
-                        sequence.getName(), "|");
-                while (st.hasMoreTokens())
+                String token = st.nextToken();
+                UPEntry[] presp = null;
+                if (picrClient != null)
                 {
-                  String token = st.nextToken();
-                  UPEntry[] presp = null;
-                  if (picrClient != null)
+                  // resolve the string against PICR to recover valid IDs
+                  try
                   {
-                    // resolve the string against PICR to recover valid IDs
-                    try
-                    {
-                      presp = picrClient.getUPIForAccession(token, null,
-                              picrClient.getMappedDatabaseNames(), null,
-                              true);
-                    } catch (Exception e)
-                    {
-                      System.err.println("Exception with Picr for '"
-                              + token + "'\n");
-                      e.printStackTrace();
-                    }
-                  }
-                  if (presp != null && presp.length > 0)
+                    presp = picrClient
+                            .getUPIForAccession(token, null,
+                                    picrClient.getMappedDatabaseNames(),
+                                    null, true);
+                  } catch (Exception e)
                   {
-                    for (int id = 0; id < presp.length; id++)
-                    {
-                      // construct sequences from response if sequences are
-                      // present, and do a transferReferences
-                      // otherwise transfer non sequence x-references directly.
-                    }
-                    System.out
-                            .println("Validated ID against PICR... (for what its worth):"
-                                    + token);
-                    addSeqId(sequence, token);
-                    queries.addElement(token.toUpperCase());
+                    System.err.println("Exception with Picr for '" + token
+                            + "'\n");
+                    e.printStackTrace();
                   }
-                  else
+                }
+                if (presp != null && presp.length > 0)
+                {
+                  for (int id = 0; id < presp.length; id++)
                   {
-                    // if ()
-                    // System.out.println("Not querying source with token="+token+"\n");
-                    addSeqId(sequence, token);
-                    queries.addElement(token.toUpperCase());
+                    // construct sequences from response if sequences are
+                    // present, and do a transferReferences
+                    // otherwise transfer non sequence x-references directly.
                   }
+                  System.out
+                          .println("Validated ID against PICR... (for what its worth):"
+                                  + token);
+                  addSeqId(sequence, token);
+                  queries.addElement(token.toUpperCase());
+                }
+                else
+                {
+                  // if ()
+                  // System.out.println("Not querying source with token="+token+"\n");
+                  addSeqId(sequence, token);
+                  queries.addElement(token.toUpperCase());
                 }
               }
             }
@@ -462,82 +502,95 @@ public class DBRefFetcher implements Runnable
       }
       // advance to next database
       db++;
-    } // all databases have been queries.
-    if (sbuffer.length() > 0)
+    } // all databases have been queried
+    if (!warningMessages.isEmpty())
     {
-      output.setText(MessageManager
-              .getString("label.your_sequences_have_been_verified")
-              + sbuffer.toString());
+      StringBuilder sb = new StringBuilder(warningMessages.size() * 30);
+      sb.append(MessageManager
+              .getString("label.your_sequences_have_been_verified"));
+      for (String msg : warningMessages)
+      {
+        sb.append(msg).append(NEWLINE);
+      }
+      output.setText(sb.toString());
+
       Desktop.addInternalFrame(output,
-              MessageManager.getString("label.sequence_names_updated"),
-              600, 300);
+              MessageManager.getString("label.sequences_updated"), 600, 300);
       // The above is the dataset, we must now find out the index
       // of the viewed sequence
 
     }
+    if (progressWindow != null)
+    {
+      progressWindow.setProgressBar(
+              MessageManager.getString("label.dbref_search_completed"),
+              startTime);
+    }
 
-    af.setProgressBar(
-            MessageManager.getString("label.dbref_search_completed"),
-            startTime);
-    // promptBeforeBlast();
-
+    for (FetchFinishedListenerI listener : listeners)
+    {
+      listener.finished();
+    }
     running = false;
-
   }
 
   /**
    * Verify local sequences in seqRefs against the retrieved sequence database
-   * records.
+   * records. Returns true if any sequence was modified as a result (start/end
+   * changed and/or sequence enlarged), else false.
    * 
+   * @param sdataset
+   *          dataset sequences we are retrieving for
+   * @param dbSource
+   *          database source we are retrieving from
+   * @param retrievedAl
+   *          retrieved sequences as alignment
    * @param trimDatasetSeqs
-   * 
+   *          if true, sequences will not be enlarged to match longer retrieved
+   *          sequences, only their start/end adjusted
+   * @param warningMessages
+   *          a list of messages to add to
    */
-  void transferReferences(Vector sdataset, String dbSource,
-          AlignmentI retrievedAl, boolean trimDatasetSeqs) // File
-  // file)
+  boolean transferReferences(Vector<SequenceI> sdataset, String dbSource,
+          AlignmentI retrievedAl, boolean trimDatasetSeqs,
+          List<String> warningMessages)
   {
-    System.out.println("trimming ? " + trimDatasetSeqs);
+    // System.out.println("trimming ? " + trimDatasetSeqs);
     if (retrievedAl == null || retrievedAl.getHeight() == 0)
     {
-      return;
+      return false;
     }
+
+    boolean modified = false;
     SequenceI[] retrieved = recoverDbSequences(retrievedAl
             .getSequencesArray());
     SequenceI sequence = null;
-    boolean transferred = false;
-    StringBuffer messages = new StringBuffer();
-
-    // Vector entries = new Uniprot().getUniprotEntries(file);
 
-    int i, iSize = retrieved.length; // entries == null ? 0 : entries.size();
-    // UniprotEntry entry;
-    for (i = 0; i < iSize; i++)
+    for (SequenceI retrievedSeq : retrieved)
     {
-      SequenceI entry = retrieved[i]; // (UniprotEntry) entries.elementAt(i);
-
       // Work out which sequences this sequence matches,
       // taking into account all accessionIds and names in the file
-      Vector sequenceMatches = new Vector();
+      Vector<SequenceI> sequenceMatches = new Vector<SequenceI>();
       // look for corresponding accession ids
-      DBRefEntry[] entryRefs = jalview.util.DBRefUtils.selectRefs(
-              entry.getDBRef(), new String[] { dbSource });
+      DBRefEntry[] entryRefs = DBRefUtils.selectRefs(
+              retrievedSeq.getDBRefs(), new String[] { dbSource });
       if (entryRefs == null)
       {
         System.err
                 .println("Dud dbSource string ? no entryrefs selected for "
-                        + dbSource + " on " + entry.getName());
+                        + dbSource + " on " + retrievedSeq.getName());
         continue;
       }
       for (int j = 0; j < entryRefs.length; j++)
       {
-        String accessionId = entryRefs[j].getAccessionId(); // .getAccession().elementAt(j).toString();
+        String accessionId = entryRefs[j].getAccessionId();
         // match up on accessionId
         if (seqRefs.containsKey(accessionId.toUpperCase()))
         {
-          Vector seqs = (Vector) seqRefs.get(accessionId);
+          Vector<SequenceI> seqs = seqRefs.get(accessionId);
           for (int jj = 0; jj < seqs.size(); jj++)
           {
-            sequence = (SequenceI) seqs.elementAt(jj);
+            sequence = seqs.elementAt(jj);
             if (!sequenceMatches.contains(sequence))
             {
               sequenceMatches.addElement(sequence);
@@ -545,17 +598,17 @@ public class DBRefFetcher implements Runnable
           }
         }
       }
-      if (sequenceMatches.size() == 0)
+      if (sequenceMatches.isEmpty())
       {
         // failed to match directly on accessionId==query so just compare all
         // sequences to entry
-        Enumeration e = seqRefs.keys();
+        Enumeration<String> e = seqRefs.keys();
         while (e.hasMoreElements())
         {
-          Vector sqs = (Vector) seqRefs.get(e.nextElement());
+          Vector<SequenceI> sqs = seqRefs.get(e.nextElement());
           if (sqs != null && sqs.size() > 0)
           {
-            Enumeration sqe = sqs.elements();
+            Enumeration<SequenceI> sqe = sqs.elements();
             while (sqe.hasMoreElements())
             {
               sequenceMatches.addElement(sqe.nextElement());
@@ -579,84 +632,91 @@ public class DBRefFetcher implements Runnable
        */
       // sequenceMatches now contains the set of all sequences associated with
       // the returned db record
-      String entrySeq = entry.getSequenceAsString().toUpperCase();
+      final String retrievedSeqString = retrievedSeq.getSequenceAsString();
+      String entrySeq = retrievedSeqString.toUpperCase();
       for (int m = 0; m < sequenceMatches.size(); m++)
       {
-        sequence = (SequenceI) sequenceMatches.elementAt(m);
+        sequence = sequenceMatches.elementAt(m);
         // only update start and end positions and shift features if there are
         // no existing references
         // TODO: test for legacy where uniprot or EMBL refs exist but no
         // mappings are made (but content matches retrieved set)
-        boolean updateRefFrame = sequence.getDBRef() == null
-                || sequence.getDBRef().length == 0;
+        boolean updateRefFrame = sequence.getDBRefs() == null
+                || sequence.getDBRefs().length == 0;
         // TODO:
         // verify sequence against the entry sequence
 
+        Mapping mp;
+        final int sequenceStart = sequence.getStart();
+
+        boolean remoteEnclosesLocal = false;
         String nonGapped = AlignSeq.extractGaps("-. ",
                 sequence.getSequenceAsString()).toUpperCase();
-
         int absStart = entrySeq.indexOf(nonGapped);
-        int mapStart = entry.getStart();
-        jalview.datamodel.Mapping mp;
-
         if (absStart == -1)
         {
-          // Is local sequence contained in dataset sequence?
+          // couldn't find local sequence in sequence from database, so check if
+          // the database sequence is a subsequence of local sequence
           absStart = nonGapped.indexOf(entrySeq);
           if (absStart == -1)
-          { // verification failed.
-            messages.append(sequence.getName()
-                    + " SEQUENCE NOT %100 MATCH \n");
+          {
+            // verification failed. couldn't find any relationship between
+            // entrySeq and local sequence
+            // messages suppressed as many-to-many matches are confusing
+            // String msg = sequence.getName()
+            // + " Sequence not 100% match with "
+            // + retrievedSeq.getName();
+            // addWarningMessage(warningMessages, msg);
             continue;
           }
-          transferred = true;
-          sbuffer.append(sequence.getName() + " HAS " + absStart
-                  + " PREFIXED RESIDUES COMPARED TO " + dbSource + "\n");
-          //
-          // + " - ANY SEQUENCE FEATURES"
-          // + " HAVE BEEN ADJUSTED ACCORDINGLY \n");
-          // absStart = 0;
-          // create valid mapping between matching region of local sequence and
-          // the mapped sequence
-          mp = new Mapping(null, new int[] {
-              sequence.getStart() + absStart,
-              sequence.getStart() + absStart + entrySeq.length() - 1 },
-                  new int[] { entry.getStart(),
-                      entry.getStart() + entrySeq.length() - 1 }, 1, 1);
-          updateRefFrame = false; // mapping is based on current start/end so
-          // don't modify start and end
+          /*
+           * retrieved sequence is a proper subsequence of local sequence
+           */
+          String msg = sequence.getName() + " has " + absStart
+                  + " prefixed residues compared to "
+                  + retrievedSeq.getName();
+          addWarningMessage(warningMessages, msg);
+
+          /*
+           * So create a mapping to the external entry from the matching region of 
+           * the local sequence, and leave local start/end untouched. 
+           */
+          mp = new Mapping(null, new int[] { sequenceStart + absStart,
+              sequenceStart + absStart + entrySeq.length() - 1 }, new int[]
+          { retrievedSeq.getStart(),
+              retrievedSeq.getStart() + entrySeq.length() - 1 }, 1, 1);
+          updateRefFrame = false;
         }
         else
         {
-          transferred = true;
-          // update start and end of local sequence to place it in entry's
-          // reference frame.
-          // apply identity map map from whole of local sequence to matching
-          // region of database
-          // sequence
-          mp = null; // Mapping.getIdentityMap();
-          // new Mapping(null,
-          // new int[] { absStart+sequence.getStart(),
-          // absStart+sequence.getStart()+entrySeq.length()-1},
-          // new int[] { entry.getStart(), entry.getEnd() }, 1, 1);
-          // relocate local features for updated start
+          /*
+           * local sequence is a subsequence of (or matches) retrieved sequence
+           */
+          remoteEnclosesLocal = true;
+          mp = null;
+
           if (updateRefFrame)
           {
-            if (sequence.getSequenceFeatures() != null)
+            SequenceFeature[] sfs = sequence.getSequenceFeatures();
+            if (sfs != null)
             {
-              SequenceFeature[] sf = sequence.getSequenceFeatures();
-              int start = sequence.getStart();
+              /*
+               * relocate existing sequence features by offset
+               */
+              int start = sequenceStart;
               int end = sequence.getEnd();
-              int startShift = 1 - absStart - start; // how much the features
-                                                     // are
-              // to be shifted by
-              for (int sfi = 0; sfi < sf.length; sfi++)
+              int startShift = 1 - absStart - start;
+
+              if (startShift != 0)
               {
-                if (sf[sfi].getBegin() >= start && sf[sfi].getEnd() <= end)
+                for (SequenceFeature sf : sfs)
                 {
-                  // shift feature along by absstart
-                  sf[sfi].setBegin(sf[sfi].getBegin() + startShift);
-                  sf[sfi].setEnd(sf[sfi].getEnd() + startShift);
+                  if (sf.getBegin() >= start && sf.getEnd() <= end)
+                  {
+                    sf.setBegin(sf.getBegin() + startShift);
+                    sf.setEnd(sf.getEnd() + startShift);
+                    modified = true;
+                  }
                 }
               }
             }
@@ -664,16 +724,37 @@ public class DBRefFetcher implements Runnable
         }
 
         System.out.println("Adding dbrefs to " + sequence.getName()
-                + " from " + dbSource + " sequence : " + entry.getName());
-        sequence.transferAnnotation(entry, mp);
-        // unknownSequences.remove(sequence);
-        int absEnd = absStart + nonGapped.length();
-        absStart += 1;
+                + " from " + dbSource + " sequence : "
+                + retrievedSeq.getName());
+        sequence.transferAnnotation(retrievedSeq, mp);
+
+        absStart += retrievedSeq.getStart();
+        int absEnd = absStart + nonGapped.length() - 1;
         if (!trimDatasetSeqs)
         {
-          // insert full length sequence from record
-          sequence.setSequence(entry.getSequenceAsString());
-          sequence.setStart(entry.getStart());
+          /*
+           * update start position and/or expand to longer retrieved sequence
+           */
+          if (!retrievedSeqString.equals(sequence.getSequenceAsString())
+                  && remoteEnclosesLocal)
+          {
+            sequence.setSequence(retrievedSeqString);
+            modified = true;
+            addWarningMessage(warningMessages,
+                    "Sequence for " + sequence.getName()
+                            + " expanded from " + retrievedSeq.getName());
+          }
+          if (sequence.getStart() != retrievedSeq.getStart())
+          {
+            sequence.setStart(retrievedSeq.getStart());
+            modified = true;
+            if (absStart != sequenceStart)
+            {
+              addWarningMessage(warningMessages, "Start/end position for "
+                      + sequence.getName() + " updated from "
+                      + retrievedSeq.getName());
+            }
+          }
         }
         if (updateRefFrame)
         {
@@ -681,8 +762,16 @@ public class DBRefFetcher implements Runnable
           if (trimDatasetSeqs)
           {
             // just fix start/end
-            sequence.setStart(absStart);
-            sequence.setEnd(absEnd);
+            if (sequence.getStart() != absStart
+                    || sequence.getEnd() != absEnd)
+            {
+              sequence.setStart(absStart);
+              sequence.setEnd(absEnd);
+              modified = true;
+              addWarningMessage(warningMessages, "Start/end for "
+                      + sequence.getName() + " updated from "
+                      + retrievedSeq.getName());
+            }
           }
           // search for alignment sequences to update coordinate frame for
           for (int alsq = 0; alsq < alseqs.length; alsq++)
@@ -699,6 +788,7 @@ public class DBRefFetcher implements Runnable
               {
                 alseqs[alsq].setEnd(ngAlsq.length()
                         + alseqs[alsq].getStart() - 1);
+                modified = true;
               }
             }
           }
@@ -715,10 +805,20 @@ public class DBRefFetcher implements Runnable
         // ids, so we can query all enabled DAS servers for them ?
       }
     }
-    if (!transferred)
+    return modified;
+  }
+
+  /**
+   * Adds the message to the list unless it already contains it
+   * 
+   * @param messageList
+   * @param msg
+   */
+  void addWarningMessage(List<String> messageList, String msg)
+  {
+    if (!messageList.contains(msg))
     {
-      // report the ID/sequence mismatches
-      sbuffer.append(messages);
+      messageList.add(msg);
     }
   }
 
@@ -730,12 +830,12 @@ public class DBRefFetcher implements Runnable
    */
   private SequenceI[] recoverDbSequences(SequenceI[] sequencesArray)
   {
-    Vector nseq = new Vector();
+    Vector<SequenceI> nseq = new Vector<SequenceI>();
     for (int i = 0; sequencesArray != null && i < sequencesArray.length; i++)
     {
       nseq.addElement(sequencesArray[i]);
-      DBRefEntry dbr[] = sequencesArray[i].getDBRef();
-      jalview.datamodel.Mapping map = null;
+      DBRefEntry[] dbr = sequencesArray[i].getDBRefs();
+      Mapping map = null;
       for (int r = 0; (dbr != null) && r < dbr.length; r++)
       {
         if ((map = dbr[r].getMap()) != null)
diff --git a/src/jalview/ws/DasSequenceFeatureFetcher.java b/src/jalview/ws/DasSequenceFeatureFetcher.java
index 345bdd7..7202b4d 100644
--- a/src/jalview/ws/DasSequenceFeatureFetcher.java
+++ b/src/jalview/ws/DasSequenceFeatureFetcher.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,11 +22,13 @@ package jalview.ws;
 
 import jalview.bin.Cache;
 import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
 import jalview.gui.FeatureSettings;
+import jalview.util.DBRefUtils;
 import jalview.util.MessageManager;
 import jalview.util.UrlLink;
 import jalview.ws.dbsources.das.api.DasSourceRegistryI;
@@ -181,13 +183,12 @@ public class DasSequenceFeatureFetcher
     int refCount = 0;
     for (int i = 0; i < sequences.length; i++)
     {
-      DBRefEntry[] dbref = sequences[i].getDBRef();
+      DBRefEntry[] dbref = sequences[i].getDBRefs();
       if (dbref != null)
       {
         for (int j = 0; j < dbref.length; j++)
         {
-          if (dbref[j].getSource().equals(
-                  jalview.datamodel.DBRefSource.UNIPROT))
+          if (dbref[j].getSource().equals(DBRefSource.UNIPROT))
           {
             refCount++;
             break;
@@ -238,6 +239,7 @@ public class DasSequenceFeatureFetcher
 
   class FetchSeqFeatures implements Runnable
   {
+    @Override
     public void run()
     {
       startFetching();
@@ -247,10 +249,14 @@ public class DasSequenceFeatureFetcher
 
   class FetchDBRefs implements Runnable
   {
+    @Override
     public void run()
     {
       running = true;
-      new DBRefFetcher(sequences, af).fetchDBRefs(true);
+      boolean isNucleotide = af.getViewport().getAlignment().isNucleotide();
+      new DBRefFetcher(sequences, af, null, af.featureSettings,
+              isNucleotide).fetchDBRefs(true);
+
       startFetching();
       setGuiFetchComplete();
     }
@@ -280,8 +286,7 @@ public class DasSequenceFeatureFetcher
       {
         jalviewSourceI[] sources = sourceRegistry.getSources().toArray(
                 new jalviewSourceI[0]);
-        String active = jalview.bin.Cache.getDefault("DAS_ACTIVE_SOURCE",
-                "uniprot");
+        String active = Cache.getDefault("DAS_ACTIVE_SOURCE", "uniprot");
         StringTokenizer st = new StringTokenizer(active, "\t");
         selectedSources = new Vector();
         String token;
@@ -637,10 +642,10 @@ public class DasSequenceFeatureFetcher
     {
       return null;
     }
-    DBRefEntry[] uprefs = jalview.util.DBRefUtils.selectRefs(
-            seq.getDBRef(), new String[] {
+    DBRefEntry[] uprefs = DBRefUtils.selectRefs(seq.getDBRefs(),
+            new String[] {
             // jalview.datamodel.DBRefSource.PDB,
-            jalview.datamodel.DBRefSource.UNIPROT,
+            DBRefSource.UNIPROT,
             // jalview.datamodel.DBRefSource.EMBL - not tested on any EMBL coord
             // sys sources
             });
@@ -659,8 +664,8 @@ public class DasSequenceFeatureFetcher
 
         for (COORDINATES csys : dasSource.getVersion().getCOORDINATES())
         {
-          if (jalview.util.DBRefUtils.isDasCoordinateSystem(
-                  csys.getAuthority(), uprefs[j]))
+          if (DBRefUtils.isDasCoordinateSystem(csys.getAuthority(),
+                  uprefs[j]))
           {
             debug("Launched fetcher for coordinate system "
                     + csys.getAuthority());
diff --git a/src/jalview/ws/HttpClientUtils.java b/src/jalview/ws/HttpClientUtils.java
index a782917..0091fbb 100644
--- a/src/jalview/ws/HttpClientUtils.java
+++ b/src/jalview/ws/HttpClientUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/JobStateSummary.java b/src/jalview/ws/JobStateSummary.java
index f5b678c..9d40dde 100644
--- a/src/jalview/ws/JobStateSummary.java
+++ b/src/jalview/ws/JobStateSummary.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/SequenceFetcher.java b/src/jalview/ws/SequenceFetcher.java
index aba3c6f..5cc6c81 100644
--- a/src/jalview/ws/SequenceFetcher.java
+++ b/src/jalview/ws/SequenceFetcher.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,24 +20,24 @@
  */
 package jalview.ws;
 
-import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.DBRefSource;
-import jalview.datamodel.SequenceI;
+import jalview.ext.ensembl.EnsemblGene;
+import jalview.ext.ensembl.EnsemblGenomes;
+import jalview.ws.dbsources.EmblCdsSource;
+import jalview.ws.dbsources.EmblSource;
+import jalview.ws.dbsources.Pdb;
+import jalview.ws.dbsources.PfamFull;
+import jalview.ws.dbsources.PfamSeed;
+import jalview.ws.dbsources.RfamSeed;
+import jalview.ws.dbsources.Uniprot;
 import jalview.ws.dbsources.das.api.jalviewSourceI;
 import jalview.ws.seqfetcher.ASequenceFetcher;
 import jalview.ws.seqfetcher.DbSourceProxy;
 
 import java.util.ArrayList;
-import java.util.Enumeration;
 import java.util.List;
-import java.util.Vector;
 
 /**
- * This is the the concrete implementation of the sequence retrieval interface
- * and abstract class in jalview.ws.seqfetcher. This implements the run-time
- * discovery of sequence database clients, and provides a hardwired main for
- * testing all registered handlers.
+ * This implements the run-time discovery of sequence database clients.
  * 
  */
 public class SequenceFetcher extends ASequenceFetcher
@@ -55,16 +55,16 @@ public class SequenceFetcher extends ASequenceFetcher
 
   public SequenceFetcher(boolean addDas)
   {
-    addDBRefSourceImpl(jalview.ws.dbsources.EmblSource.class);
-    addDBRefSourceImpl(jalview.ws.dbsources.EmblCdsSouce.class);
-    addDBRefSourceImpl(jalview.ws.dbsources.Uniprot.class);
-    addDBRefSourceImpl(jalview.ws.dbsources.UnprotName.class);
-    addDBRefSourceImpl(jalview.ws.dbsources.Pdb.class);
-    addDBRefSourceImpl(jalview.ws.dbsources.PfamFull.class);
-    addDBRefSourceImpl(jalview.ws.dbsources.PfamSeed.class);
-    // ensures Seed alignment is 'default' for PFAM
-    addDBRefSourceImpl(jalview.ws.dbsources.RfamFull.class);
-    addDBRefSourceImpl(jalview.ws.dbsources.RfamSeed.class);
+    addDBRefSourceImpl(EnsemblGene.class);
+    addDBRefSourceImpl(EnsemblGenomes.class);
+    addDBRefSourceImpl(EmblSource.class);
+    addDBRefSourceImpl(EmblCdsSource.class);
+    addDBRefSourceImpl(Uniprot.class);
+    addDBRefSourceImpl(Pdb.class);
+    addDBRefSourceImpl(PfamFull.class);
+    addDBRefSourceImpl(PfamSeed.class);
+    addDBRefSourceImpl(RfamSeed.class);
+
     if (addDas)
     {
       registerDasSequenceSources();
@@ -87,7 +87,7 @@ public class SequenceFetcher extends ASequenceFetcher
       {
         // Skip the alignment databases for the moment - they're not useful for
         // verifying a single sequence against its reference source
-        if (dbs.isA(DBRefSource.ALIGNMENTDB))
+        if (dbs.isAlignmentSource())
         {
           skip = true;
         }
@@ -150,283 +150,6 @@ public class SequenceFetcher extends ASequenceFetcher
   }
 
   /**
-   * return plaintext databse list suitable for using in a GUI element
-   */
-  public String[] _getOrderedSupportedSources()
-  {
-    String[] srcs = this.getSupportedDb();
-    ArrayList dassrc = new ArrayList(), nondas = new ArrayList();
-    for (int i = 0; i < srcs.length; i++)
-    {
-      for (DbSourceProxy dbs : getSourceProxy(srcs[i]))
-      {
-        String nm = dbs.getDbName();
-        if (getSourceProxy(srcs[i]) instanceof jalview.ws.dbsources.das.datamodel.DasSequenceSource)
-        {
-          if (nm.startsWith("das:"))
-          {
-            nm = nm.substring(4);
-          }
-          dassrc.add(new String[] { srcs[i], nm.toUpperCase() });
-        }
-        else
-        {
-          nondas.add(new String[] { srcs[i], nm.toUpperCase() });
-        }
-      }
-    }
-    Object[] sorted = nondas.toArray();
-    String[] tosort = new String[sorted.length];
-    nondas.clear();
-    for (int j = 0; j < sorted.length; j++)
-    {
-      tosort[j] = ((String[]) sorted[j])[1];
-    }
-    jalview.util.QuickSort.sort(tosort, sorted);
-    int i = 0;
-    // construct array with all sources listed
-    srcs = new String[sorted.length + dassrc.size()];
-    for (int j = sorted.length - 1; j >= 0; j--, i++)
-    {
-      srcs[i] = ((String[]) sorted[j])[0];
-      sorted[j] = null;
-    }
-
-    sorted = dassrc.toArray();
-    tosort = new String[sorted.length];
-    dassrc.clear();
-    for (int j = 0; j < sorted.length; j++)
-    {
-      tosort[j] = ((String[]) sorted[j])[1];
-    }
-    jalview.util.QuickSort.sort(tosort, sorted);
-    for (int j = sorted.length - 1; j >= 0; j--, i++)
-    {
-      srcs[i] = ((String[]) sorted[j])[0];
-      sorted[j] = null;
-    }
-    return srcs;
-  }
-
-  /**
-   * simple run method to test dbsources.
-   * 
-   * @param argv
-   */
-  public static void main(String[] argv)
-  {
-    AlignmentI ds = null;
-    Vector noProds = new Vector();
-    String usage = "SequenceFetcher.main [-nodas] [<DBNAME> [<ACCNO>]]\n"
-            + "With no arguments, all DbSources will be queried with their test Accession number.\n"
-            + "With one argument, the argument will be resolved to one or more db sources and each will be queried with their test accession only.\n"
-            + "If given two arguments, SequenceFetcher will try to find the DbFetcher corresponding to <DBNAME> and retrieve <ACCNO> from it.\n"
-            + "The -nodas option will exclude DAS sources from the database fetchers Jalview will try to use.";
-    boolean withDas = true;
-    if (argv != null && argv.length > 0
-            && argv[0].toLowerCase().startsWith("-nodas"))
-    {
-      withDas = false;
-      String targs[] = new String[argv.length - 1];
-      System.arraycopy(argv, 1, targs, 0, targs.length);
-      argv = targs;
-    }
-    if (argv != null && argv.length > 0)
-    {
-      List<DbSourceProxy> sps = new SequenceFetcher(withDas)
-              .getSourceProxy(argv[0]);
-
-      if (sps != null)
-      {
-        for (DbSourceProxy sp : sps)
-        {
-          AlignmentI al = null;
-          try
-          {
-            al = sp.getSequenceRecords(argv.length > 1 ? argv[1] : sp
-                    .getTestQuery());
-          } catch (Exception e)
-          {
-            e.printStackTrace();
-            System.err.println("Error when retrieving "
-                    + (argv.length > 1 ? argv[1] : sp.getTestQuery())
-                    + " from " + argv[0] + "\nUsage: " + usage);
-          }
-          SequenceI[] prod = al.getSequencesArray();
-          if (al != null)
-          {
-            for (int p = 0; p < prod.length; p++)
-            {
-              System.out.println("Prod " + p + ": "
-                      + prod[p].getDisplayId(true) + " : "
-                      + prod[p].getDescription());
-            }
-          }
-        }
-        return;
-      }
-      else
-      {
-        System.err.println("Can't resolve " + argv[0]
-                + " as a database name. Allowed values are :\n"
-                + new SequenceFetcher().getSupportedDb());
-      }
-      System.out.println(usage);
-      return;
-    }
-    ASequenceFetcher sfetcher = new SequenceFetcher(withDas);
-    String[] dbSources = sfetcher.getSupportedDb();
-    for (int dbsource = 0; dbsource < dbSources.length; dbsource++)
-    {
-      String db = dbSources[dbsource];
-      // skip me
-      if (db.equals(DBRefSource.PDB))
-      {
-        continue;
-      }
-      for (DbSourceProxy sp : sfetcher.getSourceProxy(db))
-      {
-        System.out.println("Source: " + sp.getDbName() + " (" + db
-                + "): retrieving test:" + sp.getTestQuery());
-        AlignmentI al = null;
-        try
-        {
-          al = sp.getSequenceRecords(sp.getTestQuery());
-          if (al != null && al.getHeight() > 0
-                  && sp.getDbSourceProperties() != null)
-          {
-            boolean dna = sp.getDbSourceProperties().containsKey(
-                    DBRefSource.DNACODINGSEQDB)
-                    || sp.getDbSourceProperties().containsKey(
-                            DBRefSource.DNASEQDB)
-                    || sp.getDbSourceProperties().containsKey(
-                            DBRefSource.CODINGSEQDB);
-            // try and find products
-            String types[] = jalview.analysis.CrossRef
-                    .findSequenceXrefTypes(dna, al.getSequencesArray());
-            if (types != null)
-            {
-              System.out.println("Xref Types for: "
-                      + (dna ? "dna" : "prot"));
-              for (int t = 0; t < types.length; t++)
-              {
-                System.out.println("Type: " + types[t]);
-                SequenceI[] prod = jalview.analysis.CrossRef
-                        .findXrefSequences(al.getSequencesArray(), dna,
-                                types[t]).getSequencesArray();
-                System.out.println("Found "
-                        + ((prod == null) ? "no" : "" + prod.length)
-                        + " products");
-                if (prod != null)
-                {
-                  for (int p = 0; p < prod.length; p++)
-                  {
-                    System.out.println("Prod " + p + ": "
-                            + prod[p].getDisplayId(true));
-                  }
-                }
-              }
-            }
-            else
-            {
-              noProds.addElement((dna ? new Object[] { al, al }
-                      : new Object[] { al }));
-            }
-
-          }
-        } catch (Exception ex)
-        {
-          System.out.println("ERROR:Failed to retrieve test query.");
-          ex.printStackTrace(System.out);
-        }
-
-        if (al == null)
-        {
-          System.out.println("ERROR:No alignment retrieved.");
-          StringBuffer raw = sp.getRawRecords();
-          if (raw != null)
-          {
-            System.out.println(raw.toString());
-          }
-          else
-          {
-            System.out.println("ERROR:No Raw results.");
-          }
-        }
-        else
-        {
-          System.out.println("Retrieved " + al.getHeight() + " sequences.");
-          for (int s = 0; s < al.getHeight(); s++)
-          {
-            SequenceI sq = al.getSequenceAt(s);
-            while (sq.getDatasetSequence() != null)
-            {
-              sq = sq.getDatasetSequence();
-
-            }
-            if (ds == null)
-            {
-              ds = new Alignment(new SequenceI[] { sq });
-
-            }
-            else
-            {
-              ds.addSequence(sq);
-            }
-          }
-        }
-        System.out.flush();
-        System.err.flush();
-
-      }
-      if (noProds.size() > 0)
-      {
-        Enumeration ts = noProds.elements();
-        while (ts.hasMoreElements())
-
-        {
-          Object[] typeSq = (Object[]) ts.nextElement();
-          boolean dna = (typeSq.length > 1);
-          AlignmentI al = (AlignmentI) typeSq[0];
-          System.out.println("Trying getProducts for "
-                  + al.getSequenceAt(0).getDisplayId(true));
-          System.out.println("Search DS Xref for: "
-                  + (dna ? "dna" : "prot"));
-          // have a bash at finding the products amongst all the retrieved
-          // sequences.
-          SequenceI[] seqs = al.getSequencesArray();
-          Alignment prodal = jalview.analysis.CrossRef.findXrefSequences(
-                  seqs, dna, null, ds);
-          System.out.println("Found "
-                  + ((prodal == null) ? "no" : "" + prodal.getHeight())
-                  + " products");
-          if (prodal != null)
-          {
-            SequenceI[] prod = prodal.getSequencesArray(); // note
-            // should
-            // test
-            // rather
-            // than
-            // throw
-            // away
-            // codon
-            // mapping
-            // (if
-            // present)
-            for (int p = 0; p < prod.length; p++)
-            {
-              System.out.println("Prod " + p + ": "
-                      + prod[p].getDisplayId(true));
-            }
-          }
-        }
-
-      }
-
-    }
-  }
-
-  /**
    * query the currently defined DAS source registry for sequence sources and
    * add a DasSequenceSource instance for each source to the SequenceFetcher
    * source list.
diff --git a/src/jalview/schemes/HydrophobicColourScheme.java b/src/jalview/ws/SequenceFetcherFactory.java
similarity index 52%
copy from src/jalview/schemes/HydrophobicColourScheme.java
copy to src/jalview/ws/SequenceFetcherFactory.java
index 56e0106..7db5c44 100644
--- a/src/jalview/schemes/HydrophobicColourScheme.java
+++ b/src/jalview/ws/SequenceFetcherFactory.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,37 +18,35 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.schemes;
+package jalview.ws;
 
-import java.awt.Color;
+import jalview.ws.seqfetcher.ASequenceFetcher;
 
-/**
- * DOCUMENT ME!
- * 
- * @author $author$
- * @version $Revision$
- */
-public class HydrophobicColourScheme extends ScoreColourScheme
+public class SequenceFetcherFactory
 {
+
+  private static SequenceFetcher instance;
+
   /**
-   * Creates a new HydrophobicColourScheme object.
+   * Returns a new SequenceFetcher object, or a mock object if one has been set
+   * 
+   * @return
    */
-  public HydrophobicColourScheme()
+  public static ASequenceFetcher getSequenceFetcher()
   {
-    super(ResidueProperties.aaIndex, ResidueProperties.hyd,
-            ResidueProperties.hydmin, ResidueProperties.hydmax);
+    return instance == null ? new SequenceFetcher() : instance;
   }
 
   /**
-   * DOCUMENT ME!
+   * Set the instance object to use (intended for unit testing with mock
+   * objects).
    * 
-   * @param c
-   *          DOCUMENT ME!
+   * Be sure to reset to null in the tearDown method of any tests!
    * 
-   * @return DOCUMENT ME!
+   * @param sf
    */
-  public Color makeColour(float c)
+  public static void setSequenceFetcher(SequenceFetcher sf)
   {
-    return new Color(c, (float) 0.0, (float) 1.0 - c);
+    instance = sf;
   }
 }
diff --git a/src/jalview/ws/WSClient.java b/src/jalview/ws/WSClient.java
index 4ca0b76..263f6e9 100644
--- a/src/jalview/ws/WSClient.java
+++ b/src/jalview/ws/WSClient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/WSClientI.java b/src/jalview/ws/WSClientI.java
index 2b3539e..c57e543 100644
--- a/src/jalview/ws/WSClientI.java
+++ b/src/jalview/ws/WSClientI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/WSMenuEntryProviderI.java b/src/jalview/ws/WSMenuEntryProviderI.java
index 76f5519..46b8e0b 100644
--- a/src/jalview/ws/WSMenuEntryProviderI.java
+++ b/src/jalview/ws/WSMenuEntryProviderI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/dbsources/EbiFileRetrievedProxy.java b/src/jalview/ws/dbsources/EbiFileRetrievedProxy.java
index 09ce4bc..6cc6149 100644
--- a/src/jalview/ws/dbsources/EbiFileRetrievedProxy.java
+++ b/src/jalview/ws/dbsources/EbiFileRetrievedProxy.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/dbsources/EmblCdsSouce.java b/src/jalview/ws/dbsources/EmblCdsSource.java
similarity index 84%
rename from src/jalview/ws/dbsources/EmblCdsSouce.java
rename to src/jalview/ws/dbsources/EmblCdsSource.java
index 40a4098..7d70006 100644
--- a/src/jalview/ws/dbsources/EmblCdsSouce.java
+++ b/src/jalview/ws/dbsources/EmblCdsSource.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,40 +22,43 @@ package jalview.ws.dbsources;
 
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefSource;
-import jalview.ws.seqfetcher.DbSourceProxy;
 
 import com.stevesoft.pat.Regex;
 
-public class EmblCdsSouce extends EmblXmlSource implements DbSourceProxy
+public class EmblCdsSource extends EmblXmlSource
 {
 
-  public EmblCdsSouce()
+  public EmblCdsSource()
   {
     super();
-    addDbSourceProperty(DBRefSource.CODINGSEQDB);
   }
 
+  @Override
   public String getAccessionSeparator()
   {
     return null;
   }
 
+  @Override
   public Regex getAccessionValidator()
   {
-    return new com.stevesoft.pat.Regex("^[A-Z]+[0-9]+");
+    return new Regex("^[A-Z]+[0-9]+");
   }
 
+  @Override
   public String getDbSource()
   {
     return DBRefSource.EMBLCDS;
   }
 
+  @Override
   public String getDbVersion()
   {
     return "0"; // TODO : this is dynamically set for a returned record - not
     // tied to proxy
   }
 
+  @Override
   public AlignmentI getSequenceRecords(String queries) throws Exception
   {
     if (queries.indexOf(".") > -1)
@@ -65,6 +68,7 @@ public class EmblCdsSouce extends EmblXmlSource implements DbSourceProxy
     return getEmblSequenceRecords(DBRefSource.EMBLCDS, queries);
   }
 
+  @Override
   public boolean isValidReference(String accession)
   {
     // most embl CDS refs look like ..
@@ -76,11 +80,13 @@ public class EmblCdsSouce extends EmblXmlSource implements DbSourceProxy
   /**
    * cDNA for LDHA_CHICK swissprot sequence
    */
+  @Override
   public String getTestQuery()
   {
     return "CAA37824";
   }
 
+  @Override
   public String getDbName()
   {
     return "EMBL (CDS)";
diff --git a/src/jalview/ws/dbsources/EmblSource.java b/src/jalview/ws/dbsources/EmblSource.java
index 3d57000..70fc667 100644
--- a/src/jalview/ws/dbsources/EmblSource.java
+++ b/src/jalview/ws/dbsources/EmblSource.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,7 +22,6 @@ package jalview.ws.dbsources;
 
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefSource;
-import jalview.ws.seqfetcher.DbSourceProxy;
 
 import com.stevesoft.pat.Regex;
 
@@ -30,13 +29,12 @@ import com.stevesoft.pat.Regex;
  * @author JimP
  * 
  */
-public class EmblSource extends EmblXmlSource implements DbSourceProxy
+public class EmblSource extends EmblXmlSource
 {
 
   public EmblSource()
   {
-    addDbSourceProperty(DBRefSource.DNASEQDB);
-    addDbSourceProperty(DBRefSource.CODINGSEQDB);
+    super();
   }
 
   /*
@@ -44,6 +42,7 @@ public class EmblSource extends EmblXmlSource implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getAccessionSeparator()
    */
+  @Override
   public String getAccessionSeparator()
   {
     // TODO Auto-generated method stub
@@ -55,9 +54,10 @@ public class EmblSource extends EmblXmlSource implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getAccessionValidator()
    */
+  @Override
   public Regex getAccessionValidator()
   {
-    return new com.stevesoft.pat.Regex("^[A-Z]+[0-9]+");
+    return new Regex("^[A-Z]+[0-9]+");
   }
 
   /*
@@ -65,6 +65,7 @@ public class EmblSource extends EmblXmlSource implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getDbSource()
    */
+  @Override
   public String getDbSource()
   {
     return DBRefSource.EMBL;
@@ -75,6 +76,7 @@ public class EmblSource extends EmblXmlSource implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getDbVersion()
    */
+  @Override
   public String getDbVersion()
   {
     // TODO Auto-generated method stub
@@ -86,6 +88,7 @@ public class EmblSource extends EmblXmlSource implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getSequenceRecords(java.lang.String[])
    */
+  @Override
   public AlignmentI getSequenceRecords(String queries) throws Exception
   {
     return getEmblSequenceRecords(DBRefSource.EMBL, queries);
@@ -96,6 +99,7 @@ public class EmblSource extends EmblXmlSource implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String)
    */
+  @Override
   public boolean isValidReference(String accession)
   {
     // most embl refs look like ..
@@ -108,11 +112,13 @@ public class EmblSource extends EmblXmlSource implements DbSourceProxy
   /**
    * return LHD_CHICK coding gene
    */
+  @Override
   public String getTestQuery()
   {
     return "X53828";
   }
 
+  @Override
   public String getDbName()
   {
     return "EMBL"; // getDbSource();
diff --git a/src/jalview/ws/dbsources/EmblXmlSource.java b/src/jalview/ws/dbsources/EmblXmlSource.java
index 697e4ee..28a87d8 100644
--- a/src/jalview/ws/dbsources/EmblXmlSource.java
+++ b/src/jalview/ws/dbsources/EmblXmlSource.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -29,14 +29,15 @@ import jalview.util.MessageManager;
 import jalview.ws.ebi.EBIFetchClient;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 
 public abstract class EmblXmlSource extends EbiFileRetrievedProxy
 {
-
-  /**
-   * Last properly parsed embl file.
+  /*
+   * JAL-1856 Embl returns this text for query not found
    */
-  public EmblFile efile = null;
+  private static final String EMBL_NOT_FOUND_REPLY = "ERROR 12 No entries found.";
 
   public EmblXmlSource()
   {
@@ -62,7 +63,8 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
     try
     {
       reply = dbFetch.fetchDataAsFile(
-              emprefx.toLowerCase() + ":" + query.trim(), "emblxml", null);
+              emprefx.toLowerCase() + ":" + query.trim(), "display=xml",
+              ".xml");
     } catch (Exception e)
     {
       stopQuery();
@@ -88,71 +90,50 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
   public AlignmentI getEmblSequenceRecords(String emprefx, String query,
           File reply) throws Exception
   {
-    SequenceI seqs[] = null;
-    StringBuffer result = new StringBuffer();
+    EmblFile efile = null;
+    List<SequenceI> seqs = new ArrayList<SequenceI>();
+
     if (reply != null && reply.exists())
     {
-      efile = null;
       file = reply.getAbsolutePath();
-      if (reply.length() > 25)
+      if (reply.length() > EMBL_NOT_FOUND_REPLY.length())
       {
         efile = EmblFile.getEmblFile(reply);
       }
-      else
-      {
-        result.append(MessageManager.formatMessage(
-                "label.no_embl_record_found",
-                new String[] { emprefx.toLowerCase(), query.trim() }));
-      }
     }
-    if (efile != null)
+
+    /*
+     * invalid accession gets a reply with no <entry> elements, text content of
+     * EmbFile reads something like (e.g.) this ungrammatical phrase
+     * Entry: <acc> display type is either not supported or entry is not found.
+     */
+    List<SequenceI> peptides = new ArrayList<SequenceI>();
+    if (efile != null && efile.getEntries() != null)
     {
       for (EmblEntry entry : efile.getEntries())
       {
-        SequenceI[] seqparts = entry.getSequences(false, true, emprefx);
-        // TODO: use !fetchNa,!fetchPeptide here instead - see todo in EmblEntry
-        if (seqparts != null)
+        SequenceI seq = entry.getSequence(emprefx, peptides);
+        if (seq != null)
         {
-          SequenceI[] newseqs = null;
-          int si = 0;
-          if (seqs == null)
-          {
-            newseqs = new SequenceI[seqparts.length];
-          }
-          else
-          {
-            newseqs = new SequenceI[seqs.length + seqparts.length];
-
-            for (; si < seqs.length; si++)
-            {
-              newseqs[si] = seqs[si];
-              seqs[si] = null;
-            }
-          }
-          for (int j = 0; j < seqparts.length; si++, j++)
-          {
-            newseqs[si] = seqparts[j].deriveSequence();
-            // place DBReferences on dataset and refer
-          }
-          seqs = newseqs;
-
+          seqs.add(seq.deriveSequence());
+          // place DBReferences on dataset and refer
         }
       }
     }
-    else
-    {
-      result = null;
-    }
+
     AlignmentI al = null;
-    if (seqs != null && seqs.length > 0)
+    if (!seqs.isEmpty())
     {
-      al = new Alignment(seqs);
-      result.append(MessageManager.formatMessage(
-              "label.embl_successfully_parsed", new String[] { emprefx }));
-      results = result;
+      al = new Alignment(seqs.toArray(new SequenceI[seqs.size()]));
     }
     stopQuery();
     return al;
   }
 
+  @Override
+  public boolean isDnaCoding()
+  {
+    return true;
+  }
+
 }
diff --git a/src/jalview/ws/dbsources/GeneDbSource.java b/src/jalview/ws/dbsources/GeneDbSource.java
index 3b251ed..36d965f 100644
--- a/src/jalview/ws/dbsources/GeneDbSource.java
+++ b/src/jalview/ws/dbsources/GeneDbSource.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,7 +22,6 @@ package jalview.ws.dbsources;
 
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefSource;
-import jalview.ws.seqfetcher.DbSourceProxy;
 
 import com.stevesoft.pat.Regex;
 
@@ -32,13 +31,12 @@ import com.stevesoft.pat.Regex;
  * @author JimP
  * 
  */
-public class GeneDbSource extends EmblXmlSource implements DbSourceProxy
+public class GeneDbSource extends EmblXmlSource
 {
 
   public GeneDbSource()
   {
-    addDbSourceProperty(DBRefSource.DNASEQDB);
-    addDbSourceProperty(DBRefSource.CODINGSEQDB);
+    super();
   }
 
   /*
@@ -46,6 +44,7 @@ public class GeneDbSource extends EmblXmlSource implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getAccessionSeparator()
    */
+  @Override
   public String getAccessionSeparator()
   {
     // TODO Auto-generated method stub
@@ -57,6 +56,7 @@ public class GeneDbSource extends EmblXmlSource implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getAccessionValidator()
    */
+  @Override
   public Regex getAccessionValidator()
   {
     // TODO Auto-generated method stub
@@ -68,6 +68,7 @@ public class GeneDbSource extends EmblXmlSource implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getDbSource()
    */
+  @Override
   public String getDbSource()
   {
     return DBRefSource.GENEDB;
@@ -78,6 +79,7 @@ public class GeneDbSource extends EmblXmlSource implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getDbVersion()
    */
+  @Override
   public String getDbVersion()
   {
     // TODO Auto-generated method stub
@@ -89,6 +91,7 @@ public class GeneDbSource extends EmblXmlSource implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getSequenceRecords(java.lang.String[])
    */
+  @Override
   public AlignmentI getSequenceRecords(String queries) throws Exception
   {
     // query of form
@@ -102,6 +105,7 @@ public class GeneDbSource extends EmblXmlSource implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String)
    */
+  @Override
   public boolean isValidReference(String accession)
   {
     // TODO Auto-generated method stub
@@ -111,11 +115,13 @@ public class GeneDbSource extends EmblXmlSource implements DbSourceProxy
   /**
    * return T.Brucei Mannosyl-Transferase TbPIG-M
    */
+  @Override
   public String getTestQuery()
   {
     return "Tb927.6.3300";
   }
 
+  @Override
   public String getDbName()
   {
     return "GeneDB"; // getDbSource();
diff --git a/src/jalview/ws/dbsources/PDBRestClient.java b/src/jalview/ws/dbsources/PDBRestClient.java
deleted file mode 100644
index 44ef3fc..0000000
--- a/src/jalview/ws/dbsources/PDBRestClient.java
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.ws.dbsources;
-
-import jalview.util.MessageManager;
-import jalview.ws.uimodel.PDBRestRequest;
-import jalview.ws.uimodel.PDBRestResponse;
-import jalview.ws.uimodel.PDBRestResponse.PDBResponseSummary;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.ws.rs.core.MediaType;
-
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
-import org.json.simple.parser.JSONParser;
-import org.json.simple.parser.ParseException;
-
-import com.sun.jersey.api.client.Client;
-import com.sun.jersey.api.client.ClientResponse;
-import com.sun.jersey.api.client.WebResource;
-import com.sun.jersey.api.client.config.ClientConfig;
-import com.sun.jersey.api.client.config.DefaultClientConfig;
-
-/**
- * A rest client for querying the Search endpoing of the PDB REST API
- * 
- * @author tcnofoegbu
- *
- */
-public class PDBRestClient
-{
-  public static final String PDB_SEARCH_ENDPOINT = "http://www.ebi.ac.uk/pdbe/search/pdb/select?";
-
-  private static int DEFAULT_RESPONSE_SIZE = 200;
-
-  /**
-   * Takes a PDBRestRequest object and returns a response upon execution
-   * 
-   * @param pdbRestRequest
-   *          the PDBRestRequest instance to be processed
-   * @return the pdbResponse object for the given request
-   * @throws Exception
-   */
-  public PDBRestResponse executeRequest(PDBRestRequest pdbRestRequest)
-          throws Exception
-  {
-    try
-    {
-      ClientConfig clientConfig = new DefaultClientConfig();
-      Client client = Client.create(clientConfig);
-
-      String wantedFields = getPDBDocFieldsAsCommaDelimitedString(pdbRestRequest
-              .getWantedFields());
-      int responseSize = (pdbRestRequest.getResponseSize() == 0) ? DEFAULT_RESPONSE_SIZE
-              : pdbRestRequest.getResponseSize();
-      String sortParam = (pdbRestRequest.getFieldToSortBy() == null || pdbRestRequest
-              .getFieldToSortBy().trim().isEmpty()) ? "" : (pdbRestRequest
-              .getFieldToSortBy() + (pdbRestRequest.isAscending() ? " asc"
-              : " desc"));
-      // Build request parameters for the REST Request
-      WebResource webResource = client.resource(PDB_SEARCH_ENDPOINT)
-              .queryParam("wt", "json").queryParam("fl", wantedFields)
-              .queryParam("rows", String.valueOf(responseSize))
-              .queryParam("q", pdbRestRequest.getQuery())
-              .queryParam("sort", sortParam);
-
-      // Execute the REST request
-      ClientResponse clientResponse = webResource.accept(
-              MediaType.APPLICATION_JSON).get(ClientResponse.class);
-
-      // Get the JSON string from the response object
-      String responseString = clientResponse.getEntity(String.class);
-
-      // Check the response status and report exception if one occurs
-      if (clientResponse.getStatus() != 200)
-      {
-        String errorMessage = "";
-        if (clientResponse.getStatus() == 400)
-        {
-          errorMessage = parseJsonExceptionString(responseString);
-          throw new Exception(errorMessage);
-        }
-        else
-        {
-          errorMessage = getMessageByHTTPStatusCode(clientResponse
-                  .getStatus());
-          throw new Exception(errorMessage);
-        }
-      }
-
-      // Make redundant objects eligible for garbage collection to conserve
-      // memory
-      clientResponse = null;
-      client = null;
-
-      // Process the response and return the result to the caller.
-      return parsePDBJsonResponse(responseString, pdbRestRequest);
-    } catch (Exception e)
-    {
-      String exceptionMsg = e.getMessage();
-      if (exceptionMsg.contains("SocketException"))
-      {
-        // No internet connection
-        throw new Exception(
-                MessageManager
-                        .getString("exception.unable_to_detect_internet_connection"));
-      }
-      else if (exceptionMsg.contains("UnknownHostException"))
-      {
-        // The server 'www.ebi.ac.uk' is unreachable
-        throw new Exception(
-                MessageManager
-                        .getString("exception.pdb_server_unreachable"));
-      }
-      else
-      {
-        throw e;
-      }
-    }
-  }
-
-  public String getMessageByHTTPStatusCode(int code)
-  {
-    String message = "";
-    switch (code)
-    {
-    case 410:
-      message = MessageManager
-              .getString("exception.pdb_rest_service_no_longer_available");
-      break;
-    case 403:
-    case 404:
-      message = MessageManager.getString("exception.resource_not_be_found");
-      break;
-    case 408:
-    case 409:
-    case 500:
-    case 501:
-    case 502:
-    case 503:
-    case 504:
-    case 505:
-      message = MessageManager.getString("exception.pdb_server_error");
-      break;
-
-    default:
-      break;
-    }
-    return message;
-  }
-
-  /**
-   * Process error response from PDB server if/when one occurs.
-   * 
-   * @param jsonResponse
-   *          the JSON string containing error message from the server
-   * @return the processed error message from the JSON string
-   */
-  public static String parseJsonExceptionString(String jsonErrorResponse)
-  {
-    StringBuilder errorMessage = new StringBuilder(
-            "\n============= PDB Rest Client RunTime error =============\n");
-
-    try
-    {
-      JSONParser jsonParser = new JSONParser();
-      JSONObject jsonObj = (JSONObject) jsonParser.parse(jsonErrorResponse);
-      JSONObject errorResponse = (JSONObject) jsonObj.get("error");
-
-      JSONObject responseHeader = (JSONObject) jsonObj
-              .get("responseHeader");
-      JSONObject paramsObj = (JSONObject) responseHeader.get("params");
-      String status = responseHeader.get("status").toString();
-      String message = errorResponse.get("msg").toString();
-      String query = paramsObj.get("q").toString();
-      String fl = paramsObj.get("fl").toString();
-
-      errorMessage.append("Status: ").append(status).append("\n");
-      errorMessage.append("Message: ").append(message).append("\n");
-      errorMessage.append("query: ").append(query).append("\n");
-      errorMessage.append("fl: ").append(fl).append("\n");
-
-    } catch (ParseException e)
-    {
-      e.printStackTrace();
-    }
-    return errorMessage.toString();
-  }
-
-  /**
-   * Parses the JSON response string from PDB REST API. The response is dynamic
-   * hence, only fields specifically requested for in the 'wantedFields'
-   * parameter is fetched/processed
-   * 
-   * @param pdbJsonResponseString
-   *          the JSON string to be parsed
-   * @param pdbRestRequest
-   *          the request object which contains parameters used to process the
-   *          JSON string
-   * @return
-   */
-  @SuppressWarnings("unchecked")
-  public static PDBRestResponse parsePDBJsonResponse(
-          String pdbJsonResponseString, PDBRestRequest pdbRestRequest)
-  {
-    PDBRestResponse searchResult = new PDBRestResponse();
-    List<PDBResponseSummary> result = null;
-    try
-    {
-      JSONParser jsonParser = new JSONParser();
-      JSONObject jsonObj = (JSONObject) jsonParser
-              .parse(pdbJsonResponseString);
-
-      JSONObject pdbResponse = (JSONObject) jsonObj.get("response");
-      String queryTime = ((JSONObject) jsonObj.get("responseHeader")).get(
-              "QTime").toString();
-      int numFound = Integer
-              .valueOf(pdbResponse.get("numFound").toString());
-      if (numFound > 0)
-      {
-        result = new ArrayList<PDBResponseSummary>();
-        JSONArray docs = (JSONArray) pdbResponse.get("docs");
-        for (Iterator<JSONObject> docIter = docs.iterator(); docIter
-                .hasNext();)
-        {
-          JSONObject doc = docIter.next();
-          result.add(searchResult.new PDBResponseSummary(doc,
-                  pdbRestRequest));
-        }
-        searchResult.setNumberOfItemsFound(numFound);
-        searchResult.setResponseTime(queryTime);
-        searchResult.setSearchSummary(result);
-      }
-    } catch (ParseException e)
-    {
-      e.printStackTrace();
-    }
-    return searchResult;
-  }
-
-  /**
-   * Takes a collection of PDBDocField and converts its 'code' Field values into
-   * a comma delimited string.
-   * 
-   * @param pdbDocfields
-   *          the collection of PDBDocField to process
-   * @return the comma delimited string from the pdbDocFields collection
-   */
-  public static String getPDBDocFieldsAsCommaDelimitedString(
-          Collection<PDBDocField> pdbDocfields)
-  {
-    String result = "";
-    if (pdbDocfields != null && !pdbDocfields.isEmpty())
-    {
-      StringBuilder returnedFields = new StringBuilder();
-      for (PDBDocField field : pdbDocfields)
-      {
-        returnedFields.append(",").append(field.getCode());
-      }
-      returnedFields.deleteCharAt(0);
-      result = returnedFields.toString();
-    }
-    return result;
-  }
-
-  /**
-   * Determines the column index for 'PDB Id' Fields in the dynamic summary
-   * table. The PDB Id serves as a unique identifier for a given row in the
-   * summary table
-   * 
-   * @param wantedFields
-   *          the available table columns in no particular order
-   * @return the pdb id field column index
-   */
-  public static int getPDBIdColumIndex(
-          Collection<PDBDocField> wantedFields, boolean hasRefSeq)
-  {
-
-    // If a reference sequence is attached then start counting from 1 else
-    // start from zero
-    int pdbFieldIndexCounter = hasRefSeq ? 1 : 0;
-
-    for (PDBDocField field : wantedFields)
-    {
-      if (field.equals(PDBDocField.PDB_ID))
-      {
-        break; // Once PDB Id index is determined exit iteration
-      }
-      ++pdbFieldIndexCounter;
-    }
-    return pdbFieldIndexCounter;
-  }
-
-  /**
-   * This enum represents the fields available in the PDB JSON response
-   *
-   */
-  public enum PDBDocField
-  {
-    PDB_ID("PDB Id", "pdb_id"), TITLE("Title", "title"), MOLECULE_NAME(
-            "Molecule", "molecule_name"), MOLECULE_TYPE("Molecule Type",
-            "molecule_type"), MOLECULE_SEQUENCE("Sequence",
-            "molecule_sequence"), PFAM_ACCESSION("PFAM Accession",
-            "pfam_accession"), PFAM_NAME("PFAM Name", "pfam_name"), INTERPRO_NAME(
-            "InterPro Name", "interpro_name"), INTERPRO_ACCESSION(
-            "InterPro Accession", "interpro_accession"), UNIPROT_ID(
-            "UniProt Id", "uniprot_id"), UNIPROT_ACCESSION(
-            "UniProt Accession", "uniprot_accession"), UNIPROT_COVERAGE(
-            "UniProt Coverage", "uniprot_coverage"), UNIPROT_FEATURES(
-            "Uniprot Features", "uniprot_features"), R_FACTOR("R Factor",
-            "r_factor"), RESOLUTION("Resolution", "resolution"), DATA_QUALITY(
-            "Data Quality", "data_quality"), OVERALL_QUALITY(
-            "Overall Quality", "overall_quality"), POLYMER_COUNT(
-            "Number of Polymers", "number_of_polymers"), PROTEIN_CHAIN_COUNT(
-            "Number of Protein Chains", "number_of_protein_chains"), BOUND_MOLECULE_COUNT(
-            "Number of Bound Molecule", "number_of_bound_molecules"), POLYMER_RESIDUE_COUNT(
-            "Number of Polymer Residue", "number_of_polymer_residues"), GENUS(
-            "GENUS", "genus"), GENE_NAME("Gene Name", "gene_name"), EXPERIMENTAL_METHOD(
-            "Experimental Method", "experimental_method"), GO_ID("GO Id",
-            "go_id"), ASSEMBLY_ID("Assembly Id", "assembly_form"), ASSEMBLY_FORM(
-            "Assembly Form", "assembly_id"), ASSEMBLY_TYPE("Assembly Type",
-            "assembly_type"), SPACE_GROUP("Space Group", "spacegroup"), CATH_CODE(
-            "Cath Code", "cath_code"), TAX_ID("Tax Id", "tax_id"), TAX_QUERY(
-            "Tax Query", "tax_query"), INTERACTING_ENTRY_ID(
-            "Interacting Entry Id", "interacting_entry_id"), INTERACTING_ENTITY_ID(
-            "Interacting Entity Id", "interacting_entity_id"), INTERACTING_MOLECULES(
-            "Interacting Molecules", "interacting_molecules"), PUBMED_ID(
-            "Pubmed Id", "pubmed_id"), STATUS("Status", "status"), MODEL_QUALITY(
-            "Model Quality", "model_quality"), PIVOT_RESOLUTION(
-            "Pivot Resolution", "pivot_resolution"), DATA_REDUCTION_SOFTWARE(
-            "Data reduction software", "data_reduction_software"), MAX_OBSERVED_RES(
-            "Max observed residues", "max_observed_residues"), ORG_SCI_NAME(
-            "Organism scientific name", "organism_scientific_name"), SUPER_KINGDOM(
-            "Super kingdom", "superkingdom"), RANK("Rank", "rank"), CRYSTALLISATION_PH(
-            "Crystallisation Ph", "crystallisation_ph"), BIOLOGICAL_FUNCTION(
-            "Biological Function", "biological_function"), BIOLOGICAL_PROCESS(
-            "Biological Process", "biological_process"), BIOLOGICAL_CELL_COMPONENT(
-            "Biological Cell Component", "biological_cell_component"), COMPOUND_NAME(
-            "Compound Name", "compound_name"), COMPOUND_ID("Compound Id",
-            "compound_id"), COMPOUND_WEIGHT("Compound Weight",
-            "compound_weight"), COMPOUND_SYSTEMATIC_NAME(
-            "Compound Systematic Name", "compound_systematic_name"), INTERACTING_LIG(
-            "Interacting Ligands", "interacting_ligands"), JOURNAL(
-            "Journal", "journal"), ALL_AUTHORS("All Authors", "all_authors"), EXPERIMENTAL_DATA_AVAILABLE(
-            "Experiment Data Available", "experiment_data_available"), DIFFRACTION_PROTOCOL(
-            "Diffraction Protocol", "diffraction_protocol"), REFINEMENT_SOFTWARE(
-            "Refinement Software", "refinement_software"), STRUCTURE_DETERMINATION_METHOD(
-            "Structure Determination Method",
-            "structure_determination_method"), SYNCHROTON_SITE(
-            "Synchrotron Site", "synchrotron_site"), SAMPLE_PREP_METHOD(
-            "Sample Preparation Method", "sample_preparation_method"), ENTRY_AUTHORS(
-            "Entry Authors", "entry_authors"), CITATION_TITLE(
-            "Citation Title", "citation_title"), STRUCTURE_SOLUTION_SOFTWARE(
-            "Structure Solution Software", "structure_solution_software"), ENTRY_ENTITY(
-            "Entry Entity", "entry_entity"), R_FREE("R Free", "r_free"), NO_OF_POLYMER_ENTITIES(
-            "Number of Polymer Entities", "number_of_polymer_entities"), NO_OF_BOUND_ENTITIES(
-            "Number of Bound Entities", "number_of_bound_entities"), CRYSTALLISATION_RESERVOIR(
-            "Crystallisation Reservoir", "crystallisation_reservoir"), DATA_SCALING_SW(
-            "Data Scalling Software", "data_scaling_software"), DETECTOR(
-            "Detector", "detector"), DETECTOR_TYPE("Detector Type",
-            "detector_type"), MODIFIED_RESIDUE_FLAG(
-            "Modified Residue Flag", "modified_residue_flag"), NUMBER_OF_COPIES(
-            "Number of Copies", "number_of_copies"), STRUCT_ASYM_ID(
-            "Struc Asym Id", "struct_asym_id"), HOMOLOGUS_PDB_ENTITY_ID(
-            "Homologus PDB Entity Id", "homologus_pdb_entity_id"), MOLECULE_SYNONYM(
-            "Molecule Synonym", "molecule_synonym"), DEPOSITION_SITE(
-            "Deposition Site", "deposition_site"), SYNCHROTRON_BEAMLINE(
-            "Synchrotron Beamline", "synchrotron_beamline"), ENTITY_ID(
-            "Entity Id", "entity_id"), BEAM_SOURCE_NAME("Beam Source Name",
-            "beam_source_name"), PROCESSING_SITE("Processing Site",
-            "processing_site"), ENTITY_WEIGHT("Entity Weight",
-            "entity_weight"), VERSION("Version", "_version_"), ALL("ALL",
-            "text");
-
-    private String name;
-
-    private String code;
-
-    PDBDocField(String name, String code)
-    {
-      this.name = name;
-      this.code = code;
-    }
-
-    public String getName()
-    {
-      return name;
-    }
-
-    public String getCode()
-    {
-      return code;
-    }
-
-    public String toString()
-    {
-      return name;
-    }
-  }
-}
diff --git a/src/jalview/ws/dbsources/Pdb.java b/src/jalview/ws/dbsources/Pdb.java
index a125ce4..63d8b5d 100644
--- a/src/jalview/ws/dbsources/Pdb.java
+++ b/src/jalview/ws/dbsources/Pdb.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,20 +20,22 @@
  */
 package jalview.ws.dbsources;
 
+import jalview.api.FeatureSettingsModelI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.PDBEntry;
+import jalview.datamodel.PDBEntry.Type;
 import jalview.datamodel.SequenceI;
 import jalview.io.FormatAdapter;
+import jalview.io.PDBFeatureSettings;
+import jalview.structure.StructureImportSettings;
 import jalview.util.MessageManager;
 import jalview.ws.ebi.EBIFetchClient;
-import jalview.ws.seqfetcher.DbSourceProxy;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Vector;
 
 import com.stevesoft.pat.Regex;
 
@@ -41,12 +43,17 @@ import com.stevesoft.pat.Regex;
  * @author JimP
  * 
  */
-public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
+public class Pdb extends EbiFileRetrievedProxy
 {
+  private static final String SEPARATOR = "|";
+
+  private static final String COLON = ":";
+
+  private static final int PDB_ID_LENGTH = 4;
+
   public Pdb()
   {
     super();
-    addDbSourceProperty(DBRefSource.PROTSEQDB);
   }
 
   /*
@@ -54,9 +61,9 @@ public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getAccessionSeparator()
    */
+  @Override
   public String getAccessionSeparator()
   {
-    // TODO Auto-generated method stub
     return null;
   }
 
@@ -65,6 +72,7 @@ public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getAccessionValidator()
    */
+  @Override
   public Regex getAccessionValidator()
   {
     return new Regex("([1-9][0-9A-Za-z]{3}):?([ _A-Za-z0-9]?)");
@@ -75,6 +83,7 @@ public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getDbSource()
    */
+  @Override
   public String getDbSource()
   {
     return DBRefSource.PDB;
@@ -85,6 +94,7 @@ public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getDbVersion()
    */
+  @Override
   public String getDbVersion()
   {
     return "0";
@@ -95,34 +105,45 @@ public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getSequenceRecords(java.lang.String[])
    */
+  @Override
   public AlignmentI getSequenceRecords(String queries) throws Exception
   {
-    AlignmentI pdbfile = null;
-    Vector result = new Vector();
+    AlignmentI pdbAlignment = null;
     String chain = null;
     String id = null;
-    if (queries.indexOf(":") > -1)
+    if (queries.indexOf(COLON) > -1)
     {
-      chain = queries.substring(queries.indexOf(":") + 1);
-      id = queries.substring(0, queries.indexOf(":"));
+      chain = queries.substring(queries.indexOf(COLON) + 1);
+      id = queries.substring(0, queries.indexOf(COLON));
     }
     else
     {
       id = queries;
     }
-    if (queries.length() > 4 && chain == null)
+
+    /*
+     * extract chain code if it is appended to the id and we
+     * don't already have one
+     */
+    if (queries.length() > PDB_ID_LENGTH && chain == null)
     {
-      chain = queries.substring(4, 5);
-      id = queries.substring(0, 4);
+      chain = queries.substring(PDB_ID_LENGTH, PDB_ID_LENGTH + 1);
+      id = queries.substring(0, PDB_ID_LENGTH);
     }
+
     if (!isValidReference(id))
     {
       System.err.println("Ignoring invalid pdb query: '" + id + "'");
       stopQuery();
       return null;
     }
+    String ext = StructureImportSettings.getDefaultStructureFileFormat()
+            .equalsIgnoreCase(Type.MMCIF.toString()) ? ".cif" : ".xml";
     EBIFetchClient ebi = new EBIFetchClient();
-    file = ebi.fetchDataAsFile("pdb:" + id, "pdb", "raw").getAbsolutePath();
+    file = ebi.fetchDataAsFile(
+            "pdb:" + id,
+            StructureImportSettings.getDefaultStructureFileFormat()
+                    .toLowerCase(), ext).getAbsolutePath();
     stopQuery();
     if (file == null)
     {
@@ -131,12 +152,13 @@ public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
     try
     {
 
-      pdbfile = new FormatAdapter().readFile(file,
-              jalview.io.AppletFormatAdapter.FILE, "PDB");
-      if (pdbfile != null)
+      pdbAlignment = new FormatAdapter().readFile(file,
+              jalview.io.AppletFormatAdapter.FILE,
+              StructureImportSettings.getDefaultStructureFileFormat());
+      if (pdbAlignment != null)
       {
         List<SequenceI> toremove = new ArrayList<SequenceI>();
-        for (SequenceI pdbcs : pdbfile.getSequences())
+        for (SequenceI pdbcs : pdbAlignment.getSequences())
         {
           String chid = null;
           // Mapping map=null;
@@ -147,16 +169,16 @@ public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
               chid = pid.getChainCode();
 
             }
-            ;
-
           }
           if (chain == null
                   || (chid != null && (chid.equals(chain)
                           || chid.trim().equals(chain.trim()) || (chain
                           .trim().length() == 0 && chid.equals("_")))))
           {
-            pdbcs.setName(jalview.datamodel.DBRefSource.PDB + "|" + id
-                    + "|" + pdbcs.getName());
+            // FIXME seems to result in 'PDB|1QIP|1qip|A' - 1QIP is redundant.
+            // TODO: suggest simplify naming to 1qip|A as default name defined
+            pdbcs.setName(jalview.datamodel.DBRefSource.PDB + SEPARATOR
+                    + id + SEPARATOR + pdbcs.getName());
             // Might need to add more metadata to the PDBEntry object
             // like below
             /*
@@ -188,18 +210,18 @@ public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
         // now remove marked sequences
         for (SequenceI pdbcs : toremove)
         {
-          pdbfile.deleteSequence(pdbcs);
+          pdbAlignment.deleteSequence(pdbcs);
           if (pdbcs.getAnnotation() != null)
           {
             for (AlignmentAnnotation aa : pdbcs.getAnnotation())
             {
-              pdbfile.deleteAnnotation(aa);
+              pdbAlignment.deleteAnnotation(aa);
             }
           }
         }
       }
 
-      if (pdbfile == null || pdbfile.getHeight() < 1)
+      if (pdbAlignment == null || pdbAlignment.getHeight() < 1)
       {
         throw new Exception(MessageManager.formatMessage(
                 "exception.no_pdb_records_for_chain", new String[] { id,
@@ -211,7 +233,7 @@ public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
       stopQuery();
       throw (ex);
     }
-    return pdbfile;
+    return pdbAlignment;
   }
 
   /*
@@ -219,6 +241,7 @@ public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String)
    */
+  @Override
   public boolean isValidReference(String accession)
   {
     Regex r = getAccessionValidator();
@@ -226,13 +249,15 @@ public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
   }
 
   /**
-   * obtain human glyoxalase chain A sequence
+   * human glyoxalase
    */
+  @Override
   public String getTestQuery()
   {
-    return "1QIPA";
+    return "1QIP";
   }
 
+  @Override
   public String getDbName()
   {
     return "PDB"; // getDbSource();
@@ -243,4 +268,19 @@ public class Pdb extends EbiFileRetrievedProxy implements DbSourceProxy
   {
     return 0;
   }
+
+  /**
+   * Returns a descriptor for suitable feature display settings with
+   * <ul>
+   * <li>ResNums or insertions features visible</li>
+   * <li>insertions features coloured red</li>
+   * <li>ResNum features coloured by label</li>
+   * <li>Insertions displayed above (on top of) ResNums</li>
+   * </ul>
+   */
+  @Override
+  public FeatureSettingsModelI getFeatureColourScheme()
+  {
+    return new PDBFeatureSettings();
+  }
 }
diff --git a/src/jalview/ws/dbsources/Pfam.java b/src/jalview/ws/dbsources/Pfam.java
index 356eac3..4c7f657 100644
--- a/src/jalview/ws/dbsources/Pfam.java
+++ b/src/jalview/ws/dbsources/Pfam.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,7 +22,8 @@ package jalview.ws.dbsources;
 
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
-import jalview.ws.seqfetcher.DbSourceProxy;
+import jalview.datamodel.DBRefSource;
+import jalview.io.FormatAdapter;
 
 import com.stevesoft.pat.Regex;
 
@@ -34,15 +35,12 @@ import com.stevesoft.pat.Regex;
  * @author JimP
  * 
  */
-abstract public class Pfam extends Xfam implements DbSourceProxy
+abstract public class Pfam extends Xfam
 {
 
   public Pfam()
   {
     super();
-    // all extensions of this PFAM source base class are DOMAINDB sources
-    addDbSourceProperty(jalview.datamodel.DBRefSource.DOMAINDB);
-    addDbSourceProperty(jalview.datamodel.DBRefSource.ALIGNMENTDB);
   }
 
   /*
@@ -50,6 +48,7 @@ abstract public class Pfam extends Xfam implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getAccessionSeparator()
    */
+  @Override
   public String getAccessionSeparator()
   {
     // TODO Auto-generated method stub
@@ -61,6 +60,7 @@ abstract public class Pfam extends Xfam implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getAccessionValidator()
    */
+  @Override
   public Regex getAccessionValidator()
   {
     // TODO Auto-generated method stub
@@ -111,22 +111,22 @@ abstract public class Pfam extends Xfam implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getSequenceRecords(java.lang.String[])
    */
+  @Override
   public AlignmentI getSequenceRecords(String queries) throws Exception
   {
     // TODO: this is not a perfect implementation. We need to be able to add
     // individual references to each sequence in each family alignment that's
     // retrieved.
     startQuery();
-    AlignmentI rcds = new jalview.io.FormatAdapter().readFile(getXFAMURL()
+    AlignmentI rcds = new FormatAdapter().readFile(getXFAMURL()
             + queries.trim().toUpperCase(), jalview.io.FormatAdapter.URL,
             "STH");
     for (int s = 0, sNum = rcds.getHeight(); s < sNum; s++)
     {
-      rcds.getSequenceAt(s).addDBRef(
-              new DBRefEntry(jalview.datamodel.DBRefSource.PFAM,
-              // getDbSource(),
-                      getDbVersion(), queries.trim().toUpperCase()));
-      if (!getDbSource().equals(jalview.datamodel.DBRefSource.PFAM))
+      rcds.getSequenceAt(s).addDBRef(new DBRefEntry(DBRefSource.PFAM,
+      // getDbSource(),
+              getDbVersion(), queries.trim().toUpperCase()));
+      if (!getDbSource().equals(DBRefSource.PFAM))
       { // add the specific ref too
         rcds.getSequenceAt(s).addDBRef(
                 new DBRefEntry(getDbSource(), getDbVersion(), queries
@@ -142,6 +142,7 @@ abstract public class Pfam extends Xfam implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String)
    */
+  @Override
   public boolean isValidReference(String accession)
   {
     return accession.indexOf("PF") == 0;
@@ -151,9 +152,10 @@ abstract public class Pfam extends Xfam implements DbSourceProxy
    * public String getDbName() { return "PFAM"; // getDbSource(); }
    */
 
+  @Override
   public String getXfamSource()
   {
-    return jalview.datamodel.DBRefSource.PFAM;
+    return DBRefSource.PFAM;
   }
 
 }
diff --git a/src/jalview/ws/dbsources/PfamFull.java b/src/jalview/ws/dbsources/PfamFull.java
index a979098..ddbe7e6 100644
--- a/src/jalview/ws/dbsources/PfamFull.java
+++ b/src/jalview/ws/dbsources/PfamFull.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,13 +20,11 @@
  */
 package jalview.ws.dbsources;
 
-import jalview.ws.seqfetcher.DbSourceProxy;
-
 /**
  * flyweight class specifying retrieval of Full family alignments from PFAM
  * 
  */
-public class PfamFull extends Pfam implements DbSourceProxy
+public class PfamFull extends Pfam
 {
   public PfamFull()
   {
@@ -38,9 +36,10 @@ public class PfamFull extends Pfam implements DbSourceProxy
    * 
    * @see jalview.ws.dbsources.Pfam#getPFAMURL()
    */
+  @Override
   protected String getXFAMURL()
   {
-    return "http://pfam.sanger.ac.uk/family/alignment/download/format?alnType=full&format=stockholm&order=t&case=l&gaps=default&entry=";
+    return "http://pfam.xfam.org/family/alignment/download/format?alnType=full&format=stockholm&order=t&case=l&gaps=default&entry=";
   }
 
   /*
@@ -48,21 +47,25 @@ public class PfamFull extends Pfam implements DbSourceProxy
    * 
    * @see jalview.ws.seqfetcher.DbSourceProxy#getDbName()
    */
+  @Override
   public String getDbName()
   {
     return "PFAM (Full)";
   }
 
+  @Override
   public String getDbSource()
   {
     return getDbName(); // so we have unique DbSource string.
   }
 
+  @Override
   public String getTestQuery()
   {
     return "PF03760";
   }
 
+  @Override
   public String getDbVersion()
   {
     return null;
diff --git a/src/jalview/ws/dbsources/PfamSeed.java b/src/jalview/ws/dbsources/PfamSeed.java
index 027db80..cbee74a 100644
--- a/src/jalview/ws/dbsources/PfamSeed.java
+++ b/src/jalview/ws/dbsources/PfamSeed.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,15 +20,13 @@
  */
 package jalview.ws.dbsources;
 
-import jalview.ws.seqfetcher.DbSourceProxy;
-
 /**
  * flyweight class specifying retrieval of Seed alignments from PFAM
  * 
  * @author JimP
  * 
  */
-public class PfamSeed extends Pfam implements DbSourceProxy
+public class PfamSeed extends Pfam
 {
   public PfamSeed()
   {
@@ -40,9 +38,10 @@ public class PfamSeed extends Pfam implements DbSourceProxy
    * 
    * @see jalview.ws.dbsources.Pfam#getPFAMURL()
    */
+  @Override
   protected String getXFAMURL()
   {
-    return "http://pfam.sanger.ac.uk/family/alignment/download/format?alnType=seed&format=stockholm&order=t&case=l&gaps=default&entry=";
+    return "http://pfam.xfam.org/family/alignment/download/format?alnType=seed&format=stockholm&order=t&case=l&gaps=default&entry=";
   }
 
   /*
@@ -50,16 +49,19 @@ public class PfamSeed extends Pfam implements DbSourceProxy
    * 
    * @see jalview.ws.seqfetcher.DbSourceProxy#getDbName()
    */
+  @Override
   public String getDbName()
   {
     return "PFAM (Seed)";
   }
 
+  @Override
   public String getDbSource()
   {
     return jalview.datamodel.DBRefSource.PFAM; // archetype source
   }
 
+  @Override
   public String getTestQuery()
   {
     return "PF03760";
diff --git a/src/jalview/ws/dbsources/Rfam.java b/src/jalview/ws/dbsources/Rfam.java
index 842090d..7a1a490 100644
--- a/src/jalview/ws/dbsources/Rfam.java
+++ b/src/jalview/ws/dbsources/Rfam.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,7 @@
  */
 package jalview.ws.dbsources;
 
-import jalview.ws.seqfetcher.DbSourceProxy;
+import jalview.datamodel.DBRefSource;
 
 import com.stevesoft.pat.Regex;
 
@@ -29,15 +29,12 @@ import com.stevesoft.pat.Regex;
  * 
  * @author Lauren Michelle Lui
  */
-abstract public class Rfam extends Xfam implements DbSourceProxy
+abstract public class Rfam extends Xfam
 {
 
   public Rfam()
   {
     super();
-    // all extensions of this RFAM source base class are DOMAINDB sources
-    addDbSourceProperty(jalview.datamodel.DBRefSource.DOMAINDB);
-    addDbSourceProperty(jalview.datamodel.DBRefSource.ALIGNMENTDB);
   }
 
   /*
@@ -46,6 +43,7 @@ abstract public class Rfam extends Xfam implements DbSourceProxy
    * @see jalview.ws.DbSourceProxy#getAccessionSeparator() Left here for
    * consistency with Pfam class
    */
+  @Override
   public String getAccessionSeparator()
   {
     // TODO Auto-generated method stub
@@ -57,6 +55,7 @@ abstract public class Rfam extends Xfam implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getAccessionValidator() * Left here for
    */
+  @Override
   public Regex getAccessionValidator()
   {
     // TODO Auto-generated method stub
@@ -100,6 +99,7 @@ abstract public class Rfam extends Xfam implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String)
    */
+  @Override
   public boolean isValidReference(String accession)
   {
     return accession.indexOf("RF") == 0;
@@ -110,9 +110,10 @@ abstract public class Rfam extends Xfam implements DbSourceProxy
    * 
    * @see jalview.ws.dbsources.Xfam#getXfamSource()
    */
+  @Override
   public String getXfamSource()
   {
-    return jalview.datamodel.DBRefSource.RFAM;
+    return DBRefSource.RFAM;
   }
 
 }
diff --git a/src/jalview/ws/dbsources/RfamFull.java b/src/jalview/ws/dbsources/RfamFull.java
index 09d5a93..f8d09f0 100644
--- a/src/jalview/ws/dbsources/RfamFull.java
+++ b/src/jalview/ws/dbsources/RfamFull.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,15 +20,13 @@
  */
 package jalview.ws.dbsources;
 
-import jalview.ws.seqfetcher.DbSourceProxy;
-
 /**
  * Flyweight class specifying retrieval of Full family alignments from RFAM
  * 
  * @author Lauren Michelle Lui
  * 
  */
-public class RfamFull extends Rfam implements DbSourceProxy
+public class RfamFull extends Rfam
 {
   public RfamFull()
   {
@@ -40,9 +38,11 @@ public class RfamFull extends Rfam implements DbSourceProxy
    * 
    * @see jalview.ws.dbsources.Rfam#getXFAMURL()
    */
+  @Override
   protected String getXFAMURL()
   {
-    return "http://rfam.sanger.ac.uk/family/alignment/download/format?alnType=full&nseLabels=0&format=stockholm&acc=";
+    return "http://rfam.xfam.org/family/alignment/download/format?alnType=full&nseLabels=0&format=stockholm&acc=";
+
   }
 
   /*
@@ -50,16 +50,19 @@ public class RfamFull extends Rfam implements DbSourceProxy
    * 
    * @see jalview.ws.seqfetcher.DbSourceProxy#getDbName()
    */
+  @Override
   public String getDbName()
   {
     return "RFAM (Full)";
   }
 
+  @Override
   public String getDbSource()
   {
     return getDbName(); // so we have unique DbSource string.
   }
 
+  @Override
   public String getTestQuery()
   {
     // Can be retrieved from http://rfam.janelia.org/cgi-bin/getdesc?acc=RF00014
@@ -68,6 +71,7 @@ public class RfamFull extends Rfam implements DbSourceProxy
     return "RF00014";
   }
 
+  @Override
   public String getDbVersion()
   {
     return null;
diff --git a/src/jalview/ws/dbsources/RfamSeed.java b/src/jalview/ws/dbsources/RfamSeed.java
index 22d8c71..b17f87a 100644
--- a/src/jalview/ws/dbsources/RfamSeed.java
+++ b/src/jalview/ws/dbsources/RfamSeed.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,15 +20,13 @@
  */
 package jalview.ws.dbsources;
 
-import jalview.ws.seqfetcher.DbSourceProxy;
-
 /**
  * Flyweight class specifying retrieval of Seed family alignments from RFAM
  * 
  * @author Lauren Michelle Lui
  * 
  */
-public class RfamSeed extends Rfam implements DbSourceProxy
+public class RfamSeed extends Rfam
 {
   public RfamSeed()
   {
@@ -40,33 +38,44 @@ public class RfamSeed extends Rfam implements DbSourceProxy
    * 
    * @see jalview.ws.dbsources.Rfam#getRFAMURL()
    */
+  @Override
   protected String getXFAMURL()
   {
-    return "http://rfam.sanger.ac.uk/family/alignment/download/format?alnType=seed&nseLabels=0&format=stockholm&acc=";
+    return "http://rfam.xfam.org/family/";
     // Janelia Farms url
     // "http://rfam.janelia.org/cgi-bin/getalignment?type=seed&fmt=stockholm&acc=";
   }
 
+  @Override
+  public String getXFAMURLSUFFIX()
+  {
+    return "/alignment";
+  }
+
   /*
    * (non-Javadoc)
    * 
    * @see jalview.ws.seqfetcher.DbSourceProxy#getDbName()
    */
+  @Override
   public String getDbName()
   {
     return "RFAM (Seed)";
   }
 
+  @Override
   public String getDbSource()
   {
     return getDbName(); // so we have unique DbSource string.
   }
 
+  @Override
   public String getTestQuery()
   {
     return "RF00014";
   } // http://rfam.janelia.org/cgi-bin/getdesc?acc=RF00014
 
+  @Override
   public String getDbVersion()
   {
     return null;
diff --git a/src/jalview/ws/dbsources/Uniprot.java b/src/jalview/ws/dbsources/Uniprot.java
index 0d38ca6..f821ad6 100644
--- a/src/jalview/ws/dbsources/Uniprot.java
+++ b/src/jalview/ws/dbsources/Uniprot.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,23 +20,27 @@
  */
 package jalview.ws.dbsources;
 
+import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.PDBEntry;
+import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.datamodel.UniprotEntry;
 import jalview.datamodel.UniprotFile;
 import jalview.ws.ebi.EBIFetchClient;
-import jalview.ws.seqfetcher.DbSourceProxy;
 import jalview.ws.seqfetcher.DbSourceProxyImpl;
 
 import java.io.File;
 import java.io.FileReader;
 import java.io.Reader;
+import java.net.URL;
+import java.util.ArrayList;
 import java.util.Vector;
 
+import org.exolab.castor.mapping.Mapping;
 import org.exolab.castor.xml.Unmarshaller;
 
 import com.stevesoft.pat.Regex;
@@ -45,14 +49,14 @@ import com.stevesoft.pat.Regex;
  * @author JimP
  * 
  */
-public class Uniprot extends DbSourceProxyImpl implements DbSourceProxy
+public class Uniprot extends DbSourceProxyImpl
 {
-
   private static final String BAR_DELIMITER = "|";
 
-  private static final String NEWLINE = "\n";
-
-  private static org.exolab.castor.mapping.Mapping map;
+  /*
+   * Castor mapping loaded from uniprot_mapping.xml
+   */
+  private static Mapping map;
 
   /**
    * Constructor
@@ -60,9 +64,6 @@ public class Uniprot extends DbSourceProxyImpl implements DbSourceProxy
   public Uniprot()
   {
     super();
-    addDbSourceProperty(DBRefSource.SEQDB, DBRefSource.SEQDB);
-    addDbSourceProperty(DBRefSource.PROTSEQDB);
-    // addDbSourceProperty(DBRefSource.MULTIACC, new Integer(50));
   }
 
   /*
@@ -70,9 +71,10 @@ public class Uniprot extends DbSourceProxyImpl implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getAccessionSeparator()
    */
+  @Override
   public String getAccessionSeparator()
   {
-    return null; // ";";
+    return null;
   }
 
   /*
@@ -80,6 +82,7 @@ public class Uniprot extends DbSourceProxyImpl implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getAccessionValidator()
    */
+  @Override
   public Regex getAccessionValidator()
   {
     return new Regex("([A-Z]+[0-9]+[A-Z0-9]+|[A-Z0-9]+_[A-Z0-9]+)");
@@ -90,6 +93,7 @@ public class Uniprot extends DbSourceProxyImpl implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getDbSource()
    */
+  @Override
   public String getDbSource()
   {
     return DBRefSource.UNIPROT;
@@ -100,6 +104,7 @@ public class Uniprot extends DbSourceProxyImpl implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getDbVersion()
    */
+  @Override
   public String getDbVersion()
   {
     return "0"; // we really don't know what version we're on.
@@ -121,9 +126,8 @@ public class Uniprot extends DbSourceProxyImpl implements DbSourceProxy
       if (map == null)
       {
         // 1. Load the mapping information from the file
-        map = new org.exolab.castor.mapping.Mapping(uni.getClass()
-                .getClassLoader());
-        java.net.URL url = getClass().getResource("/uniprot_mapping.xml");
+        map = new Mapping(uni.getClass().getClassLoader());
+        URL url = getClass().getResource("/uniprot_mapping.xml");
         map.loadMapping(url);
       }
 
@@ -148,6 +152,7 @@ public class Uniprot extends DbSourceProxyImpl implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#getSequenceRecords(java.lang.String[])
    */
+  @Override
   public AlignmentI getSequenceRecords(String queries) throws Exception
   {
     startQuery();
@@ -160,37 +165,18 @@ public class Uniprot extends DbSourceProxyImpl implements DbSourceProxy
       // uniprotxml parameter required since december 2007
       // uniprotkb dbname changed introduced december 2008
       File file = ebi.fetchDataAsFile("uniprotkb:" + queries, "uniprotxml",
-              null);
+              ".xml");
       Vector<UniprotEntry> entries = getUniprotEntries(new FileReader(file));
 
       if (entries != null)
       {
-        /*
-         * If Castor binding included sequence at length, we could guesstimate the
-         * size of buffer to hold the alignment
-         */
-        StringBuffer result = new StringBuffer(128);
-        // First, make the new sequences
+        ArrayList<SequenceI> seqs = new ArrayList<SequenceI>();
         for (UniprotEntry entry : entries)
         {
-          StringBuilder name = constructSequenceFastaHeader(entry);
-
-          result.append(name).append(NEWLINE)
-                  .append(entry.getUniprotSequence().getContent())
-                  .append(NEWLINE);
+          seqs.add(uniprotEntryToSequenceI(entry));
         }
+        al = new Alignment(seqs.toArray(new SequenceI[0]));
 
-        // Then read in the features and apply them to the dataset
-        al = parseResult(result.toString());
-        if (al != null)
-        {
-          // Decorate the alignment with database entries.
-          addUniprotXrefs(al, entries);
-        }
-        else
-        {
-          results = result;
-        }
       }
       stopQuery();
       return al;
@@ -202,99 +188,139 @@ public class Uniprot extends DbSourceProxyImpl implements DbSourceProxy
   }
 
   /**
-   * Construct a Fasta-format sequence header by concatenating the source,
-   * accession id(s) and name(s), delimited by '|', plus any protein names, now
-   * with space rather than bar delimiter
    * 
    * @param entry
-   * @return
+   *          UniprotEntry
+   * @return SequenceI instance created from the UniprotEntry instance
    */
-  public static StringBuilder constructSequenceFastaHeader(
-          UniprotEntry entry)
+  public SequenceI uniprotEntryToSequenceI(UniprotEntry entry)
   {
-    StringBuilder name = new StringBuilder(32);
-    name.append(">UniProt/Swiss-Prot");
+    String id = getUniprotEntryId(entry);
+    SequenceI sequence = new Sequence(id, entry.getUniprotSequence()
+            .getContent());
+    sequence.setDescription(getUniprotEntryDescription(entry));
+
+    final String dbVersion = getDbVersion();
+    ArrayList<DBRefEntry> dbRefs = new ArrayList<DBRefEntry>();
     for (String accessionId : entry.getAccession())
     {
-      name.append(BAR_DELIMITER);
-      name.append(accessionId);
+      DBRefEntry dbRef = new DBRefEntry(DBRefSource.UNIPROT, dbVersion,
+              accessionId);
+
+      // mark dbRef as a primary reference for this sequence
+      dbRefs.add(dbRef);
     }
-    for (String n : entry.getName())
+
+    Vector<PDBEntry> onlyPdbEntries = new Vector<PDBEntry>();
+    for (PDBEntry pdb : entry.getDbReference())
     {
-      name.append(BAR_DELIMITER);
-      name.append(n);
+      DBRefEntry dbr = new DBRefEntry();
+      dbr.setSource(pdb.getType());
+      dbr.setAccessionId(pdb.getId());
+      dbr.setVersion(DBRefSource.UNIPROT + ":" + dbVersion);
+      dbRefs.add(dbr);
+      if ("PDB".equals(pdb.getType()))
+      {
+        onlyPdbEntries.addElement(pdb);
+      }
+      if ("EMBL".equals(pdb.getType()))
+      {
+        // look for a CDS reference and add it, too.
+        String cdsId = (String) pdb.getProperty("protein sequence ID");
+        if (cdsId != null && cdsId.trim().length() > 0)
+        {
+          // remove version
+          String[] vrs = cdsId.split("\\.");
+          dbr = new DBRefEntry(DBRefSource.EMBLCDS, vrs.length > 1 ? vrs[1]
+                  : DBRefSource.UNIPROT + ":" + dbVersion, vrs[0]);
+          dbRefs.add(dbr);
+        }
+      }
+      if ("Ensembl".equals(pdb.getType()))
+      {
+        /*UniprotXML
+         * <dbReference type="Ensembl" id="ENST00000321556">
+        * <molecule id="Q9BXM7-1"/>
+        * <property type="protein sequence ID" value="ENSP00000364204"/>
+        * <property type="gene ID" value="ENSG00000158828"/>
+        * </dbReference> 
+         */
+        String cdsId = (String) pdb.getProperty("protein sequence ID");
+        if (cdsId != null && cdsId.trim().length() > 0)
+        {
+          dbr = new DBRefEntry(DBRefSource.ENSEMBL, DBRefSource.UNIPROT
+                  + ":" + dbVersion, cdsId.trim());
+          dbRefs.add(dbr);
+
+        }
+      }
+
     }
 
-    if (entry.getProtein() != null && entry.getProtein().getName() != null)
+    sequence.setPDBId(onlyPdbEntries);
+    if (entry.getFeature() != null)
     {
-      for (String nm : entry.getProtein().getName())
+      for (SequenceFeature sf : entry.getFeature())
       {
-        name.append(" ").append(nm);
+        sf.setFeatureGroup("Uniprot");
+        sequence.addSequenceFeature(sf);
       }
     }
-    return name;
+    for (DBRefEntry dbr : dbRefs)
+    {
+      sequence.addDBRef(dbr);
+    }
+    return sequence;
   }
 
   /**
-   * add an ordered set of UniprotEntry objects to an ordered set of seuqences.
    * 
-   * @param al
-   *          - a sequence of n sequences
-   * @param entries
-   *          a list of n uniprot entries to be analysed.
+   * @param entry
+   *          UniportEntry
+   * @return protein name(s) delimited by a white space character
    */
-  public void addUniprotXrefs(AlignmentI al, Vector<UniprotEntry> entries)
+  public static String getUniprotEntryDescription(UniprotEntry entry)
   {
-    final String dbVersion = getDbVersion();
-
-    for (int i = 0; i < entries.size(); i++)
+    StringBuilder desc = new StringBuilder(32);
+    if (entry.getProtein() != null && entry.getProtein().getName() != null)
     {
-      UniprotEntry entry = entries.elementAt(i);
-      Vector<PDBEntry> onlyPdbEntries = new Vector<PDBEntry>();
-      Vector<DBRefEntry> dbxrefs = new Vector<DBRefEntry>();
-
-      for (PDBEntry pdb : entry.getDbReference())
+      boolean first = true;
+      for (String nm : entry.getProtein().getName())
       {
-        DBRefEntry dbr = new DBRefEntry();
-        dbr.setSource(pdb.getType());
-        dbr.setAccessionId(pdb.getId());
-        dbr.setVersion(DBRefSource.UNIPROT + ":" + dbVersion);
-        dbxrefs.addElement(dbr);
-        if ("PDB".equals(pdb.getType()))
+        if (!first)
         {
-          onlyPdbEntries.addElement(pdb);
+          desc.append(" ");
         }
+        first = false;
+        desc.append(nm);
       }
+    }
+    return desc.toString();
+  }
 
-      SequenceI sq = al.getSequenceAt(i);
-      while (sq.getDatasetSequence() != null)
-      {
-        sq = sq.getDatasetSequence();
-      }
-
-      for (String accessionId : entry.getAccession())
-      {
-        /*
-         * add as uniprot whether retrieved from uniprot or uniprot_name
-         */
-        sq.addDBRef(new DBRefEntry(DBRefSource.UNIPROT, dbVersion,
-                accessionId));
-      }
-
-      for (DBRefEntry dbRef : dbxrefs)
-      {
-        sq.addDBRef(dbRef);
-      }
-      sq.setPDBId(onlyPdbEntries);
-      if (entry.getFeature() != null)
-      {
-        for (SequenceFeature sf : entry.getFeature())
-        {
-          sf.setFeatureGroup("Uniprot");
-          sq.addSequenceFeature(sf);
-        }
-      }
+  /**
+   *
+   * @param entry
+   *          UniportEntry
+   * @return The accession id(s) and name(s) delimited by '|'.
+   */
+  public static String getUniprotEntryId(UniprotEntry entry)
+  {
+    StringBuilder name = new StringBuilder(32);
+    // name.append("UniProt/Swiss-Prot");
+    // use 'canonicalised' name for optimal id matching
+    name.append(DBRefSource.UNIPROT);
+    for (String accessionId : entry.getAccession())
+    {
+      name.append(BAR_DELIMITER);
+      name.append(accessionId);
+    }
+    for (String n : entry.getName())
+    {
+      name.append(BAR_DELIMITER);
+      name.append(n);
     }
+    return name.toString();
   }
 
   /*
@@ -302,6 +328,7 @@ public class Uniprot extends DbSourceProxyImpl implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String)
    */
+  @Override
   public boolean isValidReference(String accession)
   {
     // TODO: make the following a standard validator
@@ -312,11 +339,13 @@ public class Uniprot extends DbSourceProxyImpl implements DbSourceProxy
   /**
    * return LDHA_CHICK uniprot entry
    */
+  @Override
   public String getTestQuery()
   {
     return "P00340";
   }
 
+  @Override
   public String getDbName()
   {
     return "Uniprot"; // getDbSource();
diff --git a/src/jalview/ws/dbsources/UnprotName.java b/src/jalview/ws/dbsources/UniprotName.java
similarity index 80%
rename from src/jalview/ws/dbsources/UnprotName.java
rename to src/jalview/ws/dbsources/UniprotName.java
index 8e4958e..6c556f8 100644
--- a/src/jalview/ws/dbsources/UnprotName.java
+++ b/src/jalview/ws/dbsources/UniprotName.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,8 @@
  */
 package jalview.ws.dbsources;
 
+import jalview.datamodel.DBRefSource;
+
 /**
  * Canonical Uniprot fetcher instance specifically retrieving UP_NAME
  * references.
@@ -27,8 +29,7 @@ package jalview.ws.dbsources;
  * @author JimP
  * 
  */
-public class UnprotName extends Uniprot implements
-        jalview.ws.seqfetcher.DbSourceProxy
+public class UniprotName extends Uniprot
 {
 
   /*
@@ -36,9 +37,10 @@ public class UnprotName extends Uniprot implements
    * 
    * @see jalview.ws.dbsources.Uniprot#getDbSource()
    */
+  @Override
   public String getDbSource()
   {
-    return jalview.datamodel.DBRefSource.UP_NAME;
+    return DBRefSource.UP_NAME;
   }
 
 }
diff --git a/src/jalview/ws/dbsources/Xfam.java b/src/jalview/ws/dbsources/Xfam.java
index 40ea0fb..edde654 100644
--- a/src/jalview/ws/dbsources/Xfam.java
+++ b/src/jalview/ws/dbsources/Xfam.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -22,6 +22,7 @@ package jalview.ws.dbsources;
 
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
+import jalview.io.FormatAdapter;
 import jalview.ws.seqfetcher.DbSourceProxyImpl;
 
 /**
@@ -40,10 +41,12 @@ public abstract class Xfam extends DbSourceProxyImpl
 
   protected abstract String getXFAMURL();
 
+  @Override
   public abstract String getDbVersion();
 
   abstract String getXfamSource();
 
+  @Override
   public AlignmentI getSequenceRecords(String queries) throws Exception
   {
     // TODO: this is not a perfect implementation. We need to be able to add
@@ -51,9 +54,9 @@ public abstract class Xfam extends DbSourceProxyImpl
     // retrieved.
     startQuery();
     // TODO: trap HTTP 404 exceptions and return null
-    AlignmentI rcds = new jalview.io.FormatAdapter().readFile(getXFAMURL()
-            + queries.trim().toUpperCase(), jalview.io.FormatAdapter.URL,
-            "STH");
+    AlignmentI rcds = new FormatAdapter().readFile(getXFAMURL()
+            + queries.trim().toUpperCase() + getXFAMURLSUFFIX(),
+            jalview.io.FormatAdapter.URL, "STH");
     for (int s = 0, sNum = rcds.getHeight(); s < sNum; s++)
     {
       rcds.getSequenceAt(s).addDBRef(new DBRefEntry(getXfamSource(),
@@ -70,4 +73,23 @@ public abstract class Xfam extends DbSourceProxyImpl
     return rcds;
   }
 
+  /**
+   * Pfam and Rfam provide alignments
+   */
+  @Override
+  public boolean isAlignmentSource()
+  {
+    return true;
+  }
+
+  /**
+   * default suffix to append the retrieval URL for this source.
+   * 
+   * @return "" for most Xfam sources
+   */
+  public String getXFAMURLSUFFIX()
+  {
+    return "";
+  }
+
 }
diff --git a/src/jalview/ws/dbsources/das/api/DasSourceRegistryI.java b/src/jalview/ws/dbsources/das/api/DasSourceRegistryI.java
index 3550b42..a73ba5a 100644
--- a/src/jalview/ws/dbsources/das/api/DasSourceRegistryI.java
+++ b/src/jalview/ws/dbsources/das/api/DasSourceRegistryI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/dbsources/das/api/jalviewSourceI.java b/src/jalview/ws/dbsources/das/api/jalviewSourceI.java
index 79d003f..100171a 100644
--- a/src/jalview/ws/dbsources/das/api/jalviewSourceI.java
+++ b/src/jalview/ws/dbsources/das/api/jalviewSourceI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/dbsources/das/datamodel/DasSequenceSource.java b/src/jalview/ws/dbsources/das/datamodel/DasSequenceSource.java
index 8f00f75..991486e 100644
--- a/src/jalview/ws/dbsources/das/datamodel/DasSequenceSource.java
+++ b/src/jalview/ws/dbsources/das/datamodel/DasSequenceSource.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/dbsources/das/datamodel/DasSourceRegistry.java b/src/jalview/ws/dbsources/das/datamodel/DasSourceRegistry.java
index 7eb74b3..99bde9f 100644
--- a/src/jalview/ws/dbsources/das/datamodel/DasSourceRegistry.java
+++ b/src/jalview/ws/dbsources/das/datamodel/DasSourceRegistry.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -259,8 +259,8 @@ public class DasSourceRegistry implements DasSourceRegistryI,
   }
 
   /*
- * 
- */
+  * 
+  */
 
   @Override
   public jalviewSourceI createLocalSource(String url, String name,
diff --git a/src/jalview/ws/dbsources/das/datamodel/JalviewSource.java b/src/jalview/ws/dbsources/das/datamodel/JalviewSource.java
index 8cb3fba..76fa593 100644
--- a/src/jalview/ws/dbsources/das/datamodel/JalviewSource.java
+++ b/src/jalview/ws/dbsources/das/datamodel/JalviewSource.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/ebi/EBIFetchClient.java b/src/jalview/ws/ebi/EBIFetchClient.java
index 5c1dd91..42eeab4 100644
--- a/src/jalview/ws/ebi/EBIFetchClient.java
+++ b/src/jalview/ws/ebi/EBIFetchClient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,7 @@
  */
 package jalview.ws.ebi;
 
+import jalview.datamodel.DBRefSource;
 import jalview.util.MessageManager;
 
 import java.io.BufferedInputStream;
@@ -41,9 +42,6 @@ import java.util.StringTokenizer;
  */
 public class EBIFetchClient
 {
-  String format = "default";
-
-  String style = "raw";
 
   /**
    * Creates a new EBIFetchClient object.
@@ -90,22 +88,23 @@ public class EBIFetchClient
    * 
    * @param ids
    *          the query formatted as db:query1;query2;query3
-   * @param f
+   * @param format
    *          the format wanted
-   * @param s
-   *          - unused parameter
+   * @param extension
+   *          for the temporary file to hold response
    * @return the file holding the response
    * @throws OutOfMemoryError
    */
-  public File fetchDataAsFile(String ids, String f, String s)
+
+  public File fetchDataAsFile(String ids, String format, String ext)
           throws OutOfMemoryError
   {
     File outFile = null;
     try
     {
-      outFile = File.createTempFile("jalview", ".xml");
+      outFile = File.createTempFile("jalview", ext);
       outFile.deleteOnExit();
-      fetchData(ids, f, s, outFile);
+      fetchData(ids, format, outFile);
       if (outFile.length() == 0)
       {
         outFile.delete();
@@ -118,81 +117,93 @@ public class EBIFetchClient
   }
 
   /**
-   * Single DB multiple record retrieval
+   * Fetches queries and either saves the response to a file or returns as
+   * string data
    * 
    * @param ids
-   *          db:query1;query2;query3
-   * @param f
-   *          raw/xml
-   * @param s
-   *          not used - remove?
-   * 
-   * @return Raw string array result of query set
+   * @param format
+   * @param outFile
+   * @return
+   * @throws OutOfMemoryError
    */
-  public String[] fetchData(String ids, String f, String s)
+  String[] fetchData(String ids, String format, File outFile)
           throws OutOfMemoryError
   {
-    return fetchData(ids, f, s, null);
+    StringBuilder querystring = new StringBuilder(ids.length());
+    String database = parseIds(ids, querystring);
+    if (database == null)
+    {
+      System.err.println("Invalid Query string : '" + ids + "'");
+      System.err.println("Should be of form 'dbname:q1;q2;q3;q4'");
+      return null;
+    }
+
+    // note: outFile is currently always specified, so return value is null
+    String[] rslt = fetchBatch(querystring.toString(), database, format,
+            outFile);
+
+    return (rslt != null && rslt.length > 0 ? rslt : null);
   }
 
-  public String[] fetchData(String ids, String f, String s, File outFile)
-          throws OutOfMemoryError
+  /**
+   * Parses ids formatted as dbname:q1;q2;q3, returns the dbname and adds
+   * queries as comma-separated items to the querystring. dbname must be
+   * specified for at least one queryId. Returns null if a mixture of different
+   * dbnames is found (ignoring case).
+   * 
+   * @param ids
+   * @param queryString
+   * @return
+   */
+  static String parseIds(String ids, StringBuilder queryString)
   {
-    // Need to split
-    // ids of the form uniprot:25KD_SARPE;ADHR_DROPS;
-    String[] rslts = new String[0];
+    String database = null;
     StringTokenizer queries = new StringTokenizer(ids, ";");
-    String db = null;
-    StringBuffer querystring = null;
-    int nq = 0;
+    boolean appending = queryString.length() > 0;
     while (queries.hasMoreTokens())
     {
       String query = queries.nextToken();
-      int p;
-      if ((p = query.indexOf(':')) > -1)
+      int p = query.indexOf(':');
+      if (p > -1)
       {
-        db = query.substring(0, p);
+        String db = query.substring(0, p);
+        if (database != null && !db.equalsIgnoreCase(database))
+        {
+          /*
+           * different databases mixed in together - invalid
+           */
+          return null;
+        }
+        database = db;
         query = query.substring(p + 1);
       }
-      if (querystring == null)
-      {
-        querystring = new StringBuffer(query);
-        nq++;
-      }
-      else
-      {
-        querystring.append("," + query);
-        nq++;
-      }
+      queryString.append(appending ? "," : "");
+      queryString.append(query);
+      appending = true;
     }
-    if (db == null)
-    {
-      System.err.println("Invalid Query string : '" + ids
-              + "'\nShould be of form 'dbname:q1;q2;q3;q4'");
-      return null;
-    }
-    String[] rslt = fetchBatch(querystring.toString(), db, f, s, outFile);
-    if (rslt != null)
-    {
-      String[] nrslts = new String[rslt.length + rslts.length];
-      System.arraycopy(rslts, 0, nrslts, 0, rslts.length);
-      System.arraycopy(rslt, 0, nrslts, rslts.length, rslt.length);
-      rslts = nrslts;
-    }
-
-    return (rslts.length == 0 ? null : rslts);
+    return database;
   }
 
-  public String[] fetchBatch(String ids, String db, String f, String s,
+  /**
+   * Fetches queries and either saves the response to a file or (if no file
+   * specified) returns as string data
+   * 
+   * @param ids
+   * @param database
+   * @param format
+   * @param outFile
+   * @return
+   * @throws OutOfMemoryError
+   */
+  String[] fetchBatch(String ids, String database, String format,
           File outFile) throws OutOfMemoryError
   {
-    long time = System.currentTimeMillis();
-    // max 200 ids can be added at one time
+    // long time = System.currentTimeMillis();
+    String url = buildUrl(ids, database, format);
+
     try
     {
-      URL rcall = new URL("http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/"
-              + db.toLowerCase() + "/" + ids.toLowerCase()
-              + (f != null ? "/" + f : ""));
+      URL rcall = new URL(url);
 
       InputStream is = new BufferedInputStream(rcall.openStream());
       if (outFile != null)
@@ -220,8 +231,7 @@ public class EBIFetchClient
       }
     } catch (OutOfMemoryError er)
     {
-
-      System.out.println("OUT OF MEMORY DOWNLOADING QUERY FROM " + db
+      System.out.println("OUT OF MEMORY DOWNLOADING QUERY FROM " + database
               + ":\n" + ids);
       throw er;
     } catch (Exception ex)
@@ -231,15 +241,41 @@ public class EBIFetchClient
       {
         return null;
       }
-      System.err.println("Unexpected exception when retrieving from " + db
-              + "\nQuery was : '" + ids + "'");
+      System.err.println("Unexpected exception when retrieving from "
+              + database + "\nQuery was : '" + ids + "'");
       ex.printStackTrace(System.err);
       return null;
     } finally
     {
-      // System.err.println("Took " + (System.currentTimeMillis() - time)
-      // / 1000 + " secs for one call.");
+      // System.err.println("EBIFetch took " + (System.currentTimeMillis() -
+      // time) + " ms");
     }
     return null;
   }
+
+  /**
+   * Constructs the URL to fetch from
+   * 
+   * @param ids
+   * @param database
+   * @param format
+   * @return
+   */
+  static String buildUrl(String ids, String database, String format)
+  {
+    String url;
+    if (database.equalsIgnoreCase(DBRefSource.EMBL)
+            || database.equalsIgnoreCase(DBRefSource.EMBLCDS))
+    {
+      url = "http://www.ebi.ac.uk/ena/data/view/" + ids.toLowerCase()
+              + (format != null ? "&" + format : "");
+    }
+    else
+    {
+      url = "http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/"
+              + database.toLowerCase() + "/" + ids.toLowerCase()
+              + (format != null ? "/" + format : "");
+    }
+    return url;
+  }
 }
diff --git a/src/jalview/ws/io/mime/HttpContentHandler.java b/src/jalview/ws/io/mime/HttpContentHandler.java
index 6d70404..88bc886 100644
--- a/src/jalview/ws/io/mime/HttpContentHandler.java
+++ b/src/jalview/ws/io/mime/HttpContentHandler.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/io/mime/JalviewMimeContentHandler.java b/src/jalview/ws/io/mime/JalviewMimeContentHandler.java
index 1a4ded5..9c0d6c8 100644
--- a/src/jalview/ws/io/mime/JalviewMimeContentHandler.java
+++ b/src/jalview/ws/io/mime/JalviewMimeContentHandler.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/io/mime/MimeTypes.java b/src/jalview/ws/io/mime/MimeTypes.java
index 366c945..b8aa285 100644
--- a/src/jalview/ws/io/mime/MimeTypes.java
+++ b/src/jalview/ws/io/mime/MimeTypes.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws1/Annotate3D.java b/src/jalview/ws/jws1/Annotate3D.java
index f3516b7..7cab4f6 100644
--- a/src/jalview/ws/jws1/Annotate3D.java
+++ b/src/jalview/ws/jws1/Annotate3D.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws1/Discoverer.java b/src/jalview/ws/jws1/Discoverer.java
index b4c29d1..71641e0 100644
--- a/src/jalview/ws/jws1/Discoverer.java
+++ b/src/jalview/ws/jws1/Discoverer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws1/JPredClient.java b/src/jalview/ws/jws1/JPredClient.java
index 55d0813..8118351 100644
--- a/src/jalview/ws/jws1/JPredClient.java
+++ b/src/jalview/ws/jws1/JPredClient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws1/JPredThread.java b/src/jalview/ws/jws1/JPredThread.java
index 79c39b4..230d4b0 100644
--- a/src/jalview/ws/jws1/JPredThread.java
+++ b/src/jalview/ws/jws1/JPredThread.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -123,7 +123,7 @@ class JPredThread extends JWS1Thread implements WSClientI
         jalview.bin.Cache.log.debug("Getting associated alignment.");
         // we ignore the returned alignment if we only predicted on a single
         // sequence
-        String format = new jalview.io.IdentifyFile().Identify(
+        String format = new jalview.io.IdentifyFile().identify(
                 result.getAligfile(), "Paste");
 
         if (jalview.io.FormatAdapter.isValidFormat(format))
diff --git a/src/jalview/ws/jws1/JWS1Thread.java b/src/jalview/ws/jws1/JWS1Thread.java
index 97d041e..8a3576e 100644
--- a/src/jalview/ws/jws1/JWS1Thread.java
+++ b/src/jalview/ws/jws1/JWS1Thread.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws1/MsaWSClient.java b/src/jalview/ws/jws1/MsaWSClient.java
index 03b9b8f..7dd81a0 100644
--- a/src/jalview/ws/jws1/MsaWSClient.java
+++ b/src/jalview/ws/jws1/MsaWSClient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,7 @@
  */
 package jalview.ws.jws1;
 
-import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
@@ -34,7 +34,6 @@ import javax.swing.JMenu;
 import javax.swing.JMenuItem;
 import javax.swing.JOptionPane;
 
-import ext.vamsas.MuscleWS;
 import ext.vamsas.MuscleWSServiceLocator;
 import ext.vamsas.MuscleWSSoapBindingStub;
 import ext.vamsas.ServiceHandle;
@@ -72,7 +71,7 @@ public class MsaWSClient extends WS1Client
 
   public MsaWSClient(ext.vamsas.ServiceHandle sh, String altitle,
           jalview.datamodel.AlignmentView msa, boolean submitGaps,
-          boolean preserveOrder, Alignment seqdataset,
+          boolean preserveOrder, AlignmentI seqdataset,
           AlignFrame _alignFrame)
   {
     super();
@@ -109,7 +108,7 @@ public class MsaWSClient extends WS1Client
   }
 
   private void startMsaWSClient(String altitle, AlignmentView msa,
-          boolean submitGaps, boolean preserveOrder, Alignment seqdataset)
+          boolean submitGaps, boolean preserveOrder, AlignmentI seqdataset)
   {
     if (!locateWebService())
     {
@@ -159,7 +158,7 @@ public class MsaWSClient extends WS1Client
 
     try
     {
-      this.server = (MuscleWS) loc.getMuscleWS(new java.net.URL(WsURL));
+      this.server = loc.getMuscleWS(new java.net.URL(WsURL));
       ((MuscleWSSoapBindingStub) this.server).setTimeout(60000); // One minute
       // timeout
     } catch (Exception ex)
@@ -201,6 +200,7 @@ public class MsaWSClient extends WS1Client
     return (WebServiceName.indexOf("lustal") > -1); // cheat!
   }
 
+  @Override
   public void attachWSMenuEntry(JMenu msawsmenu,
           final ServiceHandle serviceHandle, final AlignFrame alignFrame)
   {
@@ -209,6 +209,7 @@ public class MsaWSClient extends WS1Client
     method.setToolTipText(WsURL);
     method.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         AlignmentView msa = alignFrame.gatherSequencesForAlignment();
@@ -228,6 +229,7 @@ public class MsaWSClient extends WS1Client
       methodR.setToolTipText(WsURL);
       methodR.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent e)
         {
           AlignmentView msa = alignFrame.gatherSequencesForAlignment();
diff --git a/src/jalview/ws/jws1/MsaWSThread.java b/src/jalview/ws/jws1/MsaWSThread.java
index e6afb04..e21cfaa 100644
--- a/src/jalview/ws/jws1/MsaWSThread.java
+++ b/src/jalview/ws/jws1/MsaWSThread.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -23,6 +23,7 @@ package jalview.ws.jws1;
 import jalview.analysis.AlignSeq;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.ColumnSelection;
@@ -147,6 +148,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI
      * 
      * @return true if getAlignment will return a valid alignment result.
      */
+    @Override
     public boolean hasResults()
     {
       if (subjobComplete && result != null && result.isFinished()
@@ -273,6 +275,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI
      * 
      * @return boolean true if job can be submitted.
      */
+    @Override
     public boolean hasValidInput()
     {
       if (seqs.getSeqs() != null)
@@ -285,7 +288,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI
 
   String alTitle; // name which will be used to form new alignment window.
 
-  Alignment dataset; // dataset to which the new alignment will be
+  AlignmentI dataset; // dataset to which the new alignment will be
 
   // associated.
 
@@ -327,7 +330,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI
   MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
           WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
           String wsname, String title, AlignmentView _msa, boolean subgaps,
-          boolean presorder, Alignment seqset)
+          boolean presorder, AlignmentI seqset)
   {
     this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
     OutputHeader = wsInfo.getProgressText();
@@ -359,11 +362,13 @@ class MsaWSThread extends JWS1Thread implements WSClientI
     }
   }
 
+  @Override
   public boolean isCancellable()
   {
     return true;
   }
 
+  @Override
   public void cancelJob()
   {
     if (!jobComplete && jobs != null)
@@ -430,11 +435,13 @@ class MsaWSThread extends JWS1Thread implements WSClientI
     }
   }
 
+  @Override
   public void pollJob(AWsJob job) throws Exception
   {
     ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).getJobId());
   }
 
+  @Override
   public void StartJob(AWsJob job)
   {
     if (!(job instanceof MsaWSJob))
@@ -521,6 +528,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI
     return msa;
   }
 
+  @Override
   public void parseResult()
   {
     int results = 0; // number of result sets received
@@ -571,6 +579,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI
       wsInfo.showResultsNewFrame
               .addActionListener(new java.awt.event.ActionListener()
               {
+                @Override
                 public void actionPerformed(java.awt.event.ActionEvent evt)
                 {
                   displayResults(true);
@@ -579,6 +588,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI
       wsInfo.mergeResults
               .addActionListener(new java.awt.event.ActionListener()
               {
+                @Override
                 public void actionPerformed(java.awt.event.ActionEvent evt)
                 {
                   displayResults(false);
@@ -660,8 +670,8 @@ class MsaWSThread extends JWS1Thread implements WSClientI
 
             while (j < l)
             {
-              if (((AlignmentOrder) alorders.get(i))
-                      .equals(((AlignmentOrder) alorders.get(j))))
+              if (((AlignmentOrder) alorders.get(i)).equals((alorders
+                      .get(j))))
               {
                 alorders.remove(j);
                 l--;
@@ -704,6 +714,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI
     }
   }
 
+  @Override
   public boolean canMergeResults()
   {
     return false;
diff --git a/src/jalview/ws/jws1/SeqSearchWSClient.java b/src/jalview/ws/jws1/SeqSearchWSClient.java
index 3783f23..98f40ed 100644
--- a/src/jalview/ws/jws1/SeqSearchWSClient.java
+++ b/src/jalview/ws/jws1/SeqSearchWSClient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,7 @@
  */
 package jalview.ws.jws1;
 
-import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
@@ -39,7 +39,6 @@ import javax.swing.JMenu;
 import javax.swing.JMenuItem;
 import javax.swing.JOptionPane;
 
-import ext.vamsas.SeqSearchI;
 import ext.vamsas.SeqSearchServiceLocator;
 import ext.vamsas.SeqSearchServiceSoapBindingStub;
 import ext.vamsas.ServiceHandle;
@@ -77,7 +76,7 @@ public class SeqSearchWSClient extends WS1Client
 
   public SeqSearchWSClient(ext.vamsas.ServiceHandle sh, String altitle,
           jalview.datamodel.AlignmentView msa, String db,
-          Alignment seqdataset, AlignFrame _alignFrame)
+          AlignmentI seqdataset, AlignFrame _alignFrame)
   {
     super();
     alignFrame = _alignFrame;
@@ -128,7 +127,7 @@ public class SeqSearchWSClient extends WS1Client
   }
 
   private void startSeqSearchClient(String altitle, AlignmentView msa,
-          String db, Alignment seqdataset)
+          String db, AlignmentI seqdataset)
   {
     if (!locateWebService())
     {
@@ -173,8 +172,7 @@ public class SeqSearchWSClient extends WS1Client
 
     try
     {
-      this.server = (SeqSearchI) loc.getSeqSearchService(new java.net.URL(
-              WsURL));
+      this.server = loc.getSeqSearchService(new java.net.URL(WsURL));
       ((SeqSearchServiceSoapBindingStub) this.server).setTimeout(60000); // One
       // minute
       // timeout
@@ -241,6 +239,7 @@ public class SeqSearchWSClient extends WS1Client
     return dbs;
   }
 
+  @Override
   public void attachWSMenuEntry(JMenu wsmenu, final ServiceHandle sh,
           final AlignFrame af)
   {
@@ -281,6 +280,7 @@ public class SeqSearchWSClient extends WS1Client
     method.setToolTipText(sh.getEndpointURL());
     method.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         // use same input gatherer as for secondary structure prediction
@@ -305,6 +305,7 @@ public class SeqSearchWSClient extends WS1Client
       final String searchdb = dbs[db];
       method.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent e)
         {
           AlignmentView msa = af.gatherSeqOrMsaForSecStrPrediction();
diff --git a/src/jalview/ws/jws1/SeqSearchWSThread.java b/src/jalview/ws/jws1/SeqSearchWSThread.java
index bf791e2..43c70cf 100644
--- a/src/jalview/ws/jws1/SeqSearchWSThread.java
+++ b/src/jalview/ws/jws1/SeqSearchWSThread.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,8 +21,10 @@
 package jalview.ws.jws1;
 
 import jalview.analysis.AlignSeq;
+import jalview.api.FeatureColourI;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
@@ -34,7 +36,9 @@ import jalview.ws.AWsJob;
 import jalview.ws.JobStateSummary;
 import jalview.ws.WSClientI;
 
+import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.Map;
 import java.util.Vector;
 
 import vamsas.objects.simple.MsaResult;
@@ -150,6 +154,7 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
      * 
      * @return true if getAlignment will return a valid alignment result.
      */
+    @Override
     public boolean hasResults()
     {
       if (subjobComplete
@@ -168,7 +173,8 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
      * 
      * @return null or { Alignment(+features and annotation), NewickFile)}
      */
-    public Object[] getAlignment(Alignment dataset, Hashtable featureColours)
+    public Object[] getAlignment(AlignmentI dataset,
+            Map<String, FeatureColourI> featureColours)
     {
 
       if (result != null && result.isFinished())
@@ -285,6 +291,7 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
      * 
      * @return boolean true if job can be submitted.
      */
+    @Override
     public boolean hasValidInput()
     {
       if (seqs.getSeqs() != null)
@@ -297,7 +304,7 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
 
   String alTitle; // name which will be used to form new alignment window.
 
-  Alignment dataset; // dataset to which the new alignment will be
+  AlignmentI dataset; // dataset to which the new alignment will be
 
   // associated.
 
@@ -339,7 +346,7 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
   SeqSearchWSThread(ext.vamsas.SeqSearchI server, String wsUrl,
           WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
           String wsname, String title, AlignmentView _msa, String db,
-          Alignment seqset)
+          AlignmentI seqset)
   {
     this(server, wsUrl, wsinfo, alFrame, _msa, wsname, db);
     OutputHeader = wsInfo.getProgressText();
@@ -371,11 +378,13 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
     }
   }
 
+  @Override
   public boolean isCancellable()
   {
     return true;
   }
 
+  @Override
   public void cancelJob()
   {
     if (!jobComplete && jobs != null)
@@ -442,12 +451,14 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
     }
   }
 
+  @Override
   public void pollJob(AWsJob job) throws Exception
   {
     ((SeqSearchWSJob) job).result = server.getResult(((SeqSearchWSJob) job)
             .getJobId());
   }
 
+  @Override
   public void StartJob(AWsJob job)
   {
     if (!(job instanceof SeqSearchWSJob))
@@ -534,6 +545,7 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
     return msa;
   }
 
+  @Override
   public void parseResult()
   {
     int results = 0; // number of result sets received
@@ -577,6 +589,7 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
       wsInfo.showResultsNewFrame
               .addActionListener(new java.awt.event.ActionListener()
               {
+                @Override
                 public void actionPerformed(java.awt.event.ActionEvent evt)
                 {
                   displayResults(true);
@@ -585,6 +598,7 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
       wsInfo.mergeResults
               .addActionListener(new java.awt.event.ActionListener()
               {
+                @Override
                 public void actionPerformed(java.awt.event.ActionEvent evt)
                 {
                   displayResults(false);
@@ -610,7 +624,7 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
     // NewickFile nf[] = new NewickFile[jobs.length];
     for (int j = 0; j < jobs.length; j++)
     {
-      Hashtable featureColours = new Hashtable();
+      Map<String, FeatureColourI> featureColours = new HashMap<String, FeatureColourI>();
       Alignment al = null;
       NewickFile nf = null;
       if (jobs[j].hasResults())
@@ -662,6 +676,7 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI
     }
   }
 
+  @Override
   public boolean canMergeResults()
   {
     return false;
diff --git a/src/jalview/ws/jws1/WS1Client.java b/src/jalview/ws/jws1/WS1Client.java
index 0589ffb..1d4e778 100644
--- a/src/jalview/ws/jws1/WS1Client.java
+++ b/src/jalview/ws/jws1/WS1Client.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws1/WSJob.java b/src/jalview/ws/jws1/WSJob.java
index 8654cb2..5d21099 100644
--- a/src/jalview/ws/jws1/WSJob.java
+++ b/src/jalview/ws/jws1/WSJob.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/AAConClient.java b/src/jalview/ws/jws2/AAConClient.java
index 0c6c271..75abd13 100644
--- a/src/jalview/ws/jws2/AAConClient.java
+++ b/src/jalview/ws/jws2/AAConClient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/AADisorderClient.java b/src/jalview/ws/jws2/AADisorderClient.java
index d955db5..cfe286f 100644
--- a/src/jalview/ws/jws2/AADisorderClient.java
+++ b/src/jalview/ws/jws2/AADisorderClient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -21,13 +21,14 @@
 package jalview.ws.jws2;
 
 import jalview.api.AlignCalcWorkerI;
+import jalview.api.FeatureColourI;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.GraphLine;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
-import jalview.schemes.GraduatedColor;
+import jalview.schemes.FeatureColour;
 import jalview.schemes.UserColourScheme;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.params.WsParamSetI;
@@ -46,8 +47,7 @@ import compbio.data.sequence.Score;
 import compbio.data.sequence.ScoreManager.ScoreHolder;
 import compbio.metadata.Argument;
 
-public class AADisorderClient extends JabawsCalcWorker implements
-        AlignCalcWorkerI
+public class AADisorderClient extends JabawsCalcWorker
 {
 
   private static final String THRESHOLD = "THRESHOLD";
@@ -308,8 +308,8 @@ public class AADisorderClient extends JabawsCalcWorker implements
                 annot.description += "<br/>" + threshNote;
               }
               annot.description += "</html>";
-              Color col = new UserColourScheme(typeName)
-                      .createColourFromName(typeName + scr.getMethod());
+              Color col = UserColourScheme.createColourFromName(typeName
+                      + scr.getMethod());
               for (int p = 0, ps = annot.annotations.length; p < ps; p++)
               {
                 if (annot.annotations[p] != null)
@@ -337,13 +337,13 @@ public class AADisorderClient extends JabawsCalcWorker implements
                   .cloneFeatureRenderer();
           for (String ft : fc.keySet())
           {
-            Object gc = fr.getFeatureStyle(ft);
-            if (gc instanceof Color)
+            FeatureColourI gc = fr.getFeatureStyle(ft);
+            if (gc.isSimpleColour())
             {
               // set graduated color as fading to white for minimum, and
               // autoscaling to values on alignment
-              GraduatedColor ggc = new GraduatedColor(Color.white,
-                      (Color) gc, Float.MIN_VALUE, Float.MAX_VALUE);
+              FeatureColourI ggc = new FeatureColour(Color.white,
+                      gc.getColour(), Float.MIN_VALUE, Float.MAX_VALUE);
               ggc.setAutoScaled(true);
               fr.setColour(ft, ggc);
             }
diff --git a/src/jalview/ws/jws2/AWS2Thread.java b/src/jalview/ws/jws2/AWS2Thread.java
index 3eaeb3f..57ac227 100644
--- a/src/jalview/ws/jws2/AWS2Thread.java
+++ b/src/jalview/ws/jws2/AWS2Thread.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/AbstractJabaCalcWorker.java b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java
index fa1e3d1..c826efb 100644
--- a/src/jalview/ws/jws2/AbstractJabaCalcWorker.java
+++ b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -331,23 +331,23 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
       System.err.println("submission error with " + getServiceActionText()
               + " :");
       x.printStackTrace();
-      calcMan.workerCannotRun(this);
+      calcMan.disableWorker(this);
     } catch (ResultNotAvailableException x)
     {
       System.err.println("collection error:\nJob ID: " + rslt);
       x.printStackTrace();
-      calcMan.workerCannotRun(this);
+      calcMan.disableWorker(this);
 
     } catch (OutOfMemoryError error)
     {
-      calcMan.workerCannotRun(this);
+      calcMan.disableWorker(this);
 
       // consensus = null;
       // hconsensus = null;
       ap.raiseOOMWarning(getServiceActionText(), error);
     } catch (Exception x)
     {
-      calcMan.workerCannotRun(this);
+      calcMan.disableWorker(this);
 
       // consensus = null;
       // hconsensus = null;
diff --git a/src/jalview/ws/jws2/JPred301Client.java b/src/jalview/ws/jws2/JPred301Client.java
index b98eae4..8f1e9d8 100644
--- a/src/jalview/ws/jws2/JPred301Client.java
+++ b/src/jalview/ws/jws2/JPred301Client.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,6 @@
  */
 package jalview.ws.jws2;
 
-import jalview.api.AlignCalcWorkerI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.gui.AlignFrame;
@@ -43,9 +42,7 @@ import compbio.data.sequence.JpredAlignment;
 import compbio.metadata.Argument;
 
 public class JPred301Client extends JabawsMsaInterfaceAlignCalcWorker
-        implements AlignCalcWorkerI
 {
-
   /**
    * 
    * @return default args for this service when run as dynamic web service
@@ -87,6 +84,7 @@ public class JPred301Client extends JabawsMsaInterfaceAlignCalcWorker
     return (seqs.size() > 1);
   }
 
+  @Override
   public String getServiceActionText()
   {
     return "calculating consensus secondary structure prediction using JPred service";
@@ -112,6 +110,7 @@ public class JPred301Client extends JabawsMsaInterfaceAlignCalcWorker
    * update the consensus annotation from the sequence profile data using
    * current visualization settings.
    */
+  @Override
   public void updateResultAnnotation(boolean immediate)
   {
     if (immediate || !calcMan.isWorking(this) && msascoreset != null)
diff --git a/src/jalview/ws/jws2/JWs2Job.java b/src/jalview/ws/jws2/JWs2Job.java
index 602b882..ed8d5b0 100644
--- a/src/jalview/ws/jws2/JWs2Job.java
+++ b/src/jalview/ws/jws2/JWs2Job.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/JabaParamStore.java b/src/jalview/ws/jws2/JabaParamStore.java
index 757ee23..812eae3 100644
--- a/src/jalview/ws/jws2/JabaParamStore.java
+++ b/src/jalview/ws/jws2/JabaParamStore.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/JabaPreset.java b/src/jalview/ws/jws2/JabaPreset.java
index 561e4e8..83f667a 100644
--- a/src/jalview/ws/jws2/JabaPreset.java
+++ b/src/jalview/ws/jws2/JabaPreset.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/JabaWsServerQuery.java b/src/jalview/ws/jws2/JabaWsServerQuery.java
index e376a73..723711f 100644
--- a/src/jalview/ws/jws2/JabaWsServerQuery.java
+++ b/src/jalview/ws/jws2/JabaWsServerQuery.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/JabawsCalcWorker.java b/src/jalview/ws/jws2/JabawsCalcWorker.java
index d2e5bcd..e2d87fe 100644
--- a/src/jalview/ws/jws2/JabawsCalcWorker.java
+++ b/src/jalview/ws/jws2/JabawsCalcWorker.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/JabawsMsaInterfaceAlignCalcWorker.java b/src/jalview/ws/jws2/JabawsMsaInterfaceAlignCalcWorker.java
index d45a0e1..9e6a735 100644
--- a/src/jalview/ws/jws2/JabawsMsaInterfaceAlignCalcWorker.java
+++ b/src/jalview/ws/jws2/JabawsMsaInterfaceAlignCalcWorker.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/Jws2Client.java b/src/jalview/ws/jws2/Jws2Client.java
index d8fb846..0b5b637 100644
--- a/src/jalview/ws/jws2/Jws2Client.java
+++ b/src/jalview/ws/jws2/Jws2Client.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -256,11 +256,8 @@ public abstract class Jws2Client extends jalview.ws.WSClient
       });
       wsmenu.add(aaConEnabled);
       final JMenuItem modifyParams = new JMenuItem(aaui.getAAeditSettings());
-      modifyParams
-              .setToolTipText("<html><p>"
-                      + JvSwingUtils.wrapTooltip(false,
-                              aaui.getAAeditSettingsTooltip() + "</p>")
-                      + "</html>");
+      modifyParams.setToolTipText(JvSwingUtils.wrapTooltip(true,
+              aaui.getAAeditSettingsTooltip()));
       modifyParams.addActionListener(new ActionListener()
       {
 
diff --git a/src/jalview/ws/jws2/Jws2Discoverer.java b/src/jalview/ws/jws2/Jws2Discoverer.java
index 60fa85b..0607551 100644
--- a/src/jalview/ws/jws2/Jws2Discoverer.java
+++ b/src/jalview/ws/jws2/Jws2Discoverer.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/MsaWSClient.java b/src/jalview/ws/jws2/MsaWSClient.java
index b961cfd..29e7ac5 100644
--- a/src/jalview/ws/jws2/MsaWSClient.java
+++ b/src/jalview/ws/jws2/MsaWSClient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,7 @@
  */
 package jalview.ws.jws2;
 
-import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
@@ -58,7 +58,7 @@ public class MsaWSClient extends Jws2Client
 
   public MsaWSClient(Jws2Instance sh, String altitle,
           jalview.datamodel.AlignmentView msa, boolean submitGaps,
-          boolean preserveOrder, Alignment seqdataset,
+          boolean preserveOrder, AlignmentI seqdataset,
           AlignFrame _alignFrame)
   {
     this(sh, null, null, false, altitle, msa, submitGaps, preserveOrder,
@@ -68,7 +68,7 @@ public class MsaWSClient extends Jws2Client
 
   public MsaWSClient(Jws2Instance sh, WsParamSetI preset, String altitle,
           jalview.datamodel.AlignmentView msa, boolean submitGaps,
-          boolean preserveOrder, Alignment seqdataset,
+          boolean preserveOrder, AlignmentI seqdataset,
           AlignFrame _alignFrame)
   {
     this(sh, preset, null, false, altitle, msa, submitGaps, preserveOrder,
@@ -95,7 +95,7 @@ public class MsaWSClient extends Jws2Client
   public MsaWSClient(Jws2Instance sh, WsParamSetI preset,
           List<Argument> arguments, boolean editParams, String altitle,
           jalview.datamodel.AlignmentView msa, boolean submitGaps,
-          boolean preserveOrder, Alignment seqdataset,
+          boolean preserveOrder, AlignmentI seqdataset,
           AlignFrame _alignFrame)
   {
     super(_alignFrame, preset, arguments);
@@ -138,7 +138,7 @@ public class MsaWSClient extends Jws2Client
   }
 
   private void startMsaWSClient(String altitle, AlignmentView msa,
-          boolean submitGaps, boolean preserveOrder, Alignment seqdataset)
+          boolean submitGaps, boolean preserveOrder, AlignmentI seqdataset)
   {
     // if (!locateWebService())
     // {
@@ -216,6 +216,7 @@ public class MsaWSClient extends Jws2Client
     return (WebServiceName.indexOf("lustal") > -1); // cheat!
   }
 
+  @Override
   public void attachWSMenuEntry(JMenu rmsawsmenu,
           final Jws2Instance service, final AlignFrame alignFrame)
   {
@@ -263,6 +264,7 @@ public class MsaWSClient extends Jws2Client
 
       method.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent e)
         {
           AlignmentView msa = alignFrame.gatherSequencesForAlignment();
@@ -288,6 +290,7 @@ public class MsaWSClient extends Jws2Client
 
         method.addActionListener(new ActionListener()
         {
+          @Override
           public void actionPerformed(ActionEvent e)
           {
             AlignmentView msa = alignFrame.gatherSequencesForAlignment();
@@ -335,17 +338,20 @@ public class MsaWSClient extends Jws2Client
               }
 
             });
-            methodR.setToolTipText(JvSwingUtils.wrapTooltip(
-                    true,
-                    "<p><strong>"
-                            + (preset.isModifiable() ? MessageManager
-                                    .getString("label.user_preset")
-                                    : MessageManager
-                                            .getString("label.service_preset"))
-                            + "</strong><br/>" + preset.getDescription()
-                            + "</p>"));
+            String tooltip = JvSwingUtils
+                    .wrapTooltip(
+                            true,
+                            "<strong>"
+                                    + (preset.isModifiable() ? MessageManager
+                                            .getString("label.user_preset")
+                                            : MessageManager
+                                                    .getString("label.service_preset"))
+                                    + "</strong><br/>"
+                                    + preset.getDescription());
+            methodR.setToolTipText(tooltip);
             methodR.addActionListener(new ActionListener()
             {
+              @Override
               public void actionPerformed(ActionEvent e)
               {
                 AlignmentView msa = alignFrame
diff --git a/src/jalview/ws/jws2/MsaWSThread.java b/src/jalview/ws/jws2/MsaWSThread.java
index 40e6de7..e74407f 100644
--- a/src/jalview/ws/jws2/MsaWSThread.java
+++ b/src/jalview/ws/jws2/MsaWSThread.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -176,6 +176,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
      * 
      * @return true if getAlignment will return a valid alignment result.
      */
+    @Override
     public boolean hasResults()
     {
       if (subjobComplete
@@ -316,6 +317,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
      * 
      * @return boolean true if job can be submitted.
      */
+    @Override
     public boolean hasValidInput()
     {
       // TODO: get attributes for this MsaWS instance to check if it can do two
@@ -436,7 +438,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
 
   String alTitle; // name which will be used to form new alignment window.
 
-  Alignment dataset; // dataset to which the new alignment will be
+  AlignmentI dataset; // dataset to which the new alignment will be
 
   // associated.
 
@@ -479,7 +481,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
           String wsUrl, WebserviceInfo wsinfo,
           jalview.gui.AlignFrame alFrame, String wsname, String title,
           AlignmentView _msa, boolean subgaps, boolean presorder,
-          Alignment seqset)
+          AlignmentI seqset)
   {
     this(server2, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
     OutputHeader = wsInfo.getProgressText();
@@ -530,11 +532,13 @@ class MsaWSThread extends AWS2Thread implements WSClientI
     return validInput;
   }
 
+  @Override
   public boolean isCancellable()
   {
     return true;
   }
 
+  @Override
   public void cancelJob()
   {
     if (!jobComplete && jobs != null)
@@ -605,6 +609,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
     }
   }
 
+  @Override
   public void pollJob(AWsJob job) throws Exception
   {
     // TODO: investigate if we still need to cast here in J1.6
@@ -650,6 +655,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
     return changed;
   }
 
+  @Override
   public void StartJob(AWsJob job)
   {
     Exception lex = null;
@@ -775,6 +781,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
     }
   }
 
+  @Override
   public void parseResult()
   {
     long progbar = System.currentTimeMillis();
@@ -889,6 +896,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
       wsInfo.showResultsNewFrame
               .addActionListener(new java.awt.event.ActionListener()
               {
+                @Override
                 public void actionPerformed(java.awt.event.ActionEvent evt)
                 {
                   displayResults(true);
@@ -897,6 +905,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
       wsInfo.mergeResults
               .addActionListener(new java.awt.event.ActionListener()
               {
+                @Override
                 public void actionPerformed(java.awt.event.ActionEvent evt)
                 {
                   displayResults(false);
@@ -1023,6 +1032,10 @@ class MsaWSThread extends AWS2Thread implements WSClientI
       // becomes null if the alignment window was closed before the alignment
       // job finished.
       AlignmentI copyComplement = new Alignment(complement);
+      // todo should this be done by copy constructor?
+      copyComplement.setGapCharacter(complement.getGapCharacter());
+      // share the same dataset (and the mappings it holds)
+      copyComplement.setDataset(complement.getDataset());
       copyComplement.alignAs(al);
       if (copyComplement.getHeight() > 0)
       {
@@ -1101,6 +1114,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
     }
   }
 
+  @Override
   public boolean canMergeResults()
   {
     return false;
diff --git a/src/jalview/ws/jws2/ParameterUtils.java b/src/jalview/ws/jws2/ParameterUtils.java
index 99a0d2c..4e7d88a 100644
--- a/src/jalview/ws/jws2/ParameterUtils.java
+++ b/src/jalview/ws/jws2/ParameterUtils.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/RNAalifoldClient.java b/src/jalview/ws/jws2/RNAalifoldClient.java
index 769100e..22ae682 100644
--- a/src/jalview/ws/jws2/RNAalifoldClient.java
+++ b/src/jalview/ws/jws2/RNAalifoldClient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,6 @@
  */
 package jalview.ws.jws2;
 
-import jalview.api.AlignCalcWorkerI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.gui.AlignFrame;
@@ -50,8 +49,7 @@ import compbio.metadata.Argument;
  * 
  */
 
-public class RNAalifoldClient extends JabawsCalcWorker implements
-        AlignCalcWorkerI
+public class RNAalifoldClient extends JabawsCalcWorker
 {
 
   String methodName;
@@ -75,6 +73,7 @@ public class RNAalifoldClient extends JabawsCalcWorker implements
     initViewportParams();
   }
 
+  @Override
   public String getCalcId()
   {
     return CALC_ID;
diff --git a/src/jalview/ws/jws2/SequenceAnnotationWSClient.java b/src/jalview/ws/jws2/SequenceAnnotationWSClient.java
index 75cb678..62b46d2 100644
--- a/src/jalview/ws/jws2/SequenceAnnotationWSClient.java
+++ b/src/jalview/ws/jws2/SequenceAnnotationWSClient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -122,7 +122,7 @@ public class SequenceAnnotationWSClient extends Jws2Client
         }
         // reinstate worker if it was blacklisted (might have happened due to
         // invalid parameters)
-        alignFrame.getViewport().getCalcManager().workerMayRun(worker);
+        alignFrame.getViewport().getCalcManager().enableWorker(worker);
         worker.updateParameters(this.preset, paramset);
       }
     }
diff --git a/src/jalview/ws/jws2/dm/AAConSettings.java b/src/jalview/ws/jws2/dm/AAConSettings.java
index 285b755..dbfb17b 100644
--- a/src/jalview/ws/jws2/dm/AAConSettings.java
+++ b/src/jalview/ws/jws2/dm/AAConSettings.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/dm/JabaOption.java b/src/jalview/ws/jws2/dm/JabaOption.java
index 66b3112..f2378d5 100644
--- a/src/jalview/ws/jws2/dm/JabaOption.java
+++ b/src/jalview/ws/jws2/dm/JabaOption.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/dm/JabaParameter.java b/src/jalview/ws/jws2/dm/JabaParameter.java
index 214c15d..dbcbc9e 100644
--- a/src/jalview/ws/jws2/dm/JabaParameter.java
+++ b/src/jalview/ws/jws2/dm/JabaParameter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/dm/JabaValueConstrain.java b/src/jalview/ws/jws2/dm/JabaValueConstrain.java
index 7aff089..0150479 100644
--- a/src/jalview/ws/jws2/dm/JabaValueConstrain.java
+++ b/src/jalview/ws/jws2/dm/JabaValueConstrain.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/dm/JabaWsParamSet.java b/src/jalview/ws/jws2/dm/JabaWsParamSet.java
index 07ad45d..be16046 100644
--- a/src/jalview/ws/jws2/dm/JabaWsParamSet.java
+++ b/src/jalview/ws/jws2/dm/JabaWsParamSet.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/jabaws2/Jws2Instance.java b/src/jalview/ws/jws2/jabaws2/Jws2Instance.java
index a1307f5..41fa904 100644
--- a/src/jalview/ws/jws2/jabaws2/Jws2Instance.java
+++ b/src/jalview/ws/jws2/jabaws2/Jws2Instance.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/jws2/jabaws2/Jws2InstanceFactory.java b/src/jalview/ws/jws2/jabaws2/Jws2InstanceFactory.java
index 105a0c3..db4770f 100644
--- a/src/jalview/ws/jws2/jabaws2/Jws2InstanceFactory.java
+++ b/src/jalview/ws/jws2/jabaws2/Jws2InstanceFactory.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/ArgumentI.java b/src/jalview/ws/params/ArgumentI.java
index 8d57d0a..b33dfa1 100644
--- a/src/jalview/ws/params/ArgumentI.java
+++ b/src/jalview/ws/params/ArgumentI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/AutoCalcSetting.java b/src/jalview/ws/params/AutoCalcSetting.java
index cf892cb..7125151 100644
--- a/src/jalview/ws/params/AutoCalcSetting.java
+++ b/src/jalview/ws/params/AutoCalcSetting.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/InvalidArgumentException.java b/src/jalview/ws/params/InvalidArgumentException.java
index 58a20dc..0a9e28c 100644
--- a/src/jalview/ws/params/InvalidArgumentException.java
+++ b/src/jalview/ws/params/InvalidArgumentException.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/OptionI.java b/src/jalview/ws/params/OptionI.java
index 1fa7944..ea24054 100644
--- a/src/jalview/ws/params/OptionI.java
+++ b/src/jalview/ws/params/OptionI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/ParamDatastoreI.java b/src/jalview/ws/params/ParamDatastoreI.java
index 8ed54b2..97ebf4e 100644
--- a/src/jalview/ws/params/ParamDatastoreI.java
+++ b/src/jalview/ws/params/ParamDatastoreI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/ParamManager.java b/src/jalview/ws/params/ParamManager.java
index 0d01c28..a275575 100644
--- a/src/jalview/ws/params/ParamManager.java
+++ b/src/jalview/ws/params/ParamManager.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/ParameterI.java b/src/jalview/ws/params/ParameterI.java
index cd85c1a..c77d251 100644
--- a/src/jalview/ws/params/ParameterI.java
+++ b/src/jalview/ws/params/ParameterI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/ValueConstrainI.java b/src/jalview/ws/params/ValueConstrainI.java
index fa7a706..54af485 100644
--- a/src/jalview/ws/params/ValueConstrainI.java
+++ b/src/jalview/ws/params/ValueConstrainI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/WsParamSetI.java b/src/jalview/ws/params/WsParamSetI.java
index 2223d58..4530ff9 100644
--- a/src/jalview/ws/params/WsParamSetI.java
+++ b/src/jalview/ws/params/WsParamSetI.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/simple/BooleanOption.java b/src/jalview/ws/params/simple/BooleanOption.java
index 484a46d..3f84210 100644
--- a/src/jalview/ws/params/simple/BooleanOption.java
+++ b/src/jalview/ws/params/simple/BooleanOption.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/simple/IntegerParameter.java b/src/jalview/ws/params/simple/IntegerParameter.java
index 88cb488..9234227 100644
--- a/src/jalview/ws/params/simple/IntegerParameter.java
+++ b/src/jalview/ws/params/simple/IntegerParameter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/simple/Option.java b/src/jalview/ws/params/simple/Option.java
index c3c6db2..113c1db 100644
--- a/src/jalview/ws/params/simple/Option.java
+++ b/src/jalview/ws/params/simple/Option.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/simple/Parameter.java b/src/jalview/ws/params/simple/Parameter.java
index 46767db..d290e8b 100644
--- a/src/jalview/ws/params/simple/Parameter.java
+++ b/src/jalview/ws/params/simple/Parameter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/params/simple/StringChoiceParameter.java b/src/jalview/ws/params/simple/StringChoiceParameter.java
index 74023b7..86b964a 100644
--- a/src/jalview/ws/params/simple/StringChoiceParameter.java
+++ b/src/jalview/ws/params/simple/StringChoiceParameter.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/AlignmentProcessor.java b/src/jalview/ws/rest/AlignmentProcessor.java
index 43193b6..4fd7984 100644
--- a/src/jalview/ws/rest/AlignmentProcessor.java
+++ b/src/jalview/ws/rest/AlignmentProcessor.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/HttpResultSet.java b/src/jalview/ws/rest/HttpResultSet.java
index 836ca09..5310ffb 100644
--- a/src/jalview/ws/rest/HttpResultSet.java
+++ b/src/jalview/ws/rest/HttpResultSet.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/InputType.java b/src/jalview/ws/rest/InputType.java
index df340b0..043a0eb 100644
--- a/src/jalview/ws/rest/InputType.java
+++ b/src/jalview/ws/rest/InputType.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/NoValidInputDataException.java b/src/jalview/ws/rest/NoValidInputDataException.java
index 2038973..65246e3 100644
--- a/src/jalview/ws/rest/NoValidInputDataException.java
+++ b/src/jalview/ws/rest/NoValidInputDataException.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/RestClient.java b/src/jalview/ws/rest/RestClient.java
index 202ea12..25517a0 100644
--- a/src/jalview/ws/rest/RestClient.java
+++ b/src/jalview/ws/rest/RestClient.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/RestJob.java b/src/jalview/ws/rest/RestJob.java
index 0378d62..1e897f2 100644
--- a/src/jalview/ws/rest/RestJob.java
+++ b/src/jalview/ws/rest/RestJob.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/RestJobThread.java b/src/jalview/ws/rest/RestJobThread.java
index 2d5a83a..24fce47 100644
--- a/src/jalview/ws/rest/RestJobThread.java
+++ b/src/jalview/ws/rest/RestJobThread.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/RestServiceDescription.java b/src/jalview/ws/rest/RestServiceDescription.java
index d97ad07..09acb7f 100644
--- a/src/jalview/ws/rest/RestServiceDescription.java
+++ b/src/jalview/ws/rest/RestServiceDescription.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/params/Alignment.java b/src/jalview/ws/rest/params/Alignment.java
index d7b1fc7..3d9b00c 100644
--- a/src/jalview/ws/rest/params/Alignment.java
+++ b/src/jalview/ws/rest/params/Alignment.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/params/AnnotationFile.java b/src/jalview/ws/rest/params/AnnotationFile.java
index dd1f39e..be83092 100644
--- a/src/jalview/ws/rest/params/AnnotationFile.java
+++ b/src/jalview/ws/rest/params/AnnotationFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/params/JobConstant.java b/src/jalview/ws/rest/params/JobConstant.java
index da12bb9..d70b6f6 100644
--- a/src/jalview/ws/rest/params/JobConstant.java
+++ b/src/jalview/ws/rest/params/JobConstant.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/params/SeqGroupIndexVector.java b/src/jalview/ws/rest/params/SeqGroupIndexVector.java
index 6bc208b..c64169c 100644
--- a/src/jalview/ws/rest/params/SeqGroupIndexVector.java
+++ b/src/jalview/ws/rest/params/SeqGroupIndexVector.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/params/SeqIdVector.java b/src/jalview/ws/rest/params/SeqIdVector.java
index 66703a0..707d4ad 100644
--- a/src/jalview/ws/rest/params/SeqIdVector.java
+++ b/src/jalview/ws/rest/params/SeqIdVector.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/params/SeqVector.java b/src/jalview/ws/rest/params/SeqVector.java
index 0be6b98..40de4c9 100644
--- a/src/jalview/ws/rest/params/SeqVector.java
+++ b/src/jalview/ws/rest/params/SeqVector.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/rest/params/Tree.java b/src/jalview/ws/rest/params/Tree.java
index 736e155..3a6b24a 100644
--- a/src/jalview/ws/rest/params/Tree.java
+++ b/src/jalview/ws/rest/params/Tree.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/jalview/ws/seqfetcher/ASequenceFetcher.java b/src/jalview/ws/seqfetcher/ASequenceFetcher.java
index 4dcc657..40456d9 100644
--- a/src/jalview/ws/seqfetcher/ASequenceFetcher.java
+++ b/src/jalview/ws/seqfetcher/ASequenceFetcher.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,6 +20,8 @@
  */
 package jalview.ws.seqfetcher;
 
+import jalview.api.FeatureSettingsModelI;
+import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.SequenceI;
@@ -27,10 +29,11 @@ import jalview.util.DBRefUtils;
 import jalview.util.MessageManager;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Stack;
@@ -39,70 +42,102 @@ import java.util.Vector;
 public class ASequenceFetcher
 {
 
-  /**
+  /*
    * set of databases we can retrieve entries from
    */
-  protected Hashtable<String, Map<String, DbSourceProxy>> FETCHABLEDBS;
+  protected Hashtable<String, Map<String, DbSourceProxy>> fetchableDbs;
+
+  /*
+   * comparator to sort by tier (0/1/2) and name
+   */
+  private Comparator<DbSourceProxy> proxyComparator;
 
-  public ASequenceFetcher()
+  /**
+   * Constructor
+   */
+  protected ASequenceFetcher()
   {
     super();
+
+    /*
+     * comparator to sort proxies by tier and name
+     */
+    proxyComparator = new Comparator<DbSourceProxy>()
+    {
+      @Override
+      public int compare(DbSourceProxy o1, DbSourceProxy o2)
+      {
+        /*
+         * Tier 0 precedes 1 precedes 2
+         */
+        int compared = Integer.compare(o1.getTier(), o2.getTier());
+        if (compared == 0)
+        {
+          // defend against NullPointer - should never happen
+          String o1Name = o1.getDbName();
+          String o2Name = o2.getDbName();
+          if (o1Name != null && o2Name != null)
+          {
+            compared = o1Name.compareToIgnoreCase(o2Name);
+          }
+        }
+        return compared;
+      }
+    };
   }
 
   /**
-   * get list of supported Databases
+   * get array of supported Databases
    * 
    * @return database source string for each database - only the latest version
    *         of a source db is bound to each source.
    */
   public String[] getSupportedDb()
   {
-    if (FETCHABLEDBS == null)
+    if (fetchableDbs == null)
     {
       return null;
     }
-    String[] sf = new String[FETCHABLEDBS.size()];
-    Enumeration e = FETCHABLEDBS.keys();
-    int i = 0;
-    while (e.hasMoreElements())
-    {
-      sf[i++] = (String) e.nextElement();
-    }
-    ;
+    String[] sf = fetchableDbs.keySet().toArray(
+            new String[fetchableDbs.size()]);
     return sf;
   }
 
   public boolean isFetchable(String source)
   {
-    Enumeration e = FETCHABLEDBS.keys();
-    while (e.hasMoreElements())
+    for (String db : fetchableDbs.keySet())
     {
-      String db = (String) e.nextElement();
-      if (source.compareToIgnoreCase(db) == 0)
+      if (source.equalsIgnoreCase(db))
       {
         return true;
       }
     }
-    jalview.bin.Cache.log.warn("isFetchable doesn't know about '" + source
-            + "'");
+    Cache.log.warn("isFetchable doesn't know about '" + source + "'");
     return false;
   }
 
-  public SequenceI[] getSequences(jalview.datamodel.DBRefEntry[] refs)
+  /**
+   * Fetch sequences for the given cross-references
+   * 
+   * @param refs
+   * @param dna
+   *          if true, only fetch from nucleotide data sources, else peptide
+   * @return
+   */
+  public SequenceI[] getSequences(List<DBRefEntry> refs, boolean dna)
   {
-    SequenceI[] ret = null;
-    Vector<SequenceI> rseqs = new Vector();
-    Hashtable<String, List<String>> queries = new Hashtable();
-    for (int r = 0; r < refs.length; r++)
+    Vector<SequenceI> rseqs = new Vector<SequenceI>();
+    Hashtable<String, List<String>> queries = new Hashtable<String, List<String>>();
+    for (DBRefEntry ref : refs)
     {
-      if (!queries.containsKey(refs[r].getSource()))
+      if (!queries.containsKey(ref.getSource()))
       {
-        queries.put(refs[r].getSource(), new ArrayList<String>());
+        queries.put(ref.getSource(), new ArrayList<String>());
       }
-      List<String> qset = queries.get(refs[r].getSource());
-      if (!qset.contains(refs[r].getAccessionId()))
+      List<String> qset = queries.get(ref.getSource());
+      if (!qset.contains(ref.getAccessionId()))
       {
-        qset.add(refs[r].getAccessionId());
+        qset.add(ref.getAccessionId());
       }
     }
     Enumeration<String> e = queries.keys();
@@ -118,22 +153,22 @@ public class ASequenceFetcher
                 "Don't know how to fetch from this database :" + db));
         continue;
       }
-      Iterator<DbSourceProxy> fetchers = getSourceProxy(db).iterator();
+
       Stack<String> queriesLeft = new Stack<String>();
-      // List<String> queriesFailed = new ArrayList<String>();
       queriesLeft.addAll(query);
-      while (fetchers.hasNext())
+
+      List<DbSourceProxy> proxies = getSourceProxy(db);
+      for (DbSourceProxy fetcher : proxies)
       {
         List<String> queriesMade = new ArrayList<String>();
-        HashSet queriesFound = new HashSet<String>();
+        HashSet<String> queriesFound = new HashSet<String>();
         try
         {
-          DbSourceProxy fetcher = fetchers.next();
-          boolean doMultiple = fetcher.getAccessionSeparator() != null; // No
-          // separator
-          // - no
-          // Multiple
-          // Queries
+          if (fetcher.isDnaCoding() != dna)
+          {
+            continue; // wrong sort of data
+          }
+          boolean doMultiple = fetcher.getMaximumQueryCount() > 1;
           while (!queriesLeft.isEmpty())
           {
             StringBuffer qsb = new StringBuffer();
@@ -152,8 +187,7 @@ public class ASequenceFetcher
             try
             {
               // create a fetcher and go to it
-              seqset = fetcher.getSequenceRecords(qsb.toString()); // ,
-              // queriesFailed);
+              seqset = fetcher.getSequenceRecords(qsb.toString());
             } catch (Exception ex)
             {
               System.err.println("Failed to retrieve the following from "
@@ -170,15 +204,12 @@ public class ASequenceFetcher
                 for (int is = 0; is < seqs.length; is++)
                 {
                   rseqs.addElement(seqs[is]);
-                  DBRefEntry[] frefs = DBRefUtils.searchRefs(seqs[is]
-                          .getDBRef(), new DBRefEntry(db, null, null));
-                  if (frefs != null)
+                  List<DBRefEntry> frefs = DBRefUtils.searchRefs(seqs[is]
+                          .getDBRefs(), new DBRefEntry(db, null, null));
+                  for (DBRefEntry dbr : frefs)
                   {
-                    for (DBRefEntry dbr : frefs)
-                    {
-                      queriesFound.add(dbr.getAccessionId());
-                      queriesMade.remove(dbr.getAccessionId());
-                    }
+                    queriesFound.add(dbr.getAccessionId());
+                    queriesMade.remove(dbr.getAccessionId());
                   }
                   seqs[is] = null;
                 }
@@ -220,24 +251,24 @@ public class ASequenceFetcher
         {
           System.out.println("# Adding " + queriesMade.size()
                   + " ids back to queries list for searching again (" + db
-                  + ".");
+                  + ")");
           queriesLeft.addAll(queriesMade);
         }
       }
     }
+
+    SequenceI[] result = null;
     if (rseqs.size() > 0)
     {
-      ret = new SequenceI[rseqs.size()];
-      Enumeration sqs = rseqs.elements();
+      result = new SequenceI[rseqs.size()];
       int si = 0;
-      while (sqs.hasMoreElements())
+      for (SequenceI s : rseqs)
       {
-        SequenceI s = (SequenceI) sqs.nextElement();
-        ret[si++] = s;
+        result[si++] = s;
         s.updatePDBIds();
       }
     }
-    return ret;
+    return result;
   }
 
   public void reportStdError(String db, List<String> queriesMade,
@@ -261,50 +292,32 @@ public class ASequenceFetcher
   }
 
   /**
-   * Retrieve an instance of the proxy for the given source
+   * Returns a list of proxies for the given source
    * 
    * @param db
    *          database source string TODO: add version string/wildcard for
    *          retrieval of specific DB source/version combinations.
-   * @return an instance of DbSourceProxy for that db.
+   * @return a list of DbSourceProxy for the db
    */
   public List<DbSourceProxy> getSourceProxy(String db)
   {
-    List<DbSourceProxy> dbs;
-    Map<String, DbSourceProxy> dblist = FETCHABLEDBS.get(db);
+    db = DBRefUtils.getCanonicalName(db);
+    Map<String, DbSourceProxy> dblist = fetchableDbs.get(db);
     if (dblist == null)
     {
       return new ArrayList<DbSourceProxy>();
     }
-    ;
-    if (dblist.size() > 1)
-    {
-      DbSourceProxy[] l = dblist.values().toArray(new DbSourceProxy[0]);
-      int i = 0;
-      String[] nm = new String[l.length];
-      // make sure standard dbs appear first, followed by reference das sources,
-      // followed by anything else.
-      for (DbSourceProxy s : l)
-      {
-        nm[i++] = "" + s.getTier() + s.getDbName().toLowerCase();
-      }
-      jalview.util.QuickSort.sort(nm, l);
-      dbs = new ArrayList<DbSourceProxy>();
-      for (i = l.length - 1; i >= 0; i--)
-      {
-        dbs.add(l[i]);
-      }
-    }
-    else
-    {
-      dbs = new ArrayList<DbSourceProxy>(dblist.values());
-    }
+
+    /*
+     * sort so that primary sources precede secondary
+     */
+    List<DbSourceProxy> dbs = new ArrayList<DbSourceProxy>(dblist.values());
+    Collections.sort(dbs, proxyComparator);
     return dbs;
   }
 
   /**
-   * constructs and instance of the proxy and registers it as a valid
-   * dbrefsource
+   * constructs an instance of the proxy and registers it as a valid dbrefsource
    * 
    * @param dbSourceProxy
    *          reference for class implementing
@@ -312,7 +325,7 @@ public class ASequenceFetcher
    */
   protected void addDBRefSourceImpl(
           Class<? extends DbSourceProxy> dbSourceProxy)
-          throws java.lang.IllegalArgumentException
+          throws IllegalArgumentException
   {
     DbSourceProxy proxy = null;
     try
@@ -343,15 +356,15 @@ public class ASequenceFetcher
   {
     if (proxy != null)
     {
-      if (FETCHABLEDBS == null)
+      if (fetchableDbs == null)
       {
-        FETCHABLEDBS = new Hashtable<String, Map<String, DbSourceProxy>>();
+        fetchableDbs = new Hashtable<String, Map<String, DbSourceProxy>>();
       }
-      Map<String, DbSourceProxy> slist = FETCHABLEDBS.get(proxy
+      Map<String, DbSourceProxy> slist = fetchableDbs.get(proxy
               .getDbSource());
       if (slist == null)
       {
-        FETCHABLEDBS.put(proxy.getDbSource(),
+        fetchableDbs.put(proxy.getDbSource(),
                 slist = new Hashtable<String, DbSourceProxy>());
       }
       slist.put(proxy.getDbName(), proxy);
@@ -359,34 +372,6 @@ public class ASequenceFetcher
   }
 
   /**
-   * test if the database handler for dbName contains the given dbProperty when
-   * a dbName resolves to a set of proxies - this method will return the result
-   * of the test for the first instance. TODO implement additional method to
-   * query all sources for a db to find one with a particular property
-   * 
-   * @param dbName
-   * @param dbProperty
-   * @return true if proxy has the given property
-   */
-  public boolean hasDbSourceProperty(String dbName, String dbProperty)
-  {
-    // TODO: decide if invalidDbName exception is thrown here.
-
-    List<DbSourceProxy> proxies = getSourceProxy(dbName);
-    if (proxies != null)
-    {
-      for (DbSourceProxy proxy : proxies)
-      {
-        if (proxy.getDbSourceProperties() != null)
-        {
-          return proxy.getDbSourceProperties().containsKey(dbProperty);
-        }
-      }
-    }
-    return false;
-  }
-
-  /**
    * select sources which are implemented by instances of the given class
    * 
    * @param class that implements DbSourceProxy
@@ -394,7 +379,7 @@ public class ASequenceFetcher
    */
   public String[] getDbInstances(Class class1)
   {
-    if (!jalview.ws.seqfetcher.DbSourceProxy.class.isAssignableFrom(class1))
+    if (!DbSourceProxy.class.isAssignableFrom(class1))
     {
       throw new Error(
               MessageManager
@@ -402,17 +387,17 @@ public class ASequenceFetcher
                               "error.implementation_error_dbinstance_must_implement_interface",
                               new String[] { class1.toString() }));
     }
-    if (FETCHABLEDBS == null)
+    if (fetchableDbs == null)
     {
       return null;
     }
     String[] sources = null;
-    Vector src = new Vector();
-    Enumeration dbs = FETCHABLEDBS.keys();
+    Vector<String> src = new Vector<String>();
+    Enumeration<String> dbs = fetchableDbs.keys();
     while (dbs.hasMoreElements())
     {
-      String dbn = (String) dbs.nextElement();
-      for (DbSourceProxy dbp : FETCHABLEDBS.get(dbn).values())
+      String dbn = dbs.nextElement();
+      for (DbSourceProxy dbp : fetchableDbs.get(dbn).values())
       {
         if (class1.isAssignableFrom(dbp.getClass()))
         {
@@ -429,7 +414,7 @@ public class ASequenceFetcher
 
   public DbSourceProxy[] getDbSourceProxyInstances(Class class1)
   {
-    ArrayList<DbSourceProxy> prlist = new ArrayList<DbSourceProxy>();
+    List<DbSourceProxy> prlist = new ArrayList<DbSourceProxy>();
     for (String fetchable : getSupportedDb())
     {
       for (DbSourceProxy pr : getSourceProxy(fetchable))
@@ -447,4 +432,28 @@ public class ASequenceFetcher
     return prlist.toArray(new DbSourceProxy[0]);
   }
 
+  /**
+   * Returns a preferred feature colouring scheme for the given source, or null
+   * if none is defined.
+   * 
+   * @param source
+   * @return
+   */
+  public FeatureSettingsModelI getFeatureColourScheme(String source)
+  {
+    /*
+     * return the first non-null colour scheme for any proxy for
+     * this database source
+     */
+    for (DbSourceProxy proxy : getSourceProxy(source))
+    {
+      FeatureSettingsModelI preferredColours = proxy
+              .getFeatureColourScheme();
+      if (preferredColours != null)
+      {
+        return preferredColours;
+      }
+    }
+    return null;
+  }
 }
diff --git a/src/jalview/ws/seqfetcher/DbSourceProxy.java b/src/jalview/ws/seqfetcher/DbSourceProxy.java
index 57beea2..5123716 100644
--- a/src/jalview/ws/seqfetcher/DbSourceProxy.java
+++ b/src/jalview/ws/seqfetcher/DbSourceProxy.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,20 +20,19 @@
  */
 package jalview.ws.seqfetcher;
 
+import jalview.api.FeatureSettingsModelI;
 import jalview.datamodel.AlignmentI;
 
-import java.util.Hashtable;
-
 import com.stevesoft.pat.Regex;
 
 /**
  * generic Reference Retrieval interface for a particular database
- * source/version as cited in DBRefEntry. TODO: add/define property to describe
- * max number of queries that this source can cope with at once. TODO:
- * add/define mechanism for retrieval of Trees and distance matrices from a
- * database (unify with io)
+ * source/version as cited in DBRefEntry.
+ * 
+ * TODO: add/define mechanism for retrieval of Trees and distance matrices from
+ * a database (unify with io)
  * 
- * @author JimP TODO: promote to API
+ * @author JimP
  */
 public interface DbSourceProxy
 {
@@ -41,7 +40,7 @@ public interface DbSourceProxy
    * 
    * @return source string constant used for this DB source
    */
-  public String getDbSource();
+  String getDbSource();
 
   /**
    * Short meaningful name for this data source for display in menus or
@@ -49,13 +48,13 @@ public interface DbSourceProxy
    * 
    * @return String
    */
-  public String getDbName();
+  String getDbName();
 
   /**
    * 
    * @return version string for this database.
    */
-  public String getDbVersion();
+  String getDbVersion();
 
   /**
    * Separator between individual accession queries for a database that allows
@@ -65,7 +64,7 @@ public interface DbSourceProxy
    * @return string for separating concatenated queries (as individually
    *         validated by the accession validator)
    */
-  public String getAccessionSeparator();
+  String getAccessionSeparator();
 
   /**
    * Regular expression for checking form of query string understood by this
@@ -75,31 +74,24 @@ public interface DbSourceProxy
    * 
    * @return null or a validation regex
    */
-  public Regex getAccessionValidator();
-
-  /**
-   * DbSource properties hash - define the capabilities of this source Property
-   * hash methods defined in DbSourceProxyImpl. See constants in
-   * jalview.datamodel.DBRefSource for definition of properties.
-   * 
-   * @return
-   */
-  public Hashtable getDbSourceProperties();
+  Regex getAccessionValidator();
 
   /**
    * 
    * @return a test/example query that can be used to validate retrieval and
    *         parsing mechanisms
    */
-  public String getTestQuery();
+  String getTestQuery();
 
   /**
-   * optionally implemented
+   * Required for sources supporting multiple query retrieval for use with the
+   * DBRefFetcher, which attempts to limit its queries with putative accession
+   * strings for a source to only those that are likely to be valid.
    * 
    * @param accession
    * @return
    */
-  public boolean isValidReference(String accession);
+  boolean isValidReference(String accession);
 
   /**
    * make one or more queries to the database and attempt to parse the response
@@ -107,41 +99,83 @@ public interface DbSourceProxy
    * 
    * @param queries
    *          - one or more queries for database in expected form
-   * @return null if queries were successful but result was not parsable
+   * @return null if queries were successful but result was not parsable.
+   *         Otherwise, an AlignmentI object containing properly annotated data
+   *         (e.g. sequences with accessions for this datasource)
    * @throws Exception
    *           - propagated from underlying transport to database (note -
    *           exceptions are not raised if query not found in database)
    * 
    */
-  public AlignmentI getSequenceRecords(String queries) throws Exception;
+  AlignmentI getSequenceRecords(String queries) throws Exception;
 
   /**
    * 
    * @return true if a query is currently being made
    */
-  public boolean queryInProgress();
+  boolean queryInProgress();
 
   /**
    * get the raw reponse from the last set of queries
    * 
    * @return one or more string buffers for each individual query
    */
-  public StringBuffer getRawRecords();
+  StringBuffer getRawRecords();
 
   /**
-   * Find out more info about the source.
+   * Tier for this data source
    * 
-   * @param dbsourceproperty
-   *          - one of the database reference source properties in
-   *          jalview.datamodel.DBRefSource
-   * @return true if the source has this property
+   * @return 0 - primary datasource, 1 - das primary source, 2 - secondary
    */
-  public boolean isA(Object dbsourceproperty);
+  int getTier();
 
   /**
-   * Tier for this data source
+   * Extracts valid accession strings from a query string. If there is an
+   * accession id validator, returns the the matched region or the first
+   * subgroup match from the matched region; else just returns the whole query.
    * 
-   * @return 0 - primary datasource, 1 - das primary source, 2 - secondary
+   * @param query
+   * @return
+   */
+  String getAccessionIdFromQuery(String query);
+
+  /**
+   * Returns the maximum number of accession ids that can be queried in one
+   * request.
+   * 
+   * @return
+   */
+  int getMaximumQueryCount();
+
+  /**
+   * Returns true if the source may provide coding DNA i.e. sequences with
+   * implicit peptide products
+   * 
+   * @return
+   */
+  boolean isDnaCoding();
+
+  /**
+   * Answers true if the database is a source of alignments (for example, domain
+   * families)
+   * 
+   * @return
+   */
+  boolean isAlignmentSource();
+
+  /**
+   * Returns an (optional) description of the source, suitable for display as a
+   * tooltip, or null
+   * 
+   * @return
+   */
+  String getDescription();
+
+  /**
+   * Returns the preferred feature colour configuration if there is one, else
+   * null
+   * 
+   * @return
    */
-  public int getTier();
-}
+  FeatureSettingsModelI getFeatureColourScheme();
+}
\ No newline at end of file
diff --git a/src/jalview/ws/seqfetcher/DbSourceProxyImpl.java b/src/jalview/ws/seqfetcher/DbSourceProxyImpl.java
index f0407ad..5f7a065 100644
--- a/src/jalview/ws/seqfetcher/DbSourceProxyImpl.java
+++ b/src/jalview/ws/seqfetcher/DbSourceProxyImpl.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,12 +20,11 @@
  */
 package jalview.ws.seqfetcher;
 
+import jalview.api.FeatureSettingsModelI;
 import jalview.datamodel.AlignmentI;
 import jalview.io.FormatAdapter;
 import jalview.io.IdentifyFile;
 
-import java.util.Hashtable;
-
 /**
  * common methods for implementations of the DbSourceProxy interface.
  * 
@@ -34,50 +33,21 @@ import java.util.Hashtable;
  */
 public abstract class DbSourceProxyImpl implements DbSourceProxy
 {
-  public DbSourceProxyImpl()
-  {
-    // default constructor - do nothing probably.
-  }
 
-  private Hashtable props = null;
-
-  /*
-   * (non-Javadoc)
-   * 
-   * @see jalview.ws.DbSourceProxy#getDbSourceProperties()
-   */
-  public Hashtable getDbSourceProperties()
-  {
-    if (props == null)
-    {
-      props = new Hashtable();
-    }
-    return props;
-  }
+  boolean queryInProgress = false;
 
-  protected void addDbSourceProperty(Object propname)
-  {
-    addDbSourceProperty(propname, propname);
-  }
+  protected StringBuffer results = null;
 
-  protected void addDbSourceProperty(Object propname, Object propvalue)
+  public DbSourceProxyImpl()
   {
-    if (props == null)
-    {
-      props = new Hashtable();
-    }
-    props.put(propname, propvalue);
   }
 
-  boolean queryInProgress = false;
-
-  protected StringBuffer results = null;
-
   /*
    * (non-Javadoc)
    * 
    * @see jalview.ws.DbSourceProxy#getRawRecords()
    */
+  @Override
   public StringBuffer getRawRecords()
   {
     return results;
@@ -88,6 +58,7 @@ public abstract class DbSourceProxyImpl implements DbSourceProxy
    * 
    * @see jalview.ws.DbSourceProxy#queryInProgress()
    */
+  @Override
   public boolean queryInProgress()
   {
     return queryInProgress;
@@ -121,7 +92,7 @@ public abstract class DbSourceProxyImpl implements DbSourceProxy
   protected AlignmentI parseResult(String result) throws Exception
   {
     AlignmentI sequences = null;
-    String format = new IdentifyFile().Identify(result, "Paste");
+    String format = new IdentifyFile().identify(result, "Paste");
     if (FormatAdapter.isValidFormat(format))
     {
       sequences = new FormatAdapter().readFile(result.toString(), "Paste",
@@ -130,11 +101,58 @@ public abstract class DbSourceProxyImpl implements DbSourceProxy
     return sequences;
   }
 
+  /**
+   * Returns the first accession id in the query (up to the first accession id
+   * separator), or the whole query if there is no separator or it is not found
+   */
   @Override
-  public boolean isA(Object dbsourceproperty)
+  public String getAccessionIdFromQuery(String query)
   {
-    assert (dbsourceproperty != null);
-    return (props == null) ? false : props.containsKey(dbsourceproperty);
+    String sep = getAccessionSeparator();
+    if (sep == null)
+    {
+      return query;
+    }
+    int sepPos = query.indexOf(sep);
+    return sepPos == -1 ? query : query.substring(0, sepPos);
+  }
+
+  /**
+   * Default is only one accession id per query - override if more are allowed.
+   */
+  @Override
+  public int getMaximumQueryCount()
+  {
+    return 1;
+  }
+
+  /**
+   * Returns false - override to return true for DNA coding data sources
+   */
+  @Override
+  public boolean isDnaCoding()
+  {
+    return false;
   }
 
+  /**
+   * Answers false - override as required in subclasses
+   */
+  @Override
+  public boolean isAlignmentSource()
+  {
+    return false;
+  }
+
+  @Override
+  public String getDescription()
+  {
+    return "";
+  }
+
+  @Override
+  public FeatureSettingsModelI getFeatureColourScheme()
+  {
+    return null;
+  }
 }
diff --git a/src/jalview/ws/sifts/MappingOutputPojo.java b/src/jalview/ws/sifts/MappingOutputPojo.java
new file mode 100644
index 0000000..1c331ef
--- /dev/null
+++ b/src/jalview/ws/sifts/MappingOutputPojo.java
@@ -0,0 +1,137 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws.sifts;
+
+public class MappingOutputPojo
+{
+  private String seqName;
+
+  private String seqResidue;
+
+  private int seqStart;
+
+  private int seqEnd;
+
+  private String strName;
+
+  private String strResidue;
+
+  private int strStart;
+
+  private int strEnd;
+
+  private String type;
+
+  private static final int MAX_ID_LENGTH = 30;
+
+  public String getSeqName()
+  {
+    return seqName;
+  }
+
+  public void setSeqName(String seqName)
+  {
+    this.seqName = (seqName.length() > MAX_ID_LENGTH) ? seqName.substring(
+            0, MAX_ID_LENGTH) : seqName;
+  }
+
+  public String getSeqResidue()
+  {
+    return seqResidue;
+  }
+
+  public void setSeqResidue(String seqResidue)
+  {
+    this.seqResidue = seqResidue;
+  }
+
+  public int getSeqStart()
+  {
+    return seqStart;
+  }
+
+  public void setSeqStart(int seqStart)
+  {
+    this.seqStart = seqStart;
+  }
+
+  public int getSeqEnd()
+  {
+    return seqEnd;
+  }
+
+  public void setSeqEnd(int seqEnd)
+  {
+    this.seqEnd = seqEnd;
+  }
+
+  public String getStrName()
+  {
+    return strName;
+  }
+
+  public void setStrName(String strName)
+  {
+    this.strName = (strName.length() > MAX_ID_LENGTH) ? strName.substring(
+            0, MAX_ID_LENGTH) : strName;
+  }
+
+  public String getStrResidue()
+  {
+    return strResidue;
+  }
+
+  public void setStrResidue(String strResidue)
+  {
+    this.strResidue = strResidue;
+  }
+
+  public int getStrStart()
+  {
+    return strStart;
+  }
+
+  public void setStrStart(int strStart)
+  {
+    this.strStart = strStart;
+  }
+
+  public int getStrEnd()
+  {
+    return strEnd;
+  }
+
+  public void setStrEnd(int strEnd)
+  {
+    this.strEnd = strEnd;
+  }
+
+  public String getType()
+  {
+    return type;
+  }
+
+  public void setType(String type)
+  {
+    this.type = type;
+  }
+
+}
diff --git a/src/jalview/ws/sifts/SiftsClient.java b/src/jalview/ws/sifts/SiftsClient.java
new file mode 100644
index 0000000..166592a
--- /dev/null
+++ b/src/jalview/ws/sifts/SiftsClient.java
@@ -0,0 +1,1094 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws.sifts;
+
+import jalview.analysis.AlignSeq;
+import jalview.api.DBRefEntryI;
+import jalview.api.SiftsClientI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.SequenceI;
+import jalview.io.StructureFile;
+import jalview.schemes.ResidueProperties;
+import jalview.structure.StructureMapping;
+import jalview.util.Comparison;
+import jalview.util.DBRefUtils;
+import jalview.util.Format;
+import jalview.xml.binding.sifts.Entry;
+import jalview.xml.binding.sifts.Entry.Entity;
+import jalview.xml.binding.sifts.Entry.Entity.Segment;
+import jalview.xml.binding.sifts.Entry.Entity.Segment.ListMapRegion.MapRegion;
+import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue;
+import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.CrossRefDb;
+import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.ResidueDetail;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.file.Files;
+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;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.zip.GZIPInputStream;
+
+import javax.xml.bind.JAXBContext;
+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
+{
+  private Entry siftsEntry;
+
+  private StructureFile pdb;
+
+  private String pdbId;
+
+  private String structId;
+
+  private CoordinateSys seqCoordSys = CoordinateSys.UNIPROT;
+
+  private static final int BUFFER_SIZE = 4096;
+
+  public static final int UNASSIGNED = -1;
+
+  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/";
+
+  private final static String NEWLINE = System.lineSeparator();
+
+  private String curSourceDBRef;
+
+  private HashSet<String> curDBRefAccessionIdsString;
+
+  private enum CoordinateSys
+  {
+    UNIPROT("UniProt"), PDB("PDBresnum"), PDBe("PDBe");
+    private String name;
+
+    private CoordinateSys(String name)
+    {
+      this.name = name;
+    }
+
+    public String getName()
+    {
+      return name;
+    }
+  };
+
+  private enum ResidueDetailType
+  {
+    NAME_SEC_STRUCTURE("nameSecondaryStructure"), CODE_SEC_STRUCTURE(
+            "codeSecondaryStructure"), ANNOTATION("Annotation");
+    private String code;
+
+    private ResidueDetailType(String code)
+    {
+      this.code = code;
+    }
+
+    public String getCode()
+    {
+      return code;
+    }
+  };
+
+  /**
+   * Fetch SIFTs file for the given PDBfile and construct an instance of
+   * SiftsClient
+   * 
+   * @param pdbId
+   * @throws SiftsException
+   */
+  public SiftsClient(StructureFile pdb) throws SiftsException
+  {
+    this.pdb = pdb;
+    this.pdbId = pdb.getId();
+    File siftsFile = getSiftsFile(pdbId);
+    siftsEntry = parseSIFTs(siftsFile);
+  }
+
+  /**
+   * Parse the given SIFTs File and return a JAXB POJO of parsed data
+   * 
+   * @param siftFile
+   *          - the GZipped SIFTs XML file to parse
+   * @return
+   * @throws Exception
+   *           if a problem occurs while parsing the SIFTs XML
+   */
+  private Entry parseSIFTs(File siftFile) throws SiftsException
+  {
+    try (InputStream in = new FileInputStream(siftFile);
+            GZIPInputStream gzis = new GZIPInputStream(in);)
+    {
+      // System.out.println("File : " + siftFile.getAbsolutePath());
+      JAXBContext jc = JAXBContext.newInstance("jalview.xml.binding.sifts");
+      XMLStreamReader streamReader = XMLInputFactory.newInstance()
+              .createXMLStreamReader(gzis);
+      Unmarshaller um = jc.createUnmarshaller();
+      return (Entry) um.unmarshal(streamReader);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      throw new SiftsException(e.getMessage());
+    }
+  }
+
+  /**
+   * Get a SIFTs XML file for a given PDB Id from Cache or download from FTP
+   * repository if not found in cache
+   * 
+   * @param pdbId
+   * @return SIFTs XML file
+   * @throws SiftsException
+   */
+  public static File getSiftsFile(String pdbId) throws SiftsException
+  {
+    String siftsFileName = SiftsSettings.getSiftDownloadDirectory()
+            + pdbId.toLowerCase() + ".xml.gz";
+    File siftsFile = new File(siftsFileName);
+    if (siftsFile.exists())
+    {
+      // The line below is required for unit testing... don't comment it out!!!
+      System.out.println(">>> SIFTS File already downloaded for " + pdbId);
+
+      if (isFileOlderThanThreshold(siftsFile,
+              SiftsSettings.getCacheThresholdInDays()))
+      {
+        File oldSiftsFile = new File(siftsFileName + "_old");
+        siftsFile.renameTo(oldSiftsFile);
+        try
+        {
+          siftsFile = downloadSiftsFile(pdbId.toLowerCase());
+          oldSiftsFile.delete();
+          return siftsFile;
+        } catch (IOException e)
+        {
+          e.printStackTrace();
+          oldSiftsFile.renameTo(siftsFile);
+          return new File(siftsFileName);
+        }
+      }
+    }
+    try
+    {
+      siftsFile = downloadSiftsFile(pdbId.toLowerCase());
+    } catch (IOException e)
+    {
+      throw new SiftsException(e.getMessage());
+    }
+    return siftsFile;
+  }
+
+  /**
+   * This method enables checking if a cached file has exceeded a certain
+   * threshold(in days)
+   * 
+   * @param file
+   *          the cached file
+   * @param noOfDays
+   *          the threshold in days
+   * @return
+   */
+  public static boolean isFileOlderThanThreshold(File file, int noOfDays)
+  {
+    Path filePath = file.toPath();
+    BasicFileAttributes attr;
+    int diffInDays = 0;
+    try
+    {
+      attr = Files.readAttributes(filePath, BasicFileAttributes.class);
+      diffInDays = (int) ((new Date().getTime() - attr.lastModifiedTime()
+              .toMillis()) / (1000 * 60 * 60 * 24));
+      // System.out.println("Diff in days : " + diffInDays);
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+    return noOfDays <= diffInDays;
+  }
+
+  /**
+   * Download a SIFTs XML file for a given PDB Id from an FTP repository
+   * 
+   * @param pdbId
+   * @return downloaded SIFTs XML file
+   * @throws SiftsException
+   * @throws IOException
+   */
+  public static File downloadSiftsFile(String pdbId) throws SiftsException,
+          IOException
+  {
+    if (pdbId.contains(".cif"))
+    {
+      pdbId = pdbId.replace(".cif", "");
+    }
+    String siftFile = pdbId + ".xml.gz";
+    String siftsFileFTPURL = SIFTS_FTP_BASE_URL + siftFile;
+    String downloadedSiftsFile = SiftsSettings.getSiftDownloadDirectory()
+            + siftFile;
+    File siftsDownloadDir = new File(
+            SiftsSettings.getSiftDownloadDirectory());
+    if (!siftsDownloadDir.exists())
+    {
+      siftsDownloadDir.mkdirs();
+    }
+    // System.out.println(">> Download ftp url : " + siftsFileFTPURL);
+    URL url = new URL(siftsFileFTPURL);
+    URLConnection conn = url.openConnection();
+    InputStream inputStream = conn.getInputStream();
+    FileOutputStream outputStream = new FileOutputStream(
+            downloadedSiftsFile);
+    byte[] buffer = new byte[BUFFER_SIZE];
+    int bytesRead = -1;
+    while ((bytesRead = inputStream.read(buffer)) != -1)
+    {
+      outputStream.write(buffer, 0, bytesRead);
+    }
+    outputStream.close();
+    inputStream.close();
+    // System.out.println(">>> File downloaded : " + downloadedSiftsFile);
+    return new File(downloadedSiftsFile);
+  }
+
+  /**
+   * Delete the SIFTs file for the given PDB Id in the local SIFTs download
+   * directory
+   * 
+   * @param pdbId
+   * @return true if the file was deleted or doesn't exist
+   */
+  public static boolean deleteSiftsFileByPDBId(String pdbId)
+  {
+    File siftsFile = new File(SiftsSettings.getSiftDownloadDirectory()
+            + pdbId.toLowerCase() + ".xml.gz");
+    if (siftsFile.exists())
+    {
+      return siftsFile.delete();
+    }
+    return true;
+  }
+
+  /**
+   * Get a valid SIFTs DBRef for the given sequence current SIFTs entry
+   * 
+   * @param seq
+   *          - the target sequence for the operation
+   * @return a valid DBRefEntry that is SIFTs compatible
+   * @throws Exception
+   *           if no valid source DBRefEntry was found for the given sequences
+   */
+  public DBRefEntryI getValidSourceDBRef(SequenceI seq)
+          throws SiftsException
+  {
+    List<DBRefEntry> dbRefs = seq.getPrimaryDBRefs();
+    if (dbRefs == null || dbRefs.size() < 1)
+    {
+      throw new SiftsException(
+              "Source DBRef could not be determined. DBRefs might not have been retrieved.");
+    }
+
+    for (DBRefEntry dbRef : dbRefs)
+    {
+      if (dbRef == null || dbRef.getAccessionId() == null
+              || dbRef.getSource() == null)
+      {
+        continue;
+      }
+      String canonicalSource = DBRefUtils.getCanonicalName(dbRef
+              .getSource());
+      if (isValidDBRefEntry(dbRef)
+              && (canonicalSource.equalsIgnoreCase(DBRefSource.UNIPROT) || canonicalSource
+                      .equalsIgnoreCase(DBRefSource.PDB)))
+      {
+        return dbRef;
+      }
+    }
+    throw new SiftsException("Could not get source DB Ref");
+  }
+
+  /**
+   * Check that the DBRef Entry is properly populated and is available in this
+   * SiftClient instance
+   * 
+   * @param entry
+   *          - DBRefEntry to validate
+   * @return true validation is successful otherwise false is returned.
+   */
+  boolean isValidDBRefEntry(DBRefEntryI entry)
+  {
+    return entry != null && entry.getAccessionId() != null
+            && isFoundInSiftsEntry(entry.getAccessionId());
+  }
+
+  @Override
+  public HashSet<String> getAllMappingAccession()
+  {
+    HashSet<String> accessions = new HashSet<String>();
+    List<Entity> entities = siftsEntry.getEntity();
+    for (Entity entity : entities)
+    {
+      List<Segment> segments = entity.getSegment();
+      for (Segment segment : segments)
+      {
+        List<MapRegion> mapRegions = segment.getListMapRegion()
+                .getMapRegion();
+        for (MapRegion mapRegion : mapRegions)
+        {
+          accessions
+                  .add(mapRegion.getDb().getDbAccessionId().toLowerCase());
+        }
+      }
+    }
+    return accessions;
+  }
+
+  @Override
+  public StructureMapping getSiftsStructureMapping(SequenceI seq,
+          String pdbFile, String chain) throws SiftsException
+  {
+    structId = (chain == null) ? pdbId : pdbId + "|" + chain;
+    System.out.println("Getting SIFTS mapping for " + structId + ": seq "
+            + seq.getName());
+
+    final StringBuilder mappingDetails = new StringBuilder(128);
+    PrintStream ps = new PrintStream(System.out)
+    {
+      @Override
+      public void print(String x)
+      {
+        mappingDetails.append(x);
+      }
+
+      @Override
+      public void println()
+      {
+        mappingDetails.append(NEWLINE);
+      }
+    };
+    HashMap<Integer, int[]> mapping = getGreedyMapping(chain, seq, ps);
+
+    String mappingOutput = mappingDetails.toString();
+    StructureMapping siftsMapping = new StructureMapping(seq, pdbFile,
+            pdbId, chain, mapping, mappingOutput);
+    return siftsMapping;
+  }
+
+  @Override
+  public HashMap<Integer, int[]> getGreedyMapping(String entityId,
+          SequenceI seq, java.io.PrintStream os) throws SiftsException
+  {
+    List<Integer> omitNonObserved = new ArrayList<Integer>();
+    int nonObservedShiftIndex = 0;
+    // System.out.println("Generating mappings for : " + entityId);
+    Entity entity = null;
+    entity = getEntityById(entityId);
+    String originalSeq = AlignSeq.extractGaps(
+            jalview.util.Comparison.GapChars, seq.getSequenceAsString());
+    HashMap<Integer, int[]> mapping = new HashMap<Integer, int[]>();
+    DBRefEntryI sourceDBRef;
+    sourceDBRef = getValidSourceDBRef(seq);
+    // TODO ensure sequence start/end is in the same coordinate system and
+    // consistent with the choosen sourceDBRef
+
+    // set sequence coordinate system - default value is UniProt
+    if (sourceDBRef.getSource().equalsIgnoreCase(DBRefSource.PDB))
+    {
+      seqCoordSys = CoordinateSys.PDB;
+    }
+
+    HashSet<String> dbRefAccessionIdsString = new HashSet<String>();
+    for (DBRefEntry dbref : seq.getDBRefs())
+    {
+      dbRefAccessionIdsString.add(dbref.getAccessionId().toLowerCase());
+    }
+    dbRefAccessionIdsString.add(sourceDBRef.getAccessionId().toLowerCase());
+
+    curDBRefAccessionIdsString = dbRefAccessionIdsString;
+    curSourceDBRef = sourceDBRef.getAccessionId();
+
+    TreeMap<Integer, String> resNumMap = new TreeMap<Integer, String>();
+    List<Segment> segments = entity.getSegment();
+    SegmentHelperPojo shp = new SegmentHelperPojo(seq, mapping, resNumMap,
+            omitNonObserved, nonObservedShiftIndex);
+    processSegments(segments, shp);
+    try
+    {
+      populateAtomPositions(entityId, mapping);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    if (seqCoordSys == CoordinateSys.UNIPROT)
+    {
+      padWithGaps(resNumMap, omitNonObserved);
+    }
+    int seqStart = UNASSIGNED;
+    int seqEnd = UNASSIGNED;
+    int pdbStart = UNASSIGNED;
+    int pdbEnd = UNASSIGNED;
+
+    if (mapping.isEmpty())
+    {
+      throw new SiftsException("SIFTS mapping failed");
+    }
+
+    Integer[] keys = mapping.keySet().toArray(new Integer[0]);
+    Arrays.sort(keys);
+    seqStart = keys[0];
+    seqEnd = keys[keys.length - 1];
+
+    String matchedSeq = originalSeq;
+    if (seqStart != UNASSIGNED)
+    {
+      pdbStart = mapping.get(seqStart)[PDB_RES_POS];
+      pdbEnd = mapping.get(seqEnd)[PDB_RES_POS];
+      int orignalSeqStart = seq.getStart();
+      if (orignalSeqStart >= 1)
+      {
+        int subSeqStart = (seqStart >= orignalSeqStart) ? seqStart
+                - orignalSeqStart : 0;
+        int subSeqEnd = seqEnd - (orignalSeqStart - 1);
+        subSeqEnd = originalSeq.length() < subSeqEnd ? originalSeq.length()
+                : subSeqEnd;
+        matchedSeq = originalSeq.substring(subSeqStart, subSeqEnd);
+      }
+      else
+      {
+        matchedSeq = originalSeq.substring(1, originalSeq.length());
+      }
+    }
+
+    StringBuilder targetStrucSeqs = new StringBuilder();
+    for (String res : resNumMap.values())
+    {
+      targetStrucSeqs.append(res);
+    }
+
+    if (os != null)
+    {
+      MappingOutputPojo mop = new MappingOutputPojo();
+      mop.setSeqStart(seqStart);
+      mop.setSeqEnd(seqEnd);
+      mop.setSeqName(seq.getName());
+      mop.setSeqResidue(matchedSeq);
+
+      mop.setStrStart(pdbStart);
+      mop.setStrEnd(pdbEnd);
+      mop.setStrName(structId);
+      mop.setStrResidue(targetStrucSeqs.toString());
+
+      mop.setType("pep");
+      os.print(getMappingOutput(mop).toString());
+      os.println();
+    }
+    return mapping;
+  }
+
+  void processSegments(List<Segment> segments, SegmentHelperPojo shp)
+  {
+    SequenceI seq = shp.getSeq();
+    HashMap<Integer, int[]> mapping = shp.getMapping();
+    TreeMap<Integer, String> resNumMap = shp.getResNumMap();
+    List<Integer> omitNonObserved = shp.getOmitNonObserved();
+    int nonObservedShiftIndex = shp.getNonObservedShiftIndex();
+    for (Segment segment : segments)
+    {
+      // System.out.println("Mapping segments : " + segment.getSegId() + "\\"s
+      // + segStartEnd);
+      List<Residue> residues = segment.getListResidue().getResidue();
+      for (Residue residue : residues)
+      {
+        int currSeqIndex = UNASSIGNED;
+        List<CrossRefDb> cRefDbs = residue.getCrossRefDb();
+        CrossRefDb pdbRefDb = null;
+        for (CrossRefDb cRefDb : cRefDbs)
+        {
+          if (cRefDb.getDbSource().equalsIgnoreCase(DBRefSource.PDB))
+          {
+            pdbRefDb = cRefDb;
+          }
+          if (cRefDb.getDbCoordSys()
+                  .equalsIgnoreCase(seqCoordSys.getName())
+                  && isAccessionMatched(cRefDb.getDbAccessionId()))
+          {
+            String resNumIndexString = cRefDb.getDbResNum()
+                    .equalsIgnoreCase("None") ? String.valueOf(UNASSIGNED)
+                    : cRefDb.getDbResNum();
+            try
+            {
+              currSeqIndex = Integer.valueOf(resNumIndexString);
+            } catch (NumberFormatException nfe)
+            {
+              currSeqIndex = Integer.valueOf(resNumIndexString
+                      .split("[a-zA-Z]")[0]);
+              continue;
+            }
+            if (pdbRefDb != null)
+            {
+              break;// exit loop if pdb and uniprot are already found
+            }
+          }
+        }
+        if (currSeqIndex == UNASSIGNED)
+        {
+          continue;
+        }
+        if (currSeqIndex >= seq.getStart() && currSeqIndex <= seq.getEnd())
+        {
+          int resNum;
+          try
+          {
+            resNum = (pdbRefDb == null) ? Integer.valueOf(residue
+                    .getDbResNum()) : Integer.valueOf(pdbRefDb
+                    .getDbResNum());
+          } catch (NumberFormatException nfe)
+          {
+            resNum = (pdbRefDb == null) ? Integer.valueOf(residue
+                    .getDbResNum()) : Integer.valueOf(pdbRefDb
+                    .getDbResNum().split("[a-zA-Z]")[0]);
+            continue;
+          }
+
+          if (isResidueObserved(residue)
+                  || seqCoordSys == CoordinateSys.UNIPROT)
+          {
+            char resCharCode = ResidueProperties
+                    .getSingleCharacterCode(ResidueProperties
+                            .getCanonicalAminoAcid(residue.getDbResName()));
+            resNumMap.put(currSeqIndex, String.valueOf(resCharCode));
+          }
+          else
+          {
+            omitNonObserved.add(currSeqIndex);
+            ++nonObservedShiftIndex;
+          }
+          mapping.put(currSeqIndex - nonObservedShiftIndex, new int[] {
+              Integer.valueOf(resNum), UNASSIGNED });
+        }
+      }
+    }
+  }
+
+  /**
+   * 
+   * @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
+   * 
+   * @param residue
+   * @return
+   */
+  private boolean isResidueObserved(Residue residue)
+  {
+    Set<String> annotations = getResidueAnnotaitons(residue,
+            ResidueDetailType.ANNOTATION);
+    if (annotations == null || annotations.isEmpty())
+    {
+      return true;
+    }
+    for (String annotation : annotations)
+    {
+      if (annotation.equalsIgnoreCase(NOT_OBSERVED))
+      {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Get annotation String for a given residue and annotation type
+   * 
+   * @param residue
+   * @param type
+   * @return
+   */
+  private Set<String> getResidueAnnotaitons(Residue residue,
+          ResidueDetailType type)
+  {
+    HashSet<String> foundAnnotations = new HashSet<String>();
+    List<ResidueDetail> resDetails = residue.getResidueDetail();
+    for (ResidueDetail resDetail : resDetails)
+    {
+      if (resDetail.getProperty().equalsIgnoreCase(type.getCode()))
+      {
+        foundAnnotations.add(resDetail.getContent());
+      }
+    }
+    return foundAnnotations;
+  }
+
+  @Override
+  public boolean isAccessionMatched(String accession)
+  {
+    boolean isStrictMatch = true;
+    return isStrictMatch ? curSourceDBRef.equalsIgnoreCase(accession)
+            : curDBRefAccessionIdsString.contains(accession.toLowerCase());
+  }
+
+  private boolean isFoundInSiftsEntry(String accessionId)
+  {
+    Set<String> siftsDBRefs = getAllMappingAccession();
+    return accessionId != null
+            && siftsDBRefs.contains(accessionId.toLowerCase());
+  }
+
+  /**
+   * Pad omitted residue positions in PDB sequence with gaps
+   * 
+   * @param resNumMap
+   */
+  void padWithGaps(Map<Integer, String> resNumMap,
+          List<Integer> omitNonObserved)
+  {
+    if (resNumMap == null || resNumMap.isEmpty())
+    {
+      return;
+    }
+    Integer[] keys = resNumMap.keySet().toArray(new Integer[0]);
+    // Arrays.sort(keys);
+    int firstIndex = keys[0];
+    int lastIndex = keys[keys.length - 1];
+    // System.out.println("Min value " + firstIndex);
+    // System.out.println("Max value " + lastIndex);
+    for (int x = firstIndex; x <= lastIndex; x++)
+    {
+      if (!resNumMap.containsKey(x) && !omitNonObserved.contains(x))
+      {
+        resNumMap.put(x, "-");
+      }
+    }
+  }
+
+  @Override
+  public Entity getEntityById(String id) throws SiftsException
+  {
+    // Determines an entity to process by performing a heuristic matching of all
+    // Entities with the given chainId and choosing the best matching Entity
+    Entity entity = getEntityByMostOptimalMatchedId(id);
+    if (entity != null)
+    {
+      return entity;
+    }
+    throw new SiftsException("Entity " + id + " not found");
+  }
+
+  /**
+   * This method was added because EntityId is NOT always equal to ChainId.
+   * Hence, it provides the logic to greedily detect the "true" Entity for a
+   * given chainId where discrepancies exist.
+   * 
+   * @param chainId
+   * @return
+   */
+  public Entity getEntityByMostOptimalMatchedId(String chainId)
+  {
+    // System.out.println("---> advanced greedy entityId matching block entered..");
+    List<Entity> entities = siftsEntry.getEntity();
+    SiftsEntitySortPojo[] sPojo = new SiftsEntitySortPojo[entities.size()];
+    int count = 0;
+    for (Entity entity : entities)
+    {
+      sPojo[count] = new SiftsEntitySortPojo();
+      sPojo[count].entityId = entity.getEntityId();
+
+      List<Segment> segments = entity.getSegment();
+      for (Segment segment : segments)
+      {
+        List<Residue> residues = segment.getListResidue().getResidue();
+        for (Residue residue : residues)
+        {
+          List<CrossRefDb> cRefDbs = residue.getCrossRefDb();
+          for (CrossRefDb cRefDb : cRefDbs)
+          {
+            if (!cRefDb.getDbSource().equalsIgnoreCase("PDB"))
+            {
+              continue;
+            }
+            ++sPojo[count].resCount;
+            if (cRefDb.getDbChainId().equalsIgnoreCase(chainId))
+            {
+              ++sPojo[count].chainIdFreq;
+            }
+          }
+        }
+      }
+      sPojo[count].pid = (100 * sPojo[count].chainIdFreq)
+              / sPojo[count].resCount;
+      ++count;
+    }
+    Arrays.sort(sPojo, Collections.reverseOrder());
+    // System.out.println("highest matched entity : " + sPojo[0].entityId);
+    // System.out.println("highest matched pid : " + sPojo[0].pid);
+
+    if (sPojo[0].entityId != null)
+    {
+      if (sPojo[0].pid < 1)
+      {
+        return null;
+      }
+      for (Entity entity : entities)
+      {
+        if (!entity.getEntityId().equalsIgnoreCase(sPojo[0].entityId))
+        {
+          continue;
+        }
+        return entity;
+      }
+    }
+    return null;
+  }
+
+  private class SiftsEntitySortPojo implements
+          Comparable<SiftsEntitySortPojo>
+  {
+    public String entityId;
+
+    public int chainIdFreq;
+
+    public int pid;
+
+    public int resCount;
+
+    @Override
+    public int compareTo(SiftsEntitySortPojo o)
+    {
+      return this.pid - o.pid;
+    }
+  }
+
+  private class SegmentHelperPojo
+  {
+    private SequenceI seq;
+
+    private HashMap<Integer, int[]> mapping;
+
+    private TreeMap<Integer, String> resNumMap;
+
+    private List<Integer> omitNonObserved;
+
+    private int nonObservedShiftIndex;
+
+    public SegmentHelperPojo(SequenceI seq,
+            HashMap<Integer, int[]> mapping,
+            TreeMap<Integer, String> resNumMap,
+            List<Integer> omitNonObserved, int nonObservedShiftIndex)
+    {
+      setSeq(seq);
+      setMapping(mapping);
+      setResNumMap(resNumMap);
+      setOmitNonObserved(omitNonObserved);
+      setNonObservedShiftIndex(nonObservedShiftIndex);
+    }
+
+    public SequenceI getSeq()
+    {
+      return seq;
+    }
+
+    public void setSeq(SequenceI seq)
+    {
+      this.seq = seq;
+    }
+
+    public HashMap<Integer, int[]> getMapping()
+    {
+      return mapping;
+    }
+
+    public void setMapping(HashMap<Integer, int[]> mapping)
+    {
+      this.mapping = mapping;
+    }
+
+    public TreeMap<Integer, String> getResNumMap()
+    {
+      return resNumMap;
+    }
+
+    public void setResNumMap(TreeMap<Integer, String> resNumMap)
+    {
+      this.resNumMap = resNumMap;
+    }
+
+    public List<Integer> getOmitNonObserved()
+    {
+      return omitNonObserved;
+    }
+
+    public void setOmitNonObserved(List<Integer> omitNonObserved)
+    {
+      this.omitNonObserved = omitNonObserved;
+    }
+
+    public int getNonObservedShiftIndex()
+    {
+      return nonObservedShiftIndex;
+    }
+
+    public void setNonObservedShiftIndex(int nonObservedShiftIndex)
+    {
+      this.nonObservedShiftIndex = nonObservedShiftIndex;
+    }
+  }
+
+  @Override
+  public StringBuffer getMappingOutput(MappingOutputPojo mp)
+          throws SiftsException
+  {
+    String seqRes = mp.getSeqResidue();
+    String seqName = mp.getSeqName();
+    int sStart = mp.getSeqStart();
+    int sEnd = mp.getSeqEnd();
+
+    String strRes = mp.getStrResidue();
+    String strName = mp.getStrName();
+    int pdbStart = mp.getStrStart();
+    int pdbEnd = mp.getStrEnd();
+
+    String type = mp.getType();
+
+    int maxid = (seqName.length() >= strName.length()) ? seqName.length()
+            : strName.length();
+    int len = 72 - maxid - 1;
+
+    int nochunks = ((seqRes.length()) / len)
+            + ((seqRes.length()) % len > 0 ? 1 : 0);
+    // output mappings
+    StringBuffer output = new StringBuffer();
+    output.append(NEWLINE);
+    output.append("Sequence \u27f7 Structure mapping details").append(
+            NEWLINE);
+    output.append("Method: SIFTS");
+    output.append(NEWLINE).append(NEWLINE);
+
+    output.append(new Format("%" + maxid + "s").form(seqName));
+    output.append(" :  ");
+    output.append(String.valueOf(sStart));
+    output.append(" - ");
+    output.append(String.valueOf(sEnd));
+    output.append(" Maps to ");
+    output.append(NEWLINE);
+    output.append(new Format("%" + maxid + "s").form(structId));
+    output.append(" :  ");
+    output.append(String.valueOf(pdbStart));
+    output.append(" - ");
+    output.append(String.valueOf(pdbEnd));
+    output.append(NEWLINE).append(NEWLINE);
+
+    int matchedSeqCount = 0;
+    for (int j = 0; j < nochunks; j++)
+    {
+      // Print the first aligned sequence
+      output.append(new Format("%" + (maxid) + "s").form(seqName)).append(
+              " ");
+
+      for (int i = 0; i < len; i++)
+      {
+        if ((i + (j * len)) < seqRes.length())
+        {
+          output.append(seqRes.charAt(i + (j * len)));
+        }
+      }
+
+      output.append(NEWLINE);
+      output.append(new Format("%" + (maxid) + "s").form(" ")).append(" ");
+
+      // Print out the matching chars
+      for (int i = 0; i < len; i++)
+      {
+        try
+        {
+          if ((i + (j * len)) < seqRes.length())
+          {
+            boolean sameChar = Comparison.isSameResidue(
+                    seqRes.charAt(i + (j * len)),
+                    strRes.charAt(i + (j * len)), false);
+            if (sameChar
+                    && !jalview.util.Comparison.isGap(seqRes.charAt(i
+                            + (j * len))))
+            {
+              matchedSeqCount++;
+              output.append("|");
+            }
+            else if (type.equals("pep"))
+            {
+              if (ResidueProperties.getPAM250(seqRes.charAt(i + (j * len)),
+                      strRes.charAt(i + (j * len))) > 0)
+              {
+                output.append(".");
+              }
+              else
+              {
+                output.append(" ");
+              }
+            }
+            else
+            {
+              output.append(" ");
+            }
+          }
+        } catch (IndexOutOfBoundsException e)
+        {
+          continue;
+        }
+      }
+      // Now print the second aligned sequence
+      output = output.append(NEWLINE);
+      output = output.append(new Format("%" + (maxid) + "s").form(strName))
+              .append(" ");
+      for (int i = 0; i < len; i++)
+      {
+        if ((i + (j * len)) < strRes.length())
+        {
+          output.append(strRes.charAt(i + (j * len)));
+        }
+      }
+      output.append(NEWLINE).append(NEWLINE);
+    }
+    float pid = (float) matchedSeqCount / seqRes.length() * 100;
+    if (pid < SiftsSettings.getFailSafePIDThreshold())
+    {
+      throw new SiftsException(">>> Low PID detected for SIFTs mapping...");
+    }
+    output.append("Length of alignment = " + seqRes.length()).append(
+            NEWLINE);
+    output.append(new Format("Percentage ID = %2.2f").form(pid));
+    return output;
+  }
+
+  @Override
+  public int getEntityCount()
+  {
+    return siftsEntry.getEntity().size();
+  }
+
+  @Override
+  public String getDbAccessionId()
+  {
+    return siftsEntry.getDbAccessionId();
+  }
+
+  @Override
+  public String getDbCoordSys()
+  {
+    return siftsEntry.getDbCoordSys();
+  }
+
+  @Override
+  public String getDbSource()
+  {
+    return siftsEntry.getDbSource();
+  }
+
+  @Override
+  public String getDbVersion()
+  {
+    return siftsEntry.getDbVersion();
+  }
+
+}
diff --git a/src/org/jibble/epsgraphics/EpsException.java b/src/jalview/ws/sifts/SiftsException.java
similarity index 74%
copy from src/org/jibble/epsgraphics/EpsException.java
copy to src/jalview/ws/sifts/SiftsException.java
index faaaa07..1f92d98 100644
--- a/src/org/jibble/epsgraphics/EpsException.java
+++ b/src/jalview/ws/sifts/SiftsException.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -18,14 +18,15 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package org.jibble.epsgraphics;
+package jalview.ws.sifts;
 
-public class EpsException extends RuntimeException
+public class SiftsException extends Exception
 {
 
-  public EpsException(String message)
+  private static final long serialVersionUID = 1L;
+
+  public SiftsException(String message)
   {
     super(message);
   }
-
 }
diff --git a/src/jalview/ws/sifts/SiftsSettings.java b/src/jalview/ws/sifts/SiftsSettings.java
new file mode 100644
index 0000000..e4ae610
--- /dev/null
+++ b/src/jalview/ws/sifts/SiftsSettings.java
@@ -0,0 +1,78 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws.sifts;
+
+import java.util.Objects;
+
+public class SiftsSettings
+{
+  private static boolean mapWithSifts = false;
+
+  private static String siftDownloadDirectory;
+
+  private static int cacheThresholdInDays;
+
+  private static int failSafePIDThreshold;
+
+  public static boolean isMapWithSifts()
+  {
+    return mapWithSifts;
+  }
+
+  public static void setMapWithSifts(boolean mapWithSifts)
+  {
+    SiftsSettings.mapWithSifts = mapWithSifts;
+  }
+
+  public static String getSiftDownloadDirectory()
+  {
+    return siftDownloadDirectory;
+  }
+
+  public static void setSiftDownloadDirectory(String siftDownloadDirectory)
+  {
+    SiftsSettings.siftDownloadDirectory = siftDownloadDirectory;
+  }
+
+  public static int getCacheThresholdInDays()
+  {
+    return cacheThresholdInDays;
+  }
+
+  public static void setCacheThresholdInDays(String cacheThresholdInDays)
+  {
+    Objects.requireNonNull(cacheThresholdInDays);
+    SiftsSettings.cacheThresholdInDays = Integer
+            .valueOf(cacheThresholdInDays);
+  }
+
+  public static int getFailSafePIDThreshold()
+  {
+    return failSafePIDThreshold;
+  }
+
+  public static void setFailSafePIDThreshold(String failSafePIDThreshold)
+  {
+    Objects.requireNonNull(failSafePIDThreshold);
+    SiftsSettings.failSafePIDThreshold = Integer
+            .valueOf(failSafePIDThreshold);
+  }
+}
diff --git a/src/jalview/ws/uimodel/AlignAnalysisUIText.java b/src/jalview/ws/uimodel/AlignAnalysisUIText.java
index 09224c9..7c2bb5c 100644
--- a/src/jalview/ws/uimodel/AlignAnalysisUIText.java
+++ b/src/jalview/ws/uimodel/AlignAnalysisUIText.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
@@ -20,7 +20,6 @@
  */
 package jalview.ws.uimodel;
 
-
 public class AlignAnalysisUIText
 {
 
diff --git a/src/jalview/ws/uimodel/PDBRestResponse.java b/src/jalview/ws/uimodel/PDBRestResponse.java
deleted file mode 100644
index bbd5e84..0000000
--- a/src/jalview/ws/uimodel/PDBRestResponse.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-
-package jalview.ws.uimodel;
-
-import jalview.datamodel.SequenceI;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
-
-import java.util.Collection;
-import java.util.Objects;
-
-import javax.swing.table.DefaultTableModel;
-
-import org.json.simple.JSONObject;
-
-/**
- * Represents the response model produced by the PDBRestClient upon successful
- * execution of a given request
- * 
- * @author tcnofoegbu
- *
- */
-public class PDBRestResponse
-{
-  private int numberOfItemsFound;
-
-  private String responseTime;
-
-  private Collection<PDBResponseSummary> searchSummary;
-
-  public int getNumberOfItemsFound()
-  {
-    return numberOfItemsFound;
-  }
-
-  public void setNumberOfItemsFound(int itemFound)
-  {
-    this.numberOfItemsFound = itemFound;
-  }
-
-  public String getResponseTime()
-  {
-    return responseTime;
-  }
-
-  public void setResponseTime(String responseTime)
-  {
-    this.responseTime = responseTime;
-  }
-
-  public Collection<PDBResponseSummary> getSearchSummary()
-  {
-    return searchSummary;
-  }
-
-  public void setSearchSummary(Collection<PDBResponseSummary> searchSummary)
-  {
-    this.searchSummary = searchSummary;
-  }
-
-  /**
-   * Convenience method to obtain a Table model for a given summary List based
-   * on the request parameters
-   * 
-   * @param request
-   *          the PDBRestRequest object which holds useful information for
-   *          creating a table model
-   * @param summariesList
-   *          the summary list which contains the data for populating the
-   *          table's rows
-   * @return the table model which was dynamically generated
-   */
-  public static DefaultTableModel getTableModel(PDBRestRequest request,
-          Collection<PDBResponseSummary> summariesList)
-  {
-    DefaultTableModel tableModel = new DefaultTableModel()
-    {
-      @Override
-      public boolean isCellEditable(int row, int column)
-      {
-        return false;
-      }
-    };
-    if (request.getAssociatedSequence() != null)
-    {
-      tableModel.addColumn("Ref Sequence"); // Create sequence column header if
-      // exists in the request
-    }
-    for (PDBDocField field : request.getWantedFields())
-    {
-      tableModel.addColumn(field.getName()); // Create sequence column header if
-                                             // exists in the request
-    }
-
-    for (PDBResponseSummary res : summariesList)
-    {
-      tableModel.addRow(res.getSummaryData()); // Populate table rows with
-                                               // summary list
-    }
-
-    return tableModel;
-  }
-
-  /**
-   * Model for a unique response summary
-   * 
-   */
-  public class PDBResponseSummary
-  {
-    private String pdbId;
-
-    private Object[] summaryRowData;
-
-    private SequenceI associatedSequence;
-
-    public PDBResponseSummary(JSONObject pdbJsonDoc, PDBRestRequest request)
-    {
-      Collection<PDBDocField> diplayFields = request.getWantedFields();
-      SequenceI associatedSeq = request.getAssociatedSequence();
-      int colCounter = 0;
-      summaryRowData = new Object[(associatedSeq != null) ? diplayFields
-              .size() + 1 : diplayFields.size()];
-      if (associatedSeq != null)
-      {
-        this.associatedSequence = associatedSeq;
-        summaryRowData[0] = associatedSequence;
-        colCounter = 1;
-      }
-
-      for (PDBDocField field : diplayFields)
-      {
-        String fieldData = (pdbJsonDoc.get(field.getCode()) == null) ? ""
-                : pdbJsonDoc.get(field.getCode()).toString();
-        if (field.equals(PDBDocField.PDB_ID))
-        {
-          this.pdbId = fieldData;
-          summaryRowData[colCounter++] = this.pdbId;
-        }
-        else
-        {
-          summaryRowData[colCounter++] = fieldData;
-        }
-      }
-    }
-
-    public String getPdbId()
-    {
-      return pdbId;
-    }
-
-    public void setPdbId(String pdbId)
-    {
-      this.pdbId = pdbId;
-    }
-
-    public Object[] getSummaryData()
-    {
-      return summaryRowData;
-    }
-
-    public void setSummaryData(String[] summaryData)
-    {
-      this.summaryRowData = summaryData;
-    }
-
-    /**
-     * Returns a string representation of this object;
-     */
-    @Override
-    public String toString()
-    {
-      StringBuilder summaryFieldValues = new StringBuilder();
-      for (Object summaryField : summaryRowData)
-      {
-        summaryFieldValues.append(summaryField.toString()).append("\t");
-      }
-      return summaryFieldValues.toString();
-    }
-
-    /**
-     * Returns hash code value for this object
-     */
-    @Override
-    public int hashCode()
-    {
-      return Objects.hash(this.pdbId, this.toString());
-    }
-
-    /**
-     * Indicates whether some object is equal to this one
-     */
-    @Override
-    public boolean equals(Object that)
-    {
-      if (!(that instanceof PDBResponseSummary))
-      {
-        return false;
-      }
-      PDBResponseSummary another = (PDBResponseSummary) that;
-      return this.toString().equals(another.toString());
-    }
-
-  }
-
-}
diff --git a/src/jalview/xml/binding/sifts/Alignment.java b/src/jalview/xml/binding/sifts/Alignment.java
new file mode 100644
index 0000000..650ea19
--- /dev/null
+++ b/src/jalview/xml/binding/sifts/Alignment.java
@@ -0,0 +1,2310 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
+// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
+// Any modifications to this file will be lost upon recompilation of the source schema. 
+// Generated on: 2015.10.09 at 03:18:33 PM BST 
+//
+
+
+package jalview.xml.binding.sifts;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlValue;
+
+
+/**
+ * <p>Java class for anonymous complex type.
+ * 
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ * 
+ * <pre>
+ * <complexType>
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="alignObject" maxOccurs="unbounded">
+ *           <complexType>
+ *             <complexContent>
+ *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                 <sequence>
+ *                   <element name="alignObjectDetail" maxOccurs="unbounded" minOccurs="0">
+ *                     <complexType>
+ *                       <complexContent>
+ *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+ *                         </restriction>
+ *                       </complexContent>
+ *                     </complexType>
+ *                   </element>
+ *                   <element name="sequence" minOccurs="0">
+ *                     <complexType>
+ *                       <complexContent>
+ *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+ *                         </restriction>
+ *                       </complexContent>
+ *                     </complexType>
+ *                   </element>
+ *                 </sequence>
+ *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+ *                 <attribute name="objectVersion" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                 <attribute name="intObjectId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                 <attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *               </restriction>
+ *             </complexContent>
+ *           </complexType>
+ *         </element>
+ *         <element name="score" maxOccurs="unbounded" minOccurs="0">
+ *           <complexType>
+ *             <complexContent>
+ *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                 <attribute name="methodName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                 <attribute name="scoreValue" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *               </restriction>
+ *             </complexContent>
+ *           </complexType>
+ *         </element>
+ *         <element name="block" maxOccurs="unbounded">
+ *           <complexType>
+ *             <complexContent>
+ *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                 <sequence>
+ *                   <element name="segment" maxOccurs="unbounded">
+ *                     <complexType>
+ *                       <complexContent>
+ *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                           <sequence minOccurs="0">
+ *                             <element name="cigar" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}cigarstring"/>
+ *                           </sequence>
+ *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+ *                           <attribute name="intObjectId" use="required" type="{http://www.w3.org/2001/XMLSchema}anySimpleType" />
+ *                           <attribute name="strand" type="{http://www.w3.org/2001/XMLSchema}anySimpleType" />
+ *                         </restriction>
+ *                       </complexContent>
+ *                     </complexType>
+ *                   </element>
+ *                 </sequence>
+ *                 <attribute name="blockScore" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                 <attribute name="blockOrder" use="required" type="{http://www.w3.org/2001/XMLSchema}integer" />
+ *               </restriction>
+ *             </complexContent>
+ *           </complexType>
+ *         </element>
+ *         <element name="geo3d" maxOccurs="unbounded" minOccurs="0">
+ *           <complexType>
+ *             <complexContent>
+ *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                 <sequence>
+ *                   <element name="vector">
+ *                     <complexType>
+ *                       <complexContent>
+ *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                           <attribute name="x" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                           <attribute name="y" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                           <attribute name="z" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                         </restriction>
+ *                       </complexContent>
+ *                     </complexType>
+ *                   </element>
+ *                   <element name="matrix" maxOccurs="unbounded">
+ *                     <complexType>
+ *                       <complexContent>
+ *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                           <sequence>
+ *                             <element name="max11">
+ *                               <complexType>
+ *                                 <complexContent>
+ *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                     <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                                   </restriction>
+ *                                 </complexContent>
+ *                               </complexType>
+ *                             </element>
+ *                             <element name="max12">
+ *                               <complexType>
+ *                                 <complexContent>
+ *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                     <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                                   </restriction>
+ *                                 </complexContent>
+ *                               </complexType>
+ *                             </element>
+ *                             <element name="max13">
+ *                               <complexType>
+ *                                 <complexContent>
+ *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                     <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                                   </restriction>
+ *                                 </complexContent>
+ *                               </complexType>
+ *                             </element>
+ *                             <element name="max21">
+ *                               <complexType>
+ *                                 <complexContent>
+ *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                     <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                                   </restriction>
+ *                                 </complexContent>
+ *                               </complexType>
+ *                             </element>
+ *                             <element name="max22">
+ *                               <complexType>
+ *                                 <complexContent>
+ *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                     <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                                   </restriction>
+ *                                 </complexContent>
+ *                               </complexType>
+ *                             </element>
+ *                             <element name="max23">
+ *                               <complexType>
+ *                                 <complexContent>
+ *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                     <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                                   </restriction>
+ *                                 </complexContent>
+ *                               </complexType>
+ *                             </element>
+ *                             <element name="max31">
+ *                               <complexType>
+ *                                 <complexContent>
+ *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                     <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                                   </restriction>
+ *                                 </complexContent>
+ *                               </complexType>
+ *                             </element>
+ *                             <element name="max32">
+ *                               <complexType>
+ *                                 <complexContent>
+ *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                     <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                                   </restriction>
+ *                                 </complexContent>
+ *                               </complexType>
+ *                             </element>
+ *                             <element name="max33">
+ *                               <complexType>
+ *                                 <complexContent>
+ *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                     <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                                   </restriction>
+ *                                 </complexContent>
+ *                               </complexType>
+ *                             </element>
+ *                           </sequence>
+ *                         </restriction>
+ *                       </complexContent>
+ *                     </complexType>
+ *                   </element>
+ *                 </sequence>
+ *                 <attribute name="intObjectId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *               </restriction>
+ *             </complexContent>
+ *           </complexType>
+ *         </element>
+ *       </sequence>
+ *       <attribute name="alignType" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * </pre>
+ * 
+ * 
+ */
+ at XmlAccessorType(XmlAccessType.FIELD)
+ at XmlType(name = "", propOrder = {
+    "alignObject",
+    "score",
+    "block",
+    "geo3D"
+})
+ at XmlRootElement(name = "alignment")
+public class Alignment {
+
+    @XmlElement(required = true)
+    protected List<Alignment.AlignObject> alignObject;
+    protected List<Alignment.Score> score;
+    @XmlElement(required = true)
+    protected List<Alignment.Block> block;
+    @XmlElement(name = "geo3d")
+    protected List<Alignment.Geo3D> geo3D;
+    @XmlAttribute(name = "alignType", required = true)
+    protected String alignType;
+
+    /**
+     * Gets the value of the alignObject property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the alignObject property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getAlignObject().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link Alignment.AlignObject }
+     * 
+     * 
+     */
+    public List<Alignment.AlignObject> getAlignObject() {
+        if (alignObject == null) {
+            alignObject = new ArrayList<Alignment.AlignObject>();
+        }
+        return this.alignObject;
+    }
+
+    /**
+     * Gets the value of the score property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the score property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getScore().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link Alignment.Score }
+     * 
+     * 
+     */
+    public List<Alignment.Score> getScore() {
+        if (score == null) {
+            score = new ArrayList<Alignment.Score>();
+        }
+        return this.score;
+    }
+
+    /**
+     * Gets the value of the block property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the block property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getBlock().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link Alignment.Block }
+     * 
+     * 
+     */
+    public List<Alignment.Block> getBlock() {
+        if (block == null) {
+            block = new ArrayList<Alignment.Block>();
+        }
+        return this.block;
+    }
+
+    /**
+     * Gets the value of the geo3D property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the geo3D property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getGeo3D().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link Alignment.Geo3D }
+     * 
+     * 
+     */
+    public List<Alignment.Geo3D> getGeo3D() {
+        if (geo3D == null) {
+            geo3D = new ArrayList<Alignment.Geo3D>();
+        }
+        return this.geo3D;
+    }
+
+    /**
+     * Gets the value of the alignType property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link String }
+     *     
+     */
+    public String getAlignType() {
+        return alignType;
+    }
+
+    /**
+     * Sets the value of the alignType property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *     
+     */
+    public void setAlignType(String value) {
+        this.alignType = value;
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * <complexType>
+     *   <complexContent>
+     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       <sequence>
+     *         <element name="alignObjectDetail" maxOccurs="unbounded" minOccurs="0">
+     *           <complexType>
+     *             <complexContent>
+     *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+     *               </restriction>
+     *             </complexContent>
+     *           </complexType>
+     *         </element>
+     *         <element name="sequence" minOccurs="0">
+     *           <complexType>
+     *             <complexContent>
+     *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+     *               </restriction>
+     *             </complexContent>
+     *           </complexType>
+     *         </element>
+     *       </sequence>
+     *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+     *       <attribute name="objectVersion" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       <attribute name="intObjectId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       <attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *     </restriction>
+     *   </complexContent>
+     * </complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "alignObjectDetail",
+        "sequence"
+    })
+    public static class AlignObject {
+
+        protected List<Alignment.AlignObject.AlignObjectDetail> alignObjectDetail;
+        protected Alignment.AlignObject.Sequence sequence;
+        @XmlAttribute(name = "objectVersion", required = true)
+        protected String objectVersion;
+        @XmlAttribute(name = "intObjectId", required = true)
+        protected String intObjectId;
+        @XmlAttribute(name = "type")
+        protected String type;
+        @XmlAttribute(name = "dbSource", required = true)
+        protected String dbSource;
+        @XmlAttribute(name = "dbCoordSys", required = true)
+        protected String dbCoordSys;
+        @XmlAttribute(name = "dbAccessionId", required = true)
+        protected String dbAccessionId;
+        @XmlAttribute(name = "dbEvidence")
+        protected String dbEvidence;
+        @XmlAttribute(name = "dbVersion")
+        protected String dbVersion;
+
+        /**
+         * Gets the value of the alignObjectDetail property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the alignObjectDetail property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getAlignObjectDetail().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link Alignment.AlignObject.AlignObjectDetail }
+         * 
+         * 
+         */
+        public List<Alignment.AlignObject.AlignObjectDetail> getAlignObjectDetail() {
+            if (alignObjectDetail == null) {
+                alignObjectDetail = new ArrayList<Alignment.AlignObject.AlignObjectDetail>();
+            }
+            return this.alignObjectDetail;
+        }
+
+        /**
+         * Gets the value of the sequence property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Alignment.AlignObject.Sequence }
+         *     
+         */
+        public Alignment.AlignObject.Sequence getSequence() {
+            return sequence;
+        }
+
+        /**
+         * Sets the value of the sequence property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Alignment.AlignObject.Sequence }
+         *     
+         */
+        public void setSequence(Alignment.AlignObject.Sequence value) {
+            this.sequence = value;
+        }
+
+        /**
+         * Gets the value of the objectVersion property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getObjectVersion() {
+            return objectVersion;
+        }
+
+        /**
+         * Sets the value of the objectVersion property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setObjectVersion(String value) {
+            this.objectVersion = value;
+        }
+
+        /**
+         * Gets the value of the intObjectId property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getIntObjectId() {
+            return intObjectId;
+        }
+
+        /**
+         * Sets the value of the intObjectId property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setIntObjectId(String value) {
+            this.intObjectId = value;
+        }
+
+        /**
+         * Gets the value of the type property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getType() {
+            return type;
+        }
+
+        /**
+         * Sets the value of the type property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setType(String value) {
+            this.type = value;
+        }
+
+        /**
+         * Gets the value of the dbSource property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getDbSource() {
+            return dbSource;
+        }
+
+        /**
+         * Sets the value of the dbSource property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setDbSource(String value) {
+            this.dbSource = value;
+        }
+
+        /**
+         * Gets the value of the dbCoordSys property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getDbCoordSys() {
+            return dbCoordSys;
+        }
+
+        /**
+         * Sets the value of the dbCoordSys property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setDbCoordSys(String value) {
+            this.dbCoordSys = value;
+        }
+
+        /**
+         * Gets the value of the dbAccessionId property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getDbAccessionId() {
+            return dbAccessionId;
+        }
+
+        /**
+         * Sets the value of the dbAccessionId property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setDbAccessionId(String value) {
+            this.dbAccessionId = value;
+        }
+
+        /**
+         * Gets the value of the dbEvidence property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getDbEvidence() {
+            return dbEvidence;
+        }
+
+        /**
+         * Sets the value of the dbEvidence property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setDbEvidence(String value) {
+            this.dbEvidence = value;
+        }
+
+        /**
+         * Gets the value of the dbVersion property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getDbVersion() {
+            return dbVersion;
+        }
+
+        /**
+         * Sets the value of the dbVersion property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setDbVersion(String value) {
+            this.dbVersion = value;
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * <complexType>
+         *   <complexContent>
+         *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+         *     </restriction>
+         *   </complexContent>
+         * </complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "", propOrder = {
+            "content"
+        })
+        public static class AlignObjectDetail {
+
+            @XmlValue
+            protected String content;
+            @XmlAttribute(name = "dbSource")
+            protected String dbSource;
+            @XmlAttribute(name = "property", required = true)
+            protected String property;
+
+            /**
+             * Gets the value of the content property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getContent() {
+                return content;
+            }
+
+            /**
+             * Sets the value of the content property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setContent(String value) {
+                this.content = value;
+            }
+
+            /**
+             * Gets the value of the dbSource property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getDbSource() {
+                return dbSource;
+            }
+
+            /**
+             * Sets the value of the dbSource property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setDbSource(String value) {
+                this.dbSource = value;
+            }
+
+            /**
+             * Gets the value of the property property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getProperty() {
+                return property;
+            }
+
+            /**
+             * Sets the value of the property property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setProperty(String value) {
+                this.property = value;
+            }
+
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * <complexType>
+         *   <complexContent>
+         *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+         *     </restriction>
+         *   </complexContent>
+         * </complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "", propOrder = {
+            "content"
+        })
+        public static class Sequence {
+
+            @XmlValue
+            protected String content;
+            @XmlAttribute(name = "start")
+            protected String start;
+            @XmlAttribute(name = "end")
+            protected String end;
+
+            /**
+             * Gets the value of the content property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getContent() {
+                return content;
+            }
+
+            /**
+             * Sets the value of the content property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setContent(String value) {
+                this.content = value;
+            }
+
+            /**
+             * Gets the value of the start property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getStart() {
+                return start;
+            }
+
+            /**
+             * Sets the value of the start property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setStart(String value) {
+                this.start = value;
+            }
+
+            /**
+             * Gets the value of the end property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getEnd() {
+                return end;
+            }
+
+            /**
+             * Sets the value of the end property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setEnd(String value) {
+                this.end = value;
+            }
+
+        }
+
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * <complexType>
+     *   <complexContent>
+     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       <sequence>
+     *         <element name="segment" maxOccurs="unbounded">
+     *           <complexType>
+     *             <complexContent>
+     *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                 <sequence minOccurs="0">
+     *                   <element name="cigar" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}cigarstring"/>
+     *                 </sequence>
+     *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+     *                 <attribute name="intObjectId" use="required" type="{http://www.w3.org/2001/XMLSchema}anySimpleType" />
+     *                 <attribute name="strand" type="{http://www.w3.org/2001/XMLSchema}anySimpleType" />
+     *               </restriction>
+     *             </complexContent>
+     *           </complexType>
+     *         </element>
+     *       </sequence>
+     *       <attribute name="blockScore" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       <attribute name="blockOrder" use="required" type="{http://www.w3.org/2001/XMLSchema}integer" />
+     *     </restriction>
+     *   </complexContent>
+     * </complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "segment"
+    })
+    public static class Block {
+
+        @XmlElement(required = true)
+        protected List<Alignment.Block.Segment> segment;
+        @XmlAttribute(name = "blockScore")
+        protected String blockScore;
+        @XmlAttribute(name = "blockOrder", required = true)
+        protected BigInteger blockOrder;
+
+        /**
+         * Gets the value of the segment property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the segment property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getSegment().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link Alignment.Block.Segment }
+         * 
+         * 
+         */
+        public List<Alignment.Block.Segment> getSegment() {
+            if (segment == null) {
+                segment = new ArrayList<Alignment.Block.Segment>();
+            }
+            return this.segment;
+        }
+
+        /**
+         * Gets the value of the blockScore property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getBlockScore() {
+            return blockScore;
+        }
+
+        /**
+         * Sets the value of the blockScore property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setBlockScore(String value) {
+            this.blockScore = value;
+        }
+
+        /**
+         * Gets the value of the blockOrder property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link BigInteger }
+         *     
+         */
+        public BigInteger getBlockOrder() {
+            return blockOrder;
+        }
+
+        /**
+         * Sets the value of the blockOrder property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link BigInteger }
+         *     
+         */
+        public void setBlockOrder(BigInteger value) {
+            this.blockOrder = value;
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * <complexType>
+         *   <complexContent>
+         *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *       <sequence minOccurs="0">
+         *         <element name="cigar" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}cigarstring"/>
+         *       </sequence>
+         *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+         *       <attribute name="intObjectId" use="required" type="{http://www.w3.org/2001/XMLSchema}anySimpleType" />
+         *       <attribute name="strand" type="{http://www.w3.org/2001/XMLSchema}anySimpleType" />
+         *     </restriction>
+         *   </complexContent>
+         * </complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "", propOrder = {
+            "cigar"
+        })
+        public static class Segment {
+
+            protected String cigar;
+            @XmlAttribute(name = "intObjectId", required = true)
+            @XmlSchemaType(name = "anySimpleType")
+            protected String intObjectId;
+            @XmlAttribute(name = "strand")
+            @XmlSchemaType(name = "anySimpleType")
+            protected String strand;
+            @XmlAttribute(name = "start")
+            protected String start;
+            @XmlAttribute(name = "end")
+            protected String end;
+
+            /**
+             * Gets the value of the cigar property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getCigar() {
+                return cigar;
+            }
+
+            /**
+             * Sets the value of the cigar property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setCigar(String value) {
+                this.cigar = value;
+            }
+
+            /**
+             * Gets the value of the intObjectId property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getIntObjectId() {
+                return intObjectId;
+            }
+
+            /**
+             * Sets the value of the intObjectId property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setIntObjectId(String value) {
+                this.intObjectId = value;
+            }
+
+            /**
+             * Gets the value of the strand property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getStrand() {
+                return strand;
+            }
+
+            /**
+             * Sets the value of the strand property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setStrand(String value) {
+                this.strand = value;
+            }
+
+            /**
+             * Gets the value of the start property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getStart() {
+                return start;
+            }
+
+            /**
+             * Sets the value of the start property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setStart(String value) {
+                this.start = value;
+            }
+
+            /**
+             * Gets the value of the end property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getEnd() {
+                return end;
+            }
+
+            /**
+             * Sets the value of the end property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setEnd(String value) {
+                this.end = value;
+            }
+
+        }
+
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * <complexType>
+     *   <complexContent>
+     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       <sequence>
+     *         <element name="vector">
+     *           <complexType>
+     *             <complexContent>
+     *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                 <attribute name="x" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                 <attribute name="y" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                 <attribute name="z" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *               </restriction>
+     *             </complexContent>
+     *           </complexType>
+     *         </element>
+     *         <element name="matrix" maxOccurs="unbounded">
+     *           <complexType>
+     *             <complexContent>
+     *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                 <sequence>
+     *                   <element name="max11">
+     *                     <complexType>
+     *                       <complexContent>
+     *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                         </restriction>
+     *                       </complexContent>
+     *                     </complexType>
+     *                   </element>
+     *                   <element name="max12">
+     *                     <complexType>
+     *                       <complexContent>
+     *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                         </restriction>
+     *                       </complexContent>
+     *                     </complexType>
+     *                   </element>
+     *                   <element name="max13">
+     *                     <complexType>
+     *                       <complexContent>
+     *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                         </restriction>
+     *                       </complexContent>
+     *                     </complexType>
+     *                   </element>
+     *                   <element name="max21">
+     *                     <complexType>
+     *                       <complexContent>
+     *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                         </restriction>
+     *                       </complexContent>
+     *                     </complexType>
+     *                   </element>
+     *                   <element name="max22">
+     *                     <complexType>
+     *                       <complexContent>
+     *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                         </restriction>
+     *                       </complexContent>
+     *                     </complexType>
+     *                   </element>
+     *                   <element name="max23">
+     *                     <complexType>
+     *                       <complexContent>
+     *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                         </restriction>
+     *                       </complexContent>
+     *                     </complexType>
+     *                   </element>
+     *                   <element name="max31">
+     *                     <complexType>
+     *                       <complexContent>
+     *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                         </restriction>
+     *                       </complexContent>
+     *                     </complexType>
+     *                   </element>
+     *                   <element name="max32">
+     *                     <complexType>
+     *                       <complexContent>
+     *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                         </restriction>
+     *                       </complexContent>
+     *                     </complexType>
+     *                   </element>
+     *                   <element name="max33">
+     *                     <complexType>
+     *                       <complexContent>
+     *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                         </restriction>
+     *                       </complexContent>
+     *                     </complexType>
+     *                   </element>
+     *                 </sequence>
+     *               </restriction>
+     *             </complexContent>
+     *           </complexType>
+     *         </element>
+     *       </sequence>
+     *       <attribute name="intObjectId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *     </restriction>
+     *   </complexContent>
+     * </complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "vector",
+        "matrix"
+    })
+    public static class Geo3D {
+
+        @XmlElement(required = true)
+        protected Alignment.Geo3D.Vector vector;
+        @XmlElement(required = true)
+        protected List<Alignment.Geo3D.Matrix> matrix;
+        @XmlAttribute(name = "intObjectId", required = true)
+        protected String intObjectId;
+
+        /**
+         * Gets the value of the vector property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Alignment.Geo3D.Vector }
+         *     
+         */
+        public Alignment.Geo3D.Vector getVector() {
+            return vector;
+        }
+
+        /**
+         * Sets the value of the vector property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Alignment.Geo3D.Vector }
+         *     
+         */
+        public void setVector(Alignment.Geo3D.Vector value) {
+            this.vector = value;
+        }
+
+        /**
+         * Gets the value of the matrix property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the matrix property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getMatrix().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link Alignment.Geo3D.Matrix }
+         * 
+         * 
+         */
+        public List<Alignment.Geo3D.Matrix> getMatrix() {
+            if (matrix == null) {
+                matrix = new ArrayList<Alignment.Geo3D.Matrix>();
+            }
+            return this.matrix;
+        }
+
+        /**
+         * Gets the value of the intObjectId property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getIntObjectId() {
+            return intObjectId;
+        }
+
+        /**
+         * Sets the value of the intObjectId property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setIntObjectId(String value) {
+            this.intObjectId = value;
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * <complexType>
+         *   <complexContent>
+         *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *       <sequence>
+         *         <element name="max11">
+         *           <complexType>
+         *             <complexContent>
+         *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *               </restriction>
+         *             </complexContent>
+         *           </complexType>
+         *         </element>
+         *         <element name="max12">
+         *           <complexType>
+         *             <complexContent>
+         *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *               </restriction>
+         *             </complexContent>
+         *           </complexType>
+         *         </element>
+         *         <element name="max13">
+         *           <complexType>
+         *             <complexContent>
+         *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *               </restriction>
+         *             </complexContent>
+         *           </complexType>
+         *         </element>
+         *         <element name="max21">
+         *           <complexType>
+         *             <complexContent>
+         *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *               </restriction>
+         *             </complexContent>
+         *           </complexType>
+         *         </element>
+         *         <element name="max22">
+         *           <complexType>
+         *             <complexContent>
+         *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *               </restriction>
+         *             </complexContent>
+         *           </complexType>
+         *         </element>
+         *         <element name="max23">
+         *           <complexType>
+         *             <complexContent>
+         *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *               </restriction>
+         *             </complexContent>
+         *           </complexType>
+         *         </element>
+         *         <element name="max31">
+         *           <complexType>
+         *             <complexContent>
+         *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *               </restriction>
+         *             </complexContent>
+         *           </complexType>
+         *         </element>
+         *         <element name="max32">
+         *           <complexType>
+         *             <complexContent>
+         *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *               </restriction>
+         *             </complexContent>
+         *           </complexType>
+         *         </element>
+         *         <element name="max33">
+         *           <complexType>
+         *             <complexContent>
+         *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *               </restriction>
+         *             </complexContent>
+         *           </complexType>
+         *         </element>
+         *       </sequence>
+         *     </restriction>
+         *   </complexContent>
+         * </complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "", propOrder = {
+            "max11",
+            "max12",
+            "max13",
+            "max21",
+            "max22",
+            "max23",
+            "max31",
+            "max32",
+            "max33"
+        })
+        public static class Matrix {
+
+            @XmlElement(required = true)
+            protected Alignment.Geo3D.Matrix.Max11 max11;
+            @XmlElement(required = true)
+            protected Alignment.Geo3D.Matrix.Max12 max12;
+            @XmlElement(required = true)
+            protected Alignment.Geo3D.Matrix.Max13 max13;
+            @XmlElement(required = true)
+            protected Alignment.Geo3D.Matrix.Max21 max21;
+            @XmlElement(required = true)
+            protected Alignment.Geo3D.Matrix.Max22 max22;
+            @XmlElement(required = true)
+            protected Alignment.Geo3D.Matrix.Max23 max23;
+            @XmlElement(required = true)
+            protected Alignment.Geo3D.Matrix.Max31 max31;
+            @XmlElement(required = true)
+            protected Alignment.Geo3D.Matrix.Max32 max32;
+            @XmlElement(required = true)
+            protected Alignment.Geo3D.Matrix.Max33 max33;
+
+            /**
+             * Gets the value of the max11 property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Alignment.Geo3D.Matrix.Max11 }
+             *     
+             */
+            public Alignment.Geo3D.Matrix.Max11 getMax11() {
+                return max11;
+            }
+
+            /**
+             * Sets the value of the max11 property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Alignment.Geo3D.Matrix.Max11 }
+             *     
+             */
+            public void setMax11(Alignment.Geo3D.Matrix.Max11 value) {
+                this.max11 = value;
+            }
+
+            /**
+             * Gets the value of the max12 property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Alignment.Geo3D.Matrix.Max12 }
+             *     
+             */
+            public Alignment.Geo3D.Matrix.Max12 getMax12() {
+                return max12;
+            }
+
+            /**
+             * Sets the value of the max12 property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Alignment.Geo3D.Matrix.Max12 }
+             *     
+             */
+            public void setMax12(Alignment.Geo3D.Matrix.Max12 value) {
+                this.max12 = value;
+            }
+
+            /**
+             * Gets the value of the max13 property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Alignment.Geo3D.Matrix.Max13 }
+             *     
+             */
+            public Alignment.Geo3D.Matrix.Max13 getMax13() {
+                return max13;
+            }
+
+            /**
+             * Sets the value of the max13 property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Alignment.Geo3D.Matrix.Max13 }
+             *     
+             */
+            public void setMax13(Alignment.Geo3D.Matrix.Max13 value) {
+                this.max13 = value;
+            }
+
+            /**
+             * Gets the value of the max21 property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Alignment.Geo3D.Matrix.Max21 }
+             *     
+             */
+            public Alignment.Geo3D.Matrix.Max21 getMax21() {
+                return max21;
+            }
+
+            /**
+             * Sets the value of the max21 property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Alignment.Geo3D.Matrix.Max21 }
+             *     
+             */
+            public void setMax21(Alignment.Geo3D.Matrix.Max21 value) {
+                this.max21 = value;
+            }
+
+            /**
+             * Gets the value of the max22 property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Alignment.Geo3D.Matrix.Max22 }
+             *     
+             */
+            public Alignment.Geo3D.Matrix.Max22 getMax22() {
+                return max22;
+            }
+
+            /**
+             * Sets the value of the max22 property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Alignment.Geo3D.Matrix.Max22 }
+             *     
+             */
+            public void setMax22(Alignment.Geo3D.Matrix.Max22 value) {
+                this.max22 = value;
+            }
+
+            /**
+             * Gets the value of the max23 property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Alignment.Geo3D.Matrix.Max23 }
+             *     
+             */
+            public Alignment.Geo3D.Matrix.Max23 getMax23() {
+                return max23;
+            }
+
+            /**
+             * Sets the value of the max23 property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Alignment.Geo3D.Matrix.Max23 }
+             *     
+             */
+            public void setMax23(Alignment.Geo3D.Matrix.Max23 value) {
+                this.max23 = value;
+            }
+
+            /**
+             * Gets the value of the max31 property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Alignment.Geo3D.Matrix.Max31 }
+             *     
+             */
+            public Alignment.Geo3D.Matrix.Max31 getMax31() {
+                return max31;
+            }
+
+            /**
+             * Sets the value of the max31 property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Alignment.Geo3D.Matrix.Max31 }
+             *     
+             */
+            public void setMax31(Alignment.Geo3D.Matrix.Max31 value) {
+                this.max31 = value;
+            }
+
+            /**
+             * Gets the value of the max32 property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Alignment.Geo3D.Matrix.Max32 }
+             *     
+             */
+            public Alignment.Geo3D.Matrix.Max32 getMax32() {
+                return max32;
+            }
+
+            /**
+             * Sets the value of the max32 property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Alignment.Geo3D.Matrix.Max32 }
+             *     
+             */
+            public void setMax32(Alignment.Geo3D.Matrix.Max32 value) {
+                this.max32 = value;
+            }
+
+            /**
+             * Gets the value of the max33 property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Alignment.Geo3D.Matrix.Max33 }
+             *     
+             */
+            public Alignment.Geo3D.Matrix.Max33 getMax33() {
+                return max33;
+            }
+
+            /**
+             * Sets the value of the max33 property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Alignment.Geo3D.Matrix.Max33 }
+             *     
+             */
+            public void setMax33(Alignment.Geo3D.Matrix.Max33 value) {
+                this.max33 = value;
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * <complexType>
+             *   <complexContent>
+             *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+             *     </restriction>
+             *   </complexContent>
+             * </complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "")
+            public static class Max11 {
+
+                @XmlAttribute(name = "coord", required = true)
+                protected float coord;
+
+                /**
+                 * Gets the value of the coord property.
+                 * 
+                 */
+                public float getCoord() {
+                    return coord;
+                }
+
+                /**
+                 * Sets the value of the coord property.
+                 * 
+                 */
+                public void setCoord(float value) {
+                    this.coord = value;
+                }
+
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * <complexType>
+             *   <complexContent>
+             *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+             *     </restriction>
+             *   </complexContent>
+             * </complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "")
+            public static class Max12 {
+
+                @XmlAttribute(name = "coord", required = true)
+                protected float coord;
+
+                /**
+                 * Gets the value of the coord property.
+                 * 
+                 */
+                public float getCoord() {
+                    return coord;
+                }
+
+                /**
+                 * Sets the value of the coord property.
+                 * 
+                 */
+                public void setCoord(float value) {
+                    this.coord = value;
+                }
+
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * <complexType>
+             *   <complexContent>
+             *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+             *     </restriction>
+             *   </complexContent>
+             * </complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "")
+            public static class Max13 {
+
+                @XmlAttribute(name = "coord", required = true)
+                protected float coord;
+
+                /**
+                 * Gets the value of the coord property.
+                 * 
+                 */
+                public float getCoord() {
+                    return coord;
+                }
+
+                /**
+                 * Sets the value of the coord property.
+                 * 
+                 */
+                public void setCoord(float value) {
+                    this.coord = value;
+                }
+
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * <complexType>
+             *   <complexContent>
+             *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+             *     </restriction>
+             *   </complexContent>
+             * </complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "")
+            public static class Max21 {
+
+                @XmlAttribute(name = "coord", required = true)
+                protected float coord;
+
+                /**
+                 * Gets the value of the coord property.
+                 * 
+                 */
+                public float getCoord() {
+                    return coord;
+                }
+
+                /**
+                 * Sets the value of the coord property.
+                 * 
+                 */
+                public void setCoord(float value) {
+                    this.coord = value;
+                }
+
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * <complexType>
+             *   <complexContent>
+             *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+             *     </restriction>
+             *   </complexContent>
+             * </complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "")
+            public static class Max22 {
+
+                @XmlAttribute(name = "coord", required = true)
+                protected float coord;
+
+                /**
+                 * Gets the value of the coord property.
+                 * 
+                 */
+                public float getCoord() {
+                    return coord;
+                }
+
+                /**
+                 * Sets the value of the coord property.
+                 * 
+                 */
+                public void setCoord(float value) {
+                    this.coord = value;
+                }
+
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * <complexType>
+             *   <complexContent>
+             *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+             *     </restriction>
+             *   </complexContent>
+             * </complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "")
+            public static class Max23 {
+
+                @XmlAttribute(name = "coord", required = true)
+                protected float coord;
+
+                /**
+                 * Gets the value of the coord property.
+                 * 
+                 */
+                public float getCoord() {
+                    return coord;
+                }
+
+                /**
+                 * Sets the value of the coord property.
+                 * 
+                 */
+                public void setCoord(float value) {
+                    this.coord = value;
+                }
+
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * <complexType>
+             *   <complexContent>
+             *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+             *     </restriction>
+             *   </complexContent>
+             * </complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "")
+            public static class Max31 {
+
+                @XmlAttribute(name = "coord", required = true)
+                protected float coord;
+
+                /**
+                 * Gets the value of the coord property.
+                 * 
+                 */
+                public float getCoord() {
+                    return coord;
+                }
+
+                /**
+                 * Sets the value of the coord property.
+                 * 
+                 */
+                public void setCoord(float value) {
+                    this.coord = value;
+                }
+
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * <complexType>
+             *   <complexContent>
+             *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+             *     </restriction>
+             *   </complexContent>
+             * </complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "")
+            public static class Max32 {
+
+                @XmlAttribute(name = "coord", required = true)
+                protected float coord;
+
+                /**
+                 * Gets the value of the coord property.
+                 * 
+                 */
+                public float getCoord() {
+                    return coord;
+                }
+
+                /**
+                 * Sets the value of the coord property.
+                 * 
+                 */
+                public void setCoord(float value) {
+                    this.coord = value;
+                }
+
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * <complexType>
+             *   <complexContent>
+             *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       <attribute name="coord" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+             *     </restriction>
+             *   </complexContent>
+             * </complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "")
+            public static class Max33 {
+
+                @XmlAttribute(name = "coord", required = true)
+                protected float coord;
+
+                /**
+                 * Gets the value of the coord property.
+                 * 
+                 */
+                public float getCoord() {
+                    return coord;
+                }
+
+                /**
+                 * Sets the value of the coord property.
+                 * 
+                 */
+                public void setCoord(float value) {
+                    this.coord = value;
+                }
+
+            }
+
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * <complexType>
+         *   <complexContent>
+         *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *       <attribute name="x" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *       <attribute name="y" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *       <attribute name="z" use="required" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *     </restriction>
+         *   </complexContent>
+         * </complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "")
+        public static class Vector {
+
+            @XmlAttribute(name = "x", required = true)
+            protected float x;
+            @XmlAttribute(name = "y", required = true)
+            protected float y;
+            @XmlAttribute(name = "z", required = true)
+            protected float z;
+
+            /**
+             * Gets the value of the x property.
+             * 
+             */
+            public float getX() {
+                return x;
+            }
+
+            /**
+             * Sets the value of the x property.
+             * 
+             */
+            public void setX(float value) {
+                this.x = value;
+            }
+
+            /**
+             * Gets the value of the y property.
+             * 
+             */
+            public float getY() {
+                return y;
+            }
+
+            /**
+             * Sets the value of the y property.
+             * 
+             */
+            public void setY(float value) {
+                this.y = value;
+            }
+
+            /**
+             * Gets the value of the z property.
+             * 
+             */
+            public float getZ() {
+                return z;
+            }
+
+            /**
+             * Sets the value of the z property.
+             * 
+             */
+            public void setZ(float value) {
+                this.z = value;
+            }
+
+        }
+
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * <complexType>
+     *   <complexContent>
+     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       <attribute name="methodName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       <attribute name="scoreValue" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *     </restriction>
+     *   </complexContent>
+     * </complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "")
+    public static class Score {
+
+        @XmlAttribute(name = "methodName", required = true)
+        protected String methodName;
+        @XmlAttribute(name = "scoreValue", required = true)
+        protected String scoreValue;
+
+        /**
+         * Gets the value of the methodName property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getMethodName() {
+            return methodName;
+        }
+
+        /**
+         * Sets the value of the methodName property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setMethodName(String value) {
+            this.methodName = value;
+        }
+
+        /**
+         * Gets the value of the scoreValue property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getScoreValue() {
+            return scoreValue;
+        }
+
+        /**
+         * Sets the value of the scoreValue property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setScoreValue(String value) {
+            this.scoreValue = value;
+        }
+
+    }
+
+}
diff --git a/src/jalview/xml/binding/sifts/EntityType.java b/src/jalview/xml/binding/sifts/EntityType.java
new file mode 100644
index 0000000..f74da5a
--- /dev/null
+++ b/src/jalview/xml/binding/sifts/EntityType.java
@@ -0,0 +1,62 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
+// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
+// Any modifications to this file will be lost upon recompilation of the source schema. 
+// Generated on: 2015.10.09 at 03:18:33 PM BST 
+//
+
+
+package jalview.xml.binding.sifts;
+
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlEnumValue;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ * <p>Java class for entityType.
+ * 
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ * <p>
+ * <pre>
+ * <simpleType name="entityType">
+ *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
+ *     <enumeration value="protein"/>
+ *     <enumeration value="RNA"/>
+ *     <enumeration value="DNA"/>
+ *     <enumeration value="domain"/>
+ *   </restriction>
+ * </simpleType>
+ * </pre>
+ * 
+ */
+ at XmlType(name = "entityType", namespace = "http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd")
+ at XmlEnum
+public enum EntityType {
+
+    @XmlEnumValue("protein")
+    PROTEIN("protein"),
+    RNA("RNA"),
+    DNA("DNA"),
+    @XmlEnumValue("domain")
+    DOMAIN("domain");
+    private final String value;
+
+    EntityType(String v) {
+        value = v;
+    }
+
+    public String value() {
+        return value;
+    }
+
+    public static EntityType fromValue(String v) {
+        for (EntityType c: EntityType.values()) {
+            if (c.value.equals(v)) {
+                return c;
+            }
+        }
+        throw new IllegalArgumentException(v);
+    }
+
+}
diff --git a/src/jalview/xml/binding/sifts/Entry.java b/src/jalview/xml/binding/sifts/Entry.java
new file mode 100644
index 0000000..7429059
--- /dev/null
+++ b/src/jalview/xml/binding/sifts/Entry.java
@@ -0,0 +1,2818 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
+// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
+// Any modifications to this file will be lost upon recompilation of the source schema. 
+// Generated on: 2015.10.09 at 03:18:33 PM BST 
+//
+
+
+package jalview.xml.binding.sifts;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlValue;
+import javax.xml.datatype.XMLGregorianCalendar;
+
+
+/**
+ * <p>Java class for anonymous complex type.
+ * 
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ * 
+ * <pre>
+ * <complexType>
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="listDB">
+ *           <complexType>
+ *             <complexContent>
+ *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                 <sequence maxOccurs="unbounded">
+ *                   <element name="db">
+ *                     <complexType>
+ *                       <complexContent>
+ *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}listdbRef"/>
+ *                         </restriction>
+ *                       </complexContent>
+ *                     </complexType>
+ *                   </element>
+ *                 </sequence>
+ *               </restriction>
+ *             </complexContent>
+ *           </complexType>
+ *         </element>
+ *         <element name="entryDetail" maxOccurs="unbounded" minOccurs="0">
+ *           <complexType>
+ *             <complexContent>
+ *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+ *               </restriction>
+ *             </complexContent>
+ *           </complexType>
+ *         </element>
+ *         <element name="entity" maxOccurs="unbounded">
+ *           <complexType>
+ *             <complexContent>
+ *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                 <sequence>
+ *                   <element name="entityDetail" maxOccurs="unbounded" minOccurs="0">
+ *                     <complexType>
+ *                       <complexContent>
+ *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+ *                         </restriction>
+ *                       </complexContent>
+ *                     </complexType>
+ *                   </element>
+ *                   <element name="segment" maxOccurs="unbounded">
+ *                     <complexType>
+ *                       <complexContent>
+ *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                           <sequence>
+ *                             <element name="listResidue" minOccurs="0">
+ *                               <complexType>
+ *                                 <complexContent>
+ *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                     <sequence>
+ *                                       <element name="residue" maxOccurs="unbounded">
+ *                                         <complexType>
+ *                                           <complexContent>
+ *                                             <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                               <sequence>
+ *                                                 <element name="crossRefDb" maxOccurs="unbounded" minOccurs="0">
+ *                                                   <complexType>
+ *                                                     <complexContent>
+ *                                                       <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                                         <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+ *                                                         <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}resRef"/>
+ *                                                         <attribute name="dbChainId" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}chainId" />
+ *                                                       </restriction>
+ *                                                     </complexContent>
+ *                                                   </complexType>
+ *                                                 </element>
+ *                                                 <element name="residueDetail" maxOccurs="unbounded" minOccurs="0">
+ *                                                   <complexType>
+ *                                                     <complexContent>
+ *                                                       <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                                         <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+ *                                                       </restriction>
+ *                                                     </complexContent>
+ *                                                   </complexType>
+ *                                                 </element>
+ *                                               </sequence>
+ *                                               <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}resRef"/>
+ *                                               <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}listdbRef"/>
+ *                                             </restriction>
+ *                                           </complexContent>
+ *                                         </complexType>
+ *                                       </element>
+ *                                     </sequence>
+ *                                   </restriction>
+ *                                 </complexContent>
+ *                               </complexType>
+ *                             </element>
+ *                             <element name="listMapRegion" minOccurs="0">
+ *                               <complexType>
+ *                                 <complexContent>
+ *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                     <sequence>
+ *                                       <element name="mapRegion" maxOccurs="unbounded">
+ *                                         <complexType>
+ *                                           <complexContent>
+ *                                             <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                               <sequence>
+ *                                                 <element name="db">
+ *                                                   <complexType>
+ *                                                     <complexContent>
+ *                                                       <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                                         <sequence>
+ *                                                           <element name="dbDetail" maxOccurs="unbounded" minOccurs="0">
+ *                                                             <complexType>
+ *                                                               <complexContent>
+ *                                                                 <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                                                   <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+ *                                                                 </restriction>
+ *                                                               </complexContent>
+ *                                                             </complexType>
+ *                                                           </element>
+ *                                                         </sequence>
+ *                                                         <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+ *                                                         <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+ *                                                         <attribute name="dbChainId" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbChainId" />
+ *                                                       </restriction>
+ *                                                     </complexContent>
+ *                                                   </complexType>
+ *                                                 </element>
+ *                                               </sequence>
+ *                                               <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+ *                                             </restriction>
+ *                                           </complexContent>
+ *                                         </complexType>
+ *                                       </element>
+ *                                     </sequence>
+ *                                   </restriction>
+ *                                 </complexContent>
+ *                               </complexType>
+ *                             </element>
+ *                             <element name="segmentDetail" maxOccurs="unbounded" minOccurs="0">
+ *                               <complexType>
+ *                                 <complexContent>
+ *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                     <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+ *                                   </restriction>
+ *                                 </complexContent>
+ *                               </complexType>
+ *                             </element>
+ *                           </sequence>
+ *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+ *                           <attribute name="segId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                         </restriction>
+ *                       </complexContent>
+ *                     </complexType>
+ *                   </element>
+ *                 </sequence>
+ *                 <attribute name="type" use="required" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}entityType" />
+ *                 <attribute name="entityId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *               </restriction>
+ *             </complexContent>
+ *           </complexType>
+ *         </element>
+ *         <element ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/eFamily.xsd}alignment" maxOccurs="unbounded" minOccurs="0"/>
+ *       </sequence>
+ *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+ *       <attribute name="date" use="required" type="{http://www.w3.org/2001/XMLSchema}date" />
+ *       <attribute name="dbEntryVersion" use="required" type="{http://www.w3.org/2001/XMLSchema}date" />
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * </pre>
+ * 
+ * 
+ */
+ at XmlAccessorType(XmlAccessType.FIELD)
+ at XmlType(name = "", propOrder = {
+    "listDB",
+    "entryDetail",
+    "entity",
+    "alignment"
+})
+ at XmlRootElement(name = "entry")
+public class Entry {
+
+    @XmlElement(required = true)
+    protected Entry.ListDB listDB;
+    protected List<Entry.EntryDetail> entryDetail;
+    @XmlElement(required = true)
+    protected List<Entry.Entity> entity;
+    protected List<Alignment> alignment;
+    @XmlAttribute(name = "date", required = true)
+    @XmlSchemaType(name = "date")
+    protected XMLGregorianCalendar date;
+    @XmlAttribute(name = "dbEntryVersion", required = true)
+    @XmlSchemaType(name = "date")
+    protected XMLGregorianCalendar dbEntryVersion;
+    @XmlAttribute(name = "dbSource", required = true)
+    protected String dbSource;
+    @XmlAttribute(name = "dbCoordSys", required = true)
+    protected String dbCoordSys;
+    @XmlAttribute(name = "dbAccessionId", required = true)
+    protected String dbAccessionId;
+    @XmlAttribute(name = "dbEvidence")
+    protected String dbEvidence;
+    @XmlAttribute(name = "dbVersion")
+    protected String dbVersion;
+
+    /**
+     * Gets the value of the listDB property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link Entry.ListDB }
+     *     
+     */
+    public Entry.ListDB getListDB() {
+        return listDB;
+    }
+
+    /**
+     * Sets the value of the listDB property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link Entry.ListDB }
+     *     
+     */
+    public void setListDB(Entry.ListDB value) {
+        this.listDB = value;
+    }
+
+    /**
+     * Gets the value of the entryDetail property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the entryDetail property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getEntryDetail().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link Entry.EntryDetail }
+     * 
+     * 
+     */
+    public List<Entry.EntryDetail> getEntryDetail() {
+        if (entryDetail == null) {
+            entryDetail = new ArrayList<Entry.EntryDetail>();
+        }
+        return this.entryDetail;
+    }
+
+    /**
+     * Gets the value of the entity property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the entity property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getEntity().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link Entry.Entity }
+     * 
+     * 
+     */
+    public List<Entry.Entity> getEntity() {
+        if (entity == null) {
+            entity = new ArrayList<Entry.Entity>();
+        }
+        return this.entity;
+    }
+
+    /**
+     * Gets the value of the alignment property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the alignment property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getAlignment().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link Alignment }
+     * 
+     * 
+     */
+    public List<Alignment> getAlignment() {
+        if (alignment == null) {
+            alignment = new ArrayList<Alignment>();
+        }
+        return this.alignment;
+    }
+
+    /**
+     * Gets the value of the date property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link XMLGregorianCalendar }
+     *     
+     */
+    public XMLGregorianCalendar getDate() {
+        return date;
+    }
+
+    /**
+     * Sets the value of the date property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link XMLGregorianCalendar }
+     *     
+     */
+    public void setDate(XMLGregorianCalendar value) {
+        this.date = value;
+    }
+
+    /**
+     * Gets the value of the dbEntryVersion property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link XMLGregorianCalendar }
+     *     
+     */
+    public XMLGregorianCalendar getDbEntryVersion() {
+        return dbEntryVersion;
+    }
+
+    /**
+     * Sets the value of the dbEntryVersion property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link XMLGregorianCalendar }
+     *     
+     */
+    public void setDbEntryVersion(XMLGregorianCalendar value) {
+        this.dbEntryVersion = value;
+    }
+
+    /**
+     * Gets the value of the dbSource property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link String }
+     *     
+     */
+    public String getDbSource() {
+        return dbSource;
+    }
+
+    /**
+     * Sets the value of the dbSource property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *     
+     */
+    public void setDbSource(String value) {
+        this.dbSource = value;
+    }
+
+    /**
+     * Gets the value of the dbCoordSys property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link String }
+     *     
+     */
+    public String getDbCoordSys() {
+        return dbCoordSys;
+    }
+
+    /**
+     * Sets the value of the dbCoordSys property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *     
+     */
+    public void setDbCoordSys(String value) {
+        this.dbCoordSys = value;
+    }
+
+    /**
+     * Gets the value of the dbAccessionId property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link String }
+     *     
+     */
+    public String getDbAccessionId() {
+        return dbAccessionId;
+    }
+
+    /**
+     * Sets the value of the dbAccessionId property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *     
+     */
+    public void setDbAccessionId(String value) {
+        this.dbAccessionId = value;
+    }
+
+    /**
+     * Gets the value of the dbEvidence property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link String }
+     *     
+     */
+    public String getDbEvidence() {
+        return dbEvidence;
+    }
+
+    /**
+     * Sets the value of the dbEvidence property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *     
+     */
+    public void setDbEvidence(String value) {
+        this.dbEvidence = value;
+    }
+
+    /**
+     * Gets the value of the dbVersion property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link String }
+     *     
+     */
+    public String getDbVersion() {
+        return dbVersion;
+    }
+
+    /**
+     * Sets the value of the dbVersion property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *     
+     */
+    public void setDbVersion(String value) {
+        this.dbVersion = value;
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * <complexType>
+     *   <complexContent>
+     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       <sequence>
+     *         <element name="entityDetail" maxOccurs="unbounded" minOccurs="0">
+     *           <complexType>
+     *             <complexContent>
+     *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+     *               </restriction>
+     *             </complexContent>
+     *           </complexType>
+     *         </element>
+     *         <element name="segment" maxOccurs="unbounded">
+     *           <complexType>
+     *             <complexContent>
+     *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                 <sequence>
+     *                   <element name="listResidue" minOccurs="0">
+     *                     <complexType>
+     *                       <complexContent>
+     *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           <sequence>
+     *                             <element name="residue" maxOccurs="unbounded">
+     *                               <complexType>
+     *                                 <complexContent>
+     *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                                     <sequence>
+     *                                       <element name="crossRefDb" maxOccurs="unbounded" minOccurs="0">
+     *                                         <complexType>
+     *                                           <complexContent>
+     *                                             <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                                               <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+     *                                               <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}resRef"/>
+     *                                               <attribute name="dbChainId" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}chainId" />
+     *                                             </restriction>
+     *                                           </complexContent>
+     *                                         </complexType>
+     *                                       </element>
+     *                                       <element name="residueDetail" maxOccurs="unbounded" minOccurs="0">
+     *                                         <complexType>
+     *                                           <complexContent>
+     *                                             <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                                               <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+     *                                             </restriction>
+     *                                           </complexContent>
+     *                                         </complexType>
+     *                                       </element>
+     *                                     </sequence>
+     *                                     <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}resRef"/>
+     *                                     <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}listdbRef"/>
+     *                                   </restriction>
+     *                                 </complexContent>
+     *                               </complexType>
+     *                             </element>
+     *                           </sequence>
+     *                         </restriction>
+     *                       </complexContent>
+     *                     </complexType>
+     *                   </element>
+     *                   <element name="listMapRegion" minOccurs="0">
+     *                     <complexType>
+     *                       <complexContent>
+     *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           <sequence>
+     *                             <element name="mapRegion" maxOccurs="unbounded">
+     *                               <complexType>
+     *                                 <complexContent>
+     *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                                     <sequence>
+     *                                       <element name="db">
+     *                                         <complexType>
+     *                                           <complexContent>
+     *                                             <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                                               <sequence>
+     *                                                 <element name="dbDetail" maxOccurs="unbounded" minOccurs="0">
+     *                                                   <complexType>
+     *                                                     <complexContent>
+     *                                                       <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                                                         <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+     *                                                       </restriction>
+     *                                                     </complexContent>
+     *                                                   </complexType>
+     *                                                 </element>
+     *                                               </sequence>
+     *                                               <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+     *                                               <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+     *                                               <attribute name="dbChainId" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbChainId" />
+     *                                             </restriction>
+     *                                           </complexContent>
+     *                                         </complexType>
+     *                                       </element>
+     *                                     </sequence>
+     *                                     <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+     *                                   </restriction>
+     *                                 </complexContent>
+     *                               </complexType>
+     *                             </element>
+     *                           </sequence>
+     *                         </restriction>
+     *                       </complexContent>
+     *                     </complexType>
+     *                   </element>
+     *                   <element name="segmentDetail" maxOccurs="unbounded" minOccurs="0">
+     *                     <complexType>
+     *                       <complexContent>
+     *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+     *                         </restriction>
+     *                       </complexContent>
+     *                     </complexType>
+     *                   </element>
+     *                 </sequence>
+     *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+     *                 <attribute name="segId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *               </restriction>
+     *             </complexContent>
+     *           </complexType>
+     *         </element>
+     *       </sequence>
+     *       <attribute name="type" use="required" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}entityType" />
+     *       <attribute name="entityId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *     </restriction>
+     *   </complexContent>
+     * </complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "entityDetail",
+        "segment"
+    })
+    public static class Entity {
+
+        protected List<Entry.Entity.EntityDetail> entityDetail;
+        @XmlElement(required = true)
+        protected List<Entry.Entity.Segment> segment;
+        @XmlAttribute(name = "type", required = true)
+        protected EntityType type;
+        @XmlAttribute(name = "entityId", required = true)
+        protected String entityId;
+
+        /**
+         * Gets the value of the entityDetail property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the entityDetail property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getEntityDetail().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link Entry.Entity.EntityDetail }
+         * 
+         * 
+         */
+        public List<Entry.Entity.EntityDetail> getEntityDetail() {
+            if (entityDetail == null) {
+                entityDetail = new ArrayList<Entry.Entity.EntityDetail>();
+            }
+            return this.entityDetail;
+        }
+
+        /**
+         * Gets the value of the segment property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the segment property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getSegment().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link Entry.Entity.Segment }
+         * 
+         * 
+         */
+        public List<Entry.Entity.Segment> getSegment() {
+            if (segment == null) {
+                segment = new ArrayList<Entry.Entity.Segment>();
+            }
+            return this.segment;
+        }
+
+        /**
+         * Gets the value of the type property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link EntityType }
+         *     
+         */
+        public EntityType getType() {
+            return type;
+        }
+
+        /**
+         * Sets the value of the type property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link EntityType }
+         *     
+         */
+        public void setType(EntityType value) {
+            this.type = value;
+        }
+
+        /**
+         * Gets the value of the entityId property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getEntityId() {
+            return entityId;
+        }
+
+        /**
+         * Sets the value of the entityId property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setEntityId(String value) {
+            this.entityId = value;
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * <complexType>
+         *   <complexContent>
+         *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+         *     </restriction>
+         *   </complexContent>
+         * </complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "", propOrder = {
+            "content"
+        })
+        public static class EntityDetail {
+
+            @XmlValue
+            protected String content;
+            @XmlAttribute(name = "dbSource")
+            protected String dbSource;
+            @XmlAttribute(name = "property", required = true)
+            protected String property;
+
+            /**
+             * Gets the value of the content property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getContent() {
+                return content;
+            }
+
+            /**
+             * Sets the value of the content property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setContent(String value) {
+                this.content = value;
+            }
+
+            /**
+             * Gets the value of the dbSource property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getDbSource() {
+                return dbSource;
+            }
+
+            /**
+             * Sets the value of the dbSource property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setDbSource(String value) {
+                this.dbSource = value;
+            }
+
+            /**
+             * Gets the value of the property property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getProperty() {
+                return property;
+            }
+
+            /**
+             * Sets the value of the property property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setProperty(String value) {
+                this.property = value;
+            }
+
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * <complexType>
+         *   <complexContent>
+         *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *       <sequence>
+         *         <element name="listResidue" minOccurs="0">
+         *           <complexType>
+         *             <complexContent>
+         *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 <sequence>
+         *                   <element name="residue" maxOccurs="unbounded">
+         *                     <complexType>
+         *                       <complexContent>
+         *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                           <sequence>
+         *                             <element name="crossRefDb" maxOccurs="unbounded" minOccurs="0">
+         *                               <complexType>
+         *                                 <complexContent>
+         *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                                     <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+         *                                     <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}resRef"/>
+         *                                     <attribute name="dbChainId" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}chainId" />
+         *                                   </restriction>
+         *                                 </complexContent>
+         *                               </complexType>
+         *                             </element>
+         *                             <element name="residueDetail" maxOccurs="unbounded" minOccurs="0">
+         *                               <complexType>
+         *                                 <complexContent>
+         *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                                     <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+         *                                   </restriction>
+         *                                 </complexContent>
+         *                               </complexType>
+         *                             </element>
+         *                           </sequence>
+         *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}resRef"/>
+         *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}listdbRef"/>
+         *                         </restriction>
+         *                       </complexContent>
+         *                     </complexType>
+         *                   </element>
+         *                 </sequence>
+         *               </restriction>
+         *             </complexContent>
+         *           </complexType>
+         *         </element>
+         *         <element name="listMapRegion" minOccurs="0">
+         *           <complexType>
+         *             <complexContent>
+         *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 <sequence>
+         *                   <element name="mapRegion" maxOccurs="unbounded">
+         *                     <complexType>
+         *                       <complexContent>
+         *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                           <sequence>
+         *                             <element name="db">
+         *                               <complexType>
+         *                                 <complexContent>
+         *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                                     <sequence>
+         *                                       <element name="dbDetail" maxOccurs="unbounded" minOccurs="0">
+         *                                         <complexType>
+         *                                           <complexContent>
+         *                                             <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                                               <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+         *                                             </restriction>
+         *                                           </complexContent>
+         *                                         </complexType>
+         *                                       </element>
+         *                                     </sequence>
+         *                                     <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+         *                                     <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+         *                                     <attribute name="dbChainId" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbChainId" />
+         *                                   </restriction>
+         *                                 </complexContent>
+         *                               </complexType>
+         *                             </element>
+         *                           </sequence>
+         *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+         *                         </restriction>
+         *                       </complexContent>
+         *                     </complexType>
+         *                   </element>
+         *                 </sequence>
+         *               </restriction>
+         *             </complexContent>
+         *           </complexType>
+         *         </element>
+         *         <element name="segmentDetail" maxOccurs="unbounded" minOccurs="0">
+         *           <complexType>
+         *             <complexContent>
+         *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+         *               </restriction>
+         *             </complexContent>
+         *           </complexType>
+         *         </element>
+         *       </sequence>
+         *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+         *       <attribute name="segId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+         *     </restriction>
+         *   </complexContent>
+         * </complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "", propOrder = {
+            "listResidue",
+            "listMapRegion",
+            "segmentDetail"
+        })
+        public static class Segment {
+
+            protected Entry.Entity.Segment.ListResidue listResidue;
+            protected Entry.Entity.Segment.ListMapRegion listMapRegion;
+            protected List<Entry.Entity.Segment.SegmentDetail> segmentDetail;
+            @XmlAttribute(name = "segId", required = true)
+            protected String segId;
+            @XmlAttribute(name = "start")
+            protected String start;
+            @XmlAttribute(name = "end")
+            protected String end;
+
+            /**
+             * Gets the value of the listResidue property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Entry.Entity.Segment.ListResidue }
+             *     
+             */
+            public Entry.Entity.Segment.ListResidue getListResidue() {
+                return listResidue;
+            }
+
+            /**
+             * Sets the value of the listResidue property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Entry.Entity.Segment.ListResidue }
+             *     
+             */
+            public void setListResidue(Entry.Entity.Segment.ListResidue value) {
+                this.listResidue = value;
+            }
+
+            /**
+             * Gets the value of the listMapRegion property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Entry.Entity.Segment.ListMapRegion }
+             *     
+             */
+            public Entry.Entity.Segment.ListMapRegion getListMapRegion() {
+                return listMapRegion;
+            }
+
+            /**
+             * Sets the value of the listMapRegion property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Entry.Entity.Segment.ListMapRegion }
+             *     
+             */
+            public void setListMapRegion(Entry.Entity.Segment.ListMapRegion value) {
+                this.listMapRegion = value;
+            }
+
+            /**
+             * Gets the value of the segmentDetail property.
+             * 
+             * <p>
+             * This accessor method returns a reference to the live list,
+             * not a snapshot. Therefore any modification you make to the
+             * returned list will be present inside the JAXB object.
+             * This is why there is not a <CODE>set</CODE> method for the segmentDetail property.
+             * 
+             * <p>
+             * For example, to add a new item, do as follows:
+             * <pre>
+             *    getSegmentDetail().add(newItem);
+             * </pre>
+             * 
+             * 
+             * <p>
+             * Objects of the following type(s) are allowed in the list
+             * {@link Entry.Entity.Segment.SegmentDetail }
+             * 
+             * 
+             */
+            public List<Entry.Entity.Segment.SegmentDetail> getSegmentDetail() {
+                if (segmentDetail == null) {
+                    segmentDetail = new ArrayList<Entry.Entity.Segment.SegmentDetail>();
+                }
+                return this.segmentDetail;
+            }
+
+            /**
+             * Gets the value of the segId property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getSegId() {
+                return segId;
+            }
+
+            /**
+             * Sets the value of the segId property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setSegId(String value) {
+                this.segId = value;
+            }
+
+            /**
+             * Gets the value of the start property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getStart() {
+                return start;
+            }
+
+            /**
+             * Sets the value of the start property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setStart(String value) {
+                this.start = value;
+            }
+
+            /**
+             * Gets the value of the end property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getEnd() {
+                return end;
+            }
+
+            /**
+             * Sets the value of the end property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setEnd(String value) {
+                this.end = value;
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * <complexType>
+             *   <complexContent>
+             *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       <sequence>
+             *         <element name="mapRegion" maxOccurs="unbounded">
+             *           <complexType>
+             *             <complexContent>
+             *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *                 <sequence>
+             *                   <element name="db">
+             *                     <complexType>
+             *                       <complexContent>
+             *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *                           <sequence>
+             *                             <element name="dbDetail" maxOccurs="unbounded" minOccurs="0">
+             *                               <complexType>
+             *                                 <complexContent>
+             *                                   <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *                                     <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+             *                                   </restriction>
+             *                                 </complexContent>
+             *                               </complexType>
+             *                             </element>
+             *                           </sequence>
+             *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+             *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+             *                           <attribute name="dbChainId" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbChainId" />
+             *                         </restriction>
+             *                       </complexContent>
+             *                     </complexType>
+             *                   </element>
+             *                 </sequence>
+             *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+             *               </restriction>
+             *             </complexContent>
+             *           </complexType>
+             *         </element>
+             *       </sequence>
+             *     </restriction>
+             *   </complexContent>
+             * </complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "", propOrder = {
+                "mapRegion"
+            })
+            public static class ListMapRegion {
+
+                @XmlElement(required = true)
+                protected List<Entry.Entity.Segment.ListMapRegion.MapRegion> mapRegion;
+
+                /**
+                 * Gets the value of the mapRegion property.
+                 * 
+                 * <p>
+                 * This accessor method returns a reference to the live list,
+                 * not a snapshot. Therefore any modification you make to the
+                 * returned list will be present inside the JAXB object.
+                 * This is why there is not a <CODE>set</CODE> method for the mapRegion property.
+                 * 
+                 * <p>
+                 * For example, to add a new item, do as follows:
+                 * <pre>
+                 *    getMapRegion().add(newItem);
+                 * </pre>
+                 * 
+                 * 
+                 * <p>
+                 * Objects of the following type(s) are allowed in the list
+                 * {@link Entry.Entity.Segment.ListMapRegion.MapRegion }
+                 * 
+                 * 
+                 */
+                public List<Entry.Entity.Segment.ListMapRegion.MapRegion> getMapRegion() {
+                    if (mapRegion == null) {
+                        mapRegion = new ArrayList<Entry.Entity.Segment.ListMapRegion.MapRegion>();
+                    }
+                    return this.mapRegion;
+                }
+
+
+                /**
+                 * <p>Java class for anonymous complex type.
+                 * 
+                 * <p>The following schema fragment specifies the expected content contained within this class.
+                 * 
+                 * <pre>
+                 * <complexType>
+                 *   <complexContent>
+                 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+                 *       <sequence>
+                 *         <element name="db">
+                 *           <complexType>
+                 *             <complexContent>
+                 *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+                 *                 <sequence>
+                 *                   <element name="dbDetail" maxOccurs="unbounded" minOccurs="0">
+                 *                     <complexType>
+                 *                       <complexContent>
+                 *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+                 *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+                 *                         </restriction>
+                 *                       </complexContent>
+                 *                     </complexType>
+                 *                   </element>
+                 *                 </sequence>
+                 *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+                 *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+                 *                 <attribute name="dbChainId" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbChainId" />
+                 *               </restriction>
+                 *             </complexContent>
+                 *           </complexType>
+                 *         </element>
+                 *       </sequence>
+                 *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+                 *     </restriction>
+                 *   </complexContent>
+                 * </complexType>
+                 * </pre>
+                 * 
+                 * 
+                 */
+                @XmlAccessorType(XmlAccessType.FIELD)
+                @XmlType(name = "", propOrder = {
+                    "db"
+                })
+                public static class MapRegion {
+
+                    @XmlElement(required = true)
+                    protected Entry.Entity.Segment.ListMapRegion.MapRegion.Db db;
+                    @XmlAttribute(name = "start")
+                    protected String start;
+                    @XmlAttribute(name = "end")
+                    protected String end;
+
+                    /**
+                     * Gets the value of the db property.
+                     * 
+                     * @return
+                     *     possible object is
+                     *     {@link Entry.Entity.Segment.ListMapRegion.MapRegion.Db }
+                     *     
+                     */
+                    public Entry.Entity.Segment.ListMapRegion.MapRegion.Db getDb() {
+                        return db;
+                    }
+
+                    /**
+                     * Sets the value of the db property.
+                     * 
+                     * @param value
+                     *     allowed object is
+                     *     {@link Entry.Entity.Segment.ListMapRegion.MapRegion.Db }
+                     *     
+                     */
+                    public void setDb(Entry.Entity.Segment.ListMapRegion.MapRegion.Db value) {
+                        this.db = value;
+                    }
+
+                    /**
+                     * Gets the value of the start property.
+                     * 
+                     * @return
+                     *     possible object is
+                     *     {@link String }
+                     *     
+                     */
+                    public String getStart() {
+                        return start;
+                    }
+
+                    /**
+                     * Sets the value of the start property.
+                     * 
+                     * @param value
+                     *     allowed object is
+                     *     {@link String }
+                     *     
+                     */
+                    public void setStart(String value) {
+                        this.start = value;
+                    }
+
+                    /**
+                     * Gets the value of the end property.
+                     * 
+                     * @return
+                     *     possible object is
+                     *     {@link String }
+                     *     
+                     */
+                    public String getEnd() {
+                        return end;
+                    }
+
+                    /**
+                     * Sets the value of the end property.
+                     * 
+                     * @param value
+                     *     allowed object is
+                     *     {@link String }
+                     *     
+                     */
+                    public void setEnd(String value) {
+                        this.end = value;
+                    }
+
+
+                    /**
+                     * <p>Java class for anonymous complex type.
+                     * 
+                     * <p>The following schema fragment specifies the expected content contained within this class.
+                     * 
+                     * <pre>
+                     * <complexType>
+                     *   <complexContent>
+                     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+                     *       <sequence>
+                     *         <element name="dbDetail" maxOccurs="unbounded" minOccurs="0">
+                     *           <complexType>
+                     *             <complexContent>
+                     *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+                     *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+                     *               </restriction>
+                     *             </complexContent>
+                     *           </complexType>
+                     *         </element>
+                     *       </sequence>
+                     *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+                     *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}region"/>
+                     *       <attribute name="dbChainId" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbChainId" />
+                     *     </restriction>
+                     *   </complexContent>
+                     * </complexType>
+                     * </pre>
+                     * 
+                     * 
+                     */
+                    @XmlAccessorType(XmlAccessType.FIELD)
+                    @XmlType(name = "", propOrder = {
+                        "dbDetail"
+                    })
+                    public static class Db {
+
+                        protected List<Entry.Entity.Segment.ListMapRegion.MapRegion.Db.DbDetail> dbDetail;
+                        @XmlAttribute(name = "dbChainId")
+                        protected String dbChainId;
+                        @XmlAttribute(name = "dbSource", required = true)
+                        protected String dbSource;
+                        @XmlAttribute(name = "dbCoordSys", required = true)
+                        protected String dbCoordSys;
+                        @XmlAttribute(name = "dbAccessionId", required = true)
+                        protected String dbAccessionId;
+                        @XmlAttribute(name = "dbEvidence")
+                        protected String dbEvidence;
+                        @XmlAttribute(name = "dbVersion")
+                        protected String dbVersion;
+                        @XmlAttribute(name = "start")
+                        protected String start;
+                        @XmlAttribute(name = "end")
+                        protected String end;
+
+                        /**
+                         * Gets the value of the dbDetail property.
+                         * 
+                         * <p>
+                         * This accessor method returns a reference to the live list,
+                         * not a snapshot. Therefore any modification you make to the
+                         * returned list will be present inside the JAXB object.
+                         * This is why there is not a <CODE>set</CODE> method for the dbDetail property.
+                         * 
+                         * <p>
+                         * For example, to add a new item, do as follows:
+                         * <pre>
+                         *    getDbDetail().add(newItem);
+                         * </pre>
+                         * 
+                         * 
+                         * <p>
+                         * Objects of the following type(s) are allowed in the list
+                         * {@link Entry.Entity.Segment.ListMapRegion.MapRegion.Db.DbDetail }
+                         * 
+                         * 
+                         */
+                        public List<Entry.Entity.Segment.ListMapRegion.MapRegion.Db.DbDetail> getDbDetail() {
+                            if (dbDetail == null) {
+                                dbDetail = new ArrayList<Entry.Entity.Segment.ListMapRegion.MapRegion.Db.DbDetail>();
+                            }
+                            return this.dbDetail;
+                        }
+
+                        /**
+                         * Gets the value of the dbChainId property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbChainId() {
+                            return dbChainId;
+                        }
+
+                        /**
+                         * Sets the value of the dbChainId property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbChainId(String value) {
+                            this.dbChainId = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbSource property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbSource() {
+                            return dbSource;
+                        }
+
+                        /**
+                         * Sets the value of the dbSource property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbSource(String value) {
+                            this.dbSource = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbCoordSys property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbCoordSys() {
+                            return dbCoordSys;
+                        }
+
+                        /**
+                         * Sets the value of the dbCoordSys property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbCoordSys(String value) {
+                            this.dbCoordSys = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbAccessionId property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbAccessionId() {
+                            return dbAccessionId;
+                        }
+
+                        /**
+                         * Sets the value of the dbAccessionId property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbAccessionId(String value) {
+                            this.dbAccessionId = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbEvidence property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbEvidence() {
+                            return dbEvidence;
+                        }
+
+                        /**
+                         * Sets the value of the dbEvidence property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbEvidence(String value) {
+                            this.dbEvidence = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbVersion property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbVersion() {
+                            return dbVersion;
+                        }
+
+                        /**
+                         * Sets the value of the dbVersion property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbVersion(String value) {
+                            this.dbVersion = value;
+                        }
+
+                        /**
+                         * Gets the value of the start property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getStart() {
+                            return start;
+                        }
+
+                        /**
+                         * Sets the value of the start property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setStart(String value) {
+                            this.start = value;
+                        }
+
+                        /**
+                         * Gets the value of the end property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getEnd() {
+                            return end;
+                        }
+
+                        /**
+                         * Sets the value of the end property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setEnd(String value) {
+                            this.end = value;
+                        }
+
+
+                        /**
+                         * <p>Java class for anonymous complex type.
+                         * 
+                         * <p>The following schema fragment specifies the expected content contained within this class.
+                         * 
+                         * <pre>
+                         * <complexType>
+                         *   <complexContent>
+                         *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+                         *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+                         *     </restriction>
+                         *   </complexContent>
+                         * </complexType>
+                         * </pre>
+                         * 
+                         * 
+                         */
+                        @XmlAccessorType(XmlAccessType.FIELD)
+                        @XmlType(name = "", propOrder = {
+                            "content"
+                        })
+                        public static class DbDetail {
+
+                            @XmlValue
+                            protected String content;
+                            @XmlAttribute(name = "dbSource")
+                            protected String dbSource;
+                            @XmlAttribute(name = "property", required = true)
+                            protected String property;
+
+                            /**
+                             * Gets the value of the content property.
+                             * 
+                             * @return
+                             *     possible object is
+                             *     {@link String }
+                             *     
+                             */
+                            public String getContent() {
+                                return content;
+                            }
+
+                            /**
+                             * Sets the value of the content property.
+                             * 
+                             * @param value
+                             *     allowed object is
+                             *     {@link String }
+                             *     
+                             */
+                            public void setContent(String value) {
+                                this.content = value;
+                            }
+
+                            /**
+                             * Gets the value of the dbSource property.
+                             * 
+                             * @return
+                             *     possible object is
+                             *     {@link String }
+                             *     
+                             */
+                            public String getDbSource() {
+                                return dbSource;
+                            }
+
+                            /**
+                             * Sets the value of the dbSource property.
+                             * 
+                             * @param value
+                             *     allowed object is
+                             *     {@link String }
+                             *     
+                             */
+                            public void setDbSource(String value) {
+                                this.dbSource = value;
+                            }
+
+                            /**
+                             * Gets the value of the property property.
+                             * 
+                             * @return
+                             *     possible object is
+                             *     {@link String }
+                             *     
+                             */
+                            public String getProperty() {
+                                return property;
+                            }
+
+                            /**
+                             * Sets the value of the property property.
+                             * 
+                             * @param value
+                             *     allowed object is
+                             *     {@link String }
+                             *     
+                             */
+                            public void setProperty(String value) {
+                                this.property = value;
+                            }
+
+                        }
+
+                    }
+
+                }
+
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * <complexType>
+             *   <complexContent>
+             *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       <sequence>
+             *         <element name="residue" maxOccurs="unbounded">
+             *           <complexType>
+             *             <complexContent>
+             *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *                 <sequence>
+             *                   <element name="crossRefDb" maxOccurs="unbounded" minOccurs="0">
+             *                     <complexType>
+             *                       <complexContent>
+             *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+             *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}resRef"/>
+             *                           <attribute name="dbChainId" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}chainId" />
+             *                         </restriction>
+             *                       </complexContent>
+             *                     </complexType>
+             *                   </element>
+             *                   <element name="residueDetail" maxOccurs="unbounded" minOccurs="0">
+             *                     <complexType>
+             *                       <complexContent>
+             *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *                           <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+             *                         </restriction>
+             *                       </complexContent>
+             *                     </complexType>
+             *                   </element>
+             *                 </sequence>
+             *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}resRef"/>
+             *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}listdbRef"/>
+             *               </restriction>
+             *             </complexContent>
+             *           </complexType>
+             *         </element>
+             *       </sequence>
+             *     </restriction>
+             *   </complexContent>
+             * </complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "", propOrder = {
+                "residue"
+            })
+            public static class ListResidue {
+
+                @XmlElement(required = true)
+                protected List<Entry.Entity.Segment.ListResidue.Residue> residue;
+
+                /**
+                 * Gets the value of the residue property.
+                 * 
+                 * <p>
+                 * This accessor method returns a reference to the live list,
+                 * not a snapshot. Therefore any modification you make to the
+                 * returned list will be present inside the JAXB object.
+                 * This is why there is not a <CODE>set</CODE> method for the residue property.
+                 * 
+                 * <p>
+                 * For example, to add a new item, do as follows:
+                 * <pre>
+                 *    getResidue().add(newItem);
+                 * </pre>
+                 * 
+                 * 
+                 * <p>
+                 * Objects of the following type(s) are allowed in the list
+                 * {@link Entry.Entity.Segment.ListResidue.Residue }
+                 * 
+                 * 
+                 */
+                public List<Entry.Entity.Segment.ListResidue.Residue> getResidue() {
+                    if (residue == null) {
+                        residue = new ArrayList<Entry.Entity.Segment.ListResidue.Residue>();
+                    }
+                    return this.residue;
+                }
+
+
+                /**
+                 * <p>Java class for anonymous complex type.
+                 * 
+                 * <p>The following schema fragment specifies the expected content contained within this class.
+                 * 
+                 * <pre>
+                 * <complexType>
+                 *   <complexContent>
+                 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+                 *       <sequence>
+                 *         <element name="crossRefDb" maxOccurs="unbounded" minOccurs="0">
+                 *           <complexType>
+                 *             <complexContent>
+                 *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+                 *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+                 *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}resRef"/>
+                 *                 <attribute name="dbChainId" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}chainId" />
+                 *               </restriction>
+                 *             </complexContent>
+                 *           </complexType>
+                 *         </element>
+                 *         <element name="residueDetail" maxOccurs="unbounded" minOccurs="0">
+                 *           <complexType>
+                 *             <complexContent>
+                 *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+                 *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+                 *               </restriction>
+                 *             </complexContent>
+                 *           </complexType>
+                 *         </element>
+                 *       </sequence>
+                 *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}resRef"/>
+                 *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}listdbRef"/>
+                 *     </restriction>
+                 *   </complexContent>
+                 * </complexType>
+                 * </pre>
+                 * 
+                 * 
+                 */
+                @XmlAccessorType(XmlAccessType.FIELD)
+                @XmlType(name = "", propOrder = {
+                    "crossRefDb",
+                    "residueDetail"
+                })
+                public static class Residue {
+
+                    protected List<Entry.Entity.Segment.ListResidue.Residue.CrossRefDb> crossRefDb;
+                    protected List<Entry.Entity.Segment.ListResidue.Residue.ResidueDetail> residueDetail;
+                    @XmlAttribute(name = "dbResNum", required = true)
+                    protected String dbResNum;
+                    @XmlAttribute(name = "dbResName", required = true)
+                    protected String dbResName;
+                    @XmlAttribute(name = "dbVersion")
+                    protected String dbVersion;
+                    @XmlAttribute(name = "dbSource", required = true)
+                    protected String dbSource;
+                    @XmlAttribute(name = "dbCoordSys", required = true)
+                    protected String dbCoordSys;
+
+                    /**
+                     * Gets the value of the crossRefDb property.
+                     * 
+                     * <p>
+                     * This accessor method returns a reference to the live list,
+                     * not a snapshot. Therefore any modification you make to the
+                     * returned list will be present inside the JAXB object.
+                     * This is why there is not a <CODE>set</CODE> method for the crossRefDb property.
+                     * 
+                     * <p>
+                     * For example, to add a new item, do as follows:
+                     * <pre>
+                     *    getCrossRefDb().add(newItem);
+                     * </pre>
+                     * 
+                     * 
+                     * <p>
+                     * Objects of the following type(s) are allowed in the list
+                     * {@link Entry.Entity.Segment.ListResidue.Residue.CrossRefDb }
+                     * 
+                     * 
+                     */
+                    public List<Entry.Entity.Segment.ListResidue.Residue.CrossRefDb> getCrossRefDb() {
+                        if (crossRefDb == null) {
+                            crossRefDb = new ArrayList<Entry.Entity.Segment.ListResidue.Residue.CrossRefDb>();
+                        }
+                        return this.crossRefDb;
+                    }
+
+                    /**
+                     * Gets the value of the residueDetail property.
+                     * 
+                     * <p>
+                     * This accessor method returns a reference to the live list,
+                     * not a snapshot. Therefore any modification you make to the
+                     * returned list will be present inside the JAXB object.
+                     * This is why there is not a <CODE>set</CODE> method for the residueDetail property.
+                     * 
+                     * <p>
+                     * For example, to add a new item, do as follows:
+                     * <pre>
+                     *    getResidueDetail().add(newItem);
+                     * </pre>
+                     * 
+                     * 
+                     * <p>
+                     * Objects of the following type(s) are allowed in the list
+                     * {@link Entry.Entity.Segment.ListResidue.Residue.ResidueDetail }
+                     * 
+                     * 
+                     */
+                    public List<Entry.Entity.Segment.ListResidue.Residue.ResidueDetail> getResidueDetail() {
+                        if (residueDetail == null) {
+                            residueDetail = new ArrayList<Entry.Entity.Segment.ListResidue.Residue.ResidueDetail>();
+                        }
+                        return this.residueDetail;
+                    }
+
+                    /**
+                     * Gets the value of the dbResNum property.
+                     * 
+                     * @return
+                     *     possible object is
+                     *     {@link String }
+                     *     
+                     */
+                    public String getDbResNum() {
+                        return dbResNum;
+                    }
+
+                    /**
+                     * Sets the value of the dbResNum property.
+                     * 
+                     * @param value
+                     *     allowed object is
+                     *     {@link String }
+                     *     
+                     */
+                    public void setDbResNum(String value) {
+                        this.dbResNum = value;
+                    }
+
+                    /**
+                     * Gets the value of the dbResName property.
+                     * 
+                     * @return
+                     *     possible object is
+                     *     {@link String }
+                     *     
+                     */
+                    public String getDbResName() {
+                        return dbResName;
+                    }
+
+                    /**
+                     * Sets the value of the dbResName property.
+                     * 
+                     * @param value
+                     *     allowed object is
+                     *     {@link String }
+                     *     
+                     */
+                    public void setDbResName(String value) {
+                        this.dbResName = value;
+                    }
+
+                    /**
+                     * Gets the value of the dbVersion property.
+                     * 
+                     * @return
+                     *     possible object is
+                     *     {@link String }
+                     *     
+                     */
+                    public String getDbVersion() {
+                        return dbVersion;
+                    }
+
+                    /**
+                     * Sets the value of the dbVersion property.
+                     * 
+                     * @param value
+                     *     allowed object is
+                     *     {@link String }
+                     *     
+                     */
+                    public void setDbVersion(String value) {
+                        this.dbVersion = value;
+                    }
+
+                    /**
+                     * Gets the value of the dbSource property.
+                     * 
+                     * @return
+                     *     possible object is
+                     *     {@link String }
+                     *     
+                     */
+                    public String getDbSource() {
+                        return dbSource;
+                    }
+
+                    /**
+                     * Sets the value of the dbSource property.
+                     * 
+                     * @param value
+                     *     allowed object is
+                     *     {@link String }
+                     *     
+                     */
+                    public void setDbSource(String value) {
+                        this.dbSource = value;
+                    }
+
+                    /**
+                     * Gets the value of the dbCoordSys property.
+                     * 
+                     * @return
+                     *     possible object is
+                     *     {@link String }
+                     *     
+                     */
+                    public String getDbCoordSys() {
+                        return dbCoordSys;
+                    }
+
+                    /**
+                     * Sets the value of the dbCoordSys property.
+                     * 
+                     * @param value
+                     *     allowed object is
+                     *     {@link String }
+                     *     
+                     */
+                    public void setDbCoordSys(String value) {
+                        this.dbCoordSys = value;
+                    }
+
+
+                    /**
+                     * <p>Java class for anonymous complex type.
+                     * 
+                     * <p>The following schema fragment specifies the expected content contained within this class.
+                     * 
+                     * <pre>
+                     * <complexType>
+                     *   <complexContent>
+                     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+                     *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}dbRef"/>
+                     *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}resRef"/>
+                     *       <attribute name="dbChainId" type="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}chainId" />
+                     *     </restriction>
+                     *   </complexContent>
+                     * </complexType>
+                     * </pre>
+                     * 
+                     * 
+                     */
+                    @XmlAccessorType(XmlAccessType.FIELD)
+                    @XmlType(name = "")
+                    public static class CrossRefDb {
+
+                        @XmlAttribute(name = "dbChainId")
+                        protected String dbChainId;
+                        @XmlAttribute(name = "dbSource", required = true)
+                        protected String dbSource;
+                        @XmlAttribute(name = "dbCoordSys", required = true)
+                        protected String dbCoordSys;
+                        @XmlAttribute(name = "dbAccessionId", required = true)
+                        protected String dbAccessionId;
+                        @XmlAttribute(name = "dbEvidence")
+                        protected String dbEvidence;
+                        @XmlAttribute(name = "dbVersion")
+                        protected String dbVersion;
+                        @XmlAttribute(name = "dbResNum", required = true)
+                        protected String dbResNum;
+                        @XmlAttribute(name = "dbResName", required = true)
+                        protected String dbResName;
+
+                        /**
+                         * Gets the value of the dbChainId property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbChainId() {
+                            return dbChainId;
+                        }
+
+                        /**
+                         * Sets the value of the dbChainId property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbChainId(String value) {
+                            this.dbChainId = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbSource property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbSource() {
+                            return dbSource;
+                        }
+
+                        /**
+                         * Sets the value of the dbSource property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbSource(String value) {
+                            this.dbSource = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbCoordSys property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbCoordSys() {
+                            return dbCoordSys;
+                        }
+
+                        /**
+                         * Sets the value of the dbCoordSys property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbCoordSys(String value) {
+                            this.dbCoordSys = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbAccessionId property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbAccessionId() {
+                            return dbAccessionId;
+                        }
+
+                        /**
+                         * Sets the value of the dbAccessionId property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbAccessionId(String value) {
+                            this.dbAccessionId = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbEvidence property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbEvidence() {
+                            return dbEvidence;
+                        }
+
+                        /**
+                         * Sets the value of the dbEvidence property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbEvidence(String value) {
+                            this.dbEvidence = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbVersion property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbVersion() {
+                            return dbVersion;
+                        }
+
+                        /**
+                         * Sets the value of the dbVersion property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbVersion(String value) {
+                            this.dbVersion = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbResNum property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbResNum() {
+                            return dbResNum;
+                        }
+
+                        /**
+                         * Sets the value of the dbResNum property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbResNum(String value) {
+                            this.dbResNum = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbResName property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbResName() {
+                            return dbResName;
+                        }
+
+                        /**
+                         * Sets the value of the dbResName property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbResName(String value) {
+                            this.dbResName = value;
+                        }
+
+                    }
+
+
+                    /**
+                     * <p>Java class for anonymous complex type.
+                     * 
+                     * <p>The following schema fragment specifies the expected content contained within this class.
+                     * 
+                     * <pre>
+                     * <complexType>
+                     *   <complexContent>
+                     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+                     *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+                     *     </restriction>
+                     *   </complexContent>
+                     * </complexType>
+                     * </pre>
+                     * 
+                     * 
+                     */
+                    @XmlAccessorType(XmlAccessType.FIELD)
+                    @XmlType(name = "", propOrder = {
+                        "content"
+                    })
+                    public static class ResidueDetail {
+
+                        @XmlValue
+                        protected String content;
+                        @XmlAttribute(name = "dbSource")
+                        protected String dbSource;
+                        @XmlAttribute(name = "property", required = true)
+                        protected String property;
+
+                        /**
+                         * Gets the value of the content property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getContent() {
+                            return content;
+                        }
+
+                        /**
+                         * Sets the value of the content property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setContent(String value) {
+                            this.content = value;
+                        }
+
+                        /**
+                         * Gets the value of the dbSource property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getDbSource() {
+                            return dbSource;
+                        }
+
+                        /**
+                         * Sets the value of the dbSource property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setDbSource(String value) {
+                            this.dbSource = value;
+                        }
+
+                        /**
+                         * Gets the value of the property property.
+                         * 
+                         * @return
+                         *     possible object is
+                         *     {@link String }
+                         *     
+                         */
+                        public String getProperty() {
+                            return property;
+                        }
+
+                        /**
+                         * Sets the value of the property property.
+                         * 
+                         * @param value
+                         *     allowed object is
+                         *     {@link String }
+                         *     
+                         */
+                        public void setProperty(String value) {
+                            this.property = value;
+                        }
+
+                    }
+
+                }
+
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * <complexType>
+             *   <complexContent>
+             *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+             *     </restriction>
+             *   </complexContent>
+             * </complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "", propOrder = {
+                "content"
+            })
+            public static class SegmentDetail {
+
+                @XmlValue
+                protected String content;
+                @XmlAttribute(name = "dbSource")
+                protected String dbSource;
+                @XmlAttribute(name = "property", required = true)
+                protected String property;
+
+                /**
+                 * Gets the value of the content property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getContent() {
+                    return content;
+                }
+
+                /**
+                 * Sets the value of the content property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setContent(String value) {
+                    this.content = value;
+                }
+
+                /**
+                 * Gets the value of the dbSource property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getDbSource() {
+                    return dbSource;
+                }
+
+                /**
+                 * Sets the value of the dbSource property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setDbSource(String value) {
+                    this.dbSource = value;
+                }
+
+                /**
+                 * Gets the value of the property property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getProperty() {
+                    return property;
+                }
+
+                /**
+                 * Sets the value of the property property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setProperty(String value) {
+                    this.property = value;
+                }
+
+            }
+
+        }
+
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * <complexType>
+     *   <complexContent>
+     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}detail"/>
+     *     </restriction>
+     *   </complexContent>
+     * </complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "content"
+    })
+    public static class EntryDetail {
+
+        @XmlValue
+        protected String content;
+        @XmlAttribute(name = "dbSource")
+        protected String dbSource;
+        @XmlAttribute(name = "property", required = true)
+        protected String property;
+
+        /**
+         * Gets the value of the content property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getContent() {
+            return content;
+        }
+
+        /**
+         * Sets the value of the content property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setContent(String value) {
+            this.content = value;
+        }
+
+        /**
+         * Gets the value of the dbSource property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getDbSource() {
+            return dbSource;
+        }
+
+        /**
+         * Sets the value of the dbSource property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setDbSource(String value) {
+            this.dbSource = value;
+        }
+
+        /**
+         * Gets the value of the property property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getProperty() {
+            return property;
+        }
+
+        /**
+         * Sets the value of the property property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setProperty(String value) {
+            this.property = value;
+        }
+
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * <complexType>
+     *   <complexContent>
+     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       <sequence maxOccurs="unbounded">
+     *         <element name="db">
+     *           <complexType>
+     *             <complexContent>
+     *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                 <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}listdbRef"/>
+     *               </restriction>
+     *             </complexContent>
+     *           </complexType>
+     *         </element>
+     *       </sequence>
+     *     </restriction>
+     *   </complexContent>
+     * </complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "db"
+    })
+    public static class ListDB {
+
+        @XmlElement(required = true)
+        protected List<Entry.ListDB.Db> db;
+
+        /**
+         * Gets the value of the db property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the db property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getDb().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link Entry.ListDB.Db }
+         * 
+         * 
+         */
+        public List<Entry.ListDB.Db> getDb() {
+            if (db == null) {
+                db = new ArrayList<Entry.ListDB.Db>();
+            }
+            return this.db;
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * <complexType>
+         *   <complexContent>
+         *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *       <attGroup ref="{http://www.ebi.ac.uk/pdbe/docs/sifts/dataTypes.xsd}listdbRef"/>
+         *     </restriction>
+         *   </complexContent>
+         * </complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "")
+        public static class Db {
+
+            @XmlAttribute(name = "dbVersion")
+            protected String dbVersion;
+            @XmlAttribute(name = "dbSource", required = true)
+            protected String dbSource;
+            @XmlAttribute(name = "dbCoordSys", required = true)
+            protected String dbCoordSys;
+
+            /**
+             * Gets the value of the dbVersion property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getDbVersion() {
+                return dbVersion;
+            }
+
+            /**
+             * Sets the value of the dbVersion property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setDbVersion(String value) {
+                this.dbVersion = value;
+            }
+
+            /**
+             * Gets the value of the dbSource property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getDbSource() {
+                return dbSource;
+            }
+
+            /**
+             * Sets the value of the dbSource property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setDbSource(String value) {
+                this.dbSource = value;
+            }
+
+            /**
+             * Gets the value of the dbCoordSys property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getDbCoordSys() {
+                return dbCoordSys;
+            }
+
+            /**
+             * Sets the value of the dbCoordSys property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setDbCoordSys(String value) {
+                this.dbCoordSys = value;
+            }
+
+        }
+
+    }
+
+}
diff --git a/src/jalview/xml/binding/sifts/ObjectFactory.java b/src/jalview/xml/binding/sifts/ObjectFactory.java
new file mode 100644
index 0000000..7b74e13
--- /dev/null
+++ b/src/jalview/xml/binding/sifts/ObjectFactory.java
@@ -0,0 +1,319 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
+// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
+// Any modifications to this file will be lost upon recompilation of the source schema. 
+// Generated on: 2015.10.09 at 03:18:33 PM BST 
+//
+
+
+package jalview.xml.binding.sifts;
+
+import javax.xml.bind.annotation.XmlRegistry;
+
+
+/**
+ * This object contains factory methods for each 
+ * Java content interface and Java element interface 
+ * generated in the jalview.xml.binding.sifts package. 
+ * <p>An ObjectFactory allows you to programatically 
+ * construct new instances of the Java representation 
+ * for XML content. The Java representation of XML 
+ * content can consist of schema derived interfaces 
+ * and classes representing the binding of schema 
+ * type definitions, element declarations and model 
+ * groups.  Factory methods for each of these are 
+ * provided in this class.
+ * 
+ */
+ at XmlRegistry
+public class ObjectFactory {
+
+
+    /**
+     * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: jalview.xml.binding.sifts
+     * 
+     */
+    public ObjectFactory() {
+    }
+
+    /**
+     * Create an instance of {@link Entry }
+     * 
+     */
+    public Entry createEntry() {
+        return new Entry();
+    }
+
+    /**
+     * Create an instance of {@link Alignment }
+     * 
+     */
+    public Alignment createAlignment() {
+        return new Alignment();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Geo3D }
+     * 
+     */
+    public Alignment.Geo3D createAlignmentGeo3D() {
+        return new Alignment.Geo3D();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Geo3D.Matrix }
+     * 
+     */
+    public Alignment.Geo3D.Matrix createAlignmentGeo3DMatrix() {
+        return new Alignment.Geo3D.Matrix();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Block }
+     * 
+     */
+    public Alignment.Block createAlignmentBlock() {
+        return new Alignment.Block();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.AlignObject }
+     * 
+     */
+    public Alignment.AlignObject createAlignmentAlignObject() {
+        return new Alignment.AlignObject();
+    }
+
+    /**
+     * Create an instance of {@link Entry.Entity }
+     * 
+     */
+    public Entry.Entity createEntryEntity() {
+        return new Entry.Entity();
+    }
+
+    /**
+     * Create an instance of {@link Entry.Entity.Segment }
+     * 
+     */
+    public Entry.Entity.Segment createEntryEntitySegment() {
+        return new Entry.Entity.Segment();
+    }
+
+    /**
+     * Create an instance of {@link Entry.Entity.Segment.ListMapRegion }
+     * 
+     */
+    public Entry.Entity.Segment.ListMapRegion createEntryEntitySegmentListMapRegion() {
+        return new Entry.Entity.Segment.ListMapRegion();
+    }
+
+    /**
+     * Create an instance of {@link Entry.Entity.Segment.ListMapRegion.MapRegion }
+     * 
+     */
+    public Entry.Entity.Segment.ListMapRegion.MapRegion createEntryEntitySegmentListMapRegionMapRegion() {
+        return new Entry.Entity.Segment.ListMapRegion.MapRegion();
+    }
+
+    /**
+     * Create an instance of {@link Entry.Entity.Segment.ListMapRegion.MapRegion.Db }
+     * 
+     */
+    public Entry.Entity.Segment.ListMapRegion.MapRegion.Db createEntryEntitySegmentListMapRegionMapRegionDb() {
+        return new Entry.Entity.Segment.ListMapRegion.MapRegion.Db();
+    }
+
+    /**
+     * Create an instance of {@link Entry.Entity.Segment.ListResidue }
+     * 
+     */
+    public Entry.Entity.Segment.ListResidue createEntryEntitySegmentListResidue() {
+        return new Entry.Entity.Segment.ListResidue();
+    }
+
+    /**
+     * Create an instance of {@link Entry.Entity.Segment.ListResidue.Residue }
+     * 
+     */
+    public Entry.Entity.Segment.ListResidue.Residue createEntryEntitySegmentListResidueResidue() {
+        return new Entry.Entity.Segment.ListResidue.Residue();
+    }
+
+    /**
+     * Create an instance of {@link Entry.ListDB }
+     * 
+     */
+    public Entry.ListDB createEntryListDB() {
+        return new Entry.ListDB();
+    }
+
+    /**
+     * Create an instance of {@link Entry.EntryDetail }
+     * 
+     */
+    public Entry.EntryDetail createEntryEntryDetail() {
+        return new Entry.EntryDetail();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Score }
+     * 
+     */
+    public Alignment.Score createAlignmentScore() {
+        return new Alignment.Score();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Geo3D.Vector }
+     * 
+     */
+    public Alignment.Geo3D.Vector createAlignmentGeo3DVector() {
+        return new Alignment.Geo3D.Vector();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Geo3D.Matrix.Max11 }
+     * 
+     */
+    public Alignment.Geo3D.Matrix.Max11 createAlignmentGeo3DMatrixMax11() {
+        return new Alignment.Geo3D.Matrix.Max11();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Geo3D.Matrix.Max12 }
+     * 
+     */
+    public Alignment.Geo3D.Matrix.Max12 createAlignmentGeo3DMatrixMax12() {
+        return new Alignment.Geo3D.Matrix.Max12();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Geo3D.Matrix.Max13 }
+     * 
+     */
+    public Alignment.Geo3D.Matrix.Max13 createAlignmentGeo3DMatrixMax13() {
+        return new Alignment.Geo3D.Matrix.Max13();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Geo3D.Matrix.Max21 }
+     * 
+     */
+    public Alignment.Geo3D.Matrix.Max21 createAlignmentGeo3DMatrixMax21() {
+        return new Alignment.Geo3D.Matrix.Max21();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Geo3D.Matrix.Max22 }
+     * 
+     */
+    public Alignment.Geo3D.Matrix.Max22 createAlignmentGeo3DMatrixMax22() {
+        return new Alignment.Geo3D.Matrix.Max22();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Geo3D.Matrix.Max23 }
+     * 
+     */
+    public Alignment.Geo3D.Matrix.Max23 createAlignmentGeo3DMatrixMax23() {
+        return new Alignment.Geo3D.Matrix.Max23();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Geo3D.Matrix.Max31 }
+     * 
+     */
+    public Alignment.Geo3D.Matrix.Max31 createAlignmentGeo3DMatrixMax31() {
+        return new Alignment.Geo3D.Matrix.Max31();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Geo3D.Matrix.Max32 }
+     * 
+     */
+    public Alignment.Geo3D.Matrix.Max32 createAlignmentGeo3DMatrixMax32() {
+        return new Alignment.Geo3D.Matrix.Max32();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Geo3D.Matrix.Max33 }
+     * 
+     */
+    public Alignment.Geo3D.Matrix.Max33 createAlignmentGeo3DMatrixMax33() {
+        return new Alignment.Geo3D.Matrix.Max33();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.Block.Segment }
+     * 
+     */
+    public Alignment.Block.Segment createAlignmentBlockSegment() {
+        return new Alignment.Block.Segment();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.AlignObject.AlignObjectDetail }
+     * 
+     */
+    public Alignment.AlignObject.AlignObjectDetail createAlignmentAlignObjectAlignObjectDetail() {
+        return new Alignment.AlignObject.AlignObjectDetail();
+    }
+
+    /**
+     * Create an instance of {@link Alignment.AlignObject.Sequence }
+     * 
+     */
+    public Alignment.AlignObject.Sequence createAlignmentAlignObjectSequence() {
+        return new Alignment.AlignObject.Sequence();
+    }
+
+    /**
+     * Create an instance of {@link Entry.Entity.EntityDetail }
+     * 
+     */
+    public Entry.Entity.EntityDetail createEntryEntityEntityDetail() {
+        return new Entry.Entity.EntityDetail();
+    }
+
+    /**
+     * Create an instance of {@link Entry.Entity.Segment.SegmentDetail }
+     * 
+     */
+    public Entry.Entity.Segment.SegmentDetail createEntryEntitySegmentSegmentDetail() {
+        return new Entry.Entity.Segment.SegmentDetail();
+    }
+
+    /**
+     * Create an instance of {@link Entry.Entity.Segment.ListMapRegion.MapRegion.Db.DbDetail }
+     * 
+     */
+    public Entry.Entity.Segment.ListMapRegion.MapRegion.Db.DbDetail createEntryEntitySegmentListMapRegionMapRegionDbDbDetail() {
+        return new Entry.Entity.Segment.ListMapRegion.MapRegion.Db.DbDetail();
+    }
+
+    /**
+     * Create an instance of {@link Entry.Entity.Segment.ListResidue.Residue.CrossRefDb }
+     * 
+     */
+    public Entry.Entity.Segment.ListResidue.Residue.CrossRefDb createEntryEntitySegmentListResidueResidueCrossRefDb() {
+        return new Entry.Entity.Segment.ListResidue.Residue.CrossRefDb();
+    }
+
+    /**
+     * Create an instance of {@link Entry.Entity.Segment.ListResidue.Residue.ResidueDetail }
+     * 
+     */
+    public Entry.Entity.Segment.ListResidue.Residue.ResidueDetail createEntryEntitySegmentListResidueResidueResidueDetail() {
+        return new Entry.Entity.Segment.ListResidue.Residue.ResidueDetail();
+    }
+
+    /**
+     * Create an instance of {@link Entry.ListDB.Db }
+     * 
+     */
+    public Entry.ListDB.Db createEntryListDBDb() {
+        return new Entry.ListDB.Db();
+    }
+
+}
diff --git a/src/jalview/xml/binding/sifts/package-info.java b/src/jalview/xml/binding/sifts/package-info.java
new file mode 100644
index 0000000..aac24fe
--- /dev/null
+++ b/src/jalview/xml/binding/sifts/package-info.java
@@ -0,0 +1,9 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
+// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
+// Any modifications to this file will be lost upon recompilation of the source schema. 
+// Generated on: 2015.10.09 at 03:18:33 PM BST 
+//
+
+ at javax.xml.bind.annotation.XmlSchema(namespace = "http://www.ebi.ac.uk/pdbe/docs/sifts/eFamily.xsd", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
+package jalview.xml.binding.sifts;
diff --git a/src/org/jibble/epsgraphics/EpsDocument.java b/src/org/jibble/epsgraphics/EpsDocument.java
index 1e71632..2acef39 100644
--- a/src/org/jibble/epsgraphics/EpsDocument.java
+++ b/src/org/jibble/epsgraphics/EpsDocument.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/org/jibble/epsgraphics/EpsException.java b/src/org/jibble/epsgraphics/EpsException.java
index faaaa07..3e04614 100644
--- a/src/org/jibble/epsgraphics/EpsException.java
+++ b/src/org/jibble/epsgraphics/EpsException.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/org/jibble/epsgraphics/EpsGraphics2D.java b/src/org/jibble/epsgraphics/EpsGraphics2D.java
index 34a4ae2..f2452e1 100644
--- a/src/org/jibble/epsgraphics/EpsGraphics2D.java
+++ b/src/org/jibble/epsgraphics/EpsGraphics2D.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/picr/model/CrossReference.java b/src/uk/ac/ebi/picr/model/CrossReference.java
index edadddc..1d8da40 100644
--- a/src/uk/ac/ebi/picr/model/CrossReference.java
+++ b/src/uk/ac/ebi/picr/model/CrossReference.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/picr/model/CrossReference_Helper.java b/src/uk/ac/ebi/picr/model/CrossReference_Helper.java
index 5b5c3f7..a1f95cf 100644
--- a/src/uk/ac/ebi/picr/model/CrossReference_Helper.java
+++ b/src/uk/ac/ebi/picr/model/CrossReference_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/picr/model/UPEntry.java b/src/uk/ac/ebi/picr/model/UPEntry.java
index ca9b1f4..3f62128 100644
--- a/src/uk/ac/ebi/picr/model/UPEntry.java
+++ b/src/uk/ac/ebi/picr/model/UPEntry.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/picr/model/UPEntry_Helper.java b/src/uk/ac/ebi/picr/model/UPEntry_Helper.java
index fba9b44..eaf788f 100644
--- a/src/uk/ac/ebi/picr/model/UPEntry_Helper.java
+++ b/src/uk/ac/ebi/picr/model/UPEntry_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/www/Data.java b/src/uk/ac/ebi/www/Data.java
index 9a71fdd..9fd4384 100644
--- a/src/uk/ac/ebi/www/Data.java
+++ b/src/uk/ac/ebi/www/Data.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/www/InputParams.java b/src/uk/ac/ebi/www/InputParams.java
index 696214a..08c7b9f 100644
--- a/src/uk/ac/ebi/www/InputParams.java
+++ b/src/uk/ac/ebi/www/InputParams.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/www/WSFile.java b/src/uk/ac/ebi/www/WSFile.java
index 04dfe41..1e38b45 100644
--- a/src/uk/ac/ebi/www/WSFile.java
+++ b/src/uk/ac/ebi/www/WSFile.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/www/WSWUBlast.java b/src/uk/ac/ebi/www/WSWUBlast.java
index e540ebc..5ec155e 100644
--- a/src/uk/ac/ebi/www/WSWUBlast.java
+++ b/src/uk/ac/ebi/www/WSWUBlast.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/www/WSWUBlastService.java b/src/uk/ac/ebi/www/WSWUBlastService.java
index ee9ee35..96f0838 100644
--- a/src/uk/ac/ebi/www/WSWUBlastService.java
+++ b/src/uk/ac/ebi/www/WSWUBlastService.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/www/WSWUBlastServiceLocator.java b/src/uk/ac/ebi/www/WSWUBlastServiceLocator.java
index ae8b2a8..ce6c6fa 100644
--- a/src/uk/ac/ebi/www/WSWUBlastServiceLocator.java
+++ b/src/uk/ac/ebi/www/WSWUBlastServiceLocator.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/www/WSWUBlastSoapBindingStub.java b/src/uk/ac/ebi/www/WSWUBlastSoapBindingStub.java
index ce192ee..fefabff 100644
--- a/src/uk/ac/ebi/www/WSWUBlastSoapBindingStub.java
+++ b/src/uk/ac/ebi/www/WSWUBlastSoapBindingStub.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperBindingStub.java b/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperBindingStub.java
index 0e679dc..06898a7 100644
--- a/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperBindingStub.java
+++ b/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperBindingStub.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperInterface.java b/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperInterface.java
index 7bff797..37a2723 100644
--- a/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperInterface.java
+++ b/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperInterface.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperService.java b/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperService.java
index a384dbe..12579f2 100644
--- a/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperService.java
+++ b/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperService.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperServiceLocator.java b/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperServiceLocator.java
index 3936f73..1d12b3e 100644
--- a/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperServiceLocator.java
+++ b/src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperServiceLocator.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/IMsaWS.java b/src/vamsas/IMsaWS.java
index 1a5d92d..02a1e84 100644
--- a/src/vamsas/IMsaWS.java
+++ b/src/vamsas/IMsaWS.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/Alignment.java b/src/vamsas/objects/simple/Alignment.java
index 7070015..09429fc 100644
--- a/src/vamsas/objects/simple/Alignment.java
+++ b/src/vamsas/objects/simple/Alignment.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/Alignment_Helper.java b/src/vamsas/objects/simple/Alignment_Helper.java
index 1e31e0b..5e0f0cb 100644
--- a/src/vamsas/objects/simple/Alignment_Helper.java
+++ b/src/vamsas/objects/simple/Alignment_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/JpredResult.java b/src/vamsas/objects/simple/JpredResult.java
index 5e28795..8c8dc07 100644
--- a/src/vamsas/objects/simple/JpredResult.java
+++ b/src/vamsas/objects/simple/JpredResult.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/JpredResult_Helper.java b/src/vamsas/objects/simple/JpredResult_Helper.java
index 36c65ee..bae32b6 100644
--- a/src/vamsas/objects/simple/JpredResult_Helper.java
+++ b/src/vamsas/objects/simple/JpredResult_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/MsaResult.java b/src/vamsas/objects/simple/MsaResult.java
index 06c9853..6f686b9 100644
--- a/src/vamsas/objects/simple/MsaResult.java
+++ b/src/vamsas/objects/simple/MsaResult.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/MsaResult_Helper.java b/src/vamsas/objects/simple/MsaResult_Helper.java
index 4a140b7..e9716c4 100644
--- a/src/vamsas/objects/simple/MsaResult_Helper.java
+++ b/src/vamsas/objects/simple/MsaResult_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/Msfalignment.java b/src/vamsas/objects/simple/Msfalignment.java
index 39814f9..6a0bdeb 100644
--- a/src/vamsas/objects/simple/Msfalignment.java
+++ b/src/vamsas/objects/simple/Msfalignment.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/Msfalignment_Helper.java b/src/vamsas/objects/simple/Msfalignment_Helper.java
index 47dbd9c..cd70e65 100644
--- a/src/vamsas/objects/simple/Msfalignment_Helper.java
+++ b/src/vamsas/objects/simple/Msfalignment_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/Object.java b/src/vamsas/objects/simple/Object.java
index 713db9b..84eab5f 100644
--- a/src/vamsas/objects/simple/Object.java
+++ b/src/vamsas/objects/simple/Object.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/Object_Helper.java b/src/vamsas/objects/simple/Object_Helper.java
index 0b81b50..5d2f321 100644
--- a/src/vamsas/objects/simple/Object_Helper.java
+++ b/src/vamsas/objects/simple/Object_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/Result.java b/src/vamsas/objects/simple/Result.java
index 2361016..fbf5f5e 100644
--- a/src/vamsas/objects/simple/Result.java
+++ b/src/vamsas/objects/simple/Result.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/Result_Helper.java b/src/vamsas/objects/simple/Result_Helper.java
index 6758287..cb67282 100644
--- a/src/vamsas/objects/simple/Result_Helper.java
+++ b/src/vamsas/objects/simple/Result_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/Secstructpred.java b/src/vamsas/objects/simple/Secstructpred.java
index ad7d67c..3af58b1 100644
--- a/src/vamsas/objects/simple/Secstructpred.java
+++ b/src/vamsas/objects/simple/Secstructpred.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/Secstructpred_Helper.java b/src/vamsas/objects/simple/Secstructpred_Helper.java
index 646eafa..6048909 100644
--- a/src/vamsas/objects/simple/Secstructpred_Helper.java
+++ b/src/vamsas/objects/simple/Secstructpred_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/SeqSearchResult.java b/src/vamsas/objects/simple/SeqSearchResult.java
index 7813607..d2a778e 100644
--- a/src/vamsas/objects/simple/SeqSearchResult.java
+++ b/src/vamsas/objects/simple/SeqSearchResult.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/SeqSearchResult_Helper.java b/src/vamsas/objects/simple/SeqSearchResult_Helper.java
index b5a112f..b4f4cb8 100644
--- a/src/vamsas/objects/simple/SeqSearchResult_Helper.java
+++ b/src/vamsas/objects/simple/SeqSearchResult_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/Sequence.java b/src/vamsas/objects/simple/Sequence.java
index 0943dce..3e3be1d 100644
--- a/src/vamsas/objects/simple/Sequence.java
+++ b/src/vamsas/objects/simple/Sequence.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/SequenceSet.java b/src/vamsas/objects/simple/SequenceSet.java
index 9f024bc..7c7b5c5 100644
--- a/src/vamsas/objects/simple/SequenceSet.java
+++ b/src/vamsas/objects/simple/SequenceSet.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/SequenceSet_Helper.java b/src/vamsas/objects/simple/SequenceSet_Helper.java
index 59badf3..37d16b5 100644
--- a/src/vamsas/objects/simple/SequenceSet_Helper.java
+++ b/src/vamsas/objects/simple/SequenceSet_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/Sequence_Helper.java b/src/vamsas/objects/simple/Sequence_Helper.java
index 2f24b7b..2f351e7 100644
--- a/src/vamsas/objects/simple/Sequence_Helper.java
+++ b/src/vamsas/objects/simple/Sequence_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/WsJobId.java b/src/vamsas/objects/simple/WsJobId.java
index 3efaf36..9936a22 100644
--- a/src/vamsas/objects/simple/WsJobId.java
+++ b/src/vamsas/objects/simple/WsJobId.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
diff --git a/src/vamsas/objects/simple/WsJobId_Helper.java b/src/vamsas/objects/simple/WsJobId_Helper.java
index 4fa1555..9cf088e 100644
--- a/src/vamsas/objects/simple/WsJobId_Helper.java
+++ b/src/vamsas/objects/simple/WsJobId_Helper.java
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (2.10.1)
+ * Copyright (C) 2016 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jalview.git



More information about the pkg-java-commits mailing list