Merge JAL-2136_phyre2_integration to develop
authortcofoegbu <tcnofoegbu@dundee.ac.uk>
Mon, 20 Mar 2017 15:14:27 +0000 (15:14 +0000)
committertcofoegbu <tcnofoegbu@dundee.ac.uk>
Mon, 20 Mar 2017 15:14:27 +0000 (15:14 +0000)
14 files changed:
1  2 
resources/lang/Messages.properties
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AppletJmol.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/rbvi/chimera/AtomSpecModel.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AppJmol.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/StructureViewerBase.java
src/jalview/io/AnnotationFile.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/ws/sifts/SiftsClient.java
test/jalview/structures/models/AAStructureBindingModelTest.java

@@@ -61,7 -61,6 +61,6 @@@ action.set_as_reference = Set as Refere
  action.remove = Remove
  action.remove_redundancy = Remove Redundancy...
  action.pairwise_alignment = Pairwise Alignment
- action.by_rna_helixes = By RNA Helices
  action.user_defined = User Defined...
  action.by_conservation = By Conservation
  action.wrap = Wrap
@@@ -139,7 -138,8 +138,8 @@@ action.view_flanking_regions = Show fla
  label.view_flanking_regions = Show sequence data either side of the subsequences involved in this alignment
  label.structures_manager = Structures Manager
  label.nickname = Nickname:
- label.url = URL:
+ label.url = URL
+ label.url\: = URL:
  label.input_file_url = Enter URL or Input File
  label.select_feature = Select feature
  label.name = Name
@@@ -179,27 -179,30 +179,30 @@@ label.score_model_conservation = Physic
  label.score_model_enhconservation = Physicochemical property conservation
  label.status_bar = Status bar
  label.out_to_textbox = Output to Textbox
- label.clustalx = Clustalx
+ # delete Clustal - use FileFormat name instead
  label.clustal = Clustal
- label.zappo = Zappo
- label.taylor = Taylor
+ # label.colourScheme_<schemeName> as in JalviewColourScheme
+ label.colourScheme_clustal = Clustalx
+ label.colourScheme_blosum62 = BLOSUM62 Score
+ label.colourScheme_%_identity = Percentage Identity
+ label.colourScheme_zappo = Zappo
+ label.colourScheme_taylor = Taylor
+ label.colourScheme_hydrophobic = Hydrophobicity
+ label.colourScheme_helix_propensity = Helix Propensity
+ label.colourScheme_strand_propensity = Strand Propensity
+ label.colourScheme_turn_propensity = Turn Propensity
+ label.colourScheme_buried_index = Buried Index
+ label.colourScheme_purine/pyrimidine = Purine/Pyrimidine
+ label.colourScheme_nucleotide = Nucleotide
+ label.colourScheme_t-coffee_scores = T-Coffee Scores
+ label.colourScheme_rna_helices = By RNA Helices
  label.blc = BLC
  label.fasta = Fasta
  label.msf = MSF
  label.pfam = PFAM
  label.pileup = Pileup
  label.pir = PIR
- label.hydrophobicity = Hydrophobicity
- label.helix_propensity = Helix Propensity
- label.strand_propensity = Strand Propensity
- label.turn_propensity = Turn Propensity
- label.buried_index = Buried Index
- label.purine_pyrimidine = Purine/Pyrimidine
- label.percentage_identity = Percentage Identity
- label.blosum62 = BLOSUM62
- label.blosum62_score = BLOSUM62 Score
- label.tcoffee_scores = T-Coffee Scores
- label.average_distance_bloslum62 = Average Distance Using BLOSUM62
+ label.average_distance_blosum62 = Average Distance Using BLOSUM62
  label.neighbour_blosum62 = Neighbour Joining Using BLOSUM62
  label.show_annotations = Show annotations
  label.hide_annotations = Hide annotations
@@@ -211,7 -214,7 +214,7 @@@ label.hide_all = Hide al
  label.add_reference_annotations = Add reference annotations
  label.find_tip = Search alignment, selection or sequence ids for a subsequence (ignoring gaps).<br>Accepts regular expressions - search Help for 'regex' for details.
  label.colour_text = Colour Text
- label.show_non_conversed = Show nonconserved
+ label.show_non_conserved = Show nonconserved
  label.overview_window = Overview Window
  label.none = None
  label.above_identity_threshold = Above Identity Threshold
@@@ -323,7 -326,7 +326,7 @@@ label.size = Size
  label.style = Style:
  label.calculating = Calculating....
  label.modify_conservation_visibility = Modify conservation visibility
- label.colour_residues_above_occurence = Colour residues above % occurence
+ label.colour_residues_above_occurrence = Colour residues above % occurrence
  label.set_this_label_text = set this label text
  label.sequences_from = Sequences from {0}
  label.successfully_loaded_file  = Successfully loaded file {0}
@@@ -411,7 -414,6 +414,6 @@@ label.couldnt_import_as_vamsas_session 
  label.vamsas_document_import_failed = Vamsas Document Import Failed
  label.couldnt_locate = Couldn't locate {0}
  label.url_not_found = URL not found
- label.no_link_selected = No link selected
  label.new_sequence_url_link = New sequence URL link
  label.cannot_edit_annotations_in_wrapped_view = Cannot edit annotations in wrapped view
  label.wrapped_view_no_edit = Wrapped view - no edit
@@@ -513,7 -515,7 +515,7 @@@ label.retrieve_parse_sequence_database_
  label.standard_databases = Standard Databases
  label.fetch_embl_uniprot = Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources
  label.reset_min_max_colours_to_defaults = Reset min and max colours to defaults from user preferences.
- label.align_structures_using_linked_alignment_views = Align structures using {0} linked alignment views
+ label.align_structures_using_linked_alignment_views = Superpose structures using {0} selected alignment view(s)
  label.connect_to_session = Connect to session {0}
  label.threshold_feature_display_by_score = Threshold the feature display by score.
  label.threshold_feature_no_threshold = No Threshold
@@@ -618,6 -620,8 +620,8 @@@ label.web_services = Web Service
  label.right_click_to_edit_currently_selected_parameter = Right click to edit currently selected parameter.
  label.let_jmol_manage_structure_colours = Let Jmol manage structure colours
  label.let_chimera_manage_structure_colours = Let Chimera manage structure colours
+ label.fetch_chimera_attributes = Fetch Chimera attributes
+ label.fetch_chimera_attributes_tip = Copy Chimera attribute to Jalview feature
  label.marks_leaves_tree_not_associated_with_sequence = Marks leaves of tree not associated with a sequence
  label.index_web_services_menu_by_host_site = Index web services in menu by the host site
  label.option_want_informed_web_service_URL_cannot_be_accessed_jalview_when_starts_up = Check this option if you want to be informed<br>when a web service URL cannot be accessed by Jalview<br>when it starts up
@@@ -670,8 -674,6 +674,6 @@@ action.set_text_colour = Text Colour..
  label.structure = Structure
  label.show_pdbstruct_dialog = 3D Structure Data...
  label.view_rna_structure = VARNA 2D Structure
- label.clustalx_colours = Clustalx colours
- label.above_identity_percentage = Above % Identity
  label.create_sequence_details_report_annotation_for = Annotation for {0}
  label.sequence_details_for = Sequence Details for {0}
  label.sequence_name = Sequence Name
@@@ -713,15 -715,21 +715,21 @@@ label.link_name = Link Nam
  label.pdb_file = PDB file
  label.colour_with_jmol = Colour with Jmol
  label.colour_with_chimera = Colour with Chimera
- label.align_structures = Align Structures
+ label.superpose_structures = Superpose Structures
+ error.superposition_failed = Superposition failed: {0}
+ label.insufficient_residues = Not enough aligned residues ({0}) to perform superposition
  label.jmol = Jmol
  label.chimera = Chimera
+ label.create_chimera_attributes = Write Jalview features
+ label.create_chimera_attributes_tip = Set Chimera residue attributes for visible features
+ label.attributes_set = {0} attribute values set on Chimera
  label.sort_alignment_by_tree = Sort Alignment By Tree
  label.mark_unlinked_leaves = Mark Unlinked Leaves
  label.associate_leaves_with = Associate Leaves With
  label.save_colour_scheme_with_unique_name_added_to_colour_menu = Save your colour scheme with a unique name and it will be added to the Colour menu
  label.case_sensitive = Case Sensitive
- label.lower_case_colour = Lower Case Colour
+ label.lower_case_colour = Colour All Lower Case
+ label.lower_case_tip = Chosen colour applies to all lower case symbols
  label.index_by_host = Index by Host
  label.index_by_type = Index by Type
  label.enable_jabaws_services = Enable JABAWS Services
@@@ -770,7 -778,7 +778,7 @@@ label.original_data_for_params = Origin
  label.points_for_params = Points for {0}
  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.select_background_colour = Select Background Colour
  label.invalid_font = Invalid Font
  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 ";"
@@@ -960,7 -968,6 +968,6 @@@ error.implementation_error_maplist_is_n
  error.implementation_error_cannot_have_null_alignment = Implementation error: Cannot have null alignment property key
  error.implementation_error_null_fileparse = Implementation error. Null FileParse in copy constructor
  error.implementation_error_cannot_map_alignment_sequences = IMPLEMENTATION ERROR: Cannot map an alignment of sequences from different datasets into a single alignment in the vamsas document.
- error.implementation_error_cannot_duplicate_colour_scheme = Serious implementation error: cannot duplicate colourscheme {0}
  error.implementation_error_structure_selection_manager_null = Implementation error. Structure selection manager's context is 'null'
  exception.ssm_context_is_null = SSM context is null
  error.idstring_seqstrings_only_one_per_sequence = idstrings and seqstrings contain one string each per sequence
@@@ -1025,7 -1032,6 +1032,6 @@@ exception.replace_null_regex_pointer = 
  exception.bad_pattern_to_regex_perl_code = bad pattern to Regex.perlCode: {0}
  exception.no_stub_implementation_for_interface = There is no stub implementation for the interface: {0}
  exception.cannot_set_endpoint_address_unknown_port = Cannot set Endpoint Address for Unknown Port {0}
- exception.querying_matching_opening_parenthesis_for_non_closing_parenthesis = Querying matching opening parenthesis for non-closing parenthesis character {0}
  exception.mismatched_unseen_closing_char = Mismatched (unseen) closing character {0}
  exception.mismatched_closing_char = Mismatched closing character {0}
  exception.mismatched_opening_char = Mismatched opening character {0} at {1}
@@@ -1036,7 -1042,6 +1042,6 @@@ exception.couldnt_parse_responde_from_a
  exception.application_test_npe = Application test: throwing an NullPointerException It should arrive at the console
  exception.overwriting_vamsas_id_binding = Overwriting vamsas id binding
  exception.overwriting_jalview_id_binding = Overwriting jalview id binding
- error.implementation_error_unknown_file_format_string = Implementation error: Unknown file format string
  exception.failed_to_resolve_gzip_stream = Failed to resolve GZIP stream
  exception.problem_opening_file_also_tried = Problem opening {0} (also tried {1}) : {2}
  exception.problem_opening_file = Problem opening {0} : {1}
@@@ -1232,7 -1237,6 +1237,6 @@@ action.export_hidden_columns = Export H
  action.export_hidden_sequences = Export Hidden Sequences
  action.export_features = Export Features
  label.export_settings = Export Settings
- label.save_as_biojs_html = Save as BioJs HTML
  label.pdb_web-service_error = PDB Web-service Error
  label.structure_chooser_manual_association = Structure Chooser - Manual association
  label.structure_chooser_filter_time = Structure Chooser - Filter time ({0})
@@@ -1274,7 -1278,21 +1278,27 @@@ label.SEQUENCE_ID_no_longer_used = $SEQ
  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
+ exception.url_cannot_have_miriam_id = {0} is a MIRIAM id and cannot be used as a custom url name
+ exception.url_cannot_have_duplicate_id = {0} cannot be used as a label for more than one line
+ label.filter = Filter text:
+ action.customfilter = Custom only
+ action.showall = Show All
+ label.insert = Insert:
+ action.seq_id = $SEQUENCE_ID$
+ action.db_acc = $DB_ACCESSION$
+ label.primary = Double Click
+ label.inmenu = In Menu
+ label.id = ID
+ label.database = Database
+ label.urltooltip = Only one url, which must use a sequence id, can be selected for the 'On Click' option
+ label.edit_sequence_url_link = Edit sequence URL link
+ warn.name_cannot_be_duplicate = User-defined URL names must be unique and cannot be MIRIAM ids
+ label.invalid_name = Invalid Name !
  label.output_seq_details = Output Sequence Details to list all database references
++<<<<<<< HEAD
 +label.phyre2_model_prediction = 3D Protein Model prediction with Phyre2
 +label.run_phyre2_prediction = Run Phyre2 Prediction
- status.obtaining_mapping_with_phyre2_template_alignment = Obtaining mapping with Phyre2 Template alignment 
++status.obtaining_mapping_with_phyre2_template_alignment = Obtaining mapping with Phyre2 Template alignment 
++=======
+ label.urllinks = Links
++>>>>>>> develop
@@@ -52,6 -52,8 +52,8 @@@ import jalview.io.AppletFormatAdapter
  import jalview.io.DataSourceType;
  import jalview.io.FeaturesFile;
  import jalview.io.FileFormat;
+ import jalview.io.FileFormatI;
+ import jalview.io.FileFormats;
  import jalview.io.TCoffeeScoreFile;
  import jalview.schemes.Blosum62ColourScheme;
  import jalview.schemes.BuriedColourScheme;
@@@ -62,7 -64,7 +64,7 @@@ import jalview.schemes.HydrophobicColou
  import jalview.schemes.NucleotideColourScheme;
  import jalview.schemes.PIDColourScheme;
  import jalview.schemes.PurinePyrimidineColourScheme;
- import jalview.schemes.RNAHelicesColourChooser;
+ import jalview.schemes.RNAHelicesColour;
  import jalview.schemes.StrandColourScheme;
  import jalview.schemes.TCoffeeColourScheme;
  import jalview.schemes.TaylorColourScheme;
@@@ -282,6 -284,16 +284,16 @@@ public class AlignFrame extends Embmenu
      }
      if (viewport.getAlignment().isNucleotide())
      {
+       conservationMenuItem.setEnabled(false);
+       clustalColour.setEnabled(false);
+       BLOSUM62Colour.setEnabled(false);
+       zappoColour.setEnabled(false);
+       taylorColour.setEnabled(false);
+       hydrophobicityColour.setEnabled(false);
+       helixColour.setEnabled(false);
+       strandColour.setEnabled(false);
+       turnColour.setEnabled(false);
+       buriedColour.setEnabled(false);
        viewport.updateStrucConsensus(alignPanel);
        if (viewport.getAlignment().hasRNAStructure())
        {
      {
        RNAHelixColour.setEnabled(false);
        purinePyrimidineColour.setEnabled(false);
+       nucleotideColour.setEnabled(false);
      }
      // Some JVMS send keyevents to Top frame or lowest panel,
      // Havent worked out why yet. So add to both this frame and seqCanvas for
      // }
      else if (source == RNAHelixColour)
      {
-       new RNAHelicesColourChooser(viewport, alignPanel);
+       changeColour(new RNAHelicesColour(viewport.getAlignment()));
+       // new RNAHelicesColourChooser(viewport, alignPanel);
      }
      else if (source == modifyPID)
      {
              "label.alignment_output_command",
              new Object[] { e.getActionCommand() }), 600, 500);
  
-     FileFormat fileFormat = FileFormat.valueOf(e.getActionCommand());
+     FileFormatI fileFormat = FileFormats.getInstance().forName(
+             e.getActionCommand());
      cap.setText(new AppletFormatAdapter(alignPanel).formatSequences(
              fileFormat, viewport.getAlignment(),
              viewport.getShowJVSuffix()));
    @Override
    public void changeColour(ColourSchemeI cs)
    {
-     if (cs != null)
-     {
-       if (viewport.getAbovePIDThreshold())
-       {
-         viewport.setThreshold(SliderPanel.setPIDSliderSource(alignPanel,
-                 cs, "Background"));
-       }
-       if (viewport.getConservationSelected())
-       {
-         cs.setConservationApplied(true);
-         viewport.setIncrement(SliderPanel.setConservationSlider(alignPanel,
-                 cs, "Background"));
-       }
-       else
-       {
-         cs.setConservationApplied(false);
-       }
-     }
      viewport.setGlobalColourScheme(cs);
  
      alignPanel.paintAlignment(true);
              && viewport.getGlobalColourScheme() != null)
      {
        SliderPanel.setPIDSliderSource(alignPanel,
-               viewport.getGlobalColourScheme(), "Background");
+               viewport.getResidueShading(), alignPanel.getViewName());
        SliderPanel.showPIDSlider();
      }
    }
              && viewport.getGlobalColourScheme() != null)
      {
        SliderPanel.setConservationSlider(alignPanel,
-               viewport.getGlobalColourScheme(), "Background");
+               viewport.getResidueShading(), alignPanel.getViewName());
        SliderPanel.showConservationSlider();
      }
    }
  
    protected void conservationMenuItem_actionPerformed()
    {
-     viewport.setConservationSelected(conservationMenuItem.getState());
+     boolean selected = conservationMenuItem.getState();
+     modifyConservation.setEnabled(selected);
+     viewport.setConservationSelected(selected);
  
-     viewport.setAbovePIDThreshold(false);
-     abovePIDThreshold.setState(false);
+     // viewport.setAbovePIDThreshold(false);
+     // abovePIDThreshold.setState(false);
  
      changeColour(viewport.getGlobalColourScheme());
  
-     modifyConservation_actionPerformed();
+     if (selected)
+     {
+       modifyConservation_actionPerformed();
+     }
+     else
+     {
+       SliderPanel.hideConservationSlider();
+     }
    }
  
    public void abovePIDThreshold_actionPerformed()
    {
-     viewport.setAbovePIDThreshold(abovePIDThreshold.getState());
-     conservationMenuItem.setState(false);
-     viewport.setConservationSelected(false);
+     boolean selected = abovePIDThreshold.getState();
+     modifyPID.setEnabled(selected);
+     viewport.setAbovePIDThreshold(selected);
+     // conservationMenuItem.setState(false);
+     // viewport.setConservationSelected(false);
  
      changeColour(viewport.getGlobalColourScheme());
  
-     modifyPID_actionPerformed();
+     if (selected)
+     {
+       modifyPID_actionPerformed();
+     }
+     else
+     {
+       SliderPanel.hidePIDSlider();
+     }
    }
  
    public void sortPairwiseMenuItem_actionPerformed()
      inputText.addActionListener(this);
      Menu outputTextboxMenu = new Menu(
              MessageManager.getString("label.out_to_textbox"));
-     for (String ff : FileFormat.getWritableFormats(true))
+     for (String ff : FileFormats.getInstance().getWritableFormats(true))
      {
        MenuItem item = new MenuItem(ff);
  
              .getString("label.colour_text"));
      colourTextMenuItem.addItemListener(this);
      displayNonconservedMenuItem.setLabel(MessageManager
-             .getString("label.show_non_conversed"));
+             .getString("label.show_non_conserved"));
      displayNonconservedMenuItem.addItemListener(this);
      wrapMenuItem.setLabel(MessageManager.getString("action.wrap"));
      wrapMenuItem.addItemListener(this);
              .getString("label.apply_colour_to_all_groups"));
      applyToAllGroups.setState(true);
      applyToAllGroups.addItemListener(this);
-     clustalColour.setLabel(MessageManager.getString("label.clustalx"));
+     clustalColour.setLabel(MessageManager
+             .getString("label.colourScheme_clustal"));
      clustalColour.addActionListener(this);
-     zappoColour.setLabel(MessageManager.getString("label.zappo"));
+     zappoColour.setLabel(MessageManager
+             .getString("label.colourScheme_zappo"));
      zappoColour.addActionListener(this);
-     taylorColour.setLabel(MessageManager.getString("label.taylor"));
+     taylorColour.setLabel(MessageManager
+             .getString("label.colourScheme_taylor"));
      taylorColour.addActionListener(this);
      hydrophobicityColour.setLabel(MessageManager
-             .getString("label.hydrophobicity"));
+             .getString("label.colourScheme_hydrophobic"));
      hydrophobicityColour.addActionListener(this);
-     helixColour
-             .setLabel(MessageManager.getString("label.helix_propensity"));
+     helixColour.setLabel(MessageManager
+             .getString("label.colourScheme_helix_propensity"));
      helixColour.addActionListener(this);
      strandColour.setLabel(MessageManager
-             .getString("label.strand_propensity"));
+             .getString("label.colourScheme_strand_propensity"));
      strandColour.addActionListener(this);
-     turnColour.setLabel(MessageManager.getString("label.turn_propensity"));
+     turnColour.setLabel(MessageManager
+             .getString("label.colourScheme_turn_propensity"));
      turnColour.addActionListener(this);
-     buriedColour.setLabel(MessageManager.getString("label.buried_index"));
+     buriedColour.setLabel(MessageManager
+             .getString("label.colourScheme_buried_index"));
      buriedColour.addActionListener(this);
      purinePyrimidineColour.setLabel(MessageManager
-             .getString("label.purine_pyrimidine"));
+             .getString("label.colourScheme_purine/pyrimidine"));
      purinePyrimidineColour.addActionListener(this);
      // RNAInteractionColour.setLabel(MessageManager
      // .getString("label.rna_interaction"));
      // RNAInteractionColour.addActionListener(this);
      RNAHelixColour.setLabel(MessageManager
-             .getString("action.by_rna_helixes"));
+             .getString("label.colourScheme_rna_helices"));
      RNAHelixColour.addActionListener(this);
      userDefinedColour.setLabel(MessageManager
              .getString("action.user_defined"));
      userDefinedColour.addActionListener(this);
      PIDColour.setLabel(MessageManager
-             .getString("label.percentage_identity"));
+             .getString("label.colourScheme_%_identity"));
      PIDColour.addActionListener(this);
      BLOSUM62Colour.setLabel(MessageManager
-             .getString("label.blosum62_score"));
+             .getString("label.colourScheme_blosum62"));
      BLOSUM62Colour.addActionListener(this);
-     tcoffeeColour
-             .setLabel(MessageManager.getString("label.tcoffee_scores"));
+     tcoffeeColour.setLabel(MessageManager
+             .getString("label.colourScheme_t-coffee_scores"));
      // it will be enabled only if a score file is provided
      tcoffeeColour.setEnabled(false);
      tcoffeeColour.addActionListener(this);
      abovePIDThreshold.setLabel(MessageManager
              .getString("label.above_identity_threshold"));
      abovePIDThreshold.addItemListener(this);
-     nucleotideColour.setLabel(MessageManager.getString("label.nucleotide"));
+     nucleotideColour.setLabel(MessageManager
+             .getString("label.colourScheme_nucleotide"));
      nucleotideColour.addActionListener(this);
      modifyPID.setLabel(MessageManager
              .getString("label.modify_identity_threshold"));
+     modifyPID.setEnabled(abovePIDThreshold.getState());
      modifyPID.addActionListener(this);
      modifyConservation.setLabel(MessageManager
              .getString("label.modify_conservation_threshold"));
+     modifyConservation.setEnabled(conservationMenuItem.getState());
      modifyConservation.addActionListener(this);
      annotationColour.setLabel(MessageManager
              .getString("action.by_annotation"));
              .getString("label.neighbour_joining_identity"));
      neighbourTreeMenuItem.addActionListener(this);
      avDistanceTreeBlosumMenuItem.setLabel(MessageManager
-             .getString("label.average_distance_bloslum62"));
+             .getString("label.average_distance_blosum62"));
      avDistanceTreeBlosumMenuItem.addActionListener(this);
      njTreeBlosumMenuItem.setLabel(MessageManager
              .getString("label.neighbour_blosum62"));
      {
        // register the association(s) and quit, don't create any windows.
        if (StructureSelectionManager.getStructureSelectionManager(applet)
 -              .setMapping(seqs, chains, pdb.getFile(), protocol) == null)
 +              .setMapping(seqs, chains, pdb.getFile(), protocol, null) == null)
        {
          System.err.println("Failed to map " + pdb.getFile() + " ("
                  + protocol + ") to any sequences");
@@@ -24,8 -24,8 +24,8 @@@ import jalview.bin.JalviewLite
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SequenceI;
- import jalview.io.FileParse;
  import jalview.io.DataSourceType;
+ import jalview.io.FileParse;
  import jalview.io.StructureFile;
  import jalview.schemes.BuriedColourScheme;
  import jalview.schemes.HelixColourScheme;
@@@ -93,27 -93,29 +93,29 @@@ public class AppletJmol extends Embmenu
    MenuItem charge = new MenuItem(
            MessageManager.getString("label.charge_cysteine"));
  
-   MenuItem zappo = new MenuItem(MessageManager.getString("label.zappo"));
+   MenuItem zappo = new MenuItem(
+           MessageManager.getString("label.colourScheme_zappo"));
  
-   MenuItem taylor = new MenuItem(MessageManager.getString("label.taylor"));
+   MenuItem taylor = new MenuItem(
+           MessageManager.getString("label.colourScheme_taylor"));
  
    MenuItem hydro = new MenuItem(
-           MessageManager.getString("label.hydrophobicity"));
+           MessageManager.getString("label.colourScheme_hydrophobic"));
  
    MenuItem helix = new MenuItem(
-           MessageManager.getString("label.helix_propensity"));
+           MessageManager.getString("label.colourScheme_helix_propensity"));
  
    MenuItem strand = new MenuItem(
-           MessageManager.getString("label.strand_propensity"));
+           MessageManager.getString("label.colourScheme_strand_propensity"));
  
    MenuItem turn = new MenuItem(
-           MessageManager.getString("label.turn_propensity"));
+           MessageManager.getString("label.colourScheme_turn_propensity"));
  
    MenuItem buried = new MenuItem(
-           MessageManager.getString("label.buried_index"));
+           MessageManager.getString("label.colourScheme_buried_index"));
  
    MenuItem purinepyrimidine = new MenuItem(
-           MessageManager.getString("label.purine_pyrimidine"));
+           MessageManager.getString("label.colourScheme_purine/pyrimidine"));
  
    MenuItem user = new MenuItem(
            MessageManager.getString("label.user_defined_colours"));
      {
        reader = StructureSelectionManager.getStructureSelectionManager(
                ap.av.applet).setMapping(seq, chains, pdbentry.getFile(),
 -              protocol);
 +              protocol, null);
        // PROMPT USER HERE TO ADD TO NEW OR EXISTING VIEW?
        // FOR NOW, LETS JUST OPEN A NEW WINDOW
      }
  
    public void updateTitleAndMenus()
    {
-     if (jmb.fileLoadingError != null && jmb.fileLoadingError.length() > 0)
+     if (jmb.hasFileLoadingError())
      {
        repaint();
        return;
   */
  package jalview.ext.jmol;
  
- import jalview.api.AlignmentViewPanel;
+ import jalview.api.AlignViewportI;
  import jalview.api.FeatureRenderer;
  import jalview.api.SequenceRenderer;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SequenceI;
 +import jalview.gui.IProgressIndicator;
  import jalview.io.DataSourceType;
  import jalview.io.StructureFile;
  import jalview.schemes.ColourSchemeI;
@@@ -36,6 -35,7 +36,7 @@@ import jalview.structure.AtomSpec
  import jalview.structure.StructureMappingcommandSet;
  import jalview.structure.StructureSelectionManager;
  import jalview.structures.models.AAStructureBindingModel;
+ import jalview.util.MessageManager;
  
  import java.awt.Color;
  import java.awt.Container;
@@@ -45,6 -45,7 +46,7 @@@ import java.io.File
  import java.net.URL;
  import java.security.AccessControlException;
  import java.util.ArrayList;
+ import java.util.BitSet;
  import java.util.Hashtable;
  import java.util.List;
  import java.util.Map;
@@@ -77,8 -78,6 +79,6 @@@ public abstract class JalviewJmolBindin
  
    Hashtable<String, String> chainFile;
  
-   public String fileLoadingError;
    /*
     * the default or current model displayed if the model cannot be identified
     * from the selection message
      releaseUIResources();
    }
  
+   @Override
    public void colourByChain()
    {
      colourBySequence = false;
      evalStateCommand("select *;color chain");
    }
  
+   @Override
    public void colourByCharge()
    {
      colourBySequence = false;
    }
  
    /**
-    * Construct and send a command to align structures against a reference
-    * structure, based on one or more sequence alignments
-    * 
-    * @param _alignment
-    *          an array of alignments to process
-    * @param _refStructure
-    *          an array of corresponding reference structures (index into pdb
-    *          file array); if a negative value is passed, the first PDB file
-    *          mapped to an alignment sequence is used as the reference for
-    *          superposition
-    * @param _hiddenCols
-    *          an array of corresponding hidden columns for each alignment
+    * {@inheritDoc}
     */
-   public void superposeStructures(AlignmentI[] _alignment,
+   @Override
+   public String superposeStructures(AlignmentI[] _alignment,
            int[] _refStructure, ColumnSelection[] _hiddenCols)
    {
      while (viewer.isScriptExecuting())
      String[] files = getPdbFile();
      if (!waitForFileLoad(files))
      {
-       return;
+       return null;
      }
  
      StringBuilder selectioncom = new StringBuilder(256);
        nSeconds = " " + (2.0 / files.length) + " ";
        // if (nSeconds).substring(0,5)+" ";
      }
      // see JAL-1345 - should really automatically turn off the animation for
      // large numbers of structures, but Jmol doesn't seem to allow that.
      // nSeconds = " ";
        }
  
        /*
-        * 'matched' array will hold 'true' for visible alignment columns where
+        * 'matched' bit j will be set for visible alignment columns j 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++)
+       BitSet matched = new BitSet();
+       for (int m = 0; m < alignment.getWidth(); m++)
        {
-         matched[m] = (hiddenCols != null) ? hiddenCols.isVisible(m) : true;
+         if (hiddenCols == null || hiddenCols.isVisible(m))
+         {
+           matched.set(m);
+         }
        }
  
        SuperposeData[] structures = new SuperposeData[files.length];
        }
  
        String[] selcom = new String[files.length];
-       int nmatched = 0;
-       for (boolean b : matched)
-       {
-         if (b)
-         {
-           nmatched++;
-         }
-       }
+       int nmatched = matched.cardinality();
        if (nmatched < 4)
        {
-         // TODO: bail out here because superposition illdefined?
+         return (MessageManager.formatMessage(
+ "label.insufficient_residues",
+                 nmatched));
        }
  
        /*
            boolean run = false;
            StringBuilder molsel = new StringBuilder();
            molsel.append("{");
-           for (int r = 0; r < matched.length; r++)
+           int nextColumnMatch = matched.nextSetBit(0);
+           while (nextColumnMatch != -1)
            {
-             if (matched[r])
+             int pdbResNo = structures[pdbfnum].pdbResNo[nextColumnMatch];
+             if (lpos != pdbResNo - 1)
              {
-               int pdbResNo = structures[pdbfnum].pdbResNo[r];
-               if (lpos != pdbResNo - 1)
+               // discontinuity
+               if (lpos != -1)
                {
-                 // discontinuity
-                 if (lpos != -1)
-                 {
-                   molsel.append(lpos);
-                   molsel.append(chainCd);
-                   molsel.append("|");
-                 }
-                 run = false;
+                 molsel.append(lpos);
+                 molsel.append(chainCd);
+                 molsel.append("|");
                }
-               else
+               run = false;
+             }
+             else
+             {
+               // continuous run - and lpos >-1
+               if (!run)
                {
-                 // continuous run - and lpos >-1
-                 if (!run)
-                 {
-                   // at the beginning, so add dash
-                   molsel.append(lpos);
-                   molsel.append("-");
-                 }
-                 run = true;
+                 // at the beginning, so add dash
+                 molsel.append(lpos);
+                 molsel.append("-");
                }
-               lpos = pdbResNo;
+               run = true;
              }
+             lpos = pdbResNo;
+             nextColumnMatch = matched.nextSetBit(nextColumnMatch + 1);
            }
            /*
             * add final selection phrase
                + selectioncom.toString() + "); cartoons; ");
        // evalStateCommand("select *; backbone; select "+selcom.toString()+"; cartoons; center "+selcom.toString());
      }
+     return null;
    }
  
    public void evalStateCommand(String command)
    }
  
    /**
-    * colour any structures associated with sequences in the given alignment
-    * using the getFeatureRenderer() and getSequenceRenderer() renderers but only
-    * if colourBySequence is enabled.
+    * Sends a set of colour commands to the structure viewer
+    * 
+    * @param colourBySequenceCommands
     */
-   public void colourBySequence(AlignmentViewPanel alignmentv)
+   @Override
+   protected void colourBySequence(
+           StructureMappingcommandSet[] colourBySequenceCommands)
    {
-     boolean showFeatures = alignmentv.getAlignViewport()
-             .isShowSequenceFeatures();
-     if (!colourBySequence || !isLoadingFinished())
-     {
-       return;
-     }
-     if (getSsm() == null)
-     {
-       return;
-     }
-     String[] files = getPdbFile();
-     SequenceRenderer sr = getSequenceRenderer(alignmentv);
-     FeatureRenderer fr = null;
-     if (showFeatures)
-     {
-       fr = getFeatureRenderer(alignmentv);
-     }
-     AlignmentI alignment = alignmentv.getAlignment();
-     for (jalview.structure.StructureMappingcommandSet cpdbbyseq : getColourBySequenceCommands(
-             files, sr, fr, alignment))
+     for (StructureMappingcommandSet cpdbbyseq : colourBySequenceCommands)
      {
        for (String cbyseq : cpdbbyseq.commands)
        {
     * @param files
     * @param sr
     * @param fr
-    * @param alignment
+    * @param viewport
     * @return
     */
+   @Override
    protected StructureMappingcommandSet[] getColourBySequenceCommands(
            String[] files, SequenceRenderer sr, FeatureRenderer fr,
-           AlignmentI alignment)
+           AlignViewportI viewport)
    {
      return JmolCommands.getColourBySequenceCommand(getSsm(), files,
-             getSequence(), sr, fr, alignment);
+             getSequence(), sr, fr, viewport);
    }
  
    /**
    }
  
    /**
-    * returns the current featureRenderer that should be used to colour the
-    * structures
-    * 
-    * @param alignment
-    * 
-    * @return
-    */
-   public abstract FeatureRenderer getFeatureRenderer(
-           AlignmentViewPanel alignment);
-   /**
     * instruct the Jalview binding to update the pdbentries vector if necessary
     * prior to matching the jmol view's contents to the list of structure files
     * Jalview knows about.
      return null;
    }
  
-   /**
-    * returns the current sequenceRenderer that should be used to colour the
-    * structures
-    * 
-    * @param alignment
-    * 
-    * @return
-    */
-   public abstract SequenceRenderer getSequenceRenderer(
-           AlignmentViewPanel alignment);
+   
  
    // ///////////////////////////////
    // JmolStatusListener
            // see JAL-623 - need method of matching pasted data up
            {
              pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
 -                    pdbfile, DataSourceType.PASTE);
 +                    pdbfile, DataSourceType.PASTE, getIProgressIndicator());
              getPdbEntry(modelnum).setFile("INLINE" + pdb.getId());
              matches = true;
              foundEntry = true;
              }
              // Explicitly map to the filename used by Jmol ;
              pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
 -                    fileName, protocol);
 +                    fileName, protocol, getIProgressIndicator());
              // pdbentry[pe].getFile(), protocol);
  
            }
      return chainNames;
    }
  
 +  protected abstract IProgressIndicator getIProgressIndicator();
 +
    public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
    {
      notifyAtomPicked(iatom, strMeasure, null);
  
    }
  
+   @Override
    public void setJalviewColourScheme(ColourSchemeI cs)
    {
      colourBySequence = false;
      command.append("select *;color white;");
      List<String> residueSet = ResidueProperties.getResidues(isNucleotide(),
              false);
-     for (String res : residueSet)
+     for (String resName : residueSet)
      {
-       Color col = cs.findColour(res.charAt(0));
-       command.append("select " + res + ";color[" + col.getRed() + ","
+       char res = resName.length() == 3 ? ResidueProperties
+               .getSingleCharacterCode(resName) : resName.charAt(0);
+       Color col = cs.findColour(res, 0, null, null, 0f);
+       command.append("select " + resName + ";color[" + col.getRed() + ","
                + col.getGreen() + "," + col.getBlue() + "];");
      }
  
  
    protected org.jmol.api.JmolAppConsoleInterface console = null;
  
+   @Override
    public void setBackgroundColour(java.awt.Color col)
    {
      jmolHistory(false);
index 0000000,d62cc3c..7632b65
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,180 +1,180 @@@
+ package jalview.ext.rbvi.chimera;
+ import jalview.util.RangeComparator;
+ import java.util.ArrayList;
+ import java.util.Collections;
+ import java.util.Iterator;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.TreeMap;
+ /**
+  * A class to model a Chimera atomspec pattern, for example
+  * 
+  * <pre>
+  * #0:15.A,28.A,54.A,63.A,70-72.A,83-84.A,97-98.A|#1:2.A,6.A,11.A,13-14.A,70.A,82.A,96-97.A
+  * </pre>
+  * 
+  * where
+  * <ul>
+  * <li>#0 is a model number</li>
+  * <li>15 or 70-72 is a residue number, or range of residue numbers</li>
+  * <li>.A is a chain identifier</li>
+  * <li>residue ranges are separated by comma</li>
+  * <li>atomspecs for distinct models are separated by | (or)</li>
+  * </ul>
+  * 
+  * <pre>
+  * @see http://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec.html
+  * </pre>
+  */
+ public class AtomSpecModel
+ {
+   private Map<Integer, Map<String, List<int[]>>> atomSpec;
+   /**
+    * Constructor
+    */
+   public AtomSpecModel()
+   {
+     atomSpec = new TreeMap<Integer, Map<String, List<int[]>>>();
+   }
+   /**
+    * Adds one contiguous range to this atom spec
+    * 
+    * @param model
+    * @param startPos
+    * @param endPos
+    * @param chain
+    */
+   public void addRange(int model, int startPos, int endPos, String chain)
+   {
+     /*
+      * Get/initialize map of data for the colour and model
+      */
+     Map<String, List<int[]>> modelData = atomSpec.get(model);
+     if (modelData == null)
+     {
+       atomSpec.put(model, modelData = new TreeMap<String, List<int[]>>());
+     }
+     /*
+      * Get/initialize map of data for colour, model and chain
+      */
+     List<int[]> chainData = modelData.get(chain);
+     if (chainData == null)
+     {
+       chainData = new ArrayList<int[]>();
+       modelData.put(chain, chainData);
+     }
+     /*
+      * Add the start/end positions
+      */
+     chainData.add(new int[] { startPos, endPos });
+     // TODO add intelligently, using a RangeList class
+   }
+   /**
+    * Returns the range(s) formatted as a Chimera atomspec
+    * 
+    * @return
+    */
+   public String getAtomSpec()
+   {
+     StringBuilder sb = new StringBuilder(128);
+     boolean firstModel = true;
+     for (Integer model : atomSpec.keySet())
+     {
+       if (!firstModel)
+       {
+         sb.append("|");
+       }
+       firstModel = false;
+       sb.append("#").append(model).append(":");
+       boolean firstPositionForModel = true;
+       final Map<String, List<int[]>> modelData = atomSpec.get(model);
+       for (String chain : modelData.keySet())
+       {
 -        chain = chain.trim();
++        // chain = chain.trim();
+         List<int[]> rangeList = modelData.get(chain);
+         /*
+          * sort ranges into ascending start position order
+          */
+         Collections.sort(rangeList, new RangeComparator(true));
+         int start = rangeList.isEmpty() ? 0 : rangeList.get(0)[0];
+         int end = rangeList.isEmpty() ? 0 : rangeList.get(0)[1];
+         Iterator<int[]> iterator = rangeList.iterator();
+         while (iterator.hasNext())
+         {
+           int[] range = iterator.next();
+           if (range[0] <= end + 1)
+           {
+             /*
+              * range overlaps or is contiguous with the last one
+              * - so just extend the end position, and carry on
+              * (unless this is the last in the list)
+              */
+             end = Math.max(end, range[1]);
+           }
+           else
+           {
+             /*
+              * we have a break so append the last range
+              */
+             appendRange(sb, start, end, chain, firstPositionForModel);
+             firstPositionForModel = false;
+             start = range[0];
+             end = range[1];
+           }
+         }
+         /*
+          * and append the last range
+          */
+         if (!rangeList.isEmpty())
+         {
+           appendRange(sb, start, end, chain, firstPositionForModel);
+           firstPositionForModel = false;
+         }
+       }
+     }
+     return sb.toString();
+   }
+   /**
+    * @param sb
+    * @param start
+    * @param end
+    * @param chain
+    * @param firstPositionForModel
+    */
+   protected void appendRange(StringBuilder sb, int start, int end,
+           String chain, boolean firstPositionForModel)
+   {
+     if (!firstPositionForModel)
+     {
+       sb.append(",");
+     }
+     if (end == start)
+     {
+       sb.append(start);
+     }
+     else
+     {
+       sb.append(start).append("-").append(end);
+     }
+     if (chain.length() > 0)
+     {
+       sb.append(".").append(chain);
+     }
+   }
+ }
@@@ -59,6 -59,7 +59,7 @@@ import jalview.datamodel.SeqCigar
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
+ import jalview.gui.ColourMenuHelper.ColourChangeListener;
  import jalview.gui.ViewSelectionMenu.ViewSetProvider;
  import jalview.io.AlignmentProperties;
  import jalview.io.AnnotationFile;
@@@ -66,6 -67,7 +67,7 @@@ import jalview.io.BioJsHTMLOutput
  import jalview.io.DataSourceType;
  import jalview.io.FileFormat;
  import jalview.io.FileFormatI;
+ import jalview.io.FileFormats;
  import jalview.io.FileLoader;
  import jalview.io.FormatAdapter;
  import jalview.io.HtmlSvgOutput;
@@@ -77,24 -79,11 +79,11 @@@ import jalview.io.JnetAnnotationMaker
  import jalview.io.NewickFile;
  import jalview.io.TCoffeeScoreFile;
  import jalview.jbgui.GAlignFrame;
- import jalview.schemes.Blosum62ColourScheme;
- import jalview.schemes.BuriedColourScheme;
- import jalview.schemes.ClustalxColourScheme;
  import jalview.schemes.ColourSchemeI;
- import jalview.schemes.ColourSchemeProperty;
- import jalview.schemes.HelixColourScheme;
- import jalview.schemes.HydrophobicColourScheme;
- import jalview.schemes.NucleotideColourScheme;
- import jalview.schemes.PIDColourScheme;
- import jalview.schemes.PurinePyrimidineColourScheme;
- import jalview.schemes.RNAHelicesColourChooser;
+ import jalview.schemes.ColourSchemes;
+ import jalview.schemes.ResidueColourScheme;
  import jalview.schemes.ResidueProperties;
- import jalview.schemes.StrandColourScheme;
  import jalview.schemes.TCoffeeColourScheme;
- import jalview.schemes.TaylorColourScheme;
- import jalview.schemes.TurnColourScheme;
- import jalview.schemes.UserColourScheme;
- import jalview.schemes.ZappoColourScheme;
  import jalview.util.MessageManager;
  import jalview.viewmodel.AlignmentViewport;
  import jalview.ws.DBRefFetcher;
@@@ -125,12 -114,13 +114,13 @@@ 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.awt.print.PageFormat;
  import java.awt.print.PrinterJob;
  import java.beans.PropertyChangeEvent;
  import java.io.File;
+ import java.io.FileWriter;
+ import java.io.PrintWriter;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.Arrays;
@@@ -146,7 -136,6 +136,6 @@@ import javax.swing.JInternalFrame
  import javax.swing.JLayeredPane;
  import javax.swing.JMenu;
  import javax.swing.JMenuItem;
- import javax.swing.JRadioButtonMenuItem;
  import javax.swing.JScrollPane;
  import javax.swing.SwingUtilities;
  
   * @version $Revision$
   */
  public class AlignFrame extends GAlignFrame implements DropTargetListener,
-         IProgressIndicator, AlignViewControllerGuiI
+         IProgressIndicator, AlignViewControllerGuiI, ColourChangeListener
  {
  
    public static final int DEFAULT_WIDTH = 700;
              alignPanel);
      if (viewport.getAlignmentConservationAnnotation() == null)
      {
-       BLOSUM62Colour.setEnabled(false);
+       // BLOSUM62Colour.setEnabled(false);
        conservationMenuItem.setEnabled(false);
        modifyConservation.setEnabled(false);
        // PIDColour.setEnabled(false);
        sortPairwiseMenuItem_actionPerformed(null);
      }
  
-     if (Desktop.desktop != null)
-     {
-       this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
-       addServiceListeners();
-       setGUINucleotide(viewport.getAlignment().isNucleotide());
-     }
      this.alignPanel.av
              .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
  
      setMenusFromViewport(viewport);
      buildSortByAnnotationScoresMenu();
      buildTreeMenu();
+     buildColourMenu();
+     if (Desktop.desktop != null)
+     {
+       this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
+       addServiceListeners();
+       setGUINucleotide();
+     }
  
      if (viewport.getWrapAlignment())
      {
    /**
     * Configure menu items that vary according to whether the alignment is
     * nucleotide or protein
-    * 
-    * @param nucleotide
     */
-   public void setGUINucleotide(boolean nucleotide)
+   public void setGUINucleotide()
    {
+     AlignmentI al = getViewport().getAlignment();
+     boolean nucleotide = al.isNucleotide();
      showTranslation.setVisible(nucleotide);
      showReverse.setVisible(nucleotide);
      showReverseComplement.setVisible(nucleotide);
      conservationMenuItem.setEnabled(!nucleotide);
-     modifyConservation.setEnabled(!nucleotide);
+     modifyConservation.setEnabled(!nucleotide
+             && conservationMenuItem.isSelected());
      showGroupConservation.setEnabled(!nucleotide);
-     rnahelicesColour.setEnabled(nucleotide);
-     nucleotideColour.setEnabled(nucleotide);
-     purinePyrimidineColour.setEnabled(nucleotide);
-     RNAInteractionColour.setEnabled(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"));
    }
  
    /**
      padGapsMenuitem.setSelected(av.isPadGaps());
      colourTextMenuItem.setSelected(av.isShowColourText());
      abovePIDThreshold.setSelected(av.getAbovePIDThreshold());
+     modifyPID.setEnabled(abovePIDThreshold.isSelected());
      conservationMenuItem.setSelected(av.getConservationSelected());
+     modifyConservation.setEnabled(conservationMenuItem.isSelected());
      seqLimits.setSelected(av.getShowJVSuffix());
      idRightAlign.setSelected(av.isRightAlignIds());
      centreColumnLabelsMenuItem.setState(av.isCentreColumnLabels());
      showSequenceLogo.setSelected(av.isShowSequenceLogo());
      normaliseSequenceLogo.setSelected(av.isNormaliseSequenceLogo());
  
-     setColourSelected(ColourSchemeProperty.getColourName(av
-             .getGlobalColourScheme()));
+     ColourMenuHelper.setColourSelected(colourMenu,
+             av.getGlobalColourScheme());
  
      showSeqFeatures.setSelected(av.isShowSequenceFeatures());
      hiddenMarkers.setState(av.getShowHiddenMarkers());
      autoCalculate.setSelected(av.autoCalculateConsensus);
      sortByTree.setSelected(av.sortByTree);
      listenToViewSelections.setSelected(av.followSelection);
-     rnahelicesColour.setEnabled(av.getAlignment().hasRNAStructure());
-     rnahelicesColour
-             .setSelected(av.getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
  
      showProducts.setEnabled(canShowProducts());
      setGroovyEnabled(Desktop.getGroovyConsole() != null);
    public void saveAs_actionPerformed(ActionEvent e)
    {
      String format = currentFileFormat == null ? null : currentFileFormat
-             .toString();
+             .getName();
      JalviewFileChooser chooser = JalviewFileChooser.forWrite(
              Cache.getProperty("LAST_DIRECTORY"), format);
  
  
        fileName = chooser.getSelectedFile().getPath();
  
-       Cache.setProperty("DEFAULT_FILE_FORMAT",
-               currentFileFormat.toString());
+       Cache.setProperty("DEFAULT_FILE_FORMAT", currentFileFormat.getName());
  
        Cache.setProperty("LAST_DIRECTORY", fileName);
        saveAlignment(fileName, currentFileFormat);
      }
      else
      {
-       // if (!jalview.io.AppletFormatAdapter.isValidFormat(format, true))
-       // {
-       // warningMessage("Cannot save file " + fileName + " using format "
-       // + format, "Alignment output format not supported");
-       // if (!Jalview.isHeadlessMode())
-       // {
-       // saveAs_actionPerformed(null);
-       // }
-       // return false;
-       // }
        AlignmentExportData exportData = getAlignmentForExport(format,
                viewport, null);
        if (exportData.getSettings().isCancelled())
        {
          try
          {
-           java.io.PrintWriter out = new java.io.PrintWriter(
-                   new java.io.FileWriter(file));
+           PrintWriter out = new PrintWriter(new FileWriter(file));
  
            out.print(output);
            out.close();
            this.setTitle(file);
            statusBar.setText(MessageManager.formatMessage(
                    "label.successfully_saved_to_file_in_format",
-                   new Object[] { fileName, format }));
+                   new Object[] { fileName, format.getName() }));
          } catch (Exception ex)
          {
            success = false;
    @Override
    protected void outputText_actionPerformed(ActionEvent e)
    {
-     FileFormatI fileFormat = FileFormat.forName(e.getActionCommand());
+     FileFormatI fileFormat = FileFormats.getInstance().forName(
+             e.getActionCommand());
      AlignmentExportData exportData = getAlignmentForExport(fileFormat,
              viewport, null);
      if (exportData.getSettings().isCancelled())
      {
        FileFormatI format = fileFormat;
        cap.setText(new FormatAdapter(alignPanel, exportData.getSettings())
-               .formatSequences(format,
-                       exportData.getAlignment(),
+               .formatSequences(format, exportData.getAlignment(),
                        exportData.getOmitHidden(),
                        exportData.getStartEndPostions(),
                        viewport.getColumnSelection()));
      }
  
      String output = new FormatAdapter().formatSequences(FileFormat.Fasta,
-             seqs,
-             omitHidden, null);
+             seqs, omitHidden, null);
  
      StringSelection ss = new StringSelection(output);
  
    }
  
    @Override
-   public void textColour_actionPerformed(ActionEvent e)
+   public void textColour_actionPerformed()
    {
      new TextColourChooser().chooseColour(alignPanel, null);
    }
  
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   protected void noColourmenuItem_actionPerformed(ActionEvent e)
-   {
-     changeColour(null);
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void clustalColour_actionPerformed(ActionEvent e)
-   {
-     changeColour(new ClustalxColourScheme(viewport.getAlignment(),
-             viewport.getHiddenRepSequences()));
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void zappoColour_actionPerformed(ActionEvent e)
-   {
-     changeColour(new ZappoColourScheme());
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void taylorColour_actionPerformed(ActionEvent e)
-   {
-     changeColour(new TaylorColourScheme());
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void hydrophobicityColour_actionPerformed(ActionEvent e)
-   {
-     changeColour(new HydrophobicColourScheme());
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void helixColour_actionPerformed(ActionEvent e)
-   {
-     changeColour(new HelixColourScheme());
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void strandColour_actionPerformed(ActionEvent e)
-   {
-     changeColour(new StrandColourScheme());
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void turnColour_actionPerformed(ActionEvent e)
-   {
-     changeColour(new TurnColourScheme());
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void buriedColour_actionPerformed(ActionEvent e)
-   {
-     changeColour(new BuriedColourScheme());
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void nucleotideColour_actionPerformed(ActionEvent e)
-   {
-     changeColour(new NucleotideColourScheme());
-   }
-   @Override
-   public void purinePyrimidineColour_actionPerformed(ActionEvent e)
-   {
-     changeColour(new PurinePyrimidineColourScheme());
-   }
    /*
-    * public void covariationColour_actionPerformed(ActionEvent e) {
+    * public void covariationColour_actionPerformed() {
     * changeColour(new
     * CovariationColourScheme(viewport.getAlignment().getAlignmentAnnotation
     * ()[0])); }
     */
    @Override
-   public void annotationColour_actionPerformed(ActionEvent e)
+   public void annotationColour_actionPerformed()
    {
      new AnnotationColourChooser(viewport, alignPanel);
    }
      new AnnotationColumnChooser(viewport, alignPanel);
    }
  
+   /**
+    * Action on the user checking or unchecking the option to apply the selected
+    * colour scheme to all groups. If unchecked, groups may have their own
+    * independent colour schemes.
+    * 
+    * @param selected
+    */
    @Override
-   public void rnahelicesColour_actionPerformed(ActionEvent e)
+   public void applyToAllGroups_actionPerformed(boolean selected)
    {
-     new RNAHelicesColourChooser(viewport, alignPanel);
+     viewport.setColourAppliesToAllGroups(selected);
    }
  
    /**
-    * DOCUMENT ME!
+    * Action on user selecting a colour from the colour menu
     * 
-    * @param e
-    *          DOCUMENT ME!
+    * @param name
+    *          the name (not the menu item label!) of the colour scheme
     */
    @Override
-   protected void applyToAllGroups_actionPerformed(ActionEvent e)
+   public void changeColour_actionPerformed(String name)
    {
-     viewport.setColourAppliesToAllGroups(applyToAllGroups.isSelected());
+     /*
+      * 'User Defined' opens a panel to configure or load a
+      * user-defined colour scheme
+      */
+     if (ResidueColourScheme.USER_DEFINED.equals(name))
+     {
+       new UserDefinedColours(alignPanel, null);
+       return;
+     }
+     /*
+      * otherwise set the chosen colour scheme (or null for 'None')
+      */
+     ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme(name,
+             viewport.getAlignment(), viewport.getHiddenRepSequences());
+     changeColour(cs);
    }
  
    /**
-    * DOCUMENT ME!
+    * Actions on setting or changing the alignment colour scheme
     * 
     * @param cs
-    *          DOCUMENT ME!
     */
    @Override
    public void changeColour(ColourSchemeI cs)
    {
      // TODO: pull up to controller method
      if (cs != null)
      {
-       // Make sure viewport is up to date w.r.t. any sliders
-       if (viewport.getAbovePIDThreshold())
-       {
-         int threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
-                 "Background");
-         viewport.setThreshold(threshold);
-       }
-       if (viewport.getConservationSelected())
-       {
-         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel,
-                 cs, "Background"));
-       }
-       if (cs instanceof TCoffeeColourScheme)
-       {
-         tcoffeeColour.setEnabled(true);
-         tcoffeeColour.setSelected(true);
-       }
+       ColourMenuHelper.setColourSelected(colourMenu, cs.getSchemeName());
      }
  
      viewport.setGlobalColourScheme(cs);
    }
  
    /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
+    * Show the PID threshold slider panel
     */
    @Override
-   protected void modifyPID_actionPerformed(ActionEvent e)
+   protected void modifyPID_actionPerformed()
    {
-     if (viewport.getAbovePIDThreshold()
-             && viewport.getGlobalColourScheme() != null)
-     {
-       SliderPanel.setPIDSliderSource(alignPanel,
-               viewport.getGlobalColourScheme(), "Background");
-       SliderPanel.showPIDSlider();
-     }
+     SliderPanel.setPIDSliderSource(alignPanel,
+             viewport.getResidueShading(), alignPanel.getViewName());
+     SliderPanel.showPIDSlider();
    }
  
    /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
+    * Show the Conservation slider panel
     */
    @Override
-   protected void modifyConservation_actionPerformed(ActionEvent e)
+   protected void modifyConservation_actionPerformed()
    {
-     if (viewport.getConservationSelected()
-             && viewport.getGlobalColourScheme() != null)
-     {
-       SliderPanel.setConservationSlider(alignPanel,
-               viewport.getGlobalColourScheme(), "Background");
-       SliderPanel.showConservationSlider();
-     }
+     SliderPanel.setConservationSlider(alignPanel,
+             viewport.getResidueShading(), alignPanel.getViewName());
+     SliderPanel.showConservationSlider();
    }
  
    /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
+    * Action on selecting or deselecting (Colour) By Conservation
     */
    @Override
-   protected void conservationMenuItem_actionPerformed(ActionEvent e)
+   public void conservationMenuItem_actionPerformed(boolean selected)
    {
-     viewport.setConservationSelected(conservationMenuItem.isSelected());
-     viewport.setAbovePIDThreshold(false);
-     abovePIDThreshold.setSelected(false);
+     modifyConservation.setEnabled(selected);
+     viewport.setConservationSelected(selected);
+     viewport.getResidueShading().setConservationApplied(selected);
  
      changeColour(viewport.getGlobalColourScheme());
-     modifyConservation_actionPerformed(null);
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void abovePIDThreshold_actionPerformed(ActionEvent e)
-   {
-     viewport.setAbovePIDThreshold(abovePIDThreshold.isSelected());
-     conservationMenuItem.setSelected(false);
-     viewport.setConservationSelected(false);
-     changeColour(viewport.getGlobalColourScheme());
-     modifyPID_actionPerformed(null);
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void userDefinedColour_actionPerformed(ActionEvent e)
-   {
-     if (e.getActionCommand().equals(
-             MessageManager.getString("action.user_defined")))
+     if (selected)
      {
-       new UserDefinedColours(alignPanel, null);
+       modifyConservation_actionPerformed();
      }
      else
      {
-       UserColourScheme udc = (UserColourScheme) UserDefinedColours
-               .getUserColourSchemes().get(e.getActionCommand());
-       changeColour(udc);
+       SliderPanel.hideConservationSlider();
      }
    }
  
-   public void updateUserColourMenu()
+   /**
+    * Action on selecting or deselecting (Colour) Above PID Threshold
+    */
+   @Override
+   public void abovePIDThreshold_actionPerformed(boolean selected)
    {
+     modifyPID.setEnabled(selected);
+     viewport.setAbovePIDThreshold(selected);
+     if (!selected)
+     {
+       viewport.getResidueShading().setThreshold(0,
+               viewport.isIgnoreGapsConsensus());
+     }
  
-     Component[] menuItems = colourMenu.getMenuComponents();
-     int iSize = menuItems.length;
-     for (int i = 0; i < iSize; i++)
+     changeColour(viewport.getGlobalColourScheme());
+     if (selected)
      {
-       if (menuItems[i].getName() != null
-               && menuItems[i].getName().equals("USER_DEFINED"))
-       {
-         colourMenu.remove(menuItems[i]);
-         iSize--;
-       }
+       modifyPID_actionPerformed();
      }
-     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
+     else
      {
-       java.util.Enumeration userColours = jalview.gui.UserDefinedColours
-               .getUserColourSchemes().keys();
-       while (userColours.hasMoreElements())
-       {
-         final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
-                 userColours.nextElement().toString());
-         radioItem.setName("USER_DEFINED");
-         radioItem.addMouseListener(new MouseAdapter()
-         {
-           @Override
-           public void mousePressed(MouseEvent evt)
-           {
-             if (evt.isPopupTrigger()) // Mac
-             {
-               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 = JvOptionPane.showInternalConfirmDialog(
-                     jalview.gui.Desktop.desktop, MessageManager
-                             .getString("label.remove_from_default_list"),
-                     MessageManager
-                             .getString("label.remove_user_defined_colour"),
-                     JvOptionPane.YES_NO_OPTION);
-             if (option == JvOptionPane.YES_OPTION)
-             {
-               jalview.gui.UserDefinedColours
-                       .removeColourFromDefaults(radioItem.getText());
-               colourMenu.remove(radioItem);
-             }
-             else
-             {
-               radioItem.addActionListener(new ActionListener()
-               {
-                 @Override
-                 public void actionPerformed(ActionEvent evt)
-                 {
-                   userDefinedColour_actionPerformed(evt);
-                 }
-               });
-             }
-           }
-         });
-         radioItem.addActionListener(new ActionListener()
-         {
-           @Override
-           public void actionPerformed(ActionEvent evt)
-           {
-             userDefinedColour_actionPerformed(evt);
-           }
-         });
-         colourMenu.insert(radioItem, 15);
-         colours.add(radioItem);
-       }
+       SliderPanel.hidePIDSlider();
      }
    }
  
     *          DOCUMENT ME!
     */
    @Override
-   public void PIDColour_actionPerformed(ActionEvent e)
-   {
-     changeColour(new PIDColourScheme());
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
-   public void BLOSUM62Colour_actionPerformed(ActionEvent e)
-   {
-     changeColour(new Blosum62ColourScheme());
-   }
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
-   @Override
    public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
    {
      SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
      }
    }
  
-   @Override
-   protected void tcoffeeColorScheme_actionPerformed(ActionEvent e)
-   {
-     changeColour(new TCoffeeColourScheme(alignPanel.getAlignment()));
-   }
    public TreePanel ShowNewickTree(NewickFile nf, String title)
    {
      return ShowNewickTree(nf, title, 600, 500, 4, 5);
                // associating PDB files which have no IDs.
                for (SequenceI toassoc : (SequenceI[]) fm[2])
                {
 -                PDBEntry pe = new AssociatePdbFileWithSeq()
 -                        .associatePdbWithSeq((String) fm[0],
 +                PDBEntry pe = new AssociateStructureFileWithSeq()
 +                        .associateStructureWithSeq((String) fm[0],
                                  (DataSourceType) fm[1], toassoc, false,
                                  Desktop.instance);
                  if (pe != null)
            {
              if (tcf.annotateAlignment(viewport.getAlignment(), true))
              {
-               tcoffeeColour.setEnabled(true);
-               tcoffeeColour.setSelected(true);
+               buildColourMenu();
                changeColour(new TCoffeeColourScheme(viewport.getAlignment()));
                isAnnotation = true;
                statusBar
            }
            if (FileFormat.Jnet.equals(format))
            {
-             JPredFile predictions = new JPredFile(
-                     file, sourceType);
+             JPredFile predictions = new JPredFile(file, sourceType);
              new JnetAnnotationMaker();
              JnetAnnotationMaker.add_annotation(predictions,
                      viewport.getAlignment(), 0, false);
      }
  
      /*
+      * 'focus' any colour slider that is open to the selected viewport
+      */
+     if (viewport.getConservationSelected())
+     {
+       SliderPanel.setConservationSlider(alignPanel,
+               viewport.getResidueShading(), alignPanel.getViewName());
+     }
+     else
+     {
+       SliderPanel.hideConservationSlider();
+     }
+     if (viewport.getAbovePIDThreshold())
+     {
+       SliderPanel.setPIDSliderSource(alignPanel,
+               viewport.getResidueShading(), alignPanel.getViewName());
+     }
+     else
+     {
+       SliderPanel.hidePIDSlider();
+     }
+     /*
       * If there is a frame linked to this one in a SplitPane, switch it to the
       * same view tab index. No infinite recursion of calls should happen, since
       * tabSelectionChanged() should not get invoked on setting the selected
      }
      AlignmentI cdna = new Alignment(cdnaSeqs.toArray(new SequenceI[cdnaSeqs
              .size()]));
-     AlignFrame alignFrame = new AlignFrame(cdna, AlignFrame.DEFAULT_WIDTH,
+     GAlignFrame alignFrame = new AlignFrame(cdna, AlignFrame.DEFAULT_WIDTH,
              AlignFrame.DEFAULT_HEIGHT);
      cdna.alignAs(alignment);
      String newtitle = "cDNA " + MessageManager.getString("label.for") + " "
              true,
              (actionEvent.getModifiers() & (ActionEvent.META_MASK | ActionEvent.CTRL_MASK)) != 0);
    }
+   /**
+    * Rebuilds the Colour menu, including any user-defined colours which have
+    * been loaded either on startup or during the session
+    */
+   public void buildColourMenu()
+   {
+     colourMenu.removeAll();
+     colourMenu.add(applyToAllGroups);
+     colourMenu.add(textColour);
+     colourMenu.addSeparator();
+     ColourMenuHelper.addMenuItems(colourMenu, this,
+             viewport.getAlignment(), false);
+     colourMenu.addSeparator();
+     colourMenu.add(conservationMenuItem);
+     colourMenu.add(modifyConservation);
+     colourMenu.add(abovePIDThreshold);
+     colourMenu.add(modifyPID);
+     colourMenu.add(annotationColour);
+     ColourSchemeI colourScheme = viewport.getGlobalColourScheme();
+     String schemeName = colourScheme == null ? null : colourScheme
+             .getSchemeName();
+     ColourMenuHelper.setColourSelected(colourMenu, schemeName);
+   }
  }
  
  class PrintThread extends Thread
  package jalview.gui;
  
  import jalview.bin.Cache;
- import jalview.datamodel.Alignment;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SequenceI;
  import jalview.gui.StructureViewer.ViewerType;
- import jalview.io.JalviewFileChooser;
- import jalview.io.JalviewFileView;
- import jalview.schemes.BuriedColourScheme;
- import jalview.schemes.ColourSchemeI;
- import jalview.schemes.HelixColourScheme;
- import jalview.schemes.HydrophobicColourScheme;
- import jalview.schemes.PurinePyrimidineColourScheme;
- import jalview.schemes.StrandColourScheme;
- import jalview.schemes.TaylorColourScheme;
- import jalview.schemes.TurnColourScheme;
- import jalview.schemes.ZappoColourScheme;
  import jalview.structures.models.AAStructureBindingModel;
+ import jalview.util.BrowserLauncher;
  import jalview.util.MessageManager;
  import jalview.util.Platform;
  import jalview.ws.dbsources.Pdb;
@@@ -50,29 -38,18 +38,18 @@@ import java.awt.Font
  import java.awt.Graphics;
  import java.awt.Rectangle;
  import java.awt.event.ActionEvent;
- import java.awt.event.ItemEvent;
- import java.awt.event.ItemListener;
- import java.io.BufferedReader;
  import java.io.File;
- import java.io.FileOutputStream;
- import java.io.FileReader;
- import java.io.IOException;
- import java.io.PrintWriter;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Vector;
  
  import javax.swing.JCheckBoxMenuItem;
- import javax.swing.JColorChooser;
  import javax.swing.JInternalFrame;
- import javax.swing.JMenu;
  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;
- import javax.swing.event.MenuListener;
  
  public class AppJmol extends StructureViewerBase
  {
@@@ -91,8 -68,6 +68,6 @@@
  
    RenderPanel renderPanel;
  
-   ViewSelectionMenu seqColourBy;
    /**
     * 
     * @param files
      {
        useAlignmentPanelForSuperposition(ap);
      }
+     initMenus();
      if (leaveColouringToJmol || !usetoColour)
      {
        jmb.setColourBySequence(false);
        viewerColour.setSelected(false);
      }
      this.setBounds(bounds);
-     initMenus();
      setViewId(viewid);
      // jalview.gui.Desktop.addInternalFrame(this, "Loading File",
      // bounds.width,bounds.height);
          closeViewer(false);
        }
      });
 -    initJmol(loadStatus); // pdbentry, seq, JBPCHECK!
 +    initJmol(loadStatus); // pdbentry, seq, JBPCHECK!ˇ
 +
    }
  
-   private void initMenus()
+   @Override
+   protected void initMenus()
    {
-     seqColour.setSelected(jmb.isColourBySequence());
-     viewerColour.setSelected(!jmb.isColourBySequence());
-     if (_colourwith == null)
-     {
-       _colourwith = new Vector<AlignmentPanel>();
-     }
-     if (_alignwith == null)
-     {
-       _alignwith = new Vector<AlignmentPanel>();
-     }
-     seqColourBy = new ViewSelectionMenu(
-             MessageManager.getString("label.colour_by"), this, _colourwith,
-             new ItemListener()
-             {
-               @Override
-               public void itemStateChanged(ItemEvent e)
-               {
-                 if (!seqColour.isSelected())
-                 {
-                   seqColour.doClick();
-                 }
-                 else
-                 {
-                   // update the jmol display now.
-                   seqColour_actionPerformed(null);
-                 }
-               }
-             });
-     viewMenu.add(seqColourBy);
-     final ItemListener handler;
-     JMenu alpanels = new ViewSelectionMenu(
-             MessageManager.getString("label.superpose_with"), this,
-             _alignwith, handler = new ItemListener()
-             {
-               @Override
-               public void itemStateChanged(ItemEvent e)
-               {
-                 alignStructs.setEnabled(_alignwith.size() > 0);
-                 alignStructs.setToolTipText(MessageManager
-                         .formatMessage(
-                                 "label.align_structures_using_linked_alignment_views",
-                                 new String[] { new Integer(_alignwith
-                                         .size()).toString() }));
-               }
-             });
-     handler.itemStateChanged(null);
-     viewerActionMenu.add(alpanels);
-     viewerActionMenu.addMenuListener(new MenuListener()
-     {
-       @Override
-       public void menuSelected(MenuEvent e)
-       {
-         handler.itemStateChanged(null);
-       }
-       @Override
-       public void menuDeselected(MenuEvent e)
-       {
-         // TODO Auto-generated method stub
+     super.initMenus();
  
-       }
-       @Override
-       public void menuCanceled(MenuEvent e)
-       {
-         // TODO Auto-generated method stub
+     viewerActionMenu.setText(MessageManager.getString("label.jmol"));
  
-       }
-     });
+     viewerColour
+             .setText(MessageManager.getString("label.colour_with_jmol"));
+     viewerColour.setToolTipText(MessageManager
+             .getString("label.let_jmol_manage_structure_colours"));
    }
  
    IProgressIndicator progressBar = null;
  
 +  @Override
 +  protected IProgressIndicator getIProgressIndicator()
 +  {
 +    return progressBar;
 +  }
    /**
     * add a single PDB structure to a new or existing Jmol view
     * 
      openNewJmol(ap, new PDBEntry[] { pdbentry }, new SequenceI[][] { seq });
    }
  
-   /**
-    * Answers true if this viewer already involves the given PDB ID
-    */
-   @Override
-   protected boolean hasPdbId(String pdbId)
-   {
-     return jmb.hasPdbId(pdbId);
-   }
    private void openNewJmol(AlignmentPanel ap, PDBEntry[] pdbentrys,
            SequenceI[][] seqs)
    {
              pdbentrys, seqs, null);
      addAlignmentPanel(ap);
      useAlignmentPanelForColourbyseq(ap);
      if (pdbentrys.length > 1)
      {
        alignAddedStructures = true;
    }
  
    @Override
-   public void pdbFile_actionPerformed(ActionEvent actionEvent)
-   {
-     JalviewFileChooser chooser = new JalviewFileChooser(
-             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
-     chooser.setFileView(new JalviewFileView());
-     chooser.setDialogTitle(MessageManager.getString("label.save_pdb_file"));
-     chooser.setToolTipText(MessageManager.getString("action.save"));
-     int value = chooser.showSaveDialog(this);
-     if (value == JalviewFileChooser.APPROVE_OPTION)
-     {
-       BufferedReader in = null;
-       try
-       {
-         // TODO: cope with multiple PDB files in view
-         in = new BufferedReader(new FileReader(jmb.getPdbFile()[0]));
-         File outFile = chooser.getSelectedFile();
-         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
-         String data;
-         while ((data = in.readLine()) != null)
-         {
-           if (!(data.indexOf("<PRE>") > -1 || data.indexOf("</PRE>") > -1))
-           {
-             out.println(data);
-           }
-         }
-         out.close();
-       } catch (Exception ex)
-       {
-         ex.printStackTrace();
-       } finally
-       {
-         if (in != null)
-         {
-           try
-           {
-             in.close();
-           } catch (IOException e)
-           {
-             // ignore
-           }
-         }
-       }
-     }
-   }
-   @Override
-   public void viewMapping_actionPerformed(ActionEvent actionEvent)
-   {
-     jalview.gui.CutAndPasteTransfer cap = new jalview.gui.CutAndPasteTransfer();
-     try
-     {
-       cap.appendText(jmb.printMappings());
-     } catch (OutOfMemoryError e)
-     {
-       new OOMWarning(
-               "composing sequence-structure alignments for display in text box.",
-               e);
-       cap.dispose();
-       return;
-     }
-     jalview.gui.Desktop.addInternalFrame(cap,
-             MessageManager.getString("label.pdb_sequence_mapping"), 550,
-             600);
-   }
-   @Override
    public void eps_actionPerformed(ActionEvent e)
    {
      makePDBImage(jalview.util.ImageMaker.TYPE.EPS);
    }
  
    @Override
-   public void viewerColour_actionPerformed(ActionEvent actionEvent)
-   {
-     if (viewerColour.isSelected())
-     {
-       // disable automatic sequence colouring.
-       jmb.setColourBySequence(false);
-     }
-   }
-   @Override
-   public void seqColour_actionPerformed(ActionEvent actionEvent)
-   {
-     jmb.setColourBySequence(seqColour.isSelected());
-     if (_colourwith == null)
-     {
-       _colourwith = new Vector<AlignmentPanel>();
-     }
-     if (jmb.isColourBySequence())
-     {
-       if (!jmb.isLoadingFromArchive())
-       {
-         if (_colourwith.size() == 0 && getAlignmentPanel() != null)
-         {
-           // Make the currently displayed alignment panel the associated view
-           _colourwith.add(getAlignmentPanel().alignFrame.alignPanel);
-         }
-       }
-       // Set the colour using the current view for the associated alignframe
-       for (AlignmentPanel ap : _colourwith)
-       {
-         jmb.colourBySequence(ap);
-       }
-     }
-   }
-   @Override
-   public void chainColour_actionPerformed(ActionEvent actionEvent)
-   {
-     chainColour.setSelected(true);
-     jmb.colourByChain();
-   }
-   @Override
-   public void chargeColour_actionPerformed(ActionEvent actionEvent)
-   {
-     chargeColour.setSelected(true);
-     jmb.colourByCharge();
-   }
-   @Override
-   public void zappoColour_actionPerformed(ActionEvent actionEvent)
-   {
-     zappoColour.setSelected(true);
-     jmb.setJalviewColourScheme(new ZappoColourScheme());
-   }
-   @Override
-   public void taylorColour_actionPerformed(ActionEvent actionEvent)
-   {
-     taylorColour.setSelected(true);
-     jmb.setJalviewColourScheme(new TaylorColourScheme());
-   }
-   @Override
-   public void hydroColour_actionPerformed(ActionEvent actionEvent)
-   {
-     hydroColour.setSelected(true);
-     jmb.setJalviewColourScheme(new HydrophobicColourScheme());
-   }
-   @Override
-   public void helixColour_actionPerformed(ActionEvent actionEvent)
-   {
-     helixColour.setSelected(true);
-     jmb.setJalviewColourScheme(new HelixColourScheme());
-   }
-   @Override
-   public void strandColour_actionPerformed(ActionEvent actionEvent)
-   {
-     strandColour.setSelected(true);
-     jmb.setJalviewColourScheme(new StrandColourScheme());
-   }
-   @Override
-   public void turnColour_actionPerformed(ActionEvent actionEvent)
-   {
-     turnColour.setSelected(true);
-     jmb.setJalviewColourScheme(new TurnColourScheme());
-   }
-   @Override
-   public void buriedColour_actionPerformed(ActionEvent actionEvent)
-   {
-     buriedColour.setSelected(true);
-     jmb.setJalviewColourScheme(new BuriedColourScheme());
-   }
-   @Override
-   public void purinePyrimidineColour_actionPerformed(ActionEvent actionEvent)
-   {
-     setJalviewColourScheme(new PurinePyrimidineColourScheme());
-   }
-   @Override
-   public void userColour_actionPerformed(ActionEvent actionEvent)
-   {
-     userColour.setSelected(true);
-     new UserDefinedColours(this, null);
-   }
-   @Override
-   public void backGround_actionPerformed(ActionEvent actionEvent)
-   {
-     java.awt.Color col = JColorChooser
-             .showDialog(this, MessageManager
-                     .getString("label.select_backgroud_colour"), null);
-     if (col != null)
-     {
-       jmb.setBackgroundColour(col);
-     }
-   }
-   @Override
    public void showHelp_actionPerformed(ActionEvent actionEvent)
    {
      try
      {
-       jalview.util.BrowserLauncher
+       BrowserLauncher
                .openURL("http://jmol.sourceforge.net/docs/JmolUserGuide/");
      } catch (Exception ex)
      {
      {
        getSize(currentSize);
  
-       if (jmb != null && jmb.fileLoadingError != null)
+       if (jmb != null && jmb.hasFileLoadingError())
        {
          g.setColor(Color.black);
          g.fillRect(0, 0, currentSize.width, currentSize.height);
      }
    }
  
-   public void updateTitleAndMenus()
-   {
-     if (jmb.fileLoadingError != null && jmb.fileLoadingError.length() > 0)
-     {
-       repaint();
-       return;
-     }
-     setChainMenuItems(jmb.getChainNames());
-     this.setTitle(jmb.getViewerTitle());
-     if (jmb.getPdbFile().length > 1 && jmb.getSequence().length > 1)
-     {
-       viewerActionMenu.setVisible(true);
-     }
-     if (!jmb.isLoadingFromArchive())
-     {
-       seqColour_actionPerformed(null);
-     }
-   }
-   /*
-    * (non-Javadoc)
-    * 
-    * @see
-    * jalview.jbgui.GStructureViewer#alignStructs_actionPerformed(java.awt.event
-    * .ActionEvent)
-    */
-   @Override
-   protected void alignStructs_actionPerformed(ActionEvent actionEvent)
-   {
-     alignStructs_withAllAlignPanels();
-   }
-   private void alignStructs_withAllAlignPanels()
-   {
-     if (getAlignmentPanel() == null)
-     {
-       return;
-     }
-     ;
-     if (_alignwith.size() == 0)
-     {
-       _alignwith.add(getAlignmentPanel());
-     }
-     ;
-     try
-     {
-       AlignmentI[] als = new Alignment[_alignwith.size()];
-       ColumnSelection[] alc = new ColumnSelection[_alignwith.size()];
-       int[] alm = new int[_alignwith.size()];
-       int a = 0;
-       for (AlignmentPanel ap : _alignwith)
-       {
-         als[a] = ap.av.getAlignment();
-         alm[a] = -1;
-         alc[a++] = ap.av.getColumnSelection();
-       }
-       jmb.superposeStructures(als, alm, alc);
-     } catch (Exception e)
-     {
-       StringBuffer sp = new StringBuffer();
-       for (AlignmentPanel ap : _alignwith)
-       {
-         sp.append("'" + ap.alignFrame.getTitle() + "' ");
-       }
-       Cache.log.info("Couldn't align structures with the " + sp.toString()
-               + "associated alignment panels.", e);
-     }
-   }
-   @Override
-   public void setJalviewColourScheme(ColourSchemeI ucs)
-   {
-     jmb.setJalviewColourScheme(ucs);
-   }
-   /**
-    * 
-    * @param alignment
-    * @return first alignment panel displaying given alignment, or the default
-    *         alignment panel
-    */
-   public AlignmentPanel getAlignmentPanelFor(AlignmentI alignment)
-   {
-     for (AlignmentPanel ap : getAllAlignmentPanels())
-     {
-       if (ap.av.getAlignment() == alignment)
-       {
-         return ap;
-       }
-     }
-     return getAlignmentPanel();
-   }
    @Override
    public AAStructureBindingModel getBinding()
    {
    }
  
    @Override
+   protected String getViewerName()
+   {
+     return "Jmol";
+   }
++
++  @Override
 +  protected AAStructureBindingModel getBindingModel()
 +  {
 +    return jmb;
 +  }
  }
  package jalview.gui;
  
  import jalview.bin.Cache;
- import jalview.datamodel.Alignment;
  import jalview.datamodel.AlignmentI;
- import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SequenceI;
+ import jalview.ext.rbvi.chimera.ChimeraCommands;
  import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
  import jalview.gui.StructureViewer.ViewerType;
  import jalview.io.DataSourceType;
- import jalview.io.JalviewFileChooser;
- import jalview.io.JalviewFileView;
  import jalview.io.StructureFile;
- import jalview.schemes.BuriedColourScheme;
- import jalview.schemes.ColourSchemeI;
- import jalview.schemes.HelixColourScheme;
- import jalview.schemes.HydrophobicColourScheme;
- import jalview.schemes.PurinePyrimidineColourScheme;
- import jalview.schemes.StrandColourScheme;
- import jalview.schemes.TaylorColourScheme;
- import jalview.schemes.TurnColourScheme;
- import jalview.schemes.ZappoColourScheme;
  import jalview.structures.models.AAStructureBindingModel;
+ import jalview.util.BrowserLauncher;
  import jalview.util.MessageManager;
  import jalview.util.Platform;
  import jalview.ws.dbsources.Pdb;
  
  import java.awt.event.ActionEvent;
- import java.awt.event.ItemEvent;
- import java.awt.event.ItemListener;
- import java.io.BufferedReader;
+ import java.awt.event.ActionListener;
+ import java.awt.event.MouseAdapter;
+ import java.awt.event.MouseEvent;
  import java.io.File;
  import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.FileReader;
  import java.io.IOException;
  import java.io.InputStream;
- import java.io.PrintWriter;
  import java.util.ArrayList;
+ import java.util.Collections;
  import java.util.List;
  import java.util.Random;
- 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.event.InternalFrameAdapter;
  import javax.swing.event.InternalFrameEvent;
- import javax.swing.event.MenuEvent;
- import javax.swing.event.MenuListener;
  
  /**
   * GUI elements for handling an external chimera display
@@@ -81,8 -65,6 +65,6 @@@ public class ChimeraViewFrame extends S
  {
    private JalviewChimeraBinding jmb;
  
-   private boolean allChainsSelected = false;
    private IProgressIndicator progressBar = null;
  
    /*
  
    private Random random = new Random();
  
+   private int myWidth = 500;
+   private int myHeight = 150;
    /**
     * Initialise menu options.
     */
-   private void initMenus()
+   @Override
+   protected void initMenus()
    {
+     super.initMenus();
      viewerActionMenu.setText(MessageManager.getString("label.chimera"));
      viewerColour.setText(MessageManager
              .getString("label.colour_with_chimera"));
      viewerColour.setToolTipText(MessageManager
              .getString("label.let_chimera_manage_structure_colours"));
-     helpItem.setText(MessageManager.getString("label.chimera_help"));
-     seqColour.setSelected(jmb.isColourBySequence());
-     viewerColour.setSelected(!jmb.isColourBySequence());
-     if (_colourwith == null)
-     {
-       _colourwith = new Vector<AlignmentPanel>();
-     }
-     if (_alignwith == null)
-     {
-       _alignwith = new Vector<AlignmentPanel>();
-     }
-     // save As not yet implemented
-     savemenu.setVisible(false);
  
-     ViewSelectionMenu seqColourBy = new ViewSelectionMenu(
-             MessageManager.getString("label.colour_by"), this, _colourwith,
-             new ItemListener()
-             {
-               @Override
-               public void itemStateChanged(ItemEvent e)
-               {
-                 if (!seqColour.isSelected())
-                 {
-                   seqColour.doClick();
-                 }
-                 else
-                 {
-                   // update the Chimera display now.
-                   seqColour_actionPerformed(null);
-                 }
-               }
-             });
-     viewMenu.add(seqColourBy);
+     helpItem.setText(MessageManager.getString("label.chimera_help"));
+     savemenu.setVisible(false); // not yet implemented
      viewMenu.add(fitToWindow);
  
-     final ItemListener handler;
-     JMenu alpanels = new ViewSelectionMenu(
-             MessageManager.getString("label.superpose_with"), this,
-             _alignwith, handler = new ItemListener()
-             {
-               @Override
-               public void itemStateChanged(ItemEvent e)
-               {
-                 alignStructs.setEnabled(_alignwith.size() > 0);
-                 alignStructs.setToolTipText(MessageManager
-                         .formatMessage(
-                                 "label.align_structures_using_linked_alignment_views",
-                                 new Object[] { new Integer(_alignwith
-                                         .size()).toString() }));
-               }
-             });
-     handler.itemStateChanged(null);
-     viewerActionMenu.add(alpanels);
-     viewerActionMenu.addMenuListener(new MenuListener()
+     JMenuItem writeFeatures = new JMenuItem(
+             MessageManager.getString("label.create_chimera_attributes"));
+     writeFeatures.setToolTipText(MessageManager
+             .getString("label.create_chimera_attributes_tip"));
+     writeFeatures.addActionListener(new ActionListener()
      {
        @Override
-       public void menuSelected(MenuEvent e)
+       public void actionPerformed(ActionEvent e)
        {
-         handler.itemStateChanged(null);
+         sendFeaturesToChimera();
        }
+     });
+     viewerActionMenu.add(writeFeatures);
+     final JMenu fetchAttributes = new JMenu(
+             MessageManager.getString("label.fetch_chimera_attributes"));
+     fetchAttributes.setToolTipText(MessageManager
+             .getString("label.fetch_chimera_attributes_tip"));
+     fetchAttributes.addMouseListener(new MouseAdapter()
+     {
  
        @Override
-       public void menuDeselected(MenuEvent e)
+       public void mouseEntered(MouseEvent e)
        {
-         // TODO Auto-generated method stub
+         buildAttributesMenu(fetchAttributes);
        }
+     });
+     viewerActionMenu.add(fetchAttributes);
  
-       @Override
-       public void menuCanceled(MenuEvent e)
+   }
+   /**
+    * Query Chimera for its residue attribute names and add them as items off the
+    * attributes menu
+    * 
+    * @param attributesMenu
+    */
+   protected void buildAttributesMenu(JMenu attributesMenu)
+   {
+     List<String> atts = jmb.sendChimeraCommand("list resattr", true);
+     if (atts == null)
+     {
+       return;
+     }
+     attributesMenu.removeAll();
+     Collections.sort(atts);
+     for (String att : atts)
+     {
+       final String attName = att.split(" ")[1];
+       /*
+        * ignore 'jv_*' attributes, as these are Jalview features that have
+        * been transferred to residue attributes in Chimera!
+        */
+       if (!attName.startsWith(ChimeraCommands.NAMESPACE_PREFIX))
        {
-         // TODO Auto-generated method stub
+         JMenuItem menuItem = new JMenuItem(attName);
+         menuItem.addActionListener(new ActionListener()
+         {
+           @Override
+           public void actionPerformed(ActionEvent e)
+           {
+             getChimeraAttributes(attName);
+           }
+         });
+         attributesMenu.add(menuItem);
        }
-     });
+     }
+   }
+   /**
+    * Read residues in Chimera with the given attribute name, and set as features
+    * on the corresponding sequence positions (if any)
+    * 
+    * @param attName
+    */
+   protected void getChimeraAttributes(String attName)
+   {
+     jmb.copyStructureAttributesToFeatures(attName, getAlignmentPanel());
+   }
+   /**
+    * Send a command to Chimera to create residue attributes for Jalview features
+    * <p>
+    * The syntax is: setattr r <attName> <attValue> <atomSpec>
+    * <p>
+    * For example: setattr r jv:chain "Ferredoxin-1, Chloroplastic" #0:94.A
+    */
+   protected void sendFeaturesToChimera()
+   {
+     int count = jmb.sendFeaturesToViewer(getAlignmentPanel());
+     statusBar.setText(MessageManager.formatMessage("label.attributes_set",
+             count));
    }
  
    /**
      }
    }
  
-   /**
-    * Answers true if this viewer already involves the given PDB ID
-    */
-   @Override
-   protected boolean hasPdbId(String pdbId)
-   {
-     return jmb.hasPdbId(pdbId);
-   }
    private void openNewChimera(AlignmentPanel ap, PDBEntry[] pdbentrys,
            SequenceI[][] seqs)
    {
      createProgressBar();
-     // FIXME extractChains needs pdbentries to match IDs to PDBEntry(s) on seqs
      jmb = new JalviewChimeraBindingModel(this,
              ap.getStructureSelectionManager(), pdbentrys, seqs, null);
      addAlignmentPanel(ap);
      useAlignmentPanelForColourbyseq(ap);
      if (pdbentrys.length > 1)
      {
        alignAddedStructures = true;
        useAlignmentPanelForSuperposition(ap);
      }
      jmb.setColourBySequence(true);
-     setSize(400, 400); // probably should be a configurable/dynamic default here
+     setSize(myWidth, myHeight);
      initMenus();
  
      addingStructures = false;
  
    }
  
    /**
     * Create a new viewer from saved session state data including Chimera session
     * file
    void initChimera()
    {
      jmb.setFinishedInit(false);
-     jalview.gui.Desktop.addInternalFrame(this,
-             jmb.getViewerTitle("Chimera", true), getBounds().width,
+     Desktop.addInternalFrame(this,
+             jmb.getViewerTitle(getViewerName(), true), getBounds().width,
              getBounds().height);
  
      if (!jmb.launchChimera())
                          + chimeraSessionFile);
        }
      }
-     jmb.setFinishedInit(true);
  
      jmb.startChimeraListener();
    }
  
    /**
     * Show only the selected chain(s) in the viewer
     */
        {
          String prompt = MessageManager.formatMessage(
                  "label.confirm_close_chimera",
-                 new Object[] { jmb.getViewerTitle("Chimera", false) });
+                         new Object[] { jmb.getViewerTitle(getViewerName(),
+                                 false) });
          prompt = JvSwingUtils.wrapTooltip(true, prompt);
          int confirm = JvOptionPane.showConfirmDialog(this, prompt,
                  MessageManager.getString("label.close_viewer"),
  
      if (files.length() > 0)
      {
+       jmb.setFinishedInit(false);
        if (!addingStructures)
        {
          try
            try
            {
              int pos = filePDBpos.get(num).intValue();
-             long startTime = startProgressBar("Chimera "
+             long startTime = startProgressBar(getViewerName() + " "
                      + MessageManager.getString("status.opening_file_for")
                      + " " + pe.getId());
              jmb.openFile(pe);
              }
              // Explicitly map to the filename used by Chimera ;
              pdb = jmb.getSsm().setMapping(jmb.getSequence()[pos],
 -                    jmb.getChains()[pos], pe.getFile(), protocol);
 +                    jmb.getChains()[pos], pe.getFile(), protocol,
 +                    progressBar);
              stashFoundChains(pdb, pe.getFile());
            } catch (OutOfMemoryError oomerror)
            {
            }
          }
        }
        jmb.refreshGUI();
        jmb.setFinishedInit(true);
        jmb.setLoadingFromArchive(false);
  
    /**
     * Fetch PDB data and save to a local file. Returns the full path to the file,
 -   * or null if fetch fails.
 +   * or null if fetch fails. TODO: refactor to common with Jmol ? duplication
     * 
     * @param processingEntry
     * @return
    }
  
    @Override
-   public void pdbFile_actionPerformed(ActionEvent actionEvent)
-   {
-     JalviewFileChooser chooser = new JalviewFileChooser(
-             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
-     chooser.setFileView(new JalviewFileView());
-     chooser.setDialogTitle(MessageManager.getString("label.save_pdb_file"));
-     chooser.setToolTipText(MessageManager.getString("action.save"));
-     int value = chooser.showSaveDialog(this);
-     if (value == JalviewFileChooser.APPROVE_OPTION)
-     {
-       BufferedReader in = null;
-       try
-       {
-         // TODO: cope with multiple PDB files in view
-         in = new BufferedReader(new FileReader(jmb.getPdbFile()[0]));
-         File outFile = chooser.getSelectedFile();
-         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
-         String data;
-         while ((data = in.readLine()) != null)
-         {
-           if (!(data.indexOf("<PRE>") > -1 || data.indexOf("</PRE>") > -1))
-           {
-             out.println(data);
-           }
-         }
-         out.close();
-       } catch (Exception ex)
-       {
-         ex.printStackTrace();
-       } finally
-       {
-         if (in != null)
-         {
-           try
-           {
-             in.close();
-           } catch (IOException e)
-           {
-             e.printStackTrace();
-           }
-         }
-       }
-     }
-   }
-   @Override
-   public void viewMapping_actionPerformed(ActionEvent actionEvent)
-   {
-     jalview.gui.CutAndPasteTransfer cap = new jalview.gui.CutAndPasteTransfer();
-     try
-     {
-       cap.appendText(jmb.printMappings());
-     } catch (OutOfMemoryError e)
-     {
-       new OOMWarning(
-               "composing sequence-structure alignments for display in text box.",
-               e);
-       cap.dispose();
-       return;
-     }
-     jalview.gui.Desktop.addInternalFrame(cap,
-             MessageManager.getString("label.pdb_sequence_mapping"), 550,
-             600);
-   }
-   @Override
    public void eps_actionPerformed(ActionEvent e)
    {
      throw new Error(
    }
  
    @Override
-   public void viewerColour_actionPerformed(ActionEvent actionEvent)
-   {
-     if (viewerColour.isSelected())
-     {
-       // disable automatic sequence colouring.
-       jmb.setColourBySequence(false);
-     }
-   }
-   @Override
-   public void seqColour_actionPerformed(ActionEvent actionEvent)
-   {
-     jmb.setColourBySequence(seqColour.isSelected());
-     if (_colourwith == null)
-     {
-       _colourwith = new Vector<AlignmentPanel>();
-     }
-     if (jmb.isColourBySequence())
-     {
-       if (!jmb.isLoadingFromArchive())
-       {
-         if (_colourwith.size() == 0 && getAlignmentPanel() != null)
-         {
-           // Make the currently displayed alignment panel the associated view
-           _colourwith.add(getAlignmentPanel().alignFrame.alignPanel);
-         }
-       }
-       // Set the colour using the current view for the associated alignframe
-       for (AlignmentPanel ap : _colourwith)
-       {
-         jmb.colourBySequence(ap.av.isShowSequenceFeatures(), ap);
-       }
-     }
-   }
-   @Override
-   public void chainColour_actionPerformed(ActionEvent actionEvent)
-   {
-     chainColour.setSelected(true);
-     jmb.colourByChain();
-   }
-   @Override
-   public void chargeColour_actionPerformed(ActionEvent actionEvent)
-   {
-     chargeColour.setSelected(true);
-     jmb.colourByCharge();
-   }
-   @Override
-   public void zappoColour_actionPerformed(ActionEvent actionEvent)
-   {
-     zappoColour.setSelected(true);
-     jmb.setJalviewColourScheme(new ZappoColourScheme());
-   }
-   @Override
-   public void taylorColour_actionPerformed(ActionEvent actionEvent)
-   {
-     taylorColour.setSelected(true);
-     jmb.setJalviewColourScheme(new TaylorColourScheme());
-   }
-   @Override
-   public void hydroColour_actionPerformed(ActionEvent actionEvent)
-   {
-     hydroColour.setSelected(true);
-     jmb.setJalviewColourScheme(new HydrophobicColourScheme());
-   }
-   @Override
-   public void helixColour_actionPerformed(ActionEvent actionEvent)
-   {
-     helixColour.setSelected(true);
-     jmb.setJalviewColourScheme(new HelixColourScheme());
-   }
-   @Override
-   public void strandColour_actionPerformed(ActionEvent actionEvent)
-   {
-     strandColour.setSelected(true);
-     jmb.setJalviewColourScheme(new StrandColourScheme());
-   }
-   @Override
-   public void turnColour_actionPerformed(ActionEvent actionEvent)
-   {
-     turnColour.setSelected(true);
-     jmb.setJalviewColourScheme(new TurnColourScheme());
-   }
-   @Override
-   public void buriedColour_actionPerformed(ActionEvent actionEvent)
-   {
-     buriedColour.setSelected(true);
-     jmb.setJalviewColourScheme(new BuriedColourScheme());
-   }
-   @Override
-   public void purinePyrimidineColour_actionPerformed(ActionEvent actionEvent)
-   {
-     setJalviewColourScheme(new PurinePyrimidineColourScheme());
-   }
-   @Override
-   public void userColour_actionPerformed(ActionEvent actionEvent)
-   {
-     userColour.setSelected(true);
-     new UserDefinedColours(this, null);
-   }
-   @Override
-   public void backGround_actionPerformed(ActionEvent actionEvent)
-   {
-     java.awt.Color col = JColorChooser
-             .showDialog(this, MessageManager
-                     .getString("label.select_backgroud_colour"), null);
-     if (col != null)
-     {
-       jmb.setBackgroundColour(col);
-     }
-   }
-   @Override
    public void showHelp_actionPerformed(ActionEvent actionEvent)
    {
      try
      {
-       jalview.util.BrowserLauncher
+       BrowserLauncher
                .openURL("https://www.cgl.ucsf.edu/chimera/docs/UsersGuide");
-     } catch (Exception ex)
-     {
-     }
-   }
-   public void updateTitleAndMenus()
-   {
-     if (jmb.fileLoadingError != null && jmb.fileLoadingError.length() > 0)
-     {
-       repaint();
-       return;
-     }
-     setChainMenuItems(jmb.getChainNames());
-     this.setTitle(jmb.getViewerTitle("Chimera", true));
-     if (jmb.getPdbFile().length > 1 && jmb.getSequence().length > 1)
-     {
-       viewerActionMenu.setVisible(true);
-     }
-     if (!jmb.isLoadingFromArchive())
-     {
-       seqColour_actionPerformed(null);
-     }
-   }
-   /*
-    * (non-Javadoc)
-    * 
-    * @see
-    * jalview.jbgui.GStructureViewer#alignStructs_actionPerformed(java.awt.event
-    * .ActionEvent)
-    */
-   @Override
-   protected void alignStructs_actionPerformed(ActionEvent actionEvent)
-   {
-     alignStructs_withAllAlignPanels();
-   }
-   private void alignStructs_withAllAlignPanels()
-   {
-     if (getAlignmentPanel() == null)
-     {
-       return;
-     }
-     if (_alignwith.size() == 0)
-     {
-       _alignwith.add(getAlignmentPanel());
-     }
-     try
-     {
-       AlignmentI[] als = new Alignment[_alignwith.size()];
-       ColumnSelection[] alc = new ColumnSelection[_alignwith.size()];
-       int[] alm = new int[_alignwith.size()];
-       int a = 0;
-       for (AlignmentPanel ap : _alignwith)
-       {
-         als[a] = ap.av.getAlignment();
-         alm[a] = -1;
-         alc[a++] = ap.av.getColumnSelection();
-       }
-       jmb.superposeStructures(als, alm, alc);
-     } catch (Exception e)
-     {
-       StringBuffer sp = new StringBuffer();
-       for (AlignmentPanel ap : _alignwith)
-       {
-         sp.append("'" + ap.alignFrame.getTitle() + "' ");
-       }
-       Cache.log.info("Couldn't align structures with the " + sp.toString()
-               + "associated alignment panels.", e);
-     }
-   }
-   @Override
-   public void setJalviewColourScheme(ColourSchemeI ucs)
-   {
-     jmb.setJalviewColourScheme(ucs);
-   }
-   /**
-    * 
-    * @param alignment
-    * @return first alignment panel displaying given alignment, or the default
-    *         alignment panel
-    */
-   public AlignmentPanel getAlignmentPanelFor(AlignmentI alignment)
-   {
-     for (AlignmentPanel ap : getAllAlignmentPanels())
+     } catch (IOException ex)
      {
-       if (ap.av.getAlignment() == alignment)
-       {
-         return ap;
-       }
      }
-     return getAlignmentPanel();
    }
  
    @Override
    }
  
    @Override
-   protected AAStructureBindingModel getBindingModel()
+   protected String getViewerName()
    {
-     return jmb;
+     return "Chimera";
+   }
+   /**
+    * Sends commands to align structures according to associated alignment(s).
+    * 
+    * @return
+    */
+   @Override
+   protected String alignStructs_withAllAlignPanels()
+   {
+     String reply = super.alignStructs_withAllAlignPanels();
+     if (reply != null)
+     {
+       statusBar.setText("Superposition failed: " + reply);
+     }
+     return reply;
    }
 +
 +  @Override
 +  protected IProgressIndicator getIProgressIndicator()
 +  {
 +    return progressBar;
 +  }
++
++  @Override
++  protected AAStructureBindingModel getBindingModel()
++  {
++    return jmb;
++  }
  }
@@@ -39,6 -39,7 +39,7 @@@ import jalview.ext.varna.RnaModel
  import jalview.gui.StructureViewer.ViewerType;
  import jalview.io.DataSourceType;
  import jalview.io.FileFormat;
+ import jalview.renderer.ResidueShaderI;
  import jalview.schemabinding.version2.AlcodMap;
  import jalview.schemabinding.version2.AlcodonFrame;
  import jalview.schemabinding.version2.Annotation;
@@@ -1174,38 -1175,43 +1175,43 @@@ public class Jalview2XM
            // group has references so set its ID field
            jGroup.setId(groupRefs.get(sg));
          }
-         if (sg.cs != null)
+         ColourSchemeI colourScheme = sg.getColourScheme();
+         if (colourScheme != null)
          {
-           if (sg.cs.conservationApplied())
+           ResidueShaderI groupColourScheme = sg
+                   .getGroupColourScheme();
+           if (groupColourScheme.conservationApplied())
            {
-             jGroup.setConsThreshold(sg.cs.getConservationInc());
+             jGroup.setConsThreshold(groupColourScheme.getConservationInc());
  
-             if (sg.cs instanceof jalview.schemes.UserColourScheme)
+             if (colourScheme instanceof jalview.schemes.UserColourScheme)
              {
-               jGroup.setColour(setUserColourScheme(sg.cs, userColours, jms));
+               jGroup.setColour(setUserColourScheme(colourScheme,
+                       userColours, jms));
              }
              else
              {
-               jGroup.setColour(ColourSchemeProperty.getColourName(sg.cs));
+               jGroup.setColour(colourScheme.getSchemeName());
              }
            }
-           else if (sg.cs instanceof jalview.schemes.AnnotationColourGradient)
+           else if (colourScheme instanceof jalview.schemes.AnnotationColourGradient)
            {
              jGroup.setColour("AnnotationColourGradient");
              jGroup.setAnnotationColours(constructAnnotationColours(
-                     (jalview.schemes.AnnotationColourGradient) sg.cs,
+                     (jalview.schemes.AnnotationColourGradient) colourScheme,
                      userColours, jms));
            }
-           else if (sg.cs instanceof jalview.schemes.UserColourScheme)
+           else if (colourScheme instanceof jalview.schemes.UserColourScheme)
            {
-             jGroup.setColour(setUserColourScheme(sg.cs, userColours, jms));
+             jGroup.setColour(setUserColourScheme(colourScheme,
+                     userColours, jms));
            }
            else
            {
-             jGroup.setColour(ColourSchemeProperty.getColourName(sg.cs));
+             jGroup.setColour(colourScheme.getSchemeName());
            }
  
-           jGroup.setPidThreshold(sg.cs.getThreshold());
+           jGroup.setPidThreshold(groupColourScheme.getThreshold());
          }
  
          jGroup.setOutlineColour(sg.getOutlineColour().getRGB());
                  .getGlobalColourScheme()));
        }
  
+       ResidueShaderI vcs = av.getResidueShading();
        ColourSchemeI cs = av.getGlobalColourScheme();
  
        if (cs != null)
        {
-         if (cs.conservationApplied())
+         if (vcs.conservationApplied())
          {
-           view.setConsThreshold(cs.getConservationInc());
+           view.setConsThreshold(vcs.getConservationInc());
            if (cs instanceof jalview.schemes.UserColourScheme)
            {
              view.setBgColour(setUserColourScheme(cs, userColours, jms));
            }
          }
-         if (cs instanceof ResidueColourScheme)
-         {
-           view.setPidThreshold(cs.getThreshold());
-         }
+         view.setPidThreshold(vcs.getThreshold());
        }
  
        view.setConservationSelected(av.getConservationSelected());
      }
      else
      {
-       ac.setColourScheme(ColourSchemeProperty.getColourName(acg
-               .getBaseColour()));
+       ac.setColourScheme(ColourSchemeProperty.getColourName(acg.getBaseColour()));
      }
  
      ac.setMaxColour(acg.getMaxColour().getRGB());
                    && jGroup.getAnnotationColours() != null)
            {
              addAnnotSchemeGroup = true;
            }
            else
            {
-             cs = ColourSchemeProperty.getColour(al, jGroup.getColour());
-           }
-           if (cs != null)
-           {
-             cs.setThreshold(jGroup.getPidThreshold(), true);
+             cs = ColourSchemeProperty.getColourScheme(al, jGroup.getColour());
            }
          }
+         int pidThreshold = jGroup.getPidThreshold();
  
          Vector<SequenceI> seqs = new Vector<SequenceI>();
  
          SequenceGroup sg = new SequenceGroup(seqs, jGroup.getName(), cs,
                  jGroup.getDisplayBoxes(), jGroup.getDisplayText(),
                  jGroup.getColourText(), jGroup.getStart(), jGroup.getEnd());
+         sg.getGroupColourScheme().setThreshold(pidThreshold, true);
+         sg.getGroupColourScheme().setConservationInc(jGroup.getConsThreshold());
          sg.setOutlineColour(new java.awt.Color(jGroup.getOutlineColour()));
  
          sg.textColour = new java.awt.Color(jGroup.getTextCol1());
          if (addAnnotSchemeGroup)
          {
            // reconstruct the annotation colourscheme
-           sg.cs = constructAnnotationColour(jGroup.getAnnotationColours(),
-                   null, al, jms, false);
+           sg.setColourScheme(constructAnnotationColour(
+                   jGroup.getAnnotationColours(), null, al, jms, false));
          }
        }
      }
        StructureData filedat = oldFiles.get(id);
        String pdbFile = filedat.getFilePath();
        SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]);
 -      binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE);
 +      binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE,
 +              null);
        binding.addSequenceForStructFile(pdbFile, seq);
      }
      // and add the AlignmentPanel's reference to the view panel
  
      af.viewport.setShowAnnotation(view.getShowAnnotation());
      af.viewport.setAbovePIDThreshold(view.getPidSelected());
+     af.viewport.setThreshold(view.getPidThreshold());
  
      af.viewport.setColourText(view.getShowColourText());
  
      af.viewport.setConservationSelected(view.getConservationSelected());
+     af.viewport.setIncrement(view.getConsThreshold());
      af.viewport.setShowJVSuffix(view.getShowFullId());
      af.viewport.setRightAlignIds(view.getRightAlignIds());
      af.viewport.setFont(
        }
        else
        {
-         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
-       }
-       if (cs != null)
-       {
-         cs.setThreshold(view.getPidThreshold(), true);
-         cs.setConsensus(af.viewport.getSequenceConsensusHash());
+         cs = ColourSchemeProperty.getColourScheme(al, view.getBgColour());
        }
      }
  
      af.viewport.setGlobalColourScheme(cs);
+     af.viewport.getResidueShading().setThreshold(
+             view.getPidThreshold(), true);
+     af.viewport.getResidueShading().setConsensus(
+             af.viewport.getSequenceConsensusHash());
      af.viewport.setColourAppliesToAllGroups(false);
  
      if (view.getConservationSelected() && cs != null)
      {
-       cs.setConservationInc(view.getConsThreshold());
+       af.viewport.getResidueShading().setConservationInc(
+               view.getConsThreshold());
      }
  
      af.changeColour(cs);
        propagateAnnColour = true;
        for (jalview.datamodel.SequenceGroup sg : al.getGroups())
        {
-         if (sg.cs instanceof AnnotationColourGradient)
+         if (sg.getColourScheme() instanceof AnnotationColourGradient)
          {
            propagateAnnColour = false;
          }
                      );
            }
  
-           if (viewAnnColour.getColourScheme().equals("None"))
+           if (viewAnnColour.getColourScheme().equals(
+                   ResidueColourScheme.NONE))
            {
              cs = new AnnotationColourGradient(
                      annAlignment.getAlignmentAnnotation()[i],
            {
              cs = new AnnotationColourGradient(
                      annAlignment.getAlignmentAnnotation()[i],
-                     ColourSchemeProperty.getColour(al,
+                     ColourSchemeProperty.getColourScheme(al,
                              viewAnnColour.getColourScheme()),
                      viewAnnColour.getAboveThreshold());
            }
                }
  
                /*
-                * if (viewAnnColour.getColourScheme().equals("None" )) { sg.cs =
+                * if (viewAnnColour.getColourScheme().equals(ResidueColourScheme.NONE)) { sg.cs =
                 * new AnnotationColourGradient(
                 * annAlignment.getAlignmentAnnotation()[i], new
                 * java.awt.Color(viewAnnColour. getMinColour()), new
                 * viewAnnColour.getAboveThreshold()); } else
                 */
                {
-                 sg.cs = new AnnotationColourGradient(
-                         annAlignment.getAlignmentAnnotation()[i], sg.cs,
-                         viewAnnColour.getAboveThreshold());
+                 sg.setColourScheme(new AnnotationColourGradient(
+                         annAlignment.getAlignmentAnnotation()[i], sg
+                                 .getColourScheme(), viewAnnColour
+                                 .getAboveThreshold()));
                  if (cs instanceof AnnotationColourGradient)
                  {
                    if (viewAnnColour.hasPerSequence())
   */
  package jalview.gui;
  
+ import jalview.bin.Cache;
+ import jalview.datamodel.Alignment;
+ import jalview.datamodel.AlignmentI;
+ import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SequenceI;
  import jalview.gui.StructureViewer.ViewerType;
  import jalview.gui.ViewSelectionMenu.ViewSetProvider;
  import jalview.io.DataSourceType;
+ import jalview.io.JalviewFileChooser;
+ import jalview.io.JalviewFileView;
  import jalview.jbgui.GStructureViewer;
+ import jalview.schemes.ColourSchemeI;
+ import jalview.schemes.ColourSchemes;
  import jalview.structures.models.AAStructureBindingModel;
  import jalview.util.MessageManager;
  
+ import java.awt.Color;
  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.io.BufferedReader;
+ import java.io.File;
+ import java.io.FileOutputStream;
+ import java.io.FileReader;
+ import java.io.IOException;
+ import java.io.PrintWriter;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Vector;
  
+ import javax.swing.ButtonGroup;
  import javax.swing.JCheckBoxMenuItem;
+ import javax.swing.JColorChooser;
+ import javax.swing.JMenu;
  import javax.swing.JMenuItem;
+ import javax.swing.JRadioButtonMenuItem;
+ import javax.swing.event.MenuEvent;
+ import javax.swing.event.MenuListener;
  
  /**
   * Base class with common functionality for JMol, Chimera or other structure
  public abstract class StructureViewerBase extends GStructureViewer
          implements Runnable, ViewSetProvider
  {
+   /*
+    * names for colour options (additional to Jalview colour schemes)
+    */
+   enum ViewerColour
+   {
+     BySequence, ByChain, ChargeCysteine, ByViewer
+   }
  
    /**
     * list of sequenceSet ids associated with the view
  
    protected boolean allChainsSelected = false;
  
+   protected JMenu viewSelectionMenu;
+   /**
+    * Default constructor
+    */
+   public StructureViewerBase()
+   {
+     super();
+   }
    /**
     * 
     * @param ap2
  
    public abstract ViewerType getViewerType();
  
 +  protected abstract AAStructureBindingModel getBindingModel();
 +
 +  protected abstract IProgressIndicator getIProgressIndicator();
 +
    /**
     * add a new structure (with associated sequences and chains) to this viewer,
     * retrieving it if necessary first.
        }
      }
      // otherwise, start adding the structure.
-     getBindingModel().addSequenceAndChain(new PDBEntry[] { pdbentry },
+     getBinding().addSequenceAndChain(new PDBEntry[] { pdbentry },
              new SequenceI[][] { seqs }, new String[][] { chains });
      addingStructures = true;
      _started = false;
      return option;
    }
  
-   protected abstract boolean hasPdbId(String pdbId);
+   protected boolean hasPdbId(String pdbId)
+   {
+     return getBinding().hasPdbId(pdbId);
+   }
  
    protected abstract List<StructureViewerBase> getViewersFor(
            AlignmentPanel alp);
       * create the mappings
       */
      apanel.getStructureSelectionManager().setMapping(seq, chains,
 -            pdbFilename, DataSourceType.FILE);
 +            pdbFilename, DataSourceType.FILE, getIProgressIndicator());
  
      /*
       * alert the FeatureRenderer to show new (PDB RESNUM) features
      // JBPNOTE: this looks like a binding routine, rather than a gui routine
      for (StructureViewerBase viewer : getViewersFor(null))
      {
-       AAStructureBindingModel bindingModel = viewer.getBindingModel();
+       AAStructureBindingModel bindingModel = viewer.getBinding();
        for (int pe = 0; pe < bindingModel.getPdbCount(); pe++)
        {
          if (bindingModel.getPdbEntry(pe).getFile().equals(pdbFilename))
  
    abstract void showSelectedChains();
  
+   /**
+    * Action on selecting one of Jalview's registered colour schemes
+    */
+   @Override
+   public void changeColour_actionPerformed(String colourSchemeName)
+   {
+     AlignmentI al = getAlignmentPanel().av.getAlignment();
+     ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme(
+             colourSchemeName, al, null);
+     getBinding().setJalviewColourScheme(cs);
+   }
+   /**
+    * Builds the colour menu
+    */
+   protected void buildColourMenu()
+   {
+     colourMenu.removeAll();
+     AlignmentI al = getAlignmentPanel().av.getAlignment();
+     /*
+      * add colour by sequence, by chain, by charge and cysteine
+      */
+     colourMenu.add(seqColour);
+     colourMenu.add(chainColour);
+     colourMenu.add(chargeColour);
+     chargeColour.setEnabled(!al.isNucleotide());
+     /*
+      * add all 'simple' (per-residue) colour schemes registered to Jalview
+      */
+     ButtonGroup itemGroup = ColourMenuHelper.addMenuItems(colourMenu, this,
+             al, true);
+     /*
+      * add 'colour by viewer' (menu item text is set in subclasses)
+      */
+     viewerColour.setSelected(false);
+     viewerColour.addActionListener(new ActionListener()
+     {
+       @Override
+       public void actionPerformed(ActionEvent actionEvent)
+       {
+         viewerColour_actionPerformed(actionEvent);
+       }
+     });
+     colourMenu.add(viewerColour);
+     /*
+      * add 'set background colour'
+      */
+     JMenuItem backGround = new JMenuItem();
+     backGround
+             .setText(MessageManager.getString("action.background_colour"));
+     backGround.addActionListener(new ActionListener()
+     {
+       @Override
+       public void actionPerformed(ActionEvent actionEvent)
+       {
+         background_actionPerformed(actionEvent);
+       }
+     });
+     colourMenu.add(backGround);
+     /*
+      * add colour buttons to a group so their selection is
+      * mutually exclusive (background colour is a separate option)
+      */
+     itemGroup.add(seqColour);
+     itemGroup.add(chainColour);
+     itemGroup.add(chargeColour);
+     itemGroup.add(viewerColour);
+   }
+   /**
+    * Construct menu items
+    */
+   protected void initMenus()
+   {
+     AAStructureBindingModel binding = getBinding();
+     seqColour = new JRadioButtonMenuItem();
+     seqColour.setText(MessageManager.getString("action.by_sequence"));
+     seqColour.setName(ViewerColour.BySequence.name());
+     seqColour.setSelected(binding.isColourBySequence());
+     seqColour.addActionListener(new ActionListener()
+     {
+       @Override
+       public void actionPerformed(ActionEvent actionEvent)
+       {
+         seqColour_actionPerformed(actionEvent);
+       }
+     });
+     chainColour = new JRadioButtonMenuItem();
+     chainColour.setText(MessageManager.getString("action.by_chain"));
+     chainColour.setName(ViewerColour.ByChain.name());
+     chainColour.addActionListener(new ActionListener()
+     {
+       @Override
+       public void actionPerformed(ActionEvent actionEvent)
+       {
+         chainColour_actionPerformed(actionEvent);
+       }
+     });
+     chargeColour = new JRadioButtonMenuItem();
+     chargeColour.setText(MessageManager.getString("label.charge_cysteine"));
+     chargeColour.setName(ViewerColour.ChargeCysteine.name());
+     chargeColour.addActionListener(new ActionListener()
+     {
+       @Override
+       public void actionPerformed(ActionEvent actionEvent)
+       {
+         chargeColour_actionPerformed(actionEvent);
+       }
+     });
+     viewerColour = new JRadioButtonMenuItem();
+     // text is set in overrides of this method
+     viewerColour.setName(ViewerColour.ByViewer.name());
+     viewerColour.setSelected(!binding.isColourBySequence());
+     if (_colourwith == null)
+     {
+       _colourwith = new Vector<AlignmentPanel>();
+     }
+     if (_alignwith == null)
+     {
+       _alignwith = new Vector<AlignmentPanel>();
+     }
+     ViewSelectionMenu seqColourBy = new ViewSelectionMenu(
+             MessageManager.getString("label.colour_by"), this, _colourwith,
+             new ItemListener()
+             {
+               @Override
+               public void itemStateChanged(ItemEvent e)
+               {
+                 if (!seqColour.isSelected())
+                 {
+                   seqColour.doClick();
+                 }
+                 else
+                 {
+                   // update the Chimera display now.
+                   seqColour_actionPerformed(null);
+                 }
+               }
+             });
+     viewMenu.add(seqColourBy);
+     final ItemListener handler = new ItemListener()
+     {
+       @Override
+       public void itemStateChanged(ItemEvent e)
+       {
+         alignStructs.setEnabled(!_alignwith.isEmpty());
+         alignStructs.setToolTipText(MessageManager.formatMessage(
+                 "label.align_structures_using_linked_alignment_views",
+                 _alignwith.size()));
+       }
+     };
+     viewSelectionMenu = new ViewSelectionMenu(
+             MessageManager.getString("label.superpose_with"), this,
+             _alignwith, handler);
+     handler.itemStateChanged(null);
+     viewerActionMenu.add(viewSelectionMenu, 0);
+     viewerActionMenu.addMenuListener(new MenuListener()
+     {
+       @Override
+       public void menuSelected(MenuEvent e)
+       {
+         handler.itemStateChanged(null);
+       }
+       @Override
+       public void menuDeselected(MenuEvent e)
+       {
+       }
+       @Override
+       public void menuCanceled(MenuEvent e)
+       {
+       }
+     });
+     buildColourMenu();
+   }
+   @Override
+   public void setJalviewColourScheme(ColourSchemeI cs) {
+     getBinding().setJalviewColourScheme(cs);
+   }
+   /**
+    * Sends commands to the structure viewer to superimpose structures based on
+    * currently associated alignments. May optionally return an error message for
+    * the operation.
+    */
+   @Override
+   protected String alignStructs_actionPerformed(
+           ActionEvent actionEvent)
+   {
+     return alignStructs_withAllAlignPanels();
+   }
+   protected String alignStructs_withAllAlignPanels()
+   {
+     if (getAlignmentPanel() == null)
+     {
+       return null;
+     }
+   
+     if (_alignwith.size() == 0)
+     {
+       _alignwith.add(getAlignmentPanel());
+     }
+   
+     String reply = null;
+     try
+     {
+       AlignmentI[] als = new Alignment[_alignwith.size()];
+       ColumnSelection[] alc = new ColumnSelection[_alignwith.size()];
+       int[] alm = new int[_alignwith.size()];
+       int a = 0;
+   
+       for (AlignmentPanel ap : _alignwith)
+       {
+         als[a] = ap.av.getAlignment();
+         alm[a] = -1;
+         alc[a++] = ap.av.getColumnSelection();
+       }
+       reply = getBinding().superposeStructures(als, alm, alc);
+       if (reply != null)
+       {
+         String text = MessageManager.formatMessage(
+                 "error.superposition_failed", reply);
+         statusBar.setText(text);
+       }
+     } catch (Exception e)
+     {
+       StringBuffer sp = new StringBuffer();
+       for (AlignmentPanel ap : _alignwith)
+       {
+         sp.append("'" + ap.alignFrame.getTitle() + "' ");
+       }
+       Cache.log.info("Couldn't align structures with the " + sp.toString()
+               + "associated alignment panels.", e);
+     }
+     return reply;
+   }
+   @Override
+   public void background_actionPerformed(ActionEvent actionEvent)
+   {
+     Color col = JColorChooser.showDialog(this,
+             MessageManager.getString("label.select_background_colour"),
+             null);
+     if (col != null)
+     {
+       getBinding().setBackgroundColour(col);
+     }
+   }
+   @Override
+   public void viewerColour_actionPerformed(ActionEvent actionEvent)
+   {
+     if (viewerColour.isSelected())
+     {
+       // disable automatic sequence colouring.
+       getBinding().setColourBySequence(false);
+     }
+   }
+   @Override
+   public void chainColour_actionPerformed(ActionEvent actionEvent)
+   {
+     chainColour.setSelected(true);
+     getBinding().colourByChain();
+   }
+   @Override
+   public void chargeColour_actionPerformed(ActionEvent actionEvent)
+   {
+     chargeColour.setSelected(true);
+     getBinding().colourByCharge();
+   }
+   @Override
+   public void seqColour_actionPerformed(ActionEvent actionEvent)
+   {
+     AAStructureBindingModel binding = getBinding();
+     binding.setColourBySequence(seqColour.isSelected());
+     if (_colourwith == null)
+     {
+       _colourwith = new Vector<AlignmentPanel>();
+     }
+     if (binding.isColourBySequence())
+     {
+       if (!binding.isLoadingFromArchive())
+       {
+         if (_colourwith.size() == 0 && getAlignmentPanel() != null)
+         {
+           // Make the currently displayed alignment panel the associated view
+           _colourwith.add(getAlignmentPanel().alignFrame.alignPanel);
+         }
+       }
+       // Set the colour using the current view for the associated alignframe
+       for (AlignmentPanel ap : _colourwith)
+       {
+         binding.colourBySequence(ap);
+       }
+     }
+   }
+   @Override
+   public void pdbFile_actionPerformed(ActionEvent actionEvent)
+   {
+     JalviewFileChooser chooser = new JalviewFileChooser(
+             Cache.getProperty("LAST_DIRECTORY"));
+   
+     chooser.setFileView(new JalviewFileView());
+     chooser.setDialogTitle(MessageManager.getString("label.save_pdb_file"));
+     chooser.setToolTipText(MessageManager.getString("action.save"));
+   
+     int value = chooser.showSaveDialog(this);
+   
+     if (value == JalviewFileChooser.APPROVE_OPTION)
+     {
+       BufferedReader in = null;
+       try
+       {
+         // TODO: cope with multiple PDB files in view
+         in = new BufferedReader(
+                 new FileReader(getBinding().getPdbFile()[0]));
+         File outFile = chooser.getSelectedFile();
+   
+         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
+         String data;
+         while ((data = in.readLine()) != null)
+         {
+           if (!(data.indexOf("<PRE>") > -1 || data.indexOf("</PRE>") > -1))
+           {
+             out.println(data);
+           }
+         }
+         out.close();
+       } catch (Exception ex)
+       {
+         ex.printStackTrace();
+       } finally
+       {
+         if (in != null)
+         {
+           try
+           {
+             in.close();
+           } catch (IOException e)
+           {
+             // ignore
+           }
+         }
+       }
+     }
+   }
+   @Override
+   public void viewMapping_actionPerformed(ActionEvent actionEvent)
+   {
+     CutAndPasteTransfer cap = new CutAndPasteTransfer();
+     try
+     {
+       cap.appendText(getBinding().printMappings());
+     } catch (OutOfMemoryError e)
+     {
+       new OOMWarning(
+               "composing sequence-structure alignments for display in text box.",
+               e);
+       cap.dispose();
+       return;
+     }
+     Desktop.addInternalFrame(cap,
+             MessageManager.getString("label.pdb_sequence_mapping"), 550,
+             600);
+   }
+   protected abstract String getViewerName();
+   /**
+    * Configures the title and menu items of the viewer panel.
+    */
+   public void updateTitleAndMenus()
+   {
+     AAStructureBindingModel binding = getBinding();
+     if (binding.hasFileLoadingError())
+     {
+       repaint();
+       return;
+     }
+     setChainMenuItems(binding.getChainNames());
+   
+     this.setTitle(binding.getViewerTitle(getViewerName(), true));
+     /*
+      * enable 'Superpose with' if more than one mapped structure
+      */
+     viewSelectionMenu.setEnabled(false);
+     if (getBinding().getPdbFile().length > 1
+             && getBinding().getSequence().length > 1)
+     {
+       viewSelectionMenu.setEnabled(true);
+     }
+     /*
+      * Show action menu if it has any enabled items
+      */
+     viewerActionMenu.setVisible(false);
+     for (int i = 0; i < viewerActionMenu.getItemCount(); i++)
+     {
+       if (viewerActionMenu.getItem(i).isEnabled())
+       {
+         viewerActionMenu.setVisible(true);
+         break;
+       }
+     }
+     if (!binding.isLoadingFromArchive())
+     {
+       seqColour_actionPerformed(null);
+     }
+   }
  }
@@@ -28,18 -28,14 +28,19 @@@ import jalview.datamodel.Annotation
  import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.GraphLine;
  import jalview.datamodel.HiddenSequences;
 +import jalview.datamodel.PDBEntry;
 +import jalview.datamodel.PDBEntry.Type;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
 +import jalview.gui.Desktop;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.ColourSchemeProperty;
- import jalview.schemes.UserColourScheme;
 +import jalview.structure.StructureSelectionManager;
+ import jalview.util.ColorUtils;
  
+ import java.awt.Color;
  import java.io.BufferedReader;
 +import java.io.File;
  import java.io.FileReader;
  import java.io.InputStreamReader;
  import java.io.StringReader;
@@@ -586,7 -582,7 +587,7 @@@ public class AnnotationFil
        if (sg.cs != null)
        {
          text.append("colour=");
-         text.append(ColourSchemeProperty.getColourName(sg.cs));
+         text.append(sg.cs.toString());
          text.append("\t");
          if (sg.cs.getThreshold() != 0)
          {
    public boolean readAnnotationFile(AlignmentI al, ColumnSelection colSel,
            String file, DataSourceType sourceType)
    {
 +    baseUri = "";
      BufferedReader in = null;
      try
      {
        if (sourceType == DataSourceType.FILE)
        {
          in = new BufferedReader(new FileReader(file));
 +        baseUri = new File(file).getParent();
 +        if (baseUri == null)
 +        {
 +          baseUri = "";
 +        }
 +        else
 +        {
 +          baseUri += "/";
 +        }
        }
        else if (sourceType == DataSourceType.URL)
        {
          URL url = new URL(file);
          in = new BufferedReader(new InputStreamReader(url.openStream()));
 +        String bs = url.toExternalForm();
 +        baseUri = bs.substring(0, bs.indexOf(url.getHost())
 +                + url.getHost().length());
 +        baseUri += url.toURI().getPath();
 +        if (baseUri.lastIndexOf("/") > -1)
 +        {
 +          baseUri = baseUri.substring(0, baseUri.lastIndexOf("/")) + "/";
 +        }
        }
        else if (sourceType == DataSourceType.PASTE)
        {
          in = new BufferedReader(new StringReader(file));
 +        // TODO - support mimencoded PDBs for a paste.. ?
 +        baseUri = "";
        }
        else if (sourceType == DataSourceType.CLASSLOADER)
        {
          if (is != null)
          {
            in = new BufferedReader(new java.io.InputStreamReader(is));
 +          // TODO: this probably doesn't work for classloader - needs a test
 +          baseUri = new File("/" + file).getParent() + "/";
          }
        }
        if (in != null)
  
    String lastread = "";
  
 -  private static String GRAPHLINE = "GRAPHLINE", COMBINE = "COMBINE";
 +  /**
 +   * used for resolving absolute references to resources relative to
 +   * annotationFile location
 +   */
 +  String baseUri = "";
 +
 +  private static String GRAPHLINE = "GRAPHLINE", COMBINE = "COMBINE",
 +          STRUCTMODEL = "STRUCTMODEL";
  
    public boolean parseAnnotationFrom(AlignmentI al, ColumnSelection colSel,
            BufferedReader in) throws Exception
            modified = true;
            continue;
          }
 -
 +        else if (token.equalsIgnoreCase(STRUCTMODEL))
 +        {
 +          boolean failedtoadd = true;
 +          // expect
 +          // STRUCTMODEL <Query> <TemplateSeqId> <ModelFile> <FastaMappingFile>
 +          // <Confidence> <%.I.D>
 +          // <MatchStart> <MatchEnd> <Coverage> [<Other Information>]
 +          String querySeqId = !st.hasMoreTokens() ? "" : st.nextToken();
 +          SequenceI querySeq = al.findName(querySeqId);
 +          if (st.hasMoreTokens()) {
 +            refSeq = al.findName(refSeqId = st.nextToken());
 +            if (refSeq == null)
 +            {
 +              System.err.println("Couldn't locate " + refSeqId
 +                      + " in the alignment for STRUCTMODEL");
 +              refSeqId = null;
 +            }
 +            else
 +            {
 +              String tempId = st.nextToken();
 +              String fastaMapping = st.nextToken();
 +              String confidence = !st.hasMoreTokens() ? "" : 100
 +                      * Double.valueOf(st.nextToken()) + "";
 +              String pid = !st.hasMoreTokens() ? "" : st.nextToken();
 +              String alignRange = !st.hasMoreTokens() ? "" : st.nextToken()
 +                      + "-" + st.nextToken();
 +              String otherInfo = !st.hasMoreTokens() ? "" : st.nextToken();
 +              String coverage = "";
 +              if (add_structmodel(al, querySeq, refSeq, tempId,
 +                      fastaMapping,
 +                      alignRange, coverage,
 +                      confidence, pid, otherInfo))
 +              {
 +                failedtoadd = false;
 +              }
 +            }
 +          }
 +          if (failedtoadd)
 +          {
 +            System.err
 +                    .println("Need <Query> <TemplateSeqId> <ModelFile> <FastaMappingFile> <Confidence> <%.I.D> <MatchStart> <MatchEnd> <Coverage> [<Other Information>] as tab separated fields after"
 +                            + STRUCTMODEL
 +                            + ".\nNote: other information could be provided in html format ");
 +          } else {
 +            modified = true;
 +          }
 +          continue;
 +        }
          // Parse out the annotation row
          graphStyle = AlignmentAnnotation.getGraphValueFromString(token);
          label = st.nextToken();
      return modified;
    }
  
 +  /**
 +   * resolve a structural model and generate and add an alignment sequence for
 +   * it
 +   * 
 +   * @param refSeq2
 +   * @param tempId
 +   * @param urlToModel
 +   * @param urlToPairwise
 +   * @return true if model and sequence was added
 +   */
 +  private boolean add_structmodel(AlignmentI al, SequenceI querySequence,
 +          SequenceI templateSeq,
 +          String modelFile, String fastaFile, String aRange,
 +          String coverage, String confidence,
 +          String pid, String otherInfo)
 +  {
 +    String warningMessage = null;
 +    boolean added = false;
 +    try {
 +      String structureModelFile = resolveAbsolute(modelFile);
 +      String fastaMappingFile = resolveAbsolute(fastaFile.replaceAll(
 +              ".fasta.jal", ".fasta"));
 +      // System.out.println("Model File >> " + structureModelFile);
 +      // System.out.println("Fasta File >> " + fastaMappingFile);
 +      PDBEntry phyre2PDBEntry = new PDBEntry(modelFile, null, Type.FILE,
 +              structureModelFile);
 +      String phyre2ModelDesc = generatePhyre2InfoHTMLTable(aRange,
 +              coverage, confidence, pid, otherInfo);
 +      phyre2PDBEntry.setProperty("PHYRE2_MODEL_INFO", phyre2ModelDesc);
 +      templateSeq.getDatasetSequence().addPDBId(phyre2PDBEntry);
 +      if (querySequence != null)
 +      {
 +        querySequence.getDatasetSequence().addPDBId(phyre2PDBEntry);
 +      }
 +      StructureSelectionManager ssm = StructureSelectionManager
 +              .getStructureSelectionManager(Desktop.instance);
 +      ssm.registerPhyre2Template(structureModelFile, fastaMappingFile);
 +      added = true;
 +
 +    } catch (Exception x)
 +    {
 +      warningMessage = x.toString();
 +    } finally {
 +      if (warningMessage !=null)
 +      {
 +        System.err.println("Warnings whilst processing STRUCTMODEL: "+warningMessage);
 +      }
 +    }
 +    return added;
 +  }
 +
 +  private String generatePhyre2InfoHTMLTable(String aRange,
 +          String coverage, String confidence, String pid, String otherInfo)
 +  {
 +    StringBuilder phyre2InfoBuilder = new StringBuilder();
 +    phyre2InfoBuilder.append("<html><table border=\"1\" width=100%>");
 +    phyre2InfoBuilder
 +            .append("<tr><td colspan=\"2\"><strong>Phyre2 Template Info</strong></td></tr>");
 +    if (aRange != null && !aRange.isEmpty())
 +    {
 +      phyre2InfoBuilder.append("<tr><td>").append("Aligned range")
 +              .append("</td><td>").append(aRange).append("</td></tr>");
 +    }
 +    if (coverage != null && !coverage.isEmpty())
 +    {
 +      phyre2InfoBuilder.append("<tr><td>").append("Coverage")
 +              .append("</td><td>").append(coverage).append("</td></tr>");
 +    }
 +    if (confidence != null && !confidence.isEmpty())
 +    {
 +      phyre2InfoBuilder.append("<tr><td>").append("Confidence")
 +              .append("</td><td>").append(confidence).append("</td></tr>");
 +    }
 +    if (pid != null && !pid.isEmpty())
 +    {
 +      phyre2InfoBuilder.append("<tr><td>").append("%.i.d")
 +              .append("</td><td>").append(pid).append("</td></tr>");
 +    }
 +    if (otherInfo != null && !otherInfo.isEmpty())
 +    {
 +      phyre2InfoBuilder.append("<tr><td>").append("Other information")
 +              .append("</td><td>").append(otherInfo).append("</td></tr>");
 +    }
 +    phyre2InfoBuilder.append("</table></html>");
 +    return phyre2InfoBuilder.toString();
 +  }
 +
 +  private String resolveAbsolute(String relURI)
 +  {
 +    if (relURI.indexOf(":/") > -1 || relURI.startsWith("/")
 +            || "".equals(baseUri) || relURI.startsWith(baseUri))
 +    {
 +      return relURI;
 +    }
 +    return baseUri + relURI;
 +  }
 +
    private void parseHideCols(ColumnSelection colSel, String nextToken)
    {
      StringTokenizer inval = new StringTokenizer(nextToken, ",");
  
    Annotation parseAnnotation(String string, int graphStyle)
    {
-     boolean hasSymbols = (graphStyle == AlignmentAnnotation.NO_GRAPH); // don't
-     // do the
-     // glyph
-     // test
-     // if we
-     // don't
-     // want
-     // secondary
-     // structure
+     // don't do the glyph test if we don't want secondary structure
+     boolean hasSymbols = (graphStyle == AlignmentAnnotation.NO_GRAPH);
      String desc = null, displayChar = null;
      char ss = ' '; // secondaryStructure
      float value = 0;
      boolean parsedValue = false, dcset = false;
  
      // find colour here
-     java.awt.Color colour = null;
+     Color colour = null;
      int i = string.indexOf("[");
      int j = string.indexOf("]");
      if (i > -1 && j > -1)
      {
-       UserColourScheme ucs = new UserColourScheme();
-       colour = ucs.getColourFromString(string.substring(i + 1, j));
+       colour = ColorUtils.parseColourString(string.substring(i + 1,
+               j));
        if (i > 0 && string.charAt(i - 1) == ',')
        {
          // clip the preceding comma as well
  
    void colourAnnotations(AlignmentI al, String label, String colour)
    {
-     UserColourScheme ucs = new UserColourScheme(colour);
+     Color awtColour = ColorUtils.parseColourString(colour);
      Annotation[] annotations;
      for (int i = 0; i < al.getAlignmentAnnotation().length; i++)
      {
          {
            if (annotations[j] != null)
            {
-             annotations[j].colour = ucs.findColour('A');
+             annotations[j].colour = awtColour;
            }
          }
        }
            SequenceGroup groupRef)
    {
      String group = st.nextToken();
-     AlignmentAnnotation annotation = null, alannot[] = al
-             .getAlignmentAnnotation();
-     float value = new Float(st.nextToken()).floatValue();
+     AlignmentAnnotation[] alannot = al.getAlignmentAnnotation();
+     String nextToken = st.nextToken();
+     float value = 0f;
+     try
+     {
+       value = Float.valueOf(nextToken);
+     } catch (NumberFormatException e)
+     {
+       System.err.println("line " + nlinesread + ": Threshold '" + nextToken
+               + "' invalid, setting to zero");
+     }
      String label = st.hasMoreTokens() ? st.nextToken() : null;
-     java.awt.Color colour = null;
+     Color colour = null;
      if (st.hasMoreTokens())
      {
-       UserColourScheme ucs = new UserColourScheme(st.nextToken());
-       colour = ucs.findColour('A');
+       colour = ColorUtils.parseColourString(st.nextToken());
      }
      if (alannot != null)
      {
          }
        }
      }
-     if (annotation == null)
-     {
-       return;
-     }
    }
  
    void addGroup(AlignmentI al, StringTokenizer st)
      if (sg != null)
      {
        String keyValue, key, value;
-       ColourSchemeI def = sg.cs;
-       sg.cs = null;
+       ColourSchemeI def = sg.getColourScheme();
        while (st.hasMoreTokens())
        {
          keyValue = st.nextToken();
          }
          else if (key.equalsIgnoreCase("colour"))
          {
-           sg.cs = ColourSchemeProperty.getColour(al, value);
+           sg.cs.setColourScheme(ColourSchemeProperty
+                   .getColourScheme(al, value));
          }
          else if (key.equalsIgnoreCase("pidThreshold"))
          {
          }
          else if (key.equalsIgnoreCase("outlineColour"))
          {
-           sg.setOutlineColour(new UserColourScheme(value).findColour('A'));
+           sg.setOutlineColour(ColorUtils.parseColourString(value));
          }
          else if (key.equalsIgnoreCase("displayBoxes"))
          {
          }
          else if (key.equalsIgnoreCase("textCol1"))
          {
-           sg.textColour = new UserColourScheme(value).findColour('A');
+           sg.textColour = ColorUtils.parseColourString(value);
          }
          else if (key.equalsIgnoreCase("textCol2"))
          {
-           sg.textColour2 = new UserColourScheme(value).findColour('A');
+           sg.textColour2 = ColorUtils.parseColourString(value);
          }
          else if (key.equalsIgnoreCase("textColThreshold"))
          {
          }
          else if (key.equalsIgnoreCase("idColour"))
          {
-           // consider warning if colour doesn't resolve to a real colour
-           sg.setIdColour((def = new UserColourScheme(value))
-                   .findColour('A'));
+           Color idColour = ColorUtils.parseColourString(value);
+           sg.setIdColour(idColour == null ? Color.black : idColour);
          }
          else if (key.equalsIgnoreCase("hide"))
          {
          }
          sg.recalcConservation();
        }
-       if (sg.cs == null)
+       if (sg.getColourScheme() == null)
        {
-         sg.cs = def;
+         sg.setColourScheme(def);
        }
      }
    }
@@@ -39,7 -39,6 +39,7 @@@ import jalview.io.DataSourceType
  import jalview.io.StructureFile;
  import jalview.util.MappingUtils;
  import jalview.util.MessageManager;
 +import jalview.ws.phyre2.Phyre2Client;
  import jalview.ws.sifts.SiftsClient;
  import jalview.ws.sifts.SiftsException;
  import jalview.ws.sifts.SiftsSettings;
@@@ -50,7 -49,6 +50,7 @@@ import java.util.Arrays
  import java.util.Collections;
  import java.util.Enumeration;
  import java.util.HashMap;
 +import java.util.Hashtable;
  import java.util.IdentityHashMap;
  import java.util.List;
  import java.util.Map;
@@@ -80,7 -78,6 +80,7 @@@ public class StructureSelectionManage
  
    private long progressSessionId;
  
 +
    /*
     * Set of any registered mappings between (dataset) sequences.
     */
@@@ -90,8 -87,6 +90,8 @@@
  
    private List<SelectionListener> sel_listeners = new ArrayList<SelectionListener>();
  
 +  private Map<String, String> phyre2ModelTemplates = new Hashtable<String, String>();
 +
    /**
     * @return true if will try to use external services for processing secondary
     *         structure
     * @return null or the structure data parsed as a pdb file
     */
    synchronized public StructureFile setMapping(SequenceI[] sequence,
 -          String[] targetChains, String pdbFile, DataSourceType protocol)
 +          String[] targetChains, String pdbFile, DataSourceType protocol,
 +          IProgressIndicator progress)
    {
 -    return setMapping(true, sequence, targetChains, pdbFile, protocol);
 +    return computeMapping(true, sequence, targetChains, pdbFile, protocol,
 +            progress);
    }
  
 +
    /**
     * create sequence structure mappings between each sequence and the given
     * pdbFile (retrieved via the given protocol).
     *          - one or more sequences to be mapped to pdbFile
     * @param targetChainIds
     *          - optional chain specification for mapping each sequence to pdb
 -   *          (may be nill, individual elements may be nill)
 +   *          (may be null, individual elements may be null)
     * @param pdbFile
     *          - structure data resource
 -   * @param sourceType
 +   * @param protocol
     *          - how to resolve data from resource
     * @return null or the structure data parsed as a pdb file
     */
    synchronized public StructureFile setMapping(boolean forStructureView,
            SequenceI[] sequenceArray, String[] targetChainIds,
 -          String pdbFile, DataSourceType sourceType)
 +          String pdbFile, DataSourceType protocol)
    {
 +    return computeMapping(forStructureView, sequenceArray, targetChainIds,
 +            pdbFile, protocol, null);
 +  }
 +
 +  synchronized public StructureFile computeMapping(
 +          boolean forStructureView, SequenceI[] sequenceArray,
 +          String[] targetChainIds, String pdbFile, DataSourceType protocol,
 +          IProgressIndicator progress)
 +  {
 +    long progressSessionId = System.currentTimeMillis() * 3;
      /*
       * There will be better ways of doing this in the future, for now we'll use
       * the tried and tested MCview pdb mapping
      boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
      try
      {
 -      pdb = new JmolParser(pdbFile, sourceType);
 +      pdb = new JmolParser(pdbFile, protocol);
  
        if (pdb.getId() != null && pdb.getId().trim().length() > 0
 -              && DataSourceType.FILE == sourceType)
 +              && DataSourceType.FILE == protocol)
        {
          registerPDBFile(pdb.getId().trim(), pdbFile);
        }
          continue;
        }
  
 -      if (sourceType == DataSourceType.PASTE)
 +      if (protocol.equals(DataSourceType.PASTE))
        {
          pdbFile = "INLINE" + pdb.getId();
        }
 -
 +      boolean phyre2Template = isPhyre2Template(pdbFile);
        List<StructureMapping> seqToStrucMapping = new ArrayList<StructureMapping>();
 -      if (isMapUsingSIFTs && seq.isProtein())
 +      if (!phyre2Template && isMapUsingSIFTs && seq.isProtein())
        {
 -        setProgressBar(null);
 -        setProgressBar(MessageManager
 -                .getString("status.obtaining_mapping_with_sifts"));
 +        if (progress!=null) {
 +          progress.setProgressBar(MessageManager
 +                .getString("status.obtaining_mapping_with_sifts"),
 +                  progressSessionId);
 +        }
          jalview.datamodel.Mapping sqmpping = maxAlignseq
                  .getMappingFromS1(false);
          if (targetChainId != null && !targetChainId.trim().isEmpty())
                                                          // "IEA:Jalview" ?
              maxChain.transferResidueAnnotation(nwMapping, sqmpping);
              ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
 +          } catch (Exception e)
 +          {
 +            e.printStackTrace();
            }
          }
          else
              } catch (SiftsException e)
              {
                System.err.println(e.getMessage());
 +            } catch (Exception e)
 +            {
 +              e.printStackTrace();
              }
            }
            if (!foundSiftsMappings.isEmpty())
            }
          }
        }
 -      else
 +      else if (phyre2Template)
        {
          setProgressBar(null);
          setProgressBar(MessageManager
 -                .getString("status.obtaining_mapping_with_nw_alignment"));
 +                .getString("status.obtaining_mapping_with_phyre2_template_alignment"));
 +        String fastaFile = getPhyre2FastaFileFor(pdbFile);
 +        StructureMapping phyre2ModelMapping = new Phyre2Client(pdb)
 +                .getStructureMapping(seq, pdbFile, fastaFile, " ");
 +        seqToStrucMapping.add(phyre2ModelMapping);
 +        maxChain.makeExactMapping(maxAlignseq, seq);
 +        maxChain.transferRESNUMFeatures(seq, null);
 +        jalview.datamodel.Mapping sqmpping = maxAlignseq
 +                .getMappingFromS1(false);
 +        maxChain.transferResidueAnnotation(phyre2ModelMapping, sqmpping);
 +        ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
 +      }
 +      else
 +      {
 +        if (progress != null)
 +        {
 +          progress.setProgressBar(MessageManager
 +                                  .getString("status.obtaining_mapping_with_nw_alignment"),
 +                  progressSessionId);
 +        }
          StructureMapping nwMapping = getNWMappings(seq, pdbFile,
                  maxChainId, maxChain, pdb, maxAlignseq);
          seqToStrucMapping.add(nwMapping);
      return pdb;
    }
  
 -  public void addStructureMapping(StructureMapping sm)
 +  public void registerPhyre2Template(String phyre2Template,
 +          String fastaMappingFile)
    {
 -    mappings.add(sm);
 +    phyre2ModelTemplates.put(phyre2Template, fastaMappingFile);
    }
  
    /**
    private StructureMapping getStructureMapping(SequenceI seq,
            String pdbFile, String targetChainId, StructureFile pdb,
            PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
 -          AlignSeq maxAlignseq) throws SiftsException
 +          AlignSeq maxAlignseq) throws Exception
    {
      StructureMapping curChainMapping = siftsClient
 -            .getSiftsStructureMapping(seq, pdbFile, targetChainId);
 +            .getStructureMapping(seq, pdbFile, targetChainId);
      try
      {
        PDBChain chain = pdb.findChain(targetChainId);
        return;
      }
  
+     SearchResultsI results = findAlignmentPositionsForStructurePositions(atoms);
+     for (Object li : listeners)
+     {
+       if (li instanceof SequenceListener)
+       {
+         ((SequenceListener) li).highlightSequence(results);
+       }
+     }
+   }
+   /**
+    * Constructs a SearchResults object holding regions (if any) in the Jalview
+    * alignment which have a mapping to the structure viewer positions in the
+    * supplied list
+    * 
+    * @param atoms
+    * @return
+    */
+   public SearchResultsI findAlignmentPositionsForStructurePositions(
+           List<AtomSpec> atoms)
+   {
      SearchResultsI results = new SearchResults();
      for (AtomSpec atom : atoms)
      {
          }
        }
      }
-     for (Object li : listeners)
-     {
-       if (li instanceof SequenceListener)
-       {
-         ((SequenceListener) li).highlightSequence(results);
-       }
-     }
+     return results;
    }
  
    /**
      return seqmappings;
    }
  
 +  public boolean isPhyre2Template(String structureFile)
 +  {
 +    if (structureFile == null || phyre2ModelTemplates == null
 +            || phyre2ModelTemplates.isEmpty())
 +    {
 +      return false;
 +    }
 +    return phyre2ModelTemplates.get(structureFile) != null
 +            && !phyre2ModelTemplates.get(structureFile).isEmpty();
 +  }
 +
 +  public String getPhyre2FastaFileFor(String structureFile)
 +  {
 +    return phyre2ModelTemplates.get(structureFile);
 +  }
 +
 +
 +  public static StructureSelectionManager getStructureSelectionManager()
 +  {
 +    return instances.values().iterator().next();
 +  }
 +
++  public void addStructureMapping(StructureMapping smapping)
++  {
++    mappings.add(smapping);
++  }
++
  }
@@@ -29,8 -29,6 +29,8 @@@ import jalview.datamodel.SequenceI
  import jalview.io.StructureFile;
  import jalview.schemes.ResidueProperties;
  import jalview.structure.StructureMapping;
 +import jalview.structure.StructureMappingClient;
 +import jalview.structures.models.MappingOutputModel;
  import jalview.util.Comparison;
  import jalview.util.DBRefUtils;
  import jalview.util.Format;
@@@ -55,6 -53,7 +55,6 @@@ import java.nio.file.Path
  import java.nio.file.attribute.BasicFileAttributes;
  import java.util.ArrayList;
  import java.util.Arrays;
 -import java.util.Collection;
  import java.util.Collections;
  import java.util.Date;
  import java.util.HashMap;
@@@ -70,11 -69,21 +70,17 @@@ import javax.xml.bind.Unmarshaller
  import javax.xml.stream.XMLInputFactory;
  import javax.xml.stream.XMLStreamReader;
  
 -import MCview.Atom;
 -import MCview.PDBChain;
 -
 -public class SiftsClient implements SiftsClientI
 +public class SiftsClient extends StructureMappingClient implements
 +        SiftsClientI
  {
+   /*
+    * for use in mocking out file fetch for tests only
+    * - reset to null after testing!
+    */
+   private static File mockSiftsFile;
    private Entry siftsEntry;
  
 -  private StructureFile pdb;
 -
    private String pdbId;
  
    private String structId;
@@@ -87,6 -96,8 +93,6 @@@
  
    private static final int PDB_RES_POS = 0;
  
 -  private static final int PDB_ATOM_POS = 1;
 -
    private static final String NOT_OBSERVED = "Not_Observed";
  
    private static final String SIFTS_FTP_BASE_URL = "http://ftp.ebi.ac.uk/pub/databases/msd/sifts/xml/";
     * @param pdbId
     * @throws SiftsException
     */
 -  public SiftsClient(StructureFile pdb) throws SiftsException
 +  public SiftsClient(StructureFile structureFile) throws SiftsException
    {
 -    this.pdb = pdb;
 -    this.pdbId = pdb.getId();
 +    this.structureFile = structureFile;
 +    this.pdbId = structureFile.getId();
      File siftsFile = getSiftsFile(pdbId);
      siftsEntry = parseSIFTs(siftsFile);
    }
     */
    public static File getSiftsFile(String pdbId) throws SiftsException
    {
+     /*
+      * return mocked file if it has been set
+      */
+     if (mockSiftsFile != null)
+     {
+       return mockSiftsFile;
+     }
      String siftsFileName = SiftsSettings.getSiftDownloadDirectory()
              + pdbId.toLowerCase() + ".xml.gz";
      File siftsFile = new File(siftsFileName);
    }
  
    @Override
 -  public StructureMapping getSiftsStructureMapping(SequenceI seq,
 -          String pdbFile, String chain) throws SiftsException
 +  public StructureMapping getStructureMapping(SequenceI seq,
 +          String pdbFile, String chain) throws Exception,
 +          StructureMappingException
    {
      structId = (chain == null) ? pdbId : pdbId + "|" + chain;
      System.out.println("Getting SIFTS mapping for " + structId + ": seq "
  
    @Override
    public HashMap<Integer, int[]> getGreedyMapping(String entityId,
 -          SequenceI seq, java.io.PrintStream os) throws SiftsException
 +          SequenceI seq, java.io.PrintStream os) throws SiftsException,
 +          StructureMappingException
    {
      List<Integer> omitNonObserved = new ArrayList<Integer>();
      int nonObservedShiftIndex = 0;
  
      if (os != null)
      {
 -      MappingOutputPojo mop = new MappingOutputPojo();
 +      MappingOutputModel mop = new MappingOutputModel();
        mop.setSeqStart(seqStart);
        mop.setSeqEnd(seqEnd);
        mop.setSeqName(seq.getName());
      }
    }
  
 -  /**
 -   * 
 -   * @param chainId
 -   *          Target chain to populate mapping of its atom positions.
 -   * @param mapping
 -   *          Two dimension array of residue index versus atom position
 -   * @throws IllegalArgumentException
 -   *           Thrown if chainId or mapping is null
 -   * @throws SiftsException
 -   */
 -  void populateAtomPositions(String chainId, Map<Integer, int[]> mapping)
 -          throws IllegalArgumentException, SiftsException
 -  {
 -    try
 -    {
 -      PDBChain chain = pdb.findChain(chainId);
 -
 -      if (chain == null || mapping == null)
 -      {
 -        throw new IllegalArgumentException(
 -                "Chain id or mapping must not be null.");
 -      }
 -      for (int[] map : mapping.values())
 -      {
 -        if (map[PDB_RES_POS] != UNASSIGNED)
 -        {
 -          map[PDB_ATOM_POS] = getAtomIndex(map[PDB_RES_POS], chain.atoms);
 -        }
 -      }
 -    } catch (NullPointerException e)
 -    {
 -      throw new SiftsException(e.getMessage());
 -    } catch (Exception e)
 -    {
 -      throw new SiftsException(e.getMessage());
 -    }
 -  }
 -
 -  /**
 -   * 
 -   * @param residueIndex
 -   *          The residue index used for the search
 -   * @param atoms
 -   *          A collection of Atom to search
 -   * @return atom position for the given residue index
 -   */
 -  int getAtomIndex(int residueIndex, Collection<Atom> atoms)
 -  {
 -    if (atoms == null)
 -    {
 -      throw new IllegalArgumentException(
 -              "atoms collection must not be null!");
 -    }
 -    for (Atom atom : atoms)
 -    {
 -      if (atom.resNumber == residueIndex)
 -      {
 -        return atom.atomIndex;
 -      }
 -    }
 -    return UNASSIGNED;
 -  }
  
    /**
     * Checks if the residue instance is marked 'Not_observed' or not
    }
  
    @Override
 -  public StringBuffer getMappingOutput(MappingOutputPojo mp)
 -          throws SiftsException
 +  public StringBuffer getMappingOutput(MappingOutputModel mp)
 +          throws StructureMappingException
    {
      String seqRes = mp.getSeqResidue();
      String seqName = mp.getSeqName();
      float pid = (float) matchedSeqCount / seqRes.length() * 100;
      if (pid < SiftsSettings.getFailSafePIDThreshold())
      {
 -      throw new SiftsException(">>> Low PID detected for SIFTs mapping...");
 +      throw new StructureMappingException(
 +              ">>> Low PID detected for SIFTs mapping...");
      }
      output.append("Length of alignment = " + seqRes.length()).append(
              NEWLINE);
      return siftsEntry.getDbVersion();
    }
  
+   public static void setMockSiftsFile(File file)
+   {
+     mockSiftsFile = file;
+   }
  }
@@@ -24,19 -24,28 +24,28 @@@ import static org.testng.AssertJUnit.as
  import static org.testng.AssertJUnit.assertFalse;
  import static org.testng.AssertJUnit.assertTrue;
  
+ import jalview.api.AlignViewportI;
+ import jalview.api.AlignmentViewPanel;
+ import jalview.api.FeatureRenderer;
+ import jalview.api.SequenceRenderer;
  import jalview.datamodel.Alignment;
  import jalview.datamodel.AlignmentI;
+ import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.PDBEntry.Type;
  import jalview.datamodel.Sequence;
  import jalview.datamodel.SequenceI;
  import jalview.gui.JvOptionPane;
  import jalview.io.DataSourceType;
+ import jalview.schemes.ColourSchemeI;
  import jalview.structure.AtomSpec;
+ import jalview.structure.StructureMappingcommandSet;
  import jalview.structure.StructureSelectionManager;
  import jalview.structures.models.AAStructureBindingModel.SuperposeData;
  
+ import java.awt.Color;
  import java.util.Arrays;
+ import java.util.BitSet;
  import java.util.List;
  
  import org.testng.annotations.BeforeClass;
@@@ -121,12 -130,11 +130,12 @@@ public class AAStructureBindingModelTes
      StructureSelectionManager ssm = new StructureSelectionManager();
  
      ssm.setMapping(new SequenceI[] { seq1a, seq1b }, null, PDB_1,
 -            DataSourceType.PASTE);
 +            DataSourceType.PASTE, null);
      ssm.setMapping(new SequenceI[] { seq2 }, null, PDB_2,
 -            DataSourceType.PASTE);
 +            DataSourceType.PASTE, null);
      ssm.setMapping(new SequenceI[] { seq3 }, null, PDB_3,
 -            DataSourceType.PASTE);
 +            DataSourceType.PASTE, null);
 +
  
      testee = new AAStructureBindingModel(ssm, pdbFiles, seqs, null)
      {
        {
          return null;
        }
+       @Override
+       public void setJalviewColourScheme(ColourSchemeI cs)
+       {
+       }
+       @Override
+       public String superposeStructures(AlignmentI[] als, int[] alm,
+               ColumnSelection[] alc)
+       {
+         return null;
+       }
+       @Override
+       public void setBackgroundColour(Color col)
+       {
+       }
+       @Override
+       protected StructureMappingcommandSet[] getColourBySequenceCommands(
+               String[] files, SequenceRenderer sr, FeatureRenderer fr,
+               AlignViewportI viewport)
+       {
+         return null;
+       }
+       @Override
+       public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
+       {
+         return null;
+       }
+       @Override
+       public SequenceRenderer getSequenceRenderer(
+               AlignmentViewPanel alignment)
+       {
+         return null;
+       }
+       @Override
+       protected void colourBySequence(
+               StructureMappingcommandSet[] colourBySequenceCommands)
+       {
+       }
+       @Override
+       public void colourByChain()
+       {
+       }
+       @Override
+       public void colourByCharge()
+       {
+       }
      };
    }
  
        structs[i] = testee.new SuperposeData(al.getWidth());
      }
      /*
-      * initialise array of 'superposable columns' to true (would be false for
+      * initialise BitSet of 'superposable columns' to true (would be false for
       * hidden columns)
       */
-     boolean[] matched = new boolean[al.getWidth()];
-     Arrays.fill(matched, true);
+     BitSet matched = new BitSet();
+     for (int i = 0; i < al.getWidth(); i++)
+     {
+       matched.set(i);
+     }
  
      int refStructure = testee
              .findSuperposableResidues(al, matched, structs);
      /*
       * only ungapped, structure-mapped columns are superposable
       */
-     assertFalse(matched[0]); // gap in first sequence
-     assertTrue(matched[1]);
-     assertFalse(matched[2]); // gap in third sequence
-     assertFalse(matched[3]); // gap in fourth sequence
-     assertTrue(matched[4]);
-     assertTrue(matched[5]); // gap in second sequence
+     assertFalse(matched.get(0)); // gap in first sequence
+     assertTrue(matched.get(1));
+     assertFalse(matched.get(2)); // gap in third sequence
+     assertFalse(matched.get(3)); // gap in fourth sequence
+     assertTrue(matched.get(4));
+     assertTrue(matched.get(5)); // gap in second sequence
  
      assertEquals("1YCS", structs[0].pdbId);
      assertEquals("3A6S", structs[1].pdbId);
        structs[i] = testee.new SuperposeData(al.getWidth());
      }
      /*
-      * initialise array of 'superposable columns' to true (would be false for
+      * initialise BitSet of 'superposable columns' to true (would be false for
       * hidden columns)
       */
-     boolean[] matched = new boolean[al.getWidth()];
-     Arrays.fill(matched, true);
+     BitSet matched = new BitSet();
+     for (int i = 0; i < al.getWidth(); i++)
+     {
+       matched.set(i);
+     }
      // treat column 5 of the alignment as hidden
-     matched[4] = false;
+     matched.clear(4);
  
      int refStructure = testee
              .findSuperposableResidues(al, matched, structs);
      assertEquals(0, refStructure);
  
      // only ungapped, structure-mapped columns are not superposable
-     assertFalse(matched[0]);
-     assertTrue(matched[1]);
-     assertFalse(matched[2]);
-     assertFalse(matched[3]);
-     assertFalse(matched[4]); // superposable, but hidden, column
-     assertTrue(matched[5]);
+     assertFalse(matched.get(0));
+     assertTrue(matched.get(1));
+     assertFalse(matched.get(2));
+     assertFalse(matched.get(3));
+     assertFalse(matched.get(4)); // superposable, but hidden, column
+     assertTrue(matched.get(5));
    }
  }