X
  • July 12, 2011

Presenter.Toolbar Meets @Action Annotations

Geertjan Wielenga
Product Manager

Let's say you have a JPanel that you'd like to integrate into the NetBeans Platform toolbar. As an example, we imagine that this is the JPanel:

public class NextPrevJPanel extends JPanel {
JButton nextButton = new JButton();
JButton prevButton = new JButton();
public NextPrevJPanel() {
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
prevButton.setIcon(new ImageIcon(getClass().getResource("prev.png")));
nextButton.setIcon(new ImageIcon(getClass().getResource("next.png")));
prevButton.addActionListener(new PrevButtonActionListener());
nextButton.addActionListener(new NextButtonActionListener());
add(prevButton);
add(nextButton);
}
private class NextButtonActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Simulate next action:
JOptionPane.showMessageDialog(null, "Next");
}
}
private class PrevButtonActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Simulate previous action:
JOptionPane.showMessageDialog(null, "Previous");
}
}
}

As you can see, we have a panel that provides navigation buttons. The standard NetBeans Platform approach to integrating the above panel into the toolbar would be to have you create, essentially, a dummy action. The action implements "Presenter.Toolbar" and returns the JPanel, which represents the dummy action in the toolbar:

@ActionID(category = "NextPrev", id = "org.demo.DummyAction")
@ActionRegistration(displayName = "")
@ActionReferences({
@ActionReference(path = "Toolbars/File", position = 100)
})
public final class DummyAction extends AbstractAction implements Presenter.Toolbar {
@Override
public Component getToolbarPresenter() {
return new NextPrevJPanel();
}
@Override
public void actionPerformed(ActionEvent e) {
// Nothing to do here.
}
}

It works, but it's not very nice. Aside from the fact that you have a meaningless "actionPerformed" in the code above, you also have a whole meaningless class, since it only exists to register the panel into the toolbar.

So, with NetBeans Platform 7.0, we can simply delete the above class. And then we rewrite our JPanel as follows:

public class NextPrevJPanel extends JPanel {
@ActionID(category = "NextPrev", id = "org.demo.NextAction")
@ActionRegistration(displayName = "", iconBase = "org/demo/next.png")
@ActionReference(path = "Toolbars/NextPrev", position = 100)
public static class NextButtonActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Simulate next action:
JOptionPane.showMessageDialog(null, "Next");
}
}
@ActionID(category = "NextPrev", id = "org.demo.PrevAction")
@ActionRegistration(displayName = "", iconBase = "org/demo/prev.png")
@ActionReference(path = "Toolbars/NextPrev", position = 200)
public static class PrevButtonActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Simulate previous action:
JOptionPane.showMessageDialog(null, "Previous");
}
}
}

Isn't that great? Action annotations on inner classes. It means you don't have to create a separate class file for each of your ActionListeners, which would be another approach to solving this problem.

In the code above, note that all the stuff about constructing/decorating/adding the JButtons is now gone. All we're left with is the actual logic in our JPanel. And we register that via the new @Action annotations into the toolbar, which means that the NetBeans Platform will create buttons for us. The NetBeans Platform is responsible for everything except the logic, which is our domain-level code anyway, while all the rest is just standard stuff that the NetBeans Platform will handle for us. Another very nice side effect is that the small icon/large icon switching works (thanks to our "iconBase" attribute), which is not the case when you use Presenter.Toolbar to put your panel into the toolbar. Only not-so-nice thing is that our inner classes are now static, since under the hood the action performed calls in inner classes are reached statically from the layer.

Finally, does this mean that Presenter.Toolbar (and friends) are now obsolete? I don't think so. For example, if you have a Google Toolbar, i.e., a panel containing a label and a text field, for example, you still need to put it into the toolbar somehow. In that particular scenario, you don't want the NetBeans Platform to create buttons for you and hence you will not use the approach outlined here.

Join the discussion

Comments ( 3 )
  • Jesse Glick Tuesday, July 12, 2011

    There is nothing specific to NB 7.0 about this other than the annotations. If you do not need a special toolbar presenter, then you could always just add Action's directly as toolbar buttons. The action classes must in that case be public and (if nested) static, of course, so they can be instantiated, but the exact same would work in any earlier NB release using manual layer registration (newvalue="pkg.Outer$Inner") - using Actions.alwaysEnabled on an ActionListener where available, or registering a javax.swing.Action in any NB 3.0+.

    Note that @ActionRegistration is permitted on public static no-arg methods, so you could also have one (private) impl class with - say - a 'String message' parameter to its constructor, and two factory methods registering two variants of it ("Next" and "Previous"). This also was available for many years using methodvalue, which is exactly what the action registration produces when compiled.


  • Geertjan Tuesday, July 12, 2011

    Jesse, thanaks for the comment. So, would the code below be a correct illustration for @ActionRegistration on public static no-arg methods?

    class MyJButton extends JButton implements ActionListener {

    private final String message;

    public MyJButton(final String message) {

    this.message = message;

    }

    @ActionID(category = "NextPrev", id = "org.demo.NextAction")

    @ActionRegistration(displayName = "", iconBase = "org/demo/next.png")

    @ActionReference(path = "Toolbars/NextPrev", position = 100)

    public static MyJButton nextButtonFactory() {

    return new MyJButton("Next");

    }

    @ActionID(category = "NextPrev", id = "org.demo.PrevAction")

    @ActionRegistration(displayName = "", iconBase = "org/demo/prev.png")

    @ActionReference(path = "Toolbars/NextPrev", position = 200)

    public static MyJButton prevButtonFactory() {

    return new MyJButton("Previous");

    }

    @Override

    public void actionPerformed(ActionEvent e) {

    JOptionPane.showMessageDialog(null, message);

    }

    }


  • charmeleon Wednesday, September 12, 2012

    This one is awesome. I have a lot of color tables and I was using a JOptionPane to show them since I didn't want to deal with the clutter of 64 Action classes. Thanks Geertjan!


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

Integrated Cloud Applications & Platform Services