Monday, February 25, 2008

Using boolean state actions in NetBeans RCP

This post can be categorized as a code snippet and shows how to work with CallableSystemAction and BooleanStateAction.

If you create a new action through the new action wizard your code looks something like:


public final class SomeAction extends CallableSystemAction {

public void performAction() {
// TODO implement action body
}

public String getName() {
return NbBundle.getMessage(SomeAction.class, "CTL_SomeAction");
}

@Override
protected String iconResource() {
return "path/to/your/icon.png";
}

public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}

@Override
protected boolean asynchronous() {
return false;
}
}


The action allows you to execute some code (in the performAction() method) when the used clicks the button.
On the other side, a boolean action is like a toggle button. Imagine a play button, it can be pushed to indicate the music is playing and in normal state (not pushed) indicating the player is stopped. This can be achieved with a BooleanStateAction instead of a CallableSystemAction. Modify the previous code to:


public final class SomeAction extends BooleanStateAction implements PropertyChangeListener
{

public SomeAction()
{
// Set the initial state
setBooleanState(true);

// Register as a property listener
addPropertyChangeListener(this);
}

public String getName()
{
return NbBundle.getMessage(SomeAction.class, "CTL_SomeAction");
}

@Override
protected String iconResource()
{
return "path/to/your/icon.png";
}

public HelpCtx getHelpCtx()
{
return HelpCtx.DEFAULT_HELP;
}

public void propertyChange(PropertyChangeEvent evt)
{
// Check if the boolean state has changed.
if(BooleanStateAction.PROP_BOOLEAN_STATE.equals(evt.getPropertyName())) {

Boolean state = (Boolean) evt.getNewValue();
if (state.equals(Boolean.TRUE)) {
// Button has been pressed
}
else {
// Button has been releases
}
}
}
}


How it works?
Both CallableSystemAction and BooleanStateAction are direct subclasses of org.openide.util.actions.SystemAction. When a system action is executed its abstract actionPerformed method is invoked.

// In SystemAction...
public abstract void actionPerformed(ActionEvent ev);


In the case of CallableSystemAction, the override method makes some work and finally invokes the performAction which is the method you need to code.

public void actionPerformed(ActionEvent ev) {
if (isEnabled()) {
org.netbeans.modules.openide.util.ActionsBridge.doPerformAction(
this,
new org.netbeans.modules.openide.util.ActionsBridge.ActionRunnable(ev, this, asynchronous()) {
public void run() {
performAction();
}
}
);
} else {
// Should not normally happen.
Toolkit.getDefaultToolkit().beep();
}
}


In the case of BooleanStateAction, this class maintains a property which represents the state of the button. When the override actionPerformed method is execute it changes the property state and fires a property change event to be cached by the BooleanStateAction listeners:

public void actionPerformed(java.awt.event.ActionEvent ev) {
setBooleanState(!getBooleanState());
}

public void setBooleanState(boolean value) {
Boolean newValue = value ? Boolean.TRUE : Boolean.FALSE;
Boolean oldValue = (Boolean) putProperty(PROP_BOOLEAN_STATE, newValue);

firePropertyChange(PROP_BOOLEAN_STATE, oldValue, newValue);
}


As you can see in the SomeAction code the tip resides in registering the class as its own listener and handle the state change in the propertyChange method.

No comments: