import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.ListIterator;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
* @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())
{
int y = evt.getY();
y -= hgap;
- x -= seqCanvas.LABEL_WEST;
+ x = Math.max(0, x - seqCanvas.LABEL_WEST);
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
endEditing();
if (av.getWrapAlignment())
{
- ap.scrollToWrappedVisible(seqCanvas.cursorX);
+ av.getRanges().scrollToWrappedVisible(seqCanvas.cursorX);
}
else
{
- while (seqCanvas.cursorY < av.getRanges().getStartSeq())
- {
- ap.scrollUp(true);
- }
- while (seqCanvas.cursorY > av.getRanges().getEndSeq())
- {
- ap.scrollUp(false);
- }
- if (!av.getWrapAlignment())
- {
- HiddenColumns hidden = av.getAlignment().getHiddenColumns();
- while (seqCanvas.cursorX < hidden.adjustForHiddenColumns(av
- .getRanges().getStartRes()))
- {
- if (!ap.scrollRight(false))
- {
- break;
- }
- }
- while (seqCanvas.cursorX > hidden.adjustForHiddenColumns(av
- .getRanges().getEndRes()))
- {
- if (!ap.scrollRight(true))
- {
- break;
- }
- }
- }
+ av.getRanges().scrollToVisible(seqCanvas.cursorX, seqCanvas.cursorY);
}
setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
seqCanvas.cursorX, seqCanvas.cursorY);
@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)
{
if (av.isFollowHighlight())
{
- /*
- * if scrollToPosition requires a scroll adjustment, this flag prevents
- * another scroll event being propagated back to the originator
- *
- * @see AlignmentPanel#adjustmentValueChanged
- */
- ap.setDontScrollComplement(true);
+ // 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.setToScrollComplementPanel(false);
if (ap.scrollToPosition(results, false))
{
seqCanvas.revalidate();
}
+ ap.setToScrollComplementPanel(true);
}
setStatusMessage(results);
seqCanvas.highlightSearchResults(results);
mouseDragged(evt);
}
- int res = findRes(evt);
+ final int column = findColumn(evt);
int seq = findSeq(evt);
- int pos;
- if (res < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
+ if (column < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
{
return;
}
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,
+ .findFeaturesAtRes(sequence.getDatasetSequence(), pos);
+ if (isGapped)
+ {
+ removeAdjacentFeatures(features, column + 1, sequence);
+ }
+ seqARep.appendFeatures(tooltipText, pos, features,
this.ap.getSeqPanel().seqCanvas.fr.getMinMax());
}
if (tooltipText.length() == 6) // <html>
}
+ /**
+ * Removes from the list of features any that start after, or end before, the
+ * given column position. This allows us to retain only those features
+ * adjacent to a gapped position that straddle the position. Contact features
+ * that 'straddle' the position are also removed, since they are not 'at' the
+ * position.
+ *
+ * @param features
+ * @param column
+ * alignment column (1..)
+ * @param sequence
+ */
+ protected void removeAdjacentFeatures(List<SequenceFeature> features,
+ final int column, SequenceI sequence)
+ {
+ // TODO should this be an AlignViewController method (and reused by applet)?
+ ListIterator<SequenceFeature> it = features.listIterator();
+ while (it.hasNext())
+ {
+ SequenceFeature sf = it.next();
+ if (sf.isContactFeature()
+ || sequence.findIndex(sf.getBegin()) > column
+ || sequence.findIndex(sf.getEnd()) < column)
+ {
+ it.remove();
+ }
+ }
+ }
+
private Point lastp = null;
/*
// 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
* 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, int res, int seq)
+ int setStatusMessage(SequenceI sequence, final int column, int seq)
{
StringBuilder text = new StringBuilder(32);
.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())
+ final String displayChar = String.valueOf(sequence.getCharAt(column));
+ boolean isGapped = Comparison.isGap(sequence.getCharAt(column));
+ int pos = sequence.findPosition(column);
+
+ if (!isGapped)
{
- residue = ResidueProperties.nucleotideName.get(displayChar);
- if (residue != null)
+ boolean nucleotide = av.getAlignment().isNucleotide();
+ 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(")");
}
ap.alignFrame.statusBar.setText(text.toString());
+
return pos;
}
}
/**
- * 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()))
{
+ /*
+ * 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);
+ 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)
{
av.setSelectionGroup(null);
}
+ int column = findColumn(evt);
+ boolean isGapped = Comparison.isGap(sequence.getCharAt(column));
+
+ /*
+ * 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)));
+ sequence.findPosition(column));
+ if (isGapped)
+ {
+ removeAdjacentFeatures(features, column, sequence);
+ }
if (!features.isEmpty())
{
*/
List<SequenceI> seqs = Collections.singletonList(sequence);
seqCanvas.getFeatureRenderer().amendFeatures(seqs, features, false,
- ap, null);
+ ap);
seqCanvas.highlightSearchResults(null);
}
}
{
if (e.isShiftDown())
{
- ap.scrollRight(true);
+ av.getRanges().scrollRight(true);
}
- else
+ else if (!av.getWrapAlignment())
{
- ap.scrollUp(false);
+ av.getRanges().scrollUp(false);
}
}
else
{
if (e.isShiftDown())
{
- ap.scrollRight(false);
+ av.getRanges().scrollRight(false);
}
- else
+ else if (!av.getWrapAlignment())
{
- ap.scrollUp(true);
+ av.getRanges().scrollUp(true);
}
}
// TODO Update tooltip for new position.
*/
public void doMousePressedDefineMode(MouseEvent evt)
{
- final int res = findRes(evt);
+ final int res = findColumn(evt);
final int seq = findSeq(evt);
oldSeq = seq;
needOverviewUpdate = false;
if (av.cursorMode)
{
- seqCanvas.cursorX = findRes(evt);
+ seqCanvas.cursorX = findColumn(evt);
seqCanvas.cursorY = findSeq(evt);
seqCanvas.repaint();
return;
*/
void showPopupMenu(MouseEvent evt)
{
- final int res = findRes(evt);
+ final int res = 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>();
+ List<String> links = new ArrayList<>();
for (SequenceFeature sf : allFeatures)
{
if (sf.links != null)
}
/**
- * 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)
{
// 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();
+ needOverviewUpdate |= vischange && av.isSelectionDefinedGroup()
+ && afterDrag;
if (stretchGroup.cs != null)
{
stretchGroup.cs.alignmentChanged(stretchGroup,
*/
public void doMouseDraggedDefineMode(MouseEvent evt)
{
- int res = findRes(evt);
+ int res = findColumn(evt);
int y = findSeq(evt);
if (wrappedBlock != startWrapBlock)
scrollThread.setEvent(evt);
}
- seqCanvas.repaint();
+ // seqCanvas.repaint();
}
void scrollCanvas(MouseEvent evt)
if (mouseDragging && (evt.getY() < 0)
&& (av.getRanges().getStartSeq() > 0))
{
- running = ap.scrollUp(true);
+ running = av.getRanges().scrollUp(true);
}
if (mouseDragging && (evt.getY() >= getHeight())
&& (av.getAlignment().getHeight() > av.getRanges()
.getEndSeq()))
{
- running = ap.scrollUp(false);
+ running = av.getRanges().scrollUp(false);
}
if (mouseDragging && (evt.getX() < 0))
{
- running = ap.scrollRight(false);
+ running = av.getRanges().scrollRight(false);
}
else if (mouseDragging && (evt.getX() >= getWidth()))
{
- running = ap.scrollRight(true);
+ running = av.getRanges().scrollRight(true);
}
}
ap.getCalculationDialog().validateCalcTypes();
}
- // process further ?
- if (!av.followSelection)
- {
- return;
- }
+ return;
+ }
+
+ // process further ?
+ if (!av.followSelection)
+ {
+ return;
}
/*
if (copycolsel
&& av.hasHiddenColumns()
- && (av.getAlignment().getHiddenColumns() == null || av
- .getAlignment().getHiddenColumns().getHiddenRegions() == null))
+ && (av.getAlignment().getHiddenColumns() == null))
{
System.err.println("Bad things");
}