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.
21 package jalview.appletgui;
23 import jalview.datamodel.AlignmentI;
24 import jalview.datamodel.SearchResultsI;
25 import jalview.datamodel.SequenceGroup;
26 import jalview.datamodel.SequenceI;
27 import jalview.renderer.ScaleRenderer;
28 import jalview.renderer.ScaleRenderer.ScaleMark;
29 import jalview.viewmodel.AlignmentViewport;
30 import jalview.viewmodel.ViewportRanges;
32 import java.awt.Color;
33 import java.awt.FontMetrics;
34 import java.awt.Graphics;
35 import java.awt.Image;
36 import java.awt.Panel;
38 public class SeqCanvas extends Panel
54 boolean fastPaint = false;
60 public SeqCanvas(AlignViewport av)
63 fr = new FeatureRenderer(av);
64 sr = new SequenceRenderer(av);
65 PaintRefresher.Register(this, av.getSequenceSetId());
69 int avcharHeight = 0, avcharWidth = 0;
71 private void updateViewport()
73 avcharHeight = av.getCharHeight();
74 avcharWidth = av.getCharWidth();
77 public AlignmentViewport getViewport()
82 public FeatureRenderer getFeatureRenderer()
87 public SequenceRenderer getSequenceRenderer()
92 private void drawNorthScale(Graphics g, int startx, int endx, int ypos)
95 g.setColor(Color.black);
96 for (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx,
99 int mpos = mark.column; // (i - startx - 1)
104 String mstring = mark.text;
110 g.drawString(mstring, mpos * avcharWidth, ypos
111 - (avcharHeight / 2));
113 g.drawLine((mpos * avcharWidth) + (avcharWidth / 2), (ypos + 2)
114 - (avcharHeight / 2), (mpos * avcharWidth)
115 + (avcharWidth / 2), ypos - 2);
120 private void drawWestScale(Graphics g, int startx, int endx, int ypos)
122 FontMetrics fm = getFontMetrics(av.getFont());
123 ypos += avcharHeight;
124 if (av.hasHiddenColumns())
126 startx = av.getColumnSelection().adjustForHiddenColumns(startx);
127 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
130 int maxwidth = av.getAlignment().getWidth();
131 if (av.hasHiddenColumns())
133 maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
137 for (int i = 0; i < av.getAlignment().getHeight(); i++)
139 SequenceI seq = av.getAlignment().getSequenceAt(i);
145 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
152 value = av.getAlignment().getSequenceAt(i).findPosition(index);
159 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))
161 g.drawString(value + "", x, (ypos + (i * avcharHeight))
162 - (avcharHeight / 5));
167 private void drawEastScale(Graphics g, int startx, int endx, int ypos)
169 ypos += avcharHeight;
171 if (av.hasHiddenColumns())
173 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
178 for (int i = 0; i < av.getAlignment().getHeight(); i++)
180 seq = av.getAlignment().getSequenceAt(i);
184 while (index > startx)
186 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
193 value = seq.findPosition(index);
200 g.drawString(String.valueOf(value), 0, (ypos + (i * avcharHeight))
201 - (avcharHeight / 5));
208 void fastPaint(int horizontal, int vertical)
210 if (fastPaint || gg == null)
215 ViewportRanges ranges = av.getRanges();
219 // Its possible on certain browsers that the call to fastpaint
220 // is faster than it can paint, so this check here catches
222 if (lastsr + horizontal != ranges.getStartRes())
224 horizontal = ranges.getStartRes() - lastsr;
227 lastsr = ranges.getStartRes();
230 gg.copyArea(horizontal * avcharWidth, vertical * avcharHeight, imgWidth
231 - horizontal * avcharWidth,
232 imgHeight - vertical * avcharHeight, -horizontal * avcharWidth,
233 -vertical * avcharHeight);
235 int sr = ranges.getStartRes(), er = ranges.getEndRes(), ss = ranges
236 .getStartSeq(), es = ranges
237 .getEndSeq(), transX = 0, transY = 0;
239 if (horizontal > 0) // scrollbar pulled right, image to the left
241 transX = (er - sr - horizontal) * avcharWidth;
242 sr = er - horizontal;
244 else if (horizontal < 0)
246 er = sr - horizontal;
249 else if (vertical > 0) // scroll down
252 if (ss < ranges.getStartSeq()) // ie scrolling too fast, more than a page
256 ss = ranges.getStartSeq();
260 transY = imgHeight - ((vertical + 1) * avcharHeight);
263 else if (vertical < 0)
266 if (es > ranges.getEndSeq())
268 es = ranges.getEndSeq();
272 gg.translate(transX, transY);
274 drawPanel(gg, sr, er, ss, es, 0);
275 gg.translate(-transX, -transY);
282 * Definitions of startx and endx (hopefully): SMJS This is what I'm working
283 * towards! startx is the first residue (starting at 0) to display. endx is
284 * the last residue to display (starting at 0). starty is the first sequence
285 * to display (starting at 0). endy is the last sequence to display (starting
286 * at 0). NOTE 1: The av limits are set in setFont in this class and in the
287 * adjustment listener in SeqPanel when the scrollbars move.
290 public void update(Graphics g)
296 public void paint(Graphics g)
300 && (fastPaint || (getSize().width != g.getClipBounds().width) || (getSize().height != g
301 .getClipBounds().height)))
303 g.drawImage(img, 0, 0, this);
310 g.drawImage(img, 0, 0, this);
316 // this draws the whole of the alignment
317 imgWidth = this.getSize().width;
318 imgHeight = this.getSize().height;
320 imgWidth -= imgWidth % avcharWidth;
321 imgHeight -= imgHeight % avcharHeight;
323 if (imgWidth < 1 || imgHeight < 1)
328 if (img == null || imgWidth != img.getWidth(this)
329 || imgHeight != img.getHeight(this))
331 img = createImage(imgWidth, imgHeight);
332 gg = img.getGraphics();
333 gg.setFont(av.getFont());
336 gg.setColor(Color.white);
337 gg.fillRect(0, 0, imgWidth, imgHeight);
339 ViewportRanges ranges = av.getRanges();
341 if (av.getWrapAlignment())
343 drawWrappedPanel(gg, imgWidth, imgHeight, ranges.getStartRes());
347 drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
348 ranges.getStartSeq(), ranges.getEndSeq(), 0);
351 g.drawImage(img, 0, 0, this);
355 int LABEL_WEST, LABEL_EAST;
357 public int getWrappedCanvasWidth(int cwidth)
359 cwidth -= cwidth % av.getCharWidth();
361 FontMetrics fm = getFontMetrics(av.getFont());
366 if (av.getScaleRightWrapped())
368 LABEL_EAST = fm.stringWidth(getMask());
371 if (av.getScaleLeftWrapped())
373 LABEL_WEST = fm.stringWidth(getMask());
376 return (cwidth - LABEL_EAST - LABEL_WEST) / av.getCharWidth();
380 * Generates a string of zeroes.
389 AlignmentI alignment = av.getAlignment();
390 for (int i = 0; i < alignment.getHeight(); i++)
392 tmp = alignment.getSequenceAt(i).getEnd();
399 for (int i = maxWidth; i > 0; i /= 10)
406 private void drawWrappedPanel(Graphics g, int canvasWidth,
407 int canvasHeight, int startRes)
409 AlignmentI al = av.getAlignment();
411 FontMetrics fm = getFontMetrics(av.getFont());
413 if (av.getScaleRightWrapped())
415 LABEL_EAST = fm.stringWidth(getMask());
418 if (av.getScaleLeftWrapped())
420 LABEL_WEST = fm.stringWidth(getMask());
423 int hgap = avcharHeight;
424 if (av.getScaleAboveWrapped())
426 hgap += avcharHeight;
429 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / avcharWidth;
430 int cHeight = av.getAlignment().getHeight() * avcharHeight;
432 av.setWrappedWidth(cWidth);
434 av.getRanges().setEndRes(av.getRanges().getStartRes() + cWidth);
439 int maxwidth = av.getAlignment().getWidth() - 1;
441 if (av.hasHiddenColumns())
443 maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
446 while ((ypos <= canvasHeight) && (startRes < maxwidth))
448 endx = startRes + cWidth - 1;
455 g.setColor(Color.black);
457 if (av.getScaleLeftWrapped())
459 drawWestScale(g, startRes, endx, ypos);
462 if (av.getScaleRightWrapped())
464 g.translate(canvasWidth - LABEL_EAST, 0);
465 drawEastScale(g, startRes, endx, ypos);
466 g.translate(-(canvasWidth - LABEL_EAST), 0);
469 g.translate(LABEL_WEST, 0);
471 if (av.getScaleAboveWrapped())
473 drawNorthScale(g, startRes, endx, ypos);
475 if (av.hasHiddenColumns() && av.getShowHiddenMarkers())
477 g.setColor(Color.blue);
479 for (int i = 0; i < av.getColumnSelection().getHiddenColumns()
482 res = av.getColumnSelection().findHiddenRegionPosition(i)
485 if (res < 0 || res > endx - startRes)
490 gg.fillPolygon(new int[] { res * avcharWidth - avcharHeight / 4,
491 res * avcharWidth + avcharHeight / 4, res * avcharWidth },
492 new int[] { ypos - (avcharHeight / 2),
493 ypos - (avcharHeight / 2),
494 ypos - (avcharHeight / 2) + 8 }, 3);
499 if (g.getClip() == null)
501 g.setClip(0, 0, cWidth * avcharWidth, canvasHeight);
504 drawPanel(g, startRes, endx, 0, al.getHeight(), ypos);
507 if (av.isShowAnnotation())
509 g.translate(0, cHeight + ypos + 4);
510 if (annotations == null)
512 annotations = new AnnotationPanel(av);
515 annotations.drawComponent(g, startRes, endx + 1);
516 g.translate(0, -cHeight - ypos - 4);
518 g.translate(-LABEL_WEST, 0);
520 ypos += cHeight + getAnnotationHeight() + hgap;
527 AnnotationPanel annotations;
529 int getAnnotationHeight()
531 if (!av.isShowAnnotation())
536 if (annotations == null)
538 annotations = new AnnotationPanel(av);
541 return annotations.adjustPanelHeight();
544 private void drawPanel(Graphics g1, int startRes, int endRes,
545 int startSeq, int endSeq, int offset)
548 if (!av.hasHiddenColumns())
550 draw(g1, startRes, endRes, startSeq, endSeq, offset);
556 int blockStart = startRes;
557 int blockEnd = endRes;
559 if (av.hasHiddenColumns())
561 for (int[] region : av.getColumnSelection().getHiddenColumns())
563 int hideStart = region[0];
564 int hideEnd = region[1];
566 if (hideStart <= blockStart)
568 blockStart += (hideEnd - hideStart) + 1;
572 blockEnd = hideStart - 1;
574 g1.translate(screenY * avcharWidth, 0);
576 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
578 if (av.getShowHiddenMarkers())
580 g1.setColor(Color.blue);
581 g1.drawLine((blockEnd - blockStart + 1) * avcharWidth - 1,
582 0 + offset, (blockEnd - blockStart + 1) * avcharWidth
583 - 1, (endSeq - startSeq) * avcharHeight
587 g1.translate(-screenY * avcharWidth, 0);
588 screenY += blockEnd - blockStart + 1;
589 blockStart = hideEnd + 1;
591 if (screenY > (endRes - startRes))
593 // already rendered last block
598 if (screenY <= (endRes - startRes))
600 // remaining visible region to render
601 blockEnd = blockStart + (endRes - startRes) - screenY;
602 g1.translate(screenY * avcharWidth, 0);
603 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
605 g1.translate(-screenY * avcharWidth, 0);
611 // int startRes, int endRes, int startSeq, int endSeq, int x, int y,
612 // int x1, int x2, int y1, int y2, int startx, int starty,
613 void draw(Graphics g, int startRes, int endRes, int startSeq, int endSeq,
616 g.setFont(av.getFont());
617 sr.prepare(g, av.isRenderGaps());
621 // / First draw the sequences
622 // ///////////////////////////
623 for (int i = startSeq; i <= endSeq; i++)
625 nextSeq = av.getAlignment().getSequenceAt(i);
632 sr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
633 startRes, endRes, offset + ((i - startSeq) * avcharHeight));
635 if (av.isShowSequenceFeatures())
637 fr.drawSequence(g, nextSeq, startRes, endRes, offset
638 + ((i - startSeq) * avcharHeight), false);
641 // / Highlight search Results once all sequences have been drawn
642 // ////////////////////////////////////////////////////////
643 if (av.hasSearchResults())
645 int[] visibleResults = av.getSearchResults().getResults(nextSeq,
648 if (visibleResults != null)
650 for (int r = 0; r < visibleResults.length; r += 2)
652 sr.drawHighlightedText(nextSeq, visibleResults[r],
653 visibleResults[r + 1], (visibleResults[r] - startRes)
654 * avcharWidth, offset
655 + ((i - startSeq) * avcharHeight));
660 if (av.cursorMode && cursorY == i && cursorX >= startRes
661 && cursorX <= endRes)
663 sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * avcharWidth,
664 offset + ((i - startSeq) * avcharHeight));
668 if (av.getSelectionGroup() != null
669 || av.getAlignment().getGroups().size() > 0)
671 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
676 private void drawGroupsBoundaries(Graphics g, int startRes, int endRes,
677 int startSeq, int endSeq, int offset)
680 // ///////////////////////////////////
681 // Now outline any areas if necessary
682 // ///////////////////////////////////
683 SequenceGroup group = av.getSelectionGroup();
690 if ((group == null) && (av.getAlignment().getGroups().size() > 0))
692 group = av.getAlignment().getGroups().get(0);
702 boolean inGroup = false;
705 int alHeight = av.getAlignment().getHeight() - 1;
707 for (i = startSeq; i <= endSeq; i++)
709 sx = (group.getStartRes() - startRes) * avcharWidth;
710 sy = offset + ((i - startSeq) * avcharHeight);
711 ex = (((group.getEndRes() + 1) - group.getStartRes()) * avcharWidth) - 1;
713 if (sx + ex < 0 || sx > imgWidth)
718 if ((sx <= (endRes - startRes) * avcharWidth)
719 && group.getSequences(null).contains(
720 av.getAlignment().getSequenceAt(i)))
723 && (i >= alHeight || !group.getSequences(null)
725 av.getAlignment().getSequenceAt(i + 1))))
727 bottom = sy + avcharHeight;
732 if (((top == -1) && (i == 0))
733 || !group.getSequences(null).contains(
734 av.getAlignment().getSequenceAt(i - 1)))
742 if (group == av.getSelectionGroup())
744 g.setColor(Color.red);
748 g.setColor(group.getOutlineColour());
756 if (sx >= 0 && sx < imgWidth)
758 g.drawLine(sx, oldY, sx, sy);
761 if (sx + ex < imgWidth)
763 g.drawLine(sx + ex, oldY, sx + ex, sy);
772 if (sx + ex > imgWidth)
777 else if (sx + ex >= (endRes - startRes + 1) * avcharWidth)
779 ex = (endRes - startRes + 1) * avcharWidth;
784 g.drawLine(sx, top, sx + ex, top);
790 g.drawLine(sx, bottom, sx + ex, bottom);
801 sy = offset + ((i - startSeq) * avcharHeight);
802 if (sx >= 0 && sx < imgWidth)
804 g.drawLine(sx, oldY, sx, sy);
807 if (sx + ex < imgWidth)
809 g.drawLine(sx + ex, oldY, sx + ex, sy);
818 if (sx + ex > imgWidth)
822 else if (sx + ex >= (endRes - startRes + 1) * avcharWidth)
824 ex = (endRes - startRes + 1) * avcharWidth;
829 g.drawLine(sx, top, sx + ex, top);
835 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
844 if (groupIndex >= av.getAlignment().getGroups().size())
849 group = av.getAlignment().getGroups().get(groupIndex);
850 } while (groupIndex < av.getAlignment().getGroups().size());
855 public void highlightSearchResults(SearchResultsI results)
857 av.setSearchResults(results);