+
+ /**
+ * Shifts the visible alignment by the specified number of columns - left if
+ * negative, right if positive. Includes scale above, left or right and
+ * annotations (if shown). Does not draw newly visible columns.
+ *
+ * @param positions
+ */
+ protected void shiftWrappedAlignment(int positions)
+ {
+ if (positions == 0)
+ {
+ return;
+ }
+
+ int repeatHeight = getRepeatHeightWrapped();
+ ViewportRanges ranges = av.getRanges();
+ int widthToCopy = (ranges.getViewportWidth() - Math.abs(positions))
+ * charWidth;
+ int visibleWidths = getHeight() / repeatHeight;
+ if (getHeight() % repeatHeight > 0)
+ {
+ visibleWidths++;
+ }
+ int viewportWidth = ranges.getViewportWidth();
+
+ 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
+ */
+ int xpos = ranges.getStartRes() + (visibleWidths - 1) * viewportWidth;
+
+ /*
+ * get y-offset of last wrapped width
+ */
+ int y = getHeight() / repeatHeight * repeatHeight;
+ int copyFromLeftStart = labelWidthWest;
+ int copyFromRightStart = copyFromLeftStart + widthToCopy;
+
+ while (y >= 0)
+ {
+ // todo limit repeatHeight for a last part height width
+ gg.copyArea(copyFromLeftStart, y, widthToCopy, repeatHeight,
+ positions * charWidth, 0);
+ if (y > 0)
+ {
+ gg.copyArea(copyFromRightStart, y - repeatHeight, positions
+ * charWidth, repeatHeight, -widthToCopy, repeatHeight);
+ }
+
+ if (av.getScaleLeftWrapped())
+ {
+ // drawVerticalScale(gg, xpos, xpos + viewportWidth, y, true);
+ }
+ if (av.getScaleRightWrapped())
+ {
+ // drawVerticalScale(gg, xpos, xpos + viewportWidth, y, false);
+ }
+
+ y -= repeatHeight;
+ xpos -= viewportWidth;
+ }
+ }
+ 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 = ranges.getStartRes();
+ int y = 0;
+ int copyFromRightStart = labelWidthWest - positions * charWidth;
+
+ while (y < getHeight())
+ {
+ // todo limit repeatHeight for a last part height width
+ gg.copyArea(copyFromRightStart, y, widthToCopy, repeatHeight,
+ positions * charWidth, 0);
+ if (y + repeatHeight < getHeight())
+ {
+ gg.copyArea(labelWidthWest, y + repeatHeight, -positions
+ * charWidth, repeatHeight, widthToCopy, -repeatHeight);
+ }
+
+ if (av.getScaleLeftWrapped())
+ {
+ // drawVerticalScale(gg, xpos, xpos + viewportWidth, y, true);
+ }
+ if (av.getScaleRightWrapped())
+ {
+ // drawVerticalScale(gg, xpos, xpos + viewportWidth, y, false);
+ }
+
+ y += repeatHeight;
+ xpos += ranges.getViewportWidth();
+ }
+ }
+ }
+
+ /**
+ * Redraws any positions in the search results in the visible region of a
+ * wrapped alignment. Any highlights are drawn depending on the search results
+ * set on the Viewport, not the <code>results</code> argument. This allows
+ * this method to be called either to clear highlights (passing the previous
+ * search results), or to draw new highlights.
+ *
+ * @param results
+ * @return
+ */
+ protected boolean drawMappedPositionsWrapped(SearchResultsI results)
+ {
+ if (results == null)
+ {
+ return false;
+ }
+
+ boolean matchFound = false;
+
+ int wrappedWidth = av.getWrappedWidth();
+ int wrappedHeight = getRepeatHeightWrapped();
+
+ ViewportRanges ranges = av.getRanges();
+ int canvasHeight = getHeight();
+ int repeats = canvasHeight / wrappedHeight;
+ if (canvasHeight / wrappedHeight > 0)
+ {
+ repeats++;
+ }
+
+ int firstVisibleColumn = ranges.getStartRes();
+ int lastVisibleColumn = ranges.getStartRes() + repeats
+ * ranges.getViewportWidth() - 1;
+
+ AlignmentI alignment = av.getAlignment();
+ if (av.hasHiddenColumns())
+ {
+ firstVisibleColumn = alignment.getHiddenColumns()
+ .adjustForHiddenColumns(firstVisibleColumn);
+ lastVisibleColumn = alignment.getHiddenColumns()
+ .adjustForHiddenColumns(lastVisibleColumn);
+ }
+
+ int gapHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1);
+
+ for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
+ .getEndSeq(); seqNo++)
+ {
+ SequenceI seq = alignment.getSequenceAt(seqNo);
+
+ int[] visibleResults = results.getResults(seq, firstVisibleColumn,
+ lastVisibleColumn);
+ if (visibleResults != null)
+ {
+ for (int i = 0; i < visibleResults.length - 1; i += 2)
+ {
+ int firstMatchedColumn = visibleResults[i];
+ int lastMatchedColumn = visibleResults[i + 1];
+ if (firstMatchedColumn <= lastVisibleColumn
+ && lastMatchedColumn >= firstVisibleColumn)
+ {
+ /*
+ * found a search results match in the visible region
+ */
+ firstMatchedColumn = Math.max(firstMatchedColumn,
+ firstVisibleColumn);
+ lastMatchedColumn = Math.min(lastMatchedColumn,
+ lastVisibleColumn);
+
+ /*
+ * draw each mapped position separately (as contiguous positions may
+ * wrap across lines)
+ */
+ for (int mappedPos = firstMatchedColumn; mappedPos <= lastMatchedColumn; mappedPos++)
+ {
+ int displayColumn = mappedPos;
+ if (av.hasHiddenColumns())
+ {
+ displayColumn = alignment.getHiddenColumns()
+ .findColumnPosition(displayColumn);
+ }
+
+ /*
+ * transX: offset from left edge of canvas to residue position
+ */
+ int transX = labelWidthWest
+ + ((displayColumn - ranges.getStartRes()) % wrappedWidth)
+ * av.getCharWidth();
+
+ /*
+ * transY: offset from top edge of canvas to residue position
+ */
+ int transY = gapHeight;
+ transY += (displayColumn - ranges.getStartRes())
+ / wrappedWidth * wrappedHeight;
+ transY += (seqNo - ranges.getStartSeq()) * av.getCharHeight();
+
+ /*
+ * yOffset is from graphics origin to start of visible region
+ */
+ int yOffset = 0;// (displayColumn / wrappedWidth) * wrappedHeight;
+ if (transY < getHeight())
+ {
+ matchFound = true;
+ gg.translate(transX, transY);
+ drawPanel(gg, displayColumn, displayColumn, seqNo, seqNo,
+ yOffset);
+ gg.translate(-transX, -transY);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return matchFound;
+ }
+
+ /**
+ * Answers the height in pixels of a repeating section of the wrapped
+ * alignment, including space above, scale above if shown, sequences, and
+ * annotation panel if shown
+ *
+ * @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;
+ }