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
523 /// First draw the sequences
\r
524 /////////////////////////////
\r
525 for (int i = y1; i < y2; i++)
\r
527 nextSeq = av.alignment.getSequenceAt(i);
\r
528 g.setFont(av.getFont());
\r
531 sr.drawSequence(g, nextSeq, av.alignment.findAllGroups(nextSeq),
\r
532 x1, x2, (x1 - startx) * av.charWidth,
\r
533 offset + ( (i - starty) * av.charHeight), av.charWidth,
\r
539 if (av.showSequenceFeatures)
\r
541 fr.drawSequence(g, nextSeq,
\r
542 av.alignment.findAllGroups(nextSeq), x1, x2,
\r
543 (x1 - startx) * av.charWidth,
\r
544 offset + ((i - starty) * av.charHeight), av.charWidth,
\r
550 /////////////////////////////////////
\r
551 // Now outline any areas if necessary
\r
552 /////////////////////////////////////
\r
553 SequenceGroup group = av.getSelectionGroup();
\r
554 java.util.Vector groups = av.alignment.getGroups();
\r
559 int groupIndex = -1;
\r
561 if ((group == null) && (groups.size() > 0))
\r
563 group = (SequenceGroup) groups.elementAt(0);
\r
568 if (group != null && !isOverview)
\r
574 boolean inGroup = false;
\r
578 for (i = y1; i < y2; i++)
\r
580 sx = (group.getStartRes() - startx) * av.charWidth;
\r
581 sy = offset + ((i - starty) * av.charHeight);
\r
582 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
585 if(sx+ex<0 || sx>imgWidth)
\r
590 if ( (sx <= (x2-x1)*av.charWidth) &&
\r
591 group.sequences.contains(av.alignment.getSequenceAt(
\r
594 if ((bottom == -1) &&
\r
595 !group.sequences.contains(
\r
596 av.alignment.getSequenceAt(i + 1)))
\r
598 bottom = sy + av.charHeight;
\r
603 if (((top == -1) && (i == 0)) ||
\r
604 !group.sequences.contains(
\r
605 av.alignment.getSequenceAt(i - 1)))
\r
613 if (group == av.getSelectionGroup())
\r
615 g.setStroke(new BasicStroke(1,
\r
616 BasicStroke.CAP_BUTT,
\r
617 BasicStroke.JOIN_ROUND, 3f,
\r
618 new float[] { 5f, 3f }, 0f));
\r
619 g.setColor(Color.RED);
\r
623 g.setStroke(new BasicStroke());
\r
624 g.setColor(group.getOutlineColour());
\r
632 if (sx >= 0 && sx < imgWidth)
\r
633 g.drawLine(sx, oldY, sx, sy);
\r
635 if (sx + ex < imgWidth)
\r
636 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
644 if (sx + ex > imgWidth)
\r
647 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
648 ex = (x2 - x1 + 1) * av.charWidth;
\r
652 g.drawLine(sx, top, sx + ex, top);
\r
658 g.drawLine(sx, bottom, sx + ex, bottom);
\r
669 sy = offset + ( (i - starty) * av.charHeight);
\r
670 if (sx >= 0 && sx < imgWidth)
\r
671 g.drawLine(sx, oldY, sx, sy);
\r
673 if (sx + ex < imgWidth)
\r
674 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
682 if (sx + ex > imgWidth)
\r
684 else if (sx + ex >= (x2 - x1 + 1) * av.charWidth)
\r
685 ex = (x2 - x1 + 1) * av.charWidth;
\r
689 g.drawLine(sx, top, sx + ex, top);
\r
695 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
704 if (groupIndex >= groups.size())
\r
709 group = (SequenceGroup) groups.elementAt(groupIndex);
\r
711 while (groupIndex < groups.size());
\r
714 /// Highlight search Results once all sequences have been drawn
\r
715 //////////////////////////////////////////////////////////
\r
718 for (int r = 0; r < searchResults.length; r += 3)
\r
720 int searchSeq = searchResults[r];
\r
722 if ((searchSeq >= y1) && (searchSeq < y2))
\r
724 SequenceI seq = av.getAlignment().getSequenceAt(searchSeq);
\r
726 int searchStart = seq.findIndex(searchResults[r + 1]) - 1;
\r
727 int searchEnd = seq.findIndex(searchResults[r + 2]) - 1;
\r
729 SequenceRenderer ssr = (SequenceRenderer) sr;
\r
731 if (searchStart < x1)
\r
736 if (searchEnd > x2)
\r
741 ssr.drawHighlightedText(seq, searchStart, searchEnd,
\r
742 (searchStart - startx) * av.charWidth,
\r
743 offset + ((searchSeq - starty) * av.charHeight),
\r
744 av.charWidth, av.charHeight);
\r
753 * @param results DOCUMENT ME!
\r
755 public void highlightSearchResults(int[] results)
\r
757 // results are in the order sequence, startRes, endRes
\r
758 if (results == null)
\r
760 displaySearch = false;
\r
764 displaySearch = true;
\r
767 searchResults = results;
\r