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 boolean displaySearch = false;
\r
45 int[] searchResults = null;
\r
46 boolean fastPaint = false;
\r
51 * Creates a new SeqCanvas object.
\r
53 * @param av DOCUMENT ME!
\r
55 public SeqCanvas(AlignViewport av)
\r
58 fr = new FeatureRenderer(av);
\r
59 sr = new SequenceRenderer(av);
\r
60 setLayout(new BorderLayout());
\r
61 PaintRefresher.Register(this, av.alignment);
\r
62 setBackground(Color.white);
\r
65 MCview.PDBCanvas pdbCanvas;
\r
66 public SequenceRenderer getSequenceRenderer()
\r
71 public FeatureRenderer getFeatureRenderer()
\r
76 public void setPDBCanvas(MCview.PDBCanvas pc)
\r
81 public AlignViewport getViewport()
\r
90 * @param g DOCUMENT ME!
\r
91 * @param startx DOCUMENT ME!
\r
92 * @param endx DOCUMENT ME!
\r
93 * @param ypos DOCUMENT ME!
\r
95 void drawNorthScale(Graphics g, int startx, int endx, int ypos)
\r
97 int scalestartx = startx - (startx % 10) + 10;
\r
99 g.setColor(Color.black);
\r
102 for (int i = scalestartx; i < endx; i += 10)
\r
104 String string = String.valueOf(i);
\r
105 g.drawString(string, (i - startx - 1) * av.charWidth,
\r
106 ypos - (av.charHeight / 2));
\r
108 g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
\r
109 (ypos + 2) - (av.charHeight / 2),
\r
110 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -
\r
118 * @param g DOCUMENT ME!
\r
119 * @param startx DOCUMENT ME!
\r
120 * @param endx DOCUMENT ME!
\r
121 * @param ypos DOCUMENT ME!
\r
123 void drawWestScale(Graphics g, int startx, int endx, int ypos)
\r
125 FontMetrics fm = getFontMetrics(av.getFont());
\r
126 ypos += av.charHeight;
\r
129 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
131 SequenceI seq = av.alignment.getSequenceAt(i);
\r
132 int index = startx;
\r
135 while (index < endx)
\r
137 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
144 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
151 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))-av.charWidth/2;
\r
152 g.drawString(value + "", x,
\r
153 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
161 * @param g DOCUMENT ME!
\r
162 * @param startx DOCUMENT ME!
\r
163 * @param endx DOCUMENT ME!
\r
164 * @param ypos DOCUMENT ME!
\r
166 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
168 ypos += av.charHeight;
\r
171 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
173 SequenceI seq = av.alignment.getSequenceAt(i);
\r
177 while (index > startx)
\r
179 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
186 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
193 g.drawString(String.valueOf(value), 0,
\r
194 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
202 * @param horizontal DOCUMENT ME!
\r
203 * @param vertical DOCUMENT ME!
\r
205 public void fastPaint(int horizontal, int vertical)
\r
207 if (fastPaint || gg == null)
\r
214 gg.copyArea(horizontal * av.charWidth,
\r
215 vertical * av.charHeight,
\r
218 -horizontal * av.charWidth,
\r
219 -vertical * av.charHeight);
\r
221 int sr = av.startRes;
\r
222 int er = av.endRes;
\r
223 int ss = av.startSeq;
\r
224 int es = av.endSeq;
\r
228 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
231 transX = (er - sr - horizontal) * av.charWidth;
\r
232 sr = er - horizontal;
\r
234 else if (horizontal < 0)
\r
236 er = sr - horizontal-1;
\r
238 else if (vertical > 0) // scroll down
\r
240 ss = es - vertical;
\r
242 if (ss < av.startSeq)
\r
243 { // ie scrolling too fast, more than a page at a time
\r
248 transY = imgHeight - (vertical * av.charHeight);
\r
251 else if (vertical < 0)
\r
253 es = ss - vertical;
\r
255 if (es > av.endSeq)
\r
261 gg.translate(transX, transY);
\r
262 drawPanel(gg, sr, er, ss, es, sr, ss, 0);
\r
263 gg.translate(-transX, -transY);
\r
269 * Definitions of startx and endx (hopefully):
\r
270 * SMJS This is what I'm working towards!
\r
271 * startx is the first residue (starting at 0) to display.
\r
272 * endx is the last residue to display (starting at 0).
\r
273 * starty is the first sequence to display (starting at 0).
\r
274 * endy is the last sequence to display (starting at 0).
\r
275 * NOTE 1: The av limits are set in setFont in this class and
\r
276 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
279 // Set this to false to force a full panel paint
\r
280 public void paintComponent(Graphics g)
\r
282 super.paintComponent(g);
\r
284 sr.renderGaps(av.renderGaps);
\r
286 if ( img != null && (fastPaint
\r
287 || (getVisibleRect().width != g.getClipBounds().width)
\r
288 || (getVisibleRect().height != g.getClipBounds().height)))
\r
290 g.drawImage(img, 0, 0, this);
\r
296 // this draws the whole of the alignment
\r
297 imgWidth = getWidth();
\r
298 imgHeight = getHeight();
\r
300 imgWidth -= (imgWidth % av.charWidth);
\r
301 imgHeight -= (imgHeight % av.charHeight);
\r
303 if ((imgWidth < 1) || (imgHeight < 1))
\r
308 img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
\r
309 gg = (Graphics2D) img.getGraphics();
\r
310 gg.setFont(av.getFont());
\r
313 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
314 RenderingHints.VALUE_ANTIALIAS_ON);
\r
316 gg.setColor(Color.white);
\r
317 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
320 if (av.getWrapAlignment())
\r
322 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
326 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq,
\r
327 av.startRes, av.startSeq, 0);
\r
330 g.drawImage(img, 0, 0, this);
\r
332 if (pdbCanvas != null)
\r
334 pdbCanvas.updateSeqColours();
\r
342 * @param cwidth DOCUMENT ME!
\r
344 * @return DOCUMENT ME!
\r
346 public int getWrappedCanvasWidth(int cwidth)
\r
348 FontMetrics fm = getFontMetrics(av.getFont());
\r
353 if (av.scaleRightWrapped)
\r
355 LABEL_EAST = fm.stringWidth(getMask());
\r
358 if (av.scaleLeftWrapped)
\r
360 LABEL_WEST = fm.stringWidth(getMask());
\r
363 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
368 * Generates a string of zeroes.
\r
373 String mask = "00";
\r
374 for (int i = av.alignment.getWidth(); i > 0; i /= 10)
\r
384 * @param g DOCUMENT ME!
\r
385 * @param canvasWidth DOCUMENT ME!
\r
386 * @param canvasHeight DOCUMENT ME!
\r
387 * @param startRes DOCUMENT ME!
\r
389 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
393 AlignmentI al = av.getAlignment();
\r
395 FontMetrics fm = getFontMetrics(av.getFont());
\r
397 int LABEL_EAST = 0;
\r
399 if (av.scaleRightWrapped)
\r
401 LABEL_EAST = fm.stringWidth(getMask());
\r
404 int LABEL_WEST = 0;
\r
406 if (av.scaleLeftWrapped)
\r
408 LABEL_WEST = fm.stringWidth(getMask());
\r
411 int hgap = av.charHeight;
\r
412 if(av.scaleAboveWrapped)
\r
413 hgap += av.charHeight;
\r
415 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
416 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
418 av.setWrappedWidth(cWidth);
\r
420 av.endRes = av.startRes + cWidth;
\r
423 int endx = (startRes + cWidth) - 1;
\r
427 while ((ypos <= canvasHeight) && (startRes < av.alignment.getWidth()))
\r
429 g.setFont(av.getFont());
\r
430 g.setColor(Color.black);
\r
432 if (av.scaleLeftWrapped)
\r
434 drawWestScale(g, startRes, endx, ypos);
\r
437 if (av.scaleRightWrapped)
\r
439 g.translate(canvasWidth - LABEL_EAST, 0);
\r
440 drawEastScale(g, startRes, endx, ypos);
\r
441 g.translate(-(canvasWidth - LABEL_EAST), 0);
\r
444 g.translate(LABEL_WEST, 0);
\r
446 if (av.scaleAboveWrapped)
\r
448 drawNorthScale(g, startRes, endx, ypos);
\r
451 // When printing we have an extra clipped region,
\r
452 // the Printable page which we need to account for here
\r
453 Shape clip = g.getClip();
\r
457 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
461 g.setClip(0, (int) clip.getBounds().getY(),
\r
462 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
465 if (av.vconsensus!=null && av.alignment.getWidth() >= av.vconsensus.size())
\r
467 endx = av.vconsensus.size() - 2;
\r
471 drawPanel(g, startRes, endx, 0, al.getHeight(), startRes, 0, ypos);
\r
473 if(av.showAnnotation)
\r
475 g.translate(0, cHeight + ypos + 3);
\r
476 if(annotations==null)
\r
477 annotations = new AnnotationPanel(av);
\r
479 annotations.drawComponent( (Graphics2D) g, startRes, endx + 1);
\r
480 g.translate(0, -cHeight - ypos);
\r
483 g.translate(-LABEL_WEST, 0);
\r
485 ypos += cHeight+getAnnotationHeight()+hgap;
\r
486 if(av.showAnnotation)
\r
489 startRes += cWidth;
\r
490 endx = (startRes + cWidth) - 1;
\r
492 if (endx > al.getWidth())
\r
494 endx = al.getWidth();
\r
499 AnnotationPanel annotations;
\r
500 int getAnnotationHeight()
\r
502 if(!av.showAnnotation)
\r
505 if(annotations==null)
\r
506 annotations = new AnnotationPanel(av);
\r
508 return annotations.adjustPanelHeight();
\r
514 * @param g1 DOCUMENT ME!
\r
515 * @param x1 DOCUMENT ME!
\r
516 * @param x2 DOCUMENT ME!
\r
517 * @param y1 DOCUMENT ME!
\r
518 * @param y2 DOCUMENT ME!
\r
519 * @param startx DOCUMENT ME!
\r
520 * @param starty DOCUMENT ME!
\r
521 * @param offset DOCUMENT ME!
\r
524 float aaRatio = 2f/3f;
\r
525 public void increaseAARatio()
\r
534 public void decreaseAARation()
\r
544 void drawPanel(Graphics g1, int x1, int x2, int y1, int y2, int startx, int starty, int offset)
\r
546 Graphics2D g = (Graphics2D) g1;
\r
547 g.setFont(av.getFont());
\r
550 // int aaHeight = av.charHeight;
\r
551 // Font aafont = av.getFont();
\r
553 /* dnafont = null, , dna = null, dnaHeight = av.charHeight
\r
554 if (av.getShowTranslation())
\r
556 aaHeight = (int) (av.getCharHeight() * aaRatio);
\r
557 dnaHeight = (int) (av.getCharHeight() * (1 - aaRatio));
\r
558 java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform();
\r
559 transform.scale(1f / 3f, 1);
\r
560 dnafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
562 dnafont = dnafont.deriveFont(transform);
\r
564 aafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
566 transform = new java.awt.geom.AffineTransform();
\r
567 transform.scale(1 / aaRatio, 1);
\r
568 aafont = aafont.deriveFont(transform);
\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
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