X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FSeqCanvas.java;h=b3fcc542f3c0f6f7d2fc3899b172bf0fe3b551b3;hb=87e1ada6a62777b415f14b0444fdcc63336c8f73;hp=5c5fbbee7355015d8041b9c7a443da319474e22c;hpb=39267ff67b3f5cabdf20ea468b9e86739223adf5;p=jalview.git diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 5c5fbbe..b3fcc54 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -32,7 +32,6 @@ 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; @@ -56,6 +55,11 @@ import javax.swing.JComponent; */ public class SeqCanvas extends JComponent 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; @@ -83,9 +87,9 @@ 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 @@ -200,8 +204,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI 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()); @@ -297,7 +301,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int endSeq = ranges.getEndSeq(); int transX = 0; int transY = 0; - + gg.copyArea(horizontal * charWidth, vertical * charHeight, img.getWidth(), img.getHeight(), -horizontal * charWidth, -vertical * charHeight); @@ -339,7 +343,10 @@ public class SeqCanvas extends JComponent implements ViewportListenerI drawPanel(gg, startRes, endRes, startSeq, endSeq, 0); gg.translate(-transX, -transY); - repaint(); + // 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. + av.getAlignPanel().repaint(); } finally { fastpainting = false; @@ -353,53 +360,49 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int charHeight = av.getCharHeight(); int charWidth = av.getCharWidth(); - + ViewportRanges ranges = av.getRanges(); - + int width = getWidth(); int height = getHeight(); - + width -= (width % charWidth); height -= (height % charHeight); - - // selectImage is the selection group outline image - BufferedImage selectImage = drawSelectionGroup( - ranges.getStartRes(), ranges.getEndRes(), - ranges.getStartSeq(), ranges.getEndSeq()); - + if ((img != null) && (fastPaint || (getVisibleRect().width != g.getClipBounds().width) || (getVisibleRect().height != g.getClipBounds().height))) { - BufferedImage lcimg = buildLocalImage(selectImage); - g.drawImage(lcimg, 0, 0, this); + g.drawImage(img, 0, 0, this); + + drawSelectionGroup((Graphics2D) g, ranges.getStartRes(), + ranges.getEndRes(), ranges.getStartSeq(), ranges.getEndSeq()); + fastPaint = false; } - else if ((width > 0) && (height > 0)) + else if (width > 0 && height > 0) { - // 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 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 = setupImage(); - if (img == null) - { - return; - } + img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); gg = (Graphics2D) img.getGraphics(); gg.setFont(av.getFont()); } - + if (av.antiAlias) { gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } - + gg.setColor(Color.white); gg.fillRect(0, 0, img.getWidth(), img.getHeight()); - + if (av.getWrapAlignment()) { drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes()); @@ -410,10 +413,10 @@ public class SeqCanvas extends JComponent implements ViewportListenerI ranges.getStartSeq(), ranges.getEndSeq(), 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, ranges.getStartRes(), + ranges.getEndRes(), ranges.getStartSeq(), ranges.getEndSeq()); + g.drawImage(img, 0, 0, this); } if (av.cursorMode) @@ -442,14 +445,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); - } } /** @@ -467,99 +464,14 @@ 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; } /** @@ -646,8 +558,13 @@ public class SeqCanvas extends JComponent implements ViewportListenerI 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(canvasWidth, canvasHeight); + /* - * 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; @@ -694,14 +611,22 @@ 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), @@ -745,8 +670,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(); @@ -782,8 +708,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * 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, @@ -793,7 +719,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); @@ -801,7 +728,7 @@ 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); @@ -895,7 +822,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int res; HiddenColumns hidden = av.getAlignment().getHiddenColumns(); - Iterator it = hidden.getBoundedStartIterator(startColumn, + Iterator it = hidden.getStartRegionIterator(startColumn, endColumn); while (it.hasNext()) { @@ -943,16 +870,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 @@ -977,6 +902,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI // update horizontal offset startx += cWidth; } + g.setStroke(new BasicStroke()); } int getAnnotationHeight() @@ -1030,8 +956,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int blockEnd; HiddenColumns hidden = av.getAlignment().getHiddenColumns(); - VisibleContigsIterator regions = (VisibleContigsIterator) hidden - .getVisContigsIterator(startRes, endRes, true); + VisibleContigsIterator regions = hidden + .getVisContigsIterator(startRes, endRes + 1, true); while (regions.hasNext()) { @@ -1120,16 +1046,16 @@ 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)); } } } @@ -1143,14 +1069,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; @@ -1163,18 +1096,15 @@ 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; @@ -1188,33 +1118,28 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } - - /* - * 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, @@ -1225,9 +1150,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI drawWrappedSelection(g, group, getWidth(), getHeight(), av.getRanges().getStartRes()); } - - g.dispose(); - return selectionImage; + g.setStroke(new BasicStroke()); } /** @@ -1261,7 +1184,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI // convert the cursorX into a position on the visible alignment int cursor_xpos = av.getAlignment().getHiddenColumns() - .findColumnPosition(cursorX); + .absoluteToVisibleColumn(cursorX); if (av.getAlignment().getHiddenColumns().isVisible(cursorX)) { @@ -1318,33 +1241,23 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } - /* - * Set up graphics for selection group - */ - private void setupSelectionGroup(Graphics2D g, - BufferedImage selectionImage) - { - // set background to transparent - g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f)); - g.fillRect(0, 0, selectionImage.getWidth(), selectionImage.getHeight()); - - // 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); - } - - /* + /** * 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) @@ -1364,8 +1277,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int blockEnd; HiddenColumns hidden = av.getAlignment().getHiddenColumns(); - VisibleContigsIterator regions = (VisibleContigsIterator) hidden - .getVisContigsIterator(startRes, endRes, true); + VisibleContigsIterator regions = hidden + .getVisContigsIterator(startRes, endRes + 1, true); while (regions.hasNext()) { int[] region = regions.next(); @@ -1382,16 +1295,23 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } } - /* - * 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; @@ -1399,140 +1319,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; - // width of group in pixels - xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth) - - 1; + List seqs = group.getSequences(null); - sy = verticalOffset + (i - startSeq) * charHeight; + // position of start residue of group relative to startRes, in pixels + int sx = (group.getStartRes() - startRes) * charWidth; - if (sx + xwidth < 0 || sx > visWidth) - { - continue; - } + // width of group in pixels + int xwidth = (((group.getEndRes() + 1) - group.getStartRes()) + * charWidth) - 1; - 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; - } - - if (!inGroup) - { - if (((top == -1) && (i == 0)) || !group.getSequences(null) - .contains(av.getAlignment().getSequenceAt(i - 1))) - { - top = sy; - } + sy = verticalOffset + (i - startSeq) * charHeight; - 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) - { - 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) + if (!inGroup) { - 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); } } @@ -1619,7 +1540,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ protected boolean drawMappedPositions(SearchResultsI results) { - if (results == null) + if ((results == null) || (gg == null)) // JAL-2784 check gg is not null { return false; } @@ -1641,9 +1562,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 @@ -1686,8 +1607,8 @@ 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(); @@ -1734,7 +1655,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } ViewportRanges vpRanges = av.getRanges(); - int range = vpRanges.getEndRes() - vpRanges.getStartRes(); + int range = vpRanges.getEndRes() - vpRanges.getStartRes() + 1; if (scrollX > range) { scrollX = range; @@ -1743,7 +1664,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { 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 @@ -1775,10 +1696,17 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { fastPaint(scrollX, 0); } - // bizarrely, we only need to scroll on the x value here as fastpaint - // copies the full height of the image anyway. Passing in the y value - // causes nasty repaint artefacts, which only disappear on a full - // repaint. + } + 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); } } } @@ -1796,12 +1724,10 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { ViewportRanges ranges = av.getRanges(); - // if (Math.abs(scrollX) > ranges.getViewportWidth()) - // JAL-2836, 2836 temporarily removed wrapped fastpaint for release 2.10.3 - if (true) + 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; @@ -1992,10 +1918,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); @@ -2048,7 +1981,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ protected boolean drawMappedPositionsWrapped(SearchResultsI results) { - if (results == null) + if ((results == null) || (gg == null)) // JAL-2784 check gg is not null { return false; } @@ -2076,9 +2009,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); } int gapHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1); @@ -2117,7 +2050,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (av.hasHiddenColumns()) { displayColumn = alignment.getHiddenColumns() - .findColumnPosition(displayColumn); + .absoluteToVisibleColumn(displayColumn); } /*