Merge branch 'bug/JAL-2983negativeSliderMin' into releases/Release_2_11_1_Branch
[jalview.git] / src / jalview / gui / FeatureTypeSettings.java
index c356731..512cf06 100644 (file)
  */
 package jalview.gui;
 
+import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureColourI;
+import jalview.bin.Cache;
 import jalview.datamodel.GraphLine;
 import jalview.datamodel.features.FeatureAttributes;
 import jalview.datamodel.features.FeatureAttributes.Datatype;
@@ -47,6 +49,8 @@ import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.math.BigDecimal;
+import java.math.MathContext;
 import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.List;
@@ -61,14 +65,11 @@ import javax.swing.JComboBox;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.JRadioButton;
-import javax.swing.JSlider;
-import javax.swing.JTabbedPane;
 import javax.swing.JTextField;
-import javax.swing.SwingConstants;
+import javax.swing.border.EmptyBorder;
 import javax.swing.border.LineBorder;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
-import javax.swing.plaf.basic.BasicArrowButton;
 
 /**
  * A dialog where the user can configure colour scheme, and any filters, for one
@@ -79,6 +80,8 @@ import javax.swing.plaf.basic.BasicArrowButton;
  */
 public class FeatureTypeSettings extends JalviewDialog
 {
+  private final static MathContext FOUR_SIG_FIG = new MathContext(4);
+
   private final static String LABEL_18N = MessageManager
           .getString("label.label");
 
@@ -107,7 +110,8 @@ public class FeatureTypeSettings extends JalviewDialog
   /*
    * FeatureRenderer holds colour scheme and filters for feature types
    */
-  private final FeatureRenderer fr; // todo refactor to allow interface type here
+  private final FeatureRenderer fr; // todo refactor to allow interface type
+                                    // here
 
   /*
    * the view panel to update when settings change
@@ -141,11 +145,6 @@ public class FeatureTypeSettings extends JalviewDialog
   private float max;
 
   /*
-   * scale factor for conversion between absolute min-max and slider
-   */
-  private float scaleFactor;
-
-  /*
    * radio button group, to select what to colour by:
    * simple colour, by category (text), or graduated
    */
@@ -155,15 +154,20 @@ public class FeatureTypeSettings extends JalviewDialog
 
   private JRadioButton graduatedColour = new JRadioButton();
 
-  private JPanel singleColour = new JPanel();
+  /**
+   * colours and filters are shown in tabbed view or single content pane
+   */
+  JPanel coloursPanel, filtersPanel;
+
+  JPanel singleColour = new JPanel();
 
   private JPanel minColour = new JPanel();
 
   private JPanel maxColour = new JPanel();
 
-  private JComboBox<String> threshold = new JComboBox<>();
+  private JComboBox<Object> threshold = new JComboBox<>();
 
-  private JSlider slider = new JSlider();
+  private Slider slider;
 
   private JTextField thresholdValue = new JTextField(20);
 
@@ -178,17 +182,17 @@ public class FeatureTypeSettings extends JalviewDialog
   /*
    * choice of option for 'colour for no value'
    */
-  private JComboBox<String> noValueCombo;
+  private JComboBox<Object> noValueCombo;
 
   /*
    * choice of what to colour by text (Label or attribute)
    */
-  private JComboBox<String> colourByTextCombo;
+  private JComboBox<Object> colourByTextCombo;
 
   /*
    * choice of what to colour by range (Score or attribute)
    */
-  private JComboBox<String> colourByRangeCombo;
+  private JComboBox<Object> colourByRangeCombo;
 
   private JRadioButton andFilters;
 
@@ -199,13 +203,8 @@ public class FeatureTypeSettings extends JalviewDialog
    */
   private List<FeatureMatcherI> filters;
 
-  // set white normally, black to debug layout
-  private Color debugBorderColour = Color.white;
-
   private JPanel chooseFiltersPanel;
 
-  private JTabbedPane tabbedPane;
-
   /**
    * Constructor
    * 
@@ -214,28 +213,14 @@ public class FeatureTypeSettings extends JalviewDialog
    */
   public FeatureTypeSettings(FeatureRenderer frender, String theType)
   {
-    this(frender, false, theType);
-  }
-
-  /**
-   * Constructor, with option to make a blocking dialog (has to complete in the
-   * AWT event queue thread). Currently this option is always set to false.
-   * 
-   * @param frender
-   * @param blocking
-   * @param theType
-   */
-  FeatureTypeSettings(FeatureRenderer frender, boolean blocking,
-          String theType)
-  {
     this.fr = frender;
     this.featureType = theType;
     ap = fr.ap;
     originalFilter = fr.getFeatureFilter(theType);
     originalColour = fr.getFeatureColours().get(theType);
-
+    
     adjusting = true;
-
+    
     try
     {
       initialise();
@@ -244,20 +229,19 @@ public class FeatureTypeSettings extends JalviewDialog
       ex.printStackTrace();
       return;
     }
-
+    
     updateColoursTab();
-
+    
     updateFiltersTab();
-
+    
     adjusting = false;
-
+    
     colourChanged(false);
-
+    
     String title = MessageManager
             .formatMessage("label.display_settings_for", new String[]
             { theType });
-    initDialogFrame(this, true, blocking, title, 600, 360);
-
+    initDialogFrame(this, true, false, title, 580, 500);
     waitForInput();
   }
 
@@ -280,9 +264,9 @@ public class FeatureTypeSettings extends JalviewDialog
        */
       if (fc.isSimpleColour())
       {
-        simpleColour.setSelected(true);
         singleColour.setBackground(fc.getColour());
         singleColour.setForeground(fc.getColour());
+        simpleColour.setSelected(true);
       }
 
       /*
@@ -295,8 +279,8 @@ public class FeatureTypeSettings extends JalviewDialog
         if (fc.isColourByAttribute())
         {
           String[] attributeName = fc.getAttributeName();
-          colourByTextCombo
-                  .setSelectedItem(toAttributeDisplayName(attributeName));
+          colourByTextCombo.setSelectedItem(
+                  FeatureMatcher.toAttributeDisplayName(attributeName));
         }
         else
         {
@@ -325,6 +309,7 @@ public class FeatureTypeSettings extends JalviewDialog
        * Graduated colour, by score or attribute value range
        */
       graduatedColour.setSelected(true);
+      updateColourMinMax(); // ensure min, max are set
       colourByRangeCombo.setEnabled(colourByRangeCombo.getItemCount() > 1);
       minColour.setEnabled(true);
       maxColour.setEnabled(true);
@@ -336,8 +321,8 @@ public class FeatureTypeSettings extends JalviewDialog
       if (fc.isColourByAttribute())
       {
         String[] attributeName = fc.getAttributeName();
-        colourByRangeCombo
-                .setSelectedItem(toAttributeDisplayName(attributeName));
+        colourByRangeCombo.setSelectedItem(
+                FeatureMatcher.toAttributeDisplayName(attributeName));
       }
       else
       {
@@ -361,12 +346,11 @@ public class FeatureTypeSettings extends JalviewDialog
        * update min-max scaling if there is a range to work with,
        * else disable the widgets (this shouldn't happen if only 
        * valid options are offered in the combo box)
+       * offset slider to have only non-negative values if necessary (JAL-2983)
        */
-      scaleFactor = (max == min) ? 1f : 100f / (max - min);
-      float range = (max - min) * scaleFactor;
-      slider.setMinimum((int) (min * scaleFactor));
-      slider.setMaximum((int) (max * scaleFactor));
-      slider.setMajorTickSpacing((int) (range / 10f));
+      slider.setSliderModel(min, max, min);
+      slider.setMajorTickSpacing(
+              (int) ((slider.getMaximum() - slider.getMinimum()) / 10f));
 
       threshline = new GraphLine((max - min) / 2f, "Threshold",
               Color.black);
@@ -378,8 +362,8 @@ public class FeatureTypeSettings extends JalviewDialog
                 fc.isAboveThreshold() ? ABOVE_THRESHOLD_OPTION
                         : BELOW_THRESHOLD_OPTION);
         slider.setEnabled(true);
-        slider.setValue((int) (fc.getThreshold() * scaleFactor));
-        thresholdValue.setText(String.valueOf(getRoundedSliderValue()));
+        slider.setSliderValue(fc.getThreshold());
+        setThresholdValueText(fc.getThreshold());
         thresholdValue.setEnabled(true);
         thresholdIsMin.setEnabled(true);
       }
@@ -402,8 +386,6 @@ public class FeatureTypeSettings extends JalviewDialog
   private void initialise()
   {
     this.setLayout(new BorderLayout());
-    tabbedPane = new JTabbedPane();
-    this.add(tabbedPane, BorderLayout.CENTER);
 
     /*
      * an ActionListener that applies colour changes
@@ -418,18 +400,16 @@ public class FeatureTypeSettings extends JalviewDialog
     };
 
     /*
-     * first tab: colour options
+     * first panel/tab: colour options
      */
     JPanel coloursPanel = initialiseColoursPanel();
-    tabbedPane.addTab(MessageManager.getString("action.colour"),
-            coloursPanel);
+    this.add(coloursPanel, BorderLayout.NORTH);
 
     /*
-     * second tab: filter options
+     * second panel/tab: filter options
      */
     JPanel filtersPanel = initialiseFiltersPanel();
-    tabbedPane.addTab(MessageManager.getString("label.filters"),
-            filtersPanel);
+    this.add(filtersPanel, BorderLayout.CENTER);
 
     JPanel okCancelPanel = initialiseOkCancelPanel();
 
@@ -478,7 +458,7 @@ public class FeatureTypeSettings extends JalviewDialog
     {
       // colour by attribute range
       minMax = FeatureAttributes.getInstance().getMinMax(featureType,
-              fromAttributeDisplayName(attName));
+              FeatureMatcher.fromAttributeDisplayName(attName));
     }
     return minMax;
   }
@@ -580,14 +560,20 @@ public class FeatureTypeSettings extends JalviewDialog
     maxColour.setBorder(new LineBorder(Color.black));
 
     /*
-     * default max colour to current colour (if a plain colour),
-     * or to Black if colour by label;  make min colour a pale
-     * version of max colour
+     * if not set, default max colour to last plain colour,
+     * and make min colour a pale version of max colour
      */
-    FeatureColourI fc = fr.getFeatureColours().get(featureType);
-    Color bg = fc.isSimpleColour() ? fc.getColour() : Color.BLACK;
-    maxColour.setBackground(bg);
-    minColour.setBackground(ColorUtils.bleachColour(bg, 0.9f));
+    Color max = originalColour.getMaxColour();
+    if (max == null)
+    {
+      max = originalColour.getColour();
+      minColour.setBackground(ColorUtils.bleachColour(max, 0.9f));
+    }
+    else
+    {
+      maxColour.setBackground(max);
+      minColour.setBackground(originalColour.getMinColour());
+    }
 
     noValueCombo = new JComboBox<>();
     noValueCombo.addItem(MessageManager.getString("label.no_colour"));
@@ -652,6 +638,7 @@ public class FeatureTypeSettings extends JalviewDialog
         thresholdValue_actionPerformed();
       }
     });
+    slider = new Slider(0f, 100f, 50f);
     slider.setPaintLabels(false);
     slider.setPaintTicks(true);
     slider.setBackground(Color.white);
@@ -668,8 +655,8 @@ public class FeatureTypeSettings extends JalviewDialog
       {
         if (!adjusting)
         {
-          thresholdValue
-                  .setText(String.valueOf(slider.getValue() / scaleFactor));
+          setThresholdValueText(slider.getSliderValue());
+          thresholdValue.setBackground(Color.white); // to reset red for invalid
           sliderValueChanged();
         }
       }
@@ -685,7 +672,7 @@ public class FeatureTypeSettings extends JalviewDialog
          */
         if (ap != null)
         {
-          ap.paintAlignment(true, true);
+          refreshDisplay(true);
         }
       }
     });
@@ -735,15 +722,16 @@ public class FeatureTypeSettings extends JalviewDialog
   private JPanel initialiseColoursPanel()
   {
     JPanel colourByPanel = new JPanel();
+    colourByPanel.setBackground(Color.white);
     colourByPanel.setLayout(new BoxLayout(colourByPanel, BoxLayout.Y_AXIS));
+    JvSwingUtils.createTitledBorder(colourByPanel,
+            MessageManager.getString("action.colour"), true);
 
     /*
      * simple colour radio button and colour picker
      */
     JPanel simpleColourPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
     simpleColourPanel.setBackground(Color.white);
-    JvSwingUtils.createTitledBorder(simpleColourPanel,
-            MessageManager.getString("label.simple"), true);
     colourByPanel.add(simpleColourPanel);
 
     simpleColour = new JRadioButton(
@@ -756,15 +744,24 @@ public class FeatureTypeSettings extends JalviewDialog
       {
         if (simpleColour.isSelected() && !adjusting)
         {
-          showColourChooser(singleColour, "label.select_colour");
+          colourChanged(true);
         }
       }
-
     });
-    
+
     singleColour.setFont(JvSwingUtils.getLabelFont());
     singleColour.setBorder(BorderFactory.createLineBorder(Color.black));
     singleColour.setPreferredSize(new Dimension(40, 20));
+    // if (originalColour.isGraduatedColour())
+    // {
+    // singleColour.setBackground(originalColour.getMaxColour());
+    // singleColour.setForeground(originalColour.getMaxColour());
+    // }
+    // else
+    // {
+      singleColour.setBackground(originalColour.getColour());
+      singleColour.setForeground(originalColour.getColour());
+    // }
     singleColour.addMouseListener(new MouseAdapter()
     {
       @Override
@@ -848,9 +845,9 @@ public class FeatureTypeSettings extends JalviewDialog
   }
 
   /**
-   * Constructs and sets the selected colour options as the colour for the feature
-   * type, and repaints the alignment, and optionally the Overview and/or
-   * structure viewer if open
+   * Constructs and sets the selected colour options as the colour for the
+   * feature type, and repaints the alignment, and optionally the Overview
+   * and/or structure viewer if open
    * 
    * @param updateStructsAndOverview
    */
@@ -876,7 +873,7 @@ public class FeatureTypeSettings extends JalviewDialog
      * save the colour, and repaint stuff
      */
     fr.setColour(featureType, acg);
-    ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview);
+    refreshDisplay(updateStructsAndOverview);
 
     updateColoursTab();
   }
@@ -889,41 +886,9 @@ public class FeatureTypeSettings extends JalviewDialog
   private FeatureColourI makeColourFromInputs()
   {
     /*
-     * easiest case - a single colour
-     */
-    if (simpleColour.isSelected())
-    {
-      return new FeatureColour(singleColour.getBackground());
-    }
-
-    /*
-     * next easiest case - colour by Label, or attribute text
-     */
-    if (byCategory.isSelected())
-    {
-      Color c = this.getBackground();
-      FeatureColourI fc = new FeatureColour(c, c, null, 0f, 0f);
-      fc.setColourByLabel(true);
-      String byWhat = (String) colourByTextCombo.getSelectedItem();
-      if (!LABEL_18N.equals(byWhat))
-      {
-        fc.setAttributeName(fromAttributeDisplayName(byWhat));
-      }
-      return fc;
-    }
-
-    /*
-     * remaining case - graduated colour by score, or attribute value
+     * min-max range is to (or from) threshold value if 
+     * 'threshold is min/max' is selected 
      */
-    Color noColour = null;
-    if (noValueCombo.getSelectedIndex() == MIN_COLOUR_OPTION)
-    {
-      noColour = minColour.getBackground();
-    }
-    else if (noValueCombo.getSelectedIndex() == MAX_COLOUR_OPTION)
-    {
-      noColour = maxColour.getBackground();
-    }
 
     float thresh = 0f;
     try
@@ -933,11 +898,6 @@ public class FeatureTypeSettings extends JalviewDialog
     {
       // invalid inputs are already handled on entry
     }
-
-    /*
-     * min-max range is to (or from) threshold value if 
-     * 'threshold is min/max' is selected 
-     */
     float minValue = min;
     float maxValue = max;
     final int thresholdOption = threshold.getSelectedIndex();
@@ -951,20 +911,56 @@ public class FeatureTypeSettings extends JalviewDialog
     {
       maxValue = thresh;
     }
+    Color noColour = null;
+    if (noValueCombo.getSelectedIndex() == MIN_COLOUR_OPTION)
+    {
+      noColour = minColour.getBackground();
+    }
+    else if (noValueCombo.getSelectedIndex() == MAX_COLOUR_OPTION)
+    {
+      noColour = maxColour.getBackground();
+    }
 
     /*
-     * make the graduated colour
+     * construct a colour that 'remembers' all the options, including
+     * those not currently selected
      */
-    FeatureColourI fc = new FeatureColour(minColour.getBackground(),
-            maxColour.getBackground(), noColour, minValue, maxValue);
+    FeatureColourI fc = new FeatureColour(singleColour.getBackground(),
+            minColour.getBackground(), maxColour.getBackground(), noColour,
+            minValue, maxValue);
 
     /*
+     * easiest case - a single colour
+     */
+    if (simpleColour.isSelected())
+    {
+      ((FeatureColour) fc).setGraduatedColour(false);
+      return fc;
+    }
+
+    /*
+     * next easiest case - colour by Label, or attribute text
+     */
+    if (byCategory.isSelected())
+    {
+      fc.setColourByLabel(true);
+      String byWhat = (String) colourByTextCombo.getSelectedItem();
+      if (!LABEL_18N.equals(byWhat))
+      {
+        fc.setAttributeName(
+                FeatureMatcher.fromAttributeDisplayName(byWhat));
+      }
+      return fc;
+    }
+
+    /*
+     * remaining case - graduated colour by score, or attribute value;
      * set attribute to colour by if selected
      */
     String byWhat = (String) colourByRangeCombo.getSelectedItem();
     if (!SCORE_18N.equals(byWhat))
     {
-      fc.setAttributeName(fromAttributeDisplayName(byWhat));
+      fc.setAttributeName(FeatureMatcher.fromAttributeDisplayName(byWhat));
     }
 
     /*
@@ -989,30 +985,6 @@ public class FeatureTypeSettings extends JalviewDialog
     return fc;
   }
 
-  /**
-   * A helper method that converts a 'compound' attribute name from its display
-   * form, e.g. CSQ:PolyPhen to array form, e.g. { "CSQ", "PolyPhen" }
-   * 
-   * @param attribute
-   * @return
-   */
-  private String[] fromAttributeDisplayName(String attribute)
-  {
-    return attribute == null ? null : attribute.split(COLON);
-  }
-
-  /**
-   * A helper method that converts a 'compound' attribute name to its display
-   * form, e.g. CSQ:PolyPhen from its array form, e.g. { "CSQ", "PolyPhen" }
-   * 
-   * @param attName
-   * @return
-   */
-  private String toAttributeDisplayName(String[] attName)
-  {
-    return attName == null ? "" : String.join(COLON, attName);
-  }
-
   @Override
   protected void raiseClosed()
   {
@@ -1040,7 +1012,7 @@ public class FeatureTypeSettings extends JalviewDialog
   {
     fr.setColour(featureType, originalColour);
     fr.setFeatureFilter(featureType, originalFilter);
-    ap.paintAlignment(true, true);
+    refreshDisplay(true);
   }
 
   /**
@@ -1050,33 +1022,47 @@ public class FeatureTypeSettings extends JalviewDialog
   {
     try
     {
+      /*
+       * set 'adjusting' flag while moving the slider, so it 
+       * doesn't then in turn change the value (with rounding)
+       */
       adjusting = true;
       float f = Float.parseFloat(thresholdValue.getText());
-      slider.setValue((int) (f * scaleFactor));
+      f = Float.max(f,  this.min);
+      f = Float.min(f, this.max);
+      setThresholdValueText(f);
+      slider.setSliderValue(f);
       threshline.value = f;
       thresholdValue.setBackground(Color.white); // ok
-
-      /*
-       * force repaint of any Overview window or structure
-       */
-      ap.paintAlignment(true, true);
+      adjusting = false;
+      colourChanged(true);
     } catch (NumberFormatException ex)
     {
       thresholdValue.setBackground(Color.red); // not ok
-    } finally
-    {
       adjusting = false;
     }
   }
 
   /**
+   * Sets the text field for threshold value, rounded to four significant figures
+   * 
+   * @param f
+   */
+  void setThresholdValueText(float f)
+  {
+    BigDecimal formatted = new BigDecimal(f).round(FOUR_SIG_FIG)
+            .stripTrailingZeros();
+    thresholdValue.setText(formatted.toPlainString());
+  }
+
+  /**
    * Action on change of threshold slider value. This may be done interactively
    * (by moving the slider), or programmatically (to update the slider after
    * manual input of a threshold value).
    */
   protected void sliderValueChanged()
   {
-    threshline.value = getRoundedSliderValue();
+    threshline.value = slider.getSliderValue();
 
     /*
      * repaint alignment, but not Overview or structure,
@@ -1085,21 +1071,6 @@ public class FeatureTypeSettings extends JalviewDialog
     colourChanged(false);
   }
 
-  /**
-   * Converts the slider value to its absolute value by dividing by the
-   * scaleFactor. Rounding errors are squashed by forcing min/max of slider range
-   * to the actual min/max of feature score range
-   * 
-   * @return
-   */
-  private float getRoundedSliderValue()
-  {
-    int value = slider.getValue();
-    float f = value == slider.getMaximum() ? max
-            : (value == slider.getMinimum() ? min : value / scaleFactor);
-    return f;
-  }
-
   void addActionListener(ActionListener listener)
   {
     if (featureSettings != null)
@@ -1111,11 +1082,11 @@ public class FeatureTypeSettings extends JalviewDialog
   }
 
   /**
-   * A helper method to build the drop-down choice of attributes for a feature. If
-   * 'withRange' is true, then Score, and any attributes with a min-max range, are
-   * added. If 'withText' is true, Label and any known attributes are added. This
-   * allows 'categorical numerical' attributes e.g. codon position to be coloured
-   * by text.
+   * A helper method to build the drop-down choice of attributes for a feature.
+   * If 'withRange' is true, then Score, and any attributes with a min-max
+   * range, are added. If 'withText' is true, Label and any known attributes are
+   * added. This allows 'categorical numerical' attributes e.g. codon position
+   * to be coloured by text.
    * <p>
    * Where metadata is available with a description for an attribute, that is
    * added as a tooltip.
@@ -1129,7 +1100,7 @@ public class FeatureTypeSettings extends JalviewDialog
    * @param withRange
    * @param withText
    */
-  protected JComboBox<String> populateAttributesDropdown(
+  protected JComboBox<Object> populateAttributesDropdown(
           List<String[]> attNames, boolean withRange, boolean withText)
   {
     List<String> displayAtts = new ArrayList<>();
@@ -1159,7 +1130,7 @@ public class FeatureTypeSettings extends JalviewDialog
       {
         continue;
       }
-      displayAtts.add(toAttributeDisplayName(attName));
+      displayAtts.add(FeatureMatcher.toAttributeDisplayName(attName));
       String desc = fa.getDescription(featureType, attName);
       if (desc != null && desc.length() > MAX_TOOLTIP_LENGTH)
       {
@@ -1168,9 +1139,11 @@ public class FeatureTypeSettings extends JalviewDialog
       tooltips.add(desc == null ? "" : desc);
     }
 
-    JComboBox<String> attCombo = JvSwingUtils
-            .buildComboWithTooltips(displayAtts, tooltips);
-
+    // now convert String List to Object List for buildComboWithTooltips
+    List<Object> displayAttsObjects = new ArrayList<>(displayAtts);
+    JComboBox<Object> attCombo = JvSwingUtils
+            .buildComboWithTooltips(displayAttsObjects, tooltips);
+    
     return attCombo;
   }
 
@@ -1210,7 +1183,6 @@ public class FeatureTypeSettings extends JalviewDialog
   {
     JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
     andOrPanel.setBackground(Color.white);
-    andOrPanel.setBorder(BorderFactory.createLineBorder(debugBorderColour));
     andFilters = new JRadioButton(MessageManager.getString("label.and"));
     orFilters = new JRadioButton(MessageManager.getString("label.or"));
     ActionListener actionListener = new ActionListener()
@@ -1293,7 +1265,6 @@ public class FeatureTypeSettings extends JalviewDialog
     for (FeatureMatcherI filter : filters)
     {
       JPanel row = addFilter(filter, attNames, filterIndex);
-      row.setBorder(BorderFactory.createLineBorder(debugBorderColour));
       chooseFiltersPanel.add(row);
       filterIndex++;
     }
@@ -1305,7 +1276,8 @@ public class FeatureTypeSettings extends JalviewDialog
   /**
    * A helper method that constructs a row (panel) with one filter condition:
    * <ul>
-   * <li>a drop-down list of Label, Score and attribute names to choose from</li>
+   * <li>a drop-down list of Label, Score and attribute names to choose
+   * from</li>
    * <li>a drop-down list of conditions to choose from</li>
    * <li>a text field for input of a match pattern</li>
    * <li>optionally, a 'remove' button</li>
@@ -1342,7 +1314,7 @@ public class FeatureTypeSettings extends JalviewDialog
      * drop-down choice of attribute, with description as a tooltip 
      * if we can obtain it
      */
-    final JComboBox<String> attCombo = populateAttributesDropdown(attNames,
+    final JComboBox<Object> attCombo = populateAttributesDropdown(attNames,
             true, true);
     String filterBy = setSelectedAttribute(attCombo, filter);
 
@@ -1383,7 +1355,8 @@ public class FeatureTypeSettings extends JalviewDialog
     }
     else
     {
-      attCombo.setSelectedItem(toAttributeDisplayName(attName));
+      attCombo.setSelectedItem(
+              FeatureMatcher.toAttributeDisplayName(attName));
     }
     attCombo.addItemListener(new ItemListener()
     {
@@ -1429,16 +1402,13 @@ public class FeatureTypeSettings extends JalviewDialog
      * disable pattern field for condition 'Present / NotPresent'
      */
     Condition selectedCondition = (Condition) condCombo.getSelectedItem();
-    if (!selectedCondition.needsAPattern())
-    {
-      patternField.setEnabled(false);
-    }
+    patternField.setEnabled(selectedCondition.needsAPattern());
 
     /*
      * if a numeric condition is selected, show the value range
      * as a tooltip on the value input field
      */
-    setPatternTooltip(filterBy, selectedCondition, patternField);
+    setNumericHints(filterBy, selectedCondition, patternField);
 
     /*
      * add remove button if filter is populated (non-empty pattern)
@@ -1446,10 +1416,10 @@ public class FeatureTypeSettings extends JalviewDialog
     if (!patternField.isEnabled()
             || (pattern != null && pattern.trim().length() > 0))
     {
-      // todo: gif for button drawing '-' or 'x'
-      JButton removeCondition = new BasicArrowButton(SwingConstants.WEST);
-      removeCondition
-              .setToolTipText(MessageManager.getString("label.delete_row"));
+      JButton removeCondition = new JButton("\u2717"); // Dingbats cursive x
+      removeCondition.setToolTipText(
+              MessageManager.getString("label.delete_condition"));
+      removeCondition.setBorder(new EmptyBorder(0, 0, 0, 0));
       removeCondition.addActionListener(new ActionListener()
       {
         @Override
@@ -1472,7 +1442,7 @@ public class FeatureTypeSettings extends JalviewDialog
    * @param attCombo
    * @param filter
    */
-  private String setSelectedAttribute(JComboBox<String> attCombo,
+  private String setSelectedAttribute(JComboBox<Object> attCombo,
           FeatureMatcherI filter)
   {
     String item = null;
@@ -1486,22 +1456,27 @@ public class FeatureTypeSettings extends JalviewDialog
     }
     else
     {
-      item = toAttributeDisplayName(filter.getAttribute());
+      item = FeatureMatcher.toAttributeDisplayName(filter.getAttribute());
     }
     attCombo.setSelectedItem(item);
     return item;
   }
 
   /**
-   * If a numeric comparison condition is selected, retrieve the min-max range for
-   * the value (score or attribute), and set it as a tooltip on the value file
+   * If a numeric comparison condition is selected, retrieves the min-max range
+   * for the value (score or attribute), and sets it as a tooltip on the value
+   * field. If the field is currently empty, then pre-populates it with
+   * <ul>
+   * <li>the minimum value, if condition is > or >=</li>
+   * <li>the maximum value, if condition is < or <=</li>
+   * </ul>
    * 
    * @param attName
    * @param selectedCondition
    * @param patternField
    */
-  private void setPatternTooltip(String attName,
-          Condition selectedCondition, JTextField patternField)
+  private void setNumericHints(String attName, Condition selectedCondition,
+          JTextField patternField)
   {
     patternField.setToolTipText("");
 
@@ -1510,19 +1485,36 @@ public class FeatureTypeSettings extends JalviewDialog
       float[] minMax = getMinMax(attName);
       if (minMax != null)
       {
-        String tip = String.format("(%s - %s)",
-                DECFMT_2_2.format(minMax[0]), DECFMT_2_2.format(minMax[1]));
+        String minFormatted = DECFMT_2_2.format(minMax[0]);
+        String maxFormatted = DECFMT_2_2.format(minMax[1]);
+        String tip = String.format("(%s - %s)", minFormatted, maxFormatted);
         patternField.setToolTipText(tip);
+        if (patternField.getText().isEmpty())
+        {
+          if (selectedCondition == Condition.GE
+                  || selectedCondition == Condition.GT)
+          {
+            patternField.setText(minFormatted);
+          }
+          else
+          {
+            if (selectedCondition == Condition.LE
+                    || selectedCondition == Condition.LT)
+            {
+              patternField.setText(maxFormatted);
+            }
+          }
+        }
       }
     }
   }
 
   /**
-   * Populates the drop-down list of comparison conditions for the given attribute
-   * name. The conditions added depend on the datatype of the attribute values.
-   * The supplied condition is set as the selected item in the list, provided it
-   * is in the list. If the pattern is now invalid (non-numeric pattern for a
-   * numeric condition), it is cleared.
+   * Populates the drop-down list of comparison conditions for the given
+   * attribute name. The conditions added depend on the datatype of the
+   * attribute values. The supplied condition is set as the selected item in the
+   * list, provided it is in the list. If the pattern is now invalid
+   * (non-numeric pattern for a numeric condition), it is cleared.
    * 
    * @param attName
    * @param cond
@@ -1533,7 +1525,7 @@ public class FeatureTypeSettings extends JalviewDialog
           JComboBox<Condition> condCombo, JTextField patternField)
   {
     Datatype type = FeatureAttributes.getInstance().getDatatype(featureType,
-            fromAttributeDisplayName(attName));
+            FeatureMatcher.fromAttributeDisplayName(attName));
     if (LABEL_18N.equals(attName))
     {
       type = Datatype.Character;
@@ -1549,10 +1541,11 @@ public class FeatureTypeSettings extends JalviewDialog
     ItemListener listener = condCombo.getItemListeners()[0];
     condCombo.removeItemListener(listener);
     boolean condIsValid = false;
+
     condCombo.removeAllItems();
     for (Condition c : Condition.values())
     {
-      if ((c.isNumeric() && type != Datatype.Character)
+      if ((c.isNumeric() && type == Datatype.Number)
               || (!c.isNumeric() && type != Datatype.Number))
       {
         condCombo.addItem(c);
@@ -1575,8 +1568,6 @@ public class FeatureTypeSettings extends JalviewDialog
       condCombo.setSelectedIndex(0);
     }
 
-    condCombo.addItemListener(listener);
-
     /*
      * clear pattern if it is now invalid for condition
      */
@@ -1594,15 +1585,20 @@ public class FeatureTypeSettings extends JalviewDialog
         patternField.setText("");
       }
     }
+
+    /*
+     * restore the listener
+     */
+    condCombo.addItemListener(listener);
   }
 
   /**
-   * Answers true unless a numeric condition has been selected with a non-numeric
-   * value. Sets the value field to RED with a tooltip if in error.
+   * Answers true unless a numeric condition has been selected with a
+   * non-numeric value. Sets the value field to RED with a tooltip if in error.
    * <p>
-   * If the pattern is expected but is empty, this method returns false, but does
-   * not mark the field as invalid. This supports selecting an attribute for a new
-   * condition before a match pattern has been entered.
+   * If the pattern is expected but is empty, this method returns false, but
+   * does not mark the field as invalid. This supports selecting an attribute
+   * for a new condition before a match pattern has been entered.
    * 
    * @param value
    * @param condCombo
@@ -1648,32 +1644,42 @@ public class FeatureTypeSettings extends JalviewDialog
 
   /**
    * Constructs a filter condition from the given input fields, and replaces the
-   * condition at filterIndex with the new one. Does nothing if the pattern field
-   * is blank (unless the match condition is one that doesn't require a pattern,
-   * e.g. 'Is present'). Answers true if the filter was updated, else false.
+   * condition at filterIndex with the new one. Does nothing if the pattern
+   * field is blank (unless the match condition is one that doesn't require a
+   * pattern, e.g. 'Is present'). Answers true if the filter was updated, else
+   * false.
    * <p>
    * This method may update the tooltip on the filter value field to show the
-   * value range, if a numeric condition is selected. This ensures the tooltip is
-   * updated when a numeric valued attribute is chosen on the last 'add a filter'
-   * row.
+   * value range, if a numeric condition is selected. This ensures the tooltip
+   * is updated when a numeric valued attribute is chosen on the last 'add a
+   * filter' row.
    * 
    * @param attCombo
    * @param condCombo
    * @param valueField
    * @param filterIndex
    */
-  protected boolean updateFilter(JComboBox<String> attCombo,
+  protected boolean updateFilter(JComboBox<Object> attCombo,
           JComboBox<Condition> condCombo, JTextField valueField,
           int filterIndex)
   {
-    String attName = (String) attCombo.getSelectedItem();
+    String attName;
+    try
+    {
+      attName = (String) attCombo.getSelectedItem();
+    } catch (Exception e)
+    {
+      Cache.log.error("Problem casting Combo box entry to String");
+      attName = attCombo.getSelectedItem().toString();
+    }
     Condition cond = (Condition) condCombo.getSelectedItem();
     String pattern = valueField.getText().trim();
 
-    setPatternTooltip(attName, cond, valueField);
+    setNumericHints(attName, cond, valueField);
 
     if (pattern.length() == 0 && cond.needsAPattern())
     {
+      valueField.setEnabled(true); // ensure pattern field is enabled!
       return false;
     }
 
@@ -1693,7 +1699,7 @@ public class FeatureTypeSettings extends JalviewDialog
     else
     {
       km = FeatureMatcher.byAttribute(cond, pattern,
-              fromAttributeDisplayName(attName));
+              FeatureMatcher.fromAttributeDisplayName(attName));
     }
 
     filters.set(filterIndex, km);
@@ -1702,17 +1708,6 @@ public class FeatureTypeSettings extends JalviewDialog
   }
 
   /**
-   * Makes the dialog visible, at the Feature Colour tab or at the Filters tab
-   * 
-   * @param coloursTab
-   */
-  public void showTab(boolean coloursTab)
-  {
-    setVisible(true);
-    tabbedPane.setSelectedIndex(coloursTab ? 0 : 1);
-  }
-
-  /**
    * Action on any change to feature filtering, namely
    * <ul>
    * <li>change of selected attribute</li>
@@ -1720,8 +1715,8 @@ public class FeatureTypeSettings extends JalviewDialog
    * <li>change of match pattern</li>
    * <li>removal of a condition</li>
    * </ul>
-   * The inputs are parsed into a combined filter and this is set for the feature
-   * type, and the alignment redrawn.
+   * The inputs are parsed into a combined filter and this is set for the
+   * feature type, and the alignment redrawn.
    */
   protected void filtersChanged()
   {
@@ -1753,8 +1748,26 @@ public class FeatureTypeSettings extends JalviewDialog
      * (note this might now be an empty filter with no conditions)
      */
     fr.setFeatureFilter(featureType, combined.isEmpty() ? null : combined);
-    ap.paintAlignment(true, true);
+    refreshDisplay(true);
 
     updateFiltersTab();
   }
+
+  /**
+   * Repaints alignment, structure and overview (if shown). If there is a
+   * complementary view which is showing this view's features, then also
+   * repaints that.
+   * 
+   * @param updateStructsAndOverview
+   */
+  void refreshDisplay(boolean updateStructsAndOverview)
+  {
+    ap.paintAlignment(true, updateStructsAndOverview);
+    AlignViewportI complement = ap.getAlignViewport().getCodingComplement();
+    if (complement != null && complement.isShowComplementFeatures())
+    {
+      AlignFrame af2 = Desktop.getAlignFrameFor(complement);
+      af2.alignPanel.paintAlignment(true, updateStructsAndOverview);
+    }
+  }
 }