import jalview.datamodel.SequenceI;
import jalview.renderer.ScaleRenderer;
import jalview.renderer.ScaleRenderer.ScaleMark;
+import jalview.util.Comparison;
import jalview.viewmodel.ViewportListenerI;
import jalview.viewmodel.ViewportRanges;
boolean fastPaint = false;
+ boolean fastpainting = false;
+
int labelWidthWest;
int labelWidthEast;
}
/**
- * DOCUMENT ME!
+ * Draw the scale to the left or right of a wrapped alignment
*
* @param g
- * DOCUMENT ME!
* @param startx
- * DOCUMENT ME!
+ * first column of wrapped width (0.. excluding any hidden columns)
* @param endx
- * DOCUMENT ME!
+ * last column of wrapped width (0.. excluding any hidden columns)
* @param ypos
- * DOCUMENT ME!
+ * vertical offset at which to begin the scale
+ * @param left
+ * if true, scale is left of residues, if false, scale is right
*/
- void drawWestScale(Graphics g, int startx, int endx, int ypos)
+ void drawVerticalScale(Graphics g, int startx, int endx, int ypos,
+ boolean left)
{
- FontMetrics fm = getFontMetrics(av.getFont());
ypos += charHeight;
if (av.hasHiddenColumns())
{
- startx = av.getAlignment().getHiddenColumns()
- .adjustForHiddenColumns(startx);
- endx = av.getAlignment().getHiddenColumns()
- .adjustForHiddenColumns(endx);
- }
-
- int maxwidth = av.getAlignment().getWidth();
- if (av.hasHiddenColumns())
- {
- maxwidth = av.getAlignment().getHiddenColumns()
- .findColumnPosition(maxwidth) - 1;
+ HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns();
+ startx = hiddenColumns.adjustForHiddenColumns(startx);
+ endx = hiddenColumns.adjustForHiddenColumns(endx);
}
+ FontMetrics fm = getFontMetrics(av.getFont());
- // WEST SCALE
for (int i = 0; i < av.getAlignment().getHeight(); i++)
{
SequenceI seq = av.getAlignment().getSequenceAt(i);
- int index = startx;
- int value = -1;
- while (index < endx)
+ /*
+ * find sequence position of first non-gapped position -
+ * to the right if scale left, to the left if scale right
+ */
+ int index = left ? startx : endx;
+ int value = -1;
+ while (index >= startx && index <= endx)
{
- if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
+ if (!Comparison.isGap(seq.getCharAt(index)))
+ {
+ value = seq.findPosition(index);
+ break;
+ }
+ if (left)
{
index++;
-
- continue;
}
-
- value = av.getAlignment().getSequenceAt(i).findPosition(index);
-
- break;
- }
-
- if (value != -1)
- {
- int x = labelWidthWest - fm.stringWidth(String.valueOf(value))
- - charWidth / 2;
- g.drawString(value + "", x, (ypos + (i * charHeight))
- - (charHeight / 5));
- }
- }
- }
-
- /**
- * DOCUMENT ME!
- *
- * @param g
- * DOCUMENT ME!
- * @param startx
- * DOCUMENT ME!
- * @param endx
- * DOCUMENT ME!
- * @param ypos
- * DOCUMENT ME!
- */
- void drawEastScale(Graphics g, int startx, int endx, int ypos)
- {
- ypos += charHeight;
-
- if (av.hasHiddenColumns())
- {
- endx = av.getAlignment().getHiddenColumns()
- .adjustForHiddenColumns(endx);
- }
-
- SequenceI seq;
- // EAST SCALE
- for (int i = 0; i < av.getAlignment().getHeight(); i++)
- {
- seq = av.getAlignment().getSequenceAt(i);
- int index = endx;
- int value = -1;
-
- while (index > startx)
- {
- if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
+ else
{
index--;
-
- continue;
}
-
- value = seq.findPosition(index);
-
- break;
}
if (value != -1)
{
- g.drawString(String.valueOf(value), 0, (ypos + (i * charHeight))
+ /*
+ * draw scale value, justified, with half a character width
+ * separation from the sequence data
+ */
+ int justify = fm.stringWidth(String.valueOf(value)) + charWidth;
+ int xpos = left ? labelWidthWest - justify + charWidth / 2
+ : getWidth() - justify - charWidth / 2;
+
+ g.drawString(String.valueOf(value), xpos, (ypos + (i * charHeight))
- (charHeight / 5));
}
}
}
- boolean fastpainting = false;
-
/**
* need to make this thread safe move alignment rendering in response to
* slider adjustment
int canvasHeight, int startRes)
{
updateViewport();
- AlignmentI al = av.getAlignment();
-
int labelWidth = 0;
if (av.getScaleRightWrapped() || av.getScaleLeftWrapped())
{
}
int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) / charWidth;
- int cHeight = av.getAlignment().getHeight() * charHeight;
av.setWrappedWidth(cWidth);
av.getRanges().setViewportStartAndWidth(startRes, cWidth);
- int endx;
int ypos = hgap;
int maxwidth = av.getAlignment().getWidth();
}
int annotationHeight = getAnnotationHeight();
+ int sequencesHeight = av.getAlignment().getHeight() * charHeight;
while ((ypos <= canvasHeight) && (startRes < maxwidth))
{
- endx = startRes + cWidth - 1;
+ drawWrappedWidth(g, startRes, canvasHeight, cWidth, maxwidth, ypos);
- if (endx > maxwidth)
- {
- endx = maxwidth;
- }
+ ypos += sequencesHeight + annotationHeight + hgap;
- g.setFont(av.getFont());
- g.setColor(Color.black);
+ startRes += cWidth;
+ }
+ }
- if (av.getScaleLeftWrapped())
- {
- drawWestScale(g, startRes, endx, ypos);
- }
+ /**
+ * @param g
+ * @param startRes
+ * @param canvasHeight
+ * @param canvasWidth
+ * @param maxWidth
+ * @param ypos
+ */
+ protected void drawWrappedWidth(Graphics g, int startRes,
+ int canvasHeight, int canvasWidth, int maxWidth, int ypos)
+ {
+ int endx;
+ endx = startRes + canvasWidth - 1;
- if (av.getScaleRightWrapped())
- {
- g.translate(canvasWidth - labelWidthEast, 0);
- drawEastScale(g, startRes, endx, ypos);
- g.translate(-(canvasWidth - labelWidthEast), 0);
- }
+ if (endx > maxWidth)
+ {
+ endx = maxWidth;
+ }
- g.translate(labelWidthWest, 0);
+ g.setFont(av.getFont());
+ g.setColor(Color.black);
- if (av.getScaleAboveWrapped())
- {
- drawNorthScale(g, startRes, endx, ypos);
- }
+ if (av.getScaleLeftWrapped())
+ {
+ drawVerticalScale(g, startRes, endx, ypos, true);
+ }
- if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
- {
- g.setColor(Color.blue);
- int res;
- HiddenColumns hidden = av.getAlignment().getHiddenColumns();
- List<Integer> positions = hidden.findHiddenRegionPositions();
- for (int pos : positions)
- {
- res = pos - startRes;
+ if (av.getScaleRightWrapped())
+ {
+ drawVerticalScale(g, startRes, endx, ypos, false);
+ }
- if (res < 0 || res > endx - startRes)
- {
- continue;
- }
+ g.translate(labelWidthWest, 0);
- gg.fillPolygon(
- new int[] { res * charWidth - charHeight / 4,
- res * charWidth + charHeight / 4, res * charWidth },
- new int[] { ypos - (charHeight / 2),
- ypos - (charHeight / 2), ypos - (charHeight / 2) + 8 },
- 3);
+ if (av.getScaleAboveWrapped())
+ {
+ drawNorthScale(g, startRes, endx, ypos);
+ }
+ if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
+ {
+ g.setColor(Color.blue);
+ HiddenColumns hidden = av.getAlignment().getHiddenColumns();
+ List<Integer> positions = hidden.findHiddenRegionPositions();
+ for (int pos : positions)
+ {
+ int res = pos - startRes;
+
+ if (res < 0 || res > endx - startRes)
+ {
+ continue;
}
- }
- // When printing we have an extra clipped region,
- // the Printable page which we need to account for here
- Shape clip = g.getClip();
+ gg.fillPolygon(new int[] { res * charWidth - charHeight / 4,
+ res * charWidth + charHeight / 4, res * charWidth }, new int[] {
+ ypos - (charHeight / 2), ypos - (charHeight / 2),
+ ypos - (charHeight / 2) + 8 }, 3);
- if (clip == null)
- {
- g.setClip(0, 0, cWidth * charWidth, canvasHeight);
- }
- else
- {
- g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
- (int) clip.getBounds().getHeight());
}
+ }
- drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
+ // When printing we have an extra clipped region,
+ // the Printable page which we need to account for here
+ Shape clip = g.getClip();
- if (av.isShowAnnotation())
- {
- g.translate(0, cHeight + ypos + 3);
- if (annotations == null)
- {
- annotations = new AnnotationPanel(av);
- }
+ if (clip == null)
+ {
+ g.setClip(0, 0, canvasWidth * charWidth, canvasHeight);
+ }
+ else
+ {
+ g.setClip(0, (int) clip.getBounds().getY(), canvasWidth * charWidth,
+ (int) clip.getBounds().getHeight());
+ }
- annotations.renderer.drawComponent(annotations, av, g, -1,
- startRes, endx + 1);
- g.translate(0, -cHeight - ypos - 3);
- }
- g.setClip(clip);
- g.translate(-labelWidthWest, 0);
+ drawPanel(g, startRes, endx, 0, av.getAlignment().getHeight() - 1, ypos);
- ypos += cHeight + annotationHeight + hgap;
+ int cHeight = av.getAlignment().getHeight() * charHeight;
- startRes += cWidth;
+ if (av.isShowAnnotation())
+ {
+ g.translate(0, cHeight + ypos + 3);
+ if (annotations == null)
+ {
+ annotations = new AnnotationPanel(av);
+ }
+
+ annotations.renderer.drawComponent(annotations, av, g, -1, startRes,
+ endx + 1);
+ g.translate(0, -cHeight - ypos - 3);
}
+ g.setClip(clip);
+ g.translate(-labelWidthWest, 0);
}
AnnotationPanel annotations;
{
String eventName = evt.getPropertyName();
- if (av.getWrapAlignment())
- {
- if (eventName.equals(ViewportRanges.STARTRES))
- {
- repaint();
- }
- }
- else
+ if (true/*!av.getWrapAlignment()*/)
{
int scrollX = 0;
if (eventName.equals(ViewportRanges.STARTRES))
if (eventName.equals(ViewportRanges.STARTRES))
{
// scroll - startres and endres both change
- fastPaint(scrollX, 0);
+ if (av.getWrapAlignment())
+ {
+ fastPaintWrapped(scrollX);
+ }
+ else
+ {
+ fastPaint(scrollX, 0);
+ }
}
else if (eventName.equals(ViewportRanges.STARTSEQ))
{
}
/**
+ * Does a minimal update of the image for a scroll movement. This method
+ * handles scroll movements of up to one width of the wrapped alignment (one
+ * click in the vertical scrollbar). Larger movements (for example after a
+ * scroll to highlight a mapped position) trigger a full redraw instead.
+ *
+ * @param scrollX
+ * number of positions scrolled (right if positive, left if negative)
+ */
+ protected void fastPaintWrapped(int scrollX)
+ {
+ if (Math.abs(scrollX) > 0 /*av.getRanges().getViewportWidth()*/)
+ {
+ /*
+ * shift of more than one view width is too much
+ * to handle in this method
+ */
+ fastPaint = false;
+ repaint();
+ return;
+ }
+
+ fastPaint = true;
+ shiftWrappedAlignment(-scrollX);
+
+ // add new columns (scale above, sequence, annotation)
+ // at top left if scrollX < 0 or bottom right if scrollX > 0
+
+ repaint();
+ }
+
+ /**
+ * Shifts the visible alignment by the specified number of columns - left if
+ * negative, right if positive. Includes scale above, left or right and
+ * annotations (if shown). Does not draw newly visible columns.
+ *
+ * @param positions
+ */
+ protected void shiftWrappedAlignment(int positions)
+ {
+ if (positions == 0)
+ {
+ return;
+ }
+
+ int repeatHeight = getRepeatHeightWrapped();
+ ViewportRanges ranges = av.getRanges();
+ int widthToCopy = (ranges.getViewportWidth() - Math.abs(positions))
+ * charWidth;
+ int visibleWidths = getHeight() / repeatHeight;
+ if (getHeight() % repeatHeight > 0)
+ {
+ visibleWidths++;
+ }
+ int viewportWidth = ranges.getViewportWidth();
+
+ if (positions > 0)
+ {
+ /*
+ * shift right (after scroll left)
+ * for each wrapped width (starting with the last), copy (width-positions)
+ * columns from the left margin to the right margin, and copy positions
+ * columns from the right margin of the row above (if any) to the
+ * left margin of the current row
+ */
+ int xpos = ranges.getStartRes() + (visibleWidths - 1) * viewportWidth;
+
+ /*
+ * get y-offset of last wrapped width
+ */
+ int y = getHeight() / repeatHeight * repeatHeight;
+ int copyFromLeftStart = labelWidthWest;
+ int copyFromRightStart = copyFromLeftStart + widthToCopy;
+
+ while (y >= 0)
+ {
+ // todo limit repeatHeight for a last part height width
+ gg.copyArea(copyFromLeftStart, y, widthToCopy, repeatHeight,
+ positions * charWidth, 0);
+ if (y > 0)
+ {
+ gg.copyArea(copyFromRightStart, y - repeatHeight, positions
+ * charWidth, repeatHeight, -widthToCopy, repeatHeight);
+ }
+
+ if (av.getScaleLeftWrapped())
+ {
+ // drawVerticalScale(gg, xpos, xpos + viewportWidth, y, true);
+ }
+ if (av.getScaleRightWrapped())
+ {
+ // drawVerticalScale(gg, xpos, xpos + viewportWidth, y, false);
+ }
+
+ y -= repeatHeight;
+ xpos -= viewportWidth;
+ }
+ }
+ else
+ {
+ /*
+ * shift left (after scroll right)
+ * for each wrapped width (starting with the first), copy (width-positions)
+ * columns from the right margin to the left margin, and copy positions
+ * columns from the left margin of the row below (if any) to the
+ * right margin of the current row
+ */
+ int xpos = ranges.getStartRes();
+ int y = 0;
+ int copyFromRightStart = labelWidthWest - positions * charWidth;
+
+ while (y < getHeight())
+ {
+ // todo limit repeatHeight for a last part height width
+ gg.copyArea(copyFromRightStart, y, widthToCopy, repeatHeight,
+ positions * charWidth, 0);
+ if (y + repeatHeight < getHeight())
+ {
+ gg.copyArea(labelWidthWest, y + repeatHeight, -positions
+ * charWidth, repeatHeight, widthToCopy, -repeatHeight);
+ }
+
+ if (av.getScaleLeftWrapped())
+ {
+ // drawVerticalScale(gg, xpos, xpos + viewportWidth, y, true);
+ }
+ if (av.getScaleRightWrapped())
+ {
+ // drawVerticalScale(gg, xpos, xpos + viewportWidth, y, false);
+ }
+
+ y += repeatHeight;
+ xpos += ranges.getViewportWidth();
+ }
+ }
+ }
+
+ /**
* Redraws any positions in the search results in the visible region of a
* wrapped alignment. Any highlights are drawn depending on the search results
* set on the Viewport, not the <code>results</code> argument. This allows