From 55ebbcd126c344868d2c0ba88d7cc9fe8f86a4f1 Mon Sep 17 00:00:00 2001 From: wingsummer <1326224942@qq.com> Date: Sat, 12 Jul 2025 20:07:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AE=BE=E7=BD=AE=E9=A1=B5=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E8=B0=83=E6=95=B4=E4=BC=98=E5=8C=96=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 5 +- TestBadPlugin/CMakeLists.txt | 53 ++++ TestBadPlugin/TestBadPlugin.json | 12 + TestBadPlugin/testbadplugin.cpp | 41 +++ TestBadPlugin/testbadplugin.h | 46 ++++ TestManager/TestManager.json | 2 +- TestManager/testmanager.cpp | 16 +- TestManager/testmanager.h | 18 +- TestPlugin/TestPlugin.json | 2 +- WingPlugin | 2 +- images/devextdis.png | Bin 0 -> 5427 bytes images/plugindis.png | Bin 0 -> 8804 bytes lang/zh_CN/winghex_zh_CN.ts | 250 ++++++++++------- lang/zh_TW/winghex_zh_TW.ts | 250 ++++++++++------- resources.qrc | 2 + src/class/changedstringlist.cpp | 47 ++++ src/class/changedstringlist.h | 25 ++ src/class/pluginsystem.cpp | 53 +++- src/class/pluginsystem.h | 12 +- src/class/scriptmanager.cpp | 8 +- src/class/scriptmanager.h | 8 +- src/class/settingmanager.cpp | 68 +++++ src/class/settingmanager.h | 11 + src/dialog/settingdialog.cpp | 33 ++- src/dialog/settingdialog.h | 4 + src/settings/pluginsettingdialog.cpp | 398 +++++++++++++++++++++++---- src/settings/pluginsettingdialog.h | 20 +- src/settings/pluginsettingdialog.ui | 26 +- src/settings/scriptsettingdialog.cpp | 151 +++++++--- src/settings/scriptsettingdialog.h | 16 +- src/settings/scriptsettingdialog.ui | 7 +- 31 files changed, 1257 insertions(+), 329 deletions(-) create mode 100644 TestBadPlugin/CMakeLists.txt create mode 100644 TestBadPlugin/TestBadPlugin.json create mode 100644 TestBadPlugin/testbadplugin.cpp create mode 100644 TestBadPlugin/testbadplugin.h create mode 100644 images/devextdis.png create mode 100644 images/plugindis.png create mode 100644 src/class/changedstringlist.cpp create mode 100644 src/class/changedstringlist.h 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 0000000000000000000000000000000000000000..ea2d154971e338c59ae88cd3a48df5920821c1cc GIT binary patch literal 5427 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 literal 0 HcmV?d00001 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