added Platform settings separating mouse use of win/mac difference
[jalview.git] / src / jalview / gui / FeatureSettings.java
index fedfe3f..26cb104 100644 (file)
  */
 package jalview.gui;
 
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.COLOUR_COLUMN;
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.FILTER_COLUMN;
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.SHOW_COLUMN;
-import static jalview.viewmodel.seqfeatures.FeatureRendererModel.TYPE_COLUMN;
-
 import jalview.api.FeatureColourI;
 import jalview.api.FeatureSettingsControllerI;
-import jalview.bin.Cache;
+import jalview.bin.Jalview;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureMatcher;
+import jalview.datamodel.features.FeatureMatcherI;
+import jalview.datamodel.features.FeatureMatcherSet;
+import jalview.datamodel.features.FeatureMatcherSetI;
 import jalview.gui.Help.HelpId;
+import jalview.gui.JalviewColourChooser.ColourChooserListener;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
-import jalview.schemabinding.version2.JalviewUserColours;
 import jalview.schemes.FeatureColour;
-import jalview.util.Format;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
-import jalview.util.QuickSort;
-import jalview.util.matcher.KeyedMatcherSet;
-import jalview.util.matcher.KeyedMatcherSetI;
-import jalview.viewmodel.AlignmentViewport;
-import jalview.ws.DasSequenceFeatureFetcher;
-import jalview.ws.dbsources.das.api.jalviewSourceI;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
+import jalview.xml.binding.jalview.JalviewUserColours;
+import jalview.xml.binding.jalview.JalviewUserColours.Colour;
+import jalview.xml.binding.jalview.JalviewUserColours.Filter;
+import jalview.xml.binding.jalview.ObjectFactory;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -70,13 +67,14 @@ import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 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 javax.help.HelpSetException;
 import javax.swing.AbstractCellEditor;
@@ -85,8 +83,6 @@ 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;
@@ -98,32 +94,44 @@ import javax.swing.JSlider;
 import javax.swing.JTable;
 import javax.swing.ListSelectionModel;
 import javax.swing.SwingConstants;
-import javax.swing.SwingUtilities;
+import javax.swing.ToolTipManager;
+import javax.swing.border.Border;
 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;
 import javax.swing.table.TableColumn;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.Marshaller;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
 
 public class FeatureSettings extends JPanel
         implements FeatureSettingsControllerI
 {
-  private static final int COLUMN_COUNT = 4;
+  private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
+          .getString("label.sequence_feature_colours");
 
-  private static final String COLON = ":";
+  /*
+   * column indices of fields in Feature Settings table
+   */
+  static final int TYPE_COLUMN = 0;
 
-  private static final int MIN_WIDTH = 400;
+  static final int COLOUR_COLUMN = 1;
 
-  private static final int MIN_HEIGHT = 400;
+  static final int FILTER_COLUMN = 2;
 
-  private static final int MAX_TOOLTIP_LENGTH = 50;
+  static final int SHOW_COLUMN = 3;
 
-  DasSourceBrowser dassourceBrowser;
+  private static final int COLUMN_COUNT = 4;
 
-  DasSequenceFeatureFetcher dasFeatureFetcher;
+  private static final int MIN_WIDTH = 400;
+
+  private static final int MIN_HEIGHT = 400;
 
-  JPanel dasSettingsPane = new JPanel();
+  private final static String BASE_TOOLTIP = MessageManager.getString("label.click_to_edit");
 
   final FeatureRenderer fr;
 
@@ -134,9 +142,9 @@ public class FeatureSettings extends JPanel
    */
   Object[][] originalData;
 
-  private float originalTransparency;
+  float originalTransparency;
 
-  private Map<String, KeyedMatcherSetI> originalFilters;
+  Map<String, FeatureMatcherSetI> originalFilters;
 
   final JInternalFrame frame;
 
@@ -166,7 +174,7 @@ public class FeatureSettings extends JPanel
   /*
    * true when Feature Settings are updating from feature renderer
    */
-  private boolean handlingUpdate = false;
+  boolean handlingUpdate = false;
 
   /*
    * holds {featureCount, totalExtent} for each feature type
@@ -188,7 +196,7 @@ public class FeatureSettings extends JPanel
     int originalTransparencyAsPercent = (int) (originalTransparency * 100);
     transparency.setMaximum(100 - originalTransparencyAsPercent);
 
-    originalFilters = fr.getFeatureFilters();
+    originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
 
     try
     {
@@ -200,49 +208,103 @@ public class FeatureSettings extends JPanel
 
     table = new JTable()
     {
+       
+//     @Override
+//     public void repaint() {
+//       System.out.println("FS repaint");
+//       super.repaint();
+//       
+//     }
+//     
+//     @Override
+//        public void repaint(long tm, int x, int y, int width, int height) {
+//       System.out.println("FS repaint " + x  + " " + y + " " + width + " " + height);
+//       super.repaint(tm, x, y, width, height);
+//       
+//     }
+//     @Override
+//     public void invalidate() {
+//             if (isValid())
+//               System.out.println("FS invalidating ");
+//            super.invalidate();
+//     }
+//     
+//     @Override
+//     public void validate() {
+//            System.out.println("FS validating "  + isValid());
+//            super.validate();
+//     }
+
       @Override
       public String getToolTipText(MouseEvent e)
       {
         String tip = null;
         int column = table.columnAtPoint(e.getPoint());
+        int row = table.rowAtPoint(e.getPoint());
+
         switch (column)
         {
         case TYPE_COLUMN:
           tip = JvSwingUtils.wrapTooltip(true, MessageManager
                   .getString("label.feature_settings_click_drag"));
           break;
+        case COLOUR_COLUMN:
+          FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
+                  column);
+          tip = getColorTooltip(colour, true);
+          break;
         case FILTER_COLUMN:
-          int row = table.rowAtPoint(e.getPoint());
-          KeyedMatcherSet o = (KeyedMatcherSet) table.getValueAt(row,
+          FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
                   column);
           tip = o.isEmpty()
-                  ? MessageManager.getString("label.filters_tooltip")
+                  ? MessageManager
+                          .getString("label.configure_feature_tooltip")
                   : o.toString();
           break;
         default:
           break;
         }
+        
         return tip;
       }
+      
+
+      /**
+       * Position the tooltip near the bottom edge of, and half way across, the
+       * current cell
+       */
+      @Override
+      public Point getToolTipLocation(MouseEvent e)
+      {
+        Point point = e.getPoint();
+        int column = table.columnAtPoint(point);
+        int row = table.rowAtPoint(point);
+        Rectangle r = getCellRect(row, column, false);
+        Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
+        return loc;
+      }
     };
+    
+    // next line is needed to avoid (quiet) exceptions thrown
+    // when column ordering changes so that the above constants
+    // no longer apply.
+    table.getTableHeader().setReorderingAllowed(false); // BH 2018
+    
     table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
-    table.setFont(new Font("Verdana", Font.PLAIN, 12));
+    ToolTipManager.sharedInstance().registerComponent(table);
 
-    // table.setDefaultRenderer(Color.class, new ColorRenderer());
-    // table.setDefaultEditor(Color.class, new ColorEditor(this));
-    //
-    table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
+    table.setDefaultEditor(FeatureColour.class, new ColorEditor());
     table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
 
-    table.setDefaultEditor(KeyedMatcherSet.class, new FilterEditor(this));
-    table.setDefaultRenderer(KeyedMatcherSet.class, new FilterRenderer());
-
+    table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor());
+    table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
+    
     TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
-            new ColorRenderer(), new ColorEditor(this));
+            new ColorRenderer(), new ColorEditor());
     table.addColumn(colourColumn);
 
     TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
-            new FilterRenderer(), new FilterEditor(this));
+            new FilterRenderer(), new FilterEditor());
     table.addColumn(filterColumn);
 
     table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
@@ -257,8 +319,7 @@ public class FeatureSettings extends JPanel
         if (evt.isPopupTrigger())
         {
           Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
-          popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
-                  evt.getY());
+          showPopupMenu(selectedRow, type, colour, evt.getPoint());
         }
         else if (evt.getClickCount() == 2)
         {
@@ -279,8 +340,7 @@ public class FeatureSettings extends JPanel
         {
           String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
           Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
-          popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
-                  evt.getY());
+          showPopupMenu(selectedRow, type, colour, evt.getPoint());
         }
       }
     });
@@ -317,9 +377,6 @@ public class FeatureSettings extends JPanel
     // MessageManager.getString("label.feature_settings_click_drag")));
     scrollPane.setViewportView(table);
 
-    dassourceBrowser = new DasSourceBrowser(this);
-    dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
-
     if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
     {
       fr.findAllFeatures(true); // display everything!
@@ -346,18 +403,9 @@ public class FeatureSettings extends JPanel
 
     frame = new JInternalFrame();
     frame.setContentPane(this);
-    if (Platform.isAMac())
-    {
-      Desktop.addInternalFrame(frame,
-              MessageManager.getString("label.sequence_feature_settings"),
-              600, 480);
-    }
-    else
-    {
-      Desktop.addInternalFrame(frame,
-              MessageManager.getString("label.sequence_feature_settings"),
-              600, 450);
-    }
+    Desktop.addInternalFrame(frame,
+            MessageManager.getString("label.sequence_feature_settings"),
+            600, Platform.isAMacAndNotJS() ? 480 : 450);
     frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
 
     frame.addInternalFrameListener(
@@ -368,91 +416,82 @@ public class FeatureSettings extends JPanel
                       javax.swing.event.InternalFrameEvent evt)
               {
                 fr.removePropertyChangeListener(change);
-                dassourceBrowser.fs = null;
               };
             });
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
     inConstruction = false;
   }
 
-  protected void popupSort(final int rowSelected, final String type,
-          final Object typeCol, final Map<String, float[][]> minmax, int x,
-          int y)
+       /**
+        * Constructs and shows a popup menu of possible actions on the selected row and
+        * feature type
+        * 
+        * @param rowSelected
+        * @param type
+        * @param typeCol
+        * @param pt
+        */
+  protected void showPopupMenu(final int rowSelected, final String type,
+          final Object typeCol, final Point pt)
   {
-    final FeatureColourI featureColour = (FeatureColourI) typeCol;
-
     JPopupMenu men = new JPopupMenu(MessageManager
             .formatMessage("label.settings_for_param", new String[]
             { type }));
-    JMenuItem scr = new JMenuItem(
-            MessageManager.getString("label.sort_by_score"));
-    men.add(scr);
-    final FeatureSettings me = this;
-    scr.addActionListener(new ActionListener()
-    {
-
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        me.af.avc
-                .sortAlignmentByFeatureScore(Arrays.asList(new String[]
-                { type }));
-      }
-
-    });
-    JMenuItem dens = new JMenuItem(
-            MessageManager.getString("label.sort_by_density"));
-    dens.addActionListener(new ActionListener()
-    {
-
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        me.af.avc
-                .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
-                { type }));
-      }
-
-    });
-    men.add(dens);
+    final FeatureColourI featureColour = (FeatureColourI) typeCol;
 
     /*
-     * variable colour options include colour by label, by score,
-     * by selected attribute text, or attribute value
+     * menu option to select (or deselect) variable colour
      */
-    final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
+    final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
             MessageManager.getString("label.variable_colour"));
-    mxcol.setSelected(!featureColour.isSimpleColour());
-    men.add(mxcol);
-    mxcol.addActionListener(new ActionListener()
+    variableColourCB.setSelected(!featureColour.isSimpleColour());
+    men.add(variableColourCB);
+    
+    /*
+     * checkbox action listener doubles up as listener to OK
+     * from the variable colour / filters dialog
+     */
+    variableColourCB.addActionListener(new ActionListener()
     {
-      JColorChooser colorChooser;
-
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        if (e.getSource() == mxcol)
+        if (e.getSource() == variableColourCB)
         {
+          men.setVisible(true); // BH 2018 for JavaScript because this is a checkbox
+          men.setVisible(false); // BH 2018 for JavaScript because this is a checkbox
           if (featureColour.isSimpleColour())
           {
-            FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
+            /*
+             * toggle simple colour to variable colour - show dialog
+             */
+            FeatureTypeSettings fc = new FeatureTypeSettings(fr, type);
             fc.addActionListener(this);
           }
           else
           {
-            // bring up simple color chooser
-            colorChooser = new JColorChooser();
-            String title = MessageManager
-                    .getString("label.select_colour");
-            JDialog dialog = JColorChooser.createDialog(me,
-                    title, true, // modal
-                    colorChooser, this, // OK button handler
-                    null); // no CANCEL button handler
-            colorChooser.setColor(featureColour.getMaxColour());
-            dialog.setVisible(true);
+            /*
+             * toggle variable to simple colour - show colour chooser
+             */
+            String title = MessageManager.formatMessage("label.select_colour_for", type);
+            ColourChooserListener listener = new ColourChooserListener()
+            {
+              @Override
+              public void colourSelected(Color c)
+              {
+                table.setValueAt(new FeatureColour(c), rowSelected,
+                        COLOUR_COLUMN);
+                table.validate();
+                updateFeatureRenderer(
+                        ((FeatureTableModel) table.getModel()).getData(),
+                        false);
+              }
+            };
+            JalviewColourChooser.showColourChooser(FeatureSettings.this, title,
+               featureColour.getMaxColour(), listener);
           }
         }
-        else
+        else    
         {
           if (e.getSource() instanceof FeatureTypeSettings)
           {
@@ -461,23 +500,43 @@ public class FeatureSettings extends JPanel
              * colour will have already been set in the FeatureRenderer
              */
             FeatureColourI fci = fr.getFeatureColours().get(type);
-            table.setValueAt(fci, rowSelected, 1);
-            table.validate();
-          }
-          else
-          {
-            // probably the color chooser!
-            table.setValueAt(new FeatureColour(colorChooser.getColor()),
-                    rowSelected, 1);
-            table.validate();
-            me.updateFeatureRenderer(
-                    ((FeatureTableModel) table.getModel()).getData(),
-                    false);
+            table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
+            // BH 2018 setting a table value does not invalidate it.
+//                 System.out.println("FeatureSettings is valied" + table.isValid());
+//                 table.validate();
           }
         }
       }
+    });
+    
+    men.addSeparator();
 
+    JMenuItem scr = new JMenuItem(
+            MessageManager.getString("label.sort_by_score"));
+    men.add(scr);
+    scr.addActionListener(new ActionListener()
+    {
+
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        af.avc.sortAlignmentByFeatureScore(Arrays.asList(new String[]
+                { type }));
+      }
+    });
+    JMenuItem dens = new JMenuItem(
+            MessageManager.getString("label.sort_by_density"));
+    dens.addActionListener(new ActionListener()
+    {
+
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        af.avc.sortAlignmentByFeatureDensity(Arrays.asList(new String[]
+                { type }));
+      }
     });
+    men.add(dens);
 
     JMenuItem selCols = new JMenuItem(
             MessageManager.getString("label.select_columns_containing"));
@@ -525,7 +584,7 @@ public class FeatureSettings extends JPanel
     men.add(clearCols);
     men.add(hideCols);
     men.add(hideOtherCols);
-    men.show(table, x, y);
+    men.show(table, pt.x, pt.y);
   }
 
   @Override
@@ -651,8 +710,7 @@ public class FeatureSettings extends JPanel
       }
     }
 
-    int columnCount = COLUMN_COUNT;
-    Object[][] data = new Object[displayableTypes.size()][columnCount];
+    Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
     int dataIndex = 0;
 
     if (fr.hasRenderOrder())
@@ -677,9 +735,9 @@ public class FeatureSettings extends JPanel
 
         data[dataIndex][TYPE_COLUMN] = type;
         data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
-        KeyedMatcherSetI featureFilter = fr.getFeatureFilter(type);
+        FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
         data[dataIndex][FILTER_COLUMN] = featureFilter == null
-                ? new KeyedMatcherSet()
+                ? new FeatureMatcherSet()
                 : featureFilter;
         data[dataIndex][SHOW_COLUMN] = new Boolean(
                 af.getViewport().getFeaturesDisplayed().isVisible(type));
@@ -704,9 +762,9 @@ public class FeatureSettings extends JPanel
         fr.clearRenderOrder();
         return;
       }
-      KeyedMatcherSetI featureFilter = fr.getFeatureFilter(type);
+      FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
       data[dataIndex][FILTER_COLUMN] = featureFilter == null
-              ? new KeyedMatcherSet()
+              ? new FeatureMatcherSet()
               : featureFilter;
       data[dataIndex][SHOW_COLUMN] = new Boolean(true);
       dataIndex++;
@@ -715,11 +773,10 @@ public class FeatureSettings extends JPanel
 
     if (originalData == null)
     {
-      int size = data[0].length;
-      originalData = new Object[data.length][size];
+      originalData = new Object[data.length][COLUMN_COUNT];
       for (int i = 0; i < data.length; i++)
       {
-        System.arraycopy(data[i], 0, originalData[i], 0, size);
+        System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
       }
     }
     else
@@ -790,11 +847,12 @@ public class FeatureSettings extends JPanel
         /*
          * new feature detected - add to original data (on top)
          */
-        int size = currentData[0].length;
-        Object[][] newData = new Object[originalData.length + 1][size];
+        Object[][] newData = new Object[originalData.length
+                + 1][COLUMN_COUNT];
         for (int i = 0; i < originalData.length; i++)
         {
-          System.arraycopy(originalData[i], 0, newData[i + 1], 0, size);
+          System.arraycopy(originalData[i], 0, newData[i + 1], 0,
+                  COLUMN_COUNT);
         }
         newData[0] = row;
         originalData = newData;
@@ -848,173 +906,206 @@ public class FeatureSettings extends JPanel
     }
   }
 
+  /**
+   * Offers a file chooser dialog, and then loads the feature colours and
+   * filters from file in XML format and unmarshals to Jalview feature settings
+   */
   void load()
   {
     JalviewFileChooser chooser = new JalviewFileChooser("fc",
-            "Sequence Feature Colours");
+            SEQUENCE_FEATURE_COLOURS);
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(
             MessageManager.getString("label.load_feature_colours"));
     chooser.setToolTipText(MessageManager.getString("action.load"));
+    chooser.setResponseHandler(0, new Runnable()
+    {
+         @Override
+         public void run() 
+         {
+           File file = chooser.getSelectedFile();
+               load(file);
+         }
+       });
+    chooser.showOpenDialog(this);
+  }
 
-    int value = chooser.showOpenDialog(this);
-
-    if (value == JalviewFileChooser.APPROVE_OPTION)
+  /**
+   * Loads feature colours and filters from XML stored in the given file
+   * 
+   * @param file
+   */
+  void load(File file)
+  {
+    try
     {
-      File file = chooser.getSelectedFile();
+      InputStreamReader in = new InputStreamReader(
+              new FileInputStream(file), "UTF-8");
 
-      try
-      {
-        InputStreamReader in = new InputStreamReader(
-                new FileInputStream(file), "UTF-8");
+      JAXBContext jc = JAXBContext
+              .newInstance("jalview.xml.binding.jalview");
+      javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
+      XMLStreamReader streamReader = XMLInputFactory.newInstance()
+              .createXMLStreamReader(in);
+      JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
+              JalviewUserColours.class);
+      JalviewUserColours jucs = jbe.getValue();
 
-        JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
+      // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
 
-        for (int i = jucs.getColourCount() - 1; i >= 0; i--)
-        {
-          String name;
-          jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
-          if (newcol.hasMax())
-          {
-            Color mincol = null, maxcol = null;
-            try
-            {
-              mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
-              maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
+      /*
+       * load feature colours
+       */
+      for (int i = jucs.getColour().size() - 1; i >= 0; i--)
+      {
+        Colour newcol = jucs.getColour().get(i);
+        FeatureColourI colour = jalview.project.Jalview2XML
+                .parseColour(newcol);
+        fr.setColour(newcol.getName(), colour);
+        fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
+      }
 
-            } catch (Exception e)
-            {
-              Cache.log.warn("Couldn't parse out graduated feature color.",
-                      e);
-            }
-            FeatureColourI gcol = new FeatureColour(mincol, maxcol,
-                    newcol.getMin(), newcol.getMax());
-            if (newcol.hasAutoScale())
-            {
-              gcol.setAutoScaled(newcol.getAutoScale());
-            }
-            if (newcol.hasColourByLabel())
-            {
-              gcol.setColourByLabel(newcol.getColourByLabel());
-            }
-            if (newcol.hasThreshold())
-            {
-              gcol.setThreshold(newcol.getThreshold());
-            }
-            if (newcol.getThreshType().length() > 0)
-            {
-              String ttyp = newcol.getThreshType();
-              if (ttyp.equalsIgnoreCase("ABOVE"))
-              {
-                gcol.setAboveThreshold(true);
-              }
-              if (ttyp.equalsIgnoreCase("BELOW"))
-              {
-                gcol.setBelowThreshold(true);
-              }
-            }
-            fr.setColour(name = newcol.getName(), gcol);
-          }
-          else
-          {
-            Color color = new Color(
-                    Integer.parseInt(jucs.getColour(i).getRGB(), 16));
-            fr.setColour(name = jucs.getColour(i).getName(),
-                    new FeatureColour(color));
-          }
-          fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
-        }
-        if (table != null)
+      /*
+       * load feature filters; loaded filters will replace any that are
+       * currently defined, other defined filters are left unchanged 
+       */
+      for (int i = 0; i < jucs.getFilter().size(); i++)
+      {
+        Filter filterModel = jucs.getFilter().get(i);
+        String featureType = filterModel.getFeatureType();
+        FeatureMatcherSetI filter = jalview.project.Jalview2XML
+                .parseFilter(featureType, filterModel.getMatcherSet());
+        if (!filter.isEmpty())
         {
-          resetTable(null);
-          Object[][] data = ((FeatureTableModel) table.getModel())
-                  .getData();
-          ensureOrder(data);
-          updateFeatureRenderer(data, false);
-          table.repaint();
+          fr.setFeatureFilter(featureType, filter);
         }
-      } catch (Exception ex)
+      }
+
+      /*
+       * update feature settings table
+       */
+      if (table != null)
       {
-        System.out.println("Error loading User Colour File\n" + ex);
+        resetTable(null);
+        Object[][] data = ((FeatureTableModel) table.getModel())
+                .getData();
+        ensureOrder(data);
+        updateFeatureRenderer(data, false);
+        table.repaint();
       }
+    } catch (Exception ex)
+    {
+      System.out.println("Error loading User Colour File\n" + ex);
     }
   }
 
+  /**
+   * Offers a file chooser dialog, and then saves the current feature colours
+   * and any filters to the selected file in XML format
+   */
   void save()
   {
     JalviewFileChooser chooser = new JalviewFileChooser("fc",
-            "Sequence Feature Colours");
+            SEQUENCE_FEATURE_COLOURS);
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(
             MessageManager.getString("label.save_feature_colours"));
     chooser.setToolTipText(MessageManager.getString("action.save"));
+    int option = chooser.showSaveDialog(this);
+       if (option == JalviewFileChooser.APPROVE_OPTION) 
+       {
+         File file = chooser.getSelectedFile();
+         save(file);
+       }
+  }
 
-    int value = chooser.showSaveDialog(this);
-
-    if (value == JalviewFileChooser.APPROVE_OPTION)
+  /**
+   * Saves feature colours and filters to the given file
+   * 
+   * @param file
+   */
+  void save(File file)
+  {
+    JalviewUserColours ucs = new JalviewUserColours();
+    ucs.setSchemeName("Sequence Features");
+    try
     {
-      String choice = chooser.getSelectedFile().getPath();
-      jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
-      ucs.setSchemeName("Sequence Features");
-      try
-      {
-        PrintWriter out = new PrintWriter(new OutputStreamWriter(
-                new FileOutputStream(choice), "UTF-8"));
+      PrintWriter out = new PrintWriter(new OutputStreamWriter(
+              new FileOutputStream(file), "UTF-8"));
 
-        Set<String> fr_colours = fr.getAllFeatureColours();
-        Iterator<String> e = fr_colours.iterator();
-        float[] sortOrder = new float[fr_colours.size()];
-        String[] sortTypes = new String[fr_colours.size()];
-        int i = 0;
-        while (e.hasNext())
+      /*
+       * sort feature types by colour order, from 0 (highest)
+       * to 1 (lowest)
+       */
+      Set<String> fr_colours = fr.getAllFeatureColours();
+      String[] sortedTypes = fr_colours
+              .toArray(new String[fr_colours.size()]);
+      Arrays.sort(sortedTypes, new Comparator<String>()
+      {
+        @Override
+        public int compare(String type1, String type2)
         {
-          sortTypes[i] = e.next();
-          sortOrder[i] = fr.getOrder(sortTypes[i]);
-          i++;
+          return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
         }
-        QuickSort.sort(sortOrder, sortTypes);
-        sortOrder = null;
-        for (i = 0; i < sortTypes.length; i++)
+      });
+
+      /*
+       * save feature colours
+       */
+      for (String featureType : sortedTypes)
+      {
+        FeatureColourI fcol = fr.getFeatureStyle(featureType);
+        Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
+                fcol);
+        ucs.getColour().add(col);
+      }
+
+      /*
+       * save any feature filters
+       */
+      for (String featureType : sortedTypes)
+      {
+        FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
+        if (filter != null && !filter.isEmpty())
         {
-          jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
-          col.setName(sortTypes[i]);
-          FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
-          if (fcol.isSimpleColour())
-          {
-            col.setRGB(Format.getHexString(fcol.getColour()));
-          }
-          else
-          {
-            col.setRGB(Format.getHexString(fcol.getMaxColour()));
-            col.setMin(fcol.getMin());
-            col.setMax(fcol.getMax());
-            col.setMinRGB(
-                    jalview.util.Format.getHexString(fcol.getMinColour()));
-            col.setAutoScale(fcol.isAutoScaled());
-            col.setThreshold(fcol.getThreshold());
-            col.setColourByLabel(fcol.isColourByLabel());
-            col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
-                    : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
-          }
-          ucs.addColour(col);
+          Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
+          FeatureMatcherI firstMatcher = iterator.next();
+          jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
+                  .marshalFilter(firstMatcher, iterator,
+                  filter.isAnded());
+          Filter filterModel = new Filter();
+          filterModel.setFeatureType(featureType);
+          filterModel.setMatcherSet(ms);
+          ucs.getFilter().add(filterModel);
         }
-        ucs.marshal(out);
-        out.close();
-      } catch (Exception ex)
-      {
-        ex.printStackTrace();
       }
+      JAXBContext jaxbContext = JAXBContext
+              .newInstance(JalviewUserColours.class);
+      Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
+      jaxbMarshaller.marshal(
+              new ObjectFactory().createJalviewUserColours(ucs), out);
+
+      // jaxbMarshaller.marshal(object, pout);
+      // marshaller.marshal(object);
+      out.flush();
+
+      // ucs.marshal(out);
+      out.close();
+    } catch (Exception ex)
+    {
+      ex.printStackTrace();
     }
   }
 
   public void invertSelection()
   {
-    for (int i = 0; i < table.getRowCount(); i++)
+    Object[][] data = ((FeatureTableModel) table.getModel()).getData();
+    for (int i = 0; i < data.length; i++)
     {
-      Boolean value = (Boolean) table.getValueAt(i, SHOW_COLUMN);
-
-      table.setValueAt(new Boolean(!value.booleanValue()), i, SHOW_COLUMN);
+      data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
     }
+    updateFeatureRenderer(data, true);
+    table.repaint();
   }
 
   public void orderByAvWidth()
@@ -1103,14 +1194,34 @@ public class FeatureSettings extends JPanel
    * @param data
    * @param visibleNew
    */
-  private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
+  void updateFeatureRenderer(Object[][] data, boolean visibleNew)
   {
-    if (fr.setFeaturePriority(data, visibleNew))
+    FeatureSettingsBean[] rowData = getTableAsBeans(data);
+
+    if (fr.setFeaturePriority(rowData, visibleNew))
     {
       af.alignPanel.paintAlignment(true, true);
     }
   }
 
+  /**
+   * Converts table data into an array of data beans
+   */
+  private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
+  {
+    FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
+    for (int i = 0; i < data.length; i++)
+    {
+      String type = (String) data[i][TYPE_COLUMN];
+      FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
+      FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
+      Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
+      rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
+              isShown);
+    }
+    return rowData;
+  }
+
   private void jbInit() throws Exception
   {
     this.setLayout(new BorderLayout());
@@ -1118,8 +1229,6 @@ public class FeatureSettings extends JPanel
     JPanel settingsPane = new JPanel();
     settingsPane.setLayout(new BorderLayout());
 
-    dasSettingsPane.setLayout(new BorderLayout());
-
     JPanel bigPanel = new JPanel();
     bigPanel.setLayout(new BorderLayout());
 
@@ -1234,6 +1343,8 @@ public class FeatureSettings extends JPanel
     JButton loadColours = new JButton(
             MessageManager.getString("label.load_colours"));
     loadColours.setFont(JvSwingUtils.getLabelFont());
+    loadColours.setToolTipText(
+            MessageManager.getString("label.load_colours_tooltip"));
     loadColours.addActionListener(new ActionListener()
     {
       @Override
@@ -1246,6 +1357,8 @@ public class FeatureSettings extends JPanel
     JButton saveColours = new JButton(
             MessageManager.getString("label.save_colours"));
     saveColours.setFont(JvSwingUtils.getLabelFont());
+    saveColours.setToolTipText(
+            MessageManager.getString("label.save_colours_tooltip"));
     saveColours.addActionListener(new ActionListener()
     {
       @Override
@@ -1270,38 +1383,6 @@ public class FeatureSettings extends JPanel
     transparency.setMaximum(70);
     transparency.setToolTipText(
             MessageManager.getString("label.transparency_tip"));
-    fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
-    fetchDAS.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        fetchDAS_actionPerformed(e);
-      }
-    });
-    saveDAS.setText(MessageManager.getString("action.save_as_default"));
-    saveDAS.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        saveDAS_actionPerformed(e);
-      }
-    });
-
-    JPanel dasButtonPanel = new JPanel();
-    dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
-    dasSettingsPane.setBorder(null);
-    cancelDAS.setEnabled(false);
-    cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
-    cancelDAS.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        cancelDAS_actionPerformed(e);
-      }
-    });
 
     JPanel transPanel = new JPanel(new GridLayout(1, 2));
     bigPanel.add(transPanel, BorderLayout.SOUTH);
@@ -1321,170 +1402,93 @@ public class FeatureSettings extends JPanel
     buttonPanel.add(loadColours);
     buttonPanel.add(saveColours);
     bigPanel.add(scrollPane, BorderLayout.CENTER);
-    dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
-    dasButtonPanel.add(fetchDAS);
-    dasButtonPanel.add(cancelDAS);
-    dasButtonPanel.add(saveDAS);
     settingsPane.add(bigPanel, BorderLayout.CENTER);
     settingsPane.add(buttonPanel, BorderLayout.SOUTH);
     this.add(settingsPane);
   }
 
-  public void fetchDAS_actionPerformed(ActionEvent e)
-  {
-    fetchDAS.setEnabled(false);
-    cancelDAS.setEnabled(true);
-    dassourceBrowser.setGuiEnabled(false);
-    Vector<jalviewSourceI> selectedSources = dassourceBrowser
-            .getSelectedSources();
-    doDasFeatureFetch(selectedSources, true, true);
-  }
-
   /**
-   * get the features from selectedSources for all or the current selection
+   * Answers a suitable tooltip to show on the colour cell of the table
    * 
-   * @param selectedSources
-   * @param checkDbRefs
-   * @param promptFetchDbRefs
+   * @param fcol
+   * @param withHint
+   *          if true include 'click to edit' and similar text
+   * @return
    */
-  private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
-          boolean checkDbRefs, boolean promptFetchDbRefs)
+  public static String getColorTooltip(FeatureColourI fcol,
+          boolean withHint)
   {
-    SequenceI[] dataset, seqs;
-    int iSize;
-    AlignmentViewport vp = af.getViewport();
-    if (vp.getSelectionGroup() != null
-            && vp.getSelectionGroup().getSize() > 0)
-    {
-      iSize = vp.getSelectionGroup().getSize();
-      dataset = new SequenceI[iSize];
-      seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
+    if (fcol == null)
+    {
+      return null;
     }
-    else
+    if (fcol.isSimpleColour())
     {
-      iSize = vp.getAlignment().getHeight();
-      seqs = vp.getAlignment().getSequencesArray();
+      return withHint ? BASE_TOOLTIP : null;
     }
-
-    dataset = new SequenceI[iSize];
-    for (int i = 0; i < iSize; i++)
+    String description = fcol.getDescription();
+    description = description.replaceAll("<", "&lt;");
+    description = description.replaceAll(">", "&gt;");
+    StringBuilder tt = new StringBuilder(description);
+    if (withHint)
     {
-      dataset[i] = seqs[i].getDatasetSequence();
+      tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
     }
-
-    cancelDAS.setEnabled(true);
-    dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
-            this, selectedSources, checkDbRefs, promptFetchDbRefs);
-    af.getViewport().setShowSequenceFeatures(true);
-    af.showSeqFeatures.setSelected(true);
-  }
-
-  /**
-   * blocking call to initialise the das source browser
-   */
-  public void initDasSources()
-  {
-    dassourceBrowser.initDasSources();
-  }
-
-  /**
-   * examine the current list of das sources and return any matching the given
-   * nicknames in sources
-   * 
-   * @param sources
-   *          Vector of Strings to resolve to DAS source nicknames.
-   * @return sources that are present in source list.
-   */
-  public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
-  {
-    return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
+    return JvSwingUtils.wrapTooltip(true, tt.toString());
   }
 
-  /**
-   * get currently selected das sources. ensure you have called initDasSources
-   * before calling this.
-   * 
-   * @return vector of selected das source nicknames
-   */
-  public Vector<jalviewSourceI> getSelectedSources()
-  {
-    return dassourceBrowser.getSelectedSources();
-  }
-
-  /**
-   * properly initialise DAS fetcher and then initiate a new thread to fetch
-   * features from the named sources (rather than any turned on by default)
-   * 
-   * @param sources
-   * @param block
-   *          if true then runs in same thread, otherwise passes to the Swing
-   *          executor
-   */
-  public void fetchDasFeatures(Vector<String> sources, boolean block)
+  public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
+          int w, int h)
   {
-    initDasSources();
-    List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
-            .resolveSourceNicknames(sources);
-    if (resolved.size() == 0)
+    boolean thr = false;
+    StringBuilder tx = new StringBuilder();
+  
+    if (gcol.isColourByAttribute())
     {
-      resolved = dassourceBrowser.getSelectedSources();
+      tx.append(FeatureMatcher
+              .toAttributeDisplayName(gcol.getAttributeName()));
     }
-    if (resolved.size() > 0)
+    else if (!gcol.isColourByLabel())
     {
-      final List<jalviewSourceI> dassources = resolved;
-      fetchDAS.setEnabled(false);
-      // cancelDAS.setEnabled(true); doDasFetch does this.
-      Runnable fetcher = new Runnable()
-      {
-
-        @Override
-        public void run()
-        {
-          doDasFeatureFetch(dassources, true, false);
-
-        }
-      };
-      if (block)
+      tx.append(MessageManager.getString("label.score"));
+    }
+    tx.append(" ");
+    if (gcol.isAboveThreshold())
+    {
+      thr = true;
+      tx.append(">");
+    }
+    if (gcol.isBelowThreshold())
+    {
+      thr = true;
+      tx.append("<");
+    }
+    if (gcol.isColourByLabel())
+    {
+      if (thr)
       {
-        fetcher.run();
+        tx.append(" ");
       }
-      else
+      if (!gcol.isColourByAttribute())
       {
-        SwingUtilities.invokeLater(fetcher);
+        tx.append("Label");
       }
+      comp.setIcon(null);
     }
-  }
-
-  public void saveDAS_actionPerformed(ActionEvent e)
-  {
-    dassourceBrowser
-            .saveProperties(jalview.bin.Cache.applicationProperties);
-  }
-
-  public void complete()
-  {
-    fetchDAS.setEnabled(true);
-    cancelDAS.setEnabled(false);
-    dassourceBrowser.setGuiEnabled(true);
-
-  }
-
-  public void cancelDAS_actionPerformed(ActionEvent e)
-  {
-    if (dasFeatureFetcher != null)
+    else
     {
-      dasFeatureFetcher.cancel();
+      Color newColor = gcol.getMaxColour();
+      comp.setBackground(newColor);
+      // System.err.println("Width is " + w / 2);
+      Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
+      comp.setIcon(ficon);
+      // tt+="RGB value: Max (" + newColor.getRed() + ", "
+      // + newColor.getGreen() + ", " + newColor.getBlue()
+      // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
+      // + ", " + minCol.getBlue() + ")");
     }
-    complete();
-  }
-
-  public void noDasSourceActive()
-  {
-    complete();
-    JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
-            MessageManager.getString("label.no_das_sources_selected_warn"),
-            MessageManager.getString("label.no_das_sources_selected_title"),
-            JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
+    comp.setHorizontalAlignment(SwingConstants.CENTER);
+    comp.setText(tx.toString());
   }
 
   // ///////////////////////////////////////////////////////////////////////
@@ -1492,11 +1496,10 @@ public class FeatureSettings extends JPanel
   // ///////////////////////////////////////////////////////////////////////
   class FeatureTableModel extends AbstractTableModel
   {
-
     private String[] columnNames = {
         MessageManager.getString("label.feature_type"),
         MessageManager.getString("action.colour"),
-        MessageManager.getString("label.filter"),
+        MessageManager.getString("label.configuration"),
         MessageManager.getString("label.show") };
 
     private Object[][] data;
@@ -1573,11 +1576,9 @@ public class FeatureSettings extends JPanel
 
   class ColorRenderer extends JLabel implements TableCellRenderer
   {
-    javax.swing.border.Border unselectedBorder = null;
+    Border unselectedBorder = null;
 
-    javax.swing.border.Border selectedBorder = null;
-
-    final String baseTT = "Click to edit, right/apple click for menu.";
+    Border selectedBorder = null;
 
     public ColorRenderer()
     {
@@ -1592,7 +1593,6 @@ public class FeatureSettings extends JPanel
     {
       FeatureColourI cellColour = (FeatureColourI) color;
       setOpaque(true);
-      setToolTipText(baseTT);
       setBackground(tbl.getBackground());
       if (!cellColour.isSimpleColour())
       {
@@ -1647,7 +1647,7 @@ public class FeatureSettings extends JPanel
             Object filter, boolean isSelected, boolean hasFocus, int row,
             int column)
     {
-      KeyedMatcherSetI theFilter = (KeyedMatcherSetI) filter;
+      FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
       setOpaque(true);
       String asText = theFilter.toString();
       setBackground(tbl.getBackground());
@@ -1699,82 +1699,10 @@ public class FeatureSettings extends JPanel
     renderGraduatedColor(comp, gcol, w, h);
   }
 
-  public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
-          int w, int h)
-  {
-    boolean thr = false;
-    StringBuilder tt = new StringBuilder();
-    StringBuilder tx = new StringBuilder();
-
-    if (gcol.isColourByAttribute())
-    {
-      tx.append(String.join(":", gcol.getAttributeName()));
-    }
-    else if (!gcol.isColourByLabel())
-    {
-      tx.append(MessageManager.getString("label.score"));
-    }
-    tx.append(" ");
-    if (gcol.isAboveThreshold())
-    {
-      thr = true;
-      tx.append(">");
-      tt.append("Thresholded (Above ").append(gcol.getThreshold())
-              .append(") ");
-    }
-    if (gcol.isBelowThreshold())
-    {
-      thr = true;
-      tx.append("<");
-      tt.append("Thresholded (Below ").append(gcol.getThreshold())
-              .append(") ");
-    }
-    if (gcol.isColourByLabel())
-    {
-      tt.append("Coloured by label text. ").append(tt);
-      if (thr)
-      {
-        tx.append(" ");
-      }
-      if (!gcol.isColourByAttribute())
-      {
-        tx.append("Label");
-      }
-      comp.setIcon(null);
-    }
-    else
-    {
-      Color newColor = gcol.getMaxColour();
-      comp.setBackground(newColor);
-      // System.err.println("Width is " + w / 2);
-      Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
-      comp.setIcon(ficon);
-      // tt+="RGB value: Max (" + newColor.getRed() + ", "
-      // + newColor.getGreen() + ", " + newColor.getBlue()
-      // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
-      // + ", " + minCol.getBlue() + ")");
-    }
-    comp.setHorizontalAlignment(SwingConstants.CENTER);
-    comp.setText(tx.toString());
-    if (tt.length() > 0)
-    {
-      if (comp.getToolTipText() == null)
-      {
-        comp.setToolTipText(tt.toString());
-      }
-      else
-      {
-        comp.setToolTipText(
-                tt.append(" ").append(comp.getToolTipText()).toString());
-      }
-    }
-  }
-
-  class ColorEditor extends AbstractCellEditor
+  @SuppressWarnings("serial")
+class ColorEditor extends AbstractCellEditor
           implements TableCellEditor, ActionListener
   {
-    FeatureSettings me;
-
     FeatureColourI currentColor;
 
     FeatureTypeSettings chooser;
@@ -1783,17 +1711,12 @@ public class FeatureSettings extends JPanel
 
     JButton button;
 
-    JColorChooser colorChooser;
-
-    JDialog dialog;
-
     protected static final String EDIT = "edit";
 
     int rowSelected = 0;
 
-    public ColorEditor(FeatureSettings me)
+    public ColorEditor()
     {
-      this.me = me;
       // Set up the editor (from the table's point of view),
       // which is a button.
       // This button brings up the color chooser dialog,
@@ -1802,79 +1725,92 @@ public class FeatureSettings extends JPanel
       button.setActionCommand(EDIT);
       button.addActionListener(this);
       button.setBorderPainted(false);
-      // Set up the dialog that the button brings up.
-      colorChooser = new JColorChooser();
-      dialog = JColorChooser.createDialog(button,
-              MessageManager.getString("label.select_colour"), true, // modal
-              colorChooser, this, // OK button handler
-              null); // no CANCEL button handler
     }
 
     /**
-     * Handles events from the editor button and from the dialog's OK button.
+     * Handles events from the editor button, and from the colour/filters
+     * dialog's OK button
      */
     @Override
     public void actionPerformed(ActionEvent e)
     {
-      // todo test e.getSource() instead here
-      if (EDIT.equals(e.getActionCommand()))
-      {
-        // The user has clicked the cell, so
-        // bring up the dialog.
-        if (currentColor.isSimpleColour())
-        {
-          // bring up simple color chooser
-          button.setBackground(currentColor.getColour());
-          colorChooser.setColor(currentColor.getColour());
-          dialog.setVisible(true);
-        }
-        else
-        {
-          // bring up graduated chooser.
-          chooser = new FeatureTypeSettings(me.fr, type);
-          chooser.setRequestFocusEnabled(true);
-          chooser.requestFocus();
-          chooser.addActionListener(this);
-          chooser.showTab(true);
-        }
-        // Make the renderer reappear.
-        fireEditingStopped();
-
-      }
-      else
+      if (button == e.getSource())
       {
         if (currentColor.isSimpleColour())
         {
           /*
-           * read off colour picked in colour chooser after OK pressed
+           * simple colour chooser
            */
-          currentColor = new FeatureColour(colorChooser.getColor());
-          me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
+          String ttl = MessageManager.formatMessage("label.select_colour_for", type);
+          ColourChooserListener listener = new ColourChooserListener() 
+          {
+            @Override
+            public void colourSelected(Color c)
+            {
+              currentColor = new FeatureColour(c);
+              table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
+              fireEditingStopped();
+            }
+                       @Override
+                       public void cancel() 
+                       {
+                 fireEditingStopped();
+                       }
+          };
+          JalviewColourChooser.showColourChooser(button,  ttl,  currentColor.getColour(), listener);
         }
         else
         {
           /*
-           * after OK in variable colour dialog, any changes to colour 
-           * (or filters!) are already set in FeatureRenderer, so just
-           * update table data without triggering updateFeatureRenderer
+           * variable colour and filters dialog
            */
-          currentColor = fr.getFeatureColours().get(type);
-          KeyedMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
-          if (currentFilter == null)
+          chooser = new FeatureTypeSettings(fr, type);
+          if (!Jalview.isJS())
           {
-            currentFilter = new KeyedMatcherSet();
+            chooser.setRequestFocusEnabled(true);
+            chooser.requestFocus();
           }
-          Object[] data = ((FeatureTableModel) table.getModel())
-                  .getData()[rowSelected];
-          data[COLOUR_COLUMN] = currentColor;
-          data[FILTER_COLUMN] = currentFilter;
+          chooser.addActionListener(this);
+          fireEditingStopped();
+        }
+      }
+      else
+      {
+        /*
+         * after OK in variable colour dialog, any changes to colour 
+         * (or filters!) are already set in FeatureRenderer, so just
+         * update table data without triggering updateFeatureRenderer
+         */
+        currentColor = fr.getFeatureColours().get(type);
+        FeatureMatcherSetI currentFilter = fr.getFeatureFilter(type);
+        if (currentFilter == null)
+        {
+          currentFilter = new FeatureMatcherSet();
         }
+        Object[] data = ((FeatureTableModel) table.getModel())
+                .getData()[rowSelected];
+        data[COLOUR_COLUMN] = currentColor;
+        data[FILTER_COLUMN] = currentFilter;
         fireEditingStopped();
-        me.table.validate();
+        // SwingJS needs an explicit repaint() here, 
+        // rather than relying upon no validation having
+        // occurred since the stopEditing call was made.
+        // Its laying out has not been stopped by the modal frame
+        table.validate();
+        table.repaint();
       }
     }
 
-    // Implement the one CellEditor method that AbstractCellEditor doesn't.
+    /**
+     * Override allows access to this method from anonymous inner classes 
+     */
+    @Override
+       protected void fireEditingStopped() 
+    {
+           super.fireEditingStopped();
+       }
+
+       // Implement the one CellEditor method that AbstractCellEditor doesn't.
     @Override
     public Object getCellEditorValue()
     {
@@ -1888,9 +1824,9 @@ public class FeatureSettings extends JPanel
     {
       currentColor = (FeatureColourI) value;
       this.rowSelected = row;
-      type = me.table.getValueAt(row, TYPE_COLUMN).toString();
+      type = table.getValueAt(row, TYPE_COLUMN).toString();
       button.setOpaque(true);
-      button.setBackground(me.getBackground());
+      button.setBackground(FeatureSettings.this.getBackground());
       if (!currentColor.isSimpleColour())
       {
         JLabel btn = new JLabel();
@@ -1916,12 +1852,12 @@ public class FeatureSettings extends JPanel
    * as display text). On click in the cell, opens the Feature Display Settings
    * dialog at the Filters tab.
    */
-  class FilterEditor extends AbstractCellEditor
+  @SuppressWarnings("serial")
+class FilterEditor extends AbstractCellEditor
           implements TableCellEditor, ActionListener
   {
-    FeatureSettings me;
 
-    KeyedMatcherSetI currentFilter;
+    FeatureMatcherSetI currentFilter;
 
     Point lastLocation;
 
@@ -1933,9 +1869,8 @@ public class FeatureSettings extends JPanel
 
     int rowSelected = 0;
 
-    public FilterEditor(FeatureSettings me)
+    public FilterEditor()
     {
-      this.me = me;
       button = new JButton();
       button.setActionCommand(EDIT);
       button.addActionListener(this);
@@ -1950,7 +1885,7 @@ public class FeatureSettings extends JPanel
     {
       if (button == e.getSource())
       {
-        FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
+        FeatureTypeSettings chooser = new FeatureTypeSettings(fr, type);
         chooser.addActionListener(this);
         chooser.setRequestFocusEnabled(true);
         chooser.requestFocus();
@@ -1961,7 +1896,6 @@ public class FeatureSettings extends JPanel
                   chooser.getWidth(), chooser.getHeight());
           chooser.validate();
         }
-        chooser.showTab(false);
         fireEditingStopped();
       }
       else if (e.getSource() instanceof Component)
@@ -1973,17 +1907,23 @@ public class FeatureSettings extends JPanel
          * update table data without triggering updateFeatureRenderer
          */
         FeatureColourI currentColor = fr.getFeatureColours().get(type);
-        currentFilter = me.fr.getFeatureFilter(type);
+        currentFilter = fr.getFeatureFilter(type);
         if (currentFilter == null)
         {
-          currentFilter = new KeyedMatcherSet();
+          currentFilter = new FeatureMatcherSet();
         }
+        
         Object[] data = ((FeatureTableModel) table.getModel())
                 .getData()[rowSelected];
         data[COLOUR_COLUMN] = currentColor;
         data[FILTER_COLUMN] = currentFilter;
         fireEditingStopped();
-        me.table.validate();
+        // SwingJS needs an explicit repaint() here, 
+        // rather than relying upon no validation having
+        // occurred since the stopEditing call was made.
+        // Its laying out has not been stopped by the modal frame
+        table.validate();
+        table.repaint();
       }
     }
 
@@ -1997,13 +1937,12 @@ public class FeatureSettings extends JPanel
     public Component getTableCellEditorComponent(JTable theTable, Object value,
             boolean isSelected, int row, int column)
     {
-      currentFilter = (KeyedMatcherSetI) value;
+      currentFilter = (FeatureMatcherSetI) value;
       this.rowSelected = row;
-      type = me.table.getValueAt(row, TYPE_COLUMN).toString();
+      type = table.getValueAt(row, TYPE_COLUMN).toString();
       button.setOpaque(true);
-      button.setBackground(me.getBackground());
+      button.setBackground(FeatureSettings.this.getBackground());
       button.setText(currentFilter.toString());
-      button.setToolTipText(currentFilter.toString());
       button.setIcon(null);
       return button;
     }
@@ -2087,7 +2026,8 @@ class FeatureIcon implements Icon
         g.fillRect(s1, 0, e1 - s1, height);
       }
       g.setColor(gcol.getMaxColour());
-      g.fillRect(0, e1, width - e1, height);
+//      g.fillRect(0, e1, width - e1, height);  // BH 2018
+      g.fillRect(e1, 0, width - e1, height);
     }
   }
 }