JAL-3438 spotless for 2.11.2.0
[jalview.git] / src / jalview / appletgui / FeatureRenderer.java
old mode 100755 (executable)
new mode 100644 (file)
index 50bb665..5f5e8e0
@@ -1,32 +1,59 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
- * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ 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.
- * 
+ * 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/>.
+ * 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.appletgui;
 
-import java.util.*;
-
-import java.awt.*;
-
-import java.awt.event.*;
-
-import jalview.appletgui.FeatureSettings.MyCheckbox;
-import jalview.datamodel.*;
-import jalview.schemes.AnnotationColourGradient;
-import jalview.schemes.GraduatedColor;
+import jalview.api.FeatureColourI;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.io.FeaturesFile;
+import jalview.schemes.FeatureColour;
+import jalview.util.ColorUtils;
+import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
+
+import java.awt.BorderLayout;
+import java.awt.Button;
+import java.awt.Choice;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.GridLayout;
+import java.awt.Label;
+import java.awt.Panel;
+import java.awt.ScrollPane;
+import java.awt.TextArea;
+import java.awt.TextField;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.TextEvent;
+import java.awt.event.TextListener;
+import java.util.Hashtable;
+import java.util.List;
 
 /**
  * DOCUMENT ME!
@@ -35,63 +62,31 @@ import jalview.schemes.GraduatedColor;
  * @version $Revision$
  */
 public class FeatureRenderer
+        extends jalview.renderer.seqfeatures.FeatureRenderer
 {
-  AlignViewport av;
-
-  Hashtable featureColours = new Hashtable();
+  /*
+   * creating a new feature defaults to the type and group as
+   * the last one created
+   */
+  static String lastFeatureAdded = "feature_1";
 
-  // A higher level for grouping features of a
-  // particular type
-  Hashtable featureGroups = null;
+  static String lastFeatureGroupAdded = "Jalview";
 
   // 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.
    * 
    * @param av
-   *          DOCUMENT ME!
    */
-  public FeatureRenderer(AlignViewport av)
+  public FeatureRenderer(AlignmentViewport av)
   {
-    this.av = av;
-
-    if (!System.getProperty("java.version").startsWith("1.1"))
-    {
-      transparencySetter = new TransparencySetter();
-    }
-  }
+    super(av);
 
-  public void transferSettings(FeatureRenderer fr)
-  {
-    renderOrder = fr.renderOrder;
-    featureGroups = fr.featureGroups;
-    featureColours = fr.featureColours;
-    transparency = fr.transparency;
   }
 
-  static String lastFeatureAdded;
-
-  static String lastFeatureGroupAdded;
-
-  static String lastDescriptionAdded;
-
   int featureIndex = 0;
 
   boolean deleteFeature = false;
@@ -109,49 +104,35 @@ public class FeatureRenderer
     /**
      * render a feature style in the amend feature dialog box
      */
-    public void updateColor(Object newcol)
+    public void updateColor(FeatureColourI newcol)
     {
-
-      Color bg, col = null;
-      GraduatedColor gcol = null;
+      Color bg = null;
       String vlabel = "";
-      if (newcol instanceof Color)
-      {
-        isGcol = false;
-        col = (Color) newcol;
-        gcol = null;
-      }
-      else if (newcol instanceof GraduatedColor)
+      if (newcol.isSimpleColour())
       {
-        isGcol = true;
-        gcol = (GraduatedColor) newcol;
-        col = null;
+        bg = newcol.getColour();
+        setBackground(bg);
       }
       else
       {
-        throw new Error("Invalid color for MyCheckBox");
-      }
-      if (col != null)
-      {
-        setBackground(bg = col);
-      }
-      else
-      {
-        if (gcol.getThreshType() != AnnotationColourGradient.NO_THRESHOLD)
+        if (newcol.isAboveThreshold())
         {
-          vlabel += " "
-                  + ((gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD) ? "(>)"
-                          : "(<)");
+          vlabel += " (>)";
         }
-        if (isColourByLabel = gcol.isColourByLabel())
+        else if (newcol.isBelowThreshold())
+        {
+          vlabel += " (<)";
+        }
+
+        if (isColourByLabel = newcol.isColourByLabel())
         {
           setBackground(bg = Color.white);
           vlabel += " (by Label)";
         }
         else
         {
-          setBackground(bg = gcol.getMinColor());
-          maxCol = gcol.getMaxColor();
+          setBackground(bg = newcol.getMinColour());
+          maxCol = newcol.getMaxColour();
         }
       }
       label = vlabel;
@@ -164,6 +145,7 @@ public class FeatureRenderer
       super(null);
     }
 
+    @Override
     public void paint(Graphics g)
     {
       Dimension d = getSize();
@@ -176,7 +158,7 @@ public class FeatureRenderer
           g.setColor(Color.black);
           Font f = new Font("Verdana", Font.PLAIN, 10);
           g.setFont(f);
-          g.drawString("Label", 0, 0);
+          g.drawString(MessageManager.getString("label.label"), 0, 0);
         }
         else
         {
@@ -189,13 +171,25 @@ public class FeatureRenderer
 
   }
 
-  boolean amendFeatures(final SequenceI[] sequences,
-          final SequenceFeature[] features, boolean newFeatures,
+  /**
+   * Shows a dialog allowing the user to create, or amend or delete, sequence
+   * features. If null in the supplied feature(s), feature type and group
+   * default to those for the last feature created (with initial defaults of
+   * "feature_1" and "Jalview").
+   * 
+   * @param sequences
+   * @param features
+   * @param create
+   * @param ap
+   * @return
+   */
+  boolean amendFeatures(final List<SequenceI> sequences,
+          final List<SequenceFeature> features, boolean create,
           final AlignmentPanel ap)
   {
-    Panel bigPanel = new Panel(new BorderLayout());
+    final Panel bigPanel = new Panel(new BorderLayout());
     final TextField name = new TextField(16);
-    final TextField source = new TextField(16);
+    final TextField group = new TextField(16);
     final TextArea description = new TextArea(3, 35);
     final TextField start = new TextField(8);
     final TextField end = new TextField(8);
@@ -203,6 +197,22 @@ public class FeatureRenderer
     Button deleteButton = new Button("Delete");
     deleteFeature = false;
 
+    name.addTextListener(new TextListener()
+    {
+      @Override
+      public void textValueChanged(TextEvent e)
+      {
+        warnIfTypeHidden(ap.alignFrame, name.getText());
+      }
+    });
+    group.addTextListener(new TextListener()
+    {
+      @Override
+      public void textValueChanged(TextEvent e)
+      {
+        warnIfGroupHidden(ap.alignFrame, group.getText());
+      }
+    });
     colourPanel = new FeatureColourPanel();
     colourPanel.setSize(110, 15);
     final FeatureRenderer fr = this;
@@ -214,20 +224,20 @@ public class FeatureRenderer
 
     // /////////////////////////////////////
     // /MULTIPLE FEATURES AT SELECTED RESIDUE
-    if (!newFeatures && features.length > 1)
+    if (!create && features.size() > 1)
     {
       panel = new Panel(new GridLayout(4, 1));
       tmp = new Panel();
       tmp.add(new Label("Select Feature: "));
       overlaps = new Choice();
-      for (int i = 0; i < features.length; i++)
+      for (SequenceFeature sf : features)
       {
-        String item = features[i].getType() + "/" + features[i].getBegin()
-                + "-" + features[i].getEnd();
-
-        if (features[i].getFeatureGroup() != null)
-          item += " (" + features[i].getFeatureGroup() + ")";
-
+        String item = sf.getType() + "/" + sf.getBegin() + "-"
+                + sf.getEnd();
+        if (sf.getFeatureGroup() != null)
+        {
+          item += " (" + sf.getFeatureGroup() + ")";
+        }
         overlaps.addItem(item);
       }
 
@@ -235,30 +245,33 @@ public class FeatureRenderer
 
       overlaps.addItemListener(new java.awt.event.ItemListener()
       {
+        @Override
         public void itemStateChanged(java.awt.event.ItemEvent e)
         {
           int index = overlaps.getSelectedIndex();
           if (index != -1)
           {
             featureIndex = index;
-            name.setText(features[index].getType());
-            description.setText(features[index].getDescription());
-            source.setText(features[index].getFeatureGroup());
-            start.setText(features[index].getBegin() + "");
-            end.setText(features[index].getEnd() + "");
+            SequenceFeature sf = features.get(index);
+            name.setText(sf.getType());
+            description.setText(sf.getDescription());
+            group.setText(sf.getFeatureGroup());
+            start.setText(sf.getBegin() + "");
+            end.setText(sf.getEnd() + "");
 
-            SearchResults highlight = new SearchResults();
-            highlight.addResult(sequences[0], features[index].getBegin(),
-                    features[index].getEnd());
+            SearchResultsI highlight = new SearchResults();
+            highlight.addResult(sequences.get(0), sf.getBegin(),
+                    sf.getEnd());
 
             ap.seqPanel.seqCanvas.highlightSearchResults(highlight);
 
           }
-          Object col = getFeatureStyle(name.getText());
+          FeatureColourI col = getFeatureStyle(name.getText());
           if (col == null)
           {
-            col = new jalview.schemes.UserColourScheme()
+            Color generatedColour = ColorUtils
                     .createColourFromName(name.getText());
+            col = new FeatureColour(generatedColour);
           }
 
           colourPanel.updateColor(col);
@@ -272,33 +285,39 @@ public class FeatureRenderer
 
     tmp = new Panel();
     panel.add(tmp);
-    tmp.add(new Label("Name: ", Label.RIGHT));
+    tmp.add(new Label(MessageManager.getString("label.name:"),
+            Label.RIGHT));
     tmp.add(name);
 
     tmp = new Panel();
     panel.add(tmp);
-    tmp.add(new Label("Group: ", Label.RIGHT));
-    tmp.add(source);
+    tmp.add(new Label(MessageManager.getString("label.group:"),
+            Label.RIGHT));
+    tmp.add(group);
 
     tmp = new Panel();
     panel.add(tmp);
-    tmp.add(new Label("Colour: ", Label.RIGHT));
+    tmp.add(new Label(MessageManager.getString("label.colour"),
+            Label.RIGHT));
     tmp.add(colourPanel);
 
     bigPanel.add(panel, BorderLayout.NORTH);
 
     panel = new Panel();
-    panel.add(new Label("Description: ", Label.RIGHT));
+    panel.add(new Label(MessageManager.getString("label.description:"),
+            Label.RIGHT));
     panel.add(new ScrollPane().add(description));
 
-    if (!newFeatures)
+    if (!create)
     {
       bigPanel.add(panel, BorderLayout.SOUTH);
 
       panel = new Panel();
-      panel.add(new Label(" Start:", Label.RIGHT));
+      panel.add(new Label(MessageManager.getString("label.start"),
+              Label.RIGHT));
       panel.add(start);
-      panel.add(new Label("  End:", Label.RIGHT));
+      panel.add(new Label(MessageManager.getString("label.end"),
+              Label.RIGHT));
       panel.add(end);
       bigPanel.add(panel, BorderLayout.CENTER);
     }
@@ -307,76 +326,60 @@ public class FeatureRenderer
       bigPanel.add(panel, BorderLayout.CENTER);
     }
 
-    if (lastFeatureAdded == null)
-    {
-      if (features[0].type != null)
-      {
-        lastFeatureAdded = features[0].type;
-      }
-      else
-      {
-        lastFeatureAdded = "feature_1";
-      }
-    }
-
-    if (lastFeatureGroupAdded == null)
-    {
-      if (features[0].featureGroup != null)
-      {
-        lastFeatureGroupAdded = features[0].featureGroup;
-      }
-      else
-      {
-        lastFeatureAdded = "Jalview";
-      }
-    }
-
-    String title = newFeatures ? "Create New Sequence Feature(s)"
-            : "Amend/Delete Features for " + sequences[0].getName();
+    /*
+     * use defaults for type and group (and update them on Confirm) only
+     * if feature type has not been supplied by the caller
+     * (e.g. for Amend, or create features from Find) 
+     */
+    SequenceFeature firstFeature = features.get(0);
+    boolean useLastDefaults = firstFeature.getType() == null;
+    String featureType = useLastDefaults ? lastFeatureAdded
+            : firstFeature.getType();
+    String featureGroup = useLastDefaults ? lastFeatureGroupAdded
+            : firstFeature.getFeatureGroup();
+
+    String title = create
+            ? MessageManager.getString("label.create_new_sequence_features")
+            : MessageManager.formatMessage("label.amend_delete_features",
+                    new String[]
+                    { sequences.get(0).getName() });
 
     final JVDialog dialog = new JVDialog(ap.alignFrame, title, true, 385,
             240);
 
     dialog.setMainPanel(bigPanel);
 
-    if (newFeatures)
-    {
-      name.setText(lastFeatureAdded);
-      source.setText(lastFeatureGroupAdded);
-    }
-    else
+    name.setText(featureType);
+    group.setText(featureGroup);
+
+    if (!create)
     {
-      dialog.ok.setLabel("Amend");
+      dialog.ok.setLabel(MessageManager.getString("label.amend"));
       dialog.buttonPanel.add(deleteButton, 1);
       deleteButton.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent evt)
         {
           deleteFeature = true;
           dialog.setVisible(false);
         }
       });
-      name.setText(features[0].getType());
-      source.setText(features[0].getFeatureGroup());
     }
 
-    start.setText(features[0].getBegin() + "");
-    end.setText(features[0].getEnd() + "");
-    description.setText(features[0].getDescription());
-    Color col = getColour(name.getText());
-    if (col == null)
-    {
-      col = new jalview.schemes.UserColourScheme()
-              .createColourFromName(name.getText());
-    }
-    Object fcol = getFeatureStyle(name.getText());
+    start.setText(firstFeature.getBegin() + "");
+    end.setText(firstFeature.getEnd() + "");
+    description.setText(firstFeature.getDescription());
+    // lookup (or generate) the feature colour
+    FeatureColourI fcol = getFeatureStyle(name.getText());
     // simply display the feature color in a box
     colourPanel.updateColor(fcol);
     dialog.setResizable(true);
     // TODO: render the graduated color in the box.
-    colourPanel.addMouseListener(new java.awt.event.MouseAdapter()
+    colourPanel.addMouseListener(new MouseAdapter()
     {
-      public void mousePressed(java.awt.event.MouseEvent evt)
+      @Override
+      public void mousePressed(MouseEvent evt)
       {
         if (!colourPanel.isGcol)
         {
@@ -384,102 +387,98 @@ public class FeatureRenderer
         }
         else
         {
-          FeatureColourChooser fcc = new FeatureColourChooser(
-                  ap.alignFrame, name.getText());
+          new FeatureColourChooser(ap.alignFrame, name.getText());
           dialog.transferFocus();
         }
       }
     });
     dialog.setVisible(true);
 
-    jalview.io.FeaturesFile ffile = new jalview.io.FeaturesFile();
+    FeaturesFile ffile = new FeaturesFile();
 
-    if (dialog.accept)
+    /*
+     * only update default type and group if we used defaults
+     */
+    final String enteredType = name.getText().trim();
+    final String enteredGroup = group.getText().trim();
+    final String enteredDesc = description.getText().replace('\n', ' ');
+
+    if (dialog.accept && useLastDefaults)
     {
-      // This ensures that the last sequence
-      // is refreshed and new features are rendered
-      lastSeq = null;
-      lastFeatureAdded = name.getText().trim();
-      lastFeatureGroupAdded = source.getText().trim();
-      lastDescriptionAdded = description.getText().replace('\n', ' ');
+      lastFeatureAdded = enteredType;
+      lastFeatureGroupAdded = enteredGroup;
     }
 
-    if (lastFeatureGroupAdded != null && lastFeatureGroupAdded.length() < 1)
-      lastFeatureGroupAdded = null;
-
-    if (!newFeatures)
+    if (!create)
     {
-
-      SequenceFeature sf = features[featureIndex];
+      SequenceFeature sf = features.get(featureIndex);
       if (dialog.accept)
       {
-        sf.type = lastFeatureAdded;
-        sf.featureGroup = lastFeatureGroupAdded;
-        sf.description = lastDescriptionAdded;
         if (!colourPanel.isGcol)
         {
           // update colour - otherwise its already done.
-          setColour(sf.type, colourPanel.getBackground());
+          setColour(enteredType,
+                  new FeatureColour(colourPanel.getBackground()));
         }
+        int newBegin = sf.begin;
+        int newEnd = sf.end;
         try
         {
-          sf.begin = Integer.parseInt(start.getText());
-          sf.end = Integer.parseInt(end.getText());
+          newBegin = Integer.parseInt(start.getText());
+          newEnd = Integer.parseInt(end.getText());
         } catch (NumberFormatException ex)
         {
+          //
         }
 
+        /*
+         * replace the feature by deleting it and adding a new one
+         * (to ensure integrity of SequenceFeatures data store)
+         */
+        sequences.get(0).deleteFeature(sf);
+        SequenceFeature newSf = new SequenceFeature(sf, enteredType,
+                newBegin, newEnd, enteredGroup, sf.getScore());
+        newSf.setDescription(enteredDesc);
+        ffile.parseDescriptionHTML(newSf, false);
+        // amend features dialog only updates one sequence at a time
+        sequences.get(0).addSequenceFeature(newSf);
+        boolean typeOrGroupChanged = (!featureType.equals(newSf.getType())
+                || !featureGroup.equals(newSf.getFeatureGroup()));
+
         ffile.parseDescriptionHTML(sf, false);
+        if (typeOrGroupChanged)
+        {
+          featuresAdded();
+        }
       }
       if (deleteFeature)
       {
-        sequences[0].deleteFeature(sf);
+        sequences.get(0).deleteFeature(sf);
+        // ensure Feature Settings reflects removal of feature / group
+        featuresAdded();
       }
-
     }
     else
     {
+      /*
+       * adding feature(s)
+       */
       if (dialog.accept && name.getText().length() > 0)
       {
-        for (int i = 0; i < sequences.length; i++)
+        for (int i = 0; i < sequences.size(); i++)
         {
-          features[i].type = lastFeatureAdded;
-          features[i].featureGroup = lastFeatureGroupAdded;
-          features[i].description = lastDescriptionAdded;
-          sequences[i].addSequenceFeature(features[i]);
-          ffile.parseDescriptionHTML(features[i], false);
+          SequenceFeature sf = features.get(i);
+          SequenceFeature sf2 = new SequenceFeature(enteredType,
+                  enteredDesc, sf.getBegin(), sf.getEnd(), enteredGroup);
+          ffile.parseDescriptionHTML(sf2, false);
+          sequences.get(i).addSequenceFeature(sf2);
         }
 
-        if (av.featuresDisplayed == null)
-        {
-          av.featuresDisplayed = new Hashtable();
-        }
-
-        if (featureGroups == null)
-        {
-          featureGroups = new Hashtable();
-        }
-
-        col = colourPanel.getBackground();
+        Color newColour = colourPanel.getBackground();
         // setColour(lastFeatureAdded, fcol);
 
-        if (lastFeatureGroupAdded != null)
-        {
-          featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
-        }
-        if (fcol instanceof Color)
-        {
-          setColour(lastFeatureAdded, fcol);
-        }
-        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(enteredType, new FeatureColour(newColour)); // was fcol
+        featuresAdded();
       }
       else
       {
@@ -488,748 +487,53 @@ public class FeatureRenderer
       }
     }
     // 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();
 
-    ap.paintAlignment(true);
+    ap.paintAlignment(true, true);
 
     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;
-
-  void findAllFeatures()
-  {
-    jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
-
-    av.featuresDisplayed = new Hashtable();
-    Vector allfeatures = new Vector();
-    minmax = new Hashtable();
-
-    for (int i = 0; i < av.alignment.getHeight(); i++)
-    {
-      SequenceFeature[] features = av.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("Implementation Error: Unrecognised render object "
-            + fc.getClass() + " for features of type " + 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)
+  protected void warnIfGroupHidden(Frame frame, String group)
   {
-    buildGroupHash();
-    if (featureGroups != null)
+    if (featureGroups.containsKey(group) && !featureGroups.get(group))
     {
-      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;
+      String msg = MessageManager.formatMessage("label.warning_hidden",
+              MessageManager.getString("label.group"), group);
+      showWarning(frame, msg);
     }
-    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)
+  protected void warnIfTypeHidden(Frame frame, String type)
   {
-    buildGroupHash();
-    if (toset != null && toset.length > 0 && featureGroups != null)
+    if (getRenderOrder().contains(type))
     {
-      boolean rdrw = false;
-      for (int i = 0; i < toset.length; i++)
+      if (!showFeatureOfType(type))
       {
-        Object st = featureGroups.get(toset[i]);
-        if (st != null)
-        {
-          featureGroups.put(toset[i], new Boolean(visible));
-          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);
-        }
+        String msg = MessageManager.formatMessage("label.warning_hidden",
+                MessageManager.getString("label.feature_type"), type);
+        showWarning(frame, msg);
       }
     }
   }
 
   /**
-   * analyse alignment for groups and hash tables (used to be embedded in
-   * FeatureSettings.setTableData)
-   * 
-   * @return true if features are on the alignment
+   * @param frame
+   * @param msg
    */
-  public boolean buildGroupHash()
-  {
-    boolean alignmentHasFeatures = false;
-    if (featureGroups == null)
-    {
-      featureGroups = new Hashtable();
-    }
-    Vector allFeatures = new Vector();
-    Vector allGroups = new Vector();
-    SequenceFeature[] tmpfeatures;
-    String group;
-    for (int i = 0; i < av.alignment.getHeight(); i++)
-    {
-      if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)
-      {
-        continue;
-      }
-
-      alignmentHasFeatures = true;
-
-      tmpfeatures = av.alignment.getSequenceAt(i).getSequenceFeatures();
-      int index = 0;
-      while (index < tmpfeatures.length)
-      {
-        if (tmpfeatures[index].getFeatureGroup() != null)
-        {
-          group = tmpfeatures[index].featureGroup;
-          if (!allGroups.contains(group))
-          {
-            allGroups.addElement(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.addElement(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;
-    for (int i = 0; i < av.alignment.getHeight(); i++)
-    {
-      if (av.alignment.getSequenceAt(i).getSequenceFeatures() == null)
-      {
-        continue;
-      }
-
-      alignmentHasFeatures = true;
-
-      tmpfeatures = av.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)
+  protected void showWarning(Frame frame, String msg)
   {
-    Graphics2D g2 = (Graphics2D) g;
-    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
-            value));
+    JVDialog d = new JVDialog(frame, "", true, 350, 200);
+    Panel mp = new Panel();
+    d.ok.setLabel(MessageManager.getString("action.ok"));
+    d.cancel.setVisible(false);
+    mp.setLayout(new FlowLayout());
+    mp.add(new Label(msg));
+    d.setMainPanel(mp);
+    d.setVisible(true);
   }
 }