import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
-import java.awt.image.BufferedImage;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
-import java.util.regex.Pattern;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenuItem;
public class AnnotationLabels extends JPanel
implements MouseListener, MouseMotionListener, ActionListener
{
+ private static final String HTML_END_TAG = "</html>";
+
+ private static final String HTML_START_TAG = "<html>";
+
/**
* width in pixels within which height adjuster arrows are shown and active
*/
*/
private static int HEIGHT_ADJUSTER_HEIGHT = 10;
- private static final Pattern LEFT_ANGLE_BRACKET_PATTERN = Pattern
- .compile("<");
-
private static final Font font = new Font("Arial", Font.PLAIN, 11);
private static final String TOGGLE_LABELSCALE = MessageManager
AlignmentAnnotation aa = ap.av.getAlignment()
.getAlignmentAnnotation()[selectedRow];
- StringBuffer desc = new StringBuffer();
- if (aa.description != null
- && !aa.description.equals("New description"))
- {
- // TODO: we could refactor and merge this code with the code in
- // jalview.gui.SeqPanel.mouseMoved(..) that formats sequence feature
- // tooltips
- desc.append(aa.getDescription(true).trim());
- // check to see if the description is an html fragment.
- if (desc.length() < 6 || (desc.substring(0, 6).toLowerCase()
- .indexOf("<html>") < 0))
- {
- // clean the description ready for embedding in html
- desc = new StringBuffer(LEFT_ANGLE_BRACKET_PATTERN.matcher(desc)
- .replaceAll("<"));
- desc.insert(0, "<html>");
- }
- else
- {
- // remove terminating html if any
- int i = desc.substring(desc.length() - 7).toLowerCase()
- .lastIndexOf("</html>");
- if (i > -1)
- {
- desc.setLength(desc.length() - 7 + i);
- }
- }
- if (aa.hasScore())
- {
- desc.append("<br/>");
- }
- // if (aa.hasProperties())
- // {
- // desc.append("<table>");
- // for (String prop : aa.getProperties())
- // {
- // desc.append("<tr><td>" + prop + "</td><td>"
- // + aa.getProperty(prop) + "</td><tr>");
- // }
- // desc.append("</table>");
- // }
- }
- else
+ String desc = getTooltip(aa);
+ this.setToolTipText(desc);
+ }
+ }
+
+ /**
+ * Answers a tooltip, formatted as html, containing the annotation description
+ * (prefixed by associated sequence id if applicable), and the annotation
+ * (non-positional) score if it has one. Answers null if neither description
+ * nor score is found.
+ *
+ * @param aa
+ * @return
+ */
+ static String getTooltip(AlignmentAnnotation aa)
+ {
+ if (aa == null)
+ {
+ return null;
+ }
+ StringBuilder tooltip = new StringBuilder();
+ if (aa.description != null && !aa.description.equals("New description"))
+ {
+ // TODO: we could refactor and merge this code with the code in
+ // jalview.gui.SeqPanel.mouseMoved(..) that formats sequence feature
+ // tooltips
+ String desc = aa.getDescription(true).trim();
+ if (!desc.toLowerCase().startsWith(HTML_START_TAG))
{
- // begin the tooltip's html fragment
- desc.append("<html>");
- if (aa.hasScore())
- {
- // TODO: limit precision of score to avoid noise from imprecise
- // doubles
- // (64.7 becomes 64.7+/some tiny value).
- desc.append(" Score: " + aa.score);
- }
+ tooltip.append(HTML_START_TAG);
+ desc = desc.replace("<", "<");
}
- if (desc.length() > 6)
+ else if (desc.toLowerCase().endsWith(HTML_END_TAG))
{
- desc.append("</html>");
- this.setToolTipText(desc.toString());
+ desc = desc.substring(0, desc.length() - HTML_END_TAG.length());
}
- else
+ tooltip.append(desc);
+ }
+ else
+ {
+ // begin the tooltip's html fragment
+ tooltip.append(HTML_START_TAG);
+ }
+ if (aa.hasScore())
+ {
+ if (tooltip.length() > HTML_START_TAG.length())
{
- this.setToolTipText(null);
+ tooltip.append("<br/>");
}
+ // TODO: limit precision of score to avoid noise from imprecise
+ // doubles
+ // (64.7 becomes 64.7+/some tiny value).
+ tooltip.append(" Score: ").append(String.valueOf(aa.score));
}
+
+ if (tooltip.length() > HTML_START_TAG.length())
+ {
+ return tooltip.append(HTML_END_TAG).toString();
+ }
+
+ /*
+ * nothing in the tooltip (except "<html>")
+ */
+ return null;
}
/**
--- /dev/null
+package jalview.gui;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Sequence;
+
+import org.testng.annotations.Test;
+
+public class AnnotationLabelsTest
+{
+ @Test(groups = "Functional")
+ public void testGetTooltip()
+ {
+ assertNull(AnnotationLabels.getTooltip(null));
+
+ /*
+ * simple description only
+ */
+ AlignmentAnnotation ann = new AlignmentAnnotation("thelabel", "thedesc",
+ null);
+ String expected = "<html>thedesc</html>";
+ assertEquals(AnnotationLabels.getTooltip(ann), expected);
+
+ /*
+ * description needing html encoding
+ * (no idea why '<' is encoded but '>' is not)
+ */
+ ann.description = "TCoffee scores < 56 and > 28";
+ expected = "<html>TCoffee scores < 56 and > 28</html>";
+ assertEquals(AnnotationLabels.getTooltip(ann), expected);
+
+ /*
+ * description already html formatted
+ */
+ ann.description = "<html>hello world</html>";
+ assertEquals(AnnotationLabels.getTooltip(ann), ann.description);
+
+ /*
+ * simple description and score
+ */
+ ann.description = "hello world";
+ ann.setScore(2.34d);
+ expected = "<html>hello world<br/> Score: 2.34</html>";
+ assertEquals(AnnotationLabels.getTooltip(ann), expected);
+
+ /*
+ * html description and score
+ */
+ ann.description = "<html>hello world</html>";
+ ann.setScore(2.34d);
+ expected = "<html>hello world<br/> Score: 2.34</html>";
+ assertEquals(AnnotationLabels.getTooltip(ann), expected);
+
+ /*
+ * score, no description
+ */
+ ann.description = " ";
+ assertEquals(AnnotationLabels.getTooltip(ann),
+ "<html> Score: 2.34</html>");
+ ann.description = null;
+ assertEquals(AnnotationLabels.getTooltip(ann),
+ "<html> Score: 2.34</html>");
+
+ /*
+ * sequenceref, simple description
+ */
+ ann.description = "Count < 12";
+ ann.sequenceRef = new Sequence("Seq1", "MLRIQST");
+ ann.hasScore = false;
+ ann.score = Double.NaN;
+ expected = "<html>Seq1 : Count < 12</html>";
+ assertEquals(AnnotationLabels.getTooltip(ann), expected);
+
+ /*
+ * sequenceref, html description, score
+ */
+ ann.description = "<html>Score < 4.8</html>";
+ ann.sequenceRef = new Sequence("Seq1", "MLRIQST");
+ ann.setScore(-2.1D);
+ expected = "<html>Seq1 : Score < 4.8<br/> Score: -2.1</html>";
+ assertEquals(AnnotationLabels.getTooltip(ann), expected);
+
+ /*
+ * no score, null description
+ */
+ ann.description = null;
+ ann.hasScore = false;
+ ann.score = Double.NaN;
+ assertNull(AnnotationLabels.getTooltip(ann));
+
+ /*
+ * no score, empty description, sequenceRef
+ */
+ ann.description = "";
+ assertEquals(AnnotationLabels.getTooltip(ann), "<html>Seq1 :</html>");
+
+ /*
+ * no score, empty description, no sequenceRef
+ */
+ ann.sequenceRef = null;
+ assertNull(AnnotationLabels.getTooltip(ann));
+ }
+}