📄 jedittextarea.java
字号:
/*
* JEditTextArea.java - jEdit's text component
* Copyright (C) 1999 Slava Pestov
*
* You may use and modify this package for any purpose. Redistribution is
* permitted, in both source and binary form, provided that this notice
* remains intact in all source distributions of this package.
*/
package org.syntax.jedit;
import org.syntax.jedit.tokenmarker.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.undo.*;
import javax.swing.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.awt.*;
import java.util.Enumeration;
import java.util.Vector;
/**
* jEdit's text area component. It is more suited for editing program
* source code than JEditorPane, because it drops the unnecessary features
* (images, variable-width lines, and so on) and adds a whole bunch of
* useful goodies such as:
* <ul>
* <li>More flexible key binding scheme
* <li>Supports macro recorders
* <li>Rectangular selection
* <li>Bracket highlighting
* <li>Syntax highlighting
* <li>Command repetition
* <li>Block caret can be enabled
* </ul>
* It is also faster and doesn't have as many problems. It can be used
* in other applications; the only other part of jEdit it depends on is
* the syntax package.<p>
*
* To use it in your app, treat it like any other component, for example:
* <pre>JEditTextArea ta = new JEditTextArea();
* ta.setTokenMarker(new JavaTokenMarker());
* ta.setText("public class Test {\n"
* + " public static void main(String[] args) {\n"
* + " System.out.println(\"Hello World\");\n"
* + " }\n"
* + "}");</pre>
*
* @author Slava Pestov
* @version $Id: JEditTextArea.java,v 1.7 2004/07/15 12:11:16 gt78 Exp $
*/
public class JEditTextArea extends JComponent
{
/**
* Adding components with this name to the text area will place
* them left of the horizontal scroll bar. In jEdit, the status
* bar is added this way.
*/
public static String LEFT_OF_SCROLLBAR = "los";
/**
* Creates a new JEditTextArea with the default settings.
*/
public JEditTextArea()
{
this(TextAreaDefaults.getDefaults());
}
/**
* Creates a new JEditTextArea with the specified settings.
* @param defaults The default settings
*/
public JEditTextArea(TextAreaDefaults defaults)
{
// Enable the necessary events
enableEvents(AWTEvent.KEY_EVENT_MASK);
// Initialize some misc. stuff
painter = new TextAreaPainter(this,defaults);
documentHandler = new DocumentHandler();
listenerList = new EventListenerList();
caretEvent = new MutableCaretEvent();
lineSegment = new Segment();
bracketLine = bracketPosition = -1;
blink = true;
// Initialize the GUI
setLayout(new ScrollLayout());
add(CENTER,painter);
add(RIGHT,vertical = new JScrollBar(JScrollBar.VERTICAL));
add(BOTTOM,horizontal = new JScrollBar(JScrollBar.HORIZONTAL));
// Add some event listeners
vertical.addAdjustmentListener(new AdjustHandler());
horizontal.addAdjustmentListener(new AdjustHandler());
painter.addComponentListener(new ComponentHandler());
painter.addMouseListener(new MouseHandler());
painter.addMouseMotionListener(new DragHandler());
addFocusListener(new FocusHandler());
// Load the defaults
setInputHandler(defaults.inputHandler);
setDocument(defaults.document);
editable = defaults.editable;
caretVisible = defaults.caretVisible;
caretBlinks = defaults.caretBlinks;
electricScroll = defaults.electricScroll;
popup = defaults.popup;
// We don't seem to get the initial focus event?
focusedComponent = this;
}
/**
* Returns if this component can be traversed by pressing
* the Tab key. This returns false.
*/
public final boolean isManagingFocus()
{
return true;
}
/**
* Returns the object responsible for painting this text area.
*/
public final TextAreaPainter getPainter()
{
return painter;
}
/**
* Returns the input handler.
*/
public final InputHandler getInputHandler()
{
return inputHandler;
}
/**
* Sets the input handler.
* @param inputHandler The new input handler
*/
public void setInputHandler(InputHandler inputHandler)
{
this.inputHandler = inputHandler;
}
/**
* Returns true if the caret is blinking, false otherwise.
*/
public final boolean isCaretBlinkEnabled()
{
return caretBlinks;
}
/**
* Toggles caret blinking.
* @param caretBlinks True if the caret should blink, false otherwise
*/
public void setCaretBlinkEnabled(boolean caretBlinks)
{
this.caretBlinks = caretBlinks;
if(!caretBlinks)
blink = false;
painter.invalidateSelectedLines();
}
/**
* Returns true if the caret is visible, false otherwise.
*/
public final boolean isCaretVisible()
{
return (!caretBlinks || blink) && caretVisible;
}
/**
* Sets if the caret should be visible.
* @param caretVisible True if the caret should be visible, false
* otherwise
*/
public void setCaretVisible(boolean caretVisible)
{
this.caretVisible = caretVisible;
blink = true;
painter.invalidateSelectedLines();
}
/**
* Blinks the caret.
*/
public final void blinkCaret()
{
if(caretBlinks)
{
blink = !blink;
painter.invalidateSelectedLines();
}
else
blink = true;
}
/**
* Returns the number of lines from the top and button of the
* text area that are always visible.
*/
public final int getElectricScroll()
{
return electricScroll;
}
/**
* Sets the number of lines from the top and bottom of the text
* area that are always visible
* @param electricScroll The number of lines always visible from
* the top or bottom
*/
public final void setElectricScroll(int electricScroll)
{
this.electricScroll = electricScroll;
}
/**
* Updates the state of the scroll bars. This should be called
* if the number of lines in the document changes, or when the
* size of the text are changes.
*/
public void updateScrollBars()
{
if(vertical != null && visibleLines != 0)
{
vertical.setValues(firstLine,visibleLines,0,getLineCount());
vertical.setUnitIncrement(2);
vertical.setBlockIncrement(visibleLines);
}
int width = this.getWidth();
if(horizontal != null && width != 0)
{
horizontal.setValues(-horizontalOffset,width,0,width * 5);
horizontal.setUnitIncrement(painter.getFontMetrics()
.charWidth('w'));
horizontal.setBlockIncrement(width / 2);
}
if (this.getVisibleLines() <= this.getLineCount())
{
if (!vertical.isVisible())
{
vertical.setPreferredSize(new Dimension(16,0));
vertical.setVisible(true);
}
}
else
{
if (vertical.isVisible())
{
vertical.setPreferredSize(new Dimension(0,0));
vertical.setVisible(false);
}
}
// Trova la riga piu' linga...
int max = 0;
int tmp_max = 0;
for (int i=0 ; i<this.getLineCount(); ++i)
{
tmp_max = this.getLineText(i).length(); // getLineLength(i);
max = (max < tmp_max) ? tmp_max : max;
//System.out.println(this.getLineText(i));
}
if (max * painter.getFontMetrics().charWidth('w') > this.getWidth() )
{
if (!horizontal.isVisible())
{
horizontal.setPreferredSize(new Dimension(20,16));
horizontal.setVisible(true);
}
}
else
{
if (horizontal.isVisible())
{
horizontal.setPreferredSize(new Dimension(0,0));
horizontal.setVisible(false);
}
}
// System.out.println(""+ this.getWidth() + "<" + this.getLineCount()+" "+max+" "+ painter.getFontMetrics().charWidth('w') );
}
/**
* Returns the line displayed at the text area's origin.
*/
public final int getFirstLine()
{
return firstLine;
}
/**
* Sets the line displayed at the text area's origin without
* updating the scroll bars.
*/
public void setFirstLine(int firstLine)
{
if(firstLine == this.firstLine)
return;
int oldFirstLine = this.firstLine;
this.firstLine = firstLine;
if(firstLine != vertical.getValue())
updateScrollBars();
painter.repaint();
}
/**
* Returns the number of lines visible in this text area.
*/
public final int getVisibleLines()
{
return visibleLines;
}
/**
* Recalculates the number of visible lines. This should not
* be called directly.
*/
public final void recalculateVisibleLines()
{
if(painter == null)
return;
int height = painter.getHeight();
int lineHeight = painter.getFontMetrics().getHeight();
int oldVisibleLines = visibleLines;
visibleLines = height / lineHeight;
updateScrollBars();
}
/**
* Returns the horizontal offset of drawn lines.
*/
public final int getHorizontalOffset()
{
return horizontalOffset;
}
/**
* Sets the horizontal offset of drawn lines. This can be used to
* implement horizontal scrolling.
* @param horizontalOffset offset The new horizontal offset
*/
public void setHorizontalOffset(int horizontalOffset)
{
if(horizontalOffset == this.horizontalOffset)
return;
this.horizontalOffset = horizontalOffset;
if(horizontalOffset != horizontal.getValue())
updateScrollBars();
painter.repaint();
}
/**
* A fast way of changing both the first line and horizontal
* offset.
* @param firstLine The new first line
* @param horizontalOffset The new horizontal offset
* @return True if any of the values were changed, false otherwise
*/
public boolean setOrigin(int firstLine, int horizontalOffset)
{
boolean changed = false;
int oldFirstLine = this.firstLine;
if(horizontalOffset != this.horizontalOffset)
{
this.horizontalOffset = horizontalOffset;
changed = true;
}
if(firstLine != this.firstLine)
{
this.firstLine = firstLine;
changed = true;
}
if(changed)
{
updateScrollBars();
painter.repaint();
}
return changed;
}
/**
* Ensures that the caret is visible by scrolling the text area if
* necessary.
* @return True if scrolling was actually performed, false if the
* caret was already visible
*/
public boolean scrollToCaret()
{
int line = getCaretLine();
int lineStart = getLineStartOffset(line);
int offset = Math.max(0,Math.min(getLineLength(line) - 1,
getCaretPosition() - lineStart));
return scrollTo(line,offset);
}
/**
* Ensures that the specified line and offset is visible by scrolling
* the text area if necessary.
* @param line The line to scroll to
* @param offset The offset in the line to scroll to
* @return True if scrolling was actually performed, false if the
* line and offset was already visible
*/
public boolean scrollTo(int line, int offset)
{
// visibleLines == 0 before the component is realized
// we can't do any proper scrolling then, so we have
// this hack...
if(visibleLines == 0)
{
setFirstLine(Math.max(0,line - electricScroll));
return true;
}
int newFirstLine = firstLine;
int newHorizontalOffset = horizontalOffset;
if(line < firstLine + electricScroll)
{
newFirstLine = Math.max(0,line - electricScroll);
}
else if(line + electricScroll >= firstLine + visibleLines)
{
newFirstLine = (line - visibleLines) + electricScroll + 1;
if(newFirstLine + visibleLines >= getLineCount())
newFirstLine = getLineCount() - visibleLines;
if(newFirstLine < 0)
newFirstLine = 0;
}
int x = _offsetToX(line,offset);
int width = painter.getFontMetrics().charWidth('w');
if(x < 0)
{
newHorizontalOffset = Math.min(0,horizontalOffset
- x + width + 5);
}
else if(x + width >= painter.getWidth())
{
newHorizontalOffset = horizontalOffset +
(painter.getWidth() - x) - width - 5;
}
return setOrigin(newFirstLine,newHorizontalOffset);
}
/**
* Converts a line index to a y co-ordinate.
* @param line The line
*/
public int lineToY(int line)
{
FontMetrics fm = painter.getFontMetrics();
return (line - firstLine) * fm.getHeight()
- (fm.getLeading() + fm.getMaxDescent());
}
/**
* Converts a y co-ordinate to a line index.
* @param y The y co-ordinate
*/
public int yToLine(int y)
{
FontMetrics fm = painter.getFontMetrics();
int height = fm.getHeight();
return Math.max(0,Math.min(getLineCount() - 1,
y / height + firstLine));
}
/**
* Converts an offset in a line into an x co-ordinate. This is a
* slow version that can be used any time.
* @param line The line
* @param offset The offset, from the start of the line
*/
public final int offsetToX(int line, int offset)
{
// don't use cached tokens
painter.currentLineTokens = null;
return _offsetToX(line,offset);
}
/**
* Converts an offset in a line into an x co-ordinate. This is a
* fast version that should only be used if no changes were made
* to the text since the last repaint.
* @param line The line
* @param offset The offset, from the start of the line
*/
public int _offsetToX(int line, int offset)
{
TokenMarker tokenMarker = getTokenMarker();
/* Use painter's cached info for speed */
FontMetrics fm = painter.getFontMetrics();
getLineText(line,lineSegment);
int segmentOffset = lineSegment.offset;
int x = horizontalOffset;
/* If syntax coloring is disabled, do simple translation */
if(tokenMarker == null)
{
lineSegment.count = offset;
return x + Utilities.getTabbedTextWidth(lineSegment,
fm,x,painter,0);
}
/* If syntax coloring is enabled, we have to do this because
* tokens can vary in width */
else
{
Token tokens;
if(painter.currentLineIndex == line
&& painter.currentLineTokens != null)
tokens = painter.currentLineTokens;
else
{
painter.currentLineIndex = line;
tokens = painter.currentLineTokens
= tokenMarker.markTokens(lineSegment,line);
}
Toolkit toolkit = painter.getToolkit();
Font defaultFont = painter.getFont();
SyntaxStyle[] styles = painter.getStyles();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -