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
184 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
186 SequenceI seq = av.alignment.getSequenceAt(i);
\r
192 while (index > startx)
\r
194 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
201 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
208 g.drawString(String.valueOf(value), 0,
\r
209 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
217 * @param horizontal DOCUMENT ME!
\r
218 * @param vertical DOCUMENT ME!
\r
220 public void fastPaint(int horizontal, int vertical)
\r
230 gg.copyArea(horizontal * av.charWidth,
\r
231 vertical * av.charHeight,
\r
234 -horizontal * av.charWidth,
\r
235 -vertical * av.charHeight);
\r
237 int sr = av.startRes;
\r
238 int er = av.endRes;
\r
239 int ss = av.startSeq;
\r
240 int es = av.endSeq;
\r
245 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
248 transX = (er - sr - horizontal) * av.charWidth;
\r
249 sr = er - horizontal;
\r
251 else if (horizontal < 0)
\r
253 er = sr - horizontal-1;
\r
255 else if (vertical > 0) // scroll down
\r
257 ss = es - vertical;
\r
259 if (ss < av.startSeq)
\r
260 { // ie scrolling too fast, more than a page at a time
\r
265 transY = imgHeight - (vertical * av.charHeight);
\r
268 else if (vertical < 0)
\r
270 es = ss - vertical;
\r
272 if (es > av.endSeq)
\r
278 gg.translate(transX, transY);
\r
279 drawPanel(gg, sr, er, ss, es, 0);
\r
280 gg.translate(-transX, -transY);
\r
286 * Definitions of startx and endx (hopefully):
\r
287 * SMJS This is what I'm working towards!
\r
288 * startx is the first residue (starting at 0) to display.
\r
289 * endx is the last residue to display (starting at 0).
\r
290 * starty is the first sequence to display (starting at 0).
\r
291 * endy is the last sequence to display (starting at 0).
\r
292 * NOTE 1: The av limits are set in setFont in this class and
\r
293 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
296 // Set this to false to force a full panel paint
\r
297 public void paintComponent(Graphics g)
\r
299 super.paintComponent(g);
\r
303 if ( img != null && (fastPaint
\r
304 || (getVisibleRect().width != g.getClipBounds().width)
\r
305 || (getVisibleRect().height != g.getClipBounds().height)))
\r
307 g.drawImage(img, 0, 0, this);
\r
313 // this draws the whole of the alignment
\r
314 imgWidth = getWidth();
\r
315 imgHeight = getHeight();
\r
317 imgWidth -= (imgWidth % av.charWidth);
\r
318 imgHeight -= (imgHeight % av.charHeight);
\r
320 if ((imgWidth < 1) || (imgHeight < 1))
\r
325 img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
\r
326 gg = (Graphics2D) img.getGraphics();
\r
327 gg.setFont(av.getFont());
\r
330 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
331 RenderingHints.VALUE_ANTIALIAS_ON);
\r
333 gg.setColor(Color.white);
\r
334 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
337 if (av.getWrapAlignment())
\r
339 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
343 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, 0);
\r
346 g.drawImage(img, 0, 0, this);
\r
348 if (pdbCanvas != null)
\r
350 pdbCanvas.updateSeqColours();
\r
358 * @param cwidth DOCUMENT ME!
\r
360 * @return DOCUMENT ME!
\r
362 public int getWrappedCanvasWidth(int cwidth)
\r
364 FontMetrics fm = getFontMetrics(av.getFont());
\r
369 if (av.scaleRightWrapped)
\r
371 LABEL_EAST = fm.stringWidth(getMask());
\r
374 if (av.scaleLeftWrapped)
\r
376 LABEL_WEST = fm.stringWidth(getMask());
\r
379 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
384 * Generates a string of zeroes.
\r
389 String mask = "00";
\r
390 for (int i = av.alignment.getWidth(); i > 0; i /= 10)
\r
400 * @param g DOCUMENT ME!
\r
401 * @param canvasWidth DOCUMENT ME!
\r
402 * @param canvasHeight DOCUMENT ME!
\r
403 * @param startRes DOCUMENT ME!
\r
405 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
409 AlignmentI al = av.getAlignment();
\r
411 FontMetrics fm = getFontMetrics(av.getFont());
\r
413 int LABEL_EAST = 0;
\r
415 if (av.scaleRightWrapped)
\r
417 LABEL_EAST = fm.stringWidth(getMask());
\r
420 int LABEL_WEST = 0;
\r
422 if (av.scaleLeftWrapped)
\r
424 LABEL_WEST = fm.stringWidth(getMask());
\r
427 int hgap = av.charHeight;
\r
428 if(av.scaleAboveWrapped)
\r
429 hgap += av.charHeight;
\r
431 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
432 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
434 av.setWrappedWidth(cWidth);
\r
436 av.endRes = av.startRes + cWidth;
\r
442 while ((ypos <= canvasHeight) && (startRes < av.alignment.getWidth()))
\r
444 endx = startRes + cWidth;
\r
446 if (endx > al.getWidth())
\r
448 endx = al.getWidth();
\r
451 g.setFont(av.getFont());
\r
452 g.setColor(Color.black);
\r
454 if (av.scaleLeftWrapped)
\r
456 drawWestScale(g, startRes, endx, ypos);
\r
459 if (av.scaleRightWrapped)
\r
461 g.translate(canvasWidth - LABEL_EAST, 0);
\r
462 drawEastScale(g, startRes, endx, ypos);
\r
463 g.translate(-(canvasWidth - LABEL_EAST), 0);
\r
466 g.translate(LABEL_WEST, 0);
\r
468 if (av.scaleAboveWrapped)
\r
470 drawNorthScale(g, startRes, endx, ypos);
\r
473 // When printing we have an extra clipped region,
\r
474 // the Printable page which we need to account for here
\r
475 Shape clip = g.getClip();
\r
479 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
483 g.setClip(0, (int) clip.getBounds().getY(),
\r
484 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
487 drawPanel(g, startRes, endx, 0, al.getHeight(), ypos);
\r
489 if(av.showAnnotation)
\r
491 g.translate(0, cHeight + ypos + 3);
\r
492 if(annotations==null)
\r
493 annotations = new AnnotationPanel(av);
\r
495 annotations.drawComponent( (Graphics2D) g, startRes, endx);
\r
496 g.translate(0, -cHeight - ypos);
\r
499 g.translate(-LABEL_WEST, 0);
\r
501 ypos += cHeight+getAnnotationHeight()+hgap;
\r
502 if(av.showAnnotation)
\r
505 startRes += cWidth;
\r
509 AnnotationPanel annotations;
\r
510 int getAnnotationHeight()
\r
512 if(!av.showAnnotation)
\r
515 if(annotations==null)
\r
516 annotations = new AnnotationPanel(av);
\r
518 return annotations.adjustPanelHeight();
\r
524 * @param g1 DOCUMENT ME!
\r
525 * @param startRes DOCUMENT ME!
\r
526 * @param endRes DOCUMENT ME!
\r
527 * @param startSeq DOCUMENT ME!
\r
528 * @param endSeq DOCUMENT ME!
\r
529 * @param offset DOCUMENT ME!
\r
531 void drawPanel(Graphics g1, int startRes, int endRes,
\r
532 int startSeq, int endSeq, int offset)
\r
534 if(!av.hasHiddenColumns)
\r
536 draw(g1, startRes, endRes, startSeq, endSeq, offset);
\r
540 java.util.Vector regions = av.getColumnSelection().getHiddenColumns();
\r
543 int blockStart = startRes;
\r
544 int blockEnd = endRes;
\r
546 for (int i = 0; i < regions.size(); i++)
\r
548 int[] region = (int[]) regions.elementAt(i);
\r
549 int hideStart = region[0];
\r
550 int hideEnd = region[1];
\r
552 if (hideStart <= blockStart)
\r
554 blockStart += (hideEnd - hideStart) + 1;
\r
558 blockEnd = hideStart - 1;
\r
560 g1.translate(screenY * av.charWidth, 0);
\r
562 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
564 g1.setColor(Color.blue);
\r
565 g1.drawLine( (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
567 (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
568 startSeq + (endSeq - startSeq) * av.charHeight + offset);
\r
569 g1.drawLine( (blockEnd - blockStart + 1) * av.charWidth,
\r
571 (blockEnd - blockStart + 1) * av.charWidth,
\r
572 startSeq + (endSeq - startSeq) * av.charHeight + offset);
\r
574 g1.translate( -screenY * av.charWidth, 0);
\r
575 screenY += blockEnd - blockStart + 1;
\r
576 blockStart = hideEnd + 1;
\r
579 if (screenY <= (endRes - startRes))
\r
581 blockEnd = blockStart + (endRes - startRes) - screenY;
\r
582 g1.translate(screenY * av.charWidth, 0);
\r
583 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
584 g1.translate( -screenY * av.charWidth, 0);
\r
593 //int startRes, int endRes, int startSeq, int endSeq, int x, int y,
\r
594 // int x1, int x2, int y1, int y2, int startx, int starty,
\r
595 void draw(Graphics g1,
\r
596 int startRes, int endRes,
\r
597 int startSeq, int endSeq,
\r
602 Graphics2D g = (Graphics2D) g1;
\r
603 g.setFont(av.getFont());
\r
604 sr.prepare(g, av.renderGaps);
\r
608 /// First draw the sequences
\r
609 /////////////////////////////
\r
610 for (int i = startSeq; i < endSeq; i++)
\r
612 nextSeq = av.alignment.getSequenceAt(i);
\r
614 sr.drawSequence(nextSeq, av.alignment.findAllGroups(nextSeq),
\r
616 offset + ( (i - startSeq) * av.charHeight));
\r
618 if (av.showSequenceFeatures)
\r
620 fr.drawSequence(g1, nextSeq, startRes, endRes,
\r
621 offset + ((i - startSeq) * av.charHeight));
\r
624 /// Highlight search Results once all sequences have been drawn
\r
625 //////////////////////////////////////////////////////////
\r
626 if (searchResults != null)
\r
628 int[] visibleResults = searchResults.getResults(nextSeq, startRes, endRes);
\r
629 if (visibleResults != null)
\r
630 for (int r = 0; r < visibleResults.length; r += 2)
\r
632 sr.drawHighlightedText(nextSeq, visibleResults[r],
\r
633 visibleResults[r + 1],
\r
634 (visibleResults[r] - startRes) * av.charWidth,
\r
635 offset + ( (i - startSeq) * av.charHeight));
\r
639 if(av.cursorMode && cursorY==i
\r
640 && cursorX>=startRes && cursorX<=endRes)
\r
642 sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * av.charWidth,
\r
643 offset + ( (i - startSeq) * av.charHeight));
\r
648 /////////////////////////////////////
\r
649 // Now outline any areas if necessary
\r
650 /////////////////////////////////////
\r
651 SequenceGroup group = av.getSelectionGroup();
\r
656 int groupIndex = -1;
\r
658 if ((group == null) && (av.alignment.getGroups().size() > 0))
\r
660 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
670 boolean inGroup = false;
\r
674 for (i = startSeq; i < endSeq; i++)
\r
676 sx = (group.getStartRes() - startRes) * av.charWidth;
\r
677 sy = offset + ((i - startSeq) * av.charHeight);
\r
678 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
681 if(sx+ex<0 || sx>imgWidth)
\r
686 if ( (sx <= (endRes-startRes)*av.charWidth) &&
\r
687 group.sequences.contains(av.alignment.getSequenceAt(
\r
690 if ((bottom == -1) &&
\r
691 !group.sequences.contains(
\r
692 av.alignment.getSequenceAt(i + 1)))
\r
694 bottom = sy + av.charHeight;
\r
699 if (((top == -1) && (i == 0)) ||
\r
700 !group.sequences.contains(
\r
701 av.alignment.getSequenceAt(i - 1)))
\r
709 if (group == av.getSelectionGroup())
\r
711 g.setStroke(new BasicStroke(1,
\r
712 BasicStroke.CAP_BUTT,
\r
713 BasicStroke.JOIN_ROUND, 3f,
\r
714 new float[] { 5f, 3f }, 0f));
\r
715 g.setColor(Color.RED);
\r
719 g.setStroke(new BasicStroke());
\r
720 g.setColor(group.getOutlineColour());
\r
728 if (sx >= 0 && sx < imgWidth)
\r
729 g.drawLine(sx, oldY, sx, sy);
\r
731 if (sx + ex < imgWidth)
\r
732 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
740 if (sx + ex > imgWidth)
\r
743 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
744 ex = (endRes - startRes + 1) * av.charWidth;
\r
748 g.drawLine(sx, top, sx + ex, top);
\r
754 g.drawLine(sx, bottom, sx + ex, bottom);
\r
765 sy = offset + ( (i - startSeq) * av.charHeight);
\r
766 if (sx >= 0 && sx < imgWidth)
\r
767 g.drawLine(sx, oldY, sx, sy);
\r
769 if (sx + ex < imgWidth)
\r
770 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
778 if (sx + ex > imgWidth)
\r
780 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
781 ex = (endRes - startRes + 1) * av.charWidth;
\r
785 g.drawLine(sx, top, sx + ex, top);
\r
791 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
800 if (groupIndex >= av.alignment.getGroups().size())
\r
805 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
807 while (groupIndex < av.alignment.getGroups().size());
\r
814 * @param results DOCUMENT ME!
\r
816 public void highlightSearchResults(SearchResults results)
\r
820 searchResults = results;
\r