NB: How to create a context-aware submenu

Last time I blogged about “How create a context sensitive action with a submenu in the project-view?” [1].  Now there was an additional requirement to be fulfilled: The submenu should be only enabled, when exactly two project nodes are selected. Only if this condition is true, the submenu items should be displayed.

Thanks to the help of Jean-Marc Borer a solution can be provided too.

@ActionID(
        category = "MyActions",
        id = "de.markiewb.netbeans.sample.ContextAwarePopupAction"
)
@ActionRegistration(
        displayName = "#CTL_ContextAwarePopupAction", lazy = false
)
@ActionReferences({
    @ActionReference(path = "Projects/Actions")
})
@Messages("CTL_ContextAwarePopupAction=I am a context-aware submenu")
public final class ContextAwarePopupAction extends AbstractAction implements ActionListener, Presenter.Popup {

    private final Lookup.Result<Project> result;
    private final transient LookupListener lookupListener;

    public ContextAwarePopupAction() {
        putValue(NAME, Bundle.CTL_ContextAwarePopupAction());
        //disabled by default - at loading time
        setEnabled(false);
        //create an action, which is only enabled when exactly 2 projects are selected
        result = Utilities.actionsGlobalContext().lookupResult(Project.class);
        this.lookupListener = new LookupListener() {

            @Override
            public void resultChanged(LookupEvent ev) {
                final Runnable runnable = new Runnable() {

                    @Override
                    public void run() {
                        int s = result.allInstances().size();
                        ContextAwarePopupAction.this.setEnabled(s == 2);
                    }
                };
                // to make sure that it will be executed on EDT
                if (EventQueue.isDispatchThread()) {
                    runnable.run();
                } else {
                    SwingUtilities.invokeLater(runnable);
                }
            }
        };
        result.addLookupListener(WeakListeners.create(LookupListener.class, this.lookupListener, result));
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        //NOP
    }

    @Override
    public JMenuItem getPopupPresenter() {
        JMenu main = new JMenu(this);
        List<? extends Action> actionsForPath = Utilities.actionsForPath("Actions/MyActions/SubActions");
        for (Action action : actionsForPath) {
            main.add(action);
        }
        return main;
    }
}

The most important part of the popup action is to emulate a context-aware action using a lookup listener.

2015-09-13_21h36_02

The updated sample code can be found at https://github.com/markiewb/nb-api-samples/tree/master/SubmenuWithContextSensitiveAction. The entry of the offical developer FAQ at http://wiki.netbeans.org/DevFaqActionNodePopupSubmenu has also been updated.

[1] https://benkiew.wordpress.com/2015/09/01/nb-how-to-create-a-context-sensitive-action-within-a-submenu/

 

NB: How to create a context sensitive action within a submenu

Today somebody asked “how create a context sensitive action with a submenu in the project-view?” in the NetBeans mailing list. So I figured it out and I like to document it using this post.

First here is the action for the submenu. The action is registered to the project context menu. It uses the Presenter.Popup interface to register itself as a submenu. In the getPopupPresenter() method the submenu is assembled via Utilities.actionsForPath.

@ActionID(
        category = "MyActions",
        id = "de.markiewb.netbeans.sample.PopupAction"
)
@ActionRegistration(
        displayName = "#CTL_PopupAction", lazy = false
)
@ActionReferences({
    @ActionReference(path = "Projects/Actions")
})
@Messages("CTL_PopupAction=I am a submenu")
public final class PopupAction extends AbstractAction implements ActionListener, Presenter.Popup {

    @Override
    public void actionPerformed(ActionEvent e) {
        //NOP
    }

    @Override
    public JMenuItem getPopupPresenter() {
        JMenu main = new JMenu(Bundle.CTL_PopupAction());
        List<? extends Action> actionsForPath = Utilities.actionsForPath("Actions/MyActions/SubActions");
        for (Action action : actionsForPath) {
            main.add(action);
        }
        return main;
    }
}

In the previous code snippet Utilities.actionsForPath has been used to resolve action(s) at Actions/MyActions/SubActions. Here is a context sensitive action, which is registered at this location.

@ActionID(
        category = "MyActions/SubActions",
        id = "de.markiewb.netbeans.sample.HelloProjectsAction"
)
@ActionRegistration(
        displayName = "#CTL_HelloProjectsAction"
)
@Messages("CTL_HelloProjectsAction=HelloProjects...")
public final class HelloProjectsAction implements ActionListener {

    private final List context;

    public HelloProjectsAction(List context) {
        this.context = context;
    }

    @Override
    public void actionPerformed(ActionEvent ev) {
        JOptionPane.showMessageDialog(null, context.size() + " projects selected: " + context);
    }
}

The result:
SubmenuWithContextSenstiveAction

The complete sample code can be found at https://github.com/markiewb/nb-api-samples/tree/master/SubmenuWithContextSensitiveAction. I will also add this documentation to the offical developer FAQ at http://wiki.netbeans.org/DevFaqActionNodePopupSubmenu

Happy coding!

Update: The second part of this post can be found at https://benkiew.wordpress.com/2015/09/13/nb-how-to-create-a-context-aware-submenu/