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 for (int i = 0; i < hidden.getHiddenRegions().size(); i++)
560 res = hidden.findHiddenRegionPosition(i) - startRes;
562 if (res < 0 || res > endx - startRes)
568 new int[] { res * charWidth - charHeight / 4,
569 res * charWidth + charHeight / 4, res * charWidth },
570 new int[] { ypos - (charHeight / 2),
571 ypos - (charHeight / 2), ypos - (charHeight / 2) + 8 },
577 // When printing we have an extra clipped region,
578 // the Printable page which we need to account for here
579 Shape clip = g.getClip();
583 g.setClip(0, 0, cWidth * charWidth, canvasHeight);
587 g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
588 (int) clip.getBounds().getHeight());
591 drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
593 if (av.isShowAnnotation())
595 g.translate(0, cHeight + ypos + 3);
596 if (annotations == null)
598 annotations = new AnnotationPanel(av);
601 annotations.renderer.drawComponent(annotations, av, g, -1,
603 g.translate(0, -cHeight - ypos - 3);
606 g.translate(-labelWidthWest, 0);
608 ypos += cHeight + annotationHeight + hgap;
614 AnnotationPanel annotations;
616 int getAnnotationHeight()
618 if (!av.isShowAnnotation())
623 if (annotations == null)
625 annotations = new AnnotationPanel(av);
628 return annotations.adjustPanelHeight();
632 * Draws the visible region of the alignment on the graphics context. If there
633 * are hidden column markers in the visible region, then each sub-region
634 * between the markers is drawn separately, followed by the hidden column
639 * offset of the first column in the visible region (0..)
641 * offset of the last column in the visible region (0..)
643 * offset of the first sequence in the visible region (0..)
645 * offset of the last sequence in the visible region (0..)
647 * vertical offset at which to draw (for wrapped alignments)
649 public void drawPanel(Graphics g1, final int startRes, final int endRes,
650 final int startSeq, final int endSeq, final int yOffset)
653 if (!av.hasHiddenColumns())
655 draw(g1, startRes, endRes, startSeq, endSeq, yOffset);
659 List<int[]> regions = av.getAlignment().getHiddenColumns()
663 final int screenYMax = endRes - startRes;
664 int blockStart = startRes;
665 int blockEnd = endRes;
667 for (int[] region : regions)
669 int hideStart = region[0];
670 int hideEnd = region[1];
672 if (hideStart <= blockStart)
674 blockStart += (hideEnd - hideStart) + 1;
679 * draw up to just before the next hidden region, or the end of
680 * the visible region, whichever comes first
682 blockEnd = Math.min(hideStart - 1, blockStart + screenYMax
685 g1.translate(screenY * charWidth, 0);
687 draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset);
690 * draw the downline of the hidden column marker (ScalePanel draws the
691 * triangle on top) if we reached it
693 if (av.getShowHiddenMarkers() && blockEnd == hideStart - 1)
695 g1.setColor(Color.blue);
697 g1.drawLine((blockEnd - blockStart + 1) * charWidth - 1,
698 0 + yOffset, (blockEnd - blockStart + 1) * charWidth - 1,
699 (endSeq - startSeq + 1) * charHeight + yOffset);
702 g1.translate(-screenY * charWidth, 0);
703 screenY += blockEnd - blockStart + 1;
704 blockStart = hideEnd + 1;
706 if (screenY > screenYMax)
708 // already rendered last block
713 if (screenY <= screenYMax)
715 // remaining visible region to render
716 blockEnd = blockStart + screenYMax - screenY;
717 g1.translate(screenY * charWidth, 0);
718 draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset);
720 g1.translate(-screenY * charWidth, 0);
727 * Draws a region of the visible alignment
731 * offset of the first column in the visible region (0..)
733 * offset of the last column in the visible region (0..)
735 * offset of the first sequence in the visible region (0..)
737 * offset of the last sequence in the visible region (0..)
739 * vertical offset at which to draw (for wrapped alignments)
741 private void draw(Graphics g, int startRes, int endRes, int startSeq,
742 int endSeq, int offset)
744 g.setFont(av.getFont());
745 sr.prepare(g, av.isRenderGaps());
749 // / First draw the sequences
750 // ///////////////////////////
751 for (int i = startSeq; i <= endSeq; i++)
753 nextSeq = av.getAlignment().getSequenceAt(i);
756 // occasionally, a race condition occurs such that the alignment row is
760 sr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
761 startRes, endRes, offset + ((i - startSeq) * charHeight));
763 if (av.isShowSequenceFeatures())
765 fr.drawSequence(g, nextSeq, startRes, endRes, offset
766 + ((i - startSeq) * charHeight), false);
770 * highlight search Results once sequence has been drawn
772 if (av.hasSearchResults())
774 SearchResultsI searchResults = av.getSearchResults();
775 int[] visibleResults = searchResults.getResults(nextSeq,
777 if (visibleResults != null)
779 for (int r = 0; r < visibleResults.length; r += 2)
781 sr.drawHighlightedText(nextSeq, visibleResults[r],
782 visibleResults[r + 1], (visibleResults[r] - startRes)
784 + ((i - startSeq) * charHeight));
789 if (av.cursorMode && cursorY == i && cursorX >= startRes
790 && cursorX <= endRes)
792 sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
793 offset + ((i - startSeq) * charHeight));
797 if (av.getSelectionGroup() != null
798 || av.getAlignment().getGroups().size() > 0)
800 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
805 void drawGroupsBoundaries(Graphics g1, int startRes, int endRes,
806 int startSeq, int endSeq, int offset)
808 Graphics2D g = (Graphics2D) g1;
810 // ///////////////////////////////////
811 // Now outline any areas if necessary
812 // ///////////////////////////////////
813 SequenceGroup group = av.getSelectionGroup();
819 int visWidth = (endRes - startRes + 1) * charWidth;
821 if ((group == null) && (av.getAlignment().getGroups().size() > 0))
823 group = av.getAlignment().getGroups().get(0);
833 boolean inGroup = false;
837 for (i = startSeq; i <= endSeq; i++)
839 sx = (group.getStartRes() - startRes) * charWidth;
840 sy = offset + ((i - startSeq) * charHeight);
841 ex = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth) - 1;
843 if (sx + ex < 0 || sx > visWidth)
848 if ((sx <= (endRes - startRes) * charWidth)
849 && group.getSequences(null).contains(
850 av.getAlignment().getSequenceAt(i)))
853 && !group.getSequences(null).contains(
854 av.getAlignment().getSequenceAt(i + 1)))
856 bottom = sy + charHeight;
861 if (((top == -1) && (i == 0))
862 || !group.getSequences(null).contains(
863 av.getAlignment().getSequenceAt(i - 1)))
871 if (group == av.getSelectionGroup())
873 g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
874 BasicStroke.JOIN_ROUND, 3f, new float[] { 5f, 3f },
876 g.setColor(Color.RED);
880 g.setStroke(new BasicStroke());
881 g.setColor(group.getOutlineColour());
889 if (sx >= 0 && sx < visWidth)
891 g.drawLine(sx, oldY, sx, sy);
894 if (sx + ex < visWidth)
896 g.drawLine(sx + ex, oldY, sx + ex, sy);
905 if (sx + ex > visWidth)
910 else if (sx + ex >= (endRes - startRes + 1) * charWidth)
912 ex = (endRes - startRes + 1) * charWidth;
917 g.drawLine(sx, top, sx + ex, top);
923 g.drawLine(sx, bottom, sx + ex, bottom);
934 sy = offset + ((i - startSeq) * charHeight);
935 if (sx >= 0 && sx < visWidth)
937 g.drawLine(sx, oldY, sx, sy);
940 if (sx + ex < visWidth)
942 g.drawLine(sx + ex, oldY, sx + ex, sy);
951 if (sx + ex > visWidth)
955 else if (sx + ex >= (endRes - startRes + 1) * charWidth)
957 ex = (endRes - startRes + 1) * charWidth;
962 g.drawLine(sx, top, sx + ex, top);
968 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
977 g.setStroke(new BasicStroke());
979 if (groupIndex >= av.getAlignment().getGroups().size())
984 group = av.getAlignment().getGroups().get(groupIndex);
986 } while (groupIndex < av.getAlignment().getGroups().size());
993 * Highlights search results in the visible region by rendering as white text
994 * on a black background. Any previous highlighting is removed. Answers true
995 * if any highlight was left on the visible alignment (so status bar should be
996 * set to match), else false.
1001 public boolean highlightSearchResults(SearchResultsI results)
1006 * for now, don't attempt fastpaint if wrapped format
1008 boolean wrapped = av.getWrapAlignment();
1011 // drawWrappedMappedPositions(results);
1013 // av.setSearchResults(results);
1018 fastpainting = true;
1024 * to avoid redrawing the whole visible region, we instead
1025 * redraw just the minimal regions to remove previous highlights
1028 SearchResultsI previous = av.getSearchResults();
1029 av.setSearchResults(results);
1030 boolean redrawn = false;
1031 boolean drawn = false;
1034 redrawn = drawWrappedMappedPositions(previous);
1035 drawn = drawWrappedMappedPositions(results);
1040 redrawn = drawMappedPositions(previous);
1041 drawn = drawMappedPositions(results);
1046 * if highlights were either removed or added, repaint
1054 * return true only if highlights were added
1060 fastpainting = false;
1066 * Redraws the minimal rectangle in the visible region (if any) that includes
1067 * mapped positions of the given search results. Whether or not positions are
1068 * highlighted depends on the SearchResults set on the Viewport. This allows
1069 * this method to be called to either clear or set highlighting. Answers true
1070 * if any positions were drawn (in which case a repaint is still required),
1076 protected boolean drawMappedPositions(SearchResultsI results)
1078 if (results == null)
1084 * calculate the minimal rectangle to redraw that
1085 * includes both new and existing search results
1087 int firstSeq = Integer.MAX_VALUE;
1089 int firstCol = Integer.MAX_VALUE;
1091 boolean matchFound = false;
1093 ViewportRanges ranges = av.getRanges();
1094 int firstVisibleColumn = ranges.getStartRes();
1095 int lastVisibleColumn = ranges.getEndRes();
1096 AlignmentI alignment = av.getAlignment();
1097 if (av.hasHiddenColumns())
1099 firstVisibleColumn = alignment.getHiddenColumns()
1100 .adjustForHiddenColumns(firstVisibleColumn);
1101 lastVisibleColumn = alignment.getHiddenColumns()
1102 .adjustForHiddenColumns(lastVisibleColumn);
1105 for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
1106 .getEndSeq(); seqNo++)
1108 SequenceI seq = alignment.getSequenceAt(seqNo);
1110 int[] visibleResults = results.getResults(seq, firstVisibleColumn,
1112 if (visibleResults != null)
1114 for (int i = 0; i < visibleResults.length - 1; i += 2)
1116 int firstMatchedColumn = visibleResults[i];
1117 int lastMatchedColumn = visibleResults[i + 1];
1118 if (firstMatchedColumn <= lastVisibleColumn
1119 && lastMatchedColumn >= firstVisibleColumn)
1122 * found a search results match in the visible region -
1123 * remember the first and last sequence matched, and the first
1124 * and last visible columns in the matched positions
1127 firstSeq = Math.min(firstSeq, seqNo);
1128 lastSeq = Math.max(lastSeq, seqNo);
1129 firstMatchedColumn = Math.max(firstMatchedColumn,
1130 firstVisibleColumn);
1131 lastMatchedColumn = Math.min(lastMatchedColumn,
1133 firstCol = Math.min(firstCol, firstMatchedColumn);
1134 lastCol = Math.max(lastCol, lastMatchedColumn);
1142 if (av.hasHiddenColumns())
1144 firstCol = alignment.getHiddenColumns()
1145 .findColumnPosition(firstCol);
1146 lastCol = alignment.getHiddenColumns().findColumnPosition(lastCol);
1148 int transX = (firstCol - ranges.getStartRes()) * av.getCharWidth();
1149 int transY = (firstSeq - ranges.getStartSeq()) * av.getCharHeight();
1150 gg.translate(transX, transY);
1151 drawPanel(gg, firstCol, lastCol, firstSeq, lastSeq, 0);
1152 gg.translate(-transX, -transY);
1159 public void propertyChange(PropertyChangeEvent evt)
1161 if (!av.getWrapAlignment())
1163 if (evt.getPropertyName().equals("startres")
1164 || evt.getPropertyName().equals("endres"))
1166 // Make sure we're not trying to draw a panel
1167 // larger than the visible window
1168 ViewportRanges vpRanges = av.getRanges();
1169 int scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
1170 if (scrollX > vpRanges.getEndRes() - vpRanges.getStartRes())
1172 scrollX = vpRanges.getEndRes() - vpRanges.getStartRes();
1174 else if (scrollX < vpRanges.getStartRes() - vpRanges.getEndRes())
1176 scrollX = vpRanges.getStartRes() - vpRanges.getEndRes();
1178 fastPaint(scrollX, 0);
1180 else if (evt.getPropertyName().equals("startseq")
1181 || evt.getPropertyName().equals("endseq"))
1183 fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
1189 * Redraws any positions in the search results in the visible region. Any
1190 * highlights are drawn depending on the search results set on the Viewport,
1191 * not the results parameter. This allows this method to be called to either
1192 * clear highlighting (passing the previous search results), or set new
1198 protected boolean drawWrappedMappedPositions(SearchResultsI results)
1200 if (results == null)
1205 boolean matchFound = false;
1208 * Viewport ranges are set for the 'row' of the wrapped alignment
1209 * the cursor is in, not the whole visible region; really we want
1210 * the latter; +-6 a temporary fudge for codons wrapping across lines
1212 ViewportRanges ranges = av.getRanges();
1213 int firstVisibleColumn = ranges.getStartRes() - 6;
1214 int lastVisibleColumn = ranges.getEndRes() + 6;
1215 AlignmentI alignment = av.getAlignment();
1216 if (av.hasHiddenColumns())
1218 firstVisibleColumn = alignment.getHiddenColumns()
1219 .adjustForHiddenColumns(firstVisibleColumn);
1220 lastVisibleColumn = alignment.getHiddenColumns()
1221 .adjustForHiddenColumns(lastVisibleColumn);
1225 * find width of alignment in residues, and height of alignment,
1226 * so we can calculate where to render each matched position
1228 int wrappedWidth = av.getWrappedWidth();
1229 int wrappedHeight = av.getAlignment().getHeight() * av.getCharHeight();
1230 int gapHeight = av.getCharHeight()
1231 * (av.getScaleAboveWrapped() ? 2 : 1);
1232 wrappedHeight += gapHeight;
1233 wrappedHeight += getAnnotationHeight();
1235 for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
1236 .getEndSeq(); seqNo++)
1238 SequenceI seq = alignment.getSequenceAt(seqNo);
1240 int[] visibleResults = results.getResults(seq, firstVisibleColumn,
1242 if (visibleResults != null)
1244 for (int i = 0; i < visibleResults.length - 1; i += 2)
1246 int firstMatchedColumn = visibleResults[i];
1247 int lastMatchedColumn = visibleResults[i + 1];
1248 if (firstMatchedColumn <= lastVisibleColumn
1249 && lastMatchedColumn >= firstVisibleColumn)
1252 * found a search results match in the visible region
1254 firstMatchedColumn = Math.max(firstMatchedColumn,
1255 firstVisibleColumn);
1256 lastMatchedColumn = Math.min(lastMatchedColumn,
1260 * draw each mapped position separately (as contiguous positions may
1261 * wrap across lines)
1263 for (int mappedPos = firstMatchedColumn; mappedPos <= lastMatchedColumn; mappedPos++)
1265 int displayColumn = mappedPos;
1266 if (av.hasHiddenColumns())
1268 displayColumn = alignment.getHiddenColumns()
1269 .findColumnPosition(displayColumn);
1273 * transX: offset from left edge of canvas to residue position
1275 int transX = labelWidthWest
1276 + ((displayColumn - ranges.getStartRes()) % wrappedWidth)
1277 * av.getCharWidth();
1280 * transY: offset from top edge of canvas to residue position
1282 int transY = gapHeight;
1283 transY += (displayColumn / wrappedWidth) * wrappedHeight;
1284 transY += (seqNo - ranges.getStartSeq()) * av.getCharHeight();
1287 * yOffset is from graphics origin to start of visible region
1289 int yOffset = 0;// (displayColumn / wrappedWidth) * wrappedHeight;
1290 if (transY < getHeight())
1293 gg.translate(transX, transY);
1294 drawPanel(gg, displayColumn, displayColumn, seqNo, seqNo,
1296 gg.translate(-transX, -transY);