How do I add a dropdown menu to toolbar that is selectively enabled/disabled?

Apache NetBeans Wiki Index

Note: These pages are being reviewed.

In this case, your action also needs to implement ContextAwareAction. A ContextAwareAction is a factory for other Action instances which are tied to a specific Lookup (so that, if selection changes after the popup menu for a Node is shown, the Action does not operate on the wrong object). You can start with a subclass of javax.swing.AbstractAction, and you will need two constructors:

private final Lookup lookup;
public MyAction() {
  this (Utilities.actionsGlobalContext());
}

private MyAction(Lookup lookup) {
  this.lookup = lookup;
  Icon icon = ImageUtilities.image2Icon(
    ImageUtilities.loadImage("com/foo/icon.gif"));
  putValue(SMALL_ICON, icon);
  //set the initial enabled state
  setEnabled(false);
}

You will also need to implement the one method of ContextAwareAction:

public Action createContextAwareInstance(Lookup actionContext) {
  return new MyAction(actionContext);
}

To enable and disable the action, we will need to listen on the lookup for the presence or absence of some object type. If it is there, the action will be enabled; if it is not, it will be disabled.

Since we do not want to start listening on the global selection context until something actually cares whether the action is enabled or not, so we will override add/removePropertyChangeListener() to notice that and listen or not.

First, we must modify our class signature to implement LookupListener:

public class MyAction extends AbstractAction implements ContextAwareAction, LookupListener, Presenter.Toolbar {
...

Now we will handle listening on the Lookup.Result. We want to stop listening to it when there are no PropertyChangeListeners left, so that our action can be garbage collected if not in use:

private LookupResult<? extends DataObject> res;

@Override
public synchronized void addPropertyChangeListener(PropertyChangeListener l) {
  boolean startListening = getPropertyChangeListeners().length == 0;
  super.addPropertyChangeListener(l);
  if (startListening) {
    res = lookup.lookupResult(MyType.class);
    res.addLookupListener(this);
  }
}

@Override
public synchronized void removePropertyChangeListener(PropertyChangeListener l) {
  super.removePropertyChangeListener(l);
  if (getPropertyChangeListeners().length == 0) {
    res.removeLookupListener(this);
    res = null;
  }
}

Now comes the actual implementation of LookupListener:

public void resultChanged(LookupEvent ev) {
  setEnabled(!res.allItems().isEmpty());
}

A bit of bookkeeping is required in getToolbarPresenter() - at least until issue 179814 is fixed, we will need to manually enable/disable the actions for our menu items:

  private final Set<Action> popupMenuActions = new WeakSet<Action>();
  @Override
  public Component getToolbarPresenter() {
    JPopupMenu menu = new JPopupMenu();
    Action actionOne = new DemoMenuAction("One");
    Action actionTwo = new DemoMenuAction("Two");
    menu.add(new JMenuItem(actionOne));
    menu.add(new JMenuItem(actionTwo));
    popupMenuActions.add(actionOne);
    popupMenuActions.add(actionTwo);
    //add action listeners to the menu items to do what you want
    Icon icon = (Icon) getValue(SMALL_ICON);
    JButton result = DropDownButtonFactory.createDropDownButton(icon, menu);
    result.setAction(this);
    return result;
  }

  @Override
  public void setEnabled(boolean enabled) {
    super.setEnabled(enabled);
    for (Action a : popupMenuActions) {
      if (a != null) { //WeakSet iterator can return null
        a.setEnabled(enabled);
      }
    }
  }

  private class DemoMenuAction extends AbstractAction {
    DemoMenuAction(String name) {
      putValue(NAME, name);
      setEnabled (MyAction.this.isEnabled());
    }

    @Override
    public void actionPerformed(ActionEvent e) {
      DataObject ob = res.allInstances().iterator().next();
      DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
              ob.getName()));
    }
  }

If we want the drop-down button to do something when it is clicked on the right side (not in the popup area with the down-arrow), we can implement actionPerformed(ActionEvent) to do whatever that is.

For an older detailed example of manually creating a context-aware drop-down toolbar button (without DropDownButtonFactory, circa NetBeans 6.0), see see this post, posted in on the old dev@openide NetBeans mailing lists.