Thursday, June 21, 2012

Showing an Action on a TopComponent Node

By: Geertjan Wielenga | Product Manager

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.

Join the discussion

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.Captcha
 

Visit the Oracle Blog

 

Contact Us

Oracle

Integrated Cloud Applications & Platform Services