Geertjan's Blog

  • November 8, 2011

Changing a Node from its Popup Menu

Geertjan Wielenga
Product Manager

I learnt some interesting things with Børre Dalhaug today. Look at the screenshot below:

When a submenu is selected, the display name of the Node is automatically updated to the text in the selected submenu:

I couldn't figure out how to use the new Actions.* factories for the above scenario. Instead, it's done like this:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import net.dalhaug.borre.context691.domainobjects.IDrummer;
import org.openide.nodes.Node;
import org.openide.util.HelpCtx;
import org.openide.util.ImageUtilities;
import org.openide.util.actions.CookieAction;
import org.openide.util.actions.Presenter;
public final class ChangeDrumAction extends CookieAction
implements Presenter.Popup, ActionListener {
    private static final String PROP_KEY = "";
    private IDrummer context = null;
    public JMenuItem getPopupPresenter() {
        JMenu changeDrumKitMenu = new JMenu("Change " + context.getDrumKit());
        changeDrumKitMenu.setIcon(ImageUtilities.loadImageIcon("net/dalhaug/icons/Arrow.png", false));
        for (String string : context.getAvailableDrumsets()) {
            JMenuItem subMenu = new JMenuItem(string);
            //Create a client property on the JMenuItem:
            subMenu.putClientProperty(PROP_KEY, string);
        return changeDrumKitMenu;
    //The Action that will be invoked on the submenus:
    public void actionPerformed(ActionEvent ev) {
        if (ev.getSource() instanceof JMenuItem) {
            JMenuItem item = (JMenuItem) ev.getSource();
            //Retrieve from the client property on the JMenuItem:
            String drumKit = (String) item.getClientProperty(PROP_KEY);
    protected boolean enable(Node[] nodes) {
        if (nodes[0].getLookup().lookup(IDrummer.class)!=null){
            context = nodes[0].getLookup().lookup(IDrummer.class);
            return true;
        return false;
    protected int mode() {
        return CookieAction.MODE_EXACTLY_ONE;
    protected Class<?>[] cookieClasses() {
        return new Class[]{IDrummer.class};
    //Not used:
    public String getName() {return null;}
    //Not used:
    @Override protected void performAction(Node[] nodes) {}
    public HelpCtx getHelpCtx() {
        return HelpCtx.DEFAULT_HELP;

The above is registered as follows:

<folder name="Actions">
    <folder name="OSC">
        <file name="net-dalhaug-ChangeDrumAction.instance"/>

And here's the Node:

public class DrummerNode extends AbstractNode implements PropertyChangeListener {
    private final IDrummer drummer;
    public DrummerNode(IDrummer drummer) {
        super(Children.LEAF, Lookups.singleton(drummer));
        this.drummer = drummer;
        setName(drummer.getName() + " plays " + drummer.getDrumKit());
    public Image getIcon(int type) {
        return ImageUtilities.loadImage("net/dalhaug/borre/context691/Drum24.png");
    public Action[] getActions(boolean context) {
        List<? extends Action> oscActions = Utilities.actionsForPath("Actions/OSC");
        return oscActions.toArray(new Action[oscActions.size()]);
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals(Drummer.PROP_DRUMKIT)) {
            setDisplayName(drummer.getName() + " plays " + drummer.getDrumKit());

Join the discussion

Comments ( 1 )
  • Jesse Glick Tuesday, November 8, 2011

    You cannot use Actions.* factories for this since it is inherently nonlazy, but you should be able to use @ActionRegistration on it anyway. (By implementing Presenter.Popup you magically indicate that you do not want to use a factory.)

Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.