Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatic Updating of Action's isEnabled()

Tags:

java

swing

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:

  1. Is it OK for me to @Override the isEnabled() method as I have done here?
  2. 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?
like image 566
John Dibling Avatar asked Jul 09 '12 15:07

John Dibling


3 Answers

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.

like image 147
darri Avatar answered Nov 19 '22 07:11

darri


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.

like image 33
Hovercraft Full Of Eels Avatar answered Nov 19 '22 06:11

Hovercraft Full Of Eels


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.

like image 2
Robin Avatar answered Nov 19 '22 07:11

Robin