我有一个程序,它允许您通过按组合键来选择文本并添加 Html 标记。
我正在尝试对其进行修改,以便在发生任何更改时将突出显示应用于文档的相关部分。但是,在某些情况下,我会收到此错误:javax.swing.text.StateInvariantError: GlyphView: Stale view: javax.swing.text.BadLocationException: Length must be positive。
该错误可以在以下代码中看到。如果键入两行文本,突出显示第二行,按 ALT+1 添加标记,然后按 CTRL+Z 撤消它,程序崩溃。
在https://forums.oracle.com/thread/1486098有一个类似问题的参考。但是,发布的代码不完整,我无法弄清楚如何将其应用于我的问题。如果有人能告诉我哪里出错了,我将不胜感激!
package testprogram;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.undo.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.Color;
import java.awt.Font;
import java.io.*;
import java.util.*;
import java.util.logging.*;
import java.util.regex.*;
import javax.swing.UIManager;
import com.sun.java.swing.Painter;
import java.awt.Graphics2D;
public class XemitTest extends javax.swing.JFrame {
private TextClass TextArea;
private javax.swing.JMenuBar jMenuBar;
private javax.swing.JMenu jMenu;
private javax.swing.JMenuItem jMenuItem1;
private javax.swing.JMenuItem jMenuItem2;
private javax.swing.JMenuItem jMenuItem3;
String txtstr = "";
public XemitTest() {
setSize(800, 600);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jMenuBar = new javax.swing.JMenuBar();
jMenu = new javax.swing.JMenu("Menu");
jMenuItem1 = new javax.swing.JMenuItem();
jMenuItem1.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_1, java.awt.event.InputEvent.ALT_MASK));
jMenuItem1.setText("<h1>");
jMenuItem1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
addInlineTags("<h1>", "</h1>");
}
});
jMenu.add(jMenuItem1);
jMenuItem2 = new javax.swing.JMenuItem();
jMenuItem2.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, java.awt.event.InputEvent.CTRL_MASK));
jMenuItem2.setText("Undo");
jMenuItem2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
actionUndo();
if (TextArea.syntaxHighlighting)
highlightSyntax();
}
});
jMenu.add(jMenuItem2);
jMenuItem3 = new javax.swing.JMenuItem();
jMenuItem3.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, java.awt.event.InputEvent.CTRL_MASK | java.awt.event.InputEvent.SHIFT_MASK));
jMenuItem3.setText("Redo");
jMenuItem3.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
actionRedo();
if (TextArea.syntaxHighlighting)
highlightSyntax();
}
});
jMenu.add(jMenuItem3);
jMenuBar.add(jMenu);
setJMenuBar(jMenuBar);
JEditorPane EditorPane = new TextClass();
TextArea = (TextClass)EditorPane;
final UndoableEditListener listener = new EditListener();
TextArea.getDocument().addUndoableEditListener(listener);
TextArea.undo.setLimit(500);
TextArea.addKeyListener(
new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (
e.getKeyCode() == java.awt.event.KeyEvent.VK_TAB ||
e.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER ||
e.getKeyCode() == java.awt.event.KeyEvent.VK_SPACE ||
e.getKeyCode() == java.awt.event.KeyEvent.VK_BACK_SPACE ||
e.getKeyCode() == java.awt.event.KeyEvent.VK_DELETE
)
TextArea.keyValue = true;
else
TextArea.keyValue = false;
}
});
getContentPane().add(TextArea, BorderLayout.CENTER);
}
public class TextClass extends javax.swing.JTextPane {
UndoManager undo = new UndoManager();
public UndoableEditListener listener = null;
CompoundEdit compoundEdit;
boolean syntaxHighlighting = true, enableEdit = true, hasChanged = false, keyValue = false, lastKeyValue = false, forceCompoundEdit = false, editForced = false;
int selstart = 0, selend = 0, strlen = 0, txtoffset = 0, txtlen = 0, txtoffsetChange = 0, txtlenChange = 0;
}
public class EditListener implements UndoableEditListener {
@Override
public void undoableEditHappened(final UndoableEditEvent e) {
if (TextArea.enableEdit) {
if (TextArea.compoundEdit == null) {
TextArea.compoundEdit = new CompoundEdit();
TextArea.undo.addEdit(TextArea.compoundEdit);
}
TextArea.txtoffsetChange = TextArea.getCaretPosition() - TextArea.txtoffset;
TextArea.txtlenChange = TextArea.getDocument().getLength() - TextArea.txtlen;
if (TextArea.forceCompoundEdit) {
if (!TextArea.editForced) {
if (TextArea.compoundEdit != null)
TextArea.compoundEdit.end();
TextArea.compoundEdit = new CompoundEdit();
TextArea.undo.addEdit(TextArea.compoundEdit);
TextArea.editForced = true;
}
TextArea.compoundEdit.addEdit(e.getEdit());
}
else {
if (TextArea.txtoffsetChange == TextArea.txtlenChange && Math.abs(TextArea.txtoffsetChange) == 1) {
if (TextArea.keyValue) {
if (TextArea.lastKeyValue) {
TextArea.compoundEdit.addEdit(e.getEdit());
}
else {
if (TextArea.compoundEdit != null)
TextArea.compoundEdit.end();
TextArea.compoundEdit = new CompoundEdit();
TextArea.undo.addEdit(TextArea.compoundEdit);
TextArea.compoundEdit.addEdit(e.getEdit());
}
}
else {
if (!TextArea.lastKeyValue) {
TextArea.compoundEdit.addEdit(e.getEdit());
}
else {
if (TextArea.compoundEdit != null)
TextArea.compoundEdit.end();
TextArea.compoundEdit = new CompoundEdit();
TextArea.undo.addEdit(TextArea.compoundEdit);
TextArea.compoundEdit.addEdit(e.getEdit());
}
}
}
else {
TextArea.compoundEdit.end();
TextArea.compoundEdit = new CompoundEdit();
TextArea.undo.addEdit(TextArea.compoundEdit);
TextArea.compoundEdit.addEdit(e.getEdit());
}
}
TextArea.lastKeyValue = TextArea.keyValue;
TextArea.hasChanged = true;
TextArea.txtoffset = TextArea.getCaretPosition();
TextArea.txtlen = TextArea.getDocument().getLength();
if (TextArea.syntaxHighlighting)
highlightSyntax();
}
}
}
private void highlightSyntax() {
TextArea.enableEdit = false;
try {
int offset = TextArea.getCaretPosition();
int line = TextArea.getDocument().getDefaultRootElement().getElementIndex(offset);
int start = TextArea.getDocument().getDefaultRootElement().getElement(line).getStartOffset();
if (start < 0)
start = 0;
if (line > 0) {
start = TextArea.getDocument().getDefaultRootElement().getElement(line - 1).getStartOffset();
if (start < 0)
start = 0;
}
int end = TextArea.getDocument().getDefaultRootElement().getElement(line).getEndOffset();
if (end > TextArea.getDocument().getLength())
end = TextArea.getDocument().getLength();
int length = end - start;
if (length > 0) {
StyledDocument document = (StyledDocument) TextArea.getDocument();
Style styleDefault = document.addStyle("Default", null);
StyleConstants.setForeground(styleDefault, Color.BLACK);
Style styleHighlight = document.addStyle("Highlight", null);
StyleConstants.setForeground(styleHighlight, new Color(255, 0, 255));
String highlightstr = document.getText(start, length);
Pattern re = Pattern.compile("<[^<>\n]+>");
Matcher matcher = re.matcher(highlightstr);
document.setCharacterAttributes(start, end - start, TextArea.getStyle("Default"), true);
while (matcher.find()) {
document.setCharacterAttributes(start + matcher.start(), matcher.end() - matcher.start(), TextArea.getStyle("Highlight"), true);
}
}
}
catch (BadLocationException e) {
JOptionPane.showMessageDialog(null, "Bad Location Exception");
}
TextArea.enableEdit = true;
TextArea.requestFocusInWindow();
}
private void actionUndo() {
if (TextArea.compoundEdit != null)
TextArea.compoundEdit.end();
if (TextArea.undo.canUndo()) {
try {
TextArea.undo.undo();
}
catch (Exception ex) {
JOptionPane.showMessageDialog(null, "Action cannot be undone");
}
}
else {
JOptionPane.showMessageDialog(null, "No actions to undo");
}
}
private void actionRedo() {
if (TextArea.compoundEdit != null)
TextArea.compoundEdit.end();
if (TextArea.undo.canRedo()) {
try {
TextArea.undo.redo();
}
catch (Exception ex) {
JOptionPane.showMessageDialog(null, "Action cannot be redone");
}
}
else {
JOptionPane.showMessageDialog(null, "No actions to redo");
}
}
private void addInlineTags(String starttag, String endtag) {
TextArea.forceCompoundEdit = true;
getSel();
txtstr = starttag + txtstr + endtag;
setSel();
TextArea.forceCompoundEdit = false;
TextArea.editForced = false;
}
private void getSel() {
TextArea.selstart = TextArea.getSelectionStart();
TextArea.selend = TextArea.getSelectionEnd();
txtstr = (TextArea.getSelectedText() != null) ? TextArea.getSelectedText(): "";
}
private void setSel() {
TextArea.replaceSelection(txtstr);
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
XemitTest Xemit = new XemitTest();
Xemit.setVisible(true);
}
});
}
}