2 * Jalview - A Sequence Alignment Editor and Viewer
\r
3 * Copyright (C) 2007 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
22 import java.awt.image.*;
\r
23 import javax.swing.*;
\r
25 import jalview.datamodel.*;
\r
31 * @version $Revision$
\r
33 public class SeqCanvas
\r
36 final FeatureRenderer fr;
\r
37 final SequenceRenderer sr;
\r
43 SearchResults searchResults = null;
\r
44 boolean fastPaint = false;
\r
52 * Creates a new SeqCanvas object.
\r
54 * @param av DOCUMENT ME!
\r
56 public SeqCanvas(AlignViewport av)
\r
59 fr = new FeatureRenderer(av);
\r
60 sr = new SequenceRenderer(av);
\r
61 setLayout(new BorderLayout());
\r
62 PaintRefresher.Register(this, av.getSequenceSetId());
\r
63 setBackground(Color.white);
\r
66 MCview.PDBCanvas pdbCanvas;
\r
67 public SequenceRenderer getSequenceRenderer()
\r
72 public FeatureRenderer getFeatureRenderer()
\r
77 public void setPDBCanvas(MCview.PDBCanvas pc)
\r
82 public AlignViewport getViewport()
\r
90 * @param g DOCUMENT ME!
\r
91 * @param startx DOCUMENT ME!
\r
92 * @param endx DOCUMENT ME!
\r
93 * @param ypos DOCUMENT ME!
\r
95 void drawNorthScale(Graphics g, int startx, int endx, int ypos)
\r
97 int scalestartx = startx - (startx % 10) + 10;
\r
99 g.setColor(Color.black);
\r
102 for (int i = scalestartx; i < endx; i += 10)
\r
105 if (av.hasHiddenColumns)
\r
107 value = av.getColumnSelection().adjustForHiddenColumns(value);
\r
110 g.drawString(String.valueOf(value), (i - startx - 1) * av.charWidth,
\r
111 ypos - (av.charHeight / 2));
\r
113 g.drawLine( ( (i - startx - 1) * av.charWidth) + (av.charWidth / 2),
\r
114 (ypos + 2) - (av.charHeight / 2),
\r
115 ( (i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -
\r
123 * @param g DOCUMENT ME!
\r
124 * @param startx DOCUMENT ME!
\r
125 * @param endx DOCUMENT ME!
\r
126 * @param ypos DOCUMENT ME!
\r
128 void drawWestScale(Graphics g, int startx, int endx, int ypos)
\r
130 FontMetrics fm = getFontMetrics(av.getFont());
\r
131 ypos += av.charHeight;
\r
133 if (av.hasHiddenColumns)
\r
135 startx = av.getColumnSelection().adjustForHiddenColumns(startx);
\r
136 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
\r
139 int maxwidth = av.alignment.getWidth();
\r
140 if (av.hasHiddenColumns)
\r
142 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)) -
\r
170 g.drawString(value + "", x,
\r
171 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
179 * @param g DOCUMENT ME!
\r
180 * @param startx DOCUMENT ME!
\r
181 * @param endx DOCUMENT ME!
\r
182 * @param ypos DOCUMENT ME!
\r
184 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
186 ypos += av.charHeight;
\r
188 if (av.hasHiddenColumns)
\r
190 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
\r
195 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
197 seq = av.alignment.getSequenceAt(i);
\r
201 while (index > startx)
\r
203 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
210 value = seq.findPosition(index);
\r
217 g.drawString(String.valueOf(value), 0,
\r
218 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
226 * @param horizontal DOCUMENT ME!
\r
227 * @param vertical DOCUMENT ME!
\r
229 public void fastPaint(int horizontal, int vertical)
\r
238 gg.copyArea(horizontal * av.charWidth,
\r
239 vertical * av.charHeight,
\r
242 -horizontal * av.charWidth,
\r
243 -vertical * av.charHeight);
\r
245 int sr = av.startRes;
\r
246 int er = av.endRes;
\r
247 int ss = av.startSeq;
\r
248 int es = av.endSeq;
\r
252 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
255 transX = (er - sr - horizontal) * av.charWidth;
\r
256 sr = er - horizontal;
\r
258 else if (horizontal < 0)
\r
260 er = sr - horizontal - 1;
\r
262 else if (vertical > 0) // scroll down
\r
264 ss = es - vertical;
\r
266 if (ss < av.startSeq)
\r
267 { // ie scrolling too fast, more than a page at a time
\r
272 transY = imgHeight - (vertical * av.charHeight);
\r
275 else if (vertical < 0)
\r
277 es = ss - vertical;
\r
279 if (es > av.endSeq)
\r
285 gg.translate(transX, transY);
\r
286 drawPanel(gg, sr, er, ss, es, 0);
\r
287 gg.translate( -transX, -transY);
\r
293 * Definitions of startx and endx (hopefully):
\r
294 * SMJS This is what I'm working towards!
\r
295 * startx is the first residue (starting at 0) to display.
\r
296 * endx is the last residue to display (starting at 0).
\r
297 * starty is the first sequence to display (starting at 0).
\r
298 * endy is the last sequence to display (starting at 0).
\r
299 * NOTE 1: The av limits are set in setFont in this class and
\r
300 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
303 // Set this to false to force a full panel paint
\r
304 public void paintComponent(Graphics g)
\r
306 super.paintComponent(g);
\r
308 if (img != null && (fastPaint
\r
309 || (getVisibleRect().width != g.getClipBounds().width)
\r
310 || (getVisibleRect().height != g.getClipBounds().height)))
\r
312 g.drawImage(img, 0, 0, this);
\r
317 // this draws the whole of the alignment
\r
318 imgWidth = getWidth();
\r
319 imgHeight = getHeight();
\r
321 imgWidth -= (imgWidth % av.charWidth);
\r
322 imgHeight -= (imgHeight % av.charHeight);
\r
324 if ( (imgWidth < 1) || (imgHeight < 1))
\r
329 if (img == null || imgWidth != img.getWidth() || imgHeight != img.getHeight())
\r
333 img = new BufferedImage(imgWidth, imgHeight,
\r
334 BufferedImage.TYPE_INT_RGB);
\r
335 gg = (Graphics2D) img.getGraphics();
\r
336 gg.setFont(av.getFont());
\r
338 catch (OutOfMemoryError er)
\r
341 System.out.println(er + " making image, SeqCanvas");
\r
342 javax.swing.SwingUtilities.invokeLater(new Runnable()
\r
346 javax.swing.JOptionPane.showInternalMessageDialog(Desktop.
\r
348 "Out of memory creating alignment image!!"
\r
350 "\nSee help files for increasing Java Virtual Machine memory."
\r
352 javax.swing.JOptionPane.WARNING_MESSAGE);
\r
362 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
363 RenderingHints.VALUE_ANTIALIAS_ON);
\r
366 gg.setColor(Color.white);
\r
367 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
369 if (av.getWrapAlignment())
\r
371 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
375 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, 0);
\r
378 g.drawImage(img, 0, 0, this);
\r
380 if (pdbCanvas != null)
\r
382 pdbCanvas.updateSeqColours();
\r
390 * @param cwidth DOCUMENT ME!
\r
392 * @return DOCUMENT ME!
\r
394 public int getWrappedCanvasWidth(int cwidth)
\r
396 FontMetrics fm = getFontMetrics(av.getFont());
\r
401 if (av.scaleRightWrapped)
\r
403 LABEL_EAST = fm.stringWidth(getMask());
\r
406 if (av.scaleLeftWrapped)
\r
408 LABEL_WEST = fm.stringWidth(getMask());
\r
411 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
415 * Generates a string of zeroes.
\r
420 String mask = "00";
\r
423 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
425 tmp = av.alignment.getSequenceAt(i).getEnd();
\r
426 if (tmp > maxWidth)
\r
432 for (int i = maxWidth; i > 0; i /= 10)
\r
442 * @param g DOCUMENT ME!
\r
443 * @param canvasWidth DOCUMENT ME!
\r
444 * @param canvasHeight DOCUMENT ME!
\r
445 * @param startRes DOCUMENT ME!
\r
447 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
450 AlignmentI al = av.getAlignment();
\r
452 FontMetrics fm = getFontMetrics(av.getFont());
\r
454 if (av.scaleRightWrapped)
\r
456 LABEL_EAST = fm.stringWidth(getMask());
\r
459 if (av.scaleLeftWrapped)
\r
461 LABEL_WEST = fm.stringWidth(getMask());
\r
464 int hgap = av.charHeight;
\r
465 if (av.scaleAboveWrapped)
\r
467 hgap += av.charHeight;
\r
470 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
471 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
473 av.setWrappedWidth(cWidth);
\r
475 av.endRes = av.startRes + cWidth;
\r
479 int maxwidth = av.alignment.getWidth() - 1;
\r
481 if (av.hasHiddenColumns)
\r
483 maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
\r
486 while ( (ypos <= canvasHeight) && (startRes < maxwidth))
\r
488 endx = startRes + cWidth - 1;
\r
490 if (endx > maxwidth)
\r
495 g.setFont(av.getFont());
\r
496 g.setColor(Color.black);
\r
498 if (av.scaleLeftWrapped)
\r
500 drawWestScale(g, startRes, endx, ypos);
\r
503 if (av.scaleRightWrapped)
\r
505 g.translate(canvasWidth - LABEL_EAST, 0);
\r
506 drawEastScale(g, startRes, endx, ypos);
\r
507 g.translate( - (canvasWidth - LABEL_EAST), 0);
\r
510 g.translate(LABEL_WEST, 0);
\r
512 if (av.scaleAboveWrapped)
\r
514 drawNorthScale(g, startRes, endx, ypos);
\r
517 if (av.hasHiddenColumns && av.showHiddenMarkers)
\r
519 g.setColor(Color.blue);
\r
521 for (int i = 0; i < av.getColumnSelection().getHiddenColumns().size();
\r
524 res = av.getColumnSelection().findHiddenRegionPosition(i) -
\r
527 if (res < 0 || res > endx - startRes)
\r
532 gg.fillPolygon(new int[]
\r
533 {res * av.charWidth - av.charHeight / 4,
\r
534 res * av.charWidth + av.charHeight / 4,
\r
535 res * av.charWidth},
\r
538 ypos - (av.charHeight / 2),
\r
539 ypos - (av.charHeight / 2),
\r
540 ypos - (av.charHeight / 2) + 8
\r
546 // When printing we have an extra clipped region,
\r
547 // the Printable page which we need to account for here
\r
548 Shape clip = g.getClip();
\r
552 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
556 g.setClip(0, (int) clip.getBounds().getY(),
\r
557 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
560 drawPanel(g, startRes, endx, 0, al.getHeight(), ypos);
\r
562 if (av.showAnnotation)
\r
564 g.translate(0, cHeight + ypos + 3);
\r
565 if (annotations == null)
\r
567 annotations = new AnnotationPanel(av);
\r
570 annotations.drawComponent( (Graphics2D) g, startRes, endx + 1);
\r
571 g.translate(0, -cHeight - ypos - 3);
\r
574 g.translate( -LABEL_WEST, 0);
\r
576 ypos += cHeight + getAnnotationHeight() + hgap;
\r
578 startRes += cWidth;
\r
582 AnnotationPanel annotations;
\r
583 int getAnnotationHeight()
\r
585 if (!av.showAnnotation)
\r
590 if (annotations == null)
\r
592 annotations = new AnnotationPanel(av);
\r
595 return annotations.adjustPanelHeight();
\r
601 * @param g1 DOCUMENT ME!
\r
602 * @param startRes DOCUMENT ME!
\r
603 * @param endRes DOCUMENT ME!
\r
604 * @param startSeq DOCUMENT ME!
\r
605 * @param endSeq DOCUMENT ME!
\r
606 * @param offset DOCUMENT ME!
\r
608 void drawPanel(Graphics g1, int startRes, int endRes,
\r
609 int startSeq, int endSeq, int offset)
\r
611 if (!av.hasHiddenColumns)
\r
613 draw(g1, startRes, endRes, startSeq, endSeq, offset);
\r
617 java.util.Vector regions = av.getColumnSelection().getHiddenColumns();
\r
620 int blockStart = startRes;
\r
621 int blockEnd = endRes;
\r
623 for (int i = 0; i < regions.size(); i++)
\r
625 int[] region = (int[]) regions.elementAt(i);
\r
626 int hideStart = region[0];
\r
627 int hideEnd = region[1];
\r
629 if (hideStart <= blockStart)
\r
631 blockStart += (hideEnd - hideStart) + 1;
\r
635 blockEnd = hideStart - 1;
\r
637 g1.translate(screenY * av.charWidth, 0);
\r
639 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
641 if (av.getShowHiddenMarkers())
\r
643 g1.setColor(Color.blue);
\r
645 g1.drawLine( (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
647 (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
648 (endSeq - startSeq) * av.charHeight + offset);
\r
651 g1.translate( -screenY * av.charWidth, 0);
\r
652 screenY += blockEnd - blockStart + 1;
\r
653 blockStart = hideEnd + 1;
\r
656 if (screenY <= (endRes - startRes))
\r
658 blockEnd = blockStart + (endRes - startRes) - screenY;
\r
659 g1.translate(screenY * av.charWidth, 0);
\r
660 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
662 g1.translate( -screenY * av.charWidth, 0);
\r
668 //int startRes, int endRes, int startSeq, int endSeq, int x, int y,
\r
669 // int x1, int x2, int y1, int y2, int startx, int starty,
\r
670 void draw(Graphics g,
\r
671 int startRes, int endRes,
\r
672 int startSeq, int endSeq,
\r
675 g.setFont(av.getFont());
\r
676 sr.prepare(g, av.renderGaps);
\r
680 /// First draw the sequences
\r
681 /////////////////////////////
\r
682 for (int i = startSeq; i < endSeq; i++)
\r
684 nextSeq = av.alignment.getSequenceAt(i);
\r
686 sr.drawSequence(nextSeq, av.alignment.findAllGroups(nextSeq),
\r
688 offset + ( (i - startSeq) * av.charHeight));
\r
690 if (av.showSequenceFeatures)
\r
692 fr.drawSequence(g, nextSeq, startRes, endRes,
\r
693 offset + ( (i - startSeq) * av.charHeight));
\r
696 /// Highlight search Results once all sequences have been drawn
\r
697 //////////////////////////////////////////////////////////
\r
698 if (searchResults != null)
\r
700 int[] visibleResults = searchResults.getResults(nextSeq, startRes,
\r
702 if (visibleResults != null)
\r
704 for (int r = 0; r < visibleResults.length; r += 2)
\r
706 sr.drawHighlightedText(nextSeq, visibleResults[r],
\r
707 visibleResults[r + 1],
\r
708 (visibleResults[r] - startRes) *
\r
710 offset + ( (i - startSeq) * av.charHeight));
\r
715 if (av.cursorMode && cursorY == i
\r
716 && cursorX >= startRes && cursorX <= endRes)
\r
718 sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * av.charWidth,
\r
719 offset + ( (i - startSeq) * av.charHeight));
\r
723 if (av.getSelectionGroup() != null || av.alignment.getGroups().size() > 0)
\r
725 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
\r
730 void drawGroupsBoundaries(Graphics g1,
\r
731 int startRes, int endRes,
\r
732 int startSeq, int endSeq,
\r
735 Graphics2D g = (Graphics2D) g1;
\r
737 /////////////////////////////////////
\r
738 // Now outline any areas if necessary
\r
739 /////////////////////////////////////
\r
740 SequenceGroup group = av.getSelectionGroup();
\r
745 int groupIndex = -1;
\r
746 int visWidth = (endRes - startRes + 1) * av.charWidth;
\r
748 if ( (group == null) && (av.alignment.getGroups().size() > 0))
\r
750 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
760 boolean inGroup = false;
\r
764 for (i = startSeq; i < endSeq; i++)
\r
766 sx = (group.getStartRes() - startRes) * av.charWidth;
\r
767 sy = offset + ( (i - startSeq) * av.charHeight);
\r
768 ex = ( ( (group.getEndRes() + 1) - group.getStartRes()) *
\r
772 if (sx + ex < 0 || sx > visWidth)
\r
777 if ( (sx <= (endRes - startRes) * av.charWidth) &&
\r
778 group.getSequences(null).
\r
779 contains(av.alignment.getSequenceAt(i)))
\r
781 if ( (bottom == -1) &&
\r
782 !group.getSequences(null).contains(
\r
783 av.alignment.getSequenceAt(i + 1)))
\r
785 bottom = sy + av.charHeight;
\r
790 if ( ( (top == -1) && (i == 0)) ||
\r
791 !group.getSequences(null).contains(
\r
792 av.alignment.getSequenceAt(i - 1)))
\r
800 if (group == av.getSelectionGroup())
\r
802 g.setStroke(new BasicStroke(1,
\r
803 BasicStroke.CAP_BUTT,
\r
804 BasicStroke.JOIN_ROUND, 3f,
\r
807 g.setColor(Color.RED);
\r
811 g.setStroke(new BasicStroke());
\r
812 g.setColor(group.getOutlineColour());
\r
820 if (sx >= 0 && sx < visWidth)
\r
822 g.drawLine(sx, oldY, sx, sy);
\r
825 if (sx + ex < visWidth)
\r
827 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
836 if (sx + ex > visWidth)
\r
841 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
843 ex = (endRes - startRes + 1) * av.charWidth;
\r
848 g.drawLine(sx, top, sx + ex, top);
\r
854 g.drawLine(sx, bottom, sx + ex, bottom);
\r
865 sy = offset + ( (i - startSeq) * av.charHeight);
\r
866 if (sx >= 0 && sx < visWidth)
\r
868 g.drawLine(sx, oldY, sx, sy);
\r
871 if (sx + ex < visWidth)
\r
873 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
882 if (sx + ex > visWidth)
\r
886 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
888 ex = (endRes - startRes + 1) * av.charWidth;
\r
893 g.drawLine(sx, top, sx + ex, top);
\r
899 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
908 g.setStroke(new BasicStroke());
\r
910 if (groupIndex >= av.alignment.getGroups().size())
\r
915 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
918 while (groupIndex < av.alignment.getGroups().size());
\r
927 * @param results DOCUMENT ME!
\r
929 public void highlightSearchResults(SearchResults results)
\r
933 searchResults = results;
\r