X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FSeqCanvas.java;h=d7d9ccb7a801a31ac07fc87b4f758ef76c604710;hb=2639d90e2b52d49cf586c0b602bc606033c9b8ae;hp=4aa7851c44782deba0a0e513c2071de817e6fb51;hpb=8fb1345b723e9104cab28c80b352a882e502b1ed;p=jalview.git diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 4aa7851..d7d9ccb 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -25,6 +25,7 @@ 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; @@ -42,9 +43,10 @@ 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,7 +54,7 @@ import javax.swing.JComponent; * Wrapped mode, but not the scale above in Unwrapped mode. * */ -public class SeqCanvas extends JComponent implements ViewportListenerI +public class SeqCanvas extends JPanel implements ViewportListenerI { private static final String ZEROS = "0000000000"; @@ -87,7 +89,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI 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. @@ -194,12 +197,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 +233,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } } + /* * white fill the space for the scale */ @@ -254,6 +258,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI g.drawString(valueAsString, xOffset, y); } } + } /** @@ -276,7 +281,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ public void fastPaint(int horizontal, int vertical) { - if (fastpainting || gg == null || img == null) + if (fastpainting || img == null) { return; } @@ -295,48 +300,54 @@ public class SeqCanvas extends JComponent implements ViewportListenerI 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); - 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 { - endSeq = ranges.getEndSeq(); + 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 = startSeq - vertical; - gg.translate(transX, transY); - drawPanel(gg, startRes, endRes, startSeq, endSeq, 0); - gg.translate(-transX, -transY); + if (endSeq > ranges.getEndSeq()) + { + endSeq = ranges.getEndSeq(); + } + } - repaint(); + 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. + av.getAlignPanel().repaint(); } finally { fastpainting = false; @@ -346,8 +357,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI @Override public void paintComponent(Graphics g) { - super.paintComponent(g); - + super.paintComponent(g); + int charHeight = av.getCharHeight(); int charWidth = av.getCharWidth(); @@ -360,18 +371,14 @@ public class SeqCanvas extends JComponent implements ViewportListenerI height -= (height % charHeight); // selectImage is the selection group outline image - BufferedImage selectImage = drawSelectionGroup( - ranges.getStartRes(), ranges.getEndRes(), - ranges.getStartSeq(), ranges.getEndSeq()); - - BufferedImage cursorImage = drawCursor(ranges.getStartRes(), + 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, cursorImage); + BufferedImage lcimg = buildLocalImage(selectImage); g.drawImage(lcimg, 0, 0, this); fastPaint = false; } @@ -387,10 +394,12 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { return; } - gg = (Graphics2D) img.getGraphics(); - gg.setFont(av.getFont()); } + + Graphics2D gg = (Graphics2D) img.getGraphics(); + gg.setFont(av.getFont()); + if (av.antiAlias) { gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, @@ -409,10 +418,20 @@ public class SeqCanvas extends JComponent implements ViewportListenerI drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(), ranges.getStartSeq(), ranges.getEndSeq(), 0); } + + + gg.dispose(); // lcimg is a local *copy* of img which we'll draw selectImage on top of - BufferedImage lcimg = buildLocalImage(selectImage, cursorImage); + BufferedImage lcimg = buildLocalImage(selectImage); g.drawImage(lcimg, 0, 0, this); + + } + + if (av.cursorMode) + { + drawCursor(g, ranges.getStartRes(), ranges.getEndRes(), + ranges.getStartSeq(), ranges.getEndSeq()); } } @@ -496,12 +515,14 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * Make a local image by combining the cached image img * with any selection */ - private BufferedImage buildLocalImage(BufferedImage selectImage, - BufferedImage cursorImage) + private BufferedImage buildLocalImage(BufferedImage selectImage) { // clone the cached image - BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(), - img.getType()); + BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(), + img.getType()); + + // BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(), + // img.getType()); Graphics2D g2d = lcimg.createGraphics(); g2d.drawImage(img, 0, 0, null); @@ -512,12 +533,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); g2d.drawImage(selectImage, 0, 0, this); } - // overlay cursor on lcimg - if (cursorImage != null) - { - g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); - g2d.drawImage(cursorImage, 0, 0, this); - } + g2d.dispose(); return lcimg; @@ -546,8 +562,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI try { - lcimg = new BufferedImage(width, height, - BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works + lcimg = new BufferedImage(width, height, + BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works } catch (OutOfMemoryError er) { System.gc(); @@ -645,6 +661,11 @@ 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), * until we have run out of either alignment or vertical space available @@ -744,7 +765,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * @param endColumn * @param canvasHeight */ - protected void drawWrappedWidth(Graphics g, int ypos, int startColumn, + protected void drawWrappedWidth(Graphics gg, int ypos, int startColumn, int endColumn, int canvasHeight) { ViewportRanges ranges = av.getRanges(); @@ -761,12 +782,23 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int xOffset = labelWidthWest + ((startColumn - ranges.getStartRes()) % viewportWidth) * charWidth; + + // BH 2018 note: I have switched to using Graphics.create() here because it is + // more reliable (and simpler) to reset. The difference seems to be that SwingJS + // automatically sets a clipping region on an image to be the image dimensions, whereas + // Java sets no clip for an image. (A bug? Go figure!) + // Since we are using an off-screen BufferedImage here, the result is that g.getClip() + // returns non-null in JavaScript but not Java. + // + // Anyway, this works and, I suggest, is better design anyway. + // + Graphics g = gg.create(); + 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); @@ -777,12 +809,13 @@ public class SeqCanvas extends JComponent implements ViewportListenerI viewportWidth * charWidth, (int) clip.getBounds().getHeight()); } + /* * 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, @@ -802,8 +835,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI startColumn, endx + 1); g.translate(0, -cHeight - ypos - 3); } - g.setClip(clip); - g.translate(-xOffset, 0); + g.dispose(); +// g.translate(-xOffset, 0); +// g.setClip(clip); } /** @@ -818,6 +852,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int charWidth = av.getCharWidth(); g.setFont(av.getFont()); + g.setColor(Color.black); int ypos = wrappedSpaceAboveAlignment; @@ -839,6 +874,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); @@ -891,11 +927,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) { @@ -922,9 +961,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int canvasWidth, int canvasHeight, int startRes) { - int charHeight = av.getCharHeight(); - int charWidth = av.getCharWidth(); - + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + // height gap above each panel int hgap = charHeight; if (av.getScaleAboveWrapped()) @@ -944,7 +983,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (av.hasHiddenColumns()) { maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth); + .absoluteToVisibleColumn(maxwidth); } // chop the wrapped alignment extent up into panel-sized blocks and treat @@ -1022,29 +1061,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); @@ -1053,7 +1086,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); @@ -1064,23 +1098,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); } } @@ -1138,26 +1155,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 @@ -1189,10 +1199,10 @@ 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); @@ -1269,18 +1279,15 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * @return a transparent image of the same size as the sequence canvas, with * the cursor drawn on it, if any */ - private BufferedImage drawCursor(int startRes, int endRes, int startSeq, + private void drawCursor(Graphics g, int startRes, int endRes, + int startSeq, int endSeq) { - // define our cursor image - BufferedImage cursorImage = null; - // convert the cursorY into a position on the visible alignment - int cursor_ypos = av.getAlignment().getHiddenColumns() - .findColumnPosition(cursorY); + int cursor_ypos = cursorY; // don't do work unless we have to - if (av.cursorMode && cursor_ypos >= startSeq && cursor_ypos <= endSeq) + if (cursor_ypos >= startSeq && cursor_ypos <= endSeq) { int yoffset = 0; int xoffset = 0; @@ -1289,63 +1296,60 @@ 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.getWrapAlignment()) + if (av.getAlignment().getHiddenColumns().isVisible(cursorX)) { - // 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()) + + if (av.getWrapAlignment()) { - hgap += charHeight; - } + // 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; + int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) + / charWidth; + int cHeight = av.getAlignment().getHeight() * charHeight; - endx = startx + cWidth - 1; - int ypos = hgap; // vertical offset + 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; + // 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; + // update horizontal offset + startx += cWidth; + endx = startx + cWidth - 1; + } + yoffset = ypos; + xoffset = labelWidthWest; } - yoffset = ypos; - xoffset = labelWidthWest; - } - // now check if cursor is within range for x values - if (cursor_xpos >= startx && cursor_xpos <= endx) - { - // get a new image of the correct size - cursorImage = setupImage(); - Graphics2D g = (Graphics2D) cursorImage.getGraphics(); - - // 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()); - g.dispose(); + // 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()); + } } } - - return cursorImage; } @@ -1380,8 +1384,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group, int startRes, int endRes, int startSeq, int endSeq, int offset) { - int charWidth = av.getCharWidth(); - + int charWidth = av.getCharWidth(); + if (!av.hasHiddenColumns()) { drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq, @@ -1391,22 +1395,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, @@ -1414,24 +1413,6 @@ 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); } } } @@ -1443,9 +1424,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI 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; @@ -1453,140 +1433,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; - } - - 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) - { - g.drawLine(sx, oldY, sx, sy); - } - - // if end position is visible, draw vertical line to right of - // group - if (sx + xwidth < visWidth) + if ((bottom == -1) + && !seqs.contains(av.getAlignment().getSequenceAt(i + 1))) { - g.drawLine(sx + xwidth, oldY, sx + xwidth, sy); + bottom = sy + charHeight; } - if (sx < 0) + if (!inGroup) { - 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); } } @@ -1594,20 +1575,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) { @@ -1616,7 +1619,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI boolean wrapped = av.getWrapAlignment(); try { - fastPaint = !noFastPaint; + fastPaint = doFastPaint; fastpainting = fastPaint; /* @@ -1673,7 +1676,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; } @@ -1695,9 +1698,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 @@ -1740,14 +1743,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; @@ -1764,15 +1769,31 @@ public class SeqCanvas extends JComponent implements ViewportListenerI repaint(); return; } + else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT)) + { + fastPaint = false; + repaint(); + return; + } int scrollX = 0; - if (eventName.equals(ViewportRanges.STARTRES)) + 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(); - scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); - int range = vpRanges.getViewportWidth(); + + int range = vpRanges.getEndRes() - vpRanges.getStartRes(); if (scrollX > range) { scrollX = range; @@ -1782,30 +1803,50 @@ 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 - // paint, so scroll events are identified as changes to the horizontal or - // vertical start value. - - // scroll - startres and endres both change - if (eventName.equals(ViewportRanges.STARTRES)) - { - if (av.getWrapAlignment()) + // 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)) { - fastPaintWrapped(scrollX); + if (av.getWrapAlignment()) + { + fastPaintWrapped(scrollX); + } + else + { + fastPaint(scrollX, 0); + } } - else + else if (eventName.equals(ViewportRanges.STARTSEQ)) { - fastPaint(scrollX, 0); + // 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 fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); } + else if (eventName.equals(ViewportRanges.STARTRESANDSEQ)) + { + if (av.getWrapAlignment()) + { + fastPaintWrapped(scrollX); + } + } } /** @@ -1832,7 +1873,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return; } - if (fastpainting || gg == null) + if (fastpainting || img == null) { return; } @@ -1842,6 +1883,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI try { + + Graphics gg = img.getGraphics(); + calculateWrappedGeometry(getWidth(), getHeight()); /* @@ -1870,6 +1914,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ drawWrappedDecorators(gg, ranges.getStartRes()); + gg.dispose(); + repaint(); } finally { @@ -1893,6 +1939,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(); @@ -1919,6 +1967,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, @@ -1926,6 +1976,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI gg.translate(-xOffset, 0); drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight); + } /* @@ -1969,7 +2020,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 @@ -1985,6 +2037,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { return; } + + Graphics gg = img.getGraphics(); + int charWidth = av.getCharWidth(); int canvasHeight = getHeight(); @@ -2051,11 +2106,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * charWidth, heightToCopy, widthToCopy, -wrappedRepeatHeightPx); } - y += wrappedRepeatHeightPx; xpos += viewportWidth; } } + gg.dispose(); } @@ -2071,7 +2126,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; } @@ -2099,13 +2154,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++) { @@ -2140,7 +2198,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (av.hasHiddenColumns()) { displayColumn = alignment.getHiddenColumns() - .findColumnPosition(displayColumn); + .absoluteToVisibleColumn(displayColumn); } /* @@ -2176,6 +2234,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } } + gg.dispose(); + return matchFound; }