Merge branch 'feature/JAL-3187linkedFeatures' into
[jalview.git] / src / jalview / gui / FeatureSettings.java
index 47c0cc5..058de9c 100644 (file)
@@ -29,9 +29,11 @@ import jalview.datamodel.SequenceI;
 import jalview.datamodel.features.FeatureMatcherI;
 import jalview.datamodel.features.FeatureMatcherSet;
 import jalview.datamodel.features.FeatureMatcherSetI;
+import jalview.datamodel.ontology.OntologyI;
 import jalview.gui.Help.HelpId;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
+import jalview.io.gff.SequenceOntologyFactory;
 import jalview.schemes.FeatureColour;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
@@ -67,6 +69,7 @@ import java.io.FileOutputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -83,6 +86,7 @@ import javax.swing.BorderFactory;
 import javax.swing.Icon;
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
+import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JColorChooser;
 import javax.swing.JDialog;
 import javax.swing.JInternalFrame;
@@ -95,6 +99,7 @@ import javax.swing.JScrollPane;
 import javax.swing.JSlider;
 import javax.swing.JTable;
 import javax.swing.ListSelectionModel;
+import javax.swing.RowFilter;
 import javax.swing.SwingConstants;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
@@ -102,6 +107,7 @@ import javax.swing.table.AbstractTableModel;
 import javax.swing.table.TableCellEditor;
 import javax.swing.table.TableCellRenderer;
 import javax.swing.table.TableColumn;
+import javax.swing.table.TableRowSorter;
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBElement;
 import javax.xml.bind.Marshaller;
@@ -111,6 +117,8 @@ import javax.xml.stream.XMLStreamReader;
 public class FeatureSettings extends JPanel
         implements FeatureSettingsControllerI
 {
+  private static final Font VERDANA_12 = new Font("Verdana", Font.PLAIN, 12);
+
   private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
           .getString("label.sequence_feature_colours");
 
@@ -167,12 +175,6 @@ public class FeatureSettings extends JPanel
 
   int selectedRow = -1;
 
-  JButton fetchDAS = new JButton();
-
-  JButton saveDAS = new JButton();
-
-  JButton cancelDAS = new JButton();
-
   boolean resettingTable = false;
 
   /*
@@ -185,6 +187,17 @@ public class FeatureSettings extends JPanel
    */
   Map<String, float[]> typeWidth = null;
 
+  /*
+   * if true, 'child' feature types are not displayed
+   */
+  JCheckBox summaryView;
+
+  /*
+   * those feature types that do not have a parent feature type present
+   * (as determined by an Ontology relationship)
+   */
+  List<String> topLevelTypes;
+
   /**
    * Constructor
    * 
@@ -203,6 +216,8 @@ public class FeatureSettings extends JPanel
     originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
     originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
 
+    topLevelTypes = new ArrayList<>();
+
     try
     {
       jbInit();
@@ -211,6 +226,69 @@ public class FeatureSettings extends JPanel
       ex.printStackTrace();
     }
 
+    initTable();
+
+    scrollPane.setViewportView(table);
+
+    if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
+    {
+      fr.findAllFeatures(true); // display everything!
+    }
+
+    discoverAllFeatureData();
+    final PropertyChangeListener change;
+    final FeatureSettings fs = this;
+    fr.addPropertyChangeListener(change = new PropertyChangeListener()
+    {
+      @Override
+      public void propertyChange(PropertyChangeEvent evt)
+      {
+        if (!fs.resettingTable && !fs.handlingUpdate)
+        {
+          fs.handlingUpdate = true;
+          fs.resetTable(null);
+          // new groups may be added with new sequence feature types only
+          fs.handlingUpdate = false;
+        }
+      }
+    });
+
+    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);
+    }
+    frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
+
+    frame.addInternalFrameListener(
+            new javax.swing.event.InternalFrameAdapter()
+            {
+              @Override
+              public void internalFrameClosed(
+                      javax.swing.event.InternalFrameEvent evt)
+              {
+                fr.removePropertyChangeListener(change);
+              };
+            });
+    frame.setLayer(JLayeredPane.PALETTE_LAYER);
+    inConstruction = false;
+  }
+
+  /**
+   * Constructs and configures the JTable which displays columns of data for
+   * each feature type
+   */
+  protected void initTable()
+  {
     table = new JTable()
     {
       @Override
@@ -221,7 +299,13 @@ public class FeatureSettings extends JPanel
         switch (column)
         {
         case TYPE_COLUMN:
-          tip = JvSwingUtils.wrapTooltip(true, MessageManager
+          /*
+           * drag to reorder not enabled in Summary View
+           */
+          tip = summaryView.isSelected()
+                  ? MessageManager.getString(
+                          "label.feature_settings_select_columns")
+                  : JvSwingUtils.wrapTooltip(true, MessageManager
                   .getString("label.feature_settings_click_drag"));
           break;
         case FILTER_COLUMN:
@@ -239,12 +323,9 @@ public class FeatureSettings extends JPanel
         return tip;
       }
     };
-    table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
-    table.setFont(new Font("Verdana", Font.PLAIN, 12));
+    table.getTableHeader().setFont(VERDANA_12);
+    table.setFont(VERDANA_12);
 
-    // table.setDefaultRenderer(Color.class, new ColorRenderer());
-    // table.setDefaultEditor(Color.class, new ColorEditor(this));
-    //
     table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
     table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
 
@@ -271,16 +352,16 @@ 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());
+          popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
         }
         else if (evt.getClickCount() == 2)
         {
           boolean invertSelection = evt.isAltDown();
           boolean toggleSelection = Platform.isControlDown(evt);
           boolean extendSelection = evt.isShiftDown();
+          String[] terms = getTermsInScope(type);
           fr.ap.alignFrame.avc.markColumnsContainingFeatures(
-                  invertSelection, extendSelection, toggleSelection, type);
+                  invertSelection, extendSelection, toggleSelection, terms);
         }
       }
 
@@ -293,8 +374,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());
+          popupMenu(selectedRow, type, colour, evt.getX(), evt.getY());
         }
       }
     });
@@ -305,89 +385,44 @@ public class FeatureSettings extends JPanel
       public void mouseDragged(MouseEvent evt)
       {
         int newRow = table.rowAtPoint(evt.getPoint());
-        if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
-        {
-          /*
-           * reposition 'selectedRow' to 'newRow' (the dragged to location)
-           * this could be more than one row away for a very fast drag action
-           * so just swap it with adjacent rows until we get it there
-           */
-          Object[][] data = ((FeatureTableModel) table.getModel())
-                  .getData();
-          int direction = newRow < selectedRow ? -1 : 1;
-          for (int i = selectedRow; i != newRow; i += direction)
-          {
-            Object[] temp = data[i];
-            data[i] = data[i + direction];
-            data[i + direction] = temp;
-          }
-          updateFeatureRenderer(data);
-          table.repaint();
-          selectedRow = newRow;
-        }
+        dragRow(newRow);
       }
     });
-    // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
-    // MessageManager.getString("label.feature_settings_click_drag")));
-    scrollPane.setViewportView(table);
+  }
 
-    if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
+  /**
+   * Answers an array consisting of the given type, and also (if 'Summary View'
+   * is selected), any child terms in the sequence ontology
+   * 
+   * @param type
+   * @return
+   */
+  protected String[] getTermsInScope(String type)
+  {
+    if (!summaryView.isSelected())
     {
-      fr.findAllFeatures(true); // display everything!
+      return new String[] { type };
     }
 
-    discoverAllFeatureData();
-    final PropertyChangeListener change;
-    final FeatureSettings fs = this;
-    fr.addPropertyChangeListener(change = new PropertyChangeListener()
-    {
-      @Override
-      public void propertyChange(PropertyChangeEvent evt)
-      {
-        if (!fs.resettingTable && !fs.handlingUpdate)
-        {
-          fs.handlingUpdate = true;
-          fs.resetTable(null);
-          // new groups may be added with new sequence feature types only
-          fs.handlingUpdate = false;
-        }
-      }
+    List<String> terms = new ArrayList<>();
+    terms.add(type);
 
-    });
+    OntologyI so = SequenceOntologyFactory.getInstance();
 
-    frame = new JInternalFrame();
-    frame.setContentPane(this);
-    if (Platform.isAMac())
-    {
-      Desktop.addInternalFrame(frame,
-              MessageManager.getString("label.sequence_feature_settings"),
-              600, 480);
-    }
-    else
+    Object[][] data = ((FeatureTableModel) table.getModel()).getData();
+    for (Object[] row : data)
     {
-      Desktop.addInternalFrame(frame,
-              MessageManager.getString("label.sequence_feature_settings"),
-              600, 450);
+      String type2 = (String) row[TYPE_COLUMN];
+      if (!type2.equals(type) && so.isA(type2, type))
+      {
+        terms.add(type2);
+      }
     }
-    frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
-
-    frame.addInternalFrameListener(
-            new javax.swing.event.InternalFrameAdapter()
-            {
-              @Override
-              public void internalFrameClosed(
-                      javax.swing.event.InternalFrameEvent evt)
-              {
-                fr.removePropertyChangeListener(change);
-              };
-            });
-    frame.setLayer(JLayeredPane.PALETTE_LAYER);
-    inConstruction = false;
+    return terms.toArray(new String[terms.size()]);
   }
 
-  protected void popupSort(final int rowSelected, final String type,
-          final Object typeCol, final Map<String, float[][]> minmax, int x,
-          int y)
+  protected void popupMenu(final int rowSelected, final String type,
+          final Object typeCol, int x, int y)
   {
     final FeatureColourI featureColour = (FeatureColourI) typeCol;
 
@@ -400,32 +435,89 @@ public class FeatureSettings extends JPanel
     final FeatureSettings me = this;
     scr.addActionListener(new ActionListener()
     {
-
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        me.af.avc
-                .sortAlignmentByFeatureScore(Arrays.asList(new String[]
-                { type }));
+        String[] types = getTermsInScope(type);
+        me.af.avc.sortAlignmentByFeatureScore(Arrays.asList(types));
       }
-
     });
     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 }));
+        String[] types = getTermsInScope(type);
+        me.af.avc.sortAlignmentByFeatureDensity(Arrays.asList(types));
       }
-
     });
     men.add(dens);
 
+    // fixme is Variable Colour in popup menu or not
+    /*
+     * variable colour options include colour by label, by score,
+     * by selected attribute text, or attribute value
+     */
+    final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
+            MessageManager.getString("label.variable_colour"));
+    mxcol.setSelected(!featureColour.isSimpleColour());
+    men.add(mxcol);
+    mxcol.addActionListener(new ActionListener()
+    {
+      JColorChooser colorChooser;
+
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        if (e.getSource() == mxcol)
+        {
+          if (featureColour.isSimpleColour())
+          {
+            FeatureTypeSettings fc = new FeatureTypeSettings(me.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);
+          }
+        }
+        else
+        {
+          if (e.getSource() instanceof FeatureTypeSettings)
+          {
+            /*
+             * update after OK in feature colour dialog; the updated
+             * 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);
+          }
+        }
+      }
+    });
+
     JMenuItem selCols = new JMenuItem(
             MessageManager.getString("label.select_columns_containing"));
     selCols.addActionListener(new ActionListener()
@@ -433,8 +525,9 @@ public class FeatureSettings extends JPanel
       @Override
       public void actionPerformed(ActionEvent arg0)
       {
+        String[] types = getTermsInScope(type);
         fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
-                false, type);
+                false, types);
       }
     });
     JMenuItem clearCols = new JMenuItem(MessageManager
@@ -444,8 +537,9 @@ public class FeatureSettings extends JPanel
       @Override
       public void actionPerformed(ActionEvent arg0)
       {
+        String[] types = getTermsInScope(type);
         fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
-                false, type);
+                false, types);
       }
     });
     JMenuItem hideCols = new JMenuItem(
@@ -455,7 +549,8 @@ public class FeatureSettings extends JPanel
       @Override
       public void actionPerformed(ActionEvent arg0)
       {
-        fr.ap.alignFrame.hideFeatureColumns(type, true);
+        String[] types = getTermsInScope(type);
+        fr.ap.alignFrame.hideFeatureColumns(true, types);
       }
     });
     JMenuItem hideOtherCols = new JMenuItem(
@@ -465,7 +560,8 @@ public class FeatureSettings extends JPanel
       @Override
       public void actionPerformed(ActionEvent arg0)
       {
-        fr.ap.alignFrame.hideFeatureColumns(type, false);
+        String[] types = getTermsInScope(type);
+        fr.ap.alignFrame.hideFeatureColumns(false, types);
       }
     });
     men.add(selCols);
@@ -580,6 +676,7 @@ public class FeatureSettings extends JPanel
        */
       Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
               visibleGroups.toArray(new String[visibleGroups.size()]));
+
       for (String type : types)
       {
         displayableTypes.add(type);
@@ -598,6 +695,13 @@ public class FeatureSettings extends JPanel
       }
     }
 
+    /*
+     * enable 'Summary View' if some types are sub-types of others
+     */
+    Set<String> parents = SequenceOntologyFactory.getInstance()
+            .getParentTerms(displayableTypes);
+    summaryView.setEnabled(parents.size() < displayableTypes.size());
+
     Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
     int dataIndex = 0;
 
@@ -620,7 +724,6 @@ public class FeatureSettings extends JPanel
         {
           continue;
         }
-
         data[dataIndex][TYPE_COLUMN] = type;
         data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
         FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
@@ -672,7 +775,43 @@ public class FeatureSettings extends JPanel
       updateOriginalData(data);
     }
 
-    table.setModel(new FeatureTableModel(data));
+    /*
+     * recreate the table model
+     */
+    FeatureTableModel dataModel = new FeatureTableModel(data);
+    table.setModel(dataModel);
+
+    /*
+     * we want to be able to filter out rows for sub-types, but not to sort 
+     * rows, so have to add a RowFilter to a disabled TableRowSorter (!)
+     */
+    final TableRowSorter<FeatureTableModel> sorter = new TableRowSorter<>(
+            dataModel);
+    for (int i = 0; i < table.getColumnCount(); i++)
+    {
+      sorter.setSortable(i, false);
+    }
+
+    /*
+     * filter rows to only top-level Ontology types if requested
+     */
+    sorter.setRowFilter(new RowFilter<FeatureTableModel, Integer>()
+    {
+      @Override
+      public boolean include(
+              Entry<? extends FeatureTableModel, ? extends Integer> entry)
+      {
+        if (!summaryView.isSelected())
+        {
+          return true;
+        }
+        int row = entry.getIdentifier(); // this is model, not view, row number
+        String featureType = (String) entry.getModel().getData()[row][TYPE_COLUMN];
+        return parents.contains(featureType);
+      }
+    });
+    table.setRowSorter(sorter);
+
     table.getColumnModel().getColumn(0).setPreferredWidth(200);
 
     groupPanel.setLayout(
@@ -1268,6 +1407,20 @@ public class FeatureSettings extends JPanel
       }
     });
 
+    summaryView = new JCheckBox(
+            MessageManager.getString("label.summary_view"));
+    summaryView
+            .setToolTipText(
+                    MessageManager.getString("label.summary_view_tip"));
+    summaryView.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        resetTable(null);
+      }
+    });
+
     transparency.setMaximum(70);
     transparency.setToolTipText(
             MessageManager.getString("label.transparency_tip"));
@@ -1313,7 +1466,8 @@ public class FeatureSettings extends JPanel
 
     boolean hasComplement = af.getViewport().getCodingComplement() != null;
     JPanel transPanelLeft = new JPanel(
-            new GridLayout(hasComplement ? 3 : 2, 1));
+            new GridLayout(hasComplement ? 4 : 3, 1));
+    transPanelLeft.add(summaryView);
     transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
     transPanelLeft.add(transparency);
     if (hasComplement)
@@ -1353,6 +1507,59 @@ public class FeatureSettings extends JPanel
     }
   }
 
+  /**
+   * Reorders features by 'dragging' selectedRow to 'newRow'
+   * 
+   * @param newRow
+   */
+  protected void dragRow(int newRow)
+  {
+    if (summaryView.isSelected())
+    {
+      // no drag while in summary view
+      return;
+    }
+
+    if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
+    {
+      /*
+       * reposition 'selectedRow' to 'newRow' (the dragged to location)
+       * this could be more than one row away for a very fast drag action
+       * so just swap it with adjacent rows until we get it there
+       */
+      Object[][] data = ((FeatureTableModel) table.getModel())
+              .getData();
+      int direction = newRow < selectedRow ? -1 : 1;
+      for (int i = selectedRow; i != newRow; i += direction)
+      {
+        Object[] temp = data[i];
+        data[i] = data[i + direction];
+        data[i + direction] = temp;
+      }
+      updateFeatureRenderer(data);
+      table.repaint();
+      selectedRow = newRow;
+    }
+  }
+
+  protected void refreshTable()
+  {
+    Object[][] data = ((FeatureTableModel) table.getModel()).getData();
+    for (Object[] row : data)
+    {
+      String type = (String) row[TYPE_COLUMN];
+      FeatureColourI colour = fr.getFeatureColours().get(type);
+      FeatureMatcherSetI filter = fr.getFeatureFilter(type);
+      if (filter == null)
+      {
+        filter = new FeatureMatcherSet();
+      }
+      row[COLOUR_COLUMN] = colour;
+      row[FILTER_COLUMN] = filter;
+    }
+    repaint();
+  }
+
   // ///////////////////////////////////////////////////////////////////////
   // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
   // ///////////////////////////////////////////////////////////////////////
@@ -1429,20 +1636,57 @@ public class FeatureSettings extends JPanel
       }
     }
 
+    /**
+     * Answers true for all columns except Feature Type
+     */
     @Override
     public boolean isCellEditable(int row, int col)
     {
-      return col == 0 ? false : true;
+      return col != TYPE_COLUMN;
     }
 
+    /**
+     * Sets the value in the model for a given row and column. If Visibility
+     * (Show/Hide) is being set, and the table is in Summary View, then it is
+     * set also on any sub-types of the row's feature type.
+     */
     @Override
     public void setValueAt(Object value, int row, int col)
     {
       data[row][col] = value;
       fireTableCellUpdated(row, col);
+      if (summaryView.isSelected() && col == SHOW_COLUMN)
+      {
+        setSubtypesVisibility(row, (Boolean) value);
+      }
       updateFeatureRenderer(data);
     }
 
+    /**
+     * Sets the visibility of any feature types which are sub-types of the type
+     * in the given row of the table
+     * 
+     * @param row
+     * @param value
+     */
+    protected void setSubtypesVisibility(int row, Boolean value)
+    {
+      String type = (String) data[row][TYPE_COLUMN];
+      OntologyI so = SequenceOntologyFactory.getInstance();
+
+      for (int r = 0; r < data.length; r++)
+      {
+        if (r != row)
+        {
+          String type2 = (String) data[r][TYPE_COLUMN];
+          if (so.isA(type2, type))
+          {
+            data[r][SHOW_COLUMN] = value;
+            fireTableCellUpdated(r, SHOW_COLUMN);
+          }
+        }
+      }
+    }
   }
 
   class ColorRenderer extends JLabel implements TableCellRenderer
@@ -1655,7 +1899,7 @@ public class FeatureSettings extends JPanel
 
     String type;
 
-    JButton button;
+    JButton colourButton;
 
     JColorChooser colorChooser;
 
@@ -1672,13 +1916,13 @@ public class FeatureSettings extends JPanel
       // which is a button.
       // This button brings up the color chooser dialog,
       // which is the editor from the user's point of view.
-      button = new JButton();
-      button.setActionCommand(EDIT);
-      button.addActionListener(this);
-      button.setBorderPainted(false);
+      colourButton = new JButton();
+      colourButton.setActionCommand(EDIT);
+      colourButton.addActionListener(this);
+      colourButton.setBorderPainted(false);
       // Set up the dialog that the button brings up.
       colorChooser = new JColorChooser();
-      dialog = JColorChooser.createDialog(button,
+      dialog = JColorChooser.createDialog(colourButton,
               MessageManager.getString("label.select_colour"), true, // modal
               colorChooser, this, // OK button handler
               null); // no CANCEL button handler
@@ -1698,7 +1942,7 @@ public class FeatureSettings extends JPanel
         if (currentColor.isSimpleColour())
         {
           // bring up simple color chooser
-          button.setBackground(currentColor.getColour());
+          colourButton.setBackground(currentColor.getColour());
           colorChooser.setColor(currentColor.getColour());
           dialog.setVisible(true);
         }
@@ -1706,13 +1950,8 @@ public class FeatureSettings extends JPanel
         {
           // bring up graduated chooser.
           chooser = new FeatureTypeSettings(me.fr, type);
-          /**
-           * @j2sNative
-           */
-          {
-            chooser.setRequestFocusEnabled(true);
-            chooser.requestFocus();
-          }
+          chooser.setRequestFocusEnabled(true);
+          chooser.requestFocus();
           chooser.addActionListener(this);
           // Make the renderer reappear.
           fireEditingStopped();
@@ -1735,16 +1974,7 @@ public class FeatureSettings extends JPanel
            * (or filters!) are already set in FeatureRenderer, so just
            * update table data without triggering updateFeatureRenderer
            */
-          currentColor = fr.getFeatureColours().get(type);
-          FeatureMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
-          if (currentFilter == null)
-          {
-            currentFilter = new FeatureMatcherSet();
-          }
-          Object[] data = ((FeatureTableModel) table.getModel())
-                  .getData()[rowSelected];
-          data[COLOUR_COLUMN] = currentColor;
-          data[FILTER_COLUMN] = currentFilter;
+          refreshTable();
         }
         fireEditingStopped();
         me.table.validate();
@@ -1766,24 +1996,24 @@ public class FeatureSettings extends JPanel
       currentColor = (FeatureColourI) value;
       this.rowSelected = row;
       type = me.table.getValueAt(row, TYPE_COLUMN).toString();
-      button.setOpaque(true);
-      button.setBackground(me.getBackground());
+      colourButton.setOpaque(true);
+      colourButton.setBackground(me.getBackground());
       if (!currentColor.isSimpleColour())
       {
         JLabel btn = new JLabel();
-        btn.setSize(button.getSize());
+        btn.setSize(colourButton.getSize());
         FeatureSettings.renderGraduatedColor(btn, currentColor);
-        button.setBackground(btn.getBackground());
-        button.setIcon(btn.getIcon());
-        button.setText(btn.getText());
+        colourButton.setBackground(btn.getBackground());
+        colourButton.setIcon(btn.getIcon());
+        colourButton.setText(btn.getText());
       }
       else
       {
-        button.setText("");
-        button.setIcon(null);
-        button.setBackground(currentColor.getColour());
+        colourButton.setText("");
+        colourButton.setIcon(null);
+        colourButton.setBackground(currentColor.getColour());
       }
-      return button;
+      return colourButton;
     }
   }
 
@@ -1804,7 +2034,7 @@ public class FeatureSettings extends JPanel
 
     String type;
 
-    JButton button;
+    JButton filterButton;
 
     protected static final String EDIT = "edit";
 
@@ -1813,10 +2043,10 @@ public class FeatureSettings extends JPanel
     public FilterEditor(FeatureSettings me)
     {
       this.me = me;
-      button = new JButton();
-      button.setActionCommand(EDIT);
-      button.addActionListener(this);
-      button.setBorderPainted(false);
+      filterButton = new JButton();
+      filterButton.setActionCommand(EDIT);
+      filterButton.addActionListener(this);
+      filterButton.setBorderPainted(false);
     }
 
     /**
@@ -1825,7 +2055,7 @@ public class FeatureSettings extends JPanel
     @Override
     public void actionPerformed(ActionEvent e)
     {
-      if (button == e.getSource())
+      if (filterButton == e.getSource())
       {
         FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
         chooser.addActionListener(this);
@@ -1842,22 +2072,12 @@ public class FeatureSettings extends JPanel
       }
       else if (e.getSource() instanceof Component)
       {
-
         /*
          * after OK in variable colour dialog, any changes to filter
          * (or colours!) are already set in FeatureRenderer, so just
          * update table data without triggering updateFeatureRenderer
          */
-        FeatureColourI currentColor = fr.getFeatureColours().get(type);
-        currentFilter = me.fr.getFeatureFilter(type);
-        if (currentFilter == null)
-        {
-          currentFilter = new FeatureMatcherSet();
-        }
-        Object[] data = ((FeatureTableModel) table.getModel())
-                .getData()[rowSelected];
-        data[COLOUR_COLUMN] = currentColor;
-        data[FILTER_COLUMN] = currentFilter;
+        refreshTable();
         fireEditingStopped();
         me.table.validate();
       }
@@ -1876,18 +2096,20 @@ public class FeatureSettings extends JPanel
       currentFilter = (FeatureMatcherSetI) value;
       this.rowSelected = row;
       type = me.table.getValueAt(row, TYPE_COLUMN).toString();
-      button.setOpaque(true);
-      button.setBackground(me.getBackground());
-      button.setText(currentFilter.toString());
-      button.setToolTipText(currentFilter.toString());
-      button.setIcon(null);
-      return button;
+      filterButton.setOpaque(true);
+      filterButton.setBackground(me.getBackground());
+      filterButton.setText(currentFilter.toString());
+      filterButton.setToolTipText(currentFilter.toString());
+      filterButton.setIcon(null);
+      return filterButton;
     }
   }
 }
 
 class FeatureIcon implements Icon
 {
+  private static final Font VERDANA_9 = new Font("Verdana", Font.PLAIN, 9);
+
   FeatureColourI gcol;
 
   Color backg;
@@ -1942,7 +2164,7 @@ class FeatureIcon implements Icon
       // need an icon here.
       g.setColor(gcol.getMaxColour());
 
-      g.setFont(new Font("Verdana", Font.PLAIN, 9));
+      g.setFont(VERDANA_9);
 
       // g.setFont(g.getFont().deriveFont(
       // AffineTransform.getScaleInstance(