X

Geertjan's Blog

  • January 13, 2011

Viewer/Editor Interactions on the NetBeans Platform

Geertjan Wielenga
Product Manager
Let's start by looking at some screenshots. We have a normal viewer/editor scenario, with the viewer window in one module and the editor window in the other. The point of this blog entry is to look at the toolbar button (there's only one in each screenshot, so no confusion there). In the first screenshot, the toolbar button is enabled, because I have put a "Customer" object into the Lookup of the Node, which causes the toolbar button to be enabled because the underlying Action is sensitive to "Customer" objects:

Now, when my cursor is in the editor window, i.e., the editor window has focus, the toolbar button is disabled. That makes sense because the toolbar button is an Edit button. Only when something should be editable should the toolbar button be enabled. Below, however, the editor window is already open, so there is no need for the Edit button, hence it should be disabled:

In other words, the editor window does not have the "Customer" object in its Lookup, hence the toolbar button is not enabled, since the underlying Action is sensitive to the absent "Customer" object.

Now, let's look at a third screenshot:

Here, the editor window is selected, while the toolbar button is enabled. But it makes sense for the toolbar button to be enabled, since its underlying Action is a "Print" action. Whether the user selects the Node in the viewer window or whether the editor window is open, the "Print" action needs to be enabled because in both contexts it makes sense for the user to want to print info about the customer.

To implement the two different scenarios above is described below:

Scenario 1. In the editor window, listen for the "Customer" object and then update the editor window. You'll have a new "Customer" object whenever a different Node in the viewer window is selected, since that's how you've defined the Node:

public class CustomerNode extends BeanNode {
public CustomerNode(Customer bean) throws IntrospectionException {
super(bean, Children.LEAF, Lookups.singleton(bean));
setDisplayName(bean.getName());
}
}

Scenario 2. In addition to the above, whenever you get a new "Customer" object, put it into the Lookup of the viewer window. You can do this in one of three ways and I don't know which one is preferable:

  • Use AbstractLookup/InstanceContent:
    public final class CustomerEditorTopComponent extends TopComponent implements LookupListener {
    private InstanceContent ic;
    public CustomerEditorTopComponent() {
    ...
    ic = new InstanceContent();
    associateLookup(new AbstractLookup(ic));
    }
    @Override
    public void resultChanged(LookupEvent le) {
    Result source = (Result) le.getSource();
    Collection<Customer> allInstances = source.allInstances();
    if (allInstances.iterator().hasNext()) {
    Customer c = allInstances.iterator().next();ic.add(c);
    nameField.setText(c.getName());
    cityField.setText(c.getCity());
    }
    }
    ...
    ...
    ...
    }
  • Instead of the above, construct a dummy node from the "Customer" object and use it to set the editor window's activated Node:
    private class DummyBeanNode extends BeanNode {
    public DummyBeanNode(Customer bean) throws IntrospectionException {
    super(bean, Children.LEAF, Lookups.singleton(bean));
    }
    }
    @Override
    public void resultChanged(LookupEvent le) {
    Result source = (Result) le.getSource();
    Collection<Customer> allInstances = source.allInstances();
    if (allInstances.iterator().hasNext()) {
    Customer c = allInstances.iterator().next();try {
    setActivatedNodes(new Node[]{new DummyBeanNode(c)});
    } catch (IntrospectionException ex) {
    Exceptions.printStackTrace(ex);
    }

    nameField.setText(c.getName());
    cityField.setText(c.getCity());
    }
    }
  • Instead of the above, don't listen for a "Customer" object at all. Instead, listen for the "CustomerNode", which will automatically appear in the Lookup when a new "CustomerNode" is selected. Then, in the editor window set the CustomerNode as the activated node of the editor window:
    @Override
    public void resultChanged(LookupEvent le) {
    Result source = (Result) le.getSource();
    Collection<CustomerNode> allInstances = source.allInstances();
    if (allInstances.iterator().hasNext()) {
    CustomerNode cn = allInstances.iterator().next();
    Customer c = cn.getLookup().lookup(Customer.classsetActivatedNodes(new Node[]{cn});
    nameField.setText(c.getName());
    cityField.setText(c.getCity());
    }
    }

I find the latter solution the simplest, the middle one the most clunky, and the first is probably the official way to do it. However, the last solution, though simple, means that you're not dealing with the domain object directly, but only via the Node, which is not so nice either.

Feedback/comments/insights welcome.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.