📄 syntaxview.java
字号:
/*
* 02/24/2004
*
* SyntaxView.java - The View object used by RSyntaxTextArea when word wrap is
* disabled.
* Copyright (C) 2004 Robert Futrell
* email@address.com
* www.website.com
*
* This program 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 2
* of the License, or any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.*;
import javax.swing.event.*;
import javax.swing.text.*;
/**
* The <code>javax.swing.text.View</code> object used by
* {@link org.fife.ui.rsyntaxtextarea.RSyntaxTextArea} when word wrap is
* disabled. It implements syntax highlighting for programming languages using
* the colors and font styles specified by the <code>RSyntaxTextArea</code>.<p>
*
* You don't really have to do anything to use this class, as
* {@link org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaUI} automatically sets the
* text area's view to be an instance of this class if word wrap is disabled.<p>
*
* The tokens that specify how to paint the syntax-highlighted text are gleaned
* from the text area's
* {@link org.fife.ui.rsyntaxtextarea.RSyntaxDocument}.
*
* @author Robert Futrell
* @version 0.3
*/
public class SyntaxView extends View implements TabExpander,
TokenOrientedView {
/**
* The default font used by the text area. If this changes we need to
* recalculate the longest line.
*/
Font font;
/**
* Font metrics for the current font.
*/
protected FontMetrics metrics;
/**
* The current longest line. This is used to calculate the preferred width
* of the view. Since the calculation is potentially expensive, we try to
* avoid it by stashing which line is currently the longest.
*/
Element longLine;
float longLineWidth;
int tabSize;
int tabBase;
/**
* This is reused to keep from allocating/deallocating.
*/
private Segment s;
/**
* Cached for each paint() call so each drawLine() call has access to it.
*/
private RSyntaxTextArea host;
/**
* Cached values to speed up the painting a tad.
*/
private int lineHeight = 0;
private int ascent;
private int clipStart;
private int clipEnd;
/**
* The end-of-line marker.
*/
private static final char[] eolMarker = { '.' };
/*****************************************************************************/
/**
* Constructs a new <code>SyntaxView</code> wrapped around an element.
*
* @param elem The element representing the text to display.
*/
public SyntaxView(Element elem) {
super(elem);
s = new Segment();
}
/*****************************************************************************/
/**
* Iterate over the lines represented by the child elements
* of the element this view represents, looking for the line
* that is the longest. The <em>longLine</em> variable is updated to
* represent the longest line contained. The <em>font</em> variable
* is updated to indicate the font used to calculate the
* longest line.
*/
void calculateLongestLine() {
Component c = getContainer();
font = c.getFont();
metrics = c.getFontMetrics(font);
tabSize = getTabSize() * metrics.charWidth(' ');
Element lines = getElement();
int n = lines.getElementCount();
for (int i=0; i<n; i++) {
Element line = lines.getElement(i);
float w = getLineWidth(i);
if (w > longLineWidth) {
longLineWidth = w;
longLine = line;
}
}
}
/*****************************************************************************/
/**
* Gives notification from the document that attributes were changed
* 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
* @see View#changedUpdate
*/
public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
updateDamage(changes, a, f);
}
/*****************************************************************************/
/**
* Repaint the given line range.
*
* @param line0 The starting line number to repaint. This must
* be a valid line number in the model.
* @param line1 The ending line number to repaint. This must
* be a valid line number in the model.
* @param a The region allocated for the view to render into.
* @param host The component hosting the view (used to call repaint).
*/
protected void damageLineRange(int line0, int line1, Shape a,
Component host) {
if (a != null) {
Rectangle area0 = lineToRect(a, line0);
Rectangle area1 = lineToRect(a, line1);
if ((area0 != null) && (area1 != null)) {
Rectangle dmg = area0.union(area1); // damage.
host.repaint(dmg.x, dmg.y, dmg.width, dmg.height);
}
else
host.repaint();
}
}
/*****************************************************************************/
/**
* Draws the passed-in text using syntax highlighting for the current
* language. The tokens used to decide how to paint the syntax
* highlighting are grabbed from the text area's document.
*
* @param token The list of tokens to draw.
* @param g The graphics context in which to draw.
* @param colorScheme The syntax highlighting color scheme to use.
* @param x The x-coordinate at which to draw.
* @param y The y-coordinate at which to draw.
* @return The x-coordinate representing the end of the painted text.
*/
public float drawLine(Token token, Graphics2D g,
final SyntaxHighlightingColorScheme colorScheme,
float x, float y) {
float nextX = x; // The x-value at the end of our text.
SyntaxScheme scheme;
while (token!=null && token.isPaintable() && nextX<clipEnd) {
scheme = colorScheme.syntaxSchemes[token.type];
nextX = token.paint(g, nextX,y, scheme, host, this, clipStart);
token = token.getNextToken();
}
//// Paint the "end-of-line" marker if desired.
//if (true/*host.areEndOfLineMarkersPainted()*/) {
// drawTabbedText(eolMarker, nextX,y, g, 0, new Color(170, 205,205), null);
//}
// Return the x-coordinate at the end of the painted text.
return nextX;
}
/*****************************************************************************/
/**
* Calculates the width of the line represented by the given element.
*
* @param line The line for which to get the length.
* @param lineNumber The line number of the specified line in the document.
* @return The width of the line.
*/
private float getLineWidth(int lineNumber) {
Token tokenList = ((RSyntaxDocument)getDocument()).
getTokenListForLine(lineNumber);
return RSyntaxUtilities.getTokenListWidth(tokenList,
(RSyntaxTextArea)getContainer(),
this);
}
/*****************************************************************************/
/**
* Provides a way to determine the next visually represented model
* location that one might place a caret. Some views may not be visible,
* they might not be in the same order found in the model, or they just
* might not allow access to some of the locations in the model.
*
* @param pos the position to convert >= 0
* @param a the allocated region to render into
* @param direction the direction from the current position that can
* be thought of as the arrow keys typically found on a keyboard.
* This may be SwingConstants.WEST, SwingConstants.EAST,
* SwingConstants.NORTH, or SwingConstants.SOUTH.
* @return the location within the model that best represents the next
* location visual position.
* @exception BadLocationException
* @exception IllegalArgumentException for an invalid direction
*/
public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
int direction, Position.Bias[] biasRet)
throws BadLocationException {
return RSyntaxUtilities.getNextVisualPositionFrom(pos, b, a,
direction, biasRet, this);
}
/*****************************************************************************/
/**
* Determines the preferred span for this view along an
* axis.
*
* @param axis may be either View.X_AXIS or View.Y_AXIS
* @return the span the view would like to be rendered into >= 0.
* Typically the view is told to render into the span
* that is returned, although there is no guarantee.
* The parent may choose to resize or break the view.
* @exception IllegalArgumentException for an invalid axis
*/
public float getPreferredSpan(int axis) {
updateMetrics();
switch (axis) {
case View.X_AXIS:
return longLineWidth + 10; // "fudge factor."
case View.Y_AXIS:
// We update lineHeight here as when this method is first
// called, lineHeight isn't initialized. If we don't do it
// here, we get no vertical scrollbar (as lineHeight==0).
lineHeight = host!=null ? host.getLineHeight() : lineHeight;
return getElement().getElementCount() * lineHeight;
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
/*****************************************************************************/
/**
* Returns the tab size set for the document, defaulting to 5.
*
* @return The tab size.
*/
protected int getTabSize() {
Integer i = (Integer)getDocument().getProperty(
PlainDocument.tabSizeAttribute);
int size = (i != null) ? i.intValue() : 5;
return size;
}
/*****************************************************************************/
/**
* Returns a token list for the <i>physical</i> line above the physical
* line containing the specified offset into the document. Note that for
* this plain (non-wrapped) view, this is simply the token list for the
* logical line above the line containing <code>offset</code>, since lines
* are not wrapped.
*
* @param offset The offset in question.
* @return A token list for the physical (and in this view, logical) line
* before this one. If <code>offset</code> is in the first line in
* the document, <code>null</code> is returned.
*/
public Token getTokenListForPhysicalLineAbove(int offset) {
RSyntaxDocument document = (RSyntaxDocument)getDocument();
Element map = document.getDefaultRootElement();
int line = map.getElementIndex(offset) - 1;
if (line>=0)
return document.getTokenListForLine(line);
return null;
}
/*****************************************************************************/
/**
* Returns a token list for the <i>physical</i> line below the physical
* line containing the specified offset into the document. Note that for
* this plain (non-wrapped) view, this is simply the token list for the
* logical line below the line containing <code>offset</code>, since lines
* are not wrapped.
*
* @param offset The offset in question.
* @return A token list for the physical (and in this view, logical) line
* after this one. If <code>offset</code> is in the last physical
* line in the document, <code>null</code> is returned.
*/
public Token getTokenListForPhysicalLineBelow(int offset) {
RSyntaxDocument document = (RSyntaxDocument)getDocument();
Element map = document.getDefaultRootElement();
int line = map.getElementIndex(offset);
int lineCount = map.getElementCount();
if (line<lineCount-1)
return document.getTokenListForLine(line+1);
return null;
}
/*****************************************************************************/
/**
* Gives notification that something was inserted into 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 insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
updateDamage(changes, a, f);
}
/*****************************************************************************/
/**
* Determine the rectangle that represents the given line.
*
* @param a The region allocated for the view to render into
* @param line The line number to find the region of. This must
* be a valid line number in the model.
*/
protected Rectangle lineToRect(Shape a, int line) {
Rectangle r = null;
updateMetrics();
if (metrics != null) {
Rectangle alloc = a.getBounds();
// NOTE: lineHeight is not initially set here, leading to the
// current line not being highlighted when a document is first
// opened. So, we set it here just in case.
lineHeight = host!=null ? host.getLineHeight() : lineHeight;
r = new Rectangle(alloc.x, alloc.y + line*lineHeight,
alloc.width, lineHeight);
}
return r;
}
/*****************************************************************************/
/**
* Provides a mapping from the document model coordinate space
* to the coordinate space of the view mapped to it.
*
* @param pos the position to convert >= 0
* @param a the allocated region to render into
* @return the bounding box of the given position
* @exception BadLocationException if the given position does not
* represent a valid location in the associated document
* @see View#modelToView
*/
public Shape modelToView(int pos, Shape a, Position.Bias b)
throws BadLocationException {
// line coordinates
Element map = getElement();
RSyntaxDocument doc = (RSyntaxDocument)getDocument();
int lineIndex = map.getElementIndex(pos);
Rectangle lineArea = lineToRect(a, lineIndex);
tabBase = lineArea.x; // Used by listOffsetToView().
Token tokenList = doc.getTokenListForLine(lineIndex);
//int x = (int)RSyntaxUtilities.getTokenListWidthUpTo(tokenList,
// (RSyntaxTextArea)getContainer(),
// this, 0, pos);
// We use this method instead as it returns the actual bounding box,
// not just the x-coordinate.
lineArea = tokenList.listOffsetToView(
(RSyntaxTextArea)getContainer(), this, pos,
tabBase, lineArea);
return lineArea;
}
/*****************************************************************************/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -