Friday, January 12, 2007

NetBeans Platform: CallbackSystemAction sync madness

An annoying problem I had this week involved the CallbackSystemAction class.

Basically the general ideea of this type of action is this: you have the Action implementation, but the "#actionPerformed code" is deferred.

What was my problem: depending on the user selection in an explorer, my action was supposed to become enabled or not (plus some othe conditions).

So, the javadoc says that way to do it is by setting an action in the ActionMap of the TopComponent.

TopComponent tc = ...;
javax.swing.Action yourCopyAction = ...; // the action to invoke instead of Copy

CopyAction globalCopyAction = SystemAction.get (CopyAction.class);
Object key = globalCopyAction.getActionMapKey(); // key is a special value defined by all CallbackSystemActions

// and finally:
tc.getActionMap ().put (key, yourCopyAction);

The problem is that there is no listener on the action map. So if I want later to disable my action and I do a tc.getActionMap().remove(key) -- it won't work ! I mean, it will work if you change the current TopComponent and come back to the original (I guess there are some event thrown there that CallbackSystemAction catches and does a refresh).

So, using the ActionMap.put and #remove doesn't work !

Ok, I said to myself, I'll just use the deprecated #setActionPerformer , which seems to work better. Only it is deprecated, which is not good.

I went and complained on the dev@openide mailing list and I got a tip that I should change my TopComponent's lookup in order to fire the events that CallbackSystemAction might catch to refresh itself.

But you can't call #associateLookup twice ! Arr.

So I just started reading carefully the CallbackSystemAction source code and I've noticed that it does put a listener on the enabled/disable state of the TopComponent's action (ie. yourCopyAction in the example above).

So -- what's the solution ? Dead simple: call getActionMap().put(...) once and then just do a #setEnabled on your Action. Simple -- but very non-intuitive.

Actually in this situation I would say that the Javadoc was more confusing than useful because it says

The action will be automatically disabled when it has no performer.

and removing your action from the ActionMap means exactly that: no performer...

1 comment:

Joseph Okharedia said...

I'm not very experienced in netbeans platform, but if you say that it works when you lose focus of your top component and regain it. Why dont you just programmatically close and reopen the topcomponent.