📄 syntaxview.java
字号:
/**
* Provides a mapping, for a given region, from the document model
* coordinate space to the view coordinate space. The specified region is
* created as a union of the first and last character positions.<p>
*
* This is implemented to subtract the width of the second character, as
* this view's <code>modelToView</code> actually returns the width of the
* character instead of "1" or "0" like the View implementations in
* <code>javax.swing.text</code>. Thus, if we don't override this method,
* the <code>View</code> implementation will return one character's width
* too much for its consumers (implementations of
* <code>javax.swing.text.Highlighter</code>).
*
* @param p0 the position of the first character (>=0)
* @param b0 The bias of the first character position, toward the previous
* character or the next character represented by the offset, in
* case the position is a boundary of two views; <code>b0</code>
* will have one of these values:
* <ul>
* <li> <code>Position.Bias.Forward</code>
* <li> <code>Position.Bias.Backward</code>
* </ul>
* @param p1 the position of the last character (>=0)
* @param b1 the bias for the second character position, defined
* one of the legal values shown above
* @param a the area of the view, which encompasses the requested region
* @return the bounding box which is a union of the region specified
* by the first and last character positions
* @exception BadLocationException if the given position does
* not represent a valid location in the associated document
* @exception IllegalArgumentException if <code>b0</code> or
* <code>b1</code> are not one of the
* legal <code>Position.Bias</code> values listed above
* @see View#viewToModel
*/
public Shape modelToView(int p0, Position.Bias b0,
int p1, Position.Bias b1,
Shape a) throws BadLocationException {
Shape s0 = modelToView(p0, a, b0);
Shape s1;
if (p1 ==getEndOffset()) {
try {
s1 = modelToView(p1, a, b1);
} catch (BadLocationException ble) {
s1 = null;
}
if (s1 == null) {
// Assume extends left to right.
Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
a.getBounds();
s1 = new Rectangle(alloc.x + alloc.width - 1, alloc.y,
1, alloc.height);
}
}
else {
s1 = modelToView(p1, a, b1);
}
Rectangle r0 = s0.getBounds();
Rectangle r1 = (s1 instanceof Rectangle) ? (Rectangle) s1 :
s1.getBounds();
if (r0.y != r1.y) {
// If it spans lines, force it to be the width of the view.
Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
a.getBounds();
r0.x = alloc.x;
r0.width = alloc.width;
}
r0.add(r1);
// The next line is the only difference between this method and
// View's implementation. We're subtracting the width of the second
// character. This is because this method is used by Highlighter
// implementations to get the area to "highlight", and if we don't do
// this, one character too many is highlighted thanks to our
// modelToView() implementation returning the actual width of the
// character requested!
if (p1>p0) r0.width -= r1.width;
return r0;
}
/*****************************************************************************/
/**
* Returns the next tab stop position after a given reference position.
* This implementation does not support things like centering so it
* ignores the tabOffset argument.
*
* @param x the current position >= 0
* @param tabOffset the position within the text stream
* that the tab occurred at >= 0.
* @return the tab stop, measured in points >= 0
*/
public float nextTabStop(float x, int tabOffset) {
if (tabSize == 0)
return x;
int ntabs = (((int)x) - tabBase) / tabSize;
return tabBase + ((ntabs + 1) * tabSize);
}
/*****************************************************************************/
/**
* Actually paints the text area. Only lines that have been damaged are
* repainted.
*
* @param g The graphics context with which to paint.
* @param a The allocated region in which to render.
* @see #drawLine
*/
public void paint(Graphics g, Shape a) {
RSyntaxDocument document = (RSyntaxDocument)getDocument();
Rectangle alloc = a.getBounds();
tabBase = alloc.x;
host = (RSyntaxTextArea)getContainer();
Rectangle clip = g.getClipBounds();
// An attempt to speed things up for files with long lines. Note that
// this will actually slow things down a tad for the common case of
// regular-length lines, but I don't think it'll make a difference
// visually. We'll see...
clipStart = clip.x;
clipEnd = clipStart + clip.width;
lineHeight = host.getLineHeight();
ascent = host.getMaxAscent();//metrics.getAscent();
int heightBelow = (alloc.y + alloc.height) - (clip.y + clip.height);
int linesBelow = Math.max(0, heightBelow / lineHeight);
int heightAbove = clip.y - alloc.y;
int linesAbove = Math.max(0, heightAbove / lineHeight);
int linesTotal = alloc.height / lineHeight;
if (alloc.height % lineHeight != 0) {
linesTotal++;
}
Rectangle lineArea = lineToRect(a, linesAbove);
int y = lineArea.y + ascent;
int x = lineArea.x;
Element map = getElement();
int lineCount = map.getElementCount();
int endLine = Math.min(lineCount, linesTotal - linesBelow);
lineCount--;
/*
DefaultHighlighter dh = (DefaultHighlighter)host.getHighlighter();
if (!dh.getDrawsLayeredHighlights()) {
dh.paint(g); // Draw all highlights now.
}
*/
LayeredHighlighter dh = (LayeredHighlighter)host.getHighlighter();
final SyntaxHighlightingColorScheme colorScheme =
host.getSyntaxHighlightingColorScheme();
Graphics2D g2d = (Graphics2D)g;
Token token;
//System.err.println("Painting lines: " + linesAbove + " to " + (endLine-1));
for (int line = linesAbove; line < endLine; line++) {
// Paint the highlights (both selection and "mark all").
//if (dh.getDrawsLayeredHighlights() {
Element lineElement = map.getElement(line);
int startOffset = lineElement.getStartOffset();
int endOffset = (line==lineCount ? lineElement.getEndOffset()-1 :
lineElement.getEndOffset()-1);
dh.paintLayeredHighlights(g2d, startOffset, endOffset, a,
host, this);
//}
// Paint a line of text.
token = document.getTokenListForLine(line);
drawLine(token, g2d, colorScheme, x,y);
y += lineHeight;
}
}
/*****************************************************************************/
/**
* If the passed-in line is longer than the current longest line, then
* the longest line is updated.
*
* @param line The line to test against the current longest.
* @param lineNumber The line number of the passed-in line.
* @return <code>true</code> iff the current longest line was updated.
*/
protected boolean possiblyUpdateLongLine(Element line, int lineNumber) {
float w = getLineWidth(lineNumber);
if (w > longLineWidth) {
longLineWidth = w;
longLine = line;
return true;
}
return false;
}
/*****************************************************************************/
/**
* Gives notification that something was removed from the document
* in a location that this view is responsible for.
*
* @param changes the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
*/
public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
updateDamage(changes, a, f);
}
/*****************************************************************************/
/**
* Makes our private <code>Segment s</code> point to the text in our
* document referenced by the specified element. Note that
* <code>line</code> MUST be a valid line number in the document.
*
* @param line The line number you want to get.
* @param document The document from which you want to get the text.
*/
private void setSharedSegment(int line, Document document) {
Element map = getElement();
int numLines = map.getElementCount();
Element element = map.getElement(line);
int startOffset = element.getStartOffset();
int endOffset = (line==numLines-1 ? element.getEndOffset()-1 :
element.getEndOffset() - 1);
setSharedSegment(startOffset, endOffset, document);
}
/*****************************************************************************/
/**
* Makes our private <code>Segment s</code> point to the text in our
* document referenced by the specified positions. Note that the
* positions MUST be a valid in the document.
*
* @param p0 The starting position of the text in the document.
* @param p1 The ending position of the text in the document.
* @param document The document from which you want to get the text.
*/
private final void setSharedSegment(int p0, int p1, Document document) {
try {
document.getText(p0, p1-p0, s);
} catch (BadLocationException ble) {
ble.printStackTrace();
System.exit(0);
}
}
/*****************************************************************************/
public void setSize(float width, float height) {
super.setSize(width, height);
updateMetrics();
}
/*****************************************************************************/
/**
* Repaint the region of change covered by the given document
* event. Damages the line that begins the range to cover
* the case when the insert/remove is only on one line.
* If lines are added or removed, damages the whole
* view. The longest line is checked to see if it has
* changed.
*/
protected void updateDamage(DocumentEvent changes, Shape a, ViewFactory f) {
Component host = getContainer();
updateMetrics();
Element elem = getElement();
DocumentEvent.ElementChange ec = changes.getChange(elem);
Element[] added = (ec != null) ? ec.getChildrenAdded() : null;
Element[] removed = (ec != null) ? ec.getChildrenRemoved() : null;
if (((added != null) && (added.length > 0)) ||
((removed != null) && (removed.length > 0))) {
// lines were added or removed...
if (added != null) {
int addedAt = ec.getIndex(); // FIXME: Is this correct?????
for (int i = 0; i < added.length; i++)
possiblyUpdateLongLine(added[i], addedAt+i);
}
if (removed != null) {
for (int i = 0; i < removed.length; i++) {
if (removed[i] == longLine) {
longLineWidth = -1; // Must do this!!
calculateLongestLine();
break;
}
}
}
preferenceChanged(null, true, true);
host.repaint();
}
// This occurs when syntax highlighting only changes on lines
// (i.e. beginning a multiline comment).
else if (changes.getType()==DocumentEvent.EventType.CHANGE) {
//System.err.println("Updating the damage due to a CHANGE event...");
int startLine = changes.getOffset();
int endLine = changes.getLength();
damageLineRange(startLine,endLine, a, host);
}
else {
Element map = getElement();
int line = map.getElementIndex(changes.getOffset());
damageLineRange(line, line, a, host);
if (changes.getType() == DocumentEvent.EventType.INSERT) {
// check to see if the line is longer than current
// longest line.
Element e = map.getElement(line);
if (e == longLine) {
// We must recalculate longest line's width here
// because it has gotten longer.
longLineWidth = getLineWidth(line);
preferenceChanged(null, true, false);
}
else {
// If long line gets updated, update the status bars too.
if (possiblyUpdateLongLine(e, line))
preferenceChanged(null, true, false);
}
}
else if (changes.getType() == DocumentEvent.EventType.REMOVE) {
if (map.getElement(line) == longLine) {
// removed from longest line... recalc
longLineWidth = -1; // Must do this!
calculateLongestLine();
preferenceChanged(null, true, false);
}
}
}
}
/*****************************************************************************/
/**
* Checks to see if the font metrics and longest line are up-to-date.
*/
protected void updateMetrics() {
host = (RSyntaxTextArea)getContainer();
Font f = host.getFont();
if (font != f) {
// The font changed, we need to recalculate the longest line!
// This also updates cached font and tab size.
calculateLongestLine();
}
}
/*****************************************************************************/
/**
* Provides a mapping from the view coordinate space to the logical
* coordinate space of the model.
*
* @param fx the X coordinate >= 0
* @param fy the Y coordinate >= 0
* @param a the allocated region to render into
* @return the location within the model that best represents the
* given point in the view >= 0
*/
public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {
bias[0] = Position.Bias.Forward;
Rectangle alloc = a.getBounds();
RSyntaxDocument doc = (RSyntaxDocument)getDocument();
int x = (int) fx;
int y = (int) fy;
// If they're asking about a view position above the area covered by
// this view, then the position is assumed to be the starting position
// of this view.
if (y < alloc.y) {
return getStartOffset();
}
// If they're asking about a position below this view, the position
// is assumed to be the ending position of this view.
else if (y > alloc.y + alloc.height) {
return getEndOffset() - 1;
}
// They're asking about a position within the coverage of this view
// vertically. So, we figure out which line the point corresponds to.
// If the line is greater than the number of lines contained, then
// simply use the last line as it represents the last possible place
// we can position to.
else {
Element map = doc.getDefaultRootElement();
int lineIndex = Math.abs((y - alloc.y) / lineHeight);//metrics.getHeight() );
if (lineIndex >= map.getElementCount())
return getEndOffset() - 1;
Element line = map.getElement(lineIndex);
// If the point is to the left of the line...
if (x < alloc.x)
return line.getStartOffset();
// If the point is to the right of the line...
else if (x > alloc.x + alloc.width)
return line.getEndOffset() - 1;
else {
// Determine the offset into the text
int p0 = line.getStartOffset();
Token tokenList = doc.getTokenListForLine(lineIndex);
tabBase = alloc.x;
int offs = tokenList.getListOffset(
(RSyntaxTextArea)getContainer(),
this, tabBase, x);
return offs!=-1 ? offs : p0;
}
} // End of else.
}
/*****************************************************************************/
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -