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 public SequenceRenderer getSequenceRenderer()
\r
71 public FeatureRenderer getFeatureRenderer()
\r
76 public AlignViewport getViewport()
\r
84 * @param g DOCUMENT ME!
\r
85 * @param startx DOCUMENT ME!
\r
86 * @param endx DOCUMENT ME!
\r
87 * @param ypos DOCUMENT ME!
\r
89 void drawNorthScale(Graphics g, int startx, int endx, int ypos)
\r
91 int scalestartx = startx - (startx % 10) + 10;
\r
93 g.setColor(Color.black);
\r
96 for (int i = scalestartx; i < endx; i += 10)
\r
99 if (av.hasHiddenColumns)
\r
101 value = av.getColumnSelection().adjustForHiddenColumns(value);
\r
104 g.drawString(String.valueOf(value), (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
127 if (av.hasHiddenColumns)
\r
129 startx = av.getColumnSelection().adjustForHiddenColumns(startx);
\r
130 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
\r
133 int maxwidth = av.alignment.getWidth();
\r
134 if (av.hasHiddenColumns)
\r
136 maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
\r
140 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
142 SequenceI seq = av.alignment.getSequenceAt(i);
\r
143 int index = startx;
\r
146 while (index < endx)
\r
148 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
155 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
162 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value)) -
\r
164 g.drawString(value + "", x,
\r
165 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
173 * @param g DOCUMENT ME!
\r
174 * @param startx DOCUMENT ME!
\r
175 * @param endx DOCUMENT ME!
\r
176 * @param ypos DOCUMENT ME!
\r
178 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
180 ypos += av.charHeight;
\r
182 if (av.hasHiddenColumns)
\r
184 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
\r
189 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
191 seq = av.alignment.getSequenceAt(i);
\r
195 while (index > startx)
\r
197 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
204 value = seq.findPosition(index);
\r
211 g.drawString(String.valueOf(value), 0,
\r
212 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
220 * @param horizontal DOCUMENT ME!
\r
221 * @param vertical DOCUMENT ME!
\r
223 public void fastPaint(int horizontal, int vertical)
\r
232 gg.copyArea(horizontal * av.charWidth,
\r
233 vertical * av.charHeight,
\r
236 -horizontal * av.charWidth,
\r
237 -vertical * av.charHeight);
\r
239 int sr = av.startRes;
\r
240 int er = av.endRes;
\r
241 int ss = av.startSeq;
\r
242 int es = av.endSeq;
\r
246 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
249 transX = (er - sr - horizontal) * av.charWidth;
\r
250 sr = er - horizontal;
\r
252 else if (horizontal < 0)
\r
254 er = sr - horizontal - 1;
\r
256 else if (vertical > 0) // scroll down
\r
258 ss = es - vertical;
\r
260 if (ss < av.startSeq)
\r
261 { // ie scrolling too fast, more than a page at a time
\r
266 transY = imgHeight - (vertical * av.charHeight);
\r
269 else if (vertical < 0)
\r
271 es = ss - vertical;
\r
273 if (es > av.endSeq)
\r
279 gg.translate(transX, transY);
\r
280 drawPanel(gg, sr, er, ss, es, 0);
\r
281 gg.translate( -transX, -transY);
\r
287 * Definitions of startx and endx (hopefully):
\r
288 * SMJS This is what I'm working towards!
\r
289 * startx is the first residue (starting at 0) to display.
\r
290 * endx is the last residue to display (starting at 0).
\r
291 * starty is the first sequence to display (starting at 0).
\r
292 * endy is the last sequence to display (starting at 0).
\r
293 * NOTE 1: The av limits are set in setFont in this class and
\r
294 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
297 // Set this to false to force a full panel paint
\r
298 public void paintComponent(Graphics g)
\r
300 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
311 // this draws the whole of the alignment
\r
312 imgWidth = getWidth();
\r
313 imgHeight = getHeight();
\r
315 imgWidth -= (imgWidth % av.charWidth);
\r
316 imgHeight -= (imgHeight % av.charHeight);
\r
318 if ( (imgWidth < 1) || (imgHeight < 1))
\r
323 if (img == null || imgWidth != img.getWidth() || imgHeight != img.getHeight())
\r
327 img = new BufferedImage(imgWidth, imgHeight,
\r
328 BufferedImage.TYPE_INT_RGB);
\r
329 gg = (Graphics2D) img.getGraphics();
\r
330 gg.setFont(av.getFont());
\r
332 catch (OutOfMemoryError er)
\r
335 System.out.println(er + " making image, SeqCanvas");
\r
336 javax.swing.SwingUtilities.invokeLater(new Runnable()
\r
340 javax.swing.JOptionPane.showInternalMessageDialog(Desktop.
\r
342 "Out of memory creating alignment image!!"
\r
344 "\nSee help files for increasing Java Virtual Machine memory."
\r
346 javax.swing.JOptionPane.WARNING_MESSAGE);
\r
356 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
357 RenderingHints.VALUE_ANTIALIAS_ON);
\r
360 gg.setColor(Color.white);
\r
361 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
363 if (av.getWrapAlignment())
\r
365 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
369 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, 0);
\r
372 g.drawImage(img, 0, 0, this);
\r
380 * @param cwidth DOCUMENT ME!
\r
382 * @return DOCUMENT ME!
\r
384 public int getWrappedCanvasWidth(int cwidth)
\r
386 FontMetrics fm = getFontMetrics(av.getFont());
\r
391 if (av.scaleRightWrapped)
\r
393 LABEL_EAST = fm.stringWidth(getMask());
\r
396 if (av.scaleLeftWrapped)
\r
398 LABEL_WEST = fm.stringWidth(getMask());
\r
401 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
405 * Generates a string of zeroes.
\r
410 String mask = "00";
\r
413 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
415 tmp = av.alignment.getSequenceAt(i).getEnd();
\r
416 if (tmp > maxWidth)
\r
422 for (int i = maxWidth; i > 0; i /= 10)
\r
432 * @param g DOCUMENT ME!
\r
433 * @param canvasWidth DOCUMENT ME!
\r
434 * @param canvasHeight DOCUMENT ME!
\r
435 * @param startRes DOCUMENT ME!
\r
437 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
440 AlignmentI al = av.getAlignment();
\r
442 FontMetrics fm = getFontMetrics(av.getFont());
\r
444 if (av.scaleRightWrapped)
\r
446 LABEL_EAST = fm.stringWidth(getMask());
\r
449 if (av.scaleLeftWrapped)
\r
451 LABEL_WEST = fm.stringWidth(getMask());
\r
454 int hgap = av.charHeight;
\r
455 if (av.scaleAboveWrapped)
\r
457 hgap += av.charHeight;
\r
460 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
461 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
463 av.setWrappedWidth(cWidth);
\r
465 av.endRes = av.startRes + cWidth;
\r
469 int maxwidth = av.alignment.getWidth() - 1;
\r
471 if (av.hasHiddenColumns)
\r
473 maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
\r
476 while ( (ypos <= canvasHeight) && (startRes < maxwidth))
\r
478 endx = startRes + cWidth - 1;
\r
480 if (endx > maxwidth)
\r
485 g.setFont(av.getFont());
\r
486 g.setColor(Color.black);
\r
488 if (av.scaleLeftWrapped)
\r
490 drawWestScale(g, startRes, endx, ypos);
\r
493 if (av.scaleRightWrapped)
\r
495 g.translate(canvasWidth - LABEL_EAST, 0);
\r
496 drawEastScale(g, startRes, endx, ypos);
\r
497 g.translate( - (canvasWidth - LABEL_EAST), 0);
\r
500 g.translate(LABEL_WEST, 0);
\r
502 if (av.scaleAboveWrapped)
\r
504 drawNorthScale(g, startRes, endx, ypos);
\r
507 if (av.hasHiddenColumns && av.showHiddenMarkers)
\r
509 g.setColor(Color.blue);
\r
511 for (int i = 0; i < av.getColumnSelection().getHiddenColumns().size();
\r
514 res = av.getColumnSelection().findHiddenRegionPosition(i) -
\r
517 if (res < 0 || res > endx - startRes)
\r
522 gg.fillPolygon(new int[]
\r
523 {res * av.charWidth - av.charHeight / 4,
\r
524 res * av.charWidth + av.charHeight / 4,
\r
525 res * av.charWidth},
\r
528 ypos - (av.charHeight / 2),
\r
529 ypos - (av.charHeight / 2),
\r
530 ypos - (av.charHeight / 2) + 8
\r
536 // When printing we have an extra clipped region,
\r
537 // the Printable page which we need to account for here
\r
538 Shape clip = g.getClip();
\r
542 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
546 g.setClip(0, (int) clip.getBounds().getY(),
\r
547 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
550 drawPanel(g, startRes, endx, 0, al.getHeight(), ypos);
\r
552 if (av.showAnnotation)
\r
554 g.translate(0, cHeight + ypos + 3);
\r
555 if (annotations == null)
\r
557 annotations = new AnnotationPanel(av);
\r
560 annotations.drawComponent( (Graphics2D) g, startRes, endx + 1);
\r
561 g.translate(0, -cHeight - ypos - 3);
\r
564 g.translate( -LABEL_WEST, 0);
\r
566 ypos += cHeight + getAnnotationHeight() + hgap;
\r
568 startRes += cWidth;
\r
572 AnnotationPanel annotations;
\r
573 int getAnnotationHeight()
\r
575 if (!av.showAnnotation)
\r
580 if (annotations == null)
\r
582 annotations = new AnnotationPanel(av);
\r
585 return annotations.adjustPanelHeight();
\r
591 * @param g1 DOCUMENT ME!
\r
592 * @param startRes DOCUMENT ME!
\r
593 * @param endRes DOCUMENT ME!
\r
594 * @param startSeq DOCUMENT ME!
\r
595 * @param endSeq DOCUMENT ME!
\r
596 * @param offset DOCUMENT ME!
\r
598 void drawPanel(Graphics g1, int startRes, int endRes,
\r
599 int startSeq, int endSeq, int offset)
\r
601 if (!av.hasHiddenColumns)
\r
603 draw(g1, startRes, endRes, startSeq, endSeq, offset);
\r
607 java.util.Vector regions = av.getColumnSelection().getHiddenColumns();
\r
610 int blockStart = startRes;
\r
611 int blockEnd = endRes;
\r
613 for (int i = 0; i < regions.size(); i++)
\r
615 int[] region = (int[]) regions.elementAt(i);
\r
616 int hideStart = region[0];
\r
617 int hideEnd = region[1];
\r
619 if (hideStart <= blockStart)
\r
621 blockStart += (hideEnd - hideStart) + 1;
\r
625 blockEnd = hideStart - 1;
\r
627 g1.translate(screenY * av.charWidth, 0);
\r
629 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
631 if (av.getShowHiddenMarkers())
\r
633 g1.setColor(Color.blue);
\r
635 g1.drawLine( (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
637 (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
638 (endSeq - startSeq) * av.charHeight + offset);
\r
641 g1.translate( -screenY * av.charWidth, 0);
\r
642 screenY += blockEnd - blockStart + 1;
\r
643 blockStart = hideEnd + 1;
\r
646 if (screenY <= (endRes - startRes))
\r
648 blockEnd = blockStart + (endRes - startRes) - screenY;
\r
649 g1.translate(screenY * av.charWidth, 0);
\r
650 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
652 g1.translate( -screenY * av.charWidth, 0);
\r
658 //int startRes, int endRes, int startSeq, int endSeq, int x, int y,
\r
659 // int x1, int x2, int y1, int y2, int startx, int starty,
\r
660 void draw(Graphics g,
\r
661 int startRes, int endRes,
\r
662 int startSeq, int endSeq,
\r
665 g.setFont(av.getFont());
\r
666 sr.prepare(g, av.renderGaps);
\r
670 /// First draw the sequences
\r
671 /////////////////////////////
\r
672 for (int i = startSeq; i < endSeq; i++)
\r
674 nextSeq = av.alignment.getSequenceAt(i);
\r
676 sr.drawSequence(nextSeq, av.alignment.findAllGroups(nextSeq),
\r
678 offset + ( (i - startSeq) * av.charHeight));
\r
680 if (av.showSequenceFeatures)
\r
682 fr.drawSequence(g, nextSeq, startRes, endRes,
\r
683 offset + ( (i - startSeq) * av.charHeight));
\r
686 /// Highlight search Results once all sequences have been drawn
\r
687 //////////////////////////////////////////////////////////
\r
688 if (searchResults != null)
\r
690 int[] visibleResults = searchResults.getResults(nextSeq, startRes,
\r
692 if (visibleResults != null)
\r
694 for (int r = 0; r < visibleResults.length; r += 2)
\r
696 sr.drawHighlightedText(nextSeq, visibleResults[r],
\r
697 visibleResults[r + 1],
\r
698 (visibleResults[r] - startRes) *
\r
700 offset + ( (i - startSeq) * av.charHeight));
\r
705 if (av.cursorMode && cursorY == i
\r
706 && cursorX >= startRes && cursorX <= endRes)
\r
708 sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * av.charWidth,
\r
709 offset + ( (i - startSeq) * av.charHeight));
\r
713 if (av.getSelectionGroup() != null || av.alignment.getGroups().size() > 0)
\r
715 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
\r
720 void drawGroupsBoundaries(Graphics g1,
\r
721 int startRes, int endRes,
\r
722 int startSeq, int endSeq,
\r
725 Graphics2D g = (Graphics2D) g1;
\r
727 /////////////////////////////////////
\r
728 // Now outline any areas if necessary
\r
729 /////////////////////////////////////
\r
730 SequenceGroup group = av.getSelectionGroup();
\r
735 int groupIndex = -1;
\r
736 int visWidth = (endRes - startRes + 1) * av.charWidth;
\r
738 if ( (group == null) && (av.alignment.getGroups().size() > 0))
\r
740 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
750 boolean inGroup = false;
\r
754 for (i = startSeq; i < endSeq; i++)
\r
756 sx = (group.getStartRes() - startRes) * av.charWidth;
\r
757 sy = offset + ( (i - startSeq) * av.charHeight);
\r
758 ex = ( ( (group.getEndRes() + 1) - group.getStartRes()) *
\r
762 if (sx + ex < 0 || sx > visWidth)
\r
767 if ( (sx <= (endRes - startRes) * av.charWidth) &&
\r
768 group.getSequences(null).
\r
769 contains(av.alignment.getSequenceAt(i)))
\r
771 if ( (bottom == -1) &&
\r
772 !group.getSequences(null).contains(
\r
773 av.alignment.getSequenceAt(i + 1)))
\r
775 bottom = sy + av.charHeight;
\r
780 if ( ( (top == -1) && (i == 0)) ||
\r
781 !group.getSequences(null).contains(
\r
782 av.alignment.getSequenceAt(i - 1)))
\r
790 if (group == av.getSelectionGroup())
\r
792 g.setStroke(new BasicStroke(1,
\r
793 BasicStroke.CAP_BUTT,
\r
794 BasicStroke.JOIN_ROUND, 3f,
\r
797 g.setColor(Color.RED);
\r
801 g.setStroke(new BasicStroke());
\r
802 g.setColor(group.getOutlineColour());
\r
810 if (sx >= 0 && sx < visWidth)
\r
812 g.drawLine(sx, oldY, sx, sy);
\r
815 if (sx + ex < visWidth)
\r
817 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
826 if (sx + ex > visWidth)
\r
831 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
833 ex = (endRes - startRes + 1) * av.charWidth;
\r
838 g.drawLine(sx, top, sx + ex, top);
\r
844 g.drawLine(sx, bottom, sx + ex, bottom);
\r
855 sy = offset + ( (i - startSeq) * av.charHeight);
\r
856 if (sx >= 0 && sx < visWidth)
\r
858 g.drawLine(sx, oldY, sx, sy);
\r
861 if (sx + ex < visWidth)
\r
863 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
872 if (sx + ex > visWidth)
\r
876 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
878 ex = (endRes - startRes + 1) * av.charWidth;
\r
883 g.drawLine(sx, top, sx + ex, top);
\r
889 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
898 g.setStroke(new BasicStroke());
\r
900 if (groupIndex >= av.alignment.getGroups().size())
\r
905 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
908 while (groupIndex < av.alignment.getGroups().size());
\r
917 * @param results DOCUMENT ME!
\r
919 public void highlightSearchResults(SearchResults results)
\r
923 searchResults = results;
\r