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 drawPanelForPrinting(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 * Draw a wrapped alignment panel for printing
452 * Graphics object to draw with
454 * width of drawing area
455 * @param canvasHeight
456 * height of drawing area
458 * start residue of print area
460 public void drawWrappedPanelForPrinting(Graphics g, int canvasWidth,
461 int canvasHeight, int startRes)
463 SequenceGroup group = av.getSelectionGroup();
464 BufferedImage selectImage = new BufferedImage(canvasWidth, canvasHeight,
465 BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
469 Graphics2D g2 = selectImage.createGraphics();
470 setupSelectionGroup(g2, selectImage);
471 drawWrappedSelection(g2, group, canvasWidth, canvasHeight,
473 drawWrappedPanel(g, canvasWidth, canvasHeight, startRes);
475 AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
476 g.drawImage(selectImage, 0, 0, this);
482 * Make a local image by combining the cached image img
485 private BufferedImage buildLocalImage(BufferedImage selectImage)
487 // clone the cached image
488 BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(),
490 Graphics2D g2d = lcimg.createGraphics();
491 g2d.drawImage(img, 0, 0, null);
493 // overlay selection group on lcimg
494 if (selectImage != null)
497 AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
498 g2d.drawImage(selectImage, 0, 0, this);
506 * Set up a buffered image of the correct height and size for the sequence canvas
508 private BufferedImage setupImage()
510 BufferedImage lcimg = null;
512 int width = getWidth();
513 int height = getHeight();
515 width -= (width % charWidth);
516 height -= (height % charHeight);
518 if ((width < 1) || (height < 1))
525 lcimg = new BufferedImage(width, height,
526 BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
527 } catch (OutOfMemoryError er)
531 "Group image OutOfMemory Redraw Error.\n" + er);
532 new OOMWarning("Creating alignment image for display", er);
546 * @return DOCUMENT ME!
548 public int getWrappedCanvasWidth(int cwidth)
550 FontMetrics fm = getFontMetrics(av.getFont());
555 if (av.getScaleRightWrapped())
557 LABEL_EAST = fm.stringWidth(getMask());
560 if (av.getScaleLeftWrapped())
562 LABEL_WEST = fm.stringWidth(getMask());
565 return (cwidth - LABEL_EAST - LABEL_WEST) / charWidth;
569 * Generates a string of zeroes.
578 for (int i = 0; i < av.getAlignment().getHeight(); i++)
580 tmp = av.getAlignment().getSequenceAt(i).getEnd();
587 for (int i = maxWidth; i > 0; i /= 10)
601 * @param canvasHeight
606 private void drawWrappedPanel(Graphics g, int canvasWidth,
607 int canvasHeight, int startRes)
610 AlignmentI al = av.getAlignment();
612 FontMetrics fm = getFontMetrics(av.getFont());
617 if (av.getScaleRightWrapped())
619 LABEL_EAST = fm.stringWidth(getMask());
622 if (av.getScaleLeftWrapped())
624 LABEL_WEST = fm.stringWidth(getMask());
627 int hgap = charHeight;
628 if (av.getScaleAboveWrapped())
633 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
634 int cHeight = av.getAlignment().getHeight() * charHeight;
636 av.setWrappedWidth(cWidth);
638 av.getRanges().setViewportStartAndWidth(startRes, cWidth);
642 int maxwidth = av.getAlignment().getWidth();
644 if (av.hasHiddenColumns())
646 maxwidth = av.getAlignment().getHiddenColumns()
647 .findColumnPosition(maxwidth);
650 while ((ypos <= canvasHeight) && (startRes < maxwidth))
652 endx = startRes + cWidth - 1;
659 g.setFont(av.getFont());
660 g.setColor(Color.black);
662 if (av.getScaleLeftWrapped())
664 drawWestScale(g, startRes, endx, ypos);
667 if (av.getScaleRightWrapped())
669 g.translate(canvasWidth - LABEL_EAST, 0);
670 drawEastScale(g, startRes, endx, ypos);
671 g.translate(-(canvasWidth - LABEL_EAST), 0);
674 g.translate(LABEL_WEST, 0);
676 if (av.getScaleAboveWrapped())
678 drawNorthScale(g, startRes, endx, ypos);
681 if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
683 g.setColor(Color.blue);
685 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
686 List<Integer> positions = hidden.findHiddenRegionPositions();
687 for (int pos : positions)
689 res = pos - startRes;
691 if (res < 0 || res > endx - startRes)
698 { res * charWidth - charHeight / 4,
699 res * charWidth + charHeight / 4, res * charWidth },
701 { ypos - (charHeight / 2), ypos - (charHeight / 2),
702 ypos - (charHeight / 2) + 8 },
708 // When printing we have an extra clipped region,
709 // the Printable page which we need to account for here
710 Shape clip = g.getClip();
714 g.setClip(0, 0, cWidth * charWidth, canvasHeight);
718 g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
719 (int) clip.getBounds().getHeight());
722 drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
724 if (av.isShowAnnotation())
726 g.translate(0, cHeight + ypos + 3);
727 if (annotations == null)
729 annotations = new AnnotationPanel(av);
732 annotations.renderer.drawComponent(annotations, av, g, -1, startRes,
734 g.translate(0, -cHeight - ypos - 3);
737 g.translate(-LABEL_WEST, 0);
739 ypos += cHeight + getAnnotationHeight() + hgap;
746 * Draw a selection group over a wrapped alignment
748 private void drawWrappedSelection(Graphics2D g, SequenceGroup group,
750 int canvasHeight, int startRes)
752 // height gap above each panel
753 int hgap = charHeight;
754 if (av.getScaleAboveWrapped())
759 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
760 int cHeight = av.getAlignment().getHeight() * charHeight;
762 int startx = startRes;
764 int ypos = hgap; // vertical offset
765 int maxwidth = av.getAlignment().getWidth();
767 if (av.hasHiddenColumns())
769 maxwidth = av.getAlignment().getHiddenColumns()
770 .findColumnPosition(maxwidth);
773 // chop the wrapped alignment extent up into panel-sized blocks and treat
774 // each block as if it were a block from an unwrapped alignment
775 while ((ypos <= canvasHeight) && (startx < maxwidth))
777 // set end value to be start + width, or maxwidth, whichever is smaller
778 endx = startx + cWidth - 1;
785 g.translate(LABEL_WEST, 0);
787 drawUnwrappedSelection(g, group, startx, endx, 0,
788 av.getAlignment().getHeight() - 1,
791 g.translate(-LABEL_WEST, 0);
793 // update vertical offset
794 ypos += cHeight + getAnnotationHeight() + hgap;
796 // update horizontal offset
801 int getAnnotationHeight()
803 if (!av.isShowAnnotation())
808 if (annotations == null)
810 annotations = new AnnotationPanel(av);
813 return annotations.adjustPanelHeight();
817 * Draw an alignment panel for printing
820 * Graphics object to draw with
822 * start residue of print area
824 * end residue of print area
826 * start sequence of print area
828 * end sequence of print area
832 private void drawPanel(Graphics g1, int startRes, int endRes,
833 int startSeq, int endSeq, int offset)
836 if (!av.hasHiddenColumns())
838 draw(g1, startRes, endRes, startSeq, endSeq, offset);
843 int blockStart = startRes;
844 int blockEnd = endRes;
846 for (int[] region : av.getAlignment().getHiddenColumns()
847 .getHiddenColumnsCopy())
849 int hideStart = region[0];
850 int hideEnd = region[1];
852 if (hideStart <= blockStart)
854 blockStart += (hideEnd - hideStart) + 1;
858 blockEnd = hideStart - 1;
860 g1.translate(screenY * charWidth, 0);
862 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
864 if (av.getShowHiddenMarkers())
866 g1.setColor(Color.blue);
868 g1.drawLine((blockEnd - blockStart + 1) * charWidth - 1,
869 0 + offset, (blockEnd - blockStart + 1) * charWidth - 1,
870 (endSeq - startSeq + 1) * charHeight + offset);
873 g1.translate(-screenY * charWidth, 0);
874 screenY += blockEnd - blockStart + 1;
875 blockStart = hideEnd + 1;
877 if (screenY > (endRes - startRes))
879 // already rendered last block
884 if (screenY <= (endRes - startRes))
886 // remaining visible region to render
887 blockEnd = blockStart + (endRes - startRes) - screenY;
888 g1.translate(screenY * charWidth, 0);
889 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
891 g1.translate(-screenY * charWidth, 0);
897 private void draw(Graphics g, int startRes, int endRes, int startSeq,
898 int endSeq, int offset)
900 g.setFont(av.getFont());
901 seqRdr.prepare(g, av.isRenderGaps());
905 // / First draw the sequences
906 // ///////////////////////////
907 for (int i = startSeq; i <= endSeq; i++)
909 nextSeq = av.getAlignment().getSequenceAt(i);
912 // occasionally, a race condition occurs such that the alignment row is
916 seqRdr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
917 startRes, endRes, offset + ((i - startSeq) * charHeight));
919 if (av.isShowSequenceFeatures())
921 fr.drawSequence(g, nextSeq, startRes, endRes,
922 offset + ((i - startSeq) * charHeight), false);
925 // / Highlight search Results once all sequences have been drawn
926 // ////////////////////////////////////////////////////////
927 if (av.hasSearchResults())
929 int[] visibleResults = av.getSearchResults().getResults(nextSeq,
931 if (visibleResults != null)
933 for (int r = 0; r < visibleResults.length; r += 2)
935 seqRdr.drawHighlightedText(nextSeq, visibleResults[r],
936 visibleResults[r + 1], (visibleResults[r] - startRes)
938 + ((i - startSeq) * charHeight));
943 if (av.cursorMode && cursorY == i && cursorX >= startRes
944 && cursorX <= endRes)
946 seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
947 offset + ((i - startSeq) * charHeight));
951 if (av.getSelectionGroup() != null
952 || av.getAlignment().getGroups().size() > 0)
954 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
959 void drawGroupsBoundaries(Graphics g1, int startRes, int endRes,
960 int startSeq, int endSeq, int offset)
962 Graphics2D g = (Graphics2D) g1;
964 // ///////////////////////////////////
965 // Now outline any areas if necessary
966 // ///////////////////////////////////
968 SequenceGroup group = null;
971 if (av.getAlignment().getGroups().size() > 0)
973 group = av.getAlignment().getGroups().get(0);
979 g.setStroke(new BasicStroke());
980 g.setColor(group.getOutlineColour());
984 drawPartialGroupOutline(g, group, startRes, endRes, startSeq,
989 g.setStroke(new BasicStroke());
991 if (groupIndex >= av.getAlignment().getGroups().size())
996 group = av.getAlignment().getGroups().get(groupIndex);
998 } while (groupIndex < av.getAlignment().getGroups().size());
1006 * Draw the selection group as a separate image and overlay
1008 private BufferedImage drawSelectionGroup(int startRes, int endRes,
1009 int startSeq, int endSeq)
1011 // get a new image of the correct size
1012 BufferedImage selectionImage = setupImage();
1014 if (selectionImage == null)
1019 SequenceGroup group = av.getSelectionGroup();
1026 // set up drawing colour
1027 Graphics2D g = (Graphics2D) selectionImage.getGraphics();
1029 setupSelectionGroup(g, selectionImage);
1031 if (!av.getWrapAlignment())
1033 drawUnwrappedSelection(g, group, startRes, endRes, startSeq, endSeq,
1038 drawWrappedSelection(g, group, getWidth(), getHeight(),
1039 av.getRanges().getStartRes());
1043 return selectionImage;
1047 * Set up graphics for selection group
1049 private void setupSelectionGroup(Graphics2D g,
1050 BufferedImage selectionImage)
1052 // set background to transparent
1053 g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
1054 g.fillRect(0, 0, selectionImage.getWidth(), selectionImage.getHeight());
1056 // set up foreground to draw red dashed line
1057 g.setComposite(AlphaComposite.Src);
1058 g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
1059 BasicStroke.JOIN_ROUND, 3f, new float[]
1061 g.setColor(Color.RED);
1065 * Draw a selection group over an unwrapped alignment
1066 * @param g graphics object to draw with
1067 * @param group selection group
1068 * @param startRes start residue of area to draw
1069 * @param endRes end residue of area to draw
1070 * @param startSeq start sequence of area to draw
1071 * @param endSeq end sequence of area to draw
1072 * @param offset vertical offset (used when called from wrapped alignment code)
1074 private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group,
1075 int startRes, int endRes, int startSeq, int endSeq, int offset)
1077 if (!av.hasHiddenColumns())
1079 drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq,
1084 // package into blocks of visible columns
1086 int blockStart = startRes;
1087 int blockEnd = endRes;
1089 for (int[] region : av.getAlignment().getHiddenColumns()
1090 .getHiddenColumnsCopy())
1092 int hideStart = region[0];
1093 int hideEnd = region[1];
1095 if (hideStart <= blockStart)
1097 blockStart += (hideEnd - hideStart) + 1;
1101 blockEnd = hideStart - 1;
1103 g.translate(screenY * charWidth, 0);
1104 drawPartialGroupOutline(g, group,
1105 blockStart, blockEnd, startSeq, endSeq, offset);
1107 g.translate(-screenY * charWidth, 0);
1108 screenY += blockEnd - blockStart + 1;
1109 blockStart = hideEnd + 1;
1111 if (screenY > (endRes - startRes))
1113 // already rendered last block
1118 if (screenY <= (endRes - startRes))
1120 // remaining visible region to render
1121 blockEnd = blockStart + (endRes - startRes) - screenY;
1122 g.translate(screenY * charWidth, 0);
1123 drawPartialGroupOutline(g, group,
1124 blockStart, blockEnd, startSeq, endSeq, offset);
1126 g.translate(-screenY * charWidth, 0);
1132 * Draw the selection group as a separate image and overlay
1134 private void drawPartialGroupOutline(Graphics2D g, SequenceGroup group,
1135 int startRes, int endRes, int startSeq, int endSeq,
1138 int visWidth = (endRes - startRes + 1) * charWidth;
1142 boolean inGroup = false;
1150 for (i = startSeq; i <= endSeq; i++)
1152 // position of start residue of group relative to startRes, in pixels
1153 sx = (group.getStartRes() - startRes) * charWidth;
1155 // width of group in pixels
1156 xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
1159 sy = verticalOffset + (i - startSeq) * charHeight;
1161 if (sx + xwidth < 0 || sx > visWidth)
1166 if ((sx <= (endRes - startRes) * charWidth)
1167 && group.getSequences(null)
1168 .contains(av.getAlignment().getSequenceAt(i)))
1170 if ((bottom == -1) && !group.getSequences(null)
1171 .contains(av.getAlignment().getSequenceAt(i + 1)))
1173 bottom = sy + charHeight;
1178 if (((top == -1) && (i == 0)) || !group.getSequences(null)
1179 .contains(av.getAlignment().getSequenceAt(i - 1)))
1192 // if start position is visible, draw vertical line to left of
1194 if (sx >= 0 && sx < visWidth)
1196 g.drawLine(sx, oldY, sx, sy);
1199 // if end position is visible, draw vertical line to right of
1201 if (sx + xwidth < visWidth)
1203 g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
1212 // don't let width extend beyond current block, or group extent
1214 if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
1216 xwidth = (endRes - startRes + 1) * charWidth - sx;
1219 // draw horizontal line at top of group
1222 g.drawLine(sx, top, sx + xwidth, top);
1226 // draw horizontal line at bottom of group
1229 g.drawLine(sx, bottom, sx + xwidth, bottom);
1240 sy = verticalOffset + ((i - startSeq) * charHeight);
1241 if (sx >= 0 && sx < visWidth)
1243 g.drawLine(sx, oldY, sx, sy);
1246 if (sx + xwidth < visWidth)
1248 g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
1257 if (sx + xwidth > visWidth)
1261 else if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
1263 xwidth = (endRes - startRes + 1) * charWidth;
1268 g.drawLine(sx, top, sx + xwidth, top);
1274 g.drawLine(sx, bottom - 1, sx + xwidth, bottom - 1);
1288 public void highlightSearchResults(SearchResultsI results)
1292 av.setSearchResults(results);
1298 public void propertyChange(PropertyChangeEvent evt)
1300 String eventName = evt.getPropertyName();
1302 if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
1307 else if (av.getWrapAlignment())
1309 if (eventName.equals(ViewportRanges.STARTRES))
1317 if (eventName.equals(ViewportRanges.STARTRES))
1319 // Make sure we're not trying to draw a panel
1320 // larger than the visible window
1321 ViewportRanges vpRanges = av.getRanges();
1322 scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
1323 int range = vpRanges.getEndRes() - vpRanges.getStartRes();
1324 if (scrollX > range)
1328 else if (scrollX < -range)
1334 // Both scrolling and resizing change viewport ranges: scrolling changes
1335 // both start and end points, but resize only changes end values.
1336 // Here we only want to fastpaint on a scroll, with resize using a normal
1337 // paint, so scroll events are identified as changes to the horizontal or
1338 // vertical start value.
1339 if (eventName.equals(ViewportRanges.STARTRES))
1341 // scroll - startres and endres both change
1342 fastPaint(scrollX, 0);
1344 else if (eventName.equals(ViewportRanges.STARTSEQ))
1347 fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());