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 SearchResults searchResults = null;
\r
45 boolean fastPaint = false;
\r
50 * Creates a new SeqCanvas object.
\r
52 * @param av DOCUMENT ME!
\r
54 public SeqCanvas(AlignViewport av)
\r
57 fr = new FeatureRenderer(av);
\r
58 sr = new SequenceRenderer(av);
\r
59 setLayout(new BorderLayout());
\r
60 PaintRefresher.Register(this, av.alignment);
\r
61 setBackground(Color.white);
\r
64 MCview.PDBCanvas pdbCanvas;
\r
65 public SequenceRenderer getSequenceRenderer()
\r
70 public FeatureRenderer getFeatureRenderer()
\r
75 public void setPDBCanvas(MCview.PDBCanvas pc)
\r
80 public AlignViewport getViewport()
\r
89 * @param g DOCUMENT ME!
\r
90 * @param startx DOCUMENT ME!
\r
91 * @param endx DOCUMENT ME!
\r
92 * @param ypos DOCUMENT ME!
\r
94 void drawNorthScale(Graphics g, int startx, int endx, int ypos)
\r
96 int scalestartx = startx - (startx % 10) + 10;
\r
98 g.setColor(Color.black);
\r
101 for (int i = scalestartx; i < endx; i += 10)
\r
103 String string = String.valueOf(av.getColumnSelection().adjustForHiddenColumns(i));
\r
104 g.drawString(string, (i - startx - 1) * av.charWidth,
\r
105 ypos - (av.charHeight / 2));
\r
107 g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
\r
108 (ypos + 2) - (av.charHeight / 2),
\r
109 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -
\r
117 * @param g DOCUMENT ME!
\r
118 * @param startx DOCUMENT ME!
\r
119 * @param endx DOCUMENT ME!
\r
120 * @param ypos DOCUMENT ME!
\r
122 void drawWestScale(Graphics g, int startx, int endx, int ypos)
\r
124 FontMetrics fm = getFontMetrics(av.getFont());
\r
125 ypos += av.charHeight;
\r
128 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
130 SequenceI seq = av.alignment.getSequenceAt(i);
\r
131 int index = startx;
\r
134 while (index < endx)
\r
136 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
143 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
150 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))-av.charWidth/2;
\r
151 g.drawString(value + "", x,
\r
152 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
160 * @param g DOCUMENT ME!
\r
161 * @param startx DOCUMENT ME!
\r
162 * @param endx DOCUMENT ME!
\r
163 * @param ypos DOCUMENT ME!
\r
165 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
167 ypos += av.charHeight;
\r
170 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
172 SequenceI seq = av.alignment.getSequenceAt(i);
\r
176 while (index > startx)
\r
178 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
185 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
192 g.drawString(String.valueOf(value), 0,
\r
193 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
201 * @param horizontal DOCUMENT ME!
\r
202 * @param vertical DOCUMENT ME!
\r
204 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, 0);
\r
262 gg.translate(-transX, -transY);
\r
268 * Definitions of startx and endx (hopefully):
\r
269 * SMJS This is what I'm working towards!
\r
270 * startx is the first residue (starting at 0) to display.
\r
271 * endx is the last residue to display (starting at 0).
\r
272 * starty is the first sequence to display (starting at 0).
\r
273 * endy is the last sequence to display (starting at 0).
\r
274 * NOTE 1: The av limits are set in setFont in this class and
\r
275 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
278 // Set this to false to force a full panel paint
\r
279 public void paintComponent(Graphics g)
\r
281 super.paintComponent(g);
\r
283 sr.renderGaps(av.renderGaps);
\r
285 if ( img != null && (fastPaint
\r
286 || (getVisibleRect().width != g.getClipBounds().width)
\r
287 || (getVisibleRect().height != g.getClipBounds().height)))
\r
289 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
312 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
313 RenderingHints.VALUE_ANTIALIAS_ON);
\r
315 gg.setColor(Color.white);
\r
316 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
319 if (av.getWrapAlignment())
\r
321 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
325 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, 0);
\r
328 g.drawImage(img, 0, 0, this);
\r
330 if (pdbCanvas != null)
\r
332 pdbCanvas.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());
\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
371 String mask = "00";
\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());
\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(), 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 void drawPanel(Graphics g1, int startRes, int endRes,
\r
543 int startSeq, int endSeq, int offset)
\r
545 if(!av.hasHiddenColumns)
\r
547 draw(g1, startRes, endRes, startSeq, endSeq, offset);
\r
551 java.util.Vector regions = av.getColumnSelection().getHiddenColumns();
\r
554 int blockStart = startRes;
\r
555 int blockEnd = endRes;
\r
557 for (int i = 0; i < regions.size(); i++)
\r
559 int[] region = (int[]) regions.elementAt(i);
\r
560 int hideStart = region[0];
\r
561 int hideEnd = region[1];
\r
563 if (hideStart <= blockStart)
\r
565 blockStart += (hideEnd - hideStart) + 1;
\r
569 blockEnd = hideStart - 1;
\r
571 g1.translate(screenY * av.charWidth, 0);
\r
573 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
575 g1.setColor(Color.blue);
\r
576 g1.drawLine( (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
578 (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
579 startSeq + (endSeq - startSeq) * av.charHeight + offset);
\r
580 g1.drawLine( (blockEnd - blockStart + 1) * av.charWidth,
\r
582 (blockEnd - blockStart + 1) * av.charWidth,
\r
583 startSeq + (endSeq - startSeq) * av.charHeight + offset);
\r
585 g1.translate( -screenY * av.charWidth, 0);
\r
586 screenY += blockEnd - blockStart + 1;
\r
587 blockStart = hideEnd + 1;
\r
590 if (screenY <= (endRes - startRes))
\r
592 blockEnd = blockStart + (endRes - startRes) - screenY;
\r
593 g1.translate(screenY * av.charWidth, 0);
\r
594 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
595 g1.translate( -screenY * av.charWidth, 0);
\r
604 //int startRes, int endRes, int startSeq, int endSeq, int x, int y,
\r
605 // int x1, int x2, int y1, int y2, int startx, int starty,
\r
606 void draw(Graphics g1,
\r
607 int startRes, int endRes,
\r
608 int startSeq, int endSeq,
\r
611 Graphics2D g = (Graphics2D) g1;
\r
612 g.setFont(av.getFont());
\r
616 /// First draw the sequences
\r
617 /////////////////////////////
\r
618 for (int i = startSeq; i < endSeq; i++)
\r
620 nextSeq = av.alignment.getSequenceAt(i);
\r
622 sr.drawSequence(g, nextSeq, av.alignment.findAllGroups(nextSeq),
\r
624 offset + ( (i - startSeq) * av.charHeight));
\r
626 if (av.showSequenceFeatures)
\r
628 fr.drawSequence(g1, nextSeq, startRes, endRes,
\r
629 offset + ((i - startSeq) * av.charHeight), av.charWidth,
\r
633 /// Highlight search Results once all sequences have been drawn
\r
634 //////////////////////////////////////////////////////////
\r
635 if (searchResults != null)
\r
637 int[] visibleResults = searchResults.getResults(nextSeq, startRes, endRes);
\r
638 if (visibleResults != null)
\r
639 for (int r = 0; r < visibleResults.length; r += 2)
\r
641 sr.drawHighlightedText(nextSeq, visibleResults[r],
\r
642 visibleResults[r + 1],
\r
643 (visibleResults[r] - startRes) * av.charWidth,
\r
644 offset + ( (i - startSeq) * av.charHeight),
\r
645 av.charWidth, av.charHeight);
\r
651 /////////////////////////////////////
\r
652 // Now outline any areas if necessary
\r
653 /////////////////////////////////////
\r
654 SequenceGroup group = av.getSelectionGroup();
\r
659 int groupIndex = -1;
\r
661 if ((group == null) && (av.alignment.getGroups().size() > 0))
\r
663 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
673 boolean inGroup = false;
\r
677 for (i = startSeq; i < endSeq; i++)
\r
679 sx = (group.getStartRes() - startRes) * av.charWidth;
\r
680 sy = offset + ((i - startSeq) * av.charHeight);
\r
681 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
684 if(sx+ex<0 || sx>imgWidth)
\r
689 if ( (sx <= (endRes-startRes)*av.charWidth) &&
\r
690 group.sequences.contains(av.alignment.getSequenceAt(
\r
693 if ((bottom == -1) &&
\r
694 !group.sequences.contains(
\r
695 av.alignment.getSequenceAt(i + 1)))
\r
697 bottom = sy + av.charHeight;
\r
702 if (((top == -1) && (i == 0)) ||
\r
703 !group.sequences.contains(
\r
704 av.alignment.getSequenceAt(i - 1)))
\r
712 if (group == av.getSelectionGroup())
\r
714 g.setStroke(new BasicStroke(1,
\r
715 BasicStroke.CAP_BUTT,
\r
716 BasicStroke.JOIN_ROUND, 3f,
\r
717 new float[] { 5f, 3f }, 0f));
\r
718 g.setColor(Color.RED);
\r
722 g.setStroke(new BasicStroke());
\r
723 g.setColor(group.getOutlineColour());
\r
731 if (sx >= 0 && sx < imgWidth)
\r
732 g.drawLine(sx, oldY, sx, sy);
\r
734 if (sx + ex < imgWidth)
\r
735 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
743 if (sx + ex > imgWidth)
\r
746 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
747 ex = (endRes - startRes + 1) * av.charWidth;
\r
751 g.drawLine(sx, top, sx + ex, top);
\r
757 g.drawLine(sx, bottom, sx + ex, bottom);
\r
768 sy = offset + ( (i - startSeq) * av.charHeight);
\r
769 if (sx >= 0 && sx < imgWidth)
\r
770 g.drawLine(sx, oldY, sx, sy);
\r
772 if (sx + ex < imgWidth)
\r
773 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
781 if (sx + ex > imgWidth)
\r
783 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
784 ex = (endRes - startRes + 1) * av.charWidth;
\r
788 g.drawLine(sx, top, sx + ex, top);
\r
794 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
803 if (groupIndex >= av.alignment.getGroups().size())
\r
808 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
810 while (groupIndex < av.alignment.getGroups().size());
\r
817 * @param results DOCUMENT ME!
\r
819 public void highlightSearchResults(SearchResults results)
\r
823 searchResults = results;
\r