Showing an Action on a TopComponent Node
- by Geertjan
Let's say you want to extend the tools in NetBeans IDE, specifically for TopComponents. When the user right-clicks in the Projects window (or Files window or Favorites window) on a Java class that extends TopComponent, a menu item should be available for branding the TopComponent. What "branding" entails is, at this stage, a secondary question. The primary question, from an implementation point of view, is "how do I create an action that is only shown when the user right-clicks on a TopComponent?"
Here's the solution, in NetBeans IDE 7.2 (the "lazy" attribute, here set to false, is new in 7.2):
import com.sun.source.tree.ClassTree;
import com.sun.source.util.TreePathScanner;
import java.awt.event.ActionEvent;
import java.io.IOException;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JOptionPane;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.CompilationInfo;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.JavaSource.Phase;
import org.netbeans.api.java.source.Task;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionRegistration;
import org.openide.awt.DynamicMenuContent;
import org.openide.loaders.DataObject;
import org.openide.util.ContextAwareAction;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages;
import org.openide.util.Utilities;
@ActionID(
category = "Tools",
id = "org.tc.customizer.BrandTopComponentAction")
@ActionRegistration(
displayName = "#CTL_BrandTopComponentAction",
lazy = false)
@ActionReferences({
@ActionReference(path = "Loaders/text/x-java/Actions", position = 150)
})
@Messages("CTL_BrandTopComponentAction=Brand")
public final class BrandTopComponentAction extends AbstractAction implements ContextAwareAction {
private final DataObject dobj;
public BrandTopComponentAction() {
this(Utilities.actionsGlobalContext());
}
public BrandTopComponentAction(Lookup context) {
super(Bundle.CTL_BrandTopComponentAction());
this.dobj = context.lookup(DataObject.class);
//Enable the menu item only if we're dealing with a TopComponent
JavaSource javaSource = JavaSource.forFileObject(dobj.getPrimaryFile());
try {
javaSource.runUserActionTask(new ScanForTopComponentTask(this), true);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
//Hide the menu item if it isn't enabled:
putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, true);
}
@Override
public void actionPerformed(ActionEvent ev) {
JOptionPane.showMessageDialog(null, "Hurray, I am a TopComponent!");
//Now add your code for showing a dialog,
//where the dialog will display UI for branding the TopComponent somehow
//and retrieve those branding values
//and then change the TopComponent class accordingly.
}
@Override
public Action createContextAwareInstance(Lookup actionContext) {
return new BrandTopComponentAction(actionContext);
}
private static class ScanForTopComponentTask implements Task<CompilationController> {
private final BrandTopComponentAction action;
private ScanForTopComponentTask(BrandTopComponentAction action) {
this.action = action;
}
@Override
public void run(CompilationController compilationController) throws Exception {
compilationController.toPhase(Phase.ELEMENTS_RESOLVED);
new MemberVisitor(compilationController, action).scan(
compilationController.getCompilationUnit(), null);
}
}
private static class MemberVisitor extends TreePathScanner<Void, Void> {
private CompilationInfo info;
private final AbstractAction action;
public MemberVisitor(CompilationInfo info, AbstractAction action) {
this.info = info;
this.action = action;
}
@Override
public Void visitClass(ClassTree t, Void v) {
Element el = info.getTrees().getElement(getCurrentPath());
if (el != null) {
TypeElement te = (TypeElement) el;
if (te.getSuperclass().toString().equals("org.openide.windows.TopComponent")){
action.setEnabled(true);
} else {
action.setEnabled(false);
}
}
return null;
}
}
}
The code above is the result of combining various tutorials found on the NetBeans Platform Learning Trail.