/* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.gui; import jalview.datamodel.SequenceI; import jalview.viewmodel.ViewportListenerI; import jalview.viewmodel.ViewportRanges; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.util.List; import javax.swing.JPanel; /** * DOCUMENT ME! * * @author $author$ * @version $Revision$ */ public class IdCanvas extends JPanel implements ViewportListenerI { protected AlignViewport av; protected boolean showScores = true; protected int maxIdLength = -1; protected String maxIdStr = null; BufferedImage image; Graphics2D gg; int imgHeight = 0; boolean fastPaint = false; List searchResults; FontMetrics fm; AnnotationLabels labels = null; AnnotationPanel ap; private Font idfont; /** * Creates a new IdCanvas object. * * @param av * DOCUMENT ME! */ public IdCanvas(AlignViewport av) { setLayout(new BorderLayout()); this.av = av; PaintRefresher.Register(this, av.getSequenceSetId()); av.getRanges().addPropertyChangeListener(this); } /** * DOCUMENT ME! * * @param gg * DOCUMENT ME! * @param hiddenRows * true - check and display hidden row marker if need be * @param s * DOCUMENT ME! * @param i * DOCUMENT ME! * @param starty * DOCUMENT ME! * @param ypos * DOCUMENT ME! */ public void drawIdString(Graphics2D gg, boolean hiddenRows, SequenceI s, int i, int starty, int ypos) { int xPos = 0; int panelWidth = getWidth(); int charHeight = av.getCharHeight(); if ((searchResults != null) && searchResults.contains(s)) { gg.setColor(Color.black); gg.fillRect(0, ((i - starty) * charHeight) + ypos, getWidth(), charHeight); gg.setColor(Color.white); } else if ((av.getSelectionGroup() != null) && av.getSelectionGroup().getSequences(null).contains(s)) { gg.setColor(Color.lightGray); gg.fillRect(0, ((i - starty) * charHeight) + ypos, getWidth(), charHeight); gg.setColor(Color.white); } else { gg.setColor(av.getSequenceColour(s)); gg.fillRect(0, ((i - starty) * charHeight) + ypos, getWidth(), charHeight); gg.setColor(Color.black); } if (av.isRightAlignIds()) { xPos = panelWidth - fm.stringWidth(s.getDisplayId(av.getShowJVSuffix())) - 4; } gg.drawString(s.getDisplayId(av.getShowJVSuffix()), xPos, (((i - starty + 1) * charHeight) + ypos) - (charHeight / 5)); if (hiddenRows) { drawMarker(i, starty, ypos); } } /** * DOCUMENT ME! * * @param vertical * DOCUMENT ME! */ public void fastPaint(int vertical) { /* * for now, not attempting fast paint of wrapped ids... */ if (gg == null || av.getWrapAlignment()) { repaint(); return; } ViewportRanges ranges = av.getRanges(); gg.copyArea(0, 0, getWidth(), imgHeight, 0, -vertical * av.getCharHeight()); int ss = ranges.getStartSeq(); int es = ranges.getEndSeq(); int transY = 0; if (vertical > 0) // scroll down { ss = es - vertical; if (ss < ranges.getStartSeq()) { // ie scrolling too fast, more than a page at a time ss = ranges.getStartSeq(); } else { transY = imgHeight - ((vertical + 1) * av.getCharHeight()); } } else if (vertical < 0) // scroll up { es = ss - vertical; if (es > ranges.getEndSeq()) { es = ranges.getEndSeq(); } } gg.translate(0, transY); drawIds(ss, es); gg.translate(0, -transY); fastPaint = true; repaint(); } /** * DOCUMENT ME! * * @param g * DOCUMENT ME! */ @Override public void paintComponent(Graphics g) { g.setColor(Color.white); g.fillRect(0, 0, getWidth(), getHeight()); if (fastPaint) { fastPaint = false; g.drawImage(image, 0, 0, this); return; } int oldHeight = imgHeight; imgHeight = getHeight(); imgHeight -= (imgHeight % av.getCharHeight()); if (imgHeight < 1) { return; } if (oldHeight != imgHeight || image.getWidth(this) != getWidth()) { image = new BufferedImage(getWidth(), imgHeight, BufferedImage.TYPE_INT_RGB); } gg = (Graphics2D) image.getGraphics(); // Fill in the background gg.setColor(Color.white); gg.fillRect(0, 0, getWidth(), imgHeight); drawIds(av.getRanges().getStartSeq(), av.getRanges().getEndSeq()); g.drawImage(image, 0, 0, this); } /** * DOCUMENT ME! * * @param starty * DOCUMENT ME! * @param endy * DOCUMENT ME! */ void drawIds(int starty, int endy) { if (av.isSeqNameItalics()) { setIdfont(new Font(av.getFont().getName(), Font.ITALIC, av.getFont().getSize())); } else { setIdfont(av.getFont()); } gg.setFont(getIdfont()); fm = gg.getFontMetrics(); if (av.antiAlias) { gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } Color currentColor = Color.white; Color currentTextColor = Color.black; boolean hasHiddenRows = av.hasHiddenRows(); if (av.getWrapAlignment()) { drawIdsWrapped(starty, hasHiddenRows); return; } // No need to hang on to labels if we're not wrapped labels = null; // Now draw the id strings int panelWidth = getWidth(); int xPos = 0; SequenceI sequence; // Now draw the id strings for (int i = starty; i <= endy; i++) { sequence = av.getAlignment().getSequenceAt(i); if (sequence == null) { continue; } if (hasHiddenRows || av.isDisplayReferenceSeq()) { setHiddenFont(sequence); } // Selected sequence colours if ((searchResults != null) && searchResults.contains(sequence)) { currentColor = Color.black; currentTextColor = Color.white; } else if ((av.getSelectionGroup() != null) && av.getSelectionGroup() .getSequences(null).contains(sequence)) { currentColor = Color.lightGray; currentTextColor = Color.black; } else { currentColor = av.getSequenceColour(sequence); currentTextColor = Color.black; } gg.setColor(currentColor); gg.fillRect(0, (i - starty) * av.getCharHeight(), getWidth(), av.getCharHeight()); gg.setColor(currentTextColor); String string = sequence.getDisplayId(av.getShowJVSuffix()); if (av.isRightAlignIds()) { xPos = panelWidth - fm.stringWidth(string) - 4; } gg.drawString(string, xPos, (((i - starty) * av.getCharHeight()) + av.getCharHeight()) - (av.getCharHeight() / 5)); if (hasHiddenRows) { drawMarker(i, starty, 0); } } } /** * Draws sequence ids in wrapped mode * * @param starty * @param hasHiddenRows */ protected void drawIdsWrapped(int starty, boolean hasHiddenRows) { int maxwidth = av.getAlignment().getWidth(); int alheight = av.getAlignment().getHeight(); if (av.hasHiddenColumns()) { maxwidth = av.getAlignment().getHiddenColumns() .findColumnPosition(maxwidth) - 1; } int annotationHeight = 0; if (av.isShowAnnotation()) { if (ap == null) { ap = new AnnotationPanel(av); } annotationHeight = ap.adjustPanelHeight(); if (labels == null) { labels = new AnnotationLabels(av); } } int hgap = av.getCharHeight(); if (av.getScaleAboveWrapped()) { hgap += av.getCharHeight(); } int cHeight = alheight * av.getCharHeight() + hgap + annotationHeight; ViewportRanges ranges = av.getRanges(); int rowSize = ranges.getViewportWidth(); /* * draw repeating sequence ids until out of sequence data or * out of visible space, whichever comes first */ int ypos = hgap; int row = ranges.getStartRes(); while ((ypos <= getHeight()) && (row < maxwidth)) { for (int i = starty; i < alheight; i++) { SequenceI s = av.getAlignment().getSequenceAt(i); if (hasHiddenRows || av.isDisplayReferenceSeq()) { setHiddenFont(s); } else { gg.setFont(getIdfont()); } drawIdString(gg, hasHiddenRows, s, i, 0, ypos); } if (labels != null && av.isShowAnnotation()) { gg.translate(0, ypos + (alheight * av.getCharHeight())); labels.drawComponent(gg, getWidth()); gg.translate(0, -ypos - (alheight * av.getCharHeight())); } ypos += cHeight; row += rowSize; } } void drawMarker(int i, int starty, int yoffset) { SequenceI[] hseqs = av.getAlignment() .getHiddenSequences().hiddenSequences; // Use this method here instead of calling hiddenSeq adjust // 3 times. int hSize = hseqs.length; int hiddenIndex = i; int lastIndex = i - 1; int nextIndex = i + 1; for (int j = 0; j < hSize; j++) { if (hseqs[j] != null) { if (j - 1 < hiddenIndex) { hiddenIndex++; } if (j - 1 < lastIndex) { lastIndex++; } if (j - 1 < nextIndex) { nextIndex++; } } } boolean below = (hiddenIndex > lastIndex + 1); boolean above = (nextIndex > hiddenIndex + 1); gg.setColor(Color.blue); if (below) { gg.fillPolygon( new int[] { getWidth() - av.getCharHeight(), getWidth() - av.getCharHeight(), getWidth() }, new int[] { (i - starty) * av.getCharHeight() + yoffset, (i - starty) * av.getCharHeight() + yoffset + av.getCharHeight() / 4, (i - starty) * av.getCharHeight() + yoffset }, 3); } if (above) { gg.fillPolygon( new int[] { getWidth() - av.getCharHeight(), getWidth() - av.getCharHeight(), getWidth() }, new int[] { (i - starty + 1) * av.getCharHeight() + yoffset, (i - starty + 1) * av.getCharHeight() + yoffset - av.getCharHeight() / 4, (i - starty + 1) * av.getCharHeight() + yoffset }, 3); } } void setHiddenFont(SequenceI seq) { Font bold = new Font(av.getFont().getName(), Font.BOLD, av.getFont().getSize()); if (av.isReferenceSeq(seq) || av.isHiddenRepSequence(seq)) { gg.setFont(bold); } else { gg.setFont(getIdfont()); } } /** * DOCUMENT ME! * * @param list * DOCUMENT ME! */ public void setHighlighted(List list) { searchResults = list; repaint(); } public Font getIdfont() { return idfont; } public void setIdfont(Font idfont) { this.idfont = idfont; } /** * Respond to viewport range changes (e.g. alignment panel was scrolled). Both * scrolling and resizing change viewport ranges. Scrolling changes both start * and end points, but resize only changes end values. Here we only want to * fastpaint on a scroll, with resize using a normal paint, so scroll events * are identified as changes to the horizontal or vertical start value. *

* In unwrapped mode, only responds to a vertical scroll, as horizontal scroll * leaves sequence ids unchanged. In wrapped mode, only vertical scroll is * provided, but it generates a change of "startres" which does require an * update here. */ @Override public void propertyChange(PropertyChangeEvent evt) { String propertyName = evt.getPropertyName(); if (propertyName.equals(ViewportRanges.STARTSEQ) || (av.getWrapAlignment() && propertyName.equals(ViewportRanges.STARTRES))) { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } else if (propertyName.equals(ViewportRanges.STARTRESANDSEQ)) { fastPaint(((int[]) evt.getNewValue())[1] - ((int[]) evt.getOldValue())[1]); } else if (propertyName.equals(ViewportRanges.MOVE_VIEWPORT)) { repaint(); } } }