📄 linenumberlist.java~2~
字号:
/*
* 11/14/2003
*
* LineNumberList.java - Line numbers for a text area contained
* in an RTextScrollPane.
*/
package org.fife.ui.rtextarea;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
/**
* Used by <code>RTextScrollPane</code> to display line numbers for a text
* area. This component is capable of displaying line numbers for any
* <code>RTextArea</code>, with line wrap enabled or disabled. You can also
* choose the line number font, color, and choose whether or not to "highlight"
* the current line number.<p>
*
* NOTE: To speed things up a little, this component does not "scroll." It
* needs to be added to an <code>RTextScrollPane</code> inside a special
* viewport, like so:<p>
*
* <pre>
* JViewport viewport = new JViewport() {
* public void setViewPosition(java.awt.Point p) {
* Component c = getView();
* if (c!=null)
* c.repaint();
* }
* };
* viewport.setView(lineNumberList);
* setRowHeader(viewport);
* </pre>
*
* This needs to be done because this component figures out how to paint itself
* from the current scroll-state of the text area, so there is no need to scroll
* this guy himself.<p>
*
* This class is probably going to be replaced by
* {@link org.fife.ui.LineNumberBorder}, as not only does that class not need
* to be scrolled, but he is also not a <code>JComponent</code> (less memory)
* and he doesn't require the additional overhead of a <code>JViewport</code>
* (slower).
*
* @author Robert Futrell
* @version 0.5
*/
class LineNumberList extends JComponent implements CaretListener,
DocumentListener, PropertyChangeListener {
/**
*
*/
private static final long serialVersionUID = -7969233269296717504L;
private static final int MIN_CELL_WIDTH = 24;
private static final int RHS_BORDER_WIDTH = 8;
private RTextArea textArea; // The text component for which we're displaying the line numbers.
private int currentLine; // The last line the caret was on.
private int lastY = -1; // Used to check if caret is on a new line when line wrap is enabled.
private int cellHeight; // The height of a "cell" for a line number when word wrap is off.
private int cellWidth; // The width used for all line number cells.
private int ascent; // The ascent to use when painting line numbers.
private int currentNumLines;
/*****************************************************************************/
/**
* Constructs a new <code>LineNumberList</code> using default values for
* line number color (gray) and highlighting the current line.
*
* @param textArea The text component for which line numbers will be
* displayed.
*/
public LineNumberList(RTextArea textArea) {
this(textArea, new Color(128,128,128));
}
/*****************************************************************************/
/**
* Constructs a new <code>LineNumberList</code>.
*
* @param textArea The text component for which line numbers will be
* displayed.
* @param numberColor The color to use for the line numbers.
*/
public LineNumberList(RTextArea textArea, Color numberColor) {
// Remember what text component we're keeping line numbers for.
this.textArea = textArea;
if (numberColor!=null)
setForeground(numberColor);
else
setForeground(new Color(128,128,128));
Color bg = textArea.getBackground();
setBackground(bg==null ? Color.WHITE : bg);
textArea.addCaretListener(this);
textArea.addPropertyChangeListener(this);
textArea.getDocument().addDocumentListener(this);
// Initialize currentLine; otherwise, the current line won't start
// off as highlighted.
currentLine = 1;
// Have the line number space be just enough space for '1' through '9'.
updateCellHeights();
updateCellWidths();
}
/*****************************************************************************/
/**
* Called whenever the caret changes position; highlight the correct line
* number.
*/
public void caretUpdate(CaretEvent e) {
int caretPosition = textArea.getCaretPosition();
// We separate the line wrap/no line wrap cases because word wrap can
// make a single line from the model (document) be on multiple lines
// on the screen (in the view); thus, we have to enhance the logic for
// that case a bit - we check the actual y-coordinate of the caret
// when line wrap is enabled. For the no-line-wrap case, getting the
// line number of the caret suffices. This increases efficiency in
// the no-line-wrap case.
if (textArea.getLineWrap()==false) {
int line = textArea.getDocument().getDefaultRootElement().
getElementIndex(caretPosition) + 1;
if (currentLine!=line) {
currentLine = line;
repaint();
}
}
else { // lineWrap is enabled; must check actual y-coord. of caret.
try {
int y = textArea.modelToView(caretPosition).y;
if (y!=lastY) {
lastY = y;
currentLine = textArea.getDocument().
getDefaultRootElement().
getElementIndex(caretPosition) + 1;
repaint();
}
} catch (BadLocationException ble) {
ble.printStackTrace();
}
}
}
/*****************************************************************************/
public void changedUpdate(DocumentEvent e) {}
/*****************************************************************************/
/**
* Returns the color to use to paint line numbers.
*
* @return The color used when painting line numbers.
* @see #setLineNumberColor
*/
public Color getLineNumberColor() {
return getForeground();
}
/*****************************************************************************/
public Dimension getPreferredSize() {
return new Dimension(cellWidth, textArea.getHeight());
}
/*****************************************************************************/
/**
* Returns the length of a string if it is drawn with the specified
* graphics context. This method assumes that there are NO tabs in
* the string.<br><br>
*
* NOTE: This is basically ripped off from
* <code>javax.swing.text.Utilities</code>, but slightly optimized for our
* situation.
*
* @param text The text to be painted.
* @param metrics The metrics with which to do the calculating.
* @return The width of the string when painted.
*/
public static final int getTextWidth(String text, FontMetrics metrics) {
int width = 0;
int end = text.length();
for (int i=0; i<end; i++)
width += metrics.charWidth(text.charAt(i));
return width;
}
/*****************************************************************************/
/**
* Called whenever a character is input (key is typed) in the text document
* we're line-numbering.
*/
public void insertUpdate(DocumentEvent e) {
int newNumLines = textArea.getDocument().getDefaultRootElement().
getElementCount();
if (newNumLines > currentNumLines) {
// Adjust the amount of space the line numbers take up,
// if necessary.
if (newNumLines/10 > currentNumLines/10)
updateCellWidths();
currentNumLines = newNumLines;
}
}
/*****************************************************************************/
// Returns the Component used as the JList cell.
public void paint(Graphics g) {
Element root = textArea.getDocument().getDefaultRootElement();
Rectangle visibleRect = textArea.getVisibleRect();
if (visibleRect==null)
return;
// Fill in the background the same color as the text component.
g.setColor(getBackground());
g.fillRect(0,0, cellWidth,visibleRect.height);
if (textArea.getLineWrap()==true) {
paintWrappedLineNumbers(g, root, visibleRect);
return;
}
// Get the first and last lines to paint.
int topLine = visibleRect.y/cellHeight + 1;
int bottomLine = Math.min(topLine+visibleRect.height/cellHeight,
root.getElementCount()) + 1;
// Get where to start painting (top of the row), and where to paint
// the line number (drawString expects y==baseline).
// We need to be "scrolled up" up just enough for the missing part of
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -