From: Jim Procter Date: Mon, 24 Jul 2017 05:24:15 +0000 (+0200) Subject: Merge branch 'documentation/JAL-2418_release2102' into develop X-Git-Tag: Release_2_10_3b1~132^2~36 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=b4d2b6a2ed131265ed599db27de9b82aaf010400;hp=52298f6987c5807b9f672e33f416dee9e14e1952;p=jalview.git Merge branch 'documentation/JAL-2418_release2102' into develop --- diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java index 8f28658..65d652d 100644 --- a/src/jalview/appletgui/AlignFrame.java +++ b/src/jalview/appletgui/AlignFrame.java @@ -599,25 +599,11 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, } case KeyEvent.VK_PAGE_UP: - if (viewport.getWrapAlignment()) - { - ranges.scrollUp(true); - } - else - { - ranges.pageUp(); - } + ranges.pageUp(); break; case KeyEvent.VK_PAGE_DOWN: - if (viewport.getWrapAlignment()) - { - ranges.scrollUp(false); - } - else - { - ranges.pageDown(); - } + ranges.pageDown(); break; case KeyEvent.VK_Z: @@ -2631,7 +2617,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, } Frame frame = new Frame(); - OverviewPanel overview = new OverviewPanel(alignPanel); + final OverviewPanel overview = new OverviewPanel(alignPanel); frame.add(overview); // +50 must allow for applet frame window jalview.bin.JalviewLite.addFrame(frame, MessageManager.formatMessage( @@ -2646,6 +2632,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, @Override public void windowClosing(WindowEvent e) { + overview.dispose(); if (ap != null) { ap.setOverviewPanel(null); diff --git a/src/jalview/appletgui/AlignmentPanel.java b/src/jalview/appletgui/AlignmentPanel.java index e402b9b..4147177 100644 --- a/src/jalview/appletgui/AlignmentPanel.java +++ b/src/jalview/appletgui/AlignmentPanel.java @@ -802,42 +802,45 @@ public class AlignmentPanel extends Panel implements AdjustmentListener, sendViewPosition(); } - private void adjustVertical(int offy) + private void adjustVertical(int newY) { - int oldX = vpRanges.getStartRes(); - int oldwidth = vpRanges.getViewportWidth(); - int oldY = vpRanges.getStartSeq(); - int oldheight = vpRanges.getViewportHeight(); - if (av.getWrapAlignment()) { - int rowSize = seqPanel.seqCanvas - .getWrappedCanvasWidth(seqPanel.seqCanvas.getWidth()); - - // if we're scrolling to the position we're already at, stop - // this prevents infinite recursion of events when the scroll/viewport - // ranges values are the same - if ((offy * rowSize == oldX) && (oldwidth == rowSize)) + /* + * if we're scrolling to the position we're already at, stop + * this prevents infinite recursion of events when the scroll/viewport + * ranges values are the same + */ + int oldX = vpRanges.getStartRes(); + int oldY = vpRanges.getWrappedScrollPosition(oldX); + if (oldY == newY) { return; } - else if (offy > -1) + if (newY > -1) { - vpRanges.setViewportStartAndWidth(offy * rowSize, rowSize); + /* + * limit page up/down to one width's worth of positions + */ + int rowSize = vpRanges.getViewportWidth(); + int newX = newY > oldY ? oldX + rowSize : oldX - rowSize; + vpRanges.setViewportStartAndWidth(Math.max(0, newX), rowSize); } } else { int height = seqPanel.seqCanvas.getHeight() / av.getCharHeight(); + int oldY = vpRanges.getStartSeq(); + int oldheight = vpRanges.getViewportHeight(); // if we're scrolling to the position we're already at, stop // this prevents infinite recursion of events when the scroll/viewport // ranges values are the same - if ((offy == oldY) && (height == oldheight)) + if ((newY == oldY) && (height == oldheight)) { return; } - vpRanges.setViewportStartAndHeight(offy, height); + vpRanges.setViewportStartAndHeight(newY, height); } if (av.getWrapAlignment() || !fastPaint) { @@ -982,35 +985,23 @@ public class AlignmentPanel extends Panel implements AdjustmentListener, } - /* + /** * Set vertical scroll bar parameters for wrapped panel - * @param res - * the residue to scroll to + * + * @param topLeftColumn + * the column position at top left (0..) */ - private void setScrollingForWrappedPanel(int res) + private void setScrollingForWrappedPanel(int topLeftColumn) { - // get the width of the alignment in residues - int maxwidth = av.getAlignment().getWidth(); - if (av.hasHiddenColumns()) - { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; - } + int scrollPosition = vpRanges.getWrappedScrollPosition(topLeftColumn); + int maxScroll = vpRanges.getWrappedMaxScroll(topLeftColumn); - // get the width of the canvas in residues - int canvasWidth = seqPanel.seqCanvas - .getWrappedCanvasWidth(seqPanel.seqCanvas.getSize().width); - if (canvasWidth > 0) - { - // position we want to scroll to is number of canvasWidth's to get there - int current = res / canvasWidth; - - // max scroll position: add one because extent is 1 and scrollbar value - // can only be set to at most max - extent - int max = maxwidth / canvasWidth + 1; - vscroll.setUnitIncrement(1); - vscroll.setValues(current, 1, 0, max); - } + /* + * a scrollbar's value can be set to at most (maximum-extent) + * so we add extent (1) to the maxScroll value + */ + vscroll.setUnitIncrement(1); + vscroll.setValues(scrollPosition, 1, 0, maxScroll + 1); } protected Panel sequenceHolderPanel = new Panel(); diff --git a/src/jalview/appletgui/FeatureColourChooser.java b/src/jalview/appletgui/FeatureColourChooser.java index 0e85017..4075e8b 100644 --- a/src/jalview/appletgui/FeatureColourChooser.java +++ b/src/jalview/appletgui/FeatureColourChooser.java @@ -95,9 +95,11 @@ public class FeatureColourChooser extends Panel implements ActionListener, float mm[] = fr.getMinMax().get(type)[0]; min = mm[0]; max = mm[1]; + threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black); oldcs = fr.getFeatureColours().get(type); if (oldcs.isGraduatedColour()) { + threshline.value = oldcs.getThreshold(); cs = new FeatureColour((FeatureColour) oldcs, min, max); } else @@ -384,14 +386,6 @@ public class FeatureColourChooser extends Panel implements ActionListener, thresholdValue.setText(""); } - else if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD - && threshline == null) - { - // todo visual indication of feature threshold - threshline = new jalview.datamodel.GraphLine((max - min) / 2f, - "Threshold", Color.black); - } - if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD) { adjusting = true; diff --git a/src/jalview/appletgui/IdCanvas.java b/src/jalview/appletgui/IdCanvas.java index 5313b41..48c0c40 100755 --- a/src/jalview/appletgui/IdCanvas.java +++ b/src/jalview/appletgui/IdCanvas.java @@ -101,7 +101,7 @@ public class IdCanvas extends Panel implements ViewportListenerI public void fastPaint(int vertical) { - if (gg == null) + if (gg == null || av.getWrapAlignment()) { repaint(); return; @@ -199,130 +199,148 @@ public class IdCanvas extends Panel implements ViewportListenerI void drawIds(int starty, int endy) { - // hardwired italic IDs in applet currently - Font italic = new Font(av.getFont().getName(), Font.ITALIC, av - .getFont().getSize()); - // temp variable for speed avcharHeight = av.getCharHeight(); - gg.setFont(italic); - Color currentColor = Color.white; Color currentTextColor = Color.black; final boolean doHiddenCheck = av.isDisplayReferenceSeq() - || av.hasHiddenRows(), hiddenRows = av.hasHiddenRows() - && av.getShowHiddenMarkers(); + || av.hasHiddenRows(); + boolean hiddenRows = av.hasHiddenRows() && av.getShowHiddenMarkers(); if (av.getWrapAlignment()) { - int maxwidth = av.getAlignment().getWidth(); - int alheight = av.getAlignment().getHeight(); + drawIdsWrapped(starty, doHiddenCheck, hiddenRows); + return; + } - if (av.hasHiddenColumns()) + // Now draw the id strings + SequenceI seq; + for (int i = starty; i <= endy; i++) + { + seq = av.getAlignment().getSequenceAt(i); + if (seq == null) { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; + continue; + } + // hardwired italic IDs in applet currently + Font italic = new Font(av.getFont().getName(), Font.ITALIC, av + .getFont().getSize()); + gg.setFont(italic); + // boolean isrep=false; + if (doHiddenCheck) + { + // isrep = + setHiddenFont(seq); } - int annotationHeight = 0; - AnnotationLabels labels = null; - - if (av.isShowAnnotation()) + // Selected sequence colours + if ((searchResults != null) && searchResults.contains(seq)) { - AnnotationPanel ap = new AnnotationPanel(av); - annotationHeight = ap.adjustPanelHeight(); - labels = new AnnotationLabels(av); + currentColor = Color.black; + currentTextColor = Color.white; } - int hgap = avcharHeight; - if (av.getScaleAboveWrapped()) + else if ((av.getSelectionGroup() != null) + && av.getSelectionGroup().getSequences(null).contains(seq)) + { + currentColor = Color.lightGray; + currentTextColor = Color.black; + } + else { - hgap += avcharHeight; + currentColor = av.getSequenceColour(seq); + currentTextColor = Color.black; } - int cHeight = alheight * avcharHeight + hgap + annotationHeight; + gg.setColor(currentColor); + // TODO: isrep could be used to highlight the representative in a + // different way + gg.fillRect(0, (i - starty) * avcharHeight, getSize().width, + avcharHeight); + gg.setColor(currentTextColor); - int rowSize = av.getRanges().getEndRes() - - av.getRanges().getStartRes(); - // Draw the rest of the panels - for (int ypos = hgap, row = av.getRanges().getStartRes(); (ypos <= getSize().height) - && (row < maxwidth); ypos += cHeight, row += rowSize) + gg.drawString(seq.getDisplayId(av.getShowJVSuffix()), 0, + (((i - starty) * avcharHeight) + avcharHeight) + - (avcharHeight / 5)); + + if (hiddenRows) { - for (int i = starty; i < alheight; i++) - { + drawMarker(i, starty, 0); + } + } + } - SequenceI s = av.getAlignment().getSequenceAt(i); - gg.setFont(italic); - if (doHiddenCheck) - { - setHiddenFont(s); - } - drawIdString(gg, hiddenRows, s, i, 0, ypos); - } + /** + * Draws sequence ids in wrapped mode + * + * @param starty + * @param doHiddenCheck + * @param hiddenRows + */ + protected void drawIdsWrapped(int starty, final boolean doHiddenCheck, + boolean hiddenRows) + { + int maxwidth = av.getAlignment().getWidth(); + int alheight = av.getAlignment().getHeight(); - if (labels != null) - { - gg.translate(0, ypos + (alheight * avcharHeight)); - labels.drawComponent(gg, getSize().width); - gg.translate(0, -ypos - (alheight * avcharHeight)); - } + if (av.hasHiddenColumns()) + { + maxwidth = av.getAlignment().getHiddenColumns() + .findColumnPosition(maxwidth) - 1; + } - } + int annotationHeight = 0; + AnnotationLabels labels = null; + + if (av.isShowAnnotation()) + { + AnnotationPanel ap = new AnnotationPanel(av); + annotationHeight = ap.adjustPanelHeight(); + labels = new AnnotationLabels(av); } - else + int hgap = avcharHeight; + if (av.getScaleAboveWrapped()) { - // Now draw the id strings - SequenceI seq; - for (int i = starty; i <= endy; i++) - { + hgap += avcharHeight; + } - seq = av.getAlignment().getSequenceAt(i); - if (seq == null) - { - continue; - } - gg.setFont(italic); - // boolean isrep=false; - if (doHiddenCheck) - { - // isrep = - setHiddenFont(seq); - } + int cHeight = alheight * avcharHeight + hgap + annotationHeight; - // Selected sequence colours - if ((searchResults != null) && searchResults.contains(seq)) - { - currentColor = Color.black; - currentTextColor = Color.white; - } - else if ((av.getSelectionGroup() != null) - && av.getSelectionGroup().getSequences(null).contains(seq)) - { - currentColor = Color.lightGray; - currentTextColor = Color.black; - } - else - { - currentColor = av.getSequenceColour(seq); - currentTextColor = Color.black; - } + int rowSize = av.getRanges().getViewportWidth(); - gg.setColor(currentColor); - // TODO: isrep could be used to highlight the representative in a - // different way - gg.fillRect(0, (i - starty) * avcharHeight, getSize().width, - avcharHeight); - gg.setColor(currentTextColor); + // hardwired italic IDs in applet currently + Font italic = new Font(av.getFont().getName(), Font.ITALIC, av + .getFont().getSize()); + gg.setFont(italic); - gg.drawString(seq.getDisplayId(av.getShowJVSuffix()), 0, - (((i - starty) * avcharHeight) + avcharHeight) - - (avcharHeight / 5)); + /* + * draw repeating sequence ids until out of sequence data or + * out of visible space, whichever comes first + */ + int ypos = hgap; + int row = av.getRanges().getStartRes(); + while ((ypos <= getHeight()) && (row < maxwidth)) + { + for (int i = starty; i < alheight; i++) + { - if (hiddenRows) + SequenceI s = av.getAlignment().getSequenceAt(i); + // gg.setFont(italic); + if (doHiddenCheck) { - drawMarker(i, starty, 0); + setHiddenFont(s); } + drawIdString(gg, hiddenRows, s, i, 0, ypos); } + + if (labels != null) + { + gg.translate(0, ypos + (alheight * avcharHeight)); + labels.drawComponent(gg, getSize().width); + gg.translate(0, -ypos - (alheight * avcharHeight)); + } + ypos += cHeight; + row += rowSize; } } @@ -398,16 +416,25 @@ public class IdCanvas extends Panel implements ViewportListenerI return false; } + /** + * Respond to viewport range changes (e.g. alignment panel was scrolled). 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. + *

+ * In unwrapped mode, only responds to a vertical scroll, as horizontal scroll + * leaves sequence ids unchanged. In wrapped mode, only vertical scroll is + * provided, but it generates a change of "startres" which does require an + * update here. + */ @Override public void propertyChange(PropertyChangeEvent evt) { - // Respond to viewport range changes (e.g. alignment panel was scrolled) - // 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 (evt.getPropertyName().equals(ViewportRanges.STARTSEQ)) + String propertyName = evt.getPropertyName(); + if (propertyName.equals(ViewportRanges.STARTSEQ) + || (av.getWrapAlignment() && propertyName + .equals(ViewportRanges.STARTRES))) { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } diff --git a/src/jalview/appletgui/OverviewPanel.java b/src/jalview/appletgui/OverviewPanel.java index b3c4a37..ccdfee1 100755 --- a/src/jalview/appletgui/OverviewPanel.java +++ b/src/jalview/appletgui/OverviewPanel.java @@ -160,6 +160,14 @@ public class OverviewPanel extends Panel implements Runnable, */ public void updateOverviewImage() { + if (oviewCanvas == null) + { + /* + * panel has been disposed + */ + return; + } + if ((getSize().width > 0) && (getSize().height > 0)) { od.setWidth(getSize().width); @@ -257,4 +265,21 @@ public class OverviewPanel extends Panel implements Runnable, oviewCanvas.resetOviewDims(od); updateOverviewImage(); } + + /** + * Removes this object as a property change listener, and nulls references + */ + protected void dispose() + { + try + { + av.getRanges().removePropertyChangeListener(this); + } finally + { + av = null; + oviewCanvas = null; + ap = null; + od = null; + } + } } diff --git a/src/jalview/appletgui/ScalePanel.java b/src/jalview/appletgui/ScalePanel.java index 1737c01..5e0a2fd 100755 --- a/src/jalview/appletgui/ScalePanel.java +++ b/src/jalview/appletgui/ScalePanel.java @@ -140,7 +140,7 @@ public class ScalePanel extends Panel implements MouseMotionListener, sg.setStartRes(min); sg.setEndRes(max); } - ap.paintAlignment(true); + ap.paintAlignment(false); av.sendSelection(); } @@ -166,10 +166,6 @@ public class ScalePanel extends Panel implements MouseMotionListener, av.showColumn(reveal[0]); reveal = null; ap.paintAlignment(true); - if (ap.overviewPanel != null) - { - ap.overviewPanel.updateOverviewImage(); - } av.sendSelection(); } }); @@ -186,10 +182,6 @@ public class ScalePanel extends Panel implements MouseMotionListener, av.showAllHiddenColumns(); reveal = null; ap.paintAlignment(true); - if (ap.overviewPanel != null) - { - ap.overviewPanel.updateOverviewImage(); - } av.sendSelection(); } }); @@ -216,10 +208,6 @@ public class ScalePanel extends Panel implements MouseMotionListener, } ap.paintAlignment(true); - if (ap.overviewPanel != null) - { - ap.overviewPanel.updateOverviewImage(); - } av.sendSelection(); } }); @@ -350,9 +338,15 @@ public class ScalePanel extends Panel implements MouseMotionListener, @Override public void paint(Graphics g) { - drawScale(g, av.getRanges().getStartRes(), av.getRanges().getEndRes(), - getSize().width, - getSize().height); + /* + * shouldn't get called in wrapped mode as the scale above is + * drawn instead by SeqCanvas.drawNorthScale + */ + if (!av.getWrapAlignment()) + { + drawScale(g, av.getRanges().getStartRes(), + av.getRanges().getEndRes(), getSize().width, getSize().height); + } } // scalewidth will normally be screenwidth, diff --git a/src/jalview/appletgui/SeqCanvas.java b/src/jalview/appletgui/SeqCanvas.java index e996622..9de5452 100755 --- a/src/jalview/appletgui/SeqCanvas.java +++ b/src/jalview/appletgui/SeqCanvas.java @@ -420,6 +420,9 @@ public class SeqCanvas extends Panel implements ViewportListenerI FontMetrics fm = getFontMetrics(av.getFont()); + LABEL_EAST = 0; + LABEL_WEST = 0; + if (av.getScaleRightWrapped()) { LABEL_EAST = fm.stringWidth(getMask()); @@ -441,17 +444,17 @@ public class SeqCanvas extends Panel implements ViewportListenerI av.setWrappedWidth(cWidth); - av.getRanges().setEndRes(av.getRanges().getStartRes() + cWidth - 1); + av.getRanges().setViewportStartAndWidth(startRes, cWidth); int endx; int ypos = hgap; - int maxwidth = av.getAlignment().getWidth() - 1; + int maxwidth = av.getAlignment().getWidth(); if (av.hasHiddenColumns()) { maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; + .findColumnPosition(maxwidth); } while ((ypos <= canvasHeight) && (startRes < maxwidth)) diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index a72bbaa..e4e3640 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -616,6 +616,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, int res = 0; int x = evt.getX(); + int startRes = av.getRanges().getStartRes(); if (av.getWrapAlignment()) { @@ -630,7 +631,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, int y = evt.getY(); y -= hgap; - x -= seqCanvas.LABEL_WEST; + x = Math.max(0, x - seqCanvas.LABEL_WEST); int cwidth = seqCanvas.getWrappedCanvasWidth(getSize().width); if (cwidth < 1) @@ -639,14 +640,15 @@ public class SeqPanel extends Panel implements MouseMotionListener, } wrappedBlock = y / cHeight; - wrappedBlock += av.getRanges().getStartRes() / cwidth; - - res = wrappedBlock * cwidth + x / av.getCharWidth(); - + wrappedBlock += startRes / cwidth; + int startOffset = startRes % cwidth; // in case start is scrolled right + // from 0 + res = wrappedBlock * cwidth + + Math.min(cwidth - 1, startOffset + x / av.getCharWidth()); } else { - res = (x / av.getCharWidth()) + av.getRanges().getStartRes(); + res = (x / av.getCharWidth()) + startRes; } if (av.hasHiddenColumns()) diff --git a/src/jalview/datamodel/HiddenSequences.java b/src/jalview/datamodel/HiddenSequences.java index a98b10e..98e9694 100755 --- a/src/jalview/datamodel/HiddenSequences.java +++ b/src/jalview/datamodel/HiddenSequences.java @@ -157,9 +157,10 @@ public class HiddenSequences int absAlignmentIndex = alignment.findIndex(sequence); int alignmentIndex = adjustForHiddenSeqs(absAlignmentIndex); - if (hiddenSequences[alignmentIndex] != null) + if (alignmentIndex < 0 || hiddenSequences[alignmentIndex] != null) { System.out.println("ERROR!!!!!!!!!!!"); + return; } hiddenSequences[alignmentIndex] = sequence; @@ -253,7 +254,8 @@ public class HiddenSequences } /** - * Convert absolute alignment index to visible alignment index + * Convert absolute alignment index to visible alignment index (or -1 if + * before the first visible sequence) * * @param alignmentIndex * @return diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index fe84012..f8da0db 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -689,24 +689,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, break; } case KeyEvent.VK_PAGE_UP: - if (viewport.getWrapAlignment()) - { - vpRanges.scrollUp(true); - } - else - { - vpRanges.pageUp(); - } + vpRanges.pageUp(); break; case KeyEvent.VK_PAGE_DOWN: - if (viewport.getWrapAlignment()) - { - vpRanges.scrollUp(false); - } - else - { - vpRanges.pageDown(); - } + vpRanges.pageDown(); break; } } @@ -3214,10 +3200,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { viewport.setShowSequenceFeatures(showSeqFeatures.isSelected()); alignPanel.paintAlignment(true); - if (alignPanel.getOverviewPanel() != null) - { - alignPanel.getOverviewPanel().updateOverviewImage(); - } } /** @@ -3273,7 +3255,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } JInternalFrame frame = new JInternalFrame(); - OverviewPanel overview = new OverviewPanel(alignPanel); + final OverviewPanel overview = new OverviewPanel(alignPanel); frame.setContentPane(overview); Desktop.addInternalFrame(frame, MessageManager.formatMessage( "label.overview_params", new Object[] { this.getTitle() }), @@ -3286,6 +3268,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void internalFrameClosed( javax.swing.event.InternalFrameEvent evt) { + overview.dispose(); alignPanel.setOverviewPanel(null); }; }); diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index 395f6b3..e62707f 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -629,21 +629,24 @@ public class AlignmentPanel extends GAlignmentPanel implements annotationSpaceFillerHolder.setVisible(true); } - if (wrap) - { - int widthInRes = getSeqPanel().seqCanvas - .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth()); - vpRanges.setViewportWidth(widthInRes); - } - else - { - int widthInRes = (getSeqPanel().seqCanvas.getWidth() / av - .getCharWidth()) - 1; - int heightInSeq = (getSeqPanel().seqCanvas.getHeight() / av - .getCharHeight()) - 1; + int canvasWidth = getSeqPanel().seqCanvas.getWidth(); + if (canvasWidth > 0) + { // may not yet be laid out + if (wrap) + { + int widthInRes = getSeqPanel().seqCanvas + .getWrappedCanvasWidth(canvasWidth); + vpRanges.setViewportWidth(widthInRes); + } + else + { + int widthInRes = (canvasWidth / av.getCharWidth()) - 1; + int heightInSeq = (getSeqPanel().seqCanvas.getHeight() / av + .getCharHeight()) - 1; - vpRanges.setViewportWidth(widthInRes); - vpRanges.setViewportHeight(heightInSeq); + vpRanges.setViewportWidth(widthInRes); + vpRanges.setViewportHeight(heightInSeq); + } } idSpaceFillerPanel1.setVisible(!wrap); @@ -735,97 +738,111 @@ public class AlignmentPanel extends GAlignmentPanel implements @Override public void adjustmentValueChanged(AdjustmentEvent evt) { - int oldX = vpRanges.getStartRes(); - int oldwidth = vpRanges.getViewportWidth(); - int oldY = vpRanges.getStartSeq(); - int oldheight = vpRanges.getViewportHeight(); - if (av.getWrapAlignment()) { - if (evt.getSource() == hscroll) - { - return; // no horizontal scroll when wrapped - } - else if (evt.getSource() == vscroll) + adjustScrollingWrapped(evt); + return; + } + + if (evt.getSource() == hscroll) + { + int oldX = vpRanges.getStartRes(); + int oldwidth = vpRanges.getViewportWidth(); + int x = hscroll.getValue(); + int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(); + + // if we're scrolling to the position we're already at, stop + // this prevents infinite recursion of events when the scroll/viewport + // ranges values are the same + if ((x == oldX) && (width == oldwidth)) { - int offy = vscroll.getValue(); - int rowSize = getSeqPanel().seqCanvas - .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth()); - - // if we're scrolling to the position we're already at, stop - // this prevents infinite recursion of events when the scroll/viewport - // ranges values are the same - if ((offy * rowSize == oldX) && (oldwidth == rowSize)) - { - return; - } - else if (offy > -1) - { - vpRanges.setViewportStartAndWidth(offy * rowSize, rowSize); - } + return; } - else + vpRanges.setViewportStartAndWidth(x, width); + } + else if (evt.getSource() == vscroll) + { + int oldY = vpRanges.getStartSeq(); + int oldheight = vpRanges.getViewportHeight(); + int y = vscroll.getValue(); + int height = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(); + + // if we're scrolling to the position we're already at, stop + // this prevents infinite recursion of events when the scroll/viewport + // ranges values are the same + if ((y == oldY) && (height == oldheight)) { - // This is only called if file loaded is a jar file that - // was wrapped when saved and user has wrap alignment true - // as preference setting - SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - // When updating scrolling to use ViewportChange events, this code - // could not be validated and it is not clear if it is now being - // called. Log warning here in case it is called and unforeseen - // problems occur - Cache.log - .warn("Unexpected path through code: Wrapped jar file opened with wrap alignment set in preferences"); - - // scroll to start of panel - vpRanges.setStartRes(0); - vpRanges.setStartSeq(0); - } - }); + return; } + vpRanges.setViewportStartAndHeight(y, height); + } + if (!fastPaint) + { repaint(); } - else + } + + /** + * Responds to a scroll change by setting the start position of the viewport. + * Does + * + * @param evt + */ + protected void adjustScrollingWrapped(AdjustmentEvent evt) + { + if (evt.getSource() == hscroll) { - // horizontal scroll - if (evt.getSource() == hscroll) - { - int x = hscroll.getValue(); - int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(); + return; // no horizontal scroll when wrapped + } + if (evt.getSource() == vscroll) + { + int newY = vscroll.getValue(); - // if we're scrolling to the position we're already at, stop - // this prevents infinite recursion of events when the scroll/viewport - // ranges values are the same - if ((x == oldX) && (width == oldwidth)) - { - return; - } - vpRanges.setViewportStartAndWidth(x, width); - } - else if (evt.getSource() == vscroll) + /* + * if we're scrolling to the position we're already at, stop + * this prevents infinite recursion of events when the scroll/viewport + * ranges values are the same + */ + int oldX = vpRanges.getStartRes(); + int oldY = vpRanges.getWrappedScrollPosition(oldX); + if (oldY == newY) { - int y = vscroll.getValue(); - int height = getSeqPanel().seqCanvas.getHeight() - / av.getCharHeight(); - - // if we're scrolling to the position we're already at, stop - // this prevents infinite recursion of events when the scroll/viewport - // ranges values are the same - if ((y == oldY) && (height == oldheight)) - { - return; - } - vpRanges.setViewportStartAndHeight(y, height); + return; } - if (!fastPaint) + if (newY > -1) { - repaint(); + /* + * limit page up/down to one width's worth of positions + */ + int rowSize = vpRanges.getViewportWidth(); + int newX = newY > oldY ? oldX + rowSize : oldX - rowSize; + vpRanges.setViewportStartAndWidth(Math.max(0, newX), rowSize); } } + else + { + // This is only called if file loaded is a jar file that + // was wrapped when saved and user has wrap alignment true + // as preference setting + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run() + { + // When updating scrolling to use ViewportChange events, this code + // could not be validated and it is not clear if it is now being + // called. Log warning here in case it is called and unforeseen + // problems occur + Cache.log + .warn("Unexpected path through code: Wrapped jar file opened with wrap alignment set in preferences"); + + // scroll to start of panel + vpRanges.setStartRes(0); + vpRanges.setStartSeq(0); + } + }); + } + repaint(); } /** @@ -875,35 +892,24 @@ public class AlignmentPanel extends GAlignmentPanel implements setScrollValues(vpRanges.getStartRes(), vpRanges.getStartSeq()); } - /* - * Set vertical scroll bar parameters for wrapped panel - * @param res - * the residue to scroll to + /** + * Set vertical scroll bar position, and number of increments, for wrapped + * panel + * + * @param topLeftColumn + * the column position at top left (0..) */ - private void setScrollingForWrappedPanel(int res) + private void setScrollingForWrappedPanel(int topLeftColumn) { - // get the width of the alignment in residues - int maxwidth = av.getAlignment().getWidth(); - if (av.hasHiddenColumns()) - { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; - } + int scrollPosition = vpRanges.getWrappedScrollPosition(topLeftColumn); + int maxScroll = vpRanges.getWrappedMaxScroll(topLeftColumn); - // get the width of the canvas in residues - int canvasWidth = getSeqPanel().seqCanvas - .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth()); - if (canvasWidth > 0) - { - // position we want to scroll to is number of canvasWidth's to get there - int current = res / canvasWidth; - - // max scroll position: add one because extent is 1 and scrollbar value - // can only be set to at most max - extent - int max = maxwidth / canvasWidth + 1; - vscroll.setUnitIncrement(1); - vscroll.setValues(current, 1, 0, max); - } + /* + * a scrollbar's value can be set to at most (maximum-extent) + * so we add extent (1) to the maxScroll value + */ + vscroll.setUnitIncrement(1); + vscroll.setValues(scrollPosition, 1, 0, maxScroll + 1); } /** diff --git a/src/jalview/gui/FeatureColourChooser.java b/src/jalview/gui/FeatureColourChooser.java index 5594e1a..a1c1bff 100644 --- a/src/jalview/gui/FeatureColourChooser.java +++ b/src/jalview/gui/FeatureColourChooser.java @@ -178,9 +178,10 @@ public class FeatureColourChooser extends JalviewDialog // initialise threshold slider and selector threshold.setSelectedIndex(cs.isAboveThreshold() ? 1 : 2); slider.setEnabled(true); + slider.setValue((int) (cs.getThreshold() * scaleFactor)); thresholdValue.setEnabled(true); threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black); - + threshline.value = cs.getThreshold(); } adjusting = false; diff --git a/src/jalview/gui/IdCanvas.java b/src/jalview/gui/IdCanvas.java index 4642741..052c527 100755 --- a/src/jalview/gui/IdCanvas.java +++ b/src/jalview/gui/IdCanvas.java @@ -155,7 +155,10 @@ public class IdCanvas extends JPanel implements ViewportListenerI */ public void fastPaint(int vertical) { - if (gg == null) + /* + * for now, not attempting fast paint of wrapped ids... + */ + if (gg == null || av.getWrapAlignment()) { repaint(); @@ -283,143 +286,158 @@ public class IdCanvas extends JPanel implements ViewportListenerI Color currentColor = Color.white; Color currentTextColor = Color.black; - final boolean doHiddenCheck = av.isDisplayReferenceSeq() - || av.hasHiddenRows(), hiddenRows = av.hasHiddenRows(); + boolean hasHiddenRows = av.hasHiddenRows(); if (av.getWrapAlignment()) { - int maxwidth = av.getAlignment().getWidth(); - int alheight = av.getAlignment().getHeight(); + drawIdsWrapped(starty, hasHiddenRows); + return; + } - if (av.hasHiddenColumns()) - { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; - } + // No need to hang on to labels if we're not wrapped + labels = null; - int annotationHeight = 0; + // Now draw the id strings + int panelWidth = getWidth(); + int xPos = 0; + + SequenceI sequence; + // Now draw the id strings + for (int i = starty; i <= endy; i++) + { + sequence = av.getAlignment().getSequenceAt(i); - if (av.isShowAnnotation()) + if (sequence == null) { - if (ap == null) - { - ap = new AnnotationPanel(av); - } + continue; + } - annotationHeight = ap.adjustPanelHeight(); - if (labels == null) - { - labels = new AnnotationLabels(av); - } + if (hasHiddenRows || av.isDisplayReferenceSeq()) + { + setHiddenFont(sequence); } - int hgap = av.getCharHeight(); - if (av.getScaleAboveWrapped()) + // Selected sequence colours + if ((searchResults != null) && searchResults.contains(sequence)) { - hgap += av.getCharHeight(); + currentColor = Color.black; + currentTextColor = Color.white; } + else if ((av.getSelectionGroup() != null) + && av.getSelectionGroup().getSequences(null) + .contains(sequence)) + { + currentColor = Color.lightGray; + currentTextColor = Color.black; + } + else + { + currentColor = av.getSequenceColour(sequence); + currentTextColor = Color.black; + } + + gg.setColor(currentColor); + + gg.fillRect(0, (i - starty) * av.getCharHeight(), getWidth(), + av.getCharHeight()); - int cHeight = alheight * av.getCharHeight() + hgap + annotationHeight; + gg.setColor(currentTextColor); - int rowSize = av.getRanges().getEndRes() - - av.getRanges().getStartRes(); + String string = sequence.getDisplayId(av.getShowJVSuffix()); - // Draw the rest of the panels - for (int ypos = hgap, row = av.getRanges().getStartRes(); (ypos <= getHeight()) - && (row < maxwidth); ypos += cHeight, row += rowSize) + if (av.isRightAlignIds()) { - for (int i = starty; i < alheight; i++) - { - SequenceI s = av.getAlignment().getSequenceAt(i); - if (doHiddenCheck) - { - setHiddenFont(s); - } - else - { - gg.setFont(getIdfont()); - } - - drawIdString(gg, hiddenRows, s, i, 0, ypos); - } + xPos = panelWidth - fm.stringWidth(string) - 4; + } - if (labels != null && av.isShowAnnotation()) - { - gg.translate(0, ypos + (alheight * av.getCharHeight())); - labels.drawComponent(gg, getWidth()); - gg.translate(0, -ypos - (alheight * av.getCharHeight())); - } + gg.drawString(string, xPos, + (((i - starty) * av.getCharHeight()) + av.getCharHeight()) + - (av.getCharHeight() / 5)); + + if (hasHiddenRows) + { + drawMarker(i, starty, 0); } } - else - { - // No need to hang on to labels if we're not wrapped - labels = null; + } - // Now draw the id strings - int panelWidth = getWidth(); - int xPos = 0; + /** + * Draws sequence ids in wrapped mode + * + * @param starty + * @param hasHiddenRows + */ + protected void drawIdsWrapped(int starty, boolean hasHiddenRows) + { + int maxwidth = av.getAlignment().getWidth(); + int alheight = av.getAlignment().getHeight(); - SequenceI sequence; - // Now draw the id strings - for (int i = starty; i <= endy; i++) - { - sequence = av.getAlignment().getSequenceAt(i); + if (av.hasHiddenColumns()) + { + maxwidth = av.getAlignment().getHiddenColumns() + .findColumnPosition(maxwidth) - 1; + } - if (sequence == null) - { - continue; - } + int annotationHeight = 0; - if (doHiddenCheck) - { - setHiddenFont(sequence); - } + if (av.isShowAnnotation()) + { + if (ap == null) + { + ap = new AnnotationPanel(av); + } - // Selected sequence colours - if ((searchResults != null) && searchResults.contains(sequence)) - { - currentColor = Color.black; - currentTextColor = Color.white; - } - else if ((av.getSelectionGroup() != null) - && av.getSelectionGroup().getSequences(null) - .contains(sequence)) - { - currentColor = Color.lightGray; - currentTextColor = Color.black; - } - else - { - currentColor = av.getSequenceColour(sequence); - currentTextColor = Color.black; - } + annotationHeight = ap.adjustPanelHeight(); + if (labels == null) + { + labels = new AnnotationLabels(av); + } + } - gg.setColor(currentColor); + int hgap = av.getCharHeight(); + if (av.getScaleAboveWrapped()) + { + hgap += av.getCharHeight(); + } - gg.fillRect(0, (i - starty) * av.getCharHeight(), getWidth(), - av.getCharHeight()); + int cHeight = alheight * av.getCharHeight() + hgap + annotationHeight; - gg.setColor(currentTextColor); + ViewportRanges ranges = av.getRanges(); - String string = sequence.getDisplayId(av.getShowJVSuffix()); + int rowSize = ranges.getViewportWidth(); - if (av.isRightAlignIds()) + /* + * draw repeating sequence ids until out of sequence data or + * out of visible space, whichever comes first + */ + int ypos = hgap; + int row = ranges.getStartRes(); + while ((ypos <= getHeight()) && (row < maxwidth)) + { + for (int i = starty; i < alheight; i++) + { + SequenceI s = av.getAlignment().getSequenceAt(i); + if (hasHiddenRows || av.isDisplayReferenceSeq()) { - xPos = panelWidth - fm.stringWidth(string) - 4; + setHiddenFont(s); } - - gg.drawString(string, xPos, - (((i - starty) * av.getCharHeight()) + av.getCharHeight()) - - (av.getCharHeight() / 5)); - - if (hiddenRows) + else { - drawMarker(i, starty, 0); + gg.setFont(getIdfont()); } + drawIdString(gg, hasHiddenRows, s, i, 0, ypos); + } + + if (labels != null && av.isShowAnnotation()) + { + gg.translate(0, ypos + (alheight * av.getCharHeight())); + labels.drawComponent(gg, getWidth()); + gg.translate(0, -ypos - (alheight * av.getCharHeight())); } + ypos += cHeight; + row += rowSize; } } @@ -520,16 +538,25 @@ public class IdCanvas extends JPanel implements ViewportListenerI this.idfont = idfont; } + /** + * Respond to viewport range changes (e.g. alignment panel was scrolled). 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. + *

+ * In unwrapped mode, only responds to a vertical scroll, as horizontal scroll + * leaves sequence ids unchanged. In wrapped mode, only vertical scroll is + * provided, but it generates a change of "startres" which does require an + * update here. + */ @Override public void propertyChange(PropertyChangeEvent evt) { - // Respond to viewport range changes (e.g. alignment panel was scrolled) - // 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 (evt.getPropertyName().equals(ViewportRanges.STARTSEQ)) + String propertyName = evt.getPropertyName(); + if (propertyName.equals(ViewportRanges.STARTSEQ) + || (av.getWrapAlignment() && propertyName + .equals(ViewportRanges.STARTRES))) { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index 6097089..065f97e 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -138,7 +138,7 @@ public class IdPanel extends JPanel implements MouseListener, } lastid = seq; - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false); } /** @@ -313,7 +313,7 @@ public class IdPanel extends JPanel implements MouseListener, av.isSelectionGroupChanged(true); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false); } /** diff --git a/src/jalview/gui/OverviewPanel.java b/src/jalview/gui/OverviewPanel.java index 3fa674e..7a4456e 100755 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@ -107,13 +107,11 @@ public class OverviewPanel extends JPanel implements Runnable, @Override public void mouseDragged(MouseEvent evt) { - if (!SwingUtilities.isRightMouseButton(evt) - && !av.getWrapAlignment()) + if (!SwingUtilities.isRightMouseButton(evt)) { od.updateViewportFromMouse(evt.getX(), evt.getY(), av .getAlignment().getHiddenSequences(), av.getAlignment() .getHiddenColumns()); - } } }); @@ -130,7 +128,8 @@ public class OverviewPanel extends JPanel implements Runnable, showPopupMenu(evt); } } - else if (!av.getWrapAlignment()) + else + // if (!av.getWrapAlignment()) { od.updateViewportFromMouse(evt.getX(), evt.getY(), av .getAlignment().getHiddenSequences(), av.getAlignment() @@ -148,7 +147,6 @@ public class OverviewPanel extends JPanel implements Runnable, } }); - updateOverviewImage(); } @@ -206,6 +204,14 @@ public class OverviewPanel extends JPanel implements Runnable, */ public void updateOverviewImage() { + if (oviewCanvas == null) + { + /* + * panel has been disposed + */ + return; + } + if ((getWidth() > 0) && (getHeight() > 0)) { od.setWidth(getWidth()); @@ -222,7 +228,6 @@ public class OverviewPanel extends JPanel implements Runnable, Thread thread = new Thread(this); thread.start(); repaint(); - } @Override @@ -252,4 +257,21 @@ public class OverviewPanel extends JPanel implements Runnable, { setBoxPosition(); } + + /** + * Removes this object as a property change listener, and nulls references + */ + protected void dispose() + { + try + { + av.getRanges().removePropertyChangeListener(this); + } finally + { + av = null; + oviewCanvas = null; + ap = null; + od = null; + } + } } diff --git a/src/jalview/gui/ScalePanel.java b/src/jalview/gui/ScalePanel.java index e5f2be8..2302ebe 100755 --- a/src/jalview/gui/ScalePanel.java +++ b/src/jalview/gui/ScalePanel.java @@ -169,10 +169,6 @@ public class ScalePanel extends JPanel implements MouseMotionListener, av.showColumn(reveal[0]); reveal = null; ap.paintAlignment(true); - if (ap.overviewPanel != null) - { - ap.overviewPanel.updateOverviewImage(); - } av.sendSelection(); } }); @@ -189,10 +185,6 @@ public class ScalePanel extends JPanel implements MouseMotionListener, av.showAllHiddenColumns(); reveal = null; ap.paintAlignment(true); - if (ap.overviewPanel != null) - { - ap.overviewPanel.updateOverviewImage(); - } av.sendSelection(); } }); @@ -218,10 +210,6 @@ public class ScalePanel extends JPanel implements MouseMotionListener, } ap.paintAlignment(true); - if (ap.overviewPanel != null) - { - ap.overviewPanel.updateOverviewImage(); - } av.sendSelection(); } }); @@ -422,8 +410,15 @@ public class ScalePanel extends JPanel implements MouseMotionListener, @Override public void paintComponent(Graphics g) { - drawScale(g, av.getRanges().getStartRes(), av.getRanges().getEndRes(), - getWidth(), getHeight()); + /* + * shouldn't get called in wrapped mode as the scale above is + * drawn instead by SeqCanvas.drawNorthScale + */ + if (!av.getWrapAlignment()) + { + drawScale(g, av.getRanges().getStartRes(), + av.getRanges().getEndRes(), getWidth(), getHeight()); + } } // scalewidth will normally be screenwidth, diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index f392810..a134afa 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -495,6 +495,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI FontMetrics fm = getFontMetrics(av.getFont()); + LABEL_EAST = 0; + LABEL_WEST = 0; + if (av.getScaleRightWrapped()) { LABEL_EAST = fm.stringWidth(getMask()); @@ -516,16 +519,16 @@ public class SeqCanvas extends JComponent implements ViewportListenerI av.setWrappedWidth(cWidth); - av.getRanges().setEndRes(av.getRanges().getStartRes() + cWidth - 1); + av.getRanges().setViewportStartAndWidth(startRes, cWidth); int endx; int ypos = hgap; - int maxwidth = av.getAlignment().getWidth() - 1; + int maxwidth = av.getAlignment().getWidth(); if (av.hasHiddenColumns()) { maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; + .findColumnPosition(maxwidth); } while ((ypos <= canvasHeight) && (startRes < maxwidth)) @@ -990,7 +993,14 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { String eventName = evt.getPropertyName(); - if (!av.getWrapAlignment()) + if (av.getWrapAlignment()) + { + if (eventName.equals(ViewportRanges.STARTRES)) + { + repaint(); + } + } + else { int scrollX = 0; if (eventName.equals(ViewportRanges.STARTRES)) diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 7651eb4..6fbed49 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -189,6 +189,7 @@ public class SeqPanel extends JPanel implements MouseListener, int res = 0; int x = evt.getX(); + int startRes = av.getRanges().getStartRes(); if (av.getWrapAlignment()) { @@ -203,7 +204,7 @@ public class SeqPanel extends JPanel implements MouseListener, int y = evt.getY(); y -= hgap; - x -= seqCanvas.LABEL_WEST; + x = Math.max(0, x - seqCanvas.LABEL_WEST); int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth()); if (cwidth < 1) @@ -212,10 +213,11 @@ public class SeqPanel extends JPanel implements MouseListener, } wrappedBlock = y / cHeight; - wrappedBlock += av.getRanges().getStartRes() / cwidth; - - res = wrappedBlock * cwidth + x / av.getCharWidth(); - + wrappedBlock += startRes / cwidth; + // allow for wrapped view scrolled right (possible from Overview) + int startOffset = startRes % cwidth; + res = wrappedBlock * cwidth + + Math.min(cwidth - 1, startOffset + x / av.getCharWidth()); } else { @@ -225,7 +227,7 @@ public class SeqPanel extends JPanel implements MouseListener, // right-hand gutter x = seqCanvas.getX() + seqCanvas.getWidth(); } - res = (x / av.getCharWidth()) + av.getRanges().getStartRes(); + res = (x / av.getCharWidth()) + startRes; if (res > av.getRanges().getEndRes()) { // moused off right diff --git a/src/jalview/renderer/seqfeatures/FeatureRenderer.java b/src/jalview/renderer/seqfeatures/FeatureRenderer.java index 98f5bff..759101d 100644 --- a/src/jalview/renderer/seqfeatures/FeatureRenderer.java +++ b/src/jalview/renderer/seqfeatures/FeatureRenderer.java @@ -335,6 +335,12 @@ public class FeatureRenderer extends FeatureRendererModel } Color featureColour = getColour(sequenceFeature); + if (featureColour == null) + { + // score feature outwith threshold for colouring + continue; + } + boolean isContactFeature = sequenceFeature.isContactFeature(); if (isContactFeature) @@ -352,7 +358,7 @@ public class FeatureRenderer extends FeatureRendererModel drawnColour = featureColour; } } - else if (showFeature(sequenceFeature)) + else { if (av.isShowSequenceFeaturesHeight() && !Float.isNaN(sequenceFeature.score)) diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 9a08a2b..5a7a27f 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -405,6 +405,7 @@ public abstract class AlignmentViewport implements AlignViewportI, public void setWrapAlignment(boolean state) { viewStyle.setWrapAlignment(state); + ranges.setWrappedMode(state); } /** @@ -1390,6 +1391,9 @@ public abstract class AlignmentViewport implements AlignViewportI, // common hide/show seq stuff public void showAllHiddenSeqs() { + int startSeq = ranges.getStartSeq(); + int endSeq = ranges.getEndSeq(); + if (alignment.getHiddenSequences().getSize() > 0) { if (selectionGroup == null) @@ -1407,6 +1411,8 @@ public abstract class AlignmentViewport implements AlignViewportI, hiddenRepSequences = null; + ranges.setStartEndSeq(startSeq, endSeq + tmp.size()); + firePropertyChange("alignment", null, alignment.getSequences()); // used to set hasHiddenRows/hiddenRepSequences here, after the property // changed event @@ -1416,6 +1422,9 @@ public abstract class AlignmentViewport implements AlignViewportI, public void showSequence(int index) { + int startSeq = ranges.getStartSeq(); + int endSeq = ranges.getEndSeq(); + List tmp = alignment.getHiddenSequences().showSequence( index, hiddenRepSequences); if (tmp.size() > 0) @@ -1431,6 +1440,9 @@ public abstract class AlignmentViewport implements AlignViewportI, selectionGroup.addSequence(seq, false); setSequenceAnnotationsVisible(seq, true); } + + ranges.setStartEndSeq(startSeq, endSeq + tmp.size()); + firePropertyChange("alignment", null, alignment.getSequences()); sendSelection(); } @@ -1452,6 +1464,11 @@ public abstract class AlignmentViewport implements AlignViewportI, public void hideSequence(SequenceI[] seq) { + /* + * cache offset to first visible sequence + */ + int startSeq = ranges.getStartSeq(); + if (seq != null) { for (int i = 0; i < seq.length; i++) @@ -1459,6 +1476,7 @@ public abstract class AlignmentViewport implements AlignViewportI, alignment.getHiddenSequences().hideSequence(seq[i]); setSequenceAnnotationsVisible(seq[i], false); } + ranges.setStartSeq(startSeq); firePropertyChange("alignment", null, alignment.getSequences()); } } diff --git a/src/jalview/viewmodel/OverviewDimensions.java b/src/jalview/viewmodel/OverviewDimensions.java index d2912d8..7ac07ac 100644 --- a/src/jalview/viewmodel/OverviewDimensions.java +++ b/src/jalview/viewmodel/OverviewDimensions.java @@ -231,7 +231,8 @@ public abstract class OverviewDimensions resetAlignmentDims(); // boxX, boxY is the x,y location equivalent to startRes, startSeq - boxX = Math.round((float) startRes * width / alwidth); + int xPos = Math.min(startRes, alwidth - vpwidth + 1); + boxX = Math.round((float) xPos * width / alwidth); boxY = Math.round((float) startSeq * sequencesHeight / alheight); // boxWidth is the width in residues translated to pixels diff --git a/src/jalview/viewmodel/OverviewDimensionsHideHidden.java b/src/jalview/viewmodel/OverviewDimensionsHideHidden.java index 4bf7694..4d64f1c 100644 --- a/src/jalview/viewmodel/OverviewDimensionsHideHidden.java +++ b/src/jalview/viewmodel/OverviewDimensionsHideHidden.java @@ -39,6 +39,11 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions y = 0; } + if (ranges.isWrappedMode()) + { + y = 0; // sorry, no vertical scroll when wrapped + } + // // Convert x value to residue position // diff --git a/src/jalview/viewmodel/OverviewDimensionsShowHidden.java b/src/jalview/viewmodel/OverviewDimensionsShowHidden.java index 4b396a6..62e8000 100644 --- a/src/jalview/viewmodel/OverviewDimensionsShowHidden.java +++ b/src/jalview/viewmodel/OverviewDimensionsShowHidden.java @@ -81,6 +81,11 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions y = 0; } + if (ranges.isWrappedMode()) + { + y = 0; // sorry, no vertical scroll when wrapped + } + // // Convert x value to residue position // @@ -135,10 +140,12 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions // so convert back after getting visible region position yAsSeq = hiddenSeqs.adjustForHiddenSeqs(hiddenSeqs .findIndexWithoutHiddenSeqs(yAsSeq)); + yAsSeq = Math.max(yAsSeq, 0); // -1 if before first visible sequence // check in case we went off the edge of the alignment int visAlignHeight = hiddenSeqs.findIndexWithoutHiddenSeqs(alheight); int visYAsSeq = hiddenSeqs.findIndexWithoutHiddenSeqs(yAsSeq); + visYAsSeq = Math.max(visYAsSeq, 0); // -1 if before first visible sequence if (visYAsSeq + vpheight - 1 > visAlignHeight) { // went past the end of the alignment, adjust backwards @@ -156,7 +163,6 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions // update viewport ranges.setStartRes(visXAsRes); ranges.setStartSeq(visYAsSeq); - } /** diff --git a/src/jalview/viewmodel/ViewportRanges.java b/src/jalview/viewmodel/ViewportRanges.java index c2bcf3f..10cf583 100644 --- a/src/jalview/viewmodel/ViewportRanges.java +++ b/src/jalview/viewmodel/ViewportRanges.java @@ -40,6 +40,8 @@ public class ViewportRanges extends ViewportProperties public static final String ENDSEQ = "endseq"; + private boolean wrappedMode = false; + // start residue of viewport private int startRes; @@ -129,9 +131,14 @@ public class ViewportRanges extends ViewportProperties public void setStartEndRes(int start, int end) { int oldstartres = this.startRes; - if (start > getVisibleAlignmentWidth() - 1) + + /* + * if not wrapped, don't leave white space at the right margin + */ + int lastColumn = getVisibleAlignmentWidth() - 1; + if (!wrappedMode && (start > lastColumn)) { - startRes = Math.max(getVisibleAlignmentWidth() - 1, 0); + startRes = Math.max(lastColumn, 0); } else if (start < 0) { @@ -147,9 +154,9 @@ public class ViewportRanges extends ViewportProperties { endRes = 0; } - else if (end > getVisibleAlignmentWidth() - 1) + else if (!wrappedMode && (end > lastColumn)) { - endRes = Math.max(getVisibleAlignmentWidth() - 1, 0); + endRes = Math.max(lastColumn, 0); } else { @@ -166,23 +173,6 @@ public class ViewportRanges extends ViewportProperties } /** - * Set last residue visible in the viewport. Fires a property change event. - * - * @param res - * residue position - */ - public void setEndRes(int res) - { - int startres = res; - int width = getViewportWidth(); - if (startres + width - 1 > getVisibleAlignmentWidth() - 1) - { - startres = getVisibleAlignmentWidth() - width; - } - setStartEndRes(startres - width + 1, startres); - } - - /** * Set the first sequence visible in the viewport, maintaining the height. If * the viewport would extend past the last sequence, sets the viewport so it * sits at the bottom of the alignment. Fires a property change event. @@ -214,9 +204,10 @@ public class ViewportRanges extends ViewportProperties public void setStartEndSeq(int start, int end) { int oldstartseq = this.startSeq; - if (start > getVisibleAlignmentHeight() - 1) + int visibleHeight = getVisibleAlignmentHeight(); + if (start > visibleHeight - 1) { - startSeq = Math.max(getVisibleAlignmentHeight() - 1, 0); + startSeq = Math.max(visibleHeight - 1, 0); } else if (start < 0) { @@ -228,9 +219,9 @@ public class ViewportRanges extends ViewportProperties } int oldendseq = this.endSeq; - if (end >= getVisibleAlignmentHeight()) + if (end >= visibleHeight) { - endSeq = Math.max(getVisibleAlignmentHeight() - 1, 0); + endSeq = Math.max(visibleHeight - 1, 0); } else if (end < 0) { @@ -338,12 +329,18 @@ public class ViewportRanges extends ViewportProperties { vpstart = 0; } - else if ((w <= getVisibleAlignmentWidth()) - && (vpstart + w - 1 > getVisibleAlignmentWidth() - 1)) - // viewport width is less than the full alignment and we are running off the - // RHS edge + + /* + * if not wrapped, don't leave white space at the right margin + */ + if (!wrappedMode) { - vpstart = getVisibleAlignmentWidth() - w; + if ((w <= getVisibleAlignmentWidth()) + && (vpstart + w - 1 > getVisibleAlignmentWidth() - 1)) + { + vpstart = getVisibleAlignmentWidth() - w; + } + } setStartEndRes(vpstart, vpstart + w - 1); } @@ -514,7 +511,15 @@ public class ViewportRanges extends ViewportProperties */ public void pageUp() { - setViewportStartAndHeight(2 * startSeq - endSeq, getViewportHeight()); + if (wrappedMode) + { + setStartRes(Math.max(0, getStartRes() - getViewportWidth())); + } + else + { + setViewportStartAndHeight(startSeq - (endSeq - startSeq), + getViewportHeight()); + } } /** @@ -522,6 +527,94 @@ public class ViewportRanges extends ViewportProperties */ public void pageDown() { - setViewportStartAndHeight(endSeq, getViewportHeight()); + if (wrappedMode) + { + /* + * if height is more than width (i.e. not all sequences fit on screen), + * increase page down to height + */ + int newStart = getStartRes() + + Math.max(getViewportHeight(), getViewportWidth()); + + /* + * don't page down beyond end of alignment, or if not all + * sequences fit in the visible height + */ + if (newStart < getVisibleAlignmentWidth()) + { + setStartRes(newStart); + } + } + else + { + setViewportStartAndHeight(endSeq, getViewportHeight()); + } + } + + public void setWrappedMode(boolean wrapped) + { + wrappedMode = wrapped; + } + + public boolean isWrappedMode() + { + return wrappedMode; + } + + /** + * Answers the vertical scroll position (0..) to set, given the visible column + * that is at top left. + * + *

+   * Example:
+   *    viewport width 40 columns (0-39, 40-79, 80-119...)
+   *    column 0 returns scroll position 0
+   *    columns 1-40 return scroll position 1
+   *    columns 41-80 return scroll position 2
+   *    etc
+   * 
+ * + * @param topLeftColumn + * (0..) + * @return + */ + public int getWrappedScrollPosition(final int topLeftColumn) + { + int w = getViewportWidth(); + + /* + * visible whole widths + */ + int scroll = topLeftColumn / w; + + /* + * add 1 for a part width if there is one + */ + scroll += topLeftColumn % w > 0 ? 1 : 0; + + return scroll; + } + + /** + * Answers the maximum wrapped vertical scroll value, given the column + * position (0..) to show at top left of the visible region. + * + * @param topLeftColumn + * @return + */ + public int getWrappedMaxScroll(int topLeftColumn) + { + int scrollPosition = getWrappedScrollPosition(topLeftColumn); + + /* + * how many more widths could be drawn after this one? + */ + int columnsRemaining = getVisibleAlignmentWidth() - topLeftColumn; + int width = getViewportWidth(); + int widthsRemaining = columnsRemaining / width + + (columnsRemaining % width > 0 ? 1 : 0) - 1; + int maxScroll = scrollPosition + widthsRemaining; + + return maxScroll; } } diff --git a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java index f6addb8..40f38b6 100644 --- a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java +++ b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java @@ -575,9 +575,17 @@ public abstract class FeatureRendererModel implements public Color getColour(SequenceFeature feature) { FeatureColourI fc = getFeatureStyle(feature.getType()); - return fc.getColor(feature); + return fc.isColored(feature) ? fc.getColor(feature) : null; } + /** + * Answers true unless the feature has a score value which lies outside a + * minimum or maximum threshold configured for colouring. This method does not + * check feature type or group visibility. + * + * @param sequenceFeature + * @return + */ protected boolean showFeature(SequenceFeature sequenceFeature) { FeatureColourI fc = getFeatureStyle(sequenceFeature.type); diff --git a/test/jalview/datamodel/HiddenSequencesTest.java b/test/jalview/datamodel/HiddenSequencesTest.java index 7795988..11b993d 100644 --- a/test/jalview/datamodel/HiddenSequencesTest.java +++ b/test/jalview/datamodel/HiddenSequencesTest.java @@ -181,7 +181,8 @@ public class HiddenSequencesTest { AlignmentI al = new Alignment(seqs); HiddenSequences hs = al.getHiddenSequences(); - for (int i = 0; i < SEQ_COUNT; i++) + int height = al.getHeight(); + for (int i = 0; i < height; i++) { assertEquals(i, hs.findIndexWithoutHiddenSeqs(i)); } @@ -194,7 +195,7 @@ public class HiddenSequencesTest /* * alignment is now seq0/2/3/4/7/8/9 */ - assertEquals(SEQ_COUNT - 3, al.getHeight()); + assertEquals(height - 3, al.getHeight()); assertEquals(0, hs.findIndexWithoutHiddenSeqs(0)); assertEquals(0, hs.findIndexWithoutHiddenSeqs(1)); assertEquals(1, hs.findIndexWithoutHiddenSeqs(2)); @@ -205,6 +206,19 @@ public class HiddenSequencesTest assertEquals(4, hs.findIndexWithoutHiddenSeqs(7)); assertEquals(5, hs.findIndexWithoutHiddenSeqs(8)); assertEquals(6, hs.findIndexWithoutHiddenSeqs(9)); + + /* + * hide first two sequences + */ + hs.showAll(null); + hs.hideSequence(seqs[0]); + hs.hideSequence(seqs[1]); + assertEquals(-1, hs.findIndexWithoutHiddenSeqs(0)); + assertEquals(-1, hs.findIndexWithoutHiddenSeqs(1)); + for (int i = 2; i < height; i++) + { + assertEquals(i - 2, hs.findIndexWithoutHiddenSeqs(i)); + } } /** diff --git a/test/jalview/gui/AlignViewportTest.java b/test/jalview/gui/AlignViewportTest.java index 346d74dd..b2286e0 100644 --- a/test/jalview/gui/AlignViewportTest.java +++ b/test/jalview/gui/AlignViewportTest.java @@ -46,6 +46,7 @@ import jalview.schemes.ColourSchemeI; import jalview.schemes.PIDColourScheme; import jalview.structure.StructureSelectionManager; import jalview.util.MapList; +import jalview.viewmodel.ViewportRanges; import java.util.ArrayList; import java.util.List; @@ -469,4 +470,46 @@ public class AlignViewportTest SequenceI cons = testme.getConsensusSeq(); assertEquals("A-C", cons.getSequenceAsString()); } + + @Test(groups = { "Functional" }) + public void testHideRevealSequences() + { + ViewportRanges ranges = testee.getRanges(); + assertEquals(3, al.getHeight()); + assertEquals(0, ranges.getStartSeq()); + assertEquals(2, ranges.getEndSeq()); + + /* + * hide first sequence + */ + testee.hideSequence(new SequenceI[] { al.getSequenceAt(0) }); + assertEquals(2, al.getHeight()); + assertEquals(0, ranges.getStartSeq()); + assertEquals(1, ranges.getEndSeq()); + + /* + * reveal hidden sequences above the first + */ + testee.showSequence(0); + assertEquals(3, al.getHeight()); + assertEquals(0, ranges.getStartSeq()); + assertEquals(2, ranges.getEndSeq()); + + /* + * hide first and third sequences + */ + testee.hideSequence(new SequenceI[] { al.getSequenceAt(0), + al.getSequenceAt(2) }); + assertEquals(1, al.getHeight()); + assertEquals(0, ranges.getStartSeq()); + assertEquals(0, ranges.getEndSeq()); + + /* + * reveal all hidden sequences + */ + testee.showAllHiddenSeqs(); + assertEquals(3, al.getHeight()); + assertEquals(0, ranges.getStartSeq()); + assertEquals(2, ranges.getEndSeq()); + } } diff --git a/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java b/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java index 9b68b43..4fc079e 100644 --- a/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java +++ b/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java @@ -450,4 +450,69 @@ public class FeatureColourFinderTest FeatureColourFinder finder2 = new FeatureColourFinder(null); assertTrue(finder2.noFeaturesDisplayed()); } + + @Test(groups = "Functional") + public void testFindFeatureColour_graduatedWithThreshold() + { + seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 2, + 2, 0f, "KdGroup")); + seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 4, + 4, 5f, "KdGroup")); + seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 7, + 7, 10f, "KdGroup")); + + /* + * graduated colour from 0 to 10 + * above threshold value of 5 + */ + Color min = new Color(100, 50, 150); + Color max = new Color(200, 0, 100); + FeatureColourI fc = new FeatureColour(min, max, 0, 10); + fc.setAboveThreshold(true); + fc.setThreshold(5f); + fr.setColour("kd", fc); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + + /* + * position 2, column 1, score 0 - below threshold - default colour + */ + Color c = finder.findFeatureColour(Color.blue, seq, 1); + assertEquals(c, Color.blue); + + /* + * position 4, column 3, score 5 - at threshold - default colour + */ + c = finder.findFeatureColour(Color.blue, seq, 3); + assertEquals(c, Color.blue); + + /* + * position 7, column 9, score 10 - maximum colour in range + */ + c = finder.findFeatureColour(Color.blue, seq, 9); + assertEquals(c, max); + + /* + * now colour below threshold of 5 + */ + fc.setBelowThreshold(true); + + /* + * position 2, column 1, score 0 - min colour + */ + c = finder.findFeatureColour(Color.blue, seq, 1); + assertEquals(c, min); + + /* + * position 4, column 3, score 5 - at threshold - default colour + */ + c = finder.findFeatureColour(Color.blue, seq, 3); + assertEquals(c, Color.blue); + + /* + * position 7, column 9, score 10 - above threshold - default colour + */ + c = finder.findFeatureColour(Color.blue, seq, 9); + assertEquals(c, Color.blue); + } } diff --git a/test/jalview/viewmodel/ViewportRangesTest.java b/test/jalview/viewmodel/ViewportRangesTest.java index 636f8dd..70a3687 100644 --- a/test/jalview/viewmodel/ViewportRangesTest.java +++ b/test/jalview/viewmodel/ViewportRangesTest.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -25,6 +26,14 @@ public class ViewportRangesTest { AlignmentI smallAl = gen.generate(7, 2, 2, 5, 5); + @BeforeClass(alwaysRun = true) + public void setUp() + { + gen = new AlignmentGenerator(false); + al = gen.generate(20, 30, 1, 5, 5); + smallAl = gen.generate(7, 2, 2, 5, 5); + } + @BeforeMethod(alwaysRun = true) public void cleanUp() { @@ -65,17 +74,6 @@ public class ViewportRangesTest { } @Test(groups = { "Functional" }) - public void testSetEndRes() - { - ViewportRanges vr = new ViewportRanges(al); - vr.setEndRes(-1); - assertEquals(vr.getEndRes(), 0); - - vr.setEndRes(al.getWidth() - 1); - assertEquals(vr.getEndRes(), al.getWidth() - 1); - } - - @Test(groups = { "Functional" }) public void testSetEndSeq() { ViewportRanges vr = new ViewportRanges(al); @@ -85,7 +83,8 @@ public class ViewportRangesTest { vr.setEndSeq(al.getHeight()); assertEquals(vr.getEndSeq(), al.getHeight() - 1); - vr.setEndRes(al.getHeight() - 1); + // vr.setEndRes(al.getHeight() - 1); + vr.setEndSeq(al.getHeight() - 1); assertEquals(vr.getEndSeq(), al.getHeight() - 1); } @@ -401,15 +400,6 @@ public class ViewportRangesTest { assertTrue(l.verify(0, emptylist)); l.reset(); - vr.setEndRes(10); - assertTrue(l.verify(1, Arrays.asList("startres"))); - l.reset(); - - // no event fired for same value - vr.setEndRes(10); - assertTrue(l.verify(0, emptylist)); - l.reset(); - vr.setStartSeq(4); assertTrue(l.verify(1, Arrays.asList("startseq"))); l.reset(); @@ -523,6 +513,223 @@ public class ViewportRangesTest { assertTrue(l.verify(1, Arrays.asList("startres"))); l.reset(); } + + @Test(groups = { "Functional" }) + public void testGetWrappedScrollPosition() + { + AlignmentI al2 = gen.generate(157, 15, 1, 5, 5); + ViewportRanges vr = new ViewportRanges(al2); + vr.setStartEndRes(0, 39); + int width = vr.getViewportWidth(); // 40 + + /* + * scroll is 0 at column 0 (only) + */ + assertEquals(vr.getWrappedScrollPosition(0), 0); + + /* + * scroll is 1 at columns 1-40 + */ + int i = 1; + int j = width; + for (; i <= j; i++) + { + assertEquals(1, vr.getWrappedScrollPosition(i)); + } + + /* + * scroll is 2 at columns 41-80, etc + */ + j += width; + for (; i <= j; i++) + { + assertEquals(2, vr.getWrappedScrollPosition(i), "For " + i); + } + } + + @Test(groups = { "Functional" }) + public void testPageUpDownWrapped() + { + /* + * 15 sequences, 110 residues wide (+gaps) + */ + AlignmentI al2 = gen.generate(110, 15, 1, 5, 5); + + ViewportRanges vr = new ViewportRanges(al2); + vr.setWrappedMode(true); + + // first row + vr.setViewportStartAndWidth(0, 40); + int width = vr.getViewportWidth(); + assertEquals(width, 40); + assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getEndRes(), 39); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + // second row + vr.pageDown(); + assertEquals(vr.getStartRes(), 40); + assertEquals(vr.getEndRes(), 79); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + // third and last row + // note endRes is nominal (>width) to preserve viewport width + vr.pageDown(); + assertEquals(vr.getStartRes(), 80); + assertEquals(vr.getEndRes(), 119); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + // another pageDown should do nothing + vr.pageDown(); + assertEquals(vr.getStartRes(), 80); + assertEquals(vr.getEndRes(), 119); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + // back to second row + vr.pageUp(); + assertEquals(vr.getStartRes(), 40); + assertEquals(vr.getEndRes(), 79); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + // back to first row + vr.pageUp(); + assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getEndRes(), 39); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + // another pageUp should do nothing + vr.pageUp(); + assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getEndRes(), 39); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + /* + * simulate scroll right a few positions + */ + vr.setStartRes(5); + assertEquals(vr.getStartRes(), 5); + assertEquals(vr.getEndRes(), 5 + width - 1); // 44 + + vr.pageDown(); // 5-44 shifts to 45-84 + assertEquals(vr.getStartRes(), 45); + assertEquals(vr.getEndRes(), 84); + + vr.pageDown(); // 45-84 shifts to 85-124 + assertEquals(vr.getStartRes(), 85); + assertEquals(vr.getEndRes(), 124); + + vr.pageDown(); // no change - at end already + assertEquals(vr.getStartRes(), 85); + assertEquals(vr.getEndRes(), 124); + + vr.pageUp(); // back we go + assertEquals(vr.getStartRes(), 45); + assertEquals(vr.getEndRes(), 84); + + vr.pageUp(); + assertEquals(vr.getStartRes(), 5); + assertEquals(vr.getEndRes(), 44); + + vr.pageUp(); // back to the start + assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getEndRes(), 39); + } + + @Test(groups = { "Functional" }) + public void testSetStartEndResWrapped() + { + ViewportRanges vr = new ViewportRanges(al); + vr.setWrappedMode(true); + vr.setStartEndRes(-1, -1); + assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getEndRes(), 0); + + vr.setStartEndRes(5, 19); + assertEquals(vr.getStartRes(), 5); + assertEquals(vr.getEndRes(), 19); + + // bounds are not constrained to alignment width + // when in wrapped mode + vr.setStartEndRes(88, 888); + assertEquals(vr.getStartRes(), 88); + assertEquals(vr.getEndRes(), 888); + + ViewportRanges vrsmall = new ViewportRanges(smallAl); + vrsmall.setWrappedMode(true); + vrsmall.setStartEndRes(88, 888); + assertEquals(vrsmall.getStartRes(), 88); + assertEquals(vrsmall.getEndRes(), 888); + + // make visible alignment width = 0 + smallAl.getHiddenColumns().hideColumns(0, 6); + vrsmall.setStartEndRes(0, 4); + assertEquals(vrsmall.getStartRes(), 0); + assertEquals(vrsmall.getEndRes(), 4); + } + + @Test(groups = { "Functional" }) + public void testSetViewportStartAndWidthWrapped() + { + ViewportRanges vr = new ViewportRanges(al); + vr.setWrappedMode(true); + vr.setViewportStartAndWidth(2, 6); + assertEquals(vr.getViewportWidth(), 6); + assertEquals(vr.getStartRes(), 2); + + // reset -ve values of start to 0 + vr.setViewportStartAndWidth(-1, 7); + assertEquals(vr.getViewportWidth(), 7); + assertEquals(vr.getStartRes(), 0); + + // out of bounds values are not forced to within bounds + vr.setViewportStartAndWidth(35, 5); + assertEquals(vr.getViewportWidth(), 5); + assertEquals(vr.getStartRes(), 35); + + // small alignment doesn't get bounds reset + ViewportRanges vrsmall = new ViewportRanges(smallAl); + vrsmall.setViewportStartAndWidth(0, 63); + assertEquals(vrsmall.getViewportWidth(), 7); + assertEquals(vrsmall.getStartRes(), 0); + } + + @Test(groups = { "Functional" }) + public void testGetWrappedMaxScroll() + { + // generate an ungapped alignment of width 140 + int alignmentWidth = 140; + AlignmentI al2 = gen.generate(alignmentWidth, 15, 1, 0, 5); + ViewportRanges vr = new ViewportRanges(al2); + vr.setStartEndRes(0, 39); + int width = vr.getViewportWidth(); // 40 + int partWidth = alignmentWidth % width; // 20 + + /* + * there are 3 * 40 remainder 20 residues + * number of widths depends on offset (scroll right) + * 4 widths (maxScroll = 3) if offset by 0 or more than 19 columns + * 5 widths (maxScroll = 4) if 1 <= offset <= 19 + */ + for (int col = 0; col < alignmentWidth; col++) + { + int offset = col % width; + if (offset > 0 && offset < partWidth) + { + assertEquals(vr.getWrappedMaxScroll(col), 4, "col " + col); + } + else + { + assertEquals(vr.getWrappedMaxScroll(col), 3, "col " + col); + } + } + } } // mock listener for property change events