diff --git a/CMakeLists.txt b/CMakeLists.txt index af83184..df6c9c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ add_definitions(-DAS_NO_THREADS) if(BUILD_TEST_PLUGIN) add_subdirectory(TestPlugin) + add_subdirectory(TestBadPlugin) add_subdirectory(TestManager) endif() @@ -302,7 +303,9 @@ set(CLASS_SRC src/class/wingangel.h src/class/wingangel.cpp src/class/winggeneric.h - src/class/winggeneric.cpp) + src/class/winggeneric.cpp + src/class/changedstringlist.h + src/class/changedstringlist.cpp) set(INTERNAL_PLG_SRC src/class/wingangelapi.h src/class/wingangelapi.cpp diff --git a/TestBadPlugin/CMakeLists.txt b/TestBadPlugin/CMakeLists.txt new file mode 100644 index 0000000..77e6938 --- /dev/null +++ b/TestBadPlugin/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.16) + +project(TestBadPlugin) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_INCLUDE_CURRENT_DIR TRUE) + +# Test mode, please configure the main program directory to facilitate debugging + +# 测试模式,启用请配置主程序目录,方便调试 + +# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +option(TEST_MODE TRUE) + +# For Qt +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) +find_package( + Qt${QT_VERSION_MAJOR} + COMPONENTS Widgets + REQUIRED) + +add_library(TestBadPlugin SHARED TestBadPlugin.json + testbadplugin.h testbadplugin.cpp) + +set_target_properties(TestBadPlugin PROPERTIES SUFFIX ".wingplg") + +if(TEST_MODE) + # If you want to be able to debug easily every time you compile, please set + # this variable. Because this test plugin is a subproject of the main + # project, use CMAKE_BINARY_DIR + + # 如果你想每次编译都能够方便调试的话,请设置这个变量。因为这个测试插件是和主项目子项目,所以用 CMAKE_BINARY_DIR + + # vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + set(WINGHEX_PATH "${CMAKE_BINARY_DIR}") + + set(WINGHEX_PLUGIN_PATH "${WINGHEX_PATH}/plugin") + add_custom_command( + TARGET TestBadPlugin + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${WINGHEX_PLUGIN_PATH} + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ ${WINGHEX_PLUGIN_PATH}) +endif() + +target_link_libraries(TestBadPlugin PRIVATE Qt${QT_VERSION_MAJOR}::Widgets + WingPlugin) diff --git a/TestBadPlugin/TestBadPlugin.json b/TestBadPlugin/TestBadPlugin.json new file mode 100644 index 0000000..e4d87e6 --- /dev/null +++ b/TestBadPlugin/TestBadPlugin.json @@ -0,0 +1,12 @@ +{ + "Id": "TestBadPlugin", + "SDK": 18, + "Version": "0.0.1", + "Vendor": "WingCloudStudio", + "Dependencies": [ + + ], + "Author": "wingsummer", + "License": "MIT", + "Url": "https://github.com/Wing-summer/WingHexExplorer2" +} diff --git a/TestBadPlugin/testbadplugin.cpp b/TestBadPlugin/testbadplugin.cpp new file mode 100644 index 0000000..8db852e --- /dev/null +++ b/TestBadPlugin/testbadplugin.cpp @@ -0,0 +1,41 @@ +/*============================================================================== +** Copyright (C) 2024-2027 WingSummer +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** ============================================================================= +*/ + +#include "testbadplugin.h" + +TestBadPlugin::TestBadPlugin() {} + +bool TestBadPlugin::init(const std::unique_ptr &set) { + Q_UNUSED(set); + msgCritical( + nullptr, QStringLiteral("TestBadPlugin"), + QStringLiteral("Hello, pals! I'm a evil MESSAGEBOX! PLEASE BAN ME!")); + return true; +} + +void TestBadPlugin::unload(std::unique_ptr &set) { Q_UNUSED(set); } + +const QString TestBadPlugin::pluginName() const { + return QStringLiteral("TestBadPlugin"); +} + +const QString TestBadPlugin::pluginComment() const { + return QStringLiteral("TestBadPlugin: popup a messagebox when startup!"); +} diff --git a/TestBadPlugin/testbadplugin.h b/TestBadPlugin/testbadplugin.h new file mode 100644 index 0000000..43ad47d --- /dev/null +++ b/TestBadPlugin/testbadplugin.h @@ -0,0 +1,46 @@ +/*============================================================================== +** Copyright (C) 2024-2027 WingSummer +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** ============================================================================= +*/ + +#ifndef TESTBADPLUGIN_H +#define TESTBADPLUGIN_H + +#include "WingPlugin/iwingplugin.h" + +class TestBadPlugin : public WingHex::IWingPlugin { + Q_OBJECT + + Q_PLUGIN_METADATA(IID "com.wingsummer.iwingplugin" FILE + "TestBadPlugin.json") + Q_INTERFACES(WingHex::IWingPlugin) +public: + TestBadPlugin(); + + // IWingPluginCoreBase interface +public: + virtual bool init(const std::unique_ptr &set) override; + virtual void unload(std::unique_ptr &set) override; + + // IWingPluginBase interface +public: + virtual const QString pluginName() const override; + virtual const QString pluginComment() const override; +}; + +#endif // TESTBADPLUGIN_H diff --git a/TestManager/TestManager.json b/TestManager/TestManager.json index 3216c64..1232aab 100644 --- a/TestManager/TestManager.json +++ b/TestManager/TestManager.json @@ -4,6 +4,6 @@ "Version": "0.0.1", "Vendor": "WingCloudStudio", "Author": "wingsummer", - "License": "AGPL-3.0", + "License": "MIT", "Url": "https://github.com/Wing-summer/WingHexExplorer2" } diff --git a/TestManager/testmanager.cpp b/TestManager/testmanager.cpp index 5e951cb..9576207 100644 --- a/TestManager/testmanager.cpp +++ b/TestManager/testmanager.cpp @@ -28,11 +28,14 @@ TestManager::~TestManager() { } bool TestManager::init(const std::unique_ptr &set) { - Q_UNUSED(set); + _banTestBadPlugin = set->value("BanTestBadPlugin").toBool(); + content->initConfig(this); return true; } -void TestManager::unload(std::unique_ptr &set) { Q_UNUSED(set); } +void TestManager::unload(std::unique_ptr &set) { + set->setValue("BanTestBadPlugin", _banTestBadPlugin); +} const QString TestManager::comment() const { return QStringLiteral("Hello world!"); @@ -55,3 +58,12 @@ bool TestManager::enterGuard(const QMetaObject *sender, const QString &sig, return true; } + +bool TestManager::onLoadingPlugin(const QString &fileName, + const WingHex::PluginInfo &info) { + Q_UNUSED(fileName); + if (info.id == QStringLiteral("TestBadPlugin")) { + return !_banTestBadPlugin; + } + return true; +} diff --git a/TestManager/testmanager.h b/TestManager/testmanager.h index 821ec7f..25e0159 100644 --- a/TestManager/testmanager.h +++ b/TestManager/testmanager.h @@ -56,6 +56,10 @@ private: _cbblk = new QCheckBox(QStringLiteral("Disable msg*"), this); _cbblk->setChecked(false); layout->addWidget(_cbblk); + + _banclk = new QCheckBox(QStringLiteral("BanTestBadPlugin"), this); + + layout->addWidget(_banclk); layout->addStretch(); } // PageBase interface @@ -74,19 +78,31 @@ private: public: virtual void restore() override { _cbblk->setChecked(false); } + void initConfig(TestManager *man) { + _banclk->setChecked(man->_banTestBadPlugin); + connect(_banclk, &QCheckBox::toggled, this, [man, this](bool b) { + man->_banTestBadPlugin = b; + Q_EMIT optionNeedRestartChanged(); + }); + } + public: bool isDisableMsg() const { return _cbblk->isChecked(); } private: - QCheckBox *_cbblk; + QCheckBox *_cbblk, *_banclk; }; public slots: virtual bool enterGuard(const QMetaObject *sender, const QString &sig, const QVariantList ¶ms) override; + virtual bool onLoadingPlugin(const QString &fileName, + const WingHex::PluginInfo &info) override; + private: TestPage *content; + bool _banTestBadPlugin = false; }; #endif // TESTMANAGER_H diff --git a/TestPlugin/TestPlugin.json b/TestPlugin/TestPlugin.json index 1ebba8d..013de5c 100644 --- a/TestPlugin/TestPlugin.json +++ b/TestPlugin/TestPlugin.json @@ -7,6 +7,6 @@ ], "Author": "wingsummer", - "License": "AGPL-3.0", + "License": "MIT", "Url": "https://github.com/Wing-summer/WingHexExplorer2" } diff --git a/WingPlugin b/WingPlugin index ab94fca..420ad38 160000 --- a/WingPlugin +++ b/WingPlugin @@ -1 +1 @@ -Subproject commit ab94fca27d23da495fa743dd15ccb01ca6a431fc +Subproject commit 420ad38ba767c180c7edc726aa8641a631e24559 diff --git a/images/devextdis.png b/images/devextdis.png new file mode 100644 index 0000000..ea2d154 Binary files /dev/null and b/images/devextdis.png differ diff --git a/images/plugindis.png b/images/plugindis.png new file mode 100644 index 0000000..aa320b7 Binary files /dev/null and b/images/plugindis.png differ diff --git a/lang/zh_CN/winghex_zh_CN.ts b/lang/zh_CN/winghex_zh_CN.ts index 558e3c7..fd64ebb 100644 --- a/lang/zh_CN/winghex_zh_CN.ts +++ b/lang/zh_CN/winghex_zh_CN.ts @@ -2298,6 +2298,7 @@ + PluginInfo 插件信息 @@ -2307,97 +2308,129 @@ 插件: - + + + Save + 保存 + + + + DevExtInfo 设备插件信息 - + DevExt: 设备插件: - + APIMonitor - + Plugin 插件 - - - + + + ID ID - - + + Name 名称 - - - + + + License 协议 - - - + + + Author 作者 - - - + + + Vendor 厂家 - - - + + + Version 版本 - + NoMonitorPlugin - - - + + SelectAll + 全选 + + + + SelectEnable + + + + + SelectDisable + + + + + SelectClear + + + + + DiscardChanges + + + + + + Comment 说明 - - - + + + URL 网址 - + pluginDependencies: 插件依赖 - + PUID: 插件唯一标志符: - + Version: 版本: @@ -2405,127 +2438,127 @@ PluginSystem - + LoadingPlugin 加载插件中: - - + + InvalidPluginBrokenInfo 加载插件失败:损坏的插件数据 - - + + PluginBlockByManager - + AppClosingCanceled: 程序关闭被取消: - + - PluginID: - 插件 ID: - + FoundDrvPluginCount 总计发现设备拓展插件数目: - + PluginManagerNeedSingleton - - + + ErrLoadPluginSDKVersion 插件加载失败:非法插件 SDK 版本! - + ErrLoadPluginNoName 插件加载失败:非法插件名称! - - + + ErrLoadInitPlugin 插件加载失败:初始化插件失败! - + PluginName : 插件名: - + PluginAuthor : 插件作者: - + PluginWidgetRegister 注册插件对象中…… - + ExtPluginAuthor : 设备拓展插件作者: - + ExtPluginWidgetRegister 设备拓展注册插件对象中…… - + ErrLoadInitExtPlugin 设备拓展插件加载失败:初始化插件失败! - + ChooseFile 选择文件 - - + + Error 错误 - + FileNotExist 文件不存在! - + FilePermission 因文件权限无法继续! - + EmptyNameDockWidget: 空的贴边组件名: - + InvalidNameDockWidget: 无效贴边组件名: - + InvalidNullDockWidget: 无效空贴边组件: - + Not allowed operation in non-UI thread 该操作在非 UI 线程非法 @@ -2535,48 +2568,48 @@ - + UnsafePluginDir 不安全的插件目录,请将插件目录设置为仅管理员账户可写 - - + + InvalidPluginID 加载插件失败:非法插件标识符 - + InvalidDupPlugin 加载插件失败:重复的插件标识符 - + FoundPluginCount 总计发现插件数目: - + PluginLoadingFailedSummary 有依赖插件加载失败总结 - + - Dependencies: - 依赖: - + PUID: 插件唯一标志符: - + Version: 版本: - + PluginLoadingFinished 加载插件完毕! @@ -4539,22 +4572,22 @@ ScriptManager - + RunScript 执行脚本 - + CanNotRunUsrScriptForPolicy 由于限制策略,无法在 Root 模式下运行用户脚本! - + ScriptRunning 脚本运行中 - + ScriptRunningRequestLastStop? 脚本仍在运行中,是否终止上一个脚本再执行? @@ -4562,42 +4595,67 @@ ScriptSettingDialog - + + SelectShow + + + + + SelectHide + + + + + SelectAll + 全选 + + + + SelectClear + + + + + DiscardChanges + + + + Script 脚本 - + RawName: 源名称: - + Name: 名称: - + Author: 作者: - + License: 协议: - + ContextMenu: 是否为右键菜单: - + HomePage: 主页: - + Comment: 说明: @@ -4627,9 +4685,9 @@ 仓库 - - Refresh - 刷新 + + Save + 保存 @@ -5234,43 +5292,45 @@ - - + + Restore - + + UnsavedChanges - + + SaveChangesDiscardLeave? - + Restore current - + RestoreCurPageSets? - + Restore all - + RestoreAllSets? - + TakeEffectRestart 该选项重启软件生效 @@ -5278,7 +5338,7 @@ SettingManager - + ConfigUnableSave 程序将无法保存配置,请检查配置文件权限。 diff --git a/lang/zh_TW/winghex_zh_TW.ts b/lang/zh_TW/winghex_zh_TW.ts index 947dec0..0ca10a2 100644 --- a/lang/zh_TW/winghex_zh_TW.ts +++ b/lang/zh_TW/winghex_zh_TW.ts @@ -2298,6 +2298,7 @@ + PluginInfo 插件資訊 @@ -2307,97 +2308,129 @@ 插件: - + + + Save + 保存 + + + + DevExtInfo 設備插件資訊 - + DevExt: 設備插件: - + APIMonitor - + Plugin 插件 - - - + + + ID ID - - + + Name 名稱 - - - + + + License 協議 - - - + + + Author 作者 - - - + + + Vendor 廠家 - - - + + + Version 版本 - + NoMonitorPlugin - - - + + SelectAll + 全選 + + + + SelectEnable + + + + + SelectDisable + + + + + SelectClear + + + + + DiscardChanges + + + + + + Comment 說明 - - - + + + URL 網址 - + pluginDependencies: 插件依賴 - + PUID: 插件唯一標誌符: - + Version: 版本: @@ -2405,127 +2438,127 @@ PluginSystem - + LoadingPlugin 加載插件中: - - + + InvalidPluginBrokenInfo 加載插件失敗:損壞的插件數據 - - + + PluginBlockByManager - + AppClosingCanceled: 程式關閉被取消: - + - PluginID: - 插件 ID: - + FoundDrvPluginCount 總計發現設備拓展插件數目: - + PluginManagerNeedSingleton - - + + ErrLoadPluginSDKVersion 插件加載失敗:非法插件 SDK 版本! - + ErrLoadPluginNoName 插件加載失敗:非法插件名稱! - - + + ErrLoadInitPlugin 插件加載失敗:初始化插件失敗! - + PluginName : 插件名: - + PluginAuthor : 插件作者: - + PluginWidgetRegister 註冊插件對象中…… - + ExtPluginAuthor : 設備拓展插件作者: - + ExtPluginWidgetRegister 設備拓展註冊插件對象中…… - + ErrLoadInitExtPlugin 設備拓展插件加載失敗:初始化插件失敗! - + ChooseFile 選擇檔 - - + + Error 錯誤 - + FileNotExist 檔不存在! - + FilePermission 因檔許可權無法繼續! - + EmptyNameDockWidget: 空的貼邊組件名: - + InvalidNameDockWidget: 無效貼邊組件名: - + InvalidNullDockWidget: 無效空貼邊組件: - + Not allowed operation in non-UI thread 該操作在非 UI 線程非法 @@ -2535,48 +2568,48 @@ - + UnsafePluginDir 不安全的插件目錄,請將插件目錄設置為僅管理員帳戶可寫 - - + + InvalidPluginID 加載插件失敗:非法插件識別字 - + InvalidDupPlugin 加載插件失敗:重複的插件識別字 - + FoundPluginCount 總計發現插件數目: - + PluginLoadingFailedSummary 有依賴插件加載失敗總結 - + - Dependencies: - 依賴: - + PUID: 插件唯一標誌符: - + Version: 版本: - + PluginLoadingFinished 加載插件完畢! @@ -4539,22 +4572,22 @@ ScriptManager - + RunScript 執行腳本 - + CanNotRunUsrScriptForPolicy 由於限制策略,無法在 Root 模式下運行用戶腳本! - + ScriptRunning 腳本運行中 - + ScriptRunningRequestLastStop? 腳本仍在運行中,是否終止上一個腳本再執行? @@ -4562,42 +4595,67 @@ ScriptSettingDialog - + + SelectShow + + + + + SelectHide + + + + + SelectAll + 全選 + + + + SelectClear + + + + + DiscardChanges + + + + Script 腳本 - + RawName: 源名稱: - + Name: 名稱: - + Author: 作者: - + License: 協議: - + ContextMenu: 是否為右鍵菜單: - + HomePage: 主頁: - + Comment: 說明: @@ -4627,9 +4685,9 @@ 倉庫 - - Refresh - 刷新 + + Save + 保存 @@ -5234,43 +5292,45 @@ - - + + Restore - + + UnsavedChanges - + + SaveChangesDiscardLeave? - + Restore current - + RestoreCurPageSets? - + Restore all - + RestoreAllSets? - + TakeEffectRestart 該選項重啟軟體生效 @@ -5278,7 +5338,7 @@ SettingManager - + ConfigUnableSave 程式將無法保存配置,請檢查配置檔許可權。 diff --git a/resources.qrc b/resources.qrc index 5d8ded0..0884e12 100644 --- a/resources.qrc +++ b/resources.qrc @@ -30,6 +30,7 @@ images/defines.png images/del.png images/devext.png + images/devextdis.png images/edit.png images/encoding.png images/export.png @@ -70,6 +71,7 @@ images/paste.png images/pastehex.png images/plugin.png + images/plugindis.png images/pro.png images/qt.png images/qtloginspect.png diff --git a/src/class/changedstringlist.cpp b/src/class/changedstringlist.cpp new file mode 100644 index 0000000..e285d13 --- /dev/null +++ b/src/class/changedstringlist.cpp @@ -0,0 +1,47 @@ +#include "changedstringlist.h" + +bool ChangedStringList::containChanges() const { return !mods.isEmpty(); } + +bool ChangedStringList::containChanges(const QString &item) const { + return contents.contains(item); +} + +void ChangedStringList::clear() { + contents.clear(); + mods.clear(); +} + +void ChangedStringList::pushAddItem(const QString &item) { + if (mods.contains(item)) { + auto v = mods.value(item); + if (v) { + return; + } else { + mods.remove(item); + } + } else { + mods.insert(item, true); + } + contents.append(item); +} + +void ChangedStringList::pushRemoveItem(const QString &item) { + if (mods.contains(item)) { + auto v = mods.value(item); + if (v) { + mods.remove(item); + } else { + return; + } + } else { + mods.insert(item, false); + } + contents.removeOne(item); +} + +QStringList ChangedStringList::getContents() const { return contents; } + +void ChangedStringList::setContents(const QStringList &newContents) { + contents = newContents; + mods.clear(); +} diff --git a/src/class/changedstringlist.h b/src/class/changedstringlist.h new file mode 100644 index 0000000..fdbfb8b --- /dev/null +++ b/src/class/changedstringlist.h @@ -0,0 +1,25 @@ +#ifndef CHANGEDSTRINGLIST_H +#define CHANGEDSTRINGLIST_H + +#include +#include +#include + +class ChangedStringList { +public: + bool containChanges() const; + bool containChanges(const QString &item) const; + void clear(); + + void pushAddItem(const QString &item); + void pushRemoveItem(const QString &item); + + QStringList getContents() const; + void setContents(const QStringList &newContents); + +private: + QStringList contents; + QHash mods; +}; + +#endif // CHANGEDSTRINGLIST_H diff --git a/src/class/pluginsystem.cpp b/src/class/pluginsystem.cpp index eabe6c9..2e7be5d 100644 --- a/src/class/pluginsystem.cpp +++ b/src/class/pluginsystem.cpp @@ -2631,6 +2631,16 @@ bool PluginSystem::checkErrAllAllowAndReport(const QObject *sender, return false; } +QMap> +PluginSystem::blockedDevPlugins() const { + return _blkdevs; +} + +QMap> +PluginSystem::blockedPlugins() const { + return _blkplgs; +} + void PluginSystem::doneRegisterScriptObj() { Q_ASSERT(_angelplg); // ok, then, we will register all script objects @@ -3254,15 +3264,33 @@ std::optional PluginSystem::loadPlugin(const QFileInfo &fileinfo, } auto m = meta.value(); + if (_manager) { if (!_manager->onLoadingPlugin(fileName, m)) { + if constexpr (std::is_same_v) { + _blkplgs[BlockReason::BlockedByManager].append(m); + } else if constexpr (std::is_same_v) { + _blkdevs[BlockReason::BlockedByManager].append(m); + } Logger::critical(QStringLiteral("{ ") + m.id + - QStringLiteral("} ") + + QStringLiteral(" } ") + tr("PluginBlockByManager")); return std::nullopt; } } + if constexpr (std::is_same_v) { + if (!_enabledExtIDs.contains(m.id)) { + _blkplgs[BlockReason::Disabled].append(m); + return std::nullopt; + } + } else if constexpr (std::is_same_v) { + if (!_enabledDevIDs.contains(m.id)) { + _blkdevs[BlockReason::Disabled].append(m); + return std::nullopt; + } + } + auto p = qobject_cast(loader.instance()); if (Q_UNLIKELY(p == nullptr)) { Logger::critical(loader.errorString()); @@ -4270,6 +4298,9 @@ void PluginSystem::loadAllPlugin() { try2LoadManagerPlugin(); auto &set = SettingManager::instance(); + _enabledExtIDs = set.enabledExtPlugins(); + _enabledDevIDs = set.enabledDevPlugins(); + // manager plugin can not block WingAngelAPI, only settings if (set.scriptEnabled()) { _angelplg = new WingAngelAPI; @@ -4307,15 +4338,21 @@ void PluginSystem::loadAllPlugin() { // internal plugin has no filename if (_manager == nullptr || (_manager && _manager->onLoadingPlugin({}, meta))) { - auto cstructplg = new WingCStruct; - QDir setd(Utilities::getAppDataPath()); - auto plgset = QStringLiteral("plgset"); - setd.mkdir(plgset); - retranslateMetadata(cstructplg, meta); - loadPlugin(cstructplg, meta, setd); + if (_enabledExtIDs.contains(meta.id)) { + auto cstructplg = new WingCStruct; + QDir setd(Utilities::getAppDataPath()); + auto plgset = QStringLiteral("plgset"); + setd.mkdir(plgset); + retranslateMetadata(cstructplg, meta); + loadPlugin(cstructplg, meta, setd); + } else { + _blkplgs[BlockReason::Disabled].append(meta); + } } else { + _blkplgs[BlockReason::BlockedByManager].append(meta); Logger::critical(QStringLiteral("{ ") + meta.id + - QStringLiteral("} ") + tr("PluginBlockByManager")); + QStringLiteral(" } ") + + tr("PluginBlockByManager")); } } diff --git a/src/class/pluginsystem.h b/src/class/pluginsystem.h index 165ca38..9d84a47 100644 --- a/src/class/pluginsystem.h +++ b/src/class/pluginsystem.h @@ -111,7 +111,8 @@ public: DupID, LackDependencies }; - Q_ENUM(PluginStatus) + + enum class BlockReason { Disabled, BlockedByManager }; private: struct PluginFileContext { @@ -260,6 +261,10 @@ public: const std::optional &monitorManagerInfo() const; + QMap> blockedPlugins() const; + + QMap> blockedDevPlugins() const; + private: template T readBasicTypeContent(IWingPlugin *plg, qsizetype offset) { @@ -738,6 +743,11 @@ private: QList _loadeddevs; + QStringList _enabledExtIDs; + QStringList _enabledDevIDs; + QMap> _blkplgs; + QMap> _blkdevs; + QMap> _evplgs; QHash m_plgviewMap; diff --git a/src/class/scriptmanager.cpp b/src/class/scriptmanager.cpp index 348c47a..dfb5ab5 100644 --- a/src/class/scriptmanager.cpp +++ b/src/class/scriptmanager.cpp @@ -48,7 +48,8 @@ ScriptManager::ScriptManager() { m_usrScriptsPath = Utilities::getAppDataPath() + QDir::separator() + QStringLiteral("scripts"); - refresh(); + refreshSysScriptsDbCats(); + refreshUsrScriptsDbCats(); } ScriptManager::~ScriptManager() {} @@ -120,11 +121,6 @@ QStringList ScriptManager::getSysScriptFileNames(const QString &cat) const { return getScriptFileNames(scriptDir); } -void ScriptManager::refresh() { - refreshSysScriptsDbCats(); - refreshUsrScriptsDbCats(); -} - void ScriptManager::refreshUsrScriptsDbCats() { m_usrScriptsDbCats.clear(); diff --git a/src/class/scriptmanager.h b/src/class/scriptmanager.h index 333d97d..dc9414a 100644 --- a/src/class/scriptmanager.h +++ b/src/class/scriptmanager.h @@ -55,16 +55,16 @@ public: QStringList getSysScriptFileNames(const QString &cat) const; - void refresh(); - void refreshUsrScriptsDbCats(); - void refreshSysScriptsDbCats(); - ScriptDirMeta usrDirMeta(const QString &cat) const; ScriptDirMeta sysDirMeta(const QString &cat) const; static QList buildUpScriptRunnerContext(RibbonButtonGroup *group, QWidget *parent); +private: + void refreshUsrScriptsDbCats(); + void refreshSysScriptsDbCats(); + private: static QToolButton *addPannelAction(RibbonButtonGroup *pannel, const QString &iconName, diff --git a/src/class/settingmanager.cpp b/src/class/settingmanager.cpp index 24649b2..fdff600 100644 --- a/src/class/settingmanager.cpp +++ b/src/class/settingmanager.cpp @@ -21,6 +21,8 @@ #include "class/skinmanager.h" #include "settings/settings.h" +#include "utilities.h" + #include #include #include @@ -37,6 +39,8 @@ Q_GLOBAL_STATIC_WITH_ARGS(QString, APP_LANGUAGE, ("app.lang")) Q_GLOBAL_STATIC_WITH_ARGS(QString, PLUGIN_ENABLE, ("plugin.enableplugin")) Q_GLOBAL_STATIC_WITH_ARGS(QString, PLUGIN_ENABLE_ROOT, ("plugin.rootenableplugin")) +Q_GLOBAL_STATIC_WITH_ARGS(QString, PLUGIN_ENABLEDPLUGINS_EXT, ("plugin.ext")) +Q_GLOBAL_STATIC_WITH_ARGS(QString, PLUGIN_ENABLEDPLUGINS_DEV, ("plugin.dev")) Q_GLOBAL_STATIC_WITH_ARGS(QString, EDITOR_FONTSIZE, ("editor.fontsize")) Q_GLOBAL_STATIC_WITH_ARGS(QString, EDITOR_SHOW_ADDR, ("editor.showaddr")) @@ -110,6 +114,21 @@ void SettingManager::load() { READ_CONFIG_BOOL(m_enablePlugin, PLUGIN_ENABLE, true); READ_CONFIG_BOOL(m_enablePlgInRoot, PLUGIN_ENABLE_ROOT, false); + + { + auto data = READ_CONFIG(PLUGIN_ENABLEDPLUGINS_EXT, {}); + if (data.isValid()) { + auto rawRules = data.toByteArray(); + m_enabledExtPlugins = readPluginRule(rawRules); + } + + data = READ_CONFIG(PLUGIN_ENABLEDPLUGINS_DEV, {}); + if (data.isValid()) { + auto rawRules = data.toByteArray(); + m_enabledDevPlugins = readPluginRule(rawRules); + } + } + READ_CONFIG_INT_POSITIVE(m_editorfontSize, EDITOR_FONTSIZE, defaultFontSize); m_editorfontSize = qBound(5, m_editorfontSize, 25); @@ -187,6 +206,34 @@ QVariantList SettingManager::getVarList( return varlist; } +QStringList SettingManager::enabledDevPlugins() const { + return m_enabledDevPlugins; +} + +void SettingManager::setEnabledDevPlugins( + const QStringList &newEnabledDevPlugins) { + if (m_enabledDevPlugins != newEnabledDevPlugins) { + HANDLE_CONFIG; + WRITE_CONFIG(PLUGIN_ENABLEDPLUGINS_DEV, + savePluginRule(newEnabledDevPlugins)); + m_enabledDevPlugins = newEnabledDevPlugins; + } +} + +QStringList SettingManager::enabledExtPlugins() const { + return m_enabledExtPlugins; +} + +void SettingManager::setEnabledExtPlugins( + const QStringList &newEnabledPlugins) { + if (m_enabledExtPlugins != newEnabledPlugins) { + HANDLE_CONFIG; + WRITE_CONFIG(PLUGIN_ENABLEDPLUGINS_EXT, + savePluginRule(newEnabledPlugins)); + m_enabledExtPlugins = newEnabledPlugins; + } +} + int SettingManager::scriptTimeout() const { return m_scriptTimeout; } void SettingManager::setScriptTimeout(int newScriptTimeout) { @@ -216,6 +263,27 @@ void SettingManager::checkWriteableAndWarn() { } } +QStringList SettingManager::readPluginRule(const QByteArray &data) { + if (!data.isEmpty()) { + auto up = qUncompress(data); + if (!up.isEmpty()) { + auto rules = QString::fromUtf8(up); + auto rlist = rules.split('|', Qt::SkipEmptyParts); + rlist.removeIf([](const QString &str) { + return !Utilities::isValidIdentifier(str); + }); + return rlist; + } + } + return {}; +} + +QByteArray SettingManager::savePluginRule(const QStringList &rules) { + auto plgs = rules.join('|'); + auto data = qCompress(plgs.toUtf8()); + return data; +} + bool SettingManager::checkUpdate() const { return m_checkUpdate; } void SettingManager::setCheckUpdate(bool newCheckUpdate) { diff --git a/src/class/settingmanager.h b/src/class/settingmanager.h index 03fb704..4bceff2 100644 --- a/src/class/settingmanager.h +++ b/src/class/settingmanager.h @@ -81,6 +81,9 @@ public: qsizetype logCount() const; int scriptTimeout() const; + QStringList enabledExtPlugins() const; + QStringList enabledDevPlugins() const; + public slots: void setThemeID(int newThemeID); void setDockLayout(const QByteArray &newDockLayout); @@ -114,9 +117,15 @@ public slots: void setLogCount(qsizetype newLogCount); void setScriptTimeout(int newScriptTimeout); + void setEnabledExtPlugins(const QStringList &newEnabledPlugins); + void setEnabledDevPlugins(const QStringList &newEnabledDevPlugins); + public: void checkWriteableAndWarn(); + QStringList readPluginRule(const QByteArray &data); + QByteArray savePluginRule(const QStringList &rules); + signals: void sigEditorfontSizeChanged(int v); void sigDecodeStrlimitChanged(int v); @@ -162,6 +171,8 @@ private: QStringList m_usrHideCats; QStringList m_sysHideCats; + QStringList m_enabledExtPlugins; + QStringList m_enabledDevPlugins; QString m_lastUsedPath; bool m_dontUseSplash = false; diff --git a/src/dialog/settingdialog.cpp b/src/dialog/settingdialog.cpp index dcb378f..350a431 100644 --- a/src/dialog/settingdialog.cpp +++ b/src/dialog/settingdialog.cpp @@ -23,8 +23,10 @@ #include "utilities.h" #include +#include #include #include +#include SettingDialog::SettingDialog(QWidget *parent) : QWidget(parent), ui(new Ui::SettingDialog) { @@ -34,7 +36,7 @@ SettingDialog::SettingDialog(QWidget *parent) [this](QListWidgetItem *current, QListWidgetItem *previous) { if (previous) { auto page = m_pages.at( - ui->listWidget->indexFromItem(current).row()); + ui->listWidget->indexFromItem(previous).row()); if (page->containUnsavedChanges()) { auto ret = WingMessageBox::question( this, tr("UnsavedChanges"), @@ -42,7 +44,12 @@ SettingDialog::SettingDialog(QWidget *parent) if (ret == QMessageBox::Yes) { page->discard(); } else { - ui->listWidget->setCurrentItem(previous); + QTimer::singleShot(100, this, [this, previous]() { + ui->listWidget->blockSignals(true); + ui->listWidget->setCurrentItem(previous); + ui->listWidget->blockSignals(false); + }); + page->highlightUnsavedChange(); return; } } @@ -60,6 +67,7 @@ SettingDialog::SettingDialog(QWidget *parent) }); _dialog = new FramelessDialogBase(parent); + _dialog->installEventFilter(this); _dialog->buildUpContent(this); _dialog->setWindowTitle(this->windowTitle()); @@ -127,9 +135,11 @@ void SettingDialog::build() { } void SettingDialog::showConfig(int index) { + ui->listWidget->blockSignals(true); if (index >= 0 && index < m_pages.size()) { ui->listWidget->setCurrentRow(index); } + ui->listWidget->blockSignals(false); Utilities::moveToCenter(this); _dialog->exec(); } @@ -149,6 +159,25 @@ void SettingDialog::showConfig(const QString &id) { _dialog->exec(); } +bool SettingDialog::eventFilter(QObject *, QEvent *event) { + if (event->type() == QEvent::Close) { + auto e = static_cast(event); + auto page = m_pages.at(ui->listWidget->currentRow()); + if (page->containUnsavedChanges()) { + auto ret = WingMessageBox::question(this, tr("UnsavedChanges"), + tr("SaveChangesDiscardLeave?")); + if (ret == QMessageBox::Yes) { + page->discard(); + } else { + page->highlightUnsavedChange(); + event->ignore(); + return true; + } + } + } + return false; +} + void SettingDialog::toastTakeEffectReboot() { auto page = qobject_cast(sender()); if (page) { diff --git a/src/dialog/settingdialog.h b/src/dialog/settingdialog.h index 86d7510..b7b89d1 100644 --- a/src/dialog/settingdialog.h +++ b/src/dialog/settingdialog.h @@ -39,6 +39,10 @@ public: void showConfig(int index = -1); void showConfig(const QString &id); + // QObject interface +public: + virtual bool eventFilter(QObject *watched, QEvent *event) override; + public slots: void toastTakeEffectReboot(); diff --git a/src/settings/pluginsettingdialog.cpp b/src/settings/pluginsettingdialog.cpp index 5710d8e..412c5a2 100644 --- a/src/settings/pluginsettingdialog.cpp +++ b/src/settings/pluginsettingdialog.cpp @@ -16,11 +16,18 @@ */ #include "pluginsettingdialog.h" +#include "class/eventfilter.h" #include "class/pluginsystem.h" #include "class/settingmanager.h" #include "ui_pluginsettingdialog.h" #include "utilities.h" +enum PLUGIN_INFO { + PLUIGN_META = Qt::UserRole, + PLUIGN_NAME, + PLUIGN_COMMENT, +}; + PluginSettingDialog::PluginSettingDialog(QWidget *parent) : WingHex::SettingPage(parent), ui(new Ui::PluginSettingDialog) { ui->setupUi(this); @@ -50,18 +57,98 @@ PluginSettingDialog::PluginSettingDialog(QWidget *parent) for (auto &p : plgsys.plugins()) { auto pco = p->pluginIcon(); - ui->plglist->addItem( - new QListWidgetItem(pco.isNull() ? pico : pco, p->pluginName())); + auto lwi = + new QListWidgetItem(pco.isNull() ? pico : pco, p->pluginName()); + auto info = plgsys.getPluginInfo(p); + auto flags = lwi->flags(); + if (Q_LIKELY(p != plgsys.angelApi())) { + flags.setFlag(Qt::ItemIsUserCheckable); + lwi->setFlags(flags); + lwi->setCheckState(Qt::Checked); + } else { + flags.setFlag(Qt::ItemIsUserCheckable, false); + lwi->setFlags(flags); + } + + lwi->setData(PLUIGN_META, QVariant::fromValue(info)); + lwi->setData(PLUIGN_NAME, p->pluginName()); + lwi->setData(PLUIGN_COMMENT, p->pluginComment()); + ui->plglist->addItem(lwi); } + + pico = ICONRES("plugindis"); + auto blkplgs = plgsys.blockedPlugins(); + auto dblkplgs = blkplgs[PluginSystem::BlockReason::Disabled]; + for (auto &meta : dblkplgs) { + auto lwi = new QListWidgetItem(pico, meta.id); + auto flags = lwi->flags(); + flags.setFlag(Qt::ItemIsUserCheckable); + lwi->setFlags(flags); + lwi->setData(PLUIGN_META, QVariant::fromValue(meta)); + lwi->setCheckState(Qt::Unchecked); + ui->plglist->addItem(lwi); + } + + pico = getGrayIcon(NAMEICONRES("plugin")); + dblkplgs = blkplgs[PluginSystem::BlockReason::BlockedByManager]; + for (auto &meta : dblkplgs) { + auto lwi = new QListWidgetItem(pico, meta.id); + auto flags = lwi->flags(); + flags.setFlag(Qt::ItemIsUserCheckable, false); + lwi->setFlags(flags); + auto font = lwi->font(); + font.setStrikeOut(true); + lwi->setFont(font); + lwi->setData(PLUIGN_META, QVariant::fromValue(meta)); + ui->plglist->addItem(lwi); + } + ui->txtc->clear(); pico = ICONRES("devext"); ui->devlist->clear(); for (auto &d : plgsys.devices()) { auto pco = d->pluginIcon(); - ui->devlist->addItem( - new QListWidgetItem(pco.isNull() ? pico : pco, d->pluginName())); + auto lwi = + new QListWidgetItem(pco.isNull() ? pico : pco, d->pluginName()); + auto info = plgsys.getPluginInfo(d); + auto flags = lwi->flags(); + flags.setFlag(Qt::ItemIsUserCheckable); + lwi->setFlags(flags); + lwi->setData(PLUIGN_META, QVariant::fromValue(info)); + lwi->setData(PLUIGN_NAME, d->pluginName()); + lwi->setData(PLUIGN_COMMENT, d->pluginComment()); + lwi->setCheckState(Qt::Checked); + ui->devlist->addItem(lwi); } + + pico = ICONRES("devextdis"); + auto blkdevs = plgsys.blockedDevPlugins(); + auto dblkdevs = blkdevs[PluginSystem::BlockReason::Disabled]; + for (auto &meta : dblkdevs) { + auto lwi = new QListWidgetItem(pico, meta.id); + auto flags = lwi->flags(); + flags.setFlag(Qt::ItemIsUserCheckable); + lwi->setFlags(flags); + lwi->setData(PLUIGN_META, QVariant::fromValue(meta)); + lwi->setCheckState(Qt::Unchecked); + ui->devlist->addItem(lwi); + } + + pico = getGrayIcon(NAMEICONRES("devext")); + dblkdevs = blkdevs[PluginSystem::BlockReason::BlockedByManager]; + for (auto &meta : dblkdevs) { + auto lwi = new QListWidgetItem(pico, meta.id); + auto flags = lwi->flags(); + flags.setFlag(Qt::ItemIsUserCheckable, false); + lwi->setFlags(flags); + auto font = lwi->font(); + font.setStrikeOut(true); + lwi->setFont(font); + lwi->setData(PLUIGN_META, QVariant::fromValue(meta)); + ui->devlist->addItem(lwi); + } + ui->txtd->clear(); auto minfo = plgsys.monitorManagerInfo(); @@ -91,6 +178,154 @@ PluginSettingDialog::PluginSettingDialog(QWidget *parent) &SettingManager::setEnablePlugin); connect(ui->cbEnablePluginRoot, &QCheckBox::toggled, set, &SettingManager::setEnablePlgInRoot); + + connect(ui->plglist, &QListWidget::itemChanged, this, + [this](QListWidgetItem *item) { + if (item == nullptr) { + return; + } + auto info = item->data(PLUIGN_META).value(); + auto id = info.id; + switch (item->checkState()) { + case Qt::Unchecked: + _plgChanged.pushRemoveItem(id); + break; + case Qt::Checked: + _plgChanged.pushAddItem(id); + break; + case Qt::PartiallyChecked: + break; + } + + ui->btnplgSave->setEnabled(_plgChanged.containChanges()); + }); + connect( + ui->plglist, &QListWidget::currentItemChanged, this, + [this](QListWidgetItem *current, QListWidgetItem *) { + if (current == nullptr) { + return; + } + + auto info = current->data(PLUIGN_META).value(); + auto plgName = current->data(PLUIGN_NAME).toString(); + auto plgComment = current->data(PLUIGN_COMMENT).toString(); + + ui->txtc->clear(); + static auto sep = QStringLiteral(" : "); + ui->txtc->append(getWrappedText(tr("ID") + sep + info.id)); + ui->txtc->append(getWrappedText(tr("Name") + sep + plgName)); + ui->txtc->append( + getWrappedText(tr("License") + sep + info.license)); + ui->txtc->append(getWrappedText(tr("Author") + sep + info.author)); + ui->txtc->append(getWrappedText(tr("Vendor") + sep + info.vendor)); + ui->txtc->append( + getWrappedText(tr("Version") + sep + info.version.toString())); + ui->txtc->append(getWrappedText(tr("Comment") + sep + plgComment)); + if (!info.dependencies.isEmpty()) { + ui->txtc->append(getWrappedText(tr("pluginDependencies:"))); + for (auto &d : info.dependencies) { + ui->txtc->append( + getWrappedText(QString(4, ' ') + tr("PUID:") + d.puid)); + ui->txtc->append(getWrappedText(QString(4, ' ') + + tr("Version:") + + d.version.toString())); + } + } + ui->txtc->append(getWrappedText( + tr("URL") + sep + QStringLiteral("") + info.url + QStringLiteral(" "))); + }); + + connect(ui->devlist, &QListWidget::itemChanged, this, + [this](QListWidgetItem *item) { + if (item == nullptr) { + return; + } + auto info = item->data(PLUIGN_META).value(); + auto id = info.id; + switch (item->checkState()) { + case Qt::Unchecked: + _devChanged.pushRemoveItem(id); + break; + case Qt::Checked: + _devChanged.pushAddItem(id); + break; + case Qt::PartiallyChecked: + break; + } + + ui->btndevSave->setEnabled(_devChanged.containChanges()); + }); + connect( + ui->devlist, &QListWidget::currentItemChanged, this, + [this](QListWidgetItem *current, QListWidgetItem *) { + if (current == nullptr) { + return; + } + + auto info = current->data(PLUIGN_META).value(); + auto plgName = current->data(PLUIGN_NAME).toString(); + auto plgComment = current->data(PLUIGN_COMMENT).toString(); + + ui->txtd->clear(); + static auto sep = QStringLiteral(" : "); + ui->txtd->append(getWrappedText(tr("ID") + sep + info.id)); + ui->txtd->append(getWrappedText(tr("Name") + sep + plgName)); + ui->txtd->append( + getWrappedText(tr("License") + sep + info.license)); + ui->txtd->append(getWrappedText(tr("Author") + sep + info.author)); + ui->txtd->append(getWrappedText(tr("Vendor") + sep + info.vendor)); + ui->txtd->append( + getWrappedText(tr("Version") + sep + info.version.toString())); + ui->txtd->append(getWrappedText(tr("Comment") + sep + plgComment)); + ui->txtd->append(getWrappedText( + tr("URL") + sep + QStringLiteral("") + info.url + QStringLiteral(""))); + }); + + connect(ui->btnplgSave, &QPushButton::clicked, set, [this]() { + SettingManager::instance().setEnabledExtPlugins( + _plgChanged.getContents()); + _plgChanged.clear(); + ui->btnplgSave->setEnabled(false); + Q_EMIT optionNeedRestartChanged(); + }); + connect(ui->btndevSave, &QPushButton::clicked, set, [this]() { + SettingManager::instance().setEnabledDevPlugins( + _devChanged.getContents()); + _devChanged.clear(); + ui->btndevSave->setEnabled(false); + Q_EMIT optionNeedRestartChanged(); + }); + + auto ev = new EventFilter(QEvent::EnabledChange, this); + connect(ev, &EventFilter::eventTriggered, this, + [this](QObject *obj, QEvent *) { + if (obj == ui->btnplgSave) { + auto tabName = QCoreApplication::translate( + "PluginSettingDialog", "PluginInfo", nullptr); + if (ui->btnplgSave->isEnabled()) { + tabName.append(QStringLiteral(" *")); + } + ui->tabWidget->setTabText( + ui->tabWidget->indexOf(ui->tabPluginInfo), tabName); + } else if (obj == ui->btndevSave) { + auto tabName = QCoreApplication::translate( + "PluginSettingDialog", "DevExtInfo", nullptr); + if (ui->btndevSave->isEnabled()) { + tabName.append(QStringLiteral(" *")); + } + ui->tabWidget->setTabText( + ui->tabWidget->indexOf(ui->tabDevInfo), tabName); + } + }); + ui->btnplgSave->installEventFilter(ev); + ui->btndevSave->installEventFilter(ev); + + resetChangedList(); + + createPluginStandardMenu(ui->plglist); + createPluginStandardMenu(ui->devlist); } PluginSettingDialog::~PluginSettingDialog() { delete ui; } @@ -109,68 +344,117 @@ QString PluginSettingDialog::name() const { return tr("Plugin"); } QString PluginSettingDialog::id() const { return QStringLiteral("Plugin"); } +bool PluginSettingDialog::containUnsavedChanges() const { + return ui->btndevSave->isEnabled() || ui->btnplgSave->isEnabled(); +} + +void PluginSettingDialog::highlightUnsavedChange() { + if (ui->btnplgSave->isEnabled()) { + ui->tabWidget->setCurrentWidget(ui->tabPluginInfo); + } else if (ui->btndevSave->isEnabled()) { + ui->tabWidget->setCurrentWidget(ui->tabDevInfo); + } +} + +void PluginSettingDialog::discard() { + resetChangedList(); + resetUIChagned(); +} + void PluginSettingDialog::restore() { SettingManager::instance().reset(SettingManager::SETTING::PLUGIN); reload(); } -void PluginSettingDialog::on_devlist_currentRowChanged(int currentRow) { - if (currentRow < 0) { - return; - } - - auto &plgsys = PluginSystem::instance(); - auto plg = plgsys.device(currentRow); - - auto info = plgsys.getPluginInfo(plg); - ui->txtd->clear(); - static auto sep = QStringLiteral(" : "); - ui->txtd->append(getWrappedText(tr("ID") + sep + info.id)); - ui->txtd->append(getWrappedText(tr("Name") + sep + plg->pluginName())); - ui->txtd->append(getWrappedText(tr("License") + sep + info.license)); - ui->txtd->append(getWrappedText(tr("Author") + sep + info.author)); - ui->txtd->append(getWrappedText(tr("Vendor") + sep + info.vendor)); - ui->txtd->append( - getWrappedText(tr("Version") + sep + info.version.toString())); - ui->txtd->append( - getWrappedText(tr("Comment") + sep + plg->pluginComment())); - ui->txtd->append(getWrappedText( - tr("URL") + sep + QStringLiteral("") + info.url + QStringLiteral(""))); +void PluginSettingDialog::createPluginStandardMenu(QListWidget *widget) { + widget->setSelectionMode(QListWidget::ExtendedSelection); + widget->setContextMenuPolicy(Qt::ActionsContextMenu); + widget->addAction(tr("SelectEnable"), widget, [widget]() { + for (auto &item : widget->selectedItems()) { + auto flags = item->flags(); + if (flags.testFlag(Qt::ItemIsUserCheckable)) { + item->setCheckState(Qt::Checked); + } + } + }); + widget->addAction(tr("SelectDisable"), widget, [widget]() { + for (auto &item : widget->selectedItems()) { + auto flags = item->flags(); + if (flags.testFlag(Qt::ItemIsUserCheckable)) { + item->setCheckState(Qt::Unchecked); + } + } + }); + auto a = new QAction(widget); + a->setSeparator(true); + widget->addAction(a); + widget->addAction(tr("SelectAll"), widget, &QListWidget::selectAll); + widget->addAction(tr("SelectClear"), widget, &QListWidget::clearSelection); + a = new QAction(widget); + a->setSeparator(true); + widget->addAction(a); + widget->addAction(tr("DiscardChanges"), this, + &PluginSettingDialog::discard); } -void PluginSettingDialog::on_plglist_currentRowChanged(int currentRow) { - if (currentRow < 0) { - return; - } - - auto &plgsys = PluginSystem::instance(); - auto plg = plgsys.plugin(currentRow); - - auto info = plgsys.getPluginInfo(plg); - ui->txtc->clear(); - static auto sep = QStringLiteral(" : "); - ui->txtc->append(getWrappedText(tr("ID") + sep + info.id)); - ui->txtc->append(getWrappedText(tr("Name") + sep + plg->pluginName())); - ui->txtc->append(getWrappedText(tr("License") + sep + info.license)); - ui->txtc->append(getWrappedText(tr("Author") + sep + info.author)); - ui->txtc->append(getWrappedText(tr("Vendor") + sep + info.vendor)); - ui->txtc->append( - getWrappedText(tr("Version") + sep + info.version.toString())); - ui->txtc->append( - getWrappedText(tr("Comment") + sep + plg->pluginComment())); - if (!info.dependencies.isEmpty()) { - ui->txtc->append(getWrappedText(tr("pluginDependencies:"))); - for (auto &d : info.dependencies) { - ui->txtc->append( - getWrappedText(QString(4, ' ') + tr("PUID:") + d.puid)); - ui->txtc->append(getWrappedText(QString(4, ' ') + tr("Version:") + - d.version.toString())); +QIcon PluginSettingDialog::getGrayIcon(const QString &path) { + QImage dpico(path); + for (int y = 0; y < dpico.height(); ++y) { + auto row = reinterpret_cast(dpico.scanLine(y)); + for (int x = 0; x < dpico.width(); ++x) { + auto c = row[x]; + if (c) { + auto p = qGray(c); + row[x] = qRgba(p, p, p, qAlpha(c)); + } } } - ui->txtc->append(getWrappedText( - tr("URL") + sep + QStringLiteral("") + info.url + QStringLiteral(" "))); + return QIcon(QPixmap::fromImage(dpico)); +} + +void PluginSettingDialog::resetChangedList() { + auto &set = SettingManager::instance(); + _plgChanged.setContents(set.enabledExtPlugins()); + _devChanged.setContents(set.enabledDevPlugins()); +} + +void PluginSettingDialog::resetUIChagned() { + ui->plglist->blockSignals(true); + ui->devlist->blockSignals(true); + + for (int i = 0; i < ui->plglist->count(); ++i) { + auto item = ui->plglist->item(i); + auto flags = item->flags(); + if (flags.testFlag(Qt::ItemIsUserCheckable)) { + auto info = item->data(PLUIGN_META).value(); + auto id = info.id; + if (_plgChanged.containChanges(id)) { + item->setCheckState(Qt::Checked); + } else { + item->setCheckState(Qt::Unchecked); + } + } + } + + for (int i = 0; i < ui->devlist->count(); ++i) { + auto item = ui->devlist->item(i); + auto flags = item->flags(); + if (flags.testFlag(Qt::ItemIsUserCheckable)) { + auto info = item->data(PLUIGN_META).value(); + auto id = info.id; + if (_devChanged.containChanges(id)) { + item->setCheckState(Qt::Checked); + } else { + item->setCheckState(Qt::Unchecked); + } + } + } + + ui->plglist->blockSignals(false); + ui->devlist->blockSignals(false); + + ui->btnplgSave->setEnabled(false); + ui->btndevSave->setEnabled(false); } QString PluginSettingDialog::getWrappedText(const QString &str) { diff --git a/src/settings/pluginsettingdialog.h b/src/settings/pluginsettingdialog.h index 0517293..1df5578 100644 --- a/src/settings/pluginsettingdialog.h +++ b/src/settings/pluginsettingdialog.h @@ -19,6 +19,9 @@ #define PLUGINSETTINGDIALOG_H #include "WingPlugin/settingpage.h" +#include "class/changedstringlist.h" + +#include #include namespace Ui { @@ -35,6 +38,10 @@ public: private: Ui::PluginSettingDialog *ui; + ChangedStringList _devChanged; + ChangedStringList _plgChanged; + +private: void reload(); // SettingPage interface @@ -43,11 +50,18 @@ public: virtual QString name() const override; virtual QString id() const override; + virtual bool containUnsavedChanges() const override; + virtual void highlightUnsavedChange() override; + virtual void discard() override; virtual void restore() override; -private slots: - void on_devlist_currentRowChanged(int currentRow); - void on_plglist_currentRowChanged(int currentRow); +private: + void createPluginStandardMenu(QListWidget *widget); + + QIcon getGrayIcon(const QString &path); + + void resetChangedList(); + void resetUIChagned(); private: QString getWrappedText(const QString &str); diff --git a/src/settings/pluginsettingdialog.ui b/src/settings/pluginsettingdialog.ui index 9111f8b..817133c 100644 --- a/src/settings/pluginsettingdialog.ui +++ b/src/settings/pluginsettingdialog.ui @@ -72,7 +72,7 @@ true - + :/com.wingsummer.winghex/images/plugin.png:/com.wingsummer.winghex/images/plugin.png @@ -116,6 +116,16 @@ + + + + false + + + Save + + + @@ -127,7 +137,7 @@ - + :/com.wingsummer.winghex/images/devext.png:/com.wingsummer.winghex/images/devext.png @@ -156,6 +166,16 @@ + + + + false + + + Save + + + @@ -167,7 +187,7 @@ - + :/com.wingsummer.winghex/images/monitor.png:/com.wingsummer.winghex/images/monitor.png diff --git a/src/settings/scriptsettingdialog.cpp b/src/settings/scriptsettingdialog.cpp index f9d4b42..8c1f6b8 100644 --- a/src/settings/scriptsettingdialog.cpp +++ b/src/settings/scriptsettingdialog.cpp @@ -47,26 +47,24 @@ ScriptSettingDialog::ScriptSettingDialog(QWidget *parent) switch (item->checkState()) { case Qt::Unchecked: { if (meta.isSys) { - m_sysHideCats.append(meta.rawName); + m_sysHideCats.pushAddItem(meta.rawName); } else { - m_usrHideCats.append(meta.rawName); + m_usrHideCats.pushAddItem(meta.rawName); } } break; case Qt::Checked: { if (meta.isSys) { - m_sysHideCats.removeOne(meta.rawName); + m_sysHideCats.pushRemoveItem(meta.rawName); } else { - m_usrHideCats.removeOne(meta.rawName); + m_usrHideCats.pushRemoveItem(meta.rawName); } } break; case Qt::PartiallyChecked: break; } - } - auto &set = SettingManager::instance(); - set.setUsrHideCats(m_sysHideCats); - set.setSysHideCats(m_usrHideCats); + ui->btnSave->setEnabled(containUnsavedChanges()); + } }); auto set = &SettingManager::instance(); @@ -76,6 +74,47 @@ ScriptSettingDialog::ScriptSettingDialog(QWidget *parent) &SettingManager::setAllowUsrScriptInRoot); connect(ui->sbTimeout, &QSpinBox::valueChanged, set, &SettingManager::setScriptTimeout); + + connect(ui->btnSave, &QPushButton::clicked, this, [this]() { + auto &set = SettingManager::instance(); + set.setUsrHideCats(m_usrHideCats.getContents()); + set.setSysHideCats(m_sysHideCats.getContents()); + m_sysHideCats.clear(); + m_usrHideCats.clear(); + ui->btnSave->setEnabled(false); + Q_EMIT optionNeedRestartChanged(); + }); + + connect(ui->listWidget, &QListWidget::currentRowChanged, this, + [this](int currentRow) { + if (currentRow < 0) { + ui->textBrowser->clear(); + return; + } + auto lw = ui->listWidget->item(currentRow); + auto var = lw->data(Qt::UserRole); + if (var.isValid()) { + auto meta = var.value(); + auto info = ui->textBrowser; + info->clear(); + info->append(tr("RawName:") + meta.rawName); + info->append(tr("Name:") + meta.name); + info->append(tr("Author:") + meta.author); + info->append(tr("License:") + meta.license); + info->append(tr("ContextMenu:") + + (meta.isContextMenu + ? QStringLiteral("true") + : QStringLiteral("false"))); + info->append(tr("HomePage:") + meta.homepage); + info->append(tr("Comment:")); + auto cur = info->textCursor(); + cur.movePosition(QTextCursor::End); + info->setTextCursor(cur); + info->insertHtml(meta.comment); + } + }); + + createPluginStandardMenu(); } ScriptSettingDialog::~ScriptSettingDialog() { delete ui; } @@ -94,19 +133,26 @@ void ScriptSettingDialog::loadData() { auto &sm = ScriptManager::instance(); auto usrCats = sm.usrScriptsDbCats(); auto hidden = set.usrHideCats(); + + QStringList buffer; for (auto &cat : usrCats) { if (addCatagory(sm.usrDirMeta(cat), true, hidden.contains(cat))) { - m_usrHideCats << cat; + buffer.append(cat); } } + _usrHideCats = buffer; + m_usrHideCats.setContents(buffer); + buffer.clear(); auto sysCats = sm.sysScriptsDbCats(); hidden = set.sysHideCats(); for (auto &cat : sysCats) { if (addCatagory(sm.sysDirMeta(cat), false, hidden.contains(cat))) { - m_sysHideCats << cat; + buffer.append(cat); } } + _sysHideCats = buffer; + m_sysHideCats.setContents(buffer); } else { ui->groupBox_2->setEnabled(false); } @@ -120,7 +166,33 @@ bool ScriptSettingDialog::addCatagory(const ScriptManager::ScriptDirMeta &meta, meta.name, ui->listWidget); lw->setData(Qt::UserRole, QVariant::fromValue(meta)); lw->setCheckState(hidden ? Qt::Unchecked : Qt::Checked); - return !hidden; + return hidden; +} + +void ScriptSettingDialog::createPluginStandardMenu() { + auto widget = ui->listWidget; + widget->setSelectionMode(QListWidget::ExtendedSelection); + widget->setContextMenuPolicy(Qt::ActionsContextMenu); + widget->addAction(tr("SelectShow"), widget, [widget]() { + for (auto &item : widget->selectedItems()) { + item->setCheckState(Qt::Checked); + } + }); + widget->addAction(tr("SelectHide"), widget, [widget]() { + for (auto &item : widget->selectedItems()) { + item->setCheckState(Qt::Unchecked); + } + }); + auto a = new QAction(widget); + a->setSeparator(true); + widget->addAction(a); + widget->addAction(tr("SelectAll"), widget, &QListWidget::selectAll); + widget->addAction(tr("SelectClear"), widget, &QListWidget::clearSelection); + a = new QAction(widget); + a->setSeparator(true); + widget->addAction(a); + widget->addAction(tr("DiscardChanges"), this, + &ScriptSettingDialog::discard); } QIcon ScriptSettingDialog::categoryIcon() const { return ICONRES("script"); } @@ -129,41 +201,40 @@ QString ScriptSettingDialog::name() const { return tr("Script"); } QString ScriptSettingDialog::id() const { return QStringLiteral("Script"); } +bool ScriptSettingDialog::containUnsavedChanges() const { + return m_usrHideCats.containChanges() || m_sysHideCats.containChanges(); +} + void ScriptSettingDialog::restore() { auto &set = SettingManager::instance(); set.reset(SettingManager::SCRIPT); loadData(); } -void ScriptSettingDialog::on_btnRefresh_clicked() { - ui->listWidget->clear(); - ScriptManager::instance().refresh(); - loadData(); -} +void ScriptSettingDialog::discard() { + m_usrHideCats.setContents(_usrHideCats); + m_sysHideCats.setContents(_sysHideCats); -void ScriptSettingDialog::on_listWidget_currentRowChanged(int currentRow) { - if (currentRow < 0) { - ui->textBrowser->clear(); - return; - } - auto lw = ui->listWidget->item(currentRow); - auto var = lw->data(Qt::UserRole); - if (var.isValid()) { - auto meta = var.value(); - auto info = ui->textBrowser; - info->clear(); - info->append(tr("RawName:") + meta.rawName); - info->append(tr("Name:") + meta.name); - info->append(tr("Author:") + meta.author); - info->append(tr("License:") + meta.license); - info->append(tr("ContextMenu:") + (meta.isContextMenu - ? QStringLiteral("true") - : QStringLiteral("false"))); - info->append(tr("HomePage:") + meta.homepage); - info->append(tr("Comment:")); - auto cur = info->textCursor(); - cur.movePosition(QTextCursor::End); - info->setTextCursor(cur); - info->insertHtml(meta.comment); + ui->listWidget->blockSignals(true); + for (int i = 0; i < ui->listWidget->count(); ++i) { + auto item = ui->listWidget->item(i); + auto var = item->data(Qt::UserRole); + if (var.isValid()) { + auto meta = var.value(); + if (meta.isSys) { + if (_sysHideCats.contains(meta.rawName)) { + item->setCheckState(Qt::Unchecked); + } else { + item->setCheckState(Qt::Checked); + } + } else { + if (_usrHideCats.contains(meta.rawName)) { + item->setCheckState(Qt::Unchecked); + } else { + item->setCheckState(Qt::Checked); + } + } + } } + ui->listWidget->blockSignals(false); } diff --git a/src/settings/scriptsettingdialog.h b/src/settings/scriptsettingdialog.h index 229448c..98589dd 100644 --- a/src/settings/scriptsettingdialog.h +++ b/src/settings/scriptsettingdialog.h @@ -19,6 +19,7 @@ #define SCRIPTSETTINGDIALOG_H #include "WingPlugin/settingpage.h" +#include "class/changedstringlist.h" #include "class/scriptmanager.h" #include @@ -36,25 +37,28 @@ public: private: Ui::ScriptSettingDialog *ui; - QStringList m_usrHideCats; - QStringList m_sysHideCats; + QStringList _usrHideCats; + QStringList _sysHideCats; + + ChangedStringList m_usrHideCats; + ChangedStringList m_sysHideCats; private: void loadData(); bool addCatagory(const ScriptManager::ScriptDirMeta &meta, bool isUser, bool hidden); + void createPluginStandardMenu(); + // SettingPage interface public: virtual QIcon categoryIcon() const override; virtual QString name() const override; virtual QString id() const override; + virtual bool containUnsavedChanges() const override; virtual void restore() override; - -private slots: - void on_btnRefresh_clicked(); - void on_listWidget_currentRowChanged(int currentRow); + virtual void discard() override; }; #endif // SCRIPTSETTINGDIALOG_H diff --git a/src/settings/scriptsettingdialog.ui b/src/settings/scriptsettingdialog.ui index 87a4802..366fbe3 100644 --- a/src/settings/scriptsettingdialog.ui +++ b/src/settings/scriptsettingdialog.ui @@ -130,9 +130,12 @@ - + + + false + - Refresh + Save