1

So I have a JTextPane that basically serves as a console. I have it in the center field of a JFrame and a JTextField in the south field. The JTextField will take the text it has, and add it to the JTextPane when the user presses enter. In order to make the JTextPane not editable by the user I had to setFocusable(false), because using setEditable(false) stopped any text from appearing on the JTextPane. But while I don't want the user to edit the pane, I would still like the user to be able to highlight text in the pane, but I can't seem to find a way to do this.

Below is a sample demonstrating what I mean

Sample

package resources;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.text.AttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;

public class SampeTextPane extends JFrame
{
    public SampeTextPane()
    {
        setPreferredSize(new Dimension(350, 200));
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        JTextPane display = new JTextPane();
        display.setBorder(new EmptyBorder(5, 5, 5, 5));
        display.setMargin(new Insets(5, 5, 5, 5));
        display.setFocusable(false);
        appendToPane(display, "Example", Color.BLUE);

        JTextField field = new JTextField();
        field.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                appendToPane(display, field.getText(), Color.BLACK);
                field.setText("");
            }
        });

        add(display, BorderLayout.CENTER);
        add(field, BorderLayout.SOUTH);

        pack();
        setVisible(true);
    }

    private void appendToPane(JTextPane pane, String text, Color color)
    {
        StyleContext sc = StyleContext.getDefaultStyleContext();
        AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, color);

        aset = sc.addAttribute(aset, StyleConstants.Alignment, StyleConstants.ALIGN_JUSTIFIED);

        int len = pane.getDocument().getLength();
        pane.setCaretPosition(len);
        pane.setCharacterAttributes(aset, false);
        pane.replaceSelection(text + "\n");
    }

    public static void main(String[] args)
    {
        new SampeTextPane();
    }
}

Thanks in advance for any help.

Ryan
  • 727
  • 2
  • 14
  • 31

2 Answers2

3

using setEditable(false) stopped any text from appearing on the JTextPane.

You can make the JTextPane uneditable but you can't update the text via the text pane.

Instead you can update the text via the Document:

//int len = pane.getDocument().getLength();
//pane.setCaretPosition(len);
//pane.setCharacterAttributes(aset, false);
//pane.replaceSelection(text + "\n");

try
{
    StyledDocument doc = pane.getStyledDocument();
    doc.insertString(doc.getLength(), text, aset);
}
catch(BadLocationException e) { System.out.println(e); }
camickr
  • 321,443
  • 19
  • 166
  • 288
3

Another option, albeit more convoluted, is to use a boolean flag to allow or disallow changes to the document, something suggested StanislavL here.

In your situation, it could look something like:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.DocumentFilter;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;

@SuppressWarnings("serial")
public class SampeTextPane extends JFrame {
    private boolean isApi = false;

    public SampeTextPane() {
        setPreferredSize(new Dimension(350, 200));
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        JTextPane display = new JTextPane();

        ((DefaultStyledDocument) display.getDocument()).setDocumentFilter(new DocFilter());

        display.setBorder(new EmptyBorder(5, 5, 5, 5));
        display.setMargin(new Insets(5, 5, 5, 5));
        // !! display.setFocusable(false);
        appendToPane(display, "Example", Color.BLUE);

        JTextField field = new JTextField();
        field.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                appendToPane(display, field.getText(), Color.BLACK);
                field.setText("");
            }
        });

        add(display, BorderLayout.CENTER);
        add(field, BorderLayout.SOUTH);

        pack();
        setVisible(true);
    }

    private void appendToPane(JTextPane pane, String text, Color color) {
        isApi = true;

        StyleContext sc = StyleContext.getDefaultStyleContext();
        AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground,
                color);

        aset = sc.addAttribute(aset, StyleConstants.Alignment, StyleConstants.ALIGN_JUSTIFIED);
        int len = pane.getDocument().getLength();

        String selection = pane.getSelectedText();
        if (selection == null) {
            pane.setCaretPosition(len);
            text += "\n";
        }        
        pane.setCharacterAttributes(aset, false);
        pane.replaceSelection(text);

        isApi = false;
    }

    private class DocFilter extends DocumentFilter {
        @Override
        public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
                throws BadLocationException {
            if (isApi) {
                super.insertString(fb, offset, string, attr);
            }
        }

        @Override
        public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
            if (isApi) {
                super.remove(fb, offset, length);
            }
        }

        @Override
        public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
                throws BadLocationException {
            if (isApi) {
                super.replace(fb, offset, length, text, attrs);
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new SampeTextPane());
    }

}
Community
  • 1
  • 1
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Interesting way of going about it, and from what I can see it does about the same thing, but it slightly exceeds my Java knowledge. Thanks though! – Ryan Sep 30 '16 at 01:23
  • 1
    @Ryan: yes, Camickr's suggestion is **much** cleaner, and I'm glad that you went with that. I was creating my code before he posted his correct answer, and so I hated to not post my "efforts". I did up-vote his answer, of course. – Hovercraft Full Of Eels Sep 30 '16 at 01:29