Merge branch 'develop' into JAL-1483_featureBasedTreeCalc
authorJim Procter <jprocter@dundee.ac.uk>
Fri, 14 Nov 2014 12:16:32 +0000 (12:16 +0000)
committerJim Procter <jprocter@dundee.ac.uk>
Fri, 14 Nov 2014 12:16:32 +0000 (12:16 +0000)
Conflicts:
src/jalview/api/AlignViewportI.java
src/jalview/appletgui/FeatureRenderer.java
src/jalview/gui/FeatureRenderer.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/Jalview2XML_V1.java
src/jalview/ws/AWSThread.java

30 files changed:
1  2 
src/jalview/api/AlignViewControllerI.java
src/jalview/api/AlignViewportI.java
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignmentPanel.java
src/jalview/appletgui/AppletJmol.java
src/jalview/appletgui/FeatureRenderer.java
src/jalview/appletgui/FeatureSettings.java
src/jalview/appletgui/SeqPanel.java
src/jalview/bin/JalviewLite.java
src/jalview/controller/AlignViewController.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/AnnotationExporter.java
src/jalview/gui/AppJmol.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/FeatureRenderer.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/IdPanel.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/Jalview2XML_V1.java
src/jalview/gui/JalviewChimeraBindingModel.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/SeqPanel.java
src/jalview/io/HTMLOutput.java
src/jalview/schemes/AnnotationColourGradient.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
src/jalview/ws/AWSThread.java

@@@ -27,10 -27,13 +27,13 @@@ package jalview.api
   * 
   *         All operations should return true if the view has changed as a result
   *         of the operation
-  * @param <ViewportI>
+  * 
+  *         The controller holds methods that operate on an alignment view,
+  *         modifying its state in some way that may result in side effects
+  *         reflected in an associated GUI
   * 
   */
- public interface AlignViewControllerI<ViewportI>
+ public interface AlignViewControllerI
  {
  
    public boolean makeGroupsFromSelection();
    boolean markColumnsContainingFeatures(boolean invert,
            boolean extendCurrent, boolean clearColumns, String featureType);
  
 +  /**
 +   * sort the alignment or current selection by average score over the given set of features
 +   * @param typ list of feature names or null to use currently displayed features
 +   */
 +  void sortAlignmentByFeatureScore(String[] typ);
 +
 +  /**
 +   * sort the alignment or current selection by distribution of the given set of features
 +   * @param typ list of feature names or null to use currently displayed features
 +   */
 +  void sortAlignmentByFeatureDensity(String[] typ);
 +
  }
@@@ -22,6 -22,7 +22,7 @@@ package jalview.api
  
  import java.awt.Color;
  import java.util.Hashtable;
+ import java.util.List;
  import java.util.Map;
  
  import jalview.analysis.Conservation;
@@@ -191,12 -192,12 +192,20 @@@ public interface AlignViewport
  
    void setConservation(Conservation cons);
  
+   /**
+    * get a copy of the currently visible alignment annotation
+    * @param selectedOnly if true - trim to selected regions on the alignment
+    * @return an empty list or new alignment annotation objects shown only visible columns trimmed to selected region only
+    */
+   List<AlignmentAnnotation> getVisibleAlignmentAnnotation(
+           boolean selectedOnly);
 +  FeaturesDisplayedI getFeaturesDisplayed();
 +
 +  String getSequenceSetId();
 +
 +  boolean isShowSequenceFeatures();
 +
 +  void setShowSequenceFeatures(boolean b);
 +
  }
@@@ -559,7 -559,7 +559,7 @@@ public class APopupMenu extends java.aw
  
          if (dialog.accept)
          {
-           EditCommand editCommand = new EditCommand("Edit Sequences",
+           EditCommand editCommand = new EditCommand(MessageManager.getString("label.edit_sequences"),
                    EditCommand.REPLACE, dialog.getName().replace(' ',
                            ap.av.getGapCharacter()),
                    sg.getSequencesAsArray(ap.av.getHiddenRepSequences()),
                features, true, ap))
        {
          ap.alignFrame.sequenceFeatures.setState(true);
 -        ap.av.showSequenceFeatures(true);
 +        ap.av.setShowSequenceFeatures(true);;
          ap.highlightSearchResults(null);
        }
      }
      // TODO consider using getSequenceSelection instead here
  
      cap.setText(new jalview.io.AppletFormatAdapter().formatSequences(
-             e.getActionCommand(),
-             new Alignment(ap.av.getSelectionAsNewSequence()),
-             ap.av.showJVSuffix));
+             e.getActionCommand(), 
+             ap.av.showJVSuffix, ap.av, true));
  
    }
  
                        true,
                        true,
                        false,
 -                      (ap.seqPanel.seqCanvas.fr != null) ? ap.seqPanel.seqCanvas.fr.minmax
 +                      (ap.seqPanel.seqCanvas.fr != null) ? ap.seqPanel.seqCanvas.fr.getMinMax()
                                : null);
        contents.append("</p>");
      }
      colourMenu.add(abovePIDColour);
      colourMenu.add(conservationMenuItem);
  
-     noColourmenuItem.setLabel("None");
+     noColourmenuItem.setLabel(MessageManager.getString("label.none"));
      noColourmenuItem.addActionListener(this);
  
-     clustalColour.setLabel("Clustalx colours");
+     clustalColour.setLabel(MessageManager.getString("label.clustalx_colours"));
      clustalColour.addActionListener(this);
-     zappoColour.setLabel("Zappo");
+     zappoColour.setLabel(MessageManager.getString("label.zappo"));
      zappoColour.addActionListener(this);
-     taylorColour.setLabel("Taylor");
+     taylorColour.setLabel(MessageManager.getString("label.taylor"));
      taylorColour.addActionListener(this);
-     hydrophobicityColour.setLabel("Hydrophobicity");
+     hydrophobicityColour.setLabel(MessageManager.getString("label.hydrophobicity"));
      hydrophobicityColour.addActionListener(this);
-     helixColour.setLabel("Helix propensity");
+     helixColour.setLabel(MessageManager.getString("label.helix_propensity"));
      helixColour.addActionListener(this);
-     strandColour.setLabel("Strand propensity");
+     strandColour.setLabel(MessageManager.getString("label.strand_propensity"));
      strandColour.addActionListener(this);
-     turnColour.setLabel("Turn propensity");
+     turnColour.setLabel(MessageManager.getString("label.turn_propensity"));
      turnColour.addActionListener(this);
-     buriedColour.setLabel("Buried Index");
+     buriedColour.setLabel(MessageManager.getString("label.buried_index"));
      buriedColour.addActionListener(this);
-     abovePIDColour.setLabel("Above % Identity");
+     abovePIDColour.setLabel(MessageManager.getString("label.above_identity_percentage"));
  
-     userDefinedColour.setLabel("User Defined");
+     userDefinedColour.setLabel(MessageManager.getString("action.user_defined"));
      userDefinedColour.addActionListener(this);
-     PIDColour.setLabel("Percentage Identity");
+     PIDColour.setLabel(MessageManager.getString("action.percentage_identity"));
      PIDColour.addActionListener(this);
      BLOSUM62Colour.setLabel("BLOSUM62");
      BLOSUM62Colour.addActionListener(this);
-     conservationMenuItem.setLabel("Conservation");
+     conservationMenuItem.setLabel(MessageManager.getString("label.conservation"));
  
      editMenu.add(copy);
      copy.addActionListener(this);
  package jalview.appletgui;
  
  import jalview.analysis.AlignmentSorter;
  import jalview.api.AlignViewControllerGuiI;
  import jalview.api.AlignViewControllerI;
  import jalview.api.SequenceStructureBinding;
 +import jalview.api.FeatureRenderer;
  import jalview.bin.JalviewLite;
  import jalview.commands.CommandI;
  import jalview.commands.EditCommand;
@@@ -57,7 -55,6 +56,6 @@@ import jalview.schemes.PIDColourScheme
  import jalview.schemes.PurinePyrimidineColourScheme;
  import jalview.schemes.RNAHelicesColourChooser;
  import jalview.schemes.RNAInteractionColourScheme;
- import jalview.schemes.ResidueProperties;
  import jalview.schemes.StrandColourScheme;
  import jalview.schemes.TCoffeeColourScheme;
  import jalview.schemes.TaylorColourScheme;
@@@ -91,12 -88,9 +89,12 @@@ import java.awt.event.WindowEvent
  import java.io.IOException;
  import java.net.URL;
  import java.net.URLEncoder;
 +import java.util.Arrays;
  import java.util.Enumeration;
  import java.util.Hashtable;
 +import java.util.Iterator;
  import java.util.List;
 +import java.util.Map;
  import java.util.StringTokenizer;
  import java.util.Vector;
  
@@@ -290,7 -284,7 +288,7 @@@ public class AlignFrame extends Embmenu
      {
        featuresFile = new jalview.io.FeaturesFile(file, type)
                .parse(viewport.getAlignment(), alignPanel.seqPanel.seqCanvas
 -                      .getFeatureRenderer().featureColours, featureLinks,
 +                      .getFeatureRenderer().getFeatureColours(), featureLinks,
                        true, viewport.applet.getDefaultParameter(
                                "relaxedidmatch", false));
      } catch (Exception ex)
        }
        if (autoenabledisplay)
        {
 -        viewport.showSequenceFeatures = true;
 +        viewport.setShowSequenceFeatures(true);
          sequenceFeatures.setState(true);
        }
 +      if (alignPanel.seqPanel.seqCanvas.fr != null)
 +      {
 +        // update the min/max ranges where necessary
 +        alignPanel.seqPanel.seqCanvas.fr.findAllFeatures(true);
 +      }
        if (viewport.featureSettings != null)
        {
          viewport.featureSettings.refreshTable();
                      .getKeyCode() >= KeyEvent.VK_NUMPAD0 && evt
                      .getKeyCode() <= KeyEvent.VK_NUMPAD9))
              && Character.isDigit(evt.getKeyChar()))
+     {
        alignPanel.seqPanel.numberPressed(evt.getKeyChar());
+     }
  
      switch (evt.getKeyCode())
      {
  
      case KeyEvent.VK_LEFT:
        if (evt.isAltDown() || !viewport.cursorMode)
+       {
          slideSequences(false, alignPanel.seqPanel.getKeyboardNo1());
+       }
        else
+       {
          alignPanel.seqPanel.moveCursor(-1, 0);
+       }
        break;
  
      case KeyEvent.VK_RIGHT:
        if (evt.isAltDown() || !viewport.cursorMode)
+       {
          slideSequences(true, alignPanel.seqPanel.getKeyboardNo1());
+       }
        else
+       {
          alignPanel.seqPanel.moveCursor(1, 0);
+       }
        break;
  
      case KeyEvent.VK_SPACE:
      }
      else if (evt.getSource() == sequenceFeatures)
      {
 -      viewport.showSequenceFeatures(sequenceFeatures.getState());
 +      viewport.setShowSequenceFeatures(sequenceFeatures.getState());
        alignPanel.seqPanel.seqCanvas.repaint();
      }
      else if (evt.getSource() == conservationMenuItem)
      return annotation;
    }
  
 -  private Hashtable getDisplayedFeatureCols()
 +  private Map<String,Object> getDisplayedFeatureCols()
    {
      if (alignPanel.getFeatureRenderer() != null
 -            && viewport.featuresDisplayed != null)
 +            && viewport.getFeaturesDisplayed()!= null)
      {
 -      FeatureRenderer fr = alignPanel.getFeatureRenderer();
 -      Hashtable fcols = new Hashtable();
 -      Enumeration en = viewport.featuresDisplayed.keys();
 -      while (en.hasMoreElements())
 -      {
 -        Object col = en.nextElement();
 -        fcols.put(col, fr.featureColours.get(col));
 -      }
 -      return fcols;
 +      return alignPanel.getFeatureRenderer().getDisplayedFeatureCols();
 +      
      }
      return null;
    }
      else
      {
        if (features == null)
+       {
          features = "";
+       }
      }
  
      return features;
    /**
     * TODO: JAL-1104
     */
 +  @Override
    public void addHistoryItem(CommandI command)
    {
      if (command.getSize() > 0)
      for (int i = 0; i < viewport.getAlignment().getHeight(); i++)
      {
        if (!sg.contains(viewport.getAlignment().getSequenceAt(i)))
+       {
          invertGroup.addElement(viewport.getAlignment().getSequenceAt(i));
+       }
      }
  
      SequenceI[] seqs1 = sg.toArray(new SequenceI[sg.size()]);
      SequenceI[] seqs2 = invertGroup.toArray(new SequenceI[invertGroup
              .size()]);
      for (int i = 0; i < invertGroup.size(); i++)
+     {
        seqs2[i] = invertGroup.elementAt(i);
+     }
  
      SlideSequencesCommand ssc;
      if (right)
+     {
        ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1,
                size, viewport.getGapCharacter());
+     }
      else
+     {
        ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2,
                size, viewport.getGapCharacter());
+     }
  
      int groupAdjustment = 0;
      if (ssc.getGapsInsertedBegin() && right)
      {
        if (viewport.cursorMode)
+       {
          alignPanel.seqPanel.moveCursor(size, 0);
+       }
        else
+       {
          groupAdjustment = size;
+       }
      }
      else if (!ssc.getGapsInsertedBegin() && !right)
      {
        if (viewport.cursorMode)
+       {
          alignPanel.seqPanel.moveCursor(-size, 0);
+       }
        else
+       {
          groupAdjustment = -size;
+       }
      }
  
      if (groupAdjustment != 0)
      }
  
      if (!appendHistoryItem)
+     {
        addHistoryItem(ssc);
+     }
  
      repaint();
    }
  
        if (newAlignment)
        {
-         String newtitle = new String("Copied sequences");
-         if (getTitle().startsWith("Copied sequences"))
+         String newtitle = MessageManager.getString("label.copied_sequences");
+         if (getTitle().startsWith(MessageManager.getString("label.copied_sequences")))
          {
            newtitle = getTitle();
          }
          else
          {
-           newtitle = newtitle.concat("- from " + getTitle());
+           newtitle = newtitle.concat(MessageManager.formatMessage("label.from_msname", new String[]{getTitle()}));
          }
          AlignFrame af = new AlignFrame(new Alignment(newSeqs),
                  viewport.applet, newtitle, false);
      }
  
      // !newAlignment
-     addHistoryItem(new EditCommand("Add sequences", EditCommand.PASTE,
+     addHistoryItem(new EditCommand(MessageManager.getString("label.add_sequences"), EditCommand.PASTE,
              seqs, 0, viewport.getAlignment().getWidth(),
              viewport.getAlignment()));
  
      /*
       * //ADD HISTORY ITEM
       */
-     addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT, cut,
+     addHistoryItem(new EditCommand(MessageManager.getString("label.cut_sequences"), EditCommand.CUT, cut,
              sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
              viewport.getAlignment()));
  
      if (alignPanel != null
              && (fr = alignPanel.getFeatureRenderer()) != null)
      {
 -      return fr.getGroups();
 +      List gps = fr.getFeatureGroups();
 +      int p=0;
 +      String[] _gps = new String[gps.size()];
 +      for (Object gp:gps)
 +      {
 +        _gps[p++] = gp.toString();
 +      }
 +      return _gps;
      }
      return null;
    }
      if (alignPanel != null
              && (fr = alignPanel.getFeatureRenderer()) != null)
      {
 -      return fr.getGroups(visible);
 +      List gps = fr.getGroups(visible);
 +      int p=0;
 +      String[] _gps = new String[gps.size()];
 +      for (Object gp:gps)
 +      {
 +        _gps[p++] = gp.toString();
 +      }
 +      return _gps;
      }
      return null;
    }
    {
      FeatureRenderer fr = null;
      this.sequenceFeatures.setState(true);
 -    viewport.showSequenceFeatures(true);
 +    viewport.setShowSequenceFeatures(true);
      if (alignPanel != null
              && (fr = alignPanel.getFeatureRenderer()) != null)
      {
 -      fr.setGroupState(groups, state);
 +      
 +      fr.setGroupVisibility((List)Arrays.asList(groups), state);
        alignPanel.seqPanel.seqCanvas.repaint();
        if (alignPanel.overviewPanel != null)
        {
            MessageManager.getString("label.load_features_annotations"));
  
    MenuItem outputFeatures = new MenuItem(
-           MessageManager.getString("label.export_features"));
+           MessageManager.getString("label.export_features").concat("..."));
  
    MenuItem outputAnnotations = new MenuItem(
-           MessageManager.getString("label.export_annotations"));
+           MessageManager.getString("label.export_annotations").concat("..."));
  
    MenuItem closeMenuItem = new MenuItem(
            MessageManager.getString("action.close"));
      sortGroupMenuItem.setLabel(MessageManager.getString("action.by_group"));
      sortGroupMenuItem.addActionListener(this);
      removeRedundancyMenuItem.setLabel(MessageManager
-             .getString("action.remove_redundancy"));
+             .getString("action.remove_redundancy").concat("..."));
      removeRedundancyMenuItem.addActionListener(this);
      pairwiseAlignmentMenuItem.setLabel(MessageManager
              .getString("action.pairwise_alignment"));
          }
          pdbentry.getProperty().put("protocol", protocol);
          toaddpdb.addPDBId(pdbentry);
+         alignPanel.getStructureSelectionManager()
+                 .registerPDBEntry(pdbentry);
        }
      }
      return true;
@@@ -145,18 -145,12 +145,18 @@@ public class AlignmentPanel extends Pan
    {
      return seqPanel.seqCanvas.sr;
    }
 -
 -  public FeatureRenderer getFeatureRenderer()
 +  @Override
 +  public jalview.api.FeatureRenderer getFeatureRenderer()
    {
      return seqPanel.seqCanvas.fr;
    }
 -
 +  @Override
 +  public jalview.api.FeatureRenderer cloneFeatureRenderer()
 +  {
 +    FeatureRenderer nfr = new FeatureRenderer(av);
 +    nfr.transferSettings(seqPanel.seqCanvas.fr);
 +    return nfr;
 +  }
    public void alignmentChanged()
    {
      av.alignmentChanged(this);
      fontChanged(); // This is so that the scalePanel is resized correctly
  
      validate();
-     sequenceHolderPanel.revalidate();
+     sequenceHolderPanel.validate();
      repaint();
  
    }
@@@ -34,7 -34,7 +34,7 @@@ import jalview.util.MessageManager
  
  public class AppletJmol extends EmbmenuFrame implements
  // StructureListener,
-         KeyListener, ActionListener, ItemListener, SequenceStructureBinding
+         KeyListener, ActionListener, ItemListener
  
  {
    Menu fileMenu = new Menu(MessageManager.getString("action.file"));
            String[][] boundchains, boolean align, AlignmentPanel ap,
            String protocol)
    {
-     throw new Error("Not yet implemented.");
+     throw new Error(MessageManager.getString("error.not_yet_implemented"));
    }
  
    public AppletJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
            }
            if (freader == null)
            {
-             throw new Exception(
-                     "Invalid datasource. Could not obtain Reader.");
+             throw new Exception(MessageManager.getString("exception.invalid_datasource_couldnt_obtain_reader"));
            }
            jmb.viewer.openReader(pdbentry.getFile(), pdbentry.getId(),
                    freader);
      else if (evt.getSource() == seqColour)
      {
        setEnabled(seqColour);
 -      jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap);
 +      jmb.colourBySequence(ap.av.isShowSequenceFeatures(), ap);
      }
      else if (!allChainsSelected)
        centerViewer();
    public void updateColours(Object source)
    {
      AlignmentPanel ap = (AlignmentPanel) source;
 -    jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap);
 +    jmb.colourBySequence(ap.av.isShowSequenceFeatures(), ap);
    }
  
    public void updateTitleAndMenus()
        return;
      }
      setChainMenuItems(jmb.chainNames);
 -    jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap);
 +    jmb.colourBySequence(ap.av.isShowSequenceFeatures(), ap);
  
      setTitle(jmb.getViewerTitle());
    }
  package jalview.appletgui;
  
  import java.util.*;
 -
  import java.awt.*;
 -
  import java.awt.event.*;
  
  import jalview.datamodel.*;
  import jalview.schemes.AnnotationColourGradient;
  import jalview.schemes.GraduatedColor;
  import jalview.util.MessageManager;
 +import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
  
  /**
   * DOCUMENT ME!
   * @author $author$
   * @version $Revision$
   */
 -public class FeatureRenderer implements jalview.api.FeatureRenderer
 +public class FeatureRenderer extends jalview.renderer.seqfeatures.FeatureRenderer
  {
 -  AlignViewport av;
 -
 -  Hashtable featureColours = new Hashtable();
 -
 -  // A higher level for grouping features of a
 -  // particular type
 -  Hashtable featureGroups = null;
  
    // Holds web links for feature groups and feature types
    // in the form label|link
    Hashtable featureLinks = null;
  
 -  // This is actually an Integer held in the hashtable,
 -  // Retrieved using the key feature type
 -  Object currentColour;
 -
 -  String[] renderOrder;
 -
 -  FontMetrics fm;
 -
 -  int charOffset;
 -
 -  float transparency = 1f;
 -
 -  TransparencySetter transparencySetter = null;
 -
    /**
     * Creates a new FeatureRenderer object.
     * 
     */
    public FeatureRenderer(AlignViewport av)
    {
 +    super();
      this.av = av;
  
 -    if (!System.getProperty("java.version").startsWith("1.1"))
 -    {
 -      transparencySetter = new TransparencySetter();
 -    }
 -  }
 -
 -  public void transferSettings(FeatureRenderer fr)
 -  {
 -    renderOrder = fr.renderOrder;
 -    featureGroups = fr.featureGroups;
 -    featureColours = fr.featureColours;
 -    transparency = fr.transparency;
 -    if (av != null && fr.av != null && fr.av != av)
 -    {
 -      if (fr.av.featuresDisplayed != null)
 -      {
 -        if (av.featuresDisplayed == null)
 -        {
 -          av.featuresDisplayed = new Hashtable();
 -        }
 -        else
 -        {
 -          av.featuresDisplayed.clear();
 -        }
 -        Enumeration en = fr.av.featuresDisplayed.keys();
 -        while (en.hasMoreElements())
 -        {
 -          av.featuresDisplayed.put(en.nextElement(), Boolean.TRUE);
 -        }
 -      }
 -    }
 +    setTransparencyAvailable(!System.getProperty("java.version").startsWith("1.1"));
    }
  
    static String lastFeatureAdded;
        }
        else
        {
-         throw new Error("Invalid color for MyCheckBox");
+         throw new Error(MessageManager.getString("error.invalid_colour_for_mycheckbox"));
        }
        if (col != null)
        {
        }
      }
  
-     String title = newFeatures ? "Create New Sequence Feature(s)"
-             : "Amend/Delete Features for " + sequences[0].getName();
+     String title = newFeatures ? MessageManager.getString("label.create_new_sequence_features")
+             : MessageManager.formatMessage("label.amend_delete_features", new String[]{sequences[0].getName()});
  
      final JVDialog dialog = new JVDialog(ap.alignFrame, title, true, 385,
              240);
          }
  
          ffile.parseDescriptionHTML(sf, false);
 +        setVisible(lastFeatureAdded); // if user edited name then make sure new type is visible
        }
        if (deleteFeature)
        {
            ffile.parseDescriptionHTML(features[i], false);
          }
  
 -        if (av.featuresDisplayed == null)
 -        {
 -          av.featuresDisplayed = new Hashtable();
 -        }
 -
 -        if (featureGroups == null)
 -        {
 -          featureGroups = new Hashtable();
 -        }
 -
          col = colourPanel.getBackground();
          // setColour(lastFeatureAdded, fcol);
  
          if (lastFeatureGroupAdded != null)
          {
 -          featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
 -        }
 -        if (fcol instanceof Color)
 -        {
 -          setColour(lastFeatureAdded, fcol);
 +          setGroupVisibility(lastFeatureGroupAdded, true);
          }
 -        av.featuresDisplayed.put(lastFeatureAdded,
 -                getFeatureStyle(lastFeatureAdded));
 -
 -        findAllFeatures();
 -
 -        String[] tro = new String[renderOrder.length];
 -        tro[0] = renderOrder[renderOrder.length - 1];
 -        System.arraycopy(renderOrder, 0, tro, 1, renderOrder.length - 1);
 -        renderOrder = tro;
 +        setColour(lastFeatureAdded, fcol);
 +        setVisible(lastFeatureAdded);
 +        findAllFeatures(false); // different to original applet behaviour ? 
 +        // findAllFeatures();
        }
        else
        {
        }
      }
      // refresh the alignment and the feature settings dialog
 -    if (av.featureSettings != null)
 +    if (((jalview.appletgui.AlignViewport) av).featureSettings != null)
      {
 -      av.featureSettings.refreshTable();
 +      ((jalview.appletgui.AlignViewport) av).featureSettings.refreshTable();
      }
      // findAllFeatures();
  
  
      return true;
    }
 -
 -  public Color findFeatureColour(Color initialCol, SequenceI seq, int i)
 -  {
 -    overview = true;
 -    if (!av.showSequenceFeatures)
 -    {
 -      return initialCol;
 -    }
 -
 -    lastSeq = seq;
 -    sequenceFeatures = lastSeq.getSequenceFeatures();
 -    if (sequenceFeatures == null)
 -    {
 -      return initialCol;
 -    }
 -
 -    sfSize = sequenceFeatures.length;
 -
 -    if (jalview.util.Comparison.isGap(lastSeq.getCharAt(i)))
 -    {
 -      return Color.white;
 -    }
 -
 -    currentColour = null;
 -
 -    drawSequence(null, lastSeq, lastSeq.findPosition(i), -1, -1);
 -
 -    if (currentColour == null)
 -    {
 -      return initialCol;
 -    }
 -
 -    return new Color(((Integer) currentColour).intValue());
 -  }
 -
 -  /**
 -   * This is used by the Molecule Viewer to get the accurate colour of the
 -   * rendered sequence
 -   */
 -  boolean overview = false;
 -
 -  /**
 -   * DOCUMENT ME!
 -   * 
 -   * @param g
 -   *          DOCUMENT ME!
 -   * @param seq
 -   *          DOCUMENT ME!
 -   * @param sg
 -   *          DOCUMENT ME!
 -   * @param start
 -   *          DOCUMENT ME!
 -   * @param end
 -   *          DOCUMENT ME!
 -   * @param x1
 -   *          DOCUMENT ME!
 -   * @param y1
 -   *          DOCUMENT ME!
 -   * @param width
 -   *          DOCUMENT ME!
 -   * @param height
 -   *          DOCUMENT ME!
 -   */
 -  // String type;
 -  // SequenceFeature sf;
 -  SequenceI lastSeq;
 -
 -  SequenceFeature[] sequenceFeatures;
 -
 -  int sfSize, sfindex, spos, epos;
 -
 -  synchronized public void drawSequence(Graphics g, SequenceI seq,
 -          int start, int end, int y1)
 -  {
 -    if (seq.getSequenceFeatures() == null
 -            || seq.getSequenceFeatures().length == 0)
 -    {
 -      return;
 -    }
 -
 -    if (transparencySetter != null && g != null)
 -    {
 -      transparencySetter.setTransparency(g, transparency);
 -    }
 -
 -    if (lastSeq == null || seq != lastSeq
 -            || sequenceFeatures != seq.getSequenceFeatures())
 -    {
 -      lastSeq = seq;
 -      sequenceFeatures = seq.getSequenceFeatures();
 -      sfSize = sequenceFeatures.length;
 -    }
 -
 -    if (av.featuresDisplayed == null || renderOrder == null)
 -    {
 -      findAllFeatures();
 -      if (av.featuresDisplayed.size() < 1)
 -      {
 -        return;
 -      }
 -
 -      sequenceFeatures = seq.getSequenceFeatures();
 -      sfSize = sequenceFeatures.length;
 -    }
 -    if (!overview)
 -    {
 -      spos = lastSeq.findPosition(start);
 -      epos = lastSeq.findPosition(end);
 -      if (g != null)
 -      {
 -        fm = g.getFontMetrics();
 -      }
 -    }
 -    String type;
 -    for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
 -    {
 -      type = renderOrder[renderIndex];
 -      if (!av.featuresDisplayed.containsKey(type))
 -      {
 -        continue;
 -      }
 -
 -      // loop through all features in sequence to find
 -      // current feature to render
 -      for (sfindex = 0; sfindex < sfSize; sfindex++)
 -      {
 -        if (!sequenceFeatures[sfindex].type.equals(type))
 -        {
 -          continue;
 -        }
 -
 -        if (featureGroups != null
 -                && sequenceFeatures[sfindex].featureGroup != null
 -                && featureGroups
 -                        .containsKey(sequenceFeatures[sfindex].featureGroup)
 -                && !((Boolean) featureGroups
 -                        .get(sequenceFeatures[sfindex].featureGroup))
 -                        .booleanValue())
 -        {
 -          continue;
 -        }
 -
 -        if (!overview
 -                && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
 -                        .getEnd() < spos))
 -        {
 -          continue;
 -        }
 -
 -        if (overview)
 -        {
 -          if (sequenceFeatures[sfindex].begin <= start
 -                  && sequenceFeatures[sfindex].end >= start)
 -          {
 -            currentColour = new Integer(
 -                    getColour(sequenceFeatures[sfindex]).getRGB());// av.featuresDisplayed
 -            // .get(sequenceFeatures[sfindex].type);
 -          }
 -
 -        }
 -        else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
 -        {
 -
 -          renderFeature(g, seq,
 -                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
 -                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
 -                  getColour(sequenceFeatures[sfindex])
 -                  // new Color(((Integer) av.featuresDisplayed
 -                  // .get(sequenceFeatures[sfindex].type)).intValue())
 -                  , start, end, y1);
 -          renderFeature(g, seq,
 -                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
 -                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
 -                  getColour(sequenceFeatures[sfindex])
 -                  // new Color(((Integer) av.featuresDisplayed
 -                  // .get(sequenceFeatures[sfindex].type)).intValue())
 -                  , start, end, y1);
 -
 -        }
 -        else
 -        {
 -          if (showFeature(sequenceFeatures[sfindex]))
 -          {
 -            renderFeature(g, seq,
 -                    seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
 -                    seq.findIndex(sequenceFeatures[sfindex].end) - 1,
 -                    getColour(sequenceFeatures[sfindex]), start, end, y1);
 -          }
 -        }
 -
 -      }
 -    }
 -
 -    if (transparencySetter != null && g != null)
 -    {
 -      transparencySetter.setTransparency(g, 1.0f);
 -    }
 -  }
 -
 -  char s;
 -
 -  int i;
 -
 -  void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
 -          Color featureColour, int start, int end, int y1)
 -  {
 -
 -    if (((fstart <= end) && (fend >= start)))
 -    {
 -      if (fstart < start)
 -      { // fix for if the feature we have starts before the sequence start,
 -        fstart = start; // but the feature end is still valid!!
 -      }
 -
 -      if (fend >= end)
 -      {
 -        fend = end;
 -      }
 -
 -      for (i = fstart; i <= fend; i++)
 -      {
 -        s = seq.getCharAt(i);
 -
 -        if (jalview.util.Comparison.isGap(s))
 -        {
 -          continue;
 -        }
 -
 -        g.setColor(featureColour);
 -
 -        g.fillRect((i - start) * av.charWidth, y1, av.charWidth,
 -                av.charHeight);
 -
 -        if (!av.validCharWidth)
 -        {
 -          continue;
 -        }
 -
 -        g.setColor(Color.white);
 -        charOffset = (av.charWidth - fm.charWidth(s)) / 2;
 -        g.drawString(String.valueOf(s), charOffset
 -                + (av.charWidth * (i - start)), (y1 + av.charHeight)
 -                - av.charHeight / 5); // pady = height / 5;
 -
 -      }
 -    }
 -  }
 -
 -  Hashtable minmax = null;
 -
 -  /**
 -   * Called when alignment in associated view has new/modified features to
 -   * discover and display.
 -   * 
 -   */
 -  public void featuresAdded()
 -  {
 -    lastSeq = null;
 -    findAllFeatures();
 -  }
 -
 -  /**
 -   * find all features on the alignment
 -   */
 -  void findAllFeatures()
 -  {
 -    jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
 -
 -    av.featuresDisplayed = new Hashtable();
 -    Vector allfeatures = new Vector();
 -    minmax = new Hashtable();
 -    AlignmentI alignment = av.getAlignment();
 -    for (int i = 0; i < alignment.getHeight(); i++)
 -    {
 -      SequenceFeature[] features = alignment.getSequenceAt(i)
 -              .getSequenceFeatures();
 -
 -      if (features == null)
 -      {
 -        continue;
 -      }
 -
 -      int index = 0;
 -      while (index < features.length)
 -      {
 -        if (features[index].begin == 0 && features[index].end == 0)
 -        {
 -          index++;
 -          continue;
 -        }
 -        if (!av.featuresDisplayed.containsKey(features[index].getType()))
 -        {
 -          if (getColour(features[index].getType()) == null)
 -          {
 -            featureColours.put(features[index].getType(),
 -                    ucs.createColourFromName(features[index].getType()));
 -          }
 -
 -          av.featuresDisplayed.put(features[index].getType(), new Integer(
 -                  getColour(features[index].getType()).getRGB()));
 -          allfeatures.addElement(features[index].getType());
 -        }
 -        if (features[index].score != Float.NaN)
 -        {
 -          int nonpos = features[index].getBegin() >= 1 ? 0 : 1;
 -          float[][] mm = (float[][]) minmax.get(features[index].getType());
 -          if (mm == null)
 -          {
 -            mm = new float[][]
 -            { null, null };
 -            minmax.put(features[index].getType(), mm);
 -          }
 -          if (mm[nonpos] == null)
 -          {
 -            mm[nonpos] = new float[]
 -            { features[index].score, features[index].score };
 -
 -          }
 -          else
 -          {
 -            if (mm[nonpos][0] > features[index].score)
 -            {
 -              mm[nonpos][0] = features[index].score;
 -            }
 -            if (mm[nonpos][1] < features[index].score)
 -            {
 -              mm[nonpos][1] = features[index].score;
 -            }
 -          }
 -        }
 -
 -        index++;
 -      }
 -    }
 -
 -    renderOrder = new String[allfeatures.size()];
 -    Enumeration en = allfeatures.elements();
 -    int i = allfeatures.size() - 1;
 -    while (en.hasMoreElements())
 -    {
 -      renderOrder[i] = en.nextElement().toString();
 -      i--;
 -    }
 -  }
 -
 -  /**
 -   * get a feature style object for the given type string. Creates a
 -   * java.awt.Color for a featureType with no existing colourscheme. TODO:
 -   * replace return type with object implementing standard abstract colour/style
 -   * interface
 -   * 
 -   * @param featureType
 -   * @return java.awt.Color or GraduatedColor
 -   */
 -  public Object getFeatureStyle(String featureType)
 -  {
 -    Object fc = featureColours.get(featureType);
 -    if (fc == null)
 -    {
 -      jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
 -      Color col = ucs.createColourFromName(featureType);
 -      featureColours.put(featureType, fc = col);
 -    }
 -    return fc;
 -  }
 -
 -  public Color getColour(String featureType)
 -  {
 -    Object fc = getFeatureStyle(featureType);
 -
 -    if (fc instanceof Color)
 -    {
 -      return (Color) fc;
 -    }
 -    else
 -    {
 -      if (fc instanceof GraduatedColor)
 -      {
 -        return ((GraduatedColor) fc).getMaxColor();
 -      }
 -    }
 -    throw new Error(MessageManager.formatMessage("error.implementation_error_unrecognised_render_object_for_features_type", new String[]{fc.getClass().getCanonicalName(),featureType}));
 -  }
 -
 -  /**
 -   * 
 -   * @param sequenceFeature
 -   * @return true if feature is visible.
 -   */
 -  private boolean showFeature(SequenceFeature sequenceFeature)
 -  {
 -    Object fc = getFeatureStyle(sequenceFeature.type);
 -    if (fc instanceof GraduatedColor)
 -    {
 -      return ((GraduatedColor) fc).isColored(sequenceFeature);
 -    }
 -    else
 -    {
 -      return true;
 -    }
 -  }
 -
 -  /**
 -   * implement graduated colouring for features with scores
 -   * 
 -   * @param feature
 -   * @return render colour for the given feature
 -   */
 -  public Color getColour(SequenceFeature feature)
 -  {
 -    Object fc = getFeatureStyle(feature.getType());
 -    if (fc instanceof Color)
 -    {
 -      return (Color) fc;
 -    }
 -    else
 -    {
 -      if (fc instanceof GraduatedColor)
 -      {
 -        return ((GraduatedColor) fc).findColor(feature);
 -      }
 -    }
 -    throw new Error("Implementation Error: Unrecognised render object "
 -            + fc.getClass() + " for features of type " + feature.getType());
 -  }
 -
 -  public void setColour(String featureType, Object col)
 -  {
 -    // overwrite
 -    // Color _col = (col instanceof Color) ? ((Color) col) : (col instanceof
 -    // GraduatedColor) ? ((GraduatedColor) col).getMaxColor() : null;
 -    // Object c = featureColours.get(featureType);
 -    // if (c == null || c instanceof Color || (c instanceof GraduatedColor &&
 -    // !((GraduatedColor)c).getMaxColor().equals(_col)))
 -    {
 -      featureColours.put(featureType, col);
 -    }
 -  }
 -
 -  public void setFeaturePriority(Object[][] data)
 -  {
 -    // The feature table will display high priority
 -    // features at the top, but theses are the ones
 -    // we need to render last, so invert the data
 -    if (av.featuresDisplayed != null)
 -    {
 -      av.featuresDisplayed.clear();
 -    }
 -
 -    /*
 -     * if (visibleNew) { if (av.featuresDisplayed != null) {
 -     * av.featuresDisplayed.clear(); } else { av.featuresDisplayed = new
 -     * Hashtable(); } } if (data == null) { return; }
 -     */
 -
 -    renderOrder = new String[data.length];
 -
 -    if (data.length > 0)
 -    {
 -      for (int i = 0; i < data.length; i++)
 -      {
 -        String type = data[i][0].toString();
 -        setColour(type, data[i][1]);
 -        if (((Boolean) data[i][2]).booleanValue())
 -        {
 -          av.featuresDisplayed.put(type, new Integer(getColour(type)
 -                  .getRGB()));
 -        }
 -
 -        renderOrder[data.length - i - 1] = type;
 -      }
 -    }
 -  }
 -
 -  /**
 -   * @return a simple list of feature group names or null
 -   */
 -  public String[] getGroups()
 -  {
 -    buildGroupHash();
 -    if (featureGroups != null)
 -    {
 -      String[] gps = new String[featureGroups.size()];
 -      Enumeration gn = featureGroups.keys();
 -      int i = 0;
 -      while (gn.hasMoreElements())
 -      {
 -        gps[i++] = (String) gn.nextElement();
 -      }
 -      return gps;
 -    }
 -    return null;
 -  }
 -
 -  /**
 -   * get visible or invisible groups
 -   * 
 -   * @param visible
 -   *          true to return visible groups, false to return hidden ones.
 -   * @return list of groups
 -   */
 -  public String[] getGroups(boolean visible)
 -  {
 -    buildGroupHash();
 -    if (featureGroups != null)
 -    {
 -      Vector gp = new Vector();
 -
 -      Enumeration gn = featureGroups.keys();
 -      while (gn.hasMoreElements())
 -      {
 -        String nm = (String) gn.nextElement();
 -        Boolean state = (Boolean) featureGroups.get(nm);
 -        if (state.booleanValue() == visible)
 -        {
 -          gp.addElement(nm);
 -        }
 -      }
 -      String[] gps = new String[gp.size()];
 -      gp.copyInto(gps);
 -
 -      int i = 0;
 -      while (gn.hasMoreElements())
 -      {
 -        gps[i++] = (String) gn.nextElement();
 -      }
 -      return gps;
 -    }
 -    return null;
 -  }
 -
 -  /**
 -   * set all feature groups in toset to be visible or invisible
 -   * 
 -   * @param toset
 -   *          group names
 -   * @param visible
 -   *          the state of the named groups to set
 -   */
 -  public void setGroupState(String[] toset, boolean visible)
 -  {
 -    buildGroupHash();
 -    if (toset != null && toset.length > 0 && featureGroups != null)
 -    {
 -      boolean rdrw = false;
 -      for (int i = 0; i < toset.length; i++)
 -      {
 -        Object st = featureGroups.get(toset[i]);
 -        featureGroups.put(toset[i], new Boolean(visible));
 -        if (st != null)
 -        {
 -          rdrw = rdrw || (visible != ((Boolean) st).booleanValue());
 -        }
 -      }
 -      if (rdrw)
 -      {
 -        if (this.av != null)
 -          if (this.av.featureSettings != null)
 -          {
 -            av.featureSettings.rebuildGroups();
 -            this.av.featureSettings.resetTable(true);
 -          }
 -          else
 -          {
 -            buildFeatureHash();
 -          }
 -        if (av != null)
 -        {
 -          av.alignmentChanged(null);
 -        }
 -      }
 -    }
 -  }
 -
 -  ArrayList<String> hiddenGroups = new ArrayList<String>();
 -
 -  /**
 -   * analyse alignment for groups and hash tables (used to be embedded in
 -   * FeatureSettings.setTableData)
 -   * 
 -   * @return true if features are on the alignment
 -   */
 -  public boolean buildGroupHash()
 -  {
 -    boolean alignmentHasFeatures = false;
 -    if (featureGroups == null)
 -    {
 -      featureGroups = new Hashtable();
 -    }
 -    hiddenGroups = new ArrayList<String>();
 -    hiddenGroups.addAll(featureGroups.keySet());
 -    ArrayList allFeatures = new ArrayList();
 -    ArrayList allGroups = new ArrayList();
 -    SequenceFeature[] tmpfeatures;
 -    String group;
 -    AlignmentI alignment = av.getAlignment();
 -    for (int i = 0; i < alignment.getHeight(); i++)
 -    {
 -      if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
 -      {
 -        continue;
 -      }
 -
 -      alignmentHasFeatures = true;
 -
 -      tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
 -      int index = 0;
 -      while (index < tmpfeatures.length)
 -      {
 -        if (tmpfeatures[index].getFeatureGroup() != null)
 -        {
 -          group = tmpfeatures[index].featureGroup;
 -          // Remove group from the hiddenGroup list
 -          hiddenGroups.remove(group);
 -          if (!allGroups.contains(group))
 -          {
 -            allGroups.add(group);
 -
 -            boolean visible = true;
 -            if (featureGroups.containsKey(group))
 -            {
 -              visible = ((Boolean) featureGroups.get(group)).booleanValue();
 -            }
 -            else
 -            {
 -              featureGroups.put(group, new Boolean(visible));
 -            }
 -          }
 -        }
 -
 -        if (!allFeatures.contains(tmpfeatures[index].getType()))
 -        {
 -          allFeatures.add(tmpfeatures[index].getType());
 -        }
 -        index++;
 -      }
 -    }
 -
 -    return alignmentHasFeatures;
 -  }
 -
 -  /**
 -   * rebuild the featuresDisplayed and renderorder list based on the
 -   * featureGroups hash and any existing display state and force a repaint if
 -   * necessary
 -   * 
 -   * @return true if alignment has visible features
 -   */
 -  public boolean buildFeatureHash()
 -  {
 -    boolean alignmentHasFeatures = false;
 -    if (featureGroups == null)
 -    {
 -      alignmentHasFeatures = buildGroupHash();
 -    }
 -    if (!alignmentHasFeatures)
 -      return false;
 -    Hashtable fdisp = av.featuresDisplayed;
 -    Vector allFeatures = new Vector();
 -    SequenceFeature[] tmpfeatures;
 -    String group;
 -    AlignmentI alignment = av.getAlignment();
 -    for (int i = 0; i < alignment.getHeight(); i++)
 -    {
 -      if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
 -      {
 -        continue;
 -      }
 -
 -      alignmentHasFeatures = true;
 -
 -      tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
 -      int index = 0;
 -      while (index < tmpfeatures.length)
 -      {
 -        boolean visible = true;
 -        if (tmpfeatures[index].getFeatureGroup() != null)
 -        {
 -          group = tmpfeatures[index].featureGroup;
 -          if (featureGroups.containsKey(group))
 -          {
 -            visible = ((Boolean) featureGroups.get(group)).booleanValue();
 -          }
 -        }
 -
 -        if (visible && !allFeatures.contains(tmpfeatures[index].getType()))
 -        {
 -          allFeatures.addElement(tmpfeatures[index].getType());
 -        }
 -        index++;
 -      }
 -    }
 -    if (allFeatures.size() > 0)
 -    {
 -      String[] neworder = new String[allFeatures.size()];
 -      int p = neworder.length - 1;
 -      for (int i = renderOrder.length - 1; i >= 0; i--)
 -      {
 -        if (allFeatures.contains(renderOrder[i]))
 -        {
 -          neworder[p--] = renderOrder[i];
 -          allFeatures.removeElement(renderOrder[i]);
 -        }
 -        else
 -        {
 -          av.featuresDisplayed.remove(renderOrder[i]);
 -        }
 -      }
 -      for (int i = allFeatures.size() - 1; i > 0; i++)
 -      {
 -        Object e = allFeatures.elementAt(i);
 -        if (e != null)
 -        {
 -          neworder[p--] = (String) e;
 -          av.featuresDisplayed.put(e, getColour((String) e));
 -        }
 -      }
 -      renderOrder = neworder;
 -      return true;
 -    }
 -
 -    return alignmentHasFeatures;
 -  }
 -
 -  /**
 -   * 
 -   * @return the displayed feature type as an array of strings
 -   */
 -  protected String[] getDisplayedFeatureTypes()
 -  {
 -    String[] typ = null;
 -    synchronized (renderOrder)
 -    {
 -      typ = new String[renderOrder.length];
 -      System.arraycopy(renderOrder, 0, typ, 0, typ.length);
 -      for (int i = 0; i < typ.length; i++)
 -      {
 -        if (av.featuresDisplayed.get(typ[i]) == null)
 -        {
 -          typ[i] = null;
 -        }
 -      }
 -    }
 -    return typ;
 -  }
 -}
 -
 -class TransparencySetter
 -{
 -  void setTransparency(Graphics g, float value)
 -  {
 -    Graphics2D g2 = (Graphics2D) g;
 -    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
 -            value));
 -  }
  }
@@@ -21,7 -21,7 +21,7 @@@
  package jalview.appletgui;
  
  import java.util.*;
 -
 +import java.util.List;
  import java.awt.*;
  import java.awt.event.*;
  
@@@ -50,6 -50,8 +50,6 @@@ public class FeatureSettings extends Pa
  
    ScrollPane scrollPane;
  
 -  boolean alignmentHasFeatures = false;
 -
    Image linkImage;
  
    Scrollbar transparency;
@@@ -62,9 -64,9 +62,9 @@@
      fr = ap.seqPanel.seqCanvas.getFeatureRenderer();
  
      transparency = new Scrollbar(Scrollbar.HORIZONTAL,
 -            100 - (int) (fr.transparency * 100), 1, 1, 100);
 +            100 - (int) (fr.getTransparency() * 100), 1, 1, 100);
  
 -    if (fr.transparencySetter != null)
 +    if (fr.isTransparencyAvailable())
      {
        transparency.addAdjustmentListener(this);
      }
@@@ -79,9 -81,9 +79,9 @@@
        linkImage = java.awt.Toolkit.getDefaultToolkit().getImage(url);
      }
  
 -    if (av.featuresDisplayed == null)
 +    if (av.isShowSequenceFeatures() || !fr.hasRenderOrder())
      {
 -      fr.findAllFeatures();
 +      fr.findAllFeatures(true); // was default - now true to make all visible
      }
  
      setTableData();
@@@ -89,7 -91,7 +89,7 @@@
      this.setLayout(new BorderLayout());
      scrollPane = new ScrollPane();
      scrollPane.add(featurePanel);
 -    if (alignmentHasFeatures)
 +    if (fr.getAllFeatureColours()!=null && fr.getAllFeatureColours().size()>0)
      {
        add(scrollPane, BorderLayout.CENTER);
      }
  
      Panel tPanel = new Panel(new BorderLayout());
  
 -    if (fr.transparencySetter != null)
 +    if (fr.isTransparencyAvailable())
      {
        tPanel.add(transparency, BorderLayout.CENTER);
        tPanel.add(new Label("Transparency"), BorderLayout.EAST);
      {
        groupPanel
                .setLayout(new GridLayout(
 -                      (fr.featureGroups.size() - fr.hiddenGroups.size()) / 4 + 1,
 -                      4));
 +                      (fr.getFeatureGroupsSize()) / 4 + 1,
 +                      4)); // JBPNote - this was scaled on number of visible groups. seems broken
        groupPanel.validate();
  
        add(groupPanel, BorderLayout.NORTH);
  
        public void actionPerformed(ActionEvent e)
        {
 -        me.sortByScore(new String[]
 +        me.ap.alignFrame.avc.sortAlignmentByFeatureScore(new String[]
          { type });
        }
  
  
        public void actionPerformed(ActionEvent e)
        {
 -        me.sortByDens(new String[]
 +        me.ap.alignFrame.avc.sortAlignmentByFeatureDensity(new String[]
          { type });
        }
  
  
    public void setTableData()
    {
 -    alignmentHasFeatures = fr.buildGroupHash();
 -    if (alignmentHasFeatures)
 +    if (fr.getAllFeatureColours()!=null && fr.getAllFeatureColours().size()>0)
      {
        rebuildGroups();
  
      }
      // TODO: JAL-964 - smoothly incorporate new group entries if panel already
      // displayed and new groups present
 -    Enumeration gps = fr.featureGroups.keys();
 -    while (gps.hasMoreElements())
 +    for (String group:(List<String>)fr.getFeatureGroups())
      {
 -      String group = (String) gps.nextElement();
 -      Boolean vis = (Boolean) fr.featureGroups.get(group);
 -      Checkbox check = new MyCheckbox(group, vis.booleanValue(),
 +      boolean vis = fr.checkGroupVisibility(group, false);
 +      Checkbox check = new MyCheckbox(group, vis,
                (fr.featureLinks != null && fr.featureLinks
                        .containsKey(group)));
        check.addMouseListener(this);
        check.setFont(new Font("Serif", Font.BOLD, 12));
 -      check.addItemListener(this);
 -      check.setVisible(fr.hiddenGroups.contains(group));
 +      check.addItemListener(groupItemListener);
 +      // note - visibility seems to correlate with displayed. ???wtf ??
 +      check.setVisible(vis);
        groupPanel.add(check);
      }
      if (rdrw)
        groupPanel.validate();
      }
    }
 -
    // This routine adds and removes checkboxes depending on
    // Group selection states
    void resetTable(boolean groupsChanged)
        {
          group = tmpfeatures[index].featureGroup;
  
 -        if (group == null || fr.featureGroups.get(group) == null
 -                || ((Boolean) fr.featureGroups.get(group)).booleanValue())
 +        if (group == null || fr.checkGroupVisibility(group, true))
          {
            type = tmpfeatures[index].getType();
            if (!visibleChecks.contains(type))
        }
      }
  
 -    if (fr.renderOrder != null)
 +    if (fr.getRenderOrder() != null)
      {
        // First add the checks in the previous render order,
        // in case the window has been closed and reopened
 -      for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
 +      List<String> rol = fr.getRenderOrder();
 +      for (int ro = rol.size() - 1; ro > -1; ro--)
        {
 -        String item = fr.renderOrder[ro];
 +        String item = rol.get(ro);
  
          if (!visibleChecks.contains(item))
          {
      if (addCheck)
      {
        boolean selected = false;
 -      if (groupsChanged || av.featuresDisplayed.containsKey(type))
 +      if (groupsChanged || av.getFeaturesDisplayed().isVisible(type))
        {
          selected = true;
        }
      selectionChanged();
    }
  
 -  public void itemStateChanged(ItemEvent evt)
 -  {
 -    if (evt != null)
 -    {
 -      // Is the source a top level featureGroup?
 +  private ItemListener groupItemListener = new ItemListener() {
 +    public void itemStateChanged(ItemEvent evt) {
        Checkbox source = (Checkbox) evt.getSource();
 -      if (fr.featureGroups.containsKey(source.getLabel()))
 +      fr.setGroupVisibility(source.getLabel(),
 +              source.getState());
 +      ap.seqPanel.seqCanvas.repaint();
 +      if (ap.overviewPanel != null)
        {
 -        fr.featureGroups.put(source.getLabel(),
 -                new Boolean(source.getState()));
 -        ap.seqPanel.seqCanvas.repaint();
 -        if (ap.overviewPanel != null)
 -        {
 -          ap.overviewPanel.updateOverviewImage();
 -        }
 -
 -        resetTable(true);
 -        return;
 +        ap.overviewPanel.updateOverviewImage();
        }
 -    }
 +      resetTable(true);
 +      return;
 +    };
 +  };
 +  public void itemStateChanged(ItemEvent evt)
 +  {
      selectionChanged();
    }
  
      }
      else
      {
-       throw new Error(
-               "Implementation error: Unsupported feature colour object.");
+       throw new Error(MessageManager.getString("error.implementation_error_unsupported_feature_colour_object"));
      }
      refreshTable();
    }
      MyCheckbox check = (MyCheckbox) evt.getSource();
      if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
      {
 -      this.popupSort(check, fr.minmax, evt.getX(), evt.getY());
 +      this.popupSort(check, fr.getMinMax(), evt.getX(), evt.getY());
      }
      if (fr.featureLinks != null && fr.featureLinks.containsKey(check.type))
      {
  
    public void adjustmentValueChanged(AdjustmentEvent evt)
    {
 -    fr.transparency = ((float) (100 - transparency.getValue()) / 100f);
 +    fr.setTransparency((float) (100 - transparency.getValue()) / 100f);
      ap.seqPanel.seqCanvas.repaint();
  
    }
        }
        else
        {
-         throw new Error("Invalid color for MyCheckBox");
+         throw new Error(MessageManager.getString("error.invalid_colour_for_mycheckbox"));
        }
        if (col != null)
        {
      }
    }
  
 -  protected void sortByDens(String[] typ)
 -  {
 -    sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
 -  }
 -
 -  private String[] getDisplayedFeatureTypes()
 -  {
 -    String[] typ = null;
 -    if (fr != null)
 -    {
 -      synchronized (fr.renderOrder)
 -      {
 -        typ = new String[fr.renderOrder.length];
 -        System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
 -        for (int i = 0; i < typ.length; i++)
 -        {
 -          if (av.featuresDisplayed.get(typ[i]) == null)
 -          {
 -            typ[i] = null;
 -          }
 -        }
 -      }
 -    }
 -    return typ;
 -  }
 -
 -  protected void sortBy(String[] typ, String methodText, final String method)
 -  {
 -    if (typ == null)
 -    {
 -      typ = getDisplayedFeatureTypes();
 -    }
 -    String gps[] = null;
 -    gps = fr.getGroups(true);
 -    if (typ != null)
 -    {
 -      for (int i = 0; i < typ.length; i++)
 -      {
 -        System.err.println("Sorting on Types:" + typ[i]);
 -      }
 -    }
 -    if (gps != null)
 -    {
 -
 -      for (int i = 0; i < gps.length; i++)
 -      {
 -        System.err.println("Sorting on groups:" + gps[i]);
 -      }
 -    }
 -    AlignmentPanel alignPanel = ap;
 -    AlignmentI al = alignPanel.av.getAlignment();
 -
 -    int start, stop;
 -    SequenceGroup sg = alignPanel.av.getSelectionGroup();
 -    if (sg != null)
 -    {
 -      start = sg.getStartRes();
 -      stop = sg.getEndRes();
 -    }
 -    else
 -    {
 -      start = 0;
 -      stop = al.getWidth();
 -    }
 -    SequenceI[] oldOrder = al.getSequencesArray();
 -    AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
 -    this.ap.alignFrame.addHistoryItem(new OrderCommand(methodText,
 -            oldOrder, alignPanel.av.getAlignment()));
 -    alignPanel.paintAlignment(true);
 -
 -  }
 -
 -  protected void sortByScore(String[] typ)
 -  {
 -    sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
 -  }
 -
  }
@@@ -31,6 -31,7 +31,7 @@@ import jalview.schemes.*
  import jalview.structure.SelectionSource;
  import jalview.structure.SequenceListener;
  import jalview.structure.StructureSelectionManager;
+ import jalview.util.MessageManager;
  
  public class SeqPanel extends Panel implements MouseMotionListener,
          MouseListener, SequenceListener
      {
        for (int i = 0; i < features.length; i++)
        {
 -        if (av.featuresDisplayed == null
 -                || !av.featuresDisplayed.containsKey(features[i].getType()))
 +        if (av.getFeaturesDisplayed() == null
 +                || !av.getFeaturesDisplayed().isVisible(features[i].getType()))
          {
            continue;
          }
  
          if (features[i].featureGroup != null
 -                && seqCanvas.fr.featureGroups != null
 -                && seqCanvas.fr.featureGroups
 -                        .containsKey(features[i].featureGroup)
 -                && !((Boolean) seqCanvas.fr.featureGroups
 -                        .get(features[i].featureGroup)).booleanValue())
 +                && !seqCanvas.fr.checkGroupVisibility(features[i].featureGroup,false))
 +        {
            continue;
 +        }
  
          if ((features[i].getBegin() <= res)
                  && (features[i].getEnd() >= res))
      StringBuffer message = new StringBuffer();
      if (groupEditing)
      {
-       message.append("Edit group:");
+       message.append(MessageManager.getString("action.edit_group")).append(":");
        if (editCommand == null)
        {
-         editCommand = new EditCommand("Edit Group");
+         editCommand = new EditCommand(MessageManager.getString("action.edit_group"));
        }
      }
      else
      {
-       message.append("Edit sequence: " + seq.getName());
+       message.append(MessageManager.getString("label.edit_sequence")).append(" " + seq.getName());
        String label = seq.getName();
        if (label.length() > 10)
        {
        }
        if (editCommand == null)
        {
-         editCommand = new EditCommand("Edit " + label);
+         editCommand = new EditCommand(MessageManager.formatMessage("label.edit_params", new String[]{label}));
        }
      }
  
@@@ -1392,8 -1392,7 +1392,7 @@@ public class JalviewLite extends Apple
        }
        else
        {
-         throw new Error(
-                 "Invalid separator parameter - must be non-zero length");
+         throw new Error(MessageManager.getString("error.invalid_separator_parameter"));
        }
      }
      int r = 255;
          param = applet.getParameter("showFeatureSettings");
          if (param != null && param.equalsIgnoreCase("true"))
          {
 -          newAlignFrame.viewport.showSequenceFeatures(true);
 +          newAlignFrame.viewport.setShowSequenceFeatures(true);
            new FeatureSettings(newAlignFrame.alignPanel);
          }
  
              {
                String sequence = applet.getParameter("PDBSEQ");
                if (sequence != null)
+               {
                  seqs = new SequenceI[]
                  { matcher == null ? (Sequence) newAlignFrame
                          .getAlignViewport().getAlignment()
                          .findName(sequence) : matcher.findIdMatch(sequence) };
+               }
  
              }
              else
                  if (seqs[i] != null)
                  {
                    ((Sequence) seqs[i]).addPDBId(pdb);
+                   StructureSelectionManager.getStructureSelectionManager(
+                           applet).registerPDBEntry(pdb);
                  }
                  else
                  {
      // note separator local variable intentionally masks object field
      int seplen = separator.length();
      if (list == null || list.equals("") || list.equals(separator))
+     {
        return null;
+     }
      java.util.Vector jv = new Vector();
      int cp = 0, pos;
      while ((pos = list.indexOf(separator, cp)) > cp)
  package jalview.controller;
  
  import java.awt.Color;
 +import java.util.ArrayList;
  import java.util.BitSet;
  import java.util.List;
  
 +import jalview.analysis.AlignmentSorter;
  import jalview.api.AlignViewControllerGuiI;
  import jalview.api.AlignViewControllerI;
  import jalview.api.AlignViewportI;
  import jalview.api.AlignmentViewPanel;
 +import jalview.api.FeatureRenderer;
 +import jalview.commands.OrderCommand;
  import jalview.datamodel.AlignmentI;
 -import jalview.datamodel.AnnotatedCollectionI;
  import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.SequenceCollectionI;
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
+ import jalview.util.MessageManager;
  
  public class AlignViewController implements AlignViewControllerI
  {
        }
        viewport.setColumnSelection(cs);
        alignPanel.paintAlignment(true);
-       avcg.setStatus((toggle ? "Toggled " : "Marked ")
-               + (invert ? (alw - alStart) - bs.cardinality() : bs
-                       .cardinality()) + " columns "
-               + (invert ? "not " : "") + "containing features of type "
-               + featureType + " across " + nseq + " sequence(s)");
+       avcg.setStatus(MessageManager.formatMessage("label.view_controller_toggled_marked",
+                 new String[]{
+                               (toggle ? MessageManager.getString("label.toggled") : MessageManager.getString("label.marked")),
+                               (invert ? (Integer.valueOf((alw - alStart) - bs.cardinality()).toString()):(Integer.valueOf(bs.cardinality()).toString())),
+                               featureType, Integer.valueOf(nseq).toString()
+                               }));
        return true;
      }
      else
      {
-       avcg.setStatus("No features of type " + featureType + " found.");
+       avcg.setStatus(MessageManager.formatMessage("label.no_feature_of_type_found", new String[]{featureType}));
        if (!extendCurrent && cs != null)
        {
          cs.clear();
        return false;
      }
    }
 +
 +  @Override
 +  public void sortAlignmentByFeatureDensity(String[] typ)
 +  {
 +    sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
 +  }
 +
 +  protected void sortBy(String[] typ, String methodText, final String method)
 +  {
 +    FeatureRenderer fr = alignPanel.getFeatureRenderer();
 +    if (typ == null)
 +    {
 +      typ = fr==null ? null : fr.getDisplayedFeatureTypes();
 +    }
 +    String gps[] = null;
 +    gps = fr==null ? null : fr.getDisplayedFeatureGroups();
 +    if (typ != null)
 +    {
 +      ArrayList types = new ArrayList();
 +      for (int i = 0; i < typ.length; i++)
 +      {
 +        if (typ[i] != null)
 +        {
 +          types.add(typ[i]);
 +        }
 +        typ = new String[types.size()];
 +        types.toArray(typ);
 +      }
 +    }
 +    if (gps != null)
 +    {
 +      ArrayList grps = new ArrayList();
 +
 +      for (int i = 0; i < gps.length; i++)
 +      {
 +        if (gps[i] != null)
 +        {
 +          grps.add(gps[i]);
 +        }
 +      }
 +      gps = new String[grps.size()];
 +      grps.toArray(gps);
 +    }
 +    AlignmentI al = viewport.getAlignment();
 +
 +    int start, stop;
 +    SequenceGroup sg = viewport.getSelectionGroup();
 +    if (sg != null)
 +    {
 +      start = sg.getStartRes();
 +      stop = sg.getEndRes();
 +    }
 +    else
 +    {
 +      start = 0;
 +      stop = al.getWidth();
 +    }
 +    SequenceI[] oldOrder = al.getSequencesArray();
 +    AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
 +    avcg.addHistoryItem(new OrderCommand(methodText, oldOrder, viewport
 +            .getAlignment()));
 +    alignPanel.paintAlignment(true);
 +
 +  }
 +
 +  @Override
 +  public void sortAlignmentByFeatureScore(String[] typ)
 +  {
 +    sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
 +  }
  }
@@@ -53,6 -53,7 +53,7 @@@ import jalview.datamodel.SequenceGroup
  import jalview.datamodel.SequenceI;
  import jalview.io.AlignmentProperties;
  import jalview.io.AnnotationFile;
+ import jalview.io.BioJsHTMLOutput;
  import jalview.io.FeaturesFile;
  import jalview.io.FileLoader;
  import jalview.io.FormatAdapter;
@@@ -263,21 -264,23 +264,23 @@@ public class AlignFrame extends GAlignF
            int width, int height, String sequenceSetId, String viewId)
    {
      setSize(width, height);
-     viewport = new AlignViewport(al, hiddenColumns, sequenceSetId, viewId);
-     alignPanel = new AlignmentPanel(this, viewport);
  
      if (al.getDataset() == null)
      {
        al.setDataset(null);
      }
  
+     viewport = new AlignViewport(al, hiddenColumns, sequenceSetId, viewId);
+     alignPanel = new AlignmentPanel(this, viewport);
      addAlignmentPanel(alignPanel, true);
      init();
    }
  
    /**
-    * Make a new AlignFrame from exisiting alignmentPanels
+    * Make a new AlignFrame from existing alignmentPanels
     * 
     * @param ap
     *          AlignmentPanel
      setMenusFromViewport(viewport);
      buildSortByAnnotationScoresMenu();
      buildTreeMenu();
+     
      if (viewport.wrapAlignment)
      {
        wrapMenuItem_actionPerformed(null);
                          .getKeyCode() >= KeyEvent.VK_NUMPAD0 && evt
                          .getKeyCode() <= KeyEvent.VK_NUMPAD9))
                  && Character.isDigit(evt.getKeyChar()))
+         {
            alignPanel.seqPanel.numberPressed(evt.getKeyChar());
+         }
  
          switch (evt.getKeyCode())
          {
  
          case KeyEvent.VK_DOWN:
            if (evt.isAltDown() || !viewport.cursorMode)
+           {
              moveSelectedSequences(false);
+           }
            if (viewport.cursorMode)
+           {
              alignPanel.seqPanel.moveCursor(0, 1);
+           }
            break;
  
          case KeyEvent.VK_UP:
            if (evt.isAltDown() || !viewport.cursorMode)
+           {
              moveSelectedSequences(true);
+           }
            if (viewport.cursorMode)
+           {
              alignPanel.seqPanel.moveCursor(0, -1);
+           }
  
            break;
  
          case KeyEvent.VK_LEFT:
            if (evt.isAltDown() || !viewport.cursorMode)
+           {
              slideSequences(false, alignPanel.seqPanel.getKeyboardNo1());
+           }
            else
+           {
              alignPanel.seqPanel.moveCursor(-1, 0);
+           }
  
            break;
  
          case KeyEvent.VK_RIGHT:
            if (evt.isAltDown() || !viewport.cursorMode)
+           {
              slideSequences(true, alignPanel.seqPanel.getKeyboardNo1());
+           }
            else
+           {
              alignPanel.seqPanel.moveCursor(1, 0);
+           }
            break;
  
          case KeyEvent.VK_SPACE:
          case KeyEvent.VK_F1:
            try
            {
-             ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
-             java.net.URL url = javax.help.HelpSet.findHelpSet(cl,
-                     "help/help");
-             javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
-             javax.help.HelpBroker hb = hs.createHelpBroker();
-             hb.setCurrentID("home");
-             hb.setDisplayed(true);
+             Help.showHelpWindow();
            } catch (Exception ex)
            {
              ex.printStackTrace();
          {
          case KeyEvent.VK_LEFT:
            if (evt.isAltDown() || !viewport.cursorMode)
+           {
              viewport.firePropertyChange("alignment", null, viewport
                      .getAlignment().getSequences());
+           }
            break;
  
          case KeyEvent.VK_RIGHT:
            if (evt.isAltDown() || !viewport.cursorMode)
+           {
              viewport.firePropertyChange("alignment", null, viewport
                      .getAlignment().getSequences());
+           }
            break;
          }
        }
      scaleLeft.setVisible(av.wrapAlignment);
      scaleRight.setVisible(av.wrapAlignment);
      annotationPanelMenuItem.setState(av.showAnnotation);
+     /*
+      * Show/hide annotations only enabled if annotation panel is shown
+      */
+     showAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
+     hideAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
+     showAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
+     hideAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
      viewBoxesMenuItem.setSelected(av.showBoxes);
      viewTextMenuItem.setSelected(av.showText);
      showNonconservedMenuItem.setSelected(av.getShowUnconserved());
      setColourSelected(ColourSchemeProperty.getColourName(av
              .getGlobalColourScheme()));
  
 -    showSeqFeatures.setSelected(av.showSequenceFeatures);
 +    showSeqFeatures.setSelected(av.isShowSequenceFeatures());
      hiddenMarkers.setState(av.showHiddenMarkers);
      applyToAllGroups.setState(av.getColourAppliesToAllGroups());
      showNpFeatsMenuitem.setSelected(av.isShowNpFeats());
    {
      if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
      {
-       throw new Error(
-               "call setProgressBar before registering the progress bar's handler.");
+       throw new Error(MessageManager.getString("error.call_setprogressbar_before_registering_handler"));
      }
      progressBarHandlers.put(new Long(id), handler);
      final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
          public void actionPerformed(ActionEvent e)
          {
            handler.cancelActivity(id);
-           us.setProgressBar(
-                   "Cancelled "
-                           + ((JLabel) progressPanel.getComponent(0))
-                                   .getText(), id);
+           us.setProgressBar(MessageManager.formatMessage("label.cancelled_params", new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id);
          }
        });
        progressPanel.add(cancel, BorderLayout.EAST);
              currentFileFormat, false);
  
      chooser.setFileView(new JalviewFileView());
-     chooser.setDialogTitle("Save Alignment to file");
+     chooser.setDialogTitle(MessageManager.getString("label.save_alignment_to_file"));
      chooser.setToolTipText(MessageManager.getString("action.save"));
  
      int value = chooser.showSaveDialog(this);
              alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
    }
  
+   @Override
+   public void bioJSMenuItem_actionPerformed(ActionEvent e)
+   {
+     new BioJsHTMLOutput(alignPanel,
+             alignPanel.seqPanel.seqCanvas.getSequenceRenderer(),
+             alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
+   }
    public void createImageMap(File file, String image)
    {
      alignPanel.makePNGImageMap(file, image);
      alignPanel.makeEPS(f);
    }
  
+   public void createSVG(File f)
+   {
+     alignPanel.makeSVG(f);
+   }
    @Override
    public void pageSetup_actionPerformed(ActionEvent e)
    {
    protected void undoMenuItem_actionPerformed(ActionEvent e)
    {
      if (viewport.historyList.empty())
+     {
        return;
+     }
      CommandI command = (CommandI) viewport.historyList.pop();
      viewport.redoList.push(command);
      command.undoCommand(getViewAlignments());
      for (int i = 0; i < viewport.getAlignment().getHeight(); i++)
      {
        if (!sg.contains(viewport.getAlignment().getSequenceAt(i)))
+       {
          invertGroup.add(viewport.getAlignment().getSequenceAt(i));
+       }
      }
  
      SequenceI[] seqs1 = sg.toArray(new SequenceI[0]);
  
      SequenceI[] seqs2 = new SequenceI[invertGroup.size()];
      for (int i = 0; i < invertGroup.size(); i++)
+     {
        seqs2[i] = (SequenceI) invertGroup.elementAt(i);
+     }
  
      SlideSequencesCommand ssc;
      if (right)
+     {
        ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1,
                size, viewport.getGapCharacter());
+     }
      else
+     {
        ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2,
                size, viewport.getGapCharacter());
+     }
  
      int groupAdjustment = 0;
      if (ssc.getGapsInsertedBegin() && right)
      {
        if (viewport.cursorMode)
+       {
          alignPanel.seqPanel.moveCursor(size, 0);
+       }
        else
+       {
          groupAdjustment = size;
+       }
      }
      else if (!ssc.getGapsInsertedBegin() && !right)
      {
        if (viewport.cursorMode)
+       {
          alignPanel.seqPanel.moveCursor(-size, 0);
+       }
        else
+       {
          groupAdjustment = -size;
+       }
      }
  
      if (groupAdjustment != 0)
      }
  
      if (!appendHistoryItem)
+     {
        addHistoryItem(ssc);
+     }
  
      repaint();
    }
          // /////
          // ADD HISTORY ITEM
          //
-         addHistoryItem(new EditCommand("Add sequences", EditCommand.PASTE,
+         addHistoryItem(new EditCommand(MessageManager.getString("label.add_sequences"), EditCommand.PASTE,
                  sequences, 0, alignment.getWidth(), alignment));
        }
        // Add any annotations attached to sequences
            {
              AlignmentAnnotation sann[] = sequences[i].getAnnotation();
              if (sann == null)
+             {
                continue;
+             }
              for (int avnum = 0; avnum < alview.length; avnum++)
              {
                if (alview[avnum] != alignment)
        return;
      }
  
-     Vector seqs = new Vector();
+     List<SequenceI> seqs = new ArrayList<SequenceI>(sg.getSize());
      SequenceI seq;
      for (int i = 0; i < sg.getSize(); i++)
      {
        seq = sg.getSequenceAt(i);
-       seqs.addElement(seq);
+       seqs.add(seq);
      }
  
-     // If the cut affects all sequences, remove highlighted columns
+     // If the cut affects all sequences, warn, remove highlighted columns
      if (sg.getSize() == viewport.getAlignment().getHeight())
      {
+       int confirm = JOptionPane.showConfirmDialog(this,
+               MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
+               MessageManager.getString("label.delete_all"), // $NON-NLS-1$
+               JOptionPane.OK_CANCEL_OPTION);
+       if (confirm == JOptionPane.CANCEL_OPTION
+               || confirm == JOptionPane.CLOSED_OPTION)
+       {
+         return;
+       }
        viewport.getColumnSelection().removeElements(sg.getStartRes(),
                sg.getEndRes() + 1);
      }
      SequenceI[] cut = new SequenceI[seqs.size()];
      for (int i = 0; i < seqs.size(); i++)
      {
-       cut[i] = (SequenceI) seqs.elementAt(i);
+       cut[i] = seqs.get(i);
      }
  
      /*
       * //ADD HISTORY ITEM
       */
-     addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT, cut,
+     addHistoryItem(new EditCommand(MessageManager.getString("label.cut_sequences"), EditCommand.CUT, cut,
              sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
              viewport.getAlignment()));
  
      boolean addFirstIndex = false;
      if (viewTitle == null || viewTitle.trim().length() == 0)
      {
-       viewTitle = "View";
+       viewTitle = MessageManager.getString("action.view");
        addFirstIndex = true;
      }
      else
    {
      viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight
              .isSelected());
 -    if (viewport.getShowSequenceFeaturesHeight())
 +    if (viewport.isShowSequenceFeaturesHeight())
      {
        // ensure we're actually displaying features
        viewport.setShowSequenceFeatures(true);
    }
  
    /**
-    * DOCUMENT ME!
+    * Action on toggle of the 'Show annotations' menu item. This shows or hides
+    * the annotations panel as a whole.
+    * 
+    * The options to show/hide all annotations should be enabled when the panel
+    * is shown, and disabled when the panel is hidden.
     * 
     * @param e
-    *          DOCUMENT ME!
     */
    @Override
    public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
    {
-     viewport.setShowAnnotation(annotationPanelMenuItem.isSelected());
-     alignPanel.setAnnotationVisible(annotationPanelMenuItem.isSelected());
+     final boolean setVisible = annotationPanelMenuItem.isSelected();
+     viewport.setShowAnnotation(setVisible);
+     alignPanel.setAnnotationVisible(setVisible);
+     this.showAllSeqAnnotations.setEnabled(setVisible);
+     this.hideAllSeqAnnotations.setEnabled(setVisible);
+     this.showAllAlAnnotations.setEnabled(setVisible);
+     this.hideAllAlAnnotations.setEnabled(setVisible);
    }
  
    @Override
    public void addSortByOrderMenuItem(String title,
            final AlignmentOrder order)
    {
-     final JMenuItem item = new JMenuItem("by " + title);
+     final JMenuItem item = new JMenuItem(MessageManager.formatMessage("action.by_title_param", new String[]{title}));
      sort.add(item);
      item.addActionListener(new java.awt.event.ActionListener()
      {
            tm.setText(title);//
            tm.addActionListener(new java.awt.event.ActionListener()
            {
+             @Override
              public void actionPerformed(ActionEvent e)
              {
                NewTreePanel(type, (String) pwtype, title);
            final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
            final JMenu analymenu = new JMenu("Analysis");
            final JMenu dismenu = new JMenu("Protein Disorder");
+           // final JMenu msawsmenu = new
+           // JMenu(MessageManager.getString("label.alignment"));
+           // final JMenu secstrmenu = new
+           // JMenu(MessageManager.getString("label.secondary_structure_prediction"));
+           // final JMenu seqsrchmenu = new
+           // JMenu(MessageManager.getString("label.sequence_database_search"));
+           // final JMenu analymenu = new
+           // JMenu(MessageManager.getString("label.analysis"));
+           // final JMenu dismenu = new
+           // JMenu(MessageManager.getString("label.protein_disorder"));
            // JAL-940 - only show secondary structure prediction services from
            // the legacy server
            if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
        public void run()
        {
          final long sttime = System.currentTimeMillis();
-         ths.setProgressBar("Searching for sequences from " + fsrc, sttime);
+         ths.setProgressBar(MessageManager.formatMessage("status.searching_for_sequences_from", new String[]{fsrc}), sttime);
          try
          {
            Alignment ds = ths.getViewport().getAlignment().getDataset(); // update
                if (ds.getSequences() == null
                        || !ds.getSequences().contains(
                                sprods[s].getDatasetSequence()))
+               {
                  ds.addSequence(sprods[s].getDatasetSequence());
+               }
                sprods[s].updatePDBIds();
              }
              Alignment al = new Alignment(sprods);
            jalview.bin.Cache.log.error("Error when finding crossreferences",
                    e);
          }
-         ths.setProgressBar("Finished searching for sequences from " + fsrc,
+         ths.setProgressBar(MessageManager.formatMessage("status.finished_searching_for_sequences_from", new String[]{fsrc}),
                  sttime);
        }
  
      {
        featuresFile = new FeaturesFile(file, type).parse(viewport
                .getAlignment().getDataset(), alignPanel.seqPanel.seqCanvas
 -              .getFeatureRenderer().featureColours, false,
 +              .getFeatureRenderer().getFeatureColours(), false,
                jalview.bin.Cache.getDefault("RELAXEDSEQIDMATCHING", false));
      } catch (Exception ex)
      {
  
      if (featuresFile)
      {
 -      viewport.showSequenceFeatures = true;
 +      viewport.setShowSequenceFeatures(true);
        showSeqFeatures.setSelected(true);
        if (alignPanel.seqPanel.seqCanvas.fr != null)
        {
                {
                  PDBEntry pe = new AssociatePdbFileWithSeq()
                          .associatePdbWithSeq((String) fm[0],
-                                 (String) fm[1], toassoc, false);
+                                 (String) fm[1], toassoc, false,
+                                 Desktop.instance);
                  if (pe != null)
                  {
                    System.err.println("Associated file : "
                            "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false) || JOptionPane
                            .showConfirmDialog(
                                    this,
-                                   MessageManager
+                                   "<html>"+MessageManager
                                            .formatMessage(
                                                    "label.ignore_unmatched_dropped_files_info",
                                                    new String[]
                                                    { Integer.valueOf(
                                                            filesnotmatched
                                                                    .size())
-                                                           .toString() }),
+                                                           .toString() })+"</html>",
                                    MessageManager
                                            .getString("label.ignore_unmatched_dropped_files"),
                                    JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION))
      trimrs.setSelected(Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true));
      trimrs.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          trimrs.setSelected(trimrs.isSelected());
                    }
  
                  });
-                 fetchr.setToolTipText("<html>"
-                         + JvSwingUtils.wrapTooltip("Retrieve from "
-                                 + src.getDbName()) + "<html>");
+                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{src.getDbName()})));
                  dfetch.add(fetchr);
                  comp++;
                }
                    }
                  });
  
-                 fetchr.setToolTipText("<html>"
-                         + JvSwingUtils.wrapTooltip("Retrieve from all "
-                                 + otherdb.size() + " sources in "
-                                 + src.getDbSource() + "<br>First is :"
-                                 + src.getDbName()) + "<html>");
+                 fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.formatMessage("label.fetch_retrieve_from_all_sources", new String[]{Integer.valueOf(otherdb.size()).toString(), src.getDbSource(), src.getDbName()})));
                  dfetch.add(fetchr);
                  comp++;
                  // and then build the rest of the individual menus
-                 ifetch = new JMenu("Sources from " + src.getDbSource());
+                 ifetch = new JMenu(MessageManager.formatMessage("label.source_from_db_source", new String[]{src.getDbSource()}));
                  icomp = 0;
                  String imname = null;
                  int i = 0;
                            0, 10) + "..." : dbname;
                    if (imname == null)
                    {
-                     imname = "from '" + sname + "'";
+                     imname = MessageManager.formatMessage("label.from_msname", new String[]{sname});
                    }
                    fetchr = new JMenuItem(msname);
                    final DbSourceProxy[] dassrc =
  
                    });
                    fetchr.setToolTipText("<html>"
-                           + JvSwingUtils.wrapTooltip("Retrieve from "
-                                   + dbname) + "</html>");
+                           + MessageManager.formatMessage("label.fetch_retrieve_from", new String[]{dbname}));
                    ifetch.add(fetchr);
                    ++i;
                    if (++icomp >= mcomp || i == (otherdb.size()))
      if (!viewport.getSequenceSetId().equals(
              alignmentPanel.av.getSequenceSetId()))
      {
-       throw new Error(
-               "Implementation error: cannot show a view from another alignment in an AlignFrame.");
+       throw new Error(MessageManager.getString("error.implementation_error_cannot_show_view_alignment_frame"));
      }
      if (tabbedPane != null
              & alignPanels.indexOf(alignmentPanel) != tabbedPane
        tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
      }
    }
+   /**
+    * Action on selection of menu options to Show or Hide annotations.
+    * 
+    * @param visible
+    * @param forSequences
+    *          update sequence-related annotations
+    * @param forAlignment
+    *          update non-sequence-related annotations
+    */
+   @Override
+   protected void setAnnotationsVisibility(boolean visible,
+           boolean forSequences, boolean forAlignment)
+   {
+     for (AlignmentAnnotation aa : alignPanel.getAlignment()
+             .getAlignmentAnnotation())
+     {
+       boolean apply = (aa.sequenceRef == null && forAlignment)
+               || (aa.sequenceRef != null && forSequences);
+       if (apply)
+       {
+         aa.visible = visible;
+       }
+     }
+     this.alignPanel.paintAlignment(true);
+   }
+   /**
+    * Store selected annotation sort order for the view and repaint.
+    */
+   @Override
+   protected void sortAnnotations_actionPerformed()
+   {
+     this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder());
+     this.alignPanel.av
+             .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
+     alignPanel.paintAlignment(true);
+   }
  }
  
  class PrintThread extends Thread
@@@ -38,6 -38,7 +38,7 @@@
   */
  package jalview.gui;
  
+ import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
  import jalview.analysis.NJTree;
  import jalview.api.AlignViewportI;
  import jalview.bin.Cache;
@@@ -95,8 -96,12 +96,10 @@@ public class AlignViewport extends Alig
  
    boolean renderGaps = true;
  
 -  boolean showSequenceFeatures = false;
 -
    boolean showAnnotation = true;
  
+   SequenceAnnotationOrder sortAnnotationsBy = null;
    int charHeight;
  
    int charWidth;
  
    boolean cursorMode = false;
  
 -  /**
 -   * Keys are the feature types which are currently visible. Note: Values are
 -   * not used!
 -   */
 -  Hashtable featuresDisplayed = null;
 -
    boolean antiAlias = false;
  
    Rectangle explodedPosition;
        }
      }
  
-     wrapAlignment = jalview.bin.Cache.getDefault("WRAP_ALIGNMENT", false);
-     showUnconserved = jalview.bin.Cache.getDefault("SHOW_UNCONSERVED",
-             false);
-     sortByTree = jalview.bin.Cache.getDefault("SORT_BY_TREE", false);
-     followSelection = jalview.bin.Cache.getDefault("FOLLOW_SELECTIONS",
-             true);
+     wrapAlignment = Cache.getDefault("WRAP_ALIGNMENT", false);
+     showUnconserved = Cache.getDefault("SHOW_UNCONSERVED", false);
+     sortByTree = Cache.getDefault("SORT_BY_TREE", false);
+     followSelection = Cache.getDefault("FOLLOW_SELECTIONS", true);
+     sortAnnotationsBy = SequenceAnnotationOrder.valueOf(Cache.getDefault(
+             Preferences.SORT_ANNOTATIONS,
+             SequenceAnnotationOrder.NONE.name()));
+     showAutocalculatedAbove = Cache.getDefault(
+             Preferences.SHOW_AUTOCALC_ABOVE, false);
    }
  
    /**
 -   * set the flag
 -   * 
 -   * @param b
 -   *          features are displayed if true
 -   */
 -  public void setShowSequenceFeatures(boolean b)
 -  {
 -    showSequenceFeatures = b;
 -  }
 -
 -  public boolean getShowSequenceFeatures()
 -  {
 -    return showSequenceFeatures;
 -  }
 -
 -  /**
     * centre columnar annotation labels in displayed alignment annotation TODO:
     * add to jalviewXML and annotation display settings
     */
    {
      // TODO: JAL-1126
      if (historyList == null || redoList == null)
+     {
        return new long[]
        { -1, -1 };
+     }
      return new long[]
      { historyList.hashCode(), this.redoList.hashCode() };
    }
      return followSelection;
    }
  
 -  boolean showSeqFeaturesHeight;
 -
    public void sendSelection()
    {
      jalview.structure.StructureSelectionManager
                      new ColumnSelection(getColumnSelection()), this);
    }
  
 -  public void setShowSequenceFeaturesHeight(boolean selected)
 -  {
 -    showSeqFeaturesHeight = selected;
 -  }
 -
 -  public boolean getShowSequenceFeaturesHeight()
 -  {
 -    return showSeqFeaturesHeight;
 -  }
 -
    /**
     * return the alignPanel containing the given viewport. Use this to get the
     * components currently handling the given viewport.
          Vector pdbs = alignment.getSequenceAt(i).getDatasetSequence()
                  .getPDBId();
          if (pdbs == null)
+         {
            continue;
+         }
          SequenceI sq;
          for (int p = 0; p < pdbs.size(); p++)
          {
            if (p1.getId().equals(pdb.getId()))
            {
              if (!seqs.contains(sq = alignment.getSequenceAt(i)))
+             {
                seqs.add(sq);
+             }
  
              continue;
            }
  
    private Hashtable<String, AutoCalcSetting> calcIdParams = new Hashtable<String, AutoCalcSetting>();
  
+   private boolean showAutocalculatedAbove;
    public AutoCalcSetting getCalcIdSettingsFor(String calcId)
    {
      return calcIdParams.get(calcId);
        Cache.log.debug("trigger update for " + calcId);
      }
    }
+   protected SequenceAnnotationOrder getSortAnnotationsBy()
+   {
+     return sortAnnotationsBy;
+   }
+   protected void setSortAnnotationsBy(SequenceAnnotationOrder sortAnnotationsBy)
+   {
+     this.sortAnnotationsBy = sortAnnotationsBy;
+   }
+   protected boolean isShowAutocalculatedAbove()
+   {
+     return showAutocalculatedAbove;
+   }
+   protected void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
+   {
+     this.showAutocalculatedAbove = showAutocalculatedAbove;
+   }
  }
   */
  package jalview.gui;
  
- import java.beans.*;
- import java.io.*;
- import java.awt.*;
- import java.awt.event.*;
- import java.awt.print.*;
- import javax.swing.*;
+ import jalview.analysis.AnnotationSorter;
  import jalview.api.AlignmentViewPanel;
  import jalview.bin.Cache;
- import jalview.datamodel.*;
- import jalview.jbgui.*;
- import jalview.schemes.*;
+ import jalview.datamodel.AlignmentI;
+ import jalview.datamodel.SearchResults;
+ import jalview.datamodel.SequenceFeature;
+ import jalview.datamodel.SequenceGroup;
+ import jalview.datamodel.SequenceI;
+ import jalview.jbgui.GAlignmentPanel;
+ import jalview.schemes.ResidueProperties;
  import jalview.structure.StructureSelectionManager;
+ import jalview.util.MessageManager;
+ import java.awt.BorderLayout;
+ import java.awt.Color;
+ import java.awt.Container;
+ import java.awt.Dimension;
+ import java.awt.Font;
+ import java.awt.FontMetrics;
+ import java.awt.Graphics;
+ import java.awt.event.AdjustmentEvent;
+ import java.awt.event.AdjustmentListener;
+ import java.awt.print.PageFormat;
+ import java.awt.print.Printable;
+ import java.awt.print.PrinterException;
+ import java.beans.PropertyChangeEvent;
+ import java.beans.PropertyChangeListener;
+ import java.io.File;
+ import java.io.FileWriter;
+ import java.io.PrintWriter;
+ import javax.swing.SwingUtilities;
  
  /**
   * DOCUMENT ME!
@@@ -178,7 -196,7 +196,7 @@@ public class AlignmentPanel extends GAl
  
      int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
      int maxwidth = Math.max(20,
-             Math.min(afwidth - 200, (int) 2 * afwidth / 3));
+             Math.min(afwidth - 200, 2 * afwidth / 3));
      return calculateIdWidth(maxwidth);
    }
  
      }
    }
  
+   /**
+    * Repaint the alignment including the annotations and overview panels (if
+    * shown).
+    */
    public void paintAlignment(boolean updateOverview)
    {
+     final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),
+             av.isShowAutocalculatedAbove());
+     sorter.sort(getAlignment()
+             .getAlignmentAnnotation(),
+             av.getSortAnnotationsBy());
      repaint();
  
      if (updateOverview)
      // / How many sequences and residues can we fit on a printable page?
      int totalRes = (pwidth - idWidth) / av.getCharWidth();
  
-     int totalSeq = (int) ((pheight - scaleHeight) / av.getCharHeight()) - 1;
+     int totalSeq = (pheight - scaleHeight) / av.getCharHeight() - 1;
  
      int pagesWide = (av.getAlignment().getWidth() / totalRes) + 1;
  
        int offset = -alabels.scrollOffset;
        pg.translate(0, offset);
        pg.translate(-idWidth - 3, (endSeq - startSeq) * av.charHeight + 3);
-       alabels.drawComponent((Graphics2D) pg, idWidth);
+       alabels.drawComponent(pg, idWidth);
        pg.translate(idWidth + 3, 0);
        annotationPanel.renderer.drawComponent(annotationPanel, av,
-               (Graphics2D) pg, -1, startRes, endRes + 1);
+               pg, -1, startRes, endRes + 1);
        pg.translate(0, -offset);
      }
  
      return idwidth.intValue() + 4;
    }
  
-   void makeAlignmentImage(int type, File file)
+   void makeAlignmentImage(jalview.util.ImageMaker.TYPE type, File file)
    {
      long progress = System.currentTimeMillis();
      boolean headless = (System.getProperty("java.awt.headless") != null && System
              .getProperty("java.awt.headless").equals("true"));
      if (alignFrame != null && !headless)
      {
-       alignFrame.setProgressBar("Saving "
-               + (type == jalview.util.ImageMaker.PNG ? "PNG image"
-                       : "EPS file"), progress);
+       alignFrame.setProgressBar(MessageManager.formatMessage(
+               "status.saving_file",
+               new String[]
+               { type.getLabel() }), progress);
      }
      try
      {
  
          jalview.util.ImageMaker im;
          final String imageAction, imageTitle;
-         if (type == jalview.util.ImageMaker.PNG)
+         if (type == jalview.util.ImageMaker.TYPE.PNG)
          {
            imageAction = "Create PNG image from alignment";
            imageTitle = null;
          }
-         else
+         else if (type == jalview.util.ImageMaker.TYPE.EPS)
          {
            imageAction = "Create EPS file from alignment";
            imageTitle = alignFrame.getTitle();
          }
+         else
+         {
+           imageAction = "Create SVG file from alignment";
+           imageTitle = alignFrame.getTitle();
+         }
          im = new jalview.util.ImageMaker(this, type, imageAction, width,
                  height, file, imageTitle);
          if (av.getWrapAlignment())
      {
        if (alignFrame != null && !headless)
        {
-         alignFrame.setProgressBar("Export complete.", progress);
+         alignFrame.setProgressBar(MessageManager.getString("status.export_complete"), progress);
        }
      }
    }
     */
    public void makeEPS(File epsFile)
    {
-     makeAlignmentImage(jalview.util.ImageMaker.EPS, epsFile);
+     makeAlignmentImage(jalview.util.ImageMaker.TYPE.EPS, epsFile);
    }
  
    /**
     */
    public void makePNG(File pngFile)
    {
-     makeAlignmentImage(jalview.util.ImageMaker.PNG, pngFile);
+     makeAlignmentImage(jalview.util.ImageMaker.TYPE.PNG, pngFile);
    }
  
+   public void makeSVG(File svgFile)
+   {
+     makeAlignmentImage(jalview.util.ImageMaker.TYPE.SVG, svgFile);
+   }
    public void makePNGImageMap(File imgMapFile, String imageName)
    {
      // /////ONLY WORKS WITH NONE WRAPPED ALIGNMENTS
      new OOMWarning(string, error, this);
    }
  
 -  public FeatureRenderer cloneFeatureRenderer()
 +  @Override
 +  public jalview.api.FeatureRenderer cloneFeatureRenderer()
    {
  
      return new FeatureRenderer(this);
    }
 -
 -  public void updateFeatureRenderer(FeatureRenderer fr)
 +  @Override 
 +  public jalview.api.FeatureRenderer getFeatureRenderer()
 +  {
 +    return seqPanel.seqCanvas.getFeatureRenderer();
 +  }
 +  public void updateFeatureRenderer(jalview.renderer.seqfeatures.FeatureRenderer fr)
    {
      fr.transferSettings(seqPanel.seqCanvas.getFeatureRenderer());
    }
  
 -  public void updateFeatureRendererFrom(FeatureRenderer fr)
 +  public void updateFeatureRendererFrom(jalview.api.FeatureRenderer fr)
    {
      if (seqPanel.seqCanvas.getFeatureRenderer() != null)
      {
@@@ -75,7 -75,7 +75,7 @@@ public class AnnotationExporter extend
      this.ap = ap;
      features = true;
      CSVFormat.setVisible(false);
-     frame.setTitle("Export Features");
+     frame.setTitle(MessageManager.getString("label.export_features"));
    }
  
    public void exportAnnotations(AlignmentPanel ap,
@@@ -89,7 -89,7 +89,7 @@@
      this.annotations = annotations;
      this.sequenceGroups = list;
      this.alignmentProperties = alProperties;
-     frame.setTitle("Export Annotations");
+     frame.setTitle(MessageManager.getString("label.export_annotations"));
    }
  
    public void toFile_actionPerformed(ActionEvent e)
              jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
  
      chooser.setFileView(new JalviewFileView());
-     chooser.setDialogTitle(features ? "Save Features to File"
-             : "Save Annotation to File");
+     chooser.setDialogTitle(features ? MessageManager.getString("label.save_features_to_file")
+             : MessageManager.getString("label.save_annotation_to_file"));
      chooser.setToolTipText(MessageManager.getString("action.save"));
  
      int value = chooser.showSaveDialog(this);
  
      if (value == JalviewFileChooser.APPROVE_OPTION)
      {
-       String text = "No features found on alignment";
+       String text = MessageManager.getString("label.no_features_on_alignment");
        if (features)
        {
          if (GFFFormat.isSelected())
          {
            text = new FeaturesFile().printGFFFormat(ap.av.getAlignment()
 -                  .getDataset().getSequencesArray(),
 -                  getDisplayedFeatureCols(), true, ap.av.isShowNpFeats());// ap.av.featuresDisplayed//);
 +                  .getDataset().getSequencesArray(), ap
 +                  .getFeatureRenderer().getDisplayedFeatureCols(), true,
 +                  ap.av.isShowNpFeats());// ap.av.featuresDisplayed//);
          }
          else
          {
            text = new FeaturesFile().printJalviewFormat(ap.av.getAlignment()
 -                  .getDataset().getSequencesArray(),
 -                  getDisplayedFeatureCols(), true, ap.av.isShowNpFeats()); // ap.av.featuresDisplayed);
 +                  .getDataset().getSequencesArray(), ap
 +                  .getFeatureRenderer().getDisplayedFeatureCols(), true,
 +                  ap.av.isShowNpFeats()); // ap.av.featuresDisplayed);
          }
        }
        else
  
    public void toTextbox_actionPerformed(ActionEvent e)
    {
-     String text = "No features found on alignment";
+     String text = MessageManager.getString("label.no_features_on_alignment");
      if (features)
      {
        if (GFFFormat.isSelected())
        {
          text = new FeaturesFile().printGFFFormat(ap.av.getAlignment()
 -                .getDataset().getSequencesArray(),
 -                getDisplayedFeatureCols(), true, ap.av.isShowNpFeats());
 +                .getDataset().getSequencesArray(), ap.getFeatureRenderer()
 +                .getDisplayedFeatureCols(), true, ap.av.isShowNpFeats());
        }
        else
        {
          text = new FeaturesFile().printJalviewFormat(ap.av.getAlignment()
 -                .getDataset().getSequencesArray(),
 -                getDisplayedFeatureCols(), true, ap.av.isShowNpFeats());
 +                .getDataset().getSequencesArray(), ap.getFeatureRenderer()
 +                .getDisplayedFeatureCols(), true, ap.av.isShowNpFeats());
        }
      }
      else if (!features)
      close_actionPerformed(null);
    }
  
 -  private Hashtable getDisplayedFeatureCols()
 -  {
 -    Hashtable fcols = new Hashtable();
 -    if (ap.av.featuresDisplayed == null)
 -    {
 -      return fcols;
 -    }
 -    Enumeration en = ap.av.featuresDisplayed.keys();
 -    FeatureRenderer fr = ap.seqPanel.seqCanvas.getFeatureRenderer(); // consider
 -                                                                     // higher
 -                                                                     // level
 -                                                                     // method ?
 -    while (en.hasMoreElements())
 -    {
 -      Object col = en.nextElement();
 -      fcols.put(col, fr.featureColours.get(col));
 -    }
 -    return fcols;
 -  }
 -
    public void close_actionPerformed(ActionEvent e)
    {
      try
   */
  package jalview.gui;
  
- import java.util.*;
- import java.awt.*;
- import javax.swing.*;
- import javax.swing.event.*;
- import java.awt.event.*;
- import java.io.*;
- import jalview.jbgui.GStructureViewer;
- import jalview.api.SequenceStructureBinding;
+ import jalview.api.structures.JalviewStructureDisplayI;
  import jalview.bin.Cache;
- import jalview.datamodel.*;
- import jalview.gui.ViewSelectionMenu.ViewSetProvider;
+ import jalview.datamodel.Alignment;
+ import jalview.datamodel.AlignmentI;
+ import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.PDBEntry;
- import jalview.io.*;
- import jalview.schemes.*;
+ import jalview.datamodel.SequenceI;
+ import jalview.ext.jmol.JalviewJmolBinding;
+ import jalview.gui.ViewSelectionMenu.ViewSetProvider;
+ import jalview.io.AppletFormatAdapter;
+ import jalview.io.JalviewFileChooser;
+ import jalview.io.JalviewFileView;
+ import jalview.jbgui.GStructureViewer;
+ 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.util.MessageManager;
  import jalview.util.Platform;
  
+ import java.awt.BorderLayout;
+ import java.awt.Color;
+ import java.awt.Component;
+ import java.awt.Dimension;
+ import java.awt.Font;
+ import java.awt.Graphics;
+ import java.awt.Rectangle;
+ import java.awt.event.ActionEvent;
+ import java.awt.event.ActionListener;
+ import java.awt.event.ItemEvent;
+ import java.awt.event.ItemListener;
+ import java.io.BufferedReader;
+ import java.io.File;
+ import java.io.FileOutputStream;
+ import java.io.FileReader;
+ import java.io.PrintWriter;
+ import java.util.ArrayList;
+ import java.util.Enumeration;
+ import java.util.Vector;
+ import javax.swing.JCheckBoxMenuItem;
+ import javax.swing.JColorChooser;
+ import javax.swing.JInternalFrame;
+ import javax.swing.JMenu;
+ import javax.swing.JMenuItem;
+ import javax.swing.JOptionPane;
+ import javax.swing.JPanel;
+ import javax.swing.JSplitPane;
+ import javax.swing.event.InternalFrameAdapter;
+ import javax.swing.event.InternalFrameEvent;
+ import javax.swing.event.MenuEvent;
+ import javax.swing.event.MenuListener;
  public class AppJmol extends GStructureViewer implements Runnable,
-         SequenceStructureBinding, ViewSetProvider
+         ViewSetProvider, JalviewStructureDisplayI
  
  {
    AppJmolBinding jmb;
     * @param bounds
     * @deprecated defaults to AppJmol(String[] files, ... , viewid);
     */
+   @Deprecated
    public AppJmol(String file, String id, SequenceI[] seq,
            AlignmentPanel ap, String loadStatus, Rectangle bounds)
    {
    /**
     * @deprecated
     */
+   @Deprecated
    public AppJmol(String file, String id, SequenceI[] seq,
            AlignmentPanel ap, String loadStatus, Rectangle bounds,
            String viewid)
      {
        jmb.setColourBySequence(false);
        seqColour.setSelected(false);
-       jmolColour.setSelected(true);
+       viewerColour.setSelected(true);
      }
      if (usetoColour)
      {
        useAlignmentPanelForColourbyseq(ap);
        jmb.setColourBySequence(true);
        seqColour.setSelected(true);
-       jmolColour.setSelected(false);
+       viewerColour.setSelected(false);
      }
      this.setBounds(bounds);
      initMenus();
    private void initMenus()
    {
      seqColour.setSelected(jmb.isColourBySequence());
-     jmolColour.setSelected(!jmb.isColourBySequence());
+     viewerColour.setSelected(!jmb.isColourBySequence());
      if (_colourwith == null)
      {
        _colourwith = new Vector<AlignmentPanel>();
        _alignwith = new Vector<AlignmentPanel>();
      }
  
-     seqColourBy = new ViewSelectionMenu("Colour by ..", this, _colourwith,
+     seqColourBy = new ViewSelectionMenu(MessageManager.getString("label.colour_by"), this, _colourwith,
              new ItemListener()
              {
  
              });
      viewMenu.add(seqColourBy);
      final ItemListener handler;
-     JMenu alpanels = new ViewSelectionMenu("Superpose with ..", this,
+     JMenu alpanels = new ViewSelectionMenu(MessageManager.getString("label.superpose_with"), this,
              _alignwith, handler = new ItemListener()
              {
  
                }
              });
      handler.itemStateChanged(null);
-     jmolActionMenu.add(alpanels);
-     jmolActionMenu.addMenuListener(new MenuListener()
+     viewerActionMenu.add(alpanels);
+     viewerActionMenu.addMenuListener(new MenuListener()
      {
  
        @Override
                        "label.pdb_entry_is_already_displayed", new String[]
                        { pdbentry.getId() }), MessageManager.formatMessage(
                        "label.map_sequences_to_visible_window", new String[]
-                       { pdbentry.getId() }), JOptionPane.YES_NO_OPTION);
+                       { pdbentry.getId() }),
+               JOptionPane.YES_NO_CANCEL_OPTION);
  
+       if (option == JOptionPane.CANCEL_OPTION)
+       {
+         return;
+       }
        if (option == JOptionPane.YES_OPTION)
        {
          // TODO : Fix multiple seq to one chain issue here.
                                  { pdbentry.getId(), topJmol.getTitle() }),
                          MessageManager
                                  .getString("label.align_to_existing_structure_view"),
-                         JOptionPane.YES_NO_OPTION);
+                         JOptionPane.YES_NO_CANCEL_OPTION);
+         if (option == JOptionPane.CANCEL_OPTION)
+         {
+           return;
+         }
          if (option == JOptionPane.YES_OPTION)
          {
            topJmol.useAlignmentPanelForSuperposition(ap);
      useAlignmentPanelForColourbyseq(nap);
      jmb.setColourBySequence(enableColourBySeq);
      seqColour.setSelected(enableColourBySeq);
-     jmolColour.setSelected(!enableColourBySeq);
+     viewerColour.setSelected(!enableColourBySeq);
    }
  
    public void useAlignmentPanelForColourbyseq(AlignmentPanel nap)
      return;
    }
  
-   private Vector getJmolsFor(AlignmentPanel ap2)
+   private Vector getJmolsFor(AlignmentPanel apanel)
    {
-     Vector otherJmols = new Vector();
-     // Now this AppJmol is mapped to new sequences. We must add them to
-     // the exisiting array
+     Vector result = new Vector();
      JInternalFrame[] frames = Desktop.instance.getAllFrames();
  
-     for (int i = 0; i < frames.length; i++)
+     for (JInternalFrame frame : frames)
      {
-       if (frames[i] instanceof AppJmol)
+       if (frame instanceof AppJmol)
        {
-         AppJmol topJmol = ((AppJmol) frames[i]);
-         if (topJmol.isLinkedWith(ap2))
+         if (((AppJmol) frame).isLinkedWith(apanel))
          {
-           otherJmols.addElement(topJmol);
+           result.addElement(frame);
          }
        }
      }
-     return otherJmols;
+     return result;
    }
  
    void initJmol(String command)
          for (int i = 0; i < chainMenu.getItemCount(); i++)
          {
            if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
+           {
              ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true);
+           }
          }
          centerViewer();
          allChainsSelected = false;
          public void itemStateChanged(ItemEvent evt)
          {
            if (!allChainsSelected)
+           {
              centerViewer();
+           }
          }
        });
  
      jmb.centerViewer(toshow);
    }
  
-   void closeViewer()
+   public void closeViewer()
    {
      jmb.closeViewer();
      ap = null;
            long hdl = pdbid.hashCode() - System.currentTimeMillis();
            if (progressBar != null)
            {
-             progressBar.setProgressBar("Fetching PDB " + pdbid, hdl);
+             progressBar.setProgressBar(MessageManager.formatMessage("status.fetching_pdb", new String[]{pdbid}), hdl);
            }
            try
            {
            }
            if (progressBar != null)
            {
-             progressBar.setProgressBar("Finished.", hdl);
+             progressBar.setProgressBar(MessageManager.getString("label.state_completed"), hdl);
            }
            if (pdbseq != null)
            {
      worker = null;
    }
  
+   @Override
    public void pdbFile_actionPerformed(ActionEvent actionEvent)
    {
      JalviewFileChooser chooser = new JalviewFileChooser(
              jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
  
      chooser.setFileView(new JalviewFileView());
-     chooser.setDialogTitle("Save PDB File");
+     chooser.setDialogTitle(MessageManager.getString("label.save_pdb_file"));
      chooser.setToolTipText(MessageManager.getString("action.save"));
  
      int value = chooser.showSaveDialog(this);
      }
    }
  
+   @Override
    public void viewMapping_actionPerformed(ActionEvent actionEvent)
    {
      jalview.gui.CutAndPasteTransfer cap = new jalview.gui.CutAndPasteTransfer();
              600);
    }
  
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
+   @Override
    public void eps_actionPerformed(ActionEvent e)
    {
-     makePDBImage(jalview.util.ImageMaker.EPS);
+     makePDBImage(jalview.util.ImageMaker.TYPE.EPS);
    }
  
-   /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
-    */
+   @Override
    public void png_actionPerformed(ActionEvent e)
    {
-     makePDBImage(jalview.util.ImageMaker.PNG);
+     makePDBImage(jalview.util.ImageMaker.TYPE.PNG);
    }
  
-   void makePDBImage(int type)
+   void makePDBImage(jalview.util.ImageMaker.TYPE type)
    {
      int width = getWidth();
      int height = getHeight();
  
      jalview.util.ImageMaker im;
  
-     if (type == jalview.util.ImageMaker.PNG)
+     if (type == jalview.util.ImageMaker.TYPE.PNG)
      {
-       im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.PNG,
+       im = new jalview.util.ImageMaker(this,
+               jalview.util.ImageMaker.TYPE.PNG,
                "Make PNG image from view", width, height, null, null);
      }
-     else
+     else if (type == jalview.util.ImageMaker.TYPE.EPS)
      {
-       im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.EPS,
+       im = new jalview.util.ImageMaker(this,
+               jalview.util.ImageMaker.TYPE.EPS,
                "Make EPS file from view", width, height, null,
                this.getTitle());
      }
+     else
+     {
+       im = new jalview.util.ImageMaker(this,
+               jalview.util.ImageMaker.TYPE.SVG, "Make SVG file from PCA",
+               width, height, null, this.getTitle());
+     }
  
      if (im.getGraphics() != null)
      {
      }
    }
  
-   public void jmolColour_actionPerformed(ActionEvent actionEvent)
+   @Override
+   public void viewerColour_actionPerformed(ActionEvent actionEvent)
    {
-     if (jmolColour.isSelected())
+     if (viewerColour.isSelected())
      {
        // disable automatic sequence colouring.
        jmb.setColourBySequence(false);
      }
    }
  
+   @Override
    public void seqColour_actionPerformed(ActionEvent actionEvent)
    {
      jmb.setColourBySequence(seqColour.isSelected());
        // Set the colour using the current view for the associated alignframe
        for (AlignmentPanel ap : _colourwith)
        {
 -        jmb.colourBySequence(ap.av.showSequenceFeatures, ap);
 +        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,
-             "Select Background Colour", null);
+             MessageManager.getString("label.select_backgroud_colour"), null);
      if (col != null)
      {
        jmb.setBackgroundColour(col);
      }
    }
  
-   public void jmolHelp_actionPerformed(ActionEvent actionEvent)
+   @Override
+   public void showHelp_actionPerformed(ActionEvent actionEvent)
    {
      try
      {
      this.setTitle(jmb.getViewerTitle());
      if (jmb.getPdbFile().length > 1 && jmb.sequence.length > 1)
      {
-       jmolActionMenu.setVisible(true);
+       viewerActionMenu.setVisible(true);
      }
      if (!jmb.isLoadingFromArchive())
      {
        _alignwith.add(ap);
      }
      ;
-     for (Component c : jmolActionMenu.getMenuComponents())
+     for (Component c : viewerActionMenu.getMenuComponents())
      {
        if (c != alignStructs)
        {
-         jmolActionMenu.remove((JMenuItem) c);
+         viewerActionMenu.remove((JMenuItem) c);
        }
      }
      final ItemListener handler;
      return !jmb.isColourBySequence();
    }
  
+   public JalviewJmolBinding getBinding()
+   {
+     return jmb;
+   }
  }
index 0000000,3c53762..75aed5d
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1245 +1,1245 @@@
+ /*
+  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
+  * Copyright (C) 2014 The Jalview Authors
+  * 
+  * This file is part of Jalview.
+  * 
+  * Jalview is free software: you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License 
+  * as published by the Free Software Foundation, either version 3
+  * of the License, or (at your option) any later version.
+  *  
+  * Jalview is distributed in the hope that it will be useful, but 
+  * WITHOUT ANY WARRANTY; without even the implied warranty 
+  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+  * PURPOSE.  See the GNU General Public License for more details.
+  * 
+  * You should have received a copy of the GNU General Public License
+  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+  * The Jalview Authors are detailed in the 'AUTHORS' file.
+  */
+ package jalview.gui;
+ import jalview.api.SequenceStructureBinding;
+ import jalview.api.structures.JalviewStructureDisplayI;
+ 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.ViewSelectionMenu.ViewSetProvider;
+ import jalview.io.AppletFormatAdapter;
+ import jalview.io.JalviewFileChooser;
+ import jalview.io.JalviewFileView;
+ import jalview.jbgui.GStructureViewer;
+ 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.util.MessageManager;
+ import jalview.util.Platform;
+ import jalview.ws.dbsources.Pdb;
+ 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.JCheckBoxMenuItem;
+ import javax.swing.JColorChooser;
+ import javax.swing.JInternalFrame;
+ import javax.swing.JMenu;
+ import javax.swing.JMenuItem;
+ import javax.swing.JOptionPane;
+ import javax.swing.event.InternalFrameAdapter;
+ import javax.swing.event.InternalFrameEvent;
+ import javax.swing.event.MenuEvent;
+ import javax.swing.event.MenuListener;
+ /**
+  * GUI elements for handlnig an external chimera display
+  * 
+  * @author jprocter
+  *
+  */
+ public class ChimeraViewFrame extends GStructureViewer implements Runnable,
+         ViewSetProvider, JalviewStructureDisplayI
+ {
+   private JalviewChimeraBindingModel jmb;
+   /*
+    * list of sequenceSet ids associated with the view
+    */
+   private ArrayList<String> _aps = new ArrayList<String>();
+   /*
+    * list of alignment panels to use for superposition
+    */
+   private Vector<AlignmentPanel> _alignwith = new Vector<AlignmentPanel>();
+   /*
+    * list of alignment panels that are used for colouring structures by aligned
+    * sequences
+    */
+   private Vector<AlignmentPanel> _colourwith = new Vector<AlignmentPanel>();
+   private boolean allChainsSelected = false;
+   private boolean alignAddedStructures = false;
+   AlignmentPanel ap;
+   /*
+    * state flag for PDB retrieval thread
+    */
+   private boolean _started = false;
+   private boolean addingStructures = false;
+   private IProgressIndicator progressBar = null;
+   private String viewId = null;
+   /*
+    * pdb retrieval thread.
+    */
+   private Thread worker = null;
+   /**
+    * Initialise menu options.
+    */
+   private void 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>();
+     }
+     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;
+     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()
+     {
+       @Override
+       public void menuSelected(MenuEvent e)
+       {
+         handler.itemStateChanged(null);
+       }
+       @Override
+       public void menuDeselected(MenuEvent e)
+       {
+         // TODO Auto-generated method stub
+       }
+       @Override
+       public void menuCanceled(MenuEvent e)
+       {
+         // TODO Auto-generated method stub
+       }
+     });
+   }
+   /**
+    * add a single PDB structure to a new or existing Chimera view
+    * 
+    * @param pdbentry
+    * @param seq
+    * @param chains
+    * @param ap
+    */
+   public ChimeraViewFrame(PDBEntry pdbentry, SequenceI[] seq,
+           String[] chains, final AlignmentPanel ap)
+   {
+     super();
+     progressBar = ap.alignFrame;
+     // ////////////////////////////////
+     // Is the pdb file already loaded?
+     String alreadyMapped = ap.getStructureSelectionManager()
+             .alreadyMappedToFile(pdbentry.getId());
+     if (alreadyMapped != null)
+     {
+       int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
+               MessageManager.formatMessage(
+                       "label.pdb_entry_is_already_displayed", new Object[]
+                       { pdbentry.getId() }), MessageManager.formatMessage(
+                       "label.map_sequences_to_visible_window", new Object[]
+                       { pdbentry.getId() }),
+               JOptionPane.YES_NO_CANCEL_OPTION);
+       if (option == JOptionPane.CANCEL_OPTION)
+       {
+         return;
+       }
+       if (option == JOptionPane.YES_OPTION)
+       {
+         // TODO : Fix multiple seq to one chain issue here.
+         ap.getStructureSelectionManager().setMapping(seq, chains,
+                 alreadyMapped, AppletFormatAdapter.FILE);
+         if (ap.seqPanel.seqCanvas.fr != null)
+         {
+           ap.seqPanel.seqCanvas.fr.featuresAdded();
+           ap.paintAlignment(true);
+         }
+         // Now this ChimeraViewFrame is mapped to new sequences. We must add
+         // them to the existing array
+         JInternalFrame[] frames = Desktop.instance.getAllFrames();
+         for (JInternalFrame frame : frames)
+         {
+           if (frame instanceof ChimeraViewFrame)
+           {
+             final ChimeraViewFrame topView = ((ChimeraViewFrame) frame);
+             // JBPNOTE: this looks like a binding routine, rather than a gui
+             // routine
+             for (int pe = 0; pe < topView.jmb.pdbentry.length; pe++)
+             {
+               if (topView.jmb.pdbentry[pe].getFile().equals(alreadyMapped))
+               {
+                 topView.jmb.addSequence(pe, seq);
+                 topView.addAlignmentPanel(ap);
+                 // add it to the set used for colouring
+                 topView.useAlignmentPanelForColourbyseq(ap);
+                 topView.buildChimeraActionMenu();
+                 ap.getStructureSelectionManager()
+                         .sequenceColoursChanged(ap);
+                 break;
+               }
+             }
+           }
+         }
+         return;
+       }
+     }
+     // /////////////////////////////////
+     // Check if there are other Chimera views involving this alignment
+     // and prompt user about adding this molecule to one of them
+     List<ChimeraViewFrame> existingViews = getChimeraWindowsFor(ap);
+     for (ChimeraViewFrame topView : existingViews)
+     {
+       // TODO: highlight topView in view somehow
+       int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
+               MessageManager.formatMessage("label.add_pdbentry_to_view",
+                       new Object[]
+                       { pdbentry.getId(), topView.getTitle() }),
+               MessageManager
+                       .getString("label.align_to_existing_structure_view"),
+               JOptionPane.YES_NO_CANCEL_OPTION);
+       if (option == JOptionPane.CANCEL_OPTION)
+       {
+         return;
+       }
+       if (option == JOptionPane.YES_OPTION)
+       {
+         topView.useAlignmentPanelForSuperposition(ap);
+         topView.addStructure(pdbentry, seq, chains, true, ap.alignFrame);
+         return;
+       }
+     }
+     // /////////////////////////////////
+     openNewChimera(ap, new PDBEntry[]
+     { pdbentry }, new SequenceI[][]
+     { seq });
+   }
+   private void openNewChimera(AlignmentPanel ap, PDBEntry[] pdbentrys,
+           SequenceI[][] seqs)
+   {
+     progressBar = ap.alignFrame;
+     jmb = new JalviewChimeraBindingModel(this,
+             ap.getStructureSelectionManager(), pdbentrys, seqs, null, 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
+     initMenus();
+     worker = null;
+     {
+       addingStructures = false;
+       worker = new Thread(this);
+       worker.start();
+     }
+     this.addInternalFrameListener(new InternalFrameAdapter()
+     {
+       public void internalFrameClosing(InternalFrameEvent internalFrameEvent)
+       {
+         closeViewer();
+       }
+     });
+   }
+   /**
+    * create a new viewer containing several structures superimposed using the
+    * given alignPanel.
+    * 
+    * @param ap
+    * @param pe
+    * @param seqs
+    */
+   public ChimeraViewFrame(AlignmentPanel ap, PDBEntry[] pe,
+           SequenceI[][] seqs)
+   {
+     super();
+     openNewChimera(ap, pe, seqs);
+   }
+   public AlignmentPanel[] getAllAlignmentPanels()
+   {
+     AlignmentPanel[] t, list = new AlignmentPanel[0];
+     for (String setid : _aps)
+     {
+       AlignmentPanel[] panels = PaintRefresher.getAssociatedPanels(setid);
+       if (panels != null)
+       {
+         t = new AlignmentPanel[list.length + panels.length];
+         System.arraycopy(list, 0, t, 0, list.length);
+         System.arraycopy(panels, 0, t, list.length, panels.length);
+         list = t;
+       }
+     }
+     return list;
+   }
+   /**
+    * set the primary alignmentPanel reference and add another alignPanel to the
+    * list of ones to use for colouring and aligning
+    * 
+    * @param nap
+    */
+   public void addAlignmentPanel(AlignmentPanel nap)
+   {
+     if (ap == null)
+     {
+       ap = nap;
+     }
+     if (!_aps.contains(nap.av.getSequenceSetId()))
+     {
+       _aps.add(nap.av.getSequenceSetId());
+     }
+   }
+   /**
+    * remove any references held to the given alignment panel
+    * 
+    * @param nap
+    */
+   public void removeAlignmentPanel(AlignmentPanel nap)
+   {
+     try
+     {
+       _alignwith.remove(nap);
+       _colourwith.remove(nap);
+       if (ap == nap)
+       {
+         ap = null;
+         for (AlignmentPanel aps : getAllAlignmentPanels())
+         {
+           if (aps != nap)
+           {
+             ap = aps;
+             break;
+           }
+         }
+       }
+     } catch (Exception ex)
+     {
+     }
+     if (ap != null)
+     {
+       buildChimeraActionMenu();
+     }
+   }
+   public void useAlignmentPanelForSuperposition(AlignmentPanel nap)
+   {
+     addAlignmentPanel(nap);
+     if (!_alignwith.contains(nap))
+     {
+       _alignwith.add(nap);
+     }
+   }
+   public void excludeAlignmentPanelForSuperposition(AlignmentPanel nap)
+   {
+     if (_alignwith.contains(nap))
+     {
+       _alignwith.remove(nap);
+     }
+   }
+   public void useAlignmentPanelForColourbyseq(AlignmentPanel nap,
+           boolean enableColourBySeq)
+   {
+     useAlignmentPanelForColourbyseq(nap);
+     jmb.setColourBySequence(enableColourBySeq);
+     seqColour.setSelected(enableColourBySeq);
+     viewerColour.setSelected(!enableColourBySeq);
+   }
+   public void useAlignmentPanelForColourbyseq(AlignmentPanel nap)
+   {
+     addAlignmentPanel(nap);
+     if (!_colourwith.contains(nap))
+     {
+       _colourwith.add(nap);
+     }
+   }
+   public void excludeAlignmentPanelForColourbyseq(AlignmentPanel nap)
+   {
+     if (_colourwith.contains(nap))
+     {
+       _colourwith.remove(nap);
+     }
+   }
+   /**
+    * add a new structure (with associated sequences and chains) to this viewer,
+    * retrieving it if necessary first.
+    * 
+    * @param pdbentry
+    * @param seq
+    * @param chains
+    * @param alignFrame
+    * @param align
+    *          if true, new structure(s) will be align using associated alignment
+    */
+   private void addStructure(final PDBEntry pdbentry, final SequenceI[] seq,
+           final String[] chains, final boolean b,
+           final IProgressIndicator alignFrame)
+   {
+     if (pdbentry.getFile() == null)
+     {
+       if (worker != null && worker.isAlive())
+       {
+         // a retrieval is in progress, wait around and add ourselves to the
+         // queue.
+         new Thread(new Runnable()
+         {
+           public void run()
+           {
+             while (worker != null && worker.isAlive() && _started)
+             {
+               try
+               {
+                 Thread.sleep(100 + ((int) Math.random() * 100));
+               } catch (Exception e)
+               {
+               }
+             }
+             // and call ourselves again.
+             addStructure(pdbentry, seq, chains, b, alignFrame);
+           }
+         }).start();
+         return;
+       }
+     }
+     // otherwise, start adding the structure.
+     jmb.addSequenceAndChain(new PDBEntry[]
+     { pdbentry }, new SequenceI[][]
+     { seq }, new String[][]
+     { chains });
+     addingStructures = true;
+     _started = false;
+     alignAddedStructures = b;
+     progressBar = alignFrame; // visual indication happens on caller frame.
+     (worker = new Thread(this)).start();
+     return;
+   }
+   private List<ChimeraViewFrame> getChimeraWindowsFor(AlignmentPanel apanel)
+   {
+     List<ChimeraViewFrame> result = new ArrayList<ChimeraViewFrame>();
+     JInternalFrame[] frames = Desktop.instance.getAllFrames();
+     for (JInternalFrame frame : frames)
+     {
+       if (frame instanceof ChimeraViewFrame)
+       {
+         if (((ChimeraViewFrame) frame).isLinkedWith(apanel))
+         {
+           result.add((ChimeraViewFrame) frame);
+         }
+       }
+     }
+     return result;
+   }
+   void initChimera(String command)
+   {
+     jmb.setFinishedInit(false);
+     // TODO: consider waiting until the structure/view is fully loaded before
+     // displaying
+     jalview.gui.Desktop.addInternalFrame(this, jmb.getViewerTitle(true),
+             getBounds().width, getBounds().height);
+     if (command == null)
+     {
+       command = "";
+     }
+     jmb.evalStateCommand(command, false);
+     jmb.setFinishedInit(true);
+   }
+   void setChainMenuItems(List<String> chainNames)
+   {
+     chainMenu.removeAll();
+     if (chainNames == null)
+     {
+       return;
+     }
+     JMenuItem menuItem = new JMenuItem(
+             MessageManager.getString("label.all"));
+     menuItem.addActionListener(new ActionListener()
+     {
+       public void actionPerformed(ActionEvent evt)
+       {
+         allChainsSelected = true;
+         for (int i = 0; i < chainMenu.getItemCount(); i++)
+         {
+           if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
+           {
+             ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true);
+           }
+         }
+         centerViewer();
+         allChainsSelected = false;
+       }
+     });
+     chainMenu.add(menuItem);
+     for (String chainName : chainNames)
+     {
+       menuItem = new JCheckBoxMenuItem(chainName, true);
+       menuItem.addItemListener(new ItemListener()
+       {
+         public void itemStateChanged(ItemEvent evt)
+         {
+           if (!allChainsSelected)
+           {
+             centerViewer();
+           }
+         }
+       });
+       chainMenu.add(menuItem);
+     }
+   }
+   void centerViewer()
+   {
+     List<String> toshow = new ArrayList<String>();
+     for (int i = 0; i < chainMenu.getItemCount(); i++)
+     {
+       if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
+       {
+         JCheckBoxMenuItem item = (JCheckBoxMenuItem) chainMenu.getItem(i);
+         if (item.isSelected())
+         {
+           toshow.add(item.getText());
+         }
+       }
+     }
+     jmb.centerViewer(toshow);
+   }
+   /**
+    * Close down this instance of Jalview's Chimera viewer, giving the user the
+    * option to close the associated Chimera window (process). They may wish to
+    * keep it open until they have had an opportunity to save any work.
+    */
+   public void closeViewer()
+   {
+     if (jmb.isChimeraRunning())
+     {
+       String prompt = MessageManager
+               .formatMessage("label.confirm_close_chimera", new Object[]
+               { jmb.getViewerTitle(false) });
+       prompt = JvSwingUtils.wrapTooltip(true, prompt);
+       int confirm = JOptionPane.showConfirmDialog(this, prompt,
+               MessageManager.getString("label.close_viewer"),
+               JOptionPane.YES_NO_OPTION);
+       jmb.closeViewer(confirm == JOptionPane.YES_OPTION);
+     }
+     ap = null;
+     _aps.clear();
+     _alignwith.clear();
+     _colourwith.clear();
+     // TODO: check for memory leaks where instance isn't finalised because jmb
+     // holds a reference to the window
+     jmb = null;
+   }
+   /**
+    * Open any newly added PDB structures in Chimera, having first fetched data
+    * from PDB (if not already saved).
+    */
+   public void run()
+   {
+     _started = true;
+     // todo - record which pdbids were successfully imported.
+     StringBuilder errormsgs = new StringBuilder(128);
+     StringBuilder files = new StringBuilder(128);
+     List<PDBEntry> filePDB = new ArrayList<PDBEntry>();
+     List<Integer> filePDBpos = new ArrayList<Integer>();
+     PDBEntry thePdbEntry = null;
+     try
+     {
+       String[] curfiles = jmb.getPdbFile(); // files currently in viewer
+       // TODO: replace with reference fetching/transfer code (validate PDBentry
+       // as a DBRef?)
+       for (int pi = 0; pi < jmb.pdbentry.length; pi++)
+       {
+         String file = null;
+         thePdbEntry = jmb.pdbentry[pi];
+         if (thePdbEntry.getFile() == null)
+         {
+           /*
+            * Retrieve PDB data, save to file, attach to PDBEntry
+            */
+           file = fetchPdbFile(thePdbEntry);
+           if (file == null)
+           {
+             errormsgs.append("'" + thePdbEntry.getId() + "' ");
+           }
+         }
+         else
+         {
+           /*
+            * Got file already - ignore if already loaded in Chimera.
+            */
+           file = new File(thePdbEntry.getFile()).getAbsoluteFile()
+                   .getPath();
+           if (curfiles != null && curfiles.length > 0)
+           {
+             addingStructures = true; // already files loaded.
+             for (int c = 0; c < curfiles.length; c++)
+             {
+               if (curfiles[c].equals(file))
+               {
+                 file = null;
+                 break;
+               }
+             }
+           }
+         }
+         if (file != null)
+         {
+           filePDB.add(thePdbEntry);
+           filePDBpos.add(Integer.valueOf(pi));
+           files.append(" \"" + Platform.escapeString(file) + "\"");
+         }
+       }
+     } catch (OutOfMemoryError oomerror)
+     {
+       new OOMWarning("Retrieving PDB files: " + thePdbEntry.getId(),
+               oomerror);
+     } catch (Exception ex)
+     {
+       ex.printStackTrace();
+       errormsgs.append("When retrieving pdbfiles for '"
+               + thePdbEntry.getId() + "'");
+     }
+     if (errormsgs.length() > 0)
+     {
+       JOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
+               .formatMessage("label.pdb_entries_couldnt_be_retrieved",
+                       new Object[]
+                       { errormsgs.toString() }), MessageManager
+               .getString("label.couldnt_load_file"),
+               JOptionPane.ERROR_MESSAGE);
+     }
+     if (files.length() > 0)
+     {
+       if (!addingStructures)
+       {
+         try
+         {
+           initChimera("");
+         } catch (Exception ex)
+         {
+           Cache.log.error("Couldn't open Chimera viewer!", ex);
+         }
+       }
+       int num = -1;
+       for (PDBEntry pe : filePDB)
+       {
+         num++;
+         if (pe.getFile() != null)
+         {
+           try
+           {
+             int pos = filePDBpos.get(num).intValue();
+             jmb.openFile(pe);
+             jmb.addSequence(pos, jmb.sequence[pos]);
+             File fl = new File(pe.getFile());
+             String protocol = AppletFormatAdapter.URL;
+             try
+             {
+               if (fl.exists())
+               {
+                 protocol = AppletFormatAdapter.FILE;
+               }
+             } catch (Throwable e)
+             {
+             }
+             // Explicitly map to the filename used by Chimera ;
+             // TODO: use pe.getId() instead of pe.getFile() ?
+             jmb.ssm.setMapping(jmb.sequence[pos], null, pe.getFile(),
+                     protocol);
+           } catch (OutOfMemoryError oomerror)
+           {
+             new OOMWarning(
+                     "When trying to open and map structures from Chimera!",
+                     oomerror);
+           } catch (Exception ex)
+           {
+             Cache.log.error("Couldn't open " + pe.getFile()
+                     + " in Chimera viewer!", ex);
+           } finally
+           {
+             Cache.log.debug("File locations are " + files);
+           }
+         }
+       }
+       jmb.setFinishedInit(true);
+       jmb.setLoadingFromArchive(false);
+       // refresh the sequence colours for the new structure(s)
+       for (AlignmentPanel ap : _colourwith)
+       {
+         jmb.updateColours(ap);
+       }
+       // do superposition if asked to
+       if (alignAddedStructures)
+       {
+         new Thread(new Runnable()
+         {
+           public void run()
+           {
+             alignStructs_withAllAlignPanels();
+           }
+         }).start();
+         alignAddedStructures = false;
+       }
+       addingStructures = false;
+     }
+     _started = false;
+     worker = null;
+   }
+   /**
+    * Fetch PDB data and save to a local file. Returns the full path to the file,
+    * or null if fetch fails.
+    * 
+    * @param processingEntry
+    * @return
+    * @throws Exception
+    */
+   private String fetchPdbFile(PDBEntry processingEntry) throws Exception
+   {
+     String filePath = null;
+     Pdb pdbclient = new Pdb();
+     AlignmentI pdbseq = null;
+     String pdbid = processingEntry.getId();
+     long hdl = pdbid.hashCode() - System.currentTimeMillis();
+     if (progressBar != null)
+     {
+       progressBar.setProgressBar(MessageManager.formatMessage(
+               "status.fetching_pdb", new Object[]
+               { pdbid }), hdl);
+     }
+     try
+     {
+       pdbseq = pdbclient.getSequenceRecords(pdbid);
+     } catch (OutOfMemoryError oomerror)
+     {
+       new OOMWarning("Retrieving PDB id " + pdbid, oomerror);
+     } finally
+     {
+       if (progressBar != null)
+       {
+         progressBar.setProgressBar(
+                 MessageManager.getString("label.state_completed"), hdl);
+       }
+     }
+     /*
+      * If PDB data were saved and are not invalid (empty alignment), return the
+      * file path.
+      */
+     if (pdbseq != null && pdbseq.getHeight() > 0)
+     {
+       // just use the file name from the first sequence's first PDBEntry
+       filePath = new File(((PDBEntry) pdbseq.getSequenceAt(0).getPDBId()
+               .elementAt(0)).getFile()).getAbsolutePath();
+       processingEntry.setFile(filePath);
+     }
+     return filePath;
+   }
+   @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
+     {
+       for (int pdbe = 0; pdbe < jmb.pdbentry.length; pdbe++)
+       {
+         cap.appendText(jmb.printMapping(jmb.pdbentry[pdbe].getFile()));
+         cap.appendText("\n");
+       }
+     } 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(
+             MessageManager
+                     .getString("error.eps_generation_not_implemented"));
+   }
+   @Override
+   public void png_actionPerformed(ActionEvent e)
+   {
+     throw new Error(
+             MessageManager
+                     .getString("error.png_generation_not_implemented"));
+   }
+   @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 && ap != null)
+         {
+           // Make the currently displayed alignment panel the associated view
+           _colourwith.add(ap.alignFrame.alignPanel);
+         }
+       }
+       // Set the colour using the current view for the associated alignframe
+       for (AlignmentPanel ap : _colourwith)
+       {
 -        jmb.colourBySequence(ap.av.showSequenceFeatures, ap);
++        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
+               .openURL("https://www.cgl.ucsf.edu/chimera/docs/UsersGuide");
+     } catch (Exception ex)
+     {
+     }
+   }
+   public String getViewId()
+   {
+     if (viewId == null)
+     {
+       viewId = System.currentTimeMillis() + "." + this.hashCode();
+     }
+     return viewId;
+   }
+   public void updateTitleAndMenus()
+   {
+     if (jmb.fileLoadingError != null && jmb.fileLoadingError.length() > 0)
+     {
+       repaint();
+       return;
+     }
+     setChainMenuItems(jmb.chainNames);
+     this.setTitle(jmb.getViewerTitle(true));
+     if (jmb.getPdbFile().length > 1 && jmb.sequence.length > 1)
+     {
+       viewerActionMenu.setVisible(true);
+     }
+     if (!jmb.isLoadingFromArchive())
+     {
+       seqColour_actionPerformed(null);
+     }
+   }
+   protected void buildChimeraActionMenu()
+   {
+     if (_alignwith == null)
+     {
+       _alignwith = new Vector<AlignmentPanel>();
+     }
+     if (_alignwith.size() == 0 && ap != null)
+     {
+       _alignwith.add(ap);
+     }
+     ;
+     for (Component c : viewerActionMenu.getMenuComponents())
+     {
+       if (c != alignStructs)
+       {
+         viewerActionMenu.remove((JMenuItem) c);
+       }
+     }
+   }
+   /*
+    * (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 (ap == null)
+     {
+       return;
+     }
+     ;
+     if (_alignwith.size() == 0)
+     {
+       _alignwith.add(ap);
+     }
+     ;
+     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);
+     }
+   }
+   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 ap;
+   }
+   /**
+    * 
+    * @param ap2
+    * @return true if this Chimera instance is linked with the given alignPanel
+    */
+   public boolean isLinkedWith(AlignmentPanel ap2)
+   {
+     return _aps.contains(ap2.av.getSequenceSetId());
+   }
+   public boolean isUsedforaligment(AlignmentPanel ap2)
+   {
+     return (_alignwith != null) && _alignwith.contains(ap2);
+   }
+   public boolean isUsedforcolourby(AlignmentPanel ap2)
+   {
+     return (_colourwith != null) && _colourwith.contains(ap2);
+   }
+   /**
+    * 
+    * @return TRUE if the view is NOT being coloured by sequence associations.
+    */
+   public boolean isColouredByChimera()
+   {
+     return !jmb.isColourBySequence();
+   }
+   public SequenceStructureBinding getBinding()
+   {
+     return jmb;
+   }
+ }
  package jalview.gui;
  
  import java.util.*;
 -import java.util.concurrent.ConcurrentHashMap;
 -
  import java.awt.*;
  import java.awt.event.*;
  import java.awt.image.*;
  import java.beans.PropertyChangeListener;
 -import java.beans.PropertyChangeSupport;
  
  import javax.swing.*;
  
@@@ -38,11 -41,38 +38,11 @@@ import jalview.util.MessageManager
   * @author $author$
   * @version $Revision$
   */
 -public class FeatureRenderer implements jalview.api.FeatureRenderer
 +public class FeatureRenderer extends jalview.renderer.seqfeatures.FeatureRenderer implements jalview.api.FeatureRenderer
  {
 -  AlignmentPanel ap;
 -
 -  AlignViewport av;
 -
    Color resBoxColour;
  
 -  /**
 -   * global transparency for feature
 -   */
 -  float transparency = 1.0f;
 -
 -  FontMetrics fm;
 -
 -  int charOffset;
 -
 -  Map featureColours = new ConcurrentHashMap();
 -
 -  // A higher level for grouping features of a
 -  // particular type
 -  Map featureGroups = new ConcurrentHashMap();
 -
 -  // This is actually an Integer held in the hashtable,
 -  // Retrieved using the key feature type
 -  Object currentColour;
 -
 -  String[] renderOrder;
 -
 -  PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
 -
 -  Vector allfeatures;
 +  AlignmentPanel ap;
  
    /**
     * Creates a new FeatureRenderer object.
@@@ -52,7 -82,6 +52,7 @@@
     */
    public FeatureRenderer(AlignmentPanel ap)
    {
 +    super();
      this.ap = ap;
      this.av = ap.av;
      if (ap != null && ap.seqPanel != null && ap.seqPanel.seqCanvas != null
      }
    }
  
 -  public class FeatureRendererSettings implements Cloneable
 -  {
 -    String[] renderOrder;
 -
 -    Map featureGroups;
 -
 -    Map featureColours;
 -
 -    float transparency;
 -
 -    Map featureOrder;
 -
 -    public FeatureRendererSettings(String[] renderOrder,
 -            Hashtable featureGroups, Hashtable featureColours,
 -            float transparency, Hashtable featureOrder)
 -    {
 -      super();
 -      this.renderOrder = renderOrder;
 -      this.featureGroups = featureGroups;
 -      this.featureColours = featureColours;
 -      this.transparency = transparency;
 -      this.featureOrder = featureOrder;
 -    }
 -
 -    /**
 -     * create an independent instance of the feature renderer settings
 -     * 
 -     * @param fr
 -     */
 -    public FeatureRendererSettings(FeatureRenderer fr)
 -    {
 -      renderOrder = null;
 -      featureGroups = new ConcurrentHashMap();
 -      featureColours = new ConcurrentHashMap();
 -      featureOrder = new ConcurrentHashMap();
 -      if (fr.renderOrder != null)
 -      {
 -        this.renderOrder = new String[fr.renderOrder.length];
 -        System.arraycopy(fr.renderOrder, 0, renderOrder, 0,
 -                fr.renderOrder.length);
 -      }
 -      if (fr.featureGroups != null)
 -      {
 -        this.featureGroups = new ConcurrentHashMap(fr.featureGroups);
 -      }
 -      if (fr.featureColours != null)
 -      {
 -        this.featureColours = new ConcurrentHashMap(fr.featureColours);
 -      }
 -      Iterator en = fr.featureColours.keySet().iterator();
 -      while (en.hasNext())
 -      {
 -        Object next = en.next();
 -        Object val = featureColours.get(next);
 -        if (val instanceof GraduatedColor)
 -        {
 -          featureColours
 -                  .put(next, new GraduatedColor((GraduatedColor) val));
 -        }
 -      }
 -      this.transparency = fr.transparency;
 -      if (fr.featureOrder != null)
 -      {
 -        this.featureOrder = new ConcurrentHashMap(fr.featureOrder);
 -      }
 -    }
 -  }
 -
 -  public FeatureRendererSettings getSettings()
 -  {
 -    return new FeatureRendererSettings(this);
 -  }
 -
 -  public void transferSettings(FeatureRendererSettings fr)
 -  {
 -    this.renderOrder = fr.renderOrder;
 -    this.featureGroups = fr.featureGroups;
 -    this.featureColours = fr.featureColours;
 -    this.transparency = fr.transparency;
 -    this.featureOrder = fr.featureOrder;
 -  }
 -
 -  /**
 -   * update from another feature renderer
 -   * 
 -   * @param fr
 -   *          settings to copy
 -   */
 -  public void transferSettings(FeatureRenderer fr)
 -  {
 -    FeatureRendererSettings frs = new FeatureRendererSettings(fr);
 -    this.renderOrder = frs.renderOrder;
 -    this.featureGroups = frs.featureGroups;
 -    this.featureColours = frs.featureColours;
 -    this.transparency = frs.transparency;
 -    this.featureOrder = frs.featureOrder;
 -    if (av != null && av != fr.av)
 -    {
 -      // copy over the displayed feature settings
 -      if (fr.av != null)
 -      {
 -        if (fr.av.featuresDisplayed != null)
 -        {
 -          // update display settings
 -          if (av.featuresDisplayed == null)
 -          {
 -            av.featuresDisplayed = new Hashtable(fr.av.featuresDisplayed);
 -          }
 -          else
 -          {
 -            av.featuresDisplayed.clear();
 -            Enumeration en = fr.av.featuresDisplayed.keys();
 -            while (en.hasMoreElements())
 -            {
 -              av.featuresDisplayed.put(en.nextElement(), Boolean.TRUE);
 -            }
 -
 -          }
 -        }
 -      }
 -    }
 -  }
 -
 -  BufferedImage offscreenImage;
 -
 -  boolean offscreenRender = false;
 -
 -  public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
 -  {
 -    return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
 -  }
 -
 -  /**
 -   * This is used by the Molecule Viewer and Overview to get the accurate
 -   * colourof the rendered sequence
 -   */
 -  public synchronized int findFeatureColour(int initialCol, SequenceI seq,
 -          int column)
 -  {
 -    if (!av.showSequenceFeatures)
 -    {
 -      return initialCol;
 -    }
 -
 -    if (seq != lastSeq)
 -    {
 -      lastSeq = seq;
 -      sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
 -      if (sequenceFeatures != null)
 -      {
 -        sfSize = sequenceFeatures.length;
 -      }
 -    }
 -
 -    if (sequenceFeatures != lastSeq.getDatasetSequence()
 -            .getSequenceFeatures())
 -    {
 -      sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
 -      if (sequenceFeatures != null)
 -      {
 -        sfSize = sequenceFeatures.length;
 -      }
 -    }
 -
 -    if (sequenceFeatures == null || sfSize == 0)
 -    {
 -      return initialCol;
 -    }
 -
 -    if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
 -    {
 -      return Color.white.getRGB();
 -    }
 -
 -    // Only bother making an offscreen image if transparency is applied
 -    if (transparency != 1.0f && offscreenImage == null)
 -    {
 -      offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
 -    }
 -
 -    currentColour = null;
 -    // TODO: non-threadsafe - each rendering thread needs its own instance of
 -    // the feature renderer - or this should be synchronized.
 -    offscreenRender = true;
 -
 -    if (offscreenImage != null)
 -    {
 -      offscreenImage.setRGB(0, 0, initialCol);
 -      drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
 -
 -      return offscreenImage.getRGB(0, 0);
 -    }
 -    else
 -    {
 -      drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
 -
 -      if (currentColour == null)
 -      {
 -        return initialCol;
 -      }
 -      else
 -      {
 -        return ((Integer) currentColour).intValue();
 -      }
 -    }
 -
 -  }
 -
 -  /**
 -   * DOCUMENT ME!
 -   * 
 -   * @param g
 -   *          DOCUMENT ME!
 -   * @param seq
 -   *          DOCUMENT ME!
 -   * @param sg
 -   *          DOCUMENT ME!
 -   * @param start
 -   *          DOCUMENT ME!
 -   * @param end
 -   *          DOCUMENT ME!
 -   * @param x1
 -   *          DOCUMENT ME!
 -   * @param y1
 -   *          DOCUMENT ME!
 -   * @param width
 -   *          DOCUMENT ME!
 -   * @param height
 -   *          DOCUMENT ME!
 -   */
 -  // String type;
 -  // SequenceFeature sf;
 -  SequenceI lastSeq;
 -
 -  SequenceFeature[] sequenceFeatures;
 -
 -  int sfSize, sfindex, spos, epos;
 -
 -  /**
 -   * show scores as heights
 -   */
 -  protected boolean varyHeight = false;
 -
 -  synchronized public void drawSequence(Graphics g, SequenceI seq,
 -          int start, int end, int y1)
 -  {
 -
 -    if (seq.getDatasetSequence().getSequenceFeatures() == null
 -            || seq.getDatasetSequence().getSequenceFeatures().length == 0)
 -    {
 -      return;
 -    }
 -
 -    if (g != null)
 -    {
 -      fm = g.getFontMetrics();
 -    }
 -
 -    if (av.featuresDisplayed == null || renderOrder == null
 -            || newFeatureAdded)
 -    {
 -      findAllFeatures();
 -      if (av.featuresDisplayed.size() < 1)
 -      {
 -        return;
 -      }
 -
 -      sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
 -    }
 -
 -    if (lastSeq == null
 -            || seq != lastSeq
 -            || seq.getDatasetSequence().getSequenceFeatures() != sequenceFeatures)
 -    {
 -      lastSeq = seq;
 -      sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
 -    }
 -
 -    if (transparency != 1 && g != null)
 -    {
 -      Graphics2D g2 = (Graphics2D) g;
 -      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
 -              transparency));
 -    }
 -
 -    if (!offscreenRender)
 -    {
 -      spos = lastSeq.findPosition(start);
 -      epos = lastSeq.findPosition(end);
 -    }
 -
 -    sfSize = sequenceFeatures.length;
 -    String type;
 -    for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
 -    {
 -      type = renderOrder[renderIndex];
 -
 -      if (type == null || !av.featuresDisplayed.containsKey(type))
 -      {
 -        continue;
 -      }
 -
 -      // loop through all features in sequence to find
 -      // current feature to render
 -      for (sfindex = 0; sfindex < sfSize; sfindex++)
 -      {
 -        if (!sequenceFeatures[sfindex].type.equals(type))
 -        {
 -          continue;
 -        }
 -
 -        if (featureGroups != null
 -                && sequenceFeatures[sfindex].featureGroup != null
 -                && sequenceFeatures[sfindex].featureGroup.length() != 0
 -                && featureGroups
 -                        .containsKey(sequenceFeatures[sfindex].featureGroup)
 -                && !((Boolean) featureGroups
 -                        .get(sequenceFeatures[sfindex].featureGroup))
 -                        .booleanValue())
 -        {
 -          continue;
 -        }
 -
 -        if (!offscreenRender
 -                && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
 -                        .getEnd() < spos))
 -        {
 -          continue;
 -        }
 -
 -        if (offscreenRender && offscreenImage == null)
 -        {
 -          if (sequenceFeatures[sfindex].begin <= start
 -                  && sequenceFeatures[sfindex].end >= start)
 -          {
 -            // this is passed out to the overview and other sequence renderers
 -            // (e.g. molecule viewer) to get displayed colour for rendered
 -            // sequence
 -            currentColour = new Integer(
 -                    getColour(sequenceFeatures[sfindex]).getRGB());
 -            // used to be retreived from av.featuresDisplayed
 -            // currentColour = av.featuresDisplayed
 -            // .get(sequenceFeatures[sfindex].type);
 -
 -          }
 -        }
 -        else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
 -        {
 -
 -          renderFeature(g, seq,
 -                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
 -                  seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
 -                  getColour(sequenceFeatures[sfindex])
 -                  // new Color(((Integer) av.featuresDisplayed
 -                  // .get(sequenceFeatures[sfindex].type)).intValue())
 -                  , start, end, y1);
 -          renderFeature(g, seq,
 -                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
 -                  seq.findIndex(sequenceFeatures[sfindex].end) - 1,
 -                  getColour(sequenceFeatures[sfindex])
 -                  // new Color(((Integer) av.featuresDisplayed
 -                  // .get(sequenceFeatures[sfindex].type)).intValue())
 -                  , start, end, y1);
 -
 -        }
 -        else if (showFeature(sequenceFeatures[sfindex]))
 -        {
 -          if (av.showSeqFeaturesHeight
 -                  && sequenceFeatures[sfindex].score != Float.NaN)
 -          {
 -            renderScoreFeature(g, seq,
 -                    seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
 -                    seq.findIndex(sequenceFeatures[sfindex].end) - 1,
 -                    getColour(sequenceFeatures[sfindex]), start, end, y1,
 -                    normaliseScore(sequenceFeatures[sfindex]));
 -          }
 -          else
 -          {
 -            renderFeature(g, seq,
 -                    seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
 -                    seq.findIndex(sequenceFeatures[sfindex].end) - 1,
 -                    getColour(sequenceFeatures[sfindex]), start, end, y1);
 -          }
 -        }
 -
 -      }
 -
 -    }
 -
 -    if (transparency != 1.0f && g != null)
 -    {
 -      Graphics2D g2 = (Graphics2D) g;
 -      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
 -              1.0f));
 -    }
 -  }
 -
 -  Hashtable minmax = new Hashtable();
 -
 -  /**
 -   * normalise a score against the max/min bounds for the feature type.
 -   * 
 -   * @param sequenceFeature
 -   * @return byte[] { signed, normalised signed (-127 to 127) or unsigned
 -   *         (0-255) value.
 -   */
 -  private final byte[] normaliseScore(SequenceFeature sequenceFeature)
 -  {
 -    float[] mm = ((float[][]) minmax.get(sequenceFeature.type))[0];
 -    final byte[] r = new byte[]
 -    { 0, (byte) 255 };
 -    if (mm != null)
 -    {
 -      if (r[0] != 0 || mm[0] < 0.0)
 -      {
 -        r[0] = 1;
 -        r[1] = (byte) ((int) 128.0 + 127.0 * (sequenceFeature.score / mm[1]));
 -      }
 -      else
 -      {
 -        r[1] = (byte) ((int) 255.0 * (sequenceFeature.score / mm[1]));
 -      }
 -    }
 -    return r;
 -  }
 -
 -  char s;
 -
 -  int i;
 -
 -  void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
 -          Color featureColour, int start, int end, int y1)
 -  {
 -
 -    if (((fstart <= end) && (fend >= start)))
 -    {
 -      if (fstart < start)
 -      { // fix for if the feature we have starts before the sequence start,
 -        fstart = start; // but the feature end is still valid!!
 -      }
 -
 -      if (fend >= end)
 -      {
 -        fend = end;
 -      }
 -      int pady = (y1 + av.charHeight) - av.charHeight / 5;
 -      for (i = fstart; i <= fend; i++)
 -      {
 -        s = seq.getCharAt(i);
 -
 -        if (jalview.util.Comparison.isGap(s))
 -        {
 -          continue;
 -        }
 -
 -        g.setColor(featureColour);
 -
 -        g.fillRect((i - start) * av.charWidth, y1, av.charWidth,
 -                av.charHeight);
 -
 -        if (offscreenRender || !av.validCharWidth)
 -        {
 -          continue;
 -        }
 -
 -        g.setColor(Color.white);
 -        charOffset = (av.charWidth - fm.charWidth(s)) / 2;
 -        g.drawString(String.valueOf(s), charOffset
 -                + (av.charWidth * (i - start)), pady);
 -
 -      }
 -    }
 -  }
 -
 -  void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
 -          Color featureColour, int start, int end, int y1, byte[] bs)
 -  {
 -
 -    if (((fstart <= end) && (fend >= start)))
 -    {
 -      if (fstart < start)
 -      { // fix for if the feature we have starts before the sequence start,
 -        fstart = start; // but the feature end is still valid!!
 -      }
 -
 -      if (fend >= end)
 -      {
 -        fend = end;
 -      }
 -      int pady = (y1 + av.charHeight) - av.charHeight / 5;
 -      int ystrt = 0, yend = av.charHeight;
 -      if (bs[0] != 0)
 -      {
 -        // signed - zero is always middle of residue line.
 -        if (bs[1] < 128)
 -        {
 -          yend = av.charHeight * (128 - bs[1]) / 512;
 -          ystrt = av.charHeight - yend / 2;
 -        }
 -        else
 -        {
 -          ystrt = av.charHeight / 2;
 -          yend = av.charHeight * (bs[1] - 128) / 512;
 -        }
 -      }
 -      else
 -      {
 -        yend = av.charHeight * bs[1] / 255;
 -        ystrt = av.charHeight - yend;
 -
 -      }
 -      for (i = fstart; i <= fend; i++)
 -      {
 -        s = seq.getCharAt(i);
 -
 -        if (jalview.util.Comparison.isGap(s))
 -        {
 -          continue;
 -        }
 -
 -        g.setColor(featureColour);
 -        int x = (i - start) * av.charWidth;
 -        g.drawRect(x, y1, av.charWidth, av.charHeight);
 -        g.fillRect(x, y1 + ystrt, av.charWidth, yend);
 -
 -        if (offscreenRender || !av.validCharWidth)
 -        {
 -          continue;
 -        }
 -
 -        g.setColor(Color.black);
 -        charOffset = (av.charWidth - fm.charWidth(s)) / 2;
 -        g.drawString(String.valueOf(s), charOffset
 -                + (av.charWidth * (i - start)), pady);
 -
 -      }
 -    }
 -  }
 -
 -  boolean newFeatureAdded = false;
 -
 -  /**
 -   * Called when alignment in associated view has new/modified features to
 -   * discover and display.
 -   * 
 -   */
 -  public void featuresAdded()
 -  {
 -    lastSeq = null;
 -    findAllFeatures();
 -  }
 -
 -  boolean findingFeatures = false;
 -
 -  /**
 -   * search the alignment for all new features, give them a colour and display
 -   * them. Then fires a PropertyChangeEvent on the changeSupport object.
 -   * 
 -   */
 -  void findAllFeatures()
 -  {
 -    synchronized (firing)
 -    {
 -      if (firing.equals(Boolean.FALSE))
 -      {
 -        firing = Boolean.TRUE;
 -        findAllFeatures(true); // add all new features as visible
 -        changeSupport.firePropertyChange("changeSupport", null, null);
 -        firing = Boolean.FALSE;
 -      }
 -    }
 -  }
 -
 -  /**
 -   * Searches alignment for all features and updates colours
 -   * 
 -   * @param newMadeVisible
 -   *          if true newly added feature types will be rendered immediatly
 -   */
 -  synchronized void findAllFeatures(boolean newMadeVisible)
 -  {
 -    newFeatureAdded = false;
 -
 -    if (findingFeatures)
 -    {
 -      newFeatureAdded = true;
 -      return;
 -    }
 -
 -    findingFeatures = true;
 -
 -    if (av.featuresDisplayed == null)
 -    {
 -      av.featuresDisplayed = new Hashtable();
 -    }
 -
 -    allfeatures = new Vector();
 -    Vector oldfeatures = new Vector();
 -    if (renderOrder != null)
 -    {
 -      for (int i = 0; i < renderOrder.length; i++)
 -      {
 -        if (renderOrder[i] != null)
 -        {
 -          oldfeatures.addElement(renderOrder[i]);
 -        }
 -      }
 -    }
 -    if (minmax == null)
 -    {
 -      minmax = new Hashtable();
 -    }
 -    AlignmentI alignment = av.getAlignment();
 -    for (int i = 0; i < alignment.getHeight(); i++)
 -    {
 -      SequenceFeature[] features = alignment.getSequenceAt(i)
 -              .getDatasetSequence().getSequenceFeatures();
 -
 -      if (features == null)
 -      {
 -        continue;
 -      }
 -
 -      int index = 0;
 -      while (index < features.length)
 -      {
 -        if (!av.featuresDisplayed.containsKey(features[index].getType()))
 -        {
 -
 -          if (featureGroups.containsKey(features[index].getType()))
 -          {
 -            boolean visible = ((Boolean) featureGroups
 -                    .get(features[index].featureGroup)).booleanValue();
 -
 -            if (!visible)
 -            {
 -              index++;
 -              continue;
 -            }
 -          }
 -
 -          if (!(features[index].begin == 0 && features[index].end == 0))
 -          {
 -            // If beginning and end are 0, the feature is for the whole sequence
 -            // and we don't want to render the feature in the normal way
 -
 -            if (newMadeVisible
 -                    && !oldfeatures.contains(features[index].getType()))
 -            {
 -              // this is a new feature type on the alignment. Mark it for
 -              // display.
 -              av.featuresDisplayed.put(features[index].getType(),
 -                      new Integer(getColour(features[index].getType())
 -                              .getRGB()));
 -              setOrder(features[index].getType(), 0);
 -            }
 -          }
 -        }
 -        if (!allfeatures.contains(features[index].getType()))
 -        {
 -          allfeatures.addElement(features[index].getType());
 -        }
 -        if (features[index].score != Float.NaN)
 -        {
 -          int nonpos = features[index].getBegin() >= 1 ? 0 : 1;
 -          float[][] mm = (float[][]) minmax.get(features[index].getType());
 -          if (mm == null)
 -          {
 -            mm = new float[][]
 -            { null, null };
 -            minmax.put(features[index].getType(), mm);
 -          }
 -          if (mm[nonpos] == null)
 -          {
 -            mm[nonpos] = new float[]
 -            { features[index].score, features[index].score };
 -
 -          }
 -          else
 -          {
 -            if (mm[nonpos][0] > features[index].score)
 -            {
 -              mm[nonpos][0] = features[index].score;
 -            }
 -            if (mm[nonpos][1] < features[index].score)
 -            {
 -              mm[nonpos][1] = features[index].score;
 -            }
 -          }
 -        }
 -        index++;
 -      }
 -    }
 -    updateRenderOrder(allfeatures);
 -    findingFeatures = false;
 -  }
 -
 -  protected Boolean firing = Boolean.FALSE;
 -
 -  /**
 -   * replaces the current renderOrder with the unordered features in
 -   * allfeatures. The ordering of any types in both renderOrder and allfeatures
 -   * is preserved, and all new feature types are rendered on top of the existing
 -   * types, in the order given by getOrder or the order given in allFeatures.
 -   * Note. this operates directly on the featureOrder hash for efficiency. TODO:
 -   * eliminate the float storage for computing/recalling the persistent ordering
 -   * New Cability: updates min/max for colourscheme range if its dynamic
 -   * 
 -   * @param allFeatures
 -   */
 -  private void updateRenderOrder(Vector allFeatures)
 -  {
 -    Vector allfeatures = new Vector(allFeatures);
 -    String[] oldRender = renderOrder;
 -    renderOrder = new String[allfeatures.size()];
 -    Object mmrange, fc = null;
 -    boolean initOrders = (featureOrder == null);
 -    int opos = 0;
 -    if (oldRender != null && oldRender.length > 0)
 -    {
 -      for (int j = 0; j < oldRender.length; j++)
 -      {
 -        if (oldRender[j] != null)
 -        {
 -          if (initOrders)
 -          {
 -            setOrder(oldRender[j], (1 - (1 + (float) j)
 -                    / (float) oldRender.length));
 -          }
 -          if (allfeatures.contains(oldRender[j]))
 -          {
 -            renderOrder[opos++] = oldRender[j]; // existing features always
 -            // appear below new features
 -            allfeatures.removeElement(oldRender[j]);
 -            if (minmax != null)
 -            {
 -              mmrange = minmax.get(oldRender[j]);
 -              if (mmrange != null)
 -              {
 -                fc = featureColours.get(oldRender[j]);
 -                if (fc != null && fc instanceof GraduatedColor
 -                        && ((GraduatedColor) fc).isAutoScale())
 -                {
 -                  ((GraduatedColor) fc).updateBounds(
 -                          ((float[][]) mmrange)[0][0],
 -                          ((float[][]) mmrange)[0][1]);
 -                }
 -              }
 -            }
 -          }
 -        }
 -      }
 -    }
 -    if (allfeatures.size() == 0)
 -    {
 -      // no new features - leave order unchanged.
 -      return;
 -    }
 -    int i = allfeatures.size() - 1;
 -    int iSize = i;
 -    boolean sort = false;
 -    String[] newf = new String[allfeatures.size()];
 -    float[] sortOrder = new float[allfeatures.size()];
 -    Enumeration en = allfeatures.elements();
 -    // sort remaining elements
 -    while (en.hasMoreElements())
 -    {
 -      newf[i] = en.nextElement().toString();
 -      if (minmax != null)
 -      {
 -        // update from new features minmax if necessary
 -        mmrange = minmax.get(newf[i]);
 -        if (mmrange != null)
 -        {
 -          fc = featureColours.get(newf[i]);
 -          if (fc != null && fc instanceof GraduatedColor
 -                  && ((GraduatedColor) fc).isAutoScale())
 -          {
 -            ((GraduatedColor) fc).updateBounds(((float[][]) mmrange)[0][0],
 -                    ((float[][]) mmrange)[0][1]);
 -          }
 -        }
 -      }
 -      if (initOrders || !featureOrder.containsKey(newf[i]))
 -      {
 -        int denom = initOrders ? allfeatures.size() : featureOrder.size();
 -        // new unordered feature - compute persistent ordering at head of
 -        // existing features.
 -        setOrder(newf[i], i / (float) denom);
 -      }
 -      // set order from newly found feature from persisted ordering.
 -      sortOrder[i] = 2 - ((Float) featureOrder.get(newf[i])).floatValue();
 -      if (i < iSize)
 -      {
 -        // only sort if we need to
 -        sort = sort || sortOrder[i] > sortOrder[i + 1];
 -      }
 -      i--;
 -    }
 -    if (iSize > 1 && sort)
 -    {
 -      jalview.util.QuickSort.sort(sortOrder, newf);
 -    }
 -    sortOrder = null;
 -    System.arraycopy(newf, 0, renderOrder, opos, newf.length);
 -  }
 -
 -  /**
 -   * get a feature style object for the given type string. Creates a
 -   * java.awt.Color for a featureType with no existing colourscheme. TODO:
 -   * replace return type with object implementing standard abstract colour/style
 -   * interface
 -   * 
 -   * @param featureType
 -   * @return java.awt.Color or GraduatedColor
 -   */
 -  public Object getFeatureStyle(String featureType)
 -  {
 -    Object fc = featureColours.get(featureType);
 -    if (fc == null)
 -    {
 -      jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
 -      Color col = ucs.createColourFromName(featureType);
 -      featureColours.put(featureType, fc = col);
 -    }
 -    return fc;
 -  }
 -
 -  /**
 -   * return a nominal colour for this feature
 -   * 
 -   * @param featureType
 -   * @return standard color, or maximum colour for graduated colourscheme
 -   */
 -  public Color getColour(String featureType)
 -  {
 -    Object fc = getFeatureStyle(featureType);
 -
 -    if (fc instanceof Color)
 -    {
 -      return (Color) fc;
 -    }
 -    else
 -    {
 -      if (fc instanceof GraduatedColor)
 -      {
 -        return ((GraduatedColor) fc).getMaxColor();
 -      }
 -    }
 -    throw new Error(MessageManager.formatMessage("error.implementation_error_unrecognised_render_object_for_features_type", new String[]{fc.getClass().toString(),featureType}));
 -  }
 -
 -  /**
 -   * calculate the render colour for a specific feature using current feature
 -   * settings.
 -   * 
 -   * @param feature
 -   * @return render colour for the given feature
 -   */
 -  public Color getColour(SequenceFeature feature)
 -  {
 -    Object fc = getFeatureStyle(feature.getType());
 -    if (fc instanceof Color)
 -    {
 -      return (Color) fc;
 -    }
 -    else
 -    {
 -      if (fc instanceof GraduatedColor)
 -      {
 -        return ((GraduatedColor) fc).findColor(feature);
 -      }
 -    }
 -    throw new Error(MessageManager.formatMessage("error.implementation_error_unrecognised_render_object_for_features_type", new String[]{fc.getClass().toString(),feature.getType()}));
 -  }
 -
 -  private boolean showFeature(SequenceFeature sequenceFeature)
 -  {
 -    Object fc = getFeatureStyle(sequenceFeature.type);
 -    if (fc instanceof GraduatedColor)
 -    {
 -      return ((GraduatedColor) fc).isColored(sequenceFeature);
 -    }
 -    else
 -    {
 -      return true;
 -    }
 -  }
 -
    // // /////////////
    // // Feature Editing Dialog
    // // Will be refactored in next release.
          if (fcol instanceof Color)
          {
            Color col = JColorChooser.showDialog(Desktop.desktop,
-                   "Select Feature Colour", ((Color) fcol));
+                   MessageManager.getString("label.select_feature_colour"), ((Color) fcol));
            if (col != null)
            {
              fcol = col;
        { "OK", "Cancel" };
      }
  
-     String title = newFeatures ? "Create New Sequence Feature(s)"
-             : "Amend/Delete Features for " + sequences[0].getName();
+     String title = newFeatures ? MessageManager.getString("label.create_new_sequence_features")
+             : MessageManager.formatMessage("label.amend_delete_features", new String[]{sequences[0].getName()});
  
      int reply = JOptionPane.showInternalOptionDialog(Desktop.desktop,
              bigPanel, title, JOptionPane.YES_NO_CANCEL_OPTION,
-             JOptionPane.QUESTION_MESSAGE, null, options, "OK");
+             JOptionPane.QUESTION_MESSAGE, null, options, MessageManager.getString("action.ok"));
  
      jalview.io.FeaturesFile ffile = new jalview.io.FeaturesFile();
  
          sf.description = lastDescriptionAdded;
  
          setColour(sf.type, fcol);
 -        av.featuresDisplayed.put(sf.type, getColour(sf.type));
 +        getFeaturesDisplayed().setVisible(sf.type);
  
          try
          {
          for (int i = 0; i < sequences.length; i++)
          {
            features[i].type = lastFeatureAdded;
 -          if (lastFeatureGroupAdded != null)
 -            features[i].featureGroup = lastFeatureGroupAdded;
 +          // fix for JAL-1538 - always set feature group here
 +          features[i].featureGroup = lastFeatureGroupAdded;
            features[i].description = lastDescriptionAdded;
            sequences[i].addSequenceFeature(features[i]);
            ffile.parseDescriptionHTML(features[i], false);
          }
  
 -        if (av.featuresDisplayed == null)
 -        {
 -          av.featuresDisplayed = new Hashtable();
 -        }
 -
 +        
          if (lastFeatureGroupAdded != null)
          {
 -          if (featureGroups == null)
 -            featureGroups = new Hashtable();
 -          featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
 +          setGroupVisibility(lastFeatureGroupAdded, true);
          }
          setColour(lastFeatureAdded, fcol);
 -        av.featuresDisplayed.put(lastFeatureAdded,
 -                getColour(lastFeatureAdded));
 +        setVisible(lastFeatureAdded);
  
          findAllFeatures(false);
  
      return true;
    }
  
 +
    /**
     * update the amend feature button dependent on the given style
     * 
        // colour.setForeground(colour.getBackground());
      }
    }
 -
 -  public void setColour(String featureType, Object col)
 -  {
 -    // overwrite
 -    // Color _col = (col instanceof Color) ? ((Color) col) : (col instanceof
 -    // GraduatedColor) ? ((GraduatedColor) col).getMaxColor() : null;
 -    // Object c = featureColours.get(featureType);
 -    // if (c == null || c instanceof Color || (c instanceof GraduatedColor &&
 -    // !((GraduatedColor)c).getMaxColor().equals(_col)))
 -    {
 -      featureColours.put(featureType, col);
 -    }
 -  }
 -
 -  public void setTransparency(float value)
 -  {
 -    transparency = value;
 -  }
 -
 -  public float getTransparency()
 -  {
 -    return transparency;
 -  }
 -
 -  /**
 -   * Replace current ordering with new ordering
 -   * 
 -   * @param data
 -   *          { String(Type), Colour(Type), Boolean(Displayed) }
 -   */
 -  public void setFeaturePriority(Object[][] data)
 -  {
 -    setFeaturePriority(data, true);
 -  }
 -
 -  /**
 -   * 
 -   * @param data
 -   *          { String(Type), Colour(Type), Boolean(Displayed) }
 -   * @param visibleNew
 -   *          when true current featureDisplay list will be cleared
 -   */
 -  public void setFeaturePriority(Object[][] data, boolean visibleNew)
 -  {
 -    if (visibleNew)
 -    {
 -      if (av.featuresDisplayed != null)
 -      {
 -        av.featuresDisplayed.clear();
 -      }
 -      else
 -      {
 -        av.featuresDisplayed = new Hashtable();
 -      }
 -    }
 -    if (data == null)
 -    {
 -      return;
 -    }
 -
 -    // The feature table will display high priority
 -    // features at the top, but theses are the ones
 -    // we need to render last, so invert the data
 -    renderOrder = new String[data.length];
 -
 -    if (data.length > 0)
 -    {
 -      for (int i = 0; i < data.length; i++)
 -      {
 -        String type = data[i][0].toString();
 -        setColour(type, data[i][1]); // todo : typesafety - feature color
 -        // interface object
 -        if (((Boolean) data[i][2]).booleanValue())
 -        {
 -          av.featuresDisplayed.put(type, new Integer(getColour(type)
 -                  .getRGB()));
 -        }
 -
 -        renderOrder[data.length - i - 1] = type;
 -      }
 -    }
 -
 -  }
 -
 -  Map featureOrder = null;
 -
 -  /**
 -   * analogous to colour - store a normalized ordering for all feature types in
 -   * this rendering context.
 -   * 
 -   * @param type
 -   *          Feature type string
 -   * @param position
 -   *          normalized priority - 0 means always appears on top, 1 means
 -   *          always last.
 -   */
 -  public float setOrder(String type, float position)
 -  {
 -    if (featureOrder == null)
 -    {
 -      featureOrder = new Hashtable();
 -    }
 -    featureOrder.put(type, new Float(position));
 -    return position;
 -  }
 -
 -  /**
 -   * get the global priority (0 (top) to 1 (bottom))
 -   * 
 -   * @param type
 -   * @return [0,1] or -1 for a type without a priority
 -   */
 -  public float getOrder(String type)
 -  {
 -    if (featureOrder != null)
 -    {
 -      if (featureOrder.containsKey(type))
 -      {
 -        return ((Float) featureOrder.get(type)).floatValue();
 -      }
 -    }
 -    return -1;
 -  }
 -
 -  /**
 -   * @param listener
 -   * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
 -   */
 -  public void addPropertyChangeListener(PropertyChangeListener listener)
 -  {
 -    changeSupport.addPropertyChangeListener(listener);
 -  }
 -
 -  /**
 -   * @param listener
 -   * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
 -   */
 -  public void removePropertyChangeListener(PropertyChangeListener listener)
 -  {
 -    changeSupport.removePropertyChangeListener(listener);
 -  }
  }
   */
  package jalview.gui;
  
- import java.io.*;
- import java.util.*;
- import java.util.List;
- import java.awt.*;
- import java.awt.event.*;
- import java.beans.PropertyChangeEvent;
- import java.beans.PropertyChangeListener;
- import javax.swing.*;
- import javax.swing.event.*;
- import javax.swing.table.*;
--import jalview.analysis.AlignmentSorter;
- import jalview.api.FeaturesDisplayedI;
  import jalview.bin.Cache;
--import jalview.commands.OrderCommand;
- import jalview.datamodel.*;
- import jalview.io.*;
 -import jalview.datamodel.AlignmentI;
+ import jalview.datamodel.SequenceFeature;
 -import jalview.datamodel.SequenceGroup;
+ import jalview.datamodel.SequenceI;
+ import jalview.io.JalviewFileChooser;
  import jalview.schemes.AnnotationColourGradient;
  import jalview.schemes.GraduatedColor;
  import jalview.util.MessageManager;
  import jalview.ws.dbsources.das.api.jalviewSourceI;
  
+ import java.awt.BorderLayout;
+ import java.awt.Color;
+ import java.awt.Component;
+ import java.awt.Font;
+ import java.awt.Graphics;
+ import java.awt.GridLayout;
+ import java.awt.Rectangle;
+ import java.awt.event.ActionEvent;
+ import java.awt.event.ActionListener;
+ import java.awt.event.ItemEvent;
+ import java.awt.event.ItemListener;
+ import java.awt.event.MouseAdapter;
+ import java.awt.event.MouseEvent;
+ import java.awt.event.MouseMotionAdapter;
+ import java.beans.PropertyChangeEvent;
+ import java.beans.PropertyChangeListener;
+ import java.io.File;
+ import java.io.FileInputStream;
+ import java.io.FileOutputStream;
+ import java.io.InputStreamReader;
+ import java.io.OutputStreamWriter;
+ import java.io.PrintWriter;
 -import java.util.ArrayList;
+ import java.util.Hashtable;
+ import java.util.Iterator;
+ import java.util.List;
++import java.util.Set;
+ import java.util.Vector;
+ import javax.swing.AbstractCellEditor;
+ import javax.swing.BorderFactory;
+ import javax.swing.Icon;
+ import javax.swing.JButton;
+ import javax.swing.JCheckBox;
+ import javax.swing.JCheckBoxMenuItem;
+ import javax.swing.JColorChooser;
+ import javax.swing.JDialog;
+ import javax.swing.JInternalFrame;
+ import javax.swing.JLabel;
+ import javax.swing.JLayeredPane;
+ import javax.swing.JMenuItem;
+ import javax.swing.JOptionPane;
+ import javax.swing.JPanel;
+ import javax.swing.JPopupMenu;
+ import javax.swing.JScrollPane;
+ import javax.swing.JSlider;
+ import javax.swing.JTabbedPane;
+ import javax.swing.JTable;
+ import javax.swing.ListSelectionModel;
+ import javax.swing.SwingConstants;
+ import javax.swing.SwingUtilities;
+ import javax.swing.event.ChangeEvent;
+ import javax.swing.event.ChangeListener;
+ import javax.swing.table.AbstractTableModel;
+ import javax.swing.table.TableCellEditor;
+ import javax.swing.table.TableCellRenderer;
  public class FeatureSettings extends JPanel
  {
    DasSourceBrowser dassourceBrowser;
  
    Object[][] originalData;
  
 +  private float originalTransparency;
 +
    final JInternalFrame frame;
  
    JScrollPane scrollPane = new JScrollPane();
    {
      this.af = af;
      fr = af.getFeatureRenderer();
 -
 -    transparency.setMaximum(100 - (int) (fr.transparency * 100));
 +    // allow transparency to be recovered
 +    transparency.setMaximum(100 - (int) ((originalTransparency=fr.getTransparency()) * 100));
  
      try
      {
        public void mousePressed(MouseEvent evt)
        {
          selectedRow = table.rowAtPoint(evt.getPoint());
-         if (evt.isPopupTrigger())
+         if (SwingUtilities.isRightMouseButton(evt))
          {
            popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
 -                  table.getValueAt(selectedRow, 1), fr.minmax, evt.getX(),
 -                  evt.getY());
 +                  table.getValueAt(selectedRow, 1), fr.getMinMax(),
 +                  evt.getX(), evt.getY());
          }
          else if (evt.getClickCount() == 2)
          {
                    (String) table.getValueAt(selectedRow, 0));
          }
        }
+       // isPopupTrigger fires on mouseReleased on Mac
+       @Override
+       public void mouseReleased(MouseEvent evt)
+       {
+         selectedRow = table.rowAtPoint(evt.getPoint());
+         if (evt.isPopupTrigger())
+         {
+           popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
 -                  table.getValueAt(selectedRow, 1), fr.minmax, evt.getX(),
++                  table.getValueAt(selectedRow, 1), fr.getMinMax(),
++                  evt.getX(),
+                   evt.getY());
+         }
+       }
      });
  
      table.addMouseMotionListener(new MouseMotionAdapter()
          }
        }
      });
-     table.setToolTipText("<html>"
-             + JvSwingUtils
-                     .wrapTooltip("Click/drag feature types up or down to change render order.<br/>Double click to select columns containing feature in alignment/current selection<br/>Pressing Alt will select columns outside features rather than inside<br/>Pressing Shift to modify current selection (rather than clear current selection)<br/>Press CTRL or Command/Meta to toggle columns in/outside features<br/>")
-             + "</html>");
+     table.setToolTipText(JvSwingUtils
+                     .wrapTooltip(true, MessageManager.getString("label.feature_settings_click_drag")));
      scrollPane.setViewportView(table);
  
      dassourceBrowser = new DasSourceBrowser(this);
      dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
  
 -    if (af.getViewport().featuresDisplayed == null
 -            || fr.renderOrder == null)
 +    if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
      {
        fr.findAllFeatures(true); // display everything!
      }
  
        public void actionPerformed(ActionEvent e)
        {
 -        me.sortByScore(new String[]
 +        me.af.avc.sortAlignmentByFeatureScore(new String[]
          { type });
        }
  
  
        public void actionPerformed(ActionEvent e)
        {
 -        me.sortByDens(new String[]
 +        me.af.avc.sortAlignmentByFeatureDensity(new String[]
          { type });
        }
  
  
    synchronized public void setTableData()
    {
 -    if (fr.featureGroups == null)
 -    {
 -      fr.featureGroups = new Hashtable();
 -    }
      Vector allFeatures = new Vector();
      Vector allGroups = new Vector();
      SequenceFeature[] tmpfeatures;
            if (!allGroups.contains(group))
            {
              allGroups.addElement(group);
 -            if (group != null)
 -            {
 -              checkGroupState(group);
 -            }
 +            checkGroupState(group);
            }
          }
  
    }
  
    /**
 +   * Synchronise gui group list and check visibility of group
     * 
     * @param group
 -   * @return true if group has been seen before and is already added to set.
 +   * @return true if group is visible
     */
    private boolean checkGroupState(String group)
    {
 -    boolean visible;
 -    if (fr.featureGroups.containsKey(group))
 -    {
 -      visible = ((Boolean) fr.featureGroups.get(group)).booleanValue();
 -    }
 -    else
 -    {
 -      visible = true; // new group is always made visible
 -    }
 +    boolean visible = fr.checkGroupVisibility(group, true);
  
      if (groupPanel == null)
      {
      if (alreadyAdded)
      {
  
 -      return true;
 +      return visible;
      }
 -
 -    fr.featureGroups.put(group, new Boolean(visible));
      final String grp = group;
      final JCheckBox check = new JCheckBox(group, visible);
      check.setFont(new Font("Serif", Font.BOLD, 12));
      {
        public void itemStateChanged(ItemEvent evt)
        {
 -        fr.featureGroups.put(check.getText(),
 -                new Boolean(check.isSelected()));
 +        fr.setGroupVisibility(check.getText(), check.isSelected());
          af.alignPanel.seqPanel.seqCanvas.repaint();
          if (af.alignPanel.overviewPanel != null)
          {
        }
      });
      groupPanel.add(check);
 -    return false;
 +    return visible;
    }
  
    boolean resettingTable = false;
            continue;
          }
  
 -        if (group == null || fr.featureGroups.get(group) == null
 -                || ((Boolean) fr.featureGroups.get(group)).booleanValue())
 +        if (group == null || checkGroupState(group))
          {
 -          if (group != null)
 -            checkGroupState(group);
            type = tmpfeatures[index].getType();
            if (!visibleChecks.contains(type))
            {
      Object[][] data = new Object[fSize][3];
      int dataIndex = 0;
  
 -    if (fr.renderOrder != null)
 +    if (fr.hasRenderOrder())
      {
        if (!handlingUpdate)
 +      {
          fr.findAllFeatures(groupChanged != null); // prod to update
 -      // colourschemes. but don't
 -      // affect display
 -      // First add the checks in the previous render order,
 -      // in case the window has been closed and reopened
 -      for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
 +        // colourschemes. but don't
 +        // affect display
 +        // First add the checks in the previous render order,
 +        // in case the window has been closed and reopened
 +      }
 +      List<String> frl = fr.getRenderOrder();
 +      for (int ro = frl.size() - 1; ro > -1; ro--)
        {
 -        type = fr.renderOrder[ro];
 +        type = frl.get(ro);
  
          if (!visibleChecks.contains(type))
          {
  
          data[dataIndex][0] = type;
          data[dataIndex][1] = fr.getFeatureStyle(type);
 -        data[dataIndex][2] = new Boolean(
 -                af.getViewport().featuresDisplayed.containsKey(type));
 +        data[dataIndex][2] = new Boolean(af.getViewport()
 +                .getFeaturesDisplayed().isVisible(type));
          dataIndex++;
          visibleChecks.removeElement(type);
        }
        if (data[dataIndex][1] == null)
        {
          // "Colour has been updated in another view!!"
 -        fr.renderOrder = null;
 +        fr.clearRenderOrder();
          return;
        }
  
  
      if (groupPanel != null)
      {
 -      groupPanel.setLayout(new GridLayout(fr.featureGroups.size() / 4 + 1,
 -              4));
 +      groupPanel.setLayout(new GridLayout(
 +              fr.getFeatureGroupsSize() / 4 + 1, 4));
  
        groupPanel.validate();
        bigPanel.add(groupPanel, BorderLayout.NORTH);
      {
        order[i] = fr.getOrder(data[i][0].toString());
        if (order[i] < 0)
++      {
          order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
++      }
        if (i > 1)
++      {
          sort = sort || order[i - 1] > order[i];
++      }
      }
      if (sort)
++    {
        jalview.util.QuickSort.sort(order, data);
++    }
    }
  
    void load()
              { "fc" }, new String[]
              { "Sequence Feature Colours" }, "Sequence Feature Colours");
      chooser.setFileView(new jalview.io.JalviewFileView());
-     chooser.setDialogTitle("Load Feature Colours");
+     chooser.setDialogTitle(MessageManager.getString("label.load_feature_colours"));
      chooser.setToolTipText(MessageManager.getString("action.load"));
  
      int value = chooser.showOpenDialog(this);
                  file), "UTF-8");
  
          jalview.schemabinding.version2.JalviewUserColours jucs = new jalview.schemabinding.version2.JalviewUserColours();
-         jucs = (jalview.schemabinding.version2.JalviewUserColours) jucs
+         jucs = jucs
                  .unmarshal(in);
  
          for (int i = jucs.getColourCount() - 1; i >= 0; i--)
              { "fc" }, new String[]
              { "Sequence Feature Colours" }, "Sequence Feature Colours");
      chooser.setFileView(new jalview.io.JalviewFileView());
-     chooser.setDialogTitle("Save Feature Colour Scheme");
+     chooser.setDialogTitle(MessageManager.getString("label.save_feature_colours"));
      chooser.setToolTipText(MessageManager.getString("action.save"));
  
      int value = chooser.showSaveDialog(this);
          PrintWriter out = new PrintWriter(new OutputStreamWriter(
                  new FileOutputStream(choice), "UTF-8"));
  
 -        Iterator e = fr.featureColours.keySet().iterator();
 -        float[] sortOrder = new float[fr.featureColours.size()];
 -        String[] sortTypes = new String[fr.featureColours.size()];
 +        Set fr_colours = fr.getAllFeatureColours();
 +        Iterator e = fr_colours.iterator();
 +        float[] sortOrder = new float[fr_colours.size()];
 +        String[] sortTypes = new String[fr_colours.size()];
          int i = 0;
          while (e.hasNext())
          {
    public void orderByAvWidth()
    {
      if (table == null || table.getModel() == null)
++    {
        return;
++    }
      Object[][] data = ((FeatureTableModel) table.getModel()).getData();
      float[] width = new float[data.length];
      float[] awidth;
          width[i] = 0;
        }
        if (max < width[i])
++      {
          max = width[i];
++      }
      }
      boolean sort = false;
      for (int i = 0; i < width.length; i++)
          fr.setOrder(data[i][0].toString(), width[i]); // store for later
        }
        if (i > 0)
++      {
          sort = sort || width[i - 1] > width[i];
++      }
      }
      if (sort)
++     {
        jalview.util.QuickSort.sort(width, data);
      // update global priority order
++    }
  
      updateFeatureRenderer(data, false);
      table.repaint();
      {
        public void actionPerformed(ActionEvent e)
        {
 -        sortByScore(null);
 +        af.avc.sortAlignmentByFeatureScore(null);
        }
      });
      sortByDens.setFont(JvSwingUtils.getLabelFont());
      {
        public void actionPerformed(ActionEvent e)
        {
 -        sortByDens(null);
 +        af.avc.sortAlignmentByFeatureDensity(null);
        }
      });
      cancel.setFont(JvSwingUtils.getLabelFont());
      {
        public void actionPerformed(ActionEvent e)
        {
 +        fr.setTransparency(originalTransparency);
          updateFeatureRenderer(originalData);
          close();
        }
      {
        public void stateChanged(ChangeEvent evt)
        {
-         fr.setTransparency((float) (100 - transparency.getValue()) / 100f);
+         fr.setTransparency((100 - transparency.getValue()) / 100f);
          af.alignPanel.paintAlignment(true);
        }
      });
        }
      });
      this.add(tabbedPane, java.awt.BorderLayout.CENTER);
-     tabbedPane.addTab("Feature Settings", settingsPane);
-     tabbedPane.addTab("DAS Settings", dasSettingsPane);
+     tabbedPane.addTab(MessageManager.getString("label.feature_settings"), settingsPane);
+     tabbedPane.addTab(MessageManager.getString("label.das_settings"), dasSettingsPane);
      bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
      transbuttons.add(optimizeOrder);
      transbuttons.add(invert);
      settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
    }
  
 -  protected void sortByDens(String[] typ)
 -  {
 -    sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
 -  }
 -
 -  protected void sortBy(String[] typ, String methodText, final String method)
 -  {
 -    if (typ == null)
 -    {
 -      typ = getDisplayedFeatureTypes();
 -    }
 -    String gps[] = null;
 -    gps = getDisplayedFeatureGroups();
 -    if (typ != null)
 -    {
 -      ArrayList types = new ArrayList();
 -      for (int i = 0; i < typ.length; i++)
 -      {
 -        if (typ[i] != null)
 -        {
 -          types.add(typ[i]);
 -        }
 -        typ = new String[types.size()];
 -        types.toArray(typ);
 -      }
 -    }
 -    if (gps != null)
 -    {
 -      ArrayList grps = new ArrayList();
 -
 -      for (int i = 0; i < gps.length; i++)
 -      {
 -        if (gps[i] != null)
 -        {
 -          grps.add(gps[i]);
 -        }
 -      }
 -      gps = new String[grps.size()];
 -      grps.toArray(gps);
 -    }
 -    AlignmentPanel alignPanel = af.alignPanel;
 -    AlignmentI al = alignPanel.av.getAlignment();
 -
 -    int start, stop;
 -    SequenceGroup sg = alignPanel.av.getSelectionGroup();
 -    if (sg != null)
 -    {
 -      start = sg.getStartRes();
 -      stop = sg.getEndRes();
 -    }
 -    else
 -    {
 -      start = 0;
 -      stop = al.getWidth();
 -    }
 -    SequenceI[] oldOrder = al.getSequencesArray();
 -    AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
 -    af.addHistoryItem(new OrderCommand(methodText, oldOrder, alignPanel.av
 -            .getAlignment()));
 -    alignPanel.paintAlignment(true);
 -
 -  }
 -
 -  protected void sortByScore(String[] typ)
 -  {
 -    sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
 -  }
 -
 -  private String[] getDisplayedFeatureTypes()
 -  {
 -    String[] typ = null;
 -    if (fr != null)
 -    {
 -      synchronized (fr.renderOrder)
 -      {
 -        typ = new String[fr.renderOrder.length];
 -        System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
 -        for (int i = 0; i < typ.length; i++)
 -        {
 -          if (af.viewport.featuresDisplayed.get(typ[i]) == null)
 -          {
 -            typ[i] = null;
 -          }
 -        }
 -      }
 -    }
 -    return typ;
 -  }
 -
 -  private String[] getDisplayedFeatureGroups()
 -  {
 -    String[] gps = null;
 -    ArrayList<String> _gps = new ArrayList<String>();
 -    if (fr != null)
 -    {
 -
 -      if (fr.featureGroups != null)
 -      {
 -        Iterator en = fr.featureGroups.keySet().iterator();
 -        int g = 0;
 -        boolean valid = false;
 -        while (en.hasNext())
 -        {
 -          String gp = (String) en.next();
 -          Boolean on = (Boolean) fr.featureGroups.get(gp);
 -          if (on != null && on.booleanValue())
 -          {
 -            valid = true;
 -            _gps.add(gp);
 -          }
 -        }
 -        if (!valid)
 -        {
 -          return null;
 -        }
 -        else
 -        {
 -          gps = new String[_gps.size()];
 -          _gps.toArray(gps);
 -        }
 -      }
 -    }
 -    return gps;
 -  }
 -
    public void fetchDAS_actionPerformed(ActionEvent e)
    {
      fetchDAS.setEnabled(false);
      }
  
      private String[] columnNames =
-     { "Feature Type", "Colour", "Display" };
+     { MessageManager.getString("label.feature_type"), MessageManager.getString("action.colour"), MessageManager.getString("label.display") };
  
      private Object[][] data;
  
   */
  package jalview.gui;
  
- import java.awt.*;
- import java.awt.event.*;
+ import jalview.datamodel.Sequence;
+ import jalview.datamodel.SequenceFeature;
+ import jalview.datamodel.SequenceGroup;
+ import jalview.datamodel.SequenceI;
+ import jalview.io.SequenceAnnotationReport;
+ import jalview.util.MessageManager;
+ import jalview.util.UrlLink;
+ import java.awt.BorderLayout;
+ import java.awt.event.MouseEvent;
+ import java.awt.event.MouseListener;
+ import java.awt.event.MouseMotionListener;
+ import java.awt.event.MouseWheelEvent;
+ import java.awt.event.MouseWheelListener;
  import java.util.List;
  import java.util.Vector;
  
- import javax.swing.*;
- import jalview.datamodel.*;
- import jalview.io.SequenceAnnotationReport;
- import jalview.util.UrlLink;
+ import javax.swing.JOptionPane;
+ import javax.swing.JPanel;
+ import javax.swing.SwingUtilities;
+ import javax.swing.ToolTipManager;
  
  /**
-  * DOCUMENT ME!
+  * This panel hosts alignment sequence ids and responds to mouse clicks on them,
+  * as well as highlighting ids matched by a search from the Find menu.
   * 
   * @author $author$
   * @version $Revision$
@@@ -63,9 -75,7 +75,7 @@@ public class IdPanel extends JPanel imp
     * Creates a new IdPanel object.
     * 
     * @param av
-    *          DOCUMENT ME!
     * @param parent
-    *          DOCUMENT ME!
     */
    public IdPanel(AlignViewport av, AlignmentPanel parent)
    {
@@@ -83,7 -93,8 +93,8 @@@
    }
  
    /**
-    * DOCUMENT ME!
+    * Respond to mouse movement by constructing tooltip text for the sequence id
+    * under the mouse.
     * 
     * @param e
     *          DOCUMENT ME!
      if (seq > -1 && seq < av.getAlignment().getHeight())
      {
        SequenceI sequence = av.getAlignment().getSequenceAt(seq);
-       StringBuffer tip = new StringBuffer();
+       StringBuffer tip = new StringBuffer(64);
        seqAnnotReport
                .createSequenceAnnotationReport(tip, sequence,
                        av.isShowDbRefs(), av.isShowNpFeats(),
 -                      sp.seqCanvas.fr.minmax);
 +                      sp.seqCanvas.fr.getMinMax());
        setToolTipText("<html>" + sequence.getDisplayId(true) + " "
                + tip.toString() + "</html>");
      }
    }
  
    /**
-    * DOCUMENT ME!
+    * Responds to a mouse drag by selecting the sequences under the dragged
+    * region.
     * 
     * @param e
-    *          DOCUMENT ME!
     */
    @Override
    public void mouseDragged(MouseEvent e)
      alignPanel.paintAlignment(true);
    }
  
+   /**
+    * Response to the mouse wheel by scrolling the alignment panel.
+    */
    @Override
    public void mouseWheelMoved(MouseWheelEvent e)
    {
        if (e.isShiftDown())
        {
          alignPanel.scrollRight(true);
        }
        else
        {
    }
  
    /**
-    * DOCUMENT ME!
+    * Handle a mouse click event. Currently only responds to a double-click. The
+    * action is to try to open a browser window at a URL that searches for the
+    * selected sequence id. The search URL is configured in Preferences |
+    * Connections | URL link from Sequence ID. For example:
+    * 
+    * http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$
     * 
     * @param e
-    *          DOCUMENT ME!
     */
    @Override
    public void mouseClicked(MouseEvent e)
    {
-     if (e.getClickCount() < 2)
+     /*
+      * Ignore single click. Ignore 'left' click followed by 'right' click (user
+      * selects a row then its pop-up menu).
+      */
+     if (e.getClickCount() < 2 || SwingUtilities.isRightMouseButton(e))
      {
        return;
      }
  
-     java.util.Vector links = Preferences.sequenceURLLinks;
+     Vector links = Preferences.sequenceURLLinks;
      if (links == null || links.size() < 1)
      {
        return;
        JOptionPane
                .showInternalMessageDialog(
                        Desktop.desktop,
-                       "Unixers: Couldn't find default web browser."
-                               + "\nAdd the full path to your browser in Preferences.",
-                       "Web browser not found", JOptionPane.WARNING_MESSAGE);
+                       MessageManager.getString("label.web_browser_not_found_unix"),
+                       MessageManager.getString("label.web_browser_not_found"), JOptionPane.WARNING_MESSAGE);
        ex.printStackTrace();
      }
    }
  
    /**
    }
  
    /**
-    * DOCUMENT ME!
+    * Respond to a mouse press. Does nothing for (left) double-click as this is
+    * handled by mouseClicked().
+    * 
+    * Right mouse down - construct and show context menu.
+    * 
+    * Ctrl-down or Shift-down - add to or expand current selection group if there
+    * is one.
+    * 
+    * Mouse down - select this sequence.
     * 
     * @param e
-    *          DOCUMENT ME!
     */
    @Override
    public void mousePressed(MouseEvent e)
    {
-     if (e.getClickCount() == 2)
+     if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e))
      {
        return;
      }
  
      int seq = alignPanel.seqPanel.findSeq(e);
  
-     if (javax.swing.SwingUtilities.isRightMouseButton(e))
+     if (SwingUtilities.isRightMouseButton(e))
      {
        Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq);
        // build a new links menu based on the current links + any non-positional
    }
  
    /**
-    * DOCUMENT ME!
+    * Toggle whether the sequence is part of the current selection group.
     * 
     * @param seq
-    *          DOCUMENT ME!
     */
    void selectSeq(int seq)
    {
    }
  
    /**
-    * DOCUMENT ME!
+    * Add contiguous rows of the alignment to the current selection group. Does
+    * nothing if there is no selection group.
     * 
     * @param start
-    *          DOCUMENT ME!
     * @param end
-    *          DOCUMENT ME!
     */
    void selectSeqs(int start, int end)
    {
    }
  
    /**
-    * DOCUMENT ME!
+    * Respond to mouse released. Refreshes the display and triggers broadcast of
+    * the new selection group to any listeners.
     * 
     * @param e
-    *          DOCUMENT ME!
     */
    @Override
    public void mouseReleased(MouseEvent e)
    }
  
    /**
-    * DOCUMENT ME!
+    * Highlight sequence ids that match the given list, and if necessary scroll
+    * to the start sequence of the list.
     * 
     * @param list
-    *          DOCUMENT ME!
     */
    public void highlightSearchResults(List<SequenceI> list)
    {
   */
  package jalview.gui;
  
- import java.awt.Rectangle;
- import java.io.*;
- import java.lang.reflect.InvocationTargetException;
- import java.net.*;
- import java.util.*;
- import java.util.Map.Entry;
- import java.util.jar.*;
- import javax.swing.*;
- import org.exolab.castor.xml.*;
+ import jalview.api.structures.JalviewStructureDisplayI;
  import jalview.bin.Cache;
  import jalview.datamodel.Alignment;
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.SequenceI;
- import jalview.schemabinding.version2.*;
- import jalview.schemes.*;
+ import jalview.schemabinding.version2.AlcodMap;
+ import jalview.schemabinding.version2.Alcodon;
+ import jalview.schemabinding.version2.AlcodonFrame;
+ import jalview.schemabinding.version2.Annotation;
+ import jalview.schemabinding.version2.AnnotationColours;
+ import jalview.schemabinding.version2.AnnotationElement;
+ import jalview.schemabinding.version2.CalcIdParam;
+ import jalview.schemabinding.version2.DBRef;
+ import jalview.schemabinding.version2.Features;
+ import jalview.schemabinding.version2.Group;
+ import jalview.schemabinding.version2.HiddenColumns;
+ import jalview.schemabinding.version2.JGroup;
+ import jalview.schemabinding.version2.JSeq;
+ import jalview.schemabinding.version2.JalviewModel;
+ import jalview.schemabinding.version2.JalviewModelSequence;
+ import jalview.schemabinding.version2.MapListFrom;
+ import jalview.schemabinding.version2.MapListTo;
+ import jalview.schemabinding.version2.Mapping;
+ import jalview.schemabinding.version2.MappingChoice;
+ import jalview.schemabinding.version2.OtherData;
+ import jalview.schemabinding.version2.PdbentryItem;
+ import jalview.schemabinding.version2.Pdbids;
+ import jalview.schemabinding.version2.Property;
+ import jalview.schemabinding.version2.Sequence;
+ import jalview.schemabinding.version2.SequenceSet;
+ import jalview.schemabinding.version2.SequenceSetProperties;
+ import jalview.schemabinding.version2.Setting;
+ import jalview.schemabinding.version2.StructureState;
+ import jalview.schemabinding.version2.ThresholdLine;
+ import jalview.schemabinding.version2.Tree;
+ import jalview.schemabinding.version2.UserColours;
+ import jalview.schemabinding.version2.Viewport;
+ import jalview.schemes.AnnotationColourGradient;
+ import jalview.schemes.ColourSchemeI;
+ import jalview.schemes.ColourSchemeProperty;
+ import jalview.schemes.GraduatedColor;
+ import jalview.schemes.ResidueColourScheme;
+ import jalview.schemes.ResidueProperties;
+ import jalview.structure.StructureSelectionManager;
+ import jalview.util.MessageManager;
  import jalview.util.Platform;
  import jalview.util.jarInputStreamProvider;
  import jalview.viewmodel.AlignmentViewport;
 +import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
 +import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
  import jalview.ws.jws2.Jws2Discoverer;
  import jalview.ws.jws2.dm.AAConSettings;
  import jalview.ws.jws2.jabaws2.Jws2Instance;
@@@ -51,6 -76,41 +78,41 @@@ import jalview.ws.params.ArgumentI
  import jalview.ws.params.AutoCalcSetting;
  import jalview.ws.params.WsParamSetI;
  
+ import java.awt.Rectangle;
+ import java.io.BufferedReader;
+ import java.io.DataInputStream;
+ import java.io.DataOutputStream;
+ import java.io.File;
+ import java.io.FileInputStream;
+ import java.io.FileOutputStream;
+ import java.io.IOException;
+ import java.io.InputStreamReader;
+ import java.io.OutputStreamWriter;
+ import java.io.PrintWriter;
+ import java.lang.reflect.InvocationTargetException;
+ import java.net.MalformedURLException;
+ import java.net.URL;
+ import java.util.ArrayList;
+ import java.util.Enumeration;
+ import java.util.HashSet;
+ import java.util.Hashtable;
+ import java.util.IdentityHashMap;
+ import java.util.Iterator;
+ import java.util.List;
+ import java.util.Map.Entry;
+ import java.util.Set;
+ import java.util.StringTokenizer;
+ import java.util.Vector;
+ import java.util.jar.JarEntry;
+ import java.util.jar.JarInputStream;
+ import java.util.jar.JarOutputStream;
+ import javax.swing.JInternalFrame;
+ import javax.swing.JOptionPane;
+ import javax.swing.SwingUtilities;
+ import org.exolab.castor.xml.Unmarshaller;
  /**
   * Write out the current jalview desktop state as a Jalview XML stream.
   * 
@@@ -438,7 -498,7 +500,7 @@@ public class Jalview2XM
      for (String dssids : dsses.keySet())
      {
        AlignFrame _af = dsses.get(dssids);
-       String jfileName = fileName + " Dataset for " + _af.getTitle();
+       String jfileName = MessageManager.formatMessage("label.dataset_for", new String[]{fileName,_af.getTitle()});
        if (!jfileName.endsWith(".xml"))
        {
          jfileName = jfileName + ".xml";
                                  .startsWith(
                                          jmol.jmb.pdbentry[peid].getId()
                                                  .toLowerCase())))
+                 {
                    continue;
+                 }
                  if (matchedFile == null)
                  {
                    matchedFile = jmol.jmb.pdbentry[peid].getFile();
        view.setShowColourText(av.getColourText());
        view.setShowFullId(av.getShowJVSuffix());
        view.setRightAlignIds(av.rightAlignIds);
 -      view.setShowSequenceFeatures(av.showSequenceFeatures);
 +      view.setShowSequenceFeatures(av.isShowSequenceFeatures());
        view.setShowText(av.getShowText());
        view.setShowUnconserved(av.getShowUnconserved());
        view.setWrapAlignment(av.getWrapAlignment());
        view.setFollowHighlight(av.followHighlight);
        view.setFollowSelection(av.followSelection);
        view.setIgnoreGapsinConsensus(av.getIgnoreGapsConsensus());
 -      if (av.featuresDisplayed != null)
 +      if (av.getFeaturesDisplayed() != null)
        {
          jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
  
 -        String[] renderOrder = ap.seqPanel.seqCanvas.getFeatureRenderer().renderOrder;
 +        String[] renderOrder = ap.seqPanel.seqCanvas.getFeatureRenderer()
 +                .getRenderOrder().toArray(new String[0]);
  
          Vector settingsAdded = new Vector();
          Object gstyle = null;
                        .getColour(renderOrder[ro]).getRGB());
              }
  
 -            setting.setDisplay(av.featuresDisplayed
 -                    .containsKey(renderOrder[ro]));
 +            setting.setDisplay(av.getFeaturesDisplayed().isVisible(
 +                    renderOrder[ro]));
              float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer()
                      .getOrder(renderOrder[ro]);
              if (rorder > -1)
          }
  
          // Make sure we save none displayed feature settings
 -        Iterator en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours
 -                .keySet().iterator();
 +        Iterator en = ap.seqPanel.seqCanvas.getFeatureRenderer()
 +                .getFeatureColours().keySet().iterator();
          while (en.hasNext())
          {
            String key = en.next().toString();
            fs.addSetting(setting);
            settingsAdded.addElement(key);
          }
 -        en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups
 -                .keySet().iterator();
 +        // is groups actually supposed to be a map here ?
 +        en = ap.seqPanel.seqCanvas.getFeatureRenderer().getFeatureGroups()
 +                .iterator();
          Vector groupsAdded = new Vector();
          while (en.hasNext())
          {
            Group g = new Group();
            g.setName(grp);
            g.setDisplay(((Boolean) ap.seqPanel.seqCanvas
 -                  .getFeatureRenderer().featureGroups.get(grp))
 +                  .getFeatureRenderer().checkGroupVisibility(grp, false))
                    .booleanValue());
            fs.addGroup(g);
            groupsAdded.addElement(grp);
          calcIdSet.add(aa[i].getCalcId());
          an.setCalcId(aa[i].getCalcId());
        }
+       if (aa[i].hasProperties())
+       {
+         for (String pr : aa[i].getProperties())
+         {
+           Property prop = new Property();
+           prop.setName(pr);
+           prop.setValue(aa[i].getProperty(pr));
+           an.addProperty(prop);
+         }
+       }
 +
        AnnotationElement ae;
        if (aa[i].annotations != null)
        {
  
            ae = new AnnotationElement();
            if (aa[i].annotations[a].description != null)
+           {
              ae.setDescription(aa[i].annotations[a].description);
+           }
            if (aa[i].annotations[a].displayCharacter != null)
+           {
              ae.setDisplayCharacter(aa[i].annotations[a].displayCharacter);
+           }
  
            if (!Float.isNaN(aa[i].annotations[a].value))
+           {
              ae.setValue(aa[i].annotations[a].value);
+           }
  
            ae.setPosition(a);
            if (aa[i].annotations[a].secondaryStructure != ' '
                    && aa[i].annotations[a].secondaryStructure != '\0')
+           {
              ae.setSecondaryStructure(aa[i].annotations[a].secondaryStructure
                      + "");
+           }
  
            if (aa[i].annotations[a].colour != null
                    && aa[i].annotations[a].colour != java.awt.Color.black)
          return false;
        }
      }
-     throw new Error("Unsupported Version for calcIdparam "
-             + calcIdParam.toString());
+     throw new Error(MessageManager.formatMessage("error.unsupported_version_calcIdparam", new String[]{calcIdParam.toString()}));
    }
  
    /**
      try
      {
        // create list to store references for any new Jmol viewers created
-       newStructureViewers = new Vector<AppJmol>();
+       newStructureViewers = new Vector<JalviewStructureDisplayI>();
        // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
        // Workaround is to make sure caller implements the JarInputStreamProvider
        // interface
      errorMessage = null;
    }
  
-   Hashtable alreadyLoadedPDB;
+   Hashtable<String, String> alreadyLoadedPDB;
  
    /**
     * when set, local views will be updated from view stored in JalviewXML
    String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
    {
      if (alreadyLoadedPDB == null)
+     {
        alreadyLoadedPDB = new Hashtable();
+     }
  
      if (alreadyLoadedPDB.containsKey(pdbId))
+     {
        return alreadyLoadedPDB.get(pdbId).toString();
+     }
  
      try
      {
                  entry.setFile(pdbloaded.get(ids[p].getId()).toString());
                }
              }
+             StructureSelectionManager.getStructureSelectionManager(
+                     Desktop.instance)
+                     .registerPDBEntry(entry);
              al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
            }
          }
            // in principle Visible should always be true for annotation displayed
            // in multiple views
            if (an[i].hasVisible())
+           {
              jda.visible = an[i].getVisible();
+           }
  
            al.addAnnotation(jda);
  
              anpos = ae[aa].getPosition();
  
              if (anpos >= anot.length)
+             {
                continue;
+             }
  
              anot[anpos] = new jalview.datamodel.Annotation(
  
            jaa.setScore(an[i].getScore());
          }
          if (an[i].hasVisible())
+         {
            jaa.visible = an[i].getVisible();
+         }
  
          if (an[i].hasCentreColLabels())
+         {
            jaa.centreColLabels = an[i].getCentreColLabels();
+         }
  
          if (an[i].hasScaleColLabels())
          {
            jaa.belowAlignment = an[i].isBelowAlignment();
          }
          jaa.setCalcId(an[i].getCalcId());
+         if (an[i].getPropertyCount() > 0)
+         {
+           for (jalview.schemabinding.version2.Property prop : an[i]
+                   .getProperty())
+           {
+             jaa.setProperty(prop.getName(), prop.getValue());
+           }
+         }
          if (jaa.autoCalculated)
          {
            autoAlan.add(new JvAnnotRow(i, jaa));
                    @Override
                    public void run()
                    {
-                     AppJmol sview = null;
+                     JalviewStructureDisplayI sview = null;
                      try
                      {
-                       sview = new AppJmol(pdbf, id, sq, alf.alignPanel,
+                       // JAL-1333 note - we probably can't migrate Jmol views to UCSF Chimera!
+                       sview = new StructureViewer(alf.alignPanel.getStructureSelectionManager()).createView(StructureViewer.Viewer.JMOL, pdbf, id, sq, alf.alignPanel,
                                useinJmolsuperpos, usetoColourbyseq,
                                jmolColouring, fileloc, rect, vid);
                        addNewStructureViewer(sview);
      return true;
    }
  
-   Vector<AppJmol> newStructureViewers = null;
+   Vector<JalviewStructureDisplayI> newStructureViewers = null;
  
-   protected void addNewStructureViewer(AppJmol sview)
+   protected void addNewStructureViewer(JalviewStructureDisplayI sview)
    {
      if (newStructureViewers != null)
      {
-       sview.jmb.setFinishedLoadingFromArchive(false);
+       sview.getBinding().setFinishedLoadingFromArchive(false);
        newStructureViewers.add(sview);
      }
    }
    {
      if (newStructureViewers != null)
      {
-       for (AppJmol sview : newStructureViewers)
+       for (JalviewStructureDisplayI sview : newStructureViewers)
        {
-         sview.jmb.setFinishedLoadingFromArchive(true);
+         sview.getBinding().setFinishedLoadingFromArchive(true);
        }
        newStructureViewers.clear();
        newStructureViewers = null;
  
      af.viewport.setColourAppliesToAllGroups(true);
  
 -    if (view.getShowSequenceFeatures())
 -    {
 -      af.viewport.showSequenceFeatures = true;
 -    }
 +    af.viewport.setShowSequenceFeatures(view.getShowSequenceFeatures());
 +
      if (view.hasCentreColumnLabels())
      {
        af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
      // recover featre settings
      if (jms.getFeatureSettings() != null)
      {
 -      af.viewport.featuresDisplayed = new Hashtable();
 +      FeaturesDisplayed fdi;
 +      af.viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
        String[] renderOrder = new String[jms.getFeatureSettings()
                .getSettingCount()];
 +      Hashtable featureGroups = new Hashtable();
 +      Hashtable featureColours = new Hashtable();
 +      Hashtable featureOrder = new Hashtable();
 +
        for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
        {
          Setting setting = jms.getFeatureSettings().getSetting(fs);
              gc.setColourByLabel(setting.getColourByLabel());
            }
            // and put in the feature colour table.
 -          af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
 -                  setting.getType(), gc);
 +          featureColours.put(setting.getType(), gc);
          }
          else
          {
 -          af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
 -                  setting.getType(),
 +          featureColours.put(setting.getType(),
                    new java.awt.Color(setting.getColour()));
          }
          renderOrder[fs] = setting.getType();
          if (setting.hasOrder())
          {
 -          af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
 -                  setting.getType(), setting.getOrder());
 +          featureOrder.put(setting.getType(), setting.getOrder());
          }
          else
          {
 -          af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
 -                  setting.getType(),
 -                  fs / jms.getFeatureSettings().getSettingCount());
 +          featureOrder.put(setting.getType(), new Float(fs
 +                  / jms.getFeatureSettings().getSettingCount()));
          }
          if (setting.getDisplay())
          {
 -          af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
 -                  setting.getColour()));
 +          fdi.setVisible(setting.getType());
          }
        }
 -      af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
 -      Hashtable fgtable;
 -      af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
 +      Hashtable fgtable = new Hashtable();
        for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
        {
          Group grp = jms.getFeatureSettings().getGroup(gs);
          fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
        }
 +      // FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
 +      // fgtable, featureColours, jms.getFeatureSettings().hasTransparency() ?
 +      // jms.getFeatureSettings().getTransparency() : 0.0, featureOrder);
 +      FeatureRendererSettings frs = new FeatureRendererSettings(
 +              renderOrder, fgtable, featureColours, 1.0f, featureOrder);
 +      af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
 +              .transferSettings(frs);
 +
      }
  
      if (view.getHiddenColumnsCount() > 0)
          }
        }
        else
+       {
          Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
+       }
      }
    }
  
    {
      skipList = skipList2;
    }
--
  }
   */
  package jalview.gui;
  
- import java.io.*;
- import java.util.*;
- import java.util.jar.*;
+ import jalview.binding.Annotation;
+ import jalview.binding.AnnotationElement;
+ import jalview.binding.Features;
+ import jalview.binding.JGroup;
+ import jalview.binding.JSeq;
+ import jalview.binding.JalviewModel;
+ import jalview.binding.JalviewModelSequence;
+ import jalview.binding.Pdbids;
+ import jalview.binding.Sequence;
+ import jalview.binding.SequenceSet;
+ import jalview.binding.Setting;
+ import jalview.binding.Tree;
+ import jalview.binding.UserColours;
+ import jalview.binding.Viewport;
+ import jalview.schemes.ColourSchemeI;
+ import jalview.schemes.ColourSchemeProperty;
+ import jalview.schemes.ResidueProperties;
+ import jalview.structure.StructureSelectionManager;
+ import jalview.util.MessageManager;
+ import jalview.util.jarInputStreamProvider;
  
- import javax.swing.*;
+ import java.io.InputStreamReader;
+ import java.util.Hashtable;
+ import java.util.Vector;
+ import java.util.jar.JarEntry;
+ import java.util.jar.JarInputStream;
  
- import org.exolab.castor.xml.*;
+ import javax.swing.JOptionPane;
  
- import jalview.binding.*;
- import jalview.schemes.*;
 +import jalview.util.MessageManager;
 +import jalview.util.jarInputStreamProvider;
+ import org.exolab.castor.xml.IDResolver;
 +import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
  
  /**
   * DOCUMENT ME!
@@@ -122,7 -139,7 +142,7 @@@ public class Jalview2XML_V
            InputStreamReader in = new InputStreamReader(jin, "UTF-8");
            JalviewModel object = new JalviewModel();
  
-           object = (JalviewModel) object.unmarshal(in);
+           object = object.unmarshal(in);
  
            af = LoadFromObject(object, file);
            entryCount++;
            {
  
              JOptionPane.showInternalMessageDialog(Desktop.desktop,
-                     "Error loading  " + file, "Error loading Jalview file",
+                     MessageManager.formatMessage("label.error_loading_file_params", new String[]{file}), MessageManager.getString("label.error_loading_jalview_file"),
                      JOptionPane.WARNING_MESSAGE);
            }
          });
            entry.setId(ids[p].getId());
            entry.setType(ids[p].getType());
            al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
+           StructureSelectionManager.getStructureSelectionManager(
+                   Desktop.instance).registerPDBEntry(entry);
          }
  
        }
  
          for (int s = 0; s < ids.length; s++)
          {
-           seqs.addElement((jalview.datamodel.SequenceI) seqids
+           seqs.addElement(seqids
                    .elementAt(ids[s]));
          }
  
      }
  
      af.viewport.setColourAppliesToAllGroups(true);
 -    af.viewport.showSequenceFeatures = view.getShowSequenceFeatures();
 +    af.viewport.setShowSequenceFeatures(view.getShowSequenceFeatures());
  
      if (jms.getFeatureSettings() != null)
      {
 -      af.viewport.featuresDisplayed = new Hashtable();
 +      Hashtable featuresDisplayed = new Hashtable();
 +      Hashtable featureColours = new Hashtable();
        String[] renderOrder = new String[jms.getFeatureSettings()
                .getSettingCount()];
        for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
        {
          Setting setting = jms.getFeatureSettings().getSetting(fs);
  
 -        af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
 +        featureColours.put(
                  setting.getType(), new java.awt.Color(setting.getColour()));
  
          renderOrder[fs] = setting.getType();
  
          if (setting.getDisplay())
          {
 -          af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
 +          featuresDisplayed.put(setting.getType(), new Integer(
                    setting.getColour()));
          }
        }
 -      af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
 +      FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder, new Hashtable(), featureColours, 1.0f, null);
 +      af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().transferSettings(frs);
      }
  
      af.setMenusFromViewport(af.viewport);
index 0000000,c582c5d..d22c46c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,104 +1,104 @@@
+ package jalview.gui;
+ import jalview.api.AlignmentViewPanel;
+ import jalview.datamodel.PDBEntry;
+ import jalview.datamodel.SequenceI;
+ import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
+ import jalview.structure.StructureSelectionManager;
+ public class JalviewChimeraBindingModel extends JalviewChimeraBinding
+ {
+   private ChimeraViewFrame cvf;
+   public JalviewChimeraBindingModel(ChimeraViewFrame chimeraViewFrame,
+           StructureSelectionManager ssm, PDBEntry[] pdbentry,
+           SequenceI[][] sequenceIs, String[][] chains, String protocol)
+   {
+     super(ssm, pdbentry, sequenceIs, chains, protocol);
+     cvf = chimeraViewFrame;
+   }
+   FeatureRenderer fr = null;
+   @Override
+   public jalview.api.FeatureRenderer getFeatureRenderer(
+           AlignmentViewPanel alignment)
+   {
+     AlignmentPanel ap = (alignment == null) ? cvf.ap
+             : (AlignmentPanel) alignment;
 -    if (ap.av.showSequenceFeatures)
++    if (ap.av.isShowSequenceFeatures())
+     {
+       if (fr == null)
+       {
 -        fr = ap.cloneFeatureRenderer();
++        fr = (jalview.gui.FeatureRenderer) ap.cloneFeatureRenderer();
+       }
+       else
+       {
+         ap.updateFeatureRenderer(fr);
+       }
+     }
+     return fr;
+   }
+   @Override
+   public jalview.api.SequenceRenderer getSequenceRenderer(
+           AlignmentViewPanel alignment)
+   {
+     return new SequenceRenderer(((AlignmentPanel) alignment).av);
+   }
+   @Override
+   public void refreshGUI()
+   {
+     // appJmolWindow.repaint();
+     javax.swing.SwingUtilities.invokeLater(new Runnable()
+     {
+       public void run()
+       {
+         cvf.updateTitleAndMenus();
+         cvf.revalidate();
+       }
+     });
+   }
+   public void updateColours(Object source)
+   {
+     AlignmentPanel ap = (AlignmentPanel) source, topap;
+     // ignore events from panels not used to colour this view
+     if (!cvf.isUsedforcolourby(ap))
+     {
+       return;
+     }
+     if (!isLoadingFromArchive())
+     {
 -      colourBySequence(ap.av.getShowSequenceFeatures(), ap);
++      colourBySequence(ap.av.isShowSequenceFeatures(), ap);
+     }
+   }
+   @Override
+   public void releaseReferences(Object svl)
+   {
+     // TODO Auto-generated method stub
+   }
+   @Override
+   protected void releaseUIResources()
+   {
+     // TODO Auto-generated method stub
+   }
+   @Override
+   public void refreshPdbEntries()
+   {
+     // TODO Auto-generated method stub
+   }
+   @Override
+   public void showUrl(String url, String target)
+   {
+     // TODO Auto-generated method stub
+   }
+ }
   */
  package jalview.gui;
  
- import java.util.*;
- import java.awt.*;
- import java.awt.event.*;
- import javax.swing.*;
- import jalview.analysis.*;
- import jalview.commands.*;
- import jalview.datamodel.*;
- import jalview.io.*;
- import jalview.schemes.*;
+ import jalview.analysis.AAFrequency;
+ import jalview.analysis.AlignmentAnnotationUtils;
+ import jalview.analysis.Conservation;
+ import jalview.commands.ChangeCaseCommand;
+ import jalview.commands.EditCommand;
+ import jalview.datamodel.AlignmentAnnotation;
+ import jalview.datamodel.Annotation;
+ import jalview.datamodel.DBRefEntry;
+ import jalview.datamodel.PDBEntry;
+ import jalview.datamodel.Sequence;
+ import jalview.datamodel.SequenceFeature;
+ import jalview.datamodel.SequenceGroup;
+ import jalview.datamodel.SequenceI;
+ import jalview.io.FormatAdapter;
+ import jalview.io.SequenceAnnotationReport;
+ import jalview.schemes.AnnotationColourGradient;
+ import jalview.schemes.Blosum62ColourScheme;
+ import jalview.schemes.BuriedColourScheme;
+ import jalview.schemes.ClustalxColourScheme;
+ import jalview.schemes.HelixColourScheme;
+ import jalview.schemes.HydrophobicColourScheme;
+ import jalview.schemes.NucleotideColourScheme;
+ import jalview.schemes.PIDColourScheme;
+ import jalview.schemes.PurinePyrimidineColourScheme;
+ import jalview.schemes.ResidueProperties;
+ import jalview.schemes.StrandColourScheme;
+ import jalview.schemes.TaylorColourScheme;
+ import jalview.schemes.TurnColourScheme;
+ import jalview.schemes.UserColourScheme;
+ import jalview.schemes.ZappoColourScheme;
  import jalview.util.GroupUrlLink;
  import jalview.util.GroupUrlLink.UrlStringTooLongException;
  import jalview.util.MessageManager;
  import jalview.util.UrlLink;
  
+ import java.awt.Color;
+ import java.awt.event.ActionEvent;
+ import java.awt.event.ActionListener;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Collection;
+ import java.util.Collections;
+ import java.util.Hashtable;
+ import java.util.LinkedHashMap;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.TreeMap;
+ 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.JOptionPane;
+ import javax.swing.JPopupMenu;
+ import javax.swing.JRadioButtonMenuItem;
  /**
   * DOCUMENT ME!
   * 
   */
  public class PopupMenu extends JPopupMenu
  {
+   private static final String ALL_ANNOTATIONS = "All";
+   private static final String COMMA = ",";
    JMenu groupMenu = new JMenu();
  
    JMenuItem groupName = new JMenuItem();
  
    JMenuItem sequenceSelDetails = new JMenuItem();
  
+   JMenuItem chooseAnnotations = new JMenuItem();
    SequenceI sequence;
  
    JMenuItem createGroupMenuItem = new JMenuItem();
  
    JMenu outputMenu = new JMenu();
  
+   JMenu seqShowAnnotationsMenu = new JMenu();
+   JMenu seqHideAnnotationsMenu = new JMenu();
+   JMenuItem seqAddReferenceAnnotations = new JMenuItem();
+   JMenu groupShowAnnotationsMenu = new JMenu();
+   JMenu groupHideAnnotationsMenu = new JMenu();
+   JMenuItem groupAddReferenceAnnotations = new JMenuItem();
    JMenuItem sequenceFeature = new JMenuItem();
  
    JMenuItem textColour = new JMenuItem();
  
        item.addActionListener(new java.awt.event.ActionListener()
        {
+         @Override
          public void actionPerformed(ActionEvent e)
          {
            outputText_actionPerformed(e);
        outputMenu.add(item);
      }
  
+     /*
+      * Build menus for annotation types that may be shown or hidden, and for
+      * 'reference annotations' that may be added to the alignment. First for the
+      * currently selected sequence (if there is one):
+      */
+     final List<SequenceI> selectedSequence = (seq == null ? Collections
+             .<SequenceI> emptyList() : Arrays.asList(seq));
+     buildAnnotationTypesMenus(seqShowAnnotationsMenu,
+             seqHideAnnotationsMenu, selectedSequence);
+     configureReferenceAnnotationsMenu(seqAddReferenceAnnotations,
+             selectedSequence);
+     /*
+      * And repeat for the current selection group (if there is one):
+      */
+     final List<SequenceI> selectedGroup = (ap.av.getSelectionGroup() == null ? Collections
+             .<SequenceI> emptyList() : ap.av.getSelectionGroup()
+             .getSequences());
+     buildAnnotationTypesMenus(groupShowAnnotationsMenu,
+             groupHideAnnotationsMenu, selectedGroup);
+     configureReferenceAnnotationsMenu(groupAddReferenceAnnotations,
+             selectedGroup);
      try
      {
        jbInit();
  
            menuItem = new JMenuItem();
            menuItem.setText(pdb.getId());
-           menuItem.addActionListener(new java.awt.event.ActionListener()
+           menuItem.addActionListener(new ActionListener()
            {
+             @Override
              public void actionPerformed(ActionEvent e)
              {
                // TODO re JAL-860: optionally open dialog or provide a menu entry
                // allowing user to open just one structure per sequence
-               new AppJmol(pdb, ap.av.collateForPDB(new PDBEntry[]
-               { pdb })[0], null, ap);
-               // new PDBViewer(pdb, seqs2, null, ap, AppletFormatAdapter.FILE);
+               // new AppJmol(pdb, ap.av.collateForPDB(new PDBEntry[]
+               // { pdb })[0], null, ap);
+               new StructureViewer(ap.getStructureSelectionManager())
+                       .viewStructures(pdb,
+                               ap.av.collateForPDB(new PDBEntry[]
+                               { pdb })[0], null, ap);
              }
            });
            viewStructureMenu.add(menuItem);
  
                      "label.2d_rna_structure_line", new String[]
                      { structureLine }));
              menuItem.addActionListener(new java.awt.event.ActionListener()
              {
+               @Override
                public void actionPerformed(ActionEvent e)
                {
                  // System.out.println("1:"+structureLine);
                        { seq.getName() }));
                menuItem.addActionListener(new java.awt.event.ActionListener()
                {
+                 @Override
                  public void actionPerformed(ActionEvent e)
                  {
                    // TODO: VARNA does'nt print gaps in the sequence
                MessageManager.getString("action.hide_sequences"));
        menuItem.addActionListener(new java.awt.event.ActionListener()
        {
+         @Override
          public void actionPerformed(ActionEvent e)
          {
            hideSequences(false);
                  { seq.getName() }));
          menuItem.addActionListener(new java.awt.event.ActionListener()
          {
+           @Override
            public void actionPerformed(ActionEvent e)
            {
              hideSequences(true);
                    MessageManager.getString("action.reveal_sequences"));
            menuItem.addActionListener(new ActionListener()
            {
+             @Override
              public void actionPerformed(ActionEvent e)
              {
                ap.av.showSequence(index);
                  MessageManager.getString("action.reveal_all"));
          menuItem.addActionListener(new ActionListener()
          {
+           @Override
            public void actionPerformed(ActionEvent e)
            {
              ap.av.showAllHiddenSeqs();
        SequenceI sqass = null;
        for (SequenceI sq : ap.av.getSequenceSelection())
        {
-         Vector<PDBEntry> pes = (Vector<PDBEntry>) sq.getDatasetSequence()
-                 .getPDBId();
-         if (pes != null && pes.size()>0)
+         Vector<PDBEntry> pes = sq.getDatasetSequence().getPDBId();
+         if (pes != null && pes.size() > 0)
          {
            reppdb.put(pes.get(0).getId(), pes.get(0));
            for (PDBEntry pe : pes)
            @Override
            public void actionPerformed(ActionEvent e)
            {
-             new AppJmol(ap, pe, ap.av.collateForPDB(pe));
+             new StructureViewer(ap.getStructureSelectionManager())
+                     .viewStructures(ap, pe, ap.av.collateForPDB(pe));
            }
          });
          if (reppdb.size() > 1 && reppdb.size() < pdbe.size())
              @Override
              public void actionPerformed(ActionEvent e)
              {
-               new AppJmol(ap, pr, ap.av.collateForPDB(pr));
+               new StructureViewer(ap.getStructureSelectionManager())
+                       .viewStructures(ap, pr, ap.av.collateForPDB(pr));
              }
            });
          }
      }
    }
  
+   /**
+    * Add annotation types to 'Show annotations' and/or 'Hide annotations' menus.
+    * "All" is added first, followed by a separator. Then add any annotation
+    * types associated with the current selection. Separate menus are built for
+    * the selected sequence group (if any), and the selected sequence.
+    * <p>
+    * Some annotation rows are always rendered together - these can be identified
+    * by a common graphGroup property > -1. Only one of each group will be marked
+    * as visible (to avoid duplication of the display). For such groups we add a
+    * composite type name, e.g.
+    * <p>
+    * IUPredWS (Long), IUPredWS (Short)
+    * 
+    * @param seq
+    */
+   protected void buildAnnotationTypesMenus(JMenu showMenu, JMenu hideMenu,
+           List<SequenceI> forSequences)
+   {
+     showMenu.removeAll();
+     hideMenu.removeAll();
+     final List<String> all = Arrays.asList(ALL_ANNOTATIONS);
+     addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true, true);
+     addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true,
+             false);
+     showMenu.addSeparator();
+     hideMenu.addSeparator();
+     final AlignmentAnnotation[] annotations = ap.getAlignment()
+             .getAlignmentAnnotation();
+     /*
+      * Find shown/hidden annotations types, distinguished by source (calcId),
+      * and grouped by graphGroup. Using LinkedHashMap means we will retrieve in
+      * the insertion order, which is the order of the annotations on the
+      * alignment.
+      */
+     Map<String, List<List<String>>> shownTypes = new LinkedHashMap<String, List<List<String>>>();
+     Map<String, List<List<String>>> hiddenTypes = new LinkedHashMap<String, List<List<String>>>();
+     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes,
+             hiddenTypes,
+             AlignmentAnnotationUtils.asList(annotations),
+             forSequences);
+     for (String calcId : hiddenTypes.keySet())
+     {
+       for (List<String> type : hiddenTypes.get(calcId))
+       {
+         addAnnotationTypeToShowHide(showMenu, forSequences,
+                 calcId, type, false, true);
+       }
+     }
+     // grey out 'show annotations' if none are hidden
+     showMenu.setEnabled(!hiddenTypes.isEmpty());
+     for (String calcId : shownTypes.keySet())
+     {
+       for (List<String> type : shownTypes.get(calcId))
+       {
+         addAnnotationTypeToShowHide(hideMenu, forSequences,
+                 calcId, type, false, false);
+       }
+     }
+     // grey out 'hide annotations' if none are shown
+     hideMenu.setEnabled(!shownTypes.isEmpty());
+   }
+   /**
+    * Returns a list of sequences - either the current selection group (if there
+    * is one), else the specified single sequence.
+    * 
+    * @param seq
+    * @return
+    */
+   protected List<SequenceI> getSequenceScope(SequenceI seq)
+   {
+     List<SequenceI> forSequences = null;
+     final SequenceGroup selectionGroup = ap.av.getSelectionGroup();
+     if (selectionGroup != null && selectionGroup.getSize() > 0)
+     {
+       forSequences = selectionGroup.getSequences();
+     }
+     else
+     {
+       forSequences = seq == null ? Collections.<SequenceI> emptyList()
+               : Arrays.asList(seq);
+     }
+     return forSequences;
+   }
+   /**
+    * Add one annotation type to the 'Show Annotations' or 'Hide Annotations'
+    * menus.
+    * 
+    * @param showOrHideMenu
+    *          the menu to add to
+    * @param forSequences
+    *          the sequences whose annotations may be shown or hidden
+    * @param calcId
+    * @param types
+    *          the label to add
+    * @param allTypes
+    *          if true this is a special label meaning 'All'
+    * @param actionIsShow
+    *          if true, the select menu item action is to show the annotation
+    *          type, else hide
+    */
+   protected void addAnnotationTypeToShowHide(JMenu showOrHideMenu,
+           final List<SequenceI> forSequences, String calcId,
+           final List<String> types, final boolean allTypes,
+           final boolean actionIsShow)
+   {
+     String label = types.toString(); // [a, b, c]
+     label = label.substring(1, label.length() - 1);
+     final JMenuItem item = new JMenuItem(label);
+     item.setToolTipText(calcId);
+     item.addActionListener(new java.awt.event.ActionListener()
+     {
+       @Override
+       public void actionPerformed(ActionEvent e)
+       {
+         showHideAnnotation_actionPerformed(types, forSequences, allTypes,
+                 actionIsShow);
+       }
+     });
+     showOrHideMenu.add(item);
+   }
+   /**
+    * Action on selecting a list of annotation type (or the 'all types' values)
+    * to show or hide for the specified sequences.
+    * 
+    * @param types
+    * @param forSequences
+    * @param anyType
+    * @param doShow
+    */
+   protected void showHideAnnotation_actionPerformed(
+           Collection<String> types, List<SequenceI> forSequences,
+           boolean anyType, boolean doShow)
+   {
+     for (AlignmentAnnotation aa : ap.getAlignment()
+             .getAlignmentAnnotation())
+     {
+       if (anyType || types.contains(aa.label))
+       {
+         if ((aa.sequenceRef != null)
+                 && forSequences.contains(aa.sequenceRef))
+         {
+           aa.visible = doShow;
+         }
+       }
+     }
+     refresh();
+   }
    private void buildGroupURLMenu(SequenceGroup sg, Vector groupLinks)
    {
  
              { url }));
      item.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          new Thread(new Runnable()
          {
  
+           @Override
            public void run()
            {
              showLink(url);
      // TODO: put in info about what is being sent.
      item.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          new Thread(new Runnable()
          {
  
+           @Override
            public void run()
            {
              try
      groupName.setText(MessageManager.getString("label.name"));
      groupName.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          groupName_actionPerformed();
              .getString("label.edit_name_description"));
      sequenceName.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          sequenceName_actionPerformed();
        }
      });
+     chooseAnnotations.setText(MessageManager
+             .getString("label.choose_annotations") + "...");
+     chooseAnnotations.addActionListener(new java.awt.event.ActionListener()
+     {
+       @Override
+       public void actionPerformed(ActionEvent e)
+       {
+         chooseAnnotations_actionPerformed(e);
+       }
+     });
      sequenceDetails.setText(MessageManager
              .getString("label.sequence_details") + "...");
      sequenceDetails.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          sequenceDetails_actionPerformed();
      sequenceSelDetails
              .addActionListener(new java.awt.event.ActionListener()
              {
+               @Override
                public void actionPerformed(ActionEvent e)
                {
                  sequenceSelectionDetails_actionPerformed();
              .setText(MessageManager.getString("action.remove_group"));
      unGroupMenuItem.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          unGroupMenuItem_actionPerformed();
      createGroupMenuItem
              .addActionListener(new java.awt.event.ActionListener()
              {
+               @Override
                public void actionPerformed(ActionEvent e)
                {
                  createGroupMenuItem_actionPerformed();
      outline.setText(MessageManager.getString("action.border_colour"));
      outline.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          outline_actionPerformed();
              .setText(MessageManager.getString("label.nucleotide"));
      nucleotideMenuItem.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          nucleotideMenuItem_actionPerformed();
      showBoxes.setState(true);
      showBoxes.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          showBoxes_actionPerformed();
      showText.setState(true);
      showText.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          showText_actionPerformed();
      showColourText.setText(MessageManager.getString("label.colour_text"));
      showColourText.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          showColourText_actionPerformed();
      displayNonconserved.setState(true);
      displayNonconserved.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          showNonconserved_actionPerformed();
      cut.setText(MessageManager.getString("action.cut"));
      cut.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          cut_actionPerformed();
      upperCase.setText(MessageManager.getString("label.to_upper_case"));
      upperCase.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          changeCase(e);
      copy.setText(MessageManager.getString("action.copy"));
      copy.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          copy_actionPerformed();
      lowerCase.setText(MessageManager.getString("label.to_lower_case"));
      lowerCase.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          changeCase(e);
      toggle.setText(MessageManager.getString("label.toggle_case"));
      toggle.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          changeCase(e);
      pdbFromFile.setText(MessageManager.getString("label.from_file"));
      pdbFromFile.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          pdbFromFile_actionPerformed();
      enterPDB.setText(MessageManager.getString("label.enter_pdb_id"));
      enterPDB.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          enterPDB_actionPerformed();
      discoverPDB.setText(MessageManager.getString("label.discover_pdb_ids"));
      discoverPDB.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          discoverPDB_actionPerformed();
      });
      outputMenu.setText(MessageManager.getString("label.out_to_textbox")
              + "...");
+     seqShowAnnotationsMenu.setText(MessageManager
+             .getString("label.show_annotations"));
+     seqHideAnnotationsMenu.setText(MessageManager
+             .getString("label.hide_annotations"));
+     groupShowAnnotationsMenu.setText(MessageManager
+             .getString("label.show_annotations"));
+     groupHideAnnotationsMenu.setText(MessageManager
+             .getString("label.hide_annotations"));
      sequenceFeature.setText(MessageManager
              .getString("label.create_sequence_feature"));
      sequenceFeature.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          sequenceFeature_actionPerformed();
      textColour.setText(MessageManager.getString("label.text_colour"));
      textColour.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          textColour_actionPerformed();
              + "...");
      editSequence.addActionListener(new ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent actionEvent)
        {
          editSequence_actionPerformed(actionEvent);
      add(groupMenu);
      add(sequenceMenu);
      this.add(structureMenu);
+     // annotations configuration panel suppressed for now
+     // groupMenu.add(chooseAnnotations);
+     /*
+      * Add show/hide annotations to the Sequence menu, and to the Selection menu
+      * (if a selection group is in force).
+      */
+     sequenceMenu.add(seqShowAnnotationsMenu);
+     sequenceMenu.add(seqHideAnnotationsMenu);
+     sequenceMenu.add(seqAddReferenceAnnotations);
+     groupMenu.add(groupShowAnnotationsMenu);
+     groupMenu.add(groupHideAnnotationsMenu);
+     groupMenu.add(groupAddReferenceAnnotations);
      groupMenu.add(editMenu);
      groupMenu.add(outputMenu);
      groupMenu.add(sequenceFeature);
          JMenuItem item = new JMenuItem(userColours.nextElement().toString());
          item.addActionListener(new ActionListener()
          {
+           @Override
            public void actionPerformed(ActionEvent evt)
            {
              userDefinedColour_actionPerformed(evt);
      noColourmenuItem.setText(MessageManager.getString("label.none"));
      noColourmenuItem.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          noColourmenuItem_actionPerformed();
              .getString("label.clustalx_colours"));
      clustalColour.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          clustalColour_actionPerformed();
      zappoColour.setText(MessageManager.getString("label.zappo"));
      zappoColour.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          zappoColour_actionPerformed();
      taylorColour.setText(MessageManager.getString("label.taylor"));
      taylorColour.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          taylorColour_actionPerformed();
      hydrophobicityColour
              .addActionListener(new java.awt.event.ActionListener()
              {
+               @Override
                public void actionPerformed(ActionEvent e)
                {
                  hydrophobicityColour_actionPerformed();
      helixColour.setText(MessageManager.getString("label.helix_propensity"));
      helixColour.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          helixColour_actionPerformed();
              .getString("label.strand_propensity"));
      strandColour.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          strandColour_actionPerformed();
      turnColour.setText(MessageManager.getString("label.turn_propensity"));
      turnColour.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          turnColour_actionPerformed();
      buriedColour.setText(MessageManager.getString("label.buried_index"));
      buriedColour.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          buriedColour_actionPerformed();
              .getString("label.above_identity_percentage"));
      abovePIDColour.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          abovePIDColour_actionPerformed();
              .getString("action.user_defined"));
      userDefinedColour.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          userDefinedColour_actionPerformed(e);
              .setText(MessageManager.getString("label.percentage_identity"));
      PIDColour.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          PIDColour_actionPerformed();
      BLOSUM62Colour.setText(MessageManager.getString("label.blosum62"));
      BLOSUM62Colour.addActionListener(new java.awt.event.ActionListener()
      {
+       @Override
        public void actionPerformed(ActionEvent e)
        {
          BLOSUM62Colour_actionPerformed();
      purinePyrimidineColour
              .addActionListener(new java.awt.event.ActionListener()
              {
+               @Override
                public void actionPerformed(ActionEvent e)
                {
                  purinePyrimidineColour_actionPerformed();
      conservationMenuItem
              .addActionListener(new java.awt.event.ActionListener()
              {
+               @Override
                public void actionPerformed(ActionEvent e)
                {
                  conservationMenuItem_actionPerformed();
              });
    }
  
+   /**
+    * Check for any annotations on the underlying dataset sequences (for the
+    * current selection group) which are not on the alignment annotations for the
+    * sequence. If any are found, enable the option to add them to the alignment.
+    * The criteria for 'on the alignment' is finding an alignment annotation on
+    * the sequence, that matches on calcId and label. A tooltip is also
+    * constructed that displays the source (calcId) and type (label) of the
+    * annotations that can be added.
+    * 
+    * @param menuItem
+    * @param forSequences
+    */
+   protected void configureReferenceAnnotationsMenu(
+           JMenuItem menuItem, List<SequenceI> forSequences)
+   {
+     menuItem.setText(MessageManager
+             .getString("label.add_reference_annotations"));
+     menuItem.setEnabled(false);
+     if (forSequences == null)
+     {
+       return;
+     }
+     /*
+      * Temporary store to hold distinct calcId / type pairs for the tooltip.
+      * Using TreeMap means calcIds are shown in alphabetical order.
+      */
+     Map<String, String> tipEntries = new TreeMap<String, String>();
+     StringBuilder tooltip = new StringBuilder(64);
+     tooltip.append(MessageManager.getString("label.add_annotations_for"));
+     /*
+      * For each sequence selected in the alignment, make a list of any
+      * annotations on the underlying dataset sequence which are not already on
+      * the sequence in the alignment.
+      * 
+      * Build a map of { alignmentSequence, <List of annotations to add> }
+      */
+     final Map<SequenceI, List<AlignmentAnnotation>> candidates = new LinkedHashMap<SequenceI, List<AlignmentAnnotation>>();
+     for (SequenceI seq : forSequences)
+     {
+       SequenceI dataset = seq.getDatasetSequence();
+       if (dataset == null)
+       {
+         continue;
+       }
+       AlignmentAnnotation[] datasetAnnotations = dataset.getAnnotation();
+       if (datasetAnnotations == null)
+       {
+         continue;
+       }
+       final List<AlignmentAnnotation> result = new ArrayList<AlignmentAnnotation>();
+       for (AlignmentAnnotation dsann : datasetAnnotations)
+       {
+         /*
+          * If the sequence has no annotation that matches this one, then add
+          * this one to the results list.
+          */
+         if (seq.getAlignmentAnnotations(dsann.getCalcId(), dsann.label)
+                 .isEmpty())
+         {
+           result.add(dsann);
+           tipEntries.put(dsann.getCalcId(), dsann.label);
+         }
+       }
+       /*
+        * Save any addable annotations for this sequence
+        */
+       if (!result.isEmpty())
+       {
+         candidates.put(seq, result);
+       }
+     }
+     if (!candidates.isEmpty())
+     {
+       /*
+        * Found annotations that could be added. Enable the menu item, and
+        * configure its tooltip and action.
+        */
+       menuItem.setEnabled(true);
+       for (String calcId : tipEntries.keySet())
+       {
+         tooltip.append("<br/>" + calcId + "/" + tipEntries.get(calcId));
+       }
+       String tooltipText = JvSwingUtils.wrapTooltip(true,
+               tooltip.toString());
+       menuItem.setToolTipText(tooltipText);
+       menuItem.addActionListener(new ActionListener()
+       {
+         @Override
+         public void actionPerformed(ActionEvent e)
+         {
+           addReferenceAnnotations_actionPerformed(candidates);
+         }
+       });
+     }
+   }
+   /**
+    * Add annotations to the sequences and to the alignment.
+    * 
+    * @param candidates
+    *          a map whose keys are sequences on the alignment, and values a list
+    *          of annotations to add to each sequence
+    */
+   protected void addReferenceAnnotations_actionPerformed(
+           Map<SequenceI, List<AlignmentAnnotation>> candidates)
+   {
+     /*
+      * Add annotations at the top of the annotation, in the same order as their
+      * related sequences.
+      */
+     for (SequenceI seq : candidates.keySet())
+     {
+       for (AlignmentAnnotation ann : candidates.get(seq))
+       {
+         AlignmentAnnotation copyAnn = new AlignmentAnnotation(ann);
+         int startRes = 0;
+         int endRes = ann.annotations.length;
+         final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup();
+         if (selectionGroup != null)
+         {
+           startRes = selectionGroup.getStartRes();
+           endRes = selectionGroup.getEndRes();
+         }
+         copyAnn.restrict(startRes, endRes);
+         // add to the sequence (sets copyAnn.datasetSequence)
+         seq.addAlignmentAnnotation(copyAnn);
+         // adjust for gaps
+         copyAnn.adjustForAlignment();
+         // add to the alignment and set visible
+         this.ap.getAlignment().addAnnotation(copyAnn);
+         copyAnn.visible = true;
+       }
+     }
+     refresh();
+   }
    protected void sequenceSelectionDetails_actionPerformed()
    {
      createSequenceDetailsReport(ap.av.getSequenceSelection());
                        true,
                        true,
                        false,
 -                      (ap.seqPanel.seqCanvas.fr != null) ? ap.seqPanel.seqCanvas.fr.minmax
 +                      (ap.seqPanel.seqCanvas.fr != null) ? ap.seqPanel.seqCanvas.fr.getMinMax()
                                : null);
        contents.append("</p>");
      }
    }
  
    /**
+    * Open a panel where the user can choose which types of sequence annotation
+    * to show or hide.
+    * 
+    * @param e
+    */
+   protected void chooseAnnotations_actionPerformed(ActionEvent e)
+   {
+     // todo correct way to guard against opening a duplicate panel?
+     new AnnotationChooser(ap);
+   }
+   /**
     * DOCUMENT ME!
     * 
     * @param e
      System.out.println("PROMPT USER HERE"); // TODO: decide if a prompt happens
      // or we simply trust the user wants
      // wysiwig behaviour
-     SequenceGroup sg = ap.av.getSelectionGroup();
-     ColumnSelection csel = new ColumnSelection(ap.av.getColumnSelection());
-     omitHidden = ap.av.getViewAsString(true);
-     Alignment oal = new Alignment(ap.av.getSequenceSelection());
-     AlignmentAnnotation[] nala = ap.av.getAlignment()
-             .getAlignmentAnnotation();
-     if (nala != null)
-     {
-       for (int i = 0; i < nala.length; i++)
-       {
-         AlignmentAnnotation na = nala[i];
-         oal.addAnnotation(na);
-       }
-     }
      cap.setText(new FormatAdapter().formatSequences(e.getActionCommand(),
-             oal, omitHidden, csel, sg));
-     oal = null;
+             ap.av, true));
    }
  
    public void pdbFromFile_actionPerformed()
              { sequence.getDisplayId(false) }));
      chooser.setToolTipText(MessageManager.formatMessage(
              "label.load_pdb_file_associate_with_sequence", new String[]
-             { new Integer(sequence.getDisplayId(false)).toString() }));
+             { sequence.getDisplayId(false) }));
  
      int value = chooser.showOpenDialog(null);
  
        String choice = chooser.getSelectedFile().getPath();
        jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
        new AssociatePdbFileWithSeq().associatePdbWithSeq(choice,
-               jalview.io.AppletFormatAdapter.FILE, sequence, true);
+               jalview.io.AppletFormatAdapter.FILE, sequence, true,
+               Desktop.instance);
      }
  
    }
              : ap.av.getSequenceSelection());
      Thread discpdb = new Thread(new Runnable()
      {
+       @Override
        public void run()
        {
  
      if (sg != null)
      {
        if (sequence == null)
-         sequence = (Sequence) sg.getSequenceAt(0);
+       {
+         sequence = sg.getSequenceAt(0);
+       }
  
        EditNameDialog dialog = new EditNameDialog(
                sequence.getSequenceAsString(sg.getStartRes(),
@@@ -225,6 -225,42 +225,6 @@@ public class SeqPanel extends JPanel im
      return seq;
    }
  
 -  SequenceFeature[] findFeaturesAtRes(SequenceI sequence, int res)
 -  {
 -    Vector tmp = new Vector();
 -    SequenceFeature[] features = sequence.getSequenceFeatures();
 -    if (features != null)
 -    {
 -      for (int i = 0; i < features.length; i++)
 -      {
 -        if (av.featuresDisplayed == null
 -                || !av.featuresDisplayed.containsKey(features[i].getType()))
 -        {
 -          continue;
 -        }
 -
 -        if (features[i].featureGroup != null
 -                && seqCanvas.fr.featureGroups != null
 -                && seqCanvas.fr.featureGroups
 -                        .containsKey(features[i].featureGroup)
 -                && !((Boolean) seqCanvas.fr.featureGroups
 -                        .get(features[i].featureGroup)).booleanValue())
 -          continue;
 -
 -        if ((features[i].getBegin() <= res)
 -                && (features[i].getEnd() >= res))
 -        {
 -          tmp.addElement(features[i]);
 -        }
 -      }
 -    }
 -
 -    features = new SequenceFeature[tmp.size()];
 -    tmp.copyInto(features);
 -
 -    return features;
 -  }
 -
    void endEditing()
    {
      if (editCommand != null && editCommand.getSize() > 0)
      }
  
      // use aa to see if the mouse pointer is on a
 -    if (av.showSequenceFeatures)
 +    if (av.isShowSequenceFeatures())
      {
        int rpos;
 -      SequenceFeature[] features = findFeaturesAtRes(
 +      List<SequenceFeature> features = ap.getFeatureRenderer().findFeaturesAtRes(
                sequence.getDatasetSequence(),
                rpos = sequence.findPosition(res));
        seqARep.appendFeatures(tooltipText, rpos, features,
 -              this.ap.seqPanel.seqCanvas.fr.minmax);
 +              this.ap.seqPanel.seqCanvas.fr.getMinMax());
      }
      if (tooltipText.length() == 6) // <html></html>
      {
        message.append("Edit group:");
        if (editCommand == null)
        {
-         editCommand = new EditCommand("Edit Group");
+         editCommand = new EditCommand(MessageManager.getString("action.edit_group"));
        }
      }
      else
        }
        if (editCommand == null)
        {
-         editCommand = new EditCommand("Edit " + label);
+         editCommand = new EditCommand(MessageManager.formatMessage("label.edit_params", new String[]{label}));
        }
      }
  
          av.setSelectionGroup(null);
        }
  
 -      SequenceFeature[] features = findFeaturesAtRes(
 +      List<SequenceFeature> features = seqCanvas.getFeatureRenderer().findFeaturesAtRes(
                sequence.getDatasetSequence(),
                sequence.findPosition(findRes(evt)));
  
 -      if (features != null && features.length > 0)
 +      if (features != null && features.size()> 0)
        {
          SearchResults highlight = new SearchResults();
 -        highlight.addResult(sequence, features[0].getBegin(),
 -                features[0].getEnd());
 +        highlight.addResult(sequence, features.get(0).getBegin(),
 +                features.get(0).getEnd());
          seqCanvas.highlightSearchResults(highlight);
        }
 -      if (features != null && features.length > 0)
 +      if (features != null && features.size()> 0)
        {
          seqCanvas.getFeatureRenderer().amendFeatures(new SequenceI[]
 -        { sequence }, features, false, ap);
 +        { sequence }, features.toArray(new SequenceFeature[features.size()]), false, ap);
  
          seqCanvas.highlightSearchResults(null);
        }
  
      if (javax.swing.SwingUtilities.isRightMouseButton(evt))
      {
 -      SequenceFeature[] allFeatures = findFeaturesAtRes(
 +      List<SequenceFeature> allFeatures = ap.getFeatureRenderer().findFeaturesAtRes(
                sequence.getDatasetSequence(), sequence.findPosition(res));
        Vector links = new Vector();
 -      for (int i = 0; i < allFeatures.length; i++)
 +      for (SequenceFeature sf:allFeatures)
        {
 -        if (allFeatures[i].links != null)
 +        if (sf.links != null)
          {
 -          for (int j = 0; j < allFeatures[i].links.size(); j++)
 +          for (int j = 0; j < sf.links.size(); j++)
            {
 -            links.addElement(allFeatures[i].links.elementAt(j));
 +            links.addElement(sf.links.elementAt(j));
            }
          }
        }
@@@ -34,7 -34,7 +34,7 @@@ public class HTMLOutpu
  
    SequenceRenderer sr;
  
 -  FeatureRenderer fr;
 +  jalview.renderer.seqfeatures.FeatureRenderer fr;
  
    Color color;
  
@@@ -53,7 -53,7 +53,7 @@@
              { "HTML files" }, "HTML files");
  
      chooser.setFileView(new JalviewFileView());
-     chooser.setDialogTitle("Save as HTML");
+     chooser.setDialogTitle(MessageManager.getString("label.save_as_html"));
      chooser.setToolTipText(MessageManager.getString("action.save"));
  
      int value = chooser.showSaveDialog(null);
@@@ -23,6 -23,7 +23,7 @@@ package jalview.schemes
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.AnnotatedCollectionI;
+ import jalview.datamodel.Annotation;
  import jalview.datamodel.GraphLine;
  import jalview.datamodel.SequenceCollectionI;
  import jalview.datamodel.SequenceI;
@@@ -47,12 -48,18 +48,18 @@@ public class AnnotationColourGradient e
  
    GraphLine annotationThreshold;
  
-   float r1, g1, b1, rr, gg, bb, dr, dg, db;
+   float r1, g1, b1, rr, gg, bb;
  
    private boolean predefinedColours = false;
  
    private boolean seqAssociated = false;
  
+   /**
+    * false if the scheme was constructed without a minColour and maxColour used
+    * to decide if existing colours should be taken from annotation elements when
+    * they exist
+    */
+   private boolean noGradient = false;
    IdentityHashMap<SequenceI, AlignmentAnnotation> seqannot = null;
  
    @Override
      acg.rr = rr;
      acg.gg = gg;
      acg.bb = bb;
-     acg.dr = dr;
-     acg.dg = dg;
-     acg.db = db;
      acg.predefinedColours = predefinedColours;
      acg.seqAssociated = seqAssociated;
+     acg.noGradient = noGradient;
      return acg;
    }
  
      {
        annotationThreshold = annotation.threshold;
      }
+     // clear values so we don't get weird black bands...
+     r1 = 254;
+     g1 = 254;
+     b1 = 254;
+     rr = 0;
+     gg = 0;
+     bb = 0;
+     noGradient = true;
    }
  
    /**
      rr = maxColour.getRed() - r1;
      gg = maxColour.getGreen() - g1;
      bb = maxColour.getBlue() - b1;
+     noGradient = false;
    }
  
    @Override
    public Color findColour(char c, int j, SequenceI seq)
    {
      Color currentColour = Color.white;
 -    AlignmentAnnotation annotation = (seqAssociated ? seqannot.get(seq)
 +    AlignmentAnnotation annotation = (seqAssociated && seqannot!=null ? seqannot.get(seq)
              : this.annotation);
      if (annotation == null)
      {
                && annotation.annotations[j] != null
                && !jalview.util.Comparison.isGap(c))
        {
-         if (predefinedColours)
-         {
-           if (annotation.annotations[j].colour != null)
-             return annotation.annotations[j].colour;
-           else
-             return currentColour;
-         }
+         Annotation aj = annotation.annotations[j];
+         // 'use original colours' => colourScheme != null
+         // -> look up colour to be used
+         // predefined colours => preconfigured shading
+         // -> only use original colours reference if thresholding enabled &
+         // minmax exists
+         // annotation.hasIcons => null or black colours replaced with glyph
+         // colours
+         // -> reuse original colours if present
+         // -> if thresholding enabled then return colour on non-whitespace glyph
  
          if (aboveAnnotationThreshold == NO_THRESHOLD
-                 || (annotationThreshold != null
-                         && aboveAnnotationThreshold == ABOVE_THRESHOLD && annotation.annotations[j].value >= annotationThreshold.value)
-                 || (annotationThreshold != null
-                         && aboveAnnotationThreshold == BELOW_THRESHOLD && annotation.annotations[j].value <= annotationThreshold.value))
+                 || (annotationThreshold != null && (aboveAnnotationThreshold == ABOVE_THRESHOLD ? aj.value >= annotationThreshold.value
+                         : aj.value <= annotationThreshold.value)))
          {
-           float range = 1f;
-           if (thresholdIsMinMax
-                   && annotation.threshold != null
-                   && aboveAnnotationThreshold == ABOVE_THRESHOLD
-                   && annotation.annotations[j].value >= annotation.threshold.value)
+           if (predefinedColours && aj.colour != null)
            {
-             range = (annotation.annotations[j].value - annotation.threshold.value)
-                     / (annotation.graphMax - annotation.threshold.value);
+             currentColour = aj.colour;
            }
-           else if (thresholdIsMinMax && annotation.threshold != null
-                   && aboveAnnotationThreshold == BELOW_THRESHOLD
-                   && annotation.annotations[j].value >= annotation.graphMin)
+           else if (annotation.hasIcons
+                   && annotation.graph == AlignmentAnnotation.NO_GRAPH)
            {
-             range = (annotation.annotations[j].value - annotation.graphMin)
-                     / (annotation.threshold.value - annotation.graphMin);
+             if (aj.secondaryStructure > ' ' && aj.secondaryStructure != '.'
+                     && aj.secondaryStructure != '-')
+             {
+               if (colourScheme != null)
+               {
+                 currentColour = colourScheme.findColour(c, j, seq);
+               }
+               else
+               {
+               currentColour = annotation.annotations[j].secondaryStructure == 'H' ? jalview.renderer.AnnotationRenderer.HELIX_COLOUR
+                       : annotation.annotations[j].secondaryStructure == 'E' ? jalview.renderer.AnnotationRenderer.SHEET_COLOUR
+                               : jalview.renderer.AnnotationRenderer.STEM_COLOUR;
+               }
+             }
+             else
+             {
+               //
+               return Color.white;
+             }
            }
-           else
+           else if (noGradient)
            {
-             range = (annotation.annotations[j].value - annotation.graphMin)
-                     / (annotation.graphMax - annotation.graphMin);
-           }
-           if (colourScheme != null)
-           {
-             currentColour = colourScheme.findColour(c, j, seq);
+             if (colourScheme != null)
+             {
+               currentColour = colourScheme.findColour(c, j, seq);
+             }
+             else
+             {
+               if (aj.colour != null)
+               {
+                 currentColour = aj.colour;
+               }
+             }
            }
            else
            {
-             dr = rr * range + r1;
-             dg = gg * range + g1;
-             db = bb * range + b1;
+             // calculate a shade
+             float range = 1f;
+             if (thresholdIsMinMax
+                     && annotation.threshold != null
+                     && aboveAnnotationThreshold == ABOVE_THRESHOLD
+                     && annotation.annotations[j].value >= annotation.threshold.value)
+             {
+               range = (annotation.annotations[j].value - annotation.threshold.value)
+                       / (annotation.graphMax - annotation.threshold.value);
+             }
+             else if (thresholdIsMinMax
+                     && annotation.threshold != null
+                     && aboveAnnotationThreshold == BELOW_THRESHOLD
+                     && annotation.annotations[j].value >= annotation.graphMin)
+             {
+               range = (annotation.annotations[j].value - annotation.graphMin)
+                       / (annotation.threshold.value - annotation.graphMin);
+             }
+             else
+             {
+               range = (annotation.annotations[j].value - annotation.graphMin)
+                       / (annotation.graphMax - annotation.graphMin);
+             }
+             int dr = (int) (rr * range + r1), dg = (int) (gg * range + g1), db = (int) (bb
+                     * range + b1);
+             currentColour = new Color(dr, dg, db);
  
-             currentColour = new Color((int) dr, (int) dg, (int) db);
            }
          }
+         if (conservationColouring)
+         {
+           currentColour = applyConservation(currentColour, j);
+         }
        }
      }
-     if (conservationColouring)
-     {
-       currentColour = applyConservation(currentColour, j);
-     }
      return currentColour;
    }
  
   */
  package jalview.viewmodel;
  
 -import jalview.analysis.AAFrequency;
  import jalview.analysis.Conservation;
  import jalview.api.AlignCalcManagerI;
  import jalview.api.AlignViewportI;
  import jalview.api.AlignmentViewPanel;
 +import jalview.api.FeaturesDisplayedI;
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.AlignmentView;
@@@ -35,10 -35,10 +35,10 @@@ import jalview.datamodel.SequenceCollec
  import jalview.datamodel.SequenceGroup;
  import jalview.datamodel.SequenceI;
  import jalview.schemes.Blosum62ColourScheme;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.PIDColourScheme;
  import jalview.schemes.ResidueProperties;
 +import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
  import jalview.workers.AlignCalcManager;
  import jalview.workers.ConsensusThread;
  import jalview.workers.StrucConsensusThread;
@@@ -696,7 -696,6 +696,7 @@@ public abstract class AlignmentViewpor
      sequenceSetID = new String(newid);
    }
  
 +  @Override
    public String getSequenceSetId()
    {
      if (sequenceSetID == null)
  
    }
  
+   @Override
+   public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(boolean selectedOnly)
+   {
+     ArrayList<AlignmentAnnotation> ala = new ArrayList<AlignmentAnnotation>();
+     AlignmentAnnotation[] aa;
+     if ((aa=alignment.getAlignmentAnnotation())!=null)
+     {
+       for (AlignmentAnnotation annot:aa)
+       {
+         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
+         if (selectedOnly && selectionGroup!=null)
+         {
+           colSel.makeVisibleAnnotation(selectionGroup.getStartRes(), selectionGroup.getEndRes(),clone);
+         } else {
+           colSel.makeVisibleAnnotation(clone);
+         }
+         ala.add(clone);
+       }
+     }
+     return ala;
+   }
    /**
     * @return the padGaps
     */
    {
      sequenceColours = null;
    };
 +
 +  FeaturesDisplayedI featuresDisplayed = null;
 +
 +  @Override
 +  public FeaturesDisplayedI getFeaturesDisplayed()
 +  {
 +    return featuresDisplayed;
 +  }
 +
 +  public void setFeaturesDisplayed(FeaturesDisplayedI featuresDisplayedI)
 +  {
 +    featuresDisplayed = featuresDisplayedI;
 +  }
 +
 +  public boolean areFeaturesDisplayed()
 +  {
 +    return featuresDisplayed != null && featuresDisplayed.getRegisterdFeaturesCount()>0;
 +  }
 +
 +  /**
 +   * display setting for showing/hiding sequence features on alignment view
 +   */
 +  boolean showSequenceFeatures = false;
 +
 +  /**
 +   * set the flag
 +   * 
 +   * @param b
 +   *          features are displayed if true
 +   */
 +  @Override
 +  public void setShowSequenceFeatures(boolean b)
 +  {
 +    showSequenceFeatures = b;
 +  }
 +  @Override
 +  public boolean isShowSequenceFeatures()
 +  {
 +    return showSequenceFeatures;
 +  }
 +
 +  boolean showSeqFeaturesHeight;
 +
 +  public void setShowSequenceFeaturesHeight(boolean selected)
 +  {
 +    showSeqFeaturesHeight = selected;
 +  }
 +
 +  public boolean isShowSequenceFeaturesHeight()
 +  {
 +    return showSeqFeaturesHeight;
 +  }
 +
 +
  }
index c3b6c1e,0000000..30d14c2
mode 100644,000000..100644
--- /dev/null
@@@ -1,942 -1,0 +1,942 @@@
 +package jalview.viewmodel.seqfeatures;
 +
 +import jalview.api.AlignViewportI;
 +import jalview.api.FeaturesDisplayedI;
 +import jalview.datamodel.AlignmentI;
 +import jalview.datamodel.SequenceFeature;
 +import jalview.datamodel.SequenceI;
 +import jalview.renderer.seqfeatures.FeatureRenderer;
 +import jalview.schemes.GraduatedColor;
 +import jalview.viewmodel.AlignmentViewport;
 +
 +import java.awt.Color;
 +import java.beans.PropertyChangeListener;
 +import java.beans.PropertyChangeSupport;
 +import java.util.ArrayList;
 +import java.util.Arrays;
- import java.util.Enumeration;
 +import java.util.Hashtable;
 +import java.util.Iterator;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
- import java.util.Vector;
 +import java.util.concurrent.ConcurrentHashMap;
 +
 +public abstract class FeatureRendererModel implements
 +        jalview.api.FeatureRenderer
 +{
 +
 +  /**
 +   * global transparency for feature
 +   */
 +  protected float transparency = 1.0f;
 +
 +  protected Map<String, Object> featureColours = new ConcurrentHashMap<String, Object>();
 +
 +  protected Map<String, Boolean> featureGroups = new ConcurrentHashMap<String, Boolean>();
 +
 +  protected Object currentColour;
 +
 +  protected String[] renderOrder;
 +
 +  protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
 +          this);
 +
 +  protected AlignmentViewport av;
 +
 +  public AlignViewportI getViewport()
 +  {
 +    return av;
 +  }
 +
 +  public FeatureRendererSettings getSettings()
 +  {
 +    return new FeatureRendererSettings(this);
 +  }
 +
 +  public void transferSettings(FeatureRendererSettings fr)
 +  {
 +    this.renderOrder = fr.renderOrder;
 +    this.featureGroups = fr.featureGroups;
 +    this.featureColours = fr.featureColours;
 +    this.transparency = fr.transparency;
 +    this.featureOrder = fr.featureOrder;
 +  }
 +
 +  /**
 +   * update from another feature renderer
 +   * 
 +   * @param fr
 +   *          settings to copy
 +   */
 +  public void transferSettings(jalview.api.FeatureRenderer _fr)
 +  {
 +    FeatureRenderer fr = (FeatureRenderer) _fr;
 +    FeatureRendererSettings frs = new FeatureRendererSettings(fr);
 +    this.renderOrder = frs.renderOrder;
 +    this.featureGroups = frs.featureGroups;
 +    this.featureColours = frs.featureColours;
 +    this.transparency = frs.transparency;
 +    this.featureOrder = frs.featureOrder;
 +    if (av != null && av != fr.getViewport())
 +    {
 +      // copy over the displayed feature settings
 +      if (_fr.getFeaturesDisplayed() != null)
 +      {
 +        FeaturesDisplayedI fd = getFeaturesDisplayed();
 +        if (fd == null)
 +        {
 +          setFeaturesDisplayedFrom(_fr.getFeaturesDisplayed());
 +        }
 +        else
 +        {
 +          synchronized (fd)
 +          {
 +            fd.clear();
 +            java.util.Iterator<String> fdisp = _fr.getFeaturesDisplayed()
 +                    .getVisibleFeatures();
 +            while (fdisp.hasNext())
 +            {
 +              fd.setVisible(fdisp.next());
 +            }
 +          }
 +        }
 +      }
 +    }
 +  }
 +
 +  public void setFeaturesDisplayedFrom(FeaturesDisplayedI featuresDisplayed)
 +  {
 +    av.setFeaturesDisplayed(new FeaturesDisplayed(featuresDisplayed));
 +  }
 +
 +  @Override
 +  public void setVisible(String featureType)
 +  {
 +    FeaturesDisplayedI fdi = av.getFeaturesDisplayed();
 +    if (fdi == null)
 +    {
 +      av.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
 +    }
 +    if (!fdi.isRegistered(featureType))
 +    {
 +      pushFeatureType(Arrays.asList(new String[]
 +      { featureType }));
 +    }
 +    fdi.setVisible(featureType);
 +  }
 +
 +  @Override
 +  public void setAllVisible(List<String> featureTypes)
 +  {
 +    FeaturesDisplayedI fdi = av.getFeaturesDisplayed();
 +    if (fdi == null)
 +    {
 +      av.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
 +    }
 +    List<String> nft = new ArrayList<String>();
 +    for (String featureType : featureTypes)
 +    {
 +      if (!fdi.isRegistered(featureType))
 +      {
 +        nft.add(featureType);
 +      }
 +    }
 +    if (nft.size() > 0)
 +    {
 +      pushFeatureType(nft);
 +    }
 +    fdi.setAllVisible(featureTypes);
 +  }
 +
 +  /**
 +   * push a set of new types onto the render order stack. Note - this is a
 +   * direct mechanism rather than the one employed in updateRenderOrder
 +   * 
 +   * @param types
 +   */
 +  private void pushFeatureType(List<String> types)
 +  {
 +
 +    int ts = types.size();
 +    String neworder[] = new String[(renderOrder == null ? 0
 +            : renderOrder.length) + ts];
 +    types.toArray(neworder);
 +    if (renderOrder != null)
 +    {
 +      System.arraycopy(neworder,0,neworder,renderOrder.length,ts);
 +      System.arraycopy(renderOrder, 0, neworder, 0, renderOrder.length);
 +    }
 +    renderOrder = neworder;
 +  }
 +
 +  protected Hashtable minmax = new Hashtable();
 +
 +  public Hashtable getMinMax()
 +  {
 +    return minmax;
 +  }
 +
 +  /**
 +   * normalise a score against the max/min bounds for the feature type.
 +   * 
 +   * @param sequenceFeature
 +   * @return byte[] { signed, normalised signed (-127 to 127) or unsigned
 +   *         (0-255) value.
 +   */
 +  protected final byte[] normaliseScore(SequenceFeature sequenceFeature)
 +  {
 +    float[] mm = ((float[][]) minmax.get(sequenceFeature.type))[0];
 +    final byte[] r = new byte[]
 +    { 0, (byte) 255 };
 +    if (mm != null)
 +    {
 +      if (r[0] != 0 || mm[0] < 0.0)
 +      {
 +        r[0] = 1;
 +        r[1] = (byte) ((int) 128.0 + 127.0 * (sequenceFeature.score / mm[1]));
 +      }
 +      else
 +      {
 +        r[1] = (byte) ((int) 255.0 * (sequenceFeature.score / mm[1]));
 +      }
 +    }
 +    return r;
 +  }
 +
 +  boolean newFeatureAdded = false;
 +
 +  boolean findingFeatures = false;
 +
 +  protected boolean updateFeatures()
 +  {
 +    if (av.getFeaturesDisplayed() == null || renderOrder == null
 +            || newFeatureAdded)
 +    {
 +      findAllFeatures();
 +      if (av.getFeaturesDisplayed().getVisibleFeatureCount() < 1)
 +      {
 +        return false;
 +      }
 +    }
 +    // TODO: decide if we should check for the visible feature count first
 +    return true;
 +  }
 +
 +  /**
 +   * search the alignment for all new features, give them a colour and display
 +   * them. Then fires a PropertyChangeEvent on the changeSupport object.
 +   * 
 +   */
 +  protected void findAllFeatures()
 +  {
 +    synchronized (firing)
 +    {
 +      if (firing.equals(Boolean.FALSE))
 +      {
 +        firing = Boolean.TRUE;
 +        findAllFeatures(true); // add all new features as visible
 +        changeSupport.firePropertyChange("changeSupport", null, null);
 +        firing = Boolean.FALSE;
 +      }
 +    }
 +  }
 +
 +  @Override
 +  public List<SequenceFeature> findFeaturesAtRes(SequenceI sequence, int res)
 +  {
 +    ArrayList<SequenceFeature> tmp = new ArrayList<SequenceFeature>();
 +    SequenceFeature[] features = sequence.getSequenceFeatures();
 +    if (features != null)
 +    {
 +      for (int i = 0; i < features.length; i++)
 +      {
 +        if (!av.areFeaturesDisplayed()
 +                || !av.getFeaturesDisplayed().isVisible(
 +                        features[i].getType()))
 +        {
 +          continue;
 +        }
 +
 +        if (features[i].featureGroup != null
 +                && featureGroups != null
 +                && featureGroups.containsKey(features[i].featureGroup)
-                 && !((Boolean) featureGroups.get(features[i].featureGroup))
++                && !featureGroups.get(features[i].featureGroup)
 +                        .booleanValue())
++        {
 +          continue;
++        }
 +
 +        if ((features[i].getBegin() <= res)
 +                && (features[i].getEnd() >= res))
 +        {
 +          tmp.add(features[i]);
 +        }
 +      }
 +    }
 +    return tmp;
 +  }
 +
 +  /**
 +   * Searches alignment for all features and updates colours
 +   * 
 +   * @param newMadeVisible
 +   *          if true newly added feature types will be rendered immediatly
 +   *          TODO: check to see if this method should actually be proxied so
 +   *          repaint events can be propagated by the renderer code
 +   */
 +  @Override
 +  public synchronized void findAllFeatures(boolean newMadeVisible)
 +  {
 +    newFeatureAdded = false;
 +
 +    if (findingFeatures)
 +    {
 +      newFeatureAdded = true;
 +      return;
 +    }
 +
 +    findingFeatures = true;
 +    if (av.getFeaturesDisplayed() == null)
 +    {
 +      av.setFeaturesDisplayed(new FeaturesDisplayed());
 +    }
 +    FeaturesDisplayedI featuresDisplayed = av.getFeaturesDisplayed();
 +
 +    ArrayList<String> allfeatures = new ArrayList<String>();
 +    ArrayList<String> oldfeatures = new ArrayList<String>();
 +    if (renderOrder != null)
 +    {
 +      for (int i = 0; i < renderOrder.length; i++)
 +      {
 +        if (renderOrder[i] != null)
 +        {
 +          oldfeatures.add(renderOrder[i]);
 +        }
 +      }
 +    }
 +    if (minmax == null)
 +    {
 +      minmax = new Hashtable();
 +    }
 +    AlignmentI alignment = av.getAlignment();
 +    for (int i = 0; i < alignment.getHeight(); i++)
 +    {
 +      SequenceI asq = alignment.getSequenceAt(i);
 +      SequenceI dasq = asq.getDatasetSequence();
 +      SequenceFeature[] features = dasq != null ? dasq
 +              .getSequenceFeatures() : asq.getSequenceFeatures();
 +
 +      if (features == null)
 +      {
 +        continue;
 +      }
 +
 +      int index = 0;
 +      while (index < features.length)
 +      {
 +        if (!featuresDisplayed.isRegistered(features[index].getType()))
 +        {
 +          String fgrp = features[index].getFeatureGroup();
 +          if (fgrp != null)
 +          {
 +            Boolean groupDisplayed = featureGroups.get(fgrp);
 +            if (groupDisplayed == null)
 +            {
 +              groupDisplayed = Boolean.valueOf(newMadeVisible);
 +              featureGroups.put(fgrp, groupDisplayed);
 +            }
-             if (!((Boolean) groupDisplayed).booleanValue())
++            if (!groupDisplayed.booleanValue())
 +            {
 +              index++;
 +              continue;
 +            }
 +          }
 +          if (!(features[index].begin == 0 && features[index].end == 0))
 +          {
 +            // If beginning and end are 0, the feature is for the whole sequence
 +            // and we don't want to render the feature in the normal way
 +
 +            if (newMadeVisible
 +                    && !oldfeatures.contains(features[index].getType()))
 +            {
 +              // this is a new feature type on the alignment. Mark it for
 +              // display.
 +              featuresDisplayed.setVisible(features[index].getType());
 +              setOrder(features[index].getType(), 0);
 +            }
 +          }
 +        }
 +        if (!allfeatures.contains(features[index].getType()))
 +        {
 +          allfeatures.add(features[index].getType());
 +        }
 +        if (features[index].score != Float.NaN)
 +        {
 +          int nonpos = features[index].getBegin() >= 1 ? 0 : 1;
 +          float[][] mm = (float[][]) minmax.get(features[index].getType());
 +          if (mm == null)
 +          {
 +            mm = new float[][]
 +            { null, null };
 +            minmax.put(features[index].getType(), mm);
 +          }
 +          if (mm[nonpos] == null)
 +          {
 +            mm[nonpos] = new float[]
 +            { features[index].score, features[index].score };
 +
 +          }
 +          else
 +          {
 +            if (mm[nonpos][0] > features[index].score)
 +            {
 +              mm[nonpos][0] = features[index].score;
 +            }
 +            if (mm[nonpos][1] < features[index].score)
 +            {
 +              mm[nonpos][1] = features[index].score;
 +            }
 +          }
 +        }
 +        index++;
 +      }
 +    }
 +    updateRenderOrder(allfeatures);
 +    findingFeatures = false;
 +  }
 +
 +  protected Boolean firing = Boolean.FALSE;
 +
 +  /**
 +   * replaces the current renderOrder with the unordered features in
 +   * allfeatures. The ordering of any types in both renderOrder and allfeatures
 +   * is preserved, and all new feature types are rendered on top of the existing
 +   * types, in the order given by getOrder or the order given in allFeatures.
 +   * Note. this operates directly on the featureOrder hash for efficiency. TODO:
 +   * eliminate the float storage for computing/recalling the persistent ordering
 +   * New Cability: updates min/max for colourscheme range if its dynamic
 +   * 
 +   * @param allFeatures
 +   */
 +  private void updateRenderOrder(List<String> allFeatures)
 +  {
 +    List<String> allfeatures = new ArrayList<String>(allFeatures);
 +    String[] oldRender = renderOrder;
 +    renderOrder = new String[allfeatures.size()];
 +    Object mmrange, fc = null;
 +    boolean initOrders = (featureOrder == null);
 +    int opos = 0;
 +    if (oldRender != null && oldRender.length > 0)
 +    {
 +      for (int j = 0; j < oldRender.length; j++)
 +      {
 +        if (oldRender[j] != null)
 +        {
 +          if (initOrders)
 +          {
 +            setOrder(oldRender[j], (1 - (1 + (float) j)
-                     / (float) oldRender.length));
++                    / oldRender.length));
 +          }
 +          if (allfeatures.contains(oldRender[j]))
 +          {
 +            renderOrder[opos++] = oldRender[j]; // existing features always
 +            // appear below new features
 +            allfeatures.remove(oldRender[j]);
 +            if (minmax != null)
 +            {
 +              mmrange = minmax.get(oldRender[j]);
 +              if (mmrange != null)
 +              {
 +                fc = featureColours.get(oldRender[j]);
 +                if (fc != null && fc instanceof GraduatedColor
 +                        && ((GraduatedColor) fc).isAutoScale())
 +                {
 +                  ((GraduatedColor) fc).updateBounds(
 +                          ((float[][]) mmrange)[0][0],
 +                          ((float[][]) mmrange)[0][1]);
 +                }
 +              }
 +            }
 +          }
 +        }
 +      }
 +    }
 +    if (allfeatures.size() == 0)
 +    {
 +      // no new features - leave order unchanged.
 +      return;
 +    }
 +    int i = allfeatures.size() - 1;
 +    int iSize = i;
 +    boolean sort = false;
 +    String[] newf = new String[allfeatures.size()];
 +    float[] sortOrder = new float[allfeatures.size()];
 +    for (String newfeat : allfeatures)
 +    {
 +      newf[i] = newfeat;
 +      if (minmax != null)
 +      {
 +        // update from new features minmax if necessary
 +        mmrange = minmax.get(newf[i]);
 +        if (mmrange != null)
 +        {
 +          fc = featureColours.get(newf[i]);
 +          if (fc != null && fc instanceof GraduatedColor
 +                  && ((GraduatedColor) fc).isAutoScale())
 +          {
 +            ((GraduatedColor) fc).updateBounds(((float[][]) mmrange)[0][0],
 +                    ((float[][]) mmrange)[0][1]);
 +          }
 +        }
 +      }
 +      if (initOrders || !featureOrder.containsKey(newf[i]))
 +      {
 +        int denom = initOrders ? allfeatures.size() : featureOrder.size();
 +        // new unordered feature - compute persistent ordering at head of
 +        // existing features.
 +        setOrder(newf[i], i / (float) denom);
 +      }
 +      // set order from newly found feature from persisted ordering.
 +      sortOrder[i] = 2 - ((Float) featureOrder.get(newf[i])).floatValue();
 +      if (i < iSize)
 +      {
 +        // only sort if we need to
 +        sort = sort || sortOrder[i] > sortOrder[i + 1];
 +      }
 +      i--;
 +    }
 +    if (iSize > 1 && sort)
 +    {
 +      jalview.util.QuickSort.sort(sortOrder, newf);
 +    }
 +    sortOrder = null;
 +    System.arraycopy(newf, 0, renderOrder, opos, newf.length);
 +  }
 +
 +  /**
 +   * get a feature style object for the given type string. Creates a
 +   * java.awt.Color for a featureType with no existing colourscheme. TODO:
 +   * replace return type with object implementing standard abstract colour/style
 +   * interface
 +   * 
 +   * @param featureType
 +   * @return java.awt.Color or GraduatedColor
 +   */
 +  public Object getFeatureStyle(String featureType)
 +  {
 +    Object fc = featureColours.get(featureType);
 +    if (fc == null)
 +    {
 +      jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
 +      Color col = ucs.createColourFromName(featureType);
 +      featureColours.put(featureType, fc = col);
 +    }
 +    return fc;
 +  }
 +
 +  /**
 +   * return a nominal colour for this feature
 +   * 
 +   * @param featureType
 +   * @return standard color, or maximum colour for graduated colourscheme
 +   */
 +  public Color getColour(String featureType)
 +  {
 +    Object fc = getFeatureStyle(featureType);
 +
 +    if (fc instanceof Color)
 +    {
 +      return (Color) fc;
 +    }
 +    else
 +    {
 +      if (fc instanceof GraduatedColor)
 +      {
 +        return ((GraduatedColor) fc).getMaxColor();
 +      }
 +    }
 +    throw new Error("Implementation Error: Unrecognised render object "
 +            + fc.getClass() + " for features of type " + featureType);
 +  }
 +
 +  /**
 +   * calculate the render colour for a specific feature using current feature
 +   * settings.
 +   * 
 +   * @param feature
 +   * @return render colour for the given feature
 +   */
 +  public Color getColour(SequenceFeature feature)
 +  {
 +    Object fc = getFeatureStyle(feature.getType());
 +    if (fc instanceof Color)
 +    {
 +      return (Color) fc;
 +    }
 +    else
 +    {
 +      if (fc instanceof GraduatedColor)
 +      {
 +        return ((GraduatedColor) fc).findColor(feature);
 +      }
 +    }
 +    throw new Error("Implementation Error: Unrecognised render object "
 +            + fc.getClass() + " for features of type " + feature.getType());
 +  }
 +
 +  protected boolean showFeature(SequenceFeature sequenceFeature)
 +  {
 +    Object fc = getFeatureStyle(sequenceFeature.type);
 +    if (fc instanceof GraduatedColor)
 +    {
 +      return ((GraduatedColor) fc).isColored(sequenceFeature);
 +    }
 +    else
 +    {
 +      return true;
 +    }
 +  }
 +
 +  protected boolean showFeatureOfType(String type)
 +  {
 +    return av.getFeaturesDisplayed().isVisible(type);
 +  }
 +
 +  public void setColour(String featureType, Object col)
 +  {
 +    // overwrite
 +    // Color _col = (col instanceof Color) ? ((Color) col) : (col instanceof
 +    // GraduatedColor) ? ((GraduatedColor) col).getMaxColor() : null;
 +    // Object c = featureColours.get(featureType);
 +    // if (c == null || c instanceof Color || (c instanceof GraduatedColor &&
 +    // !((GraduatedColor)c).getMaxColor().equals(_col)))
 +    {
 +      featureColours.put(featureType, col);
 +    }
 +  }
 +
 +  public void setTransparency(float value)
 +  {
 +    transparency = value;
 +  }
 +
 +  public float getTransparency()
 +  {
 +    return transparency;
 +  }
 +
 +  Map featureOrder = null;
 +
 +  /**
 +   * analogous to colour - store a normalized ordering for all feature types in
 +   * this rendering context.
 +   * 
 +   * @param type
 +   *          Feature type string
 +   * @param position
 +   *          normalized priority - 0 means always appears on top, 1 means
 +   *          always last.
 +   */
 +  public float setOrder(String type, float position)
 +  {
 +    if (featureOrder == null)
 +    {
 +      featureOrder = new Hashtable();
 +    }
 +    featureOrder.put(type, new Float(position));
 +    return position;
 +  }
 +
 +  /**
 +   * get the global priority (0 (top) to 1 (bottom))
 +   * 
 +   * @param type
 +   * @return [0,1] or -1 for a type without a priority
 +   */
 +  public float getOrder(String type)
 +  {
 +    if (featureOrder != null)
 +    {
 +      if (featureOrder.containsKey(type))
 +      {
 +        return ((Float) featureOrder.get(type)).floatValue();
 +      }
 +    }
 +    return -1;
 +  }
 +
 +  @Override
-   public Map getFeatureColours()
++  public Map<String, Object> getFeatureColours()
 +  {
-     return new ConcurrentHashMap<>(featureColours);
++    return new ConcurrentHashMap<String, Object>(featureColours);
 +  }
 +
 +  /**
 +   * Replace current ordering with new ordering
 +   * 
 +   * @param data
 +   *          { String(Type), Colour(Type), Boolean(Displayed) }
 +   */
 +  public void setFeaturePriority(Object[][] data)
 +  {
 +    setFeaturePriority(data, true);
 +  }
 +
 +  /**
 +   * 
 +   * @param data
 +   *          { String(Type), Colour(Type), Boolean(Displayed) }
 +   * @param visibleNew
 +   *          when true current featureDisplay list will be cleared
 +   */
 +  public void setFeaturePriority(Object[][] data, boolean visibleNew)
 +  {
 +    FeaturesDisplayedI av_featuresdisplayed = null;
 +    if (visibleNew)
 +    {
 +      if ((av_featuresdisplayed = av.getFeaturesDisplayed()) != null)
 +      {
 +        av.getFeaturesDisplayed().clear();
 +      }
 +      else
 +      {
 +        av.setFeaturesDisplayed(av_featuresdisplayed = new FeaturesDisplayed());
 +      }
 +    }
 +    else
 +    {
 +      av_featuresdisplayed = av.getFeaturesDisplayed();
 +    }
 +    if (data == null)
 +    {
 +      return;
 +    }
 +    // The feature table will display high priority
 +    // features at the top, but theses are the ones
 +    // we need to render last, so invert the data
 +    renderOrder = new String[data.length];
 +
 +    if (data.length > 0)
 +    {
 +      for (int i = 0; i < data.length; i++)
 +      {
 +        String type = data[i][0].toString();
 +        setColour(type, data[i][1]); // todo : typesafety - feature color
 +        // interface object
 +        if (((Boolean) data[i][2]).booleanValue())
 +        {
 +          av_featuresdisplayed.setVisible(type);
 +        }
 +
 +        renderOrder[data.length - i - 1] = type;
 +      }
 +    }
 +
 +  }
 +
 +  /**
 +   * @param listener
 +   * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
 +   */
 +  public void addPropertyChangeListener(PropertyChangeListener listener)
 +  {
 +    changeSupport.addPropertyChangeListener(listener);
 +  }
 +
 +  /**
 +   * @param listener
 +   * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
 +   */
 +  public void removePropertyChangeListener(PropertyChangeListener listener)
 +  {
 +    changeSupport.removePropertyChangeListener(listener);
 +  }
 +
 +  public Set getAllFeatureColours()
 +  {
 +    return featureColours.keySet();
 +  }
 +
 +  public void clearRenderOrder()
 +  {
 +    renderOrder = null;
 +  }
 +
 +  public boolean hasRenderOrder()
 +  {
 +    return renderOrder != null;
 +  }
 +
 +  public List<String> getRenderOrder()
 +  {
 +    if (renderOrder == null)
 +    {
 +      return Arrays.asList(new String[]
 +      {});
 +    }
 +    return Arrays.asList(renderOrder);
 +  }
 +
 +  public int getFeatureGroupsSize()
 +  {
 +    return featureGroups != null ? 0 : featureGroups.size();
 +  }
 +
 +  @Override
 +  public List<String> getFeatureGroups()
 +  {
 +    // conflict between applet and desktop - featureGroups returns the map in
 +    // the desktop featureRenderer
 +    return (featureGroups == null) ? Arrays.asList(new String[0]) : Arrays
 +            .asList(featureGroups.keySet().toArray(new String[0]));
 +  }
 +
 +  public boolean checkGroupVisibility(String group, boolean newGroupsVisible)
 +  {
 +    if (featureGroups == null)
 +    {
 +      // then an exception happens next..
 +    }
 +    if (featureGroups.containsKey(group))
 +    {
-       return ((Boolean) featureGroups.get(group)).booleanValue();
++      return featureGroups.get(group).booleanValue();
 +    }
 +    if (newGroupsVisible)
 +    {
 +      featureGroups.put(group, new Boolean(true));
 +      return true;
 +    }
 +    return false;
 +  }
 +
 +  /**
 +   * get visible or invisible groups
 +   * 
 +   * @param visible
 +   *          true to return visible groups, false to return hidden ones.
 +   * @return list of groups
 +   */
 +  @Override
 +  public List getGroups(boolean visible)
 +  {
 +    if (featureGroups != null)
 +    {
 +      ArrayList gp = new ArrayList();
 +
 +      for (Object grp : featureGroups.keySet())
 +      {
-         Boolean state = (Boolean) featureGroups.get(grp);
++        Boolean state = featureGroups.get(grp);
 +        if (state.booleanValue() == visible)
 +        {
 +          gp.add(grp);
 +        }
 +      }
 +      return gp;
 +    }
 +    return null;
 +  }
 +
 +  @Override
 +  public void setGroupVisibility(String group, boolean visible)
 +  {
 +    featureGroups.put(group, new Boolean(visible));
 +  }
 +
 +  @Override
 +  public void setGroupVisibility(List<String> toset, boolean visible)
 +  {
 +    if (toset != null && toset.size() > 0 && featureGroups != null)
 +    {
 +      boolean rdrw = false;
 +      for (String gst : toset)
 +      {
 +        Boolean st = featureGroups.get(gst);
 +        featureGroups.put(gst, new Boolean(visible));
 +        if (st != null)
 +        {
-           rdrw = rdrw || (visible != ((Boolean) st).booleanValue());
++          rdrw = rdrw || (visible != st.booleanValue());
 +        }
 +      }
 +      if (rdrw)
 +      {
 +        // set local flag indicating redraw needed ?
 +      }
 +    }
 +  }
 +
 +  @Override
 +  public Hashtable getDisplayedFeatureCols()
 +  {
 +    Hashtable fcols = new Hashtable();
 +    if (getViewport().getFeaturesDisplayed() == null)
 +    {
 +      return fcols;
 +    }
 +    Iterator<String> en = getViewport().getFeaturesDisplayed()
 +            .getVisibleFeatures();
 +    while (en.hasNext())
 +    {
 +      String col = en.next();
 +      fcols.put(col, getColour(col));
 +    }
 +    return fcols;
 +  }
 +
 +  @Override
 +  public FeaturesDisplayedI getFeaturesDisplayed()
 +  {
 +    return av.getFeaturesDisplayed();
 +  }
 +
 +  @Override
 +  public String[] getDisplayedFeatureTypes()
 +  {
 +    String[] typ = null;
 +    typ = getRenderOrder().toArray(new String[0]);
 +    FeaturesDisplayedI feature_disp = av.getFeaturesDisplayed();
 +    if (feature_disp != null)
 +    {
 +      synchronized (feature_disp)
 +      {
 +        for (int i = 0; i < typ.length; i++)
 +        {
 +          if (feature_disp.isVisible(typ[i]))
 +          {
 +            typ[i] = null;
 +          }
 +        }
 +      }
 +    }
 +    return typ;
 +  }
 +
 +  @Override
 +  public String[] getDisplayedFeatureGroups()
 +  {
 +    String[] gps = null;
 +    ArrayList<String> _gps = new ArrayList<String>();
 +    Iterator en = getFeatureGroups().iterator();
 +    int g = 0;
 +    boolean valid = false;
 +    while (en.hasNext())
 +    {
 +      String gp = (String) en.next();
 +      if (checkGroupVisibility(gp, false))
 +      {
 +        valid = true;
 +        _gps.add(gp);
 +      }
 +      if (!valid)
 +      {
 +        return null;
 +      }
 +      else
 +      {
 +        gps = new String[_gps.size()];
 +        _gps.toArray(gps);
 +      }
 +    }
 +    return gps;
 +  }
 +
 +}
@@@ -28,7 -28,8 +28,8 @@@ import jalview.datamodel.AlignmentView
  import jalview.datamodel.SequenceI;
  import jalview.gui.AlignFrame;
  import jalview.gui.WebserviceInfo;
 -import jalview.gui.FeatureRenderer.FeatureRendererSettings;
 +import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
+ import jalview.util.MessageManager;
  
  public abstract class AWSThread extends Thread
  {
            } catch (Exception ex)
            {
              // Deal with Transaction exceptions
-             wsInfo.appendProgressText(jobs[j].jobnum, "\n" + WebServiceName
-                     + " Server exception!\n" + ex.getMessage());
+             wsInfo.appendProgressText(jobs[j].jobnum, 
+                       MessageManager.formatMessage("info.server_exception", new String[]{WebServiceName,ex.getMessage()}));
              // always output the exception's stack trace to the log
              Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum
                      + ") Server exception.");
        Cache.log
                .debug("WebServiceJob poll loop finished with no jobs created.");
        wsInfo.setStatus(wsInfo.STATE_STOPPED_ERROR);
-       wsInfo.appendProgressText("No jobs ran.");
+       wsInfo.appendProgressText(MessageManager.getString("info.no_jobs_ran"));
        wsInfo.setFinishedNoResults();
      }
    }