/*
* Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
* Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
* Jalview is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* Jalview is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jalview. If not, see
* Mouse position is not set if in wrapped mode with the cursor either between * sequences, or over the left or right vertical scale. * * @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 { ViewportRanges ranges = av.getRanges(); seqIndex = Math.min((y / charHeight) + ranges.getStartSeq(), alignmentHeight - 1); seqIndex = Math.min(seqIndex, ranges.getEndSeq()); } return new MousePos(col, seqIndex, annIndex); } /** * Returns the aligned sequence position (base 0) at the mouse position, or * the closest visible one *
* Returns -1 if in wrapped mode with the mouse over either left or right * vertical scale. * * @param evt * @return */ int findColumn(MouseEvent evt) { int res = 0; int x = evt.getX(); final int startRes = av.getRanges().getStartRes(); final int charWidth = av.getCharWidth(); if (av.getWrapAlignment()) { int hgap = av.getCharHeight(); if (av.getScaleAboveWrapped()) { hgap += av.getCharHeight(); } int cHeight = av.getAlignment().getHeight() * av.getCharHeight() + hgap + seqCanvas.getAnnotationHeight(); int y = evt.getY(); y = Math.max(0, y - hgap); 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 / charWidth); } else { /* * 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()) { res = av.getAlignment().getHiddenColumns() .visibleToAbsoluteColumn(res); } return res; } /** * When all of a sequence of edits are complete, put the resulting edit list * on the history stack (undo list), and reset flags for editing in progress. */ void endEditing() { try { if (editCommand != null && editCommand.getSize() > 0) { ap.alignFrame.addHistoryItem(editCommand); av.firePropertyChange("alignment", null, av.getAlignment().getSequences()); } } finally { /* * Tidy up come what may... */ editStartSeq = -1; editLastRes = -1; editingSeqs = false; groupEditing = false; keyboardNo1 = null; keyboardNo2 = null; editCommand = null; } } void setCursorRow() { seqCanvas.cursorY = getKeyboardNo1() - 1; scrollToVisible(true); } void setCursorColumn() { seqCanvas.cursorX = getKeyboardNo1() - 1; scrollToVisible(true); } void setCursorRowAndColumn() { if (keyboardNo2 == null) { keyboardNo2 = new StringBuffer(); } else { seqCanvas.cursorX = getKeyboardNo1() - 1; seqCanvas.cursorY = getKeyboardNo2() - 1; scrollToVisible(true); } } void setCursorPosition() { SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY); seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1; scrollToVisible(true); } void moveCursor(int dx, int dy) { seqCanvas.cursorX += dx; seqCanvas.cursorY += dy; HiddenColumns hidden = av.getAlignment().getHiddenColumns(); if (av.hasHiddenColumns() && !hidden.isVisible(seqCanvas.cursorX)) { int original = seqCanvas.cursorX - dx; int maxWidth = av.getAlignment().getWidth(); if (!hidden.isVisible(seqCanvas.cursorX)) { 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 || !hidden.isVisible(seqCanvas.cursorX)) { seqCanvas.cursorX = original; } } scrollToVisible(false); } /** * 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) { seqCanvas.cursorX = 0; } else if (seqCanvas.cursorX > av.getAlignment().getWidth() - 1) { seqCanvas.cursorX = av.getAlignment().getWidth() - 1; } if (seqCanvas.cursorY < 0) { seqCanvas.cursorY = 0; } else if (seqCanvas.cursorY > av.getAlignment().getHeight() - 1) { seqCanvas.cursorY = av.getAlignment().getHeight() - 1; } endEditing(); boolean repaintNeeded = true; if (jump) { // 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 { if (av.getWrapAlignment()) { // scrollToWrappedVisible expects x-value to have hidden cols subtracted int x = av.getAlignment().getHiddenColumns() .absoluteToVisibleColumn(seqCanvas.cursorX); av.getRanges().scrollToWrappedVisible(x); } else { av.getRanges().scrollToVisible(seqCanvas.cursorX, seqCanvas.cursorY); } } if (av.getAlignment().getHiddenColumns().isVisible(seqCanvas.cursorX)) { setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY), seqCanvas.cursorX, seqCanvas.cursorY); } if (repaintNeeded) { seqCanvas.repaint(); } } void setSelectionAreaAtCursor(boolean topLeft) { SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY); if (av.getSelectionGroup() != null) { SequenceGroup sg = av.getSelectionGroup(); // Find the top and bottom of this group int min = av.getAlignment().getHeight(), max = 0; for (int i = 0; i < sg.getSize(); i++) { int index = av.getAlignment().findIndex(sg.getSequenceAt(i)); if (index > max) { max = index; } if (index < min) { min = index; } } max++; if (topLeft) { sg.setStartRes(seqCanvas.cursorX); if (sg.getEndRes() < seqCanvas.cursorX) { sg.setEndRes(seqCanvas.cursorX); } min = seqCanvas.cursorY; } else { sg.setEndRes(seqCanvas.cursorX); if (sg.getStartRes() > seqCanvas.cursorX) { sg.setStartRes(seqCanvas.cursorX); } max = seqCanvas.cursorY + 1; } if (min > max) { // Only the user can do this av.setSelectionGroup(null); } else { // Now add any sequences between min and max sg.getSequences(null).clear(); for (int i = min; i < max; i++) { sg.addSequence(av.getAlignment().getSequenceAt(i), false); } } } if (av.getSelectionGroup() == null) { SequenceGroup sg = new SequenceGroup(); sg.setStartRes(seqCanvas.cursorX); sg.setEndRes(seqCanvas.cursorX); sg.addSequence(sequence, false); av.setSelectionGroup(sg); } ap.paintAlignment(false, false); av.sendSelection(); } void insertGapAtCursor(boolean group) { groupEditing = group; editStartSeq = seqCanvas.cursorY; editLastRes = seqCanvas.cursorX; editSequence(true, false, seqCanvas.cursorX + getKeyboardNo1()); endEditing(); } void deleteGapAtCursor(boolean group) { groupEditing = group; editStartSeq = seqCanvas.cursorY; editLastRes = seqCanvas.cursorX + getKeyboardNo1(); editSequence(false, false, seqCanvas.cursorX); endEditing(); } void insertNucAtCursor(boolean group, String nuc) { // TODO not called - delete? groupEditing = group; editStartSeq = seqCanvas.cursorY; editLastRes = seqCanvas.cursorX; editSequence(false, true, seqCanvas.cursorX + getKeyboardNo1()); endEditing(); } void numberPressed(char value) { if (keyboardNo1 == null) { keyboardNo1 = new StringBuffer(); } if (keyboardNo2 != null) { keyboardNo2.append(value); } else { keyboardNo1.append(value); } } int getKeyboardNo1() { try { if (keyboardNo1 != null) { int value = Integer.parseInt(keyboardNo1.toString()); keyboardNo1 = null; return value; } } catch (Exception x) { } keyboardNo1 = null; return 1; } int getKeyboardNo2() { try { if (keyboardNo2 != null) { int value = Integer.parseInt(keyboardNo2.toString()); keyboardNo2 = null; return value; } } catch (Exception x) { } keyboardNo2 = null; return 1; } /** * DOCUMENT ME! * * @param evt * DOCUMENT ME! */ @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, pos); evt.consume(); return; } if (editingSeqs) { endEditing(); } else { doMouseReleasedDefineMode(evt, didDrag); } } /** * DOCUMENT ME! * * @param evt * DOCUMENT ME! */ @Override 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)) { mouseWheelPressed = true; return; } boolean isControlDown = Platform.isControlDown(evt); if (evt.isShiftDown() || isControlDown) { editingSeqs = true; if (isControlDown) { groupEditing = true; } } else { doMousePressedDefineMode(evt, pos); return; } int seq = pos.seqIndex; int res = pos.column; if ((seq < av.getAlignment().getHeight()) && (res < av.getAlignment().getSequenceAt(seq).getLength())) { editStartSeq = seq; editLastRes = res; } else { editStartSeq = -1; editLastRes = -1; } return; } String lastMessage; @Override public void mouseOverSequence(SequenceI sequence, int index, int pos) { String tmp = sequence.hashCode() + " " + index + " " + pos; if (lastMessage == null || !lastMessage.equals(tmp)) { // System.err.println("mouseOver Sequence: "+tmp); ssm.mouseOverSequence(sequence, index, pos, av); } lastMessage = tmp; } /** * Highlight the mapped region described by the search results object (unless * unchanged). This supports highlight of protein while mousing over linked * cDNA and vice versa. The status bar is also updated to show the location of * the start of the highlighted region. */ @Override public String highlightSequence(SearchResultsI results) { if (results == null || results.equals(lastSearchResults)) { return null; } 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.setToScrollComplementPanel(false); wasScrolled = ap.scrollToPosition(results); if (wasScrolled) { seqCanvas.revalidate(); } ap.setToScrollComplementPanel(true); } boolean fastPaint = !(wasScrolled && av.getWrapAlignment()); if (seqCanvas.highlightSearchResults(results, fastPaint)) { setStatusMessage(results); } return results.isEmpty() ? null : getHighlightInfo(results); } /** * temporary hack: answers a message suitable to show on structure hover * label. This is normally null. It is a peptide variation description if *
* 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) ** * @param seqName * @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) */ protected void setStatusMessage(String seqName, int seqIndex, char sequenceChar, int residuePos) { StringBuilder text = new StringBuilder(32); /* * Sequence number (if known), and sequence name. */ String seqno = seqIndex == -1 ? "" : " " + (seqIndex + 1); text.append("Sequence").append(seqno).append(" ID: ") .append(seqName); String residue = null; /* * Try to translate the display character to residue name (null for gap). */ boolean isGapped = Comparison.isGap(sequenceChar); if (!isGapped) { boolean nucleotide = av.getAlignment().isNucleotide(); String displayChar = String.valueOf(sequenceChar); if (nucleotide) { residue = ResidueProperties.nucleotideName.get(displayChar); } else { residue = "X".equalsIgnoreCase(displayChar) ? "X" : ("*".equals(displayChar) ? "STOP" : ResidueProperties.aa2Triplet.get(displayChar)); } text.append(" ").append(nucleotide ? "Nucleotide" : "Residue") .append(": ").append(residue == null ? displayChar : residue); text.append(" (").append(Integer.toString(residuePos)).append(")"); } ap.alignFrame.setStatus(text.toString()); } /** * Set the status bar message to highlight the first matched position in * search results. * * @param results */ private void setStatusMessage(SearchResultsI results) { AlignmentI al = this.av.getAlignment(); int sequenceIndex = al.findIndex(results); if (sequenceIndex == -1) { return; } SequenceI alignedSeq = al.getSequenceAt(sequenceIndex); SequenceI ds = alignedSeq.getDatasetSequence(); for (SearchResultMatchI m : results.getResults()) { SequenceI seq = m.getSequence(); if (seq.getDatasetSequence() != null) { seq = seq.getDatasetSequence(); } if (seq == ds) { int start = m.getStart(); setStatusMessage(alignedSeq.getName(), sequenceIndex, seq.getCharAt(start - 1), start); return; } } } /** * {@inheritDoc} */ @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; 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++; } if (fontSize < 1) { fontSize = 1; } 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) { newWidth = av.getCharWidth() - 1; av.setCharWidth(newWidth); } else if (evt.getX() > lastMousePress.getX()) { 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(); } } } FontMetrics fm = getFontMetrics(av.getFont()); av.validCharWidth = fm.charWidth('M') <= av.getCharWidth(); lastMousePress = evt.getPoint(); return; } if (!editingSeqs) { dragStretchGroup(evt); return; } int res = pos.column; if (res < 0) { res = 0; } if ((editLastRes == -1) || (editLastRes == res)) { return; } if ((res < av.getAlignment().getWidth()) && (res < editLastRes)) { // dragLeft, delete gap editSequence(false, false, res); } else { editSequence(true, false, res); } mouseDragging = true; if (scrollThread != null) { scrollThread.setMousePosition(evt.getPoint()); } } /** * Edits the sequence to insert or delete one or more gaps, in response to a * mouse drag or cursor mode command. The number of inserts/deletes may be * specified with the cursor command, or else depends on the mouse event * (normally one column, but potentially more for a fast mouse drag). *
* Delete gaps is limited to the number of gaps left of the cursor position * (mouse drag), or at or right of the cursor position (cursor mode). *
* In group editing mode (Ctrl or Cmd down), the edit acts on all sequences in * the current selection group. *
* In locked editing mode (with a selection group present), inserts/deletions
* within the selection group are limited to its boundaries (and edits outside
* the group stop at its border).
*
* @param insertGap
* true to insert gaps, false to delete gaps
* @param editSeq
* (unused parameter)
* @param startres
* the column at which to perform the action; the number of columns
* affected depends on this.editLastRes
(cursor column
* position)
*/
synchronized void editSequence(boolean insertGap, boolean editSeq,
final int startres)
{
int fixedLeft = -1;
int fixedRight = -1;
boolean fixedColumns = false;
SequenceGroup sg = av.getSelectionGroup();
final SequenceI seq = av.getAlignment().getSequenceAt(editStartSeq);
// No group, but the sequence may represent a group
if (!groupEditing && av.hasHiddenRows())
{
if (av.isHiddenRepSequence(seq))
{
sg = av.getRepresentedSequences(seq);
groupEditing = true;
}
}
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)
{
label = label.substring(0, 10);
}
label = MessageManager.formatMessage("label.edit_params",
new String[]
{ label });
}
/*
* initialise the edit command if there is not
* already one being extended
*/
if (editCommand == null)
{
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
* (but the edit may be inside or outside the selection group)
*/
boolean inSelectionGroup = sg != null
&& sg.getSequences(av.getHiddenRepSequences()).contains(seq);
if (groupEditing || inSelectionGroup)
{
fixedColumns = true;
// sg might be null as the user may only see 1 sequence,
// but the sequence represents a group
if (sg == null)
{
if (!av.isHiddenRepSequence(seq))
{
endEditing();
return;
}
sg = av.getRepresentedSequences(seq);
}
fixedLeft = sg.getStartRes();
fixedRight = sg.getEndRes();
if ((startres < fixedLeft && editLastRes >= fixedLeft)
|| (startres >= fixedLeft && editLastRes < fixedLeft)
|| (startres > fixedRight && editLastRes <= fixedRight)
|| (startres <= fixedRight && editLastRes > fixedRight))
{
endEditing();
return;
}
if (fixedLeft > startres)
{
fixedRight = fixedLeft - 1;
fixedLeft = 0;
}
else if (fixedRight < startres)
{
fixedLeft = fixedRight;
fixedRight = -1;
}
}
if (av.hasHiddenColumns())
{
fixedColumns = true;
int y1 = av.getAlignment().getHiddenColumns()
.getNextHiddenBoundary(true, startres);
int y2 = av.getAlignment().getHiddenColumns()
.getNextHiddenBoundary(false, startres);
if ((insertGap && startres > y1 && editLastRes < y1)
|| (!insertGap && startres < y2 && editLastRes > y2))
{
endEditing();
return;
}
// System.out.print(y1+" "+y2+" "+fixedLeft+" "+fixedRight+"~~");
// Selection spans a hidden region
if (fixedLeft < y1 && (fixedRight > y2 || fixedRight == -1))
{
if (startres >= y2)
{
fixedLeft = y2;
}
else
{
fixedRight = y2 - 1;
}
}
}
boolean success = doEditSequence(insertGap, editSeq, startres,
fixedRight, fixedColumns, sg);
/*
* report what actually happened (might be less than
* what was requested), by inspecting the edit commands added
*/
String msg = getEditStatusMessage(editCommand);
ap.alignFrame.setStatus(msg == null ? " " : msg);
if (!success)
{
endEditing();
}
editLastRes = startres;
seqCanvas.repaint();
}
/**
* A helper method that performs the requested editing to insert or delete
* gaps (if possible). Answers true if the edit was successful, false if could
* only be performed in part or not at all. Failure may occur in 'locked edit'
* mode, when an insertion requires a matching gapped position (or column) to
* delete, and deletion requires an adjacent gapped position (or column) to
* remove.
*
* @param insertGap
* true if inserting gap(s), false if deleting
* @param editSeq
* (unused parameter, currently always false)
* @param startres
* the column at which to perform the edit
* @param fixedRight
* fixed right boundary column of a locked edit (within or to the
* left of a selection group)
* @param fixedColumns
* true if this is a locked edit
* @param sg
* the sequence group (if group edit is being performed)
* @return
*/
protected boolean doEditSequence(final boolean insertGap,
final boolean editSeq, final int startres, int fixedRight,
final boolean fixedColumns, final SequenceGroup sg)
{
final SequenceI seq = av.getAlignment().getSequenceAt(editStartSeq);
SequenceI[] seqs = new SequenceI[] { seq };
if (groupEditing)
{
List
*
* Answers true if a scroll was performed, false if not - meaning either
* that the mouse position is within the panel, or the edge of the alignment
* has been reached.
*/
boolean scrollOnce()
{
/*
* quit after mouseUp ensures interrupt in JalviewJS
*/
if (!mouseDragging)
{
return false;
}
boolean scrolled = false;
ViewportRanges ranges = SeqPanel.this.av.getRanges();
/*
* scroll up or down
*/
if (mousePos.y < 0)
{
// mouse is above this panel - try scroll up
scrolled = ranges.scrollUp(true);
}
else if (mousePos.y >= getHeight())
{
// mouse is below this panel - try scroll down
scrolled = ranges.scrollUp(false);
}
/*
* scroll left or right
*/
if (mousePos.x < 0)
{
scrolled |= ranges.scrollRight(false);
}
else if (mousePos.x >= getWidth())
{
scrolled |= ranges.scrollRight(true);
}
return scrolled;
}
}
/**
* modify current selection according to a received message.
*/
@Override
public void selection(SequenceGroup seqsel, ColumnSelection colsel,
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)
{
// respond to our own event by updating dependent dialogs
if (ap.getCalculationDialog() != null)
{
ap.getCalculationDialog().validateCalcTypes();
}
return;
}
// process further ?
if (!av.followSelection)
{
return;
}
/*
* Ignore the selection if there is one of our own pending.
*/
if (av.isSelectionGroupChanged(false) || av.isColSelChanged(false))
{
return;
}
/*
* Check for selection in a view of which this one is a dna/protein
* complement.
*/
if (selectionFromTranslation(seqsel, colsel, hidden, source))
{
return;
}
// do we want to thread this ? (contention with seqsel and colsel locks, I
// suspect)
/*
* only copy colsel if there is a real intersection between
* sequence selection and this panel's alignment
*/
boolean repaint = false;
boolean copycolsel = false;
SequenceGroup sgroup = null;
if (seqsel != null && seqsel.getSize() > 0)
{
if (av.getAlignment() == null)
{
Cache.log.warn("alignviewport av SeqSetId=" + av.getSequenceSetId()
+ " ViewId=" + av.getViewId()
+ " 's alignment is NULL! returning immediately.");
return;
}
sgroup = seqsel.intersect(av.getAlignment(),
(av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
if ((sgroup != null && sgroup.getSize() > 0))
{
copycolsel = true;
}
}
if (sgroup != null && sgroup.getSize() > 0)
{
av.setSelectionGroup(sgroup);
}
else
{
av.setSelectionGroup(null);
}
av.isSelectionGroupChanged(true);
repaint = true;
if (copycolsel)
{
// the current selection is unset or from a previous message
// so import the new colsel.
if (colsel == null || colsel.isEmpty())
{
if (av.getColumnSelection() != null)
{
av.getColumnSelection().clear();
repaint = true;
}
}
else
{
// TODO: shift colSel according to the intersecting sequences
if (av.getColumnSelection() == null)
{
av.setColumnSelection(new ColumnSelection(colsel));
}
else
{
av.getColumnSelection().setElementsFrom(colsel,
av.getAlignment().getHiddenColumns());
}
}
av.isColSelChanged(true);
repaint = true;
}
if (copycolsel && av.hasHiddenColumns()
&& (av.getAlignment().getHiddenColumns() == null))
{
System.err.println("Bad things");
}
if (repaint) // always true!
{
// probably finessing with multiple redraws here
PaintRefresher.Refresh(this, av.getSequenceSetId());
// ap.paintAlignment(false);
}
// lastly, update dependent dialogs
if (ap.getCalculationDialog() != null)
{
ap.getCalculationDialog().validateCalcTypes();
}
}
/**
* If this panel is a cdna/protein translation view of the selection source,
* tries to map the source selection to a local one, and returns true. Else
* returns false.
*
* @param seqsel
* @param colsel
* @param source
*/
protected boolean selectionFromTranslation(SequenceGroup seqsel,
ColumnSelection colsel, HiddenColumns hidden,
SelectionSource source)
{
if (!(source instanceof AlignViewportI))
{
return false;
}
final AlignViewportI sourceAv = (AlignViewportI) source;
if (sourceAv.getCodingComplement() != av
&& av.getCodingComplement() != sourceAv)
{
return false;
}
/*
* Map sequence selection
*/
SequenceGroup sg = MappingUtils.mapSequenceGroup(seqsel, sourceAv, av);
av.setSelectionGroup(sg != null && sg.getSize() > 0 ? sg : null);
av.isSelectionGroupChanged(true);
/*
* Map column selection
*/
// 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);
boolean hiddenChanged = av.getAlignment().setHiddenColumns(hs);
// lastly, update any dependent dialogs
if (ap.getCalculationDialog() != null)
{
ap.getCalculationDialog().validateCalcTypes();
}
/*
* repaint alignment, and also Overview or Structure
* if hidden column selection has changed
*/
ap.paintAlignment(hiddenChanged, hiddenChanged);
return true;
}
/**
*
* @return null or last search results handled by this panel
*/
public SearchResultsI getLastSearchResults()
{
return lastSearchResults;
}
}