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.
C/C++ Code Model Utilities
Edit C/C++ Code Model Utilities module dependency. Set Imlpementation version. In future module will be added in API.
new BeanTreeView();
BeanTreeView is one of several views provided by the Explorer and Property Sheet API.
Setup an explorer manager.
private transient ExplorerManager explorerManager = new ExplorerManager();
public ExplorerManager getExplorerManager() { return explorerManager; }
private AbstractNode root;
((BeanTreeView)navigatorPane).setRootVisible(false); Children.Array children = new Children.SortedArray(); root = new AbstractNode(children); getExplorerManager().setRootContext(root);
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.PropertyChangeListener, CsmModelListener
and implements abstract methods.
private CsmFile csmFile;
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); }
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.
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.
public void propertyChange(PropertyChangeEvent evt) { if (TopComponent.Registry.PROP_ACTIVATED.equals(evt.getPropertyName())){ checkNodes(); } }
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(); } }
with name directive.png in the package org.netbeans.mycppextension.cppnavigatortreeview.

Downloads
Download NetBeans Project or NetBeans Module.