0

Basically, I have a dropdown menu containing templates. For example:

apple(     )
banana(     )

Once one of them is selected, it pastes onto a JTextArea. My problem is if "apple( )" is selected, I want "apple" and the two brackets non-deletable in the TextArea, and user can enter anything inside the brackets.

Can anyone give me any direction/ideas here? I have been searching on the internet and found very little about this.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
user11998
  • 177
  • 3
  • 6
  • 13
  • putting some code will be helpful – Esqarrouth Jan 23 '14 at 20:07
  • I can sort of see a DocumentFilter working for this, but it would seem to me to be a bit kludgy. Must it be a JTextArea? Would a single column JTable work as well? That would seem a little easier to me. The menu would add a new row, and the table cell editor would only allow edits between the parenthesis, possibly here also via a DocumentFilter. Your posting a [minimal example](http://stackoverflow.com/help/mcve) would greatly help us help you better and quicker. – Hovercraft Full Of Eels Jan 23 '14 at 20:07
  • @HovercraftFullOfEels Hi, it has to be in a JTextArea sadly. – user11998 Jan 23 '14 at 20:10
  • *"it has to be in a JTextArea"* Why? – Andrew Thompson Jan 23 '14 at 20:10
  • @user3080993 Why does it need to be in a JTextArea? – Dodd10x Jan 23 '14 at 20:11
  • How about two JLabel with the JTextArea in between? I see where you are going, but your approach is very complicated. – Steve11235 Jan 23 '14 at 20:14
  • It is part of my project. When I select something from dropdown menu, it also appears on TextArea. – user11998 Jan 23 '14 at 20:15
  • Your restrictions still don't make sense. You're so far just saying "it has to be a JTextArea because that's what I selected". Please give a decent rationale. – Hovercraft Full Of Eels Jan 23 '14 at 20:16
  • It has to have a JTextArea because the user can select additional templates from the dropdown menu. Like a database schema – user11998 Jan 23 '14 at 20:23
  • That's not a good reason. You can implement the same functionality easier if you use a different mechanism. – Dodd10x Jan 23 '14 at 20:28
  • Really? What mechanism are you thinking? – user11998 Jan 23 '14 at 20:31

2 Answers2

2

Check out the Proctected Text Component. It allows you to mark individual pieces of text as protected so that it can't be changed or deleted.

It uses a DocumentFilter as well as a NavigationFilter.

For a simpler solution you might be able to just use a NavigationFilter. The example below shows how you can prevent the selection of text at the beginning of the Document. You should be able to customize it to also prevent selection of text at the end of the document as well.

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();
            }
        });
    }
}
camickr
  • 321,443
  • 19
  • 166
  • 288
  • Why don't you properly use [Swing's EDT](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html)? Proper threading standards should be encouraged. – takendarkk Jan 23 '14 at 22:57
  • @csmckelvey, good point :) It was old code that I've had lying around for a decade (before the recommendation to use invokeLater) and I didn't notice the code hadn't been updated to the new standards. – camickr Jan 23 '14 at 23:10
-2

You'll have to do this yourself. I'd suggest you to make an event handler that fires every time the text changes (Click here to find out how). And inside that handler check if the JTextArea still starts with "apple(" and ends with ")".

Community
  • 1
  • 1
Jojo
  • 278
  • 1
  • 10
  • 1
    Your link is to a question where my answer about use of a DocumentListener was the accepted answer, but I would not recommend that solution for this problem. The DocumentListener creates events *after* document changes have occurred, and the above problem needs to prevent document changes before they occur. Again, a DocumentFilter would likely be better. – Hovercraft Full Of Eels Jan 23 '14 at 20:18