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(AlignmentPanel ap)
\r
59 fr = new FeatureRenderer(ap);
\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
80 * @param g DOCUMENT ME!
\r
81 * @param startx DOCUMENT ME!
\r
82 * @param endx DOCUMENT ME!
\r
83 * @param ypos DOCUMENT ME!
\r
85 void drawNorthScale(Graphics g, int startx, int endx, int ypos)
\r
87 int scalestartx = startx - (startx % 10) + 10;
\r
89 g.setColor(Color.black);
\r
92 for (int i = scalestartx; i < endx; i += 10)
\r
95 if (av.hasHiddenColumns)
\r
97 value = av.getColumnSelection().adjustForHiddenColumns(value);
\r
100 g.drawString(String.valueOf(value), (i - startx - 1) * av.charWidth,
\r
101 ypos - (av.charHeight / 2));
\r
103 g.drawLine( ( (i - startx - 1) * av.charWidth) + (av.charWidth / 2),
\r
104 (ypos + 2) - (av.charHeight / 2),
\r
105 ( (i - startx - 1) * av.charWidth) + (av.charWidth / 2), ypos -
\r
113 * @param g DOCUMENT ME!
\r
114 * @param startx DOCUMENT ME!
\r
115 * @param endx DOCUMENT ME!
\r
116 * @param ypos DOCUMENT ME!
\r
118 void drawWestScale(Graphics g, int startx, int endx, int ypos)
\r
120 FontMetrics fm = getFontMetrics(av.getFont());
\r
121 ypos += av.charHeight;
\r
123 if (av.hasHiddenColumns)
\r
125 startx = av.getColumnSelection().adjustForHiddenColumns(startx);
\r
126 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
\r
129 int maxwidth = av.alignment.getWidth();
\r
130 if (av.hasHiddenColumns)
\r
132 maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
\r
136 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
138 SequenceI seq = av.alignment.getSequenceAt(i);
\r
139 int index = startx;
\r
142 while (index < endx)
\r
144 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
151 value = av.alignment.getSequenceAt(i).findPosition(index);
\r
158 int x = LABEL_WEST - fm.stringWidth(String.valueOf(value)) -
\r
160 g.drawString(value + "", x,
\r
161 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
169 * @param g DOCUMENT ME!
\r
170 * @param startx DOCUMENT ME!
\r
171 * @param endx DOCUMENT ME!
\r
172 * @param ypos DOCUMENT ME!
\r
174 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
176 ypos += av.charHeight;
\r
178 if (av.hasHiddenColumns)
\r
180 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
228 gg.copyArea(horizontal * av.charWidth,
\r
229 vertical * av.charHeight,
\r
232 -horizontal * av.charWidth,
\r
233 -vertical * av.charHeight);
\r
235 int sr = av.startRes;
\r
236 int er = av.endRes;
\r
237 int ss = av.startSeq;
\r
238 int es = av.endSeq;
\r
242 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
245 transX = (er - sr - horizontal) * av.charWidth;
\r
246 sr = er - horizontal;
\r
248 else if (horizontal < 0)
\r
250 er = sr - horizontal - 1;
\r
252 else if (vertical > 0) // scroll down
\r
254 ss = es - vertical;
\r
256 if (ss < av.startSeq)
\r
257 { // ie scrolling too fast, more than a page at a time
\r
262 transY = imgHeight - (vertical * av.charHeight);
\r
265 else if (vertical < 0)
\r
267 es = ss - vertical;
\r
269 if (es > av.endSeq)
\r
275 gg.translate(transX, transY);
\r
276 drawPanel(gg, sr, er, ss, es, 0);
\r
277 gg.translate( -transX, -transY);
\r
283 * Definitions of startx and endx (hopefully):
\r
284 * SMJS This is what I'm working towards!
\r
285 * startx is the first residue (starting at 0) to display.
\r
286 * endx is the last residue to display (starting at 0).
\r
287 * starty is the first sequence to display (starting at 0).
\r
288 * endy is the last sequence to display (starting at 0).
\r
289 * NOTE 1: The av limits are set in setFont in this class and
\r
290 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
293 // Set this to false to force a full panel paint
\r
294 public void paintComponent(Graphics g)
\r
296 super.paintComponent(g);
\r
298 if (img != null && (fastPaint
\r
299 || (getVisibleRect().width != g.getClipBounds().width)
\r
300 || (getVisibleRect().height != g.getClipBounds().height)))
\r
302 g.drawImage(img, 0, 0, this);
\r
307 // this draws the whole of the alignment
\r
308 imgWidth = getWidth();
\r
309 imgHeight = getHeight();
\r
311 imgWidth -= (imgWidth % av.charWidth);
\r
312 imgHeight -= (imgHeight % av.charHeight);
\r
314 if ( (imgWidth < 1) || (imgHeight < 1))
\r
319 if (img == null || imgWidth != img.getWidth() || imgHeight != img.getHeight())
\r
323 img = new BufferedImage(imgWidth, imgHeight,
\r
324 BufferedImage.TYPE_INT_RGB);
\r
325 gg = (Graphics2D) img.getGraphics();
\r
326 gg.setFont(av.getFont());
\r
328 catch (OutOfMemoryError er)
\r
331 System.out.println(er + " making image, SeqCanvas");
\r
332 javax.swing.SwingUtilities.invokeLater(new Runnable()
\r
336 javax.swing.JOptionPane.showInternalMessageDialog(Desktop.
\r
338 "Out of memory creating alignment image!!"
\r
340 "\nSee help files for increasing Java Virtual Machine memory."
\r
342 javax.swing.JOptionPane.WARNING_MESSAGE);
\r
352 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
353 RenderingHints.VALUE_ANTIALIAS_ON);
\r
356 gg.setColor(Color.white);
\r
357 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
359 if (av.getWrapAlignment())
\r
361 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
365 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, 0);
\r
368 g.drawImage(img, 0, 0, this);
\r
376 * @param cwidth DOCUMENT ME!
\r
378 * @return DOCUMENT ME!
\r
380 public int getWrappedCanvasWidth(int cwidth)
\r
382 FontMetrics fm = getFontMetrics(av.getFont());
\r
387 if (av.scaleRightWrapped)
\r
389 LABEL_EAST = fm.stringWidth(getMask());
\r
392 if (av.scaleLeftWrapped)
\r
394 LABEL_WEST = fm.stringWidth(getMask());
\r
397 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
401 * Generates a string of zeroes.
\r
406 String mask = "00";
\r
409 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
411 tmp = av.alignment.getSequenceAt(i).getEnd();
\r
412 if (tmp > maxWidth)
\r
418 for (int i = maxWidth; i > 0; i /= 10)
\r
428 * @param g DOCUMENT ME!
\r
429 * @param canvasWidth DOCUMENT ME!
\r
430 * @param canvasHeight DOCUMENT ME!
\r
431 * @param startRes DOCUMENT ME!
\r
433 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
436 AlignmentI al = av.getAlignment();
\r
438 FontMetrics fm = getFontMetrics(av.getFont());
\r
440 if (av.scaleRightWrapped)
\r
442 LABEL_EAST = fm.stringWidth(getMask());
\r
445 if (av.scaleLeftWrapped)
\r
447 LABEL_WEST = fm.stringWidth(getMask());
\r
450 int hgap = av.charHeight;
\r
451 if (av.scaleAboveWrapped)
\r
453 hgap += av.charHeight;
\r
456 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
457 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
459 av.setWrappedWidth(cWidth);
\r
461 av.endRes = av.startRes + cWidth;
\r
465 int maxwidth = av.alignment.getWidth() - 1;
\r
467 if (av.hasHiddenColumns)
\r
469 maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
\r
472 while ( (ypos <= canvasHeight) && (startRes < maxwidth))
\r
474 endx = startRes + cWidth - 1;
\r
476 if (endx > maxwidth)
\r
481 g.setFont(av.getFont());
\r
482 g.setColor(Color.black);
\r
484 if (av.scaleLeftWrapped)
\r
486 drawWestScale(g, startRes, endx, ypos);
\r
489 if (av.scaleRightWrapped)
\r
491 g.translate(canvasWidth - LABEL_EAST, 0);
\r
492 drawEastScale(g, startRes, endx, ypos);
\r
493 g.translate( - (canvasWidth - LABEL_EAST), 0);
\r
496 g.translate(LABEL_WEST, 0);
\r
498 if (av.scaleAboveWrapped)
\r
500 drawNorthScale(g, startRes, endx, ypos);
\r
503 if (av.hasHiddenColumns && av.showHiddenMarkers)
\r
505 g.setColor(Color.blue);
\r
507 for (int i = 0; i < av.getColumnSelection().getHiddenColumns().size();
\r
510 res = av.getColumnSelection().findHiddenRegionPosition(i) -
\r
513 if (res < 0 || res > endx - startRes)
\r
518 gg.fillPolygon(new int[]
\r
519 {res * av.charWidth - av.charHeight / 4,
\r
520 res * av.charWidth + av.charHeight / 4,
\r
521 res * av.charWidth},
\r
524 ypos - (av.charHeight / 2),
\r
525 ypos - (av.charHeight / 2),
\r
526 ypos - (av.charHeight / 2) + 8
\r
532 // When printing we have an extra clipped region,
\r
533 // the Printable page which we need to account for here
\r
534 Shape clip = g.getClip();
\r
538 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
542 g.setClip(0, (int) clip.getBounds().getY(),
\r
543 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
546 drawPanel(g, startRes, endx, 0, al.getHeight(), ypos);
\r
548 if (av.showAnnotation)
\r
550 g.translate(0, cHeight + ypos + 3);
\r
551 if (annotations == null)
\r
553 annotations = new AnnotationPanel(av);
\r
556 annotations.drawComponent( (Graphics2D) g, startRes, endx + 1);
\r
557 g.translate(0, -cHeight - ypos - 3);
\r
560 g.translate( -LABEL_WEST, 0);
\r
562 ypos += cHeight + getAnnotationHeight() + hgap;
\r
564 startRes += cWidth;
\r
568 AnnotationPanel annotations;
\r
569 int getAnnotationHeight()
\r
571 if (!av.showAnnotation)
\r
576 if (annotations == null)
\r
578 annotations = new AnnotationPanel(av);
\r
581 return annotations.adjustPanelHeight();
\r
587 * @param g1 DOCUMENT ME!
\r
588 * @param startRes DOCUMENT ME!
\r
589 * @param endRes DOCUMENT ME!
\r
590 * @param startSeq DOCUMENT ME!
\r
591 * @param endSeq DOCUMENT ME!
\r
592 * @param offset DOCUMENT ME!
\r
594 void drawPanel(Graphics g1, int startRes, int endRes,
\r
595 int startSeq, int endSeq, int offset)
\r
597 if (!av.hasHiddenColumns)
\r
599 draw(g1, startRes, endRes, startSeq, endSeq, offset);
\r
603 java.util.Vector regions = av.getColumnSelection().getHiddenColumns();
\r
606 int blockStart = startRes;
\r
607 int blockEnd = endRes;
\r
609 for (int i = 0; i < regions.size(); i++)
\r
611 int[] region = (int[]) regions.elementAt(i);
\r
612 int hideStart = region[0];
\r
613 int hideEnd = region[1];
\r
615 if (hideStart <= blockStart)
\r
617 blockStart += (hideEnd - hideStart) + 1;
\r
621 blockEnd = hideStart - 1;
\r
623 g1.translate(screenY * av.charWidth, 0);
\r
625 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
627 if (av.getShowHiddenMarkers())
\r
629 g1.setColor(Color.blue);
\r
631 g1.drawLine( (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
633 (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
634 (endSeq - startSeq) * av.charHeight + offset);
\r
637 g1.translate( -screenY * av.charWidth, 0);
\r
638 screenY += blockEnd - blockStart + 1;
\r
639 blockStart = hideEnd + 1;
\r
642 if (screenY <= (endRes - startRes))
\r
644 blockEnd = blockStart + (endRes - startRes) - screenY;
\r
645 g1.translate(screenY * av.charWidth, 0);
\r
646 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
648 g1.translate( -screenY * av.charWidth, 0);
\r
654 //int startRes, int endRes, int startSeq, int endSeq, int x, int y,
\r
655 // int x1, int x2, int y1, int y2, int startx, int starty,
\r
656 void draw(Graphics g,
\r
657 int startRes, int endRes,
\r
658 int startSeq, int endSeq,
\r
661 g.setFont(av.getFont());
\r
662 sr.prepare(g, av.renderGaps);
\r
666 /// First draw the sequences
\r
667 /////////////////////////////
\r
668 for (int i = startSeq; i < endSeq; i++)
\r
670 nextSeq = av.alignment.getSequenceAt(i);
\r
672 sr.drawSequence(nextSeq, av.alignment.findAllGroups(nextSeq),
\r
674 offset + ( (i - startSeq) * av.charHeight));
\r
676 if (av.showSequenceFeatures)
\r
678 fr.drawSequence(g, nextSeq, startRes, endRes,
\r
679 offset + ( (i - startSeq) * av.charHeight));
\r
682 /// Highlight search Results once all sequences have been drawn
\r
683 //////////////////////////////////////////////////////////
\r
684 if (searchResults != null)
\r
686 int[] visibleResults = searchResults.getResults(nextSeq, startRes,
\r
688 if (visibleResults != null)
\r
690 for (int r = 0; r < visibleResults.length; r += 2)
\r
692 sr.drawHighlightedText(nextSeq, visibleResults[r],
\r
693 visibleResults[r + 1],
\r
694 (visibleResults[r] - startRes) *
\r
696 offset + ( (i - startSeq) * av.charHeight));
\r
701 if (av.cursorMode && cursorY == i
\r
702 && cursorX >= startRes && cursorX <= endRes)
\r
704 sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * av.charWidth,
\r
705 offset + ( (i - startSeq) * av.charHeight));
\r
709 if (av.getSelectionGroup() != null || av.alignment.getGroups().size() > 0)
\r
711 drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
\r
716 void drawGroupsBoundaries(Graphics g1,
\r
717 int startRes, int endRes,
\r
718 int startSeq, int endSeq,
\r
721 Graphics2D g = (Graphics2D) g1;
\r
723 /////////////////////////////////////
\r
724 // Now outline any areas if necessary
\r
725 /////////////////////////////////////
\r
726 SequenceGroup group = av.getSelectionGroup();
\r
731 int groupIndex = -1;
\r
732 int visWidth = (endRes - startRes + 1) * av.charWidth;
\r
734 if ( (group == null) && (av.alignment.getGroups().size() > 0))
\r
736 group = (SequenceGroup) av.alignment.getGroups().elementAt(0);
\r
746 boolean inGroup = false;
\r
750 for (i = startSeq; i < endSeq; i++)
\r
752 sx = (group.getStartRes() - startRes) * av.charWidth;
\r
753 sy = offset + ( (i - startSeq) * av.charHeight);
\r
754 ex = ( ( (group.getEndRes() + 1) - group.getStartRes()) *
\r
758 if (sx + ex < 0 || sx > visWidth)
\r
763 if ( (sx <= (endRes - startRes) * av.charWidth) &&
\r
764 group.getSequences(null).
\r
765 contains(av.alignment.getSequenceAt(i)))
\r
767 if ( (bottom == -1) &&
\r
768 !group.getSequences(null).contains(
\r
769 av.alignment.getSequenceAt(i + 1)))
\r
771 bottom = sy + av.charHeight;
\r
776 if ( ( (top == -1) && (i == 0)) ||
\r
777 !group.getSequences(null).contains(
\r
778 av.alignment.getSequenceAt(i - 1)))
\r
786 if (group == av.getSelectionGroup())
\r
788 g.setStroke(new BasicStroke(1,
\r
789 BasicStroke.CAP_BUTT,
\r
790 BasicStroke.JOIN_ROUND, 3f,
\r
793 g.setColor(Color.RED);
\r
797 g.setStroke(new BasicStroke());
\r
798 g.setColor(group.getOutlineColour());
\r
806 if (sx >= 0 && sx < visWidth)
\r
808 g.drawLine(sx, oldY, sx, sy);
\r
811 if (sx + ex < visWidth)
\r
813 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
822 if (sx + ex > visWidth)
\r
827 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
829 ex = (endRes - startRes + 1) * av.charWidth;
\r
834 g.drawLine(sx, top, sx + ex, top);
\r
840 g.drawLine(sx, bottom, sx + ex, bottom);
\r
851 sy = offset + ( (i - startSeq) * av.charHeight);
\r
852 if (sx >= 0 && sx < visWidth)
\r
854 g.drawLine(sx, oldY, sx, sy);
\r
857 if (sx + ex < visWidth)
\r
859 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
868 if (sx + ex > visWidth)
\r
872 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
874 ex = (endRes - startRes + 1) * av.charWidth;
\r
879 g.drawLine(sx, top, sx + ex, top);
\r
885 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
894 g.setStroke(new BasicStroke());
\r
896 if (groupIndex >= av.alignment.getGroups().size())
\r
901 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
904 while (groupIndex < av.alignment.getGroups().size());
\r
913 * @param results DOCUMENT ME!
\r
915 public void highlightSearchResults(SearchResults results)
\r
919 searchResults = results;
\r