Merge remote-tracking branch 'origin/bug/JAL-3049colourCellTooltip' into
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 5 Mar 2019 09:01:48 +0000 (09:01 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 5 Mar 2019 09:01:48 +0000 (09:01 +0000)
merge/JAL-3049

Conflicts:
src/jalview/api/FeatureColourI.java
src/jalview/schemes/FeatureColour.java
test/jalview/schemes/FeatureColourTest.java

1  2 
src/jalview/api/FeatureColourI.java
src/jalview/gui/FeatureSettings.java
src/jalview/schemes/FeatureColour.java
test/jalview/gui/FeatureSettingsTest.java
test/jalview/schemes/FeatureColourTest.java

@@@ -194,14 -194,10 +194,22 @@@ public interface FeatureColour
    void setAttributeName(String... name);
  
    /**
 +   * Answers true if colour has a threshold set, and the feature score (or other
 +   * attribute selected for colouring) is outwith the threshold.
 +   * <p>
 +   * Answers false if not a graduated colour, or no threshold is set, or value
 +   * is not outwith the threshold, or value is null or non-numeric.
 +   * 
 +   * @param sf
 +   * @return
 +   */
 +  boolean isOutwithThreshold(SequenceFeature sf);
++
++  /*
+    * Answers a human-readable text description of the colour, suitable for
+    * display as a tooltip, possibly internationalised for the user's locale.
+    * 
+    * @return
+    */
+   String getDescription();
  }
@@@ -222,10 -233,23 +234,25 @@@ public class FeatureSettings extends JP
          }
          return tip;
        }
+       /**
+        * Position the tooltip at the bottom edge of, and half way across, the
+        * current cell
+        */
+       @Override
+       public Point getToolTipLocation(MouseEvent e)
+       {
+         Point point = e.getPoint();
+         int column = table.columnAtPoint(point);
+         int row = table.rowAtPoint(point);
+         Rectangle r = getCellRect(row, column, false);
+         Point loc = new Point(r.x + r.width / 2, r.y + r.height);
+         return loc;
+       }
      };
 -    table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
 +    JTableHeader tableHeader = table.getTableHeader();
 +    tableHeader.setFont(new Font("Verdana", Font.PLAIN, 12));
 +    tableHeader.setReorderingAllowed(false);
      table.setFont(new Font("Verdana", Font.PLAIN, 12));
  
      // table.setDefaultRenderer(Color.class, new ColorRenderer());
@@@ -888,31 -898,49 +895,77 @@@ public class FeatureColour implements F
    }
  
    @Override
 +  public boolean isOutwithThreshold(SequenceFeature feature)
 +  {
 +    if (!isGraduatedColour())
 +    {
 +      return false;
 +    }
 +    float scr = feature.getScore();
 +    if (attributeName != null)
 +    {
 +      try
 +      {
 +        String attVal = feature.getValueAsString(attributeName);
 +        scr = Float.valueOf(attVal);
 +      } catch (Throwable e)
 +      {
 +        scr = Float.NaN;
 +      }
 +    }
 +    if (Float.isNaN(scr))
 +    {
 +      return false;
 +    }
 +
 +    return ((isAboveThreshold() && scr <= threshold)
 +            || (isBelowThreshold() && scr >= threshold));
 +  }
 +
++  @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();
+   }
  }
@@@ -188,4 -189,42 +189,42 @@@ public class FeatureSettingsTes
      });
      seq.addSequenceFeature(sf);
    }
+   /**
+    * @see FeatureColourTest#testGetDescription()
+    * @throws IOException
+    */
+   @Test(groups = "Functional")
+   public void testGetColorTooltip() throws IOException
+   {
+     assertNull(FeatureSettings.getColorTooltip(null));
+     /*
+      * simple colour
+      */
+     FeatureColourI fc = new FeatureColour(Color.black);
+     String simpleTooltip = "Click to edit, right-click for menu";
+     assertEquals(FeatureSettings.getColorTooltip(fc), simpleTooltip);
+     /*
+      * graduated colour tooltip includes description of colour
+      */
+     fc.setColourByLabel(true);
+     assertEquals(FeatureSettings.getColorTooltip(fc),
+             "<html>By Label<br>" + simpleTooltip + "</br></html>");
+     /*
+      * graduated colour with threshold is html-encoded
+      */
 -    fc = new FeatureColour(Color.red, Color.blue, 2f, 10f);
++    fc = new FeatureColour(null, Color.red, Color.blue, null, 2f, 10f);
+     fc.setBelowThreshold(true);
+     fc.setThreshold(4f);
+     assertEquals(FeatureSettings.getColorTooltip(fc),
+             "<html>By Score (&lt; 4.0)<br>" + simpleTooltip
+                     + "</br></html>");
+     fc.setAboveThreshold(true);
+     assertEquals(FeatureSettings.getColorTooltip(fc),
+             "<html>By Score (&gt; 4.0)<br>" + simpleTooltip
+                     + "</br></html>");
+   }
  }
@@@ -660,44 -664,100 +660,141 @@@ public class FeatureColourTes
      assertEquals(expected, fc.getColor(sf));
    }
  
 +  @Test(groups = { "Functional" })
 +  public void testIsOutwithThreshold()
 +  {
 +    FeatureColourI fc = new FeatureColour(Color.red);
 +    SequenceFeature sf = new SequenceFeature("METAL", "desc", 10, 12, 1.2f, "grp");
 +    assertFalse(fc.isOutwithThreshold(null));
 +    assertFalse(fc.isOutwithThreshold(sf));
 +
 +    fc = new FeatureColour(null, Color.white, Color.black, Color.green, 0f,
 +            10f);
 +    assertFalse(fc.isOutwithThreshold(sf)); // no threshold
 +
 +    fc.setAboveThreshold(true);
 +    fc.setThreshold(1f);
 +    assertFalse(fc.isOutwithThreshold(sf)); // feature score 1.2 is above 1
 +
 +    fc.setThreshold(2f);
 +    assertTrue(fc.isOutwithThreshold(sf)); // feature score 1.2 is not above 2
 +
 +    fc.setBelowThreshold(true);
 +    assertFalse(fc.isOutwithThreshold(sf)); // feature score 1.2 is below 2
 +
 +    fc.setThreshold(1f);
 +    assertTrue(fc.isOutwithThreshold(sf)); // feature score 1.2 is not below 1
 +
 +    /*
 +     * with attribute value threshold
 +     */
 +    fc.setAttributeName("AC");
 +    assertFalse(fc.isOutwithThreshold(sf)); // missing attribute AC is ignored
 +
 +    sf.setValue("AC", "-1");
 +    assertFalse(fc.isOutwithThreshold(sf)); // value -1 is below 1
 +
 +    sf.setValue("AC", "1");
 +    assertTrue(fc.isOutwithThreshold(sf)); // value 1 is not below 1
 +
 +    sf.setValue("AC", "junk");
 +    assertFalse(fc.isOutwithThreshold(sf)); // bad value is ignored
 +  }
++
+   /**
+    * 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);
++    fc = new FeatureColour(null, Color.GREEN, Color.RED, null, 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());
+   }
  }