Extend C/C++ Pack with Switch Source/Header files plug-in
In this tutorial, you will see how easy to extend C/C++ Pack base functionality with feature requested by community.
Once this tutorial is finished, you will have action for quick switching between associated Source and Header files (same name but different suffix). Action name will be file-type sensitive, has associated shortcut and be presented by short name in Go To submenu of editor's popup menu.
Creating test C++ application.
We need test application with source and header files to test our plug-in. Let's create test C++ Application:
Choose File > New Project. In the New Project wizard, choose C/C++ Development under Categories and C/C++ Application under Projects. Click Next. Type SwitchTest in Project Name and set Project Location to an appropriate folder on your disk. Uncheck Set as Main Project. Click Finish.
In Project View right click on Header Files to display context menu and choose New->C++ Header File. In Wizard leave File Name as newfile. Click Finish. newfile.h is created.
In Project View right click on Source Files to display context menu and choose New->Empty C++ File. In Wizard leave File Name as newfile. Click Finish. newfile.cc is created.
Creating Netbeans plug-in Module and Action.
Choose File > New Project. In the New Project wizard, choose NetBeans Plug-in Modules under Categories and Module Project under Projects. Click Next. Type CppSwitchFiles 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.modules.cppswitchfiles in Code Name Base. Click Finish.
Right-click the module project, choose New > File/Folder and choose Action... from the context menu.
In Wizard choose Action Type as Conditionally Enabled. Leave selected User Selects One Node and Cookie Class as DataObject
-
Click Next and update GUI Registration. Change Category to Edit and Menu to Navigate:
Click Next. Change Class Name to CppSwitchAction and Display Name to C/C++ Switch Files. Click Finish.
Right-click the module project, choose Properties, click Display. Change Display Name to C/C++ Switch Files and Display Category to C/C++. Click OK.
As result our projects look like:
First Review
Right-click the “C/C++ Switch Files” project node and choose Install/Reload in Development IDE. If a warning message appears, click OK. You can alway uninstall module using Tools->Module Manager Category C/C++
You can see new menu item in Navigate menu:
Implementing Switch Files Action.
Add Libraries dependency
Right-click the project, choose Properties, click Libraries in the Project Properties dialog box and declare a dependency on the following APIs:
C/C++ Core
Configure enable state of action
Enable action only for Header and Source C/C++ files. Change cookieClasses() method (use Alt+Shift+F to fix imports):
protected Class[] cookieClasses() { return new Class[] { HDataObject.class, CDataObject.class, CCDataObject.class }; }
Add utilities to find file where to switch
In activated nodes look for Source or Header file and find associated Header or Source.
private FileObject findToggleFile(final Node[] activatedNodes) { FileObject res = null; // check whether current file is C++ Source file DataObject dob = (CCDataObject) activatedNodes[0].getLookup().lookup(CCDataObject.class); if (dob == null) { // check whether current file is C Source file dob = (CDataObject) activatedNodes[0].getLookup().lookup(CDataObject.class); } if (dob != null) { // it was Source file, find Header res = findBrother(dob, getSuffices(HDataLoader.getInstance().getExtensions())); } else { // check whether current file is Header file dob = (HDataObject) activatedNodes[0].getLookup().lookup(HDataObject.class); if (dob != null) { // try to find C++ Source file res = findBrother(dob, getSuffices(CCDataLoader.getInstance().getExtensions())); if (res == null) { // try to find C Source file res = findBrother(dob, getSuffices(CDataLoader.getInstance().getExtensions())); } } } return res; } private FileObject findBrother(DataObject dob, String[] ext) { assert (dob != null); assert (dob.getPrimaryFile() != null); // get a file object associated with the data object FileObject fo = dob.getPrimaryFile(); if (ext != null && ext.length > 0) { // try to find a file with the same name and one of passed extensions for (int i = 0; i < ext.length; i++) { // use FileUtilities to find brother of the file object FileObject res = FileUtil.findBrother(fo, ext[i]); if (res != null) { return res; } } } return null; } private static String[] getSuffices(ExtensionList list) { Listsuffixes = new ArrayList (); for (Enumeration e = list.extensions(); e != null && e.hasMoreElements();) { String ex = (String) e.nextElement(); suffixes.add(ex); } return (String[])suffixes.toArray(new String[suffixes.size()]); }
Implement method to open file in editor
private void doToggle(final FileObject fo) { assert (fo != null); try { // find a data object for the input file object DataObject toggled = DataObject.find(fo); if (toggled != null) { // check if the data object has possibility to be opened in editor final OpenCookie oc = (OpenCookie)toggled.getCookie(OpenCookie.class); if (oc != null) { // try to open ASAP, but better not in EQ RequestProcessor.getDefault().post(new Runnable() { public void run() { // open component oc.open(); } }, 0, Thread.MAX_PRIORITY); } } } catch (DataObjectNotFoundException ex) { // may be error message? } }
Implement performAction
Change performAction() method to find file where to switch:
protected void performAction(Node[] activatedNodes) { // find file where to switch FileObject res = findToggleFile(activatedNodes); if (res != null) { doToggle(res); } }
Use Action
Right-click the “C/C++ Switch Files” project node and choose Install/Reload in Development IDE. Open newfile.cc or newfile.h and try Navigate->C/C++ Switch File menu item. Files will be switched (opened if necessary).
Improve Usability - I
In this part we plan to introduce shortcut Ctrl+Shift+A for our action and put it in C/C++ editor context menu.
Register shortcut for the Action
Open layer.xml file and add following on filesystem level:
<filesystem> ... <folder name="Shortcuts"> <file name="DS-A.shadow"> <attr name="originalFile" stringvalue="Actions/Edit/org-netbeans-modules-cppswitchfiles-CppSwitchAction.instance"/> </file> </folder> ... </filesystem>
Register Action in editor context menu
Open layer.xml file and add following on filesystem level for C and C++ languages:
<filesystem> ... <folder name="Editors"> <folder name="text"> <folder name="x-c++"> <folder name="Popup"> <folder name="goto"> <file name="org-netbeans-modules-cppswitchfiles-CppSwitchAction .shadow"> <attr name="originalFile" stringvalue="Actions/Edit/org-netbeans-modules-cppswitchfiles-CppSwitchAction.instance"/> </file> </folder> </folder> </folder> <folder name="x-c"> <folder name="Popup"> <folder name="goto"> <file name="org-netbeans-modules-cppswitchfiles-CppSwitchAction.shadow"> <attr name="originalFile" stringvalue="Actions/Edit/org-netbeans-modules-cppswitchfiles-CppSwitchAction.instance"/> </file> </folder> </folder> </folder> </folder> </folder> ... </filesystem>
Use Action
Right-click the “C/C++ Switch Files” project node and choose Install/Reload in Development IDE. Open newfile.cc or newfile.h and try Ctrl+Shift+A. Files will be switched. Make sure context menu has added action.
Improve Usability - II
In this part we plan to make our action consistent with Netbeans infrastructure and make it file-type sensitive.
Add Libraries dependency
Right-click the project, choose Properties, click Libraries in the Project Properties dialog box and declare a dependency on the Editor Library.
Remember caret positions
To have possibility to go back/forward between caret positions with Alt+K/Alt+L update doToggle method:
private void doToggle(final FileObject fo) { ... if (oc != null) { // remember current caret position JTextComponent textComponent = Registry.getMostActiveComponent(); JumpList.checkAddEntry(textComponent); // try to open ASAP, but better not in EQ ... }
Change Action name to be file type sensitive
By Netbeans convetions Go To submenu uses trimmed
name for menu items (Line... instead of Go to Line...) and we will add the same support for our action.
Also let's make action name to be file type sensitive.
Update Bundle.properties:
CTL_CppSwitchAction=C/C++ Switch Files OpenIDE-Module-Display-Category=C/C++ OpenIDE-Module-Name=C/C++ Switch Files #names for Navigate menu CppGoToSourceFileAction=C/C++ Switch to Source File CppGoToHeaderFileAction=C/C++ Switch to Header File #short names for Go To popup submenu goto-cpp-switch-file=Source/Header File goto-cpp-source-file=&Source File goto-cpp-header-file=&Header File
Change CppSwitchAction
Add necessary support in CppSwitchAction to handle file-type. Update action name in Navigate menu and editor context submenu to be file-type sensitive:
public String getName() { String trimmedNameKey = "goto-cpp-switch-file"; //NOI18N String fullNameKey = "CTL_CppSwitchAction"; //NOI18N if (isEnabled()) { switch (getToggleInfo(getActivatedNodes())) { case NodeKind.HEADER: trimmedNameKey = "goto-cpp-header-file"; //NOI18N fullNameKey = "CppGoToHeaderFileAction"; //NOI18N break; case NodeKind.SOURCE: trimmedNameKey = "goto-cpp-source-file"; //NOI18N fullNameKey = "CppGoToSourceFileAction"; //NOI18N break; } } String trimmedName = NbBundle.getMessage(CppSwitchAction.class, trimmedNameKey);; putValue(ExtKit.TRIMMED_TEXT, trimmedName); putValue(BaseAction.POPUP_MENU_TEXT, trimmedName); return NbBundle.getMessage(CppSwitchAction.class, fullNameKey); } private static final class NodeKind { private static final int HEADER = 1; private static final int SOURCE = 2; private static final int NONE = 0; } private int getToggleInfo(Node[] activatedNodes) { if (activatedNodes != null && activatedNodes.length == 1) { if (activatedNodes[0].getLookup().lookup(HDataObject.class) != null) { return NodeKind.SOURCE; } else if (activatedNodes[0].getLookup().lookup(CCDataObject.class) != null || activatedNodes[0].getLookup().lookup(CDataObject.class) != null) { return NodeKind.HEADER; } } return NodeKind.NONE; }
Use Action
Right-click the “C/C++ Switch Files” project node and choose Install/Reload in Development IDE. Open newfile.cc or newfile.h, make sure context menu has short name for action. Also check, that name is file-type sensitive.