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 pdbViewer;
\r
69 public SequenceRenderer getSequenceRenderer()
\r
74 public FeatureRenderer getFeatureRenderer()
\r
79 public void setPDBViewer(MCview.PDBCanvas pc)
\r
87 * @param g DOCUMENT ME!
\r
88 * @param startx DOCUMENT ME!
\r
89 * @param endx DOCUMENT ME!
\r
90 * @param ypos DOCUMENT ME!
\r
92 void drawNorthScale(Graphics g, int startx, int endx, int ypos)
\r
94 int scalestartx = startx - (startx % 10) + 10;
\r
96 g.setColor(Color.black);
\r
99 for (int i = scalestartx; i < endx; i += 10)
\r
101 String string = String.valueOf(i);
\r
102 g.drawString(string, (i - startx - 1) * av.charWidth,
\r
103 ypos - (av.charHeight / 2));
\r
105 g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
\r
106 (ypos + 2) - (av.charHeight / 2),
\r
107 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -
\r
115 * @param g DOCUMENT ME!
\r
116 * @param startx DOCUMENT ME!
\r
117 * @param endx DOCUMENT ME!
\r
118 * @param ypos DOCUMENT ME!
\r
120 void drawWestScale(Graphics g, int startx, int endx, int ypos)
\r
122 FontMetrics fm = getFontMetrics(av.getFont());
\r
123 ypos += av.charHeight;
\r
126 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
128 SequenceI seq = av.alignment.getSequenceAt(i);
\r
129 int index = startx;
\r
132 while (index < endx)
\r
134 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
141 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
148 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))-av.charWidth/2;
\r
149 g.drawString(value + "", x,
\r
150 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
158 * @param g DOCUMENT ME!
\r
159 * @param startx DOCUMENT ME!
\r
160 * @param endx DOCUMENT ME!
\r
161 * @param ypos DOCUMENT ME!
\r
163 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
165 ypos += av.charHeight;
\r
168 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
170 SequenceI seq = av.alignment.getSequenceAt(i);
\r
174 while (index > startx)
\r
176 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
183 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
190 g.drawString(String.valueOf(value), av.charWidth/2,
\r
191 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
199 * @param horizontal DOCUMENT ME!
\r
200 * @param vertical DOCUMENT ME!
\r
202 public void fastPaint(int horizontal, int vertical)
\r
209 gg.copyArea(horizontal * av.charWidth,
\r
210 vertical * av.charHeight,
\r
213 -horizontal * av.charWidth,
\r
214 -vertical * av.charHeight);
\r
216 int sr = av.startRes;
\r
217 int er = av.endRes;
\r
218 int ss = av.startSeq;
\r
219 int es = av.endSeq;
\r
223 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
226 transX = (er - sr - horizontal) * av.charWidth;
\r
227 sr = er - horizontal;
\r
229 else if (horizontal < 0)
\r
231 er = sr - horizontal-1;
\r
233 else if (vertical > 0) // scroll down
\r
235 ss = es - vertical;
\r
237 if (ss < av.startSeq)
\r
238 { // ie scrolling too fast, more than a page at a time
\r
243 transY = imgHeight - (vertical * av.charHeight);
\r
246 else if (vertical < 0)
\r
248 es = ss - vertical;
\r
250 if (es > av.endSeq)
\r
256 gg.translate(transX, transY);
\r
257 drawPanel(gg, sr, er, ss, es, sr, ss, 0);
\r
258 gg.translate(-transX, -transY);
\r
265 * Definitions of startx and endx (hopefully):
\r
266 * SMJS This is what I'm working towards!
\r
267 * startx is the first residue (starting at 0) to display.
\r
268 * endx is the last residue to display (starting at 0).
\r
269 * starty is the first sequence to display (starting at 0).
\r
270 * endy is the last sequence to display (starting at 0).
\r
271 * NOTE 1: The av limits are set in setFont in this class and
\r
272 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
275 // Set this to false to force a full panel paint
\r
276 public void paintComponent(Graphics g)
\r
278 sr.renderGaps(av.renderGaps);
\r
280 if ((img != null) &&
\r
281 (fastPaint || (getWidth() != g.getClipBounds().width) ||
\r
282 (getHeight() != g.getClipBounds().height)))
\r
284 g.drawImage(img, 0, 0, this);
\r
289 // this draws the whole of the alignment
\r
290 imgWidth = getWidth();
\r
291 imgHeight = getHeight();
\r
293 imgWidth -= (imgWidth % av.charWidth);
\r
294 imgHeight -= (imgHeight % av.charHeight);
\r
296 if ((imgWidth < 1) || (imgHeight < 1))
\r
302 img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
\r
303 gg = (Graphics2D) img.getGraphics();
\r
304 gg.setFont(av.getFont());
\r
305 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
306 RenderingHints.VALUE_ANTIALIAS_ON);
\r
308 gg.setColor(Color.white);
\r
309 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
312 if (av.getWrapAlignment())
\r
314 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
318 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq,
\r
319 av.startRes, av.startSeq, 0);
\r
322 g.drawImage(img, 0, 0, this);
\r
324 if (pdbViewer != null)
\r
326 pdbViewer.updateSeqColours();
\r
334 * @param cwidth DOCUMENT ME!
\r
336 * @return DOCUMENT ME!
\r
338 public int getWrappedCanvasWidth(int cwidth)
\r
340 FontMetrics fm = getFontMetrics(av.getFont());
\r
345 if (av.scaleRightWrapped)
\r
347 LABEL_EAST = fm.stringWidth(getMask()+"0");
\r
350 if (av.scaleLeftWrapped)
\r
352 LABEL_WEST = fm.stringWidth(getMask());
\r
355 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
360 * Generates a string of zeroes.
\r
366 for (int i = av.alignment.getWidth(); i > 0; i /= 10)
\r
376 * @param g DOCUMENT ME!
\r
377 * @param canvasWidth DOCUMENT ME!
\r
378 * @param canvasHeight DOCUMENT ME!
\r
379 * @param startRes DOCUMENT ME!
\r
381 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
385 AlignmentI al = av.getAlignment();
\r
387 FontMetrics fm = getFontMetrics(av.getFont());
\r
389 int LABEL_EAST = 0;
\r
391 if (av.scaleRightWrapped)
\r
393 LABEL_EAST = fm.stringWidth(getMask()+"0");
\r
396 int LABEL_WEST = 0;
\r
398 if (av.scaleLeftWrapped)
\r
400 LABEL_WEST = fm.stringWidth(getMask());
\r
403 int hgap = av.charHeight;
\r
404 if(av.scaleAboveWrapped)
\r
405 hgap += av.charHeight;
\r
407 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
408 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
410 av.setWrappedWidth(cWidth);
\r
412 av.endRes = av.startRes + cWidth;
\r
415 int endx = (startRes + cWidth) - 1;
\r
419 while ((ypos <= canvasHeight) && (startRes < av.alignment.getWidth()))
\r
421 g.setFont(av.getFont());
\r
422 g.setColor(Color.black);
\r
424 if (av.scaleLeftWrapped)
\r
426 drawWestScale(g, startRes, endx, ypos);
\r
429 if (av.scaleRightWrapped)
\r
431 g.translate(canvasWidth - LABEL_EAST, 0);
\r
432 drawEastScale(g, startRes, endx, ypos);
\r
433 g.translate(-(canvasWidth - LABEL_EAST), 0);
\r
436 g.translate(LABEL_WEST, 0);
\r
438 if (av.scaleAboveWrapped)
\r
440 drawNorthScale(g, startRes, endx, ypos);
\r
443 // When printing we have an extra clipped region,
\r
444 // the Printable page which we need to account for here
\r
445 Shape clip = g.getClip();
\r
449 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
453 g.setClip(0, (int) clip.getBounds().getY(),
\r
454 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
457 if (av.vconsensus!=null && av.alignment.getWidth() >= av.vconsensus.size())
\r
459 endx = av.vconsensus.size() - 2;
\r
463 drawPanel(g, startRes, endx, 0, al.getHeight(), startRes, 0, ypos);
\r
465 if(av.showAnnotation)
\r
467 g.translate(0, cHeight + ypos + 3);
\r
468 if(annotations==null)
\r
469 annotations = new AnnotationPanel(av);
\r
471 annotations.drawComponent( (Graphics2D) g, startRes, endx + 1);
\r
472 g.translate(0, -cHeight - ypos);
\r
475 g.translate(-LABEL_WEST, 0);
\r
477 ypos += cHeight+getAnnotationHeight()+hgap;
\r
478 if(av.showAnnotation)
\r
481 startRes += cWidth;
\r
482 endx = (startRes + cWidth) - 1;
\r
484 if (endx > al.getWidth())
\r
486 endx = al.getWidth();
\r
491 AnnotationPanel annotations;
\r
492 int getAnnotationHeight()
\r
494 if(!av.showAnnotation)
\r
497 if(annotations==null)
\r
498 annotations = new AnnotationPanel(av);
\r
500 return annotations.adjustPanelHeight();
\r
506 * @param g1 DOCUMENT ME!
\r
507 * @param x1 DOCUMENT ME!
\r
508 * @param x2 DOCUMENT ME!
\r
509 * @param y1 DOCUMENT ME!
\r
510 * @param y2 DOCUMENT ME!
\r
511 * @param startx DOCUMENT ME!
\r
512 * @param starty DOCUMENT ME!
\r
513 * @param offset DOCUMENT ME!
\r
516 float aaRatio = 2f/3f;
\r
517 public void increaseAARatio()
\r
526 public void decreaseAARation()
\r
536 synchronized public void drawPanel(Graphics g1, int x1, int x2, int y1,
\r
537 int y2, int startx, int starty, int offset)
\r
539 Graphics2D g = (Graphics2D) g1;
\r
540 g.setFont(av.getFont());
\r
542 SequenceI nextSeq, dna = null;
\r
543 int aaHeight = av.charHeight, dnaHeight = av.charHeight;
\r
544 Font dnafont = null, aafont = av.getFont();
\r
546 /* if (av.getShowTranslation())
\r
548 aaHeight = (int) (av.getCharHeight() * aaRatio);
\r
549 dnaHeight = (int) (av.getCharHeight() * (1 - aaRatio));
\r
550 java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform();
\r
551 transform.scale(1f / 3f, 1);
\r
552 dnafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
554 dnafont = dnafont.deriveFont(transform);
\r
556 aafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
558 transform = new java.awt.geom.AffineTransform();
\r
559 transform.scale(1 / aaRatio, 1);
\r
560 aafont = aafont.deriveFont(transform);
\r
564 ColourSchemeI cs = av.getGlobalColourScheme();
\r
566 /// First draw the sequences
\r
567 /////////////////////////////
\r
568 for (int i = y1; i < y2; i++)
\r
570 nextSeq = av.alignment.getSequenceAt(i);
\r
572 /* if(av.getShowTranslation())
\r
575 StringBuffer sb = new StringBuffer();
\r
576 for(int r = 0; r<nextSeq.getLength(); r+=3)
\r
578 String codon = nextSeq.getSequence(r, r+3);
\r
579 codon = codon.replace('U', 'T');
\r
580 String res = ResidueProperties.codonTranslate(codon);
\r
583 if (res.equals("STOP"))
\r
589 nextSeq = new Sequence("d", sb.toString());
\r
592 sr.drawSequence(g, nextSeq, av.alignment.findAllGroups(nextSeq),
\r
593 x1, x2, (x1 - startx) * av.charWidth,
\r
594 offset + ( (i - starty) * av.charHeight), av.charWidth,
\r
597 /* if(av.getShowTranslation())
\r
599 av.setGlobalColourScheme(new NucleotideColourScheme());
\r
600 g.setFont(dnafont);
\r
601 sr.drawSequence(g, dna, null,
\r
602 x1*3, (x2+2) * 3, ( (x1 - startx) * av.charWidth) / 3,
\r
603 offset + ( (i - starty) * av.charHeight) + aaHeight,
\r
606 av.setGlobalColourScheme(cs);
\r
609 if (av.showSequenceFeatures)
\r
611 fr.drawSequence(g, nextSeq, x1, x2,
\r
612 (x1 - startx) * av.charWidth,
\r
613 offset + ((i - starty) * av.charHeight), av.charWidth,
\r
619 /////////////////////////////////////
\r
620 // Now outline any areas if necessary
\r
621 /////////////////////////////////////
\r
622 SequenceGroup group = av.getSelectionGroup();
\r
627 int groupIndex = -1;
\r
629 if ((group == null) && (av.alignment.getGroups().size() > 0))
\r
631 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
635 if (group != null && !isOverview)
\r
641 boolean inGroup = false;
\r
645 for (i = y1; i < y2; i++)
\r
647 sx = (group.getStartRes() - startx) * av.charWidth;
\r
648 sy = offset + ((i - starty) * av.charHeight);
\r
649 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
652 if(sx+ex<0 || sx>imgWidth)
\r
657 if ( (sx <= (x2-x1)*av.charWidth) &&
\r
658 group.sequences.contains(av.alignment.getSequenceAt(
\r
661 if ((bottom == -1) &&
\r
662 !group.sequences.contains(
\r
663 av.alignment.getSequenceAt(i + 1)))
\r
665 bottom = sy + av.charHeight;
\r
670 if (((top == -1) && (i == 0)) ||
\r
671 !group.sequences.contains(
\r
672 av.alignment.getSequenceAt(i - 1)))
\r
680 if (group == av.getSelectionGroup())
\r
682 g.setStroke(new BasicStroke(1,
\r
683 BasicStroke.CAP_BUTT,
\r
684 BasicStroke.JOIN_ROUND, 3f,
\r
685 new float[] { 5f, 3f }, 0f));
\r
686 g.setColor(Color.RED);
\r
690 g.setStroke(new BasicStroke());
\r
691 g.setColor(group.getOutlineColour());
\r
699 if (sx >= 0 && sx < imgWidth)
\r
700 g.drawLine(sx, oldY, sx, sy);
\r
702 if (sx + ex < imgWidth)
\r
703 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
711 if (sx + ex > imgWidth)
\r
714 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
715 ex = (x2 - x1 + 1) * av.charWidth;
\r
719 g.drawLine(sx, top, sx + ex, top);
\r
725 g.drawLine(sx, bottom, sx + ex, bottom);
\r
736 sy = offset + ( (i - starty) * av.charHeight);
\r
737 if (sx >= 0 && sx < imgWidth)
\r
738 g.drawLine(sx, oldY, sx, sy);
\r
740 if (sx + ex < imgWidth)
\r
741 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
749 if (sx + ex > imgWidth)
\r
751 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
752 ex = (x2 - x1 + 1) * av.charWidth;
\r
756 g.drawLine(sx, top, sx + ex, top);
\r
762 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
771 if (groupIndex >= av.alignment.getGroups().size())
\r
776 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
778 while (groupIndex < av.alignment.getGroups().size());
\r
781 /// Highlight search Results once all sequences have been drawn
\r
782 //////////////////////////////////////////////////////////
\r
785 for (int r = 0; r < searchResults.length; r += 3)
\r
787 int searchSeq = searchResults[r];
\r
789 if ((searchSeq >= y1) && (searchSeq < y2))
\r
791 SequenceI seq = av.getAlignment().getSequenceAt(searchSeq);
\r
793 int searchStart = seq.findIndex(searchResults[r + 1]) - 1;
\r
794 int searchEnd = seq.findIndex(searchResults[r + 2]) - 1;
\r
796 SequenceRenderer ssr = (SequenceRenderer) sr;
\r
798 if (searchStart < x1)
\r
803 if (searchEnd > x2)
\r
808 ssr.drawHighlightedText(seq, searchStart, searchEnd,
\r
809 (searchStart - startx) * av.charWidth,
\r
810 offset + ((searchSeq - starty) * av.charHeight),
\r
811 av.charWidth, av.charHeight);
\r
820 * @param results DOCUMENT ME!
\r
822 public void highlightSearchResults(int[] results)
\r
824 // results are in the order sequence, startRes, endRes
\r
825 if (results == null)
\r
827 displaySearch = false;
\r
831 displaySearch = true;
\r
834 searchResults = results;
\r