From: kiramt Date: Tue, 31 Oct 2017 13:59:31 +0000 (+0000) Subject: Merge remote-tracking branch 'origin/develop' into imp/JAL-2774 X-Git-Tag: Release_2_10_4~67^2~20 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=c78840385a5ac9dd6d16a37aa7b55cfaeb360456;hp=0a253653ec4c4a607ba3029444b2790721361766;p=jalview.git Merge remote-tracking branch 'origin/develop' into imp/JAL-2774 Conflicts: src/jalview/gui/SeqCanvas.java --- diff --git a/src/jalview/appletgui/AnnotationPanel.java b/src/jalview/appletgui/AnnotationPanel.java index c06f7b1..2b2fdea 100755 --- a/src/jalview/appletgui/AnnotationPanel.java +++ b/src/jalview/appletgui/AnnotationPanel.java @@ -778,5 +778,10 @@ public class AnnotationPanel extends Panel { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } + else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ)) + { + fastPaint(((int[]) evt.getNewValue())[0] + - ((int[]) evt.getOldValue())[0]); + } } } diff --git a/src/jalview/appletgui/IdCanvas.java b/src/jalview/appletgui/IdCanvas.java index 5eddc4f..dc906f4 100755 --- a/src/jalview/appletgui/IdCanvas.java +++ b/src/jalview/appletgui/IdCanvas.java @@ -448,5 +448,10 @@ public class IdCanvas extends Panel implements ViewportListenerI { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } + else if (propertyName.equals(ViewportRanges.STARTRESANDSEQ)) + { + fastPaint(((int[]) evt.getNewValue())[1] + - ((int[]) evt.getOldValue())[1]); + } } } diff --git a/src/jalview/appletgui/ScalePanel.java b/src/jalview/appletgui/ScalePanel.java index 7d4150d..75d4040 100755 --- a/src/jalview/appletgui/ScalePanel.java +++ b/src/jalview/appletgui/ScalePanel.java @@ -468,7 +468,8 @@ public class ScalePanel extends Panel // 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 (evt.getPropertyName().equals(ViewportRanges.STARTRES)) + if (evt.getPropertyName().equals(ViewportRanges.STARTRES) + || evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ)) { // scroll event, repaint panel repaint(); diff --git a/src/jalview/appletgui/SeqCanvas.java b/src/jalview/appletgui/SeqCanvas.java index f59967d..ef47f75 100755 --- a/src/jalview/appletgui/SeqCanvas.java +++ b/src/jalview/appletgui/SeqCanvas.java @@ -892,12 +892,21 @@ public class SeqCanvas extends Panel implements ViewportListenerI if (!av.getWrapAlignment()) { 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(); if (scrollX > range) { @@ -924,6 +933,11 @@ public class SeqCanvas extends Panel implements ViewportListenerI // 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]); + } } } diff --git a/src/jalview/gui/AnnotationPanel.java b/src/jalview/gui/AnnotationPanel.java index 12d1369..ef41047 100755 --- a/src/jalview/gui/AnnotationPanel.java +++ b/src/jalview/gui/AnnotationPanel.java @@ -1185,5 +1185,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } + else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ)) + { + fastPaint(((int[]) evt.getNewValue())[0] + - ((int[]) evt.getOldValue())[0]); + } } } diff --git a/src/jalview/gui/IdCanvas.java b/src/jalview/gui/IdCanvas.java index a7dff86..235dea0 100755 --- a/src/jalview/gui/IdCanvas.java +++ b/src/jalview/gui/IdCanvas.java @@ -564,5 +564,10 @@ public class IdCanvas extends JPanel implements ViewportListenerI { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } + else if (propertyName.equals(ViewportRanges.STARTRESANDSEQ)) + { + fastPaint(((int[]) evt.getNewValue())[1] + - ((int[]) evt.getOldValue())[1]); + } } } diff --git a/src/jalview/gui/ScalePanel.java b/src/jalview/gui/ScalePanel.java index e677769..2dc198e 100755 --- a/src/jalview/gui/ScalePanel.java +++ b/src/jalview/gui/ScalePanel.java @@ -549,7 +549,8 @@ public class ScalePanel extends JPanel // 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 (evt.getPropertyName().equals(ViewportRanges.STARTRES)) + if (evt.getPropertyName().equals(ViewportRanges.STARTRES) + || evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ)) { // scroll event, repaint panel repaint(); diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 2a9c704..320102f 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -27,7 +27,6 @@ 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; @@ -47,56 +46,53 @@ import java.util.List; import javax.swing.JComponent; /** - * 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. + * DOCUMENT ME! * + * @author $author$ + * @version $Revision$ */ public class SeqCanvas extends JComponent implements ViewportListenerI { - private static final String ZEROS = "0000000000"; + private static String ZEROS = "0000000000"; final FeatureRenderer fr; - BufferedImage img; - - AlignViewport av; + final SequenceRenderer seqRdr; - int cursorX = 0; + BufferedImage img; - int cursorY = 0; + Graphics2D gg; - private final SequenceRenderer seqRdr; + AlignViewport av; - private boolean fastPaint = false; + boolean fastPaint = false; - private boolean fastpainting = false; + int labelWidthWest; - private AnnotationPanel annotations; + int labelWidthEast; - /* - * measurements for drawing a wrapped alignment - */ - private int labelWidthEast; // label right width in pixels if shown + int cursorX = 0; - private int labelWidthWest; // label left width in pixels if shown + int cursorY = 0; - private int wrappedSpaceAboveAlignment; // gap between widths + int charHeight = 0; - private int wrappedRepeatHeightPx; // height in pixels of wrapped width + int charWidth = 0; - private int wrappedVisibleWidths; // number of wrapped widths displayed + boolean fastpainting = false; - private Graphics2D gg; + AnnotationPanel annotations; /** * Creates a new SeqCanvas object. * - * @param ap + * @param av + * DOCUMENT ME! */ public SeqCanvas(AlignmentPanel ap) { this.av = ap.av; + updateViewport(); fr = new FeatureRenderer(ap); seqRdr = new SequenceRenderer(av); setLayout(new BorderLayout()); @@ -116,36 +112,29 @@ 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). + * DOCUMENT ME! * * @param g - * the graphics context to draw on, positioned at the start (bottom - * left) of the line on which to draw any scale marks + * DOCUMENT ME! * @param startx - * start alignment column (0..) + * DOCUMENT ME! * @param endx - * end alignment column (0..) + * DOCUMENT ME! * @param ypos - * y offset to draw at + * DOCUMENT ME! */ private void drawNorthScale(Graphics g, int startx, int endx, int ypos) { - int charHeight = av.getCharHeight(); - int charWidth = av.getCharWidth(); - - /* - * white fill the scale space (for the fastPaint case) - */ - g.setColor(Color.white); - g.fillRect(0, ypos - charHeight - charHeight / 2, getWidth(), - charHeight * 3 / 2 + 2); - g.setColor(Color.black); - - List marks = new ScaleRenderer().calculateMarks(av, startx, - endx); - for (ScaleMark mark : marks) + updateViewport(); + for (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx, + endx)) { int mpos = mark.column; // (i - startx - 1) if (mpos < 0) @@ -160,119 +149,137 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { g.drawString(mstring, mpos * charWidth, ypos - (charHeight / 2)); } - - /* - * draw a tick mark below the column number, centred on the column; - * height of tick mark is 4 pixels less than half a character - */ - int xpos = (mpos * charWidth) + (charWidth / 2); - g.drawLine(xpos, (ypos + 2) - (charHeight / 2), xpos, ypos - 2); + g.drawLine((mpos * charWidth) + (charWidth / 2), + (ypos + 2) - (charHeight / 2), + (mpos * charWidth) + (charWidth / 2), ypos - 2); } } } /** - * Draw the scale to the left or right of a wrapped alignment + * DOCUMENT ME! * * @param g - * graphics context, positioned at the start of the scale to be drawn + * DOCUMENT ME! * @param startx - * first column of wrapped width (0.. excluding any hidden columns) + * DOCUMENT ME! * @param endx - * last column of wrapped width (0.. excluding any hidden columns) + * DOCUMENT ME! * @param ypos - * vertical offset at which to begin the scale - * @param left - * if true, scale is left of residues, if false, scale is right + * DOCUMENT ME! */ - void drawVerticalScale(Graphics g, final int startx, final int endx, - final int ypos, final boolean left) + void drawWestScale(Graphics g, int startx, int endx, int ypos) { - int charHeight = av.getCharHeight(); - int charWidth = av.getCharWidth(); + FontMetrics fm = getFontMetrics(av.getFont()); + ypos += charHeight; - int yPos = ypos + charHeight; - int startX = startx; - int endX = endx; + if (av.hasHiddenColumns()) + { + startx = av.getAlignment().getHiddenColumns() + .adjustForHiddenColumns(startx); + endx = av.getAlignment().getHiddenColumns() + .adjustForHiddenColumns(endx); + } + int maxwidth = av.getAlignment().getWidth(); if (av.hasHiddenColumns()) { - HiddenColumns hiddenColumns = av.getAlignment().getHiddenColumns(); - startX = hiddenColumns.adjustForHiddenColumns(startx); - endX = hiddenColumns.adjustForHiddenColumns(endx); + maxwidth = av.getAlignment().getHiddenColumns() + .findColumnPosition(maxwidth) - 1; } - FontMetrics fm = getFontMetrics(av.getFont()); + // WEST SCALE for (int i = 0; i < av.getAlignment().getHeight(); i++) { SequenceI seq = av.getAlignment().getSequenceAt(i); - - /* - * 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 = startx; int value = -1; - while (index >= startX && index <= endX) + + while (index < endx) { - if (!Comparison.isGap(seq.getCharAt(index))) - { - value = seq.findPosition(index); - break; - } - if (left) + if (jalview.util.Comparison.isGap(seq.getCharAt(index))) { index++; + + continue; } - else + + value = av.getAlignment().getSequenceAt(i).findPosition(index); + + break; + } + + if (value != -1) + { + int x = labelWidthWest - fm.stringWidth(String.valueOf(value)) + - charWidth / 2; + g.drawString(value + "", x, + (ypos + (i * charHeight)) - (charHeight / 5)); + } + } + } + + /** + * DOCUMENT ME! + * + * @param g + * DOCUMENT ME! + * @param startx + * DOCUMENT ME! + * @param endx + * DOCUMENT ME! + * @param ypos + * DOCUMENT ME! + */ + void drawEastScale(Graphics g, int startx, int endx, int ypos) + { + ypos += charHeight; + + if (av.hasHiddenColumns()) + { + endx = av.getAlignment().getHiddenColumns() + .adjustForHiddenColumns(endx); + } + + SequenceI seq; + // EAST SCALE + for (int i = 0; i < av.getAlignment().getHeight(); i++) + { + seq = av.getAlignment().getSequenceAt(i); + int index = endx; + int value = -1; + + while (index > startx) + { + if (jalview.util.Comparison.isGap(seq.getCharAt(index))) { index--; + + continue; } - } - /* - * white fill the space for the scale - */ - g.setColor(Color.white); - int y = (yPos + (i * charHeight)) - (charHeight / 5); - // fillRect origin is top left of rectangle - g.fillRect(0, y - charHeight, left ? labelWidthWest : labelWidthEast, - charHeight + 1); + value = seq.findPosition(index); + + break; + } if (value != -1) { - /* - * 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 labelLength = fm.stringWidth(valueAsString); - int xOffset = labelSpace - labelLength; - g.setColor(Color.black); - g.drawString(valueAsString, xOffset, y); + g.drawString(String.valueOf(value), 0, + (ypos + (i * charHeight)) - (charHeight / 5)); } } } + /** - * Does a fast paint of an alignment in response to a scroll. Most of the - * visible region is simply copied and shifted, and then any newly visible - * columns or rows are drawn. The scroll may be horizontal or vertical, but - * not both at once. Scrolling may be the result of - * + * need to make this thread safe move alignment rendering in response to + * slider adjustment * * @param horizontal - * columns to shift right (positive) or left (negative) + * shift along * @param vertical - * rows to shift down (positive) or up (negative) + * shift up or down in repaint */ public void fastPaint(int horizontal, int vertical) { @@ -282,19 +289,15 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } fastpainting = true; fastPaint = true; + updateViewport(); - try - { - int charHeight = av.getCharHeight(); - int charWidth = av.getCharWidth(); - - ViewportRanges ranges = av.getRanges(); - int startRes = ranges.getStartRes(); - int endRes = ranges.getEndRes(); - int startSeq = ranges.getStartSeq(); - int endSeq = ranges.getEndSeq(); - int transX = 0; - int transY = 0; + ViewportRanges ranges = av.getRanges(); + int startRes = ranges.getStartRes(); + int endRes = ranges.getEndRes(); + int startSeq = ranges.getStartSeq(); + int endSeq = ranges.getEndSeq(); + int transX = 0; + int transY = 0; gg.copyArea(horizontal * charWidth, vertical * charHeight, img.getWidth(), img.getHeight(), -horizontal * charWidth, @@ -337,19 +340,15 @@ public class SeqCanvas extends JComponent implements ViewportListenerI gg.translate(-transX, -transY); repaint(); - } finally - { - fastpainting = false; - } + fastpainting = false; } @Override public void paintComponent(Graphics g) { - super.paintComponent(g); - - int charHeight = av.getCharHeight(); - int charWidth = av.getCharWidth(); + super.paintComponent(g); + + updateViewport(); ViewportRanges ranges = av.getRanges(); @@ -412,7 +411,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI g.drawImage(lcimg, 0, 0, this); } } - + /** * Draw an alignment panel for printing * @@ -520,9 +519,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { BufferedImage lcimg = null; - int charWidth = av.getCharWidth(); - int charHeight = av.getCharHeight(); - int width = getWidth(); int height = getHeight(); @@ -562,30 +558,29 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ public int getWrappedCanvasWidth(int canvasWidth) { - int charWidth = av.getCharWidth(); - FontMetrics fm = getFontMetrics(av.getFont()); - int labelWidth = 0; - - if (av.getScaleRightWrapped() || av.getScaleLeftWrapped()) + labelWidthEast = 0; + labelWidthWest = 0; + + if (av.getScaleRightWrapped()) { - labelWidth = getLabelWidth(fm); + labelWidthEast = getLabelWidth(fm); } - labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0; - - labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0; + if (av.getScaleLeftWrapped()) + { + labelWidthWest = labelWidthEast > 0 ? labelWidthEast + : getLabelWidth(fm); + } return (canvasWidth - labelWidthEast - labelWidthWest) / charWidth; } /** - * Returns a pixel width sufficient to show the largest sequence coordinate - * (end position) in the alignment, calculated as the FontMetrics width of - * zeroes "0000000" limited to the number of decimal digits to be shown (3 for - * 1-10, 4 for 11-99 etc). One character width is added to this, to allow for - * half a character width space on either side. + * Returns a pixel width suitable for showing the largest sequence coordinate + * (end position) in the alignment. Returns 2 plus the number of decimal + * digits to be shown (3 for 1-10, 4 for 11-99 etc). * * @param fm * @return @@ -603,305 +598,160 @@ public class SeqCanvas extends JComponent implements ViewportListenerI maxWidth = Math.max(maxWidth, alignment.getSequenceAt(i).getEnd()); } - int length = 0; + int length = 2; for (int i = maxWidth; i > 0; i /= 10) { length++; } - return fm.stringWidth(ZEROS.substring(0, length)) + av.getCharWidth(); + return fm.stringWidth(ZEROS.substring(0, length)); } /** - * Draws as many widths of a wrapped alignment as can fit in the visible - * window + * DOCUMENT ME! * * @param g + * DOCUMENT ME! * @param canvasWidth - * available width in pixels + * DOCUMENT ME! * @param canvasHeight - * 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) - { - int wrappedWidthInResidues = calculateWrappedGeometry(canvasWidth, - canvasHeight); - - av.setWrappedWidth(wrappedWidthInResidues); - - ViewportRanges ranges = av.getRanges(); - ranges.setViewportStartAndWidth(startColumn, wrappedWidthInResidues); - - /* - * draw one width at a time (including any scales or annotation shown), - * until we have run out of either alignment or vertical space available - */ - int ypos = wrappedSpaceAboveAlignment; - int maxWidth = ranges.getVisibleAlignmentWidth(); - - int start = startColumn; - int currentWidth = 0; - while ((currentWidth < wrappedVisibleWidths) && (start < maxWidth)) - { - int endColumn = Math - .min(maxWidth, start + wrappedWidthInResidues - 1); - drawWrappedWidth(g, ypos, start, endColumn, canvasHeight); - ypos += wrappedRepeatHeightPx; - start += wrappedWidthInResidues; - currentWidth++; - } - - drawWrappedDecorators(g, startColumn); - } - - /** - * Calculates and saves values needed when rendering a wrapped alignment. - * These depend on many factors, including - * - * - * @param canvasWidth - * @param canvasHeight - * @return the number of residue columns in each width + * DOCUMENT ME! + * @param startRes + * DOCUMENT ME! */ - protected int calculateWrappedGeometry(int canvasWidth, int canvasHeight) + private void drawWrappedPanel(Graphics g, int canvasWidth, + int canvasHeight, int startRes) { - int charHeight = av.getCharHeight(); - - /* - * vertical space in pixels between wrapped widths of alignment - * - one character height, or two if scale above is drawn - */ - wrappedSpaceAboveAlignment = charHeight - * (av.getScaleAboveWrapped() ? 2 : 1); - - /* - * height in pixels of the wrapped widths - */ - wrappedRepeatHeightPx = wrappedSpaceAboveAlignment; - // add sequences - wrappedRepeatHeightPx += av.getRanges().getViewportHeight() - * charHeight; - // add annotations panel height if shown - wrappedRepeatHeightPx += getAnnotationHeight(); + updateViewport(); + AlignmentI al = av.getAlignment(); - /* - * 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; - if (remainder >= (wrappedSpaceAboveAlignment + charHeight)) + int labelWidth = 0; + if (av.getScaleRightWrapped() || av.getScaleLeftWrapped()) { - wrappedVisibleWidths++; + FontMetrics fm = getFontMetrics(av.getFont()); + labelWidth = getLabelWidth(fm); } - /* - * compute width in residues; this also sets East and West label widths - */ - int wrappedWidthInResidues = getWrappedCanvasWidth(canvasWidth); + labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0; + labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0; - /* - * limit visibleWidths to not exceed width of alignment - */ - int xMax = ranges.getVisibleAlignmentWidth(); - int startToEnd = xMax - ranges.getStartRes(); - int maxWidths = startToEnd / wrappedWidthInResidues; - if (startToEnd % wrappedWidthInResidues > 0) + int hgap = charHeight; + if (av.getScaleAboveWrapped()) { - maxWidths++; + hgap += charHeight; } - wrappedVisibleWidths = Math.min(wrappedVisibleWidths, maxWidths); - - return wrappedWidthInResidues; - } - /** - * Draws one width of a wrapped alignment, including sequences and - * annnotations, if shown, but not scales or hidden column markers - * - * @param g - * @param ypos - * @param startColumn - * @param endColumn - * @param canvasHeight - */ - protected void drawWrappedWidth(Graphics g, int ypos, int startColumn, - int endColumn, int canvasHeight) - { - ViewportRanges ranges = av.getRanges(); - int viewportWidth = ranges.getViewportWidth(); + int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) / charWidth; + int cHeight = av.getAlignment().getHeight() * charHeight; - int endx = Math.min(startColumn + viewportWidth - 1, endColumn); + av.setWrappedWidth(cWidth); - /* - * move right before drawing by the width of the scale left (if any) - * plus column offset from left margin (usually zero, but may be non-zero - * when fast painting is drawing just a few columns) - */ - int charWidth = av.getCharWidth(); - int xOffset = labelWidthWest - + ((startColumn - ranges.getStartRes()) % viewportWidth) - * charWidth; - g.translate(xOffset, 0); + av.getRanges().setViewportStartAndWidth(startRes, cWidth); - // When printing we have an extra clipped region, - // the Printable page which we need to account for here - Shape clip = g.getClip(); + int endx; + int ypos = hgap; + int maxwidth = av.getAlignment().getWidth(); - if (clip == null) - { - g.setClip(0, 0, viewportWidth * charWidth, canvasHeight); - } - else + if (av.hasHiddenColumns()) { - g.setClip(0, (int) clip.getBounds().getY(), - viewportWidth * charWidth, (int) clip.getBounds().getHeight()); + maxwidth = av.getAlignment().getHiddenColumns() + .findColumnPosition(maxwidth); } - /* - * 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, - wrappedRepeatHeightPx); - - drawPanel(g, startColumn, endx, 0, av.getAlignment().getHeight() - 1, - ypos); - - int cHeight = av.getAlignment().getHeight() * av.getCharHeight(); + int annotationHeight = getAnnotationHeight(); - if (av.isShowAnnotation()) + while ((ypos <= canvasHeight) && (startRes < maxwidth)) { - g.translate(0, cHeight + ypos + 3); - if (annotations == null) + endx = startRes + cWidth - 1; + + if (endx > maxwidth) { - annotations = new AnnotationPanel(av); + endx = maxwidth; } - annotations.renderer.drawComponent(annotations, av, g, -1, - startColumn, endx + 1); - g.translate(0, -cHeight - ypos - 3); - } - g.setClip(clip); - g.translate(-xOffset, 0); - } - - /** - * Draws scales left, right and above (if shown), and any hidden column - * markers, on all widths of the wrapped alignment - * - * @param g - * @param startColumn - */ - protected void drawWrappedDecorators(Graphics g, final int startColumn) - { - int charWidth = av.getCharWidth(); - - g.setFont(av.getFont()); - g.setColor(Color.black); - - int ypos = wrappedSpaceAboveAlignment; - ViewportRanges ranges = av.getRanges(); - int viewportWidth = ranges.getViewportWidth(); - int maxWidth = ranges.getVisibleAlignmentWidth(); - int widthsDrawn = 0; - int startCol = startColumn; - - while (widthsDrawn < wrappedVisibleWidths) - { - int endColumn = Math.min(maxWidth, startCol + viewportWidth - 1); + g.setFont(av.getFont()); + g.setColor(Color.black); if (av.getScaleLeftWrapped()) { - drawVerticalScale(g, startCol, endColumn - 1, ypos, true); + drawWestScale(g, startRes, endx, ypos); } if (av.getScaleRightWrapped()) { - int x = labelWidthWest + viewportWidth * charWidth; - g.translate(x, 0); - drawVerticalScale(g, startCol, endColumn, ypos, false); - g.translate(-x, 0); + g.translate(canvasWidth - labelWidthEast, 0); + drawEastScale(g, startRes, endx, ypos); + g.translate(-(canvasWidth - labelWidthEast), 0); } - /* - * white fill region of scale above and hidden column markers - * (to support incremental fast paint of image) - */ - g.translate(labelWidthWest, 0); - g.setColor(Color.white); - g.fillRect(0, ypos - wrappedSpaceAboveAlignment, viewportWidth - * charWidth + labelWidthWest, wrappedSpaceAboveAlignment); - g.setColor(Color.black); - g.translate(-labelWidthWest, 0); - g.translate(labelWidthWest, 0); if (av.getScaleAboveWrapped()) { - drawNorthScale(g, startCol, endColumn, ypos); + drawNorthScale(g, startRes, endx, ypos); } if (av.hasHiddenColumns() && av.getShowHiddenMarkers()) { - drawHiddenColumnMarkers(g, ypos, startCol, endColumn); - } + g.setColor(Color.blue); + int res; + HiddenColumns hidden = av.getAlignment().getHiddenColumns(); + List positions = hidden.findHiddenRegionPositions(); + for (int pos : positions) + { + res = pos - startRes; - g.translate(-labelWidthWest, 0); + if (res < 0 || res > endx - startRes) + { + continue; + } - ypos += wrappedRepeatHeightPx; - startCol += viewportWidth; - widthsDrawn++; - } - } + gg.fillPolygon( + new int[] + { res * charWidth - charHeight / 4, + res * charWidth + charHeight / 4, res * charWidth }, + new int[] + { ypos - (charHeight / 2), ypos - (charHeight / 2), + ypos - (charHeight / 2) + 8 }, + 3); - /** - * Draws markers (triangles) above hidden column positions between startColumn - * and endColumn. - * - * @param g - * @param ypos - * @param startColumn - * @param endColumn - */ - protected void drawHiddenColumnMarkers(Graphics g, int ypos, - int startColumn, int endColumn) - { - int charHeight = av.getCharHeight(); - int charWidth = av.getCharWidth(); + } + } - g.setColor(Color.blue); - HiddenColumns hidden = av.getAlignment().getHiddenColumns(); - List positions = hidden.findHiddenRegionPositions(); - for (int pos : positions) - { - int res = pos - startColumn; + // When printing we have an extra clipped region, + // the Printable page which we need to account for here + Shape clip = g.getClip(); - if (res < 0 || res > endColumn - startColumn + 1) + if (clip == null) { - continue; + g.setClip(0, 0, cWidth * charWidth, canvasHeight); + } + else + { + g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth, + (int) clip.getBounds().getHeight()); } - /* - * draw a downward-pointing triangle at the hidden columns location - * (before the following visible column) - */ - int xMiddle = res * charWidth; - int[] xPoints = new int[] { xMiddle - charHeight / 4, - xMiddle + charHeight / 4, xMiddle }; - int yTop = ypos - (charHeight / 2); - int[] yPoints = new int[] { yTop, yTop, yTop + 8 }; - g.fillPolygon(xPoints, yPoints, 3); + drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos); + + if (av.isShowAnnotation()) + { + g.translate(0, cHeight + ypos + 3); + if (annotations == null) + { + annotations = new AnnotationPanel(av); + } + + annotations.renderer.drawComponent(annotations, av, g, -1, startRes, + endx + 1); + g.translate(0, -cHeight - ypos - 3); + } + g.setClip(clip); + g.translate(-labelWidthWest, 0); + + ypos += cHeight + annotationHeight + hgap; + + startRes += cWidth; } } @@ -912,9 +762,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int canvasWidth, int canvasHeight, int startRes) { - int charHeight = av.getCharHeight(); - int charWidth = av.getCharWidth(); - // height gap above each panel int hgap = charHeight; if (av.getScaleAboveWrapped()) @@ -987,24 +834,22 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * marker. * * @param g1 - * the graphics context, positioned at the first residue to be drawn + * Graphics object to draw with * @param startRes - * offset of the first column to draw (0..) + * offset of the first column in the visible region (0..) * @param endRes - * offset of the last column to draw (0..) + * offset of the last column in the visible region (0..) * @param startSeq - * offset of the first sequence to draw (0..) + * offset of the first sequence in the visible region (0..) * @param endSeq - * offset of the last sequence to draw (0..) + * offset of the last sequence in the visible region (0..) * @param yOffset * vertical offset at which to draw (for wrapped alignments) */ public void drawPanel(Graphics g1, final int startRes, final int endRes, final int startSeq, final int endSeq, final int yOffset) { - int charHeight = av.getCharHeight(); - int charWidth = av.getCharWidth(); - + updateViewport(); if (!av.hasHiddenColumns()) { draw(g1, startRes, endRes, startSeq, endSeq, yOffset); @@ -1094,9 +939,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI private void draw(Graphics g, int startRes, int endRes, int startSeq, int endSeq, int offset) { - int charHeight = av.getCharHeight(); - int charWidth = av.getCharWidth(); - g.setFont(av.getFont()); seqRdr.prepare(g, av.isRenderGaps()); @@ -1276,8 +1118,6 @@ 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(); - if (!av.hasHiddenColumns()) { drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq, @@ -1339,9 +1179,6 @@ 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 visWidth = (endRes - startRes + 1) * charWidth; int oldY = -1; @@ -1510,11 +1347,14 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return false; } boolean wrapped = av.getWrapAlignment(); + try { fastPaint = !noFastPaint; fastpainting = fastPaint; + updateViewport(); + /* * to avoid redrawing the whole visible region, we instead * redraw just the minimal regions to remove previous highlights @@ -1649,6 +1489,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return matchFound; } + @Override public void propertyChange(PropertyChangeEvent evt) { @@ -1658,303 +1499,68 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { fastPaint = true; repaint(); - return; } - - int scrollX = 0; - if (eventName.equals(ViewportRanges.STARTRES)) + else if (av.getWrapAlignment()) { - // Make sure we're not trying to draw a panel - // larger than the visible window - ViewportRanges vpRanges = av.getRanges(); - scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); - int range = vpRanges.getViewportWidth(); - if (scrollX > range) - { - scrollX = range; - } - else if (scrollX < -range) + if (eventName.equals(ViewportRanges.STARTRES) + || eventName.equals(ViewportRanges.STARTRESANDSEQ)) { - scrollX = -range; + repaint(); } } - - // 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)) + else { - if (av.getWrapAlignment()) - { - fastPaintWrapped(scrollX); - } - else + int scrollX = 0; + if (eventName.equals(ViewportRanges.STARTRES) + || eventName.equals(ViewportRanges.STARTRESANDSEQ)) { - fastPaint(scrollX, 0); - } - } - else if (eventName.equals(ViewportRanges.STARTSEQ)) - { - // scroll - fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); - } - } - - /** - * Does a minimal update of the image for a scroll movement. This method - * handles scroll movements of up to one width of the wrapped alignment (one - * click in the vertical scrollbar). Larger movements (for example after a - * scroll to highlight a mapped position) trigger a full redraw instead. - * - * @param scrollX - * number of positions scrolled (right if positive, left if negative) - */ - protected void fastPaintWrapped(int scrollX) - { - ViewportRanges ranges = av.getRanges(); - - if (Math.abs(scrollX) > ranges.getViewportWidth()) - { - /* - * shift of more than one view width is - * overcomplicated to handle in this method - */ - fastPaint = false; - repaint(); - return; - } - - if (fastpainting || gg == null) - { - return; - } - - fastPaint = true; - fastpainting = true; - - try - { - calculateWrappedGeometry(getWidth(), getHeight()); - - /* - * relocate the regions of the alignment that are still visible - */ - shiftWrappedAlignment(-scrollX); + // 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(); - /* - * add new columns (sequence, annotation) - * - at top left if scrollX < 0 - * - at right of last two widths if scrollX > 0 - */ - if (scrollX < 0) - { - int startRes = ranges.getStartRes(); - drawWrappedWidth(gg, wrappedSpaceAboveAlignment, startRes, startRes - - scrollX - 1, getHeight()); + int range = vpRanges.getEndRes() - vpRanges.getStartRes(); + if (scrollX > range) + { + scrollX = range; + } + else if (scrollX < -range) + { + scrollX = -range; + } } - else + + // 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)) { - fastPaintWrappedAddRight(scrollX); + // scroll - startres and endres both change + fastPaint(scrollX, 0); } - - /* - * draw all scales (if shown) and hidden column markers - */ - drawWrappedDecorators(gg, ranges.getStartRes()); - - repaint(); - } finally - { - fastpainting = false; - } - } - - /** - * Draws the specified number of columns at the 'end' (bottom right) of a - * wrapped alignment view, including sequences and annotations if shown, but - * not scales. Also draws the same number of columns at the right hand end of - * the second last width shown, if the last width is not full height (so - * cannot simply be copied from the graphics image). - * - * @param columns - */ - protected void fastPaintWrappedAddRight(int columns) - { - if (columns == 0) - { - return; - } - - 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 - * last row was not full height - */ - int visibleWidths = wrappedVisibleWidths; - int canvasHeight = getHeight(); - boolean lastWidthPartHeight = (wrappedVisibleWidths * wrappedRepeatHeightPx) > canvasHeight; - - if (lastWidthPartHeight) - { - int widthsAbove = Math.max(0, visibleWidths - 2); - int ypos = wrappedRepeatHeightPx * widthsAbove - + wrappedSpaceAboveAlignment; - int endRes = ranges.getEndRes(); - endRes += widthsAbove * viewportWidth; - int startRes = endRes - columns; - int xOffset = ((startRes - ranges.getStartRes()) % viewportWidth) - * charWidth; - - /* - * white fill first to erase annotations - */ - gg.translate(xOffset, 0); - gg.setColor(Color.white); - gg.fillRect(labelWidthWest, ypos, - (endRes - startRes + 1) * charWidth, wrappedRepeatHeightPx); - gg.translate(-xOffset, 0); - - drawWrappedWidth(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 - */ - int widthsAbove = visibleWidths - 1; - int ypos = wrappedRepeatHeightPx * widthsAbove - + wrappedSpaceAboveAlignment; - int endRes = ranges.getEndRes(); - endRes += widthsAbove * viewportWidth; - int startRes = endRes - columns + 1; - - /* - * white fill first to erase annotations - */ - int xOffset = ((startRes - ranges.getStartRes()) % viewportWidth) - * charWidth; - gg.translate(xOffset, 0); - gg.setColor(Color.white); - int width = viewportWidth * charWidth - xOffset; - gg.fillRect(labelWidthWest, ypos, width, wrappedRepeatHeightPx); - gg.translate(-xOffset, 0); - - gg.setFont(av.getFont()); - gg.setColor(Color.black); - - if (startRes < ranges.getVisibleAlignmentWidth()) - { - drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight); - } - - /* - * and finally, white fill any space below the visible alignment - */ - int heightBelow = canvasHeight - visibleWidths * wrappedRepeatHeightPx; - if (heightBelow > 0) - { - gg.setColor(Color.white); - gg.fillRect(0, canvasHeight - heightBelow, getWidth(), heightBelow); - } - } - - /** - * Shifts the visible alignment by the specified number of columns - left if - * negative, right if positive. Copies and moves sequences and annotations (if - * shown). Scales, hidden column markers and any newly visible columns must be - * drawn separately. - * - * @param positions - */ - protected void shiftWrappedAlignment(int positions) - { - if (positions == 0) - { - return; - } - int charWidth = av.getCharWidth(); - - int canvasHeight = getHeight(); - ViewportRanges ranges = av.getRanges(); - int viewportWidth = ranges.getViewportWidth(); - int widthToCopy = (ranges.getViewportWidth() - Math.abs(positions)) - * charWidth; - int heightToCopy = wrappedRepeatHeightPx - wrappedSpaceAboveAlignment; - int xMax = ranges.getVisibleAlignmentWidth(); - - if (positions > 0) - { - /* - * shift right (after scroll left) - * for each wrapped width (starting with the last), copy (width-positions) - * columns from the left margin to the right margin, and copy positions - * columns from the right margin of the row above (if any) to the - * left margin of the current row - */ - - /* - * get y-offset of last wrapped width, first row of sequences - */ - int y = canvasHeight / wrappedRepeatHeightPx * wrappedRepeatHeightPx; - y += wrappedSpaceAboveAlignment; - int copyFromLeftStart = labelWidthWest; - int copyFromRightStart = copyFromLeftStart + widthToCopy; - - while (y >= 0) + else if (eventName.equals(ViewportRanges.STARTSEQ)) { - gg.copyArea(copyFromLeftStart, y, widthToCopy, heightToCopy, - positions * charWidth, 0); - if (y > 0) - { - gg.copyArea(copyFromRightStart, y - wrappedRepeatHeightPx, - positions * charWidth, heightToCopy, -widthToCopy, - wrappedRepeatHeightPx); - } - - y -= wrappedRepeatHeightPx; + // scroll + fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); } - } - else - { - /* - * shift left (after scroll right) - * for each wrapped width (starting with the first), copy (width-positions) - * columns from the right margin to the left margin, and copy positions - * columns from the left margin of the row below (if any) to the - * right margin of the current row - */ - int xpos = av.getRanges().getStartRes(); - int y = wrappedSpaceAboveAlignment; - int copyFromRightStart = labelWidthWest - positions * charWidth; - - while (y < canvasHeight) + else if (eventName.equals(ViewportRanges.STARTRESANDSEQ)) { - gg.copyArea(copyFromRightStart, y, widthToCopy, heightToCopy, - positions * charWidth, 0); - if (y + wrappedRepeatHeightPx < canvasHeight - wrappedRepeatHeightPx - && (xpos + viewportWidth <= xMax)) - { - gg.copyArea(labelWidthWest, y + wrappedRepeatHeightPx, -positions - * charWidth, heightToCopy, widthToCopy, - -wrappedRepeatHeightPx); - } - - y += wrappedRepeatHeightPx; - xpos += viewportWidth; + fastPaint(scrollX, ((int[]) evt.getNewValue())[1] + - ((int[]) evt.getOldValue())[1]); } } } - /** * Redraws any positions in the search results in the visible region of a * wrapped alignment. Any highlights are drawn depending on the search results @@ -1971,13 +1577,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { return false; } - int charHeight = av.getCharHeight(); - + boolean matchFound = false; - calculateWrappedGeometry(getWidth(), getHeight()); int wrappedWidth = av.getWrappedWidth(); - int wrappedHeight = wrappedRepeatHeightPx; + int wrappedHeight = getRepeatHeightWrapped(); ViewportRanges ranges = av.getRanges(); int canvasHeight = getHeight(); @@ -2076,6 +1680,27 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } /** + * 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 + * + * @return + */ + protected int getRepeatHeightWrapped() + { + // 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; + } + + /** * Answers the width in pixels of the left scale labels (0 if not shown) * * @return diff --git a/src/jalview/viewmodel/OverviewDimensionsHideHidden.java b/src/jalview/viewmodel/OverviewDimensionsHideHidden.java index c525bc6..c158ce7 100644 --- a/src/jalview/viewmodel/OverviewDimensionsHideHidden.java +++ b/src/jalview/viewmodel/OverviewDimensionsHideHidden.java @@ -132,9 +132,7 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions } } - // update viewport - ranges.setStartRes(xAsRes); - ranges.setStartSeq(yAsSeq); + ranges.setStartResAndSeq(xAsRes, yAsSeq); } @Override diff --git a/src/jalview/viewmodel/OverviewDimensionsShowHidden.java b/src/jalview/viewmodel/OverviewDimensionsShowHidden.java index 0bda56e..9dde16e 100644 --- a/src/jalview/viewmodel/OverviewDimensionsShowHidden.java +++ b/src/jalview/viewmodel/OverviewDimensionsShowHidden.java @@ -176,8 +176,7 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions } // update viewport - ranges.setStartRes(visXAsRes); - ranges.setStartSeq(visYAsSeq); + ranges.setStartResAndSeq(visXAsRes, visYAsSeq); } /** diff --git a/src/jalview/viewmodel/ViewportRanges.java b/src/jalview/viewmodel/ViewportRanges.java index 24ff57f..6331ffb 100644 --- a/src/jalview/viewmodel/ViewportRanges.java +++ b/src/jalview/viewmodel/ViewportRanges.java @@ -24,11 +24,10 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.HiddenColumns; /** - * Slightly less embryonic class which: Supplies and updates viewport properties - * relating to position such as: start and end residues and sequences; ideally - * will serve hidden columns/rows too. Intention also to support calculations - * for positioning, scrolling etc. such as finding the middle of the viewport, - * checking for scrolls off screen + * Supplies and updates viewport properties relating to position such as: start + * and end residues and sequences; ideally will serve hidden columns/rows too. + * Intention also to support calculations for positioning, scrolling etc. such + * as finding the middle of the viewport, checking for scrolls off screen */ public class ViewportRanges extends ViewportProperties { @@ -40,6 +39,8 @@ public class ViewportRanges extends ViewportProperties public static final String ENDSEQ = "endseq"; + public static final String STARTRESANDSEQ = "startresandseq"; + private boolean wrappedMode = false; // start residue of viewport @@ -130,6 +131,31 @@ public class ViewportRanges extends ViewportProperties */ public void setStartEndRes(int start, int end) { + int[] oldvalues = updateStartEndRes(start, end); + int oldstartres = oldvalues[0]; + int oldendres = oldvalues[1]; + + changeSupport.firePropertyChange(STARTRES, oldstartres, startRes); + if (oldstartres == startRes) + { + // event won't be fired if start positions are same + // fire an event for the end positions in case they changed + changeSupport.firePropertyChange(ENDRES, oldendres, endRes); + } + } + + /** + * Update start and end residue values, adjusting for width constraints if + * necessary + * + * @param start + * start residue + * @param end + * end residue + * @return array containing old start and end residue values + */ + private int[] updateStartEndRes(int start, int end) + { int oldstartres = this.startRes; /* @@ -162,14 +188,7 @@ public class ViewportRanges extends ViewportProperties { endRes = end; } - - changeSupport.firePropertyChange(STARTRES, oldstartres, startRes); - if (oldstartres == startRes) - { - // event won't be fired if start positions are same - // fire an event for the end positions in case they changed - changeSupport.firePropertyChange(ENDRES, oldendres, endRes); - } + return new int[] { oldstartres, oldendres }; } /** @@ -203,6 +222,31 @@ public class ViewportRanges extends ViewportProperties */ public void setStartEndSeq(int start, int end) { + int[] oldvalues = updateStartEndSeq(start, end); + int oldstartseq = oldvalues[0]; + int oldendseq = oldvalues[1]; + + changeSupport.firePropertyChange(STARTSEQ, oldstartseq, startSeq); + if (oldstartseq == startSeq) + { + // event won't be fired if start positions are the same + // fire in case the end positions changed + changeSupport.firePropertyChange(ENDSEQ, oldendseq, endSeq); + } + } + + /** + * Update start and end sequence values, adjusting for height constraints if + * necessary + * + * @param start + * start sequence + * @param end + * end sequence + * @return array containing old start and end sequence values + */ + private int[] updateStartEndSeq(int start, int end) + { int oldstartseq = this.startSeq; int visibleHeight = getVisibleAlignmentHeight(); if (start > visibleHeight - 1) @@ -231,14 +275,7 @@ public class ViewportRanges extends ViewportProperties { endSeq = end; } - - changeSupport.firePropertyChange(STARTSEQ, oldstartseq, startSeq); - if (oldstartseq == startSeq) - { - // event won't be fired if start positions are the same - // fire in case the end positions changed - changeSupport.firePropertyChange(ENDSEQ, oldendseq, endSeq); - } + return new int[] { oldstartseq, oldendseq }; } /** @@ -255,6 +292,32 @@ public class ViewportRanges extends ViewportProperties } /** + * Set start residue and start sequence together (fires single event) + * + * @param res + * the start residue + * @param seq + * the start sequence + */ + public void setStartResAndSeq(int res, int seq) + { + int width = getViewportWidth(); + int[] oldresvalues = updateStartEndRes(res, res + width - 1); + + int startseq = seq; + int height = getViewportHeight(); + if (startseq + height - 1 > getVisibleAlignmentHeight() - 1) + { + startseq = getVisibleAlignmentHeight() - height; + } + int[] oldseqvalues = updateStartEndSeq(startseq, startseq + height - 1); + + int[] old = new int[] { oldresvalues[0], oldseqvalues[0] }; + int[] newresseq = new int[] { startRes, startSeq }; + changeSupport.firePropertyChange(STARTRESANDSEQ, old, newresseq); + } + + /** * Get start residue of viewport */ public int getStartRes() diff --git a/test/jalview/viewmodel/ViewportRangesTest.java b/test/jalview/viewmodel/ViewportRangesTest.java index c0cb4ba..af2424d 100644 --- a/test/jalview/viewmodel/ViewportRangesTest.java +++ b/test/jalview/viewmodel/ViewportRangesTest.java @@ -85,7 +85,6 @@ public class ViewportRangesTest { vr.setEndSeq(al.getHeight()); assertEquals(vr.getEndSeq(), al.getHeight() - 1); - // vr.setEndRes(al.getHeight() - 1); vr.setEndSeq(al.getHeight() - 1); assertEquals(vr.getEndSeq(), al.getHeight() - 1); } @@ -169,6 +168,24 @@ public class ViewportRangesTest { } @Test(groups = { "Functional" }) + public void testSetStartResAndSeq() + { + ViewportRanges vr = new ViewportRanges(al); + vr.setViewportHeight(10); + vr.setStartResAndSeq(3, 6); + assertEquals(vr.getStartRes(), 3); + assertEquals(vr.getStartSeq(), 6); + assertEquals(vr.getEndRes(), 3 + vr.getViewportWidth() - 1); + assertEquals(vr.getEndSeq(), 6 + vr.getViewportHeight() - 1); + + vr.setStartResAndSeq(10, 25); + assertEquals(vr.getStartRes(), 10); + assertEquals(vr.getStartSeq(), 19); + assertEquals(vr.getEndRes(), 10 + vr.getViewportWidth() - 1); + assertEquals(vr.getEndSeq(), 19 + vr.getViewportHeight() - 1); + } + + @Test(groups = { "Functional" }) public void testSetViewportHeight() { ViewportRanges vr = new ViewportRanges(al); @@ -418,7 +435,7 @@ public class ViewportRangesTest { // one event fired when startRes is called with new value vr.setStartRes(4); - assertTrue(l.verify(1, Arrays.asList("startres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES))); l.reset(); // no event fired for same value @@ -427,7 +444,7 @@ public class ViewportRangesTest { l.reset(); vr.setStartSeq(4); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); vr.setStartSeq(4); @@ -435,7 +452,7 @@ public class ViewportRangesTest { l.reset(); vr.setEndSeq(10); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); vr.setEndSeq(10); @@ -443,7 +460,7 @@ public class ViewportRangesTest { l.reset(); vr.setStartEndRes(2, 15); - assertTrue(l.verify(1, Arrays.asList("startres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES))); l.reset(); vr.setStartEndRes(2, 15); @@ -452,16 +469,18 @@ public class ViewportRangesTest { // check new value fired by event is corrected startres vr.setStartEndRes(-1, 5); - assertTrue(l.verify(1, Arrays.asList("startres"), Arrays.asList(0))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES), + Arrays.asList(0))); l.reset(); // check new value fired by event is corrected endres vr.setStartEndRes(0, -1); - assertTrue(l.verify(1, Arrays.asList("endres"), Arrays.asList(0))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDRES), + Arrays.asList(0))); l.reset(); vr.setStartEndSeq(2, 15); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); vr.setStartEndSeq(2, 15); @@ -474,12 +493,14 @@ public class ViewportRangesTest { // check new value fired by event is corrected startseq vr.setStartEndSeq(-1, 5); - assertTrue(l.verify(1, Arrays.asList("startseq"), Arrays.asList(0))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ), + Arrays.asList(0))); l.reset(); // check new value fired by event is corrected endseq vr.setStartEndSeq(0, -1); - assertTrue(l.verify(1, Arrays.asList("endseq"), Arrays.asList(0))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDSEQ), + Arrays.asList(0))); l.reset(); // reset for later tests @@ -488,51 +509,52 @@ public class ViewportRangesTest { // test viewport height and width setting triggers event vr.setViewportHeight(10); - assertTrue(l.verify(1, Arrays.asList("endseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDSEQ))); l.reset(); vr.setViewportWidth(18); - assertTrue(l.verify(1, Arrays.asList("endres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDRES))); l.reset(); // already has seq start set to 2, so triggers endseq vr.setViewportStartAndHeight(2, 16); - assertTrue(l.verify(1, Arrays.asList("endseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDSEQ))); l.reset(); vr.setViewportStartAndWidth(1, 14); - assertTrue(l.verify(1, Arrays.asList("startres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES))); l.reset(); // test page up/down triggers event vr.pageUp(); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); vr.pageDown(); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); // test scrolling triggers event vr.scrollUp(true); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); vr.scrollUp(false); - assertTrue(l.verify(1, Arrays.asList("startseq"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ))); l.reset(); vr.scrollRight(true); - assertTrue(l.verify(1, Arrays.asList("startres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES))); l.reset(); vr.scrollRight(false); - assertTrue(l.verify(1, Arrays.asList("startres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES))); l.reset(); vr.scrollToVisible(10, 10); assertTrue(l.verify(4, - Arrays.asList("startseq", "startseq", "startseq", "startseq"))); + Arrays.asList(ViewportRanges.STARTSEQ, ViewportRanges.STARTSEQ, + ViewportRanges.STARTSEQ, ViewportRanges.STARTSEQ))); l.reset(); /* @@ -544,7 +566,15 @@ public class ViewportRangesTest { l.reset(); vr.scrollToWrappedVisible(25); - assertTrue(l.verify(1, Arrays.asList("startres"))); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES))); + l.reset(); + + // test setStartResAndSeq triggers one event + vr.setStartResAndSeq(5, 7); + assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRESANDSEQ), + Arrays.asList(5, 7))); + + l.reset(); } @Test(groups = { "Functional" }) @@ -844,7 +874,15 @@ class MockPropChangeListener implements ViewportListenerI { firecount++; events.add(evt.getPropertyName()); - newvalues.add((Integer) evt.getNewValue()); + if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ)) + { + newvalues.add(((int[]) evt.getNewValue())[0]); + newvalues.add(((int[]) evt.getNewValue())[1]); + } + else + { + newvalues.add((Integer) evt.getNewValue()); + } } public boolean verify(int count, List eventslist,