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
50 boolean isOverview = false;
\r
53 * Creates a new SeqCanvas object.
\r
55 * @param av DOCUMENT ME!
\r
57 public SeqCanvas(AlignViewport av)
\r
60 fr = new FeatureRenderer(av);
\r
61 sr = new SequenceRenderer(av);
\r
62 setLayout(new BorderLayout());
\r
63 PaintRefresher.Register(this, av.alignment);
\r
64 setBackground(Color.white);
\r
70 * @param g DOCUMENT ME!
\r
71 * @param startx DOCUMENT ME!
\r
72 * @param endx DOCUMENT ME!
\r
73 * @param ypos DOCUMENT ME!
\r
75 void drawNorthScale(Graphics g, int startx, int endx, int ypos)
\r
77 int scalestartx = startx - (startx % 10) + 10;
\r
79 g.setColor(Color.black);
\r
82 for (int i = scalestartx; i < endx; i += 10)
\r
84 String string = String.valueOf(i);
\r
85 g.drawString(string, (i - startx - 1) * av.charWidth,
\r
86 ypos - (av.charHeight / 2));
\r
88 g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
\r
89 (ypos + 2) - (av.charHeight / 2),
\r
90 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -
\r
98 * @param g DOCUMENT ME!
\r
99 * @param startx DOCUMENT ME!
\r
100 * @param endx DOCUMENT ME!
\r
101 * @param ypos DOCUMENT ME!
\r
103 void drawWestScale(Graphics g, int startx, int endx, int ypos)
\r
105 FontMetrics fm = getFontMetrics(av.getFont());
\r
106 ypos += av.charHeight;
\r
109 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
111 SequenceI seq = av.alignment.getSequenceAt(i);
\r
112 int index = startx;
\r
115 while (index < endx)
\r
117 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
124 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
131 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))-av.charWidth/2;
\r
132 g.drawString(value + "", x,
\r
133 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
141 * @param g DOCUMENT ME!
\r
142 * @param startx DOCUMENT ME!
\r
143 * @param endx DOCUMENT ME!
\r
144 * @param ypos DOCUMENT ME!
\r
146 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
148 ypos += av.charHeight;
\r
151 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
153 SequenceI seq = av.alignment.getSequenceAt(i);
\r
157 while (index > startx)
\r
159 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
166 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
173 g.drawString(String.valueOf(value), av.charWidth/2,
\r
174 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
182 * @param horizontal DOCUMENT ME!
\r
183 * @param vertical DOCUMENT ME!
\r
185 public void fastPaint(int horizontal, int vertical)
\r
192 gg.copyArea(horizontal * av.charWidth,
\r
193 vertical * av.charHeight,
\r
196 -horizontal * av.charWidth,
\r
197 -vertical * av.charHeight);
\r
199 int sr = av.startRes;
\r
200 int er = av.endRes;
\r
201 int ss = av.startSeq;
\r
202 int es = av.endSeq;
\r
206 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
209 transX = (er - sr - horizontal) * av.charWidth;
\r
210 sr = er - horizontal;
\r
212 else if (horizontal < 0)
\r
214 er = sr - horizontal-1;
\r
216 else if (vertical > 0) // scroll down
\r
218 ss = es - vertical;
\r
220 if (ss < av.startSeq)
\r
221 { // ie scrolling too fast, more than a page at a time
\r
226 transY = imgHeight - (vertical * av.charHeight);
\r
229 else if (vertical < 0)
\r
231 es = ss - vertical;
\r
233 if (es > av.endSeq)
\r
239 gg.translate(transX, transY);
\r
240 drawPanel(gg, sr, er, ss, es, sr, ss, 0);
\r
241 gg.translate(-transX, -transY);
\r
248 * Definitions of startx and endx (hopefully):
\r
249 * SMJS This is what I'm working towards!
\r
250 * startx is the first residue (starting at 0) to display.
\r
251 * endx is the last residue to display (starting at 0).
\r
252 * starty is the first sequence to display (starting at 0).
\r
253 * endy is the last sequence to display (starting at 0).
\r
254 * NOTE 1: The av limits are set in setFont in this class and
\r
255 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
258 // Set this to false to force a full panel paint
\r
259 public void paintComponent(Graphics g)
\r
261 sr.renderGaps(av.renderGaps);
\r
263 if ((img != null) &&
\r
264 (fastPaint || (getWidth() != g.getClipBounds().width) ||
\r
265 (getHeight() != g.getClipBounds().height)))
\r
267 g.drawImage(img, 0, 0, this);
\r
273 // this draws the whole of the alignment
\r
274 imgWidth = getWidth();
\r
275 imgHeight = getHeight();
\r
277 imgWidth -= (imgWidth % av.charWidth);
\r
278 imgHeight -= (imgHeight % av.charHeight);
\r
280 if ((imgWidth < 1) || (imgHeight < 1))
\r
286 img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
\r
287 gg = (Graphics2D) img.getGraphics();
\r
288 gg.setFont(av.getFont());
\r
289 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
290 RenderingHints.VALUE_ANTIALIAS_ON);
\r
292 gg.setColor(Color.white);
\r
293 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
296 if (av.getWrapAlignment())
\r
298 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
302 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq,
\r
303 av.startRes, av.startSeq, 0);
\r
306 g.drawImage(img, 0, 0, this);
\r
312 * @param cwidth DOCUMENT ME!
\r
314 * @return DOCUMENT ME!
\r
316 public int getWrappedCanvasWidth(int cwidth)
\r
318 FontMetrics fm = getFontMetrics(av.getFont());
\r
323 if (av.scaleRightWrapped)
\r
325 LABEL_EAST = fm.stringWidth(getMask()+"0");
\r
328 if (av.scaleLeftWrapped)
\r
330 LABEL_WEST = fm.stringWidth(getMask());
\r
333 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
338 * Generates a string of zeroes.
\r
344 for (int i = av.alignment.getWidth(); i > 0; i /= 10)
\r
354 * @param g DOCUMENT ME!
\r
355 * @param canvasWidth DOCUMENT ME!
\r
356 * @param canvasHeight DOCUMENT ME!
\r
357 * @param startRes DOCUMENT ME!
\r
359 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
363 AlignmentI al = av.getAlignment();
\r
365 FontMetrics fm = getFontMetrics(av.getFont());
\r
367 int LABEL_EAST = 0;
\r
369 if (av.scaleRightWrapped)
\r
371 LABEL_EAST = fm.stringWidth(getMask()+"0");
\r
374 int LABEL_WEST = 0;
\r
376 if (av.scaleLeftWrapped)
\r
378 LABEL_WEST = fm.stringWidth(getMask());
\r
381 int hgap = av.charHeight;
\r
382 if(av.scaleAboveWrapped)
\r
383 hgap += av.charHeight;
\r
385 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
386 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
388 av.setWrappedWidth(cWidth);
\r
390 av.endRes = av.startRes + cWidth;
\r
393 int endx = (startRes + cWidth) - 1;
\r
397 while ((ypos <= canvasHeight) && (startRes < av.alignment.getWidth()))
\r
399 g.setFont(av.getFont());
\r
400 g.setColor(Color.black);
\r
402 if (av.scaleLeftWrapped)
\r
404 drawWestScale(g, startRes, endx, ypos);
\r
407 if (av.scaleRightWrapped)
\r
409 g.translate(canvasWidth - LABEL_EAST, 0);
\r
410 drawEastScale(g, startRes, endx, ypos);
\r
411 g.translate(-(canvasWidth - LABEL_EAST), 0);
\r
414 g.translate(LABEL_WEST, 0);
\r
416 if (av.scaleAboveWrapped)
\r
418 drawNorthScale(g, startRes, endx, ypos);
\r
421 // When printing we have an extra clipped region,
\r
422 // the Printable page which we need to account for here
\r
423 Shape clip = g.getClip();
\r
427 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
431 g.setClip(0, (int) clip.getBounds().getY(),
\r
432 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
435 if (av.vconsensus!=null && av.alignment.getWidth() >= av.vconsensus.size())
\r
437 endx = av.vconsensus.size() - 2;
\r
441 drawPanel(g, startRes, endx, 0, al.getHeight(), startRes, 0, ypos);
\r
443 if(av.showAnnotation)
\r
445 g.translate(0, cHeight + ypos + 3);
\r
446 if(annotations==null)
\r
447 annotations = new AnnotationPanel(av);
\r
449 annotations.drawComponent( (Graphics2D) g, startRes, endx + 1);
\r
450 g.translate(0, -cHeight - ypos);
\r
453 g.translate(-LABEL_WEST, 0);
\r
455 ypos += cHeight+getAnnotationHeight()+hgap;
\r
456 if(av.showAnnotation)
\r
459 startRes += cWidth;
\r
460 endx = (startRes + cWidth) - 1;
\r
462 if (endx > al.getWidth())
\r
464 endx = al.getWidth();
\r
469 AnnotationPanel annotations;
\r
470 int getAnnotationHeight()
\r
472 if(!av.showAnnotation)
\r
475 if(annotations==null)
\r
476 annotations = new AnnotationPanel(av);
\r
478 return annotations.adjustPanelHeight();
\r
484 * @param g1 DOCUMENT ME!
\r
485 * @param x1 DOCUMENT ME!
\r
486 * @param x2 DOCUMENT ME!
\r
487 * @param y1 DOCUMENT ME!
\r
488 * @param y2 DOCUMENT ME!
\r
489 * @param startx DOCUMENT ME!
\r
490 * @param starty DOCUMENT ME!
\r
491 * @param offset DOCUMENT ME!
\r
494 float aaRatio = 2f/3f;
\r
495 public void increaseAARatio()
\r
504 public void decreaseAARation()
\r
513 synchronized public void drawPanel(Graphics g1, int x1, int x2, int y1,
\r
514 int y2, int startx, int starty, int offset)
\r
516 Graphics2D g = (Graphics2D) g1;
\r
517 g.setFont(av.getFont());
\r
522 SequenceI dna = null;
\r
524 int aaHeight = (int)(av.getCharHeight()*aaRatio);
\r
525 int dnaHeight =(int)(av.getCharHeight() * (1-aaRatio));
\r
528 java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform();
\r
529 transform.scale(1f/3f , 1);
\r
530 Font dnafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
532 dnafont = dnafont.deriveFont(transform);
\r
534 Font aafont = new Font(av.getFont().getName(), av.getFont().getStyle(),
\r
536 transform = new java.awt.geom.AffineTransform();
\r
537 transform.scale(1/aaRatio, 1);
\r
538 aafont = aafont.deriveFont(transform);
\r
540 /// First draw the sequences
\r
541 /////////////////////////////
\r
542 for (int i = y1; i < y2; i++)
\r
544 nextSeq = av.alignment.getSequenceAt(i);
\r
547 StringBuffer dnasb = new StringBuffer();
\r
548 for (int j = 0; j < nextSeq.getLength(); j++)
\r
550 java.util.Vector codons = jalview.schemes.ResidueProperties.getCodons(nextSeq.getSequence(j,
\r
552 if (codons != null && codons.size() > 0)
\r
553 dnasb.append(codons.elementAt(0).toString());
\r
556 dna = new Sequence("dna", dnasb.toString());*/
\r
559 sr.drawSequence(g, nextSeq, av.alignment.findAllGroups(nextSeq),
\r
560 x1, x2, (x1 - startx) * av.charWidth,
\r
561 offset + ( (i - starty) * av.charHeight), av.charWidth,
\r
565 /* g.setFont(dnafont);
\r
567 sr.drawSequence(g, dna, null,
\r
568 x1, x2 * 3 +2, ( (x1 - startx) * av.charWidth) / 3,
\r
569 offset + ( (i - starty) * av.charHeight) + aaHeight, av.charWidth / 3,
\r
572 if (av.showSequenceFeatures)
\r
574 fr.drawSequence(g, nextSeq,
\r
575 av.alignment.findAllGroups(nextSeq), x1, x2,
\r
576 (x1 - startx) * av.charWidth,
\r
577 offset + ((i - starty) * av.charHeight), av.charWidth,
\r
583 /////////////////////////////////////
\r
584 // Now outline any areas if necessary
\r
585 /////////////////////////////////////
\r
586 SequenceGroup group = av.getSelectionGroup();
\r
587 java.util.Vector groups = av.alignment.getGroups();
\r
592 int groupIndex = -1;
\r
594 if ((group == null) && (groups.size() > 0))
\r
596 group = (SequenceGroup) groups.elementAt(0);
\r
601 if (group != null && !isOverview)
\r
607 boolean inGroup = false;
\r
611 for (i = y1; i < y2; i++)
\r
613 sx = (group.getStartRes() - startx) * av.charWidth;
\r
614 sy = offset + ((i - starty) * av.charHeight);
\r
615 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
618 if(sx+ex<0 || sx>imgWidth)
\r
623 if ( (sx <= (x2-x1)*av.charWidth) &&
\r
624 group.sequences.contains(av.alignment.getSequenceAt(
\r
627 if ((bottom == -1) &&
\r
628 !group.sequences.contains(
\r
629 av.alignment.getSequenceAt(i + 1)))
\r
631 bottom = sy + av.charHeight;
\r
636 if (((top == -1) && (i == 0)) ||
\r
637 !group.sequences.contains(
\r
638 av.alignment.getSequenceAt(i - 1)))
\r
646 if (group == av.getSelectionGroup())
\r
648 g.setStroke(new BasicStroke(1,
\r
649 BasicStroke.CAP_BUTT,
\r
650 BasicStroke.JOIN_ROUND, 3f,
\r
651 new float[] { 5f, 3f }, 0f));
\r
652 g.setColor(Color.RED);
\r
656 g.setStroke(new BasicStroke());
\r
657 g.setColor(group.getOutlineColour());
\r
665 if (sx >= 0 && sx < imgWidth)
\r
666 g.drawLine(sx, oldY, sx, sy);
\r
668 if (sx + ex < imgWidth)
\r
669 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
677 if (sx + ex > imgWidth)
\r
680 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
681 ex = (x2 - x1 + 1) * av.charWidth;
\r
685 g.drawLine(sx, top, sx + ex, top);
\r
691 g.drawLine(sx, bottom, sx + ex, bottom);
\r
702 sy = offset + ( (i - starty) * av.charHeight);
\r
703 if (sx >= 0 && sx < imgWidth)
\r
704 g.drawLine(sx, oldY, sx, sy);
\r
706 if (sx + ex < imgWidth)
\r
707 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
715 if (sx + ex > imgWidth)
\r
717 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
718 ex = (x2 - x1 + 1) * av.charWidth;
\r
722 g.drawLine(sx, top, sx + ex, top);
\r
728 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
737 if (groupIndex >= groups.size())
\r
742 group = (SequenceGroup) groups.elementAt(groupIndex);
\r
744 while (groupIndex < groups.size());
\r
747 /// Highlight search Results once all sequences have been drawn
\r
748 //////////////////////////////////////////////////////////
\r
751 for (int r = 0; r < searchResults.length; r += 3)
\r
753 int searchSeq = searchResults[r];
\r
755 if ((searchSeq >= y1) && (searchSeq < y2))
\r
757 SequenceI seq = av.getAlignment().getSequenceAt(searchSeq);
\r
759 int searchStart = seq.findIndex(searchResults[r + 1]) - 1;
\r
760 int searchEnd = seq.findIndex(searchResults[r + 2]) - 1;
\r
762 SequenceRenderer ssr = (SequenceRenderer) sr;
\r
764 if (searchStart < x1)
\r
769 if (searchEnd > x2)
\r
774 ssr.drawHighlightedText(seq, searchStart, searchEnd,
\r
775 (searchStart - startx) * av.charWidth,
\r
776 offset + ((searchSeq - starty) * av.charHeight),
\r
777 av.charWidth, av.charHeight);
\r
786 * @param results DOCUMENT ME!
\r
788 public void highlightSearchResults(int[] results)
\r
790 // results are in the order sequence, startRes, endRes
\r
791 if (results == null)
\r
793 displaySearch = false;
\r
797 displaySearch = true;
\r
800 searchResults = results;
\r