JAL-2835 spike updated with latest
[jalview.git] / src / jalview / gui / FeatureSettings.java
index d724b8c..ed98830 100644 (file)
@@ -53,6 +53,7 @@ import java.awt.FlowLayout;
 import java.awt.Font;
 import java.awt.Graphics;
 import java.awt.GridLayout;
+import java.awt.LayoutManager;
 import java.awt.Rectangle;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -84,6 +85,7 @@ import java.util.Vector;
 import javax.help.HelpSetException;
 import javax.swing.AbstractCellEditor;
 import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
 import javax.swing.ButtonGroup;
 import javax.swing.Icon;
 import javax.swing.JButton;
@@ -118,10 +120,14 @@ import javax.swing.table.TableCellRenderer;
 public class FeatureSettings extends JPanel
         implements FeatureSettingsControllerI
 {
+  private static final String COLON = ":";
+
   private static final int MIN_WIDTH = 400;
 
   private static final int MIN_HEIGHT = 400;
 
+  private static final int MAX_TOOLTIP_LENGTH = 50;
+
   DasSourceBrowser dassourceBrowser;
 
   DasSequenceFeatureFetcher dasFeatureFetcher;
@@ -196,6 +202,9 @@ public class FeatureSettings extends JPanel
 
   private JTextArea filtersAsText;
 
+  // set white normally, black to debug layout
+  private Color debugBorderColour = Color.white;
+
   /**
    * Constructor
    * 
@@ -414,84 +423,63 @@ public class FeatureSettings extends JPanel
 
     });
     men.add(dens);
-    if (minmax != null)
+
+    /*
+     * 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()
     {
-      final float[][] typeMinMax = minmax.get(type);
-      /*
-       * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
-       * this is broken at the moment and isn't that useful anyway!
-       * chb.setSelected(minmax.get(type) != null); chb.addActionListener(new
-       * ActionListener() {
-       * 
-       * public void actionPerformed(ActionEvent e) {
-       * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
-       * null); } else { minmax.put(type, typeMinMax); } }
-       * 
-       * });
-       * 
-       * men.add(chb);
-       */
-      if (typeMinMax != null && typeMinMax[0] != null)
-      {
-        // if (table.getValueAt(row, column));
-        // graduated colourschemes for those where minmax exists for the
-        // positional features
-        final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
-                "Graduated Colour");
-        mxcol.setSelected(!featureColour.isSimpleColour());
-        men.add(mxcol);
-        mxcol.addActionListener(new ActionListener()
-        {
-          JColorChooser colorChooser;
+      JColorChooser colorChooser;
 
-          @Override
-          public void actionPerformed(ActionEvent e)
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        if (e.getSource() == mxcol)
+        {
+          if (featureColour.isSimpleColour())
           {
-            if (e.getSource() == mxcol)
-            {
-              if (featureColour.isSimpleColour())
-              {
-                FeatureColourChooser fc = new FeatureColourChooser(me.fr,
-                        type);
-                fc.addActionListener(this);
-              }
-              else
-              {
-                // bring up simple color chooser
-                colorChooser = new JColorChooser();
-                JDialog dialog = JColorChooser.createDialog(me,
-                        "Select new Colour", true, // modal
-                        colorChooser, this, // OK button handler
-                        null); // no CANCEL button handler
-                colorChooser.setColor(featureColour.getMaxColour());
-                dialog.setVisible(true);
-              }
-            }
-            else
-            {
-              if (e.getSource() instanceof FeatureColourChooser)
-              {
-                FeatureColourChooser fc = (FeatureColourChooser) e
-                        .getSource();
-                table.setValueAt(fc.getLastColour(), selectedRow, 1);
-                table.validate();
-              }
-              else
-              {
-                // probably the color chooser!
-                table.setValueAt(new FeatureColour(colorChooser.getColor()),
-                        selectedRow, 1);
-                table.validate();
-                me.updateFeatureRenderer(
-                        ((FeatureTableModel) table.getModel()).getData(),
-                        false);
-              }
-            }
+            FeatureColourChooser fc = new FeatureColourChooser(me.fr, type);
+            fc.addActionListener(this);
           }
-
-        });
+          else
+          {
+            // bring up simple color chooser
+            colorChooser = new JColorChooser();
+            JDialog dialog = JColorChooser.createDialog(me,
+                    "Select new Colour", true, // modal
+                    colorChooser, this, // OK button handler
+                    null); // no CANCEL button handler
+            colorChooser.setColor(featureColour.getMaxColour());
+            dialog.setVisible(true);
+          }
+        }
+        else
+        {
+          if (e.getSource() instanceof FeatureColourChooser)
+          {
+            FeatureColourChooser fc = (FeatureColourChooser) e.getSource();
+            table.setValueAt(fc.getLastColour(), selectedRow, 1);
+            table.validate();
+          }
+          else
+          {
+            // probably the color chooser!
+            table.setValueAt(new FeatureColour(colorChooser.getColor()),
+                    selectedRow, 1);
+            table.validate();
+            me.updateFeatureRenderer(
+                    ((FeatureTableModel) table.getModel()).getData(), false);
+          }
+        }
       }
-    }
+
+    });
+
     JMenuItem selCols = new JMenuItem(
             MessageManager.getString("label.select_columns_containing"));
     selCols.addActionListener(new ActionListener()
@@ -1359,9 +1347,8 @@ public class FeatureSettings extends JPanel
      */
     JPanel chooseTypePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
     chooseTypePanel.setBackground(Color.white);
-    chooseTypePanel.setBorder(BorderFactory
-            .createTitledBorder(MessageManager
-                    .getString("label.feature_type")));
+    JvSwingUtils.createItalicTitledBorder(chooseTypePanel,
+            MessageManager.getString("label.feature_type"), true);
     filteredFeatureChoice = new JComboBox<>();
     filteredFeatureChoice.addItemListener(new ItemListener()
     {
@@ -1379,16 +1366,18 @@ public class FeatureSettings extends JPanel
     /*
      * the panel with the filters for the selected feature type
      */
-    JPanel filtersPanel = new JPanel(new GridLayout(0, 1));
+    JPanel filtersPanel = new JPanel();
+    filtersPanel.setLayout(new BoxLayout(filtersPanel, BoxLayout.Y_AXIS));
     filtersPanel.setBackground(Color.white);
-    filtersPanel.setBorder(BorderFactory
-            .createTitledBorder(MessageManager.getString("label.filters")));
+    JvSwingUtils.createItalicTitledBorder(filtersPanel,
+            MessageManager.getString("label.filters"), true);
 
     /*
      * add AND or OR radio buttons
      */
     JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
     andOrPanel.setBackground(Color.white);
+    andOrPanel.setBorder(BorderFactory.createLineBorder(debugBorderColour));
     andFilters = new JRadioButton("And");
     orFilters = new JRadioButton("Or");
     ActionListener actionListener = new ActionListener()
@@ -1414,7 +1403,10 @@ public class FeatureSettings extends JPanel
     /*
      * panel with filters - populated by refreshFiltersDisplay
      */
-    chooseFiltersPanel = new JPanel(new GridLayout(0, 1));
+    chooseFiltersPanel = new JPanel();
+    LayoutManager box = new BoxLayout(chooseFiltersPanel,
+            BoxLayout.Y_AXIS);
+    chooseFiltersPanel.setLayout(box);
     filtersPanel.add(chooseFiltersPanel);
 
     /*
@@ -1422,9 +1414,8 @@ public class FeatureSettings extends JPanel
      */
     JPanel showFiltersPanel = new JPanel(new BorderLayout(5, 5));
     showFiltersPanel.setBackground(Color.white);
-    showFiltersPanel.setBorder(BorderFactory
-            .createTitledBorder(MessageManager
-                    .getString("label.match_condition")));
+    JvSwingUtils.createItalicTitledBorder(showFiltersPanel,
+            MessageManager.getString("label.match_condition"), true);
     filtersAsText = new JTextArea();
     filtersAsText.setLineWrap(true);
     filtersAsText.setWrapStyleWord(true);
@@ -1471,12 +1462,12 @@ public class FeatureSettings extends JPanel
     }
     if (!found)
     {
-      filteredFeatureChoice
-              .addItem("No filterable feature attributes known");
+      filteredFeatureChoice.addItem(MessageManager
+              .getString("label.no_feature_attributes"));
+      filteredFeatureChoice.setEnabled(false);
     }
 
     filteredFeatureChoice.addItemListener(listener);
-
   }
 
   /**
@@ -1491,16 +1482,14 @@ public class FeatureSettings extends JPanel
      * clear the panel and list of filter conditions
      */
     chooseFiltersPanel.removeAll();
-
-    String selectedType = (String) filteredFeatureChoice.getSelectedItem();
-
     filters.clear();
 
     /*
      * look up attributes known for feature type
      */
-    List<String> attNames = FeatureAttributes.getInstance().getAttributes(
-            selectedType);
+    String selectedType = (String) filteredFeatureChoice.getSelectedItem();
+    List<String[]> attNames = FeatureAttributes.getInstance()
+            .getAttributes(selectedType);
 
     /*
      * if this feature type has filters set, load them first
@@ -1514,33 +1503,32 @@ public class FeatureSettings extends JPanel
       {
         orFilters.setSelected(true);
       }
-      Iterator<KeyedMatcherI> matchers = featureFilters.getMatchers();
-      while (matchers.hasNext())
-      {
-        filters.add(matchers.next());
-      }
+      featureFilters.getMatchers().forEach(matcher -> filters.add(matcher));
     }
 
     /*
      * and an empty filter for the user to populate (add)
      */
-    KeyedMatcherI noFilter = new KeyedMatcher("", Condition.values()[0], "");
+    KeyedMatcherI noFilter = new KeyedMatcher(Condition.values()[0], "",
+            (String) null);
     filters.add(noFilter);
 
     /*
      * render the conditions in rows, each in its own JPanel
      */
-    int i = 0;
+    int filterIndex = 0;
     for (KeyedMatcherI filter : filters)
     {
-      String key = filter.getKey();
+      String[] attName = filter.getKey();
       Condition condition = filter.getMatcher()
               .getCondition();
       String pattern = filter.getMatcher().getPattern();
-      JPanel row = addFilter(key, attNames, condition, pattern, i);
+      JPanel row = addFilter(attName, attNames, condition, pattern, filterIndex);
+      row.setBorder(BorderFactory.createLineBorder(debugBorderColour));
       chooseFiltersPanel.add(row);
-      i++;
+      filterIndex++;
     }
+    // chooseFiltersPanel.add(Box.createVerticalGlue());
 
     filtersPane.validate();
     filtersPane.repaint();
@@ -1554,27 +1542,30 @@ public class FeatureSettings extends JPanel
    * <li>a text field for input of a match pattern</li>
    * <li>optionally, a 'remove' button</li>
    * </ul>
-   * If attribute, condition or pattern are not null, they are set as defaults
-   * for the input fields. The 'remove' button is added unless the pattern is
-   * null or empty (incomplete filter condition).
+   * If attribute, condition or pattern are not null, they are set as defaults for
+   * the input fields. The 'remove' button is added unless the pattern is null or
+   * empty (incomplete filter condition).
    * 
-   * @param attribute
+   * @param attName
    * @param attNames
    * @param cond
    * @param pattern
-   * @param i
+   * @param filterIndex
    * @return
    */
-  protected JPanel addFilter(String attribute, List<String> attNames,
-          Condition cond, String pattern, int i)
+  protected JPanel addFilter(String[] attName, List<String[]> attNames,
+          Condition cond, String pattern, int filterIndex)
   {
     JPanel filterRow = new JPanel(new FlowLayout(FlowLayout.LEFT));
     filterRow.setBackground(Color.white);
 
     /*
-     * inputs for attribute, condition, pattern
+     * drop-down choice of attribute, with description as a tooltip 
+     * if we can obtain it
      */
-    JComboBox<String> attCombo = new JComboBox<>();
+    String featureType = (String) filteredFeatureChoice.getSelectedItem();
+    final JComboBox<String> attCombo = populateAttributesDropdown(
+            featureType, attNames);
     JComboBox<Condition> condCombo = new JComboBox<>();
     JTextField patternField = new JTextField(8);
 
@@ -1590,7 +1581,7 @@ public class FeatureSettings extends JPanel
         {
           if (validateFilter(patternField, condCombo))
           {
-            updateFilter(attCombo, condCombo, patternField, i);
+            updateFilter(attCombo, condCombo, patternField, filterIndex);
             filtersChanged();
           }
         }
@@ -1605,32 +1596,16 @@ public class FeatureSettings extends JPanel
       }
     };
 
-    /*
-     * drop-down choice of attribute
-     */
-    if (attNames.isEmpty())
+    if (attName == null) // the 'add a condition' row
     {
-      attCombo.addItem("---");
-      attCombo.setToolTipText(MessageManager
-              .getString("label.no_attributes_known"));
+      attCombo.setSelectedItem(null);
     }
     else
     {
-      attCombo.setToolTipText("");
-      for (String attName : attNames)
-      {
-        attCombo.addItem(attName);
-      }
-      if ("".equals(attribute))
-      {
-        attCombo.setSelectedItem(null);
-      }
-      else
-      {
-        attCombo.setSelectedItem(attribute);
-      }
-      attCombo.addItemListener(itemListener);
+      attCombo.setSelectedItem(String.join(COLON, attName));
     }
+    attCombo.addItemListener(itemListener);
+
     filterRow.add(attCombo);
 
     /*
@@ -1667,7 +1642,7 @@ public class FeatureSettings extends JPanel
      */
     if (pattern != null && pattern.trim().length() > 0)
     {
-      // todo: gif for - button
+      // todo: gif for button drawing '-' or 'x'
       JButton removeCondition = new BasicArrowButton(SwingConstants.WEST);
       removeCondition.setToolTipText(MessageManager
               .getString("label.delete_row"));
@@ -1676,7 +1651,7 @@ public class FeatureSettings extends JPanel
         @Override
         public void actionPerformed(ActionEvent e)
         {
-          filters.remove(i);
+          filters.remove(filterIndex);
           filtersChanged();
         }
       });
@@ -1687,6 +1662,41 @@ public class FeatureSettings extends JPanel
   }
 
   /**
+   * A helper method to build the drop-down choice of attributes for a feature.
+   * Where metadata is available with a description for an attribute, that is
+   * added as a tooltip.
+   * 
+   * @param featureType
+   * @param attNames
+   */
+  protected JComboBox<String> populateAttributesDropdown(
+          String featureType, List<String[]> attNames)
+  {
+    List<String> displayNames = new ArrayList<>();
+    List<String> tooltips = new ArrayList<>();
+    FeatureAttributes fa = FeatureAttributes.getInstance();
+    for (String[] attName : attNames)
+    {
+      String desc = fa.getDescription(featureType, attName);
+      if (desc != null && desc.length() > MAX_TOOLTIP_LENGTH)
+      {
+        desc = desc.substring(0, MAX_TOOLTIP_LENGTH) + "...";
+      }
+      displayNames.add(String.join(COLON, attName));
+      tooltips.add(desc == null ? "" : desc);
+    }
+
+    JComboBox<String> attCombo = JvSwingUtils.buildComboWithTooltips(
+            displayNames, tooltips);
+    if (attNames.isEmpty())
+    {
+      attCombo.setToolTipText(MessageManager
+              .getString("label.no_attributes"));
+    }
+    return attCombo;
+  }
+
+  /**
    * Action on any change to feature filtering, namely
    * <ul>
    * <li>change of selected attribute</li>
@@ -1755,7 +1765,8 @@ public class FeatureSettings extends JPanel
     String attName = (String) attCombo.getSelectedItem();
     Condition cond = (Condition) condCombo.getSelectedItem();
     String pattern = valueField.getText();
-    KeyedMatcherI km = new KeyedMatcher(attName, cond, pattern);
+    KeyedMatcherI km = new KeyedMatcher(cond, pattern,
+            attName.split(COLON));
 
     filters.set(filterIndex, km);
   }
@@ -2060,12 +2071,7 @@ public class FeatureSettings extends JPanel
             boolean isSelected, boolean hasFocus, int row, int column)
     {
       FeatureColourI cellColour = (FeatureColourI) color;
-      // JLabel comp = new JLabel();
-      // comp.
       setOpaque(true);
-      // comp.
-      // setBounds(getBounds());
-      Color newColor;
       setToolTipText(baseTT);
       setBackground(tbl.getBackground());
       if (!cellColour.isSimpleColour())
@@ -2073,14 +2079,12 @@ public class FeatureSettings extends JPanel
         Rectangle cr = tbl.getCellRect(row, column, false);
         FeatureSettings.renderGraduatedColor(this, cellColour,
                 (int) cr.getWidth(), (int) cr.getHeight());
-
       }
       else
       {
         this.setText("");
         this.setIcon(null);
-        newColor = cellColour.getColour();
-        setBackground(newColor);
+        setBackground(cellColour.getColour());
       }
       if (isSelected)
       {
@@ -2131,28 +2135,43 @@ public class FeatureSettings extends JPanel
           int w, int h)
   {
     boolean thr = false;
-    String tt = "";
-    String tx = "";
+    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 += ">";
-      tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
+      tx.append(">");
+      tt.append("Thresholded (Above ").append(gcol.getThreshold())
+              .append(") ");
     }
     if (gcol.isBelowThreshold())
     {
       thr = true;
-      tx += "<";
-      tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
+      tx.append("<");
+      tt.append("Thresholded (Below ").append(gcol.getThreshold())
+              .append(") ");
     }
     if (gcol.isColourByLabel())
     {
-      tt = "Coloured by label text. " + tt;
+      tt.append("Coloured by label text. ").append(tt);
       if (thr)
       {
-        tx += " ";
+        tx.append(" ");
+      }
+      if (!gcol.isColourByAttribute())
+      {
+        tx.append("Label");
       }
-      tx += "Label";
       comp.setIcon(null);
     }
     else
@@ -2168,16 +2187,17 @@ public class FeatureSettings extends JPanel
       // + ", " + minCol.getBlue() + ")");
     }
     comp.setHorizontalAlignment(SwingConstants.CENTER);
-    comp.setText(tx);
+    comp.setText(tx.toString());
     if (tt.length() > 0)
     {
       if (comp.getToolTipText() == null)
       {
-        comp.setToolTipText(tt);
+        comp.setToolTipText(tt.toString());
       }
       else
       {
-        comp.setToolTipText(tt + " " + comp.getToolTipText());
+        comp.setToolTipText(tt.append(" ").append(comp.getToolTipText())
+                .toString());
       }
     }
   }
@@ -2299,7 +2319,8 @@ class ColorEditor extends AbstractCellEditor
     button.setBorderPainted(false);
     // Set up the dialog that the button brings up.
     colorChooser = new JColorChooser();
-    dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
+    dialog = JColorChooser.createDialog(button,
+            MessageManager.getString("label.select_new_colour"), true, // modal
             colorChooser, this, // OK button handler
             null); // no CANCEL button handler
   }