Session Manager provides an API which can be used by Notepad++ or other plugins. The "SessionMgrApi.h" file has documentation for this feature. A client sends an NPPM_MSGTOPLUGIN message to Notepad++ with wParam pointing to L"SessionMgr.dll" and lParam pointing to a SessionMgrApiData object. Clients can load sessions, save the current session, get the current session name, set the session folder, and more.
Global Properties
Notepad++ saves a file's bookmarks, folded lines and other properties in the session file, so the same file in different sessions will not have the same bookmarks, folded lines, etc. Session Manager can keep files' bookmarks, folded lines, first visible line and language synchronized across different sessions (See Global properties in the Settings Dialog section).
The file "global.xml", in the Session Manager configuration folder, stores global properties for each unique pathname in all your sessions. There are 3 cases where these global properties are used...
-
- - After a session is saved, the global properties are updated from the session properties.
- - When a session is about to be loaded, the session properties are updated from the global properties, then the session is loaded.
- - When an existing document is added to a session, its properties are updated from the global properties, then the session is saved.
-
+
+ - After a session is saved, the global properties are updated from the session properties.
+ - When a session is about to be loaded, the session properties are updated from the global properties, then the session is loaded.
+ - When an existing document is added to a session, its properties are updated from the global properties, then the session is saved.
+
If you remove a certain file from all sessions then later add it to a session it will have bookmarks, folded lines and first visible line restored from the last time it was part of a session.
The global properties feature was introduced in Session Manager 0.8, so if you have been using version <= 0.7.1 and you install version >= 0.8, the global properties file will be initially empty. Your global properties will get updated as you load different sessions. You can speed up this process by loading all your sessions, one by one, in order from earliest to latest last-modified time, or in order from least to most important.
@@ -293,11 +300,21 @@
Shutdown
So far, there is only one scenario I have found where Session Manager will save the session incorrectly. If you close one or more files and then quit Notepad++ before sessionSaveDelay seconds have elapsed then those closed files will still be part of that session.
Ideally, Notepad++ would issue NPPN_BEFORESHUTDOWN, then close all files, then issue NPPN_SHUTDOWN. In the case where the user cancels the shutdown it would issue NPPN_CANCELSHUTDOWN.
+
Session Manager provides an API which can be used by Notepad++ or other plugins. The "SessionMgrApi.h" file has documentation for this feature. A client sends an NPPM_MSGTOPLUGIN message to Notepad++ with wParam pointing to L"SessionMgr.dll" and lParam pointing to a SessionMgrApiData object. Session Manager exposes all its settings and features through the API.
+
Revision History
If you are upgrading from a version older than v1.2 you must configure your settings again after the upgrade.
+
1.4.2, 15Feb2015
+
+ - Bug-fix: The context menu feature assumed "contextMenu.xml" to be in Notepad++'s installation folder, but now it determines if it is there or in %AppData%.
+ - Improvement: The P2P API is now much more comprehensive.
+ - Made improvements to the help page.
+
1.4.1, 08Feb2015
- For all Windows versions, use 0 instead of MB_ERR_INVALID_CHARS and WC_ERR_INVALID_CHARS in MultiByteToWideChar and WideCharToMultiByte calls.
@@ -499,11 +516,9 @@ 0.1, 03Aug2011
diff --git a/src/ContextMenu.cpp b/src/ContextMenu.cpp
index 850dce4..9dd1667 100644
--- a/src/ContextMenu.cpp
+++ b/src/ContextMenu.cpp
@@ -125,7 +125,7 @@ void saveContextMenu()
if (cfg::getBool(kUseContextMenu)) {
if (_pCtxXmlDoc) {
- xmlErr = _pCtxXmlDoc->SaveFile(sys_getContextMenuFile());
+ xmlErr = _pCtxXmlDoc->SaveFile(sys_getNppCtxMnuFile());
if (xmlErr != kXmlSuccess) {
lastErr = ::GetLastError();
msg::error(lastErr, L"%s: Error %u saving the context menu file.", _W(__FUNCTION__), xmlErr);
@@ -162,7 +162,7 @@ tXmlEleP getFavSeparator()
// Load the contextMenu file if not already loaded
if (!_pCtxXmlDoc) {
_pCtxXmlDoc = new tinyxml2::XMLDocument();
- xmlErr = _pCtxXmlDoc->LoadFile(sys_getContextMenuFile());
+ xmlErr = _pCtxXmlDoc->LoadFile(sys_getNppCtxMnuFile());
if (xmlErr != kXmlSuccess) {
lastErr = ::GetLastError();
msg::error(lastErr, L"%s: Error %u loading the context menu file.", _W(__FUNCTION__), xmlErr);
diff --git a/src/DllMain.cpp b/src/DllMain.cpp
index cb0b543..528538e 100644
--- a/src/DllMain.cpp
+++ b/src/DllMain.cpp
@@ -21,7 +21,6 @@
#include "Settings.h"
#include "Properties.h"
#include "ContextMenu.h"
-#include "Util.h"
using namespace NppPlugin::api;
@@ -62,7 +61,6 @@ BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
extern "C" __declspec(dllexport) void setInfo(NppData nppd)
{
sys_init(nppd);
- util_init();
app_init();
mnu_init();
prp_init();
diff --git a/src/Menu.cpp b/src/Menu.cpp
index cf3ac73..e81db13 100644
--- a/src/Menu.cpp
+++ b/src/Menu.cpp
@@ -206,14 +206,13 @@ extern "C" void cbHelp()
extern "C" void cbAbout()
{
- const size_t s = 7 * MAX_PATH;
- WCHAR m[s + 1], b[MAX_PATH];
+ const size_t s = 450;
+ WCHAR m[s];
::StringCchCopyW(m, s, PLUGIN_ABOUT);
- ::StringCchCatW(m, s, L"\n\nConfiguration directory:\n");
- ::StringCchCatW(m, s, sys_getCfgDir());
- ::StringCchCatW(m, s, L"\n\nSpecial thanks to...\n- Don Ho, for Notepad++\n- Dave Brotherstone, for PluginManager\n- Julien Audo, for ResEdit\n- Lee Thomason, for TinyXML2\n- Nemanja Trifunovic, for UTF8-CPP\n- Jack Handy, for wildcardMatch\n- Jens Lorenz and Thell Fowler, for example code\n- Users at the plugin forum, for testing and feedback\n- You! for using Session Manager");
+ ::StringCchCatW(m, s, L"\n\nSpecial thanks to....\n\n- Don Ho and team, for Notepad++\n- You! for using Session Manager\n- Dave Brotherstone, for PluginManager\n- Jack Handy, for wildcardMatch\n- Jens Lorenz and Thell Fowler, for example code\n- Julien Audo, for ResEdit\n- Lee Thomason, for TinyXML2\n- Nemanja Trifunovic, for UTF8-CPP\n- Users at the plugin forum, for testing and feedback");
msg::show(m, L"About Session Manager", MB_ICONINFORMATION);
+ //LOG("strlen = %u", ::wcslen(m));
}
void loadFavorite(INT mnuOfs)
diff --git a/src/Properties.cpp b/src/Properties.cpp
index def590d..dec18fb 100644
--- a/src/Properties.cpp
+++ b/src/Properties.cpp
@@ -24,7 +24,6 @@
#include "System.h"
#include "Properties.h"
#include "Util.h"
-#include "utf8\unchecked.h"
//------------------------------------------------------------------------------
@@ -50,7 +49,6 @@ namespace {
#define XA_FIRSTVISIBLELINE "firstVisibleLine"
#define XA_LINE "line"
-INT utf8ToAscii(LPCSTR str, LPSTR buf = NULL);
void removeMissingFilesFromGlobal();
void deleteChildren(tXmlEleP parent, LPCSTR eleName);
@@ -86,7 +84,7 @@ void updateGlobalFromSession(LPWSTR sesFile)
// Load the properties file (global file properties)
tXmlDoc globalDoc;
- xmlErr = globalDoc.LoadFile(sys_getPropsFile());
+ xmlErr = globalDoc.LoadFile(sys_getGlobalFile());
if (xmlErr != kXmlSuccess) {
lastErr = ::GetLastError();
msg::error(lastErr, L"%s: Error %u loading the global properties file.", _W(__FUNCTION__), xmlErr);
@@ -165,7 +163,7 @@ void updateGlobalFromSession(LPWSTR sesFile)
globalDoc.InsertFirstChild(globalDoc.NewDeclaration());
}
// Save changes to the properties file
- xmlErr = globalDoc.SaveFile(sys_getPropsFile());
+ xmlErr = globalDoc.SaveFile(sys_getGlobalFile());
if (xmlErr != kXmlSuccess) {
lastErr = ::GetLastError();
msg::error(lastErr, L"%s: Error %u saving the global properties file.", _W(__FUNCTION__), xmlErr);
@@ -187,7 +185,7 @@ void updateSessionFromGlobal(LPWSTR sesFile)
// Load the properties file (global file properties)
tXmlDoc globalDoc;
- xmlErr = globalDoc.LoadFile(sys_getPropsFile());
+ xmlErr = globalDoc.LoadFile(sys_getGlobalFile());
if (xmlErr != kXmlSuccess) {
lastErr = ::GetLastError();
msg::error(lastErr, L"%s: Error %u loading the global properties file.", _W(__FUNCTION__), xmlErr);
@@ -227,11 +225,11 @@ void updateSessionFromGlobal(LPWSTR sesFile)
if (globalFileEle) {
save = true;
// Update current local File attributes with values from the global File attributes
- buf = (LPSTR)sys_alloc(utf8ToAscii(target) * sizeof CHAR);
+ buf = (LPSTR)sys_alloc(str::utf8ToAscii(target) * sizeof CHAR);
if (buf == NULL) {
return;
}
- utf8ToAscii(target, buf); // NPP expects the pathname to be encoded like this
+ str::utf8ToAscii(target, buf); // NPP expects the pathname to be encoded like this
localFileEle->SetAttribute(XA_FILENAME, buf);
sys_free(buf);
localFileEle->SetAttribute(XA_LANG, globalFileEle->Attribute(XA_LANG));
@@ -304,7 +302,7 @@ void updateDocumentFromGlobal(INT bufferId)
LOGG(20, "File = %s", mbPathname);
// Load the properties file (global file properties)
tXmlDoc globalDoc;
- tXmlError xmlErr = globalDoc.LoadFile(sys_getPropsFile());
+ tXmlError xmlErr = globalDoc.LoadFile(sys_getGlobalFile());
if (xmlErr != kXmlSuccess) {
DWORD lastErr = ::GetLastError();
msg::error(lastErr, L"%s: Error %u loading the global properties file.", _W(__FUNCTION__), xmlErr);
@@ -368,41 +366,9 @@ void updateDocumentFromGlobal(INT bufferId)
namespace {
-/** If buf is non-NULL, converts a UTF-8 string to a string where all chars < 32
- or > 126 are converted to entities.
- @return the number of bytes in the converted string including the terminator */
-INT utf8ToAscii(LPCSTR str, LPSTR buf)
-{
- INT bytes = 0;
- LPSTR b = buf;
- LPCSTR s = str;
- utf8::uint32_t cp;
- while (*s) {
- cp = utf8::unchecked::next(s);
- if (cp < 32 || cp > 126) {
- if (buf) {
- ::sprintf_s(b, 9, "%04X;", cp);
- b += 8;
- }
- bytes += 8;
- }
- else {
- if (buf) {
- *b++ = (unsigned char)cp;
- }
- ++bytes;
- }
- }
- if (buf) {
- *b = 0;
- }
- return bytes + 1;
-}
-
/** Removes global File elements whose files do not exist on disk. */
void removeMissingFilesFromGlobal()
{
- INT wLen;
DWORD lastErr;
tXmlError xmlErr;
bool save = false;
@@ -414,7 +380,7 @@ void removeMissingFilesFromGlobal()
// Load the properties file (global file properties)
tXmlDoc globalDoc;
- xmlErr = globalDoc.LoadFile(sys_getPropsFile());
+ xmlErr = globalDoc.LoadFile(sys_getGlobalFile());
if (xmlErr != kXmlSuccess) {
lastErr = ::GetLastError();
msg::error(lastErr, L"%s: Error %u loading the global properties file.", _W(__FUNCTION__), xmlErr);
@@ -447,7 +413,7 @@ void removeMissingFilesFromGlobal()
globalDoc.InsertFirstChild(globalDoc.NewDeclaration());
}
// Save changes to the properties file
- xmlErr = globalDoc.SaveFile(sys_getPropsFile());
+ xmlErr = globalDoc.SaveFile(sys_getGlobalFile());
if (xmlErr != kXmlSuccess) {
lastErr = ::GetLastError();
msg::error(lastErr, L"%s: Error %u saving the global properties file.", _W(__FUNCTION__), xmlErr);
diff --git a/src/SessionMgr.cpp b/src/SessionMgr.cpp
index 136b879..f6c613c 100644
--- a/src/SessionMgr.cpp
+++ b/src/SessionMgr.cpp
@@ -19,7 +19,6 @@
#include "Util.h"
#include "Properties.h"
#include "ContextMenu.h"
-#include "SessionMgrApi.h"
#include
#include
#include
@@ -92,7 +91,9 @@ void app_onUnload()
void app_init()
{
- LOG("-------------- START %S %s with debug level %i", PLUGIN_FULL_NAME, RES_VERSION_S, cfg::getInt(kDebugLogLevel));
+ DWORD nppVer = sys_getNppVer();
+ LOG("-------------- START %S %s", PLUGIN_FULL_NAME, RES_VERSION_S);
+ LOG("win:%u, npp:%u.%u, dbg:%u, cfg:\"%S\", ctx:\"%S\"", sys_getWinVer(), HIWORD(nppVer), LOWORD(nppVer), gDbgLvl, sys_getCfgDir(), sys_getNppCtxMnuFile());
app_readSessionDirectory(true);
}
@@ -238,83 +239,182 @@ void app_onNotify(SCNotification *pscn)
}
}
-/** Session Manager API. Handles messages from NPP or a plugin.
- @since v0.8.4.1
- @see SessionMgrApi.h */
+/** Session Manager P2P API. Handles messages from NPP or a plugin.
+ @see SessionMgrApi.h */
LRESULT app_msgProc(UINT Message, WPARAM wParam, LPARAM lParam)
{
- if (Message == NPPM_MSGTOPLUGIN) {
- INT si;
- SessionMgrApiData *api = (SessionMgrApiData*)lParam;
- if (gDbgLvl >= 10) {
- switch (api->message) {
- case SESMGRM_SES_LOAD: LOG("SESMGRM_SES_LOAD"); break;
- case SESMGRM_SES_LOAD_PRV: LOG("SESMGRM_SES_LOAD_PRV"); break;
- case SESMGRM_SES_LOAD_DEF: LOG("SESMGRM_SES_LOAD_DEF"); break;
- case SESMGRM_SES_SAVE: LOG("SESMGRM_SES_SAVE"); break;
- case SESMGRM_SES_GET_NAME: LOG("SESMGRM_SES_GET_NAME"); break;
- case SESMGRM_SES_GET_FQN: LOG("SESMGRM_SES_GET_FQN"); break;
- case SESMGRM_CFG_GET_DIR: LOG("SESMGRM_CFG_GET_DIR"); break;
- case SESMGRM_CFG_GET_EXT: LOG("SESMGRM_CFG_GET_EXT"); break;
- case SESMGRM_CFG_SET_DIR: LOG("SESMGRM_CFG_SET_DIR"); break;
- case SESMGRM_CFG_SET_EXT: LOG("SESMGRM_CFG_SET_EXT"); break;
+ if (Message != NPPM_MSGTOPLUGIN) {
+ return 1;
+ }
+ SessionMgrApiData *api = (SessionMgrApiData*)lParam;
+ LOGG(10, "API msg=%u, iData=%i, wData=\"%S\"", api->message, api->iData, api->wData);
+ if (!_appReady || _sesLoading) {
+ LOGG(10, "SM_BUSY");
+ api->iData = SM_BUSY;
+ return 1;
+ }
+
+ INT i;
+ SettingId si;
+ Session *ses;
+
+ switch (api->message) {
+ case SMM_SES_LOAD:
+ i = app_getSessionIndex(api->wData);
+ if (i <= SI_NONE) {
+ api->iData = SM_INVARG; // session not found in current list
}
- }
- if (!_appReady || _sesLoading) {
- LOGG(10, "SESMGR_BUSY");
- api->iData = SESMGR_BUSY;
- return 1;
- }
- switch (api->message) {
- case SESMGRM_SES_LOAD:
- si = app_getSessionIndex((LPWSTR)api->wData);
- if (si <= SI_NONE) {
- api->iData = SESMGR_ERROR; // session not found in current list
+ else {
+ app_loadSession(i);
+ api->iData = SM_OK;
+ }
+ break;
+ case SMM_SES_LOAD_PRV:
+ app_loadSession(SI_PREVIOUS);
+ api->iData = SM_OK;
+ break;
+ case SMM_SES_LOAD_DEF:
+ app_loadSession(SI_DEFAULT);
+ api->iData = SM_OK;
+ break;
+ case SMM_SES_SAVE:
+ app_saveSession(SI_CURRENT);
+ api->iData = SM_OK;
+ break;
+ case SMM_SES_GET_NAME:
+ ::StringCchCopyW(api->wData, MAX_PATH, app_getSessionName(SI_CURRENT));
+ api->iData = SM_OK;
+ break;
+ case SMM_SES_GET_FQN:
+ app_getSessionFile(SI_CURRENT, api->wData);
+ api->iData = SM_OK;
+ break;
+ case SMM_CFG_GET_INT:
+ si = (SettingId)api->iData;
+ if ((si >= kAutomaticSave && si <= kSettingsSavePoll) ||
+ (si >= kCurrentMark && si <= kSessionSortOrder) ||
+ (si >= kSessionsDialogWidth && si <= kDebugLogLevel))
+ {
+ api->wData[0] = (WCHAR)cfg::getInt(si);
+ api->iData = SM_OK;
+ }
+ else {
+ api->iData = SM_INVARG;
+ }
+ break;
+ case SMM_CFG_PUT_INT:
+ si = (SettingId)api->iData;
+ if ((si >= kAutomaticSave && si <= kSettingsSavePoll) ||
+ (si >= kCurrentMark && si <= kSessionSortOrder) ||
+ si == kDebugLogLevel)
+ {
+ i = (INT)api->wData[0];
+ if (si == kShowInTitlebar) {
+ cfg::setShowInTitlebar(i != 0);
}
- else {
- app_loadSession(si);
+ else if (si == kShowInStatusbar) {
+ cfg::setShowInStatusbar(i != 0);
}
- break;
- case SESMGRM_SES_LOAD_PRV:
- app_loadSession(SI_PREVIOUS);
- break;
- case SESMGRM_SES_LOAD_DEF:
- app_loadSession(SI_DEFAULT);
- break;
- case SESMGRM_SES_SAVE:
- app_saveSession(SI_CURRENT);
- break;
- case SESMGRM_SES_GET_NAME:
- ::StringCchCopyW((LPWSTR)api->wData, MAX_PATH, app_getSessionName(SI_CURRENT));
- break;
- case SESMGRM_SES_GET_FQN:
- app_getSessionFile(SI_CURRENT, (LPWSTR)api->wData);
- break;
- case SESMGRM_CFG_GET_DIR:
- ::StringCchCopyW((LPWSTR)api->wData, MAX_PATH, cfg::getStr(kSessionDirectory));
- break;
- case SESMGRM_CFG_SET_DIR:
- if (!cfg::setSessionDirectory((LPWSTR)api->wData)) {
- api->iData = SESMGR_ERROR; // error creating directory
+ else if (si == kUseContextMenu) {
+ if (cfg::getBool(kUseContextMenu) && !i) {
+ ctx::unload();
+ }
+ cfg::putInt(si, i);
}
else {
+ cfg::putInt(si, i);
+ }
+ api->iData = SM_OK;
+ }
+ else {
+ api->iData = SM_INVARG;
+ }
+ break;
+ case SMM_CFG_GET_STR:
+ si = (SettingId)api->iData;
+ if (si >= 0 && si <= kDebugLogFile) {
+ cfg::getStr(si, api->wData, MAX_PATH);
+ api->iData = SM_OK;
+ }
+ else {
+ api->iData = SM_INVARG;
+ }
+ break;
+ case SMM_CFG_PUT_STR:
+ si = (SettingId)api->iData;
+ if ((si >= kSessionDirectory && si <= kSessionExtension) ||
+ (si >= kMenuLabelMain && si <= kMenuLabelSub6) ||
+ si == kDebugLogFile)
+ {
+ if (si == kSessionDirectory) {
+ if (!cfg::setSessionDirectory(api->wData)) {
+ api->iData = SM_ERROR; // error creating directory
+ }
+ else {
+ cfg::saveSettings();
+ app_readSessionDirectory();
+ api->iData = SM_OK;
+ }
+ }
+ else if (si == kSessionExtension) {
+ cfg::setSessionExtension(api->wData);
cfg::saveSettings();
app_readSessionDirectory();
+ api->iData = SM_OK;
}
- break;
- case SESMGRM_CFG_GET_EXT:
- ::StringCchCopyW((LPWSTR)api->wData, MAX_PATH, cfg::getStr(kSessionExtension));
- break;
- case SESMGRM_CFG_SET_EXT:
- cfg::setSessionExtension((LPWSTR)api->wData);
- cfg::saveSettings();
- app_readSessionDirectory();
- break;
- }
- if (api->iData == SESMGR_NULL) {
- api->iData = SESMGR_OK;
- }
+ else {
+ cfg::putStr(si, api->wData);
+ api->iData = SM_OK;
+ }
+ }
+ else {
+ api->iData = SM_INVARG;
+ }
+ break;
+ case SMM_NPP_CFG_DIR:
+ ::StringCchCopyW(api->wData, MAX_PATH, sys_getNppCtxMnuFile());
+ if (pth::removeName(api->wData, MAX_PATH) != 0) {
+ api->iData = SM_ERROR;
+ }
+ else {
+ api->iData = SM_OK;
+ }
+ break;
+ case SMM_FAV_CLR:
+ app_updateFavorites(true);
+ api->iData = SM_OK;
+ break;
+ case SMM_FAV_SET:
+ i = app_getSessionIndex(api->wData);
+ if (i <= SI_NONE) {
+ api->iData = SM_INVARG; // session not found in current list
+ }
+ else {
+ ses = app_getSessionObject(i);
+ if (!ses) {
+ api->iData = SM_ERROR;
+ }
+ else {
+ ses->isFavorite = (bool)api->iData;
+ app_updateFavorites();
+ api->iData = SM_OK;
+ }
+ }
+ break;
+ case SMM_FIL_CLR:
+ cfg::deleteChildren(kFilters);
+ cfg::addChild(kFilters, L"*");
+ api->iData = SM_OK;
+ break;
+ case SMM_FIL_ADD:
+ cfg::moveToTop(kFilters, api->wData);
+ api->iData = SM_OK;
+ break;
+ default:
+ api->iData = SM_INVMSG;
+ break;
}
+
return 1;
}
@@ -673,16 +773,16 @@ void app_updateNppBars()
}
}
-/** Removes all favorites from settings then adds all the currently marked
- favorite sessions. */
-void app_updateFavorites()
+/** Removes all favorites then adds all the currently marked favorite sessions. */
+void app_updateFavorites(bool clearAll)
{
- INT i = 0;
-
cfg::deleteChildren(kFavorites);
ctx::deleteFavorites();
- for (vector::const_iterator it = _sessions.begin(); it != _sessions.end(); ++it) {
- if (it->isFavorite) {
+ for (vector::iterator it = _sessions.begin(); it != _sessions.end(); ++it) {
+ if (clearAll) {
+ it->isFavorite = false;
+ }
+ else if (it->isFavorite) {
cfg::addChild(kFavorites, it->name);
ctx::addFavorite(it->name);
}
diff --git a/src/SessionMgr.h b/src/SessionMgr.h
index 294c8b8..767cdf8 100644
--- a/src/SessionMgr.h
+++ b/src/SessionMgr.h
@@ -78,7 +78,7 @@ void app_getSessionFile(INT si, LPWSTR buf);
Session* app_getSessionObject(INT si);
void app_confirmDefaultSession();
void app_updateNppBars();
-void app_updateFavorites();
+void app_updateFavorites(bool clearAll = false);
INT app_getLbIdxStartingWith(WCHAR targetChar);
} // end namespace NppPlugin
diff --git a/src/SessionMgrApi.h b/src/SessionMgrApi.h
index 64ed7bf..61a9498 100644
--- a/src/SessionMgrApi.h
+++ b/src/SessionMgrApi.h
@@ -12,7 +12,7 @@
@file SessionMgrApi.h
@copyright Copyright 2014,2015 Michael Foster
- Session Manager API
+ Session Manager P2P API
Clients should send NPPM_MSGTOPLUGIN to NPP with wParam pointing to
L"SessionMgr.dll" and lParam pointing to a SessionMgrApiData object.
@@ -21,81 +21,151 @@
#ifndef NPP_PLUGIN_SESSIONMGRAPI_H
#define NPP_PLUGIN_SESSIONMGRAPI_H
-#define SESMGR_NULL 0
-#define SESMGR_OK -1
-#define SESMGR_ERROR -2
-#define SESMGR_BUSY -3
+#define SM_NULL 0
+#define SM_OK -1
+#define SM_BUSY -2
+#define SM_ERROR -3
+#define SM_INVMSG -4
+#define SM_INVARG -5
/** This is compatible with casting to NPP's CommunicationInfo struct.
- @since v1.1
- @see npp\Notepad_plus_msgs.h */
-typedef struct SessionMgrApiData_tag {
- long message; ///< one of the SESMGRM_ message codes
+ @see npp\Notepad_plus_msgs.h */
+struct SessionMgrApiData {
+ long message; ///< one of the SMM_ message codes
LPCWSTR caller; ///< for NPP but not used as of v6.6.9
INT iData; ///< input and output API usage
WCHAR wData[MAX_PATH]; ///< input or output API usage
-} SessionMgrApiData;
+};
+
+enum SettingId {
+ kAutomaticSave = 0,
+ kAutomaticLoad,
+ kLoadIntoCurrent,
+ kLoadWithoutClosing,
+ kShowInTitlebar,
+ kShowInStatusbar,
+ kUseGlobalProperties,
+ kCleanGlobalProperties,
+ kUseContextMenu,
+ kBackupOnStartup,
+ kSessionSaveDelay,
+ kSettingsSavePoll,
+ kSessionDirectory,
+ kSessionExtension,
+ kCurrentMark,
+ kCurrentFavMark,
+ kPreviousMark,
+ kPreviousFavMark,
+ kDefaultMark,
+ kDefaultFavMark,
+ kFavoriteMark,
+ kUseFilterWildcards,
+ kSessionSortOrder,
+ kCurrentSession,
+ kPreviousSession,
+ kDefaultSession,
+ kMenuLabelMain,
+ kMenuLabelSub1,
+ kMenuLabelSub2,
+ kMenuLabelSub3,
+ kMenuLabelSub4,
+ kMenuLabelSub5,
+ kMenuLabelSub6,
+ kSessionsDialogWidth,
+ kSessionsDialogHeight,
+ kSettingsDialogWidth,
+ kSettingsDialogHeight,
+ kDebugLogLevel,
+ kDebugLogFile,
+ kSettingsCount
+};
+
+//------------------------------------------------------------------------------
/** Loads a session from the current sessions list.
@pre wData = session name, no path or extension
- @pre iData = SESMGR_NULL
- @post iData = SESMGR_ERROR if not in the list, else SESMGR_OK */
-#define SESMGRM_SES_LOAD (WM_APP + 1)
+ @pre iData = SM_NULL
+ @post iData = SM_OK else SM_BUSY or SM_INVARG */
+#define SMM_SES_LOAD (WM_APP + 1)
/** Loads the previous session.
- @pre iData = SESMGR_NULL
- @post iData = SESMGR_OK */
-#define SESMGRM_SES_LOAD_PRV (WM_APP + 2)
+ @pre iData = SM_NULL
+ @post iData = SM_OK else SM_BUSY */
+#define SMM_SES_LOAD_PRV (WM_APP + 2)
/** Loads the default session.
- @pre iData = SESMGR_NULL
- @post iData = SESMGR_OK */
-#define SESMGRM_SES_LOAD_DEF (WM_APP + 3)
+ @pre iData = SM_NULL
+ @post iData = SM_OK else SM_BUSY */
+#define SMM_SES_LOAD_DEF (WM_APP + 3)
/** Saves the current session.
- @pre iData = SESMGR_NULL
- @post iData = SESMGR_OK */
-#define SESMGRM_SES_SAVE (WM_APP + 4)
+ @pre iData = SM_NULL
+ @post iData = SM_OK else SM_BUSY */
+#define SMM_SES_SAVE (WM_APP + 4)
/** Gets the current session name, no path or extension.
- @pre iData = SESMGR_NULL
- @post iData = SESMGR_OK
+ @pre iData = SM_NULL
+ @post iData = SM_OK else SM_BUSY
@post wData = name */
-#define SESMGRM_SES_GET_NAME (WM_APP + 5)
+#define SMM_SES_GET_NAME (WM_APP + 5)
-/** Gets the fully qualified name of the current session.
- @pre iData = SESMGR_NULL
- @post iData = SESMGR_OK
+/** Gets the fully qualified name of the current session file.
+ @pre iData = SM_NULL
+ @post iData = SM_OK else SM_BUSY
@post wData = fqn */
-#define SESMGRM_SES_GET_FQN (WM_APP + 6)
-
-/** Gets the current session directory.
- @pre iData = SESMGR_NULL
- @post iData = SESMGR_OK
- @post wData = directory */
-#define SESMGRM_CFG_GET_DIR (WM_APP + 7)
-
-/** Gets the current session file extension.
- @pre iData = SESMGR_NULL
- @post iData = SESMGR_OK
- @post wData = extension */
-#define SESMGRM_CFG_GET_EXT (WM_APP + 8)
-
-/** Sets the current session directory. Creates the directory if needed, saves
- the settings file, then reloads the current sessions list.
- @pre wData = directory
- @pre iData = SESMGR_NULL
- @post iData = SESMGR_ERROR if directory creation failed, else SESMGR_OK */
-#define SESMGRM_CFG_SET_DIR (WM_APP + 9)
-
-/** Sets the current session file extension. Saves the settings file then
- reloads the current sessions list.
- @pre wData = extension
- @pre iData = SESMGR_NULL
- @post iData = SESMGR_OK */
-#define SESMGRM_CFG_SET_EXT (WM_APP + 10)
-
-/** TODO: Read/write access to favorites, filters and more settings.
-*/
+#define SMM_SES_GET_FQN (WM_APP + 6)
+
+/** Gets the integer value of a setting.
+ @pre iData = SettingId
+ @post iData = SM_OK else SM_BUSY or SM_INVARG
+ @post wData[0] = value of setting */
+#define SMM_CFG_GET_INT (WM_APP + 7)
+
+/** Sets the integer value of a setting.
+ @pre iData = SettingId
+ @pre wData[0] = value to set
+ @post iData = SM_OK else SM_BUSY or SM_INVARG */
+#define SMM_CFG_PUT_INT (WM_APP + 8)
+
+/** Gets the string value of a setting.
+ @pre iData = SettingId
+ @post iData = SM_OK else SM_BUSY or SM_INVARG
+ @post wData = value of setting */
+#define SMM_CFG_GET_STR (WM_APP + 9)
+
+/** Sets the string value of a setting.
+ @pre iData = SettingId
+ @pre wData = value to set
+ @post iData = SM_OK else SM_BUSY, SM_INVARG or SM_ERROR */
+#define SMM_CFG_PUT_STR (WM_APP + 10)
+
+/** Removes all favorites.
+ @pre iData = SM_NULL
+ @post iData = SM_OK else SM_BUSY */
+#define SMM_FAV_CLR (WM_APP + 11)
+
+/** Adds or removes a session as a favorite.
+ @pre wData = session name, no path or extension
+ @pre iData = 0: remove, 1: add
+ @post iData = SM_OK else SM_BUSY, SM_INVARG or SM_ERROR */
+#define SMM_FAV_SET (WM_APP + 12)
+
+/** Removes all filters.
+ @pre iData = SM_NULL
+ @post iData = SM_OK else SM_BUSY */
+#define SMM_FIL_CLR (WM_APP + 13)
+
+/** Adds a filter. It is moved to the top if it is already in the list.
+ @pre wData = filter
+ @pre iData = SM_NULL
+ @post iData = SM_OK else SM_BUSY */
+#define SMM_FIL_ADD (WM_APP + 14)
+
+/** Gets NPP's configuration directory.
+ @pre iData = SM_NULL
+ @post iData = SM_OK else SM_BUSY or SM_ERROR
+ @post wData = path */
+#define SMM_NPP_CFG_DIR (WM_APP + 15)
+
#endif // NPP_PLUGIN_SESSIONMGRAPI_H
diff --git a/src/Settings.h b/src/Settings.h
index 29ddf1f..5a1f355 100644
--- a/src/Settings.h
+++ b/src/Settings.h
@@ -16,6 +16,7 @@
#ifndef NPP_PLUGIN_SETTINGS_H
#define NPP_PLUGIN_SETTINGS_H
+#include "SessionMgrApi.h"
#include "xml\tinyxml.h"
//------------------------------------------------------------------------------
@@ -31,48 +32,7 @@ enum ContainerId {
kContainersCount
};
-enum SettingId {
- kAutomaticSave = 0,
- kAutomaticLoad,
- kLoadIntoCurrent,
- kLoadWithoutClosing,
- kShowInTitlebar,
- kShowInStatusbar,
- kUseGlobalProperties,
- kCleanGlobalProperties,
- kUseContextMenu,
- kBackupOnStartup,
- kSessionSaveDelay,
- kSettingsSavePoll,
- kSessionDirectory,
- kSessionExtension,
- kCurrentMark,
- kCurrentFavMark,
- kPreviousMark,
- kPreviousFavMark,
- kDefaultMark,
- kDefaultFavMark,
- kFavoriteMark,
- kUseFilterWildcards,
- kSessionSortOrder,
- kCurrentSession,
- kPreviousSession,
- kDefaultSession,
- kMenuLabelMain,
- kMenuLabelSub1,
- kMenuLabelSub2,
- kMenuLabelSub3,
- kMenuLabelSub4,
- kMenuLabelSub5,
- kMenuLabelSub6,
- kSessionsDialogWidth,
- kSessionsDialogHeight,
- kSettingsDialogWidth,
- kSettingsDialogHeight,
- kDebugLogLevel,
- kDebugLogFile,
- kSettingsCount
-};
+// See SessionMgrApi.h for enum SettingId
#define SORT_ORDER_ALPHA 1
#define SORT_ORDER_DATE 2
diff --git a/src/System.cpp b/src/System.cpp
index d28b9f7..4603e93 100644
--- a/src/System.cpp
+++ b/src/System.cpp
@@ -17,6 +17,7 @@
#include "SessionMgr.h"
#include "Util.h"
#include
+//#include // for findNppCtxMnuFile
//------------------------------------------------------------------------------
@@ -28,20 +29,25 @@ namespace {
#define INI_FILE_NAME L"settings.ini"
#define CFG_FILE_NAME L"settings.xml"
-#define PROPS_FILE_NAME L"global.xml"
-#define PROPS_DEFAULT_CONTENT "\n\n"
+#define CTX_FILE_NAME L"contextMenu.xml"
+#define GLB_FILE_NAME L"global.xml"
+#define GLB_DEFAULT_CONTENT "\n\n"
+#define BAK_DIR_NAME L"backup"
+#define BAK_SES_DIR_NAME L"sessions"
HWND _hNpp;
HWND _hSci1;
HWND _hSci2;
HANDLE _hHeap;
HINSTANCE _hDll;
-//UINT _nppVersion;
-LPWSTR _cfgDir; ///< SessionMgr's config directory, includes trailing slash
-LPWSTR _cfgFile; ///< pathname of settings.xml
-LPWSTR _ctxFile; ///< pathname of NPP's contextMenu.xml file
-LPWSTR _propsFile; ///< pathname of global.xml
-
+UINT _nppVersion;
+UINT _winVersion;
+LPWSTR _cfgDir; ///< SessionMgr's config directory, includes trailing slash
+LPWSTR _cfgFile; ///< pathname of settings.xml
+LPWSTR _glbFile; ///< pathname of global.xml
+LPWSTR _ctxFile; ///< pathname of NPP's contextMenu.xml file
+
+//void findNppCtxMnuFile();
void backupFiles();
void backupConfigFiles(LPCWSTR backupDir);
void backupSessionFiles(LPCWSTR backupDir);
@@ -59,10 +65,10 @@ void sys_onLoad(HINSTANCE hDLLInstance)
void sys_onUnload()
{
- sys_free(_cfgDir);
- sys_free(_cfgFile);
sys_free(_ctxFile);
- sys_free(_propsFile);
+ sys_free(_glbFile);
+ sys_free(_cfgFile);
+ sys_free(_cfgDir);
}
void sys_init(NppData nppd)
@@ -76,31 +82,40 @@ void sys_init(NppData nppd)
// Allocate buffers
_cfgDir = (LPWSTR)sys_alloc(MAX_PATH * sizeof WCHAR);
_cfgFile = (LPWSTR)sys_alloc(MAX_PATH * sizeof WCHAR);
+ _glbFile = (LPWSTR)sys_alloc(MAX_PATH * sizeof WCHAR);
_ctxFile = (LPWSTR)sys_alloc(MAX_PATH * sizeof WCHAR);
- _propsFile = (LPWSTR)sys_alloc(MAX_PATH * sizeof WCHAR);
- //_nppVersion = ::SendMessage(_hNpp, NPPM_GETNPPVERSION, 0, 0);
+ _nppVersion = ::SendMessage(_hNpp, NPPM_GETNPPVERSION, 0, 0);
+ _winVersion = ::SendMessage(_hNpp, NPPM_GETWINDOWSVERSION, 0, 0);
- // Get NPP's contextMenu.xml pathname.
- ::SendMessage(_hNpp, NPPM_GETNPPDIRECTORY, MAX_PATH, (LPARAM)_ctxFile);
- pth::appendSlash(_ctxFile, MAX_PATH);
- ::StringCchCatW(_ctxFile, MAX_PATH, L"contextMenu.xml");
- // Get plugin config directory from NPP.
- ::SendMessage(_hNpp, NPPM_GETPLUGINSCONFIGDIR, MAX_PATH, (LPARAM)_cfgDir);
- // Get SessionMgr config directory and create it if not present.
+ // Get NPP's "plugins\Config" directory.
+ ::SendMessage(_hNpp, NPPM_GETPLUGINSCONFIGDIR, MAX_PATH, (LPARAM)_ctxFile);
+
+ // Get SessionMgr config directory and create it if missing.
+ ::StringCchCopyW(_cfgDir, MAX_PATH, _ctxFile);
pth::appendSlash(_cfgDir, MAX_PATH);
::StringCchCatW(_cfgDir, MAX_PATH, PLUGIN_DLL_NAME);
::StringCchCatW(_cfgDir, MAX_PATH, L"\\");
::CreateDirectoryW(_cfgDir, NULL);
+ // Get the global.xml file pathname and create it if missing.
+ ::StringCchCopyW(_glbFile, MAX_PATH, _cfgDir);
+ ::StringCchCatW(_glbFile, MAX_PATH, GLB_FILE_NAME);
+ pth::createFileIfMissing(_glbFile, GLB_DEFAULT_CONTENT);
+
// Get the settings.xml file pathname and load the configuration.
::StringCchCopyW(_cfgFile, MAX_PATH, _cfgDir);
::StringCchCatW(_cfgFile, MAX_PATH, CFG_FILE_NAME);
cfg::loadSettings();
- // Create sessions directory if it doesn't exist.
+ // Create sessions directory if missing.
::CreateDirectoryW(cfg::getStr(kSessionDirectory), NULL);
- // Create default session file if it doesn't exist.
+ // Create default session file if missing.
app_confirmDefaultSession();
+
+ // Get NPP's contextMenu.xml pathname (two directories up from "plugins\Config").
+ LPWSTR p = ::wcsstr(_ctxFile, L"plugins\\Config");
+ ::StringCchCopyW(p, MAX_PATH, CTX_FILE_NAME);
+
// Backup existing config and session files.
if (cfg::getBool(kBackupOnStartup)) {
backupFiles();
@@ -121,17 +136,12 @@ LPWSTR sys_getSettingsFile()
return _cfgFile;
}
-LPWSTR sys_getPropsFile()
+LPWSTR sys_getGlobalFile()
{
- if (!_propsFile[0]) {
- ::StringCchCopyW(_propsFile, MAX_PATH, _cfgDir);
- ::StringCchCatW(_propsFile, MAX_PATH, PROPS_FILE_NAME);
- pth::createFileIfMissing(_propsFile, PROPS_DEFAULT_CONTENT);
- }
- return _propsFile;
+ return _glbFile;
}
-LPCWSTR sys_getContextMenuFile()
+LPCWSTR sys_getNppCtxMnuFile()
{
return _ctxFile;
}
@@ -153,6 +163,19 @@ HWND sys_getSciHandle(INT v)
else return NULL;
}
+/** @return NPP version with major in hiword and minor in loword */
+DWORD sys_getNppVer()
+{
+ return _nppVersion;
+}
+
+/** @return a winVer enum
+ @see npp/Notepad_plus_msgs.h */
+UINT sys_getWinVer()
+{
+ return _winVersion;
+}
+
LPVOID sys_alloc(INT bytes)
{
LPVOID p = ::HeapAlloc(_hHeap, HEAP_ZERO_MEMORY, bytes);
@@ -172,21 +195,76 @@ void sys_free(LPVOID p)
namespace {
+/* Gets the pathname of NPP's contextMenu.xml. Now not used. Just assume the
+ file is two directories up from "plugins\Config".
+void findNppCtxMnuFile()
+{
+ ITEMIDLIST *pidl;
+ bool isLocal = false;
+ WCHAR tmp[MAX_PATH], nppDir[MAX_PATH];
+
+ ::SendMessage(_hNpp, NPPM_GETNPPDIRECTORY, MAX_PATH, (LPARAM)nppDir);
+ pth::appendSlash(nppDir, MAX_PATH);
+ ::StringCchCopyW(tmp, MAX_PATH, nppDir);
+ ::StringCchCatW(tmp, MAX_PATH, L"doLocalConf.xml");
+ if (pth::fileExists(tmp)) {
+ isLocal = true;
+ LOGG(12, "Found doLocalConf.xml");
+ }
+
+ // See NppParameters::load in NPP's Parameters.cpp...
+ if (isLocal && _winVersion >= WV_VISTA) {
+ if (::SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidl) == S_OK) {
+ if (::SHGetPathFromIDList(pidl, tmp)) {
+ LOGG(12, "prg=\"%S\", npp=\"%S\"", tmp, nppDir);
+ if (::_wcsnicmp(tmp, nppDir, wcslen(tmp)) == 0) {
+ isLocal = false;
+ }
+ }
+ else LOGG(12, "SHGetPathFromIDList failed for CSIDL_PROGRAM_FILES");
+ //::CoTaskMemFree(pidl);
+ }
+ else LOGG(12, "SHGetSpecialFolderLocation failed for CSIDL_PROGRAM_FILES");
+ }
+ ::StringCchCopyW(_ctxFile, MAX_PATH, nppDir);
+ if (!isLocal) {
+ if (::SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidl) == S_OK) {
+ if (::SHGetPathFromIDList(pidl, tmp)) {
+ pth::appendSlash(tmp, MAX_PATH);
+ ::StringCchCatW(tmp, MAX_PATH, L"Notepad++");
+ if (pth::dirExists(tmp)) {
+ LOGG(12, "Found \"%S\"", tmp);
+ ::StringCchCopyW(_ctxFile, MAX_PATH, tmp);
+ }
+ else LOGG(12, "Could not find \"%S\"", tmp);
+ }
+ else LOGG(12, "SHGetPathFromIDList failed for CSIDL_APPDATA");
+ //::CoTaskMemFree(pidl);
+ }
+ else LOGG(12, "SHGetSpecialFolderLocation failed for CSIDL_APPDATA");
+ }
+
+ pth::appendSlash(_ctxFile, MAX_PATH);
+ ::StringCchCatW(_ctxFile, MAX_PATH, CTX_FILE_NAME);
+ LOGG(12, "Will use \"%S\"", _ctxFile);
+}
+*/
+
void backupFiles()
{
WCHAR backupDir[MAX_PATH];
- // Create main backup directory if it doesn't exist and copy config files.
+ // Create main backup directory if missing and copy config files.
::StringCchCopyW(backupDir, MAX_PATH, _cfgDir);
- ::StringCchCatW(backupDir, MAX_PATH, L"backup");
+ ::StringCchCatW(backupDir, MAX_PATH, BAK_DIR_NAME);
if (!pth::dirExists(backupDir)) {
::CreateDirectoryW(backupDir, NULL);
}
if (pth::dirExists(backupDir)) {
::StringCchCatW(backupDir, MAX_PATH, L"\\");
backupConfigFiles(backupDir);
- // Create backup directory for session files if it doesn't exist and copy session files.
- ::StringCchCatW(backupDir, MAX_PATH, L"sessions");
+ // Create backup directory for session files if missing and copy session files.
+ ::StringCchCatW(backupDir, MAX_PATH, BAK_SES_DIR_NAME);
if (!pth::dirExists(backupDir)) {
::CreateDirectoryW(backupDir, NULL);
}
@@ -208,15 +286,15 @@ void backupConfigFiles(LPCWSTR backupDir)
::CopyFileW(_cfgFile, dstFile, FALSE);
}
// Copy global.xml
- if (pth::fileExists(sys_getPropsFile())) {
+ if (pth::fileExists(_glbFile)) {
::StringCchCopyW(dstFile, MAX_PATH, backupDir);
- ::StringCchCatW(dstFile, MAX_PATH, PROPS_FILE_NAME);
- ::CopyFileW(_propsFile, dstFile, FALSE);
+ ::StringCchCatW(dstFile, MAX_PATH, GLB_FILE_NAME);
+ ::CopyFileW(_glbFile, dstFile, FALSE);
}
// Copy NPP's contextMenu.xml
if (pth::fileExists(_ctxFile)) {
::StringCchCopyW(dstFile, MAX_PATH, backupDir);
- ::StringCchCatW(dstFile, MAX_PATH, L"contextMenu.xml");
+ ::StringCchCatW(dstFile, MAX_PATH, CTX_FILE_NAME);
::CopyFileW(_ctxFile, dstFile, FALSE);
}
}
diff --git a/src/System.h b/src/System.h
index a6ffcd2..169bfde 100644
--- a/src/System.h
+++ b/src/System.h
@@ -40,11 +40,13 @@ void sys_init(NppData nppd);
LPWSTR sys_getCfgDir();
LPWSTR sys_getSettingsFile();
-LPWSTR sys_getPropsFile();
-LPCWSTR sys_getContextMenuFile();
+LPWSTR sys_getGlobalFile();
+LPCWSTR sys_getNppCtxMnuFile();
HINSTANCE sys_getDllHandle();
HWND sys_getNppHandle();
HWND sys_getSciHandle(INT v);
+DWORD sys_getNppVer();
+UINT sys_getWinVer();
LPVOID sys_alloc(INT bytes);
void sys_free(LPVOID p);
diff --git a/src/Util.cpp b/src/Util.cpp
index 28cd0ff..6a67eda 100644
--- a/src/Util.cpp
+++ b/src/Util.cpp
@@ -16,6 +16,7 @@
#include "System.h"
#include "SessionMgr.h"
#include "Util.h"
+#include "utf8\unchecked.h"
#include
//------------------------------------------------------------------------------
@@ -24,31 +25,6 @@ namespace NppPlugin {
//------------------------------------------------------------------------------
-namespace {
-
-DWORD _mbConversionFlag = 0;
-DWORD _wcConversionFlag = 0;
-
-} // end namespace
-
-//------------------------------------------------------------------------------
-
-namespace api {
-
-void util_init()
-{
- LRESULT winVer = ::SendMessage(sys_getNppHandle(), NPPM_GETWINDOWSVERSION, 0, 0);
- LOG("winVer %lu", winVer);
- //if (winVer >= WV_VISTA) {
- // _mbConversionFlag = MB_ERR_INVALID_CHARS;
- // _wcConversionFlag = WC_ERR_INVALID_CHARS;
- //}
-}
-
-} // end namespace NppPlugin::api
-
-//------------------------------------------------------------------------------
-
namespace msg {
/** Displays a simple message box. For title/options see the M_* constants. */
@@ -257,7 +233,7 @@ bool wildcardMatchI(LPCWSTR wild, LPCWSTR str)
}
/** Originally written by Jack Handy and slightly modified by Mike Foster.
- http://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing */
+ @see http://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing */
bool wildcardMatch(LPCWSTR wild, LPCWSTR str)
{
LPCWSTR cp = NULL, mp = NULL;
@@ -295,6 +271,37 @@ bool wildcardMatch(LPCWSTR wild, LPCWSTR str)
return !*wild;
}
+/** Converts a UTF-8 string to a string where all chars < 32 or > 126 are
+ converted to entities. Pass NULL for buf to get the size needed for buf.
+ @return the number of bytes in the converted string including the terminator */
+INT utf8ToAscii(LPCSTR str, LPSTR buf)
+{
+ INT bytes = 0;
+ LPSTR b = buf;
+ LPCSTR s = str;
+ utf8::uint32_t cp;
+ while (*s) {
+ cp = utf8::unchecked::next(s);
+ if (cp < 32 || cp > 126) {
+ if (buf) {
+ ::sprintf_s(b, 9, "%04X;", cp);
+ b += 8;
+ }
+ bytes += 8;
+ }
+ else {
+ if (buf) {
+ *b++ = (unsigned char)cp;
+ }
+ ++bytes;
+ }
+ }
+ if (buf) {
+ *b = 0;
+ }
+ return bytes + 1;
+}
+
/** cStr must be zero-terminated.
@return a pointer to an allocated buffer which caller must free, else NULL
on error */
@@ -312,7 +319,7 @@ LPWSTR utf8ToUtf16(LPCSTR cStr, LPWSTR buf, size_t bufLen)
DWORD lastError;
LPWSTR wBuf = NULL;
- wLen = ::MultiByteToWideChar(CP_UTF8, _mbConversionFlag, cStr, -1, NULL, 0);
+ wLen = ::MultiByteToWideChar(CP_UTF8, 0, cStr, -1, NULL, 0);
if (wLen > 0) {
if (buf) {
if (bufLen >= wLen) {
@@ -368,7 +375,7 @@ LPSTR utf16ToUtf8(LPCWSTR wStr, LPSTR buf, size_t bufLen)
DWORD lastError;
LPSTR cBuf = NULL;
- cLen = ::WideCharToMultiByte(CP_UTF8, _wcConversionFlag, wStr, -1, NULL, 0, NULL, NULL);
+ cLen = ::WideCharToMultiByte(CP_UTF8, 0, wStr, -1, NULL, 0, NULL, NULL);
if (cLen > 0) {
if (buf) {
if (bufLen >= cLen) {
@@ -509,7 +516,7 @@ void getCbSelText(HWND hDlg, UINT idCtrl, LPWSTR buf)
}
}
-// http://stackoverflow.com/questions/1823883/updating-text-in-a-c-win32-api-static-control-drawn-with-ws-ex-transparent
+/** @see http://stackoverflow.com/questions/1823883/updating-text-in-a-c-win32-api-static-control-drawn-with-ws-ex-transparent */
void redrawControl(HWND hDlg, HWND hCtrl)
{
RECT r;
diff --git a/src/Util.h b/src/Util.h
index c5bfbb8..ab834bf 100644
--- a/src/Util.h
+++ b/src/Util.h
@@ -42,15 +42,6 @@ namespace NppPlugin {
inline LPCWSTR boolToStr(const bool b) { return b ? L"true" : L"false"; }
inline const bool uintToBool(UINT n) { return n == 0 ? false : true; }
-//------------------------------------------------------------------------------
-/// @namespace NppPlugin::api Contains functions called only from DllMain.
-
-namespace api {
-
-void util_init();
-
-} // end namespace NppPlugin::api
-
//------------------------------------------------------------------------------
/** @namespace NppPlugin::msg Contains functions for displaying error and
informational messages to the user and for logging to the debug log file. */
@@ -88,6 +79,7 @@ void removeAmp(LPCWSTR src, LPWSTR dst);
void removeAmp(LPCSTR src, LPSTR dst);
bool wildcardMatchI(LPCWSTR wild, LPCWSTR str);
bool wildcardMatch(LPCWSTR wild, LPCWSTR str);
+INT utf8ToAscii(LPCSTR str, LPSTR buf = NULL);
LPWSTR utf8ToUtf16(LPCSTR cStr);
LPWSTR utf8ToUtf16(LPCSTR cStr, LPWSTR buf, size_t bufLen);
LPSTR utf16ToUtf8(LPCWSTR wStr);
diff --git a/src/res/version.h b/src/res/version.h
index 3200b59..33d8a1f 100644
--- a/src/res/version.h
+++ b/src/res/version.h
@@ -17,8 +17,8 @@
#define NPP_PLUGIN_VERSION_H
-#define RES_VERSION_B 1,4,1,0
-#define RES_VERSION_S "1.4.1.0"
+#define RES_VERSION_B 1,4,2,0
+#define RES_VERSION_S "1.4.2.0"
#define PLUGIN_VERSION _W(RES_VERSION_S)
#define RES_COPYRIGHT "Copyright 2011-2015 Michael Foster. Distributed under the terms of the GNU GPL."
diff --git a/x.cmd b/x.cmd
index 800d7bf..2a78e7a 100644
--- a/x.cmd
+++ b/x.cmd
@@ -1,7 +1,7 @@
@echo off
-set sesmgr_old_ver=1.4
-set sesmgr_new_ver=1.4.1
+set sesmgr_old_ver=1.4.1
+set sesmgr_new_ver=1.4.2
set release_dir=c:\prj\npp-session-manager\dist\%sesmgr_new_ver%
set zip_exe="c:\Program Files\7-Zip\7z.exe"
set md5_exe=c:\bin\md5sums\md5sums.exe