2 * Jalview - A Sequence Alignment Editor and Viewer
\r
3 * Copyright (C) 2006 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.getSequenceSetId());
\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
137 startx = av.getColumnSelection().adjustForHiddenColumns(startx);
\r
138 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
\r
141 int maxwidth = av.alignment.getWidth();
\r
142 if (av.hasHiddenColumns)
\r
143 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))-av.charWidth/2;
\r
169 g.drawString(value + "", x,
\r
170 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
178 * @param g DOCUMENT ME!
\r
179 * @param startx DOCUMENT ME!
\r
180 * @param endx DOCUMENT ME!
\r
181 * @param ypos DOCUMENT ME!
\r
183 void drawEastScale(Graphics g, int startx, int endx, int ypos)
\r
185 ypos += av.charHeight;
\r
187 if(av.hasHiddenColumns)
\r
188 endx = av.getColumnSelection().adjustForHiddenColumns(endx);
\r
192 for (int i = 0; i < av.alignment.getHeight(); i++)
\r
194 seq = av.alignment.getSequenceAt(i);
\r
198 while (index > startx)
\r
200 if (jalview.util.Comparison.isGap(seq.getCharAt(index)))
\r
207 value = seq.findPosition(index);
\r
214 g.drawString(String.valueOf(value), 0,
\r
215 (ypos + (i * av.charHeight)) - (av.charHeight / 5));
\r
223 * @param horizontal DOCUMENT ME!
\r
224 * @param vertical DOCUMENT ME!
\r
226 public void fastPaint(int horizontal, int vertical)
\r
236 gg.copyArea(horizontal * av.charWidth,
\r
237 vertical * av.charHeight,
\r
240 -horizontal * av.charWidth,
\r
241 -vertical * av.charHeight);
\r
243 int sr = av.startRes;
\r
244 int er = av.endRes;
\r
245 int ss = av.startSeq;
\r
246 int es = av.endSeq;
\r
251 if (horizontal > 0) // scrollbar pulled right, image to the left
\r
254 transX = (er - sr - horizontal) * av.charWidth;
\r
255 sr = er - horizontal;
\r
257 else if (horizontal < 0)
\r
259 er = sr - horizontal-1;
\r
261 else if (vertical > 0) // scroll down
\r
263 ss = es - vertical;
\r
265 if (ss < av.startSeq)
\r
266 { // ie scrolling too fast, more than a page at a time
\r
271 transY = imgHeight - (vertical * av.charHeight);
\r
274 else if (vertical < 0)
\r
276 es = ss - vertical;
\r
278 if (es > av.endSeq)
\r
284 gg.translate(transX, transY);
\r
285 drawPanel(gg, sr, er, ss, es, 0);
\r
286 gg.translate(-transX, -transY);
\r
292 * Definitions of startx and endx (hopefully):
\r
293 * SMJS This is what I'm working towards!
\r
294 * startx is the first residue (starting at 0) to display.
\r
295 * endx is the last residue to display (starting at 0).
\r
296 * starty is the first sequence to display (starting at 0).
\r
297 * endy is the last sequence to display (starting at 0).
\r
298 * NOTE 1: The av limits are set in setFont in this class and
\r
299 * in the adjustment listener in SeqPanel when the scrollbars move.
\r
302 // Set this to false to force a full panel paint
\r
303 public void paintComponent(Graphics g)
\r
305 super.paintComponent(g);
\r
309 if ( img != null && (fastPaint
\r
310 || (getVisibleRect().width != g.getClipBounds().width)
\r
311 || (getVisibleRect().height != g.getClipBounds().height)))
\r
313 g.drawImage(img, 0, 0, this);
\r
319 // this draws the whole of the alignment
\r
320 imgWidth = getWidth();
\r
321 imgHeight = getHeight();
\r
323 imgWidth -= (imgWidth % av.charWidth);
\r
324 imgHeight -= (imgHeight % av.charHeight);
\r
326 if ((imgWidth < 1) || (imgHeight < 1))
\r
331 if (img == null || imgWidth != img.getWidth() || imgHeight != img.getHeight())
\r
334 img = new BufferedImage(imgWidth, imgHeight,
\r
335 BufferedImage.TYPE_INT_RGB);
\r
336 gg = (Graphics2D) img.getGraphics();
\r
337 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
361 gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
\r
362 RenderingHints.VALUE_ANTIALIAS_ON);
\r
364 gg.setColor(Color.white);
\r
365 gg.fillRect(0, 0, imgWidth, imgHeight);
\r
368 if (av.getWrapAlignment())
\r
370 drawWrappedPanel(gg, getWidth(), getHeight(), av.startRes);
\r
374 drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, 0);
\r
377 g.drawImage(img, 0, 0, this);
\r
379 if (pdbCanvas != null)
\r
381 pdbCanvas.updateSeqColours();
\r
389 * @param cwidth DOCUMENT ME!
\r
391 * @return DOCUMENT ME!
\r
393 public int getWrappedCanvasWidth(int cwidth)
\r
395 FontMetrics fm = getFontMetrics(av.getFont());
\r
400 if (av.scaleRightWrapped)
\r
402 LABEL_EAST = fm.stringWidth(getMask());
\r
405 if (av.scaleLeftWrapped)
\r
407 LABEL_WEST = fm.stringWidth(getMask());
\r
410 return (cwidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
415 * Generates a string of zeroes.
\r
423 for(int i=0; i<av.alignment.getHeight(); i++)
\r
425 tmp = av.alignment.getSequenceAt(i).getEnd();
\r
430 for (int i = maxWidth; i > 0; i /= 10)
\r
440 * @param g DOCUMENT ME!
\r
441 * @param canvasWidth DOCUMENT ME!
\r
442 * @param canvasHeight DOCUMENT ME!
\r
443 * @param startRes DOCUMENT ME!
\r
445 public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight,
\r
448 AlignmentI al = av.getAlignment();
\r
450 FontMetrics fm = getFontMetrics(av.getFont());
\r
453 if (av.scaleRightWrapped)
\r
455 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
466 hgap += av.charHeight;
\r
468 int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / av.charWidth;
\r
469 int cHeight = av.getAlignment().getHeight() * av.charHeight;
\r
471 av.setWrappedWidth(cWidth);
\r
473 av.endRes = av.startRes + cWidth;
\r
478 int maxwidth = av.alignment.getWidth();
\r
480 if(av.hasHiddenColumns)
\r
481 maxwidth = av.getColumnSelection().findColumnPosition(maxwidth)-1;
\r
483 while ((ypos <= canvasHeight) && (startRes < maxwidth))
\r
485 endx = startRes + cWidth -1;
\r
487 if (endx > maxwidth)
\r
492 g.setFont(av.getFont());
\r
493 g.setColor(Color.black);
\r
495 if (av.scaleLeftWrapped)
\r
497 drawWestScale(g, startRes, endx, ypos);
\r
500 if (av.scaleRightWrapped)
\r
502 g.translate(canvasWidth - LABEL_EAST, 0);
\r
503 drawEastScale(g, startRes, endx, ypos);
\r
504 g.translate(-(canvasWidth - LABEL_EAST), 0);
\r
507 g.translate(LABEL_WEST, 0);
\r
509 if (av.scaleAboveWrapped)
\r
511 drawNorthScale(g, startRes, endx, ypos);
\r
514 if (av.hasHiddenColumns && av.showHiddenMarkers)
\r
516 g.setColor(Color.blue);
\r
518 for (int i = 0; i < av.getColumnSelection().getHiddenColumns().size();
\r
521 res = av.getColumnSelection().findHiddenRegionPosition(i) -
\r
524 if (res < 0 || res > endx - startRes)
\r
527 gg.fillPolygon(new int[]
\r
528 {res * av.charWidth - av.charHeight / 4,
\r
529 res * av.charWidth + av.charHeight / 4,
\r
530 res * av.charWidth},
\r
533 ypos - (av.charHeight / 2),
\r
534 ypos - (av.charHeight / 2),
\r
535 ypos - (av.charHeight / 2) + 8
\r
543 // When printing we have an extra clipped region,
\r
544 // the Printable page which we need to account for here
\r
545 Shape clip = g.getClip();
\r
549 g.setClip(0, 0, cWidth * av.charWidth, canvasHeight);
\r
553 g.setClip(0, (int) clip.getBounds().getY(),
\r
554 cWidth * av.charWidth, (int) clip.getBounds().getHeight());
\r
557 drawPanel(g, startRes, endx, 0, al.getHeight(), ypos);
\r
559 if(av.showAnnotation)
\r
561 g.translate(0, cHeight + ypos + 3);
\r
562 if(annotations==null)
\r
563 annotations = new AnnotationPanel(av);
\r
565 annotations.drawComponent( (Graphics2D) g, startRes, endx+1);
\r
566 g.translate(0, -cHeight - ypos -3);
\r
569 g.translate(-LABEL_WEST, 0);
\r
571 ypos += cHeight+getAnnotationHeight()+hgap;
\r
572 if(av.showAnnotation)
\r
575 startRes += cWidth;
\r
579 AnnotationPanel annotations;
\r
580 int getAnnotationHeight()
\r
582 if(!av.showAnnotation)
\r
585 if(annotations==null)
\r
586 annotations = new AnnotationPanel(av);
\r
588 return annotations.adjustPanelHeight();
\r
594 * @param g1 DOCUMENT ME!
\r
595 * @param startRes DOCUMENT ME!
\r
596 * @param endRes DOCUMENT ME!
\r
597 * @param startSeq DOCUMENT ME!
\r
598 * @param endSeq DOCUMENT ME!
\r
599 * @param offset DOCUMENT ME!
\r
601 void drawPanel(Graphics g1, int startRes, int endRes,
\r
602 int startSeq, int endSeq, int offset)
\r
604 if(!av.hasHiddenColumns)
\r
606 draw(g1, startRes, endRes, startSeq, endSeq, offset);
\r
610 java.util.Vector regions = av.getColumnSelection().getHiddenColumns();
\r
613 int blockStart = startRes;
\r
614 int blockEnd = endRes;
\r
616 for (int i = 0; i < regions.size(); i++)
\r
618 int[] region = (int[]) regions.elementAt(i);
\r
619 int hideStart = region[0];
\r
620 int hideEnd = region[1];
\r
622 if (hideStart <= blockStart)
\r
624 blockStart += (hideEnd - hideStart) + 1;
\r
628 blockEnd = hideStart - 1;
\r
630 g1.translate(screenY * av.charWidth, 0);
\r
632 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
634 if(av.getShowHiddenMarkers())
\r
636 g1.setColor(Color.blue);
\r
638 g1.drawLine( (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
640 (blockEnd - blockStart + 1) * av.charWidth - 1,
\r
641 (endSeq - startSeq) * av.charHeight + offset);
\r
644 g1.translate( -screenY * av.charWidth, 0);
\r
645 screenY += blockEnd - blockStart + 1;
\r
646 blockStart = hideEnd + 1;
\r
649 if (screenY <= (endRes - startRes))
\r
651 blockEnd = blockStart + (endRes - startRes) - screenY;
\r
652 g1.translate(screenY * av.charWidth, 0);
\r
653 draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
\r
655 g1.translate( -screenY * av.charWidth, 0);
\r
664 //int startRes, int endRes, int startSeq, int endSeq, int x, int y,
\r
665 // int x1, int x2, int y1, int y2, int startx, int starty,
\r
666 void draw(Graphics g,
\r
667 int startRes, int endRes,
\r
668 int startSeq, int endSeq,
\r
671 g.setFont(av.getFont());
\r
672 sr.prepare(g, av.renderGaps);
\r
676 /// First draw the sequences
\r
677 /////////////////////////////
\r
678 for (int i = startSeq; i < endSeq; i++)
\r
680 nextSeq = av.alignment.getSequenceAt(i);
\r
682 sr.drawSequence(nextSeq, av.alignment.findAllGroups(nextSeq),
\r
684 offset + ( (i - startSeq) * av.charHeight));
\r
686 if (av.showSequenceFeatures)
\r
688 fr.drawSequence(g, nextSeq, startRes, endRes,
\r
689 offset + ((i - startSeq) * av.charHeight));
\r
692 /// Highlight search Results once all sequences have been drawn
\r
693 //////////////////////////////////////////////////////////
\r
694 if (searchResults != null)
\r
696 int[] visibleResults = searchResults.getResults(nextSeq, startRes, endRes);
\r
697 if (visibleResults != null)
\r
698 for (int r = 0; r < visibleResults.length; r += 2)
\r
700 sr.drawHighlightedText(nextSeq, visibleResults[r],
\r
701 visibleResults[r + 1],
\r
702 (visibleResults[r] - startRes) * av.charWidth,
\r
703 offset + ( (i - startSeq) * av.charHeight));
\r
707 if(av.cursorMode && cursorY==i
\r
708 && cursorX>=startRes && cursorX<=endRes)
\r
710 sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * av.charWidth,
\r
711 offset + ( (i - startSeq) * av.charHeight));
\r
715 if(av.getSelectionGroup()!=null || av.alignment.getGroups().size()>0)
\r
716 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
751 boolean inGroup = false;
\r
755 for (i = startSeq; i < endSeq; i++)
\r
757 sx = (group.getStartRes() - startRes) * av.charWidth;
\r
758 sy = offset + ((i - startSeq) * av.charHeight);
\r
759 ex = (((group.getEndRes() + 1) - group.getStartRes()) * av.charWidth) -
\r
762 if(sx+ex<0 || sx>visWidth)
\r
767 if ( (sx <= (endRes-startRes)*av.charWidth) &&
\r
768 group.getSequences(false).
\r
769 contains(av.alignment.getSequenceAt(i)))
\r
771 if ((bottom == -1) &&
\r
772 !group.getSequences(false).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(false).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
795 new float[] { 5f, 3f }, 0f));
\r
796 g.setColor(Color.RED);
\r
800 g.setStroke(new BasicStroke());
\r
801 g.setColor(group.getOutlineColour());
\r
809 if (sx >= 0 && sx < visWidth)
\r
810 g.drawLine(sx, oldY, sx, sy);
\r
812 if (sx + ex < visWidth)
\r
813 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
821 if (sx + ex > visWidth)
\r
824 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
825 ex = (endRes - startRes + 1) * av.charWidth;
\r
829 g.drawLine(sx, top, sx + ex, top);
\r
835 g.drawLine(sx, bottom, sx + ex, bottom);
\r
846 sy = offset + ( (i - startSeq) * av.charHeight);
\r
847 if (sx >= 0 && sx < visWidth)
\r
848 g.drawLine(sx, oldY, sx, sy);
\r
850 if (sx + ex < visWidth)
\r
851 g.drawLine(sx + ex, oldY, sx + ex, sy);
\r
859 if (sx + ex > visWidth)
\r
861 else if (sx + ex >= (endRes - startRes + 1) * av.charWidth)
\r
862 ex = (endRes - startRes + 1) * av.charWidth;
\r
866 g.drawLine(sx, top, sx + ex, top);
\r
872 g.drawLine(sx, bottom - 1, sx + ex, bottom - 1);
\r
881 g.setStroke(new BasicStroke());
\r
883 if (groupIndex >= av.alignment.getGroups().size())
\r
888 group = (SequenceGroup) av.alignment.getGroups().elementAt(groupIndex);
\r
892 while (groupIndex < av.alignment.getGroups().size());
\r
901 * @param results DOCUMENT ME!
\r
903 public void highlightSearchResults(SearchResults results)
\r
907 searchResults = results;
\r