Question

I use a JTextArea where using double click I can able to select the word at any place but I don't want to enable edit. Which means text can be entered only at the end of text area and not anywhere in between.

I have tried with mouse listeners like below:

@Override
public void mouseClicked(MouseEvent me) {
       if(SwingUtilities.isLeftMouseButton(me)){
        System.err.println("clicked");
        int pos = textArea.getCaretPosition();
        if(pos < textArea.getDocument().getLength()){
            textArea.setCaretPosition(textArea.getDocument().getLength());
        }
    }
   }

This makes double click not to select the word. I understand it is because caret position is moved to end. But how can I achieve this?

Was it helpful?

Solution

Check out the Protected Text Component which allows you to protect multiple areas of a Document from change.

Or if you don't need to be able to "select" any of the protected text than a simpler solution is to use a NavigationFilter:

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

public class NavigationFilterPrefixWithBackspace extends NavigationFilter
{
    private int prefixLength;
    private Action deletePrevious;

    public NavigationFilterPrefixWithBackspace(int prefixLength, JTextComponent component)
    {
        this.prefixLength = prefixLength;
        deletePrevious = component.getActionMap().get("delete-previous");
        component.getActionMap().put("delete-previous", new BackspaceAction());
        component.setCaretPosition(prefixLength);
    }

    @Override
    public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
    {
        fb.setDot(Math.max(dot, prefixLength), bias);
    }

    @Override
    public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
    {
        fb.moveDot(Math.max(dot, prefixLength), bias);
    }

    class BackspaceAction extends AbstractAction
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            JTextComponent component = (JTextComponent)e.getSource();

            if (component.getCaretPosition() > prefixLength)
            {
                deletePrevious.actionPerformed( null );
            }
        }
    }

    private static void createAndShowUI()
    {
        JTextField textField = new JTextField("Prefix_", 20);
        textField.setNavigationFilter( new NavigationFilterPrefixWithBackspace(7, textField) );

        JFrame frame = new JFrame("Navigation Filter Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(textField);
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible(true);
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}

OTHER TIPS

Okay, this is slightly hacky...

Basically what this does is installs a "protected" DocumentFilter, which will only allow input to put in from a certain point in the Document.

It overrides the JTextArea's insert-break key binding (Enter) and records a marker. The "protected" DocumentFilter then ensures that the content does not precede this point

I was forced to implement a KeyListener on the field to move the cursor to the end of the input, while the DocumentFilter was capable of handling this, it did present some issues with deleting and general usability. This also ensures that the selection is un-highlighted when new content is added...

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
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 Terminal {

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

    public Terminal() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JTextArea terminal = new JTextArea(20, 40);
                ProtectedDocumentFilter.install(terminal);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new JScrollPane(terminal));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static interface UserInput {

        public int getUserInputStart();

        public void setUserInputStart(int lastPoint);
    }

    public static class DefaultUserInput implements UserInput {

        private final JTextArea textArea;
        private int userInputStart;

        public DefaultUserInput(JTextArea ta) {
            textArea = ta;
            ActionMap am = ta.getActionMap();
            Action action = am.get("insert-break");
            am.put("insert-break", new ProxyAction(action));

            ta.addKeyListener(new KeyAdapter() {

                @Override
                public void keyPressed(KeyEvent e) {
                    if (textArea.getCaretPosition() != textArea.getDocument().getLength()) {
                        textArea.setCaretPosition(textArea.getDocument().getLength());
                    }
                }

            });
        }

        @Override
        public void setUserInputStart(int userInputStart) {
            this.userInputStart = userInputStart;
        }

        @Override
        public int getUserInputStart() {
            return userInputStart;
        }

        public class ProxyAction extends AbstractAction {

            private final Action proxy;

            public ProxyAction(Action proxy) {
                this.proxy = proxy;
            }

            @Override
            public void actionPerformed(ActionEvent e) {

                proxy.actionPerformed(e);

                int range = textArea.getCaretPosition() - userInputStart;
                userInputStart += range;

            }

        }

    }

    public static class ProtectedDocumentFilter extends DocumentFilter {

        protected static void install(JTextArea textArea) {
            UserInput ui = new DefaultUserInput(textArea);
            ((AbstractDocument) textArea.getDocument()).setDocumentFilter(new ProtectedDocumentFilter(ui));
        }

        private UserInput userInput;

        public ProtectedDocumentFilter(UserInput userInput) {
            this.userInput = userInput;
        }

        public UserInput getUserInput() {
            return userInput;
        }

        @Override
        public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
            if (offset < getUserInput().getUserInputStart()) {
                offset = fb.getDocument().getLength();
            }

            System.out.println("Insert");

            super.insertString(fb, offset, string, attr);
        }

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

        @Override
        public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
            if (offset < getUserInput().getUserInputStart()) {
                offset = fb.getDocument().getLength();
                length = 0;
            }
            super.replace(fb, offset, length, text, attrs);
            if (text.contains("\n")) {
                int lastPoint = offset + text.lastIndexOf("\n");
                if (lastPoint > getUserInput().getUserInputStart()) {
                    getUserInput().setUserInputStart(lastPoint + 1);
                }
            }
        }
    }

}

This is just an example, you're going to need to play with it and tweak it to meet your own needs....

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top