Let's say you have two toolbar buttons, one for expanding and the other for collapsing the currently selected node, as shown below, within the black box drawn on the screenshot:
How to, from the two toolbar buttons above, expand/collapse the currently selected node?
The answer is very similar to "Add Widget from Action in Toolbar". Start by creating two interfaces, one for expanding and the other for collapsing:
public interface Expandable { void expand(); }
public interface Collapsible { void collapse(); }
Next, put implementations of the above into the Lookup of the TopComponent that displays the Node hierarchy. So, start by going to the TopComponent and, within the TopComponent (since that's where you have access to the BeanTreeView and to the ExplorerManager that controls the Node hierarchy) implement your two interfaces:
private class CollapsibleMovieNodes implements Collapsible { @Override public void collapse() { myBeanTreeView.collapseNode(myExplorerManager.getSelectedNodes()[0]); } } private class ExpandableMovieNodes implements Expandable { @Override public void expand() { myBeanTreeView.expandNode(myExplorerManager.getSelectedNodes()[0]); } }
Then, put those two implementations into the Lookup of the TopComponent:
associateLookup(new ProxyLookup( Lookups.fixed(new CollapsibleMovieNodes(), new ExpandableMovieNodes()), ExplorerUtils.createLookup(manager, map)));
Finally, now, you can get the two implementations from the Lookup, anywhere in your application, and just call the method on it. For example, here is an Action that can be invoked from a toolbar button to expand the currently selected Node:
@ActionID( category = "Edit", id = "com.mit.moview.controler.ExpandNodeAction") @ActionRegistration( iconBase = "com/mit/moview/controler/expandIcon.png", displayName = "#CTL_ExpandNodeAction") @ActionReference( path = "Toolbars/ExpCol", position = 3330) @Messages("CTL_ExpandNodeAction=Expand Node") public final class ExpandNodeAction implements ActionListener { private final Expandable context; public ExpandNodeAction(Expandable context) { this.context = context; } @Override public void actionPerformed(ActionEvent ev) { context.expand(); } }
What's cool about the above is that if there's no Expandable in the Lookup, e.g., when the Properties window or Output window are selected, the toolbar button will automatically be disabled and greyed out.
And here's the solution in the form of a screenshot, showing that the Actions can be in different modules to where the View is found, and are loosely coupled from each other, since the module providing the Actions does not need a dependency on the module providing the View, since they are loosely coupled because they interact via the two capabilities:
But what about if the toolbar button (i.e., the underlying Action) should only be enabled if a Node has children? In other words, if a Node cannot be expanded because it does not have child Nodes, then the toolbar button should not be enabled. In that case, you need to do a bit more work because you want to have access in your Action to two different objects: the Node, as well as the Expandable. That's where you need to use a CookieAction and let the Action be enabled eagerly, i.e., not via the Action*. factories. Here's the solution:
@ActionID( category = "Edit", id = "com.mit.moview.controler.ExpandNodeAction") @ActionRegistration( lazy = false, displayName = "not-used") @ActionReference( path = "Toolbars/ExpCol", position = 3330) public final class ExpandNodeAction extends CookieAction { private final Lookup context; private Expandable expandable; public ExpandNodeAction() { context = Utilities.actionsGlobalContext(); } @Override protected boolean enable(Node[] activatedNodes) { if (context.lookup(Expandable.class) != null&& context.lookup(Node.class) != null&& !context.lookup(Node.class).isLeaf()) { this.expandable = context.lookup(Expandable.class); return true; } return false; } @Override protected int mode() { return CookieAction.MODE_ONE; } @Override protected Class>[] cookieClasses() { return new Class[]{Node.class, Expandable.class}; } @Override protected void performAction(Node[] nodes) { if (expandable != null) { expandable.expand(); } } @Override public String getName() { return null; } @Override protected String iconResource() { return "com/mit/moview/controler/expandIcon.png"; } @Override public HelpCtx getHelpCtx() { return HelpCtx.DEFAULT_HELP; } }
Finally, what if the toolbar button for expanding should be disabled when a Node has already been expanded? Create a new capability, NodeExpandable, introduce it into the Lookup of the Node whenever it is expanded, remove it again when it is collapsed. Then, in the Action, check for the presence/absence of that capability and enable/disable accordingly.