📄 rsyntaxtextareaeditorkit.java
字号:
/*
* 08/29/2004
*
* RSyntaxTextAreaEditorKit.java - The editor kit used by RSyntaxTextArea.
* 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 java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import org.fife.ui.rtextarea.RecordableTextAction;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextAreaEditorKit;
/**
* An extension of <code>RTextAreaEditorKit</code> that adds functionality for
* programming-specific stuff. There are currently subclasses to handle:
*
* <ul>
* <li>Block indentation (increasing the indent of one or multiple lines)</li>
* <li>Block un-indentation (decreasing the indent of one or multiple lines)
* </li>
* <li>Auto-indent (handled by simply remembering the indent of the previous
* line when a newline is inserted)</li>
* <li>Inserting a "code template" when a configurable key (e.g. a space) is
* pressed</li>
* <li>Decreasing the point size of all fonts in the text area</li>
* <li>Increasing the point size of all fonts in the text area</li>
* <li>Moving the caret to the "matching bracket" of the one at the current
* caret position</li>
* </ul>
*
* @author Robert Futrell
* @version 0.3
*/
public class RSyntaxTextAreaEditorKit extends RTextAreaEditorKit {
/**
*
*/
private static final long serialVersionUID = 6058766564696326312L;
public static final String rstaDecreaseIndentAction = "RSyntaxTextArea.DecreaseIndentAction";
public static final String rstaGoToMatchingBracketAction = "RSyntaxTextArea.GoToMatchingBracketAction";
public static final String rstaPossiblyInsertTemplateAction = "RSyntaxTextArea.TemplateAction";
/**
* The actions that <code>RSyntaxTextAreaEditorKit</code> adds to those of
* <code>RTextAreaEditorKit</code>.
*/
private static final Action[] defaultActions = {
new DecreaseIndentAction(),
new InsertBreakAction(),
new InsertTabAction(),
new PossiblyInsertTemplateAction(),
};
/*****************************************************************************/
/**
* Constructor.
*/
public RSyntaxTextAreaEditorKit() {
super();
}
/*****************************************************************************/
/**
* Returns the default document used by <code>RSyntaxTextArea</code>s.
*
* @return The document.
*/
public Document createDefaultDocument() {
return new RSyntaxDocument(SyntaxConstants.NO_SYNTAX_STYLE);
}
/*****************************************************************************/
/**
* Fetches the set of commands that can be used
* on a text component that is using a model and
* view produced by this kit.
*
* @return the command list
*/
public Action[] getActions() {
return TextAction.augmentList(super.getActions(),
RSyntaxTextAreaEditorKit.defaultActions);
}
/*****************************************************************************/
/**************************** INNER CLASSES **********************************/
/*****************************************************************************/
/**
* Action for decreasing the font size of all fonts in the text area.
*/
public static class DecreaseFontSizeAction
extends RTextAreaEditorKit.DecreaseFontSizeAction {
/**
*
*/
private static final long serialVersionUID = 8797959862173386776L;
public DecreaseFontSizeAction() {
super();
}
public DecreaseFontSizeAction(String name, Icon icon, String desc,
Integer mnemonic, KeyStroke accelerator) {
super(name, icon, desc, mnemonic, accelerator);
}
public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
SyntaxHighlightingColorScheme scheme = rsta.
getSyntaxHighlightingColorScheme();
// All we need to do is update all of the fonts in syntax
// schemes, then call setSyntaxHighlightingColorScheme with the
// same scheme already being used. This relies on the fact that
// that method does not check whether the new scheme is different
// from the old scheme before updating.
boolean changed = false;
int count = scheme.syntaxSchemes.length;
for (int i=0; i<count; i++) {
SyntaxScheme ss = scheme.syntaxSchemes[i];
if (ss!=null) {
Font font = ss.font;
if (font!=null) {
float oldSize = font.getSize2D();
float newSize = oldSize - decreaseAmount;
if (newSize>=MINIMUM_SIZE) {
// Shrink by decreaseAmount.
ss.font = font.deriveFont(newSize);
changed = true;
}
else if (oldSize>MINIMUM_SIZE) {
// Can't shrink by full decreaseAmount, but
// can shrink a little bit.
ss.font = font.deriveFont(MINIMUM_SIZE);
changed = true;
}
}
}
}
// If we updated at least one font, update the screen. If
// all of the fonts were already the minimum size, beep.
if (changed) {
rsta.setSyntaxHighlightingColorScheme(scheme);
// NOTE: This is a hack to get an encompassing
// RTextScrollPane to repaint its line numbers to account
// for a change in line height due to a font change. I'm
// not sure why we need to do this here but not when we
// change the syntax highlighting color scheme via the
// Options dialog... setSyntaxHighlightingColorScheme()
// calls revalidate() which won't repaint the scroll pane
// if scrollbars don't change, which is why we need this.
Component parent = rsta.getParent();
if (parent instanceof javax.swing.JViewport) {
parent = parent.getParent();
if (parent instanceof JScrollPane) {
parent.repaint();
}
}
}
else
UIManager.getLookAndFeel().provideErrorFeedback(rsta);
}
}
/*****************************************************************************/
/**
* Action for when un-indenting lines (either the current line if there is
* selection, or all selected lines if there is one).
*/
public static class DecreaseIndentAction extends RecordableTextAction {
/**
*
*/
private static final long serialVersionUID = -8480135115767268218L;
private Segment s;
public DecreaseIndentAction() {
this(rstaDecreaseIndentAction);
}
public DecreaseIndentAction(String name) {
super(name);
s = new Segment();
}
public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
if (!textArea.isEditable() || !textArea.isEnabled()) {
UIManager.getLookAndFeel().provideErrorFeedback(textArea);
return;
}
Document document = textArea.getDocument();
Element map = document.getDefaultRootElement();
Caret c = textArea.getCaret();
int dot = c.getDot();
int mark = c.getMark();
int line1 = map.getElementIndex(dot);
int tabSize = textArea.getTabSize();
// If there is a selection, indent all lines in the selection.
// Otherwise, indent the line the caret is on.
if (dot!=mark) {
// Note that we cheaply reuse variables here, so don't
// take their names to mean what they are.
int line2 = map.getElementIndex(mark);
dot = Math.min(line1, line2);
mark = Math.max(line1, line2);
Element elem;
try {
for (line1=dot; line1<mark; line1++) {
elem = map.getElement(line1);
handleDecreaseIndent(elem, document, tabSize);
}
// Don't do the last line if the caret is at its
// beginning. We must call getDot() again and not just
// use 'dot' as the caret's position may have changed
// due to the insertion of the tabs above.
elem = map.getElement(mark);
if (c.getDot()!=elem.getStartOffset())
handleDecreaseIndent(elem, document, tabSize);
} catch (BadLocationException ble) {
ble.printStackTrace();
UIManager.getLookAndFeel().
provideErrorFeedback(textArea);
}
}
else {
Element elem = map.getElement(line1);
try {
handleDecreaseIndent(elem, document, tabSize);
} catch (BadLocationException ble) {
ble.printStackTrace();
UIManager.getLookAndFeel().
provideErrorFeedback(textArea);
}
}
}
public final String getMacroID() {
return rstaDecreaseIndentAction;
}
/**
* Actually does the "de-indentation." This method finds where the
* given element's leading whitespace ends, then, if there is indeed
* leading whitespace, removes either the last char in it (if it is a
* tab), or removes up to the number of spaces equal to a tab in the
* specified document (i.e., if the tab size was 5 and there were 3
* spaces at the end of the leading whitespace, the three will be
* removed; if there were 8 spaces, only the first 5 would be
* removed).
*
* @param elem The element to "de-indent."
* @param doc The document containing the specified element.
* @param tabSize The size of a tab, in spaces.
*/
private final void handleDecreaseIndent(Element elem, Document doc,
int tabSize)
throws BadLocationException {
int start = elem.getStartOffset();
int end = elem.getEndOffset() - 1; // Why always true??
doc.getText(start,end-start, s);
int i = s.offset;
end = i+s.count;
if (end>i) {
// If the first character is a tab, remove it.
if (s.array[i]=='\t') {
doc.remove(start, 1);
}
// Otherwise, see if the first character is a space. If it
// is, remove all contiguous whitespaces at the beginning of
// this line, up to the tab size.
else if (s.array[i]==' ') {
i++;
int toRemove = 1;
while (i<end && s.array[i]==' ' && toRemove<tabSize) {
i++;
toRemove++;
}
doc.remove(start, toRemove);
}
}
}
}
/*****************************************************************************/
/**
* Action for moving the caret to the "matching bracket" of the bracket
* at the caret position (either before or after).
*/
public static class GoToMatchingBracketAction
extends RecordableTextAction {
/**
*
*/
private static final long serialVersionUID = -2144358106223772276L;
public GoToMatchingBracketAction() {
super(rstaGoToMatchingBracketAction);
}
public GoToMatchingBracketAction(String name, Icon icon, String desc,
Integer mnemonic, KeyStroke accelerator) {
super(name, icon, desc, mnemonic, accelerator);
}
public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
RSyntaxTextArea rsta = (RSyntaxTextArea)textArea;
int pos = RSyntaxUtilities.getMatchingBracketPosition(rsta);
if (pos>-1) {
// Go to the position AFTER the bracket so the previous
// bracket (which we were just on) is highlighted.
rsta.setCaretPosition(pos+1);
}
else {
UIManager.getLookAndFeel().provideErrorFeedback(rsta);
}
}
public final String getMacroID() {
return rstaGoToMatchingBracketAction;
}
}
/*****************************************************************************/
/**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -