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 drawPanel(g1, startRes, endRes, startSeq, endSeq, 0);
442 BufferedImage selectImage = drawSelectionGroup(startRes, endRes,
444 if (selectImage != null)
446 ((Graphics2D) g1).setComposite(AlphaComposite
447 .getInstance(AlphaComposite.SRC_OVER));
448 g1.drawImage(selectImage, 0, 0, this);
453 * Draw a wrapped alignment panel for printing
456 * Graphics object to draw with
458 * width of drawing area
459 * @param canvasHeight
460 * height of drawing area
462 * start residue of print area
464 public void drawWrappedPanelForPrinting(Graphics g, int canvasWidth,
465 int canvasHeight, int startRes)
467 SequenceGroup group = av.getSelectionGroup();
469 drawWrappedPanel(g, canvasWidth, canvasHeight, startRes);
473 BufferedImage selectImage = null;
476 selectImage = new BufferedImage(canvasWidth, canvasHeight,
477 BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
478 } catch (OutOfMemoryError er)
481 System.err.println("Print image OutOfMemory Error.\n" + er);
482 new OOMWarning("Creating wrapped alignment image for printing", er);
484 if (selectImage != null)
486 Graphics2D g2 = selectImage.createGraphics();
487 setupSelectionGroup(g2, selectImage);
488 drawWrappedSelection(g2, group, canvasWidth, canvasHeight,
492 AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
493 g.drawImage(selectImage, 0, 0, this);
500 * Make a local image by combining the cached image img
503 private BufferedImage buildLocalImage(BufferedImage selectImage)
505 // clone the cached image
506 BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(),
508 Graphics2D g2d = lcimg.createGraphics();
509 g2d.drawImage(img, 0, 0, null);
511 // overlay selection group on lcimg
512 if (selectImage != null)
515 AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
516 g2d.drawImage(selectImage, 0, 0, this);
524 * Set up a buffered image of the correct height and size for the sequence canvas
526 private BufferedImage setupImage()
528 BufferedImage lcimg = null;
530 int width = getWidth();
531 int height = getHeight();
533 width -= (width % charWidth);
534 height -= (height % charHeight);
536 if ((width < 1) || (height < 1))
543 lcimg = new BufferedImage(width, height,
544 BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works
545 } catch (OutOfMemoryError er)
549 "Group image OutOfMemory Redraw Error.\n" + er);
550 new OOMWarning("Creating alignment image for display", er);
564 * @return DOCUMENT ME!
566 public int getWrappedCanvasWidth(int cwidth)
568 FontMetrics fm = getFontMetrics(av.getFont());
573 if (av.getScaleRightWrapped())
575 LABEL_EAST = fm.stringWidth(getMask());
578 if (av.getScaleLeftWrapped())
580 LABEL_WEST = fm.stringWidth(getMask());
583 return (cwidth - LABEL_EAST - LABEL_WEST) / charWidth;
587 * Generates a string of zeroes.
596 for (int i = 0; i < av.getAlignment().getHeight(); i++)
598 tmp = av.getAlignment().getSequenceAt(i).getEnd();
605 for (int i = maxWidth; i > 0; i /= 10)
619 * @param canvasHeight
624 private void drawWrappedPanel(Graphics g, int canvasWidth,
625 int canvasHeight, int startRes)
628 AlignmentI al = av.getAlignment();
630 FontMetrics fm = getFontMetrics(av.getFont());
635 if (av.getScaleRightWrapped())
637 LABEL_EAST = fm.stringWidth(getMask());
640 if (av.getScaleLeftWrapped())
642 LABEL_WEST = fm.stringWidth(getMask());
645 int hgap = charHeight;
646 if (av.getScaleAboveWrapped())
651 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
652 int cHeight = av.getAlignment().getHeight() * charHeight;
654 av.setWrappedWidth(cWidth);
656 av.getRanges().setViewportStartAndWidth(startRes, cWidth);
660 int maxwidth = av.getAlignment().getWidth();
662 if (av.hasHiddenColumns())
664 maxwidth = av.getAlignment().getHiddenColumns()
665 .findColumnPosition(maxwidth);
668 while ((ypos <= canvasHeight) && (startRes < maxwidth))
670 endx = startRes + cWidth - 1;
677 g.setFont(av.getFont());
678 g.setColor(Color.black);
680 if (av.getScaleLeftWrapped())
682 drawWestScale(g, startRes, endx, ypos);
685 if (av.getScaleRightWrapped())
687 g.translate(canvasWidth - LABEL_EAST, 0);
688 drawEastScale(g, startRes, endx, ypos);
689 g.translate(-(canvasWidth - LABEL_EAST), 0);
692 g.translate(LABEL_WEST, 0);
694 if (av.getScaleAboveWrapped())
696 drawNorthScale(g, startRes, endx, ypos);
699 if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
701 g.setColor(Color.blue);
703 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
704 List<Integer> positions = hidden.findHiddenRegionPositions();
705 for (int pos : positions)
707 res = pos - startRes;
709 if (res < 0 || res > endx - startRes)
716 { res * charWidth - charHeight / 4,
717 res * charWidth + charHeight / 4, res * charWidth },
719 { ypos - (charHeight / 2), ypos - (charHeight / 2),
720 ypos - (charHeight / 2) + 8 },
726 // When printing we have an extra clipped region,
727 // the Printable page which we need to account for here
728 Shape clip = g.getClip();
732 g.setClip(0, 0, cWidth * charWidth, canvasHeight);
736 g.setClip(0, (int) clip.getBounds().getY(), cWidth * charWidth,
737 (int) clip.getBounds().getHeight());
740 drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
742 if (av.isShowAnnotation())
744 g.translate(0, cHeight + ypos + 3);
745 if (annotations == null)
747 annotations = new AnnotationPanel(av);
750 annotations.renderer.drawComponent(annotations, av, g, -1, startRes,
752 g.translate(0, -cHeight - ypos - 3);
755 g.translate(-LABEL_WEST, 0);
757 ypos += cHeight + getAnnotationHeight() + hgap;
764 * Draw a selection group over a wrapped alignment
766 private void drawWrappedSelection(Graphics2D g, SequenceGroup group,
768 int canvasHeight, int startRes)
770 // height gap above each panel
771 int hgap = charHeight;
772 if (av.getScaleAboveWrapped())
777 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
778 int cHeight = av.getAlignment().getHeight() * charHeight;
780 int startx = startRes;
782 int ypos = hgap; // vertical offset
783 int maxwidth = av.getAlignment().getWidth();
785 if (av.hasHiddenColumns())
787 maxwidth = av.getAlignment().getHiddenColumns()
788 .findColumnPosition(maxwidth);
791 // chop the wrapped alignment extent up into panel-sized blocks and treat
792 // each block as if it were a block from an unwrapped alignment
793 while ((ypos <= canvasHeight) && (startx < maxwidth))
795 // set end value to be start + width, or maxwidth, whichever is smaller
796 endx = startx + cWidth - 1;
803 g.translate(LABEL_WEST, 0);
805 drawUnwrappedSelection(g, group, startx, endx, 0,
806 av.getAlignment().getHeight() - 1,
809 g.translate(-LABEL_WEST, 0);
811 // update vertical offset
812 ypos += cHeight + getAnnotationHeight() + hgap;
814 // update horizontal offset
819 int getAnnotationHeight()
821 if (!av.isShowAnnotation())
826 if (annotations == null)
828 annotations = new AnnotationPanel(av);
831 return annotations.adjustPanelHeight();
835 * Draw an alignment panel for printing
838 * Graphics object to draw with
840 * start residue of print area
842 * end residue of print area
844 * start sequence of print area
846 * end sequence of print area
850 private void drawPanel(Graphics g1, int startRes, int endRes,
851 int startSeq, int endSeq, int offset)
854 if (!av.hasHiddenColumns())
856 draw(g1, startRes, endRes, startSeq, endSeq, offset);
861 int blockStart = startRes;
862 int blockEnd = endRes;
864 for (int[] region : av.getAlignment().getHiddenColumns()
865 .getHiddenColumnsCopy())
867 int hideStart = region[0];
868 int hideEnd = region[1];
870 if (hideStart <= blockStart)
872 blockStart += (hideEnd - hideStart) + 1;
876 blockEnd = hideStart - 1;
878 g1.translate(screenY * charWidth, 0);
880 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
882 if (av.getShowHiddenMarkers())
884 g1.setColor(Color.blue);
886 g1.drawLine((blockEnd - blockStart + 1) * charWidth - 1,
887 0 + offset, (blockEnd - blockStart + 1) * charWidth - 1,
888 (endSeq - startSeq + 1) * charHeight + offset);
891 g1.translate(-screenY * charWidth, 0);
892 screenY += blockEnd - blockStart + 1;
893 blockStart = hideEnd + 1;
895 if (screenY > (endRes - startRes))
897 // already rendered last block
902 if (screenY <= (endRes - startRes))
904 // remaining visible region to render
905 blockEnd = blockStart + (endRes - startRes) - screenY;
906 g1.translate(screenY * charWidth, 0);
907 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
909 g1.translate(-screenY * charWidth, 0);
915 private void draw(Graphics g, int startRes, int endRes, int startSeq,
916 int endSeq, int offset)
918 g.setFont(av.getFont());
919 seqRdr.prepare(g, av.isRenderGaps());
923 // / First draw the sequences
924 // ///////////////////////////
925 for (int i = startSeq; i <= endSeq; i++)
927 nextSeq = av.getAlignment().getSequenceAt(i);
930 // occasionally, a race condition occurs such that the alignment row is
934 seqRdr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
935 startRes, endRes, offset + ((i - startSeq) * charHeight));
937 if (av.isShowSequenceFeatures())
939 fr.drawSequence(g, nextSeq, startRes, endRes,
940 offset + ((i - startSeq) * charHeight), false);
943 // / Highlight search Results once all sequences have been drawn
944 // ////////////////////////////////////////////////////////
945 if (av.hasSearchResults())
947 int[] visibleResults = av.getSearchResults().getResults(nextSeq,
949 if (visibleResults != null)
951 for (int r = 0; r < visibleResults.length; r += 2)
953 seqRdr.drawHighlightedText(nextSeq, visibleResults[r],
954 visibleResults[r + 1], (visibleResults[r] - startRes)
956 + ((i - startSeq) * charHeight));
961 if (av.cursorMode && cursorY == i && cursorX >= startRes
962 && cursorX <= endRes)
964 seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
965 offset + ((i - startSeq) * charHeight));
969 if (av.getSelectionGroup() != null
970 || av.getAlignment().getGroups().size() > 0)
972 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
977 void drawGroupsBoundaries(Graphics g1, int startRes, int endRes,
978 int startSeq, int endSeq, int offset)
980 Graphics2D g = (Graphics2D) g1;
982 // ///////////////////////////////////
983 // Now outline any areas if necessary
984 // ///////////////////////////////////
986 SequenceGroup group = null;
989 if (av.getAlignment().getGroups().size() > 0)
991 group = av.getAlignment().getGroups().get(0);
997 g.setStroke(new BasicStroke());
998 g.setColor(group.getOutlineColour());
1002 drawPartialGroupOutline(g, group, startRes, endRes, startSeq,
1007 g.setStroke(new BasicStroke());
1009 if (groupIndex >= av.getAlignment().getGroups().size())
1014 group = av.getAlignment().getGroups().get(groupIndex);
1016 } while (groupIndex < av.getAlignment().getGroups().size());
1024 * Draw the selection group as a separate image and overlay
1026 private BufferedImage drawSelectionGroup(int startRes, int endRes,
1027 int startSeq, int endSeq)
1029 // get a new image of the correct size
1030 BufferedImage selectionImage = setupImage();
1032 if (selectionImage == null)
1037 SequenceGroup group = av.getSelectionGroup();
1044 // set up drawing colour
1045 Graphics2D g = (Graphics2D) selectionImage.getGraphics();
1047 setupSelectionGroup(g, selectionImage);
1049 if (!av.getWrapAlignment())
1051 drawUnwrappedSelection(g, group, startRes, endRes, startSeq, endSeq,
1056 drawWrappedSelection(g, group, getWidth(), getHeight(),
1057 av.getRanges().getStartRes());
1061 return selectionImage;
1065 * Set up graphics for selection group
1067 private void setupSelectionGroup(Graphics2D g,
1068 BufferedImage selectionImage)
1070 // set background to transparent
1071 g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
1072 g.fillRect(0, 0, selectionImage.getWidth(), selectionImage.getHeight());
1074 // set up foreground to draw red dashed line
1075 g.setComposite(AlphaComposite.Src);
1076 g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
1077 BasicStroke.JOIN_ROUND, 3f, new float[]
1079 g.setColor(Color.RED);
1083 * Draw a selection group over an unwrapped alignment
1084 * @param g graphics object to draw with
1085 * @param group selection group
1086 * @param startRes start residue of area to draw
1087 * @param endRes end residue of area to draw
1088 * @param startSeq start sequence of area to draw
1089 * @param endSeq end sequence of area to draw
1090 * @param offset vertical offset (used when called from wrapped alignment code)
1092 private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group,
1093 int startRes, int endRes, int startSeq, int endSeq, int offset)
1095 if (!av.hasHiddenColumns())
1097 drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq,
1102 // package into blocks of visible columns
1104 int blockStart = startRes;
1105 int blockEnd = endRes;
1107 for (int[] region : av.getAlignment().getHiddenColumns()
1108 .getHiddenColumnsCopy())
1110 int hideStart = region[0];
1111 int hideEnd = region[1];
1113 if (hideStart <= blockStart)
1115 blockStart += (hideEnd - hideStart) + 1;
1119 blockEnd = hideStart - 1;
1121 g.translate(screenY * charWidth, 0);
1122 drawPartialGroupOutline(g, group,
1123 blockStart, blockEnd, startSeq, endSeq, offset);
1125 g.translate(-screenY * charWidth, 0);
1126 screenY += blockEnd - blockStart + 1;
1127 blockStart = hideEnd + 1;
1129 if (screenY > (endRes - startRes))
1131 // already rendered last block
1136 if (screenY <= (endRes - startRes))
1138 // remaining visible region to render
1139 blockEnd = blockStart + (endRes - startRes) - screenY;
1140 g.translate(screenY * charWidth, 0);
1141 drawPartialGroupOutline(g, group,
1142 blockStart, blockEnd, startSeq, endSeq, offset);
1144 g.translate(-screenY * charWidth, 0);
1150 * Draw the selection group as a separate image and overlay
1152 private void drawPartialGroupOutline(Graphics2D g, SequenceGroup group,
1153 int startRes, int endRes, int startSeq, int endSeq,
1156 int visWidth = (endRes - startRes + 1) * charWidth;
1160 boolean inGroup = false;
1168 for (i = startSeq; i <= endSeq; i++)
1170 // position of start residue of group relative to startRes, in pixels
1171 sx = (group.getStartRes() - startRes) * charWidth;
1173 // width of group in pixels
1174 xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
1177 sy = verticalOffset + (i - startSeq) * charHeight;
1179 if (sx + xwidth < 0 || sx > visWidth)
1184 if ((sx <= (endRes - startRes) * charWidth)
1185 && group.getSequences(null)
1186 .contains(av.getAlignment().getSequenceAt(i)))
1188 if ((bottom == -1) && !group.getSequences(null)
1189 .contains(av.getAlignment().getSequenceAt(i + 1)))
1191 bottom = sy + charHeight;
1196 if (((top == -1) && (i == 0)) || !group.getSequences(null)
1197 .contains(av.getAlignment().getSequenceAt(i - 1)))
1210 // if start position is visible, draw vertical line to left of
1212 if (sx >= 0 && sx < visWidth)
1214 g.drawLine(sx, oldY, sx, sy);
1217 // if end position is visible, draw vertical line to right of
1219 if (sx + xwidth < visWidth)
1221 g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
1230 // don't let width extend beyond current block, or group extent
1232 if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
1234 xwidth = (endRes - startRes + 1) * charWidth - sx;
1237 // draw horizontal line at top of group
1240 g.drawLine(sx, top, sx + xwidth, top);
1244 // draw horizontal line at bottom of group
1247 g.drawLine(sx, bottom, sx + xwidth, bottom);
1258 sy = verticalOffset + ((i - startSeq) * charHeight);
1259 if (sx >= 0 && sx < visWidth)
1261 g.drawLine(sx, oldY, sx, sy);
1264 if (sx + xwidth < visWidth)
1266 g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
1275 if (sx + xwidth > visWidth)
1279 else if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
1281 xwidth = (endRes - startRes + 1) * charWidth;
1286 g.drawLine(sx, top, sx + xwidth, top);
1292 g.drawLine(sx, bottom - 1, sx + xwidth, bottom - 1);
1306 public void highlightSearchResults(SearchResultsI results)
1310 av.setSearchResults(results);
1316 public void propertyChange(PropertyChangeEvent evt)
1318 String eventName = evt.getPropertyName();
1320 if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
1325 else if (av.getWrapAlignment())
1327 if (eventName.equals(ViewportRanges.STARTRES))
1335 if (eventName.equals(ViewportRanges.STARTRES))
1337 // Make sure we're not trying to draw a panel
1338 // larger than the visible window
1339 ViewportRanges vpRanges = av.getRanges();
1340 scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
1341 int range = vpRanges.getEndRes() - vpRanges.getStartRes();
1342 if (scrollX > range)
1346 else if (scrollX < -range)
1352 // Both scrolling and resizing change viewport ranges: scrolling changes
1353 // both start and end points, but resize only changes end values.
1354 // Here we only want to fastpaint on a scroll, with resize using a normal
1355 // paint, so scroll events are identified as changes to the horizontal or
1356 // vertical start value.
1357 if (eventName.equals(ViewportRanges.STARTRES))
1359 // scroll - startres and endres both change
1360 fastPaint(scrollX, 0);
1362 else if (eventName.equals(ViewportRanges.STARTSEQ))
1365 fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());