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();
465 drawWrappedPanel(g, canvasWidth, canvasHeight, startRes);
469 BufferedImage selectImage = null;
472 selectImage = new BufferedImage(canvasWidth, canvasHeight,
473 BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
474 } catch (OutOfMemoryError er)
477 System.err.println("Print image OutOfMemory Error.\n" + er);
478 new OOMWarning("Creating wrapped alignment image for printing", er);
480 if (selectImage != null)
482 Graphics2D g2 = selectImage.createGraphics();
483 setupSelectionGroup(g2, selectImage);
484 drawWrappedSelection(g2, group, canvasWidth, canvasHeight,
488 AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
489 g.drawImage(selectImage, 0, 0, this);
496 * Make a local image by combining the cached image img
499 private BufferedImage buildLocalImage(BufferedImage selectImage)
501 // clone the cached image
502 BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(),
504 Graphics2D g2d = lcimg.createGraphics();
505 g2d.drawImage(img, 0, 0, null);
507 // overlay selection group on lcimg
508 if (selectImage != null)
511 AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
512 g2d.drawImage(selectImage, 0, 0, this);
520 * Set up a buffered image of the correct height and size for the sequence canvas
522 private BufferedImage setupImage()
524 BufferedImage lcimg = null;
526 int width = getWidth();
527 int height = getHeight();
529 width -= (width % charWidth);
530 height -= (height % charHeight);
532 if ((width < 1) || (height < 1))
539 lcimg = new BufferedImage(width, height,
540 BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
541 } catch (OutOfMemoryError er)
545 "Group image OutOfMemory Redraw Error.\n" + er);
546 new OOMWarning("Creating alignment image for display", er);
560 * @return DOCUMENT ME!
562 public int getWrappedCanvasWidth(int cwidth)
564 FontMetrics fm = getFontMetrics(av.getFont());
569 if (av.getScaleRightWrapped())
571 LABEL_EAST = fm.stringWidth(getMask());
574 if (av.getScaleLeftWrapped())
576 LABEL_WEST = fm.stringWidth(getMask());
579 return (cwidth - LABEL_EAST - LABEL_WEST) / charWidth;
583 * Generates a string of zeroes.
592 for (int i = 0; i < av.getAlignment().getHeight(); i++)
594 tmp = av.getAlignment().getSequenceAt(i).getEnd();
601 for (int i = maxWidth; i > 0; i /= 10)
615 * @param canvasHeight
620 private void drawWrappedPanel(Graphics g, int canvasWidth,
621 int canvasHeight, int startRes)
624 AlignmentI al = av.getAlignment();
626 FontMetrics fm = getFontMetrics(av.getFont());
631 if (av.getScaleRightWrapped())
633 LABEL_EAST = fm.stringWidth(getMask());
636 if (av.getScaleLeftWrapped())
638 LABEL_WEST = fm.stringWidth(getMask());
641 int hgap = charHeight;
642 if (av.getScaleAboveWrapped())
647 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
648 int cHeight = av.getAlignment().getHeight() * charHeight;
650 av.setWrappedWidth(cWidth);
652 av.getRanges().setViewportStartAndWidth(startRes, cWidth);
656 int maxwidth = av.getAlignment().getWidth();
658 if (av.hasHiddenColumns())
660 maxwidth = av.getAlignment().getHiddenColumns()
661 .findColumnPosition(maxwidth);
664 while ((ypos <= canvasHeight) && (startRes < maxwidth))
666 endx = startRes + cWidth - 1;
673 g.setFont(av.getFont());
674 g.setColor(Color.black);
676 if (av.getScaleLeftWrapped())
678 drawWestScale(g, startRes, endx, ypos);
681 if (av.getScaleRightWrapped())
683 g.translate(canvasWidth - LABEL_EAST, 0);
684 drawEastScale(g, startRes, endx, ypos);
685 g.translate(-(canvasWidth - LABEL_EAST), 0);
688 g.translate(LABEL_WEST, 0);
690 if (av.getScaleAboveWrapped())
692 drawNorthScale(g, startRes, endx, ypos);
695 if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
697 g.setColor(Color.blue);
699 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
700 List<Integer> positions = hidden.findHiddenRegionPositions();
701 for (int pos : positions)
703 res = pos - startRes;
705 if (res < 0 || res > endx - startRes)
712 { res * charWidth - charHeight / 4,
713 res * charWidth + charHeight / 4, res * charWidth },
715 { ypos - (charHeight / 2), ypos - (charHeight / 2),
716 ypos - (charHeight / 2) + 8 },
722 // When printing we have an extra clipped region,
723 // the Printable page which we need to account for here
724 Shape clip = g.getClip();
728 g.setClip(0, 0, cWidth * charWidth, canvasHeight);
732 g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
733 (int) clip.getBounds().getHeight());
736 drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
738 if (av.isShowAnnotation())
740 g.translate(0, cHeight + ypos + 3);
741 if (annotations == null)
743 annotations = new AnnotationPanel(av);
746 annotations.renderer.drawComponent(annotations, av, g, -1, startRes,
748 g.translate(0, -cHeight - ypos - 3);
751 g.translate(-LABEL_WEST, 0);
753 ypos += cHeight + getAnnotationHeight() + hgap;
760 * Draw a selection group over a wrapped alignment
762 private void drawWrappedSelection(Graphics2D g, SequenceGroup group,
764 int canvasHeight, int startRes)
766 // height gap above each panel
767 int hgap = charHeight;
768 if (av.getScaleAboveWrapped())
773 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
774 int cHeight = av.getAlignment().getHeight() * charHeight;
776 int startx = startRes;
778 int ypos = hgap; // vertical offset
779 int maxwidth = av.getAlignment().getWidth();
781 if (av.hasHiddenColumns())
783 maxwidth = av.getAlignment().getHiddenColumns()
784 .findColumnPosition(maxwidth);
787 // chop the wrapped alignment extent up into panel-sized blocks and treat
788 // each block as if it were a block from an unwrapped alignment
789 while ((ypos <= canvasHeight) && (startx < maxwidth))
791 // set end value to be start + width, or maxwidth, whichever is smaller
792 endx = startx + cWidth - 1;
799 g.translate(LABEL_WEST, 0);
801 drawUnwrappedSelection(g, group, startx, endx, 0,
802 av.getAlignment().getHeight() - 1,
805 g.translate(-LABEL_WEST, 0);
807 // update vertical offset
808 ypos += cHeight + getAnnotationHeight() + hgap;
810 // update horizontal offset
815 int getAnnotationHeight()
817 if (!av.isShowAnnotation())
822 if (annotations == null)
824 annotations = new AnnotationPanel(av);
827 return annotations.adjustPanelHeight();
831 * Draw an alignment panel for printing
834 * Graphics object to draw with
836 * start residue of print area
838 * end residue of print area
840 * start sequence of print area
842 * end sequence of print area
846 private void drawPanel(Graphics g1, int startRes, int endRes,
847 int startSeq, int endSeq, int offset)
850 if (!av.hasHiddenColumns())
852 draw(g1, startRes, endRes, startSeq, endSeq, offset);
857 int blockStart = startRes;
858 int blockEnd = endRes;
860 for (int[] region : av.getAlignment().getHiddenColumns()
861 .getHiddenColumnsCopy())
863 int hideStart = region[0];
864 int hideEnd = region[1];
866 if (hideStart <= blockStart)
868 blockStart += (hideEnd - hideStart) + 1;
872 blockEnd = hideStart - 1;
874 g1.translate(screenY * charWidth, 0);
876 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
878 if (av.getShowHiddenMarkers())
880 g1.setColor(Color.blue);
882 g1.drawLine((blockEnd - blockStart + 1) * charWidth - 1,
883 0 + offset, (blockEnd - blockStart + 1) * charWidth - 1,
884 (endSeq - startSeq + 1) * charHeight + offset);
887 g1.translate(-screenY * charWidth, 0);
888 screenY += blockEnd - blockStart + 1;
889 blockStart = hideEnd + 1;
891 if (screenY > (endRes - startRes))
893 // already rendered last block
898 if (screenY <= (endRes - startRes))
900 // remaining visible region to render
901 blockEnd = blockStart + (endRes - startRes) - screenY;
902 g1.translate(screenY * charWidth, 0);
903 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
905 g1.translate(-screenY * charWidth, 0);
911 private void draw(Graphics g, int startRes, int endRes, int startSeq,
912 int endSeq, int offset)
914 g.setFont(av.getFont());
915 seqRdr.prepare(g, av.isRenderGaps());
919 // / First draw the sequences
920 // ///////////////////////////
921 for (int i = startSeq; i <= endSeq; i++)
923 nextSeq = av.getAlignment().getSequenceAt(i);
926 // occasionally, a race condition occurs such that the alignment row is
930 seqRdr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
931 startRes, endRes, offset + ((i - startSeq) * charHeight));
933 if (av.isShowSequenceFeatures())
935 fr.drawSequence(g, nextSeq, startRes, endRes,
936 offset + ((i - startSeq) * charHeight), false);
939 // / Highlight search Results once all sequences have been drawn
940 // ////////////////////////////////////////////////////////
941 if (av.hasSearchResults())
943 int[] visibleResults = av.getSearchResults().getResults(nextSeq,
945 if (visibleResults != null)
947 for (int r = 0; r < visibleResults.length; r += 2)
949 seqRdr.drawHighlightedText(nextSeq, visibleResults[r],
950 visibleResults[r + 1], (visibleResults[r] - startRes)
952 + ((i - startSeq) * charHeight));
957 if (av.cursorMode && cursorY == i && cursorX >= startRes
958 && cursorX <= endRes)
960 seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
961 offset + ((i - startSeq) * charHeight));
965 if (av.getSelectionGroup() != null
966 || av.getAlignment().getGroups().size() > 0)
968 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
973 void drawGroupsBoundaries(Graphics g1, int startRes, int endRes,
974 int startSeq, int endSeq, int offset)
976 Graphics2D g = (Graphics2D) g1;
978 // ///////////////////////////////////
979 // Now outline any areas if necessary
980 // ///////////////////////////////////
982 SequenceGroup group = null;
985 if (av.getAlignment().getGroups().size() > 0)
987 group = av.getAlignment().getGroups().get(0);
993 g.setStroke(new BasicStroke());
994 g.setColor(group.getOutlineColour());
998 drawPartialGroupOutline(g, group, startRes, endRes, startSeq,
1003 g.setStroke(new BasicStroke());
1005 if (groupIndex >= av.getAlignment().getGroups().size())
1010 group = av.getAlignment().getGroups().get(groupIndex);
1012 } while (groupIndex < av.getAlignment().getGroups().size());
1020 * Draw the selection group as a separate image and overlay
1022 private BufferedImage drawSelectionGroup(int startRes, int endRes,
1023 int startSeq, int endSeq)
1025 // get a new image of the correct size
1026 BufferedImage selectionImage = setupImage();
1028 if (selectionImage == null)
1033 SequenceGroup group = av.getSelectionGroup();
1040 // set up drawing colour
1041 Graphics2D g = (Graphics2D) selectionImage.getGraphics();
1043 setupSelectionGroup(g, selectionImage);
1045 if (!av.getWrapAlignment())
1047 drawUnwrappedSelection(g, group, startRes, endRes, startSeq, endSeq,
1052 drawWrappedSelection(g, group, getWidth(), getHeight(),
1053 av.getRanges().getStartRes());
1057 return selectionImage;
1061 * Set up graphics for selection group
1063 private void setupSelectionGroup(Graphics2D g,
1064 BufferedImage selectionImage)
1066 // set background to transparent
1067 g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
1068 g.fillRect(0, 0, selectionImage.getWidth(), selectionImage.getHeight());
1070 // set up foreground to draw red dashed line
1071 g.setComposite(AlphaComposite.Src);
1072 g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
1073 BasicStroke.JOIN_ROUND, 3f, new float[]
1075 g.setColor(Color.RED);
1079 * Draw a selection group over an unwrapped alignment
1080 * @param g graphics object to draw with
1081 * @param group selection group
1082 * @param startRes start residue of area to draw
1083 * @param endRes end residue of area to draw
1084 * @param startSeq start sequence of area to draw
1085 * @param endSeq end sequence of area to draw
1086 * @param offset vertical offset (used when called from wrapped alignment code)
1088 private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group,
1089 int startRes, int endRes, int startSeq, int endSeq, int offset)
1091 if (!av.hasHiddenColumns())
1093 drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq,
1098 // package into blocks of visible columns
1100 int blockStart = startRes;
1101 int blockEnd = endRes;
1103 for (int[] region : av.getAlignment().getHiddenColumns()
1104 .getHiddenColumnsCopy())
1106 int hideStart = region[0];
1107 int hideEnd = region[1];
1109 if (hideStart <= blockStart)
1111 blockStart += (hideEnd - hideStart) + 1;
1115 blockEnd = hideStart - 1;
1117 g.translate(screenY * charWidth, 0);
1118 drawPartialGroupOutline(g, group,
1119 blockStart, blockEnd, startSeq, endSeq, offset);
1121 g.translate(-screenY * charWidth, 0);
1122 screenY += blockEnd - blockStart + 1;
1123 blockStart = hideEnd + 1;
1125 if (screenY > (endRes - startRes))
1127 // already rendered last block
1132 if (screenY <= (endRes - startRes))
1134 // remaining visible region to render
1135 blockEnd = blockStart + (endRes - startRes) - screenY;
1136 g.translate(screenY * charWidth, 0);
1137 drawPartialGroupOutline(g, group,
1138 blockStart, blockEnd, startSeq, endSeq, offset);
1140 g.translate(-screenY * charWidth, 0);
1146 * Draw the selection group as a separate image and overlay
1148 private void drawPartialGroupOutline(Graphics2D g, SequenceGroup group,
1149 int startRes, int endRes, int startSeq, int endSeq,
1152 int visWidth = (endRes - startRes + 1) * charWidth;
1156 boolean inGroup = false;
1164 for (i = startSeq; i <= endSeq; i++)
1166 // position of start residue of group relative to startRes, in pixels
1167 sx = (group.getStartRes() - startRes) * charWidth;
1169 // width of group in pixels
1170 xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
1173 sy = verticalOffset + (i - startSeq) * charHeight;
1175 if (sx + xwidth < 0 || sx > visWidth)
1180 if ((sx <= (endRes - startRes) * charWidth)
1181 && group.getSequences(null)
1182 .contains(av.getAlignment().getSequenceAt(i)))
1184 if ((bottom == -1) && !group.getSequences(null)
1185 .contains(av.getAlignment().getSequenceAt(i + 1)))
1187 bottom = sy + charHeight;
1192 if (((top == -1) && (i == 0)) || !group.getSequences(null)
1193 .contains(av.getAlignment().getSequenceAt(i - 1)))
1206 // if start position is visible, draw vertical line to left of
1208 if (sx >= 0 && sx < visWidth)
1210 g.drawLine(sx, oldY, sx, sy);
1213 // if end position is visible, draw vertical line to right of
1215 if (sx + xwidth < visWidth)
1217 g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
1226 // don't let width extend beyond current block, or group extent
1228 if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
1230 xwidth = (endRes - startRes + 1) * charWidth - sx;
1233 // draw horizontal line at top of group
1236 g.drawLine(sx, top, sx + xwidth, top);
1240 // draw horizontal line at bottom of group
1243 g.drawLine(sx, bottom, sx + xwidth, bottom);
1254 sy = verticalOffset + ((i - startSeq) * charHeight);
1255 if (sx >= 0 && sx < visWidth)
1257 g.drawLine(sx, oldY, sx, sy);
1260 if (sx + xwidth < visWidth)
1262 g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
1271 if (sx + xwidth > visWidth)
1275 else if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
1277 xwidth = (endRes - startRes + 1) * charWidth;
1282 g.drawLine(sx, top, sx + xwidth, top);
1288 g.drawLine(sx, bottom - 1, sx + xwidth, bottom - 1);
1302 public void highlightSearchResults(SearchResultsI results)
1306 av.setSearchResults(results);
1312 public void propertyChange(PropertyChangeEvent evt)
1314 String eventName = evt.getPropertyName();
1316 if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
1321 else if (av.getWrapAlignment())
1323 if (eventName.equals(ViewportRanges.STARTRES))
1331 if (eventName.equals(ViewportRanges.STARTRES))
1333 // Make sure we're not trying to draw a panel
1334 // larger than the visible window
1335 ViewportRanges vpRanges = av.getRanges();
1336 scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
1337 int range = vpRanges.getEndRes() - vpRanges.getStartRes();
1338 if (scrollX > range)
1342 else if (scrollX < -range)
1348 // Both scrolling and resizing change viewport ranges: scrolling changes
1349 // both start and end points, but resize only changes end values.
1350 // Here we only want to fastpaint on a scroll, with resize using a normal
1351 // paint, so scroll events are identified as changes to the horizontal or
1352 // vertical start value.
1353 if (eventName.equals(ViewportRanges.STARTRES))
1355 // scroll - startres and endres both change
1356 fastPaint(scrollX, 0);
1358 else if (eventName.equals(ViewportRanges.STARTSEQ))
1361 fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());