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().setEndRes(av.getRanges().getStartRes() + cWidth - 1);
519 int maxwidth = av.getAlignment().getWidth() - 1;
521 if (av.hasHiddenColumns())
523 maxwidth = av.getAlignment().getHiddenColumns()
524 .findColumnPosition(maxwidth) - 1;
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.
1007 public boolean highlightSearchResults(SearchResultsI results)
1011 boolean wrapped = av.getWrapAlignment();
1016 * to avoid redrawing the whole visible region, we instead
1017 * redraw just the minimal regions to remove previous highlights
1020 SearchResultsI previous = av.getSearchResults();
1021 av.setSearchResults(results);
1022 boolean redrawn = false;
1023 boolean drawn = false;
1026 redrawn = drawMappedPositionsWrapped(previous);
1027 drawn = drawMappedPositionsWrapped(results);
1032 redrawn = drawMappedPositions(previous);
1033 drawn = drawMappedPositions(results);
1038 * if highlights were either removed or added, repaint
1046 * return true only if highlights were added
1052 fastpainting = false;
1058 * Redraws the minimal rectangle in the visible region (if any) that includes
1059 * mapped positions of the given search results. Whether or not positions are
1060 * highlighted depends on the SearchResults set on the Viewport. This allows
1061 * this method to be called to either clear or set highlighting. Answers true
1062 * if any positions were drawn (in which case a repaint is still required),
1068 protected boolean drawMappedPositions(SearchResultsI results)
1070 if (results == null)
1076 * calculate the minimal rectangle to redraw that
1077 * includes both new and existing search results
1079 int firstSeq = Integer.MAX_VALUE;
1081 int firstCol = Integer.MAX_VALUE;
1083 boolean matchFound = false;
1085 ViewportRanges ranges = av.getRanges();
1086 int firstVisibleColumn = ranges.getStartRes();
1087 int lastVisibleColumn = ranges.getEndRes();
1088 AlignmentI alignment = av.getAlignment();
1089 if (av.hasHiddenColumns())
1091 firstVisibleColumn = alignment.getHiddenColumns()
1092 .adjustForHiddenColumns(firstVisibleColumn);
1093 lastVisibleColumn = alignment.getHiddenColumns()
1094 .adjustForHiddenColumns(lastVisibleColumn);
1097 for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
1098 .getEndSeq(); seqNo++)
1100 SequenceI seq = alignment.getSequenceAt(seqNo);
1102 int[] visibleResults = results.getResults(seq, firstVisibleColumn,
1104 if (visibleResults != null)
1106 for (int i = 0; i < visibleResults.length - 1; i += 2)
1108 int firstMatchedColumn = visibleResults[i];
1109 int lastMatchedColumn = visibleResults[i + 1];
1110 if (firstMatchedColumn <= lastVisibleColumn
1111 && lastMatchedColumn >= firstVisibleColumn)
1114 * found a search results match in the visible region -
1115 * remember the first and last sequence matched, and the first
1116 * and last visible columns in the matched positions
1119 firstSeq = Math.min(firstSeq, seqNo);
1120 lastSeq = Math.max(lastSeq, seqNo);
1121 firstMatchedColumn = Math.max(firstMatchedColumn,
1122 firstVisibleColumn);
1123 lastMatchedColumn = Math.min(lastMatchedColumn,
1125 firstCol = Math.min(firstCol, firstMatchedColumn);
1126 lastCol = Math.max(lastCol, lastMatchedColumn);
1134 if (av.hasHiddenColumns())
1136 firstCol = alignment.getHiddenColumns()
1137 .findColumnPosition(firstCol);
1138 lastCol = alignment.getHiddenColumns().findColumnPosition(lastCol);
1140 int transX = (firstCol - ranges.getStartRes()) * av.getCharWidth();
1141 int transY = (firstSeq - ranges.getStartSeq()) * av.getCharHeight();
1142 gg.translate(transX, transY);
1143 drawPanel(gg, firstCol, lastCol, firstSeq, lastSeq, 0);
1144 gg.translate(-transX, -transY);
1151 public void propertyChange(PropertyChangeEvent evt)
1153 String eventName = evt.getPropertyName();
1155 if (!av.getWrapAlignment())
1158 if (eventName.equals(ViewportRanges.STARTRES))
1160 // Make sure we're not trying to draw a panel
1161 // larger than the visible window
1162 ViewportRanges vpRanges = av.getRanges();
1163 scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
1164 int range = vpRanges.getEndRes() - vpRanges.getStartRes();
1165 if (scrollX > range)
1169 else if (scrollX < -range)
1175 // Both scrolling and resizing change viewport ranges: scrolling changes
1176 // both start and end points, but resize only changes end values.
1177 // Here we only want to fastpaint on a scroll, with resize using a normal
1178 // paint, so scroll events are identified as changes to the horizontal or
1179 // vertical start value.
1180 if (eventName.equals(ViewportRanges.STARTRES))
1182 // scroll - startres and endres both change
1183 fastPaint(scrollX, 0);
1185 else if (eventName.equals(ViewportRanges.STARTSEQ))
1188 fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
1194 * Redraws any positions in the search results in the visible region of a
1195 * wrapped alignment. Any highlights are drawn depending on the search results
1196 * set on the Viewport, not the <code>results</code> argument. This allows
1197 * this method to be called either to clear highlights (passing the previous
1198 * search results), or to draw new highlights.
1203 protected boolean drawMappedPositionsWrapped(SearchResultsI results)
1205 if (results == null)
1210 boolean matchFound = false;
1212 int wrappedWidth = av.getWrappedWidth();
1213 int wrappedHeight = getRepeatHeightWrapped();
1215 ViewportRanges ranges = av.getRanges();
1216 int canvasHeight = getHeight();
1217 int repeats = wrappedHeight / canvasHeight;
1218 if (wrappedHeight % canvasHeight > 0)
1223 int firstVisibleColumn = ranges.getStartRes();
1224 int lastVisibleColumn = ranges.getStartRes() + repeats
1225 * ranges.getViewportWidth() - 1;
1227 AlignmentI alignment = av.getAlignment();
1228 if (av.hasHiddenColumns())
1230 firstVisibleColumn = alignment.getHiddenColumns()
1231 .adjustForHiddenColumns(firstVisibleColumn);
1232 lastVisibleColumn = alignment.getHiddenColumns()
1233 .adjustForHiddenColumns(lastVisibleColumn);
1236 int gapHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1);
1238 for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
1239 .getEndSeq(); seqNo++)
1241 SequenceI seq = alignment.getSequenceAt(seqNo);
1243 int[] visibleResults = results.getResults(seq, firstVisibleColumn,
1245 if (visibleResults != null)
1247 for (int i = 0; i < visibleResults.length - 1; i += 2)
1249 int firstMatchedColumn = visibleResults[i];
1250 int lastMatchedColumn = visibleResults[i + 1];
1251 if (firstMatchedColumn <= lastVisibleColumn
1252 && lastMatchedColumn >= firstVisibleColumn)
1255 * found a search results match in the visible region
1257 firstMatchedColumn = Math.max(firstMatchedColumn,
1258 firstVisibleColumn);
1259 lastMatchedColumn = Math.min(lastMatchedColumn,
1263 * draw each mapped position separately (as contiguous positions may
1264 * wrap across lines)
1266 for (int mappedPos = firstMatchedColumn; mappedPos <= lastMatchedColumn; mappedPos++)
1268 int displayColumn = mappedPos;
1269 if (av.hasHiddenColumns())
1271 displayColumn = alignment.getHiddenColumns()
1272 .findColumnPosition(displayColumn);
1276 * transX: offset from left edge of canvas to residue position
1278 int transX = labelWidthWest
1279 + ((displayColumn - ranges.getStartRes()) % wrappedWidth)
1280 * av.getCharWidth();
1283 * transY: offset from top edge of canvas to residue position
1285 int transY = gapHeight;
1286 transY += (displayColumn / wrappedWidth) * wrappedHeight;
1287 transY += (seqNo - ranges.getStartSeq()) * av.getCharHeight();
1290 * yOffset is from graphics origin to start of visible region
1292 int yOffset = 0;// (displayColumn / wrappedWidth) * wrappedHeight;
1293 if (transY < getHeight())
1296 gg.translate(transX, transY);
1297 drawPanel(gg, displayColumn, displayColumn, seqNo, seqNo,
1299 gg.translate(-transX, -transY);
1311 * Answers the height in pixels of a repeating section of the wrapped
1312 * alignment, including space above, scale above if shown, sequences, and
1313 * annotation panel if shown
1317 protected int getRepeatHeightWrapped()
1319 // gap (and maybe scale) above
1320 int repeatHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1);
1323 repeatHeight += av.getRanges().getViewportHeight() * charHeight;
1325 // add annotations panel height if shown
1326 repeatHeight += getAnnotationHeight();
1328 return repeatHeight;