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
52 * Creates a new SeqCanvas object.
\r
54 * @param av DOCUMENT ME!
\r
56 public SeqCanvas(AlignViewport av)
\r
59 fr = new FeatureRenderer(av);
\r
60 sr = new SequenceRenderer(av);
\r
61 setLayout(new BorderLayout());
\r
62 PaintRefresher.Register(this, av.alignment);
\r
63 setBackground(Color.white);
\r
66 MCview.PDBCanvas pdbCanvas;
\r
67 public SequenceRenderer getSequenceRenderer()
\r
72 public FeatureRenderer getFeatureRenderer()
\r
77 public void setPDBCanvas(MCview.PDBCanvas pc)
\r
82 public AlignViewport getViewport()
\r
91 * @param g DOCUMENT ME!
\r
92 * @param startx DOCUMENT ME!
\r
93 * @param endx DOCUMENT ME!
\r
94 * @param ypos DOCUMENT ME!
\r
96 void drawNorthScale(Graphics g, int startx, int endx, int ypos)
\r
98 int scalestartx = startx - (startx % 10) + 10;
\r
100 g.setColor(Color.black);
\r
103 for (int i = scalestartx; i < endx; i += 10)
\r
105 String string = String.valueOf(i);
\r
106 g.drawString(string, (i - startx - 1) * av.charWidth,
\r
107 ypos - (av.charHeight / 2));
\r
109 g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
\r
110 (ypos + 2) - (av.charHeight / 2),
\r
111 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -
\r
119 * @param g DOCUMENT ME!
\r
120 * @param startx DOCUMENT ME!
\r
121 * @param endx DOCUMENT ME!
\r
122 * @param ypos DOCUMENT ME!
\r
124 void drawWestScale(Graphics g, int startx, int endx, int ypos)
\r
126 FontMetrics fm = getFontMetrics(av.getFont());
\r
127 ypos += av.charHeight;
\r
130 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
132 SequenceI seq = av.alignment.getSequenceAt(i);
\r
133 int index = startx;
\r
136 while (index < endx)
\r
138 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
145 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
152 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))-av.charWidth/2;
\r
153 g.drawString(value + "", x,
\r
154 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
162 * @param g DOCUMENT ME!
\r
163 * @param startx DOCUMENT ME!
\r
164 * @param endx DOCUMENT ME!
\r
165 * @param ypos DOCUMENT ME!
\r
167 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
169 ypos += av.charHeight;
\r
172 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
174 SequenceI seq = av.alignment.getSequenceAt(i);
\r
178 while (index > startx)
\r
180 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
187 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
194 g.drawString(String.valueOf(value), av.charWidth/2,
\r
195 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
203 * @param horizontal DOCUMENT ME!
\r
204 * @param vertical DOCUMENT ME!
\r
206 public void fastPaint(int horizontal, int vertical)
\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
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 sr.renderGaps(av.renderGaps);
\r
284 if ((img != null) &&
\r
285 (fastPaint && getVisibleRect() != g.getClipBounds()))
\r
287 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
310 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
311 RenderingHints.VALUE_ANTIALIAS_ON);
\r
313 gg.setColor(Color.white);
\r
314 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
317 if (av.getWrapAlignment())
\r
319 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
323 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq,
\r
324 av.startRes, av.startSeq, 0);
\r
327 g.drawImage(img, 0, 0, this);
\r
329 if (pdbCanvas != null)
\r
331 pdbCanvas.updateSeqColours();
\r
339 * @param cwidth DOCUMENT ME!
\r
341 * @return DOCUMENT ME!
\r
343 public int getWrappedCanvasWidth(int cwidth)
\r
345 FontMetrics fm = getFontMetrics(av.getFont());
\r
350 if (av.scaleRightWrapped)
\r
352 LABEL_EAST = fm.stringWidth(getMask()+"0");
\r
355 if (av.scaleLeftWrapped)
\r
357 LABEL_WEST = fm.stringWidth(getMask());
\r
360 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
365 * Generates a string of zeroes.
\r
371 for (int i = av.alignment.getWidth(); i > 0; i /= 10)
\r
381 * @param g DOCUMENT ME!
\r
382 * @param canvasWidth DOCUMENT ME!
\r
383 * @param canvasHeight DOCUMENT ME!
\r
384 * @param startRes DOCUMENT ME!
\r
386 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
390 AlignmentI al = av.getAlignment();
\r
392 FontMetrics fm = getFontMetrics(av.getFont());
\r
394 int LABEL_EAST = 0;
\r
396 if (av.scaleRightWrapped)
\r
398 LABEL_EAST = fm.stringWidth(getMask()+"0");
\r
401 int LABEL_WEST = 0;
\r
403 if (av.scaleLeftWrapped)
\r
405 LABEL_WEST = fm.stringWidth(getMask());
\r
408 int hgap = av.charHeight;
\r
409 if(av.scaleAboveWrapped)
\r
410 hgap += av.charHeight;
\r
412 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
413 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
415 av.setWrappedWidth(cWidth);
\r
417 av.endRes = av.startRes + cWidth;
\r
420 int endx = (startRes + cWidth) - 1;
\r
424 while ((ypos <= canvasHeight) && (startRes < av.alignment.getWidth()))
\r
426 g.setFont(av.getFont());
\r
427 g.setColor(Color.black);
\r
429 if (av.scaleLeftWrapped)
\r
431 drawWestScale(g, startRes, endx, ypos);
\r
434 if (av.scaleRightWrapped)
\r
436 g.translate(canvasWidth - LABEL_EAST, 0);
\r
437 drawEastScale(g, startRes, endx, ypos);
\r
438 g.translate(-(canvasWidth - LABEL_EAST), 0);
\r
441 g.translate(LABEL_WEST, 0);
\r
443 if (av.scaleAboveWrapped)
\r
445 drawNorthScale(g, startRes, endx, ypos);
\r
448 // When printing we have an extra clipped region,
\r
449 // the Printable page which we need to account for here
\r
450 Shape clip = g.getClip();
\r
454 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
458 g.setClip(0, (int) clip.getBounds().getY(),
\r
459 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
462 if (av.vconsensus!=null && av.alignment.getWidth() >= av.vconsensus.size())
\r
464 endx = av.vconsensus.size() - 2;
\r
468 drawPanel(g, startRes, endx, 0, al.getHeight(), startRes, 0, ypos);
\r
470 if(av.showAnnotation)
\r
472 g.translate(0, cHeight + ypos + 3);
\r
473 if(annotations==null)
\r
474 annotations = new AnnotationPanel(av);
\r
476 annotations.drawComponent( (Graphics2D) g, startRes, endx + 1);
\r
477 g.translate(0, -cHeight - ypos);
\r
480 g.translate(-LABEL_WEST, 0);
\r
482 ypos += cHeight+getAnnotationHeight()+hgap;
\r
483 if(av.showAnnotation)
\r
486 startRes += cWidth;
\r
487 endx = (startRes + cWidth) - 1;
\r
489 if (endx > al.getWidth())
\r
491 endx = al.getWidth();
\r
496 AnnotationPanel annotations;
\r
497 int getAnnotationHeight()
\r
499 if(!av.showAnnotation)
\r
502 if(annotations==null)
\r
503 annotations = new AnnotationPanel(av);
\r
505 return annotations.adjustPanelHeight();
\r
511 * @param g1 DOCUMENT ME!
\r
512 * @param x1 DOCUMENT ME!
\r
513 * @param x2 DOCUMENT ME!
\r
514 * @param y1 DOCUMENT ME!
\r
515 * @param y2 DOCUMENT ME!
\r
516 * @param startx DOCUMENT ME!
\r
517 * @param starty DOCUMENT ME!
\r
518 * @param offset DOCUMENT ME!
\r
521 float aaRatio = 2f/3f;
\r
522 public void increaseAARatio()
\r
531 public void decreaseAARation()
\r
541 synchronized public void drawPanel(Graphics g1, int x1, int x2, int y1,
\r
542 int y2, int startx, int starty, int offset)
\r
544 Graphics2D g = (Graphics2D) g1;
\r
545 g.setFont(av.getFont());
\r
547 SequenceI nextSeq, dna = null;
\r
548 int aaHeight = av.charHeight, dnaHeight = av.charHeight;
\r
549 Font dnafont = null, aafont = av.getFont();
\r
551 /* if (av.getShowTranslation())
\r
553 aaHeight = (int) (av.getCharHeight() * aaRatio);
\r
554 dnaHeight = (int) (av.getCharHeight() * (1 - aaRatio));
\r
555 java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform();
\r
556 transform.scale(1f / 3f, 1);
\r
557 dnafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
559 dnafont = dnafont.deriveFont(transform);
\r
561 aafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
563 transform = new java.awt.geom.AffineTransform();
\r
564 transform.scale(1 / aaRatio, 1);
\r
565 aafont = aafont.deriveFont(transform);
\r
569 ColourSchemeI cs = av.getGlobalColourScheme();
\r
571 /// First draw the sequences
\r
572 /////////////////////////////
\r
573 for (int i = y1; i < y2; i++)
\r
575 nextSeq = av.alignment.getSequenceAt(i);
\r
577 /* if(av.getShowTranslation())
\r
580 StringBuffer sb = new StringBuffer();
\r
581 for(int r = 0; r<nextSeq.getLength(); r+=3)
\r
583 String codon = nextSeq.getSequence(r, r+3);
\r
584 codon = codon.replace('U', 'T');
\r
585 String res = ResidueProperties.codonTranslate(codon);
\r
588 if (res.equals("STOP"))
\r
594 nextSeq = new Sequence("d", sb.toString());
\r
597 sr.drawSequence(g, nextSeq, av.alignment.findAllGroups(nextSeq),
\r
598 x1, x2, (x1 - startx) * av.charWidth,
\r
599 offset + ( (i - starty) * av.charHeight), av.charWidth,
\r
602 /* if(av.getShowTranslation())
\r
604 av.setGlobalColourScheme(new NucleotideColourScheme());
\r
605 g.setFont(dnafont);
\r
606 sr.drawSequence(g, dna, null,
\r
607 x1*3, (x2+2) * 3, ( (x1 - startx) * av.charWidth) / 3,
\r
608 offset + ( (i - starty) * av.charHeight) + aaHeight,
\r
611 av.setGlobalColourScheme(cs);
\r
614 if (av.showSequenceFeatures)
\r
616 fr.drawSequence(g, nextSeq, x1, x2,
\r
617 (x1 - startx) * av.charWidth,
\r
618 offset + ((i - starty) * av.charHeight), av.charWidth,
\r
624 /////////////////////////////////////
\r
625 // Now outline any areas if necessary
\r
626 /////////////////////////////////////
\r
627 SequenceGroup group = av.getSelectionGroup();
\r
632 int groupIndex = -1;
\r
634 if ((group == null) && (av.alignment.getGroups().size() > 0))
\r
636 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
646 boolean inGroup = false;
\r
650 for (i = y1; i < y2; i++)
\r
652 sx = (group.getStartRes() - startx) * av.charWidth;
\r
653 sy = offset + ((i - starty) * av.charHeight);
\r
654 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
657 if(sx+ex<0 || sx>imgWidth)
\r
662 if ( (sx <= (x2-x1)*av.charWidth) &&
\r
663 group.sequences.contains(av.alignment.getSequenceAt(
\r
666 if ((bottom == -1) &&
\r
667 !group.sequences.contains(
\r
668 av.alignment.getSequenceAt(i + 1)))
\r
670 bottom = sy + av.charHeight;
\r
675 if (((top == -1) && (i == 0)) ||
\r
676 !group.sequences.contains(
\r
677 av.alignment.getSequenceAt(i - 1)))
\r
685 if (group == av.getSelectionGroup())
\r
687 g.setStroke(new BasicStroke(1,
\r
688 BasicStroke.CAP_BUTT,
\r
689 BasicStroke.JOIN_ROUND, 3f,
\r
690 new float[] { 5f, 3f }, 0f));
\r
691 g.setColor(Color.RED);
\r
695 g.setStroke(new BasicStroke());
\r
696 g.setColor(group.getOutlineColour());
\r
704 if (sx >= 0 && sx < imgWidth)
\r
705 g.drawLine(sx, oldY, sx, sy);
\r
707 if (sx + ex < imgWidth)
\r
708 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
716 if (sx + ex > imgWidth)
\r
719 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
720 ex = (x2 - x1 + 1) * av.charWidth;
\r
724 g.drawLine(sx, top, sx + ex, top);
\r
730 g.drawLine(sx, bottom, sx + ex, bottom);
\r
741 sy = offset + ( (i - starty) * av.charHeight);
\r
742 if (sx >= 0 && sx < imgWidth)
\r
743 g.drawLine(sx, oldY, sx, sy);
\r
745 if (sx + ex < imgWidth)
\r
746 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
754 if (sx + ex > imgWidth)
\r
756 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
757 ex = (x2 - x1 + 1) * av.charWidth;
\r
761 g.drawLine(sx, top, sx + ex, top);
\r
767 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
776 if (groupIndex >= av.alignment.getGroups().size())
\r
781 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
783 while (groupIndex < av.alignment.getGroups().size());
\r
786 /// Highlight search Results once all sequences have been drawn
\r
787 //////////////////////////////////////////////////////////
\r
790 for (int r = 0; r < searchResults.length; r += 3)
\r
792 int searchSeq = searchResults[r];
\r
794 if ((searchSeq >= y1) && (searchSeq < y2))
\r
796 SequenceI seq = av.getAlignment().getSequenceAt(searchSeq);
\r
798 int searchStart = seq.findIndex(searchResults[r + 1]) - 1;
\r
799 int searchEnd = seq.findIndex(searchResults[r + 2]) - 1;
\r
801 SequenceRenderer ssr = (SequenceRenderer) sr;
\r
803 if (searchStart < x1)
\r
808 if (searchEnd > x2)
\r
813 ssr.drawHighlightedText(seq, searchStart, searchEnd,
\r
814 (searchStart - startx) * av.charWidth,
\r
815 offset + ((searchSeq - starty) * av.charHeight),
\r
816 av.charWidth, av.charHeight);
\r
825 * @param results DOCUMENT ME!
\r
827 public void highlightSearchResults(int[] results)
\r
829 // results are in the order sequence, startRes, endRes
\r
830 if (results == null)
\r
832 displaySearch = false;
\r
836 displaySearch = true;
\r
839 searchResults = results;
\r