0

我有一个程序,它允许您通过按组合键来选择文本并添加 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);
            }
        });
    }
}
4

1 回答 1

0

我终于想出了解决这个问题的方法......

package test;

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.util.regex.*;

public class test extends javax.swing.JFrame {

    private static 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 = "";
    static Pattern re;

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                test Xemit = new test();
                Xemit.setVisible(true);
            }
        });
    }

    public test() {

    // Create GUI

        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();
            }
        });
        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();
            }
        });
        jMenu.add(jMenuItem3);

        jMenuBar.add(jMenu);
        setJMenuBar(jMenuBar);

        JEditorPane EditorPane = new TextClass();
        TextArea = (TextClass)EditorPane;

        final Document document = TextArea.getDocument();
        final DocumentListener doclistener = new DocumentClass();
        document.addDocumentListener(doclistener);

        final UndoableEditListener editlistener = new EditClass();
        TextArea.getDocument().addUndoableEditListener(editlistener);
        TextArea.undo.setLimit(500);

        TextArea.compoundEdit = new CompoundEdit();
        TextArea.undo.addEdit(TextArea.compoundEdit);

        final KeyListener keylistener = new KeyClass();
        TextArea.addKeyListener(keylistener);

        final CaretListener caretlistener = new CaretClass();
        TextArea.addCaretListener(caretlistener);

        getContentPane().add(TextArea, BorderLayout.CENTER);
    }

// Classes

public class TextClass extends javax.swing.JTextPane {
    UndoManager undo = new UndoManager();
        CompoundEdit compoundEdit;
        boolean enableedit = true, newedit = false, haschanged = false, newkeytype = false, lastkeytype = false, enablehighlight = true, highlight = false, fileopening = false;
        int strlen = 0, txtlen = 0, lasttxtlen = 0, caretpos = 0, lastcaretpos = 0, selstart = 0, selend = 0, highlightstart = 0, highlightend = 0, swap = 0;
        String filepath = "", filename = "", tempstr = "";

        public void selectAll() {
        selstart = 0;
        selend = getDocument().getLength();
        setSelectionStart(selstart);
        setSelectionEnd(selend);
        txtstr = getSelectedText();
        }

        public void cut() {
            this.newedit = true;
            super.cut();
        }

        public void paste() {
            this.newedit = true;
            txtstr = txtstr.replace("\t", "    ");
            super.paste();
        }
}

public class DocumentClass implements DocumentListener {
    @Override
    public void changedUpdate(DocumentEvent e) {
    }
    @Override
    public void insertUpdate(DocumentEvent e) {
    if (TextArea.enablehighlight) {
        TextArea.highlightstart = TextArea.getCaretPosition();
        HighlightClass.highlightSyntax();
        }
        }
        @Override
        public void removeUpdate(DocumentEvent e) {
        if (TextArea.enablehighlight) {
            TextArea.highlightstart = TextArea.getCaretPosition();
            HighlightClass.highlightSyntax();
        }
        }
    }

    public class EditClass implements UndoableEditListener {
        @Override
        public void undoableEditHappened(final UndoableEditEvent e) {
            if (TextArea.enableedit) {
                if (! TextArea.highlight) {
                    if (TextArea.newedit) {
                        if (TextArea.compoundEdit.isInProgress())
                            TextArea.compoundEdit.end();
                        TextArea.compoundEdit = new CompoundEdit();
                        TextArea.undo.addEdit(TextArea.compoundEdit);
                    } else if (! TextArea.compoundEdit.isInProgress()) {
                        TextArea.compoundEdit = new CompoundEdit();
                        TextArea.undo.addEdit(TextArea.compoundEdit);
                    }
                TextArea.haschanged = true;
                }
                TextArea.compoundEdit.addEdit(e.getEdit());
                TextArea.newedit = false;
            }
        }
    }

    public class KeyClass implements KeyListener {
        @Override
        public void keyPressed(KeyEvent e) {
            TextArea.caretpos = TextArea.getCaretPosition();
            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.newkeytype = true;
            } else {
                TextArea.newkeytype = false;
            }
            if (TextArea.newkeytype != TextArea.lastkeytype || Math.abs(TextArea.caretpos - TextArea.lastcaretpos) > 1)
                TextArea.newedit = true;
            TextArea.lastkeytype = TextArea.newkeytype;
            TextArea.lastcaretpos = TextArea.caretpos;
        }
        @Override
        public void keyReleased(KeyEvent e) {
        }
        @Override
        public void keyTyped(KeyEvent e) {
        }
    }

    public class CaretClass implements CaretListener {
        public void caretUpdate(CaretEvent e) {
            TextArea.setCaretColor(Color.BLACK);
        }
    }

    public static class HighlightClass extends DefaultStyledDocument {
        public static void highlightSyntax() {
            java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                TextArea.highlight = true;
                if (! TextArea.fileopening)
                    TextArea.highlightend = TextArea.getCaretPosition();
                if (TextArea.highlightstart > TextArea.highlightend) {
                    TextArea.swap = TextArea.highlightend;
                    TextArea.highlightstart = TextArea.highlightend;
                    TextArea.highlightend = TextArea.swap;
                }
                    int startpos = TextArea.highlightstart;
                    int endpos = TextArea.highlightend;
                    int startline = TextArea.getDocument().getDefaultRootElement().getElementIndex(startpos);
                    int start = TextArea.getDocument().getDefaultRootElement().getElement(startline).getStartOffset();
                    int endline = TextArea.getDocument().getDefaultRootElement().getElementIndex(endpos);
                    int end = TextArea.getDocument().getDefaultRootElement().getElement(endline).getEndOffset();
                    int length = Math.abs(end - start);
                    try {
                        StyledDocument document = (StyledDocument) TextArea.getDocument();
                        Style styleDefault = document.addStyle("Default", null);
                        StyleConstants.setForeground(styleDefault, new Color(0, 0, 0));
                        document.setCharacterAttributes(start, length, TextArea.getStyle("Default"), true);
                        Style styleHighlight = document.addStyle("Highlight", null);
                        StyleConstants.setForeground(styleHighlight, new Color(255, 0, 255));
                        String highlightstr = document.getText(start, length);
        try {
            re = Pattern.compile("<[a-z/][^<>\n]*>");
            } catch(PatternSyntaxException ex) {
                JOptionPane.showMessageDialog(null, "Search string cannot be compiled.");
                return;
                }
                Matcher matcher = re.matcher(highlightstr);
                while (matcher.find()) {
                document.setCharacterAttributes(start + matcher.start(), matcher.end() - matcher.start(), TextArea.getStyle("Highlight"), true);
                }
                } catch (BadLocationException ex) {
                }
                TextArea.fileopening = false;
                TextArea.highlight = false;
                }
            });
        }
    }

    // Methods

    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) {
        getSel();
        if (txtstr.matches("\\A\\s*\\Z")) {
            txtstr = starttag + endtag;
            setSel();
        } else {
            txtstr = txtstr
                .replaceAll("\\A(\\s*)(\\S)", "$1" + starttag + "$2")
                .replaceAll("(\\S)(\\s*)\\Z", "$1" + endtag + "$2");
            setSel();
        }
    }

    private void getSel() {
        TextArea.selstart = TextArea.getSelectionStart();
        TextArea.selend = TextArea.getSelectionEnd();
        txtstr = (TextArea.getSelectedText() != null) ? TextArea.getSelectedText(): "";
        TextArea.strlen = txtstr.length();
    }

    private void setSel() {
        TextArea.selstart = TextArea.getSelectionStart();
        TextArea.selend = TextArea.selstart + txtstr.length();
        TextArea.newedit = true;
        TextArea.replaceSelection(txtstr);
    }
}
于 2014-05-11T10:47:07.050 回答