+ }
+ }
+
+ /**
+ * Draws part of a selection group outline
+ *
+ * @param g
+ * @param group
+ * @param startRes
+ * @param endRes
+ * @param startSeq
+ * @param endSeq
+ * @param verticalOffset
+ */
+ private void drawPartialGroupOutline(Graphics2D g, SequenceGroup group,
+ 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;
+ int i = 0;
+ boolean inGroup = false;
+ int top = -1;
+ int bottom = -1;
+ int sy = -1;
+
+ List<SequenceI> seqs = group.getSequences(null);
+
+ // position of start residue of group relative to startRes, in pixels
+ int sx = (group.getStartRes() - startRes) * charWidth;
+
+ // width of group in pixels
+ int xwidth = (((group.getEndRes() + 1) - group.getStartRes())
+ * charWidth) - 1;
+
+ if (!(sx + xwidth < 0 || sx > visWidth))
+ {
+ for (i = startSeq; i <= endSeq; i++)
+ {
+ sy = verticalOffset + (i - startSeq) * charHeight;
+
+ if ((sx <= (endRes - startRes) * charWidth)
+ && seqs.contains(av.getAlignment().getSequenceAt(i)))
+ {
+ if ((bottom == -1)
+ && !seqs.contains(av.getAlignment().getSequenceAt(i + 1)))
+ {
+ bottom = sy + charHeight;
+ }
+
+ if (!inGroup)
+ {
+ if (((top == -1) && (i == 0)) || !seqs
+ .contains(av.getAlignment().getSequenceAt(i - 1)))
+ {
+ top = sy;
+ }
+
+ oldY = sy;
+ inGroup = true;
+ }
+ }
+ else if (inGroup)
+ {
+ drawVerticals(g, sx, xwidth, visWidth, oldY, sy);
+ drawHorizontals(g, sx, xwidth, visWidth, top, bottom);
+
+ // reset top and bottom
+ top = -1;
+ bottom = -1;
+ inGroup = false;
+ }
+ }
+ if (inGroup)
+ {
+ sy = verticalOffset + ((i - startSeq) * charHeight);
+ drawVerticals(g, sx, xwidth, visWidth, oldY, sy);
+ drawHorizontals(g, sx, xwidth, visWidth, top, bottom);
+ }
+ }
+ }
+
+ /**
+ * Draw horizontal selection group boundaries at top and bottom positions
+ *
+ * @param g
+ * graphics object to draw on
+ * @param sx
+ * start x position
+ * @param xwidth
+ * width of gap
+ * @param visWidth
+ * visWidth maximum available width
+ * @param top
+ * position to draw top of group at
+ * @param bottom
+ * position to draw bottom of group at
+ */
+ private void drawHorizontals(Graphics2D g, int sx, int xwidth,
+ int visWidth, int top, int bottom)
+ {
+ int width = xwidth;
+ int startx = sx;
+ if (startx < 0)
+ {
+ width += startx;
+ startx = 0;
+ }
+
+ // don't let width extend beyond current block, or group extent
+ // fixes JAL-2672
+ if (startx + width >= visWidth)
+ {
+ width = visWidth - startx;
+ }
+
+ if (top != -1)
+ {
+ g.drawLine(startx, top, startx + width, top);
+ }
+
+ if (bottom != -1)
+ {
+ g.drawLine(startx, bottom - 1, startx + width, bottom - 1);
+ }
+ }
+
+ /**
+ * Draw vertical lines at sx and sx+xwidth providing they lie within
+ * [0,visWidth)
+ *
+ * @param g
+ * graphics object to draw on
+ * @param sx
+ * start x position
+ * @param xwidth
+ * width of gap
+ * @param visWidth
+ * visWidth maximum available width
+ * @param oldY
+ * top y value
+ * @param sy
+ * bottom y value
+ */
+ private void drawVerticals(Graphics2D g, int sx, int xwidth, int visWidth,
+ int oldY, int sy)
+ {
+ // if start position is visible, draw vertical line to left of
+ // group
+ if (sx >= 0 && sx < visWidth)
+ {
+ g.drawLine(sx, oldY, sx, sy);
+ }
+
+ // if end position is visible, draw vertical line to right of
+ // group
+ if (sx + xwidth < visWidth)
+ {
+ g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
+ }
+ }
+
+ /**
+ * Highlights search results in the visible region by rendering as white text
+ * on a black background. Any previous highlighting is removed. Answers true
+ * if any highlight was left on the visible alignment (so status bar should be
+ * set to match), else false. This method does _not_ set the 'fastPaint' flag,
+ * so allows the next repaint to update the whole display.
+ *
+ * @param results
+ * @return
+ */
+ public boolean highlightSearchResults(SearchResultsI results)
+ {
+ return highlightSearchResults(results, false);
+
+ }
+
+ /**
+ * Highlights search results in the visible region by rendering as white text
+ * on a black background. Any previous highlighting is removed. Answers true
+ * if any highlight was left on the visible alignment (so status bar should be
+ * set to match), else false.
+ * <p>
+ * Optionally, set the 'fastPaint' flag for a faster redraw if only the
+ * highlighted regions are modified. This speeds up highlighting across linked
+ * alignments.
+ * <p>
+ * Currently fastPaint is not implemented for scrolled wrapped alignments. If
+ * a wrapped alignment had to be scrolled to show the highlighted region, then
+ * it should be fully redrawn, otherwise a fast paint can be performed. This
+ * argument could be removed if fast paint of scrolled wrapped alignment is
+ * coded in future (JAL-2609).
+ *
+ * @param results
+ * @param doFastPaint
+ * if true, sets a flag so the next repaint only redraws the modified
+ * image
+ * @return
+ */
+ public boolean highlightSearchResults(SearchResultsI results,
+ boolean doFastPaint)
+ {
+ if (fastpainting)
+ {
+ return false;
+ }
+ boolean wrapped = av.getWrapAlignment();
+ try
+ {
+ fastPaint = doFastPaint;
+ fastpainting = fastPaint;
+
+ /*
+ * to avoid redrawing the whole visible region, we instead
+ * redraw just the minimal regions to remove previous highlights
+ * and add new ones
+ */
+ SearchResultsI previous = av.getSearchResults();
+ av.setSearchResults(results);
+ boolean redrawn = false;
+ boolean drawn = false;
+ if (wrapped)
+ {
+ redrawn = drawMappedPositionsWrapped(previous);
+ drawn = drawMappedPositionsWrapped(results);
+ redrawn |= drawn;
+ }
+ else
+ {
+ redrawn = drawMappedPositions(previous);
+ drawn = drawMappedPositions(results);
+ redrawn |= drawn;
+ }
+
+ /*
+ * if highlights were either removed or added, repaint
+ */
+ if (redrawn)
+ {
+ repaint();
+ }
+
+ /*
+ * return true only if highlights were added
+ */
+ return drawn;
+
+ } finally
+ {
+ fastpainting = false;