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)
292 ViewportRanges ranges = av.getRanges();
293 int startRes = ranges.getStartRes();
294 int endRes = ranges.getEndRes();
295 int startSeq = ranges.getStartSeq();
296 int endSeq = ranges.getEndSeq();
300 gg.copyArea(horizontal * charWidth, vertical * charHeight, imgWidth,
301 imgHeight, -horizontal * charWidth, -vertical * charHeight);
303 if (horizontal > 0) // scrollbar pulled right, image to the left
305 transX = (endRes - startRes - horizontal) * charWidth;
306 startRes = endRes - horizontal;
308 else if (horizontal < 0)
310 endRes = startRes - horizontal;
312 else if (vertical > 0) // scroll down
314 startSeq = endSeq - vertical;
316 if (startSeq < ranges.getStartSeq())
317 { // ie scrolling too fast, more than a page at a time
318 startSeq = ranges.getStartSeq();
322 transY = imgHeight - ((vertical + 1) * charHeight);
325 else if (vertical < 0)
327 endSeq = startSeq - vertical;
329 if (endSeq > ranges.getEndSeq())
331 endSeq = ranges.getEndSeq();
335 gg.translate(transX, transY);
336 drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
337 gg.translate(-transX, -transY);
340 fastpainting = false;
344 public void paintComponent(Graphics g)
347 BufferedImage lcimg = img; // take reference since other threads may null
348 // img and call later.
349 super.paintComponent(g);
353 || (getVisibleRect().width != g.getClipBounds().width) || (getVisibleRect().height != g
354 .getClipBounds().height)))
356 g.drawImage(lcimg, 0, 0, this);
361 // this draws the whole of the alignment
362 imgWidth = getWidth();
363 imgHeight = getHeight();
365 imgWidth -= (imgWidth % charWidth);
366 imgHeight -= (imgHeight % charHeight);
368 if ((imgWidth < 1) || (imgHeight < 1))
373 if (lcimg == null || imgWidth != lcimg.getWidth()
374 || imgHeight != lcimg.getHeight())
378 lcimg = img = new BufferedImage(imgWidth, imgHeight,
379 BufferedImage.TYPE_INT_RGB);
380 gg = (Graphics2D) img.getGraphics();
381 gg.setFont(av.getFont());
382 } catch (OutOfMemoryError er)
385 System.err.println("SeqCanvas OutOfMemory Redraw Error.\n" + er);
386 new OOMWarning("Creating alignment image for display", er);
394 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
395 RenderingHints.VALUE_ANTIALIAS_ON);
398 gg.setColor(Color.white);
399 gg.fillRect(0, 0, imgWidth, imgHeight);
401 ViewportRanges ranges = av.getRanges();
402 if (av.getWrapAlignment())
404 drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
408 drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
409 ranges.getStartSeq(), ranges.getEndSeq(), 0);
412 g.drawImage(lcimg, 0, 0, this);
417 * Returns the visible width of the canvas in residues, after allowing for
418 * East or West scales (if shown)
421 * the width in pixels (possibly including scales)
425 public int getWrappedCanvasWidth(int canvasWidth)
427 FontMetrics fm = getFontMetrics(av.getFont());
432 if (av.getScaleRightWrapped())
434 labelWidthEast = getLabelWidth(fm);
437 if (av.getScaleLeftWrapped())
439 labelWidthWest = labelWidthEast > 0 ? labelWidthEast
443 return (canvasWidth - labelWidthEast - labelWidthWest) / charWidth;
447 * Returns a pixel width suitable for showing the largest sequence coordinate
448 * (end position) in the alignment. Returns 2 plus the number of decimal
449 * digits to be shown (3 for 1-10, 4 for 11-99 etc).
454 protected int getLabelWidth(FontMetrics fm)
457 * find the biggest sequence end position we need to show
458 * (note this is not necessarily the sequence length)
461 AlignmentI alignment = av.getAlignment();
462 for (int i = 0; i < alignment.getHeight(); i++)
464 maxWidth = Math.max(maxWidth, alignment.getSequenceAt(i).getEnd());
468 for (int i = maxWidth; i > 0; i /= 10)
473 return fm.stringWidth(ZEROS.substring(0, length));
483 * @param canvasHeight
488 public void drawWrappedPanel(Graphics g, int canvasWidth,
489 int canvasHeight, int startRes)
492 AlignmentI al = av.getAlignment();
495 if (av.getScaleRightWrapped() || av.getScaleLeftWrapped())
497 FontMetrics fm = getFontMetrics(av.getFont());
498 labelWidth = getLabelWidth(fm);
501 labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0;
502 labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0;
504 int hgap = charHeight;
505 if (av.getScaleAboveWrapped())
510 int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) / charWidth;
511 int cHeight = av.getAlignment().getHeight() * charHeight;
513 av.setWrappedWidth(cWidth);
515 av.getRanges().setViewportStartAndWidth(startRes, cWidth);
519 int maxwidth = av.getAlignment().getWidth();
521 if (av.hasHiddenColumns())
523 maxwidth = av.getAlignment().getHiddenColumns()
524 .findColumnPosition(maxwidth);
527 int annotationHeight = getAnnotationHeight();
529 while ((ypos <= canvasHeight) && (startRes < maxwidth))
531 endx = startRes + cWidth - 1;
538 g.setFont(av.getFont());
539 g.setColor(Color.black);
541 if (av.getScaleLeftWrapped())
543 drawWestScale(g, startRes, endx, ypos);
546 if (av.getScaleRightWrapped())
548 g.translate(canvasWidth - labelWidthEast, 0);
549 drawEastScale(g, startRes, endx, ypos);
550 g.translate(-(canvasWidth - labelWidthEast), 0);
553 g.translate(labelWidthWest, 0);
555 if (av.getScaleAboveWrapped())
557 drawNorthScale(g, startRes, endx, ypos);
560 if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
562 g.setColor(Color.blue);
564 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
565 List<Integer> positions = hidden.findHiddenRegionPositions();
566 for (int pos : positions)
568 res = pos - startRes;
570 if (res < 0 || res > endx - startRes)
576 new int[] { res * charWidth - charHeight / 4,
577 res * charWidth + charHeight / 4, res * charWidth },
578 new int[] { ypos - (charHeight / 2),
579 ypos - (charHeight / 2), ypos - (charHeight / 2) + 8 },
585 // When printing we have an extra clipped region,
586 // the Printable page which we need to account for here
587 Shape clip = g.getClip();
591 g.setClip(0, 0, cWidth * charWidth, canvasHeight);
595 g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
596 (int) clip.getBounds().getHeight());
599 drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
601 if (av.isShowAnnotation())
603 g.translate(0, cHeight + ypos + 3);
604 if (annotations == null)
606 annotations = new AnnotationPanel(av);
609 annotations.renderer.drawComponent(annotations, av, g, -1,
611 g.translate(0, -cHeight - ypos - 3);
614 g.translate(-labelWidthWest, 0);
616 ypos += cHeight + annotationHeight + hgap;
622 AnnotationPanel annotations;
624 int getAnnotationHeight()
626 if (!av.isShowAnnotation())
631 if (annotations == null)
633 annotations = new AnnotationPanel(av);
636 return annotations.adjustPanelHeight();
640 * Draws the visible region of the alignment on the graphics context. If there
641 * are hidden column markers in the visible region, then each sub-region
642 * between the markers is drawn separately, followed by the hidden column
647 * offset of the first column in the visible region (0..)
649 * offset of the last column in the visible region (0..)
651 * offset of the first sequence in the visible region (0..)
653 * offset of the last sequence in the visible region (0..)
655 * vertical offset at which to draw (for wrapped alignments)
657 public void drawPanel(Graphics g1, final int startRes, final int endRes,
658 final int startSeq, final int endSeq, final int yOffset)
661 if (!av.hasHiddenColumns())
663 draw(g1, startRes, endRes, startSeq, endSeq, yOffset);
668 final int screenYMax = endRes - startRes;
669 int blockStart = startRes;
670 int blockEnd = endRes;
672 for (int[] region : av.getAlignment().getHiddenColumns()
673 .getHiddenColumnsCopy())
675 int hideStart = region[0];
676 int hideEnd = region[1];
678 if (hideStart <= blockStart)
680 blockStart += (hideEnd - hideStart) + 1;
685 * draw up to just before the next hidden region, or the end of
686 * the visible region, whichever comes first
688 blockEnd = Math.min(hideStart - 1, blockStart + screenYMax
691 g1.translate(screenY * charWidth, 0);
693 draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset);
696 * draw the downline of the hidden column marker (ScalePanel draws the
697 * triangle on top) if we reached it
699 if (av.getShowHiddenMarkers() && blockEnd == hideStart - 1)
701 g1.setColor(Color.blue);
703 g1.drawLine((blockEnd - blockStart + 1) * charWidth - 1,
704 0 + yOffset, (blockEnd - blockStart + 1) * charWidth - 1,
705 (endSeq - startSeq + 1) * charHeight + yOffset);
708 g1.translate(-screenY * charWidth, 0);
709 screenY += blockEnd - blockStart + 1;
710 blockStart = hideEnd + 1;
712 if (screenY > screenYMax)
714 // already rendered last block
719 if (screenY <= screenYMax)
721 // remaining visible region to render
722 blockEnd = blockStart + screenYMax - screenY;
723 g1.translate(screenY * charWidth, 0);
724 draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset);
726 g1.translate(-screenY * charWidth, 0);
733 * Draws a region of the visible alignment
737 * offset of the first column in the visible region (0..)
739 * offset of the last column in the visible region (0..)
741 * offset of the first sequence in the visible region (0..)
743 * offset of the last sequence in the visible region (0..)
745 * vertical offset at which to draw (for wrapped alignments)
747 private void draw(Graphics g, int startRes, int endRes, int startSeq,
748 int endSeq, int offset)
750 g.setFont(av.getFont());
751 sr.prepare(g, av.isRenderGaps());
755 // / First draw the sequences
756 // ///////////////////////////
757 for (int i = startSeq; i <= endSeq; i++)
759 nextSeq = av.getAlignment().getSequenceAt(i);
762 // occasionally, a race condition occurs such that the alignment row is
766 sr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
767 startRes, endRes, offset + ((i - startSeq) * charHeight));
769 if (av.isShowSequenceFeatures())
771 fr.drawSequence(g, nextSeq, startRes, endRes, offset
772 + ((i - startSeq) * charHeight), false);
776 * highlight search Results once sequence has been drawn
778 if (av.hasSearchResults())
780 SearchResultsI searchResults = av.getSearchResults();
781 int[] visibleResults = searchResults.getResults(nextSeq,
783 if (visibleResults != null)
785 for (int r = 0; r < visibleResults.length; r += 2)
787 sr.drawHighlightedText(nextSeq, visibleResults[r],
788 visibleResults[r + 1], (visibleResults[r] - startRes)
790 + ((i - startSeq) * charHeight));
795 if (av.cursorMode && cursorY == i && cursorX >= startRes
796 && cursorX <= endRes)
798 sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
799 offset + ((i - startSeq) * charHeight));
803 if (av.getSelectionGroup() != null
804 || av.getAlignment().getGroups().size() > 0)
806 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
811 void drawGroupsBoundaries(Graphics g1, int startRes, int endRes,
812 int startSeq, int endSeq, int offset)
814 Graphics2D g = (Graphics2D) g1;
816 // ///////////////////////////////////
817 // Now outline any areas if necessary
818 // ///////////////////////////////////
819 SequenceGroup group = av.getSelectionGroup();
825 int visWidth = (endRes - startRes + 1) * charWidth;
827 if ((group == null) && (av.getAlignment().getGroups().size() > 0))
829 group = av.getAlignment().getGroups().get(0);
839 boolean inGroup = false;
843 for (i = startSeq; i <= endSeq; i++)
845 sx = (group.getStartRes() - startRes) * charWidth;
846 sy = offset + ((i - startSeq) * charHeight);
847 ex = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth) - 1;
849 if (sx + ex < 0 || sx > visWidth)
854 if ((sx <= (endRes - startRes) * charWidth)
855 && group.getSequences(null).contains(
856 av.getAlignment().getSequenceAt(i)))
859 && !group.getSequences(null).contains(
860 av.getAlignment().getSequenceAt(i + 1)))
862 bottom = sy + charHeight;
867 if (((top == -1) && (i == 0))
868 || !group.getSequences(null).contains(
869 av.getAlignment().getSequenceAt(i - 1)))
877 if (group == av.getSelectionGroup())
879 g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
880 BasicStroke.JOIN_ROUND, 3f, new float[] { 5f, 3f },
882 g.setColor(Color.RED);
886 g.setStroke(new BasicStroke());
887 g.setColor(group.getOutlineColour());
895 if (sx >= 0 && sx < visWidth)
897 g.drawLine(sx, oldY, sx, sy);
900 if (sx + ex < visWidth)
902 g.drawLine(sx + ex, oldY, sx + ex, sy);
911 if (sx + ex > visWidth)
916 else if (sx + ex >= (endRes - startRes + 1) * charWidth)
918 ex = (endRes - startRes + 1) * charWidth;
923 g.drawLine(sx, top, sx + ex, top);
929 g.drawLine(sx, bottom, sx + ex, bottom);
940 sy = offset + ((i - startSeq) * charHeight);
941 if (sx >= 0 && sx < visWidth)
943 g.drawLine(sx, oldY, sx, sy);
946 if (sx + ex < visWidth)
948 g.drawLine(sx + ex, oldY, sx + ex, sy);
957 if (sx + ex > visWidth)
961 else if (sx + ex >= (endRes - startRes + 1) * charWidth)
963 ex = (endRes - startRes + 1) * charWidth;
968 g.drawLine(sx, top, sx + ex, top);
974 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
983 g.setStroke(new BasicStroke());
985 if (groupIndex >= av.getAlignment().getGroups().size())
990 group = av.getAlignment().getGroups().get(groupIndex);
992 } while (groupIndex < av.getAlignment().getGroups().size());
999 * Highlights search results in the visible region by rendering as white text
1000 * on a black background. Any previous highlighting is removed. Answers true
1001 * if any highlight was left on the visible alignment (so status bar should be
1002 * set to match), else false.
1004 * Currently fastPaint is not implemented for wrapped alignments. If a wrapped
1005 * alignment had to be scrolled to show the highlighted region, then it should
1006 * be fully redrawn, otherwise a fast paint can be performed. This argument
1007 * could be removed if fast paint of scrolled wrapped alignment is coded in
1008 * future (JAL-2609).
1011 * @param noFastPaint
1014 public boolean highlightSearchResults(SearchResultsI results,
1015 boolean noFastPaint)
1021 boolean wrapped = av.getWrapAlignment();
1025 fastPaint = !noFastPaint;
1026 fastpainting = fastPaint;
1031 * to avoid redrawing the whole visible region, we instead
1032 * redraw just the minimal regions to remove previous highlights
1035 SearchResultsI previous = av.getSearchResults();
1036 av.setSearchResults(results);
1037 boolean redrawn = false;
1038 boolean drawn = false;
1041 redrawn = drawMappedPositionsWrapped(previous);
1042 drawn = drawMappedPositionsWrapped(results);
1047 redrawn = drawMappedPositions(previous);
1048 drawn = drawMappedPositions(results);
1053 * if highlights were either removed or added, repaint
1061 * return true only if highlights were added
1067 fastpainting = false;
1072 * Redraws the minimal rectangle in the visible region (if any) that includes
1073 * mapped positions of the given search results. Whether or not positions are
1074 * highlighted depends on the SearchResults set on the Viewport. This allows
1075 * this method to be called to either clear or set highlighting. Answers true
1076 * if any positions were drawn (in which case a repaint is still required),
1082 protected boolean drawMappedPositions(SearchResultsI results)
1084 if (results == null)
1090 * calculate the minimal rectangle to redraw that
1091 * includes both new and existing search results
1093 int firstSeq = Integer.MAX_VALUE;
1095 int firstCol = Integer.MAX_VALUE;
1097 boolean matchFound = false;
1099 ViewportRanges ranges = av.getRanges();
1100 int firstVisibleColumn = ranges.getStartRes();
1101 int lastVisibleColumn = ranges.getEndRes();
1102 AlignmentI alignment = av.getAlignment();
1103 if (av.hasHiddenColumns())
1105 firstVisibleColumn = alignment.getHiddenColumns()
1106 .adjustForHiddenColumns(firstVisibleColumn);
1107 lastVisibleColumn = alignment.getHiddenColumns()
1108 .adjustForHiddenColumns(lastVisibleColumn);
1111 for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
1112 .getEndSeq(); seqNo++)
1114 SequenceI seq = alignment.getSequenceAt(seqNo);
1116 int[] visibleResults = results.getResults(seq, firstVisibleColumn,
1118 if (visibleResults != null)
1120 for (int i = 0; i < visibleResults.length - 1; i += 2)
1122 int firstMatchedColumn = visibleResults[i];
1123 int lastMatchedColumn = visibleResults[i + 1];
1124 if (firstMatchedColumn <= lastVisibleColumn
1125 && lastMatchedColumn >= firstVisibleColumn)
1128 * found a search results match in the visible region -
1129 * remember the first and last sequence matched, and the first
1130 * and last visible columns in the matched positions
1133 firstSeq = Math.min(firstSeq, seqNo);
1134 lastSeq = Math.max(lastSeq, seqNo);
1135 firstMatchedColumn = Math.max(firstMatchedColumn,
1136 firstVisibleColumn);
1137 lastMatchedColumn = Math.min(lastMatchedColumn,
1139 firstCol = Math.min(firstCol, firstMatchedColumn);
1140 lastCol = Math.max(lastCol, lastMatchedColumn);
1148 if (av.hasHiddenColumns())
1150 firstCol = alignment.getHiddenColumns()
1151 .findColumnPosition(firstCol);
1152 lastCol = alignment.getHiddenColumns().findColumnPosition(lastCol);
1154 int transX = (firstCol - ranges.getStartRes()) * av.getCharWidth();
1155 int transY = (firstSeq - ranges.getStartSeq()) * av.getCharHeight();
1156 gg.translate(transX, transY);
1157 drawPanel(gg, firstCol, lastCol, firstSeq, lastSeq, 0);
1158 gg.translate(-transX, -transY);
1165 public void propertyChange(PropertyChangeEvent evt)
1167 String eventName = evt.getPropertyName();
1169 if (av.getWrapAlignment())
1171 if (eventName.equals(ViewportRanges.STARTRES))
1179 if (eventName.equals(ViewportRanges.STARTRES))
1181 // Make sure we're not trying to draw a panel
1182 // larger than the visible window
1183 ViewportRanges vpRanges = av.getRanges();
1184 scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
1185 int range = vpRanges.getEndRes() - vpRanges.getStartRes();
1186 if (scrollX > range)
1190 else if (scrollX < -range)
1196 // Both scrolling and resizing change viewport ranges: scrolling changes
1197 // both start and end points, but resize only changes end values.
1198 // Here we only want to fastpaint on a scroll, with resize using a normal
1199 // paint, so scroll events are identified as changes to the horizontal or
1200 // vertical start value.
1201 if (eventName.equals(ViewportRanges.STARTRES))
1203 // scroll - startres and endres both change
1204 fastPaint(scrollX, 0);
1206 else if (eventName.equals(ViewportRanges.STARTSEQ))
1209 fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
1215 * Redraws any positions in the search results in the visible region of a
1216 * wrapped alignment. Any highlights are drawn depending on the search results
1217 * set on the Viewport, not the <code>results</code> argument. This allows
1218 * this method to be called either to clear highlights (passing the previous
1219 * search results), or to draw new highlights.
1224 protected boolean drawMappedPositionsWrapped(SearchResultsI results)
1226 if (results == null)
1231 boolean matchFound = false;
1233 int wrappedWidth = av.getWrappedWidth();
1234 int wrappedHeight = getRepeatHeightWrapped();
1236 ViewportRanges ranges = av.getRanges();
1237 int canvasHeight = getHeight();
1238 int repeats = canvasHeight / wrappedHeight;
1239 if (canvasHeight / wrappedHeight > 0)
1244 int firstVisibleColumn = ranges.getStartRes();
1245 int lastVisibleColumn = ranges.getStartRes() + repeats
1246 * ranges.getViewportWidth() - 1;
1248 AlignmentI alignment = av.getAlignment();
1249 if (av.hasHiddenColumns())
1251 firstVisibleColumn = alignment.getHiddenColumns()
1252 .adjustForHiddenColumns(firstVisibleColumn);
1253 lastVisibleColumn = alignment.getHiddenColumns()
1254 .adjustForHiddenColumns(lastVisibleColumn);
1257 int gapHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1);
1259 for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
1260 .getEndSeq(); seqNo++)
1262 SequenceI seq = alignment.getSequenceAt(seqNo);
1264 int[] visibleResults = results.getResults(seq, firstVisibleColumn,
1266 if (visibleResults != null)
1268 for (int i = 0; i < visibleResults.length - 1; i += 2)
1270 int firstMatchedColumn = visibleResults[i];
1271 int lastMatchedColumn = visibleResults[i + 1];
1272 if (firstMatchedColumn <= lastVisibleColumn
1273 && lastMatchedColumn >= firstVisibleColumn)
1276 * found a search results match in the visible region
1278 firstMatchedColumn = Math.max(firstMatchedColumn,
1279 firstVisibleColumn);
1280 lastMatchedColumn = Math.min(lastMatchedColumn,
1284 * draw each mapped position separately (as contiguous positions may
1285 * wrap across lines)
1287 for (int mappedPos = firstMatchedColumn; mappedPos <= lastMatchedColumn; mappedPos++)
1289 int displayColumn = mappedPos;
1290 if (av.hasHiddenColumns())
1292 displayColumn = alignment.getHiddenColumns()
1293 .findColumnPosition(displayColumn);
1297 * transX: offset from left edge of canvas to residue position
1299 int transX = labelWidthWest
1300 + ((displayColumn - ranges.getStartRes()) % wrappedWidth)
1301 * av.getCharWidth();
1304 * transY: offset from top edge of canvas to residue position
1306 int transY = gapHeight;
1307 transY += (displayColumn - ranges.getStartRes())
1308 / wrappedWidth * wrappedHeight;
1309 transY += (seqNo - ranges.getStartSeq()) * av.getCharHeight();
1312 * yOffset is from graphics origin to start of visible region
1314 int yOffset = 0;// (displayColumn / wrappedWidth) * wrappedHeight;
1315 if (transY < getHeight())
1318 gg.translate(transX, transY);
1319 drawPanel(gg, displayColumn, displayColumn, seqNo, seqNo,
1321 gg.translate(-transX, -transY);
1333 * Answers the height in pixels of a repeating section of the wrapped
1334 * alignment, including space above, scale above if shown, sequences, and
1335 * annotation panel if shown
1339 protected int getRepeatHeightWrapped()
1341 // gap (and maybe scale) above
1342 int repeatHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1);
1345 repeatHeight += av.getRanges().getViewportHeight() * charHeight;
1347 // add annotations panel height if shown
1348 repeatHeight += getAnnotationHeight();
1350 return repeatHeight;