diff --git a/doc/SessionMgr.html b/doc/SessionMgr.html index e45b073..d76d9f2 100644 --- a/doc/SessionMgr.html +++ b/doc/SessionMgr.html @@ -13,7 +13,7 @@
-

Session Manager 1.4.1

+

Session Manager 1.4.2

A Plugin for Notepad++

@@ -48,9 +48,9 @@

Contents

  • Revision History
  • @@ -67,8 +67,8 @@

    Installation

  • Exit Notepad++.
  • When upgrading from a previous version: @@ -96,7 +96,7 @@

    Basic Concepts

    Plugins Menu

    -

    Session Manager creates a submenu in the Notepad++ Plugins menu. You can customize Session Manager's menu items (see the Manual Settings section). The default labels and shortcuts follow. You can also configure your own shortcuts with the Notepad++ Shortcut Mapper. Favorite sessions are listed after the About item.

    +

    Session Manager creates a submenu in the Notepad++ Plugins menu. You can customize Session Manager's menu item labels and shortcut keys (see the Manual Settings section). The default labels and shortcuts follow. Favorite sessions are listed after the About item.

    Main Menu Item

    Session Manager (alt+p,s): Opens the Session Manager submenu.

    Submenu Items

    @@ -111,6 +111,7 @@

    Submenu Items

    Context Menu

    Session Manager creates a submenu in the context (right-click) menu if you have Use context menu checked in Settings. The submenu is only created or updated when you make a change to your favorite sessions. You must restart Notepad++ for the changes to appear in the menus. The menu labels used in the context submenu are the same as those used in the Plugins menu and favorite sessions are listed after the About item.

    +

    If you have Use context menu enabled and you change existing menuLabel* settings, Session Manager does not remove the old items from the "contextMenu.xml" file. You have to remove those manually. See the Advanced/ContextMenu section for more information.

    Sessions Dialog

    @@ -164,7 +165,7 @@

    Options

    If Auto save is disabled sessions will only be saved when you select Save current from the plugin menu or click the Save button on the Sessions dialog. The default for Auto save is enabled.

    -

    Auto load: If this is enabled the most recently opened session will be loaded when Notepad++ starts up. If you enable Auto load you should disable the Notepad++ Remember current session for next launch feature. The default for Auto load is disabled.

    +

    Auto load: If this is enabled the most recently opened session will be loaded when Notepad++ starts up. Important: If you enable Auto load you must disable the Notepad++ Remember current session for next launch feature. The default for Auto load is disabled.

    Load into current: This setting is applied any time a session is loaded. It also determines the initial state of the Load into current checkbox on the Sessions dialog. The default is disabled.

    Load without closing: This setting is applied any time a session is loaded except on startup where it is considered enabled. It also determines the initial state of the Load without closing checkbox on the Sessions dialog. The default is disabled.

    Show in title bar: If this is enabled the current session name will be prepended to the text in the Notepad++ title bar. The default is disabled.

    @@ -222,7 +223,7 @@

    Changes Not Saved

    Feedback & Support

    -

    There is an ongoing discussion at the Notepad++ plugins forum. Join us!

    +

    Look for the newest Session Manager thread in the Notepad++ Plugins Forum. Ask questions, request features, submit bug reports, or just drop in to chat. We look forward to hearing what you think about Session Manager!

    Advanced

    @@ -230,8 +231,18 @@

    Advanced

    This section contains advanced and technical topics which are not necessary to understand for normal usage of Session Manager.

    Context Menu

    -

    For this feature Session Manager makes changes to the Notepad++ "contextMenu.xml" file. If the Session Manager submenu does not already exist it will be added to the end of the context menu. You can move it to a different location simply by cutting all Session Manager Item elements and pasting them somewhere else in the file. The Item element with id="0" creates a separator and you can add those wherever you want. Before editing the file disable the Use context menu setting and reenable it after editing the file or Session Manager may overwrite your changes.

    -

    If you have Use context menu enabled and you change existing menuLabel* settings, Session Manager does not remove the old items from the "contextMenu.xml" file. You have to remove those manually.

    +

    For this feature Session Manager makes changes to Notepad++'s "contextMenu.xml" file. If the Session Manager submenu does not already exist it will be added to the end of the context menu. You can move it to a different location simply by cutting all Session Manager Item elements and pasting them somewhere else in the file. The Item element with id="0" creates a separator and you can add those wherever you want. Before editing the file disable the Use context menu setting and reenable it after editing the file or Session Manager may overwrite your changes.

    +

    If you have Use context menu enabled and you change existing menuLabel* settings, Session Manager does not remove the old items from the "contextMenu.xml" file. You have to remove those manually. Here's a procedure for that:

    +
      +
    1. Open Session Manager's Settings dialog, uncheck the Use context menu option and save the change.
    2. +
    3. Open Notepad++'s "contextMenu.xml" file.
    4. +
    5. Delete all Session Manager Item elements.
    6. +
    7. Save the changes to "contextMenu.xml".
    8. +
    9. Restart Notepad++.
    10. +
    11. Open Session Manager's Settings dialog, check the Use context menu option and save the change.
    12. +
    13. Open Session Manager's Sessions dialog, select some favorites and close the dialog.
    14. +
    15. Restart Notepad++.
    16. +

    Manual Settings

    @@ -254,13 +265,13 @@

    Manual Settings

    - menuLabel*: These settings optionally define the labels for the Session Manager plugin menu and context submenu. Session Manager will use default values (see the Plugin Menu section) for any items not defined here. Here is an example of customizing the menu items: + menuLabel*: These settings optionally define the labels and shortcut keys for the Session Manager plugin menu and context submenu. Session Manager will use default values (see the Plugin Menu section) for any items not defined here. Here is an example of customizing the menu items:

     <menuLabelMain value="Session &Manager"/>
     <menuLabelSub1 value="&Open..."/>
     <menuLabelSub2 value="&Configure..."/>
     <menuLabelSub3 value="&Save current"/>
    -<menuLabelSub4 value="&Load previous"/>
    +<menuLabelSub4 value="Load &previous"/>
     <menuLabelSub5 value="&Help"/>
     <menuLabelSub6 value="&About..."/>

    @@ -270,19 +281,15 @@

    Manual Settings

    Favorites: These items define the favorites. You can edit these or add more, or delete these if you want to clear all favorites. Note that Session Manager supports session names up to 100 characters but Notepad++ only allows menu items up to 64 characters.

    Filters: These items define the filters. You can edit these or add more, or delete these if you want to clear the filters list. On startup a "*" filter will be automatically added.

    -

    Plugin-To-Plugin API

    -
    -

    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.

    +

    Plugin-To-Plugin API

    +
    +

    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

    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, "&#x%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, "&#x%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