Using Actions with DocumentListener - java

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.

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.

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

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.

Related

JTextField respond to key event AFTER the text is updated

Right now, when I add a KeyListener to a JTextField, I get an event, then the text updates. But what I need is for the KeyListener to respond after the text is updated. How would I go about doing that? Right now, I am setting a 10-millisecond delay to the response of the KeyListener in another thread, which is sufficient for the text to update and the user to not notice.
Don't use a KeyListener. Swing has newer and better API's than AWT.
Instead you should be adding a DocumentListener to the Document of the JTextfield
A DocumentEvent is generated whenever the Document is updated.
Read the section from the Swing tutorial on How to Write a DocumentListener for more information and examples.
So.... ummmm i know it's kind'a late X)
I kind'a gone around that by using the keyReleased method I noticed that the text gets updated before the key event it should give you something like this
JTextField jtf = new JTextField();
jtf.addKeyaddKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
// not here
}
#Override
public void keyReleased(KeyEvent e) {
// not here
}
#Override
public void keyPressed(KeyEvent e) {
//do the stuff here
}
});
note that I am not sure why it works but I would think that it has something to do with the typing speed or something, also I am not an expert but i wanted to help (that problem drove me crazy for a couple days) if i am saying any stupid stuff pleas let me know !

java Swing how consume document event

I want consume a DocumentEvent captured into insertUpdate method of a DocumentListener
I don't see any way to prevent as a KeyEvent (e.consume()).
I don't want use the key listener because can't prevent the clipboard events (Copy Paste).
How I work with this events?
How I can raise Document events since cose?
Isbn13TextField.getDocument().addDocumentListener(new DocumentListener(){
public void insertUpdate(DocumentEvent e) {
e.consume(); //Not Exists, How consume a copy paste?
}
public void removeUpdate(DocumentEvent e) {}
public void changedUpdate(DocumentEvent e) {}
});
Depending on what it is you are trying to achieve...
You could...
Use a DocumentFilter which will allow you to filter input before it reaches the underlying Document. This is used by the Document itself and therefore is not depended on how the content is being added/removed from the Document but the Document itself.
Take a look at Text Component Features and Implementing a Document Filter in particular and here for examples
You could...
Make the field non-editable...
Isbn13TextField.setEditable(false)
How consume a copy paste?
You can't. A DocumentEvent is generated AFTER the Document has already been updated.
If you just want to disable the copy/paste functionality of a text field then you can remove the Key Bindings:
KeyStroke copy = KeyStroke.getKeyStroke("control C");
textField.getInputMap().put(copy, "none");

Automatic Updating of Action's isEnabled()

I have written a Swing GUI with several controls associated with the same Action subclass. The implementation of the Action subclass follows this psudocode:
public class MyGUI
{
Gizmo gizmo_; // Defined elsewhere
public class Action_StartPlayback extends AbstractAction
{
/* ctor */
public Action_StartPlayback(String text, ImageIcon icon, String desc, Integer mnem)
{
super(text, icon);
putValue(SHORT_DESCRIPTION, desc);
putValue(MNEMONIC_KEY, mnem);
}
#Override public boolean isEnabled()
{
return gizmo_ == null;
}
#Override public void actionPerformed(ActionEvent e)
{
gizmo_ = new Gizmo();
}
Action_StartPlayback act_;
};
The action is associated with both a button and a menu item, in a way similar to this psudocode:
act_ = new Action_StartPlayback(/*...*/);
// ...
JButton btn = new JButton(act_);
JMenu mnu = new JMenu(act_);
When I click the button or the menu item, the action's actionPerformed is fired correctly, gizmo_ is initialized and is non-null and everything works as expected -- except that the button and menu item are still enabled.
I expected that isEnabled would have been called again "automagically" but this is obviously not happening. isEnabled() is never called again.
This evokes two questions:
Is it OK for me to #Override the isEnabled() method as I have done here?
Assuming the answer to #1 is yes, how do I trigger a refresh of the GUI so that isEnabled() is called again, resulting in the button & menu item being disabled?
Instead of overriding setEnabled you could simply call setEnabled(false) after you intitialize your gizmo in your actionPerformed method:
#Override public void actionPerformed(ActionEvent e)
{
gizmo_ = new Gizmo();
setEnabled(false);
}
Here's the setEnabled implementation from AbstractAction:
public void setEnabled(boolean newValue) {
boolean oldValue = this.enabled;
if (oldValue != newValue) {
this.enabled = newValue;
firePropertyChange("enabled",
Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
}
}
The automagical you're looking for is the call to firePropertyChange, which notifies components based on this action that the state has changed, so the component can update its own state accordingly.
I am no pro at this, but I don't see a see an automatic way of doing this, of notifying listeners that the state of enabled has changed. Of course you can call setEnabled(false) at the start of the actionPerformed, and then code Gizmo (or a wrapper on Gizmo) to have property change support and then add a PropertyChangeListener to Gizmo, and in that listener, when the state changes to DONE, call setEnabled(true). A bit kludgy but it would work.
This is not strictly limited to Swing, but a more general Java principle. A lot of classes in the JDK (and in other libraries) have a getter and a setter for a property. Those methods are not meant to be overridden to return a dynamic value as most of the times the superclass accesses the corresponding field directly and does not go through the getters.
If you have dynamic behavior, you should call the corresponding setter each time the value changes. This will notify the super class changes have been made, and typically this will also fire a property change event to notify other interested parties.
You can find a bit more on this convention if you do a search on Java beans.
In your case, a possible solution is to let your UI class fire a PropertyChangeEvent when that gizmo instance changes, and let your actions listen for that event. When they receive such an event, they update their own enabled state.
The enabled-state is stored in both of your objects, in the AbstractAction and in the JButton.
This is important because you only need one instance of Action_StartPlayback for multiple Components like:
In the menu's button.
In a toolbar.
In a Shortcut Strgp in example.
All of them can have the same instance of Action_startPlayback. The Action_startPlayback is the only source of truth. The components are responsible to respect this source of truth so every Component will ask the AbstractAction to notify them if something has been changed. The AbstractAction will remember all the components and will notify them using the Method firePropertyChange().
But how to repaint all pending components? You must force all pending Components to ask the Action_startPlayback for the actuall enabled-state! Look at this:
#Override public void actionPerformed(ActionEvent e)
{
gizmo_ = new Gizmo();
// now, force the components to get notified.
setEnabled(true);
}

How to perform an action programmatic like action performed of JButton?

I have a actionSave class which extends AbstractAction.I use it for save button. somewhere else i want to run the same instant of it which was used for the button.
I came to conclusion to use it as below but i do not know what to pass as argument?
model.getActionSave().actionPerformed("what should i add here for action event");
Extract the code of the actionPerformed() method into another method without argument, and call this method instead:
public void actionPerformed(ActionEvent e) {
save();
}
public void save() {
...
}
...
model.getActionSave().save();
Just create your own ActionEvent, it has a public constructor. E.g.
model.getActionSave().actionPerformed(
new ActionEvent( this, ActionEvent.ACTION_PERFORMED, "Save" )
);
If you are e.g. testing your UI, you can also opt to perform a click on the button through the API:
button.doClick();
But in general I prefer the first approach, and avoid the coupling with the UI

how to Override FocusListener - Swing

The application i work with override the default JComboBox from swing. Leets Call it MyComboBox. This version of ComboBox implement the FocusListener and contains the two methods focusGained and focusLost.
Now, in one of the panel of the application, the form contains a ComboBox of this type:
MyComboBox aMyComboBox = new MyComboBox();
I want to add a listener on this like that :
aMyComboBox.addFocusListener(new FocusListener() {
public void focusLost(FocusEvent e) {
//Do something here
}
public void focusGained(FocusEvent e) {
//Do something else
}
});
But when i run the code, it never pass into these method but only execute the focusGained/lost from the MyComboBox class.
Is there a way to add a listener on an object that already implements FocusListener?
Additional FocusListener should work unless the instance used in MyComboBox consumes the event (AWT event consumption).
Try making an example with an ordinary JComboBox -- this will help narrowing down the cause of the problem.

Categories