Netbeans actions toolbar with local lookup context

This article talks about Netbeans and an action toolbar that is sensitive to a local selection lookup context. Actually, global selection context sensitive actions is powerful mechanism with a subtle limitation: everything happens globally. It is no use for a local actions menu or toolbar sensitive to a limited selection context restricted to a JPanel or TopComponent. Not surprisingly, the Netbeans Java IDE itself reinvents the wheel each time this requirement arises, for example, on the ‘variables’ and ‘breakpoint’ windows.

Thus, we still look forward for our local context to reuse conveniences like action annotations that prevent boilerplate code and extensibility through the layer.xml configuration.

Originally published at https://techtavern.wordpress.com.

We suppose that the JPanel or TopComponent owns a JToolbar that will hold all actions that apply to the local selection context. We recommend turning off the ‘floatable’ property, or the user may drag the toolbar out of the JPanel or TopComponent.

The solution relies on the convention that interested actions are to be declared under a pre-defined path within the layer.xml configuration. This allows any module to later provide further actions and works similar to the global toolbar convention itself. We have arbitrarily chosen to declare the actions under “TopComponentName/Toolbar”.

Actions for the local selection context are declared as any global action, except for the @ActionReference annotation, which references the previously pre-defined path instead of an entry in the global menu or global toolbar.

@ActionID(category = "category ",
  id = "fully.qualified.name.of.LocalAction")
@ActionRegistration(
  iconBase = "path/to/icon.png",
  displayName = "#CTL_LocalAction")
@ActionReferences({
  @ActionReference(path = "TopComponentName/Toolbar", position = 30)
})
@Messages("CTL_LocalAction=Action text")
public final class LocalAction implements ActionListener {
  public LocalAction (Type context) {
	...
  }

  @Override
  public void actionPerformed(final ActionEvent ev) {
    ...
  }
}

If fact, this action is global, as actions define a business procedure on a compatible context, regardless on which view or presentation, regardless if made available globally or locally.

Here is the code that populates a local toolbar:

List<? extends Action> actions = Utilities.actionsForPath("TopComponentName/Toolbar");
for (Action action : actions) {
  if (action == null) {
	toolbar.addSeparator();
  } else {
	if (lookup != null && action instanceof ContextAwareAction) {
	  action = ((ContextAwareAction) action).createContextAwareInstance(lookup);
	}
	Component item;
	if (action instanceof Presenter.Toolbar) {
	  item = ((Presenter.Toolbar) action).getToolbarPresenter();
	  if (item == null) {
		continue;
	  }
	} else {
	  item = ActionPresenterProvider.getDefault().createToolbarPresenter(action);
	}
	toolbar.add(item);
  }
}

Simplest JTable icon cell renderer ever

While there are many examples of JTable cell renderers spread on the Internet, they lack a clear understanding about how they were proposed to work. This article presents and explains a minimal and correct cell renderer, extending the default cell renderer as supposed by the Java API. The example explains how to display icons instead of text.

Originally published at https://techtavern.wordpress.com.

For the hurried, first the code:

public class EnumIconCellRenderer extends DefaultTableCellRenderer {

private final Map<!--?, ImageIcon--> icones;

  public EnumIconCellRenderer(final Map<!--?, ImageIcon--> icones) {
    // Initialize data used to render cells.
    this.icones = icones;

    // Set properties that never change.
    this.setHorizontalAlignment(JLabel.CENTER);
    this.setVerticalAlignment(JLabel.CENTER);
    this.setText(null);
  }

  @Override
  protected void setValue(Object value) {
    // Set properties that change on individual cells.
    this.setIcon(icones.get(value));
  }
}

A cell renderer works like a rubber-stamp for each cell in the table. The renders uses a Swing component to draw the first cell, changes the contents of that cell renderer, and for each following row shifts the origin of the component to the new location, then re-draws it. The default cell renderer uses the convenient JLabel to stamp a potentially inconvenient toString() representation of data objects. Note that getTableCellRendererComponent() returns itself (the JLabel), but properly configured for an individual cell. Therefore, setting properties of the renderes itself actually configures the component returned by getTableCellRendererComponent().

The proposed cell renderer extends DefaultTableCellRenderer as expected. The constructor sets those JLabel properties that will never change. And might initialize some internal data, in our example, a map of icons for each possible value.

I strongly discourage overriding getTableCellRendererComponent() as usually recommended by the Java community. It would incur separation of concerns, plainness and performance issues.

The getTableCellRendererComponent() method is intended to configure the JLabel appearance according to the focus and selection state. Handling the value in this method makes code more confusing by mixing appearance, look & feel and data. The setValue() method offers clearer and more reasonable location to adjust properties that depend solely on data.

By insisting on handling data in getTableCellRendererComponent() instead of setValue(), performance decreases. By design, setValue() is called by your customized code. Most implementations end up reseting or undoing properties from the default implementation of setValue(), which in turn resorts to the quite expensive and misleading toString() method over the data objects.