CONTEXTE :
jN Npp Plugin
jN (JavaScript for Notepad++) allows you to extend Notepad++ by using JavaScript.
jN uses the built-in javascript engine of Microsoft Windows. This powerful engine allows to access a lot of ActiveX base services like Shell, WMI of operating system. Due to automate Notepad++ jN wraps the Notepad++ API into ActiveX interfaces accessible via global objects Editor and System.

=-=-=-=-=-=-=

J'ai actuellement le code suivant dans un fichier dans "Editor.cpp"

-------------

❨
#include "Editor.h"
#include "common\System.h"
#include "DockableDialog.h"
#define SUBCLASSING TEXT("jN.Npp.Subclassing")
const TCHAR* langs[] ={TEXT("TXT"), /* bla bla bla */ , TEXT("EXTERNAL")};
CEditor::~CEditor(void){
	// Release Views
	for(int i=0; i<m_ViewsNumber; i++){
		m_Views[i]->Release();
	}
	// Release Scripting elements
	if (m_Listener){
		delete m_Listener;
		m_Listener = NULL;
	}
	// Restore WndProc
	WNDPROC oldWndProc = (WNDPROC)GetProp(m_NppHandle, SUBCLASSING);
	SetWindowLongPtr(m_NppHandle, GWLP_WNDPROC, (LPARAM)oldWndProc);
	AcceleratorHook::getInstance()->remove(&m_Accelerators);
}
int CEditor::GetCurrentView(){
	int currentEdit;
	NPPM(GETCURRENTSCINTILLA,0,&currentEdit);
	return currentEdit;
}
int CEditor::GetMenuItemPosition(HANDLE hMenu, TCHAR* text)
{
	/* bla bla bla */
}
// 1.) because NPP use lets IDs for  menu items 
// and we can not get new IDs for our dynamic menu items. To solve 
// this problem we switch menu message handling from WM_COMMAND to WM_MENUCOMMAND
// see 2.) and 3.)
LRESULT CALLBACK CEditor::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
	if (message == WM_MENUCOMMAND){
		CMenuItem* mItem = CMenuItem::GetInstance((HMENU)lParam, wParam);
		if (mItem){ 
			// here we have our menu item
			mItem->call();
		}else{ 
			// we have menu items from NPP
			// simulate WM_COMMAND, NPP handles his menu items self
			MENUITEMINFO mii;
			mii.cbSize = sizeof(mii);
			mii.fMask = MIIM_ID | MIIM_DATA;
			// 2.) WM_MENUCOMMAND + submenu handle and item position help us to
			// get MenuItemInfo without using of menu item ID
			BOOL res = GetMenuItemInfo((HMENU)lParam, wParam, TRUE, &mii);
			if (res)
				::SendMessage(hwnd, WM_COMMAND,mii.wID,0);
		}
	} else if (message == WM_INITMENUPOPUP){
		CMenu* menu = CMenu::GetInstance((HMENU)wParam);
		if (menu){
			menu->oninitpopup();
			return NULL;
		}
	}	
	WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, SUBCLASSING);
	return CallWindowProc(oldWndProc, hwnd, message, wParam, lParam);
}
CEditor::CEditor(HWND nppHandle):CComDispatch(),m_Accelerators(nppHandle){
	AcceleratorHook::getInstance()->add(&m_Accelerators);
	// 3.) set our wndproc to have WM_MENUCOMMAND instead WM_COMMAND
	// see 1.) and 2.)
	m_NppHandle = nppHandle;
	WNDPROC oldWndProc = (WNDPROC)SetWindowLongPtr(nppHandle, GWLP_WNDPROC, (LPARAM)CEditor::WndProc);
	SetProp(m_NppHandle, SUBCLASSING, oldWndProc);
	m_Listener = NULL;
	// get main menu handle
	m_mainMenu = (HMENU)NPPM(GETMENUHANDLE,1,0);
	if (!m_mainMenu){
		VARIANT_BOOL hidden;
		get_menuHidden(&hidden);
		if (!hidden){
			m_mainMenu = GetMenu(nppData._nppHandle);
		}else{
			put_menuHidden(VARIANT_FALSE); 
			m_mainMenu = GetMenu(nppData._nppHandle);
			put_menuHidden(VARIANT_TRUE);
		}
	}
	// create views
	for(int i=0; i<m_ViewsNumber; i++){
		m_Views[i] = new CEditorView(i+1);
	}
	MENUINFO mi;
	memset(&mi, 0, sizeof(mi));
	mi.cbSize = sizeof(mi);
	mi.fMask = MIM_STYLE | MIM_APPLYTOSUBMENUS;
	mi.dwStyle = MNS_NOTIFYBYPOS;
	BOOL res = SetMenuInfo( m_mainMenu, &mi);	
}
void CEditor::getViewAndPosFrom(SCNotification* eventArgs, int* view, int* pos){
	/* bla bla bla */
}
void CEditor::doOnMessage(SCNotification* eventArgs){
	int r = 0;
	// provide messages to current view
	int view = GetCurrentView(); // can be -1 what is not valid
	if (view >= 0 && view < m_ViewsNumber){
		m_Views[view]->DoMessage(eventArgs);
	}
	if (m_Listener==NULL)
		return ;
	TCHAR* method1 = NULL;
	TCHAR* method2 = NULL;
	switch(eventArgs->nmhdr.code){
		case SCN_MODIFYATTEMPTRO:
			method2 = TEXT("MODIFYATTEMPTRO");
			break;
		case NPPN_SHUTDOWN:{
			method1 = TEXT("SHUTDOWN");
			break;
		}
		case NPPN_FILECLOSED:{
			method1 = TEXT("FILECLOSED");
			break;
		}
		case NPPN_READONLYCHANGED:{
			method2 = TEXT("READONLYCHANGED");
			break;
		}
		case NPPN_LANGCHANGED:{
			method2 = TEXT("LANGCHANGED");
			break;
		}
	}
	if (method1){
		CallListener(method1);
	}else if(method2){
		int view, pos;	getViewAndPosFrom(eventArgs, &view, &pos);
		CallListener(method2, view, pos);
	}
}
void CEditor::CallListener(TCHAR* method, int view, int file){
	if (m_Listener == NULL)
		return;
	if (view >= 0 && view < m_ViewsNumber){
		VARIANT var[2];
		var[0].vt = VT_DISPATCH;
		var[0].pdispVal = m_Views[view]; 
		var[1].vt = VT_INT;
		var[1].intVal = file;
		m_Listener->callMethod(method, var, 2);
	}else{
		m_Listener->callMethod(method);
	}
}
HRESULT STDMETHODCALLTYPE CEditor::get_firstView(  IView **result){
	*result	= m_Views[0];
	(*result)->AddRef();
	return S_OK;
}
/*
HRESULT STDMETHODCALLTYPE CEditor::get_currentView( IView **result){ // bla bla bla 
HRESULT STDMETHODCALLTYPE CEditor::get_secondView( IView **result){ // bla bla bla 
HRESULT STDMETHODCALLTYPE CEditor::alert( BSTR *value){ // bla bla bla 
HRESULT STDMETHODCALLTYPE CEditor::saveAll( void){ // bla bla bla 
HRESULT STDMETHODCALLTYPE CEditor::get_statusBarHidden / put_statusBarHidden / open / runMenuCmd / setListener / etc.
*/
HRESULT STDMETHODCALLTYPE CEditor::createDockable(IDispatch* cfg, IDialog** result){
	if (!cfg)
		return S_OK;
	Dialog* d = new DockableDialog(MyActiveSite::getInstance()->WrapScriptObj(cfg), nppData._nppHandle);
	d->Init();
	d->AddRef();
	*result = d;
	NPPM(MODELESSDIALOG,MODELESSDIALOGADD,d->hwnd());
	return S_OK;
}
HRESULT STDMETHODCALLTYPE CEditor::get_handle(int *result){
	*result = (int)m_NppHandle;
	return S_OK;
}

-------------

Ce code dans "Editor.cpp" me permet de créer un dock (fenêtre ancrée) avec des options de manipulations.

Dock que je vais créer plus tard dans le plugin compilé .dll avec un script javascript WSH contenant :

		var options = {
			name: 'blablabla',
			docking: 'bottom',
			onclose: function () { /* bla bla bla */}
		};
		var dock  = Editor.createDockable(options);
	}

-------------

Et j'ai également le code suivant dans le fichier "DockableDialog.cpp" :

-------------

#include <set>
#include "..\common\Statics.h"
#include "..\npp\PluginDefinition.h"
#include "..\npp\dockingResource.h"
#include "DockableDialog.h"
// Because of wrong architecture of Notepad++ it is necessary
// to destroy Strings given to docable dialog manager after destoroying 
// of it self. Hence we don't destroy them at all. To reduce memory consumption 
// create cache.
class StringCache{
	struct StringCmp{
		bool operator()(TCHAR* left, TCHAR* right) const {
			int result = StrCmp(left, right);
			return result < 0;
		}
	};
	typedef std::set<TCHAR*, StringCmp> Cache;
	Cache m_Cache;
public:
	TCHAR* GetCached(TCHAR* value){
		Cache::iterator findIt = m_Cache.find(value);
		if (findIt != m_Cache.end())
			return *findIt;
		// create a copy of value and put it to the cache
		TCHAR* copy = StrDup(value); // uses LocalAlloc
		m_Cache.insert(copy);
		return copy;
	}
	~StringCache(){
		for(Cache::iterator it = m_Cache.begin(); it!=m_Cache.end(); ++it){
			LocalFree(*it);
		}
	}
};
StringCache CachedStrings;
#define CHARS_IN(a) sizeof(a)/sizeof(TCHAR)
#define DD_AFTER_REGISTERASDOCKABLE WM_USER+1 // sent to control after
DockableDialog::DockableDialog(ScriptObj* cfg, HWND parent): Dialog(cfg, parent),CComDispatch(),m_Modul(TEXT("jN.dll,")), m_Name(TEXT("jN"))
{
	StringCchCopy(m_Info,CHARS_IN(m_Info), TEXT(""));
	VARIANT* textValue = m_Cfg->getProperty(TEXT("name"), VT_BSTR);
	if (textValue != NULL){
		m_Name.Set(textValue->bstrVal);
		m_Modul.Append(textValue->bstrVal);
		VariantClear(textValue);
		delete textValue;
	}
}
void DockableDialog::BeforeCreate(){
	m_Style   = WS_CHILD;
	m_ExStyle = WS_EX_CLIENTEDGE;
}
void DockableDialog::AfterCreate(){
	// Nothing to do override standard behavior
	RegisterAsDockable();
}
int DockableDialog::GetDockingFlags(){
	int result = DWS_DF_CONT_BOTTOM;
	VARIANT* dockingText = m_Cfg->getProperty(TEXT("docking"), VT_BSTR);
	if (dockingText){
		if (0 == StrCmp(TEXT("top"),dockingText->bstrVal))
			result = DWS_DF_CONT_TOP;
		else if (0 == StrCmp(TEXT("right"),dockingText->bstrVal))
			result = DWS_DF_CONT_RIGHT;
		else if (0 == StrCmp(TEXT("bottom"),dockingText->bstrVal))
			result = DWS_DF_CONT_BOTTOM;
		else if (0 == StrCmp(TEXT("left"),dockingText->bstrVal))
			result = DWS_DF_CONT_LEFT;
		else if (0 == StrCmp(TEXT("floating"),dockingText->bstrVal))
			result = DWS_DF_FLOATING;
		VariantClear(dockingText);
		delete dockingText;
	}
	return result;
}
void DockableDialog::RegisterAsDockable(){
	int mask = GetDockingFlags() | DWS_ICONTAB | DWS_ADDINFO;
	memset(&m_Data,0,sizeof(m_Data));
	m_Data.dlgID = 0;												//Nr of menu item to assign (!= _cmdID, beware)
	m_Data.hIconTab      = (HICON) LoadImage(Statics::instance().hInstance, MAKEINTRESOURCE(1),IMAGE_ICON, 0,0,LR_CREATEDIBSECTION|LR_SHARED|LR_LOADTRANSPARENT);												//icon to use
	m_Data.pszAddInfo    = m_Info;											//Titlebar info pointer
	m_Data.pszModuleName = CachedStrings.GetCached( m_Modul );							//name of the dll
	m_Data.pszName       = CachedStrings.GetCached( m_Name );												//Name for titlebar
	m_Data.uMask         = mask;													//Flags to use (see docking.h)
	m_Data.hClient       =  m_Hwnd;												//HWND Handle of window this dock belongs to
	m_Data.iPrevCont     = -1;
	SendMessage(nppData._nppHandle, NPPM_DMMREGASDCKDLG, 0, (LPARAM)&m_Data);		//Register it
	// update ui
	SendMessage(m_Hwnd,  DD_AFTER_REGISTERASDOCKABLE, 0, 0);
}
void DockableDialog::ResetUI(){
	//Registering as dockable breaks window styles, reset them
	LONG_PTR oldStyle = ::GetWindowLongPtr(m_Hwnd, GWL_STYLE);
	::SetWindowLongPtr(m_Hwnd, GWL_STYLE, m_Style | oldStyle);
	::SetWindowLongPtr(m_Hwnd, GWL_EXSTYLE, m_ExStyle);
	SetWindowPos(m_Hwnd, 0, 0, 0, 0, 0,  SWP_FRAMECHANGED| SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
	UpdateWindow(m_Hwnd);
}
void DockableDialog::UpdateInfo(){
	m_Data.pszAddInfo = m_Info;
	m_Data.pszName = m_Name;
	SendMessage(m_Hwnd, NPPM_DMMUPDATEDISPINFO, 0, (LPARAM)m_Hwnd);
}
LRESULT CALLBACK DockableDialog::MessageProc(UINT message, WPARAM wParam, LPARAM lParam){
	switch(message){
		case DD_AFTER_REGISTERASDOCKABLE:{
			ResetUI();
			return TRUE;
		}
		case WM_NOTIFY:{
			NMHDR nmh = (NMHDR) *((NMHDR*)lParam);
			if (nmh.hwndFrom == nppData._nppHandle) {
				if (nmh.code == DMN_CLOSE) {
					::PostMessage(m_Hwnd, WM_CLOSE, 0, 0);
					return TRUE;
				} else if ((nmh.code & DMN_DOCK) == DMN_DOCK && (nmh.code & DMN_FLOAT) != DMN_FLOAT) {// only if just docked
					PostMessage(m_Hwnd, DD_AFTER_REGISTERASDOCKABLE,0,0);
					return TRUE;
				}
			} 
			break;
		}
		case WM_ERASEBKGND:
			return TRUE;
		//case WM_SETFOCUS: {
		//	//Why restore focus here? This window should never be able to get focus in the first place
		//	HWND hPrev = (HWND)wParam;
		//	if (hPrev != NULL)
		//		::SetFocus(hPrev);
		//	break; 
		//}
	}
	return Dialog::MessageProc(message, wParam, lParam);
}
HRESULT STDMETHODCALLTYPE DockableDialog::get_visible( VARIANT_BOOL *result){
	if (isCreated())
		*result = IsWindowVisible(m_Hwnd);
	return S_OK;
}
HRESULT STDMETHODCALLTYPE DockableDialog::put_visible( VARIANT_BOOL value){
	::SendMessage(nppData._nppHandle, value ? NPPM_DMMSHOW : NPPM_DMMHIDE, 0, (LPARAM)m_Hwnd);
	return S_OK;
}

-------------

Ce code de "DockableDialog.cpp" permet des manipulations sur le dock qu'on a créé. Par exemple je peux modifier sa visibilité avec dock.visible = true/false.

-------------

Ma question : qu'est ce que je dois modifier ou ajoûter dans les fichiers "Editor.cpp" et  "DockableDialog.cpp" pour pouvoir cloturer / détruire / remove un dock que j'ai ajouté avec var dock  = Editor.createDockable(options); ?

J'aimerais une option genre Editor.closeDock(x); pour clôturer un dock créer avec x = Editor.createDockable(options);
Et bien-sûr Editor.closeDock(y); cloturerait y = Editor.createDockable(options); sans toucher un dock x ou z…