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 MCview.PDBCanvas pdbCanvas;
\r
69 public SequenceRenderer getSequenceRenderer()
\r
74 public FeatureRenderer getFeatureRenderer()
\r
79 public void setPDBCanvas(MCview.PDBCanvas pc)
\r
84 public AlignViewport getViewport()
\r
93 * @param g DOCUMENT ME!
\r
94 * @param startx DOCUMENT ME!
\r
95 * @param endx DOCUMENT ME!
\r
96 * @param ypos DOCUMENT ME!
\r
98 void drawNorthScale(Graphics g, int startx, int endx, int ypos)
\r
100 int scalestartx = startx - (startx % 10) + 10;
\r
102 g.setColor(Color.black);
\r
105 for (int i = scalestartx; i < endx; i += 10)
\r
107 String string = String.valueOf(i);
\r
108 g.drawString(string, (i - startx - 1) * av.charWidth,
\r
109 ypos - (av.charHeight / 2));
\r
111 g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
\r
112 (ypos + 2) - (av.charHeight / 2),
\r
113 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -
\r
121 * @param g DOCUMENT ME!
\r
122 * @param startx DOCUMENT ME!
\r
123 * @param endx DOCUMENT ME!
\r
124 * @param ypos DOCUMENT ME!
\r
126 void drawWestScale(Graphics g, int startx, int endx, int ypos)
\r
128 FontMetrics fm = getFontMetrics(av.getFont());
\r
129 ypos += av.charHeight;
\r
132 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
134 SequenceI seq = av.alignment.getSequenceAt(i);
\r
135 int index = startx;
\r
138 while (index < endx)
\r
140 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
147 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
154 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))-av.charWidth/2;
\r
155 g.drawString(value + "", x,
\r
156 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
164 * @param g DOCUMENT ME!
\r
165 * @param startx DOCUMENT ME!
\r
166 * @param endx DOCUMENT ME!
\r
167 * @param ypos DOCUMENT ME!
\r
169 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
171 ypos += av.charHeight;
\r
174 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
176 SequenceI seq = av.alignment.getSequenceAt(i);
\r
180 while (index > startx)
\r
182 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
189 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
196 g.drawString(String.valueOf(value), av.charWidth/2,
\r
197 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
205 * @param horizontal DOCUMENT ME!
\r
206 * @param vertical DOCUMENT ME!
\r
208 public void fastPaint(int horizontal, int vertical)
\r
215 gg.copyArea(horizontal * av.charWidth,
\r
216 vertical * av.charHeight,
\r
219 -horizontal * av.charWidth,
\r
220 -vertical * av.charHeight);
\r
222 int sr = av.startRes;
\r
223 int er = av.endRes;
\r
224 int ss = av.startSeq;
\r
225 int es = av.endSeq;
\r
229 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
232 transX = (er - sr - horizontal) * av.charWidth;
\r
233 sr = er - horizontal;
\r
235 else if (horizontal < 0)
\r
237 er = sr - horizontal-1;
\r
239 else if (vertical > 0) // scroll down
\r
241 ss = es - vertical;
\r
243 if (ss < av.startSeq)
\r
244 { // ie scrolling too fast, more than a page at a time
\r
249 transY = imgHeight - (vertical * av.charHeight);
\r
252 else if (vertical < 0)
\r
254 es = ss - vertical;
\r
256 if (es > av.endSeq)
\r
262 gg.translate(transX, transY);
\r
263 drawPanel(gg, sr, er, ss, es, sr, ss, 0);
\r
264 gg.translate(-transX, -transY);
\r
271 * Definitions of startx and endx (hopefully):
\r
272 * SMJS This is what I'm working towards!
\r
273 * startx is the first residue (starting at 0) to display.
\r
274 * endx is the last residue to display (starting at 0).
\r
275 * starty is the first sequence to display (starting at 0).
\r
276 * endy is the last sequence to display (starting at 0).
\r
277 * NOTE 1: The av limits are set in setFont in this class and
\r
278 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
281 // Set this to false to force a full panel paint
\r
282 public void paintComponent(Graphics g)
\r
284 sr.renderGaps(av.renderGaps);
\r
286 if ((img != null) &&
\r
287 (fastPaint && getVisibleRect() != g.getClipBounds()))
\r
289 g.drawImage(img, 0, 0, this);
\r
297 // this draws the whole of the alignment
\r
298 imgWidth = getWidth();
\r
299 imgHeight = getHeight();
\r
301 imgWidth -= (imgWidth % av.charWidth);
\r
302 imgHeight -= (imgHeight % av.charHeight);
\r
304 if ((imgWidth < 1) || (imgHeight < 1))
\r
309 img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
\r
310 gg = (Graphics2D) img.getGraphics();
\r
311 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()+"0");
\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
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()+"0");
\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 synchronized public void drawPanel(Graphics g1, int x1, int x2, int y1,
\r
544 int y2, int startx, int starty, int offset)
\r
546 Graphics2D g = (Graphics2D) g1;
\r
547 g.setFont(av.getFont());
\r
549 SequenceI nextSeq, dna = null;
\r
550 int aaHeight = av.charHeight, dnaHeight = av.charHeight;
\r
551 Font dnafont = null, aafont = av.getFont();
\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
571 ColourSchemeI cs = av.getGlobalColourScheme();
\r
573 /// First draw the sequences
\r
574 /////////////////////////////
\r
575 for (int i = y1; i < y2; i++)
\r
577 nextSeq = av.alignment.getSequenceAt(i);
\r
579 /* if(av.getShowTranslation())
\r
582 StringBuffer sb = new StringBuffer();
\r
583 for(int r = 0; r<nextSeq.getLength(); r+=3)
\r
585 String codon = nextSeq.getSequence(r, r+3);
\r
586 codon = codon.replace('U', 'T');
\r
587 String res = ResidueProperties.codonTranslate(codon);
\r
590 if (res.equals("STOP"))
\r
596 nextSeq = new Sequence("d", sb.toString());
\r
599 sr.drawSequence(g, nextSeq, av.alignment.findAllGroups(nextSeq),
\r
600 x1, x2, (x1 - startx) * av.charWidth,
\r
601 offset + ( (i - starty) * av.charHeight), av.charWidth,
\r
604 /* if(av.getShowTranslation())
\r
606 av.setGlobalColourScheme(new NucleotideColourScheme());
\r
607 g.setFont(dnafont);
\r
608 sr.drawSequence(g, dna, null,
\r
609 x1*3, (x2+2) * 3, ( (x1 - startx) * av.charWidth) / 3,
\r
610 offset + ( (i - starty) * av.charHeight) + aaHeight,
\r
613 av.setGlobalColourScheme(cs);
\r
616 if (av.showSequenceFeatures)
\r
618 fr.drawSequence(g, nextSeq, x1, x2,
\r
619 (x1 - startx) * av.charWidth,
\r
620 offset + ((i - starty) * av.charHeight), av.charWidth,
\r
626 /////////////////////////////////////
\r
627 // Now outline any areas if necessary
\r
628 /////////////////////////////////////
\r
629 SequenceGroup group = av.getSelectionGroup();
\r
634 int groupIndex = -1;
\r
636 if ((group == null) && (av.alignment.getGroups().size() > 0))
\r
638 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
642 if (group != null && !isOverview)
\r
648 boolean inGroup = false;
\r
652 for (i = y1; i < y2; i++)
\r
654 sx = (group.getStartRes() - startx) * av.charWidth;
\r
655 sy = offset + ((i - starty) * av.charHeight);
\r
656 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
659 if(sx+ex<0 || sx>imgWidth)
\r
664 if ( (sx <= (x2-x1)*av.charWidth) &&
\r
665 group.sequences.contains(av.alignment.getSequenceAt(
\r
668 if ((bottom == -1) &&
\r
669 !group.sequences.contains(
\r
670 av.alignment.getSequenceAt(i + 1)))
\r
672 bottom = sy + av.charHeight;
\r
677 if (((top == -1) && (i == 0)) ||
\r
678 !group.sequences.contains(
\r
679 av.alignment.getSequenceAt(i - 1)))
\r
687 if (group == av.getSelectionGroup())
\r
689 g.setStroke(new BasicStroke(1,
\r
690 BasicStroke.CAP_BUTT,
\r
691 BasicStroke.JOIN_ROUND, 3f,
\r
692 new float[] { 5f, 3f }, 0f));
\r
693 g.setColor(Color.RED);
\r
697 g.setStroke(new BasicStroke());
\r
698 g.setColor(group.getOutlineColour());
\r
706 if (sx >= 0 && sx < imgWidth)
\r
707 g.drawLine(sx, oldY, sx, sy);
\r
709 if (sx + ex < imgWidth)
\r
710 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
718 if (sx + ex > imgWidth)
\r
721 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
722 ex = (x2 - x1 + 1) * av.charWidth;
\r
726 g.drawLine(sx, top, sx + ex, top);
\r
732 g.drawLine(sx, bottom, sx + ex, bottom);
\r
743 sy = offset + ( (i - starty) * av.charHeight);
\r
744 if (sx >= 0 && sx < imgWidth)
\r
745 g.drawLine(sx, oldY, sx, sy);
\r
747 if (sx + ex < imgWidth)
\r
748 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
756 if (sx + ex > imgWidth)
\r
758 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
759 ex = (x2 - x1 + 1) * av.charWidth;
\r
763 g.drawLine(sx, top, sx + ex, top);
\r
769 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
778 if (groupIndex >= av.alignment.getGroups().size())
\r
783 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
785 while (groupIndex < av.alignment.getGroups().size());
\r
788 /// Highlight search Results once all sequences have been drawn
\r
789 //////////////////////////////////////////////////////////
\r
792 for (int r = 0; r < searchResults.length; r += 3)
\r
794 int searchSeq = searchResults[r];
\r
796 if ((searchSeq >= y1) && (searchSeq < y2))
\r
798 SequenceI seq = av.getAlignment().getSequenceAt(searchSeq);
\r
800 int searchStart = seq.findIndex(searchResults[r + 1]) - 1;
\r
801 int searchEnd = seq.findIndex(searchResults[r + 2]) - 1;
\r
803 SequenceRenderer ssr = (SequenceRenderer) sr;
\r
805 if (searchStart < x1)
\r
810 if (searchEnd > x2)
\r
815 ssr.drawHighlightedText(seq, searchStart, searchEnd,
\r
816 (searchStart - startx) * av.charWidth,
\r
817 offset + ((searchSeq - starty) * av.charHeight),
\r
818 av.charWidth, av.charHeight);
\r
827 * @param results DOCUMENT ME!
\r
829 public void highlightSearchResults(int[] results)
\r
831 // results are in the order sequence, startRes, endRes
\r
832 if (results == null)
\r
834 displaySearch = false;
\r
838 displaySearch = true;
\r
841 searchResults = results;
\r