From 7476ada22352363f91d279f7d5b6d48c34ca1fc3 Mon Sep 17 00:00:00 2001 From: Don HO Date: Mon, 16 Oct 2017 03:26:39 +0200 Subject: [PATCH] Enhance Plugin Admin UI --- PowerEditor/src/Notepad_plus.cpp | 2 +- .../WinControls/AnsiCharPanel/ListView.cpp | 18 +- .../src/WinControls/AnsiCharPanel/ListView.h | 2 + .../WinControls/PluginsAdmin/pluginsAdmin.cpp | 320 +++++++++++++----- .../WinControls/PluginsAdmin/pluginsAdmin.h | 46 ++- 5 files changed, 274 insertions(+), 114 deletions(-) diff --git a/PowerEditor/src/Notepad_plus.cpp b/PowerEditor/src/Notepad_plus.cpp index bdafe1ea5..d634e7226 100644 --- a/PowerEditor/src/Notepad_plus.cpp +++ b/PowerEditor/src/Notepad_plus.cpp @@ -6055,7 +6055,7 @@ Quote quotes[nbQuote] = {"Anonymous #25", "In a way, I feel sorry for the kids of this generation.\nThey'll have parents who know how to check browser history."}, {"Anonymous #26", "I would never bungee jump.\nI came into this world because of a broken rubber, and I'm not going out cause of one."}, {"Anonymous #27", "I don't have a problem with caffeine.\nI have a problem without caffeine."}, - {"Anonymous #28", "Why 6 afraid of 7?\nBecause 7 8 9 (seven ate nine) while 6 and 9 were flirting."}, + {"Anonymous #28", "Why 6 afraid of 7?\nBecause 7 8 9 while 6 and 9 were flirting."}, {"Anonymous #30", "Why do Java developers wear glasses?\nBecause they don't C#."}, {"Anonymous #31", "A baby's laughter is one of the most beautiful sounds you will ever hear. Unless it's 3 AM. And you're home alone. And you don't have a baby."}, {"Anonymous #32", "Two bytes meet. The first byte asks, \"You look terrible. Are you OK?\"\nThe second byte replies, \"No, just feeling a bit off.\""}, diff --git a/PowerEditor/src/WinControls/AnsiCharPanel/ListView.cpp b/PowerEditor/src/WinControls/AnsiCharPanel/ListView.cpp index 5c7790785..4230cc266 100644 --- a/PowerEditor/src/WinControls/AnsiCharPanel/ListView.cpp +++ b/PowerEditor/src/WinControls/AnsiCharPanel/ListView.cpp @@ -32,6 +32,8 @@ #include "Parameters.h" #include "localization.h" +using namespace std; + void ListView::init(HINSTANCE hInst, HWND parent) { Window::init(hInst, parent); @@ -91,7 +93,7 @@ void ListView::destroy() _hSelf = NULL; } -void ListView::addLine(const std::vector & values2Add, LPARAM lParam, int pos2insert) +void ListView::addLine(const vector & values2Add, LPARAM lParam, int pos2insert) { if (not values2Add.size()) return; @@ -119,7 +121,6 @@ void ListView::addLine(const std::vector & values2Add, LPARAM lP } - LPARAM ListView::getLParamFromIndex(int itemIndex) const { LVITEM item; @@ -130,6 +131,19 @@ LPARAM ListView::getLParamFromIndex(int itemIndex) const return item.lParam; } +std::vector ListView::getCheckedIndexes() const +{ + vector checkedIndexes; + size_t nbItem = ListView_GetItemCount(_hSelf); + for (size_t i = 0; i < nbItem; ++i) + { + UINT st = ListView_GetItemState(_hSelf, i, LVIS_STATEIMAGEMASK); + if (st == INDEXTOSTATEIMAGEMASK(2)) // checked + checkedIndexes.push_back(i); + } + return checkedIndexes; +} + LRESULT ListView::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { return ::CallWindowProc(_defaultProc, hwnd, Message, wParam, lParam); diff --git a/PowerEditor/src/WinControls/AnsiCharPanel/ListView.h b/PowerEditor/src/WinControls/AnsiCharPanel/ListView.h index ad701fce8..023a0af50 100644 --- a/PowerEditor/src/WinControls/AnsiCharPanel/ListView.h +++ b/PowerEditor/src/WinControls/AnsiCharPanel/ListView.h @@ -74,6 +74,8 @@ public: LPARAM getLParamFromIndex(int itemIndex) const; + std::vector getCheckedIndexes() const; + virtual void init(HINSTANCE hInst, HWND hwnd); virtual void destroy(); diff --git a/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.cpp b/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.cpp index bc47b49ce..249051191 100644 --- a/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.cpp +++ b/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.cpp @@ -71,37 +71,64 @@ void Version::setVersionFrom(generic_string filePath) generic_string Version::toString() { - std::wstring v = std::to_wstring(_major); - v += TEXT("."); - v += std::to_wstring(_minor); - v += TEXT("."); - v += std::to_wstring(_patch); - v += TEXT("."); - v += std::to_wstring(_build); - return v; + if (_build == 0 && _patch == 0 && _minor == 0 && _major == 0) // "" + { + return TEXT(""); + } + else if (_build == 0 && _patch == 0 && _minor == 0) // "major" + { + return std::to_wstring(_major); + } + else if (_build == 0 && _patch == 0) // "major.minor" + { + std::wstring v = std::to_wstring(_major); + v += TEXT("."); + v += std::to_wstring(_minor); + return v; + } + else if (_build == 0) // "major.minor.patch" + { + std::wstring v = std::to_wstring(_major); + v += TEXT("."); + v += std::to_wstring(_minor); + v += TEXT("."); + v += std::to_wstring(_patch); + return v; + } + + // "major.minor.patch.build" + std::wstring ver = std::to_wstring(_major); + ver += TEXT("."); + ver += std::to_wstring(_minor); + ver += TEXT("."); + ver += std::to_wstring(_patch); + ver += TEXT("."); + ver += std::to_wstring(_build); + + return ver; } generic_string PluginUpdateInfo::describe() { generic_string desc; const TCHAR *EOL = TEXT("\r\n"); - if (not description.empty()) + if (not _description.empty()) { - desc = description; + desc = _description; desc += EOL; } - if (not author.empty()) + if (not _author.empty()) { desc += TEXT("Author: "); - desc += author; + desc += _author; desc += EOL; } - if (not homepage.empty()) + if (not _homepage.empty()) { desc += TEXT("Homepage: "); - desc += homepage; + desc += _homepage; desc += EOL; } @@ -119,21 +146,6 @@ bool findStrNoCase(const generic_string & strHaystack, const generic_string & st return (it != strHaystack.end()); } -LoadedPluginInfo::LoadedPluginInfo(const generic_string & fullFilePath, const generic_string & filename) -{ - if (not::PathFileExists(fullFilePath.c_str())) - return; - - _fullFilePath = fullFilePath; - _name = filename; - - WcharMbcsConvertor *wmc = WcharMbcsConvertor::getInstance(); - const char *path = wmc->wchar2char(fullFilePath.c_str(), CP_ACP); - MD5 md5; - _id = wmc->char2wchar(md5.digestFile(path), CP_ACP); - - _version.setVersionFrom(fullFilePath); -} long PluginsAdminDlg::searchFromCurrentSel(generic_string str2search, bool inWhichPart, bool isNextMode) const { @@ -148,9 +160,9 @@ long PluginsAdminDlg::searchFromCurrentSel(generic_string str2search, bool inWhi size_t j = _availableListView.getLParamFromIndex(i); generic_string searchIn; if (inWhichPart == inNames) - searchIn = _availablePluginList[j].name; + searchIn = _availablePluginList[j]._name; else //(inWhichPart == inDescs) - searchIn = _availablePluginList[j].description; + searchIn = _availablePluginList[j]._description; if (findStrNoCase(searchIn, str2search)) return i; @@ -166,9 +178,9 @@ long PluginsAdminDlg::searchFromCurrentSel(generic_string str2search, bool inWhi size_t j = _availableListView.getLParamFromIndex(i); generic_string searchIn; if (inWhichPart == inNames) - searchIn = _availablePluginList[j].name; + searchIn = _availablePluginList[j]._name; else //(inWhichPart == inDescs) - searchIn = _availablePluginList[j].description; + searchIn = _availablePluginList[j]._description; if (findStrNoCase(searchIn, str2search)) return i; @@ -180,9 +192,9 @@ long PluginsAdminDlg::searchFromCurrentSel(generic_string str2search, bool inWhi size_t j = _availableListView.getLParamFromIndex(i); generic_string searchIn; if (inWhichPart == inNames) - searchIn = _availablePluginList[j].name; + searchIn = _availablePluginList[j]._name; else //(inWhichPart == inDescs) - searchIn = _availablePluginList[j].description; + searchIn = _availablePluginList[j]._description; if (findStrNoCase(searchIn, str2search)) return i; @@ -338,21 +350,36 @@ void PluginsAdminDlg::collectNppCurrentStatusInfos() bool PluginsAdminDlg::installPlugins() { + vector indexes = _availableListView.getCheckedIndexes(); + for (auto i : indexes) + { + printStr(_availablePluginList[i]._name .c_str()); + } return true; } bool PluginsAdminDlg::updatePlugins() { + vector indexes = _updateListView.getCheckedIndexes(); + for (auto i : indexes) + { + printStr(_updatePluginList[i]._fullFilePath.c_str()); + } return true; } bool PluginsAdminDlg::removePlugins() { + vector indexes = _installedListView.getCheckedIndexes(); + for (auto i : indexes) + { + printStr(_installedPluginList[i]._fullFilePath.c_str()); + } return true; } -bool loadFromJson(std::vector & pl, const json& j) +bool loadFromJson(vector & pl, const json& j) { if (j.empty()) return false; @@ -369,22 +396,22 @@ bool loadFromJson(std::vector & pl, const json& j) PluginUpdateInfo pi; string valStr = i.at("folder-name").get(); - pi.name = wmc->char2wchar(valStr.c_str(), CP_ACP); + pi._name = wmc->char2wchar(valStr.c_str(), CP_ACP); valStr = i.at("display-name").get(); - pi.alias = wmc->char2wchar(valStr.c_str(), CP_ACP); + pi._alias = wmc->char2wchar(valStr.c_str(), CP_ACP); valStr = i.at("author").get(); - pi.author = wmc->char2wchar(valStr.c_str(), CP_ACP); + pi._author = wmc->char2wchar(valStr.c_str(), CP_ACP); valStr = i.at("description").get(); - pi.description = wmc->char2wchar(valStr.c_str(), CP_ACP); + pi._description = wmc->char2wchar(valStr.c_str(), CP_ACP); valStr = i.at("repository").get(); - pi.repository = wmc->char2wchar(valStr.c_str(), CP_ACP); + pi._repository = wmc->char2wchar(valStr.c_str(), CP_ACP); valStr = i.at("homepage").get(); - pi.homepage = wmc->char2wchar(valStr.c_str(), CP_ACP); + pi._homepage = wmc->char2wchar(valStr.c_str(), CP_ACP); pl.push_back(pi); @@ -421,11 +448,18 @@ bool PluginsAdminDlg::updateListAndLoadFromJson() json pluginsJson; nppPluginListJson >> pluginsJson; + // initialize available list view loadFromJson(_availablePluginList, pluginsJson); - - // update available list view updateAvailableListView(); + // initialize update list view + checkUpdates(); + updateUpdateListView(); + + // initialize installed list view + loadFromPluginInfos(); + updateInstalledListView(); + return true; } @@ -436,27 +470,80 @@ void PluginsAdminDlg::updateAvailableListView() for (const auto& pui : _availablePluginList) { vector values2Add; - values2Add.push_back(pui.name); - values2Add.push_back(pui.version); + values2Add.push_back(pui._name); + Version v = pui._version; + values2Add.push_back(v.toString()); values2Add.push_back(TEXT("Yes")); _availableListView.addLine(values2Add, i++); } } -bool PluginsAdminDlg::getLoadedPluginInfos() +void PluginsAdminDlg::updateInstalledListView() +{ + size_t i = 0; + // + for (const auto& lpi : _installedPluginList) + { + vector values2Add; + values2Add.push_back(lpi._name); + Version v = lpi._version; + values2Add.push_back(v.toString()); + values2Add.push_back(TEXT("Yes")); + _installedListView.addLine(values2Add, i++); + } +} + +void PluginsAdminDlg::updateUpdateListView() +{ + size_t i = 0; + // + for (const auto& pui : _updatePluginList) + { + vector values2Add; + values2Add.push_back(pui._name); + Version v = pui._version; + values2Add.push_back(v.toString()); + values2Add.push_back(TEXT("Yes")); + _updateListView.addLine(values2Add, i++); + } +} + +PluginUpdateInfo::PluginUpdateInfo(const generic_string& fullFilePath, const generic_string& filename) +{ + if (not::PathFileExists(fullFilePath.c_str())) + return; + + _fullFilePath = fullFilePath; + _name = filename; + + WcharMbcsConvertor *wmc = WcharMbcsConvertor::getInstance(); + const char *path = wmc->wchar2char(fullFilePath.c_str(), CP_ACP); + MD5 md5; + _id = wmc->char2wchar(md5.digestFile(path), CP_ACP); + + _version.setVersionFrom(fullFilePath); + +} + +bool PluginsAdminDlg::loadFromPluginInfos() { if (!_pPluginsManager) return false; for (const auto& i : _pPluginsManager->_loadedDlls) { - LoadedPluginInfo lpi(i._fullFilePath, i._fileName); - _loadedPluginInfos.push_back(lpi); + PluginUpdateInfo pui(i._fullFilePath, i._fileName); + _installedPluginList.push_back(pui); } return true; } +bool PluginsAdminDlg::checkUpdates() +{ + return true; +} + // begin insentive-case search from the second key-in character bool PluginsAdminDlg::searchInPlugins(bool isNextMode) const { @@ -480,63 +567,83 @@ bool PluginsAdminDlg::searchInPlugins(bool isNextMode) const void PluginsAdminDlg::switchDialog(int indexToSwitch) { - std::vector* pUpiList = nullptr; - ListView* pListView = nullptr; - + generic_string desc; bool showAvailable, showUpdate, showInstalled; switch (indexToSwitch) { case 0: // available plugins + { showAvailable = true; showUpdate = false; showInstalled = false; - pUpiList = &_availablePluginList; - pListView = &_availableListView; - break; + + long infoIndex = _availableListView.getSelectedIndex(); + if (infoIndex != -1 && infoIndex < static_cast(_availablePluginList.size())) + desc = _availablePluginList.at(infoIndex).describe(); + } + break; case 1: // to be updated plugins + { showAvailable = false; showUpdate = true; showInstalled = false; - pUpiList = &_updatePluginList; - pListView = &_updateListView; - break; + + long infoIndex = _updateListView.getSelectedIndex(); + if (infoIndex != -1 && infoIndex < static_cast(_updatePluginList.size())) + desc = _updatePluginList.at(infoIndex).describe(); + } + break; case 2: // installed plugin + { showAvailable = false; showUpdate = false; showInstalled = true; - pUpiList = &_installedPluginList; - pListView = &_installedListView; - break; + + long infoIndex = _installedListView.getSelectedIndex(); + if (infoIndex != -1 && infoIndex < static_cast(_installedPluginList.size())) + desc = _installedPluginList.at(infoIndex).describe(); + } + break; default: return; } - HWND hInstallButton = ::GetDlgItem(_hSelf, IDC_PLUGINADM_INSTALL); - HWND hUpdateButton = ::GetDlgItem(_hSelf, IDC_PLUGINADM_UPDATE); - HWND hRemoveButton = ::GetDlgItem(_hSelf, IDC_PLUGINADM_REMOVE); - - ::ShowWindow(hInstallButton, showAvailable ? SW_SHOW : SW_HIDE); - ::EnableWindow(hInstallButton, showAvailable); - - ::ShowWindow(hUpdateButton, showUpdate ? SW_SHOW : SW_HIDE); - ::EnableWindow(hUpdateButton, showUpdate); - - ::ShowWindow(hRemoveButton, showInstalled ? SW_SHOW : SW_HIDE); - ::EnableWindow(hRemoveButton, showInstalled); - _availableListView.display(showAvailable); _updateListView.display(showUpdate); _installedListView.display(showInstalled); - generic_string desc; - long infoIndex = pListView->getSelectedIndex(); - if (infoIndex != -1) - desc = pUpiList->at(infoIndex).describe(); - ::SetDlgItemText(_hSelf, IDC_PLUGINADM_EDIT, desc.c_str()); + + HWND hInstallButton = ::GetDlgItem(_hSelf, IDC_PLUGINADM_INSTALL); + HWND hUpdateButton = ::GetDlgItem(_hSelf, IDC_PLUGINADM_UPDATE); + HWND hRemoveButton = ::GetDlgItem(_hSelf, IDC_PLUGINADM_REMOVE); + + ::ShowWindow(hInstallButton, showAvailable ? SW_SHOW : SW_HIDE); + if (showAvailable) + { + vector checkedArray = _availableListView.getCheckedIndexes(); + showAvailable = checkedArray.size() > 0; + } + ::EnableWindow(hInstallButton, showAvailable); + + ::ShowWindow(hUpdateButton, showUpdate ? SW_SHOW : SW_HIDE); + if (showUpdate) + { + vector checkedArray = _updateListView.getCheckedIndexes(); + showUpdate = checkedArray.size() > 0; + } + ::EnableWindow(hUpdateButton, showUpdate); + + ::ShowWindow(hRemoveButton, showInstalled ? SW_SHOW : SW_HIDE); + if (showInstalled) + { + vector checkedArray = _installedListView.getCheckedIndexes(); + showInstalled = checkedArray.size() > 0; + } + ::EnableWindow(hRemoveButton, showInstalled); } INT_PTR CALLBACK PluginsAdminDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) @@ -583,8 +690,11 @@ INT_PTR CALLBACK PluginsAdminDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA return true; case IDC_PLUGINADM_REMOVE: + { removePlugins(); + return true; + } default : @@ -605,16 +715,54 @@ INT_PTR CALLBACK PluginsAdminDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA switchDialog(indexClicked); } } - else if (pnmh->hwndFrom == _availableListView.getHSelf() && pnmh->code == LVN_ITEMCHANGED) + else if (pnmh->hwndFrom == _availableListView.getHSelf() || + pnmh->hwndFrom == _updateListView.getHSelf() || + pnmh->hwndFrom == _installedListView.getHSelf()) { - LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam; - if (pnmv->uChanged & LVIF_STATE) + ListView* pListView; + vector* pPluginInfos; + int buttonID; + + if (pnmh->hwndFrom == _availableListView.getHSelf()) { - if (pnmv->uNewState & LVIS_SELECTED) + pListView = &_availableListView; + pPluginInfos = &_availablePluginList; + buttonID = IDC_PLUGINADM_INSTALL; + } + else if (pnmh->hwndFrom == _updateListView.getHSelf()) + { + pListView = &_updateListView; + pPluginInfos = &_updatePluginList; + buttonID = IDC_PLUGINADM_UPDATE; + } + else // pnmh->hwndFrom == _installedListView.getHSelf() + { + pListView = &_installedListView; + pPluginInfos = &_installedPluginList; + buttonID = IDC_PLUGINADM_REMOVE; + } + + LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam; + + if (pnmh->code == LVN_ITEMCHANGED) + { + if (pnmv->uChanged & LVIF_STATE) { - size_t infoIndex = _availableListView.getLParamFromIndex(pnmv->iItem); - generic_string desc = _availablePluginList[infoIndex].describe(); - ::SetDlgItemText(_hSelf, IDC_PLUGINADM_EDIT, desc.c_str()); + if ((pnmv->uNewState & LVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(2) || // checked + (pnmv->uNewState & LVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(1)) // unchecked + { + HWND hButton = ::GetDlgItem(_hSelf, buttonID); + vector checkedArray = pListView->getCheckedIndexes(); + bool showButton = checkedArray.size() > 0; + + ::EnableWindow(hButton, showButton); + } + else if (pnmv->uNewState & LVIS_SELECTED) + { + size_t infoIndex = pListView->getLParamFromIndex(pnmv->iItem); + generic_string desc = pPluginInfos->at(infoIndex).describe(); + ::SetDlgItemText(_hSelf, IDC_PLUGINADM_EDIT, desc.c_str()); + } } } } diff --git a/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.h b/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.h index 2048424cc..6b5abdbc2 100644 --- a/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.h +++ b/PowerEditor/src/WinControls/PluginsAdmin/pluginsAdmin.h @@ -36,21 +36,6 @@ class PluginsManager; -struct PluginUpdateInfo -{ - generic_string name; - generic_string version; - generic_string homepage; - generic_string sourceUrl; - generic_string description; - generic_string author; - generic_string md5; - generic_string alias; - generic_string repository; - - generic_string describe(); -}; - struct Version { unsigned long _major = 0; @@ -61,15 +46,24 @@ struct Version generic_string toString(); }; -struct LoadedPluginInfo +struct PluginUpdateInfo { generic_string _fullFilePath; generic_string _id; - generic_string _name; // found from id/hash (or product name - retrieved from binary) or file name + generic_string _name; Version _version; + generic_string _homepage; + generic_string _sourceUrl; + generic_string _description; + generic_string _author; + generic_string _md5; + generic_string _alias; + generic_string _repository; - LoadedPluginInfo(const generic_string & fullFilePath, const generic_string & filename); + generic_string describe(); + PluginUpdateInfo() {}; + PluginUpdateInfo(const generic_string& fullFilePath, const generic_string& fileName); }; struct NppCurrentStatus @@ -122,6 +116,8 @@ public : bool updateListAndLoadFromJson(); // call GitUup for the 1st time void updateAvailableListView(); + void updateInstalledListView(); + void updateUpdateListView(); void setPluginsManager(PluginsManager *pluginsManager) { _pPluginsManager = pluginsManager; }; void setAdminMode(bool isAdm) { _nppCurrentStatus._isAdminMode = isAdm; }; @@ -139,14 +135,11 @@ private : ListView _availableListView; ListView _updateListView; ListView _installedListView; - std::vector _availablePluginList; - std::vector _updatePluginList; - std::vector _installedPluginList; + std::vector _availablePluginList; // All plugins (pluginList.json) - installed plugins + std::vector _updatePluginList; // A list returned by gitup.exe + std::vector _installedPluginList; // for each installed plugin, check its json file PluginsManager *_pPluginsManager = nullptr; - - std::vector _loadedPluginInfos; - NppCurrentStatus _nppCurrentStatus; void collectNppCurrentStatusInfos(); @@ -157,9 +150,12 @@ private : long searchInNamesFromCurrentSel(generic_string str2search, bool isNextMode) const { return searchFromCurrentSel(str2search, inNames, isNextMode); }; + long searchInDescsFromCurrentSel(generic_string str2search, bool isNextMode) const { return searchFromCurrentSel(str2search, inDescs, isNextMode); }; - bool getLoadedPluginInfos(); + + bool loadFromPluginInfos(); + bool checkUpdates(); };