2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.datamodel.AlignmentI;
24 import jalview.datamodel.HiddenColumns;
25 import jalview.datamodel.SearchResultsI;
26 import jalview.datamodel.SequenceGroup;
27 import jalview.datamodel.SequenceI;
28 import jalview.renderer.ScaleRenderer;
29 import jalview.renderer.ScaleRenderer.ScaleMark;
30 import jalview.viewmodel.ViewportListenerI;
31 import jalview.viewmodel.ViewportRanges;
33 import java.awt.BasicStroke;
34 import java.awt.BorderLayout;
35 import java.awt.Color;
36 import java.awt.FontMetrics;
37 import java.awt.Graphics;
38 import java.awt.Graphics2D;
39 import java.awt.RenderingHints;
40 import java.awt.Shape;
41 import java.awt.image.BufferedImage;
42 import java.beans.PropertyChangeEvent;
43 import java.util.List;
45 import javax.swing.JComponent;
53 public class SeqCanvas extends JComponent implements ViewportListenerI
55 private static String ZEROS = "0000000000";
57 final FeatureRenderer fr;
59 final SequenceRenderer sr;
71 boolean fastPaint = false;
82 * Creates a new SeqCanvas object.
87 public SeqCanvas(AlignmentPanel ap)
91 fr = new FeatureRenderer(ap);
92 sr = new SequenceRenderer(av);
93 setLayout(new BorderLayout());
94 PaintRefresher.Register(this, av.getSequenceSetId());
95 setBackground(Color.white);
97 av.getRanges().addPropertyChangeListener(this);
100 public SequenceRenderer getSequenceRenderer()
105 public FeatureRenderer getFeatureRenderer()
110 int charHeight = 0, charWidth = 0;
112 private void updateViewport()
114 charHeight = av.getCharHeight();
115 charWidth = av.getCharWidth();
130 private void drawNorthScale(Graphics g, int startx, int endx, int ypos)
133 for (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx,
136 int mpos = mark.column; // (i - startx - 1)
141 String mstring = mark.text;
147 g.drawString(mstring, mpos * charWidth, ypos - (charHeight / 2));
149 g.drawLine((mpos * charWidth) + (charWidth / 2), (ypos + 2)
150 - (charHeight / 2), (mpos * charWidth) + (charWidth / 2),
168 void drawWestScale(Graphics g, int startx, int endx, int ypos)
170 FontMetrics fm = getFontMetrics(av.getFont());
173 if (av.hasHiddenColumns())
175 startx = av.getAlignment().getHiddenColumns()
176 .adjustForHiddenColumns(startx);
177 endx = av.getAlignment().getHiddenColumns()
178 .adjustForHiddenColumns(endx);
181 int maxwidth = av.getAlignment().getWidth();
182 if (av.hasHiddenColumns())
184 maxwidth = av.getAlignment().getHiddenColumns()
185 .findColumnPosition(maxwidth) - 1;
189 for (int i = 0; i < av.getAlignment().getHeight(); i++)
191 SequenceI seq = av.getAlignment().getSequenceAt(i);
197 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
204 value = av.getAlignment().getSequenceAt(i).findPosition(index);
211 int x = labelWidthWest - fm.stringWidth(String.valueOf(value))
213 g.drawString(value + "", x, (ypos + (i * charHeight))
231 void drawEastScale(Graphics g, int startx, int endx, int ypos)
235 if (av.hasHiddenColumns())
237 endx = av.getAlignment().getHiddenColumns()
238 .adjustForHiddenColumns(endx);
243 for (int i = 0; i < av.getAlignment().getHeight(); i++)
245 seq = av.getAlignment().getSequenceAt(i);
249 while (index > startx)
251 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
258 value = seq.findPosition(index);
265 g.drawString(String.valueOf(value), 0, (ypos + (i * charHeight))
271 boolean fastpainting = false;
274 * need to make this thread safe move alignment rendering in response to
280 * shift up or down in repaint
282 public void fastPaint(int horizontal, int vertical)
284 if (fastpainting || gg == null)
291 gg.copyArea(horizontal * charWidth, vertical * charHeight, imgWidth,
292 imgHeight, -horizontal * charWidth, -vertical * charHeight);
294 ViewportRanges ranges = av.getRanges();
295 int startRes = ranges.getStartRes();
296 int endRes = ranges.getEndRes();
297 int startSeq = ranges.getStartSeq();
298 int endSeq = ranges.getEndSeq();
302 if (horizontal > 0) // scrollbar pulled right, image to the left
304 transX = (endRes - startRes - horizontal) * charWidth;
305 startRes = endRes - horizontal;
307 else if (horizontal < 0)
309 endRes = startRes - horizontal;
311 else if (vertical > 0) // scroll down
313 startSeq = endSeq - vertical;
315 if (startSeq < ranges.getStartSeq())
316 { // ie scrolling too fast, more than a page at a time
317 startSeq = ranges.getStartSeq();
321 transY = imgHeight - ((vertical + 1) * charHeight);
324 else if (vertical < 0)
326 endSeq = startSeq - vertical;
328 if (endSeq > ranges.getEndSeq())
330 endSeq = ranges.getEndSeq();
334 gg.translate(transX, transY);
335 drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
336 gg.translate(-transX, -transY);
339 fastpainting = false;
343 public void paintComponent(Graphics g)
346 BufferedImage lcimg = img; // take reference since other threads may null
347 // img and call later.
348 super.paintComponent(g);
352 || (getVisibleRect().width != g.getClipBounds().width) || (getVisibleRect().height != g
353 .getClipBounds().height)))
355 g.drawImage(lcimg, 0, 0, this);
360 // this draws the whole of the alignment
361 imgWidth = getWidth();
362 imgHeight = getHeight();
364 imgWidth -= (imgWidth % charWidth);
365 imgHeight -= (imgHeight % charHeight);
367 if ((imgWidth < 1) || (imgHeight < 1))
372 if (lcimg == null || imgWidth != lcimg.getWidth()
373 || imgHeight != lcimg.getHeight())
377 lcimg = img = new BufferedImage(imgWidth, imgHeight,
378 BufferedImage.TYPE_INT_RGB);
379 gg = (Graphics2D) img.getGraphics();
380 gg.setFont(av.getFont());
381 } catch (OutOfMemoryError er)
384 System.err.println("SeqCanvas OutOfMemory Redraw Error.\n" + er);
385 new OOMWarning("Creating alignment image for display", er);
393 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
394 RenderingHints.VALUE_ANTIALIAS_ON);
397 gg.setColor(Color.white);
398 gg.fillRect(0, 0, imgWidth, imgHeight);
400 ViewportRanges ranges = av.getRanges();
401 if (av.getWrapAlignment())
403 drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
407 drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
408 ranges.getStartSeq(), ranges.getEndSeq(), 0);
411 g.drawImage(lcimg, 0, 0, this);
421 * @return DOCUMENT ME!
423 public int getWrappedCanvasWidth(int cwidth)
425 FontMetrics fm = getFontMetrics(av.getFont());
430 if (av.getScaleRightWrapped())
432 labelWidthEast = getLabelWidth(fm);
435 if (av.getScaleLeftWrapped())
437 labelWidthWest = getLabelWidth(fm);
440 return (cwidth - labelWidthEast - labelWidthWest) / charWidth;
444 * Returns a pixel width suitable for showing the largest sequence coordinate
445 * (end position) in the alignment. Returns 2 plus the number of decimal
446 * digits to be shown (3 for 1-10, 4 for 11-99 etc).
451 protected int getLabelWidth(FontMetrics fm)
454 for (int i = 0; i < av.getAlignment().getHeight(); i++)
456 maxWidth = Math.max(maxWidth, av.getAlignment().getSequenceAt(i)
461 for (int i = maxWidth; i > 0; i /= 10)
466 return fm.stringWidth(ZEROS.substring(0, length));
476 * @param canvasHeight
481 public void drawWrappedPanel(Graphics g, int canvasWidth,
482 int canvasHeight, int startRes)
485 AlignmentI al = av.getAlignment();
488 if (av.getScaleRightWrapped() || av.getScaleLeftWrapped())
490 FontMetrics fm = getFontMetrics(av.getFont());
491 labelWidth = getLabelWidth(fm);
494 labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0;
495 labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0;
497 int hgap = charHeight;
498 if (av.getScaleAboveWrapped())
503 int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) / charWidth;
504 int cHeight = av.getAlignment().getHeight() * charHeight;
506 av.setWrappedWidth(cWidth);
508 av.getRanges().setEndRes(av.getRanges().getStartRes() + cWidth - 1);
512 int maxwidth = av.getAlignment().getWidth() - 1;
514 if (av.hasHiddenColumns())
516 maxwidth = av.getAlignment().getHiddenColumns()
517 .findColumnPosition(maxwidth) - 1;
520 int annotationHeight = getAnnotationHeight();
522 while ((ypos <= canvasHeight) && (startRes < maxwidth))
524 endx = startRes + cWidth - 1;
531 g.setFont(av.getFont());
532 g.setColor(Color.black);
534 if (av.getScaleLeftWrapped())
536 drawWestScale(g, startRes, endx, ypos);
539 if (av.getScaleRightWrapped())
541 g.translate(canvasWidth - labelWidthEast, 0);
542 drawEastScale(g, startRes, endx, ypos);
543 g.translate(-(canvasWidth - labelWidthEast), 0);
546 g.translate(labelWidthWest, 0);
548 if (av.getScaleAboveWrapped())
550 drawNorthScale(g, startRes, endx, ypos);
553 if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
555 g.setColor(Color.blue);
557 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
558 List<Integer> positions = hidden.findHiddenRegionPositions();
559 for (int pos : positions)
561 res = pos - startRes;
563 if (res < 0 || res > endx - startRes)
569 new int[] { res * charWidth - charHeight / 4,
570 res * charWidth + charHeight / 4, res * charWidth },
571 new int[] { ypos - (charHeight / 2),
572 ypos - (charHeight / 2), ypos - (charHeight / 2) + 8 },
578 // When printing we have an extra clipped region,
579 // the Printable page which we need to account for here
580 Shape clip = g.getClip();
584 g.setClip(0, 0, cWidth * charWidth, canvasHeight);
588 g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
589 (int) clip.getBounds().getHeight());
592 drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
594 if (av.isShowAnnotation())
596 g.translate(0, cHeight + ypos + 3);
597 if (annotations == null)
599 annotations = new AnnotationPanel(av);
602 annotations.renderer.drawComponent(annotations, av, g, -1,
604 g.translate(0, -cHeight - ypos - 3);
607 g.translate(-labelWidthWest, 0);
609 ypos += cHeight + annotationHeight + hgap;
615 AnnotationPanel annotations;
617 int getAnnotationHeight()
619 if (!av.isShowAnnotation())
624 if (annotations == null)
626 annotations = new AnnotationPanel(av);
629 return annotations.adjustPanelHeight();
633 * Draws the visible region of the alignment on the graphics context. If there
634 * are hidden column markers in the visible region, then each sub-region
635 * between the markers is drawn separately, followed by the hidden column
640 * offset of the first column in the visible region (0..)
642 * offset of the last column in the visible region (0..)
644 * offset of the first sequence in the visible region (0..)
646 * offset of the last sequence in the visible region (0..)
648 * vertical offset at which to draw (for wrapped alignments)
650 public void drawPanel(Graphics g1, final int startRes, final int endRes,
651 final int startSeq, final int endSeq, final int yOffset)
654 if (!av.hasHiddenColumns())
656 draw(g1, startRes, endRes, startSeq, endSeq, yOffset);
661 final int screenYMax = endRes - startRes;
662 int blockStart = startRes;
663 int blockEnd = endRes;
665 for (int[] region : av.getAlignment().getHiddenColumns()
666 .getHiddenColumnsCopy())
668 int hideStart = region[0];
669 int hideEnd = region[1];
671 if (hideStart <= blockStart)
673 blockStart += (hideEnd - hideStart) + 1;
678 * draw up to just before the next hidden region, or the end of
679 * the visible region, whichever comes first
681 blockEnd = Math.min(hideStart - 1, blockStart + screenYMax
684 g1.translate(screenY * charWidth, 0);
686 draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset);
689 * draw the downline of the hidden column marker (ScalePanel draws the
690 * triangle on top) if we reached it
692 if (av.getShowHiddenMarkers() && blockEnd == hideStart - 1)
694 g1.setColor(Color.blue);
696 g1.drawLine((blockEnd - blockStart + 1) * charWidth - 1,
697 0 + yOffset, (blockEnd - blockStart + 1) * charWidth - 1,
698 (endSeq - startSeq + 1) * charHeight + yOffset);
701 g1.translate(-screenY * charWidth, 0);
702 screenY += blockEnd - blockStart + 1;
703 blockStart = hideEnd + 1;
705 if (screenY > screenYMax)
707 // already rendered last block
712 if (screenY <= screenYMax)
714 // remaining visible region to render
715 blockEnd = blockStart + screenYMax - screenY;
716 g1.translate(screenY * charWidth, 0);
717 draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset);
719 g1.translate(-screenY * charWidth, 0);
726 * Draws a region of the visible alignment
730 * offset of the first column in the visible region (0..)
732 * offset of the last column in the visible region (0..)
734 * offset of the first sequence in the visible region (0..)
736 * offset of the last sequence in the visible region (0..)
738 * vertical offset at which to draw (for wrapped alignments)
740 private void draw(Graphics g, int startRes, int endRes, int startSeq,
741 int endSeq, int offset)
743 g.setFont(av.getFont());
744 sr.prepare(g, av.isRenderGaps());
748 // / First draw the sequences
749 // ///////////////////////////
750 for (int i = startSeq; i <= endSeq; i++)
752 nextSeq = av.getAlignment().getSequenceAt(i);
755 // occasionally, a race condition occurs such that the alignment row is
759 sr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
760 startRes, endRes, offset + ((i - startSeq) * charHeight));
762 if (av.isShowSequenceFeatures())
764 fr.drawSequence(g, nextSeq, startRes, endRes, offset
765 + ((i - startSeq) * charHeight), false);
769 * highlight search Results once sequence has been drawn
771 if (av.hasSearchResults())
773 SearchResultsI searchResults = av.getSearchResults();
774 int[] visibleResults = searchResults.getResults(nextSeq,
776 if (visibleResults != null)
778 for (int r = 0; r < visibleResults.length; r += 2)
780 sr.drawHighlightedText(nextSeq, visibleResults[r],
781 visibleResults[r + 1], (visibleResults[r] - startRes)
783 + ((i - startSeq) * charHeight));
788 if (av.cursorMode && cursorY == i && cursorX >= startRes
789 && cursorX <= endRes)
791 sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
792 offset + ((i - startSeq) * charHeight));
796 if (av.getSelectionGroup() != null
797 || av.getAlignment().getGroups().size() > 0)
799 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
804 void drawGroupsBoundaries(Graphics g1, int startRes, int endRes,
805 int startSeq, int endSeq, int offset)
807 Graphics2D g = (Graphics2D) g1;
809 // ///////////////////////////////////
810 // Now outline any areas if necessary
811 // ///////////////////////////////////
812 SequenceGroup group = av.getSelectionGroup();
818 int visWidth = (endRes - startRes + 1) * charWidth;
820 if ((group == null) && (av.getAlignment().getGroups().size() > 0))
822 group = av.getAlignment().getGroups().get(0);
832 boolean inGroup = false;
836 for (i = startSeq; i <= endSeq; i++)
838 sx = (group.getStartRes() - startRes) * charWidth;
839 sy = offset + ((i - startSeq) * charHeight);
840 ex = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth) - 1;
842 if (sx + ex < 0 || sx > visWidth)
847 if ((sx <= (endRes - startRes) * charWidth)
848 && group.getSequences(null).contains(
849 av.getAlignment().getSequenceAt(i)))
852 && !group.getSequences(null).contains(
853 av.getAlignment().getSequenceAt(i + 1)))
855 bottom = sy + charHeight;
860 if (((top == -1) && (i == 0))
861 || !group.getSequences(null).contains(
862 av.getAlignment().getSequenceAt(i - 1)))
870 if (group == av.getSelectionGroup())
872 g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
873 BasicStroke.JOIN_ROUND, 3f, new float[] { 5f, 3f },
875 g.setColor(Color.RED);
879 g.setStroke(new BasicStroke());
880 g.setColor(group.getOutlineColour());
888 if (sx >= 0 && sx < visWidth)
890 g.drawLine(sx, oldY, sx, sy);
893 if (sx + ex < visWidth)
895 g.drawLine(sx + ex, oldY, sx + ex, sy);
904 if (sx + ex > visWidth)
909 else if (sx + ex >= (endRes - startRes + 1) * charWidth)
911 ex = (endRes - startRes + 1) * charWidth;
916 g.drawLine(sx, top, sx + ex, top);
922 g.drawLine(sx, bottom, sx + ex, bottom);
933 sy = offset + ((i - startSeq) * charHeight);
934 if (sx >= 0 && sx < visWidth)
936 g.drawLine(sx, oldY, sx, sy);
939 if (sx + ex < visWidth)
941 g.drawLine(sx + ex, oldY, sx + ex, sy);
950 if (sx + ex > visWidth)
954 else if (sx + ex >= (endRes - startRes + 1) * charWidth)
956 ex = (endRes - startRes + 1) * charWidth;
961 g.drawLine(sx, top, sx + ex, top);
967 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
976 g.setStroke(new BasicStroke());
978 if (groupIndex >= av.getAlignment().getGroups().size())
983 group = av.getAlignment().getGroups().get(groupIndex);
985 } while (groupIndex < av.getAlignment().getGroups().size());
992 * Highlights search results in the visible region by rendering as white text
993 * on a black background. Any previous highlighting is removed. Answers true
994 * if any highlight was left on the visible alignment (so status bar should be
995 * set to match), else false.
1000 public boolean highlightSearchResults(SearchResultsI results)
1005 * for now, don't attempt fastpaint if wrapped format
1007 boolean wrapped = av.getWrapAlignment();
1010 // drawWrappedMappedPositions(results);
1012 // av.setSearchResults(results);
1017 fastpainting = true;
1023 * to avoid redrawing the whole visible region, we instead
1024 * redraw just the minimal regions to remove previous highlights
1027 SearchResultsI previous = av.getSearchResults();
1028 av.setSearchResults(results);
1029 boolean redrawn = false;
1030 boolean drawn = false;
1033 redrawn = drawWrappedMappedPositions(previous);
1034 drawn = drawWrappedMappedPositions(results);
1039 redrawn = drawMappedPositions(previous);
1040 drawn = drawMappedPositions(results);
1045 * if highlights were either removed or added, repaint
1053 * return true only if highlights were added
1059 fastpainting = false;
1065 * Redraws the minimal rectangle in the visible region (if any) that includes
1066 * mapped positions of the given search results. Whether or not positions are
1067 * highlighted depends on the SearchResults set on the Viewport. This allows
1068 * this method to be called to either clear or set highlighting. Answers true
1069 * if any positions were drawn (in which case a repaint is still required),
1075 protected boolean drawMappedPositions(SearchResultsI results)
1077 if (results == null)
1083 * calculate the minimal rectangle to redraw that
1084 * includes both new and existing search results
1086 int firstSeq = Integer.MAX_VALUE;
1088 int firstCol = Integer.MAX_VALUE;
1090 boolean matchFound = false;
1092 ViewportRanges ranges = av.getRanges();
1093 int firstVisibleColumn = ranges.getStartRes();
1094 int lastVisibleColumn = ranges.getEndRes();
1095 AlignmentI alignment = av.getAlignment();
1096 if (av.hasHiddenColumns())
1098 firstVisibleColumn = alignment.getHiddenColumns()
1099 .adjustForHiddenColumns(firstVisibleColumn);
1100 lastVisibleColumn = alignment.getHiddenColumns()
1101 .adjustForHiddenColumns(lastVisibleColumn);
1104 for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
1105 .getEndSeq(); seqNo++)
1107 SequenceI seq = alignment.getSequenceAt(seqNo);
1109 int[] visibleResults = results.getResults(seq, firstVisibleColumn,
1111 if (visibleResults != null)
1113 for (int i = 0; i < visibleResults.length - 1; i += 2)
1115 int firstMatchedColumn = visibleResults[i];
1116 int lastMatchedColumn = visibleResults[i + 1];
1117 if (firstMatchedColumn <= lastVisibleColumn
1118 && lastMatchedColumn >= firstVisibleColumn)
1121 * found a search results match in the visible region -
1122 * remember the first and last sequence matched, and the first
1123 * and last visible columns in the matched positions
1126 firstSeq = Math.min(firstSeq, seqNo);
1127 lastSeq = Math.max(lastSeq, seqNo);
1128 firstMatchedColumn = Math.max(firstMatchedColumn,
1129 firstVisibleColumn);
1130 lastMatchedColumn = Math.min(lastMatchedColumn,
1132 firstCol = Math.min(firstCol, firstMatchedColumn);
1133 lastCol = Math.max(lastCol, lastMatchedColumn);
1141 if (av.hasHiddenColumns())
1143 firstCol = alignment.getHiddenColumns()
1144 .findColumnPosition(firstCol);
1145 lastCol = alignment.getHiddenColumns().findColumnPosition(lastCol);
1147 int transX = (firstCol - ranges.getStartRes()) * av.getCharWidth();
1148 int transY = (firstSeq - ranges.getStartSeq()) * av.getCharHeight();
1149 gg.translate(transX, transY);
1150 drawPanel(gg, firstCol, lastCol, firstSeq, lastSeq, 0);
1151 gg.translate(-transX, -transY);
1158 public void propertyChange(PropertyChangeEvent evt)
1160 if (!av.getWrapAlignment())
1162 if (evt.getPropertyName().equals("startres")
1163 || evt.getPropertyName().equals("endres"))
1165 // Make sure we're not trying to draw a panel
1166 // larger than the visible window
1167 ViewportRanges vpRanges = av.getRanges();
1168 int scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
1169 if (scrollX > vpRanges.getEndRes() - vpRanges.getStartRes())
1171 scrollX = vpRanges.getEndRes() - vpRanges.getStartRes();
1173 else if (scrollX < vpRanges.getStartRes() - vpRanges.getEndRes())
1175 scrollX = vpRanges.getStartRes() - vpRanges.getEndRes();
1177 fastPaint(scrollX, 0);
1179 else if (evt.getPropertyName().equals("startseq")
1180 || evt.getPropertyName().equals("endseq"))
1182 fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
1188 * Redraws any positions in the search results in the visible region. Any
1189 * highlights are drawn depending on the search results set on the Viewport,
1190 * not the results parameter. This allows this method to be called to either
1191 * clear highlighting (passing the previous search results), or set new
1197 protected boolean drawWrappedMappedPositions(SearchResultsI results)
1199 if (results == null)
1204 boolean matchFound = false;
1207 * Viewport ranges are set for the 'row' of the wrapped alignment
1208 * the cursor is in, not the whole visible region; really we want
1209 * the latter; +-6 a temporary fudge for codons wrapping across lines
1211 ViewportRanges ranges = av.getRanges();
1212 int firstVisibleColumn = ranges.getStartRes() - 6;
1213 int lastVisibleColumn = ranges.getEndRes() + 6;
1214 AlignmentI alignment = av.getAlignment();
1215 if (av.hasHiddenColumns())
1217 firstVisibleColumn = alignment.getHiddenColumns()
1218 .adjustForHiddenColumns(firstVisibleColumn);
1219 lastVisibleColumn = alignment.getHiddenColumns()
1220 .adjustForHiddenColumns(lastVisibleColumn);
1224 * find width of alignment in residues, and height of alignment,
1225 * so we can calculate where to render each matched position
1227 int wrappedWidth = av.getWrappedWidth();
1228 int wrappedHeight = av.getAlignment().getHeight() * av.getCharHeight();
1229 int gapHeight = av.getCharHeight()
1230 * (av.getScaleAboveWrapped() ? 2 : 1);
1231 wrappedHeight += gapHeight;
1232 wrappedHeight += getAnnotationHeight();
1234 for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
1235 .getEndSeq(); seqNo++)
1237 SequenceI seq = alignment.getSequenceAt(seqNo);
1239 int[] visibleResults = results.getResults(seq, firstVisibleColumn,
1241 if (visibleResults != null)
1243 for (int i = 0; i < visibleResults.length - 1; i += 2)
1245 int firstMatchedColumn = visibleResults[i];
1246 int lastMatchedColumn = visibleResults[i + 1];
1247 if (firstMatchedColumn <= lastVisibleColumn
1248 && lastMatchedColumn >= firstVisibleColumn)
1251 * found a search results match in the visible region
1253 firstMatchedColumn = Math.max(firstMatchedColumn,
1254 firstVisibleColumn);
1255 lastMatchedColumn = Math.min(lastMatchedColumn,
1259 * draw each mapped position separately (as contiguous positions may
1260 * wrap across lines)
1262 for (int mappedPos = firstMatchedColumn; mappedPos <= lastMatchedColumn; mappedPos++)
1264 int displayColumn = mappedPos;
1265 if (av.hasHiddenColumns())
1267 displayColumn = alignment.getHiddenColumns()
1268 .findColumnPosition(displayColumn);
1272 * transX: offset from left edge of canvas to residue position
1274 int transX = labelWidthWest
1275 + ((displayColumn - ranges.getStartRes()) % wrappedWidth)
1276 * av.getCharWidth();
1279 * transY: offset from top edge of canvas to residue position
1281 int transY = gapHeight;
1282 transY += (displayColumn / wrappedWidth) * wrappedHeight;
1283 transY += (seqNo - ranges.getStartSeq()) * av.getCharHeight();
1286 * yOffset is from graphics origin to start of visible region
1288 int yOffset = 0;// (displayColumn / wrappedWidth) * wrappedHeight;
1289 if (transY < getHeight())
1292 gg.translate(transX, transY);
1293 drawPanel(gg, displayColumn, displayColumn, seqNo, seqNo,
1295 gg.translate(-transX, -transY);