package jalview.gui;
import jalview.datamodel.AlignmentI;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.SearchResults;
import jalview.datamodel.SearchResultsI;
import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
import jalview.renderer.ScaleRenderer;
import jalview.renderer.ScaleRenderer.ScaleMark;
+import jalview.viewmodel.ViewportListenerI;
+import jalview.viewmodel.ViewportRanges;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.image.BufferedImage;
+import java.beans.PropertyChangeEvent;
import java.util.List;
import javax.swing.JComponent;
* @author $author$
* @version $Revision$
*/
-public class SeqCanvas extends JComponent
+public class SeqCanvas extends JComponent implements ViewportListenerI
{
final FeatureRenderer fr;
setLayout(new BorderLayout());
PaintRefresher.Register(this, av.getSequenceSetId());
setBackground(Color.white);
+
+ av.getRanges().addPropertyChangeListener(this);
}
public SequenceRenderer getSequenceRenderer()
if (av.hasHiddenColumns())
{
- startx = av.getColumnSelection().adjustForHiddenColumns(startx);
- endx = av.getColumnSelection().adjustForHiddenColumns(endx);
+ startx = av.getAlignment().getHiddenColumns()
+ .adjustForHiddenColumns(startx);
+ endx = av.getAlignment().getHiddenColumns()
+ .adjustForHiddenColumns(endx);
}
int maxwidth = av.getAlignment().getWidth();
if (av.hasHiddenColumns())
{
- maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
+ maxwidth = av.getAlignment().getHiddenColumns()
+ .findColumnPosition(maxwidth) - 1;
}
// WEST SCALE
if (av.hasHiddenColumns())
{
- endx = av.getColumnSelection().adjustForHiddenColumns(endx);
+ endx = av.getAlignment().getHiddenColumns()
+ .adjustForHiddenColumns(endx);
}
SequenceI seq;
gg.copyArea(horizontal * charWidth, vertical * charHeight, imgWidth,
imgHeight, -horizontal * charWidth, -vertical * charHeight);
- int sr = av.getStartRes();
- int er = av.getEndRes();
- int ss = av.getStartSeq();
- int es = av.getEndSeq();
+ ViewportRanges ranges = av.getRanges();
+ int startRes = ranges.getStartRes();
+ int endRes = ranges.getEndRes();
+ int startSeq = ranges.getStartSeq();
+ int endSeq = ranges.getEndSeq();
int transX = 0;
int transY = 0;
if (horizontal > 0) // scrollbar pulled right, image to the left
{
- er++;
- transX = (er - sr - horizontal) * charWidth;
- sr = er - horizontal;
+ transX = (endRes - startRes - horizontal) * charWidth;
+ startRes = endRes - horizontal;
}
else if (horizontal < 0)
{
- er = sr - horizontal - 1;
+ endRes = startRes - horizontal;
}
else if (vertical > 0) // scroll down
{
- ss = es - vertical;
+ startSeq = endSeq - vertical;
- if (ss < av.getStartSeq())
+ if (startSeq < ranges.getStartSeq())
{ // ie scrolling too fast, more than a page at a time
- ss = av.getStartSeq();
+ startSeq = ranges.getStartSeq();
}
else
{
- transY = imgHeight - (vertical * charHeight);
+ transY = imgHeight - ((vertical + 1) * charHeight);
}
}
else if (vertical < 0)
{
- es = ss - vertical;
+ endSeq = startSeq - vertical;
- if (es > av.getEndSeq())
+ if (endSeq > ranges.getEndSeq())
{
- es = av.getEndSeq();
+ endSeq = ranges.getEndSeq();
}
}
gg.translate(transX, transY);
- drawPanel(gg, sr, er, ss, es, 0);
+ drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
gg.translate(-transX, -transY);
repaint();
fastpainting = false;
}
- /**
- * Definitions of startx and endx (hopefully): SMJS This is what I'm working
- * towards! startx is the first residue (starting at 0) to display. endx is
- * the last residue to display (starting at 0). starty is the first sequence
- * to display (starting at 0). endy is the last sequence to display (starting
- * at 0). NOTE 1: The av limits are set in setFont in this class and in the
- * adjustment listener in SeqPanel when the scrollbars move.
- */
-
- // Set this to false to force a full panel paint
@Override
public void paintComponent(Graphics g)
{
gg.setColor(Color.white);
gg.fillRect(0, 0, imgWidth, imgHeight);
+ ViewportRanges ranges = av.getRanges();
if (av.getWrapAlignment())
{
- drawWrappedPanel(gg, getWidth(), getHeight(), av.getStartRes());
+ drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
}
else
{
- drawPanel(gg, av.getStartRes(), av.getEndRes(), av.getStartSeq(),
- av.getEndSeq(), 0);
+ drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(),
+ ranges.getStartSeq(), ranges.getEndSeq(), 0);
}
g.drawImage(lcimg, 0, 0, this);
av.setWrappedWidth(cWidth);
- av.setEndRes(av.getStartRes() + cWidth);
+ av.getRanges().setEndRes(av.getRanges().getStartRes() + cWidth - 1);
int endx;
int ypos = hgap;
if (av.hasHiddenColumns())
{
- maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
+ maxwidth = av.getAlignment().getHiddenColumns()
+ .findColumnPosition(maxwidth) - 1;
}
while ((ypos <= canvasHeight) && (startRes < maxwidth))
{
g.setColor(Color.blue);
int res;
- for (int i = 0; i < av.getColumnSelection().getHiddenColumns()
- .size(); i++)
+ HiddenColumns hidden = av.getAlignment().getHiddenColumns();
+ for (int i = 0; i < hidden.getHiddenRegions().size(); i++)
{
- res = av.getColumnSelection().findHiddenRegionPosition(i)
- - startRes;
+ res = hidden.findHiddenRegionPosition(i) - startRes;
if (res < 0 || res > endx - startRes)
{
(int) clip.getBounds().getHeight());
}
- drawPanel(g, startRes, endx, 0, al.getHeight(), ypos);
+ drawPanel(g, startRes, endx, 0, al.getHeight() - 1, ypos);
if (av.isShowAnnotation())
{
}
/**
- * DOCUMENT ME!
+ * Draws the visible region of the alignment on the graphics context. If there
+ * are hidden column markers in the visible region, then each sub-region
+ * between the markers is drawn separately, followed by the hidden column
+ * marker.
*
* @param g1
- * DOCUMENT ME!
* @param startRes
- * DOCUMENT ME!
+ * offset of the first column in the visible region (0..)
* @param endRes
- * DOCUMENT ME!
+ * offset of the last column in the visible region (0..)
* @param startSeq
- * DOCUMENT ME!
+ * offset of the first sequence in the visible region (0..)
* @param endSeq
- * DOCUMENT ME!
- * @param offset
- * DOCUMENT ME!
+ * offset of the last sequence in the visible region (0..)
+ * @param yOffset
+ * vertical offset at which to draw (for wrapped alignments)
*/
- public void drawPanel(Graphics g1, int startRes, int endRes,
- int startSeq, int endSeq, int offset)
+ public void drawPanel(Graphics g1, final int startRes, final int endRes,
+ final int startSeq, final int endSeq, final int yOffset)
{
updateViewport();
if (!av.hasHiddenColumns())
{
- draw(g1, startRes, endRes, startSeq, endSeq, offset);
+ draw(g1, startRes, endRes, startSeq, endSeq, yOffset);
}
else
{
- List<int[]> regions = av.getColumnSelection().getHiddenColumns();
+ List<int[]> regions = av.getAlignment().getHiddenColumns()
+ .getHiddenRegions();
int screenY = 0;
+ final int screenYMax = endRes - startRes;
int blockStart = startRes;
int blockEnd = endRes;
continue;
}
- blockEnd = hideStart - 1;
+ /*
+ * draw up to just before the next hidden region, or the end of
+ * the visible region, whichever comes first
+ */
+ blockEnd = Math.min(hideStart - 1, blockStart + screenYMax
+ - screenY);
g1.translate(screenY * charWidth, 0);
- draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
+ draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset);
- if (av.getShowHiddenMarkers())
+ /*
+ * draw the downline of the hidden column marker (ScalePanel draws the
+ * triangle on top) if we reached it
+ */
+ if (av.getShowHiddenMarkers() && blockEnd == hideStart - 1)
{
g1.setColor(Color.blue);
g1.drawLine((blockEnd - blockStart + 1) * charWidth - 1,
- 0 + offset, (blockEnd - blockStart + 1) * charWidth - 1,
- (endSeq - startSeq) * charHeight + offset);
+ 0 + yOffset, (blockEnd - blockStart + 1) * charWidth - 1,
+ (endSeq - startSeq + 1) * charHeight + yOffset);
}
g1.translate(-screenY * charWidth, 0);
screenY += blockEnd - blockStart + 1;
blockStart = hideEnd + 1;
- if (screenY > (endRes - startRes))
+ if (screenY > screenYMax)
{
// already rendered last block
return;
}
}
- if (screenY <= (endRes - startRes))
+ if (screenY <= screenYMax)
{
// remaining visible region to render
- blockEnd = blockStart + (endRes - startRes) - screenY;
+ blockEnd = blockStart + screenYMax - screenY;
g1.translate(screenY * charWidth, 0);
- draw(g1, blockStart, blockEnd, startSeq, endSeq, offset);
+ draw(g1, blockStart, blockEnd, startSeq, endSeq, yOffset);
g1.translate(-screenY * charWidth, 0);
}
}
- // int startRes, int endRes, int startSeq, int endSeq, int x, int y,
- // int x1, int x2, int y1, int y2, int startx, int starty,
+ /**
+ * Draws a region of the visible alignment
+ *
+ * @param g1
+ * @param startRes
+ * offset of the first column in the visible region (0..)
+ * @param endRes
+ * offset of the last column in the visible region (0..)
+ * @param startSeq
+ * offset of the first sequence in the visible region (0..)
+ * @param endSeq
+ * offset of the last sequence in the visible region (0..)
+ * @param yOffset
+ * vertical offset at which to draw (for wrapped alignments)
+ */
private void draw(Graphics g, int startRes, int endRes, int startSeq,
int endSeq, int offset)
{
// / First draw the sequences
// ///////////////////////////
- for (int i = startSeq; i < endSeq; i++)
+ for (int i = startSeq; i <= endSeq; i++)
{
nextSeq = av.getAlignment().getSequenceAt(i);
if (nextSeq == null)
if (av.isShowSequenceFeatures())
{
fr.drawSequence(g, nextSeq, startRes, endRes, offset
- + ((i - startSeq) * charHeight));
+ + ((i - startSeq) * charHeight), false);
}
- // / Highlight search Results once all sequences have been drawn
- // ////////////////////////////////////////////////////////
+ /*
+ * highlight search Results once sequence has been drawn
+ */
if (av.hasSearchResults())
{
- int[] visibleResults = av.getSearchResults().getResults(nextSeq,
+ SearchResultsI searchResults = av.getSearchResults();
+ int[] visibleResults = searchResults.getResults(nextSeq,
startRes, endRes);
if (visibleResults != null)
{
int top = -1;
int bottom = -1;
- for (i = startSeq; i < endSeq; i++)
+ for (i = startSeq; i <= endSeq; i++)
{
sx = (group.getStartRes() - startRes) * charWidth;
sy = offset + ((i - startSeq) * charHeight);
}
/**
- * DOCUMENT ME!
+ * Highlights search results in the visible region by rendering as white text
+ * on a black background. Any previous highlighting is removed.
*
* @param results
- * DOCUMENT ME!
*/
public void highlightSearchResults(SearchResultsI results)
{
- img = null;
+ updateViewport();
+ fastpainting = true;
+ fastPaint = true;
+
+ /*
+ * to avoid redrawing the whole visible region, we instead
+ * calculate the minimal redraw to remove previous highlights
+ * and add new ones;
+ * first make a temporary SearchResults that includes
+ * both the new and any existing search results
+ */
+ SearchResultsI combined = results;
+ SearchResultsI previous = av.getSearchResults();
+ if (previous != null)
+ {
+ combined = new SearchResults();
+ combined.addSearchResults(results);
+ combined.addSearchResults(previous);
+ }
+
+ /*
+ * calculate the minimal rectangle to redraw that
+ * includes both new and existing search results
+ */
+ int firstSeq = Integer.MAX_VALUE;
+ int lastSeq = -1;
+ int firstCol = Integer.MAX_VALUE;
+ int lastCol = -1;
+ boolean matchFound = false;
+
+ ViewportRanges ranges = av.getRanges();
+ int firstVisibleColumn = ranges.getStartRes();
+ int lastVisibleColumn = ranges.getEndRes();
+ if (av.hasHiddenColumns())
+ {
+ firstVisibleColumn = av.getAlignment().getHiddenColumns()
+ .adjustForHiddenColumns(firstVisibleColumn);
+ lastVisibleColumn = av.getAlignment().getHiddenColumns()
+ .adjustForHiddenColumns(lastVisibleColumn);
+ }
+
+ for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
+ .getEndSeq(); seqNo++)
+ {
+ SequenceI seq = av.getAlignment().getSequenceAt(seqNo);
+
+ int[] visibleResults = combined.getResults(seq, firstVisibleColumn,
+ lastVisibleColumn);
+ if (visibleResults != null)
+ {
+ for (int i = 0; i < visibleResults.length - 1; i += 2)
+ {
+ int firstMatchedColumn = visibleResults[i];
+ int lastMatchedColumn = visibleResults[i + 1];
+ if (firstMatchedColumn <= lastVisibleColumn
+ && lastMatchedColumn >= firstVisibleColumn)
+ {
+ /*
+ * found a search results match in the visible region -
+ * remember the first and last sequence matched, and the first
+ * and last visible columns in the matched positions
+ */
+ matchFound = true;
+ firstSeq = Math.min(firstSeq, seqNo);
+ lastSeq = Math.max(lastSeq, seqNo);
+ firstMatchedColumn = Math.max(firstMatchedColumn,
+ firstVisibleColumn);
+ lastMatchedColumn = Math.min(lastMatchedColumn,
+ lastVisibleColumn);
+ firstCol = Math.min(firstCol, firstMatchedColumn);
+ lastCol = Math.max(lastCol, lastMatchedColumn);
+ }
+ }
+ }
+ }
av.setSearchResults(results);
- repaint();
+ if (matchFound)
+ {
+ if (av.hasHiddenColumns())
+ {
+ firstCol = av.getAlignment().getHiddenColumns()
+ .findColumnPosition(firstCol);
+ lastCol = av.getAlignment().getHiddenColumns()
+ .findColumnPosition(lastCol);
+ }
+ int transX = (firstCol - firstVisibleColumn) * av.getCharWidth();
+ int transY = (firstSeq - ranges.getStartSeq()) * av.getCharHeight();
+ gg.translate(transX, transY);
+ drawPanel(gg, firstCol, lastCol, firstSeq, lastSeq, 0);
+ gg.translate(-transX, -transY);
+
+ repaint();
+ }
+
+ fastpainting = false; // todo in finally block?
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ if (!av.getWrapAlignment())
+ {
+ if (evt.getPropertyName().equals("startres")
+ || evt.getPropertyName().equals("endres"))
+ {
+ // Make sure we're not trying to draw a panel
+ // larger than the visible window
+ ViewportRanges vpRanges = av.getRanges();
+ int scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
+ if (scrollX > vpRanges.getEndRes() - vpRanges.getStartRes())
+ {
+ scrollX = vpRanges.getEndRes() - vpRanges.getStartRes();
+ }
+ else if (scrollX < vpRanges.getStartRes() - vpRanges.getEndRes())
+ {
+ scrollX = vpRanges.getStartRes() - vpRanges.getEndRes();
+ }
+ fastPaint(scrollX, 0);
+ }
+ else if (evt.getPropertyName().equals("startseq")
+ || evt.getPropertyName().equals("endseq"))
+ {
+ fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+ }
+ }
}
}