import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
+import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
* Wrapped mode, but not the scale above in Unwrapped mode.
*
*/
+@SuppressWarnings("serial")
public class SeqCanvas extends JPanel implements ViewportListenerI
{
+ /*
+ * pixels gap between sequences and annotations when in wrapped mode
+ */
+ static final int SEQS_ANNOTATION_GAP = 3;
+
private static final String ZEROS = "0000000000";
final FeatureRenderer fr;
private final SequenceRenderer seqRdr;
- private boolean fastPaint = false;
+ boolean fastPaint = false;
private boolean fastpainting = false;
private int labelWidthWest; // label left width in pixels if shown
- private int wrappedSpaceAboveAlignment; // gap between widths
+ int wrappedSpaceAboveAlignment; // gap between widths
- private int wrappedRepeatHeightPx; // height in pixels of wrapped width
+ int wrappedRepeatHeightPx; // height in pixels of wrapped width
private int wrappedVisibleWidths; // number of wrapped widths displayed
* <li>scrolling by trackpad, middle mouse button, or other device</li>
* <li>by moving the box in the Overview window</li>
* <li>programmatically to make a highlighted position visible</li>
+ * <li>pasting a block of sequences</li>
* </ul>
*
* @param horizontal
*/
public void fastPaint(int horizontal, int vertical)
{
- if (fastpainting || img == null)
+
+ // effectively:
+ // if (horizontal != 0 && vertical != 0)
+ // throw new InvalidArgumentException();
+ if (fastpainting || img == null)
{
return;
}
fastpainting = true;
fastPaint = true;
-
try
{
int charHeight = av.getCharHeight();
int charWidth = av.getCharWidth();
-
+
ViewportRanges ranges = av.getRanges();
int startRes = ranges.getStartRes();
int endRes = ranges.getEndRes();
int endSeq = ranges.getEndSeq();
int transX = 0;
int transY = 0;
-
- Graphics gg = img.getGraphics();
- gg.copyArea(horizontal * charWidth, vertical * charHeight,
- img.getWidth(), img.getHeight(), -horizontal * charWidth,
- -vertical * charHeight);
if (horizontal > 0) // scrollbar pulled right, image to the left
{
}
}
+
+ // System.err.println(">>> FastPaint to " + transX + " " + transY + " "
+ // + horizontal + " " + vertical + " " + startRes + " " + endRes
+ // + " " + startSeq + " " + endSeq);
+
+ Graphics gg = img.getGraphics();
+ gg.copyArea(horizontal * charWidth, vertical * charHeight,
+ img.getWidth(), img.getHeight(), -horizontal * charWidth,
+ -vertical * charHeight);
gg.translate(transX, transY);
drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
gg.translate(-transX, -transY);
gg.dispose();
-
+
// Call repaint on alignment panel so that repaints from other alignment
// panel components can be aggregated. Otherwise performance of the
// overview window and others may be adversely affected.
+ // System.out.println("SeqCanvas fastPaint() repaint() request...");
av.getAlignPanel().repaint();
} finally
{
@Override
public void paintComponent(Graphics g)
{
- //super.paintComponent(g); // BH 2019
int charHeight = av.getCharHeight();
int charWidth = av.getCharWidth();
- ViewportRanges ranges = av.getRanges();
-
int width = getWidth();
int height = getHeight();
width -= (width % charWidth);
height -= (height % charHeight);
-
- if ((img != null) && (fastPaint
- || (getVisibleRect().width != g.getClipBounds().width)
- || (getVisibleRect().height != g.getClipBounds().height)))
- {
- g.drawImage(img, 0, 0, this);
+ // BH 2019 can't possibly fastPaint if either width or height is 0
- drawSelectionGroup((Graphics2D) g, ranges.getStartRes(),
- ranges.getEndRes(), ranges.getStartSeq(), ranges.getEndSeq());
+ if (width == 0 || height == 0)
+ {
+ return;
+ }
+ ViewportRanges ranges = av.getRanges();
+ int startRes = ranges.getStartRes();
+ int startSeq = ranges.getStartSeq();
+ int endRes = ranges.getEndRes();
+ int endSeq = ranges.getEndSeq();
+
+ // [JAL-3226] problem that JavaScript (or Java) may consolidate multiple
+ // repaint() requests in unpredictable ways. In this case, the issue was
+ // that in response to a CTRL-C/CTRL-V paste request, in Java a fast
+ // repaint request preceded two full requests, thus resulting
+ // in a full request for paint. In constrast, in JavaScript, the three
+ // requests were bundled together into one, so the fastPaint flag was
+ // still present for the second and third request.
+ //
+ // This resulted in incomplete painting.
+ //
+ // The solution was to set seqCanvas.fastPaint and idCanvas.fastPaint false
+ // in PaintRefresher when the target to be painted is one of those two
+ // components.
+ //
+ // BH 2019.04.22
+ //
+ // An initial idea; can be removed once we determine this issue is closed:
+ // if (av.isFastPaintDisabled())
+ // {
+ // fastPaint = false;
+ // }
+
+ Rectangle vis, clip;
+ if (img != null
+ && (fastPaint
+ || (vis = getVisibleRect()).width != (clip = g
+ .getClipBounds()).width
+ || vis.height != clip.height))
+ {
+ g.drawImage(img, 0, 0, this);
+ drawSelectionGroup((Graphics2D) g, startRes, endRes, startSeq,
+ endSeq);
fastPaint = false;
}
- else if (width > 0 && height > 0)
+ else
{
- /*
- * img is a cached version of the last view we drew, if any
- * if we have no img or the size has changed, make a new one
- */
+ // img is a cached version of the last view we drew.
+ // If we have no img or the size has changed, make a new one.
+ //
if (img == null || width != img.getWidth()
|| height != img.getHeight())
{
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
}
-
+
Graphics2D gg = (Graphics2D) img.getGraphics();
gg.setFont(av.getFont());
if (av.getWrapAlignment())
{
- drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
+ drawWrappedPanel(gg, width, height, ranges.getStartRes());
}
else
{
- drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
- ranges.getStartSeq(), ranges.getEndSeq(), 0);
+ drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
}
- drawSelectionGroup(gg, ranges.getStartRes(),
- ranges.getEndRes(), ranges.getStartSeq(), ranges.getEndSeq());
+ drawSelectionGroup(gg, startRes, endRes, startSeq, endSeq);
g.drawImage(img, 0, 0, this);
gg.dispose();
if (av.cursorMode)
{
- drawCursor(g, ranges.getStartRes(), ranges.getEndRes(),
- ranges.getStartSeq(), ranges.getEndSeq());
+ drawCursor(g, startRes, endRes, startSeq, endSeq);
}
}
calculateWrappedGeometry(canvasWidth, canvasHeight);
/*
- * draw one width at a time (excluding any scales or annotation shown),
+ * draw one width at a time (excluding any scales shown),
* until we have run out of either alignment or vertical space available
*/
int ypos = wrappedSpaceAboveAlignment;
* (av.getScaleAboveWrapped() ? 2 : 1);
/*
- * height in pixels of the wrapped widths
+ * compute height in pixels of the wrapped widths
+ * - start with space above plus sequences
*/
wrappedRepeatHeightPx = wrappedSpaceAboveAlignment;
- // add sequences
wrappedRepeatHeightPx += av.getAlignment().getHeight()
* charHeight;
- // add annotations panel height if shown
- wrappedRepeatHeightPx += getAnnotationHeight();
+
+ /*
+ * add annotations panel height if shown
+ * also gap between sequences and annotations
+ */
+ if (av.isShowAnnotation())
+ {
+ wrappedRepeatHeightPx += getAnnotationHeight();
+ wrappedRepeatHeightPx += SEQS_ANNOTATION_GAP; // 3px
+ }
/*
* number of visible widths (the last one may be part height),
* @param endColumn
* @param canvasHeight
*/
- protected void drawWrappedWidth(Graphics g, int ypos, int startColumn,
- int endColumn, int canvasHeight)
+ protected void drawWrappedWidth(Graphics g, final int ypos,
+ final int startColumn, final int endColumn,
+ final int canvasHeight)
{
ViewportRanges ranges = av.getRanges();
int viewportWidth = ranges.getViewportWidth();
if (av.isShowAnnotation())
{
- g.translate(0, cHeight + ypos + 3);
+ final int yShift = cHeight + ypos + SEQS_ANNOTATION_GAP;
+ g.translate(0, yShift);
if (annotations == null)
{
annotations = new AnnotationPanel(av);
annotations.renderer.drawComponent(annotations, av, g, -1,
startColumn, endx + 1);
- g.translate(0, -cHeight - ypos - 3);
+ g.translate(0, -yShift);
}
g.translate(-xOffset, 0);
}
int startx = startRes;
int endx;
int ypos = hgap; // vertical offset
- int maxwidth = av.getAlignment().getWidth();
-
- if (av.hasHiddenColumns())
- {
- maxwidth = av.getAlignment().getHiddenColumns()
- .absoluteToVisibleColumn(maxwidth);
- }
+ int maxwidth = av.getAlignment().getVisibleWidth();
// chop the wrapped alignment extent up into panel-sized blocks and treat
// each block as if it were a block from an unwrapped alignment
{
String eventName = evt.getPropertyName();
- if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
+ // BH 2019.07.27 removes dead code introduced in aad3650 and simplifies
+ // logic, emphasizing no check for ENDRES or ENDSEQ
+
+ // Both scrolling and resizing change viewport ranges: scrolling changes
+ // both start and end points, but resize only changes end values.
+ // Here we only want to fastpaint on a scroll, with resize using a normal
+ // paint, so scroll events are identified as changes to the horizontal or
+ // vertical start value.
+
+ // Make sure we're not trying to draw a panel
+ // larger than the visible window
+ int scrollX = 0;
+ switch (eventName)
{
+ case SequenceGroup.SEQ_GROUP_CHANGED:
fastPaint = true;
repaint();
return;
- }
- else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT))
- {
+ case ViewportRanges.MOVE_VIEWPORT:
fastPaint = false;
repaint();
return;
- }
-
- int scrollX = 0;
- if (eventName.equals(ViewportRanges.STARTRES)
- || eventName.equals(ViewportRanges.STARTRESANDSEQ))
- {
- // Make sure we're not trying to draw a panel
- // larger than the visible window
- if (eventName.equals(ViewportRanges.STARTRES))
- {
- scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
- }
- else
- {
- scrollX = ((int[]) evt.getNewValue())[0]
- - ((int[]) evt.getOldValue())[0];
- }
- ViewportRanges vpRanges = av.getRanges();
-
- int range = vpRanges.getEndRes() - vpRanges.getStartRes();
- if (scrollX > range)
- {
- scrollX = range;
- }
- else if (scrollX < -range)
- {
- scrollX = -range;
- }
- }
- // Both scrolling and resizing change viewport ranges: scrolling changes
- // both start and end points, but resize only changes end values.
- // Here we only want to fastpaint on a scroll, with resize using a normal
- // paint, so scroll events are identified as changes to the horizontal or
- // vertical start value.
- if (eventName.equals(ViewportRanges.STARTRES))
- {
- if (av.getWrapAlignment())
- {
- fastPaintWrapped(scrollX);
- }
- else
- {
- fastPaint(scrollX, 0);
- }
- }
- else if (eventName.equals(ViewportRanges.STARTSEQ))
- {
- // scroll
- fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
- }
- else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
- {
- if (av.getWrapAlignment())
- {
- fastPaintWrapped(scrollX);
- }
- else
- {
- fastPaint(scrollX, 0);
- }
- }
- else if (eventName.equals(ViewportRanges.STARTSEQ))
- {
- // scroll
+ case ViewportRanges.STARTSEQ:
+ // meaning STARTOREND
+ // typically scroll, but possibly just the end changed
fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+ return;
+ case ViewportRanges.ENDRES:
+ case ViewportRanges.ENDSEQ:
+ // meaning second event along with "START" -- ENDONLY,NOTSTART
+ // TODO: ignore??
+ return;
+ case ViewportRanges.STARTRES:
+ // meaning STARTOREND
+ scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
+ break;
+ case ViewportRanges.STARTRESANDSEQ:
+ scrollX = ((int[]) evt.getNewValue())[0]
+ - ((int[]) evt.getOldValue())[0];
+ break;
+ }
+ ViewportRanges vpRanges = av.getRanges();
+ int range = vpRanges.getEndRes() - vpRanges.getStartRes() + 1;
+ scrollX = Math.max(Math.min(scrollX, range), -range);
+ // only STARTRES or STARTRESANDSEQ:
+ if (av.getWrapAlignment())
+ {
+ fastPaintWrapped(scrollX);
}
- else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+ else
{
- if (av.getWrapAlignment())
- {
- fastPaintWrapped(scrollX);
- }
- }
+ fastPaint(scrollX, 0);
+ }
+
+ // BH 2019.07.27 was:
+ // if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
+ // {
+ // fastPaint = true;
+ // repaint();
+ // return;
+ // }
+ // else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT))
+ // {
+ // fastPaint = false;
+ // // System.err.println("!!!! fastPaint false from MOVE_VIEWPORT");
+ // repaint();
+ // return;
+ // }
+ //
+ // if (eventName.equals(ViewportRanges.STARTRES)
+ // || eventName.equals(ViewportRanges.STARTRESANDSEQ))
+ // {
+ // // Make sure we're not trying to draw a panel
+ // // larger than the visible window
+ // if (eventName.equals(ViewportRanges.STARTRES))
+ // {
+ // scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
+ // }
+ // else
+ // {
+ // scrollX = ((int[]) evt.getNewValue())[0]
+ // - ((int[]) evt.getOldValue())[0];
+ // }
+ // ViewportRanges vpRanges = av.getRanges();
+ //
+ // int range = vpRanges.getEndRes() - vpRanges.getStartRes() + 1;
+ // if (scrollX > range)
+ // {
+ // scrollX = range;
+ // }
+ // else if (scrollX < -range)
+ // {
+ // scrollX = -range;
+ // }
+ // }
+ // Both scrolling and resizing change viewport ranges: scrolling changes
+ // both start and end points, but resize only changes end values.
+ // Here we only want to fastpaint on a scroll, with resize using a normal
+ // paint, so scroll events are identified as changes to the horizontal or
+ // vertical start value.
+ // BH 2019.07.27 was:
+ // if (eventName.equals(ViewportRanges.STARTRES))
+ // {
+ // if (av.getWrapAlignment())
+ // {
+ // fastPaintWrapped(scrollX);
+ // }
+ // else
+ // {
+ // fastPaint(scrollX, 0);
+ // }
+ // }
+ // else if (eventName.equals(ViewportRanges.STARTSEQ))
+ // {
+ // // scroll
+ // fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+ // }
+ // else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+ // {
+ // if (av.getWrapAlignment())
+ // {
+ // fastPaintWrapped(scrollX);
+ // }
+ // else
+ // {
+ // fastPaint(scrollX, 0);
+ // }
+ // }
+ //
+ // BH oops!
+ //
+ // else if (eventName.equals(ViewportRanges.STARTSEQ))
+ // {
+ // // scroll
+ // fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+ // }
+ // else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+ // {
+ // if (av.getWrapAlignment())
+ // {
+ // fastPaintWrapped(scrollX);
+ // }
+ // }
}
/**
{
ViewportRanges ranges = av.getRanges();
- if (Math.abs(scrollX) > ranges.getViewportWidth())
+ if (Math.abs(scrollX) >= ranges.getViewportWidth())
{
/*
- * shift of more than one view width is
+ * shift of one view width or more is
* overcomplicated to handle in this method
*/
fastPaint = false;
while (y >= 0)
{
+ /*
+ * shift 'widthToCopy' residues by 'positions' places to the right
+ */
gg.copyArea(copyFromLeftStart, y, widthToCopy, heightToCopy,
positions * charWidth, 0);
if (y > 0)
{
+ /*
+ * copy 'positions' residue from the row above (right hand end)
+ * to this row's left hand end
+ */
gg.copyArea(copyFromRightStart, y - wrappedRepeatHeightPx,
positions * charWidth, heightToCopy, -widthToCopy,
wrappedRepeatHeightPx);
{
return labelWidthWest;
}
+
}