2 * Jalview - A Sequence Alignment Editor and Viewer
\r
3 * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
\r
5 * This program is free software; you can redistribute it and/or
\r
6 * modify it under the terms of the GNU General Public License
\r
7 * as published by the Free Software Foundation; either version 2
\r
8 * of the License, or (at your option) any later version.
\r
10 * This program is distributed in the hope that it will be useful,
\r
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 * GNU General Public License for more details.
\r
15 * You should have received a copy of the GNU General Public License
\r
16 * along with this program; if not, write to the Free Software
\r
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
\r
19 package jalview.gui;
\r
21 import jalview.datamodel.*;
\r
24 import java.awt.image.*;
\r
26 import javax.swing.*;
\r
27 import jalview.schemes.*;
\r
34 * @version $Revision$
\r
36 public class SeqCanvas extends JComponent
\r
39 SequenceRenderer sr;
\r
45 boolean displaySearch = false;
\r
46 int[] searchResults = null;
\r
47 boolean fastPaint = false;
\r
51 boolean isOverview = false;
\r
54 * Creates a new SeqCanvas object.
\r
56 * @param av DOCUMENT ME!
\r
58 public SeqCanvas(AlignViewport av)
\r
61 fr = new FeatureRenderer(av);
\r
62 sr = new SequenceRenderer(av);
\r
63 setLayout(new BorderLayout());
\r
64 PaintRefresher.Register(this, av.alignment);
\r
65 setBackground(Color.white);
\r
68 public FeatureRenderer getFeatureRenderer()
\r
76 * @param g DOCUMENT ME!
\r
77 * @param startx DOCUMENT ME!
\r
78 * @param endx DOCUMENT ME!
\r
79 * @param ypos DOCUMENT ME!
\r
81 void drawNorthScale(Graphics g, int startx, int endx, int ypos)
\r
83 int scalestartx = startx - (startx % 10) + 10;
\r
85 g.setColor(Color.black);
\r
88 for (int i = scalestartx; i < endx; i += 10)
\r
90 String string = String.valueOf(i);
\r
91 g.drawString(string, (i - startx - 1) * av.charWidth,
\r
92 ypos - (av.charHeight / 2));
\r
94 g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
\r
95 (ypos + 2) - (av.charHeight / 2),
\r
96 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -
\r
104 * @param g DOCUMENT ME!
\r
105 * @param startx DOCUMENT ME!
\r
106 * @param endx DOCUMENT ME!
\r
107 * @param ypos DOCUMENT ME!
\r
109 void drawWestScale(Graphics g, int startx, int endx, int ypos)
\r
111 FontMetrics fm = getFontMetrics(av.getFont());
\r
112 ypos += av.charHeight;
\r
115 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
117 SequenceI seq = av.alignment.getSequenceAt(i);
\r
118 int index = startx;
\r
121 while (index < endx)
\r
123 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
130 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
137 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))-av.charWidth/2;
\r
138 g.drawString(value + "", x,
\r
139 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
147 * @param g DOCUMENT ME!
\r
148 * @param startx DOCUMENT ME!
\r
149 * @param endx DOCUMENT ME!
\r
150 * @param ypos DOCUMENT ME!
\r
152 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
154 ypos += av.charHeight;
\r
157 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
159 SequenceI seq = av.alignment.getSequenceAt(i);
\r
163 while (index > startx)
\r
165 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
172 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
179 g.drawString(String.valueOf(value), av.charWidth/2,
\r
180 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
188 * @param horizontal DOCUMENT ME!
\r
189 * @param vertical DOCUMENT ME!
\r
191 public void fastPaint(int horizontal, int vertical)
\r
198 gg.copyArea(horizontal * av.charWidth,
\r
199 vertical * av.charHeight,
\r
202 -horizontal * av.charWidth,
\r
203 -vertical * av.charHeight);
\r
205 int sr = av.startRes;
\r
206 int er = av.endRes;
\r
207 int ss = av.startSeq;
\r
208 int es = av.endSeq;
\r
212 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
215 transX = (er - sr - horizontal) * av.charWidth;
\r
216 sr = er - horizontal;
\r
218 else if (horizontal < 0)
\r
220 er = sr - horizontal-1;
\r
222 else if (vertical > 0) // scroll down
\r
224 ss = es - vertical;
\r
226 if (ss < av.startSeq)
\r
227 { // ie scrolling too fast, more than a page at a time
\r
232 transY = imgHeight - (vertical * av.charHeight);
\r
235 else if (vertical < 0)
\r
237 es = ss - vertical;
\r
239 if (es > av.endSeq)
\r
245 gg.translate(transX, transY);
\r
246 drawPanel(gg, sr, er, ss, es, sr, ss, 0);
\r
247 gg.translate(-transX, -transY);
\r
254 * Definitions of startx and endx (hopefully):
\r
255 * SMJS This is what I'm working towards!
\r
256 * startx is the first residue (starting at 0) to display.
\r
257 * endx is the last residue to display (starting at 0).
\r
258 * starty is the first sequence to display (starting at 0).
\r
259 * endy is the last sequence to display (starting at 0).
\r
260 * NOTE 1: The av limits are set in setFont in this class and
\r
261 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
264 // Set this to false to force a full panel paint
\r
265 public void paintComponent(Graphics g)
\r
267 sr.renderGaps(av.renderGaps);
\r
269 if ((img != null) &&
\r
270 (fastPaint || (getWidth() != g.getClipBounds().width) ||
\r
271 (getHeight() != g.getClipBounds().height)))
\r
273 g.drawImage(img, 0, 0, this);
\r
279 // this draws the whole of the alignment
\r
280 imgWidth = getWidth();
\r
281 imgHeight = getHeight();
\r
283 imgWidth -= (imgWidth % av.charWidth);
\r
284 imgHeight -= (imgHeight % av.charHeight);
\r
286 if ((imgWidth < 1) || (imgHeight < 1))
\r
292 img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
\r
293 gg = (Graphics2D) img.getGraphics();
\r
294 gg.setFont(av.getFont());
\r
295 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
296 RenderingHints.VALUE_ANTIALIAS_ON);
\r
298 gg.setColor(Color.white);
\r
299 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
302 if (av.getWrapAlignment())
\r
304 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
308 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq,
\r
309 av.startRes, av.startSeq, 0);
\r
312 g.drawImage(img, 0, 0, this);
\r
318 * @param cwidth DOCUMENT ME!
\r
320 * @return DOCUMENT ME!
\r
322 public int getWrappedCanvasWidth(int cwidth)
\r
324 FontMetrics fm = getFontMetrics(av.getFont());
\r
329 if (av.scaleRightWrapped)
\r
331 LABEL_EAST = fm.stringWidth(getMask()+"0");
\r
334 if (av.scaleLeftWrapped)
\r
336 LABEL_WEST = fm.stringWidth(getMask());
\r
339 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
344 * Generates a string of zeroes.
\r
350 for (int i = av.alignment.getWidth(); i > 0; i /= 10)
\r
360 * @param g DOCUMENT ME!
\r
361 * @param canvasWidth DOCUMENT ME!
\r
362 * @param canvasHeight DOCUMENT ME!
\r
363 * @param startRes DOCUMENT ME!
\r
365 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
369 AlignmentI al = av.getAlignment();
\r
371 FontMetrics fm = getFontMetrics(av.getFont());
\r
373 int LABEL_EAST = 0;
\r
375 if (av.scaleRightWrapped)
\r
377 LABEL_EAST = fm.stringWidth(getMask()+"0");
\r
380 int LABEL_WEST = 0;
\r
382 if (av.scaleLeftWrapped)
\r
384 LABEL_WEST = fm.stringWidth(getMask());
\r
387 int hgap = av.charHeight;
\r
388 if(av.scaleAboveWrapped)
\r
389 hgap += av.charHeight;
\r
391 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
392 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
394 av.setWrappedWidth(cWidth);
\r
396 av.endRes = av.startRes + cWidth;
\r
399 int endx = (startRes + cWidth) - 1;
\r
403 while ((ypos <= canvasHeight) && (startRes < av.alignment.getWidth()))
\r
405 g.setFont(av.getFont());
\r
406 g.setColor(Color.black);
\r
408 if (av.scaleLeftWrapped)
\r
410 drawWestScale(g, startRes, endx, ypos);
\r
413 if (av.scaleRightWrapped)
\r
415 g.translate(canvasWidth - LABEL_EAST, 0);
\r
416 drawEastScale(g, startRes, endx, ypos);
\r
417 g.translate(-(canvasWidth - LABEL_EAST), 0);
\r
420 g.translate(LABEL_WEST, 0);
\r
422 if (av.scaleAboveWrapped)
\r
424 drawNorthScale(g, startRes, endx, ypos);
\r
427 // When printing we have an extra clipped region,
\r
428 // the Printable page which we need to account for here
\r
429 Shape clip = g.getClip();
\r
433 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
437 g.setClip(0, (int) clip.getBounds().getY(),
\r
438 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
441 if (av.vconsensus!=null && av.alignment.getWidth() >= av.vconsensus.size())
\r
443 endx = av.vconsensus.size() - 2;
\r
447 drawPanel(g, startRes, endx, 0, al.getHeight(), startRes, 0, ypos);
\r
449 if(av.showAnnotation)
\r
451 g.translate(0, cHeight + ypos + 3);
\r
452 if(annotations==null)
\r
453 annotations = new AnnotationPanel(av);
\r
455 annotations.drawComponent( (Graphics2D) g, startRes, endx + 1);
\r
456 g.translate(0, -cHeight - ypos);
\r
459 g.translate(-LABEL_WEST, 0);
\r
461 ypos += cHeight+getAnnotationHeight()+hgap;
\r
462 if(av.showAnnotation)
\r
465 startRes += cWidth;
\r
466 endx = (startRes + cWidth) - 1;
\r
468 if (endx > al.getWidth())
\r
470 endx = al.getWidth();
\r
475 AnnotationPanel annotations;
\r
476 int getAnnotationHeight()
\r
478 if(!av.showAnnotation)
\r
481 if(annotations==null)
\r
482 annotations = new AnnotationPanel(av);
\r
484 return annotations.adjustPanelHeight();
\r
490 * @param g1 DOCUMENT ME!
\r
491 * @param x1 DOCUMENT ME!
\r
492 * @param x2 DOCUMENT ME!
\r
493 * @param y1 DOCUMENT ME!
\r
494 * @param y2 DOCUMENT ME!
\r
495 * @param startx DOCUMENT ME!
\r
496 * @param starty DOCUMENT ME!
\r
497 * @param offset DOCUMENT ME!
\r
500 float aaRatio = 2f/3f;
\r
501 public void increaseAARatio()
\r
510 public void decreaseAARation()
\r
519 synchronized public void drawPanel(Graphics g1, int x1, int x2, int y1,
\r
520 int y2, int startx, int starty, int offset)
\r
522 Graphics2D g = (Graphics2D) g1;
\r
523 g.setFont(av.getFont());
\r
525 SequenceI nextSeq, dna = null;
\r
526 int aaHeight = av.charHeight, dnaHeight = av.charHeight;
\r
527 Font dnafont = null, aafont = av.getFont();
\r
529 /* if (av.getShowTranslation())
\r
531 aaHeight = (int) (av.getCharHeight() * aaRatio);
\r
532 dnaHeight = (int) (av.getCharHeight() * (1 - aaRatio));
\r
533 java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform();
\r
534 transform.scale(1f / 3f, 1);
\r
535 dnafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
537 dnafont = dnafont.deriveFont(transform);
\r
539 aafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
541 transform = new java.awt.geom.AffineTransform();
\r
542 transform.scale(1 / aaRatio, 1);
\r
543 aafont = aafont.deriveFont(transform);
\r
547 ColourSchemeI cs = av.getGlobalColourScheme();
\r
549 /// First draw the sequences
\r
550 /////////////////////////////
\r
551 for (int i = y1; i < y2; i++)
\r
553 nextSeq = av.alignment.getSequenceAt(i);
\r
555 /* if(av.getShowTranslation())
\r
558 StringBuffer sb = new StringBuffer();
\r
559 for(int r = 0; r<nextSeq.getLength(); r+=3)
\r
561 String codon = nextSeq.getSequence(r, r+3);
\r
562 codon = codon.replace('U', 'T');
\r
563 String res = ResidueProperties.codonTranslate(codon);
\r
566 if (res.equals("STOP"))
\r
572 nextSeq = new Sequence("d", sb.toString());
\r
575 sr.drawSequence(g, nextSeq, av.alignment.findAllGroups(nextSeq),
\r
576 x1, x2, (x1 - startx) * av.charWidth,
\r
577 offset + ( (i - starty) * av.charHeight), av.charWidth,
\r
580 /* if(av.getShowTranslation())
\r
582 av.setGlobalColourScheme(new NucleotideColourScheme());
\r
583 g.setFont(dnafont);
\r
584 sr.drawSequence(g, dna, null,
\r
585 x1*3, (x2+2) * 3, ( (x1 - startx) * av.charWidth) / 3,
\r
586 offset + ( (i - starty) * av.charHeight) + aaHeight,
\r
589 av.setGlobalColourScheme(cs);
\r
592 if (av.showSequenceFeatures)
\r
594 fr.drawSequence(g, nextSeq, x1, x2,
\r
595 (x1 - startx) * av.charWidth,
\r
596 offset + ((i - starty) * av.charHeight), av.charWidth,
\r
602 /////////////////////////////////////
\r
603 // Now outline any areas if necessary
\r
604 /////////////////////////////////////
\r
605 SequenceGroup group = av.getSelectionGroup();
\r
610 int groupIndex = -1;
\r
612 if ((group == null) && (av.alignment.getGroups().size() > 0))
\r
614 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
618 if (group != null && !isOverview)
\r
624 boolean inGroup = false;
\r
628 for (i = y1; i < y2; i++)
\r
630 sx = (group.getStartRes() - startx) * av.charWidth;
\r
631 sy = offset + ((i - starty) * av.charHeight);
\r
632 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
635 if(sx+ex<0 || sx>imgWidth)
\r
640 if ( (sx <= (x2-x1)*av.charWidth) &&
\r
641 group.sequences.contains(av.alignment.getSequenceAt(
\r
644 if ((bottom == -1) &&
\r
645 !group.sequences.contains(
\r
646 av.alignment.getSequenceAt(i + 1)))
\r
648 bottom = sy + av.charHeight;
\r
653 if (((top == -1) && (i == 0)) ||
\r
654 !group.sequences.contains(
\r
655 av.alignment.getSequenceAt(i - 1)))
\r
663 if (group == av.getSelectionGroup())
\r
665 g.setStroke(new BasicStroke(1,
\r
666 BasicStroke.CAP_BUTT,
\r
667 BasicStroke.JOIN_ROUND, 3f,
\r
668 new float[] { 5f, 3f }, 0f));
\r
669 g.setColor(Color.RED);
\r
673 g.setStroke(new BasicStroke());
\r
674 g.setColor(group.getOutlineColour());
\r
682 if (sx >= 0 && sx < imgWidth)
\r
683 g.drawLine(sx, oldY, sx, sy);
\r
685 if (sx + ex < imgWidth)
\r
686 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
694 if (sx + ex > imgWidth)
\r
697 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
698 ex = (x2 - x1 + 1) * av.charWidth;
\r
702 g.drawLine(sx, top, sx + ex, top);
\r
708 g.drawLine(sx, bottom, sx + ex, bottom);
\r
719 sy = offset + ( (i - starty) * av.charHeight);
\r
720 if (sx >= 0 && sx < imgWidth)
\r
721 g.drawLine(sx, oldY, sx, sy);
\r
723 if (sx + ex < imgWidth)
\r
724 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
732 if (sx + ex > imgWidth)
\r
734 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
735 ex = (x2 - x1 + 1) * av.charWidth;
\r
739 g.drawLine(sx, top, sx + ex, top);
\r
745 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
754 if (groupIndex >= av.alignment.getGroups().size())
\r
759 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
761 while (groupIndex < av.alignment.getGroups().size());
\r
764 /// Highlight search Results once all sequences have been drawn
\r
765 //////////////////////////////////////////////////////////
\r
768 for (int r = 0; r < searchResults.length; r += 3)
\r
770 int searchSeq = searchResults[r];
\r
772 if ((searchSeq >= y1) && (searchSeq < y2))
\r
774 SequenceI seq = av.getAlignment().getSequenceAt(searchSeq);
\r
776 int searchStart = seq.findIndex(searchResults[r + 1]) - 1;
\r
777 int searchEnd = seq.findIndex(searchResults[r + 2]) - 1;
\r
779 SequenceRenderer ssr = (SequenceRenderer) sr;
\r
781 if (searchStart < x1)
\r
786 if (searchEnd > x2)
\r
791 ssr.drawHighlightedText(seq, searchStart, searchEnd,
\r
792 (searchStart - startx) * av.charWidth,
\r
793 offset + ((searchSeq - starty) * av.charHeight),
\r
794 av.charWidth, av.charHeight);
\r
803 * @param results DOCUMENT ME!
\r
805 public void highlightSearchResults(int[] results)
\r
807 // results are in the order sequence, startRes, endRes
\r
808 if (results == null)
\r
810 displaySearch = false;
\r
814 displaySearch = true;
\r
817 searchResults = results;
\r