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);
422 * @return DOCUMENT ME!
424 public int getWrappedCanvasWidth(int cwidth)
426 FontMetrics fm = getFontMetrics(av.getFont());
431 if (av.getScaleRightWrapped())
433 labelWidthEast = getLabelWidth(fm);
436 if (av.getScaleLeftWrapped())
438 labelWidthWest = getLabelWidth(fm);
441 return (cwidth - labelWidthEast - labelWidthWest) / charWidth;
445 * Returns a pixel width suitable for showing the largest sequence coordinate
446 * (end position) in the alignment. Returns 2 plus the number of decimal
447 * digits to be shown (3 for 1-10, 4 for 11-99 etc).
452 protected int getLabelWidth(FontMetrics fm)
455 for (int i = 0; i < av.getAlignment().getHeight(); i++)
457 maxWidth = Math.max(maxWidth, av.getAlignment().getSequenceAt(i)
462 for (int i = maxWidth; i > 0; i /= 10)
467 return fm.stringWidth(ZEROS.substring(0, length));
477 * @param canvasHeight
482 public void drawWrappedPanel(Graphics g, int canvasWidth,
483 int canvasHeight, int startRes)
486 AlignmentI al = av.getAlignment();
489 if (av.getScaleRightWrapped() || av.getScaleLeftWrapped())
491 FontMetrics fm = getFontMetrics(av.getFont());
492 labelWidth = getLabelWidth(fm);
495 labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0;
496 labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0;
498 int hgap = charHeight;
499 if (av.getScaleAboveWrapped())
504 int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) / charWidth;
505 int cHeight = av.getAlignment().getHeight() * charHeight;
507 av.setWrappedWidth(cWidth);
509 av.getRanges().setEndRes(av.getRanges().getStartRes() + cWidth - 1);
513 int maxwidth = av.getAlignment().getWidth() - 1;
515 if (av.hasHiddenColumns())
517 maxwidth = av.getAlignment().getHiddenColumns()
518 .findColumnPosition(maxwidth) - 1;
521 int annotationHeight = getAnnotationHeight();
523 while ((ypos <= canvasHeight) && (startRes < maxwidth))
525 endx = startRes + cWidth - 1;
532 g.setFont(av.getFont());
533 g.setColor(Color.black);
535 if (av.getScaleLeftWrapped())
537 drawWestScale(g, startRes, endx, ypos);
540 if (av.getScaleRightWrapped())
542 g.translate(canvasWidth - labelWidthEast, 0);
543 drawEastScale(g, startRes, endx, ypos);
544 g.translate(-(canvasWidth - labelWidthEast), 0);
547 g.translate(labelWidthWest, 0);
549 if (av.getScaleAboveWrapped())
551 drawNorthScale(g, startRes, endx, ypos);
554 if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
556 g.setColor(Color.blue);
558 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
559 List<Integer> positions = hidden.findHiddenRegionPositions();
560 for (int pos : positions)
562 res = pos - startRes;
564 if (res < 0 || res > endx - startRes)
570 new int[] { res * charWidth - charHeight / 4,
571 res * charWidth + charHeight / 4, res * charWidth },
572 new int[] { ypos - (charHeight / 2),
573 ypos - (charHeight / 2), ypos - (charHeight / 2) + 8 },
579 // When printing we have an extra clipped region,
580 // the Printable page which we need to account for here
581 Shape clip = g.getClip();
585 g.setClip(0, 0, cWidth * charWidth, canvasHeight);
589 g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
590 (int) clip.getBounds().getHeight());
593 drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
595 if (av.isShowAnnotation())
597 g.translate(0, cHeight + ypos + 3);
598 if (annotations == null)
600 annotations = new AnnotationPanel(av);
603 annotations.renderer.drawComponent(annotations, av, g, -1,
605 g.translate(0, -cHeight - ypos - 3);
608 g.translate(-labelWidthWest, 0);
610 ypos += cHeight + annotationHeight + hgap;
616 AnnotationPanel annotations;
618 int getAnnotationHeight()
620 if (!av.isShowAnnotation())
625 if (annotations == null)
627 annotations = new AnnotationPanel(av);
630 return annotations.adjustPanelHeight();
634 * Draws the visible region of the alignment on the graphics context. If there
635 * are hidden column markers in the visible region, then each sub-region
636 * between the markers is drawn separately, followed by the hidden column
641 * offset of the first column in the visible region (0..)
643 * offset of the last column in the visible region (0..)
645 * offset of the first sequence in the visible region (0..)
647 * offset of the last sequence in the visible region (0..)
649 * vertical offset at which to draw (for wrapped alignments)
651 public void drawPanel(Graphics g1, final int startRes, final int endRes,
652 final int startSeq, final int endSeq, final int yOffset)
655 if (!av.hasHiddenColumns())
657 draw(g1, startRes, endRes, startSeq, endSeq, yOffset);
662 final int screenYMax = endRes - startRes;
663 int blockStart = startRes;
664 int blockEnd = endRes;
666 for (int[] region : av.getAlignment().getHiddenColumns()
667 .getHiddenColumnsCopy())
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 String eventName = evt.getPropertyName();
1163 if (!av.getWrapAlignment())
1166 if (eventName.equals(ViewportRanges.STARTRES))
1168 // Make sure we're not trying to draw a panel
1169 // larger than the visible window
1170 ViewportRanges vpRanges = av.getRanges();
1171 scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
1172 int range = vpRanges.getEndRes() - vpRanges.getStartRes();
1173 if (scrollX > range)
1177 else if (scrollX < -range)
1183 // Both scrolling and resizing change viewport ranges: scrolling changes
1184 // both start and end points, but resize only changes end values.
1185 // Here we only want to fastpaint on a scroll, with resize using a normal
1186 // paint, so scroll events are identified as changes to the horizontal or
1187 // vertical start value.
1188 if (eventName.equals(ViewportRanges.STARTRES))
1190 // scroll - startres and endres both change
1191 fastPaint(scrollX, 0);
1193 else if (eventName.equals(ViewportRanges.STARTSEQ))
1196 fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
1202 * Redraws any positions in the search results in the visible region. Any
1203 * highlights are drawn depending on the search results set on the Viewport,
1204 * not the results parameter. This allows this method to be called to either
1205 * clear highlighting (passing the previous search results), or set new
1211 protected boolean drawWrappedMappedPositions(SearchResultsI results)
1213 if (results == null)
1218 boolean matchFound = false;
1221 * Viewport ranges are set for the 'row' of the wrapped alignment
1222 * the cursor is in, not the whole visible region; really we want
1223 * the latter; +-6 a temporary fudge for codons wrapping across lines
1225 ViewportRanges ranges = av.getRanges();
1226 int firstVisibleColumn = ranges.getStartRes() - 6;
1227 int lastVisibleColumn = ranges.getEndRes() + 6;
1228 AlignmentI alignment = av.getAlignment();
1229 if (av.hasHiddenColumns())
1231 firstVisibleColumn = alignment.getHiddenColumns()
1232 .adjustForHiddenColumns(firstVisibleColumn);
1233 lastVisibleColumn = alignment.getHiddenColumns()
1234 .adjustForHiddenColumns(lastVisibleColumn);
1238 * find width of alignment in residues, and height of alignment,
1239 * so we can calculate where to render each matched position
1241 int wrappedWidth = av.getWrappedWidth();
1242 int wrappedHeight = av.getAlignment().getHeight() * av.getCharHeight();
1243 int gapHeight = av.getCharHeight()
1244 * (av.getScaleAboveWrapped() ? 2 : 1);
1245 wrappedHeight += gapHeight;
1246 wrappedHeight += getAnnotationHeight();
1248 for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
1249 .getEndSeq(); seqNo++)
1251 SequenceI seq = alignment.getSequenceAt(seqNo);
1253 int[] visibleResults = results.getResults(seq, firstVisibleColumn,
1255 if (visibleResults != null)
1257 for (int i = 0; i < visibleResults.length - 1; i += 2)
1259 int firstMatchedColumn = visibleResults[i];
1260 int lastMatchedColumn = visibleResults[i + 1];
1261 if (firstMatchedColumn <= lastVisibleColumn
1262 && lastMatchedColumn >= firstVisibleColumn)
1265 * found a search results match in the visible region
1267 firstMatchedColumn = Math.max(firstMatchedColumn,
1268 firstVisibleColumn);
1269 lastMatchedColumn = Math.min(lastMatchedColumn,
1273 * draw each mapped position separately (as contiguous positions may
1274 * wrap across lines)
1276 for (int mappedPos = firstMatchedColumn; mappedPos <= lastMatchedColumn; mappedPos++)
1278 int displayColumn = mappedPos;
1279 if (av.hasHiddenColumns())
1281 displayColumn = alignment.getHiddenColumns()
1282 .findColumnPosition(displayColumn);
1286 * transX: offset from left edge of canvas to residue position
1288 int transX = labelWidthWest
1289 + ((displayColumn - ranges.getStartRes()) % wrappedWidth)
1290 * av.getCharWidth();
1293 * transY: offset from top edge of canvas to residue position
1295 int transY = gapHeight;
1296 transY += (displayColumn / wrappedWidth) * wrappedHeight;
1297 transY += (seqNo - ranges.getStartSeq()) * av.getCharHeight();
1300 * yOffset is from graphics origin to start of visible region
1302 int yOffset = 0;// (displayColumn / wrappedWidth) * wrappedHeight;
1303 if (transY < getHeight())
1306 gg.translate(transX, transY);
1307 drawPanel(gg, displayColumn, displayColumn, seqNo, seqNo,
1309 gg.translate(-transX, -transY);