Showing an Action on a TopComponent Node

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.

Comments:

Post a Comment:
  • HTML Syntax: NOT allowed
About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
23
24
25
26
27
28
29
30
   
       
Today