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),
151 (ypos + 2) - (charHeight / 2),
152 (mpos * charWidth) + (charWidth / 2), ypos - 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,
215 (ypos + (i * charHeight)) - (charHeight / 5));
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,
267 (ypos + (i * charHeight)) - (charHeight / 5));
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)
665 { res * charWidth - charHeight / 4,
666 res * charWidth + charHeight / 4, res * charWidth },
668 { ypos - (charHeight / 2), ypos - (charHeight / 2),
669 ypos - (charHeight / 2) + 8 },
675 // When printing we have an extra clipped region,
676 // the Printable page which we need to account for here
677 Shape clip = g.getClip();
681 g.setClip(0, 0, cWidth * charWidth, canvasHeight);
685 g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
686 (int) clip.getBounds().getHeight());
689 drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
691 if (av.isShowAnnotation())
693 g.translate(0, cHeight + ypos + 3);
694 if (annotations == null)
696 annotations = new AnnotationPanel(av);
699 annotations.renderer.drawComponent(annotations, av, g, -1, startRes,
701 g.translate(0, -cHeight - ypos - 3);
704 g.translate(-LABEL_WEST, 0);
706 ypos += cHeight + getAnnotationHeight() + hgap;
713 * Draw a selection group over a wrapped alignment
715 private void drawWrappedSelection(Graphics2D g, SequenceGroup group,
717 int canvasHeight, int startRes)
719 // height gap above each panel
720 int hgap = charHeight;
721 if (av.getScaleAboveWrapped())
726 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
727 int cHeight = av.getAlignment().getHeight() * charHeight;
729 int startx = startRes;
731 int ypos = hgap; // vertical offset
732 int maxwidth = av.getAlignment().getWidth();
734 if (av.hasHiddenColumns())
736 maxwidth = av.getAlignment().getHiddenColumns()
737 .findColumnPosition(maxwidth);
740 // chop the wrapped alignment extent up into panel-sized blocks and treat
741 // each block as if it were a block from an unwrapped alignment
742 while ((ypos <= canvasHeight) && (startx < maxwidth))
744 // set end value to be start + width, or maxwidth, whichever is smaller
745 endx = startx + cWidth - 1;
752 g.translate(LABEL_WEST, 0);
754 drawUnwrappedSelection(g, group, startx, endx, 0,
755 av.getAlignment().getHeight() - 1,
758 g.translate(-LABEL_WEST, 0);
760 // update vertical offset
761 ypos += cHeight + getAnnotationHeight() + hgap;
763 // update horizontal offset
768 int getAnnotationHeight()
770 if (!av.isShowAnnotation())
775 if (annotations == null)
777 annotations = new AnnotationPanel(av);
780 return annotations.adjustPanelHeight();
784 * Draw an alignment panel for printing
787 * Graphics object to draw with
789 * start residue of print area
791 * end residue of print area
793 * start sequence of print area
795 * end sequence of print area
799 private void drawPanel(Graphics g1, int startRes, int endRes,
800 int startSeq, int endSeq, int offset)
803 if (!av.hasHiddenColumns())
805 draw(g1, startRes, endRes, startSeq, endSeq, offset);
810 int blockStart = startRes;
811 int blockEnd = endRes;
813 for (int[] region : av.getAlignment().getHiddenColumns()
814 .getHiddenColumnsCopy())
816 int hideStart = region[0];
817 int hideEnd = region[1];
819 if (hideStart <= blockStart)
821 blockStart += (hideEnd - hideStart) + 1;
825 blockEnd = hideStart - 1;
827 g1.translate(screenY * charWidth, 0);
829 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
831 if (av.getShowHiddenMarkers())
833 g1.setColor(Color.blue);
835 g1.drawLine((blockEnd - blockStart + 1) * charWidth - 1,
836 0 + offset, (blockEnd - blockStart + 1) * charWidth - 1,
837 (endSeq - startSeq + 1) * charHeight + offset);
840 g1.translate(-screenY * charWidth, 0);
841 screenY += blockEnd - blockStart + 1;
842 blockStart = hideEnd + 1;
844 if (screenY > (endRes - startRes))
846 // already rendered last block
851 if (screenY <= (endRes - startRes))
853 // remaining visible region to render
854 blockEnd = blockStart + (endRes - startRes) - screenY;
855 g1.translate(screenY * charWidth, 0);
856 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
858 g1.translate(-screenY * charWidth, 0);
864 private void draw(Graphics g, int startRes, int endRes, int startSeq,
865 int endSeq, int offset)
867 g.setFont(av.getFont());
868 seqRdr.prepare(g, av.isRenderGaps());
872 // / First draw the sequences
873 // ///////////////////////////
874 for (int i = startSeq; i <= endSeq; i++)
876 nextSeq = av.getAlignment().getSequenceAt(i);
879 // occasionally, a race condition occurs such that the alignment row is
883 seqRdr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
884 startRes, endRes, offset + ((i - startSeq) * charHeight));
886 if (av.isShowSequenceFeatures())
888 fr.drawSequence(g, nextSeq, startRes, endRes,
889 offset + ((i - startSeq) * charHeight), false);
892 // / Highlight search Results once all sequences have been drawn
893 // ////////////////////////////////////////////////////////
894 if (av.hasSearchResults())
896 int[] visibleResults = av.getSearchResults().getResults(nextSeq,
898 if (visibleResults != null)
900 for (int r = 0; r < visibleResults.length; r += 2)
902 seqRdr.drawHighlightedText(nextSeq, visibleResults[r],
903 visibleResults[r + 1], (visibleResults[r] - startRes)
905 + ((i - startSeq) * charHeight));
910 if (av.cursorMode && cursorY == i && cursorX >= startRes
911 && cursorX <= endRes)
913 seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
914 offset + ((i - startSeq) * charHeight));
918 if (av.getSelectionGroup() != null
919 || av.getAlignment().getGroups().size() > 0)
921 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
926 void drawGroupsBoundaries(Graphics g1, int startRes, int endRes,
927 int startSeq, int endSeq, int offset)
929 Graphics2D g = (Graphics2D) g1;
931 // ///////////////////////////////////
932 // Now outline any areas if necessary
933 // ///////////////////////////////////
935 SequenceGroup group = null;
938 if (av.getAlignment().getGroups().size() > 0)
940 group = av.getAlignment().getGroups().get(0);
946 g.setStroke(new BasicStroke());
947 g.setColor(group.getOutlineColour());
951 drawPartialGroupOutline(g, group, startRes, endRes, startSeq,
956 g.setStroke(new BasicStroke());
958 if (groupIndex >= av.getAlignment().getGroups().size())
963 group = av.getAlignment().getGroups().get(groupIndex);
965 } while (groupIndex < av.getAlignment().getGroups().size());
973 * Draw the selection group as a separate image and overlay
975 private BufferedImage drawSelectionGroup(int startRes, int endRes,
976 int startSeq, int endSeq)
978 // get a new image of the correct size
979 BufferedImage selectionImage = setupImage();
981 if (selectionImage == null)
986 SequenceGroup group = av.getSelectionGroup();
993 // set up drawing colour
994 Graphics2D g = (Graphics2D) selectionImage.getGraphics();
996 // set background to transparent
997 g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
998 g.fillRect(0, 0, selectionImage.getWidth(), selectionImage.getHeight());
1000 // set up foreground to draw red dashed line
1001 g.setComposite(AlphaComposite.Src);
1002 g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
1003 BasicStroke.JOIN_ROUND, 3f, new float[]
1005 g.setColor(Color.RED);
1007 if (!av.getWrapAlignment())
1009 drawUnwrappedSelection(g, group, startRes, endRes, startSeq, endSeq,
1014 drawWrappedSelection(g, group, getWidth(), getHeight(),
1015 av.getRanges().getStartRes());
1019 return selectionImage;
1023 * Draw a selection group over an unwrapped alignment
1024 * @param g graphics object to draw with
1025 * @param group selection group
1026 * @param startRes start residue of area to draw
1027 * @param endRes end residue of area to draw
1028 * @param startSeq start sequence of area to draw
1029 * @param endSeq end sequence of area to draw
1030 * @param offset vertical offset (used when called from wrapped alignment code)
1032 private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group,
1033 int startRes, int endRes, int startSeq, int endSeq, int offset)
1035 if (!av.hasHiddenColumns())
1037 drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq,
1042 // package into blocks of visible columns
1044 int blockStart = startRes;
1045 int blockEnd = endRes;
1047 for (int[] region : av.getAlignment().getHiddenColumns()
1048 .getHiddenColumnsCopy())
1050 int hideStart = region[0];
1051 int hideEnd = region[1];
1053 if (hideStart <= blockStart)
1055 blockStart += (hideEnd - hideStart) + 1;
1059 blockEnd = hideStart - 1;
1061 g.translate(screenY * charWidth, 0);
1062 drawPartialGroupOutline(g, group,
1063 blockStart, blockEnd, startSeq, endSeq, offset);
1065 g.translate(-screenY * charWidth, 0);
1066 screenY += blockEnd - blockStart + 1;
1067 blockStart = hideEnd + 1;
1069 if (screenY > (endRes - startRes))
1071 // already rendered last block
1076 if (screenY <= (endRes - startRes))
1078 // remaining visible region to render
1079 blockEnd = blockStart + (endRes - startRes) - screenY;
1080 g.translate(screenY * charWidth, 0);
1081 drawPartialGroupOutline(g, group,
1082 blockStart, blockEnd, startSeq, endSeq, offset);
1084 g.translate(-screenY * charWidth, 0);
1090 * Draw the selection group as a separate image and overlay
1092 private void drawPartialGroupOutline(Graphics2D g, SequenceGroup group,
1093 int startRes, int endRes, int startSeq, int endSeq,
1096 int visWidth = (endRes - startRes + 1) * charWidth;
1100 boolean inGroup = false;
1108 for (i = startSeq; i <= endSeq; i++)
1110 // position of start residue of group relative to startRes, in pixels
1111 sx = (group.getStartRes() - startRes) * charWidth;
1113 // width of group in pixels
1114 xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
1117 sy = verticalOffset + (i - startSeq) * charHeight;
1119 if (sx + xwidth < 0 || sx > visWidth)
1124 if ((sx <= (endRes - startRes) * charWidth)
1125 && group.getSequences(null)
1126 .contains(av.getAlignment().getSequenceAt(i)))
1128 if ((bottom == -1) && !group.getSequences(null)
1129 .contains(av.getAlignment().getSequenceAt(i + 1)))
1131 bottom = sy + charHeight;
1136 if (((top == -1) && (i == 0)) || !group.getSequences(null)
1137 .contains(av.getAlignment().getSequenceAt(i - 1)))
1150 // if start position is visible, draw vertical line to left of
1152 if (sx >= 0 && sx < visWidth)
1154 g.drawLine(sx, oldY, sx, sy);
1157 // if end position is visible, draw vertical line to right of
1159 if (sx + xwidth < visWidth)
1161 g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
1170 // don't let width extend beyond current block, or group extent
1172 if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
1174 xwidth = (endRes - startRes + 1) * charWidth - sx;
1177 // draw horizontal line at top of group
1180 g.drawLine(sx, top, sx + xwidth, top);
1184 // draw horizontal line at bottom of group
1187 g.drawLine(sx, bottom, sx + xwidth, bottom);
1198 sy = verticalOffset + ((i - startSeq) * charHeight);
1199 if (sx >= 0 && sx < visWidth)
1201 g.drawLine(sx, oldY, sx, sy);
1204 if (sx + xwidth < visWidth)
1206 g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
1215 if (sx + xwidth > visWidth)
1219 else if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
1221 xwidth = (endRes - startRes + 1) * charWidth;
1226 g.drawLine(sx, top, sx + xwidth, top);
1232 g.drawLine(sx, bottom - 1, sx + xwidth, bottom - 1);
1246 public void highlightSearchResults(SearchResultsI results)
1250 av.setSearchResults(results);
1256 public void propertyChange(PropertyChangeEvent evt)
1258 String eventName = evt.getPropertyName();
1260 if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
1265 else if (av.getWrapAlignment())
1267 if (eventName.equals(ViewportRanges.STARTRES))
1275 if (eventName.equals(ViewportRanges.STARTRES))
1277 // Make sure we're not trying to draw a panel
1278 // larger than the visible window
1279 ViewportRanges vpRanges = av.getRanges();
1280 scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
1281 int range = vpRanges.getEndRes() - vpRanges.getStartRes();
1282 if (scrollX > range)
1286 else if (scrollX < -range)
1292 // Both scrolling and resizing change viewport ranges: scrolling changes
1293 // both start and end points, but resize only changes end values.
1294 // Here we only want to fastpaint on a scroll, with resize using a normal
1295 // paint, so scroll events are identified as changes to the horizontal or
1296 // vertical start value.
1297 if (eventName.equals(ViewportRanges.STARTRES))
1299 // scroll - startres and endres both change
1300 fastPaint(scrollX, 0);
1302 else if (eventName.equals(ViewportRanges.STARTSEQ))
1305 fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());