X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=inline;f=src%2Fjalview%2Fgui%2FSeqCanvas.java;h=537af58e6d5b8fde5317c50400d7b2e9dfd30739;hb=4ad05f84d30ed345acbb533bb51dd832c727de99;hp=088a359dbb56f9b78b6d970a517a1f3456350a97;hpb=2f96125e48e277a8296e9e2b53719f346f043ceb;p=jalview.git diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 088a359..537af58 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -20,94 +20,95 @@ */ package jalview.gui; -import jalview.datamodel.AlignmentI; -import jalview.datamodel.HiddenColumns; -import jalview.datamodel.SearchResultsI; -import jalview.datamodel.SequenceGroup; -import jalview.datamodel.SequenceI; -import jalview.renderer.ScaleRenderer; -import jalview.renderer.ScaleRenderer.ScaleMark; -import jalview.util.Comparison; -import jalview.viewmodel.ViewportListenerI; -import jalview.viewmodel.ViewportRanges; - 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; + +import jalview.api.SequenceRenderer; +import jalview.datamodel.AlignmentI; +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; /** - * DOCUMENT ME! + * The Swing component on which the alignment sequences, and annotations (if + * shown), are drawn. This includes scales above, left and right (if shown) in + * Wrapped mode, but not the scale above in Unwrapped mode. * - * @author $author$ - * @version $Revision$ */ -public class SeqCanvas extends JComponent implements ViewportListenerI +@SuppressWarnings("serial") +public class SeqCanvas extends JPanel implements ViewportListenerI { - private static String ZEROS = "0000000000"; + /** + * vertical gap in pixels between sequences and annotations when in wrapped + * mode + */ + static final int SEQS_ANNOTATION_GAP = 3; - final FeatureRenderer fr; + private static final String ZEROS = "0000000000"; - final SequenceRenderer sr; + final FeatureRenderer fr; BufferedImage img; - Graphics2D gg; - - int imgWidth; - - int imgHeight; - AlignViewport av; - boolean fastPaint = false; - - boolean fastpainting = false; - int cursorX = 0; int cursorY = 0; - int charHeight = 0; + private final SequenceRenderer seqRdr; + + boolean fastPaint = false; - int charWidth = 0; + private boolean fastpainting = false; private AnnotationPanel annotations; /* * measurements for drawing a wrapped alignment */ - int labelWidthWest; // label left width in pixels if shown - private int labelWidthEast; // label right width in pixels if shown - private int wrappedSpaceAboveAlignment; // gap between widths + private int labelWidthWest; // label left width in pixels if shown + + 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 + // Don't do this! Graphics handles are supposed to be transient + // private Graphics2D gg; + /** * Creates a new SeqCanvas object. * - * @param av - * DOCUMENT ME! + * @param ap */ public SeqCanvas(AlignmentPanel ap) { this.av = ap.av; - updateViewport(); fr = new FeatureRenderer(ap); - sr = new SequenceRenderer(av); + seqRdr = new jalview.gui.SequenceRenderer(av); setLayout(new BorderLayout()); PaintRefresher.Register(this, av.getSequenceSetId()); setBackground(Color.white); @@ -117,7 +118,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI public SequenceRenderer getSequenceRenderer() { - return sr; + return seqRdr; } public FeatureRenderer getFeatureRenderer() @@ -125,12 +126,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return fr; } - private void updateViewport() - { - charHeight = av.getCharHeight(); - charWidth = av.getCharWidth(); - } - /** * Draws the scale above a region of a wrapped alignment, consisting of a * column number every major interval (10 columns). @@ -147,7 +142,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ private void drawNorthScale(Graphics g, int startx, int endx, int ypos) { - updateViewport(); + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); /* * white fill the scale space (for the fastPaint case) @@ -189,6 +185,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * Draw the scale to the left or right of a wrapped alignment * * @param g + * graphics context, positioned at the start of the scale to be drawn * @param startx * first column of wrapped width (0.. excluding any hidden columns) * @param endx @@ -198,16 +195,21 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * @param left * if true, scale is left of residues, if false, scale is right */ - void drawVerticalScale(Graphics g, int startx, int endx, int ypos, - boolean left) + void drawVerticalScale(Graphics g, final int startx, final int endx, + final int ypos, final boolean left) { - ypos += charHeight; + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + + 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()); @@ -219,9 +221,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * find sequence position of first non-gapped position - * to the right if scale left, to the left if scale right */ - int index = left ? startx : endx; + int index = left ? startX : endX; int value = -1; - while (index >= startx && index <= endx) + while (index >= startX && index <= endX) { if (!Comparison.isGap(seq.getCharAt(index))) { @@ -242,30 +244,27 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * white fill the space for the scale */ g.setColor(Color.white); - int y = (ypos + (i * charHeight)) - (charHeight / 5); - y -= charHeight; // fillRect: origin is top left of rectangle - int xpos = left ? 0 : labelWidthWest + charWidth - * av.getRanges().getViewportWidth(); - g.fillRect(xpos, y, left ? labelWidthWest : labelWidthEast, + int y = (yPos + (i * charHeight)) - (charHeight / 5); + // fillRect origin is top left of rectangle + g.fillRect(0, y - charHeight, left ? labelWidthWest : labelWidthEast, charHeight + 1); - y += charHeight; // drawString: origin is bottom left of text if (value != -1) { - /* - * draw scale value, right justified, with half a character width - * separation from the sequence data + * draw scale value, right justified within its width less half a + * character width padding on the right */ + int labelSpace = left ? labelWidthWest : labelWidthEast; + labelSpace -= charWidth / 2; // leave space to the right String valueAsString = String.valueOf(value); - int justify = fm.stringWidth(valueAsString) + charWidth; - xpos = left ? labelWidthWest - justify + charWidth / 2 - : getWidth() - justify - charWidth / 2; - + int labelLength = fm.stringWidth(valueAsString); + int xOffset = labelSpace - labelLength; g.setColor(Color.black); - g.drawString(valueAsString, xpos, y); + g.drawString(valueAsString, xOffset, y); } } + } /** @@ -279,6 +278,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI *
- * 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) { return false; } boolean wrapped = av.getWrapAlignment(); - try { - fastPaint = !noFastPaint; + fastPaint = doFastPaint; fastpainting = fastPaint; - updateViewport(); - /* * to avoid redrawing the whole visible region, we instead * redraw just the minimal regions to remove previous highlights @@ -1232,7 +1601,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; } @@ -1254,9 +1623,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 @@ -1299,14 +1668,17 @@ 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); + drawPanel(seqRdr, gg, firstCol, lastCol, firstSeq, lastSeq, 0); gg.translate(-transX, -transY); + gg.dispose(); } return matchFound; @@ -1316,15 +1688,39 @@ public class SeqCanvas extends JComponent implements ViewportListenerI public void propertyChange(PropertyChangeEvent evt) { String eventName = evt.getPropertyName(); + // jalview.bin.Console.errPrintln(">>SeqCanvas propertyChange " + eventName); + if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED)) + { + fastPaint = true; + repaint(); + return; + } + else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT)) + { + fastPaint = false; + // jalview.bin.Console.errPrintln("!!!! fastPaint false from MOVE_VIEWPORT"); + 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.getEndRes() - vpRanges.getStartRes(); + + int range = vpRanges.getEndRes() - vpRanges.getStartRes() + 1; if (scrollX > range) { scrollX = range; @@ -1334,7 +1730,6 @@ 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 @@ -1342,7 +1737,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI // vertical start value. if (eventName.equals(ViewportRanges.STARTRES)) { - // scroll - startres and endres both change if (av.getWrapAlignment()) { fastPaintWrapped(scrollX); @@ -1354,8 +1748,32 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } else if (eventName.equals(ViewportRanges.STARTSEQ)) { + // scroll fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); } + else if (eventName.equals(ViewportRanges.STARTRESANDSEQ)) + { + if (av.getWrapAlignment()) + { + fastPaintWrapped(scrollX); + } + else + { + fastPaint(scrollX, 0); + } + } + else if (eventName.equals(ViewportRanges.STARTSEQ)) + { + // scroll + fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); + } + else if (eventName.equals(ViewportRanges.STARTRESANDSEQ)) + { + if (av.getWrapAlignment()) + { + fastPaintWrapped(scrollX); + } + } } /** @@ -1371,10 +1789,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; @@ -1382,7 +1800,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return; } - if (fastpainting || gg == null) + if (fastpainting || img == null) { return; } @@ -1392,6 +1810,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI try { + + Graphics gg = img.getGraphics(); + calculateWrappedGeometry(getWidth(), getHeight()); /* @@ -1407,8 +1828,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (scrollX < 0) { int startRes = ranges.getStartRes(); - drawWrappedWidth(gg, wrappedSpaceAboveAlignment, startRes, startRes - - scrollX - 1, getHeight()); + drawWrappedWidth(seqRdr,gg, wrappedSpaceAboveAlignment, startRes, + startRes - scrollX - 1, getHeight()); } else { @@ -1420,6 +1841,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ drawWrappedDecorators(gg, ranges.getStartRes()); + gg.dispose(); + repaint(); } finally { @@ -1443,8 +1866,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return; } + Graphics gg = img.getGraphics(); + ViewportRanges ranges = av.getRanges(); int viewportWidth = ranges.getViewportWidth(); + int charWidth = av.getCharWidth(); /** * draw full height alignment in the second last row, last columns, if the @@ -1452,11 +1878,12 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ int visibleWidths = wrappedVisibleWidths; int canvasHeight = getHeight(); - boolean lastWidthPartHeight = (wrappedVisibleWidths * wrappedRepeatHeightPx) > canvasHeight; + boolean lastWidthPartHeight = (wrappedVisibleWidths + * wrappedRepeatHeightPx) > canvasHeight; if (lastWidthPartHeight) { - int widthsAbove = visibleWidths - 2; + int widthsAbove = Math.max(0, visibleWidths - 2); int ypos = wrappedRepeatHeightPx * widthsAbove + wrappedSpaceAboveAlignment; int endRes = ranges.getEndRes(); @@ -1468,16 +1895,20 @@ 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, - (endRes - startRes + 1) * charWidth, wrappedRepeatHeightPx); + gg.fillRect(labelWidthWest, ypos, (endRes - startRes + 1) * charWidth, + wrappedRepeatHeightPx); gg.translate(-xOffset, 0); - drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight); + drawWrappedWidth(seqRdr, gg, ypos, startRes, endRes, canvasHeight); + } /* + * draw newly visible columns in last wrapped width (none if we + * have reached the end of the alignment) * y-offset for drawing last width is height of widths above, * plus one gap row */ @@ -1486,7 +1917,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI + wrappedSpaceAboveAlignment; int endRes = ranges.getEndRes(); endRes += widthsAbove * viewportWidth; - endRes = Math.min(endRes, ranges.getVisibleAlignmentWidth()); int startRes = endRes - columns + 1; /* @@ -1503,7 +1933,10 @@ public class SeqCanvas extends JComponent implements ViewportListenerI gg.setFont(av.getFont()); gg.setColor(Color.black); - drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight); + if (startRes < ranges.getVisibleAlignmentWidth()) + { + drawWrappedWidth(seqRdr, gg, ypos, startRes, endRes, canvasHeight); + } /* * and finally, white fill any space below the visible alignment @@ -1514,6 +1947,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI gg.setColor(Color.white); gg.fillRect(0, canvasHeight - heightBelow, getWidth(), heightBelow); } + gg.dispose(); } /** @@ -1531,6 +1965,10 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return; } + Graphics gg = img.getGraphics(); + + int charWidth = av.getCharWidth(); + int canvasHeight = getHeight(); ViewportRanges ranges = av.getRanges(); int viewportWidth = ranges.getViewportWidth(); @@ -1559,10 +1997,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); @@ -1591,15 +2036,15 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (y + wrappedRepeatHeightPx < canvasHeight - wrappedRepeatHeightPx && (xpos + viewportWidth <= xMax)) { - gg.copyArea(labelWidthWest, y + wrappedRepeatHeightPx, -positions - * charWidth, heightToCopy, widthToCopy, + gg.copyArea(labelWidthWest, y + wrappedRepeatHeightPx, + -positions * charWidth, heightToCopy, widthToCopy, -wrappedRepeatHeightPx); } - y += wrappedRepeatHeightPx; xpos += viewportWidth; } } + gg.dispose(); } /** @@ -1614,15 +2059,17 @@ 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; } - + int charHeight = av.getCharHeight(); + boolean matchFound = false; + calculateWrappedGeometry(getWidth(), getHeight()); int wrappedWidth = av.getWrappedWidth(); - int wrappedHeight = getRepeatHeightWrapped(); + int wrappedHeight = wrappedRepeatHeightPx; ViewportRanges ranges = av.getRanges(); int canvasHeight = getHeight(); @@ -1633,20 +2080,22 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } int firstVisibleColumn = ranges.getStartRes(); - int lastVisibleColumn = ranges.getStartRes() + repeats - * ranges.getViewportWidth() - 1; + int lastVisibleColumn = ranges.getStartRes() + + repeats * ranges.getViewportWidth() - 1; AlignmentI alignment = av.getAlignment(); 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++) { @@ -1681,15 +2130,15 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (av.hasHiddenColumns()) { displayColumn = alignment.getHiddenColumns() - .findColumnPosition(displayColumn); + .absoluteToVisibleColumn(displayColumn); } /* * transX: offset from left edge of canvas to residue position */ int transX = labelWidthWest - + ((displayColumn - ranges.getStartRes()) % wrappedWidth) - * av.getCharWidth(); + + ((displayColumn - ranges.getStartRes()) + % wrappedWidth) * av.getCharWidth(); /* * transY: offset from top edge of canvas to residue position @@ -1707,7 +2156,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { matchFound = true; gg.translate(transX, transY); - drawPanel(gg, displayColumn, displayColumn, seqNo, seqNo, + drawPanel(seqRdr,gg, displayColumn, displayColumn, seqNo, seqNo, yOffset); gg.translate(-transX, -transY); } @@ -1716,28 +2165,20 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } } } - + + gg.dispose(); + return matchFound; } /** - * Answers the height in pixels of a repeating section of the wrapped - * alignment, including space above, scale above if shown, sequences, and - * annotation panel if shown + * Answers the width in pixels of the left scale labels (0 if not shown) * * @return */ - protected int getRepeatHeightWrapped() + int getLabelWidthWest() { - // gap (and maybe scale) above - int repeatHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1); - - // add sequences - repeatHeight += av.getRanges().getViewportHeight() * charHeight; - - // add annotations panel height if shown - repeatHeight += getAnnotationHeight(); - - return repeatHeight; + return labelWidthWest; } + }