Merge branch 'feature/JAL-3143ensemblJSON' into
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 1 Nov 2018 14:31:16 +0000 (14:31 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 1 Nov 2018 14:31:16 +0000 (14:31 +0000)
Jalview-BH/JAL-3026-JAL-3063-JAXB

Conflicts:
src/jalview/gui/FeatureTypeSettings.java

1  2 
src/jalview/gui/FeatureTypeSettings.java
src/jalview/schemes/FeatureColour.java
test/jalview/schemes/FeatureColourTest.java

@@@ -29,7 -29,6 +29,7 @@@ import jalview.datamodel.features.Featu
  import jalview.datamodel.features.FeatureMatcherI;
  import jalview.datamodel.features.FeatureMatcherSet;
  import jalview.datamodel.features.FeatureMatcherSetI;
 +import jalview.gui.JalviewColourChooser.ColourChooserListener;
  import jalview.schemes.FeatureColour;
  import jalview.util.ColorUtils;
  import jalview.util.MessageManager;
@@@ -57,16 -56,18 +57,15 @@@ import javax.swing.BoxLayout
  import javax.swing.ButtonGroup;
  import javax.swing.JButton;
  import javax.swing.JCheckBox;
 -import javax.swing.JColorChooser;
  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.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
@@@ -111,7 -112,7 +110,7 @@@ public class FeatureTypeSettings extend
    /*
     * the view panel to update when settings change
     */
 -  private final AlignmentViewPanel ap;
 +  final AlignmentViewPanel ap;
  
    private final String featureType;
  
     * set flag to true when setting values programmatically,
     * to avoid invocation of action handlers
     */
 -  private boolean adjusting = false;
 +  boolean adjusting = false;
  
    /*
     * minimum of the value range for graduated colour
    /*
     * scale factor for conversion between absolute min-max and slider
     */
 -  private float scaleFactor;
 +  float scaleFactor;
  
    /*
     * radio button group, to select what to colour by:
     * simple colour, by category (text), or graduated
     */
 -  private JRadioButton simpleColour = new JRadioButton();
 +  JRadioButton simpleColour = new JRadioButton();
  
 -  private JRadioButton byCategory = new JRadioButton();
 +  JRadioButton byCategory = new JRadioButton();
  
 -  private JRadioButton graduatedColour = new JRadioButton();
 +  JRadioButton graduatedColour = new JRadioButton();
  
--  /**
--   * colours and filters are shown in tabbed view or single content pane
--   */
--  JPanel coloursPanel, filtersPanel;
++  JPanel coloursPanel;
++  
++  JPanel filtersPanel;
  
    JPanel singleColour = new JPanel();
  
 -  private JPanel minColour = new JPanel();
 +  JPanel minColour = new JPanel();
  
 -  private JPanel maxColour = new JPanel();
 +  JPanel maxColour = new JPanel();
  
    private JComboBox<String> threshold = new JComboBox<>();
  
 -  private JSlider slider = new JSlider();
 +  JSlider slider = new JSlider();
  
 -  private JTextField thresholdValue = new JTextField(20);
 +  JTextField thresholdValue = new JTextField(20);
  
    private JCheckBox thresholdIsMin = new JCheckBox();
  
    /*
     * filters for the currently selected feature type
     */
 -  private List<FeatureMatcherI> filters;
 +  List<FeatureMatcherI> filters;
  
    private JPanel chooseFiltersPanel;
  
     */
    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();
        ex.printStackTrace();
        return;
      }
-     updateColoursTab();
-     updateFiltersTab();
+     
 -    updateColoursTab();
++    updateColoursPanel();
+     
 -    updateFiltersTab();
++    updateFiltersPanel();
+     
      adjusting = false;
+     
      colourChanged(false);
+     
      String title = MessageManager
              .formatMessage("label.display_settings_for", new String[]
              { theType });
-     initDialogFrame(this, true, blocking, title, 500, 500);
+     initDialogFrame(this, true, false, title, 500, 500);
 -    
      waitForInput();
    }
  
    /**
--   * Configures the widgets on the Colours tab according to the current feature
++   * Configures the widgets on the Colours panel according to the current feature
     * colour scheme
     */
--  private void updateColoursTab()
++  private void updateColoursPanel()
    {
      FeatureColourI fc = fr.getFeatureColours().get(featureType);
  
         */
        if (fc.isSimpleColour())
        {
-         simpleColour.setSelected(true);
          singleColour.setBackground(fc.getColour());
          singleColour.setForeground(fc.getColour());
+         simpleColour.setSelected(true);
        }
  
        /*
      };
  
      /*
--     * first panel/tab: colour options
++     * first panel: colour options
       */
      JPanel coloursPanel = initialiseColoursPanel();
      this.add(coloursPanel, BorderLayout.NORTH);
  
      /*
--     * second panel/tab: filter options
++     * second panel: filter options
       */
      JPanel filtersPanel = initialiseFiltersPanel();
      this.add(filtersPanel, BorderLayout.CENTER);
      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
+      * default max colour to last plain colour;
+      * make min colour a pale version of max colour
       */
      FeatureColourI fc = fr.getFeatureColours().get(featureType);
-     Color bg = fc.isSimpleColour() ? fc.getColour() : Color.BLACK;
+     Color bg = fc.getColour() == null ? Color.BLACK : fc.getColour();
      maxColour.setBackground(bg);
      minColour.setBackground(ColorUtils.bleachColour(bg, 0.9f));
  
       */
      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(
        {
          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
      return colourByPanel;
    }
  
 -  private void showColourChooser(JPanel colourPanel, String key)
 +  /**
 +   * Shows a colour chooser dialog, and if a selection is made, updates the
 +   * colour of the given panel
 +   * 
 +   * @param colourPanel
 +   *          the panel whose background colour is being picked
 +   * @param key
 +   *          message bundle key for the dialog title
 +   */
 +  void showColourChooser(JPanel colourPanel, String key)
    {
 -    Color col = JColorChooser.showDialog(this,
 -            MessageManager.getString(key), colourPanel.getBackground());
 -    if (col != null)
 +    ColourChooserListener listener = new ColourChooserListener()
      {
 -      colourPanel.setBackground(col);
 -      colourPanel.setForeground(col);
 -    }
 -    colourPanel.repaint();
 -    colourChanged(true);
 +      @Override
 +      public void colourSelected(Color col)
 +      {
 +        colourPanel.setBackground(col);
 +        colourPanel.setForeground(col);
 +        colourPanel.repaint();
 +        colourChanged(true);
 +      }
 +    };
 +    JalviewColourChooser.showColourChooser(this,
 +            MessageManager.getString(key), colourPanel.getBackground(),
 +            listener);
    }
  
    /**
      fr.setColour(featureType, acg);
      ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview);
  
--    updateColoursTab();
++    updateColoursPanel();
    }
  
    /**
       */
      if (byCategory.isSelected())
      {
-       Color c = this.getBackground();
-       FeatureColourI fc = new FeatureColour(c, c, null, 0f, 0f);
+       Color c = singleColour.getBackground();
+       FeatureColourI fc = new FeatureColour(c);
        fc.setColourByLabel(true);
        String byWhat = (String) colourByTextCombo.getSelectedItem();
        if (!LABEL_18N.equals(byWhat))
       */
      float minValue = min;
      float maxValue = max;
 -    final int thresholdOption = threshold.getSelectedIndex();
 +    int thresholdOption = threshold.getSelectedIndex();
      if (thresholdIsMin.isSelected()
              && thresholdOption == ABOVE_THRESHOLD_OPTION)
      {
     * for adding a condition. This should be called after a filter has been
     * removed, added or amended.
     */
--  private void updateFiltersTab()
++  private void updateFiltersPanel()
    {
      /*
       * clear the panel and list of filter conditions
        {
          orFilters.setSelected(true);
        }
 -      featureFilters.getMatchers().forEach(matcher -> filters.add(matcher));
 +      // avoid use of lambda expression to keep SwingJS happy
 +      // featureFilters.getMatchers().forEach(item -> filters.add(item));
 +      for (FeatureMatcherI matcher : featureFilters.getMatchers())
 +      {
 +        filters.add(matcher);
 +      }
      }
  
      /*
       * drop-down choice of attribute, with description as a tooltip 
       * if we can obtain it
       */
 -    final JComboBox<String> attCombo = populateAttributesDropdown(attNames,
 -            true, true);
 +    JComboBox<String> attCombo = populateAttributesDropdown(attNames, true,
 +            true);
      String filterBy = setSelectedAttribute(attCombo, filter);
  
      JComboBox<Condition> condCombo = new JComboBox<>();
      if (!patternField.isEnabled()
              || (pattern != null && pattern.trim().length() > 0))
      {
 -      // todo: gif for button drawing '-' or 'x'
 -      JButton removeCondition = new BasicArrowButton(SwingConstants.WEST);
 +      JButton removeCondition = new JButton("\u2717"); // Dingbats cursive x
 +      removeCondition.setPreferredSize(new Dimension(23, 17));
        removeCondition
                .setToolTipText(MessageManager.getString("label.delete_row"));
        removeCondition.addActionListener(new ActionListener()
     * @param condCombo
     * @param patternField
     */
 -  private void populateConditions(String attName, Condition cond,
 +  void populateConditions(String attName, Condition cond,
            JComboBox<Condition> condCombo, JTextField patternField)
    {
      Datatype type = FeatureAttributes.getInstance().getDatatype(featureType,
      fr.setFeatureFilter(featureType, combined.isEmpty() ? null : combined);
      ap.paintAlignment(true, true);
  
--    updateFiltersTab();
++    updateFiltersPanel();
    }
  }
@@@ -25,7 -25,6 +25,7 @@@ import jalview.datamodel.SequenceFeatur
  import jalview.datamodel.features.FeatureMatcher;
  import jalview.util.ColorUtils;
  import jalview.util.Format;
 +import jalview.util.MessageManager;
  
  import java.awt.Color;
  import java.util.StringTokenizer;
   */
  public class FeatureColour implements FeatureColourI
  {
 +  private static final String I18N_LABEL = MessageManager
 +          .getString("label.label");
 +
 +  private static final String I18N_SCORE = MessageManager
 +          .getString("label.score");
 +
    private static final String ABSOLUTE = "abso";
  
    private static final String ABOVE = "above";
        } catch (Exception e)
        {
          throw new IllegalArgumentException(
-                 "Couldn't parse the minimum value for graduated colour ("
-                         + descriptor + ")");
+                 "Couldn't parse the minimum value for graduated colour ('"
+                         + minval + "')");
        }
        try
        {
      attributeName = name;
    }
  
 +  @Override
 +  public String getDescription()
 +  {
 +    if (isSimpleColour())
 +    {
 +      return "r=" + colour.getRed() + ",g=" + colour.getGreen() + ",b="
 +              + colour.getBlue();
 +    }
 +    StringBuilder tt = new StringBuilder();
 +    String by = null;
 +
 +    if (getAttributeName() != null)
 +    {
 +      by = FeatureMatcher.toAttributeDisplayName(getAttributeName());
 +    }
 +    else if (isColourByLabel())
 +    {
 +      by = I18N_LABEL;
 +    }
 +    else
 +    {
 +      by = I18N_SCORE;
 +    }
 +    tt.append(MessageManager.formatMessage("action.by_title_param", by));
 +
 +    /*
 +     * add threshold if any
 +     */
 +    if (isAboveThreshold() || isBelowThreshold())
 +    {
 +      tt.append(" (");
 +      if (isColourByLabel())
 +      {
 +        /*
 +         * Jalview features file supports the combination of 
 +         * colour by label or attribute text with score threshold
 +         */
 +        tt.append(I18N_SCORE).append(" ");
 +      }
 +      tt.append(isAboveThreshold() ? "> " : "< ");
 +      tt.append(getThreshold()).append(")");
 +    }
 +
 +    return tt.toString();
 +  }
 +
  }
@@@ -523,11 -523,60 +523,60 @@@ public class FeatureColourTes
      assertFalse(fc.hasThreshold());
      assertEquals(Color.RED, fc.getMinColour());
      assertEquals(Color.GREEN, fc.getMaxColour());
+     assertEquals(Color.RED, fc.getNoColour());
      assertEquals(10f, fc.getMin());
      assertEquals(20f, fc.getMax());
      assertTrue(fc.isAutoScaled());
  
      /*
+      * the same, with 'no value colour' specified as max
+      */
+     fc = FeatureColour
+             .parseJalviewFeatureColour("red|green|novaluemax|10.0|20.0");
+     assertEquals(Color.RED, fc.getMinColour());
+     assertEquals(Color.GREEN, fc.getMaxColour());
+     assertEquals(Color.GREEN, fc.getNoColour());
+     assertEquals(10f, fc.getMin());
+     assertEquals(20f, fc.getMax());
+     /*
+      * the same, with 'no value colour' specified as min
+      */
+     fc = FeatureColour
+             .parseJalviewFeatureColour("red|green|novalueMin|10.0|20.0");
+     assertEquals(Color.RED, fc.getMinColour());
+     assertEquals(Color.GREEN, fc.getMaxColour());
+     assertEquals(Color.RED, fc.getNoColour());
+     assertEquals(10f, fc.getMin());
+     assertEquals(20f, fc.getMax());
+     /*
+      * the same, with 'no value colour' specified as none
+      */
+     fc = FeatureColour
+             .parseJalviewFeatureColour("red|green|novaluenone|10.0|20.0");
+     assertEquals(Color.RED, fc.getMinColour());
+     assertEquals(Color.GREEN, fc.getMaxColour());
+     assertNull(fc.getNoColour());
+     assertEquals(10f, fc.getMin());
+     assertEquals(20f, fc.getMax());
+     /*
+      * the same, with invalid 'no value colour'
+      */
+     try
+     {
+       fc = FeatureColour
+               .parseJalviewFeatureColour("red|green|blue|10.0|20.0");
+       fail("expected exception");
+     } catch (IllegalArgumentException e)
+     {
+       assertEquals(
+               "Couldn't parse the minimum value for graduated colour ('blue')",
+               e.getMessage());
+     }
+     /*
       * graduated colour (explicitly by 'score') (no threshold)
       */
      fc = FeatureColour
      Color expected = new Color(70, 120, 170);
      assertEquals(expected, fc.getColor(sf));
    }
 +
 +  /**
 +   * Test description of feature colour suitable for a tooltip
 +   */
 +  @Test(groups = { "Functional" })
 +  public void testGetDescription()
 +  {
 +    /*
 +     * plain colour
 +     */
 +    FeatureColour fc = new FeatureColour(Color.RED);
 +    assertEquals(
 +            String.format("r=%d,g=%d,b=%d", Color.RED.getRed(),
 +                    Color.red.getGreen(), Color.red.getBlue()),
 +            fc.getDescription());
 +  
 +    /*
 +     * colour by label (no threshold)
 +     */
 +    fc = new FeatureColour();
 +    fc.setColourByLabel(true);
 +    assertEquals("By Label", fc.getDescription());
 +  
 +    /*
 +     * colour by attribute text (no threshold)
 +     */
 +    fc = new FeatureColour();
 +    fc.setColourByLabel(true);
 +    fc.setAttributeName("CLIN_SIG");
 +    assertEquals("By CLIN_SIG", fc.getDescription());
 +  
 +    /*
 +     * colour by label (above score threshold) 
 +     */
 +    fc = new FeatureColour();
 +    fc.setColourByLabel(true);
 +    fc.setAutoScaled(false);
 +    fc.setThreshold(12.5f);
 +    fc.setAboveThreshold(true);
 +    assertEquals("By Label (Score > 12.5)",
 +            fc.getDescription());
 +  
 +    /*
 +     * colour by label (below score threshold)
 +     */
 +    fc.setBelowThreshold(true);
 +    assertEquals("By Label (Score < 12.5)",
 +            fc.getDescription());
 +  
 +    /*
 +     * colour by attributes text (below score threshold)
 +     */
 +    fc.setBelowThreshold(true);
 +    fc.setAttributeName("CSQ", "Consequence");
 +    assertEquals(
 +            "By CSQ:Consequence (Score < 12.5)",
 +            fc.getDescription());
 +  
 +    /*
 +     * graduated colour by score, no threshold
 +     */
 +    fc = new FeatureColour(Color.GREEN, Color.RED, 12f, 25f);
 +    assertEquals("By Score", fc.getDescription());
 +  
 +    /*
 +     * graduated colour by score, below threshold
 +     */
 +    fc.setThreshold(12.5f);
 +    fc.setBelowThreshold(true);
 +    assertEquals("By Score (< 12.5)",
 +            fc.getDescription());
 +  
 +    /*
 +     * graduated colour by score, above threshold
 +     */
 +    fc.setThreshold(12.5f);
 +    fc.setAboveThreshold(true);
 +    fc.setAutoScaled(false);
 +    assertEquals("By Score (> 12.5)",
 +            fc.getDescription());
 +
 +    /*
 +     * graduated colour by attribute, no threshold
 +     */
 +    fc.setAttributeName("CSQ", "AF");
 +    fc.setAboveThreshold(false);
 +    fc.setAutoScaled(false);
 +    assertEquals("By CSQ:AF", fc.getDescription());
 +  
 +    /*
 +     * graduated colour by attribute, above threshold
 +     */
 +    fc.setAboveThreshold(true);
 +    fc.setAutoScaled(false);
 +    assertEquals("By CSQ:AF (> 12.5)",
 +            fc.getDescription());
 +  }
  }