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),
150 (ypos + 2) - (charHeight / 2),
151 (mpos * charWidth) + (charWidth / 2), ypos - 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,
214 (ypos + (i * charHeight)) - (charHeight / 5));
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,
266 (ypos + (i * charHeight)) - (charHeight / 5));
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);
351 if (lcimg != null && (fastPaint
352 || (getVisibleRect().width != g.getClipBounds().width)
353 || (getVisibleRect().height != g.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);
416 * Returns the visible width of the canvas in residues, after allowing for
417 * East or West scales (if shown)
420 * the width in pixels (possibly including scales)
424 public int getWrappedCanvasWidth(int canvasWidth)
426 FontMetrics fm = getFontMetrics(av.getFont());
431 if (av.getScaleRightWrapped())
433 labelWidthEast = getLabelWidth(fm);
436 if (av.getScaleLeftWrapped())
438 labelWidthWest = labelWidthEast > 0 ? labelWidthEast
442 return (canvasWidth - labelWidthEast - labelWidthWest) / charWidth;
446 * Returns a pixel width suitable for showing the largest sequence coordinate
447 * (end position) in the alignment. Returns 2 plus the number of decimal
448 * digits to be shown (3 for 1-10, 4 for 11-99 etc).
453 protected int getLabelWidth(FontMetrics fm)
456 * find the biggest sequence end position we need to show
457 * (note this is not necessarily the sequence length)
460 AlignmentI alignment = av.getAlignment();
461 for (int i = 0; i < alignment.getHeight(); i++)
463 maxWidth = Math.max(maxWidth, alignment.getSequenceAt(i).getEnd());
467 for (int i = maxWidth; i > 0; i /= 10)
472 return fm.stringWidth(ZEROS.substring(0, length));
482 * @param canvasHeight
487 public void drawWrappedPanel(Graphics g, int canvasWidth,
488 int canvasHeight, int startRes)
491 AlignmentI al = av.getAlignment();
494 if (av.getScaleRightWrapped() || av.getScaleLeftWrapped())
496 FontMetrics fm = getFontMetrics(av.getFont());
497 labelWidth = getLabelWidth(fm);
500 labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0;
501 labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0;
503 int hgap = charHeight;
504 if (av.getScaleAboveWrapped())
509 int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) / charWidth;
510 int cHeight = av.getAlignment().getHeight() * charHeight;
512 av.setWrappedWidth(cWidth);
514 av.getRanges().setViewportStartAndWidth(startRes, cWidth);
518 int maxwidth = av.getAlignment().getWidth();
520 if (av.hasHiddenColumns())
522 maxwidth = av.getAlignment().getHiddenColumns()
523 .findColumnPosition(maxwidth);
526 int annotationHeight = getAnnotationHeight();
528 while ((ypos <= canvasHeight) && (startRes < maxwidth))
530 endx = startRes + cWidth - 1;
537 g.setFont(av.getFont());
538 g.setColor(Color.black);
540 if (av.getScaleLeftWrapped())
542 drawWestScale(g, startRes, endx, ypos);
545 if (av.getScaleRightWrapped())
547 g.translate(canvasWidth - labelWidthEast, 0);
548 drawEastScale(g, startRes, endx, ypos);
549 g.translate(-(canvasWidth - labelWidthEast), 0);
552 g.translate(labelWidthWest, 0);
554 if (av.getScaleAboveWrapped())
556 drawNorthScale(g, startRes, endx, ypos);
559 if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
561 g.setColor(Color.blue);
563 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
564 List<Integer> positions = hidden.findHiddenRegionPositions();
565 for (int pos : positions)
567 res = pos - startRes;
569 if (res < 0 || res > endx - startRes)
576 { res * charWidth - charHeight / 4,
577 res * charWidth + charHeight / 4, res * charWidth },
579 { ypos - (charHeight / 2), ypos - (charHeight / 2),
580 ypos - (charHeight / 2) + 8 },
586 // When printing we have an extra clipped region,
587 // the Printable page which we need to account for here
588 Shape clip = g.getClip();
592 g.setClip(0, 0, cWidth * charWidth, canvasHeight);
596 g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
597 (int) clip.getBounds().getHeight());
600 drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
602 if (av.isShowAnnotation())
604 g.translate(0, cHeight + ypos + 3);
605 if (annotations == null)
607 annotations = new AnnotationPanel(av);
610 annotations.renderer.drawComponent(annotations, av, g, -1, startRes,
612 g.translate(0, -cHeight - ypos - 3);
615 g.translate(-labelWidthWest, 0);
617 ypos += cHeight + annotationHeight + hgap;
623 AnnotationPanel annotations;
625 int getAnnotationHeight()
627 if (!av.isShowAnnotation())
632 if (annotations == null)
634 annotations = new AnnotationPanel(av);
637 return annotations.adjustPanelHeight();
641 * Draws the visible region of the alignment on the graphics context. If there
642 * are hidden column markers in the visible region, then each sub-region
643 * between the markers is drawn separately, followed by the hidden column
648 * offset of the first column in the visible region (0..)
650 * offset of the last column in the visible region (0..)
652 * offset of the first sequence in the visible region (0..)
654 * offset of the last sequence in the visible region (0..)
656 * vertical offset at which to draw (for wrapped alignments)
658 public void drawPanel(Graphics g1, final int startRes, final int endRes,
659 final int startSeq, final int endSeq, final int yOffset)
662 if (!av.hasHiddenColumns())
664 draw(g1, startRes, endRes, startSeq, endSeq, yOffset);
669 final int screenYMax = endRes - startRes;
670 int blockStart = startRes;
671 int blockEnd = endRes;
673 for (int[] region : av.getAlignment().getHiddenColumns()
674 .getHiddenColumnsCopy())
676 int hideStart = region[0];
677 int hideEnd = region[1];
679 if (hideStart <= blockStart)
681 blockStart += (hideEnd - hideStart) + 1;
686 * draw up to just before the next hidden region, or the end of
687 * the visible region, whichever comes first
689 blockEnd = Math.min(hideStart - 1, blockStart + screenYMax
692 g1.translate(screenY * charWidth, 0);
694 draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset);
697 * draw the downline of the hidden column marker (ScalePanel draws the
698 * triangle on top) if we reached it
700 if (av.getShowHiddenMarkers() && blockEnd == hideStart - 1)
702 g1.setColor(Color.blue);
704 g1.drawLine((blockEnd - blockStart + 1) * charWidth - 1,
705 0 + yOffset, (blockEnd - blockStart + 1) * charWidth - 1,
706 (endSeq - startSeq + 1) * charHeight + yOffset);
709 g1.translate(-screenY * charWidth, 0);
710 screenY += blockEnd - blockStart + 1;
711 blockStart = hideEnd + 1;
713 if (screenY > screenYMax)
715 // already rendered last block
720 if (screenY <= screenYMax)
722 // remaining visible region to render
723 blockEnd = blockStart + screenYMax - screenY;
724 g1.translate(screenY * charWidth, 0);
725 draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset);
727 g1.translate(-screenY * charWidth, 0);
734 * Draws a region of the visible alignment
738 * offset of the first column in the visible region (0..)
740 * offset of the last column in the visible region (0..)
742 * offset of the first sequence in the visible region (0..)
744 * offset of the last sequence in the visible region (0..)
746 * vertical offset at which to draw (for wrapped alignments)
748 private void draw(Graphics g, int startRes, int endRes, int startSeq,
749 int endSeq, int offset)
751 g.setFont(av.getFont());
752 sr.prepare(g, av.isRenderGaps());
756 // / First draw the sequences
757 // ///////////////////////////
758 for (int i = startSeq; i <= endSeq; i++)
760 nextSeq = av.getAlignment().getSequenceAt(i);
763 // occasionally, a race condition occurs such that the alignment row is
767 sr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
768 startRes, endRes, offset + ((i - startSeq) * charHeight));
770 if (av.isShowSequenceFeatures())
772 fr.drawSequence(g, nextSeq, startRes, endRes,
773 offset + ((i - startSeq) * charHeight), false);
777 * highlight search Results once sequence has been drawn
779 if (av.hasSearchResults())
781 SearchResultsI searchResults = av.getSearchResults();
782 int[] visibleResults = searchResults.getResults(nextSeq,
784 if (visibleResults != null)
786 for (int r = 0; r < visibleResults.length; r += 2)
788 sr.drawHighlightedText(nextSeq, visibleResults[r],
789 visibleResults[r + 1],
790 (visibleResults[r] - startRes) * charWidth,
791 offset + ((i - startSeq) * charHeight));
796 if (av.cursorMode && cursorY == i && cursorX >= startRes
797 && cursorX <= endRes)
799 sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
800 offset + ((i - startSeq) * charHeight));
804 if (av.getSelectionGroup() != null
805 || av.getAlignment().getGroups().size() > 0)
807 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
812 void drawGroupsBoundaries(Graphics g1, int startRes, int endRes,
813 int startSeq, int endSeq, int offset)
815 Graphics2D g = (Graphics2D) g1;
817 // ///////////////////////////////////
818 // Now outline any areas if necessary
819 // ///////////////////////////////////
820 SequenceGroup group = av.getSelectionGroup();
826 int visWidth = (endRes - startRes + 1) * charWidth;
828 if ((group == null) && (av.getAlignment().getGroups().size() > 0))
830 group = av.getAlignment().getGroups().get(0);
840 boolean inGroup = false;
844 for (i = startSeq; i <= endSeq; i++)
846 sx = (group.getStartRes() - startRes) * charWidth;
847 sy = offset + ((i - startSeq) * charHeight);
848 ex = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
851 if (sx + ex < 0 || sx > visWidth)
856 if ((sx <= (endRes - startRes) * charWidth)
857 && group.getSequences(null)
858 .contains(av.getAlignment().getSequenceAt(i)))
860 if ((bottom == -1) && !group.getSequences(null)
861 .contains(av.getAlignment().getSequenceAt(i + 1)))
863 bottom = sy + charHeight;
868 if (((top == -1) && (i == 0)) || !group.getSequences(null)
869 .contains(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[]
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;