return progressBar.operationInProgress();
}
+ /**
+ * Sets the text of the status bar. Note that setting a null or empty value
+ * will cause the status bar to be hidden, with possibly undesirable flicker
+ * of the screen layout.
+ */
@Override
public void setStatus(String text)
{
AlignFrame newAlignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
AlignFrame.DEFAULT_HEIGHT);
newAlignFrame.setTitle(title);
- newAlignFrame.statusBar.setText(MessageManager
+ newAlignFrame.setStatus(MessageManager
.formatMessage("label.successfully_loaded_file", new Object[]
{ title }));
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
AlignmentUtils.showOrHideSequenceAnnotations(
ap.av.getAlignment(), Collections.singleton(label),
null, false, false);
- // for (AlignmentAnnotation ann : ap.av.getAlignment()
- // .getAlignmentAnnotation())
- // {
- // if (ann.sequenceRef != null && ann.label != null
- // && ann.label.equals(label))
- // {
- // ann.visible = false;
- // }
- // }
ap.refresh(true);
}
});
}
else if (label.indexOf("Consensus") > -1)
{
- pop.addSeparator();
- // av and sequencegroup need to implement same interface for
- final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
- MessageManager.getString("label.ignore_gaps_consensus"),
- (aa[selectedRow].groupRef != null)
- ? aa[selectedRow].groupRef.getIgnoreGapsConsensus()
- : ap.av.isIgnoreGapsConsensus());
- final AlignmentAnnotation aaa = aa[selectedRow];
- cbmi.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- if (aaa.groupRef != null)
- {
- // TODO: pass on reference to ap so the view can be updated.
- aaa.groupRef.setIgnoreGapsConsensus(cbmi.getState());
- ap.getAnnotationPanel()
- .paint(ap.getAnnotationPanel().getGraphics());
- }
- else
- {
- ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap);
- }
- ap.alignmentChanged();
- }
- });
- pop.add(cbmi);
- // av and sequencegroup need to implement same interface for
+ addConsensusMenuOptions(ap, aa[selectedRow], pop);
+
+ final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ);
+ consclipbrd.addActionListener(this);
+ pop.add(consclipbrd);
+ }
+ }
+ pop.show(this, evt.getX(), evt.getY());
+ }
+
+ /**
+ * A helper method that adds menu options for calculation and visualisation of
+ * group and/or alignment consensus annotation to a popup menu. This is
+ * designed to be reusable for either unwrapped mode (popup menu is shown on
+ * component AnnotationLabels), or wrapped mode (popup menu is shown on
+ * IdPanel when the mouse is over an annotation label).
+ *
+ * @param ap
+ * @param ann
+ * @param pop
+ */
+ static void addConsensusMenuOptions(AlignmentPanel ap,
+ AlignmentAnnotation ann,
+ JPopupMenu pop)
+ {
+ pop.addSeparator();
+
+ final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
+ MessageManager.getString("label.ignore_gaps_consensus"),
+ (ann.groupRef != null) ? ann.groupRef.getIgnoreGapsConsensus()
+ : ap.av.isIgnoreGapsConsensus());
+ final AlignmentAnnotation aaa = ann;
+ cbmi.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
if (aaa.groupRef != null)
{
- final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
- MessageManager.getString("label.show_group_histogram"),
- aa[selectedRow].groupRef.isShowConsensusHistogram());
- chist.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- // TODO: pass on reference
- // to ap
- // so the
- // view
- // can be
- // updated.
- aaa.groupRef.setShowConsensusHistogram(chist.getState());
- ap.repaint();
- // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
- }
- });
- pop.add(chist);
- final JCheckBoxMenuItem cprofl = new JCheckBoxMenuItem(
- MessageManager.getString("label.show_group_logo"),
- aa[selectedRow].groupRef.isShowSequenceLogo());
- cprofl.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- // TODO: pass on reference
- // to ap
- // so the
- // view
- // can be
- // updated.
- aaa.groupRef.setshowSequenceLogo(cprofl.getState());
- ap.repaint();
- // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
- }
- });
- pop.add(cprofl);
- final JCheckBoxMenuItem cproflnorm = new JCheckBoxMenuItem(
- MessageManager.getString("label.normalise_group_logo"),
- aa[selectedRow].groupRef.isNormaliseSequenceLogo());
- cproflnorm.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
-
- // TODO: pass on reference
- // to ap
- // so the
- // view
- // can be
- // updated.
- aaa.groupRef.setNormaliseSequenceLogo(cproflnorm.getState());
- // automatically enable logo display if we're clicked
- aaa.groupRef.setshowSequenceLogo(true);
- ap.repaint();
- // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
- }
- });
- pop.add(cproflnorm);
+ aaa.groupRef.setIgnoreGapsConsensus(cbmi.getState());
+ ap.getAnnotationPanel()
+ .paint(ap.getAnnotationPanel().getGraphics());
}
else
{
- final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
- MessageManager.getString("label.show_histogram"),
- av.isShowConsensusHistogram());
- chist.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- // TODO: pass on reference
- // to ap
- // so the
- // view
- // can be
- // updated.
- av.setShowConsensusHistogram(chist.getState());
- ap.alignFrame.setMenusForViewport();
- ap.repaint();
- // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
- }
- });
- pop.add(chist);
- final JCheckBoxMenuItem cprof = new JCheckBoxMenuItem(
- MessageManager.getString("label.show_logo"),
- av.isShowSequenceLogo());
- cprof.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- // TODO: pass on reference
- // to ap
- // so the
- // view
- // can be
- // updated.
- av.setShowSequenceLogo(cprof.getState());
- ap.alignFrame.setMenusForViewport();
- ap.repaint();
- // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
- }
- });
- pop.add(cprof);
- final JCheckBoxMenuItem cprofnorm = new JCheckBoxMenuItem(
- MessageManager.getString("label.normalise_logo"),
- av.isNormaliseSequenceLogo());
- cprofnorm.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- // TODO: pass on reference
- // to ap
- // so the
- // view
- // can be
- // updated.
- av.setShowSequenceLogo(true);
- av.setNormaliseSequenceLogo(cprofnorm.getState());
- ap.alignFrame.setMenusForViewport();
- ap.repaint();
- // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
- }
- });
- pop.add(cprofnorm);
+ ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap);
}
- final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ);
- consclipbrd.addActionListener(this);
- pop.add(consclipbrd);
+ ap.alignmentChanged();
}
+ });
+ pop.add(cbmi);
+
+ if (aaa.groupRef != null)
+ {
+ /*
+ * group consensus options
+ */
+ final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
+ MessageManager.getString("label.show_group_histogram"),
+ ann.groupRef.isShowConsensusHistogram());
+ chist.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ aaa.groupRef.setShowConsensusHistogram(chist.getState());
+ ap.repaint();
+ }
+ });
+ pop.add(chist);
+ final JCheckBoxMenuItem cprofl = new JCheckBoxMenuItem(
+ MessageManager.getString("label.show_group_logo"),
+ ann.groupRef.isShowSequenceLogo());
+ cprofl.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ aaa.groupRef.setshowSequenceLogo(cprofl.getState());
+ ap.repaint();
+ }
+ });
+ pop.add(cprofl);
+ final JCheckBoxMenuItem cproflnorm = new JCheckBoxMenuItem(
+ MessageManager.getString("label.normalise_group_logo"),
+ ann.groupRef.isNormaliseSequenceLogo());
+ cproflnorm.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ aaa.groupRef.setNormaliseSequenceLogo(cproflnorm.getState());
+ // automatically enable logo display if we're clicked
+ aaa.groupRef.setshowSequenceLogo(true);
+ ap.repaint();
+ }
+ });
+ pop.add(cproflnorm);
+ }
+ else
+ {
+ /*
+ * alignment consensus options
+ */
+ final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
+ MessageManager.getString("label.show_histogram"),
+ ap.av.isShowConsensusHistogram());
+ chist.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ ap.av.setShowConsensusHistogram(chist.getState());
+ ap.alignFrame.setMenusForViewport();
+ ap.repaint();
+ }
+ });
+ pop.add(chist);
+ final JCheckBoxMenuItem cprof = new JCheckBoxMenuItem(
+ MessageManager.getString("label.show_logo"),
+ ap.av.isShowSequenceLogo());
+ cprof.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ ap.av.setShowSequenceLogo(cprof.getState());
+ ap.alignFrame.setMenusForViewport();
+ ap.repaint();
+ }
+ });
+ pop.add(cprof);
+ final JCheckBoxMenuItem cprofnorm = new JCheckBoxMenuItem(
+ MessageManager.getString("label.normalise_logo"),
+ ap.av.isNormaliseSequenceLogo());
+ cprofnorm.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ ap.av.setShowSequenceLogo(true);
+ ap.av.setNormaliseSequenceLogo(cprofnorm.getState());
+ ap.alignFrame.setMenusForViewport();
+ ap.repaint();
+ }
+ });
+ pop.add(cprofnorm);
}
- pop.show(this, evt.getX(), evt.getY());
}
/**
if (selectedRow > -1 && ap.av.getAlignment()
.getAlignmentAnnotation().length > selectedRow)
{
- AlignmentAnnotation aa = ap.av.getAlignment()
- .getAlignmentAnnotation()[selectedRow];
+ AlignmentAnnotation[] anns = ap.av.getAlignment()
+ .getAlignmentAnnotation();
+ AlignmentAnnotation aa = anns[selectedRow];
+
+ String desc = getTooltip(aa);
+ this.setToolTipText(desc);
+ String msg = getStatusMessage(aa, anns);
+ ap.alignFrame.setStatus(msg);
+ }
+ }
+
+ /**
+ * Constructs suitable text to show in the status bar when over an annotation
+ * label, containing the associated sequence name (if any), and the annotation
+ * labels (or all labels for a graph group annotation)
+ *
+ * @param aa
+ * @param anns
+ * @return
+ */
+ static String getStatusMessage(AlignmentAnnotation aa,
+ AlignmentAnnotation[] anns)
+ {
+ if (aa == null)
+ {
+ return null;
+ }
- StringBuffer desc = new StringBuffer();
- if (aa.description != null
- && !aa.description.equals("New description"))
+ StringBuilder msg = new StringBuilder(32);
+ if (aa.sequenceRef != null)
+ {
+ msg.append(aa.sequenceRef.getName()).append(" : ");
+ }
+
+ if (aa.graphGroup == -1)
+ {
+ msg.append(aa.label);
+ }
+ else if (anns != null)
+ {
+ boolean first = true;
+ for (int i = anns.length - 1; i >= 0; i--)
{
- // 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))
+ if (anns[i].graphGroup == aa.graphGroup)
{
- // 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)
+ if (!first)
{
- desc.setLength(desc.length() - 7 + i);
+ msg.append(", ");
}
+ msg.append(anns[i].label);
+ first = false;
}
- 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
+ }
+
+ return msg.toString();
+ }
+
+ /**
+ * 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;
}
/**
package jalview.gui;
import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
import jalview.datamodel.Annotation;
import jalview.datamodel.ColumnSelection;
import jalview.datamodel.HiddenColumns;
@Override
public void mouseMoved(MouseEvent evt)
{
+ int yPos = evt.getY();
AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
- if (aa == null)
- {
- this.setToolTipText(null);
- return;
- }
-
- int row = -1;
- int height = 0;
-
- for (int i = 0; i < aa.length; i++)
- {
- if (aa[i].visible)
- {
- height += aa[i].height;
- }
-
- if (evt.getY() < height)
- {
- row = i;
- break;
- }
- }
+ int row = getRowIndex(yPos, aa);
if (row == -1)
{
if (row > -1 && ann.annotations != null
&& column < ann.annotations.length)
{
- buildToolTip(ann, column, aa);
- setStatusMessage(column, ann);
+ setToolTipText(buildToolTip(ann, column, aa));
+ String msg = getStatusMessage(av.getAlignment(), column, ann);
+ ap.alignFrame.setStatus(msg);
}
else
{
this.setToolTipText(null);
- ap.alignFrame.statusBar.setText(" ");
+ ap.alignFrame.setStatus(" ");
}
}
/**
- * Builds a tooltip for the annotation at the current mouse position.
+ * Answers the index in the annotations array of the visible annotation at the
+ * given y position. This is done by adding the heights of visible annotations
+ * until the y position has been exceeded. Answers -1 if no annotations are
+ * visible, or the y position is below all annotations.
+ *
+ * @param yPos
+ * @param aa
+ * @return
+ */
+ static int getRowIndex(int yPos, AlignmentAnnotation[] aa)
+ {
+ if (aa == null)
+ {
+ return -1;
+ }
+ int row = -1;
+ int height = 0;
+
+ for (int i = 0; i < aa.length; i++)
+ {
+ if (aa[i].visible)
+ {
+ height += aa[i].height;
+ }
+
+ if (height > yPos)
+ {
+ row = i;
+ break;
+ }
+ }
+ return row;
+ }
+
+ /**
+ * Answers a tooltip for the annotation at the current mouse position
*
* @param ann
* @param column
* @param anns
*/
- void buildToolTip(AlignmentAnnotation ann, int column,
+ static String buildToolTip(AlignmentAnnotation ann, int column,
AlignmentAnnotation[] anns)
{
+ String tooltip = null;
if (ann.graphGroup > -1)
{
StringBuilder tip = new StringBuilder(32);
if (tip.length() != 6)
{
tip.setLength(tip.length() - 4);
- this.setToolTipText(tip.toString() + "</html>");
+ tooltip = tip.toString() + "</html>";
}
}
- else if (ann.annotations[column] != null)
+ else if (column < ann.annotations.length
+ && ann.annotations[column] != null)
{
String description = ann.annotations[column].description;
if (description != null && description.length() > 0)
{
- this.setToolTipText(JvSwingUtils.wrapTooltip(true, description));
+ tooltip = JvSwingUtils.wrapTooltip(true, description);
}
else
{
- this.setToolTipText(null); // no tooltip if null or empty description
+ tooltip = null; // no tooltip if null or empty description
}
}
else
{
// clear the tooltip.
- this.setToolTipText(null);
+ tooltip = null;
}
+ return tooltip;
}
/**
- * Constructs and displays the status bar message
+ * Constructs and returns the status bar message
*
+ * @param al
* @param column
* @param ann
*/
- void setStatusMessage(int column, AlignmentAnnotation ann)
+ static String getStatusMessage(AlignmentI al, int column,
+ AlignmentAnnotation ann)
{
/*
* show alignment column and annotation description if any
text.append(MessageManager.getString("label.column")).append(" ")
.append(column + 1);
- if (ann.annotations[column] != null)
+ if (column < ann.annotations.length && ann.annotations[column] != null)
{
String description = ann.annotations[column].description;
if (description != null && description.trim().length() > 0)
SequenceI seqref = ann.sequenceRef;
if (seqref != null)
{
- int seqIndex = av.getAlignment().findIndex(seqref);
+ int seqIndex = al.findIndex(seqref);
if (seqIndex != -1)
{
text.append(", ").append(MessageManager.getString("label.sequence"))
{
text.append(" ");
String name;
- if (av.getAlignment().isNucleotide())
+ if (al.isNucleotide())
{
name = ResidueProperties.nucleotideName
.get(String.valueOf(residue));
}
}
- ap.alignFrame.statusBar.setText(text.toString());
+ return text.toString();
}
/**
af.currentFileFormat = format;
Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH,
AlignFrame.DEFAULT_HEIGHT);
- af.statusBar.setText(MessageManager
+ af.setStatus(MessageManager
.getString("label.successfully_pasted_alignment_file"));
try
*/
package jalview.gui;
+import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
+import jalview.gui.SeqPanel.MousePos;
import jalview.io.SequenceAnnotationReport;
import jalview.util.MessageManager;
import jalview.util.Platform;
import java.util.List;
import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
}
/**
- * Respond to mouse movement by constructing tooltip text for the sequence id
- * under the mouse.
+ * Responds to mouse movement by setting tooltip text for the sequence id
+ * under the mouse (or possibly annotation label, when in wrapped mode)
*
* @param e
- * DOCUMENT ME!
*/
@Override
public void mouseMoved(MouseEvent e)
{
SeqPanel sp = alignPanel.getSeqPanel();
- int seq = Math.max(0, sp.findSeq(e));
- if (seq > -1 && seq < av.getAlignment().getHeight())
+ MousePos pos = sp.findMousePosition(e);
+ if (pos.isOverAnnotation())
{
- SequenceI sequence = av.getAlignment().getSequenceAt(seq);
- StringBuilder tip = new StringBuilder(64);
- seqAnnotReport.createTooltipAnnotationReport(tip, sequence,
- av.isShowDBRefs(), av.isShowNPFeats(), sp.seqCanvas.fr);
- setToolTipText(JvSwingUtils.wrapTooltip(true,
- sequence.getDisplayId(true) + " " + tip.toString()));
+ /*
+ * mouse is over an annotation label in wrapped mode
+ */
+ AlignmentAnnotation[] anns = av.getAlignment()
+ .getAlignmentAnnotation();
+ AlignmentAnnotation annotation = anns[pos.annotationIndex];
+ setToolTipText(AnnotationLabels.getTooltip(annotation));
+ alignPanel.alignFrame.setStatus(
+ AnnotationLabels.getStatusMessage(annotation, anns));
+ }
+ else
+ {
+ int seq = Math.max(0, pos.seqIndex);
+ if (seq < av.getAlignment().getHeight())
+ {
+ SequenceI sequence = av.getAlignment().getSequenceAt(seq);
+ StringBuilder tip = new StringBuilder(64);
+ tip.append(sequence.getDisplayId(true)).append(" ");
+ seqAnnotReport.createTooltipAnnotationReport(tip, sequence,
+ av.isShowDBRefs(), av.isShowNPFeats(), sp.seqCanvas.fr);
+ setToolTipText(JvSwingUtils.wrapTooltip(true, tip.toString()));
+
+ StringBuilder text = new StringBuilder();
+ text.append("Sequence ").append(String.valueOf(seq + 1))
+ .append(" ID: ")
+ .append(sequence.getName());
+ alignPanel.alignFrame.setStatus(text.toString());
+ }
}
}
{
mouseDragging = true;
- int seq = Math.max(0, alignPanel.getSeqPanel().findSeq(e));
+ MousePos pos = alignPanel.getSeqPanel().findMousePosition(e);
+ if (pos.isOverAnnotation())
+ {
+ // mouse is over annotation label in wrapped mode
+ return;
+ }
+
+ int seq = Math.max(0, pos.seqIndex);
if (seq < lastid)
{
return;
}
- int seq = alignPanel.getSeqPanel().findSeq(e);
+ MousePos pos = alignPanel.getSeqPanel().findMousePosition(e);
+ int seq = pos.seqIndex;
+ if (pos.isOverAnnotation() || seq < 0)
+ {
+ return;
+ }
+
String id = av.getAlignment().getSequenceAt(seq).getName();
String url = Preferences.sequenceUrlLinks.getPrimaryUrl(id);
return;
}
+ MousePos pos = alignPanel.getSeqPanel().findMousePosition(e);
+
if (e.isPopupTrigger()) // Mac reports this in mousePressed
{
- showPopupMenu(e);
+ showPopupMenu(e, pos);
return;
}
av.getSelectionGroup().setEndRes(av.getAlignment().getWidth() - 1);
}
- int seq = alignPanel.getSeqPanel().findSeq(e);
if (e.isShiftDown() && (lastid != -1))
{
- selectSeqs(lastid, seq);
+ selectSeqs(lastid, pos.seqIndex);
}
else
{
- selectSeq(seq);
+ selectSeq(pos.seqIndex);
}
av.isSelectionGroupChanged(true);
*
* @param e
*/
- void showPopupMenu(MouseEvent e)
+ void showPopupMenu(MouseEvent e, MousePos pos)
{
- int seq2 = alignPanel.getSeqPanel().findSeq(e);
- Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq2);
+ if (pos.isOverAnnotation())
+ {
+ showAnnotationMenu(e, pos);
+ return;
+ }
+
+ Sequence sq = (Sequence) av.getAlignment().getSequenceAt(pos.seqIndex);
/*
* build a new links menu based on the current links
{
if (sf.links != null)
{
- for (String link : sf.links)
- {
- nlinks.add(link);
- }
+ nlinks.addAll(sf.links);
}
}
}
}
/**
+ * On right mouse click on a Consensus annotation label, shows a limited popup
+ * menu, with options to configure the consensus calculation and rendering.
+ *
+ * @param e
+ * @param pos
+ * @see AnnotationLabels#showPopupMenu(MouseEvent)
+ */
+ void showAnnotationMenu(MouseEvent e, MousePos pos)
+ {
+ if (pos.annotationIndex == -1)
+ {
+ return;
+ }
+ AlignmentAnnotation[] anns = this.av.getAlignment()
+ .getAlignmentAnnotation();
+ if (anns == null || pos.annotationIndex >= anns.length)
+ {
+ return;
+ }
+ AlignmentAnnotation ann = anns[pos.annotationIndex];
+ if (!ann.label.contains("Consensus"))
+ {
+ return;
+ }
+
+ JPopupMenu pop = new JPopupMenu(
+ MessageManager.getString("label.annotations"));
+ AnnotationLabels.addConsensusMenuOptions(this.alignPanel, ann, pop);
+ pop.show(this, e.getX(), e.getY());
+ }
+
+ /**
* Toggle whether the sequence is part of the current selection group.
*
* @param seq
{
scrollThread.running = false;
}
+ MousePos pos = alignPanel.getSeqPanel().findMousePosition(e);
mouseDragging = false;
PaintRefresher.Refresh(this, av.getSequenceSetId());
if (e.isPopupTrigger()) // Windows reports this in mouseReleased
{
- showPopupMenu(e);
+ showPopupMenu(e, pos);
}
}
*/
public class SeqCanvas extends JComponent implements ViewportListenerI
{
+ /*
+ * pixels gap between sequences and annotations when in wrapped mode
+ */
+ static final int SEQS_ANNOTATION_GAP = 3;
+
private static final String ZEROS = "0000000000";
final FeatureRenderer fr;
private int labelWidthWest; // label left width in pixels if shown
- private int wrappedSpaceAboveAlignment; // gap between widths
+ int wrappedSpaceAboveAlignment; // gap between widths
- private int wrappedRepeatHeightPx; // height in pixels of wrapped width
+ int wrappedRepeatHeightPx; // height in pixels of wrapped width
private int wrappedVisibleWidths; // number of wrapped widths displayed
calculateWrappedGeometry(canvasWidth, canvasHeight);
/*
- * draw one width at a time (excluding any scales or annotation shown),
+ * draw one width at a time (excluding any scales shown),
* until we have run out of either alignment or vertical space available
*/
int ypos = wrappedSpaceAboveAlignment;
* (av.getScaleAboveWrapped() ? 2 : 1);
/*
- * height in pixels of the wrapped widths
+ * compute height in pixels of the wrapped widths
+ * - start with space above plus sequences
*/
wrappedRepeatHeightPx = wrappedSpaceAboveAlignment;
- // add sequences
wrappedRepeatHeightPx += av.getAlignment().getHeight()
* charHeight;
- // add annotations panel height if shown
- wrappedRepeatHeightPx += getAnnotationHeight();
+
+ /*
+ * add annotations panel height if shown
+ * also gap between sequences and annotations
+ */
+ if (av.isShowAnnotation())
+ {
+ wrappedRepeatHeightPx += getAnnotationHeight();
+ wrappedRepeatHeightPx += SEQS_ANNOTATION_GAP; // 3px
+ }
/*
* number of visible widths (the last one may be part height),
* @param endColumn
* @param canvasHeight
*/
- protected void drawWrappedWidth(Graphics g, int ypos, int startColumn,
- int endColumn, int canvasHeight)
+ protected void drawWrappedWidth(Graphics g, final int ypos,
+ final int startColumn, final int endColumn,
+ final int canvasHeight)
{
ViewportRanges ranges = av.getRanges();
int viewportWidth = ranges.getViewportWidth();
if (av.isShowAnnotation())
{
- g.translate(0, cHeight + ypos + 3);
+ final int yShift = cHeight + ypos + SEQS_ANNOTATION_GAP;
+ g.translate(0, yShift);
if (annotations == null)
{
annotations = new AnnotationPanel(av);
annotations.renderer.drawComponent(annotations, av, g, -1,
startColumn, endx + 1);
- g.translate(0, -cHeight - ypos - 3);
+ g.translate(0, -yShift);
}
g.setClip(clip);
g.translate(-xOffset, 0);
import jalview.commands.EditCommand;
import jalview.commands.EditCommand.Action;
import jalview.commands.EditCommand.Edit;
+import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.ColumnSelection;
import jalview.datamodel.HiddenColumns;
implements MouseListener, MouseMotionListener, MouseWheelListener,
SequenceListener, SelectionListener
{
+ /*
+ * a class that holds computed mouse position
+ * - column of the alignment (0...)
+ * - sequence offset (0...)
+ * - annotation row offset (0...)
+ * where annotation offset is -1 unless the alignment is shown
+ * in wrapped mode, annotations are shown, and the mouse is
+ * over an annnotation row
+ */
+ static class MousePos
+ {
+ /*
+ * alignment column position of cursor (0...)
+ */
+ final int column;
+
+ /*
+ * index in alignment of sequence under cursor,
+ * or nearest above if cursor is not over a sequence
+ */
+ final int seqIndex;
+
+ /*
+ * index in annotations array of annotation under the cursor
+ * (only possible in wrapped mode with annotations shown),
+ * or -1 if cursor is not over an annotation row
+ */
+ final int annotationIndex;
+
+ MousePos(int col, int seq, int ann)
+ {
+ column = col;
+ seqIndex = seq;
+ annotationIndex = ann;
+ }
+
+ boolean isOverAnnotation()
+ {
+ return annotationIndex != -1;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == null || !(obj instanceof MousePos))
+ {
+ return false;
+ }
+ MousePos o = (MousePos) obj;
+ boolean b = (column == o.column && seqIndex == o.seqIndex
+ && annotationIndex == o.annotationIndex);
+ // System.out.println(obj + (b ? "= " : "!= ") + this);
+ return b;
+ }
+
+ /**
+ * A simple hashCode that ensures that instances that satisfy equals() have
+ * the same hashCode
+ */
+ @Override
+ public int hashCode()
+ {
+ return column + seqIndex + annotationIndex;
+ }
+
+ /**
+ * toString method for debug output purposes only
+ */
+ @Override
+ public String toString()
+ {
+ return String.format("c%d:s%d:a%d", column, seqIndex,
+ annotationIndex);
+ }
+ }
+
private static final int MAX_TOOLTIP_LENGTH = 300;
public SeqCanvas seqCanvas;
public AlignmentPanel ap;
/*
- * last column position for mouseMoved event
+ * last position for mouseMoved event
*/
- private int lastMouseColumn;
-
- /*
- * last sequence offset for mouseMoved event
- */
- private int lastMouseSeq;
+ private MousePos lastMousePosition;
protected int editLastRes;
ssm.addStructureViewerListener(this);
ssm.addSelectionListener(this);
}
-
- lastMouseColumn = -1;
- lastMouseSeq = -1;
}
int startWrapBlock = -1;
int wrappedBlock = -1;
/**
+ * Computes the column and sequence row (and possibly annotation row when in
+ * wrapped mode) for the given mouse position
+ *
+ * @param evt
+ * @return
+ */
+ MousePos findMousePosition(MouseEvent evt)
+ {
+ int col = findColumn(evt);
+ int seqIndex = -1;
+ int annIndex = -1;
+ int y = evt.getY();
+
+ int charHeight = av.getCharHeight();
+ int alignmentHeight = av.getAlignment().getHeight();
+ if (av.getWrapAlignment())
+ {
+ seqCanvas.calculateWrappedGeometry(seqCanvas.getWidth(),
+ seqCanvas.getHeight());
+
+ /*
+ * yPos modulo height of repeating width
+ */
+ int yOffsetPx = y % seqCanvas.wrappedRepeatHeightPx;
+
+ /*
+ * height of sequences plus space / scale above,
+ * plus gap between sequences and annotations
+ */
+ int alignmentHeightPixels = seqCanvas.wrappedSpaceAboveAlignment
+ + alignmentHeight * charHeight
+ + SeqCanvas.SEQS_ANNOTATION_GAP;
+ if (yOffsetPx >= alignmentHeightPixels)
+ {
+ /*
+ * mouse is over annotations; find annotation index, also set
+ * last sequence above (for backwards compatible behaviour)
+ */
+ AlignmentAnnotation[] anns = av.getAlignment()
+ .getAlignmentAnnotation();
+ int rowOffsetPx = yOffsetPx - alignmentHeightPixels;
+ annIndex = AnnotationPanel.getRowIndex(rowOffsetPx, anns);
+ seqIndex = alignmentHeight - 1;
+ }
+ else
+ {
+ /*
+ * mouse is over sequence (or the space above sequences)
+ */
+ yOffsetPx -= seqCanvas.wrappedSpaceAboveAlignment;
+ if (yOffsetPx >= 0)
+ {
+ seqIndex = Math.min(yOffsetPx / charHeight, alignmentHeight - 1);
+ }
+ }
+ }
+ else
+ {
+ seqIndex = Math.min((y / charHeight) + av.getRanges().getStartSeq(),
+ alignmentHeight - 1);
+ }
+
+ return new MousePos(col, seqIndex, annIndex);
+ }
+ /**
* Returns the aligned sequence position (base 0) at the mouse position, or
* the closest visible one
*
int res = 0;
int x = evt.getX();
- int startRes = av.getRanges().getStartRes();
+ final int startRes = av.getRanges().getStartRes();
+ final int charWidth = av.getCharWidth();
+
if (av.getWrapAlignment())
{
-
int hgap = av.getCharHeight();
if (av.getScaleAboveWrapped())
{
int y = evt.getY();
y = Math.max(0, y - hgap);
- x = Math.max(0, x - seqCanvas.getLabelWidthWest());
+ x -= seqCanvas.getLabelWidthWest();
+ if (x < 0)
+ {
+ // mouse is over left scale
+ return -1;
+ }
int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
if (cwidth < 1)
{
return 0;
}
+ if (x >= cwidth * charWidth)
+ {
+ // mouse is over right scale
+ return -1;
+ }
wrappedBlock = y / cHeight;
wrappedBlock += startRes / cwidth;
// allow for wrapped view scrolled right (possible from Overview)
int startOffset = startRes % cwidth;
res = wrappedBlock * cwidth + startOffset
- + +Math.min(cwidth - 1, x / av.getCharWidth());
+ + Math.min(cwidth - 1, x / charWidth);
}
else
{
- if (x > seqCanvas.getX() + seqCanvas.getWidth())
- {
- // make sure we calculate relative to visible alignment, rather than
- // right-hand gutter
- x = seqCanvas.getX() + seqCanvas.getWidth();
- }
- res = (x / av.getCharWidth()) + startRes;
- if (res > av.getRanges().getEndRes())
- {
- // moused off right
- res = av.getRanges().getEndRes();
- }
+ /*
+ * make sure we calculate relative to visible alignment,
+ * rather than right-hand gutter
+ */
+ x = Math.min(x, seqCanvas.getX() + seqCanvas.getWidth());
+ res = (x / charWidth) + startRes;
+ res = Math.min(res, av.getRanges().getEndRes());
}
if (av.hasHiddenColumns())
}
return res;
-
- }
-
- int findSeq(MouseEvent evt)
- {
- int seq = 0;
- int y = evt.getY();
-
- if (av.getWrapAlignment())
- {
- int hgap = av.getCharHeight();
- if (av.getScaleAboveWrapped())
- {
- hgap += av.getCharHeight();
- }
-
- int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
- + hgap + seqCanvas.getAnnotationHeight();
-
- y -= hgap;
-
- seq = Math.min((y % cHeight) / av.getCharHeight(),
- av.getAlignment().getHeight() - 1);
- }
- else
- {
- seq = Math.min(
- (y / av.getCharHeight()) + av.getRanges().getStartSeq(),
- av.getAlignment().getHeight() - 1);
- }
-
- return seq;
}
/**
@Override
public void mouseReleased(MouseEvent evt)
{
+ MousePos pos = findMousePosition(evt);
+ if (pos.isOverAnnotation() || pos.seqIndex == -1 || pos.column == -1)
+ {
+ return;
+ }
+
boolean didDrag = mouseDragging; // did we come here after a drag
mouseDragging = false;
mouseWheelPressed = false;
if (evt.isPopupTrigger()) // Windows: mouseReleased
{
- showPopupMenu(evt);
+ showPopupMenu(evt, pos);
evt.consume();
return;
}
public void mousePressed(MouseEvent evt)
{
lastMousePress = evt.getPoint();
+ MousePos pos = findMousePosition(evt);
+ if (pos.isOverAnnotation() || pos.seqIndex == -1 || pos.column == -1)
+ {
+ return;
+ }
if (SwingUtilities.isMiddleMouseButton(evt))
{
}
else
{
- doMousePressedDefineMode(evt);
+ doMousePressedDefineMode(evt, pos);
return;
}
- int seq = findSeq(evt);
- int res = findColumn(evt);
-
- if (seq < 0 || res < 0)
- {
- return;
- }
+ int seq = pos.seqIndex;
+ int res = pos.column;
if ((seq < av.getAlignment().getHeight())
&& (res < av.getAlignment().getSequenceAt(seq).getLength()))
mouseDragged(evt);
}
- final int column = findColumn(evt);
- final int seq = findSeq(evt);
+ final MousePos mousePos = findMousePosition(evt);
+ if (mousePos.equals(lastMousePosition))
+ {
+ /*
+ * just a pixel move without change of 'cell'
+ */
+ return;
+ }
+ lastMousePosition = mousePos;
- if (column < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
+ if (mousePos.isOverAnnotation())
{
- lastMouseSeq = -1;
+ mouseMovedOverAnnotation(mousePos);
return;
}
- if (column == lastMouseColumn && seq == lastMouseSeq)
+ final int seq = mousePos.seqIndex;
+
+ final int column = mousePos.column;
+ if (column < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
{
- /*
- * just a pixel move without change of residue
- */
+ lastMousePosition = null;
+ setToolTipText(null);
+ lastTooltip = null;
+ ap.alignFrame.setStatus("");
return;
}
- lastMouseColumn = column;
- lastMouseSeq = seq;
SequenceI sequence = av.getAlignment().getSequenceAt(seq);
}
}
+ /**
+ * When the view is in wrapped mode, and the mouse is over an annotation row,
+ * shows the corresponding tooltip and status message (if any)
+ *
+ * @param pos
+ * @param column
+ */
+ protected void mouseMovedOverAnnotation(MousePos pos)
+ {
+ final int column = pos.column;
+ final int rowIndex = pos.annotationIndex;
+
+ if (column < 0 || !av.getWrapAlignment() || !av.isShowAnnotation()
+ || rowIndex < 0)
+ {
+ return;
+ }
+ AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation();
+
+ String tooltip = AnnotationPanel.buildToolTip(anns[rowIndex], column,
+ anns);
+ setToolTipText(tooltip);
+ lastTooltip = tooltip;
+
+ String msg = AnnotationPanel.getStatusMessage(av.getAlignment(), column,
+ anns[rowIndex]);
+ ap.alignFrame.setStatus(msg);
+ }
+
private Point lastp = null;
/*
@Override
public Point getToolTipLocation(MouseEvent event)
{
- int x = event.getX(), w = getWidth();
- int wdth = (w - x < 200) ? -(w / 2) : 5; // switch sides when tooltip is too
- // close to edge
+ if (tooltipText == null || tooltipText.length() <= 6)
+ {
+ lastp = null;
+ return null;
+ }
+
+ int x = event.getX();
+ int w = getWidth();
+ // switch sides when tooltip is too close to edge
+ int wdth = (w - x < 200) ? -(w / 2) : 5;
Point p = lastp;
if (!event.isShiftDown() || p == null)
{
- p = (tooltipText != null && tooltipText.length() > 6)
- ? new Point(event.getX() + wdth, event.getY() - 20)
- : null;
+ p = new Point(event.getX() + wdth, event.getY() - 20);
+ lastp = p;
}
/*
- * TODO: try to modify position region is not obcured by tooltip
+ * TODO: try to set position so region is not obscured by tooltip
*/
- return lastp = p;
+ return p;
}
String lastTooltip;
text.append(" (").append(Integer.toString(residuePos)).append(")");
}
- ap.alignFrame.statusBar.setText(text.toString());
+ ap.alignFrame.setStatus(text.toString());
}
/**
@Override
public void mouseDragged(MouseEvent evt)
{
+ MousePos pos = findMousePosition(evt);
+ if (pos.isOverAnnotation() || pos.column == -1)
+ {
+ return;
+ }
+
if (mouseWheelPressed)
{
boolean inSplitFrame = ap.av.getCodingComplement() != null;
if (!editingSeqs)
{
- doMouseDraggedDefineMode(evt);
+ dragStretchGroup(evt);
return;
}
- int res = findColumn(evt);
+ int res = pos.column;
if (res < 0)
{
}
}
+ StringBuilder message = new StringBuilder(64); // for status bar
+
/*
* make a name for the edit action, for
* status bar message and Undo/Redo menu
String label = null;
if (groupEditing)
{
+ message.append("Edit group:");
label = MessageManager.getString("action.edit_group");
}
else
{
+ message.append("Edit sequence: " + seq.getName());
label = seq.getName();
if (label.length() > 10)
{
editCommand = new EditCommand(label);
}
+ if (insertGap)
+ {
+ message.append(" insert ");
+ }
+ else
+ {
+ message.append(" delete ");
+ }
+
+ message.append(Math.abs(startres - editLastRes) + " gaps.");
+ ap.alignFrame.setStatus(message.toString());
+
/*
* is there a selection group containing the sequence being edited?
* if so the boundary of the group is the limit of the edit
* what was requested), by inspecting the edit commands added
*/
String msg = getEditStatusMessage(editCommand);
- ap.alignFrame.statusBar.setText(msg == null ? " " : msg);
+ ap.alignFrame.setStatus(msg == null ? " " : msg);
if (!success)
{
endEditing();
@Override
public void mouseExited(MouseEvent e)
{
+ ap.alignFrame.setStatus(" ");
if (av.getWrapAlignment())
{
return;
public void mouseClicked(MouseEvent evt)
{
SequenceGroup sg = null;
- SequenceI sequence = av.getAlignment().getSequenceAt(findSeq(evt));
+ MousePos pos = findMousePosition(evt);
+ if (pos.isOverAnnotation() || pos.seqIndex == -1 || pos.column == -1)
+ {
+ return;
+ }
+
if (evt.getClickCount() > 1)
{
sg = av.getSelectionGroup();
av.setSelectionGroup(null);
}
- int column = findColumn(evt);
+ int column = pos.column;
/*
* find features at the position (if not gapped), or straddling
* the position (if at a gap)
*/
+ SequenceI sequence = av.getAlignment().getSequenceAt(pos.seqIndex);
List<SequenceFeature> features = seqCanvas.getFeatureRenderer()
.findFeaturesAtColumn(sequence, column + 1);
/**
* DOCUMENT ME!
*
- * @param evt
+ * @param pos
* DOCUMENT ME!
*/
- public void doMousePressedDefineMode(MouseEvent evt)
+ protected void doMousePressedDefineMode(MouseEvent evt, MousePos pos)
{
- final int res = findColumn(evt);
- final int seq = findSeq(evt);
- oldSeq = seq;
- updateOverviewAndStructs = false;
-
- startWrapBlock = wrappedBlock;
-
- if (av.getWrapAlignment() && seq > av.getAlignment().getHeight())
+ if (pos.isOverAnnotation() || pos.seqIndex == -1 || pos.column == -1)
{
- JvOptionPane.showInternalMessageDialog(Desktop.desktop,
- MessageManager.getString(
- "label.cannot_edit_annotations_in_wrapped_view"),
- MessageManager.getString("label.wrapped_view_no_edit"),
- JvOptionPane.WARNING_MESSAGE);
return;
}
- if (seq < 0 || res < 0)
- {
- return;
- }
+ final int res = pos.column;
+ final int seq = pos.seqIndex;
+ oldSeq = seq;
+ updateOverviewAndStructs = false;
+
+ startWrapBlock = wrappedBlock;
SequenceI sequence = av.getAlignment().getSequenceAt(seq);
if (evt.isPopupTrigger()) // Mac: mousePressed
{
- showPopupMenu(evt);
+ showPopupMenu(evt, pos);
return;
}
if (av.cursorMode)
{
- seqCanvas.cursorX = findColumn(evt);
- seqCanvas.cursorY = findSeq(evt);
+ seqCanvas.cursorX = res;
+ seqCanvas.cursorY = seq;
seqCanvas.repaint();
return;
}
/**
* Build and show a pop-up menu at the right-click mouse position
- *
+ *
* @param evt
- * @param res
- * @param sequences
+ * @param pos
*/
- void showPopupMenu(MouseEvent evt)
+ void showPopupMenu(MouseEvent evt, MousePos pos)
{
- final int column = findColumn(evt);
- final int seq = findSeq(evt);
+ final int column = pos.column;
+ final int seq = pos.seqIndex;
SequenceI sequence = av.getAlignment().getSequenceAt(seq);
List<SequenceFeature> features = ap.getFeatureRenderer()
.findFeaturesAtColumn(sequence, column + 1);
* true if this event is happening after a mouse drag (rather than a
* mouse down)
*/
- public void doMouseReleasedDefineMode(MouseEvent evt, boolean afterDrag)
+ protected void doMouseReleasedDefineMode(MouseEvent evt,
+ boolean afterDrag)
{
if (stretchGroup == null)
{
}
/**
- * DOCUMENT ME!
+ * Resizes the borders of a selection group depending on the direction of
+ * mouse drag
*
* @param evt
- * DOCUMENT ME!
*/
- public void doMouseDraggedDefineMode(MouseEvent evt)
+ protected void dragStretchGroup(MouseEvent evt)
{
- int res = findColumn(evt);
- int y = findSeq(evt);
+ if (stretchGroup == null)
+ {
+ return;
+ }
- if (wrappedBlock != startWrapBlock)
+ MousePos pos = findMousePosition(evt);
+ if (pos.isOverAnnotation() || pos.column == -1 || pos.seqIndex == -1)
{
return;
}
- if (stretchGroup == null)
+ int res = pos.column;
+ int y = pos.seqIndex;
+
+ if (wrappedBlock != startWrapBlock)
{
return;
}
Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH,
AlignFrame.DEFAULT_HEIGHT);
- af.statusBar.setText(MessageManager
+ af.setStatus(MessageManager
.getString("label.successfully_pasted_alignment_file"));
try
alignFrame.getViewport()
.applyFeaturesStyle(proxyColourScheme);
}
- alignFrame.statusBar.setText(MessageManager.formatMessage(
+ alignFrame.setStatus(MessageManager.formatMessage(
"label.successfully_loaded_file", new String[]
{ title }));
protected JMenu sortByAnnotScore = new JMenu();
- public JLabel statusBar = new JLabel();
+ protected JLabel statusBar = new JLabel();
protected JMenu outputTextboxMenu = new JMenu();
private boolean showAutoCalculatedAbove = false;
- private Map<KeyStroke, JMenuItem> accelerators = new HashMap<KeyStroke, JMenuItem>();
+ private Map<KeyStroke, JMenuItem> accelerators = new HashMap<>();
private SplitContainerI splitFrame;
--- /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));
+ }
+
+ @Test(groups = "Functional")
+ public void testGetStatusMessage()
+ {
+ assertNull(AnnotationLabels.getStatusMessage(null, null));
+
+ /*
+ * simple label
+ */
+ AlignmentAnnotation aa = new AlignmentAnnotation("IUPredWS Short",
+ "Protein disorder", null);
+ assertEquals(AnnotationLabels.getStatusMessage(aa, null),
+ "IUPredWS Short");
+
+ /*
+ * with sequence ref
+ */
+ aa.setSequenceRef(new Sequence("FER_CAPAA", "MIGRKQL"));
+ assertEquals(AnnotationLabels.getStatusMessage(aa, null),
+ "FER_CAPAA : IUPredWS Short");
+
+ /*
+ * with graph group (degenerate, one annotation only)
+ */
+ aa.graphGroup = 1;
+ AlignmentAnnotation aa2 = new AlignmentAnnotation("IUPredWS Long",
+ "Protein disorder", null);
+ assertEquals(
+ AnnotationLabels.getStatusMessage(aa, new AlignmentAnnotation[]
+ { aa, aa2 }), "FER_CAPAA : IUPredWS Short");
+
+ /*
+ * graph group with two members; note labels are appended in
+ * reverse order (matching rendering order on screen)
+ */
+ aa2.graphGroup = 1;
+ assertEquals(
+ AnnotationLabels.getStatusMessage(aa, new AlignmentAnnotation[]
+ { aa, aa2 }), "FER_CAPAA : IUPredWS Long, IUPredWS Short");
+
+ /*
+ * graph group with no sequence ref
+ */
+ aa.sequenceRef = null;
+ assertEquals(
+ AnnotationLabels.getStatusMessage(aa, new AlignmentAnnotation[]
+ { aa, aa2 }), "IUPredWS Long, IUPredWS Short");
+ }
+}
--- /dev/null
+package jalview.gui;
+
+import static org.testng.Assert.assertEquals;
+
+import jalview.datamodel.AlignmentAnnotation;
+
+import org.testng.annotations.Test;
+
+public class AnnotationPanelTest
+{
+
+ @Test(groups = "Functional")
+ public void testGetRowIndex()
+ {
+ assertEquals(AnnotationPanel.getRowIndex(0, null), -1);
+
+ AlignmentAnnotation[] anns = new AlignmentAnnotation[] {};
+ assertEquals(AnnotationPanel.getRowIndex(0, anns), -1);
+
+ AlignmentAnnotation ann1 = new AlignmentAnnotation(null, null, null);
+ AlignmentAnnotation ann2 = new AlignmentAnnotation(null, null, null);
+ AlignmentAnnotation ann3 = new AlignmentAnnotation(null, null, null);
+ ann1.visible = true;
+ ann2.visible = true;
+ ann3.visible = true;
+ ann1.height = 10;
+ ann2.height = 20;
+ ann3.height = 30;
+ anns = new AlignmentAnnotation[] { ann1, ann2, ann3 };
+
+ assertEquals(AnnotationPanel.getRowIndex(0, anns), 0);
+ assertEquals(AnnotationPanel.getRowIndex(9, anns), 0);
+ assertEquals(AnnotationPanel.getRowIndex(10, anns), 1);
+ assertEquals(AnnotationPanel.getRowIndex(29, anns), 1);
+ assertEquals(AnnotationPanel.getRowIndex(30, anns), 2);
+ assertEquals(AnnotationPanel.getRowIndex(59, anns), 2);
+ assertEquals(AnnotationPanel.getRowIndex(60, anns), -1);
+
+ ann2.visible = false;
+ assertEquals(AnnotationPanel.getRowIndex(0, anns), 0);
+ assertEquals(AnnotationPanel.getRowIndex(9, anns), 0);
+ assertEquals(AnnotationPanel.getRowIndex(10, anns), 2);
+ assertEquals(AnnotationPanel.getRowIndex(39, anns), 2);
+ assertEquals(AnnotationPanel.getRowIndex(40, anns), -1);
+
+ ann1.visible = false;
+ assertEquals(AnnotationPanel.getRowIndex(0, anns), 2);
+ assertEquals(AnnotationPanel.getRowIndex(29, anns), 2);
+ assertEquals(AnnotationPanel.getRowIndex(30, anns), -1);
+ }
+}
import java.awt.Font;
import java.awt.FontMetrics;
-import junit.extensions.PA;
-
import org.testng.annotations.Test;
+import junit.extensions.PA;
+
public class SeqCanvasTest
{
/**
assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
/*
- * reduce canvas height by 1 pixel - should not be enough height
- * to draw 3 widths
+ * reduce canvas height by 1 pixel
+ * - should not be enough height to draw 3 widths
*/
canvasHeight -= 1;
testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
canvasWidth += 8;
wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
canvasHeight);
- assertEquals(wrappedWidth, 27);
+ assertEquals(wrappedWidth, 27); // 8px not enough
canvasWidth += 1;
wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
canvasHeight);
- assertEquals(wrappedWidth, 28);
+ assertEquals(wrappedWidth, 28); // 9px is enough
/*
* now West but not East scale - lose 39 pixels or 4 columns
canvasWidth += 2;
wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
canvasHeight);
- assertEquals(wrappedWidth, 24);
+ assertEquals(wrappedWidth, 24); // 2px not enough
canvasWidth += 1;
wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
canvasHeight);
- assertEquals(wrappedWidth, 25);
+ assertEquals(wrappedWidth, 25); // 3px is enough
/*
* turn off scales left and right, make width exactly 157 columns
2 * charHeight);
int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())
- + annotationHeight);
+ + SeqCanvas.SEQS_ANNOTATION_GAP + annotationHeight);
assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
/*
- * repeat height is 17 * (2 + 15) = 289 + annotationHeight = 507
- * make canvas height 2 * 289 + 3 * charHeight so just enough to
- * draw 2 widths and the first sequence of a third
+ * repeat height is 17 * (2 + 15) = 289 + 3 + annotationHeight = 510
+ * make canvas height 2 of these plus 3 charHeights
+ * so just enough to draw 2 widths, gap + scale + the first sequence of a third
*/
- canvasHeight = charHeight * (17 * 2 + 3) + 2 * annotationHeight;
+ canvasHeight = charHeight * (17 * 2 + 3)
+ + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP);
testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
* reduce height to enough for 2 widths and not quite a third
* i.e. two repeating heights + spacer + sequence - 1 pixel
*/
- canvasHeight = charHeight * (16 * 2 + 2) + 2 * annotationHeight - 1;
+ canvasHeight = charHeight * (16 * 2 + 2)
+ + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP) - 1;
testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import jalview.api.AlignViewportI;
+import jalview.bin.Cache;
+import jalview.bin.Jalview;
import jalview.commands.EditCommand;
import jalview.commands.EditCommand.Action;
import jalview.commands.EditCommand.Edit;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceI;
+import jalview.gui.SeqPanel.MousePos;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
import jalview.util.MessageManager;
+import java.awt.Event;
+import java.awt.event.MouseEvent;
+
+import javax.swing.JLabel;
+
+import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import junit.extensions.PA;
+
public class SeqPanelTest
{
AlignFrame af;
assertEquals(
alignFrame.alignPanel.getSeqPanel().setStatusMessage(
visAl.getSequenceAt(1), 1, 1), 2);
- assertEquals(alignFrame.statusBar.getText(),
+ assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
"Sequence 2 ID: Seq2 Residue: ALA (2)");
assertEquals(
alignFrame.alignPanel.getSeqPanel().setStatusMessage(
visAl.getSequenceAt(1), 4, 1), 3);
- assertEquals(alignFrame.statusBar.getText(),
+ assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
"Sequence 2 ID: Seq2 Residue: GLU (3)");
// no status message at a gap, returns next residue position to the right
assertEquals(
alignFrame.alignPanel.getSeqPanel().setStatusMessage(
visAl.getSequenceAt(1), 2, 1), 3);
- assertEquals(alignFrame.statusBar.getText(), "Sequence 2 ID: Seq2");
+ assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
+ "Sequence 2 ID: Seq2");
assertEquals(
alignFrame.alignPanel.getSeqPanel().setStatusMessage(
visAl.getSequenceAt(1), 3, 1), 3);
- assertEquals(alignFrame.statusBar.getText(), "Sequence 2 ID: Seq2");
+ assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
+ "Sequence 2 ID: Seq2");
}
@Test(groups = "Functional")
assertEquals(
alignFrame.alignPanel.getSeqPanel().setStatusMessage(
visAl.getSequenceAt(1), 1, 1), 2);
- assertEquals(alignFrame.statusBar.getText(),
+ assertEquals(((JLabel) PA.getValue(alignFrame, "statusBar")).getText(),
"Sequence 2 ID: Seq2 Residue: B (2)");
}
expected = MessageManager.formatMessage("label.delete_gaps", "3");
assertEquals(SeqPanel.getEditStatusMessage(edit), expected);
}
+
+ public void testFindMousePosition_unwrapped()
+ {
+ String seqData = ">Seq1\nAACDE\n>Seq2\nAA--E\n";
+ AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(seqData,
+ DataSourceType.PASTE);
+ AlignViewportI av = alignFrame.getViewport();
+ av.setShowAnnotation(true);
+ av.setWrapAlignment(false);
+ final int charHeight = av.getCharHeight();
+ final int charWidth = av.getCharWidth();
+ // sanity checks:
+ assertTrue(charHeight > 0);
+ assertTrue(charWidth > 0);
+ assertTrue(alignFrame.alignPanel.getSeqPanel().getWidth() > 0);
+
+ SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
+ int x = 0;
+ int y = 0;
+
+ /*
+ * mouse at top left of unwrapped panel
+ */
+ MouseEvent evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y,
+ 0, 0, 0, false, 0);
+ MousePos pos = testee.findMousePosition(evt);
+ assertEquals(pos.column, 0);
+ assertEquals(pos.seqIndex, 0);
+ assertEquals(pos.annotationIndex, -1);
+ }
+
+ @AfterMethod(alwaysRun = true)
+ public void tearDown()
+ {
+ Desktop.instance.closeAll_actionPerformed(null);
+ }
+
+ @Test(groups = "Functional")
+ public void testFindMousePosition_wrapped_annotations()
+ {
+ Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", "true");
+ Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
+ AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
+ "examples/uniref50.fa", DataSourceType.FILE);
+ AlignViewportI av = alignFrame.getViewport();
+ av.setScaleAboveWrapped(false);
+ av.setScaleLeftWrapped(false);
+ av.setScaleRightWrapped(false);
+ alignFrame.alignPanel.paintAlignment(false, false);
+ waitForSwing(); // for Swing thread
+
+ final int charHeight = av.getCharHeight();
+ final int charWidth = av.getCharWidth();
+ final int alignmentHeight = av.getAlignment().getHeight();
+
+ // sanity checks:
+ assertTrue(charHeight > 0);
+ assertTrue(charWidth > 0);
+ assertTrue(alignFrame.alignPanel.getSeqPanel().getWidth() > 0);
+
+ SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
+ int x = 0;
+ int y = 0;
+
+ /*
+ * mouse at top left of wrapped panel; there is a gap of charHeight
+ * above the alignment
+ */
+ MouseEvent evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y,
+ 0, 0, 0, false, 0);
+ MousePos pos = testee.findMousePosition(evt);
+ assertEquals(pos.column, 0);
+ assertEquals(pos.seqIndex, -1); // above sequences
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at bottom of gap above
+ */
+ y = charHeight - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, -1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor over top of first sequence
+ */
+ y = charHeight;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, 0);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at bottom of first sequence
+ */
+ y = 2 * charHeight - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, 0);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at top of second sequence
+ */
+ y = 2 * charHeight;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, 1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at bottom of second sequence
+ */
+ y = 3 * charHeight - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, 1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at bottom of last sequence
+ */
+ y = charHeight * (1 + alignmentHeight) - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor below sequences, in 3-pixel gap above annotations
+ * method reports index of nearest sequence above
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor still in the gap above annotations, now at the bottom of it
+ */
+ y += SeqCanvas.SEQS_ANNOTATION_GAP - 1; // 3-1 = 2
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at the top of the first annotation
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, 0); // over first annotation
+
+ /*
+ * cursor at the bottom of the first annotation
+ */
+ y += av.getAlignment().getAlignmentAnnotation()[0].height - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, 0);
+
+ /*
+ * cursor at the top of the second annotation
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, 1);
+
+ /*
+ * cursor at the bottom of the second annotation
+ */
+ y += av.getAlignment().getAlignmentAnnotation()[1].height - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, 1);
+
+ /*
+ * cursor at the top of the third annotation
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, 2);
+
+ /*
+ * cursor at the bottom of the third annotation
+ */
+ y += av.getAlignment().getAlignmentAnnotation()[2].height - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, 2);
+
+ /*
+ * cursor in gap between wrapped widths
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, -1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at bottom of gap between wrapped widths
+ */
+ y += charHeight - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, -1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at top of first sequence, second wrapped width
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, 0);
+ assertEquals(pos.annotationIndex, -1);
+ }
+
+ @Test(groups = "Functional")
+ public void testFindMousePosition_wrapped_scaleAbove()
+ {
+ Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", "true");
+ Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
+ AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
+ "examples/uniref50.fa", DataSourceType.FILE);
+ AlignViewportI av = alignFrame.getViewport();
+ av.setScaleAboveWrapped(true);
+ av.setScaleLeftWrapped(false);
+ av.setScaleRightWrapped(false);
+ alignFrame.alignPanel.paintAlignment(false, false);
+ waitForSwing();
+
+ final int charHeight = av.getCharHeight();
+ final int charWidth = av.getCharWidth();
+ final int alignmentHeight = av.getAlignment().getHeight();
+
+ // sanity checks:
+ assertTrue(charHeight > 0);
+ assertTrue(charWidth > 0);
+ assertTrue(alignFrame.alignPanel.getSeqPanel().getWidth() > 0);
+
+ SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
+ int x = 0;
+ int y = 0;
+
+ /*
+ * mouse at top left of wrapped panel; there is a gap of charHeight
+ * above the alignment
+ */
+ MouseEvent evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y,
+ 0, 0, 0, false, 0);
+ MousePos pos = testee.findMousePosition(evt);
+ assertEquals(pos.column, 0);
+ assertEquals(pos.seqIndex, -1); // above sequences
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at bottom of gap above
+ * two charHeights including scale panel
+ */
+ y = 2 * charHeight - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, -1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor over top of first sequence
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, 0);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at bottom of first sequence
+ */
+ y += charHeight - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, 0);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at top of second sequence
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, 1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at bottom of second sequence
+ */
+ y += charHeight - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, 1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at bottom of last sequence
+ * (scale + gap + sequences)
+ */
+ y = charHeight * (2 + alignmentHeight) - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor below sequences, in 3-pixel gap above annotations
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor still in the gap above annotations, now at the bottom of it
+ * method reports index of nearest sequence above
+ */
+ y += SeqCanvas.SEQS_ANNOTATION_GAP - 1; // 3-1 = 2
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at the top of the first annotation
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, 0); // over first annotation
+
+ /*
+ * cursor at the bottom of the first annotation
+ */
+ y += av.getAlignment().getAlignmentAnnotation()[0].height - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, 0);
+
+ /*
+ * cursor at the top of the second annotation
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, 1);
+
+ /*
+ * cursor at the bottom of the second annotation
+ */
+ y += av.getAlignment().getAlignmentAnnotation()[1].height - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, 1);
+
+ /*
+ * cursor at the top of the third annotation
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, 2);
+
+ /*
+ * cursor at the bottom of the third annotation
+ */
+ y += av.getAlignment().getAlignmentAnnotation()[2].height - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, 2);
+
+ /*
+ * cursor in gap between wrapped widths
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, -1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at bottom of gap between wrapped widths
+ */
+ y += charHeight - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, -1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at top of scale, second wrapped width
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, -1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at bottom of scale, second wrapped width
+ */
+ y += charHeight - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, -1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at top of first sequence, second wrapped width
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, 0);
+ assertEquals(pos.annotationIndex, -1);
+ }
+
+ @Test(groups = "Functional")
+ public void testFindMousePosition_wrapped_noAnnotations()
+ {
+ Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", "false");
+ Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
+ AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
+ "examples/uniref50.fa", DataSourceType.FILE);
+ AlignViewportI av = alignFrame.getViewport();
+ av.setScaleAboveWrapped(false);
+ av.setScaleLeftWrapped(false);
+ av.setScaleRightWrapped(false);
+ alignFrame.alignPanel.paintAlignment(false, false);
+ waitForSwing();
+
+ final int charHeight = av.getCharHeight();
+ final int charWidth = av.getCharWidth();
+ final int alignmentHeight = av.getAlignment().getHeight();
+
+ // sanity checks:
+ assertTrue(charHeight > 0);
+ assertTrue(charWidth > 0);
+ assertTrue(alignFrame.alignPanel.getSeqPanel().getWidth() > 0);
+
+ SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
+ int x = 0;
+ int y = 0;
+
+ /*
+ * mouse at top left of wrapped panel; there is a gap of charHeight
+ * above the alignment
+ */
+ MouseEvent evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y,
+ 0, 0, 0, false, 0);
+ MousePos pos = testee.findMousePosition(evt);
+ assertEquals(pos.column, 0);
+ assertEquals(pos.seqIndex, -1); // above sequences
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor over top of first sequence
+ */
+ y = charHeight;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, 0);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at bottom of last sequence
+ */
+ y = charHeight * (1 + alignmentHeight) - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, alignmentHeight - 1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor below sequences, at top of charHeight gap between widths
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, -1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor below sequences, at top of charHeight gap between widths
+ */
+ y += charHeight - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, -1);
+ assertEquals(pos.annotationIndex, -1);
+
+ /*
+ * cursor at the top of the first sequence, second width
+ */
+ y += 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+ false, 0);
+ pos = testee.findMousePosition(evt);
+ assertEquals(pos.seqIndex, 0);
+ assertEquals(pos.annotationIndex, -1);
+ }
+
+ @Test(groups = "Functional")
+ public void testFindColumn_unwrapped()
+ {
+ Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "false");
+ AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
+ "examples/uniref50.fa", DataSourceType.FILE);
+ SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
+ int x = 0;
+ final int charWidth = alignFrame.getViewport().getCharWidth();
+ assertTrue(charWidth > 0); // sanity check
+ assertEquals(alignFrame.getViewport().getRanges().getStartRes(), 0);
+
+ /*
+ * mouse at top left of unwrapped panel
+ */
+ MouseEvent evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0,
+ 0, 0, 0, false, 0);
+ assertEquals(testee.findColumn(evt), 0);
+
+ /*
+ * not quite one charWidth across
+ */
+ x = charWidth-1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0,
+ 0, 0, 0, false, 0);
+ assertEquals(testee.findColumn(evt), 0);
+
+ /*
+ * one charWidth across
+ */
+ x = charWidth;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+ false, 0);
+ assertEquals(testee.findColumn(evt), 1);
+
+ /*
+ * two charWidths across
+ */
+ x = 2 * charWidth;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+ false, 0);
+ assertEquals(testee.findColumn(evt), 2);
+
+ /*
+ * limited to last column of seqcanvas
+ */
+ x = 20000;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+ false, 0);
+ SeqCanvas seqCanvas = alignFrame.alignPanel.getSeqPanel().seqCanvas;
+ int w = seqCanvas.getWidth();
+ // limited to number of whole columns, base 0
+ int expected = w / charWidth - 1;
+ assertEquals(testee.findColumn(evt), expected);
+
+ /*
+ * hide columns 5-10 (base 1)
+ */
+ alignFrame.getViewport().hideColumns(4, 9);
+ x = 5 * charWidth + 2;
+ // x is in 6th visible column, absolute column 12, or 11 base 0
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+ false, 0);
+ assertEquals(testee.findColumn(evt), 11);
+ }
+
+ @Test(groups = "Functional")
+ public void testFindColumn_wrapped()
+ {
+ Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
+ AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
+ "examples/uniref50.fa", DataSourceType.FILE);
+ AlignViewport av = alignFrame.getViewport();
+ av.setScaleAboveWrapped(false);
+ av.setScaleLeftWrapped(false);
+ av.setScaleRightWrapped(false);
+ alignFrame.alignPanel.paintAlignment(false, false);
+ // need to wait for repaint to finish!
+ waitForSwing();
+ SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
+ int x = 0;
+ final int charWidth = av.getCharWidth();
+ assertTrue(charWidth > 0); // sanity check
+ assertEquals(av.getRanges().getStartRes(), 0);
+
+ /*
+ * mouse at top left of wrapped panel, no West (left) scale
+ */
+ MouseEvent evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0,
+ 0, 0, 0, false, 0);
+ assertEquals(testee.findColumn(evt), 0);
+
+ /*
+ * not quite one charWidth across
+ */
+ x = charWidth-1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0,
+ 0, 0, 0, false, 0);
+ assertEquals(testee.findColumn(evt), 0);
+
+ /*
+ * one charWidth across
+ */
+ x = charWidth;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+ false, 0);
+ assertEquals(testee.findColumn(evt), 1);
+
+ /*
+ * x over scale left (before drawn columns) results in -1
+ */
+ av.setScaleLeftWrapped(true);
+ alignFrame.alignPanel.paintAlignment(false, false);
+ waitForSwing();
+ SeqCanvas seqCanvas = testee.seqCanvas;
+ int labelWidth = (int) PA.getValue(seqCanvas, "labelWidthWest");
+ assertTrue(labelWidth > 0);
+ x = labelWidth - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+ false, 0);
+ assertEquals(testee.findColumn(evt), -1);
+
+ x = labelWidth;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+ false, 0);
+ assertEquals(testee.findColumn(evt), 0);
+
+ /*
+ * x over right edge of last residue (including scale left)
+ */
+ int residuesWide = av.getRanges().getViewportWidth();
+ assertTrue(residuesWide > 0);
+ x = labelWidth + charWidth * residuesWide - 1;
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+ false, 0);
+ assertEquals(testee.findColumn(evt), residuesWide - 1);
+
+ /*
+ * x over scale right (beyond drawn columns) results in -1
+ */
+ av.setScaleRightWrapped(true);
+ alignFrame.alignPanel.paintAlignment(false, false);
+ waitForSwing();
+ labelWidth = (int) PA.getValue(seqCanvas, "labelWidthEast");
+ assertTrue(labelWidth > 0);
+ int residuesWide2 = av.getRanges().getViewportWidth();
+ assertTrue(residuesWide2 > 0);
+ assertTrue(residuesWide2 < residuesWide); // available width reduced
+ x += 1; // just over left edge of scale right
+ evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+ false, 0);
+ assertEquals(testee.findColumn(evt), -1);
+
+ // todo add startRes offset, hidden columns
+
+ }
+ @BeforeClass(alwaysRun = true)
+ public static void setUpBeforeClass() throws Exception
+ {
+ /*
+ * use read-only test properties file
+ */
+ Cache.loadProperties("test/jalview/io/testProps.jvprops");
+ Jalview.main(new String[] { "-nonews" });
+ }
+
+ /**
+ * waits a few ms for Swing to do something
+ */
+ synchronized void waitForSwing()
+ {
+ try
+ {
+ super.wait(10);
+ } catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
}