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
37 final FeatureRenderer fr;
\r
38 final SequenceRenderer sr;
\r
44 SearchResults searchResults = null;
\r
45 boolean fastPaint = false;
\r
55 * Creates a new SeqCanvas object.
\r
57 * @param av DOCUMENT ME!
\r
59 public SeqCanvas(AlignViewport av)
\r
62 fr = new FeatureRenderer(av);
\r
63 sr = new SequenceRenderer(av);
\r
64 setLayout(new BorderLayout());
\r
65 PaintRefresher.Register(this, av.alignment);
\r
66 setBackground(Color.white);
\r
69 MCview.PDBCanvas pdbCanvas;
\r
70 public SequenceRenderer getSequenceRenderer()
\r
75 public FeatureRenderer getFeatureRenderer()
\r
80 public void setPDBCanvas(MCview.PDBCanvas pc)
\r
85 public AlignViewport getViewport()
\r
94 * @param g DOCUMENT ME!
\r
95 * @param startx DOCUMENT ME!
\r
96 * @param endx DOCUMENT ME!
\r
97 * @param ypos DOCUMENT ME!
\r
99 void drawNorthScale(Graphics g, int startx, int endx, int ypos)
\r
101 int scalestartx = startx - (startx % 10) + 10;
\r
103 g.setColor(Color.black);
\r
106 for (int i = scalestartx; i < endx; i += 10)
\r
109 if(av.hasHiddenColumns)
\r
110 value = av.getColumnSelection().adjustForHiddenColumns(value);
\r
112 g.drawString( String.valueOf(value), (i - startx - 1) * av.charWidth,
\r
113 ypos - (av.charHeight / 2));
\r
115 g.drawLine(((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
\r
116 (ypos + 2) - (av.charHeight / 2),
\r
117 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -
\r
125 * @param g DOCUMENT ME!
\r
126 * @param startx DOCUMENT ME!
\r
127 * @param endx DOCUMENT ME!
\r
128 * @param ypos DOCUMENT ME!
\r
130 void drawWestScale(Graphics g, int startx, int endx, int ypos)
\r
132 FontMetrics fm = getFontMetrics(av.getFont());
\r
133 ypos += av.charHeight;
\r
135 if(av.hasHiddenColumns)
\r
137 startx = av.getColumnSelection().adjustForHiddenColumns(startx);
\r
138 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
\r
141 int maxwidth = av.alignment.getWidth();
\r
142 if (av.hasHiddenColumns)
\r
143 maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
\r
146 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
148 SequenceI seq = av.alignment.getSequenceAt(i);
\r
149 int index = startx;
\r
152 while (index < endx)
\r
154 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
161 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
168 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))-av.charWidth/2;
\r
169 g.drawString(value + "", x,
\r
170 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
178 * @param g DOCUMENT ME!
\r
179 * @param startx DOCUMENT ME!
\r
180 * @param endx DOCUMENT ME!
\r
181 * @param ypos DOCUMENT ME!
\r
183 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
185 ypos += av.charHeight;
\r
187 if(av.hasHiddenColumns)
\r
188 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
\r
192 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
194 seq = av.alignment.getSequenceAt(i);
\r
198 while (index > startx)
\r
200 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
207 value = seq.findPosition(index);
\r
214 g.drawString(String.valueOf(value), 0,
\r
215 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
223 * @param horizontal DOCUMENT ME!
\r
224 * @param vertical DOCUMENT ME!
\r
226 public void fastPaint(int horizontal, int vertical)
\r
236 gg.copyArea(horizontal * av.charWidth,
\r
237 vertical * av.charHeight,
\r
240 -horizontal * av.charWidth,
\r
241 -vertical * av.charHeight);
\r
243 int sr = av.startRes;
\r
244 int er = av.endRes;
\r
245 int ss = av.startSeq;
\r
246 int es = av.endSeq;
\r
251 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
254 transX = (er - sr - horizontal) * av.charWidth;
\r
255 sr = er - horizontal;
\r
257 else if (horizontal < 0)
\r
259 er = sr - horizontal-1;
\r
261 else if (vertical > 0) // scroll down
\r
263 ss = es - vertical;
\r
265 if (ss < av.startSeq)
\r
266 { // ie scrolling too fast, more than a page at a time
\r
271 transY = imgHeight - (vertical * av.charHeight);
\r
274 else if (vertical < 0)
\r
276 es = ss - vertical;
\r
278 if (es > av.endSeq)
\r
284 gg.translate(transX, transY);
\r
285 drawPanel(gg, sr, er, ss, es, 0);
\r
286 gg.translate(-transX, -transY);
\r
292 * Definitions of startx and endx (hopefully):
\r
293 * SMJS This is what I'm working towards!
\r
294 * startx is the first residue (starting at 0) to display.
\r
295 * endx is the last residue to display (starting at 0).
\r
296 * starty is the first sequence to display (starting at 0).
\r
297 * endy is the last sequence to display (starting at 0).
\r
298 * NOTE 1: The av limits are set in setFont in this class and
\r
299 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
302 // Set this to false to force a full panel paint
\r
303 public void paintComponent(Graphics g)
\r
305 super.paintComponent(g);
\r
309 if ( img != null && (fastPaint
\r
310 || (getVisibleRect().width != g.getClipBounds().width)
\r
311 || (getVisibleRect().height != g.getClipBounds().height)))
\r
313 g.drawImage(img, 0, 0, this);
\r
319 // this draws the whole of the alignment
\r
320 imgWidth = getWidth();
\r
321 imgHeight = getHeight();
\r
323 imgWidth -= (imgWidth % av.charWidth);
\r
324 imgHeight -= (imgHeight % av.charHeight);
\r
326 if ((imgWidth < 1) || (imgHeight < 1))
\r
331 img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
\r
332 gg = (Graphics2D) img.getGraphics();
\r
333 gg.setFont(av.getFont());
\r
336 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
337 RenderingHints.VALUE_ANTIALIAS_ON);
\r
339 gg.setColor(Color.white);
\r
340 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
343 if (av.getWrapAlignment())
\r
345 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
349 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, 0);
\r
352 g.drawImage(img, 0, 0, this);
\r
354 if (pdbCanvas != null)
\r
356 pdbCanvas.updateSeqColours();
\r
364 * @param cwidth DOCUMENT ME!
\r
366 * @return DOCUMENT ME!
\r
368 public int getWrappedCanvasWidth(int cwidth)
\r
370 FontMetrics fm = getFontMetrics(av.getFont());
\r
375 if (av.scaleRightWrapped)
\r
377 LABEL_EAST = fm.stringWidth(getMask());
\r
380 if (av.scaleLeftWrapped)
\r
382 LABEL_WEST = fm.stringWidth(getMask());
\r
385 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
390 * Generates a string of zeroes.
\r
395 String mask = "00";
\r
396 for (int i = av.alignment.getWidth(); i > 0; i /= 10)
\r
406 * @param g DOCUMENT ME!
\r
407 * @param canvasWidth DOCUMENT ME!
\r
408 * @param canvasHeight DOCUMENT ME!
\r
409 * @param startRes DOCUMENT ME!
\r
411 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
414 AlignmentI al = av.getAlignment();
\r
416 FontMetrics fm = getFontMetrics(av.getFont());
\r
419 if (av.scaleRightWrapped)
\r
421 LABEL_EAST = fm.stringWidth(getMask());
\r
425 if (av.scaleLeftWrapped)
\r
427 LABEL_WEST = fm.stringWidth(getMask());
\r
430 int hgap = av.charHeight;
\r
431 if(av.scaleAboveWrapped)
\r
432 hgap += av.charHeight;
\r
434 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
435 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
437 av.setWrappedWidth(cWidth);
\r
439 av.endRes = av.startRes + cWidth;
\r
444 int maxwidth = av.alignment.getWidth();
\r
446 if(av.hasHiddenColumns)
\r
447 maxwidth = av.getColumnSelection().findColumnPosition(maxwidth)-1;
\r
449 while ((ypos <= canvasHeight) && (startRes < maxwidth))
\r
451 endx = startRes + cWidth -1;
\r
453 if (endx > maxwidth)
\r
458 g.setFont(av.getFont());
\r
459 g.setColor(Color.black);
\r
461 if (av.scaleLeftWrapped)
\r
463 drawWestScale(g, startRes, endx, ypos);
\r
466 if (av.scaleRightWrapped)
\r
468 g.translate(canvasWidth - LABEL_EAST, 0);
\r
469 drawEastScale(g, startRes, endx, ypos);
\r
470 g.translate(-(canvasWidth - LABEL_EAST), 0);
\r
473 g.translate(LABEL_WEST, 0);
\r
475 if (av.scaleAboveWrapped)
\r
477 drawNorthScale(g, startRes, endx, ypos);
\r
480 if (av.hasHiddenColumns && av.showHiddenMarkers)
\r
482 g.setColor(Color.blue);
\r
484 for (int i = 0; i < av.getColumnSelection().getHiddenColumns().size();
\r
487 res = av.getColumnSelection().findHiddenRegionPosition(i) -
\r
490 if (res < 0 || res > endx - startRes)
\r
493 gg.fillPolygon(new int[]
\r
494 {res * av.charWidth - av.charHeight / 4,
\r
495 res * av.charWidth + av.charHeight / 4,
\r
496 res * av.charWidth},
\r
499 ypos - (av.charHeight / 2),
\r
500 ypos - (av.charHeight / 2),
\r
501 ypos - (av.charHeight / 2) + 8
\r
509 // When printing we have an extra clipped region,
\r
510 // the Printable page which we need to account for here
\r
511 Shape clip = g.getClip();
\r
515 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
519 g.setClip(0, (int) clip.getBounds().getY(),
\r
520 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
523 drawPanel(g, startRes, endx, 0, al.getHeight(), ypos);
\r
525 if(av.showAnnotation)
\r
527 g.translate(0, cHeight + ypos + 3);
\r
528 if(annotations==null)
\r
529 annotations = new AnnotationPanel(av);
\r
531 annotations.drawComponent( (Graphics2D) g, startRes, endx+1);
\r
532 g.translate(0, -cHeight - ypos);
\r
535 g.translate(-LABEL_WEST, 0);
\r
537 ypos += cHeight+getAnnotationHeight()+hgap;
\r
538 if(av.showAnnotation)
\r
541 startRes += cWidth;
\r
545 AnnotationPanel annotations;
\r
546 int getAnnotationHeight()
\r
548 if(!av.showAnnotation)
\r
551 if(annotations==null)
\r
552 annotations = new AnnotationPanel(av);
\r
554 return annotations.adjustPanelHeight();
\r
560 * @param g1 DOCUMENT ME!
\r
561 * @param startRes DOCUMENT ME!
\r
562 * @param endRes DOCUMENT ME!
\r
563 * @param startSeq DOCUMENT ME!
\r
564 * @param endSeq DOCUMENT ME!
\r
565 * @param offset DOCUMENT ME!
\r
567 void drawPanel(Graphics g1, int startRes, int endRes,
\r
568 int startSeq, int endSeq, int offset)
\r
570 if(!av.hasHiddenColumns)
\r
572 draw(g1, startRes, endRes, startSeq, endSeq, offset);
\r
576 java.util.Vector regions = av.getColumnSelection().getHiddenColumns();
\r
579 int blockStart = startRes;
\r
580 int blockEnd = endRes;
\r
582 for (int i = 0; i < regions.size(); i++)
\r
584 int[] region = (int[]) regions.elementAt(i);
\r
585 int hideStart = region[0];
\r
586 int hideEnd = region[1];
\r
588 if (hideStart <= blockStart)
\r
590 blockStart += (hideEnd - hideStart) + 1;
\r
594 blockEnd = hideStart - 1;
\r
596 g1.translate(screenY * av.charWidth, 0);
\r
598 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
600 if(av.getShowHiddenMarkers())
\r
602 g1.setColor(Color.blue);
\r
603 g1.drawLine( (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
605 (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
606 startSeq + (endSeq - startSeq) * av.charHeight + offset);
\r
609 g1.translate( -screenY * av.charWidth, 0);
\r
610 screenY += blockEnd - blockStart + 1;
\r
611 blockStart = hideEnd + 1;
\r
614 if (screenY <= (endRes - startRes))
\r
616 blockEnd = blockStart + (endRes - startRes) - screenY;
\r
617 g1.translate(screenY * av.charWidth, 0);
\r
618 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
620 g1.translate( -screenY * av.charWidth, 0);
\r
629 //int startRes, int endRes, int startSeq, int endSeq, int x, int y,
\r
630 // int x1, int x2, int y1, int y2, int startx, int starty,
\r
631 void draw(Graphics g,
\r
632 int startRes, int endRes,
\r
633 int startSeq, int endSeq,
\r
636 g.setFont(av.getFont());
\r
637 sr.prepare(g, av.renderGaps);
\r
641 /// First draw the sequences
\r
642 /////////////////////////////
\r
643 for (int i = startSeq; i < endSeq; i++)
\r
645 nextSeq = av.alignment.getSequenceAt(i);
\r
647 sr.drawSequence(nextSeq, av.alignment.findAllGroups(nextSeq),
\r
649 offset + ( (i - startSeq) * av.charHeight));
\r
651 if (av.showSequenceFeatures)
\r
653 fr.drawSequence(g, nextSeq, startRes, endRes,
\r
654 offset + ((i - startSeq) * av.charHeight));
\r
657 /// Highlight search Results once all sequences have been drawn
\r
658 //////////////////////////////////////////////////////////
\r
659 if (searchResults != null)
\r
661 int[] visibleResults = searchResults.getResults(nextSeq, startRes, endRes);
\r
662 if (visibleResults != null)
\r
663 for (int r = 0; r < visibleResults.length; r += 2)
\r
665 sr.drawHighlightedText(nextSeq, visibleResults[r],
\r
666 visibleResults[r + 1],
\r
667 (visibleResults[r] - startRes) * av.charWidth,
\r
668 offset + ( (i - startSeq) * av.charHeight));
\r
672 if(av.cursorMode && cursorY==i
\r
673 && cursorX>=startRes && cursorX<=endRes)
\r
675 sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * av.charWidth,
\r
676 offset + ( (i - startSeq) * av.charHeight));
\r
680 if(av.getSelectionGroup()!=null || av.alignment.getGroups().size()>0)
\r
681 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
\r
685 void drawGroupsBoundaries(Graphics g1,
\r
686 int startRes, int endRes,
\r
687 int startSeq, int endSeq,
\r
690 Graphics2D g = (Graphics2D)g1;
\r
692 /////////////////////////////////////
\r
693 // Now outline any areas if necessary
\r
694 /////////////////////////////////////
\r
695 SequenceGroup group = av.getSelectionGroup();
\r
700 int groupIndex = -1;
\r
702 if ((group == null) && (av.alignment.getGroups().size() > 0))
\r
704 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
714 boolean inGroup = false;
\r
718 for (i = startSeq; i < endSeq; i++)
\r
720 sx = (group.getStartRes() - startRes) * av.charWidth;
\r
721 sy = offset + ((i - startSeq) * av.charHeight);
\r
722 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
725 if(sx+ex<0 || sx>imgWidth)
\r
730 if ( (sx <= (endRes-startRes)*av.charWidth) &&
\r
731 group.getSequences(false).
\r
732 contains(av.alignment.getSequenceAt(i)))
\r
734 if ((bottom == -1) &&
\r
735 !group.getSequences(false).contains(
\r
736 av.alignment.getSequenceAt(i + 1)))
\r
738 bottom = sy + av.charHeight;
\r
743 if (((top == -1) && (i == 0)) ||
\r
744 !group.getSequences(false).contains(
\r
745 av.alignment.getSequenceAt(i - 1)))
\r
753 if (group == av.getSelectionGroup())
\r
755 g.setStroke(new BasicStroke(1,
\r
756 BasicStroke.CAP_BUTT,
\r
757 BasicStroke.JOIN_ROUND, 3f,
\r
758 new float[] { 5f, 3f }, 0f));
\r
759 g.setColor(Color.RED);
\r
763 g.setStroke(new BasicStroke());
\r
764 g.setColor(group.getOutlineColour());
\r
772 if (sx >= 0 && sx < imgWidth)
\r
773 g.drawLine(sx, oldY, sx, sy);
\r
775 if (sx + ex < imgWidth)
\r
776 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
784 if (sx + ex > imgWidth)
\r
787 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
788 ex = (endRes - startRes + 1) * av.charWidth;
\r
792 g.drawLine(sx, top, sx + ex, top);
\r
798 g.drawLine(sx, bottom, sx + ex, bottom);
\r
809 sy = offset + ( (i - startSeq) * av.charHeight);
\r
810 if (sx >= 0 && sx < imgWidth)
\r
811 g.drawLine(sx, oldY, sx, sy);
\r
813 if (sx + ex < imgWidth)
\r
814 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
822 if (sx + ex > imgWidth)
\r
824 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
825 ex = (endRes - startRes + 1) * av.charWidth;
\r
829 g.drawLine(sx, top, sx + ex, top);
\r
835 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
844 if (groupIndex >= av.alignment.getGroups().size())
\r
849 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
851 g.setStroke(new BasicStroke());
\r
853 while (groupIndex < av.alignment.getGroups().size());
\r
862 * @param results DOCUMENT ME!
\r
864 public void highlightSearchResults(SearchResults results)
\r
868 searchResults = results;
\r