Search code examples
javaswingjtextarea

How can I replace a tab with four spaces in JTextArea?


I'm using a JTextArea in a JFrame. I would like the tab key to insert four spaces instead of a tab.

The method setTabSize does not work, as it puts a tab ('\t') in the contents of the text area.

How can I have JTextArea insert four spaces instead of a tab whenever I press the tab key? That way the getText() method will return indentations of four spaces for every tab.


Solution

  • I would avoid using KeyListeners (as a general rule with JTextComponents) and even Key Bindings, since while Key Bindings would work for keyboard input, it wouldn't work for copy-and-paste.

    In my mind, the best way is to use a DocumentFilter set on the JTextArea's Document (which is a PlainDocument, by the way). This way, even if you copy and paste text into the JTextAreas, one with tabs, then all the tabs will automatically be converted to 4 spaces on insertion.

    For example:

    import javax.swing.*;
    import javax.swing.text.AttributeSet;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.DocumentFilter;
    import javax.swing.text.PlainDocument;
    
    public class TestTextArea {
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> {
                JTextArea textArea = new JTextArea(20, 50);
                JScrollPane scrollPane = new JScrollPane(textArea);
    
                int spaceCount = 4;
                ((PlainDocument) textArea.getDocument()).setDocumentFilter(new ChangeTabToSpacesFilter(spaceCount));
    
                JFrame frame = new JFrame("GUI");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(scrollPane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            });
        }
        
        private static class ChangeTabToSpacesFilter extends DocumentFilter {
            private int spaceCount;
            private String spaces = "";
            
            public ChangeTabToSpacesFilter(int spaceCount) {
                this.spaceCount = spaceCount;
                for (int i = 0; i < spaceCount; i++) {
                    spaces += " ";
                }
            }
    
            @Override
            public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
                    throws BadLocationException {
                string = string.replace("\t", spaces);
                super.insertString(fb, offset, string, attr);
            }
            
            @Override
            public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
                super.remove(fb, offset, length);
            }
            
            @Override
            public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
                    throws BadLocationException {
                text = text.replace("\t", spaces);
                super.replace(fb, offset, length, text, attrs);
            }
            
        }
    
    }
    

    So now, even if I copy and paste a document with tabs within it, into the JTextArea, all tabs will be automatically replaced with spaceCount spaces.