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.AlphaComposite;
34 import java.awt.BasicStroke;
35 import java.awt.BorderLayout;
36 import java.awt.Color;
37 import java.awt.FontMetrics;
38 import java.awt.Graphics;
39 import java.awt.Graphics2D;
40 import java.awt.RenderingHints;
41 import java.awt.Shape;
42 import java.awt.image.BufferedImage;
43 import java.beans.PropertyChangeEvent;
44 import java.util.List;
46 import javax.swing.JComponent;
54 public class SeqCanvas extends JComponent implements ViewportListenerI
56 final FeatureRenderer fr;
58 final SequenceRenderer seqRdr;
66 boolean fastPaint = false;
80 boolean fastpainting = false;
82 AnnotationPanel annotations;
85 * Creates a new SeqCanvas object.
90 public SeqCanvas(AlignmentPanel ap)
94 fr = new FeatureRenderer(ap);
95 seqRdr = new SequenceRenderer(av);
96 setLayout(new BorderLayout());
97 PaintRefresher.Register(this, av.getSequenceSetId());
98 setBackground(Color.white);
100 av.getRanges().addPropertyChangeListener(this);
103 public SequenceRenderer getSequenceRenderer()
108 public FeatureRenderer getFeatureRenderer()
113 private void updateViewport()
115 charHeight = av.getCharHeight();
116 charWidth = av.getCharWidth();
131 private void drawNorthScale(Graphics g, int startx, int endx, int ypos)
134 for (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx,
137 int mpos = mark.column; // (i - startx - 1)
142 String mstring = mark.text;
148 g.drawString(mstring, mpos * charWidth, ypos - (charHeight / 2));
150 g.drawLine((mpos * charWidth) + (charWidth / 2), (ypos + 2)
151 - (charHeight / 2), (mpos * charWidth) + (charWidth / 2),
169 void drawWestScale(Graphics g, int startx, int endx, int ypos)
171 FontMetrics fm = getFontMetrics(av.getFont());
174 if (av.hasHiddenColumns())
176 startx = av.getAlignment().getHiddenColumns()
177 .adjustForHiddenColumns(startx);
178 endx = av.getAlignment().getHiddenColumns()
179 .adjustForHiddenColumns(endx);
182 int maxwidth = av.getAlignment().getWidth();
183 if (av.hasHiddenColumns())
185 maxwidth = av.getAlignment().getHiddenColumns()
186 .findColumnPosition(maxwidth) - 1;
190 for (int i = 0; i < av.getAlignment().getHeight(); i++)
192 SequenceI seq = av.getAlignment().getSequenceAt(i);
198 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
205 value = av.getAlignment().getSequenceAt(i).findPosition(index);
212 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))
214 g.drawString(value + "", x, (ypos + (i * charHeight))
232 void drawEastScale(Graphics g, int startx, int endx, int ypos)
236 if (av.hasHiddenColumns())
238 endx = av.getAlignment().getHiddenColumns()
239 .adjustForHiddenColumns(endx);
244 for (int i = 0; i < av.getAlignment().getHeight(); i++)
246 seq = av.getAlignment().getSequenceAt(i);
250 while (index > startx)
252 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
259 value = seq.findPosition(index);
266 g.drawString(String.valueOf(value), 0, (ypos + (i * charHeight))
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 sr = ranges.getStartRes();
294 int er = ranges.getEndRes();
295 int ss = ranges.getStartSeq();
296 int es = ranges.getEndSeq();
300 gg.copyArea(horizontal * charWidth, vertical * charHeight,
301 img.getWidth(), img.getHeight(), -horizontal * charWidth,
302 -vertical * charHeight);
304 if (horizontal > 0) // scrollbar pulled right, image to the left
306 transX = (er - sr - horizontal) * charWidth;
307 sr = er - horizontal;
309 else if (horizontal < 0)
311 er = sr - horizontal;
313 else if (vertical > 0) // scroll down
317 if (ss < ranges.getStartSeq())
318 { // ie scrolling too fast, more than a page at a time
319 ss = ranges.getStartSeq();
323 transY = img.getHeight() - ((vertical + 1) * charHeight);
326 else if (vertical < 0)
330 if (es > ranges.getEndSeq())
332 es = ranges.getEndSeq();
336 gg.translate(transX, transY);
337 drawPanel(gg, sr, er, ss, es, 0);
338 gg.translate(-transX, -transY);
341 fastpainting = false;
345 * Definitions of startx and endx (hopefully): SMJS This is what I'm working
346 * towards! startx is the first residue (starting at 0) to display. endx is
347 * the last residue to display (starting at 0). starty is the first sequence
348 * to display (starting at 0). endy is the last sequence to display (starting
349 * at 0). NOTE 1: The av limits are set in setFont in this class and in the
350 * adjustment listener in SeqPanel when the scrollbars move.
353 // Set this to false to force a full panel paint
355 public void paintComponent(Graphics g)
357 super.paintComponent(g);
361 ViewportRanges ranges = av.getRanges();
363 int width = getWidth();
364 int height = getHeight();
366 width -= (width % charWidth);
367 height -= (height % charHeight);
369 // selectImage is the selection group outline image
370 BufferedImage selectImage = drawSelectionGroup(
371 ranges.getStartRes(), ranges.getEndRes(),
372 ranges.getStartSeq(), ranges.getEndSeq());
374 if ((img != null) && (fastPaint
375 || (getVisibleRect().width != g.getClipBounds().width)
376 || (getVisibleRect().height != g.getClipBounds().height)))
378 BufferedImage lcimg = buildLocalImage(selectImage);
379 g.drawImage(lcimg, 0, 0, this);
382 else if ((width > 0) && (height > 0))
384 // img is a cached version of the last view we drew, if any
385 // if we have no img or the size has changed, make a new one
386 if (img == null || width != img.getWidth()
387 || height != img.getHeight())
394 gg = (Graphics2D) img.getGraphics();
395 gg.setFont(av.getFont());
400 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
401 RenderingHints.VALUE_ANTIALIAS_ON);
404 gg.setColor(Color.white);
405 gg.fillRect(0, 0, img.getWidth(), img.getHeight());
407 if (av.getWrapAlignment())
409 drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
413 drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
414 ranges.getStartSeq(), ranges.getEndSeq(), 0);
417 // lcimg is a local *copy* of img which we'll draw selectImage on top of
418 BufferedImage lcimg = buildLocalImage(selectImage);
419 g.drawImage(lcimg, 0, 0, this);
424 * Draw an alignment panel for printing
427 * Graphics object to draw with
429 * start residue of print area
431 * end residue of print area
433 * start sequence of print area
435 * end sequence of print area
437 public void drawPanelForPrint(Graphics g1, int startRes, int endRes,
438 int startSeq, int endSeq)
440 BufferedImage selectImage = drawSelectionGroup(startRes, endRes,
442 drawPanel(g1, startRes, endRes, startSeq, endSeq, 0);
443 ((Graphics2D) g1).setComposite(
444 AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
445 g1.drawImage(selectImage, 0, 0, this);
449 * Make a local image by combining the cached image img
452 private BufferedImage buildLocalImage(BufferedImage selectImage)
454 // clone the cached image
455 BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(),
457 Graphics2D g2d = lcimg.createGraphics();
458 g2d.drawImage(img, 0, 0, null);
460 // overlay selection group on lcimg
461 if (selectImage != null)
464 AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
465 g2d.drawImage(selectImage, 0, 0, this);
473 * Set up a buffered image of the correct height and size for the sequence canvas
475 private BufferedImage setupImage()
477 BufferedImage lcimg = null;
479 int width = getWidth();
480 int height = getHeight();
482 width -= (width % charWidth);
483 height -= (height % charHeight);
485 if ((width < 1) || (height < 1))
492 lcimg = new BufferedImage(width, height,
493 BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
494 } catch (OutOfMemoryError er)
498 "Group image OutOfMemory Redraw Error.\n" + er);
499 new OOMWarning("Creating alignment image for display", er);
513 * @return DOCUMENT ME!
515 public int getWrappedCanvasWidth(int cwidth)
517 FontMetrics fm = getFontMetrics(av.getFont());
522 if (av.getScaleRightWrapped())
524 LABEL_EAST = fm.stringWidth(getMask());
527 if (av.getScaleLeftWrapped())
529 LABEL_WEST = fm.stringWidth(getMask());
532 return (cwidth - LABEL_EAST - LABEL_WEST) / charWidth;
536 * Generates a string of zeroes.
545 for (int i = 0; i < av.getAlignment().getHeight(); i++)
547 tmp = av.getAlignment().getSequenceAt(i).getEnd();
554 for (int i = maxWidth; i > 0; i /= 10)
568 * @param canvasHeight
573 public void drawWrappedPanel(Graphics g, int canvasWidth,
574 int canvasHeight, int startRes)
577 AlignmentI al = av.getAlignment();
579 FontMetrics fm = getFontMetrics(av.getFont());
584 if (av.getScaleRightWrapped())
586 LABEL_EAST = fm.stringWidth(getMask());
589 if (av.getScaleLeftWrapped())
591 LABEL_WEST = fm.stringWidth(getMask());
594 int hgap = charHeight;
595 if (av.getScaleAboveWrapped())
600 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
601 int cHeight = av.getAlignment().getHeight() * charHeight;
603 av.setWrappedWidth(cWidth);
605 av.getRanges().setViewportStartAndWidth(startRes, cWidth);
609 int maxwidth = av.getAlignment().getWidth();
611 if (av.hasHiddenColumns())
613 maxwidth = av.getAlignment().getHiddenColumns()
614 .findColumnPosition(maxwidth);
617 while ((ypos <= canvasHeight) && (startRes < maxwidth))
619 endx = startRes + cWidth - 1;
626 g.setFont(av.getFont());
627 g.setColor(Color.black);
629 if (av.getScaleLeftWrapped())
631 drawWestScale(g, startRes, endx, ypos);
634 if (av.getScaleRightWrapped())
636 g.translate(canvasWidth - LABEL_EAST, 0);
637 drawEastScale(g, startRes, endx, ypos);
638 g.translate(-(canvasWidth - LABEL_EAST), 0);
641 g.translate(LABEL_WEST, 0);
643 if (av.getScaleAboveWrapped())
645 drawNorthScale(g, startRes, endx, ypos);
648 if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
650 g.setColor(Color.blue);
652 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
653 List<Integer> positions = hidden.findHiddenRegionPositions();
654 for (int pos : positions)
656 res = pos - startRes;
658 if (res < 0 || res > endx - startRes)
664 new int[] { res * charWidth - charHeight / 4,
665 res * charWidth + charHeight / 4, res * charWidth },
666 new int[] { ypos - (charHeight / 2),
667 ypos - (charHeight / 2), ypos - (charHeight / 2) + 8 },
673 // When printing we have an extra clipped region,
674 // the Printable page which we need to account for here
675 Shape clip = g.getClip();
679 g.setClip(0, 0, cWidth * charWidth, canvasHeight);
683 g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
684 (int) clip.getBounds().getHeight());
687 drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
689 if (av.isShowAnnotation())
691 g.translate(0, cHeight + ypos + 3);
692 if (annotations == null)
694 annotations = new AnnotationPanel(av);
697 annotations.renderer.drawComponent(annotations, av, g, -1,
699 g.translate(0, -cHeight - ypos - 3);
702 g.translate(-LABEL_WEST, 0);
704 ypos += cHeight + getAnnotationHeight() + hgap;
711 * Draw a selection group over a wrapped alignment
713 private void drawWrappedSelection(Graphics2D g, SequenceGroup group,
715 int canvasHeight, int startRes)
717 // height gap above each panel
718 int hgap = charHeight;
719 if (av.getScaleAboveWrapped())
724 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
725 int cHeight = av.getAlignment().getHeight() * charHeight;
727 int startx = startRes;
729 int ypos = hgap; // vertical offset
730 int maxwidth = av.getAlignment().getWidth();
732 if (av.hasHiddenColumns())
734 maxwidth = av.getAlignment().getHiddenColumns()
735 .findColumnPosition(maxwidth);
738 // chop the wrapped alignment extent up into panel-sized blocks and treat
739 // each block as if it were a block from an unwrapped alignment
740 while ((ypos <= canvasHeight) && (startx < maxwidth))
742 // set end value to be start + width, or maxwidth, whichever is smaller
743 endx = startx + cWidth - 1;
750 g.translate(LABEL_WEST, 0);
752 drawUnwrappedSelection(g, group, startx, endx, 0,
753 av.getAlignment().getHeight() - 1,
756 g.translate(-LABEL_WEST, 0);
758 // update vertical offset
759 ypos += cHeight + getAnnotationHeight() + hgap;
761 // update horizontal offset
766 int getAnnotationHeight()
768 if (!av.isShowAnnotation())
773 if (annotations == null)
775 annotations = new AnnotationPanel(av);
778 return annotations.adjustPanelHeight();
782 * Draw an alignment panel for printing
785 * Graphics object to draw with
787 * start residue of print area
789 * end residue of print area
791 * start sequence of print area
793 * end sequence of print area
797 private void drawPanel(Graphics g1, int startRes, int endRes,
798 int startSeq, int endSeq, int offset)
801 if (!av.hasHiddenColumns())
803 draw(g1, startRes, endRes, startSeq, endSeq, offset);
808 int blockStart = startRes;
809 int blockEnd = endRes;
811 for (int[] region : av.getAlignment().getHiddenColumns()
812 .getHiddenColumnsCopy())
814 int hideStart = region[0];
815 int hideEnd = region[1];
817 if (hideStart <= blockStart)
819 blockStart += (hideEnd - hideStart) + 1;
823 blockEnd = hideStart - 1;
825 g1.translate(screenY * charWidth, 0);
827 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
829 if (av.getShowHiddenMarkers())
831 g1.setColor(Color.blue);
833 g1.drawLine((blockEnd - blockStart + 1) * charWidth - 1,
834 0 + offset, (blockEnd - blockStart + 1) * charWidth - 1,
835 (endSeq - startSeq + 1) * charHeight + offset);
838 g1.translate(-screenY * charWidth, 0);
839 screenY += blockEnd - blockStart + 1;
840 blockStart = hideEnd + 1;
842 if (screenY > (endRes - startRes))
844 // already rendered last block
849 if (screenY <= (endRes - startRes))
851 // remaining visible region to render
852 blockEnd = blockStart + (endRes - startRes) - screenY;
853 g1.translate(screenY * charWidth, 0);
854 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
856 g1.translate(-screenY * charWidth, 0);
862 private void draw(Graphics g, int startRes, int endRes, int startSeq,
863 int endSeq, int offset)
865 g.setFont(av.getFont());
866 seqRdr.prepare(g, av.isRenderGaps());
870 // / First draw the sequences
871 // ///////////////////////////
872 for (int i = startSeq; i <= endSeq; i++)
874 nextSeq = av.getAlignment().getSequenceAt(i);
877 // occasionally, a race condition occurs such that the alignment row is
881 seqRdr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
882 startRes, endRes, offset + ((i - startSeq) * charHeight));
884 if (av.isShowSequenceFeatures())
886 fr.drawSequence(g, nextSeq, startRes, endRes, offset
887 + ((i - startSeq) * charHeight), false);
890 // / Highlight search Results once all sequences have been drawn
891 // ////////////////////////////////////////////////////////
892 if (av.hasSearchResults())
894 int[] visibleResults = av.getSearchResults().getResults(nextSeq,
896 if (visibleResults != null)
898 for (int r = 0; r < visibleResults.length; r += 2)
900 seqRdr.drawHighlightedText(nextSeq, visibleResults[r],
901 visibleResults[r + 1], (visibleResults[r] - startRes)
903 + ((i - startSeq) * charHeight));
908 if (av.cursorMode && cursorY == i && cursorX >= startRes
909 && cursorX <= endRes)
911 seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
912 offset + ((i - startSeq) * charHeight));
916 if (av.getSelectionGroup() != null
917 || av.getAlignment().getGroups().size() > 0)
919 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
924 void drawGroupsBoundaries(Graphics g1, int startRes, int endRes,
925 int startSeq, int endSeq, int offset)
927 Graphics2D g = (Graphics2D) g1;
929 // ///////////////////////////////////
930 // Now outline any areas if necessary
931 // ///////////////////////////////////
933 SequenceGroup group = null;
936 if (av.getAlignment().getGroups().size() > 0)
938 group = av.getAlignment().getGroups().get(0);
944 g.setStroke(new BasicStroke());
945 g.setColor(group.getOutlineColour());
949 drawPartialGroupOutline(g, group, startRes, endRes, startSeq,
954 g.setStroke(new BasicStroke());
956 if (groupIndex >= av.getAlignment().getGroups().size())
961 group = av.getAlignment().getGroups().get(groupIndex);
963 } while (groupIndex < av.getAlignment().getGroups().size());
971 * Draw the selection group as a separate image and overlay
973 private BufferedImage drawSelectionGroup(int startRes, int endRes,
974 int startSeq, int endSeq)
976 // get a new image of the correct size
977 BufferedImage selectionImage = setupImage();
979 if (selectionImage == null)
984 SequenceGroup group = av.getSelectionGroup();
991 // set up drawing colour
992 Graphics2D g = (Graphics2D) selectionImage.getGraphics();
994 // set background to transparent
995 g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
996 g.fillRect(0, 0, selectionImage.getWidth(), selectionImage.getHeight());
998 // set up foreground to draw red dashed line
999 g.setComposite(AlphaComposite.Src);
1000 g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
1001 BasicStroke.JOIN_ROUND, 3f, new float[]
1003 g.setColor(Color.RED);
1005 if (!av.getWrapAlignment())
1007 drawUnwrappedSelection(g, group, startRes, endRes, startSeq, endSeq,
1012 drawWrappedSelection(g, group, getWidth(), getHeight(),
1013 av.getRanges().getStartRes());
1017 return selectionImage;
1021 * Draw a selection group over an unwrapped alignment
1022 * @param g graphics object to draw with
1023 * @param group selection group
1024 * @param startRes start residue of area to draw
1025 * @param endRes end residue of area to draw
1026 * @param startSeq start sequence of area to draw
1027 * @param endSeq end sequence of area to draw
1028 * @param offset vertical offset (used when called from wrapped alignment code)
1030 private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group,
1031 int startRes, int endRes, int startSeq, int endSeq, int offset)
1033 if (!av.hasHiddenColumns())
1035 drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq,
1040 // package into blocks of visible columns
1042 int blockStart = startRes;
1043 int blockEnd = endRes;
1045 for (int[] region : av.getAlignment().getHiddenColumns()
1046 .getHiddenColumnsCopy())
1048 int hideStart = region[0];
1049 int hideEnd = region[1];
1051 if (hideStart <= blockStart)
1053 blockStart += (hideEnd - hideStart) + 1;
1057 blockEnd = hideStart - 1;
1059 g.translate(screenY * charWidth, 0);
1060 drawPartialGroupOutline(g, group,
1061 blockStart, blockEnd, startSeq, endSeq, offset);
1063 g.translate(-screenY * charWidth, 0);
1064 screenY += blockEnd - blockStart + 1;
1065 blockStart = hideEnd + 1;
1067 if (screenY > (endRes - startRes))
1069 // already rendered last block
1074 if (screenY <= (endRes - startRes))
1076 // remaining visible region to render
1077 blockEnd = blockStart + (endRes - startRes) - screenY;
1078 g.translate(screenY * charWidth, 0);
1079 drawPartialGroupOutline(g, group,
1080 blockStart, blockEnd, startSeq, endSeq, offset);
1082 g.translate(-screenY * charWidth, 0);
1088 * Draw the selection group as a separate image and overlay
1090 private void drawPartialGroupOutline(Graphics2D g, SequenceGroup group,
1091 int startRes, int endRes, int startSeq, int endSeq,
1094 int visWidth = (endRes - startRes + 1) * charWidth;
1098 boolean inGroup = false;
1106 for (i = startSeq; i <= endSeq; i++)
1108 // position of start residue of group relative to startRes, in pixels
1109 sx = (group.getStartRes() - startRes) * charWidth;
1111 // width of group in pixels
1112 xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
1115 sy = verticalOffset + (i - startSeq) * charHeight;
1117 if (sx + xwidth < 0 || sx > visWidth)
1122 if ((sx <= (endRes - startRes) * charWidth)
1123 && group.getSequences(null)
1124 .contains(av.getAlignment().getSequenceAt(i)))
1126 if ((bottom == -1) && !group.getSequences(null)
1127 .contains(av.getAlignment().getSequenceAt(i + 1)))
1129 bottom = sy + charHeight;
1134 if (((top == -1) && (i == 0)) || !group.getSequences(null)
1135 .contains(av.getAlignment().getSequenceAt(i - 1)))
1148 // if start position is visible, draw vertical line to left of
1150 if (sx >= 0 && sx < visWidth)
1152 g.drawLine(sx, oldY, sx, sy);
1155 // if end position is visible, draw vertical line to right of
1157 if (sx + xwidth < visWidth)
1159 g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
1168 // don't let width extend beyond current block, or group extent
1170 if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
1172 xwidth = (endRes - startRes + 1) * charWidth - sx;
1175 // draw horizontal line at top of group
1178 g.drawLine(sx, top, sx + xwidth, top);
1182 // draw horizontal line at bottom of group
1185 g.drawLine(sx, bottom, sx + xwidth, bottom);
1196 sy = verticalOffset + ((i - startSeq) * charHeight);
1197 if (sx >= 0 && sx < visWidth)
1199 g.drawLine(sx, oldY, sx, sy);
1202 if (sx + xwidth < visWidth)
1204 g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
1213 if (sx + xwidth > visWidth)
1217 else if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
1219 xwidth = (endRes - startRes + 1) * charWidth;
1224 g.drawLine(sx, top, sx + xwidth, top);
1230 g.drawLine(sx, bottom - 1, sx + xwidth, bottom - 1);
1244 public void highlightSearchResults(SearchResultsI results)
1248 av.setSearchResults(results);
1254 public void propertyChange(PropertyChangeEvent evt)
1256 String eventName = evt.getPropertyName();
1258 if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
1263 else if (av.getWrapAlignment())
1265 if (eventName.equals(ViewportRanges.STARTRES))
1273 if (eventName.equals(ViewportRanges.STARTRES))
1275 // Make sure we're not trying to draw a panel
1276 // larger than the visible window
1277 ViewportRanges vpRanges = av.getRanges();
1278 scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
1279 int range = vpRanges.getEndRes() - vpRanges.getStartRes();
1280 if (scrollX > range)
1284 else if (scrollX < -range)
1290 // Both scrolling and resizing change viewport ranges: scrolling changes
1291 // both start and end points, but resize only changes end values.
1292 // Here we only want to fastpaint on a scroll, with resize using a normal
1293 // paint, so scroll events are identified as changes to the horizontal or
1294 // vertical start value.
1295 if (eventName.equals(ViewportRanges.STARTRES))
1297 // scroll - startres and endres both change
1298 fastPaint(scrollX, 0);
1300 else if (eventName.equals(ViewportRanges.STARTSEQ))
1303 fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());