X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FSeqCanvas.java;h=123e649838d401e19ed9d4667303e7fa44712f79;hb=da8e34c5aeee5e83aa844d374eb21e4c2e3cebef;hp=58af47b4f6242743dd6f6172045e62950e5e481b;hpb=42a3c5add84dd6d89be5efb6cbcef72d38c1132c;p=jalview.git diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 58af47b..123e649 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -25,26 +25,27 @@ import jalview.datamodel.HiddenColumns; import jalview.datamodel.SearchResultsI; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; +import jalview.datamodel.VisibleContigsIterator; import jalview.renderer.ScaleRenderer; import jalview.renderer.ScaleRenderer.ScaleMark; import jalview.util.Comparison; import jalview.viewmodel.ViewportListenerI; import jalview.viewmodel.ViewportRanges; -import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; +import java.awt.Rectangle; import java.awt.RenderingHints; -import java.awt.Shape; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; +import java.util.Iterator; import java.util.List; -import javax.swing.JComponent; +import javax.swing.JPanel; /** * The Swing component on which the alignment sequences, and annotations (if @@ -52,8 +53,14 @@ import javax.swing.JComponent; * Wrapped mode, but not the scale above in Unwrapped mode. * */ -public class SeqCanvas extends JComponent implements ViewportListenerI +@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; @@ -68,7 +75,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI private final SequenceRenderer seqRdr; - private boolean fastPaint = false; + boolean fastPaint = false; private boolean fastpainting = false; @@ -81,13 +88,14 @@ public class SeqCanvas extends JComponent implements ViewportListenerI 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 - private Graphics2D gg; + // Don't do this! Graphics handles are supposed to be transient + //private Graphics2D gg; /** * Creates a new SeqCanvas object. @@ -108,7 +116,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI public SequenceRenderer getSequenceRenderer() { - return seqRdr; + return seqRdr; } public FeatureRenderer getFeatureRenderer() @@ -194,12 +202,12 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int yPos = ypos + charHeight; int startX = startx; int endX = endx; - + if (av.hasHiddenColumns()) { HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns(); - startX = hiddenColumns.adjustForHiddenColumns(startx); - endX = hiddenColumns.adjustForHiddenColumns(endx); + startX = hiddenColumns.visibleToAbsoluteColumn(startx); + endX = hiddenColumns.visibleToAbsoluteColumn(endx); } FontMetrics fm = getFontMetrics(av.getFont()); @@ -230,6 +238,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } } + /* * white fill the space for the scale */ @@ -254,6 +263,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI g.drawString(valueAsString, xOffset, y); } } + } /** @@ -267,6 +277,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI *
  • scrolling by trackpad, middle mouse button, or other device
  • *
  • by moving the box in the Overview window
  • *
  • programmatically to make a highlighted position visible
  • + *
  • pasting a block of sequences
  • * * * @param horizontal @@ -276,18 +287,21 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ public void fastPaint(int horizontal, int vertical) { - if (fastpainting || gg == null || 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(); @@ -296,47 +310,58 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int transX = 0; int transY = 0; - gg.copyArea(horizontal * charWidth, vertical * charHeight, - img.getWidth(), img.getHeight(), -horizontal * charWidth, - -vertical * charHeight); - - if (horizontal > 0) // scrollbar pulled right, image to the left - { - transX = (endRes - startRes - horizontal) * charWidth; - startRes = endRes - horizontal; - } - else if (horizontal < 0) - { - endRes = startRes - horizontal; - } - else if (vertical > 0) // scroll down - { - startSeq = endSeq - vertical; - - if (startSeq < ranges.getStartSeq()) - { // ie scrolling too fast, more than a page at a time - startSeq = ranges.getStartSeq(); + if (horizontal > 0) // scrollbar pulled right, image to the left + { + transX = (endRes - startRes - horizontal) * charWidth; + startRes = endRes - horizontal; } - else + else if (horizontal < 0) { - transY = img.getHeight() - ((vertical + 1) * charHeight); + endRes = startRes - horizontal; } - } - else if (vertical < 0) - { - endSeq = startSeq - vertical; - if (endSeq > ranges.getEndSeq()) + if (vertical > 0) // scroll down + { + startSeq = endSeq - vertical; + + if (startSeq < ranges.getStartSeq()) + { // ie scrolling too fast, more than a page at a time + startSeq = ranges.getStartSeq(); + } + else + { + transY = img.getHeight() - ((vertical + 1) * charHeight); + } + } + else if (vertical < 0) { - endSeq = ranges.getEndSeq(); + endSeq = startSeq - vertical; + + if (endSeq > ranges.getEndSeq()) + { + endSeq = ranges.getEndSeq(); + } } - } - gg.translate(transX, transY); - drawPanel(gg, startRes, endRes, startSeq, endSeq, 0); - gg.translate(-transX, -transY); - repaint(); + // 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 { fastpainting = false; @@ -346,48 +371,82 @@ public class SeqCanvas extends JComponent implements ViewportListenerI @Override public void paintComponent(Graphics g) { - super.paintComponent(g); - + + if (av.getAlignPanel().getHoldRepaint()) + { + return; + } int charHeight = av.getCharHeight(); int charWidth = av.getCharWidth(); - ViewportRanges ranges = av.getRanges(); - - int width = getWidth(); - int height = getHeight(); + int availWidth = getWidth(); + int availHeight = getHeight(); - width -= (width % charWidth); - height -= (height % charHeight); + availWidth -= (availWidth % charWidth); + availHeight -= (availHeight % charHeight); - // selectImage is the selection group outline image - BufferedImage selectImage = drawSelectionGroup( - ranges.getStartRes(), ranges.getEndRes(), - ranges.getStartSeq(), ranges.getEndSeq()); + // BH 2019 can't possibly fastPaint if either width or height is 0 - if ((img != null) && (fastPaint - || (getVisibleRect().width != g.getClipBounds().width) - || (getVisibleRect().height != g.getClipBounds().height))) + if (availWidth == 0 || availHeight == 0) { - BufferedImage lcimg = buildLocalImage(selectImage); - g.drawImage(lcimg, 0, 0, this); + 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 - if (img == null || width != img.getWidth() - || height != img.getHeight()) + // 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 || availWidth != img.getWidth() + || availHeight != img.getHeight()) { - img = setupImage(); - if (img == null) - { - return; - } - gg = (Graphics2D) img.getGraphics(); - gg.setFont(av.getFont()); + img = new BufferedImage(availWidth, availHeight, + BufferedImage.TYPE_INT_RGB); } + Graphics2D gg = (Graphics2D) img.getGraphics(); + gg.setFont(av.getFont()); + if (av.antiAlias) { gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, @@ -395,21 +454,26 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } gg.setColor(Color.white); - gg.fillRect(0, 0, img.getWidth(), img.getHeight()); + gg.fillRect(0, 0, availWidth, availHeight); if (av.getWrapAlignment()) { - drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes()); + drawWrappedPanel(gg, availWidth, availHeight, ranges.getStartRes()); } else { - drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(), - ranges.getStartSeq(), ranges.getEndSeq(), 0); + drawPanel(gg, startRes, endRes, startSeq, endSeq, 0); } - // lcimg is a local *copy* of img which we'll draw selectImage on top of - BufferedImage lcimg = buildLocalImage(selectImage); - g.drawImage(lcimg, 0, 0, this); + drawSelectionGroup(gg, startRes, endRes, startSeq, endSeq); + + g.drawImage(img, 0, 0, this); + gg.dispose(); + } + + if (av.cursorMode) + { + drawCursor(g, startRes, endRes, startSeq, endSeq); } } @@ -432,14 +496,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { drawPanel(g1, startRes, endRes, startSeq, endSeq, 0); - BufferedImage selectImage = drawSelectionGroup(startRes, endRes, + drawSelectionGroup((Graphics2D) g1, startRes, endRes, startSeq, endSeq); - if (selectImage != null) - { - ((Graphics2D) g1).setComposite(AlphaComposite - .getInstance(AlphaComposite.SRC_OVER)); - g1.drawImage(selectImage, 0, 0, this); - } } /** @@ -457,127 +515,42 @@ public class SeqCanvas extends JComponent implements ViewportListenerI public void drawWrappedPanelForPrinting(Graphics g, int canvasWidth, int canvasHeight, int startRes) { - SequenceGroup group = av.getSelectionGroup(); - drawWrappedPanel(g, canvasWidth, canvasHeight, startRes); + SequenceGroup group = av.getSelectionGroup(); if (group != null) { - BufferedImage selectImage = null; - try - { - selectImage = new BufferedImage(canvasWidth, canvasHeight, - BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works - } catch (OutOfMemoryError er) - { - System.gc(); - System.err.println("Print image OutOfMemory Error.\n" + er); - new OOMWarning("Creating wrapped alignment image for printing", er); - } - if (selectImage != null) - { - Graphics2D g2 = selectImage.createGraphics(); - setupSelectionGroup(g2, selectImage); - drawWrappedSelection(g2, group, canvasWidth, canvasHeight, + drawWrappedSelection((Graphics2D) g, group, canvasWidth, canvasHeight, startRes); - - g2.setComposite( - AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); - g.drawImage(selectImage, 0, 0, this); - g2.dispose(); - } - } - } - - /* - * Make a local image by combining the cached image img - * with any selection - */ - private BufferedImage buildLocalImage(BufferedImage selectImage) - { - // clone the cached image - BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(), - img.getType()); - Graphics2D g2d = lcimg.createGraphics(); - g2d.drawImage(img, 0, 0, null); - - // overlay selection group on lcimg - if (selectImage != null) - { - g2d.setComposite( - AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); - g2d.drawImage(selectImage, 0, 0, this); } - g2d.dispose(); - - return lcimg; - } - - /* - * Set up a buffered image of the correct height and size for the sequence canvas - */ - private BufferedImage setupImage() - { - BufferedImage lcimg = null; - - int charWidth = av.getCharWidth(); - int charHeight = av.getCharHeight(); - - int width = getWidth(); - int height = getHeight(); - - width -= (width % charWidth); - height -= (height % charHeight); - - if ((width < 1) || (height < 1)) - { - return null; - } - - try - { - lcimg = new BufferedImage(width, height, - BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works - } catch (OutOfMemoryError er) - { - System.gc(); - System.err.println( - "Group image OutOfMemory Redraw Error.\n" + er); - new OOMWarning("Creating alignment image for display", er); - - return null; - } - - return lcimg; } /** - * Returns the visible width of the canvas in residues, after allowing for - * East or West scales (if shown) + * Using the current font, determine fields labelWidthEast and labelWidthWest, + * and return the number of residues that can fill the remaining width. * - * @param canvasWidth + * @param width * the width in pixels (possibly including scales) * - * @return + * @return the visible width in residues, after allowing for East or West + * scales (if shown) + * */ - public int getWrappedCanvasWidth(int canvasWidth) + public int getWrappedCanvasWidth(int width) { int charWidth = av.getCharWidth(); FontMetrics fm = getFontMetrics(av.getFont()); - int labelWidth = 0; - - if (av.getScaleRightWrapped() || av.getScaleLeftWrapped()) - { - labelWidth = getLabelWidth(fm); - } + int labelWidth = (av.getScaleRightWrapped() || av.getScaleLeftWrapped() + ? getLabelWidth(fm) + : 0); labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0; labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0; - return (canvasWidth - labelWidthEast - labelWidthWest) / charWidth; + return (width - labelWidthEast - labelWidthWest) / charWidth; } /** @@ -603,6 +576,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI maxWidth = Math.max(maxWidth, alignment.getSequenceAt(i).getEnd()); } + // quick int log10 int length = 0; for (int i = maxWidth; i > 0; i /= 10) { @@ -617,26 +591,31 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * window * * @param g - * @param canvasWidth + * @param availWidth * available width in pixels - * @param canvasHeight + * @param availHeight * available height in pixels * @param startColumn * the first column (0...) of the alignment to draw */ - public void drawWrappedPanel(Graphics g, int canvasWidth, - int canvasHeight, final int startColumn) + public void drawWrappedPanel(Graphics g, int availWidth, int availHeight, + final int startColumn) { - int wrappedWidthInResidues = calculateWrappedGeometry(canvasWidth, - canvasHeight); + int wrappedWidthInResidues = calculateWrappedGeometry(availWidth, + availHeight); av.setWrappedWidth(wrappedWidthInResidues); ViewportRanges ranges = av.getRanges(); ranges.setViewportStartAndWidth(startColumn, wrappedWidthInResidues); + // we need to call this again to make sure the startColumn + + // wrappedWidthInResidues values are used to calculate wrappedVisibleWidths + // correctly. + calculateWrappedGeometry(availWidth, availHeight); + /* - * draw one width at a time (including 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; @@ -648,7 +627,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { int endColumn = Math .min(maxWidth, start + wrappedWidthInResidues - 1); - drawWrappedWidth(g, ypos, start, endColumn, canvasHeight); + drawWrappedWidth(g, ypos, start, endColumn, availHeight); ypos += wrappedRepeatHeightPx; start += wrappedWidthInResidues; currentWidth++; @@ -667,11 +646,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI *
  • whether scales are shown left, right or above the alignment
  • * * - * @param canvasWidth - * @param canvasHeight + * @param availWidth + * @param availHeight * @return the number of residue columns in each width */ - protected int calculateWrappedGeometry(int canvasWidth, int canvasHeight) + protected int calculateWrappedGeometry(int availWidth, int availHeight) { int charHeight = av.getCharHeight(); @@ -683,22 +662,30 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * (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.getRanges().getViewportHeight() + 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), * ensuring a part height includes at least one sequence */ ViewportRanges ranges = av.getRanges(); - wrappedVisibleWidths = canvasHeight / wrappedRepeatHeightPx; - int remainder = canvasHeight % wrappedRepeatHeightPx; + wrappedVisibleWidths = availHeight / wrappedRepeatHeightPx; + int remainder = availHeight % wrappedRepeatHeightPx; if (remainder >= (wrappedSpaceAboveAlignment + charHeight)) { wrappedVisibleWidths++; @@ -707,7 +694,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI /* * compute width in residues; this also sets East and West label widths */ - int wrappedWidthInResidues = getWrappedCanvasWidth(canvasWidth); + int wrappedWidthInResidues = getWrappedCanvasWidth(availWidth); /* * limit visibleWidths to not exceed width of alignment @@ -734,8 +721,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * @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(); @@ -751,28 +739,15 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int xOffset = labelWidthWest + ((startColumn - ranges.getStartRes()) % viewportWidth) * charWidth; - g.translate(xOffset, 0); - // When printing we have an extra clipped region, - // the Printable page which we need to account for here - Shape clip = g.getClip(); - - if (clip == null) - { - g.setClip(0, 0, viewportWidth * charWidth, canvasHeight); - } - else - { - g.setClip(0, (int) clip.getBounds().getY(), - viewportWidth * charWidth, (int) clip.getBounds().getHeight()); - } + g.translate(xOffset, 0); /* * white fill the region to be drawn (so incremental fast paint doesn't * scribble over an existing image) */ - gg.setColor(Color.white); - gg.fillRect(0, ypos, (endx - startColumn + 1) * charWidth, + g.setColor(Color.white); + g.fillRect(0, ypos, (endx - startColumn + 1) * charWidth, wrappedRepeatHeightPx); drawPanel(g, startColumn, endx, 0, av.getAlignment().getHeight() - 1, @@ -782,7 +757,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI 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); @@ -790,9 +766,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI annotations.renderer.drawComponent(annotations, av, g, -1, startColumn, endx + 1); - g.translate(0, -cHeight - ypos - 3); + g.translate(0, -yShift); } - g.setClip(clip); g.translate(-xOffset, 0); } @@ -808,6 +783,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int charWidth = av.getCharWidth(); g.setFont(av.getFont()); + g.setColor(Color.black); int ypos = wrappedSpaceAboveAlignment; @@ -829,6 +805,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (av.getScaleRightWrapped()) { int x = labelWidthWest + viewportWidth * charWidth; + g.translate(x, 0); drawVerticalScale(g, startCol, endColumn, ypos, false); g.translate(-x, 0); @@ -881,11 +858,14 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int charWidth = av.getCharWidth(); g.setColor(Color.blue); + int res; HiddenColumns hidden = av.getAlignment().getHiddenColumns(); - List positions = hidden.findHiddenRegionPositions(); - for (int pos : positions) + + Iterator it = hidden.getStartRegionIterator(startColumn, + endColumn); + while (it.hasNext()) { - int res = pos - startColumn; + res = it.next() - startColumn; if (res < 0 || res > endColumn - startColumn + 1) { @@ -929,16 +909,14 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int startx = startRes; int endx; int ypos = hgap; // vertical offset - int maxwidth = av.getAlignment().getWidth(); - - if (av.hasHiddenColumns()) - { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(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 + g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_ROUND, 3f, new float[] + { 5f, 3f }, 0f)); + g.setColor(Color.RED); while ((ypos <= canvasHeight) && (startx < maxwidth)) { // set end value to be start + width, or maxwidth, whichever is smaller @@ -963,6 +941,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI // update horizontal offset startx += cWidth; } + g.setStroke(new BasicStroke()); } int getAnnotationHeight() @@ -1012,29 +991,23 @@ public class SeqCanvas extends JComponent implements ViewportListenerI else { int screenY = 0; - final int screenYMax = endRes - startRes; - int blockStart = startRes; - int blockEnd = endRes; + int blockStart; + int blockEnd; - for (int[] region : av.getAlignment().getHiddenColumns() - .getHiddenColumnsCopy()) - { - int hideStart = region[0]; - int hideEnd = region[1]; + HiddenColumns hidden = av.getAlignment().getHiddenColumns(); + VisibleContigsIterator regions = hidden + .getVisContigsIterator(startRes, endRes + 1, true); - if (hideStart <= blockStart) - { - blockStart += (hideEnd - hideStart) + 1; - continue; - } + while (regions.hasNext()) + { + int[] region = regions.next(); + blockEnd = region[1]; + blockStart = region[0]; /* * draw up to just before the next hidden region, or the end of * the visible region, whichever comes first */ - blockEnd = Math.min(hideStart - 1, blockStart + screenYMax - - screenY); - g1.translate(screenY * charWidth, 0); draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset); @@ -1043,7 +1016,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * draw the downline of the hidden column marker (ScalePanel draws the * triangle on top) if we reached it */ - if (av.getShowHiddenMarkers() && blockEnd == hideStart - 1) + if (av.getShowHiddenMarkers() + && (regions.hasNext() || regions.endsAtHidden())) { g1.setColor(Color.blue); @@ -1054,23 +1028,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI g1.translate(-screenY * charWidth, 0); screenY += blockEnd - blockStart + 1; - blockStart = hideEnd + 1; - - if (screenY > screenYMax) - { - // already rendered last block - return; - } - } - - if (screenY <= screenYMax) - { - // remaining visible region to render - blockEnd = blockStart + screenYMax - screenY; - g1.translate(screenY * charWidth, 0); - draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset); - - g1.translate(-screenY * charWidth, 0); } } @@ -1128,26 +1085,19 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (av.hasSearchResults()) { SearchResultsI searchResults = av.getSearchResults(); - int[] visibleResults = searchResults.getResults(nextSeq, - startRes, endRes); + int[] visibleResults = searchResults.getResults(nextSeq, startRes, + endRes); if (visibleResults != null) { for (int r = 0; r < visibleResults.length; r += 2) { seqRdr.drawHighlightedText(nextSeq, visibleResults[r], - visibleResults[r + 1], (visibleResults[r] - startRes) - * charWidth, offset - + ((i - startSeq) * charHeight)); + visibleResults[r + 1], + (visibleResults[r] - startRes) * charWidth, + offset + ((i - startSeq) * charHeight)); } } } - - if (av.cursorMode && cursorY == i && cursorX >= startRes - && cursorX <= endRes) - { - seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth, - offset + ((i - startSeq) * charHeight)); - } } if (av.getSelectionGroup() != null @@ -1158,14 +1108,21 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } + /** + * Draws the outlines of any groups defined on the alignment (excluding the + * current selection group, if any) + * + * @param g1 + * @param startRes + * @param endRes + * @param startSeq + * @param endSeq + * @param offset + */ void drawGroupsBoundaries(Graphics g1, int startRes, int endRes, int startSeq, int endSeq, int offset) { Graphics2D g = (Graphics2D) g1; - // - // /////////////////////////////////// - // Now outline any areas if necessary - // /////////////////////////////////// SequenceGroup group = null; int groupIndex = -1; @@ -1178,58 +1135,44 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (group != null) { - g.setStroke(new BasicStroke()); - g.setColor(group.getOutlineColour()); - do { + g.setColor(group.getOutlineColour()); drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq, offset); groupIndex++; - - g.setStroke(new BasicStroke()); - if (groupIndex >= av.getAlignment().getGroups().size()) { break; } - group = av.getAlignment().getGroups().get(groupIndex); - } while (groupIndex < av.getAlignment().getGroups().size()); - } - } - - /* - * Draw the selection group as a separate image and overlay + /** + * Draws the outline of the current selection group (if any) + * + * @param g + * @param startRes + * @param endRes + * @param startSeq + * @param endSeq */ - private BufferedImage drawSelectionGroup(int startRes, int endRes, + private void drawSelectionGroup(Graphics2D g, int startRes, int endRes, int startSeq, int endSeq) { - // get a new image of the correct size - BufferedImage selectionImage = setupImage(); - - if (selectionImage == null) - { - return null; - } - SequenceGroup group = av.getSelectionGroup(); if (group == null) { - // nothing to draw - return null; + return; } - // set up drawing colour - Graphics2D g = (Graphics2D) selectionImage.getGraphics(); - - setupSelectionGroup(g, selectionImage); - + g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_ROUND, 3f, new float[] + { 5f, 3f }, 0f)); + g.setColor(Color.RED); if (!av.getWrapAlignment()) { drawUnwrappedSelection(g, group, startRes, endRes, startSeq, endSeq, @@ -1240,38 +1183,114 @@ public class SeqCanvas extends JComponent implements ViewportListenerI drawWrappedSelection(g, group, getWidth(), getHeight(), av.getRanges().getStartRes()); } - - g.dispose(); - return selectionImage; + g.setStroke(new BasicStroke()); } - /* - * Set up graphics for selection group + /** + * Draw the cursor as a separate image and overlay + * + * @param startRes + * start residue of area to draw cursor in + * @param endRes + * end residue of area to draw cursor in + * @param startSeq + * start sequence of area to draw cursor in + * @param endSeq + * end sequence of are to draw cursor in + * @return a transparent image of the same size as the sequence canvas, with + * the cursor drawn on it, if any */ - private void setupSelectionGroup(Graphics2D g, - BufferedImage selectionImage) + private void drawCursor(Graphics g, int startRes, int endRes, + int startSeq, + int endSeq) { - // set background to transparent - g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f)); - g.fillRect(0, 0, selectionImage.getWidth(), selectionImage.getHeight()); + // convert the cursorY into a position on the visible alignment + int cursor_ypos = cursorY; - // set up foreground to draw red dashed line - g.setComposite(AlphaComposite.Src); - g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_ROUND, 3f, new float[] - { 5f, 3f }, 0f)); - g.setColor(Color.RED); + // don't do work unless we have to + if (cursor_ypos >= startSeq && cursor_ypos <= endSeq) + { + int yoffset = 0; + int xoffset = 0; + int startx = startRes; + int endx = endRes; + + // convert the cursorX into a position on the visible alignment + int cursor_xpos = av.getAlignment().getHiddenColumns() + .absoluteToVisibleColumn(cursorX); + + if (av.getAlignment().getHiddenColumns().isVisible(cursorX)) + { + + if (av.getWrapAlignment()) + { + // work out the correct offsets for the cursor + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + int canvasWidth = getWidth(); + int canvasHeight = getHeight(); + + // height gap above each panel + int hgap = charHeight; + if (av.getScaleAboveWrapped()) + { + hgap += charHeight; + } + + int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) + / charWidth; + int cHeight = av.getAlignment().getHeight() * charHeight; + + endx = startx + cWidth - 1; + int ypos = hgap; // vertical offset + + // iterate down the wrapped panels + while ((ypos <= canvasHeight) && (endx < cursor_xpos)) + { + // update vertical offset + ypos += cHeight + getAnnotationHeight() + hgap; + + // update horizontal offset + startx += cWidth; + endx = startx + cWidth - 1; + } + yoffset = ypos; + xoffset = labelWidthWest; + } + + // now check if cursor is within range for x values + if (cursor_xpos >= startx && cursor_xpos <= endx) + { + // get the character the cursor is drawn at + SequenceI seq = av.getAlignment().getSequenceAt(cursorY); + char s = seq.getCharAt(cursorX); + + seqRdr.drawCursor(g, s, + xoffset + (cursor_xpos - startx) * av.getCharWidth(), + yoffset + (cursor_ypos - startSeq) * av.getCharHeight()); + } + } + } } - /* + + /** * Draw a selection group over an unwrapped alignment - * @param g graphics object to draw with - * @param group selection group - * @param startRes start residue of area to draw - * @param endRes end residue of area to draw - * @param startSeq start sequence of area to draw - * @param endSeq end sequence of area to draw - * @param offset vertical offset (used when called from wrapped alignment code) + * + * @param g + * graphics object to draw with + * @param group + * selection group + * @param startRes + * start residue of area to draw + * @param endRes + * end residue of area to draw + * @param startSeq + * start sequence of area to draw + * @param endSeq + * end sequence of area to draw + * @param offset + * vertical offset (used when called from wrapped alignment code) */ private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group, int startRes, int endRes, int startSeq, int endSeq, int offset) @@ -1287,22 +1306,17 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { // package into blocks of visible columns int screenY = 0; - int blockStart = startRes; - int blockEnd = endRes; + int blockStart; + int blockEnd; - for (int[] region : av.getAlignment().getHiddenColumns() - .getHiddenColumnsCopy()) + HiddenColumns hidden = av.getAlignment().getHiddenColumns(); + VisibleContigsIterator regions = hidden + .getVisContigsIterator(startRes, endRes + 1, true); + while (regions.hasNext()) { - int hideStart = region[0]; - int hideEnd = region[1]; - - if (hideStart <= blockStart) - { - blockStart += (hideEnd - hideStart) + 1; - continue; - } - - blockEnd = hideStart - 1; + int[] region = regions.next(); + blockEnd = region[1]; + blockStart = region[0]; g.translate(screenY * charWidth, 0); drawPartialGroupOutline(g, group, @@ -1310,38 +1324,27 @@ public class SeqCanvas extends JComponent implements ViewportListenerI g.translate(-screenY * charWidth, 0); screenY += blockEnd - blockStart + 1; - blockStart = hideEnd + 1; - - if (screenY > (endRes - startRes)) - { - // already rendered last block - break; - } - } - - if (screenY <= (endRes - startRes)) - { - // remaining visible region to render - blockEnd = blockStart + (endRes - startRes) - screenY; - g.translate(screenY * charWidth, 0); - drawPartialGroupOutline(g, group, - blockStart, blockEnd, startSeq, endSeq, offset); - - g.translate(-screenY * charWidth, 0); } } } - /* - * Draw the selection group as a separate image and overlay + /** + * Draws part of a selection group outline + * + * @param g + * @param group + * @param startRes + * @param endRes + * @param startSeq + * @param endSeq + * @param verticalOffset */ private void drawPartialGroupOutline(Graphics2D g, SequenceGroup group, int startRes, int endRes, int startSeq, int endSeq, int verticalOffset) { - int charHeight = av.getCharHeight(); - int charWidth = av.getCharWidth(); - + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); int visWidth = (endRes - startRes + 1) * charWidth; int oldY = -1; @@ -1349,140 +1352,141 @@ public class SeqCanvas extends JComponent implements ViewportListenerI boolean inGroup = false; int top = -1; int bottom = -1; - - int sx = -1; int sy = -1; - int xwidth = -1; - for (i = startSeq; i <= endSeq; i++) - { - // position of start residue of group relative to startRes, in pixels - sx = (group.getStartRes() - startRes) * charWidth; + List seqs = group.getSequences(null); - // width of group in pixels - xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth) - - 1; + // position of start residue of group relative to startRes, in pixels + int sx = (group.getStartRes() - startRes) * charWidth; - sy = verticalOffset + (i - startSeq) * charHeight; + // width of group in pixels + int xwidth = (((group.getEndRes() + 1) - group.getStartRes()) + * charWidth) - 1; - if (sx + xwidth < 0 || sx > visWidth) - { - continue; - } - - if ((sx <= (endRes - startRes) * charWidth) - && group.getSequences(null) - .contains(av.getAlignment().getSequenceAt(i))) + if (!(sx + xwidth < 0 || sx > visWidth)) + { + for (i = startSeq; i <= endSeq; i++) { - if ((bottom == -1) && !group.getSequences(null) - .contains(av.getAlignment().getSequenceAt(i + 1))) - { - bottom = sy + charHeight; - } + sy = verticalOffset + (i - startSeq) * charHeight; - if (!inGroup) - { - if (((top == -1) && (i == 0)) || !group.getSequences(null) - .contains(av.getAlignment().getSequenceAt(i - 1))) - { - top = sy; - } - - oldY = sy; - inGroup = true; - } - } - else - { - if (inGroup) + if ((sx <= (endRes - startRes) * charWidth) + && seqs.contains(av.getAlignment().getSequenceAt(i))) { - // if start position is visible, draw vertical line to left of - // group - if (sx >= 0 && sx < visWidth) + if ((bottom == -1) + && !seqs.contains(av.getAlignment().getSequenceAt(i + 1))) { - g.drawLine(sx, oldY, sx, sy); + bottom = sy + charHeight; } - // if end position is visible, draw vertical line to right of - // group - if (sx + xwidth < visWidth) + if (!inGroup) { - g.drawLine(sx + xwidth, oldY, sx + xwidth, sy); - } - - if (sx < 0) - { - xwidth += sx; - sx = 0; - } - - // don't let width extend beyond current block, or group extent - // fixes JAL-2672 - if (sx + xwidth >= (endRes - startRes + 1) * charWidth) - { - xwidth = (endRes - startRes + 1) * charWidth - sx; - } - - // draw horizontal line at top of group - if (top != -1) - { - g.drawLine(sx, top, sx + xwidth, top); - top = -1; - } + if (((top == -1) && (i == 0)) || !seqs + .contains(av.getAlignment().getSequenceAt(i - 1))) + { + top = sy; + } - // draw horizontal line at bottom of group - if (bottom != -1) - { - g.drawLine(sx, bottom, sx + xwidth, bottom); - bottom = -1; + oldY = sy; + inGroup = true; } + } + else if (inGroup) + { + drawVerticals(g, sx, xwidth, visWidth, oldY, sy); + drawHorizontals(g, sx, xwidth, visWidth, top, bottom); + // reset top and bottom + top = -1; + bottom = -1; inGroup = false; } } - } - - if (inGroup) - { - sy = verticalOffset + ((i - startSeq) * charHeight); - if (sx >= 0 && sx < visWidth) + if (inGroup) { - g.drawLine(sx, oldY, sx, sy); + sy = verticalOffset + ((i - startSeq) * charHeight); + drawVerticals(g, sx, xwidth, visWidth, oldY, sy); + drawHorizontals(g, sx, xwidth, visWidth, top, bottom); } + } + } - if (sx + xwidth < visWidth) - { - g.drawLine(sx + xwidth, oldY, sx + xwidth, sy); - } + /** + * Draw horizontal selection group boundaries at top and bottom positions + * + * @param g + * graphics object to draw on + * @param sx + * start x position + * @param xwidth + * width of gap + * @param visWidth + * visWidth maximum available width + * @param top + * position to draw top of group at + * @param bottom + * position to draw bottom of group at + */ + private void drawHorizontals(Graphics2D g, int sx, int xwidth, + int visWidth, int top, int bottom) + { + int width = xwidth; + int startx = sx; + if (startx < 0) + { + width += startx; + startx = 0; + } - if (sx < 0) - { - xwidth += sx; - sx = 0; - } + // don't let width extend beyond current block, or group extent + // fixes JAL-2672 + if (startx + width >= visWidth) + { + width = visWidth - startx; + } - if (sx + xwidth > visWidth) - { - xwidth = visWidth; - } - else if (sx + xwidth >= (endRes - startRes + 1) * charWidth) - { - xwidth = (endRes - startRes + 1) * charWidth; - } + if (top != -1) + { + g.drawLine(startx, top, startx + width, top); + } - if (top != -1) - { - g.drawLine(sx, top, sx + xwidth, top); - top = -1; - } + if (bottom != -1) + { + g.drawLine(startx, bottom - 1, startx + width, bottom - 1); + } + } - if (bottom != -1) - { - g.drawLine(sx, bottom - 1, sx + xwidth, bottom - 1); - bottom = -1; - } + /** + * Draw vertical lines at sx and sx+xwidth providing they lie within + * [0,visWidth) + * + * @param g + * graphics object to draw on + * @param sx + * start x position + * @param xwidth + * width of gap + * @param visWidth + * visWidth maximum available width + * @param oldY + * top y value + * @param sy + * bottom y value + */ + private void drawVerticals(Graphics2D g, int sx, int xwidth, int visWidth, + int oldY, int sy) + { + // if start position is visible, draw vertical line to left of + // group + if (sx >= 0 && sx < visWidth) + { + g.drawLine(sx, oldY, sx, sy); + } - inGroup = false; + // if end position is visible, draw vertical line to right of + // group + if (sx + xwidth < visWidth) + { + g.drawLine(sx + xwidth, oldY, sx + xwidth, sy); } } @@ -1490,20 +1494,42 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * Highlights search results in the visible region by rendering as white text * on a black background. Any previous highlighting is removed. Answers true * if any highlight was left on the visible alignment (so status bar should be + * set to match), else false. This method does _not_ set the 'fastPaint' flag, + * so allows the next repaint to update the whole display. + * + * @param results + * @return + */ + public boolean highlightSearchResults(SearchResultsI results) + { + return highlightSearchResults(results, false); + + } + + /** + * Highlights search results in the visible region by rendering as white text + * on a black background. Any previous highlighting is removed. Answers true + * if any highlight was left on the visible alignment (so status bar should be * set to match), else false. *

    - * Currently fastPaint is not implemented for wrapped alignments. If a wrapped - * alignment had to be scrolled to show the highlighted region, then it should - * be fully redrawn, otherwise a fast paint can be performed. This argument - * could be removed if fast paint of scrolled wrapped alignment is coded in - * future (JAL-2609). + * Optionally, set the 'fastPaint' flag for a faster redraw if only the + * highlighted regions are modified. This speeds up highlighting across linked + * alignments. + *

    + * Currently fastPaint is not implemented for scrolled wrapped alignments. If + * a wrapped alignment had to be scrolled to show the highlighted region, then + * it should be fully redrawn, otherwise a fast paint can be performed. This + * argument could be removed if fast paint of scrolled wrapped alignment is + * coded in future (JAL-2609). * * @param results - * @param noFastPaint + * @param doFastPaint + * if true, sets a flag so the next repaint only redraws the modified + * image * @return */ public boolean highlightSearchResults(SearchResultsI results, - boolean noFastPaint) + boolean doFastPaint) { if (fastpainting) { @@ -1512,7 +1538,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI boolean wrapped = av.getWrapAlignment(); try { - fastPaint = !noFastPaint; + fastPaint = doFastPaint; fastpainting = fastPaint; /* @@ -1569,7 +1595,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ protected boolean drawMappedPositions(SearchResultsI results) { - if (results == null) + if ((results == null) || (img == null)) // JAL-2784 check gg is not null { return false; } @@ -1591,9 +1617,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (av.hasHiddenColumns()) { firstVisibleColumn = alignment.getHiddenColumns() - .adjustForHiddenColumns(firstVisibleColumn); + .visibleToAbsoluteColumn(firstVisibleColumn); lastVisibleColumn = alignment.getHiddenColumns() - .adjustForHiddenColumns(lastVisibleColumn); + .visibleToAbsoluteColumn(lastVisibleColumn); } for (int seqNo = ranges.getStartSeq(); seqNo <= ranges @@ -1636,14 +1662,16 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (av.hasHiddenColumns()) { firstCol = alignment.getHiddenColumns() - .findColumnPosition(firstCol); - lastCol = alignment.getHiddenColumns().findColumnPosition(lastCol); + .absoluteToVisibleColumn(firstCol); + lastCol = alignment.getHiddenColumns().absoluteToVisibleColumn(lastCol); } int transX = (firstCol - ranges.getStartRes()) * av.getCharWidth(); int transY = (firstSeq - ranges.getStartSeq()) * av.getCharHeight(); + Graphics gg = img.getGraphics(); gg.translate(transX, transY); drawPanel(gg, firstCol, lastCol, firstSeq, lastSeq, 0); gg.translate(-transX, -transY); + gg.dispose(); } return matchFound; @@ -1654,67 +1682,163 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { 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; + int scrollY = 0; + switch (eventName) { + case SequenceGroup.SEQ_GROUP_CHANGED: fastPaint = true; 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 + case ViewportRanges.MOVE_VIEWPORT: + fastPaint = false; + repaint(); + return; + 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]; + scrollY = ((int[]) evt.getNewValue())[1] + - ((int[]) evt.getOldValue())[1]; + + // System.out.println("SC dx dy " + scrollX + " " + scrollY); + + if (scrollX != 0 && scrollY != 0) { - scrollX = ((int[]) evt.getNewValue())[0] - - ((int[]) evt.getOldValue())[0]; - } - ViewportRanges vpRanges = av.getRanges(); + // all sorts of problems in JavaScript if this is commented out. + repaint(); + return; - int range = vpRanges.getEndRes() - vpRanges.getStartRes(); - if (scrollX > range) - { - scrollX = range; - } - else if (scrollX < -range) - { - scrollX = -range; } + break; + } - // 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)) - { - fastPaint(scrollX, ((int[]) evt.getNewValue())[1] - - ((int[]) evt.getOldValue())[1]); - } + 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 + { + fastPaint(scrollX, scrollY); + } + + // 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); + // } + // } } /** @@ -1730,10 +1854,10 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { 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; @@ -1741,7 +1865,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return; } - if (fastpainting || gg == null) + if (fastpainting || img == null) { return; } @@ -1751,6 +1875,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI try { + + Graphics gg = img.getGraphics(); + calculateWrappedGeometry(getWidth(), getHeight()); /* @@ -1779,6 +1906,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ drawWrappedDecorators(gg, ranges.getStartRes()); + gg.dispose(); + repaint(); } finally { @@ -1802,6 +1931,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return; } + Graphics gg = img.getGraphics(); + ViewportRanges ranges = av.getRanges(); int viewportWidth = ranges.getViewportWidth(); int charWidth = av.getCharWidth(); @@ -1828,6 +1959,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI /* * white fill first to erase annotations */ + + gg.translate(xOffset, 0); gg.setColor(Color.white); gg.fillRect(labelWidthWest, ypos, @@ -1835,6 +1968,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI gg.translate(-xOffset, 0); drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight); + } /* @@ -1878,7 +2012,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI gg.setColor(Color.white); gg.fillRect(0, canvasHeight - heightBelow, getWidth(), heightBelow); } - } + gg.dispose(); + } /** * Shifts the visible alignment by the specified number of columns - left if @@ -1894,6 +2029,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { return; } + + Graphics gg = img.getGraphics(); + int charWidth = av.getCharWidth(); int canvasHeight = getHeight(); @@ -1924,10 +2062,17 @@ public class SeqCanvas extends JComponent implements ViewportListenerI 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); @@ -1960,11 +2105,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * charWidth, heightToCopy, widthToCopy, -wrappedRepeatHeightPx); } - y += wrappedRepeatHeightPx; xpos += viewportWidth; } } + gg.dispose(); } @@ -1980,7 +2125,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ protected boolean drawMappedPositionsWrapped(SearchResultsI results) { - if (results == null) + if ((results == null) || (img == null)) // JAL-2784 check gg is not null { return false; } @@ -2008,13 +2153,16 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (av.hasHiddenColumns()) { firstVisibleColumn = alignment.getHiddenColumns() - .adjustForHiddenColumns(firstVisibleColumn); + .visibleToAbsoluteColumn(firstVisibleColumn); lastVisibleColumn = alignment.getHiddenColumns() - .adjustForHiddenColumns(lastVisibleColumn); + .visibleToAbsoluteColumn(lastVisibleColumn); } int gapHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1); + + Graphics gg = img.getGraphics(); + for (int seqNo = ranges.getStartSeq(); seqNo <= ranges .getEndSeq(); seqNo++) { @@ -2049,7 +2197,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (av.hasHiddenColumns()) { displayColumn = alignment.getHiddenColumns() - .findColumnPosition(displayColumn); + .absoluteToVisibleColumn(displayColumn); } /* @@ -2085,6 +2233,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } } + gg.dispose(); + return matchFound; } @@ -2097,4 +2247,5 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { return labelWidthWest; } + }