Using Actions with DocumentListener
Asked Answered
K

3

7

I'm developing an application where I want something to be triggered both by the user updating the contents of a JTextArea, or manually via pressing a JButton.

I have done the first part using a DocumentListener and putting the relevant code in its insertUpdate method.

I haven't used Actions before, but I've heard they are useful for situations where you need something to be triggered by multiple controls. Is it possible to trigger the action from the DocumentListener? Is it a good idea to use Actions at all, or should I just put my code in a normal method?

(in the constructor):

    textAreaInput.getDocument().addDocumentListener(new DocumentListener() {
        public void insertUpdate(DocumentEvent e) {
            // do something
        }
        public void removeUpdate(DocumentEvent e) {}
        public void changedUpdate(DocumentEvent e) {}
    });

and the Action, which is a field:

Action doSomething = new AbstractAction("Do Something!") {
    @Override
    public void actionPerformed(ActionEvent e) {
        // do it
    }
};

clarification:

The JTextArea will receive text that is pasted in by the user, which I want to parse automatically. The parsing depends on other values set elsewhere in the GUI; if the user changes these other values, he may want to re-parse the text, hence the need to perform the same action by pressing a button.

Kakapo answered 7/9, 2011 at 1:2 Comment(0)
Q
3

You can invoke the actionPerformed() method, whether it's in an Action or not. There's an example here.

Queenhood answered 7/9, 2011 at 1:13 Comment(6)
The benefits of Action generally outweigh the slight marginal overhead, as mentioned here.Queenhood
You mean add something like doSomething.actionPerformed(new ActionEvent(???)) in the insertUpdate method?Kakapo
Yes; but, as @Ghent notes, you'll have to decide if it makes sense to to so. Why not just calla a common method?Queenhood
I was looking the the Java Tutorials where it says "An Action can be used to separate functionality and state from a component. For example, if you have two or more components that perform the same function, consider using an Action object to implement the function.". I have two components that need to implement the same function, so "why not call a common method" is one thing I'm asking!Kakapo
+1, a method is probably better than an Action for this case.Ghent
Yes, Action gives you a common method to respond to an ActionEvent, but the property maintenance is better handled as camickr and kleopatra suggest.Queenhood
G
4

I want something to be triggered both by the user updating the contents of a JTextArea, or manually via pressing a JButton.

This doesn't make sense to me.

Why would clicking a button invoke the same Action as a user typing text into a text area?

I haven't used Actions before, but I've heard they are useful for situations where you need something to be triggered by multiple controls

That statement is meant for controls that the user clicks, like JMenuItems, JButtons or hitting Enter on a text field. In general they can be used when you use an ActionListner.

A DocumentListener is not an ActionListener so as I stated earlier the use of an Action doesn't seem appropriate.

I think you need to clarify your requirement.

Edit, based on clarification

if the user changes these other values, he may want to re-parse the text

Why does the user have a choice? If you change the font, text, foreground, background of a text area, the component it automatically repainting, you don't have to ask for this to be done. If you look at the code for these methods they always end up invoking the revalidate() and repaint() methods.

The parsing depends on other values set elsewhere in the GUI;

Sounds like you need a custom class. Maybe a ParsedTextArea or ParsedDocument. This class would contain the "properties" that can be set elsewhere in the GUI. It would implmenent the DocumentListener. It would also support your "parseTheText" method. So whenever a property is changed or a DocumentEvent is generated you automatically invoked the "parseTheText" method. This way you don't need a separate button and the component will always be in sync because the parsing is automatic.

Ghent answered 7/9, 2011 at 1:22 Comment(3)
I've added some clarification on my requirement above. I might be misunderstanding what Actions are for, so perhaps you're right and they're not appropriate here.Kakapo
nice outline :-) nitpicking only - except that most probably the custom class wouldn't implement a DocumentListener (but use one) nor extend Document/TextComponentEjectment
Interesting idea re: subclassing of the JTextArea. I'm a bit reluctant to do this in this particular case because a) it adds complexity and b) I think from an OO point of view it would make sense to have the DocumentListener in a Controller class separate from the JTextArea (the view), and since the output text isn't a function of the input alone, it belongs in a separate model class (which is then displayed in a separate text area). Also, parsing is potentially an expensive operation, so I want to delay this until the user has made all the settings changes.Kakapo
Q
3

You can invoke the actionPerformed() method, whether it's in an Action or not. There's an example here.

Queenhood answered 7/9, 2011 at 1:13 Comment(6)
The benefits of Action generally outweigh the slight marginal overhead, as mentioned here.Queenhood
You mean add something like doSomething.actionPerformed(new ActionEvent(???)) in the insertUpdate method?Kakapo
Yes; but, as @Ghent notes, you'll have to decide if it makes sense to to so. Why not just calla a common method?Queenhood
I was looking the the Java Tutorials where it says "An Action can be used to separate functionality and state from a component. For example, if you have two or more components that perform the same function, consider using an Action object to implement the function.". I have two components that need to implement the same function, so "why not call a common method" is one thing I'm asking!Kakapo
+1, a method is probably better than an Action for this case.Ghent
Yes, Action gives you a common method to respond to an ActionEvent, but the property maintenance is better handled as camickr and kleopatra suggest.Queenhood
H
2

I think you need not create the Action object. You can add ActionListener to the Button just like you have added DocumentListener to the Document of the input. If I correctly understand your problem, may be you should do something like this:

textInput.getDocument().addDocumentListener(new DocumentListener(){             
    @Override
    public void insertUpdate(DocumentEvent e) {
        doIt();
    }               
    @Override
    public void removeUpdate(DocumentEvent e) {}                
    @Override
    public void changedUpdate(DocumentEvent e) {}
});

button.addActionListener(new ActionListener(){
    @Override
    public void actionPerformed(ActionEvent e) {
        doIt();
    }
});

doIt() is a method in which you will do what you wanted to do.

Hesse answered 7/9, 2011 at 3:17 Comment(3)
Expecting that some experts will add some recommendation on my post.Hesse
you get what you asked for :-) -1 for favouring ActionListener over Action (the rule is: Do use the highest abstractcion available, always) +1 for the common methodEjectment
@kleopatra: Thanks. I never used Action actually. I visited your profile and I agree that I got what I asked for :).Hesse

© 2022 - 2024 — McMap. All rights reserved.