How to set tab size in JTextPane

622 Views Asked by At

I'm working on creating a text editor written in Java, and I've decided to use a JTextPane instead of JTextArea because it will enable me to create a rich text edit using different color schemes. However JTextPane does not have a setTabSize(int size) function, but I'm able to use TabStops and a TabSet to achieve this.

My first attempt

    import java.awt.Dimension;
    import java.awt.Font;
    import java.awt.FontMetrics;

    import javax.swing.*;
    import javax.swing.text.AttributeSet;
    import javax.swing.text.SimpleAttributeSet;
    import javax.swing.text.StyleConstants;
    import javax.swing.text.StyleContext;
    import javax.swing.text.TabSet;
    import javax.swing.text.TabStop;

    public class Editor extends JFrame {
        JTextPane txt;
        public Editor() {
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            RichTextEdit rte = new RichTextEdit();
            txt = new JTextPane(rte);
            txt.setPreferredSize(new Dimension(400,200));
            add(new JScrollPane(txt));

            Font f = new Font("Monospaced", Font.PLAIN, txt.getFont().getSize());
            txt.setFont(f);
            FontMetrics fm = txt.getFontMetrics(f);
            int width = fm.stringWidth("w");
            int tabSize = 4;

            TabStop[] tabs = new TabStop[1];
            tabs[0] = new TabStop(width*tabSize, TabStop.ALIGN_LEFT, TabStop.LEAD_NONE);
            TabSet tabset = new TabSet(tabs);

            StyleContext cont = StyleContext.getDefaultStyleContext();
            AttributeSet a = cont.addAttribute(SimpleAttributeSet.EMPTY, 
StyleConstants.TabSet, tabset);
            txt.setParagraphAttributes(a, false);

            this.pack();
            this.setLocationRelativeTo(null);
            this.setVisible(true);
        }
    }

This attempt works only when the cursor is at the beginning of the document. If i start typing something in the JTextPane then press tab, it only moves up to the 4th position. So, if i type two letters, the caret only moves over two chars on tab press. Then after that, it only moves one character over each press.

My second attempt: All I did was add another tab and it worked if i pressed tab twice on a line as long as no other characters are on that line. Basically the same as my first attempt except, it allowed for two tab pressed (to equal 8 chars long)

TabStop[] tabs = new TabStop[2];
tabs[0] = new TabStop(width * tabSize, TabStop.ALIGN_LEFT, TabStop.LEAD_NONE);
tabs[1] = new TabStop(width * tabSize * 2, TabStop.ALIGN_LEFT, TabStop.LEAD_NONE);

I had to multiply the second tab by 2 to get the tab space to move over another 4 characters long.

My last attempt was to be able to get the amount of characters on a given line. (I've accomplished) Then to add 4 characters to that line. So, if the user has typed: 123456 into the JTextPane and pressed tab, it will just add 4 spaces to the end, but I keep getting an error when trying to setParagraphAttributes after the initial setup. So my thought is that it already has the AttributeSet, then to either change the value or remove the AttributeSet, then add it with the updated TabSize. This is what I have so far:

import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.util.Enumeration;

import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.TabSet;
import javax.swing.text.TabStop;
import javax.swing.text.Utilities;

public class Editor extends JFrame {
    JTextPane txt;
    public Editor() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        RichTextEdit rte = new RichTextEdit();
        txt = new JTextPane(rte);
        txt.setPreferredSize(new Dimension(400,200));
        add(new JScrollPane(txt));
        txt.setFont(new Font("Monospaced", Font.PLAIN, 16));

        txt.addCaretListener(new CaretListener() {

            @Override
            public void caretUpdate(CaretEvent arg0) {
                // TODO Auto-generated method stub
                Update();
            }

        });

        Update();

        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }

    public void Update() {

        int caretPosition = txt.getCaretPosition();
        Element root = txt.getDocument().getDefaultRootElement();
        int n = root.getElementIndex(caretPosition) + 1;
        Element child = root.getElement(n-1);
        int start = child.getStartOffset();
        int end = child.getEndOffset();
        int length = end - start;
        int j = (length >0) ? length - 1 : 0;
        System.out.println(n);
        System.out.println(caretPosition);
        System.out.println(j);
        System.out.println("----------");

        Font f = new Font("Monospaced", Font.PLAIN, txt.getFont().getSize());
        FontMetrics fm = txt.getFontMetrics(f);
        int width = fm.stringWidth("w");
        int tabSize = (j > 0) ? j + 4 : 4;
        TabStop[] tabs = new TabStop[1];
        tabs[0] = new TabStop(width * tabSize, TabStop.ALIGN_LEFT, TabStop.LEAD_NONE);
        TabSet tabset = new TabSet(tabs);

        StyleContext cont = StyleContext.getDefaultStyleContext();
        AttributeSet a = cont.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.TabSet, tabset);
        txt.setParagraphAttributes(a, true);
    }
}

I'm getting a IllegalStateException at this line txt.setParagraphAttributes(a,true); when I call the update function from the caretListener

0

There are 0 best solutions below