C/C++ Pack, Language Model and C/C++ Navigator Tutorial
In this tutorial, you will learn how to create a BeanTreeView navigator based on C/C++ language model.
Once this tutorial is finished, you will have a BeanTreeView navigator that provides structure of currently edited C/C++ file.
Creating module and navigator view.
- Choose File > New Project. In the New Project wizard, choose
NetBeans Plug-in Modules under Categories and Module Project
under Projects. Click Next. Type CppNavigatorTreeView in
Project Name and set Project Location to an appropriate folder on
your disk. If they are not selected, select Standalone Module and Set
as Main Project. Click Next.
- Type org.netbeans.mycppextension in Code Name Base and C/C++
Navigator Tree View in Module Display Name. Click Finish.
- Right-click the project, choose Properties, click
Libraries in the Project Properties dialog box and declare a
dependency on the following APIs:
- Right-click the project, choose Properties, click
Libraries in the Project Properties dialog box and declare a
dependency on the Non-API module (check Show Non-API Modules):
Edit C/C++ Code Model Utilities module dependency.
Set Imlpementation version.
In future module will be added in API.
- Right-click the module project, choose New > File/Folder and
choose Window Component from the NetBeans Module Development
category. Click Next. Choose navigator in the
drop-down list and select Open on Application Start. Click Next.
- Type CppNavigator in Class Name Prefix. Optionally,
add an icon with a dimension of 16x16 pixels. Click Finish.
- Open CppNavigatorTopComponent.java in the Design view.
Right-click in the TopComponent, choose Set Layout, and select
BorderLayout.
- Use the Palette (Ctrl-Shift-8) to drop a
JScrollPane on CppNavigatorTopComponent.java.
Right-click the JScrollPane, choose Change
Variable Name and type navigatorPane.
- Open the Inspector, if it isn't open. (Use the Window menu.) In the Inspector,
select the navigatorPane, open the Properties window
(Ctrl-Shift-7), click the "Code" tab, and add this line to
the Custom Creation Code property (the very last property in the
list):
new BeanTreeView();
BeanTreeView is one of several views provided by the Explorer and
Property Sheet API.
- Click the Source toggle button in the GUI Builder. Right-click
in the Source Editor, and choose Fix imports (Alt-Shift-F). The dependency you set
on "Explorer and Property Sheet API" will cause the import
statement for the BeanTreeView being generated for you by the
IDE.
- In the Bundle.properties file, change the
CTL_CppNavigatorTopComponent and CTL_CppNavigatorAction keys to the
value C/C++ Navigator.
- Right-click the project
node and choose Install/Reload in Development IDE. If a
warning message appears, click OK. When the module installs, look
under the Window menu and you will find a new menu item called C/C++
Navigator, at the top of the list of menu items. Choose it and
you will see the start of your navigator view.
Setup an explorer manager.
- Open the CppnavigatorTopComponent.java in the Source view and add
implements ExplorerManager.Provider to the signature at the
top of the class.
- Next, instantiate the ExplorerManager
as a transient object:
private transient ExplorerManager explorerManager = new ExplorerManager();
- Place the cursor in the signature. A light bulb will prompt you to
let the IDE insert an import statement and implement the abstract
methods. Follow its advice, by clicking on the suggestion, and then
fill out the generated getExplorerManager() as follows:
public ExplorerManager getExplorerManager() {
return explorerManager;
}
- Create field:
private AbstractNode root;
- Now go to the Constructor and add the following after the last existing line:
((BeanTreeView)navigatorPane).setRootVisible(false);
Children.Array children = new Children.SortedArray();
root = new AbstractNode(children);
getExplorerManager().setRootContext(root);
- Fix imports. Select org.openide.nodes.Children for Children.
Create node and nodes support.
Create class called CppDeclarationNode.java and add following content:
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.swing.AbstractAction;
import javax.swing.Action;
import org.netbeans.modules.cnd.api.model.CsmClass;
import org.netbeans.modules.cnd.api.model.CsmClassifier;
import org.netbeans.modules.cnd.api.model.CsmDeclaration;
import org.netbeans.modules.cnd.api.model.CsmEnum;
import org.netbeans.modules.cnd.api.model.CsmEnumerator;
import org.netbeans.modules.cnd.api.model.CsmFunction;
import org.netbeans.modules.cnd.api.model.CsmInclude;
import org.netbeans.modules.cnd.api.model.CsmMacro;
import org.netbeans.modules.cnd.api.model.CsmMember;
import org.netbeans.modules.cnd.api.model.CsmNamespaceDefinition;
import org.netbeans.modules.cnd.api.model.CsmObject;
import org.netbeans.modules.cnd.api.model.CsmOffsetable;
import org.netbeans.modules.cnd.api.model.CsmParameter;
import org.netbeans.modules.cnd.api.model.CsmType;
import org.netbeans.modules.cnd.api.model.util.CsmKindUtilities;
import org.netbeans.modules.cnd.modelutil.AbstractCsmNode;
import org.netbeans.modules.cnd.modelutil.CsmUtilities;
import org.openide.nodes.Children;
import org.openide.util.Utilities;
/**
* Navigator Tree node.
*/
public class CppDeclarationNode extends AbstractCsmNode implements Comparable {
private Image icon;
private CsmObject object;
private CppDeclarationNode(CsmObject element) {
super(new NavigatorChildren(element));
object = element;
}
private CppDeclarationNode(Children children, CsmObject element) {
super(children);
object = element;
}
public CsmObject getCsmObject() {
return object;
}
public int compareTo(Object o) {
if( o instanceof CppDeclarationNode ) {
return getDisplayName().compareTo(((CppDeclarationNode) o).getDisplayName());
}
return 0;
}
public void setIcon(Image icon) {
this.icon = icon;
}
public Image getIcon(int param) {
if (icon != null){
return icon;
}
return super.getIcon(param);
}
public Image getOpenedIcon(int param) {
return getIcon(param);
}
public Action getPreferredAction() {
if (CsmKindUtilities.isOffsetable(object)){
return new AbstractAction(){
public void actionPerformed(ActionEvent e) {
CsmUtilities.openSource((CsmOffsetable)object);
}
};
}
return super.getPreferredAction();
}
public static CppDeclarationNode nodeFactory(CsmObject element){
CppDeclarationNode node = null;
if (CsmKindUtilities.isClassifier(element)){
node = new CppDeclarationNode(element);
node.setName(((CsmClassifier)element).getName());
} else if(CsmKindUtilities.isNamespaceDefinition(element)){
node = new CppDeclarationNode(element);
node.setName(((CsmNamespaceDefinition)element).getName());
} else if(CsmKindUtilities.isDeclaration(element)){
node = new CppDeclarationNode(Children.LEAF,element);
node.setName(((CsmDeclaration)element).getName());
if(CsmKindUtilities.isFunction(element)){
node.setName(getSignature((CsmFunction)element));
} else {
node.setName(((CsmDeclaration)element).getName());
}
} else if(CsmKindUtilities.isEnumerator(element)){
node = new CppDeclarationNode(Children.LEAF,element);
node.setName(((CsmEnumerator)element).getName());
} else if(CsmKindUtilities.isMacro(element)){
node = new CppDeclarationNode(Children.LEAF,element);
node.setName(((CsmMacro)element).getName());
node.setIcon(Utilities.loadImage("org/netbeans/mycppextension/cppnavigatortreeview/directive.png", true));
} else if(element instanceof CsmInclude){
node = new CppDeclarationNode(Children.LEAF,element);
node.setName(((CsmInclude)element).getIncludeName());
node.setIcon(Utilities.loadImage("org/netbeans/mycppextension/cppnavigatortreeview/directive.png", true));
}
return node;
}
private static String getSignature(CsmFunction fun) {
StringBuffer sb = new StringBuffer(fun.getName());
sb.append('(');
boolean addComma = false;
for( Iterator iter = fun.getParameters().iterator(); iter.hasNext(); ) {
CsmParameter par = (CsmParameter) iter.next();
if( addComma ) {
sb.append(", ");
} else {
addComma = true;
}
CsmType type = par.getType();
if( type != null ) {
sb.append(type.getText());
sb.append(' ');
}
sb.append(par.getName());
}
sb.append(')');
return sb.toString();
}
private static class NavigatorChildren extends Children.SortedArray {
private CsmObject element;
public NavigatorChildren(CsmObject element){
this.element = element;
}
protected Collection initCollection() {
Collection retValue = new ArrayList();
if (CsmKindUtilities.isClass(element)){
CsmClass cls = (CsmClass)element;
for (Iterator it = cls.getMembers().iterator(); it.hasNext();){
CppDeclarationNode node = nodeFactory((CsmMember)it.next());
if (node != null){
retValue.add(node);
}
}
} else if (CsmKindUtilities.isEnum(element)){
CsmEnum cls = (CsmEnum)element;
for (Iterator it = cls.getEnumerators().iterator(); it.hasNext();){
CppDeclarationNode node = nodeFactory((CsmEnumerator)it.next());
if (node != null){
retValue.add(node);
}
}
} else if(CsmKindUtilities.isNamespaceDefinition(element)){
CsmNamespaceDefinition ns = (CsmNamespaceDefinition)element;
for (Iterator it = ns.getDeclarations().iterator(); it.hasNext();){
CppDeclarationNode node = nodeFactory((CsmDeclaration)it.next());
if (node != null){
retValue.add(node);
}
}
}
return retValue;
}
}
}
Class extends AbstractCsmNode and override methods:
getIcon(int) {
getOpenedIcon(int)
getPreferredAction()
Method getPreferredAction() uses implementation of module
C/C++ Code Model Utilities to open model element in editor.
Also class implements Comparable interface.
Classifier and name space nodes use Children.SortedArray for children array.
Comparable interface allow sorting nodes by name.
Inner class NavigatorChildren extends Children.SortedArray and is used for children array. Class override method:
protected Collection initCollection()
Method responsible for lazy filling classes and name spaces nodes' content.
Both class CppDeclarationNode constructors are private. Static factory method nodeFactory(CsmObject)
is responsible for node creation.
C/C++ language support.
Modify class
CppNavigatorTopComponent.
- Add in implements list two interfaces:
PropertyChangeListener, CsmModelListener
and implements abstract methods.
- Create field that will keep model file:
private CsmFile csmFile;
- At first we needed register and unregister component and model listeners. Replace methods:
public void componentOpened() {
TopComponent.getRegistry().addPropertyChangeListener(this);
CsmModelAccessor.getModel().addModelListener(this);
checkNodes();
}
public void componentClosed() {
TopComponent.getRegistry().removePropertyChangeListener(this);
CsmModelAccessor.getModel().removeModelListener(this);
setFile(null);
}
- Create private methods that find model file by active node:
private void checkNodes(){
checkNodes(TopComponent.getRegistry().getCurrentNodes());
}
private void checkNodes(Node[] arr){
if (arr != null) {
for (int j = 0; j < arr.length; j++) {
CsmFile file = CsmUtilities.getCsmFile(arr[j],false);
if (file != null)
setFile(file);
}
}
}
Method checkNodes() uses implementation of module
C/C++ Code Model Utilities to find model file by node.
- Create methods that setup navigator content:
private void setFile(CsmFile file) {
if (csmFile != file){
csmFile = file;
update();
}
}
private synchronized void update() {
if (csmFile != null){
final Children children = root.getChildren();
if (!children.MUTEX.isReadAccess()){
children.MUTEX.writeAccess(new Runnable(){
public void run() {
children.remove(children.getNodes());
for(Iterator it = csmFile.getIncludes().iterator(); it.hasNext();){
CsmInclude element = (CsmInclude)it.next();
Node node = CppDeclarationNode.nodeFactory((CsmObject)element);
if (node != null){
children.add(new Node[]{node});
}
}
for(Iterator it = csmFile.getMacros().iterator(); it.hasNext();){
CsmMacro element = (CsmMacro)it.next();
Node node = CppDeclarationNode.nodeFactory((CsmObject)element);
if (node != null){
children.add(new Node[]{node});
}
}
for(Iterator it = csmFile.getDeclarations().iterator(); it.hasNext();){
CsmDeclaration element = (CsmDeclaration)it.next();
Node node = CppDeclarationNode.nodeFactory((CsmObject)element);
if (node != null){
children.add(new Node[]{node});
}
}
}
});
}
} else {
final Children children = root.getChildren();
if (!children.MUTEX.isReadAccess()){
children.MUTEX.writeAccess(new Runnable(){
public void run() {
children.remove(children.getNodes());
}
});
}
}
}
Method update() refreshes navigator tree.
Method passes through model file include directives, macros and declarations.
- Add component support. Replace method:
public void propertyChange(PropertyChangeEvent evt) {
if (TopComponent.Registry.PROP_ACTIVATED.equals(evt.getPropertyName())){
checkNodes();
}
}
- Add Model support. Replace methods:
public void projectOpened(CsmProject project) {
checkNodes();
}
public void projectClosed(CsmProject project) {
if (csmFile != null){
if (csmFile.getProject() == project){
setFile(null);
}
}
}
public void modelChanged(CsmChangeEvent e) {
if (csmFile != null){
if (e.getChangedFiles().contains(csmFile)){
update();
}
} else {
checkNodes();
}
}
- Fix imports. Select java.itil.Iterator for Iterator and
org.openide.nodes.Node for Node.
- Save icon:
with name directive.png in the package
org.netbeans.mycppextension.cppnavigatortreeview.
- Right-click the project node and choose Install/Reload in Development IDE.
Open C/C++ project. You can see edited file declarations in C/C++ Navigator:
Downloads
Download NetBeans Project or
NetBeans Module.