*/
public class SeqCanvas extends JComponent implements ViewportListenerI
{
+ private static String ZEROS = "0000000000";
+
final FeatureRenderer fr;
final SequenceRenderer sr;
boolean fastPaint = false;
- int LABEL_WEST;
+ int labelWidthWest;
- int LABEL_EAST;
+ int labelWidthEast;
int cursorX = 0;
if (value != -1)
{
- int x = LABEL_WEST - fm.stringWidth(String.valueOf(value))
+ int x = labelWidthWest - fm.stringWidth(String.valueOf(value))
- charWidth / 2;
g.drawString(value + "", x, (ypos + (i * charHeight))
- (charHeight / 5));
{
FontMetrics fm = getFontMetrics(av.getFont());
- LABEL_EAST = 0;
- LABEL_WEST = 0;
+ labelWidthEast = 0;
+ labelWidthWest = 0;
if (av.getScaleRightWrapped())
{
- LABEL_EAST = fm.stringWidth(getMask());
+ labelWidthEast = getLabelWidth(fm);
}
if (av.getScaleLeftWrapped())
{
- LABEL_WEST = fm.stringWidth(getMask());
+ labelWidthWest = getLabelWidth(fm);
}
- return (cwidth - LABEL_EAST - LABEL_WEST) / charWidth;
+ return (cwidth - labelWidthEast - labelWidthWest) / charWidth;
}
/**
- * Generates a string of zeroes.
+ * Returns a pixel width suitable for showing the largest sequence coordinate
+ * (end position) in the alignment. Returns 2 plus the number of decimal
+ * digits to be shown (3 for 1-10, 4 for 11-99 etc).
*
- * @return String
+ * @param fm
+ * @return
*/
- String getMask()
+ protected int getLabelWidth(FontMetrics fm)
{
- String mask = "00";
int maxWidth = 0;
- int tmp;
for (int i = 0; i < av.getAlignment().getHeight(); i++)
{
- tmp = av.getAlignment().getSequenceAt(i).getEnd();
- if (tmp > maxWidth)
- {
- maxWidth = tmp;
- }
+ maxWidth = Math.max(maxWidth, av.getAlignment().getSequenceAt(i)
+ .getEnd());
}
+ int length = 2;
for (int i = maxWidth; i > 0; i /= 10)
{
- mask += "0";
+ length++;
}
- return mask;
+
+ return fm.stringWidth(ZEROS.substring(0, length));
}
/**
updateViewport();
AlignmentI al = av.getAlignment();
- FontMetrics fm = getFontMetrics(av.getFont());
-
- if (av.getScaleRightWrapped())
+ int labelWidth = 0;
+ if (av.getScaleRightWrapped() || av.getScaleLeftWrapped())
{
- LABEL_EAST = fm.stringWidth(getMask());
+ FontMetrics fm = getFontMetrics(av.getFont());
+ labelWidth = getLabelWidth(fm);
}
- if (av.getScaleLeftWrapped())
- {
- LABEL_WEST = fm.stringWidth(getMask());
- }
+ labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0;
+ labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0;
int hgap = charHeight;
if (av.getScaleAboveWrapped())
hgap += charHeight;
}
- int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth;
+ int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) / charWidth;
int cHeight = av.getAlignment().getHeight() * charHeight;
av.setWrappedWidth(cWidth);
.findColumnPosition(maxwidth) - 1;
}
+ int annotationHeight = getAnnotationHeight();
+
while ((ypos <= canvasHeight) && (startRes < maxwidth))
{
endx = startRes + cWidth - 1;
if (av.getScaleRightWrapped())
{
- g.translate(canvasWidth - LABEL_EAST, 0);
+ g.translate(canvasWidth - labelWidthEast, 0);
drawEastScale(g, startRes, endx, ypos);
- g.translate(-(canvasWidth - LABEL_EAST), 0);
+ g.translate(-(canvasWidth - labelWidthEast), 0);
}
- g.translate(LABEL_WEST, 0);
+ g.translate(labelWidthWest, 0);
if (av.getScaleAboveWrapped())
{
g.setColor(Color.blue);
int res;
HiddenColumns hidden = av.getAlignment().getHiddenColumns();
- for (int i = 0; i < hidden.getHiddenRegions().size(); i++)
+ List<Integer> positions = hidden.findHiddenRegionPositions();
+ for (int pos : positions)
{
- res = hidden.findHiddenRegionPosition(i) - startRes;
+ res = pos - startRes;
if (res < 0 || res > endx - startRes)
{
g.translate(0, -cHeight - ypos - 3);
}
g.setClip(clip);
- g.translate(-LABEL_WEST, 0);
+ g.translate(-labelWidthWest, 0);
- ypos += cHeight + getAnnotationHeight() + hgap;
+ ypos += cHeight + annotationHeight + hgap;
startRes += cWidth;
}
}
else
{
- List<int[]> regions = av.getAlignment().getHiddenColumns()
- .getHiddenRegions();
-
int screenY = 0;
final int screenYMax = endRes - startRes;
int blockStart = startRes;
int blockEnd = endRes;
- for (int[] region : regions)
+ for (int[] region : av.getAlignment().getHiddenColumns()
+ .getHiddenColumnsCopy())
{
int hideStart = region[0];
int hideEnd = region[1];
/**
* Highlights search results in the visible region by rendering as white text
- * on a black background. Any previous highlighting is removed.
+ * on a black background. Any previous highlighting is removed. Answers true
+ * if any highlight was left on the visible alignment (so status bar should be
+ * set to match), else false.
*
* @param results
+ * @return
*/
- public void highlightSearchResults(SearchResultsI results)
+ public boolean highlightSearchResults(SearchResultsI results)
{
updateViewport();
+
+ /*
+ * for now, don't attempt fastpaint if wrapped format
+ */
+ boolean wrapped = av.getWrapAlignment();
+ if (wrapped)
+ {
+ // drawWrappedMappedPositions(results);
+ // img = null;
+ // av.setSearchResults(results);
+ // repaint();
+ // return;
+ }
+
fastpainting = true;
fastPaint = true;
*/
SearchResultsI previous = av.getSearchResults();
av.setSearchResults(results);
- boolean redrawn = drawMappedPositions(previous);
- redrawn |= drawMappedPositions(results);
+ boolean redrawn = false;
+ boolean drawn = false;
+ if (wrapped)
+ {
+ redrawn = drawWrappedMappedPositions(previous);
+ drawn = drawWrappedMappedPositions(results);
+ redrawn |= drawn;
+ }
+ else
+ {
+ redrawn = drawMappedPositions(previous);
+ drawn = drawMappedPositions(results);
+ redrawn |= drawn;
+ }
+
+ /*
+ * if highlights were either removed or added, repaint
+ */
if (redrawn)
{
repaint();
}
+
+ /*
+ * return true only if highlights were added
+ */
+ return drawn;
+
} finally
{
fastpainting = false;
}
+
}
/**
}
}
}
+
+ /**
+ * Redraws any positions in the search results in the visible region. Any
+ * highlights are drawn depending on the search results set on the Viewport,
+ * not the results parameter. This allows this method to be called to either
+ * clear highlighting (passing the previous search results), or set new
+ * highlighting.
+ *
+ * @param results
+ * @return
+ */
+ protected boolean drawWrappedMappedPositions(SearchResultsI results)
+ {
+ if (results == null)
+ {
+ return false;
+ }
+
+ boolean matchFound = false;
+
+ /*
+ * Viewport ranges are set for the 'row' of the wrapped alignment
+ * the cursor is in, not the whole visible region; really we want
+ * the latter; +-6 a temporary fudge for codons wrapping across lines
+ */
+ ViewportRanges ranges = av.getRanges();
+ int firstVisibleColumn = ranges.getStartRes() - 6;
+ int lastVisibleColumn = ranges.getEndRes() + 6;
+ AlignmentI alignment = av.getAlignment();
+ if (av.hasHiddenColumns())
+ {
+ firstVisibleColumn = alignment.getHiddenColumns()
+ .adjustForHiddenColumns(firstVisibleColumn);
+ lastVisibleColumn = alignment.getHiddenColumns()
+ .adjustForHiddenColumns(lastVisibleColumn);
+ }
+
+ /*
+ * find width of alignment in residues, and height of alignment,
+ * so we can calculate where to render each matched position
+ */
+ int wrappedWidth = av.getWrappedWidth();
+ int wrappedHeight = av.getAlignment().getHeight() * av.getCharHeight();
+ int gapHeight = av.getCharHeight()
+ * (av.getScaleAboveWrapped() ? 2 : 1);
+ wrappedHeight += gapHeight;
+ wrappedHeight += getAnnotationHeight();
+
+ for (int seqNo = ranges.getStartSeq(); seqNo <= ranges
+ .getEndSeq(); seqNo++)
+ {
+ SequenceI seq = alignment.getSequenceAt(seqNo);
+
+ int[] visibleResults = results.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
+ */
+ firstMatchedColumn = Math.max(firstMatchedColumn,
+ firstVisibleColumn);
+ lastMatchedColumn = Math.min(lastMatchedColumn,
+ lastVisibleColumn);
+
+ /*
+ * draw each mapped position separately (as contiguous positions may
+ * wrap across lines)
+ */
+ for (int mappedPos = firstMatchedColumn; mappedPos <= lastMatchedColumn; mappedPos++)
+ {
+ int displayColumn = mappedPos;
+ if (av.hasHiddenColumns())
+ {
+ displayColumn = alignment.getHiddenColumns()
+ .findColumnPosition(displayColumn);
+ }
+
+ /*
+ * transX: offset from left edge of canvas to residue position
+ */
+ int transX = labelWidthWest
+ + ((displayColumn - ranges.getStartRes()) % wrappedWidth)
+ * av.getCharWidth();
+
+ /*
+ * transY: offset from top edge of canvas to residue position
+ */
+ int transY = gapHeight;
+ transY += (displayColumn / wrappedWidth) * wrappedHeight;
+ transY += (seqNo - ranges.getStartSeq()) * av.getCharHeight();
+
+ /*
+ * yOffset is from graphics origin to start of visible region
+ */
+ int yOffset = 0;// (displayColumn / wrappedWidth) * wrappedHeight;
+ if (transY < getHeight())
+ {
+ matchFound = true;
+ gg.translate(transX, transY);
+ drawPanel(gg, displayColumn, displayColumn, seqNo, seqNo,
+ yOffset);
+ gg.translate(-transX, -transY);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return matchFound;
+ }
}