Search code examples
javaswingjtextpane

Find Removed text in JTextPane


I am learning about DocumentListener in java with JTextPane. I'm trying to get the current string or
word (text) which is inserted/removed recently in textpane. I've tried this:

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.text.*;

class ChangeEffect implements DocumentListener {
    @Override
    public void insertUpdate(DocumentEvent de) {
        int offset = de.getOffset();
        try {
              String str = de.getDocument().getText(offset,de.getLength());
              System.out.println("You entered the text: " + str);
        }catch(Exception e){e.printStackTrace();}
    }
    @Override
    public void changedUpdate(DocumentEvent de) {
    }
    @Override
    public void removeUpdate(DocumentEvent de) {
        try {
            String s  = de.getDocument().getText(de.getOffset(),de.getLength());
           System.out.println("Removed text is: " +s);
        }catch(Exception e){}
    }
}


public class MultiModeColor {
    private static void displayGUI() {
        JFrame frame = new JFrame("Demo");
        JTextPane text = new JTextPane();
        frame.add(text);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setVisible(true);
        text.getDocument().addDocumentListener(new ChangeEffect());
    }

    public static void main(String[] arg) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                displayGUI();
            }
        };
        SwingUtilities.invokeLater(r);
    }
}

In above example i got the recently inserted text but removed text display nothing(except message in println statement).
Is there any way to get the recently removed text from document?


Solution

  • Try using a DocumentFilter instead of DocumentListener. The main problem you're facing is the DocumentListener is being notified after the fact, meaning by the time you're listener is notified, the text has already being removed.

    The DocumentFilter on the other gets notified first...

    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    import javax.swing.JFrame;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    import javax.swing.text.AbstractDocument;
    import javax.swing.text.AttributeSet;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.DocumentFilter;
    
    public class DocumentMonitor {
    
        public static void main(String[] args) {
            new DocumentMonitor();
        }
    
        public DocumentMonitor() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    JTextArea ta = new JTextArea(20, 20);
                    ((AbstractDocument) ta.getDocument()).setDocumentFilter(new DocumentFilter() {
    
                        @Override
                        public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
                            if (offset >= fb.getDocument().getLength()) {
                                System.out.println("Added: " + text);
                            } else {
                                String old = fb.getDocument().getText(offset, length);
                                System.out.println("Replaced " + old + " with " + text);
                            }
                            super.replace(fb, offset, length, text, attrs);
                        }
    
                        @Override
                        public void insertString(DocumentFilter.FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {
                            System.out.println("Added: " + text);
                            super.insertString(fb, offset, text, attr);
                        }
    
                        @Override
                        public void remove(DocumentFilter.FilterBypass fb, int offset, int length) throws BadLocationException {
                            System.out.println("Removed: " + fb.getDocument().getText(offset, length));
                            super.remove(fb, offset, length);
                        }
    
                    });
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new JScrollPane(ta));
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    }
    

    Yes, this is using a JTextArea, but the process is the same for a JTextPane...