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;
83 * Creates a new SeqCanvas object.
88 public SeqCanvas(AlignmentPanel ap)
92 fr = new FeatureRenderer(ap);
93 seqRdr = new SequenceRenderer(av);
94 setLayout(new BorderLayout());
95 PaintRefresher.Register(this, av.getSequenceSetId());
96 setBackground(Color.white);
98 av.getRanges().addPropertyChangeListener(this);
101 public SequenceRenderer getSequenceRenderer()
106 public FeatureRenderer getFeatureRenderer()
111 private void updateViewport()
113 charHeight = av.getCharHeight();
114 charWidth = av.getCharWidth();
129 private void drawNorthScale(Graphics g, int startx, int endx, int ypos)
132 for (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx,
135 int mpos = mark.column; // (i - startx - 1)
140 String mstring = mark.text;
146 g.drawString(mstring, mpos * charWidth, ypos - (charHeight / 2));
148 g.drawLine((mpos * charWidth) + (charWidth / 2), (ypos + 2)
149 - (charHeight / 2), (mpos * charWidth) + (charWidth / 2),
167 void drawWestScale(Graphics g, int startx, int endx, int ypos)
169 FontMetrics fm = getFontMetrics(av.getFont());
172 if (av.hasHiddenColumns())
174 startx = av.getAlignment().getHiddenColumns()
175 .adjustForHiddenColumns(startx);
176 endx = av.getAlignment().getHiddenColumns()
177 .adjustForHiddenColumns(endx);
180 int maxwidth = av.getAlignment().getWidth();
181 if (av.hasHiddenColumns())
183 maxwidth = av.getAlignment().getHiddenColumns()
184 .findColumnPosition(maxwidth) - 1;
188 for (int i = 0; i < av.getAlignment().getHeight(); i++)
190 SequenceI seq = av.getAlignment().getSequenceAt(i);
196 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
203 value = av.getAlignment().getSequenceAt(i).findPosition(index);
210 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))
212 g.drawString(value + "", x, (ypos + (i * charHeight))
230 void drawEastScale(Graphics g, int startx, int endx, int ypos)
234 if (av.hasHiddenColumns())
236 endx = av.getAlignment().getHiddenColumns()
237 .adjustForHiddenColumns(endx);
242 for (int i = 0; i < av.getAlignment().getHeight(); i++)
244 seq = av.getAlignment().getSequenceAt(i);
248 while (index > startx)
250 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
257 value = seq.findPosition(index);
264 g.drawString(String.valueOf(value), 0, (ypos + (i * charHeight))
272 * need to make this thread safe move alignment rendering in response to
278 * shift up or down in repaint
280 public void fastPaint(int horizontal, int vertical)
282 if (fastpainting || gg == null)
290 ViewportRanges ranges = av.getRanges();
291 int sr = ranges.getStartRes();
292 int er = ranges.getEndRes();
293 int ss = ranges.getStartSeq();
294 int es = ranges.getEndSeq();
298 gg.copyArea(horizontal * charWidth, vertical * charHeight,
299 img.getWidth(), img.getHeight(), -horizontal * charWidth,
300 -vertical * charHeight);
302 if (horizontal > 0) // scrollbar pulled right, image to the left
304 transX = (er - sr - horizontal) * charWidth;
305 sr = er - horizontal;
307 else if (horizontal < 0)
309 er = sr - horizontal;
311 else if (vertical > 0) // scroll down
315 if (ss < ranges.getStartSeq())
316 { // ie scrolling too fast, more than a page at a time
317 ss = ranges.getStartSeq();
321 transY = img.getHeight() - ((vertical + 1) * charHeight);
324 else if (vertical < 0)
328 if (es > ranges.getEndSeq())
330 es = ranges.getEndSeq();
334 gg.translate(transX, transY);
335 drawPanel(gg, sr, er, ss, es, 0);
336 gg.translate(-transX, -transY);
339 fastpainting = false;
343 * Definitions of startx and endx (hopefully): SMJS This is what I'm working
344 * towards! startx is the first residue (starting at 0) to display. endx is
345 * the last residue to display (starting at 0). starty is the first sequence
346 * to display (starting at 0). endy is the last sequence to display (starting
347 * at 0). NOTE 1: The av limits are set in setFont in this class and in the
348 * adjustment listener in SeqPanel when the scrollbars move.
351 // Set this to false to force a full panel paint
353 public void paintComponent(Graphics g)
355 super.paintComponent(g);
359 ViewportRanges ranges = av.getRanges();
361 int width = getWidth();
362 int height = getHeight();
364 width -= (width % charWidth);
365 height -= (height % charHeight);
367 // selectImage is the selection group outline image
368 BufferedImage selectImage = drawSelectionGroup(
369 ranges.getStartRes(), ranges.getEndRes(),
370 ranges.getStartSeq(), ranges.getEndSeq());
372 if (fastPaint || (getVisibleRect().width != g.getClipBounds().width)
373 || (getVisibleRect().height != g.getClipBounds().height))
375 BufferedImage lcimg = buildLocalImage(selectImage);
376 g.drawImage(lcimg, 0, 0, this);
379 else if ((width > 0) && (height > 0))
381 // img is a cached version of the last view we drew, if any
382 // if we have no img or the size has changed, make a new one
383 if (img == null || width != img.getWidth()
384 || height != img.getHeight())
391 gg = (Graphics2D) img.getGraphics();
392 gg.setFont(av.getFont());
397 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
398 RenderingHints.VALUE_ANTIALIAS_ON);
401 gg.setColor(Color.white);
402 gg.fillRect(0, 0, img.getWidth(), img.getHeight());
404 if (av.getWrapAlignment())
406 drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
410 drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
411 ranges.getStartSeq(), ranges.getEndSeq(), 0);
414 // lcimg is a local *copy* of img which we'll draw selectImage on top of
415 BufferedImage lcimg = buildLocalImage(selectImage);
416 g.drawImage(lcimg, 0, 0, this);
421 * Make a local image by combining the cached image img
424 private BufferedImage buildLocalImage(BufferedImage selectImage)
426 // clone the cached image
427 BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(),
429 Graphics2D g2d = lcimg.createGraphics();
430 g2d.drawImage(img, 0, 0, null);
432 // overlay selection group on lcimg
433 if (selectImage != null)
436 AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
437 g2d.drawImage(selectImage, 0, 0, this);
444 private void paintSeqGroup()
450 private BufferedImage setupImage()
452 BufferedImage lcimg = null;
454 int width = getWidth();
455 int height = getHeight();
457 width -= (width % charWidth);
458 height -= (height % charHeight);
460 if ((width < 1) || (height < 1))
467 lcimg = new BufferedImage(width, height,
468 BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
469 } catch (OutOfMemoryError er)
473 "Selection Group image OutOfMemory Redraw Error.\n" + er);
474 new OOMWarning("Creating alignment image for display", er);
488 * @return DOCUMENT ME!
490 public int getWrappedCanvasWidth(int cwidth)
492 FontMetrics fm = getFontMetrics(av.getFont());
497 if (av.getScaleRightWrapped())
499 LABEL_EAST = fm.stringWidth(getMask());
502 if (av.getScaleLeftWrapped())
504 LABEL_WEST = fm.stringWidth(getMask());
507 return (cwidth - LABEL_EAST - LABEL_WEST) / charWidth;
511 * Generates a string of zeroes.
520 for (int i = 0; i < av.getAlignment().getHeight(); i++)
522 tmp = av.getAlignment().getSequenceAt(i).getEnd();
529 for (int i = maxWidth; i > 0; i /= 10)
543 * @param canvasHeight
548 public void drawWrappedPanel(Graphics g, int canvasWidth,
549 int canvasHeight, int startRes)
552 AlignmentI al = av.getAlignment();
554 FontMetrics fm = getFontMetrics(av.getFont());
559 if (av.getScaleRightWrapped())
561 LABEL_EAST = fm.stringWidth(getMask());
564 if (av.getScaleLeftWrapped())
566 LABEL_WEST = fm.stringWidth(getMask());
569 int hgap = charHeight;
570 if (av.getScaleAboveWrapped())
575 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
576 int cHeight = av.getAlignment().getHeight() * charHeight;
578 av.setWrappedWidth(cWidth);
580 av.getRanges().setViewportStartAndWidth(startRes, cWidth);
584 int maxwidth = av.getAlignment().getWidth();
586 if (av.hasHiddenColumns())
588 maxwidth = av.getAlignment().getHiddenColumns()
589 .findColumnPosition(maxwidth);
592 while ((ypos <= canvasHeight) && (startRes < maxwidth))
594 endx = startRes + cWidth - 1;
601 g.setFont(av.getFont());
602 g.setColor(Color.black);
604 if (av.getScaleLeftWrapped())
606 drawWestScale(g, startRes, endx, ypos);
609 if (av.getScaleRightWrapped())
611 g.translate(canvasWidth - LABEL_EAST, 0);
612 drawEastScale(g, startRes, endx, ypos);
613 g.translate(-(canvasWidth - LABEL_EAST), 0);
616 g.translate(LABEL_WEST, 0);
618 if (av.getScaleAboveWrapped())
620 drawNorthScale(g, startRes, endx, ypos);
623 if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
625 g.setColor(Color.blue);
627 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
628 List<Integer> positions = hidden.findHiddenRegionPositions();
629 for (int pos : positions)
631 res = pos - startRes;
633 if (res < 0 || res > endx - startRes)
639 new int[] { res * charWidth - charHeight / 4,
640 res * charWidth + charHeight / 4, res * charWidth },
641 new int[] { ypos - (charHeight / 2),
642 ypos - (charHeight / 2), ypos - (charHeight / 2) + 8 },
648 // When printing we have an extra clipped region,
649 // the Printable page which we need to account for here
650 Shape clip = g.getClip();
654 g.setClip(0, 0, cWidth * charWidth, canvasHeight);
658 g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
659 (int) clip.getBounds().getHeight());
662 drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
664 if (av.isShowAnnotation())
666 g.translate(0, cHeight + ypos + 3);
667 if (annotations == null)
669 annotations = new AnnotationPanel(av);
672 annotations.renderer.drawComponent(annotations, av, g, -1,
674 g.translate(0, -cHeight - ypos - 3);
677 g.translate(-LABEL_WEST, 0);
679 ypos += cHeight + getAnnotationHeight() + hgap;
686 * Draw a selection group over a wrapped alignment
688 private void drawWrappedSelection(Graphics2D g, SequenceGroup group,
690 int canvasHeight, int startRes)
692 // height gap above each panel
693 int hgap = charHeight;
694 if (av.getScaleAboveWrapped())
699 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
700 int cHeight = av.getAlignment().getHeight() * charHeight;
702 int startx = startRes;
704 int ypos = hgap; // vertical offset
705 int maxwidth = av.getAlignment().getWidth();
707 if (av.hasHiddenColumns())
709 maxwidth = av.getAlignment().getHiddenColumns()
710 .findColumnPosition(maxwidth);
713 // chop the wrapped alignment extent up into panel-sized blocks and treat
714 // each block as if it were a block from an unwrapped alignment
715 while ((ypos <= canvasHeight) && (startx < maxwidth))
717 // set end value to be start + width, or maxwidth, whichever is smaller
718 endx = startx + cWidth - 1;
725 g.translate(LABEL_WEST, 0);
727 drawUnwrappedSelection(g, group, startx, endx, 0,
728 av.getAlignment().getHeight() - 1,
731 g.translate(-LABEL_WEST, 0);
733 // update vertical offset
734 ypos += cHeight + getAnnotationHeight() + hgap;
736 // update horizontal offset
741 AnnotationPanel annotations;
743 int getAnnotationHeight()
745 if (!av.isShowAnnotation())
750 if (annotations == null)
752 annotations = new AnnotationPanel(av);
755 return annotations.adjustPanelHeight();
774 public void drawPanel(Graphics g1, int startRes, int endRes,
775 int startSeq, int endSeq, int offset)
778 if (!av.hasHiddenColumns())
780 draw(g1, startRes, endRes, startSeq, endSeq, offset);
785 int blockStart = startRes;
786 int blockEnd = endRes;
788 for (int[] region : av.getAlignment().getHiddenColumns()
789 .getHiddenColumnsCopy())
791 int hideStart = region[0];
792 int hideEnd = region[1];
794 if (hideStart <= blockStart)
796 blockStart += (hideEnd - hideStart) + 1;
800 blockEnd = hideStart - 1;
802 g1.translate(screenY * charWidth, 0);
804 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
806 if (av.getShowHiddenMarkers())
808 g1.setColor(Color.blue);
810 g1.drawLine((blockEnd - blockStart + 1) * charWidth - 1,
811 0 + offset, (blockEnd - blockStart + 1) * charWidth - 1,
812 (endSeq - startSeq + 1) * charHeight + offset);
815 g1.translate(-screenY * charWidth, 0);
816 screenY += blockEnd - blockStart + 1;
817 blockStart = hideEnd + 1;
819 if (screenY > (endRes - startRes))
821 // already rendered last block
826 if (screenY <= (endRes - startRes))
828 // remaining visible region to render
829 blockEnd = blockStart + (endRes - startRes) - screenY;
830 g1.translate(screenY * charWidth, 0);
831 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
833 g1.translate(-screenY * charWidth, 0);
839 private void draw(Graphics g, int startRes, int endRes, int startSeq,
840 int endSeq, int offset)
842 g.setFont(av.getFont());
843 seqRdr.prepare(g, av.isRenderGaps());
847 // / First draw the sequences
848 // ///////////////////////////
849 for (int i = startSeq; i <= endSeq; i++)
851 nextSeq = av.getAlignment().getSequenceAt(i);
854 // occasionally, a race condition occurs such that the alignment row is
858 seqRdr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
859 startRes, endRes, offset + ((i - startSeq) * charHeight));
861 if (av.isShowSequenceFeatures())
863 fr.drawSequence(g, nextSeq, startRes, endRes, offset
864 + ((i - startSeq) * charHeight), false);
867 // / Highlight search Results once all sequences have been drawn
868 // ////////////////////////////////////////////////////////
869 if (av.hasSearchResults())
871 int[] visibleResults = av.getSearchResults().getResults(nextSeq,
873 if (visibleResults != null)
875 for (int r = 0; r < visibleResults.length; r += 2)
877 seqRdr.drawHighlightedText(nextSeq, visibleResults[r],
878 visibleResults[r + 1], (visibleResults[r] - startRes)
880 + ((i - startSeq) * charHeight));
885 if (av.cursorMode && cursorY == i && cursorX >= startRes
886 && cursorX <= endRes)
888 seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
889 offset + ((i - startSeq) * charHeight));
893 if (av.getSelectionGroup() != null
894 || av.getAlignment().getGroups().size() > 0)
896 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
901 void drawGroupsBoundaries(Graphics g1, int startRes, int endRes,
902 int startSeq, int endSeq, int offset)
904 Graphics2D g = (Graphics2D) g1;
906 // ///////////////////////////////////
907 // Now outline any areas if necessary
908 // ///////////////////////////////////
910 SequenceGroup group = null;
916 int visWidth = (endRes - startRes + 1) * charWidth;
918 if (av.getAlignment().getGroups().size() > 0)
920 group = av.getAlignment().getGroups().get(0);
930 boolean inGroup = false;
934 for (i = startSeq; i <= endSeq; i++)
936 // position of start residue of group relative to startRes, in pixels
937 sx = (group.getStartRes() - startRes) * charWidth;
938 sy = offset + ((i - startSeq) * charHeight);
939 // width of group in pixels
940 ex = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
943 if (sx + ex < 0 || sx > visWidth)
948 if ((sx <= (endRes - startRes) * charWidth)
949 && group.getSequences(null).contains(
950 av.getAlignment().getSequenceAt(i)))
953 && !group.getSequences(null).contains(
954 av.getAlignment().getSequenceAt(i + 1)))
956 bottom = sy + charHeight;
961 if (((top == -1) && (i == 0))
962 || !group.getSequences(null).contains(
963 av.getAlignment().getSequenceAt(i - 1)))
971 g.setStroke(new BasicStroke());
972 g.setColor(group.getOutlineColour());
979 // if start position is visible, draw vertical line to left of
981 if (sx >= 0 && sx < visWidth)
983 g.drawLine(sx, oldY, sx, sy);
986 // if end position is visible, draw vertical line to right of
988 if (sx + ex < visWidth)
990 g.drawLine(sx + ex, oldY, sx + ex, sy);
999 if (sx + ex > visWidth)
1003 else if (sx + ex >= (endRes - startRes + 1) * charWidth)
1005 ex = (endRes - startRes + 1) * charWidth;
1008 // draw horizontal line at top of group
1011 g.drawLine(sx, top, sx + ex, top);
1015 // draw horizontal line at bottom of group
1018 g.drawLine(sx, bottom, sx + ex, bottom);
1029 sy = offset + ((i - startSeq) * charHeight);
1030 if (sx >= 0 && sx < visWidth)
1032 g.drawLine(sx, oldY, sx, sy);
1035 if (sx + ex < visWidth)
1037 g.drawLine(sx + ex, oldY, sx + ex, sy);
1046 if (sx + ex > visWidth)
1050 else if (sx + ex >= (endRes - startRes + 1) * charWidth)
1052 ex = (endRes - startRes + 1) * charWidth;
1057 g.drawLine(sx, top, sx + ex, top);
1063 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
1072 g.setStroke(new BasicStroke());
1074 if (groupIndex >= av.getAlignment().getGroups().size())
1079 group = av.getAlignment().getGroups().get(groupIndex);
1081 } while (groupIndex < av.getAlignment().getGroups().size());
1089 * Draw the selection group as a separate image and overlay
1091 private BufferedImage drawSelectionGroup(int startRes, int endRes,
1092 int startSeq, int endSeq)
1094 // get a new image of the correct size
1095 BufferedImage selectionImage = setupImage();
1097 if (selectionImage == null)
1102 SequenceGroup group = av.getSelectionGroup();
1109 // set up drawing colour
1110 Graphics2D g = (Graphics2D) selectionImage.getGraphics();
1112 // set background to transparent
1113 g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
1114 g.fillRect(0, 0, selectionImage.getWidth(), selectionImage.getHeight());
1116 // set up foreground to draw red dashed line
1117 g.setComposite(AlphaComposite.Src);
1118 g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
1119 BasicStroke.JOIN_ROUND, 3f, new float[]
1121 g.setColor(Color.RED);
1123 if (!av.getWrapAlignment())
1125 drawUnwrappedSelection(g, group, startRes, endRes, startSeq, endSeq,
1130 drawWrappedSelection(g, group, getWidth(), getHeight(),
1131 av.getRanges().getStartRes());
1135 return selectionImage;
1139 * Draw a selection group over an unwrapped alignment
1140 * @param g graphics object to draw with
1141 * @param group selection group
1142 * @param startRes start residue of area to draw
1143 * @param endRes end residue of area to draw
1144 * @param startSeq start sequence of area to draw
1145 * @param endSeq end sequence of area to draw
1146 * @param offset vertical offset (used when called from wrapped alignment code)
1148 private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group,
1149 int startRes, int endRes, int startSeq, int endSeq, int offset)
1151 if (!av.hasHiddenColumns())
1153 drawSelectionGroupPart(g, group, startRes, endRes, startSeq, endSeq,
1158 // package into blocks of visible columns
1160 int blockStart = startRes;
1161 int blockEnd = endRes;
1163 for (int[] region : av.getAlignment().getHiddenColumns()
1164 .getHiddenColumnsCopy())
1166 int hideStart = region[0];
1167 int hideEnd = region[1];
1169 if (hideStart <= blockStart)
1171 blockStart += (hideEnd - hideStart) + 1;
1175 blockEnd = hideStart - 1;
1177 g.translate(screenY * charWidth, 0);
1178 drawSelectionGroupPart(g, group,
1179 blockStart, blockEnd, startSeq, endSeq, offset);
1181 g.translate(-screenY * charWidth, 0);
1182 screenY += blockEnd - blockStart + 1;
1183 blockStart = hideEnd + 1;
1185 if (screenY > (endRes - startRes))
1187 // already rendered last block
1192 if (screenY <= (endRes - startRes))
1194 // remaining visible region to render
1195 blockEnd = blockStart + (endRes - startRes) - screenY;
1196 g.translate(screenY * charWidth, 0);
1197 drawSelectionGroupPart(g, group,
1198 blockStart, blockEnd, startSeq, endSeq, offset);
1200 g.translate(-screenY * charWidth, 0);
1206 * Draw the selection group as a separate image and overlay
1208 private void drawSelectionGroupPart(Graphics2D g, SequenceGroup group,
1209 int startRes, int endRes, int startSeq, int endSeq,
1212 int visWidth = (endRes - startRes + 1) * charWidth;
1216 boolean inGroup = false;
1224 for (i = startSeq; i <= endSeq; i++)
1226 // position of start residue of group relative to startRes, in pixels
1227 sx = (group.getStartRes() - startRes) * charWidth;
1229 // width of group in pixels
1230 xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
1233 sy = verticalOffset + (i - startSeq) * charHeight;
1235 if (sx + xwidth < 0 || sx > visWidth)
1240 if ((sx <= (endRes - startRes) * charWidth)
1241 && group.getSequences(null)
1242 .contains(av.getAlignment().getSequenceAt(i)))
1244 if ((bottom == -1) && !group.getSequences(null)
1245 .contains(av.getAlignment().getSequenceAt(i + 1)))
1247 bottom = sy + charHeight;
1252 if (((top == -1) && (i == 0)) || !group.getSequences(null)
1253 .contains(av.getAlignment().getSequenceAt(i - 1)))
1266 // if start position is visible, draw vertical line to left of
1268 if (sx >= 0 && sx < visWidth)
1270 g.drawLine(sx, oldY, sx, sy);
1273 // if end position is visible, draw vertical line to right of
1275 if (sx + xwidth < visWidth)
1277 g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
1286 if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
1288 xwidth = (endRes - startRes + 1) * charWidth - sx;
1291 // draw horizontal line at top of group
1294 g.drawLine(sx, top, sx + xwidth, top);
1298 // draw horizontal line at bottom of group
1301 g.drawLine(sx, bottom, sx + xwidth, bottom);
1312 sy = verticalOffset + ((i - startSeq) * charHeight);
1313 if (sx >= 0 && sx < visWidth)
1315 g.drawLine(sx, oldY, sx, sy);
1318 if (sx + xwidth < visWidth)
1320 g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
1329 if (sx + xwidth > visWidth)
1333 else if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
1335 xwidth = (endRes - startRes + 1) * charWidth;
1340 g.drawLine(sx, top, sx + xwidth, top);
1346 g.drawLine(sx, bottom - 1, sx + xwidth, bottom - 1);
1360 public void highlightSearchResults(SearchResultsI results)
1364 av.setSearchResults(results);
1370 public void propertyChange(PropertyChangeEvent evt)
1372 String eventName = evt.getPropertyName();
1374 if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
1378 else if (av.getWrapAlignment())
1380 if (eventName.equals(ViewportRanges.STARTRES))
1388 if (eventName.equals(ViewportRanges.STARTRES))
1390 // Make sure we're not trying to draw a panel
1391 // larger than the visible window
1392 ViewportRanges vpRanges = av.getRanges();
1393 scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
1394 int range = vpRanges.getEndRes() - vpRanges.getStartRes();
1395 if (scrollX > range)
1399 else if (scrollX < -range)
1405 // Both scrolling and resizing change viewport ranges: scrolling changes
1406 // both start and end points, but resize only changes end values.
1407 // Here we only want to fastpaint on a scroll, with resize using a normal
1408 // paint, so scroll events are identified as changes to the horizontal or
1409 // vertical start value.
1410 if (eventName.equals(ViewportRanges.STARTRES))
1412 // scroll - startres and endres both change
1413 fastPaint(scrollX, 0);
1415 else if (eventName.equals(ViewportRanges.STARTSEQ))
1418 fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());