|
|
||
Tim Boudreau's BlogHow to replace the selection model in the NetBeans PlatformPosted by timboudreau on January 23, 2007 at 12:48 AM | Comments (1)NetBeans has a selection model where whatever logical window has focus owns the selection (determines what actions are enabled/disabled, what the property sheet shows, etc.). Some applications need something different - for example, an image editor probably wants the current image to own the selection no matter what logical window has focus. You can completely replace the selection management mechanism in the NetBeans platform, to accomodate any selection model you want. Here's how...
First, let's go over how NetBeans defines selection. The selection is a Lookup. That's basically a bag-o-stuff owned (by default) by whatever component has focus. So, an Let me back up a bit...a Lookup is like a Map, where the keys are Class objects and the value for any Class key is a Collection of zero or more instances of that class. A lot of lookup-like APIs use Strings for keys (think JNDI - when you realize that could have been done in a type-safe way and wasn't, it seems kind of grotesque and depressing, doesn't it? A Lookup may be a magic bag-o-stuff, but it's a type-safe magic bag-o-stuff - I want all the help the compiler can give me!); Lookup is nicer in that you have type-safety - if I call Map.get ("foo"), I have no idea what I will get back - the compiler cannot help me; if I call Lookup.lookup (MyObject.class), I know I'm getting back instances of MyObject.
For some applications, that's not what you want. I discovered this when working on the Imagine project - this is an image editor. There are palettes that display attributes of the image being edited. It would be very disconcerting if the component that shows all of the individual layers that make up the currently edited image suddenly disappeared when you clicked the tool palette to select a different tool - the image you are editing hasn't changed. I spent the last couple of days here in New Orleans working with some folks with a similar problem - they are building a CRM application on the NetBeans Platform, and the appropriate context for actions and palettes is the selected Customer - it doesn't matter which window has focus.
public final class Ctx implements ContextGlobalProvider {
private final InstanceContent content = new InstanceContent();
private final Lookup lkp = new AbstractLookup (content);
public Lookup getLookup() {
return lkp;
}
}
The next step is to actually put something in the empty lookup, whose content we control. So we add a method:
public void setContent (Collection c) {
ic.set (c, null);
}
Depending on what kind of contents you want the lookup to have, you might expose add, remove, or whatever methods you want. If, as in the CRM application, there is only one selected customer, you might do something like
public void setCustomer (Customer customer) {
Customer old = lkp.lookup (Customer.class);
if (((old == null) != (customer == null)) || (customer != null && !customer.equals(old))) {
ic.remove (old);
if (customer != null) {
ic.add (customer);
}
}
}
Now you have a guarantee that there is only either zero or one Customer objects selectable. You can always put addition objects of other types into the global selection - that's the whole point - you're in control. If a customer might have multiple addresses or names, you can put as many Address objects in there as you need - the UI that displays them just needs to be able to handle getting more than one.
Then you have the question of event model; in the case of the CRM app, there are a bunch of UI pieces that display the address, etc. of a customer. Different user roles will see different attributes. There are two ways to solve this: First is the Swing-like way, where you can add property change listeners to a Customer object, and the UI that cares about it can listen for changes. But this introduces boatloads of bookkeeping, because probably Address, etc. objects all ought to have listeners, and pretty soon you are drowning in potential memory leaks and objects listening to each other. Particularly in this case, where the Customer object arrived from a web service and its object-identity is determined by its database id.
So we can simply make the model more granular - object identity remains the same, but we need to update all UI's showing an attribute of a customer when it changes. Since we control the selection model, we can simply remove the current customer and re-add it. Slightly less efficient, but one line of code and a thousand trips through a heap-dump averted - just remove The point here isn't so much that the above is good or bad, but that if you're writing an application on the NetBeans Platform, you can create whatever selection model makes sense for your application.
The final step is to register the custom implementation of #-org.netbeans.modules.openide.windows.GlobalActionContextImpl net.java.dev.imagine.CtxThese lines do two things. The first is an extension to the spec, legally embedded in a comment - Lookup lets you delete an entry by putting its name in a comment line preceded by a minus sign. So the first line ensures that the default selection model provided by the NetBeans window system is removed. The following line, in accordance with the spec, registers our own selection model implementation - an implementation of ContextGlobalProvider, by listing its fully-qualified class name. While all of this may take a moment to digest, the important fact is that the NetBeans Platform and window system are not married to the NetBeans IDE's notion of what is selection context - you can customize how selection works to your heart's content. Bookmark blog post: CommentsComments are listed in date ascending order (oldest first) | Post Comment
| ||
|
|