import jalview.commands.EditCommand.Edit;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenColumns;
import jalview.datamodel.SearchResultMatchI;
import jalview.datamodel.SearchResults;
import jalview.datamodel.SearchResultsI;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
-import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import javax.swing.JPanel;
* @author $author$
* @version $Revision: 1.130 $
*/
-public class SeqPanel extends JPanel implements MouseListener,
- MouseMotionListener, MouseWheelListener, SequenceListener,
- SelectionListener
-
+public class SeqPanel extends JPanel
+ implements MouseListener, MouseMotionListener, MouseWheelListener,
+ SequenceListener, SelectionListener
{
- /** DOCUMENT ME!! */
+ private static final int MAX_TOOLTIP_LENGTH = 300;
+
public SeqCanvas seqCanvas;
- /** DOCUMENT ME!! */
public AlignmentPanel ap;
+ /*
+ * last column position for mouseMoved event
+ */
+ private int lastMouseColumn;
+
+ /*
+ * last sequence offset for mouseMoved event
+ */
+ private int lastMouseSeq;
+
protected int lastres;
protected int startseq;
SearchResultsI lastSearchResults;
/**
- * Creates a new SeqPanel object.
+ * Creates a new SeqPanel object
*
- * @param avp
- * DOCUMENT ME!
- * @param p
- * DOCUMENT ME!
+ * @param viewport
+ * @param alignPanel
*/
- public SeqPanel(AlignViewport av, AlignmentPanel ap)
+ public SeqPanel(AlignViewport viewport, AlignmentPanel alignPanel)
{
linkImageURL = getClass().getResource("/images/link.gif");
seqARep = new SequenceAnnotationReport(linkImageURL.toString());
ToolTipManager.sharedInstance().registerComponent(this);
ToolTipManager.sharedInstance().setInitialDelay(0);
ToolTipManager.sharedInstance().setDismissDelay(10000);
- this.av = av;
+ this.av = viewport;
setBackground(Color.white);
- seqCanvas = new SeqCanvas(ap);
+ seqCanvas = new SeqCanvas(alignPanel);
setLayout(new BorderLayout());
add(seqCanvas, BorderLayout.CENTER);
- this.ap = ap;
+ this.ap = alignPanel;
- if (!av.isDataset())
+ if (!viewport.isDataset())
{
addMouseMotionListener(this);
addMouseListener(this);
addMouseWheelListener(this);
- ssm = av.getStructureSelectionManager();
+ ssm = viewport.getStructureSelectionManager();
ssm.addStructureViewerListener(this);
ssm.addSelectionListener(this);
}
+
+ lastMouseColumn = -1;
+ lastMouseSeq = -1;
}
int startWrapBlock = -1;
* @param evt
* @return
*/
- int findRes(MouseEvent evt)
+ int findColumn(MouseEvent evt)
{
int res = 0;
int x = evt.getX();
+ int startRes = av.getRanges().getStartRes();
if (av.getWrapAlignment())
{
+ hgap + seqCanvas.getAnnotationHeight();
int y = evt.getY();
- y -= hgap;
- x -= seqCanvas.LABEL_WEST;
+ y = Math.max(0, y - hgap);
+ x = Math.max(0, x - seqCanvas.getLabelWidthWest());
int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
if (cwidth < 1)
}
wrappedBlock = y / cHeight;
- wrappedBlock += av.getRanges().getStartRes() / cwidth;
-
- res = wrappedBlock * cwidth + x / av.getCharWidth();
-
+ 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());
}
else
{
// right-hand gutter
x = seqCanvas.getX() + seqCanvas.getWidth();
}
- res = (x / av.getCharWidth()) + av.getRanges().getStartRes();
+ res = (x / av.getCharWidth()) + startRes;
if (res > av.getRanges().getEndRes())
{
// moused off right
if (av.hasHiddenColumns())
{
- res = av.getColumnSelection().adjustForHiddenColumns(res);
+ res = av.getAlignment().getHiddenColumns()
+ .visibleToAbsoluteColumn(res);
}
return res;
y -= hgap;
- seq = Math.min((y % cHeight) / av.getCharHeight(), av.getAlignment()
- .getHeight() - 1);
+ 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);
+ seq = Math.min(
+ (y / av.getCharHeight()) + av.getRanges().getStartSeq(),
+ av.getAlignment().getHeight() - 1);
}
return seq;
if (editCommand != null && editCommand.getSize() > 0)
{
ap.alignFrame.addHistoryItem(editCommand);
- av.firePropertyChange("alignment", null, av.getAlignment()
- .getSequences());
+ av.firePropertyChange("alignment", null,
+ av.getAlignment().getSequences());
}
} finally
{
void setCursorRow()
{
seqCanvas.cursorY = getKeyboardNo1() - 1;
- scrollToVisible();
+ scrollToVisible(true);
}
void setCursorColumn()
{
seqCanvas.cursorX = getKeyboardNo1() - 1;
- scrollToVisible();
+ scrollToVisible(true);
}
void setCursorRowAndColumn()
{
seqCanvas.cursorX = getKeyboardNo1() - 1;
seqCanvas.cursorY = getKeyboardNo2() - 1;
- scrollToVisible();
+ scrollToVisible(true);
}
}
SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1;
- scrollToVisible();
+ scrollToVisible(true);
}
void moveCursor(int dx, int dy)
{
seqCanvas.cursorX += dx;
seqCanvas.cursorY += dy;
- if (av.hasHiddenColumns()
- && !av.getColumnSelection().isVisible(seqCanvas.cursorX))
+
+ HiddenColumns hidden = av.getAlignment().getHiddenColumns();
+
+ if (av.hasHiddenColumns() && !hidden.isVisible(seqCanvas.cursorX))
{
int original = seqCanvas.cursorX - dx;
int maxWidth = av.getAlignment().getWidth();
- while (!av.getColumnSelection().isVisible(seqCanvas.cursorX)
- && seqCanvas.cursorX < maxWidth && seqCanvas.cursorX > 0)
+ if (!hidden.isVisible(seqCanvas.cursorX))
{
- seqCanvas.cursorX += dx;
+ int visx = hidden.absoluteToVisibleColumn(seqCanvas.cursorX - dx);
+ int[] region = hidden.getRegionWithEdgeAtRes(visx);
+
+ if (region != null) // just in case
+ {
+ if (dx == 1)
+ {
+ // moving right
+ seqCanvas.cursorX = region[1] + 1;
+ }
+ else if (dx == -1)
+ {
+ // moving left
+ seqCanvas.cursorX = region[0] - 1;
+ }
+ }
+ seqCanvas.cursorX = (seqCanvas.cursorX < 0) ? 0 : seqCanvas.cursorX;
}
if (seqCanvas.cursorX >= maxWidth
- || !av.getColumnSelection().isVisible(seqCanvas.cursorX))
+ || !hidden.isVisible(seqCanvas.cursorX))
{
seqCanvas.cursorX = original;
}
}
- scrollToVisible();
+ scrollToVisible(false);
}
- void scrollToVisible()
+ /**
+ * Scroll to make the cursor visible in the viewport.
+ *
+ * @param jump
+ * just jump to the location rather than scrolling
+ */
+ void scrollToVisible(boolean jump)
{
if (seqCanvas.cursorX < 0)
{
}
endEditing();
- if (av.getWrapAlignment())
+
+ boolean repaintNeeded = true;
+ if (jump)
{
- av.getRanges().scrollToWrappedVisible(seqCanvas.cursorX);
+ // only need to repaint if the viewport did not move, as otherwise it will
+ // get a repaint
+ repaintNeeded = !av.getRanges().setViewportLocation(seqCanvas.cursorX,
+ seqCanvas.cursorY);
}
else
{
- while (seqCanvas.cursorY < av.getRanges().getStartSeq())
+ if (av.getWrapAlignment())
{
- av.getRanges().scrollUp(true);
+ // scrollToWrappedVisible expects x-value to have hidden cols subtracted
+ int x = av.getAlignment().getHiddenColumns()
+ .absoluteToVisibleColumn(seqCanvas.cursorX);
+ av.getRanges().scrollToWrappedVisible(x);
}
- while (seqCanvas.cursorY + 1 > av.getRanges().getEndSeq())
- {
- av.getRanges().scrollUp(false);
- }
-
- while (seqCanvas.cursorX < av.getColumnSelection()
- .adjustForHiddenColumns(av.getRanges().getStartRes()))
+ else
{
- if (!av.getRanges().scrollRight(false))
- {
- break;
- }
+ av.getRanges().scrollToVisible(seqCanvas.cursorX,
+ seqCanvas.cursorY);
}
- while (seqCanvas.cursorX > av.getColumnSelection()
- .adjustForHiddenColumns(av.getRanges().getEndRes()))
- {
- if (!av.getRanges().scrollRight(true))
- {
- break;
- }
- }
-
}
- setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
+
+ if (av.getAlignment().getHiddenColumns().isVisible(seqCanvas.cursorX))
+ {
+ setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
seqCanvas.cursorX, seqCanvas.cursorY);
+ }
- seqCanvas.repaint();
+ if (repaintNeeded)
+ {
+ seqCanvas.repaint();
+ }
}
+
void setSelectionAreaAtCursor(boolean topLeft)
{
SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
av.setSelectionGroup(sg);
}
- ap.paintAlignment(false);
+ ap.paintAlignment(false, false);
av.sendSelection();
}
@Override
public void mouseReleased(MouseEvent evt)
{
+ boolean didDrag = mouseDragging; // did we come here after a drag
mouseDragging = false;
mouseWheelPressed = false;
if (!editingSeqs)
{
- doMouseReleasedDefineMode(evt);
+ doMouseReleasedDefineMode(evt, didDrag);
return;
}
}
int seq = findSeq(evt);
- int res = findRes(evt);
+ int res = findColumn(evt);
if (seq < 0 || res < 0)
{
}
lastSearchResults = results;
+ boolean wasScrolled = false;
+
if (av.isFollowHighlight())
{
// don't allow highlight of protein/cDNA to also scroll a complementary
// panel,as this sets up a feedback loop (scrolling panel 1 causes moused
// over residue to change abruptly, causing highlighted residue in panel 2
// to change, causing a scroll in panel 1 etc)
- ap.setDontScrollComplement(true);
- if (ap.scrollToPosition(results, false))
+ ap.setToScrollComplementPanel(false);
+ wasScrolled = ap.scrollToPosition(results, false);
+ if (wasScrolled)
{
seqCanvas.revalidate();
}
- ap.setDontScrollComplement(false);
+ ap.setToScrollComplementPanel(true);
+ }
+
+ boolean noFastPaint = wasScrolled && av.getWrapAlignment();
+ if (seqCanvas.highlightSearchResults(results, noFastPaint))
+ {
+ setStatusMessage(results);
}
- setStatusMessage(results);
- seqCanvas.highlightSearchResults(results);
}
@Override
}
/**
- * DOCUMENT ME!
+ * Action on mouse movement is to update the status bar to show the current
+ * sequence position, and (if features are shown) to show any features at the
+ * position in a tooltip. Does nothing if the mouse move does not change
+ * residue position.
*
* @param evt
- * DOCUMENT ME!
*/
@Override
public void mouseMoved(MouseEvent evt)
mouseDragged(evt);
}
- int res = findRes(evt);
- int seq = findSeq(evt);
- int pos;
- if (res < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
+ final int column = findColumn(evt);
+ final int seq = findSeq(evt);
+
+ if (column < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
{
+ lastMouseSeq = -1;
return;
}
+ if (column == lastMouseColumn && seq == lastMouseSeq)
+ {
+ /*
+ * just a pixel move without change of residue
+ */
+ return;
+ }
+ lastMouseColumn = column;
+ lastMouseSeq = seq;
SequenceI sequence = av.getAlignment().getSequenceAt(seq);
- if (res >= sequence.getLength())
+ if (column >= sequence.getLength())
{
return;
}
- pos = setStatusMessage(sequence, res, seq);
- if (ssm != null && pos > -1)
+ /*
+ * set status bar message, returning residue position in sequence
+ */
+ boolean isGapped = Comparison.isGap(sequence.getCharAt(column));
+ final int pos = setStatusMessage(sequence, column, seq);
+ if (ssm != null && !isGapped)
{
- mouseOverSequence(sequence, res, pos);
+ mouseOverSequence(sequence, column, pos);
}
tooltipText.setLength(6); // Cuts the buffer back to <html>
{
for (int g = 0; g < groups.length; g++)
{
- if (groups[g].getStartRes() <= res && groups[g].getEndRes() >= res)
+ if (groups[g].getStartRes() <= column
+ && groups[g].getEndRes() >= column)
{
if (!groups[g].getName().startsWith("JTreeGroup")
&& !groups[g].getName().startsWith("JGroup"))
}
}
- // use aa to see if the mouse pointer is on a
+ /*
+ * add any features at the position to the tooltip; if over a gap, only
+ * add features that straddle the gap (pos may be the residue before or
+ * after the gap)
+ */
if (av.isShowSequenceFeatures())
{
- int rpos;
List<SequenceFeature> features = ap.getFeatureRenderer()
- .findFeaturesAtRes(sequence.getDatasetSequence(),
- rpos = sequence.findPosition(res));
- seqARep.appendFeatures(tooltipText, rpos, features,
- this.ap.getSeqPanel().seqCanvas.fr.getMinMax());
+ .findFeaturesAtColumn(sequence, column + 1);
+ seqARep.appendFeatures(tooltipText, pos, features,
+ this.ap.getSeqPanel().seqCanvas.fr);
}
if (tooltipText.length() == 6) // <html>
{
}
else
{
- if (lastTooltip == null
- || !lastTooltip.equals(tooltipText.toString()))
+ if (tooltipText.length() > MAX_TOOLTIP_LENGTH) // constant
{
- String formatedTooltipText = JvSwingUtils.wrapTooltip(true,
- tooltipText.toString());
- // String formatedTooltipText = tooltipText.toString();
- setToolTipText(formatedTooltipText);
- lastTooltip = tooltipText.toString();
+ tooltipText.setLength(MAX_TOOLTIP_LENGTH);
+ tooltipText.append("...");
+ }
+ String textString = tooltipText.toString();
+ if (lastTooltip == null || !lastTooltip.equals(textString))
+ {
+ String formattedTooltipText = JvSwingUtils.wrapTooltip(true,
+ textString);
+ setToolTipText(formattedTooltipText);
+ lastTooltip = textString;
}
-
}
-
}
private Point lastp = null;
Point p = lastp;
if (!event.isShiftDown() || p == null)
{
- p = (tooltipText != null && tooltipText.length() > 6) ? new Point(
- event.getX() + wdth, event.getY() - 20) : null;
+ p = (tooltipText != null && tooltipText.length() > 6)
+ ? new Point(event.getX() + wdth, event.getY() - 20)
+ : null;
}
/*
* TODO: try to modify position region is not obcured by tooltip
/**
* set when the current UI interaction has resulted in a change that requires
- * overview shading to be recalculated. this could be changed to something
- * more expressive that indicates what actually has changed, so selective
- * redraws can be applied
+ * shading in overviews and structures to be recalculated. this could be
+ * changed to a something more expressive that indicates what actually has
+ * changed, so selective redraws can be applied (ie. only structures, only
+ * overview, etc)
*/
- private boolean needOverviewUpdate = false; // TODO: refactor to avcontroller
+ private boolean updateOverviewAndStructs = false; // TODO: refactor to avcontroller
/**
* set if av.getSelectionGroup() refers to a group that is defined on the
// avcontroller or viewModel
/**
- * Set status message in alignment panel
+ * Sets the status message in alignment panel, showing the sequence number
+ * (index) and id, and residue and residue position if not at a gap, for the
+ * given sequence and column position. Returns the residue position returned
+ * by Sequence.findPosition. Note this may be for the nearest adjacent residue
+ * if at a gapped position.
*
* @param sequence
* aligned sequence object
- * @param res
+ * @param column
* alignment column
- * @param seq
+ * @param seqIndex
* index of sequence in alignment
- * @return position of res in sequence
+ * @return sequence position of residue at column, or adjacent residue if at a
+ * gap
+ */
+ int setStatusMessage(SequenceI sequence, final int column, int seqIndex)
+ {
+ char sequenceChar = sequence.getCharAt(column);
+ int pos = sequence.findPosition(column);
+ setStatusMessage(sequence, seqIndex, sequenceChar, pos);
+
+ return pos;
+ }
+
+ /**
+ * Builds the status message for the current cursor location and writes it to
+ * the status bar, for example
+ *
+ * <pre>
+ * Sequence 3 ID: FER1_SOLLC
+ * Sequence 5 ID: FER1_PEA Residue: THR (4)
+ * Sequence 5 ID: FER1_PEA Residue: B (3)
+ * Sequence 6 ID: O.niloticus.3 Nucleotide: Uracil (2)
+ * </pre>
+ *
+ * @param sequence
+ * @param seqIndex
+ * sequence position in the alignment (1..)
+ * @param sequenceChar
+ * the character under the cursor
+ * @param residuePos
+ * the sequence residue position (if not over a gap)
*/
- int setStatusMessage(SequenceI sequence, int res, int seq)
+ protected void setStatusMessage(SequenceI sequence, int seqIndex,
+ char sequenceChar, int residuePos)
{
StringBuilder text = new StringBuilder(32);
/*
* Sequence number (if known), and sequence name.
*/
- String seqno = seq == -1 ? "" : " " + (seq + 1);
+ String seqno = seqIndex == -1 ? "" : " " + (seqIndex + 1);
text.append("Sequence").append(seqno).append(" ID: ")
.append(sequence.getName());
String residue = null;
+
/*
* Try to translate the display character to residue name (null for gap).
*/
- final String displayChar = String.valueOf(sequence.getCharAt(res));
- if (av.getAlignment().isNucleotide())
+ boolean isGapped = Comparison.isGap(sequenceChar);
+
+ if (!isGapped)
{
- residue = ResidueProperties.nucleotideName.get(displayChar);
- if (residue != null)
+ boolean nucleotide = av.getAlignment().isNucleotide();
+ String displayChar = String.valueOf(sequenceChar);
+ if (nucleotide)
{
- text.append(" Nucleotide: ").append(residue);
+ residue = ResidueProperties.nucleotideName.get(displayChar);
}
- }
- else
- {
- residue = "X".equalsIgnoreCase(displayChar) ? "X" : ("*"
- .equals(displayChar) ? "STOP" : ResidueProperties.aa2Triplet
- .get(displayChar));
- if (residue != null)
+ else
{
- text.append(" Residue: ").append(residue);
+ residue = "X".equalsIgnoreCase(displayChar) ? "X"
+ : ("*".equals(displayChar) ? "STOP"
+ : ResidueProperties.aa2Triplet.get(displayChar));
}
- }
+ text.append(" ").append(nucleotide ? "Nucleotide" : "Residue")
+ .append(": ").append(residue == null ? displayChar : residue);
- int pos = -1;
- if (residue != null)
- {
- pos = sequence.findPosition(res);
- text.append(" (").append(Integer.toString(pos)).append(")");
+ text.append(" (").append(Integer.toString(residuePos)).append(")");
}
ap.alignFrame.statusBar.setText(text.toString());
- return pos;
}
/**
if (seq == ds)
{
- /*
- * Convert position in sequence (base 1) to sequence character array
- * index (base 0)
- */
- int start = m.getStart() - m.getSequence().getStart();
- setStatusMessage(seq, start, sequenceIndex);
+ int start = m.getStart();
+ setStatusMessage(seq, sequenceIndex, seq.getCharAt(start - 1),
+ start);
return;
}
}
}
/**
- * DOCUMENT ME!
- *
- * @param evt
- * DOCUMENT ME!
+ * {@inheritDoc}
*/
@Override
public void mouseDragged(MouseEvent evt)
{
if (mouseWheelPressed)
{
+ boolean inSplitFrame = ap.av.getCodingComplement() != null;
+ boolean copyChanges = inSplitFrame && av.isProteinFontAsCdna();
+
int oldWidth = av.getCharWidth();
// Which is bigger, left-right or up-down?
- if (Math.abs(evt.getY() - lastMousePress.getY()) > Math.abs(evt
- .getX() - lastMousePress.getX()))
+ if (Math.abs(evt.getY() - lastMousePress.getY()) > Math
+ .abs(evt.getX() - lastMousePress.getX()))
{
+ /*
+ * on drag up or down, decrement or increment font size
+ */
int fontSize = av.font.getSize();
+ boolean fontChanged = false;
if (evt.getY() < lastMousePress.getY())
{
+ fontChanged = true;
fontSize--;
}
else if (evt.getY() > lastMousePress.getY())
{
+ fontChanged = true;
fontSize++;
}
fontSize = 1;
}
- av.setFont(
- new Font(av.font.getName(), av.font.getStyle(), fontSize),
- true);
- av.setCharWidth(oldWidth);
- ap.fontChanged();
+ if (fontChanged)
+ {
+ Font newFont = new Font(av.font.getName(), av.font.getStyle(),
+ fontSize);
+ av.setFont(newFont, true);
+ av.setCharWidth(oldWidth);
+ ap.fontChanged();
+ if (copyChanges)
+ {
+ ap.av.getCodingComplement().setFont(newFont, true);
+ SplitFrame splitFrame = (SplitFrame) ap.alignFrame
+ .getSplitViewContainer();
+ splitFrame.adjustLayout();
+ splitFrame.repaint();
+ }
+ }
}
else
{
+ /*
+ * on drag left or right, decrement or increment character width
+ */
+ int newWidth = 0;
if (evt.getX() < lastMousePress.getX() && av.getCharWidth() > 1)
{
- av.setCharWidth(av.getCharWidth() - 1);
+ newWidth = av.getCharWidth() - 1;
+ av.setCharWidth(newWidth);
}
else if (evt.getX() > lastMousePress.getX())
{
- av.setCharWidth(av.getCharWidth() + 1);
+ newWidth = av.getCharWidth() + 1;
+ av.setCharWidth(newWidth);
+ }
+ if (newWidth > 0)
+ {
+ ap.paintAlignment(false, false);
+ if (copyChanges)
+ {
+ /*
+ * need to ensure newWidth is set on cdna, regardless of which
+ * panel the mouse drag happened in; protein will compute its
+ * character width as 1:1 or 3:1
+ */
+ av.getCodingComplement().setCharWidth(newWidth);
+ SplitFrame splitFrame = (SplitFrame) ap.alignFrame
+ .getSplitViewContainer();
+ splitFrame.adjustLayout();
+ splitFrame.repaint();
+ }
}
-
- ap.paintAlignment(false);
}
FontMetrics fm = getFontMetrics(av.getFont());
return;
}
- int res = findRes(evt);
+ int res = findColumn(evt);
if (res < 0)
{
}
mouseDragging = true;
- if (scrollThread != null)
+ if ((scrollThread != null) && (scrollThread.isRunning()))
{
scrollThread.setEvent(evt);
}
}
if (editCommand == null)
{
- editCommand = new EditCommand(MessageManager.formatMessage(
- "label.edit_params", new String[] { label }));
+ editCommand = new EditCommand(MessageManager
+ .formatMessage("label.edit_params", new String[]
+ { label }));
}
}
ap.alignFrame.statusBar.setText(message.toString());
// Are we editing within a selection group?
- if (groupEditing
- || (sg != null && sg.getSequences(av.getHiddenRepSequences())
- .contains(seq)))
+ if (groupEditing || (sg != null
+ && sg.getSequences(av.getHiddenRepSequences()).contains(seq)))
{
fixedColumns = true;
if (av.hasHiddenColumns())
{
fixedColumns = true;
- int y1 = av.getColumnSelection().getHiddenBoundaryLeft(startres);
- int y2 = av.getColumnSelection().getHiddenBoundaryRight(startres);
+ int y1 = av.getAlignment().getHiddenColumns()
+ .getNextHiddenBoundary(true, startres);
+ int y2 = av.getAlignment().getHiddenColumns()
+ .getNextHiddenBoundary(false, startres);
if ((insertGap && startres > y1 && lastres < y1)
|| (!insertGap && startres < y2 && lastres > y2))
// Find the next gap before the end
// of the visible region boundary
boolean blank = false;
- for (fixedRight = fixedRight; fixedRight > lastres; fixedRight--)
+ for (; fixedRight > lastres; fixedRight--)
{
blank = true;
{
if (sg.getSize() == av.getAlignment().getHeight())
{
- if ((av.hasHiddenColumns() && startres < av
- .getColumnSelection().getHiddenBoundaryRight(startres)))
+ if ((av.hasHiddenColumns() && startres < av.getAlignment()
+ .getHiddenColumns()
+ .getNextHiddenBoundary(false, startres)))
{
endEditing();
return;
}
else
{
- appendEdit(Action.INSERT_GAP, groupSeqs, startres, startres
- - lastres);
+ appendEdit(Action.INSERT_GAP, groupSeqs, startres,
+ startres - lastres);
}
}
else
}
else
{
- appendEdit(Action.DELETE_GAP, groupSeqs, startres, lastres
- - startres);
+ appendEdit(Action.DELETE_GAP, groupSeqs, startres,
+ lastres - startres);
}
}
oldSeq = 0;
}
- if (scrollThread != null)
+ if ((scrollThread != null) && (scrollThread.isRunning()))
{
- scrollThread.running = false;
+ scrollThread.stopScrolling();
scrollThread = null;
}
}
return;
}
- if (mouseDragging)
+ if (mouseDragging && scrollThread == null)
{
scrollThread = new ScrollThread();
}
}
+ /**
+ * Handler for double-click on a position with one or more sequence features.
+ * Opens the Amend Features dialog to allow feature details to be amended, or
+ * the feature deleted.
+ */
@Override
public void mouseClicked(MouseEvent evt)
{
av.setSelectionGroup(null);
}
+ int column = findColumn(evt);
+
+ /*
+ * find features at the position (if not gapped), or straddling
+ * the position (if at a gap)
+ */
List<SequenceFeature> features = seqCanvas.getFeatureRenderer()
- .findFeaturesAtRes(sequence.getDatasetSequence(),
- sequence.findPosition(findRes(evt)));
+ .findFeaturesAtColumn(sequence, column + 1);
- if (features != null && features.size() > 0)
+ if (!features.isEmpty())
{
+ /*
+ * highlight the first feature at the position on the alignment
+ */
SearchResultsI highlight = new SearchResults();
highlight.addResult(sequence, features.get(0).getBegin(), features
.get(0).getEnd());
- seqCanvas.highlightSearchResults(highlight);
- }
- if (features != null && features.size() > 0)
- {
- seqCanvas.getFeatureRenderer().amendFeatures(
- new SequenceI[] { sequence },
- features.toArray(new SequenceFeature[features.size()]),
- false, ap);
+ seqCanvas.highlightSearchResults(highlight, false);
- seqCanvas.highlightSearchResults(null);
+ /*
+ * open the Amend Features dialog; clear highlighting afterwards,
+ * whether changes were made or not
+ */
+ List<SequenceI> seqs = Collections.singletonList(sequence);
+ seqCanvas.getFeatureRenderer().amendFeatures(seqs, features, false,
+ ap);
+ av.setSearchResults(null); // clear highlighting
+ seqCanvas.repaint(); // draw new/amended features
}
}
}
public void mouseWheelMoved(MouseWheelEvent e)
{
e.consume();
- if (e.getWheelRotation() > 0)
+ double wheelRotation = e.getPreciseWheelRotation();
+ if (wheelRotation > 0)
{
if (e.isShiftDown())
{
av.getRanges().scrollUp(false);
}
}
- else
+ else if (wheelRotation < 0)
{
if (e.isShiftDown())
{
av.getRanges().scrollUp(true);
}
}
- // TODO Update tooltip for new position.
+
+ /*
+ * update status bar and tooltip for new position
+ * (need to synthesize a mouse movement to refresh tooltip)
+ */
+ mouseMoved(e);
+ ToolTipManager.sharedInstance().mouseMoved(e);
}
/**
*/
public void doMousePressedDefineMode(MouseEvent evt)
{
- final int res = findRes(evt);
+ final int res = findColumn(evt);
final int seq = findSeq(evt);
oldSeq = seq;
- needOverviewUpdate = false;
+ updateOverviewAndStructs = false;
startWrapBlock = wrappedBlock;
if (av.getWrapAlignment() && seq > av.getAlignment().getHeight())
{
- JvOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
- .getString("label.cannot_edit_annotations_in_wrapped_view"),
+ 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 (av.cursorMode)
{
- seqCanvas.cursorX = findRes(evt);
+ seqCanvas.cursorX = findColumn(evt);
seqCanvas.cursorY = findSeq(evt);
seqCanvas.repaint();
return;
if (stretchGroup == null)
{
- // Only if left mouse button do we want to change group sizes
+ createStretchGroup(res, sequence);
+ }
- // define a new group here
- SequenceGroup sg = new SequenceGroup();
- sg.setStartRes(res);
- sg.setEndRes(res);
- sg.addSequence(sequence, false);
- av.setSelectionGroup(sg);
- stretchGroup = sg;
+ if (stretchGroup != null)
+ {
+ stretchGroup.addPropertyChangeListener(seqCanvas);
+ }
- if (av.getConservationSelected())
- {
- SliderPanel.setConservationSlider(ap, av.getResidueShading(),
- ap.getViewName());
- }
+ seqCanvas.repaint();
+ }
- if (av.getAbovePIDThreshold())
- {
- SliderPanel.setPIDSliderSource(ap, av.getResidueShading(),
- ap.getViewName());
- }
- // TODO: stretchGroup will always be not null. Is this a merge error ?
- if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
- {
- // Edit end res position of selected group
- changeEndRes = true;
- }
- else if ((stretchGroup != null)
- && (stretchGroup.getStartRes() == res))
- {
- // Edit end res position of selected group
- changeStartRes = true;
- }
- stretchGroup.getWidth();
+ private void createStretchGroup(int res, SequenceI sequence)
+ {
+ // Only if left mouse button do we want to change group sizes
+ // define a new group here
+ SequenceGroup sg = new SequenceGroup();
+ sg.setStartRes(res);
+ sg.setEndRes(res);
+ sg.addSequence(sequence, false);
+ av.setSelectionGroup(sg);
+ stretchGroup = sg;
+
+ if (av.getConservationSelected())
+ {
+ SliderPanel.setConservationSlider(ap, av.getResidueShading(),
+ ap.getViewName());
}
- seqCanvas.repaint();
+ if (av.getAbovePIDThreshold())
+ {
+ SliderPanel.setPIDSliderSource(ap, av.getResidueShading(),
+ ap.getViewName());
+ }
+ // TODO: stretchGroup will always be not null. Is this a merge error ?
+ // or is there a threading issue here?
+ if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
+ {
+ // Edit end res position of selected group
+ changeEndRes = true;
+ }
+ else if ((stretchGroup != null) && (stretchGroup.getStartRes() == res))
+ {
+ // Edit end res position of selected group
+ changeStartRes = true;
+ }
+ stretchGroup.getWidth();
+
}
/**
*
* @param evt
* @param res
- * @param sequence
+ * @param sequences
*/
void showPopupMenu(MouseEvent evt)
{
- final int res = findRes(evt);
+ final int column = findColumn(evt);
final int seq = findSeq(evt);
SequenceI sequence = av.getAlignment().getSequenceAt(seq);
- List<SequenceFeature> allFeatures = ap.getFeatureRenderer()
- .findFeaturesAtRes(sequence.getDatasetSequence(),
- sequence.findPosition(res));
- List<String> links = new ArrayList<String>();
- for (SequenceFeature sf : allFeatures)
- {
- if (sf.links != null)
- {
- for (String link : sf.links)
- {
- links.add(link);
- }
- }
- }
+ List<SequenceFeature> features = ap.getFeatureRenderer()
+ .findFeaturesAtColumn(sequence, column + 1);
- PopupMenu pop = new PopupMenu(ap, null, links);
+ PopupMenu pop = new PopupMenu(ap, null, features);
pop.show(this, evt.getX(), evt.getY());
}
/**
- * DOCUMENT ME!
+ * Update the display after mouse up on a selection or group
*
* @param evt
- * DOCUMENT ME!
+ * mouse released event details
+ * @param afterDrag
+ * true if this event is happening after a mouse drag (rather than a
+ * mouse down)
*/
- public void doMouseReleasedDefineMode(MouseEvent evt)
+ public void doMouseReleasedDefineMode(MouseEvent evt, boolean afterDrag)
{
if (stretchGroup == null)
{
return;
}
+
+ stretchGroup.removePropertyChangeListener(seqCanvas);
+
// always do this - annotation has own state
// but defer colourscheme update until hidden sequences are passed in
- boolean vischange = stretchGroup.recalcConservation(true);
- needOverviewUpdate |= vischange && av.isSelectionDefinedGroup();
+ boolean vischange = stretchGroup.recalcAnnotations(true);
+ updateOverviewAndStructs |= vischange && av.isSelectionDefinedGroup()
+ && afterDrag;
if (stretchGroup.cs != null)
{
stretchGroup.cs.alignmentChanged(stretchGroup,
av.getHiddenRepSequences());
- ResidueShaderI groupColourScheme = stretchGroup.getGroupColourScheme();
+ ResidueShaderI groupColourScheme = stretchGroup
+ .getGroupColourScheme();
String name = stretchGroup.getName();
if (stretchGroup.cs.conservationApplied())
{
}
}
PaintRefresher.Refresh(this, av.getSequenceSetId());
- ap.paintAlignment(needOverviewUpdate);
- needOverviewUpdate = false;
+ // TODO: structure colours only need updating if stretchGroup used to or now
+ // does contain sequences with structure views
+ ap.paintAlignment(updateOverviewAndStructs, updateOverviewAndStructs);
+ updateOverviewAndStructs = false;
changeEndRes = false;
changeStartRes = false;
stretchGroup = null;
*/
public void doMouseDraggedDefineMode(MouseEvent evt)
{
- int res = findRes(evt);
+ int res = findColumn(evt);
int y = findSeq(evt);
if (wrappedBlock != startWrapBlock)
if (res > (stretchGroup.getStartRes() - 1))
{
stretchGroup.setEndRes(res);
- needOverviewUpdate |= av.isSelectionDefinedGroup();
+ updateOverviewAndStructs |= av.isSelectionDefinedGroup();
}
}
else if (changeStartRes)
if (res < (stretchGroup.getEndRes() + 1))
{
stretchGroup.setStartRes(res);
- needOverviewUpdate |= av.isSelectionDefinedGroup();
+ updateOverviewAndStructs |= av.isSelectionDefinedGroup();
}
}
if (stretchGroup.getSequences(null).contains(nextSeq))
{
stretchGroup.deleteSequence(seq, false);
- needOverviewUpdate |= av.isSelectionDefinedGroup();
+ updateOverviewAndStructs |= av.isSelectionDefinedGroup();
}
else
{
}
stretchGroup.addSequence(nextSeq, false);
- needOverviewUpdate |= av.isSelectionDefinedGroup();
+ updateOverviewAndStructs |= av.isSelectionDefinedGroup();
}
}
mouseDragging = true;
- if (scrollThread != null)
+ if ((scrollThread != null) && (scrollThread.isRunning()))
{
scrollThread.setEvent(evt);
}
-
- seqCanvas.repaint();
}
void scrollCanvas(MouseEvent evt)
{
if (evt == null)
{
- if (scrollThread != null)
+ if ((scrollThread != null) && (scrollThread.isRunning()))
{
- scrollThread.running = false;
+ scrollThread.stopScrolling();
scrollThread = null;
}
mouseDragging = false;
{
MouseEvent evt;
- boolean running = false;
+ private volatile boolean threadRunning = true;
public ScrollThread()
{
public void stopScrolling()
{
- running = false;
+ threadRunning = false;
+ }
+
+ public boolean isRunning()
+ {
+ return threadRunning;
}
@Override
public void run()
{
- running = true;
-
- while (running)
+ while (threadRunning)
{
if (evt != null)
{
if (mouseDragging && (evt.getY() < 0)
&& (av.getRanges().getStartSeq() > 0))
{
- running = av.getRanges().scrollUp(true);
+ av.getRanges().scrollUp(true);
}
- if (mouseDragging && (evt.getY() >= getHeight())
- && (av.getAlignment().getHeight() > av.getRanges()
- .getEndSeq()))
+ if (mouseDragging && (evt.getY() >= getHeight()) && (av
+ .getAlignment().getHeight() > av.getRanges().getEndSeq()))
{
- running = av.getRanges().scrollUp(false);
+ av.getRanges().scrollUp(false);
}
if (mouseDragging && (evt.getX() < 0))
{
- running = av.getRanges().scrollRight(false);
+ av.getRanges().scrollRight(false);
}
else if (mouseDragging && (evt.getX() >= getWidth()))
{
- running = av.getRanges().scrollRight(true);
+ av.getRanges().scrollRight(true);
}
}
*/
@Override
public void selection(SequenceGroup seqsel, ColumnSelection colsel,
- SelectionSource source)
+ HiddenColumns hidden, SelectionSource source)
{
// TODO: fix this hack - source of messages is align viewport, but SeqPanel
// handles selection messages...
// TODO: extend config options to allow user to control if selections may be
// shared between viewports.
- boolean iSentTheSelection = (av == source || (source instanceof AlignViewport && ((AlignmentViewport) source)
- .getSequenceSetId().equals(av.getSequenceSetId())));
- if (iSentTheSelection || !av.followSelection)
+ boolean iSentTheSelection = (av == source
+ || (source instanceof AlignViewport
+ && ((AlignmentViewport) source).getSequenceSetId()
+ .equals(av.getSequenceSetId())));
+
+ if (iSentTheSelection)
+ {
+ // respond to our own event by updating dependent dialogs
+ if (ap.getCalculationDialog() != null)
+ {
+ ap.getCalculationDialog().validateCalcTypes();
+ }
+
+ return;
+ }
+
+ // process further ?
+ if (!av.followSelection)
{
return;
}
* Check for selection in a view of which this one is a dna/protein
* complement.
*/
- if (selectionFromTranslation(seqsel, colsel, source))
+ if (selectionFromTranslation(seqsel, colsel, hidden, source))
{
return;
}
}
else
{
- av.getColumnSelection().setElementsFrom(colsel);
+ av.getColumnSelection().setElementsFrom(colsel,
+ av.getAlignment().getHiddenColumns());
}
}
av.isColSelChanged(true);
repaint = true;
}
- if (copycolsel
- && av.hasHiddenColumns()
- && (av.getColumnSelection() == null || av.getColumnSelection()
- .getHiddenColumns() == null))
+ if (copycolsel && av.hasHiddenColumns()
+ && (av.getAlignment().getHiddenColumns() == null))
{
System.err.println("Bad things");
}
PaintRefresher.Refresh(this, av.getSequenceSetId());
// ap.paintAlignment(false);
}
+
+ // lastly, update dependent dialogs
+ if (ap.getCalculationDialog() != null)
+ {
+ ap.getCalculationDialog().validateCalcTypes();
+ }
+
}
/**
* @param source
*/
protected boolean selectionFromTranslation(SequenceGroup seqsel,
- ColumnSelection colsel, SelectionSource source)
+ ColumnSelection colsel, HiddenColumns hidden,
+ SelectionSource source)
{
if (!(source instanceof AlignViewportI))
{
/*
* Map column selection
*/
- ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
- av);
+ // ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
+ // av);
+ ColumnSelection cs = new ColumnSelection();
+ HiddenColumns hs = new HiddenColumns();
+ MappingUtils.mapColumnSelection(colsel, hidden, sourceAv, av, cs, hs);
av.setColumnSelection(cs);
+ av.getAlignment().setHiddenColumns(hs);
+
+ // lastly, update any dependent dialogs
+ if (ap.getCalculationDialog() != null)
+ {
+ ap.getCalculationDialog().validateCalcTypes();
+ }
PaintRefresher.Refresh(this, av.getSequenceSetId());
return true;
}
+
+ /**
+ *
+ * @return null or last search results handled by this panel
+ */
+ public SearchResultsI getLastSearchResults()
+ {
+ return lastSearchResults;
+ }
}