plugin template, docking feature, and c++ core guidelines
I’ve been footling around with an existing plugin and I thought I’d try applying the c++ core guideline document (as MSVC has support for them) to the project
It produced a bimcj of warnings for dockingfeature code from the plugin template (https://github.com/npp-plugins/plugintemplate) for methods not being noexcept.
Having had a look at them, I’m a little confused, as there’s no documentation explaining this.
In no particular order:
There’s a lot of virtual functions in Window.h where it seems odd for them to be virtual. (getWindowRect for instance).
It also ends up with the slightly odd situation that getHeight returns 0 if the window isn’t visible, but it doesn’t use the isVisible method (leading to code-nearly-duplication) - presumably because that is a virtual method and might have a different idea as to whether or not the window is visible to what windows does.
Additionally two of the functions are commented as ‘should NEVER be const’ without any explanation as to why, because they look pretty const.
Adding noexcept to a virtual function is generally a bit risky, as you don’t know how clients are going to override it, but I’m not sure I can see the point of overriding (e.g.) getHeight.
Also (in passing) the getFocus method should probably be called setFocus.
I suppose I should also point out that the init() method looks like a constructor, and why isn’t it one? At least in the plugin I’m working on, by the time it needs to create the docked window, it already knows enough to call the init method from the constructor for the class controlling the docked window.
So I guess what I’m asking is ‘why?’, and what versions of C++ are used by plugins, and whether anyone either does or should rely on some of these methods being virtual.
I haven’t written a plugin that uses the docking interface, but as far as I can tell, the only mandatory part of the DockingFeature folder is Docking.h, which is required by the NPPM_DMMREGASDCKDLG method.
The rest appears to form a C++ wrapper around a Windows dialog. It looks as if it is taken from Notepad++ source (Window.h, StaticDialog and DockingWnd); so my guess is that the GoToLineDlg example in the template was built up as quickly as possible based on structures and logic already available and familiar to Notepad++ authors.
My point being that most of that code was written for Notepad++. My guess is that it was carried over intact as a quick way to build an example, not built from scratch in such a way that everything in it would make sense in the context of a typical plugin. Trying to “rationalize” it from that perspective is probably not possible.
As I wrote above, I haven’t attempted to write a dockable dialog. If I were to do so, I would begin by taking only the Docking.h file as normative, and look to the rest only as a resource for discovering what a docking dialog is expected to do. Of course, actually trying that might change my mind…
Hmmm. I’ve tried debugging this and found that the DMN_DOCK message is sent to the plugin, from https://github.com/notepad-plus-plus/notepad-plus-plus/blob/07041f456545b8339323d2b0441007923bec7571/PowerEditor/src/WinControls/DockingWnd/DockingManager.cpp#L691
Which sort of implies that it is (or was) possible to have undocked dialogues - not that I can find out how to make that happen.
So that header is also required.
DMN_DOCK message is sent to the plugin
undocked dialogues - not that I can find out how to make that happen
The user can drag the dialog into or out of the dock. Aside from that, I’d guess one sends the DMM_FLOAT message from dockingResource.h… which, on examination, leads me to retract most of what I said earlier in this thread.
My presumption — that the interface for docking dialogs would be a C interface, like everything else for plugins — appears to have been mistaken. I’m seeing that the DMM_FLOAT message in Notepad++ expects LPARAM to be a pointer to a DockingCont, which is based on a StaticDialog, which is very much a C++ structure, not a C structure.
So perhaps it is necessary to copy the entire set of C++ declarations: it looks like Notepad++ expects to see a very specific structure surrounding a docking dialog. That would also mean the compiler for a plugin that implements a docking dialog must be ABI compatible with the one used to compile Notepad++.
All of which points back to the concerns in your original post being valid.
To be precise, the LPARAM is converted to a
DockingContpointer by means of reinterpret_cast. The plugin only needs to provide the equivalent of an opaque pointer. It’s the job of N++ to (re-)interpret that pointer as something it can use.
It follows that any compiled language capable of addressing objects via pointers can use the plugin API. See, for example, the many re-implementations of the plugin template in C#, Object Pascal (and derivatives like Modula-2 — e.g., the WebEdit plugin), V, etc.
To be precise, the LPARAM is converted to a DockingCont pointer by means of reinterpret_cast . The plugin only needs to provide the equivalent of an opaque pointer. It’s the job of N++ to (re-)interpret that pointer as something it can use.
Thank you for that clarification. I think I see, somewhat. The only reference to DockingCont in the plugin template is in a comment. So your point, I take it, is that the pointer to DockingCont is opaque to the plugin; the address is something that would come from Notepad++. In the absence of documentation, though, it is not obvious — to me, anyway — how this pointer would be obtained. But then, it could be that the messages that use it (like DMM_FLOAT) are meant for Notepad++ internal use, and they’re only in the plugin demo code because they’re in the same file as the DMN_* notifications, which are apparently sent to the dialog procedure for the dockable dialog and must be meant for use by plugins.
So, are we back to what I speculated earlier (and then doubted): that it is a C interface, and the DockingDlgInterface based on StaticDialog based on Window classes are just part of an example, not structures that need to be present as such in a plugin that implements a dockable dialog?
The essential point to remember is that the every facet of the API surface is C-compatible, just like the Win32 API, which underlies all of N++'s internal functions. The details of how N++ dispatches plugin messages naturally involves a few C++ semantics, because it’s a C++ application. Plugins can implement their own C++ types, too, but nothing in the API says they have to. If you just want to display a free-floating form, you can use whatever functions your plugin natively supports, from
The (poorly documented) purpose of the docking API is to register a form with the Docking Manager. In practice, that means preserving the form’s position and geometry in
config.xml, so that N++ can optionally reload it when the application starts. To do that, a plugin sends
NPPM_DMMREGASDCKDLGwith a pointer to a C-like structure filled with some basic parameters, as shown in the developers’ manual.
The form’s creation is entirely up to the plugin. N++ simply invokes the function that creates the form by calling through a function pointer. No plugin is required to instantiate a
DockingContor any other object derived from a C++ class. Examples of plugins that do this anyway are special cases, not illustrations of how the docking API works.