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
136 startx = av.getColumnSelection().adjustForHiddenColumns(startx);
\r
139 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
141 SequenceI seq = av.alignment.getSequenceAt(i);
\r
142 int index = startx;
\r
145 while (index < endx)
\r
147 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
154 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
161 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))-av.charWidth/2;
\r
162 g.drawString(value + "", x,
\r
163 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
171 * @param g DOCUMENT ME!
\r
172 * @param startx DOCUMENT ME!
\r
173 * @param endx DOCUMENT ME!
\r
174 * @param ypos DOCUMENT ME!
\r
176 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
178 ypos += av.charHeight;
\r
180 if(av.hasHiddenColumns)
\r
181 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
\r
185 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
187 seq = av.alignment.getSequenceAt(i);
\r
191 while (index > startx)
\r
193 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
200 value = seq.findPosition(index);
\r
207 g.drawString(String.valueOf(value), 0,
\r
208 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
216 * @param horizontal DOCUMENT ME!
\r
217 * @param vertical DOCUMENT ME!
\r
219 public void fastPaint(int horizontal, int vertical)
\r
229 gg.copyArea(horizontal * av.charWidth,
\r
230 vertical * av.charHeight,
\r
233 -horizontal * av.charWidth,
\r
234 -vertical * av.charHeight);
\r
236 int sr = av.startRes;
\r
237 int er = av.endRes;
\r
238 int ss = av.startSeq;
\r
239 int es = av.endSeq;
\r
244 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
247 transX = (er - sr - horizontal) * av.charWidth;
\r
248 sr = er - horizontal;
\r
250 else if (horizontal < 0)
\r
252 er = sr - horizontal-1;
\r
254 else if (vertical > 0) // scroll down
\r
256 ss = es - vertical;
\r
258 if (ss < av.startSeq)
\r
259 { // ie scrolling too fast, more than a page at a time
\r
264 transY = imgHeight - (vertical * av.charHeight);
\r
267 else if (vertical < 0)
\r
269 es = ss - vertical;
\r
271 if (es > av.endSeq)
\r
277 gg.translate(transX, transY);
\r
278 drawPanel(gg, sr, er, ss, es, 0);
\r
279 gg.translate(-transX, -transY);
\r
285 * Definitions of startx and endx (hopefully):
\r
286 * SMJS This is what I'm working towards!
\r
287 * startx is the first residue (starting at 0) to display.
\r
288 * endx is the last residue to display (starting at 0).
\r
289 * starty is the first sequence to display (starting at 0).
\r
290 * endy is the last sequence to display (starting at 0).
\r
291 * NOTE 1: The av limits are set in setFont in this class and
\r
292 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
295 // Set this to false to force a full panel paint
\r
296 public void paintComponent(Graphics g)
\r
298 super.paintComponent(g);
\r
302 if ( img != null && (fastPaint
\r
303 || (getVisibleRect().width != g.getClipBounds().width)
\r
304 || (getVisibleRect().height != g.getClipBounds().height)))
\r
306 g.drawImage(img, 0, 0, this);
\r
312 // this draws the whole of the alignment
\r
313 imgWidth = getWidth();
\r
314 imgHeight = getHeight();
\r
316 imgWidth -= (imgWidth % av.charWidth);
\r
317 imgHeight -= (imgHeight % av.charHeight);
\r
319 if ((imgWidth < 1) || (imgHeight < 1))
\r
324 img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
\r
325 gg = (Graphics2D) img.getGraphics();
\r
326 gg.setFont(av.getFont());
\r
329 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
330 RenderingHints.VALUE_ANTIALIAS_ON);
\r
332 gg.setColor(Color.white);
\r
333 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
336 if (av.getWrapAlignment())
\r
338 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
342 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, 0);
\r
345 g.drawImage(img, 0, 0, this);
\r
347 if (pdbCanvas != null)
\r
349 pdbCanvas.updateSeqColours();
\r
357 * @param cwidth DOCUMENT ME!
\r
359 * @return DOCUMENT ME!
\r
361 public int getWrappedCanvasWidth(int cwidth)
\r
363 FontMetrics fm = getFontMetrics(av.getFont());
\r
368 if (av.scaleRightWrapped)
\r
370 LABEL_EAST = fm.stringWidth(getMask());
\r
373 if (av.scaleLeftWrapped)
\r
375 LABEL_WEST = fm.stringWidth(getMask());
\r
378 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
383 * Generates a string of zeroes.
\r
388 String mask = "00";
\r
389 for (int i = av.alignment.getWidth(); i > 0; i /= 10)
\r
399 * @param g DOCUMENT ME!
\r
400 * @param canvasWidth DOCUMENT ME!
\r
401 * @param canvasHeight DOCUMENT ME!
\r
402 * @param startRes DOCUMENT ME!
\r
404 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
408 AlignmentI al = av.getAlignment();
\r
410 FontMetrics fm = getFontMetrics(av.getFont());
\r
412 int LABEL_EAST = 0;
\r
414 if (av.scaleRightWrapped)
\r
416 LABEL_EAST = fm.stringWidth(getMask());
\r
419 int LABEL_WEST = 0;
\r
421 if (av.scaleLeftWrapped)
\r
423 LABEL_WEST = fm.stringWidth(getMask());
\r
426 int hgap = av.charHeight;
\r
427 if(av.scaleAboveWrapped)
\r
428 hgap += av.charHeight;
\r
430 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
431 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
433 av.setWrappedWidth(cWidth);
\r
435 av.endRes = av.startRes + cWidth;
\r
441 while ((ypos <= canvasHeight) && (startRes < av.alignment.getWidth()))
\r
443 endx = startRes + cWidth -1;
\r
445 if (endx > al.getWidth())
\r
447 endx = al.getWidth();
\r
450 g.setFont(av.getFont());
\r
451 g.setColor(Color.black);
\r
453 if (av.scaleLeftWrapped)
\r
455 drawWestScale(g, startRes, endx, ypos);
\r
458 if (av.scaleRightWrapped)
\r
460 g.translate(canvasWidth - LABEL_EAST, 0);
\r
461 drawEastScale(g, startRes, endx, ypos);
\r
462 g.translate(-(canvasWidth - LABEL_EAST), 0);
\r
465 g.translate(LABEL_WEST, 0);
\r
467 if (av.scaleAboveWrapped)
\r
469 drawNorthScale(g, startRes, endx, ypos);
\r
472 // When printing we have an extra clipped region,
\r
473 // the Printable page which we need to account for here
\r
474 Shape clip = g.getClip();
\r
478 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
482 g.setClip(0, (int) clip.getBounds().getY(),
\r
483 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
486 drawPanel(g, startRes, endx, 0, al.getHeight(), ypos);
\r
488 if(av.showAnnotation)
\r
490 g.translate(0, cHeight + ypos + 3);
\r
491 if(annotations==null)
\r
492 annotations = new AnnotationPanel(av);
\r
494 annotations.drawComponent( (Graphics2D) g, startRes, endx+1);
\r
495 g.translate(0, -cHeight - ypos);
\r
498 g.translate(-LABEL_WEST, 0);
\r
500 ypos += cHeight+getAnnotationHeight()+hgap;
\r
501 if(av.showAnnotation)
\r
504 startRes += cWidth;
\r
508 AnnotationPanel annotations;
\r
509 int getAnnotationHeight()
\r
511 if(!av.showAnnotation)
\r
514 if(annotations==null)
\r
515 annotations = new AnnotationPanel(av);
\r
517 return annotations.adjustPanelHeight();
\r
523 * @param g1 DOCUMENT ME!
\r
524 * @param startRes DOCUMENT ME!
\r
525 * @param endRes DOCUMENT ME!
\r
526 * @param startSeq DOCUMENT ME!
\r
527 * @param endSeq DOCUMENT ME!
\r
528 * @param offset DOCUMENT ME!
\r
530 void drawPanel(Graphics g1, int startRes, int endRes,
\r
531 int startSeq, int endSeq, int offset)
\r
533 if(!av.hasHiddenColumns)
\r
535 draw(g1, startRes, endRes, startSeq, endSeq, offset);
\r
539 java.util.Vector regions = av.getColumnSelection().getHiddenColumns();
\r
542 int blockStart = startRes;
\r
543 int blockEnd = endRes;
\r
545 for (int i = 0; i < regions.size(); i++)
\r
547 int[] region = (int[]) regions.elementAt(i);
\r
548 int hideStart = region[0];
\r
549 int hideEnd = region[1];
\r
551 if (hideStart <= blockStart)
\r
553 blockStart += (hideEnd - hideStart) + 1;
\r
557 blockEnd = hideStart - 1;
\r
559 g1.translate(screenY * av.charWidth, 0);
\r
561 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
563 g1.setColor(Color.blue);
\r
564 g1.drawLine( (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
566 (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
567 startSeq + (endSeq - startSeq) * av.charHeight + offset);
\r
568 g1.drawLine( (blockEnd - blockStart + 1) * av.charWidth,
\r
570 (blockEnd - blockStart + 1) * av.charWidth,
\r
571 startSeq + (endSeq - startSeq) * av.charHeight + offset);
\r
573 g1.translate( -screenY * av.charWidth, 0);
\r
574 screenY += blockEnd - blockStart + 1;
\r
575 blockStart = hideEnd + 1;
\r
578 if (screenY <= (endRes - startRes))
\r
580 blockEnd = blockStart + (endRes - startRes) - screenY;
\r
581 g1.translate(screenY * av.charWidth, 0);
\r
582 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
583 g1.translate( -screenY * av.charWidth, 0);
\r
592 //int startRes, int endRes, int startSeq, int endSeq, int x, int y,
\r
593 // int x1, int x2, int y1, int y2, int startx, int starty,
\r
594 void draw(Graphics g,
\r
595 int startRes, int endRes,
\r
596 int startSeq, int endSeq,
\r
599 g.setFont(av.getFont());
\r
600 sr.prepare(g, av.renderGaps);
\r
604 /// First draw the sequences
\r
605 /////////////////////////////
\r
606 for (int i = startSeq; i < endSeq; i++)
\r
608 nextSeq = av.alignment.getSequenceAt(i);
\r
610 sr.drawSequence(nextSeq, av.alignment.findAllGroups(nextSeq),
\r
612 offset + ( (i - startSeq) * av.charHeight));
\r
614 if (av.showSequenceFeatures)
\r
616 fr.drawSequence(g, nextSeq, startRes, endRes,
\r
617 offset + ((i - startSeq) * av.charHeight));
\r
620 /// Highlight search Results once all sequences have been drawn
\r
621 //////////////////////////////////////////////////////////
\r
622 if (searchResults != null)
\r
624 int[] visibleResults = searchResults.getResults(nextSeq, startRes, endRes);
\r
625 if (visibleResults != null)
\r
626 for (int r = 0; r < visibleResults.length; r += 2)
\r
628 sr.drawHighlightedText(nextSeq, visibleResults[r],
\r
629 visibleResults[r + 1],
\r
630 (visibleResults[r] - startRes) * av.charWidth,
\r
631 offset + ( (i - startSeq) * av.charHeight));
\r
635 if(av.cursorMode && cursorY==i
\r
636 && cursorX>=startRes && cursorX<=endRes)
\r
638 sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * av.charWidth,
\r
639 offset + ( (i - startSeq) * av.charHeight));
\r
643 if(av.getSelectionGroup()!=null || av.alignment.getGroups().size()>0)
\r
644 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
\r
648 void drawGroupsBoundaries(Graphics g1,
\r
649 int startRes, int endRes,
\r
650 int startSeq, int endSeq,
\r
653 Graphics2D g = (Graphics2D)g1;
\r
655 /////////////////////////////////////
\r
656 // Now outline any areas if necessary
\r
657 /////////////////////////////////////
\r
658 SequenceGroup group = av.getSelectionGroup();
\r
663 int groupIndex = -1;
\r
665 if ((group == null) && (av.alignment.getGroups().size() > 0))
\r
667 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
677 boolean inGroup = false;
\r
681 for (i = startSeq; i < endSeq; i++)
\r
683 sx = (group.getStartRes() - startRes) * av.charWidth;
\r
684 sy = offset + ((i - startSeq) * av.charHeight);
\r
685 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
688 if(sx+ex<0 || sx>imgWidth)
\r
693 if ( (sx <= (endRes-startRes)*av.charWidth) &&
\r
694 group.sequences.contains(av.alignment.getSequenceAt(
\r
697 if ((bottom == -1) &&
\r
698 !group.sequences.contains(
\r
699 av.alignment.getSequenceAt(i + 1)))
\r
701 bottom = sy + av.charHeight;
\r
706 if (((top == -1) && (i == 0)) ||
\r
707 !group.sequences.contains(
\r
708 av.alignment.getSequenceAt(i - 1)))
\r
716 if (group == av.getSelectionGroup())
\r
718 g.setStroke(new BasicStroke(1,
\r
719 BasicStroke.CAP_BUTT,
\r
720 BasicStroke.JOIN_ROUND, 3f,
\r
721 new float[] { 5f, 3f }, 0f));
\r
722 g.setColor(Color.RED);
\r
726 g.setStroke(new BasicStroke());
\r
727 g.setColor(group.getOutlineColour());
\r
735 if (sx >= 0 && sx < imgWidth)
\r
736 g.drawLine(sx, oldY, sx, sy);
\r
738 if (sx + ex < imgWidth)
\r
739 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
747 if (sx + ex > imgWidth)
\r
750 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
751 ex = (endRes - startRes + 1) * av.charWidth;
\r
755 g.drawLine(sx, top, sx + ex, top);
\r
761 g.drawLine(sx, bottom, sx + ex, bottom);
\r
772 sy = offset + ( (i - startSeq) * av.charHeight);
\r
773 if (sx >= 0 && sx < imgWidth)
\r
774 g.drawLine(sx, oldY, sx, sy);
\r
776 if (sx + ex < imgWidth)
\r
777 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
785 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 - 1, sx + ex, bottom - 1);
\r
807 if (groupIndex >= av.alignment.getGroups().size())
\r
812 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
814 while (groupIndex < av.alignment.getGroups().size());
\r
821 * @param results DOCUMENT ME!
\r
823 public void highlightSearchResults(SearchResults results)
\r
827 searchResults = results;
\r