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
33 * @version $Revision$
\r
35 public class SeqCanvas extends JComponent
\r
38 SequenceRenderer sr;
\r
44 SearchResults searchResults = null;
\r
45 boolean fastPaint = false;
\r
50 * Creates a new SeqCanvas object.
\r
52 * @param av DOCUMENT ME!
\r
54 public SeqCanvas(AlignViewport av)
\r
57 fr = new FeatureRenderer(av);
\r
58 sr = new SequenceRenderer(av);
\r
59 setLayout(new BorderLayout());
\r
60 PaintRefresher.Register(this, av.alignment);
\r
61 setBackground(Color.white);
\r
64 MCview.PDBCanvas pdbCanvas;
\r
65 public SequenceRenderer getSequenceRenderer()
\r
70 public FeatureRenderer getFeatureRenderer()
\r
75 public void setPDBCanvas(MCview.PDBCanvas pc)
\r
80 public AlignViewport getViewport()
\r
89 * @param g DOCUMENT ME!
\r
90 * @param startx DOCUMENT ME!
\r
91 * @param endx DOCUMENT ME!
\r
92 * @param ypos DOCUMENT ME!
\r
94 void drawNorthScale(Graphics g, int startx, int endx, int ypos)
\r
96 int scalestartx = startx - (startx % 10) + 10;
\r
98 g.setColor(Color.black);
\r
101 for (int i = scalestartx; i < endx; i += 10)
\r
103 String string = String.valueOf(i);
\r
104 g.drawString(string, (i - startx - 1) * av.charWidth,
\r
105 ypos - (av.charHeight / 2));
\r
107 g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
\r
108 (ypos + 2) - (av.charHeight / 2),
\r
109 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -
\r
117 * @param g DOCUMENT ME!
\r
118 * @param startx DOCUMENT ME!
\r
119 * @param endx DOCUMENT ME!
\r
120 * @param ypos DOCUMENT ME!
\r
122 void drawWestScale(Graphics g, int startx, int endx, int ypos)
\r
124 FontMetrics fm = getFontMetrics(av.getFont());
\r
125 ypos += av.charHeight;
\r
128 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
130 SequenceI seq = av.alignment.getSequenceAt(i);
\r
131 int index = startx;
\r
134 while (index < endx)
\r
136 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
143 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
150 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))-av.charWidth/2;
\r
151 g.drawString(value + "", x,
\r
152 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
160 * @param g DOCUMENT ME!
\r
161 * @param startx DOCUMENT ME!
\r
162 * @param endx DOCUMENT ME!
\r
163 * @param ypos DOCUMENT ME!
\r
165 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
167 ypos += av.charHeight;
\r
170 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
172 SequenceI seq = av.alignment.getSequenceAt(i);
\r
176 while (index > startx)
\r
178 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
185 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
192 g.drawString(String.valueOf(value), 0,
\r
193 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
201 * @param horizontal DOCUMENT ME!
\r
202 * @param vertical DOCUMENT ME!
\r
204 public void fastPaint(int horizontal, int vertical)
\r
206 if (fastPaint || gg == null)
\r
213 gg.copyArea(horizontal * av.charWidth,
\r
214 vertical * av.charHeight,
\r
217 -horizontal * av.charWidth,
\r
218 -vertical * av.charHeight);
\r
220 int sr = av.startRes;
\r
221 int er = av.endRes;
\r
222 int ss = av.startSeq;
\r
223 int es = av.endSeq;
\r
227 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
230 transX = (er - sr - horizontal) * av.charWidth;
\r
231 sr = er - horizontal;
\r
233 else if (horizontal < 0)
\r
235 er = sr - horizontal-1;
\r
237 else if (vertical > 0) // scroll down
\r
239 ss = es - vertical;
\r
241 if (ss < av.startSeq)
\r
242 { // ie scrolling too fast, more than a page at a time
\r
247 transY = imgHeight - (vertical * av.charHeight);
\r
250 else if (vertical < 0)
\r
252 es = ss - vertical;
\r
254 if (es > av.endSeq)
\r
260 gg.translate(transX, transY);
\r
261 drawPanel(gg, sr, er, ss, es, sr, ss, 0);
\r
262 gg.translate(-transX, -transY);
\r
268 * Definitions of startx and endx (hopefully):
\r
269 * SMJS This is what I'm working towards!
\r
270 * startx is the first residue (starting at 0) to display.
\r
271 * endx is the last residue to display (starting at 0).
\r
272 * starty is the first sequence to display (starting at 0).
\r
273 * endy is the last sequence to display (starting at 0).
\r
274 * NOTE 1: The av limits are set in setFont in this class and
\r
275 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
278 // Set this to false to force a full panel paint
\r
279 public void paintComponent(Graphics g)
\r
281 super.paintComponent(g);
\r
283 sr.renderGaps(av.renderGaps);
\r
285 if ( img != null && (fastPaint
\r
286 || (getVisibleRect().width != g.getClipBounds().width)
\r
287 || (getVisibleRect().height != g.getClipBounds().height)))
\r
289 g.drawImage(img, 0, 0, this);
\r
295 // this draws the whole of the alignment
\r
296 imgWidth = getWidth();
\r
297 imgHeight = getHeight();
\r
299 imgWidth -= (imgWidth % av.charWidth);
\r
300 imgHeight -= (imgHeight % av.charHeight);
\r
302 if ((imgWidth < 1) || (imgHeight < 1))
\r
307 img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
\r
308 gg = (Graphics2D) img.getGraphics();
\r
309 gg.setFont(av.getFont());
\r
312 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
313 RenderingHints.VALUE_ANTIALIAS_ON);
\r
315 gg.setColor(Color.white);
\r
316 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
319 if (av.getWrapAlignment())
\r
321 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
325 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq,
\r
326 av.startRes, av.startSeq, 0);
\r
329 g.drawImage(img, 0, 0, this);
\r
331 if (pdbCanvas != null)
\r
333 pdbCanvas.updateSeqColours();
\r
341 * @param cwidth DOCUMENT ME!
\r
343 * @return DOCUMENT ME!
\r
345 public int getWrappedCanvasWidth(int cwidth)
\r
347 FontMetrics fm = getFontMetrics(av.getFont());
\r
352 if (av.scaleRightWrapped)
\r
354 LABEL_EAST = fm.stringWidth(getMask());
\r
357 if (av.scaleLeftWrapped)
\r
359 LABEL_WEST = fm.stringWidth(getMask());
\r
362 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
367 * Generates a string of zeroes.
\r
372 String mask = "00";
\r
373 for (int i = av.alignment.getWidth(); i > 0; i /= 10)
\r
383 * @param g DOCUMENT ME!
\r
384 * @param canvasWidth DOCUMENT ME!
\r
385 * @param canvasHeight DOCUMENT ME!
\r
386 * @param startRes DOCUMENT ME!
\r
388 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
392 AlignmentI al = av.getAlignment();
\r
394 FontMetrics fm = getFontMetrics(av.getFont());
\r
396 int LABEL_EAST = 0;
\r
398 if (av.scaleRightWrapped)
\r
400 LABEL_EAST = fm.stringWidth(getMask());
\r
403 int LABEL_WEST = 0;
\r
405 if (av.scaleLeftWrapped)
\r
407 LABEL_WEST = fm.stringWidth(getMask());
\r
410 int hgap = av.charHeight;
\r
411 if(av.scaleAboveWrapped)
\r
412 hgap += av.charHeight;
\r
414 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
415 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
417 av.setWrappedWidth(cWidth);
\r
419 av.endRes = av.startRes + cWidth;
\r
422 int endx = (startRes + cWidth) - 1;
\r
426 while ((ypos <= canvasHeight) && (startRes < av.alignment.getWidth()))
\r
428 g.setFont(av.getFont());
\r
429 g.setColor(Color.black);
\r
431 if (av.scaleLeftWrapped)
\r
433 drawWestScale(g, startRes, endx, ypos);
\r
436 if (av.scaleRightWrapped)
\r
438 g.translate(canvasWidth - LABEL_EAST, 0);
\r
439 drawEastScale(g, startRes, endx, ypos);
\r
440 g.translate(-(canvasWidth - LABEL_EAST), 0);
\r
443 g.translate(LABEL_WEST, 0);
\r
445 if (av.scaleAboveWrapped)
\r
447 drawNorthScale(g, startRes, endx, ypos);
\r
450 // When printing we have an extra clipped region,
\r
451 // the Printable page which we need to account for here
\r
452 Shape clip = g.getClip();
\r
456 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
460 g.setClip(0, (int) clip.getBounds().getY(),
\r
461 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
464 if (av.vconsensus!=null && av.alignment.getWidth() >= av.vconsensus.size())
\r
466 endx = av.vconsensus.size() - 2;
\r
470 drawPanel(g, startRes, endx, 0, al.getHeight(), startRes, 0, ypos);
\r
472 if(av.showAnnotation)
\r
474 g.translate(0, cHeight + ypos + 3);
\r
475 if(annotations==null)
\r
476 annotations = new AnnotationPanel(av);
\r
478 annotations.drawComponent( (Graphics2D) g, startRes, endx + 1);
\r
479 g.translate(0, -cHeight - ypos);
\r
482 g.translate(-LABEL_WEST, 0);
\r
484 ypos += cHeight+getAnnotationHeight()+hgap;
\r
485 if(av.showAnnotation)
\r
488 startRes += cWidth;
\r
489 endx = (startRes + cWidth) - 1;
\r
491 if (endx > al.getWidth())
\r
493 endx = al.getWidth();
\r
498 AnnotationPanel annotations;
\r
499 int getAnnotationHeight()
\r
501 if(!av.showAnnotation)
\r
504 if(annotations==null)
\r
505 annotations = new AnnotationPanel(av);
\r
507 return annotations.adjustPanelHeight();
\r
513 * @param g1 DOCUMENT ME!
\r
514 * @param x1 DOCUMENT ME!
\r
515 * @param x2 DOCUMENT ME!
\r
516 * @param y1 DOCUMENT ME!
\r
517 * @param y2 DOCUMENT ME!
\r
518 * @param startx DOCUMENT ME!
\r
519 * @param starty DOCUMENT ME!
\r
520 * @param offset DOCUMENT ME!
\r
523 float aaRatio = 2f/3f;
\r
524 public void increaseAARatio()
\r
533 public void decreaseAARation()
\r
543 void drawPanel(Graphics g1, int x1, int x2, int y1, int y2, int startx, int starty, int offset)
\r
545 Graphics2D g = (Graphics2D) g1;
\r
546 g.setFont(av.getFont());
\r
549 // int aaHeight = av.charHeight;
\r
550 // Font aafont = av.getFont();
\r
552 /* dnafont = null, , dna = null, dnaHeight = av.charHeight
\r
553 if (av.getShowTranslation())
\r
555 aaHeight = (int) (av.getCharHeight() * aaRatio);
\r
556 dnaHeight = (int) (av.getCharHeight() * (1 - aaRatio));
\r
557 java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform();
\r
558 transform.scale(1f / 3f, 1);
\r
559 dnafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
561 dnafont = dnafont.deriveFont(transform);
\r
563 aafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
565 transform = new java.awt.geom.AffineTransform();
\r
566 transform.scale(1 / aaRatio, 1);
\r
567 aafont = aafont.deriveFont(transform);
\r
572 /// First draw the sequences
\r
573 /////////////////////////////
\r
574 for (int i = y1; i < y2; i++)
\r
576 nextSeq = av.alignment.getSequenceAt(i);
\r
578 /* if(av.getShowTranslation())
\r
581 StringBuffer sb = new StringBuffer();
\r
582 for(int r = 0; r<nextSeq.getLength(); r+=3)
\r
584 String codon = nextSeq.getSequence(r, r+3);
\r
585 codon = codon.replace('U', 'T');
\r
586 String res = ResidueProperties.codonTranslate(codon);
\r
589 if (res.equals("STOP"))
\r
595 nextSeq = new Sequence("d", sb.toString());
\r
598 sr.drawSequence(g, nextSeq, av.alignment.findAllGroups(nextSeq),
\r
599 x1, x2, (x1 - startx) * av.charWidth,
\r
600 offset + ( (i - starty) * av.charHeight), av.charWidth,
\r
603 /* if(av.getShowTranslation())
\r
605 av.setGlobalColourScheme(new NucleotideColourScheme());
\r
606 g.setFont(dnafont);
\r
607 sr.drawSequence(g, dna, null,
\r
608 x1*3, (x2+2) * 3, ( (x1 - startx) * av.charWidth) / 3,
\r
609 offset + ( (i - starty) * av.charHeight) + aaHeight,
\r
612 av.setGlobalColourScheme(cs);
\r
615 if (av.showSequenceFeatures)
\r
617 fr.drawSequence(g, nextSeq, x1, x2,
\r
618 (x1 - startx) * av.charWidth,
\r
619 offset + ((i - starty) * av.charHeight), av.charWidth,
\r
623 /// Highlight search Results once all sequences have been drawn
\r
624 //////////////////////////////////////////////////////////
\r
625 if (searchResults != null)
\r
627 int[] visibleResults = searchResults.getResults(nextSeq, x1, x2);
\r
628 if (visibleResults != null)
\r
629 for (int r = 0; r < visibleResults.length; r += 2)
\r
631 sr.drawHighlightedText(nextSeq, visibleResults[r],
\r
632 visibleResults[r + 1],
\r
633 (visibleResults[r] - startx) * av.charWidth,
\r
634 offset + ( (i - starty) * av.charHeight),
\r
635 av.charWidth, av.charHeight);
\r
641 /////////////////////////////////////
\r
642 // Now outline any areas if necessary
\r
643 /////////////////////////////////////
\r
644 SequenceGroup group = av.getSelectionGroup();
\r
649 int groupIndex = -1;
\r
651 if ((group == null) && (av.alignment.getGroups().size() > 0))
\r
653 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
663 boolean inGroup = false;
\r
667 for (i = y1; i < y2; i++)
\r
669 sx = (group.getStartRes() - startx) * av.charWidth;
\r
670 sy = offset + ((i - starty) * av.charHeight);
\r
671 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
674 if(sx+ex<0 || sx>imgWidth)
\r
679 if ( (sx <= (x2-x1)*av.charWidth) &&
\r
680 group.sequences.contains(av.alignment.getSequenceAt(
\r
683 if ((bottom == -1) &&
\r
684 !group.sequences.contains(
\r
685 av.alignment.getSequenceAt(i + 1)))
\r
687 bottom = sy + av.charHeight;
\r
692 if (((top == -1) && (i == 0)) ||
\r
693 !group.sequences.contains(
\r
694 av.alignment.getSequenceAt(i - 1)))
\r
702 if (group == av.getSelectionGroup())
\r
704 g.setStroke(new BasicStroke(1,
\r
705 BasicStroke.CAP_BUTT,
\r
706 BasicStroke.JOIN_ROUND, 3f,
\r
707 new float[] { 5f, 3f }, 0f));
\r
708 g.setColor(Color.RED);
\r
712 g.setStroke(new BasicStroke());
\r
713 g.setColor(group.getOutlineColour());
\r
721 if (sx >= 0 && sx < imgWidth)
\r
722 g.drawLine(sx, oldY, sx, sy);
\r
724 if (sx + ex < imgWidth)
\r
725 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
733 if (sx + ex > imgWidth)
\r
736 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
737 ex = (x2 - x1 + 1) * av.charWidth;
\r
741 g.drawLine(sx, top, sx + ex, top);
\r
747 g.drawLine(sx, bottom, sx + ex, bottom);
\r
758 sy = offset + ( (i - starty) * av.charHeight);
\r
759 if (sx >= 0 && sx < imgWidth)
\r
760 g.drawLine(sx, oldY, sx, sy);
\r
762 if (sx + ex < imgWidth)
\r
763 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
771 if (sx + ex > imgWidth)
\r
773 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
774 ex = (x2 - x1 + 1) * av.charWidth;
\r
778 g.drawLine(sx, top, sx + ex, top);
\r
784 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
793 if (groupIndex >= av.alignment.getGroups().size())
\r
798 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
800 while (groupIndex < av.alignment.getGroups().size());
\r
807 * @param results DOCUMENT ME!
\r
809 public void highlightSearchResults(SearchResults results)
\r
813 searchResults = results;
\r