import jalview.api.AlignViewportI;
import jalview.bin.Cache;
+import jalview.bin.Console;
import jalview.commands.EditCommand;
import jalview.commands.EditCommand.Action;
import jalview.commands.EditCommand.Edit;
MousePos o = (MousePos) obj;
boolean b = (column == o.column && seqIndex == o.seqIndex
&& annotationIndex == o.annotationIndex);
- // System.out.println(obj + (b ? "= " : "!= ") + this);
+ // jalview.bin.Console.outPrintln(obj + (b ? "= " : "!= ") + this);
return b;
}
}
}
+ /**
+ * Rotation threshold for up/down trackpad/wheelmouse movement to register for
+ * an alignment in wrapped format. Up/down scrolling here results in a large
+ * jump so a larger threshold is appropriate, and reduces unintended up/down
+ * jumps when panning left/right. Should be at least 0.1 and less than 1.0
+ * since some platforms only send a value of 1.0.
+ */
+ private static double wrappedVerticalScrollRotationThreshold;
+
+ /**
+ * Property name if user needs to change rotation threshold
+ */
+ private static String WRAPPEDVERTICALSCROLLROTATIONTHRESHOLDPC = "WRAPPEDVERTICALSCROLLROTATIONTHRESHOLDPC";
+
+ /**
+ * Time threshold since last left/right trackpad/wheelmouse scroll for up/down
+ * trackpad/wheelmouse movement to register for an alignment in wrapped
+ * format. This reduces unintended up/down jumps when panning left/right. In
+ * ms.
+ */
+ private static int wrappedVerticalScrollChangeTimeThreshold;
+
+ /**
+ * Property name if user needs to change rotation threshold
+ */
+ private static String WRAPPEDVERTICALSCROLLCHANGETIMETHRESHOLD = "WRAPPEDVERTICALSCROLLCHANGETIMETHRESHOLD ";
+
+ static
+ {
+ wrappedVerticalScrollRotationThreshold = Cache.getDefault(
+ WRAPPEDVERTICALSCROLLROTATIONTHRESHOLDPC, 50) / 100.0;
+ wrappedVerticalScrollChangeTimeThreshold = Cache
+ .getDefault(WRAPPEDVERTICALSCROLLCHANGETIMETHRESHOLD, 200);
+ }
+
private static final int MAX_TOOLTIP_LENGTH = 300;
public SeqCanvas seqCanvas;
ToolTipManager.sharedInstance().registerComponent(this);
ToolTipManager.sharedInstance().setInitialDelay(0);
ToolTipManager.sharedInstance().setDismissDelay(10000);
-
-
+
this.av = viewport;
setBackground(Color.white);
return new MousePos(col, seqIndex, annIndex);
}
+
+ /**
+ * @param evt
+ * @return absolute column in alignment nearest to the mouse pointer
+ */
+ int findAlignmentColumn(MouseEvent evt)
+ {
+ return findNearestColumn(evt, true);
+ }
+
/**
* Returns the aligned sequence position (base 0) at the mouse position, or
* the closest visible one
*/
int findColumn(MouseEvent evt)
{
+ return findNearestColumn(evt, false);
+ }
+
+ /**
+ * @param nearestColumn
+ * when false returns negative values for out of bound positions - -1
+ * for scale left/right, <-1 if far to right
+ * @return nearest absolute column to mouse pointer
+ */
+ private int findNearestColumn(MouseEvent evt, boolean nearestColumn)
+ {
int res = 0;
int x = evt.getX();
if (x < 0)
{
// mouse is over left scale
- return -1;
+ if (!nearestColumn)
+ {
+ return -1;
+ }
+ else
+ {
+ x = 0;
+ }
}
int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
}
if (x >= cwidth * charWidth)
{
- // mouse is over right scale
- return -1;
+ if (!nearestColumn)
+ {
+ // mouse is over right scale
+ return -1;
+ }
+ else
+ {
+ x = cwidth * charWidth - 1;
+ }
}
wrappedBlock = y / cHeight;
* rather than right-hand gutter
*/
x = Math.min(x, seqCanvas.getX() + seqCanvas.getWidth());
+ if (nearestColumn)
+ {
+ x = Math.max(x, 0);
+ }
+
res = (x / charWidth) + startRes;
res = Math.min(res, av.getRanges().getEndRes());
+
}
if (av.hasHiddenColumns())
void moveCursor(int dx, int dy)
{
- moveCursor(dx, dy,false);
+ moveCursor(dx, dy, false);
}
+
void moveCursor(int dx, int dy, boolean nextWord)
{
HiddenColumns hidden = av.getAlignment().getHiddenColumns();
if (nextWord)
{
int maxWidth = av.getAlignment().getWidth();
- int maxHeight=av.getAlignment().getHeight();
- SequenceI seqAtRow = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
+ int maxHeight = av.getAlignment().getHeight();
+ SequenceI seqAtRow = av.getAlignment()
+ .getSequenceAt(seqCanvas.cursorY);
// look for next gap or residue
- boolean isGap = Comparison.isGap(seqAtRow.getCharAt(seqCanvas.cursorX));
- int p = seqCanvas.cursorX,lastP,r=seqCanvas.cursorY,lastR;
+ boolean isGap = Comparison
+ .isGap(seqAtRow.getCharAt(seqCanvas.cursorX));
+ int p = seqCanvas.cursorX, lastP, r = seqCanvas.cursorY, lastR;
do
{
lastP = p;
p = nextVisible(hidden, maxWidth, p, dx);
} while ((dx != 0 ? p != lastP : r != lastR)
&& isGap == Comparison.isGap(seqAtRow.getCharAt(p)));
- seqCanvas.cursorX=p;
- seqCanvas.cursorY=r;
- } else {
+ seqCanvas.cursorX = p;
+ seqCanvas.cursorY = r;
+ }
+ else
+ {
int maxWidth = av.getAlignment().getWidth();
- seqCanvas.cursorX = nextVisible(hidden, maxWidth, seqCanvas.cursorX, dx);
+ seqCanvas.cursorX = nextVisible(hidden, maxWidth, seqCanvas.cursorX,
+ dx);
seqCanvas.cursorY += dy;
}
scrollToVisible(false);
}
- private int nextVisible(HiddenColumns hidden,int maxWidth, int original, int dx)
+ private int nextVisible(HiddenColumns hidden, int maxWidth, int original,
+ int dx)
{
- int newCursorX=original+dx;
+ int newCursorX = original + dx;
if (av.hasHiddenColumns() && !hidden.isVisible(newCursorX))
{
int visx = hidden.absoluteToVisibleColumn(newCursorX - dx);
}
}
newCursorX = (newCursorX < 0) ? 0 : newCursorX;
- if (newCursorX >= maxWidth
- || !hidden.isVisible(newCursorX))
+ if (newCursorX >= maxWidth || !hidden.isVisible(newCursorX))
{
newCursorX = original;
}
return newCursorX;
}
+
/**
* Scroll to make the cursor visible in the viewport.
*
if (av.getAlignment().getHiddenColumns().isVisible(seqCanvas.cursorX))
{
setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
- seqCanvas.cursorX, seqCanvas.cursorY);
+ seqCanvas.cursorX, seqCanvas.cursorY);
}
if (repaintNeeded)
}
}
-
void setSelectionAreaAtCursor(boolean topLeft)
{
SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
if (lastMessage == null || !lastMessage.equals(tmp))
{
- // System.err.println("mouseOver Sequence: "+tmp);
+ // jalview.bin.Console.errPrintln("mouseOver Sequence: "+tmp);
ssm.mouseOverSequence(sequence, index, pos, av);
}
lastMessage = tmp;
SequenceI seq = match.getSequence();
SequenceI ds = seq.getDatasetSequence() == null ? seq
: seq.getDatasetSequence();
- MappedFeatures mf = fr2
- .findComplementFeaturesAtResidue(ds, pos);
+ MappedFeatures mf = fr2.findComplementFeaturesAtResidue(ds, pos);
if (mf != null)
{
for (SequenceFeature sf : mf.features)
@Override
public void updateColours(SequenceI seq, int index)
{
- System.out.println("update the seqPanel colours");
+ jalview.bin.Console.outPrintln("update the seqPanel colours");
// repaint();
}
{
List<SequenceFeature> features = ap.getFeatureRenderer()
.findFeaturesAtColumn(sequence, column + 1);
- unshownFeatures = seqARep.appendFeatures(tooltipText, pos,
- features, this.ap.getSeqPanel().seqCanvas.fr,
- MAX_TOOLTIP_LENGTH);
+ unshownFeatures = seqARep.appendFeatures(tooltipText, pos, features,
+ this.ap.getSeqPanel().seqCanvas.fr, MAX_TOOLTIP_LENGTH);
/*
* add features in CDS/protein complement at the corresponding
pos);
if (mf != null)
{
- unshownFeatures += seqARep.appendFeatures(tooltipText,
- pos, mf, fr2, MAX_TOOLTIP_LENGTH);
+ unshownFeatures += seqARep.appendFeatures(tooltipText, pos, mf,
+ fr2, MAX_TOOLTIP_LENGTH);
}
}
}
if (!textString.equals(lastTooltip))
{
lastTooltip = textString;
- lastFormattedTooltip = JvSwingUtils.wrapTooltip(true,
- textString);
+ lastFormattedTooltip = JvSwingUtils.wrapTooltip(true, textString);
setToolTipText(lastFormattedTooltip);
}
}
final int column = pos.column;
final int rowIndex = pos.annotationIndex;
+ // TODO - get yOffset for annotation, too
if (column < 0 || !av.getWrapAlignment() || !av.isShowAnnotation()
|| rowIndex < 0)
{
AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation();
String tooltip = AnnotationPanel.buildToolTip(anns[rowIndex], column,
- anns);
- if (!tooltip.equals(lastTooltip))
+ anns, 0, av, ap);
+ if (tooltip == null ? tooltip != lastTooltip
+ : !tooltip.equals(lastTooltip))
{
lastTooltip = tooltip;
lastFormattedTooltip = tooltip == null ? null
}
String msg = AnnotationPanel.getStatusMessage(av.getAlignment(), column,
- anns[rowIndex]);
+ anns[rowIndex], 0, av);
ap.alignFrame.setStatus(msg);
}
tempTip.setTipText(lastFormattedTooltip);
int tipWidth = (int) tempTip.getPreferredSize().getWidth();
-
- // was x += (w - x < 200) ? -(w / 2) : 5;
+
+ // was x += (w - x < 200) ? -(w / 2) : 5;
x = (x + tipWidth < w ? x + 10 : w - tipWidth);
Point p = new Point(x, y + av.getCharHeight()); // BH 2018 was - 20?
* changed, so selective redraws can be applied (ie. only structures, only
* overview, etc)
*/
- private boolean updateOverviewAndStructs = 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
* Sequence number (if known), and sequence name.
*/
String seqno = seqIndex == -1 ? "" : " " + (seqIndex + 1);
- text.append("Sequence").append(seqno).append(" ID: ")
- .append(seqName);
+ text.append("Sequence").append(seqno).append(" ID: ").append(seqName);
String residue = null;
String label = null;
if (groupEditing)
{
- message.append("Edit group:");
+ message.append("Edit group:");
label = MessageManager.getString("action.edit_group");
}
else
{
- message.append("Edit sequence: " + seq.getName());
+ message.append("Edit sequence: " + seq.getName());
label = seq.getName();
if (label.length() > 10)
{
{
for (int j = 0; j < startres - editLastRes; j++)
{
- if (!Comparison
- .isGap(groupSeqs[g].getCharAt(fixedRight - j)))
+ if (!Comparison.isGap(groupSeqs[g].getCharAt(fixedRight - j)))
{
blank = false;
break;
* 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());
+ highlight.addResult(sequence, features.get(0).getBegin(),
+ features.get(0).getEnd());
seqCanvas.highlightSearchResults(highlight, true);
/*
}
}
+ /**
+ * recorded time of last left/right mousewheel/trackpad scroll in wrapped mode
+ */
+ private long lastLeftRightWrappedScrollTime = 0;
+
+ /**
+ * Responds to a mouse wheel movement by scrolling the alignment
+ * <ul>
+ * <li>left or right, if the shift key is down, else up or down</li>
+ * <li>right (or down) if the reported mouse movement is positive</li>
+ * <li>left (or up) if the reported mouse movement is negative</li>
+ * </ul>
+ * Note that this method may also be fired by scrolling with a gesture on a
+ * trackpad.
+ */
@Override
public void mouseWheelMoved(MouseWheelEvent e)
{
e.consume();
- double wheelRotation = e.getPreciseWheelRotation();
+ double preciseWheelRotation = e.getPreciseWheelRotation();
+ int wheelRotation = e.getWheelRotation();
+ if (wheelRotation == 0 && Math.abs(preciseWheelRotation) > 0.1)
+ {
+ // this is one of -1, 0 ,+1 for <0, ==0, >0
+ wheelRotation = (int) Math.signum(preciseWheelRotation);
+ }
+
+ /*
+ * scroll more for large (fast) mouse movements
+ */
+ int size = Math.abs(wheelRotation);
+
if (wheelRotation > 0)
{
if (e.isShiftDown())
{
- av.getRanges().scrollRight(true);
-
+ /*
+ * scroll right
+ * stop trying to scroll right when limit is reached (saves
+ * expensive calls to Alignment.getWidth())
+ */
+ if (!ap.isScrolledFullyRight())
+ {
+ av.getRanges().scrollRight(true, size);
+ }
+ this.lastLeftRightWrappedScrollTime = System.currentTimeMillis();
}
else
{
- av.getRanges().scrollUp(false);
+ /*
+ * scroll down
+ */
+ // apply a more definite threshold for up and down scrolling in wrap
+ // format (either not a wrapped alignment, or BOTH time since last
+ // left/right scroll is above threshold AND trackpad/mousewheel movement
+ // is above threshold)
+ if (!ap.getAlignViewport().getWrapAlignment() || (Math.abs(
+ preciseWheelRotation) > wrappedVerticalScrollRotationThreshold
+ && System.currentTimeMillis()
+ - lastLeftRightWrappedScrollTime > wrappedVerticalScrollChangeTimeThreshold))
+ {
+ while (size-- > 0)
+ {
+ if (!av.getRanges().scrollUp(false))
+ {
+ break;
+ }
+ }
+ }
}
}
else if (wheelRotation < 0)
{
if (e.isShiftDown())
{
- av.getRanges().scrollRight(false);
+ /*
+ * scroll left if not already at start
+ */
+ if (av.getRanges().getStartRes() > 0)
+ {
+ av.getRanges().scrollRight(false, size);
+ }
+ this.lastLeftRightWrappedScrollTime = System.currentTimeMillis();
}
else
{
- av.getRanges().scrollUp(true);
+ /*
+ * scroll up
+ */
+ // apply a more definite threshold for up and down scrolling in wrap
+ // format (either not a wrapped alignment, or BOTH time since last
+ // left/right scroll is above threshold AND trackpad/mousewheel movement
+ // is above threshold)
+ if (!ap.getAlignViewport().getWrapAlignment() || (Math.abs(
+ preciseWheelRotation) > wrappedVerticalScrollRotationThreshold
+ && System.currentTimeMillis()
+ - lastLeftRightWrappedScrollTime > wrappedVerticalScrollChangeTimeThreshold))
+ {
+ while (size-- > 0)
+ {
+ if (!av.getRanges().scrollUp(true))
+ {
+ break;
+ }
+ }
+ }
}
}
return;
}
- res = Math.min(res, av.getAlignment().getWidth()-1);
+ res = Math.min(res, av.getAlignment().getWidth() - 1);
if (stretchGroup.getEndRes() == res)
{
{
if (av.getAlignment() == null)
{
- Cache.warn("alignviewport av SeqSetId=" + av.getSequenceSetId()
+ Console.warn("alignviewport av SeqSetId=" + av.getSequenceSetId()
+ " ViewId=" + av.getViewId()
+ " 's alignment is NULL! returning immediately.");
return;
if (copycolsel && av.hasHiddenColumns()
&& (av.getAlignment().getHiddenColumns() == null))
{
- System.err.println("Bad things");
+ jalview.bin.Console.errPrintln("Bad things");
}
if (repaint) // always true!
{
* if hidden column selection has changed
*/
ap.paintAlignment(hiddenChanged, hiddenChanged);
+ // propagate any selection changes
+ PaintRefresher.Refresh(ap, av.getSequenceSetId());
return true;
}