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:

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

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

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

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

  2. Type org.netbeans.modules.cppswitchfiles in Code Name Base. Click Finish.

  3. Right-click the module project, choose New > File/Folder and choose Action... from the context menu.

  4. In Wizard choose Action Type as Conditionally Enabled. Leave selected User Selects One Node and Cookie Class as DataObject




  5. Click Next and update GUI Registration. Change Category to Edit and Menu to Navigate:




  6. Click Next. Change Class Name to CppSwitchAction and Display Name to C/C++ Switch Files. Click Finish.

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

  1. Nodes API

  2. C/C++ Core

  3. Datasystems API

  4. File System API

  5. Utilites API

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) {
        List suffixes = 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.




Downloads

Download sources of the tutorial or result nbm files.

Project Features

About this Project

CND was started in November 2009, is owned by DimaZh, and has 191 members.
By use of this website, you agree to the NetBeans Policies and Terms of Use (revision 20160708.bf2ac18). © 2014, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo
 
 
Close
loading
Please Confirm
Close