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
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 || (getWidth() != g.getClipBounds().width) ||
\r
288 (getHeight() != g.getClipBounds().height)))
\r
290 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
308 img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
\r
309 gg = (Graphics2D) img.getGraphics();
\r
310 gg.setFont(av.getFont());
\r
311 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
312 RenderingHints.VALUE_ANTIALIAS_ON);
\r
314 gg.setColor(Color.white);
\r
315 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
318 if (av.getWrapAlignment())
\r
320 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
324 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq,
\r
325 av.startRes, av.startSeq, 0);
\r
328 g.drawImage(img, 0, 0, this);
\r
330 if (pdbViewer != null)
\r
332 pdbViewer.updateSeqColours();
\r
340 * @param cwidth DOCUMENT ME!
\r
342 * @return DOCUMENT ME!
\r
344 public int getWrappedCanvasWidth(int cwidth)
\r
346 FontMetrics fm = getFontMetrics(av.getFont());
\r
351 if (av.scaleRightWrapped)
\r
353 LABEL_EAST = fm.stringWidth(getMask()+"0");
\r
356 if (av.scaleLeftWrapped)
\r
358 LABEL_WEST = fm.stringWidth(getMask());
\r
361 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
366 * Generates a string of zeroes.
\r
372 for (int i = av.alignment.getWidth(); i > 0; i /= 10)
\r
382 * @param g DOCUMENT ME!
\r
383 * @param canvasWidth DOCUMENT ME!
\r
384 * @param canvasHeight DOCUMENT ME!
\r
385 * @param startRes DOCUMENT ME!
\r
387 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
391 AlignmentI al = av.getAlignment();
\r
393 FontMetrics fm = getFontMetrics(av.getFont());
\r
395 int LABEL_EAST = 0;
\r
397 if (av.scaleRightWrapped)
\r
399 LABEL_EAST = fm.stringWidth(getMask()+"0");
\r
402 int LABEL_WEST = 0;
\r
404 if (av.scaleLeftWrapped)
\r
406 LABEL_WEST = fm.stringWidth(getMask());
\r
409 int hgap = av.charHeight;
\r
410 if(av.scaleAboveWrapped)
\r
411 hgap += av.charHeight;
\r
413 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
414 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
416 av.setWrappedWidth(cWidth);
\r
418 av.endRes = av.startRes + cWidth;
\r
421 int endx = (startRes + cWidth) - 1;
\r
425 while ((ypos <= canvasHeight) && (startRes < av.alignment.getWidth()))
\r
427 g.setFont(av.getFont());
\r
428 g.setColor(Color.black);
\r
430 if (av.scaleLeftWrapped)
\r
432 drawWestScale(g, startRes, endx, ypos);
\r
435 if (av.scaleRightWrapped)
\r
437 g.translate(canvasWidth - LABEL_EAST, 0);
\r
438 drawEastScale(g, startRes, endx, ypos);
\r
439 g.translate(-(canvasWidth - LABEL_EAST), 0);
\r
442 g.translate(LABEL_WEST, 0);
\r
444 if (av.scaleAboveWrapped)
\r
446 drawNorthScale(g, startRes, endx, ypos);
\r
449 // When printing we have an extra clipped region,
\r
450 // the Printable page which we need to account for here
\r
451 Shape clip = g.getClip();
\r
455 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
459 g.setClip(0, (int) clip.getBounds().getY(),
\r
460 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
463 if (av.vconsensus!=null && av.alignment.getWidth() >= av.vconsensus.size())
\r
465 endx = av.vconsensus.size() - 2;
\r
469 drawPanel(g, startRes, endx, 0, al.getHeight(), startRes, 0, ypos);
\r
471 if(av.showAnnotation)
\r
473 g.translate(0, cHeight + ypos + 3);
\r
474 if(annotations==null)
\r
475 annotations = new AnnotationPanel(av);
\r
477 annotations.drawComponent( (Graphics2D) g, startRes, endx + 1);
\r
478 g.translate(0, -cHeight - ypos);
\r
481 g.translate(-LABEL_WEST, 0);
\r
483 ypos += cHeight+getAnnotationHeight()+hgap;
\r
484 if(av.showAnnotation)
\r
487 startRes += cWidth;
\r
488 endx = (startRes + cWidth) - 1;
\r
490 if (endx > al.getWidth())
\r
492 endx = al.getWidth();
\r
497 AnnotationPanel annotations;
\r
498 int getAnnotationHeight()
\r
500 if(!av.showAnnotation)
\r
503 if(annotations==null)
\r
504 annotations = new AnnotationPanel(av);
\r
506 return annotations.adjustPanelHeight();
\r
512 * @param g1 DOCUMENT ME!
\r
513 * @param x1 DOCUMENT ME!
\r
514 * @param x2 DOCUMENT ME!
\r
515 * @param y1 DOCUMENT ME!
\r
516 * @param y2 DOCUMENT ME!
\r
517 * @param startx DOCUMENT ME!
\r
518 * @param starty DOCUMENT ME!
\r
519 * @param offset DOCUMENT ME!
\r
522 float aaRatio = 2f/3f;
\r
523 public void increaseAARatio()
\r
532 public void decreaseAARation()
\r
542 synchronized public void drawPanel(Graphics g1, int x1, int x2, int y1,
\r
543 int y2, int startx, int starty, int offset)
\r
545 Graphics2D g = (Graphics2D) g1;
\r
546 g.setFont(av.getFont());
\r
548 SequenceI nextSeq, dna = null;
\r
549 int aaHeight = av.charHeight, dnaHeight = av.charHeight;
\r
550 Font dnafont = null, aafont = av.getFont();
\r
552 /* if (av.getShowTranslation())
\r
554 aaHeight = (int) (av.getCharHeight() * aaRatio);
\r
555 dnaHeight = (int) (av.getCharHeight() * (1 - aaRatio));
\r
556 java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform();
\r
557 transform.scale(1f / 3f, 1);
\r
558 dnafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
560 dnafont = dnafont.deriveFont(transform);
\r
562 aafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
564 transform = new java.awt.geom.AffineTransform();
\r
565 transform.scale(1 / aaRatio, 1);
\r
566 aafont = aafont.deriveFont(transform);
\r
570 ColourSchemeI cs = av.getGlobalColourScheme();
\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
625 /////////////////////////////////////
\r
626 // Now outline any areas if necessary
\r
627 /////////////////////////////////////
\r
628 SequenceGroup group = av.getSelectionGroup();
\r
633 int groupIndex = -1;
\r
635 if ((group == null) && (av.alignment.getGroups().size() > 0))
\r
637 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
641 if (group != null && !isOverview)
\r
647 boolean inGroup = false;
\r
651 for (i = y1; i < y2; i++)
\r
653 sx = (group.getStartRes() - startx) * av.charWidth;
\r
654 sy = offset + ((i - starty) * av.charHeight);
\r
655 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
658 if(sx+ex<0 || sx>imgWidth)
\r
663 if ( (sx <= (x2-x1)*av.charWidth) &&
\r
664 group.sequences.contains(av.alignment.getSequenceAt(
\r
667 if ((bottom == -1) &&
\r
668 !group.sequences.contains(
\r
669 av.alignment.getSequenceAt(i + 1)))
\r
671 bottom = sy + av.charHeight;
\r
676 if (((top == -1) && (i == 0)) ||
\r
677 !group.sequences.contains(
\r
678 av.alignment.getSequenceAt(i - 1)))
\r
686 if (group == av.getSelectionGroup())
\r
688 g.setStroke(new BasicStroke(1,
\r
689 BasicStroke.CAP_BUTT,
\r
690 BasicStroke.JOIN_ROUND, 3f,
\r
691 new float[] { 5f, 3f }, 0f));
\r
692 g.setColor(Color.RED);
\r
696 g.setStroke(new BasicStroke());
\r
697 g.setColor(group.getOutlineColour());
\r
705 if (sx >= 0 && sx < imgWidth)
\r
706 g.drawLine(sx, oldY, sx, sy);
\r
708 if (sx + ex < imgWidth)
\r
709 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
717 if (sx + ex > imgWidth)
\r
720 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
721 ex = (x2 - x1 + 1) * av.charWidth;
\r
725 g.drawLine(sx, top, sx + ex, top);
\r
731 g.drawLine(sx, bottom, sx + ex, bottom);
\r
742 sy = offset + ( (i - starty) * av.charHeight);
\r
743 if (sx >= 0 && sx < imgWidth)
\r
744 g.drawLine(sx, oldY, sx, sy);
\r
746 if (sx + ex < imgWidth)
\r
747 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
755 if (sx + ex > imgWidth)
\r
757 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
758 ex = (x2 - x1 + 1) * av.charWidth;
\r
762 g.drawLine(sx, top, sx + ex, top);
\r
768 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
777 if (groupIndex >= av.alignment.getGroups().size())
\r
782 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
784 while (groupIndex < av.alignment.getGroups().size());
\r
787 /// Highlight search Results once all sequences have been drawn
\r
788 //////////////////////////////////////////////////////////
\r
791 for (int r = 0; r < searchResults.length; r += 3)
\r
793 int searchSeq = searchResults[r];
\r
795 if ((searchSeq >= y1) && (searchSeq < y2))
\r
797 SequenceI seq = av.getAlignment().getSequenceAt(searchSeq);
\r
799 int searchStart = seq.findIndex(searchResults[r + 1]) - 1;
\r
800 int searchEnd = seq.findIndex(searchResults[r + 2]) - 1;
\r
802 SequenceRenderer ssr = (SequenceRenderer) sr;
\r
804 if (searchStart < x1)
\r
809 if (searchEnd > x2)
\r
814 ssr.drawHighlightedText(seq, searchStart, searchEnd,
\r
815 (searchStart - startx) * av.charWidth,
\r
816 offset + ((searchSeq - starty) * av.charHeight),
\r
817 av.charWidth, av.charHeight);
\r
826 * @param results DOCUMENT ME!
\r
828 public void highlightSearchResults(int[] results)
\r
830 // results are in the order sequence, startRes, endRes
\r
831 if (results == null)
\r
833 displaySearch = false;
\r
837 displaySearch = true;
\r
840 searchResults = results;
\r