diff --git a/3rdparty/QHexView/document/qhexcursor.cpp b/3rdparty/QHexView/document/qhexcursor.cpp index 77b70c1..5f2eb9f 100644 --- a/3rdparty/QHexView/document/qhexcursor.cpp +++ b/3rdparty/QHexView/document/qhexcursor.cpp @@ -93,6 +93,7 @@ bool QHexCursor::hasInternalSelection() const { return !m_sels.isEmpty(); } void QHexCursor::clearPreviewSelection() { m_selection = m_position; m_preMode = SelectionNormal; + emit selectionChanged(); } void QHexCursor::clearSelection() { m_sels.clear(); } @@ -156,7 +157,6 @@ void QHexCursor::select(qsizetype line, int column, int nibbleindex, } } - emit positionChanged(); emit selectionChanged(); } @@ -306,4 +306,6 @@ void QHexCursor::mergePreviewSelection() { break; } clearPreviewSelection(); + + emit selectionChanged(); } diff --git a/3rdparty/QHexView/document/qhexcursor.h b/3rdparty/QHexView/document/qhexcursor.h index 4a36dda..c9fe6f7 100644 --- a/3rdparty/QHexView/document/qhexcursor.h +++ b/3rdparty/QHexView/document/qhexcursor.h @@ -57,6 +57,7 @@ struct QHexPosition { return this->offset() >= rhs.offset(); } }; +Q_DECLARE_METATYPE(QHexPosition) struct QHexSelection { QHexPosition start; diff --git a/3rdparty/QHexView/document/qhexdocument.cpp b/3rdparty/QHexView/document/qhexdocument.cpp index 04b1b41..d601389 100644 --- a/3rdparty/QHexView/document/qhexdocument.cpp +++ b/3rdparty/QHexView/document/qhexdocument.cpp @@ -96,12 +96,17 @@ bool QHexDocument::metaCommentVisible() { return m_metacomment; } void QHexDocument::insertBookMarkAdjust(qsizetype offset, qsizetype length) { QMap bms; + if (_bookmarks.isEmpty()) { + return; + } + #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) auto rmbegin = _bookmarks.lowerBound(offset); #else auto rmbegin = std::as_const(_bookmarks).lowerBound(offset); #endif auto addbegin = _bookmarks.upperBound(offset); + for (auto p = addbegin; p != _bookmarks.end(); ++p) { bms.insert(p.key() + length, p.value()); } @@ -120,6 +125,10 @@ void QHexDocument::insertBookMarkAdjust(qsizetype offset, qsizetype length) { void QHexDocument::removeBookMarkAdjust(qsizetype offset, qsizetype length) { QMap bms; + if (_bookmarks.isEmpty()) { + return; + } + #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) auto rmbegin = _bookmarks.lowerBound(offset); #else diff --git a/3rdparty/QHexView/qhexview.cpp b/3rdparty/QHexView/qhexview.cpp index 93336f5..db6a1b7 100644 --- a/3rdparty/QHexView/qhexview.cpp +++ b/3rdparty/QHexView/qhexview.cpp @@ -113,8 +113,10 @@ void QHexView::establishSignal(QHexDocument *doc) { connect(m_cursor, &QHexCursor::positionChanged, this, &QHexView::moveToSelection); - connect(m_cursor, &QHexCursor::selectionChanged, this, - [this]() { this->viewport()->update(); }); + connect(m_cursor, &QHexCursor::selectionChanged, this, [this]() { + this->viewport()->update(); + emit cursorSelectionChanged(); + }); connect(m_cursor, &QHexCursor::insertionModeChanged, this, &QHexView::renderCurrentLine); connect(doc, &QHexDocument::canUndoChanged, this, diff --git a/3rdparty/QHexView/qhexview.h b/3rdparty/QHexView/qhexview.h index fb04f50..d9fe8d2 100644 --- a/3rdparty/QHexView/qhexview.h +++ b/3rdparty/QHexView/qhexview.h @@ -128,6 +128,7 @@ private: signals: void documentChanged(QHexDocument *doc); void cursorLocationChanged(); + void cursorSelectionChanged(); // TODO void canUndoChanged(bool canUndo); void canRedoChanged(bool canRedo); void documentSaved(bool saved); diff --git a/CMakeLists.txt b/CMakeLists.txt index a1e2607..88ae722 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,7 +188,9 @@ set(CONTROL_SRC src/control/codeedit.h src/control/codeedit.cpp src/control/asobjtreewidget.h - src/control/asobjtreewidget.cpp) + src/control/asobjtreewidget.cpp + src/control/qtlonglongspinbox.cpp + src/control/qtlonglongspinbox.h) set(CLASS_SRC src/class/logger.cpp @@ -252,7 +254,9 @@ set(CLASS_SRC src/class/layoutmanager.h src/class/layoutmanager.cpp src/class/wingupdater.h - src/class/wingupdater.cpp) + src/class/wingupdater.cpp + src/class/richtextitemdelegate.h + src/class/richtextitemdelegate.cpp) if(WINGHEX_USE_FRAMELESS) set(WIDGET_FRAME_SRC diff --git a/TestPlugin/lang/TestPlugin_zh_CN.ts b/TestPlugin/lang/TestPlugin_zh_CN.ts index c0b34b9..b7f974f 100644 --- a/TestPlugin/lang/TestPlugin_zh_CN.ts +++ b/TestPlugin/lang/TestPlugin_zh_CN.ts @@ -316,30 +316,30 @@ TestPlugin - + Test 测试 - - - - + + + + TestPlugin 测试插件 - + Button - 按钮 - - + Click 点击 - + A Test Plugin for WingHexExplorer2. 一个用来测试羽云十六进制编辑器2的插件 diff --git a/TestPlugin/testplugin.cpp b/TestPlugin/testplugin.cpp index 2122438..d7cc23a 100644 --- a/TestPlugin/testplugin.cpp +++ b/TestPlugin/testplugin.cpp @@ -245,6 +245,18 @@ void TestPlugin::test_b(const QString &a) { } QHash -TestPlugin::registeredScriptFn() { +TestPlugin::registeredScriptFns() const { return _scriptInfo; } + +WingHex::IWingPlugin::RegisteredEvents TestPlugin::registeredEvents() const { + return RegisteredEvent::AppReady; +} + +void TestPlugin::eventReady() { + bool ret; + emit invokeService( + QStringLiteral("WingAngelAPI"), "execCode", Qt::AutoConnection, + WINGAPI_RETURN_ARG(bool, ret), + WINGAPI_ARG(QString, R"(print("Hello, this is TestPlugin!");)")); +} diff --git a/TestPlugin/testplugin.h b/TestPlugin/testplugin.h index 0c5397e..27c6a13 100644 --- a/TestPlugin/testplugin.h +++ b/TestPlugin/testplugin.h @@ -66,7 +66,10 @@ public: virtual QList registeredPages() const override; virtual QList registeredEditorViewWidgets() const override; - virtual QHash registeredScriptFn() override; + virtual QHash registeredScriptFns() const override; + + virtual RegisteredEvents registeredEvents() const override; + virtual void eventReady() override; private: QString getPuid() const; diff --git a/lang/zh_CN/winghex.ts b/lang/zh_CN/winghex.ts index b02b2c2..2891a81 100644 --- a/lang/zh_CN/winghex.ts +++ b/lang/zh_CN/winghex.ts @@ -442,7 +442,7 @@ 编码 - + Untitled 未命名 @@ -541,47 +541,47 @@ FindDialog - + findstring 字符串 - + findhex 十六进制 - + Region: 搜索区域: - + None 全局搜索 - + Region 区域 - + BeforeCursor 光标前搜索 - + AfterCursor 光标后搜索 - + Selection 选区搜索 - + find 查找 @@ -589,25 +589,30 @@ FindResultModel - + line - + col - + offset 偏移 - + value + + + encoding + + FormatConfigModel @@ -846,978 +851,978 @@ MainWindow - + File 文件 - - + + View 视图 - + About 关于 - + WingHexExplorer 羽云十六进制编辑器 - + loc: 坐标: - + sel: 选长: - + Edit 编辑 - + Script 脚本 - - - + + + Plugin 插件 - + Setting 设置 - - + + Log 日志 - + ExportFindResult 导出搜索结果 - + ClearFindResult 清空记录 - + FindResult 搜索结果 - - - + + + Copy 复制 - - - - + + + + CopyToClipBoard 数据已拷贝到粘贴板 - + LittleEndian 小端 - + BigEndian 大端 - + Number 数值 - - + + CheckSum 校验和 - - + + DeleteBookMark 删除书签 - - + + ClearBookMark 清空书签 - - - - + + + + BookMark 书签 - + DecodeText 解码字符串 - + ScriptConsole 脚本控制台 - + DVList 可视化列表 - + DVTree 可视化树数据 - + DVTable 可视化表格 - + DVText 可视化文本 - - + + Basic 基础 - + New 新建 - + OpenF 打开文件 - + OpenFR 打开局部文件 - + OpenWorkSpace 打开工作区 - + OpenD 打开驱动器 - + RecentFiles 最近打开 - + Reload 重新加载 - - + + Save 保存 - + SaveAs 另存为 - + Export 导出 - + SaveSel 保存选区字节 - - - - + + + + General 基本 - + Undo 撤销 - + Redo 恢复 - + Cut 剪切 - + Paste 粘贴 - + Delete 删除 - + Clone 克隆 - + Lookup 查询 - + Find 查找 - + Goto 跳转 - + Encoding 编码 - + FileInfo 文件信息 - - + + Hex 十六进制 - + CutHex 剪切(十六进制) - + CopyHex 复制(十六进制) - + PasteHex 粘贴(十六进制) - - + + Fill 填充 - + FillZero 填充零 - - - - - + + + + + MetaData 标注 - + DeleteMetadata 删除标注 - + ClearMetadata 清空标注 - + MetaDataEdit 编辑标注 - + DeleteMetaData 删除标注 - + ClearMetaData 清空标注 - + Display 显示 - + ViewText 文本预览 - + Scale 缩放 - + SetupRecent 启动最近文件服务 - + SetupUI 初始化界面 - + SetupDocking 启动 Dock 服务 - + LaunchingLog 启动日志系统 - + SetupPluginSystem 启动插件系统 - + LoadingPlg: 加载插件中: - + SetupConsole 启动脚本控制台 - + SetupScriptManager 启动脚本管理器 - + SetupScriptService 启动脚本服务 - + SetupScriptEditor 构建脚本编辑器 - + SetupSetDialog 构建设置窗体 - + SetupPlgWidgets 启动插件组件 - + SetupDockingLayout 恢复 Dock 布局 - + SetupWaiting 启动即将完成 - + SetupFinished 启动完毕 - + ResetScale 重置缩放 - + ShowMetafg 标注前景色 - + ShowMetabg 标注背景色 - + ShowMetaComment 批注 - + MetaShowAll 显示所有标注 - + MetaHideAll 隐藏所有标注 - + FileStatus 文件状态 - + InfoSave 是否保存 - + ReadOnly 可读写 - + SetLocked 启用/禁用锁定编辑 - + ErrUnLock 锁定编辑失败 - + SetOver 启用/禁用改变大小 - + ErrUnOver 锁定文件大小失败 - + InfoCanOverLimit 当前编辑处于受限模式! - + Window 窗体 - + Editor 编辑器 - + Tools 工具 - + HexEditorLayout 编辑器布局 - + SetBaseAddr 设置基址 - + addressBase 基址 - + inputAddressBase 请输入基址 - + WarnBigBaseAddress 基址过大,你得到的地址将会不正确! - + ErrBaseAddress 非法基址输入 - + SetColInfo 显示/隐藏地址栏 - + SetHeaderInfo 显示/隐藏表头 - + SetAsciiString 显示/隐藏解码字符串 - + Layout 布局 - + Fullscreen 全屏 - + Default 默认 - + RestoreLayout 恢复布局 - - + + SaveLayout 保存布局 - + ExportLog 导出日志 - + ClearLog 清空日志 - + ScriptEditor 脚本编辑器 - + Scripts 脚本仓库 - + PluginFunctions 插件功能 - + ScriptSetting 脚本设置 - + PluginSettings 插件设置 - + Info 信息 - + Software 软件 - + Sponsor 赞助 - + CheckUpdate 检查更新 - + Wiki 网页 Wiki - + AboutQT 关于 QT - + SetPageIDEmptyTryUseName 设置页 ID 为空,尝试使用名称作为 ID - + SetPageDupNameIgnored 设置页重复的 ID 名称,已忽略加载 - + Plugin %1 contains a duplicate ID (%2) that is already registered by plugin %3 插件 %1 包含重复 ID (%2),该 ID 已被插件 %3 注册 - - + + ChooseFile 选择文件 - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + Error 错误 - - - - + + + + FileNotExist 文件不存在! - - - - - + + + + + FilePermission 因文件权限无法继续! - + ProjectFile (*.wingpro) 项目文件 (*.wingpro) - + Root Required! 需要管理员权限继续操作! - + ReloadSuccessfully 文件重新加载成功! - + ReloadUnSuccessfully 文件重新加载失败! - - - + + + ChooseSaveFile 请选择保存文件路径: - + NoMoreClone 克隆已到上限,无法继续操作! - + FindFininishBusy 查找任务繁忙,请勿重复查找! - + MayTooMuchFindResult 搜索数量已到达上限,结果可能不全,建议请按区段搜索。 - + SaveLayoutSuccess 保存布局成功 - + SaveLayoutError 保存布局失败 - + ConfirmSave 正在关闭未保存的文件或工作区,你确定保存吗? - + ConfirmAPPSave 你尝试关闭程序,但仍存在未保存的文件或工作区,你确定保存这些更改吗? - - + + SaveSuccessfully 保存成功! - - + + SaveWSError 保存工作区错误! - - - + + + Warn 警告 - + ScriptObjShow 脚本对象 - - - + + + SourceChanged 局部打开原始文件更改! - - + + SaveSourceFileError 由于原文件更改,保存文件失败! - + SaveUnSuccessfully 保存失败! - + ChooseExportFile 请选择导出文件路径: - + ExportSuccessfully 导出成功! - + ExportSourceFileError 由于原文件更改,导出文件失败! - + ExportUnSuccessfully 导出失败! - + SaveSelSuccess 保存选区字节成功! - + SaveSelError 保存选区字节失败,因文件不具有可写权限! - - + + CutToClipBoard 数据已剪切到粘贴板! - - + + UnCutToClipBoard 由于保持大小限制,数据剪切到粘贴板失败! - - + + UnCopyToClipBoard 由于保持大小限制,数据剪切到复制板失败! - + FindFininish 查找结果完毕! - + PleaseInputFill 请输入填充字节值 - + FillInputError 填充字节输入错误 - - + + InputComment 请输入批注: - - - + + + NoSelection 没有选区,无法继续的操作! - + NoMetaData 无可编辑标记 - + EmptyFindResult 没有可导出的搜索结果! - + SaveFindResult 导出搜索结果成功! - + SaveFindResultError 导出结果失败! - + TooManyBytesDecode 超出解码字节限制…… - + ExportLogError 导出日志失败! - + ExportLogSuccess 导出日志成功,路径: - + ClearLogSuccess 清空日志成功! - + BadNetwork 无法与远程服务器的更新检查建立连接,请检查网络。 - + NewestVersion 当前软件为最新版本 - - + + OlderVersion 你使用的软件为老版本,建议到 Github 和 Gitee 的仓库发行版下载更新。 - + CheckingUpdate 检查更新中…… - + Too much opened files 打开的文件过多,无法继续操作! - + CopyLimit 拷贝字节超出限制 - + ErrOpenFileBelow 打开文件出现错误(由于权限不足),如下为打开错误的文件: @@ -2028,87 +2033,92 @@ PluginSystem - + LoadingPlugin 正在加载插件: - + RegisteredFnDup 注册重复函数对象 - + ErrLoadPluginSign 插件加载失败:非法插件签名! - + ErrLoadPluginSDKVersion 插件加载失败:非法插件 SDK 版本! - + ErrLoadPluginNoName 插件加载失败:非法插件名称! - + ErrLoadLoadedPlugin 插件加载失败:重复加载插件! - + ErrLoadInitPlugin 插件加载失败:初始化插件失败! - + PluginName : 插件名: - + PluginAuthor : 插件作者: - + PluginWidgetRegister 注册插件对象中…… - + EmptyNameDockWidget: 空的贴边组件名: - + InvalidNameDockWidget: 无效贴边组件名: - + InvalidNullDockWidget: 无效空贴边组件: - + + [EvilCall] + 【恶意调用】 + + + Not allowed operation in non-UI thread 该操作在非 UI 线程非法 - + UnsafePluginDir 不安全的插件目录,请将插件目录设置为仅管理员账户可写 - + FoundPluginCount 总计发现插件数目: - + PluginLoadingFinished 加载插件完毕! @@ -5113,22 +5123,22 @@ Do you wish to keep up to date by reloading the file? WingAngelAPI - + AngelScriptService AngelScript 服务 - + A internal plugin that provides AngelScript scripts with the ability to call the host API. 为 AngelScript 脚本提供调用主机 API 能力的内部插件。 - + RegisterScriptFnInvalidSig: 因脚本函数签名非法而注册失败: - + NotSupportedQMetaType: 不支持的 QT 数据元类型: diff --git a/src/class/richtextitemdelegate.cpp b/src/class/richtextitemdelegate.cpp new file mode 100644 index 0000000..e2e707a --- /dev/null +++ b/src/class/richtextitemdelegate.cpp @@ -0,0 +1,54 @@ +#include "richtextitemdelegate.h" + +#include +#include +#include +#include + +RichTextItemDelegate::RichTextItemDelegate(QObject *parent) + : QStyledItemDelegate(parent) {} + +void RichTextItemDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const { + auto options = option; + initStyleOption(&options, index); + + painter->save(); + + QTextDocument doc; + doc.setHtml(options.text); + + options.text.clear(); + options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, + painter); + + // shift text right to make icon visible + QSize iconSize = options.icon.actualSize(options.rect.size()); + painter->translate(options.rect.left() + iconSize.width(), + options.rect.top()); + QRect clip(0, 0, options.rect.width() + iconSize.width(), + options.rect.height()); + + painter->setClipRect(clip); + QAbstractTextDocumentLayout::PaintContext ctx; + // set text color to red for selected item + if (option.state & QStyle::State_Selected) + ctx.palette.setColor(QPalette::Text, Qt::red); + + ctx.clip = clip; + doc.documentLayout()->draw(painter, ctx); + + painter->restore(); +} + +QSize RichTextItemDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const { + auto options = option; + initStyleOption(&options, index); + + QTextDocument doc; + doc.setHtml(options.text); + doc.setTextWidth(options.rect.width()); + return QSize(doc.idealWidth(), doc.size().height()); +} diff --git a/src/class/richtextitemdelegate.h b/src/class/richtextitemdelegate.h new file mode 100644 index 0000000..571fb4c --- /dev/null +++ b/src/class/richtextitemdelegate.h @@ -0,0 +1,19 @@ +#ifndef RICHTEXTITEMDELEGATE_H +#define RICHTEXTITEMDELEGATE_H + +#include + +class RichTextItemDelegate : public QStyledItemDelegate { + Q_OBJECT +public: + explicit RichTextItemDelegate(QObject *parent = nullptr); + + // QAbstractItemDelegate interface +public: + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + virtual QSize sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const override; +}; + +#endif // RICHTEXTITEMDELEGATE_H diff --git a/src/class/wingangelapi.cpp b/src/class/wingangelapi.cpp index 9bd78d9..b4f6a02 100644 --- a/src/class/wingangelapi.cpp +++ b/src/class/wingangelapi.cpp @@ -23,6 +23,7 @@ #include "class/scriptmachine.h" #include "class/wingfiledialog.h" #include "class/winginputdialog.h" +#include "control/scriptingconsole.h" #include "define.h" #include "scriptaddon/scriptqdictionary.h" @@ -96,6 +97,8 @@ void WingAngelAPI::registerScriptFns(const QString &ns, } void WingAngelAPI::installAPI(ScriptMachine *machine) { + Q_ASSERT(machine); + auto engine = machine->engine(); auto stringTypeID = machine->typeInfo(ScriptMachine::tString)->GetTypeId(); @@ -566,12 +569,6 @@ void WingAngelAPI::installHexReaderAPI(asIScriptEngine *engine) { engine, std::bind(&WingHex::WingPlugin::Reader::addressBase, reader), QPTR_WRAP("addressBase()")); - registerAPI( - engine, - std::bind(&WingAngelAPI::_HexReader_read, this, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3), - "bool read(" QSIZETYPE " offset, ? &out obj)"); - registerAPI( engine, std::bind(&WingAngelAPI::_HexReader_readBytes, this, @@ -738,24 +735,6 @@ void WingAngelAPI::installHexControllerAPI(asIScriptEngine *engine) { std::placeholders::_1), "bool setAddressBase(" QPTR " base)"); - registerAPI( - engine, - std::bind(&WingAngelAPI::_HexReader_write, this, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3), - "bool write(" QSIZETYPE " offset, ? &in obj)"); - - registerAPI( - engine, - std::bind(&WingAngelAPI::_HexReader_insert, this, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3), - "bool insert(" QSIZETYPE " offset, ? &in obj)"); - - registerAPI(engine, - std::bind(&WingAngelAPI::_HexReader_append, - this, std::placeholders::_1, - std::placeholders::_2), - "bool append(? &in obj)"); - registerAPI( engine, std::bind(&WingHex::WingPlugin::Controller::writeInt8, ctl, @@ -1232,329 +1211,6 @@ QByteArray WingAngelAPI::cArray2ByteArray(const CScriptArray &array, int byteID, return buffer; } -bool WingAngelAPI::read2Ref(qsizetype offset, void *ref, int typeId) { - asIScriptContext *ctx = asGetActiveContext(); - if (ctx) { - asIScriptEngine *engine = ctx->GetEngine(); - - if (typeId == asTYPEID_VOID) - return false; - else if (typeId == asTYPEID_BOOL) - *reinterpret_cast(ref) = - (emit reader.readInt8(offset) != 0); - else if (typeId == asTYPEID_INT8) - *reinterpret_cast(ref) = emit reader.readInt8(offset); - else if (typeId == asTYPEID_INT16) - *reinterpret_cast(ref) = emit reader.readInt16(offset); - else if (typeId == asTYPEID_INT32) - *reinterpret_cast(ref) = emit reader.readInt32(offset); - else if (typeId == asTYPEID_INT64) - *reinterpret_cast(ref) = emit reader.readInt64(offset); - else if (typeId == asTYPEID_UINT8) - *reinterpret_cast(ref) = - quint8(emit reader.readInt8(offset)); - else if (typeId == asTYPEID_UINT16) - *reinterpret_cast(ref) = - quint16(emit reader.readInt16(offset)); - else if (typeId == asTYPEID_UINT32) - *reinterpret_cast(ref) = - quint32(emit reader.readInt32(offset)); - else if (typeId == asTYPEID_UINT64) - *reinterpret_cast(ref) = - quint64(emit reader.readInt64(offset)); - else if (typeId == asTYPEID_FLOAT) - *reinterpret_cast(ref) = emit reader.readFloat(offset); - else if (typeId == asTYPEID_DOUBLE) - *reinterpret_cast(ref) = emit reader.readDouble(offset); - else if ((typeId & asTYPEID_MASK_OBJECT) == 0) { - bool ok = false; - // Check if the value matches one of the defined enums - if (engine) { - asITypeInfo *t = engine->GetTypeInfoById(typeId); - for (int n = t->GetEnumValueCount(); n-- > 0;) { - int enumVal; - t->GetEnumValueByIndex(n, &enumVal); - if (enumVal == *(int *)ref) { - *reinterpret_cast(ref) = enumVal; - ok = true; - break; - } - } - } - if (!ok) { - return false; - } - } else if (typeId & asTYPEID_SCRIPTOBJECT) { - void *value = ref; - - // Dereference handles, so we can see what it points to - if (typeId & asTYPEID_OBJHANDLE) - value = *(void **)value; - - asIScriptObject *obj = (asIScriptObject *)value; - - if (obj) { - for (asUINT n = 0; n < obj->GetPropertyCount(); n++) { - auto id = obj->GetPropertyTypeId(n); - auto data = obj->GetAddressOfProperty(n); - auto size = getAsTypeSize(id, data); - auto ret = read2Ref(offset, data, id); - if (!ret) { - return false; - } - if (size > 0) { - offset += size; - } - } - } - } else { - // Dereference handles, so we can see what it points to - void *value = ref; - - if (typeId & asTYPEID_OBJHANDLE) - value = *(void **)value; - - if (engine) { - asITypeInfo *type = engine->GetTypeInfoByName("string"); - if (value) { - // only string supported - if (type->GetTypeId() == (typeId & ~asTYPEID_OBJHANDLE)) { - *reinterpret_cast(value) = - emit reader.readString(offset); - } - } - } - } - return true; - } else { - return false; - } -} - -bool WingAngelAPI::write2Ref(qsizetype offset, void *ref, int typeId) { - asIScriptContext *ctx = asGetActiveContext(); - if (ctx) { - asIScriptEngine *engine = ctx->GetEngine(); - - if (typeId == asTYPEID_VOID) - return false; - else if (typeId == asTYPEID_BOOL) - return emit controller.writeInt8( - offset, *reinterpret_cast(ref) ? 1 : 0); - else if (typeId == asTYPEID_INT8 || typeId == asTYPEID_UINT8) - return emit controller.writeInt8(offset, - *reinterpret_cast(ref)); - else if (typeId == asTYPEID_INT16 || typeId == asTYPEID_UINT16) - return emit controller.writeInt16(offset, - *reinterpret_cast(ref)); - else if (typeId == asTYPEID_INT32 || typeId == asTYPEID_UINT32) - return emit controller.writeInt32(offset, - *reinterpret_cast(ref)); - else if (typeId == asTYPEID_INT64 || typeId == asTYPEID_UINT64) - return emit controller.writeInt64(offset, - *reinterpret_cast(ref)); - else if (typeId == asTYPEID_FLOAT) - return emit controller.writeFloat(offset, - *reinterpret_cast(ref)); - else if (typeId == asTYPEID_DOUBLE) - return emit controller.writeDouble( - offset, *reinterpret_cast(ref)); - else if ((typeId & asTYPEID_MASK_OBJECT) == 0) - return emit controller.writeInt32(offset, - *reinterpret_cast(ref)); - else if (typeId & asTYPEID_SCRIPTOBJECT) { - // Dereference handles, so we can see what it points to - void *value = ref; - if (typeId & asTYPEID_OBJHANDLE) - value = *(void **)value; - - asIScriptObject *obj = (asIScriptObject *)value; - - if (obj) { - for (asUINT n = 0; n < obj->GetPropertyCount(); n++) { - auto id = obj->GetPropertyTypeId(n); - auto data = obj->GetAddressOfProperty(n); - auto size = getAsTypeSize(id, data); - auto ret = write2Ref(offset, data, id); - if (!ret) { - return false; - } - if (size > 0) { - offset += size; - } - } - } - } else { - // Dereference handles, so we can see what it points to - void *value = ref; - - if (typeId & asTYPEID_OBJHANDLE) - value = *(void **)value; - - if (engine) { - asITypeInfo *type = engine->GetTypeInfoById(typeId); - if (value) { - // only string supported - if (type->GetTypeId() == (typeId & ~asTYPEID_OBJHANDLE)) { - return emit controller.writeString( - offset, *reinterpret_cast(value)); - } - } - } - } - return true; - } else { - return false; - } -} - -bool WingAngelAPI::insert2Ref(qsizetype offset, void *ref, int typeId) { - asIScriptContext *ctx = asGetActiveContext(); - if (ctx) { - asIScriptEngine *engine = ctx->GetEngine(); - - if (typeId == asTYPEID_VOID) - return false; - else if (typeId == asTYPEID_BOOL) - return emit controller.insertInt8( - offset, *reinterpret_cast(ref) ? 1 : 0); - else if (typeId == asTYPEID_INT8 || typeId == asTYPEID_UINT8) - return emit controller.insertInt8(offset, - *reinterpret_cast(ref)); - else if (typeId == asTYPEID_INT16 || typeId == asTYPEID_UINT16) - return emit controller.insertInt16( - offset, *reinterpret_cast(ref)); - else if (typeId == asTYPEID_INT32 || typeId == asTYPEID_UINT32) - return emit controller.insertInt32( - offset, *reinterpret_cast(ref)); - else if (typeId == asTYPEID_INT64 || typeId == asTYPEID_UINT64) - return emit controller.insertInt64( - offset, *reinterpret_cast(ref)); - else if (typeId == asTYPEID_FLOAT) - return emit controller.insertFloat(offset, - *reinterpret_cast(ref)); - else if (typeId == asTYPEID_DOUBLE) - return emit controller.insertDouble( - offset, *reinterpret_cast(ref)); - else if ((typeId & asTYPEID_MASK_OBJECT) == 0) - return emit controller.insertInt32(offset, - *reinterpret_cast(ref)); - else if (typeId & asTYPEID_SCRIPTOBJECT) { - // Dereference handles, so we can see what it points to - void *value = ref; - if (typeId & asTYPEID_OBJHANDLE) - value = *(void **)value; - - asIScriptObject *obj = (asIScriptObject *)value; - - if (obj) { - for (asUINT n = 0; n < obj->GetPropertyCount(); n++) { - auto id = obj->GetPropertyTypeId(n); - auto data = obj->GetAddressOfProperty(n); - auto size = getAsTypeSize(id, data); - auto ret = insert2Ref(offset, data, id); - if (!ret) { - return false; - } - if (size > 0) { - offset += size; - } - } - } - } else { - // Dereference handles, so we can see what it points to - void *value = ref; - - if (typeId & asTYPEID_OBJHANDLE) - value = *(void **)value; - - if (engine) { - asITypeInfo *type = engine->GetTypeInfoById(typeId); - if (value) { - // TODO support other type, now only string - if (type->GetTypeId() == (typeId & ~asTYPEID_OBJHANDLE)) { - return emit controller.insertString( - offset, *reinterpret_cast(value)); - } - } - } - } - return true; - } else { - return false; - } -} - -bool WingAngelAPI::append2Ref(void *ref, int typeId) { - asIScriptContext *ctx = asGetActiveContext(); - if (ctx) { - asIScriptEngine *engine = ctx->GetEngine(); - - if (typeId == asTYPEID_VOID) - return false; - else if (typeId == asTYPEID_BOOL) - return emit controller.appendInt8( - *reinterpret_cast(ref) ? 1 : 0); - else if (typeId == asTYPEID_INT8 || typeId == asTYPEID_UINT8) - return emit controller.appendInt8(*reinterpret_cast(ref)); - else if (typeId == asTYPEID_INT16 || typeId == asTYPEID_UINT16) - return emit controller.appendInt16( - *reinterpret_cast(ref)); - else if (typeId == asTYPEID_INT32 || typeId == asTYPEID_UINT32) - return emit controller.appendInt32( - *reinterpret_cast(ref)); - else if (typeId == asTYPEID_INT64 || typeId == asTYPEID_UINT64) - return emit controller.appendInt64( - *reinterpret_cast(ref)); - else if (typeId == asTYPEID_FLOAT) - return emit controller.appendFloat(*reinterpret_cast(ref)); - else if (typeId == asTYPEID_DOUBLE) - return emit controller.appendDouble( - *reinterpret_cast(ref)); - else if ((typeId & asTYPEID_MASK_OBJECT) == 0) - return emit controller.appendInt32(*reinterpret_cast(ref)); - else if (typeId & asTYPEID_SCRIPTOBJECT) { - // Dereference handles, so we can see what it points to - void *value = ref; - if (typeId & asTYPEID_OBJHANDLE) - value = *(void **)value; - - asIScriptObject *obj = (asIScriptObject *)value; - - if (obj) { - for (asUINT n = 0; n < obj->GetPropertyCount(); n++) { - auto id = obj->GetPropertyTypeId(n); - auto data = obj->GetAddressOfProperty(n); - auto ret = append2Ref(data, id); - if (!ret) { - return false; - } - } - } - } else { - // Dereference handles, so we can see what it points to - void *value = ref; - - if (typeId & asTYPEID_OBJHANDLE) - value = *(void **)value; - - if (engine) { - asITypeInfo *type = engine->GetTypeInfoById(typeId); - if (value) { - // only string supported - if (type->GetTypeId() == (typeId & ~asTYPEID_OBJHANDLE)) { - return emit controller.appendString( - *reinterpret_cast(value)); - } - } - } - } - return true; - } else { - return false; - } -} - qsizetype WingAngelAPI::getAsTypeSize(int typeId, void *data) { if (typeId == asTYPEID_VOID) return false; @@ -1904,7 +1560,7 @@ int WingAngelAPI::qvariantCastASID(asIScriptEngine *engine, bool WingAngelAPI::isTempBuffered(QMetaType::Type type) { switch (type) { - case QMetaType::Type::Bool: + case QMetaType::Bool: case QMetaType::Short: case QMetaType::UShort: case QMetaType::Int: @@ -1997,6 +1653,52 @@ void WingAngelAPI::script_call(asIScriptGeneric *gen) { std::bind(op, gen, std::placeholders::_1, std::placeholders::_2)); } +bool WingAngelAPI::execScriptCode(const QString &code) { + if (code.isEmpty()) { + return true; + } + + if (_console) { + QTemporaryFile f; + if (f.open()) { + f.write(code.toUtf8()); + f.close(); + } + + _console->setMode(ScriptingConsole::Output); + _console->write(QStringLiteral("(") % + property("__LAST_CALLER__").toString() % + QStringLiteral(") ")); + _console->machine()->executeScript(f.fileName()); + _console->appendCommandPrompt(); + _console->setMode(ScriptingConsole::Input); + } + + return false; +} + +bool WingAngelAPI::execScript(const QString &fileName) { + _console->setMode(ScriptingConsole::Output); + _console->write(QStringLiteral("(") % + property("__LAST_CALLER__").toString() % + QStringLiteral(") ")); + auto ret = _console->machine()->executeScript(fileName); + _console->appendCommandPrompt(); + _console->setMode(ScriptingConsole::Input); + return ret; +} + +bool WingAngelAPI::execCode(const QString &code) { + _console->setMode(ScriptingConsole::Output); + _console->write(QStringLiteral("(") % + property("__LAST_CALLER__").toString() % + QStringLiteral(") ")); + auto ret = _console->machine()->executeCode(code); + _console->appendCommandPrompt(); + _console->setMode(ScriptingConsole::Input); + return ret; +} + QString WingAngelAPI::_InputBox_getItem(int stringID, const QString &title, const QString &label, const CScriptArray &items, int current, @@ -2068,22 +1770,6 @@ CScriptArray *WingAngelAPI::_HexReader_readBytes(qsizetype offset, }); } -bool WingAngelAPI::_HexReader_read(qsizetype offset, void *ref, int typeId) { - return read2Ref(offset, ref, typeId); -} - -bool WingAngelAPI::_HexReader_write(qsizetype offset, void *ref, int typeId) { - return write2Ref(offset, ref, typeId); -} - -bool WingAngelAPI::_HexReader_insert(qsizetype offset, void *ref, int typeId) { - return insert2Ref(offset, ref, typeId); -} - -bool WingAngelAPI::_HexReader_append(void *ref, int typeId) { - return append2Ref(ref, typeId); -} - qsizetype WingAngelAPI::_HexReader_searchForward(qsizetype begin, const CScriptArray &ba) { // If called from the script, there will always be an active @@ -2263,3 +1949,9 @@ bool WingAngelAPI::_DataVisual_updateTextTable( return false; } } + +ScriptingConsole *WingAngelAPI::bindingConsole() const { return _console; } + +void WingAngelAPI::setBindingConsole(ScriptingConsole *console) { + _console = console; +} diff --git a/src/class/wingangelapi.h b/src/class/wingangelapi.h index ea5dc7d..461b590 100644 --- a/src/class/wingangelapi.h +++ b/src/class/wingangelapi.h @@ -27,6 +27,7 @@ class asIScriptEngine; class ScriptMachine; +class ScriptingConsole; class WingAngelAPI : public WingHex::IWingPlugin { Q_OBJECT @@ -46,12 +47,16 @@ public: virtual uint pluginVersion() const override; virtual const QString pluginComment() const override; +public: void registerScriptFns(const QString &ns, const QHash &rfns); void installAPI(ScriptMachine *machine); + ScriptingConsole *bindingConsole() const; + void setBindingConsole(ScriptingConsole *console); + private: void installLogAPI(asIScriptEngine *engine); void installExtAPI(asIScriptEngine *engine); @@ -86,14 +91,6 @@ private: QByteArray cArray2ByteArray(const CScriptArray &array, int byteID, bool *ok = nullptr); - bool read2Ref(qsizetype offset, void *ref, int typeId); - - bool write2Ref(qsizetype offset, void *ref, int typeId); - - bool insert2Ref(qsizetype offset, void *ref, int typeId); - - bool append2Ref(void *ref, int typeId); - qsizetype getAsTypeSize(int typeId, void *data); template @@ -124,6 +121,11 @@ private: static void script_call(asIScriptGeneric *gen); +private: + WING_SERVICE bool execScriptCode(const QString &code); + WING_SERVICE bool execScript(const QString &fileName); + WING_SERVICE bool execCode(const QString &code); + private: QString _InputBox_getItem(int stringID, const QString &title, const QString &label, const CScriptArray &items, @@ -142,14 +144,6 @@ private: CScriptArray *_HexReader_readBytes(qsizetype offset, qsizetype len); - bool _HexReader_read(qsizetype offset, void *ref, int typeId); - - bool _HexReader_write(qsizetype offset, void *ref, int typeId); - - bool _HexReader_insert(qsizetype offset, void *ref, int typeId); - - bool _HexReader_append(void *ref, int typeId); - qsizetype _HexReader_searchForward(qsizetype begin, const CScriptArray &ba); qsizetype _HexReader_searchBackward(qsizetype begin, @@ -180,6 +174,8 @@ private: std::vector _fnbuffer; QVector _sfns; + ScriptingConsole *_console = nullptr; + QHash> _rfns; }; diff --git a/src/control/editorview.cpp b/src/control/editorview.cpp index fec57f4..e9f0591 100644 --- a/src/control/editorview.cpp +++ b/src/control/editorview.cpp @@ -170,8 +170,7 @@ void EditorView::registerQMenu(QMenu *menu) { m_menu->addMenu(menu); } -EditorView::FindError EditorView::find(const QByteArray &data, - const FindDialog::Result &result) { +EditorView::FindError EditorView::find(const FindDialog::Result &result) { if (m_findMutex.tryLock(3000)) { std::unique_lock locker(m_findMutex, std::adopt_lock_t()); auto d = m_hex->document(); @@ -196,19 +195,41 @@ EditorView::FindError EditorView::find(const QByteArray &data, end = -1; } break; } + + QByteArray data; + + if (result.isStringFind) { + data = Utilities::encodingString(result.str, result.encoding); + } else { + data = result.buffer; + } + d->findAllBytes(begin, end, data, results); m_findResults->beginUpdate(); m_findResults->clear(); - for (auto &result : results) { + + auto lineWidth = m_hex->renderer()->hexLineWidth(); + for (auto &ritem : results) { FindResult r; - r.offset = result; - r.line = r.offset / m_hex->renderer()->hexLineWidth(); - r.col = r.offset % m_hex->renderer()->hexLineWidth(); + r.offset = ritem; + r.line = r.offset / lineWidth; + r.col = r.offset % lineWidth; m_findResults->results().append(r); + + QString content; + QByteArray buffer; + // TODO + FindResultModel::FindInfo info; + + // default show lineWidth count + if (data.size() > lineWidth - 4) { + } + + info.decoding = Utilities::decodingString(buffer, result.encoding); + m_findResults->lastFindData().append(info); } - m_findResults->lastFindData() = data.toHex(' ').toUpper(); m_findResults->endUpdate(); if (m_findResults->size() == diff --git a/src/control/editorview.h b/src/control/editorview.h index e8eaafb..41842fb 100644 --- a/src/control/editorview.h +++ b/src/control/editorview.h @@ -92,7 +92,7 @@ public slots: void switchView(WingEditorViewWidget *w); void registerQMenu(QMenu *menu); - FindError find(const QByteArray &data, const FindDialog::Result &result); + FindError find(const FindDialog::Result &result); void clearFindResult(); diff --git a/src/control/qtlonglongspinbox.cpp b/src/control/qtlonglongspinbox.cpp new file mode 100644 index 0000000..a65864f --- /dev/null +++ b/src/control/qtlonglongspinbox.cpp @@ -0,0 +1,257 @@ +/*============================================================================== +** Copyright (C) 2024-2027 WingSummer +** +** This program is free software: you can redistribute it and/or modify it under +** the terms of the GNU Affero General Public License as published by the Free +** Software Foundation, version 3. +** +** This program is distributed in the hope that it will be useful, but WITHOUT +** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +** details. +** +** You should have received a copy of the GNU Affero General Public License +** along with this program. If not, see . +** ============================================================================= +*/ + +#include "qtlonglongspinbox.h" + +#include +#include +#include +#include + +QtLongLongSpinBox::QtLongLongSpinBox(QWidget *parent) + : QAbstractSpinBox(parent) { + m_minimum = std::numeric_limits::min(); + m_maximum = std::numeric_limits::max(); + m_value = 0; + m_singleStep = 1; + m_base = 10; + + setValue(m_value); +} + +qlonglong QtLongLongSpinBox::value() const { return m_value; } + +void QtLongLongSpinBox::setValue(qlonglong expectedNewValue) { + const qlonglong newValue = qBound(m_minimum, expectedNewValue, m_maximum); + const QString newValueString = QString::number(newValue, m_base); + lineEdit()->setText(m_prefix + newValueString + m_suffix); + if (m_value != newValue) { + m_value = newValue; + emit valueChanged(newValue); + } +} + +QString QtLongLongSpinBox::prefix() const { return m_prefix; } + +void QtLongLongSpinBox::setPrefix(const QString &prefix) { + m_prefix = prefix; + + setValue(m_value); +} + +QString QtLongLongSpinBox::suffix() const { return m_suffix; } + +void QtLongLongSpinBox::setSuffix(const QString &suffix) { + m_suffix = suffix; + + setValue(m_value); +} + +QString QtLongLongSpinBox::cleanText() const { + return QString::number(m_value); +} + +qlonglong QtLongLongSpinBox::singleStep() const { return m_singleStep; } + +void QtLongLongSpinBox::setSingleStep(qlonglong step) { m_singleStep = step; } + +qlonglong QtLongLongSpinBox::minimum() const { return m_minimum; } + +void QtLongLongSpinBox::setMinimum(qlonglong min) { + m_minimum = min; + if (m_maximum < m_minimum) { + m_maximum = m_minimum; + } + + setValue(m_value); +} + +qlonglong QtLongLongSpinBox::maximum() const { return m_maximum; } + +void QtLongLongSpinBox::setMaximum(qlonglong max) { + m_maximum = max; + if (m_maximum < m_minimum) { + m_maximum = m_minimum; + } + + setValue(m_value); +} + +void QtLongLongSpinBox::setRange(qlonglong min, qlonglong max) { + if (min < max) { + m_minimum = min; + m_maximum = max; + } else { + m_minimum = max; + m_maximum = min; + } + + setValue(m_value); +} + +void QtLongLongSpinBox::setDisplayIntegerBase(int base) { + if (m_base != base) { + m_base = base; + setValue(m_value); + } +} + +int QtLongLongSpinBox::displayIntegerBase() const { return m_base; } + +void QtLongLongSpinBox::keyPressEvent(QKeyEvent *event) { + switch (event->key()) { + case Qt::Key_Enter: + case Qt::Key_Return: + selectCleanText(); + lineEditEditingFinalize(); + } + + QAbstractSpinBox::keyPressEvent(event); +} + +void QtLongLongSpinBox::focusOutEvent(QFocusEvent *event) { + lineEditEditingFinalize(); + + QAbstractSpinBox::focusOutEvent(event); +} + +QAbstractSpinBox::StepEnabled QtLongLongSpinBox::stepEnabled() const { + if (isReadOnly()) { + return StepNone; + } + + StepEnabled se = StepNone; + if (wrapping() || m_value < m_maximum) { + se |= StepUpEnabled; + } + if (wrapping() || m_value > m_minimum) { + se |= StepDownEnabled; + } + + return se; +} + +void QtLongLongSpinBox::stepBy(int steps) { + if (isReadOnly()) { + return; + } + + if (m_prefix + QString::number(m_value) + m_suffix != lineEdit()->text()) { + lineEditEditingFinalize(); + } + + qlonglong newValue = m_value + (steps * m_singleStep); + if (wrapping()) { + // emulating the behavior of QSpinBox + if (newValue > m_maximum) { + if (m_value == m_maximum) { + newValue = m_minimum; + } else { + newValue = m_maximum; + } + } else if (newValue < m_minimum) { + if (m_value == m_minimum) { + newValue = m_maximum; + } else { + newValue = m_minimum; + } + } + } else { + newValue = qBound(m_minimum, newValue, m_maximum); + } + + setValue(newValue); + selectCleanText(); +} + +QValidator::State QtLongLongSpinBox::validate(QString &input, int &pos) const { + // first, we try to interpret as a number without prefixes + bool ok; + const qlonglong value = input.toLongLong(&ok); + if (input.isEmpty() || (ok && value <= m_maximum)) { + input = m_prefix + input + m_suffix; + pos += m_prefix.length(); + return QValidator::Acceptable; + } + + // if string of text editor aren't simple number, try to interpret it + // as a number with prefix and suffix + bool valid = true; + if (!m_prefix.isEmpty() && !input.startsWith(m_prefix)) { + valid = false; + } + if (!m_suffix.isEmpty() && !input.endsWith(m_suffix)) { + valid = false; + } + + if (valid) { + const int start = m_prefix.length(); + const int length = input.length() - start - m_suffix.length(); + + bool ok; + const QString number = input.mid(start, length); + const qlonglong value = number.toLongLong(&ok); + if (number.isEmpty() || (ok && value <= m_maximum)) { + return QValidator::Acceptable; + } + } + + // otherwise not acceptable + return QValidator::Invalid; +} + +void QtLongLongSpinBox::lineEditEditingFinalize() { + const QString text = lineEdit()->text(); + + // first, we try to read as a number without prefixes + bool ok; + qlonglong value = text.toLongLong(&ok); + if (ok) { + setValue(value); + return; + } + + // if string of text editor aren't simple number, try to interpret it + // as a number with prefix and suffix + bool valid = true; + if (!m_prefix.isEmpty() && !text.startsWith(m_prefix)) { + valid = false; + } else if (!m_suffix.isEmpty() && !text.endsWith(m_suffix)) { + valid = false; + } + + if (valid) { + const int start = m_prefix.length(); + const int length = text.length() - start - m_suffix.length(); + + bool ok; + const qlonglong value = text.mid(start, length).toLongLong(&ok); + if (ok) { + setValue(value); + return; + } + } + + // otherwise set old value + setValue(m_value); +} + +void QtLongLongSpinBox::selectCleanText() { + lineEdit()->setSelection(m_prefix.length(), lineEdit()->text().length() - + m_prefix.length() - + m_suffix.length()); +} diff --git a/src/control/qtlonglongspinbox.h b/src/control/qtlonglongspinbox.h new file mode 100644 index 0000000..a033907 --- /dev/null +++ b/src/control/qtlonglongspinbox.h @@ -0,0 +1,85 @@ +/*============================================================================== +** Copyright (C) 2024-2027 WingSummer +** +** This program is free software: you can redistribute it and/or modify it under +** the terms of the GNU Affero General Public License as published by the Free +** Software Foundation, version 3. +** +** This program is distributed in the hope that it will be useful, but WITHOUT +** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +** details. +** +** You should have received a copy of the GNU Affero General Public License +** along with this program. If not, see . +** ============================================================================= +*/ + +#ifndef QTLONGLONGSPINBOX_H +#define QTLONGLONGSPINBOX_H + +#include +#include + +class QtLongLongSpinBox : public QAbstractSpinBox { + Q_OBJECT +public: + explicit QtLongLongSpinBox(QWidget *parent = 0); + + qlonglong value() const; + + QString prefix() const; + void setPrefix(const QString &prefix); + + QString suffix() const; + void setSuffix(const QString &suffix); + + QString cleanText() const; + + qlonglong singleStep() const; + void setSingleStep(qlonglong val); + + qlonglong minimum() const; + void setMinimum(qlonglong min); + + qlonglong maximum() const; + void setMaximum(qlonglong max); + + void setRange(qlonglong min, qlonglong max); + + void setDisplayIntegerBase(int base); + + int displayIntegerBase() const; + +public slots: + void setValue(qlonglong value); + +signals: + void valueChanged(qlonglong i); + +protected: + virtual void keyPressEvent(QKeyEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void stepBy(int steps); + virtual StepEnabled stepEnabled() const; + virtual QValidator::State validate(QString &input, int &pos) const; + +private: + void lineEditEditingFinalize(); + void selectCleanText(); + +private: + QString m_prefix; + QString m_suffix; + qlonglong m_singleStep; + qlonglong m_minimum; + qlonglong m_maximum; + qlonglong m_value; + + int m_base; + +private: + Q_DISABLE_COPY(QtLongLongSpinBox) +}; + +#endif // QTLONGLONGSPINBOX_H diff --git a/src/dialog/finddialog.cpp b/src/dialog/finddialog.cpp index 8423d28..a0490f9 100644 --- a/src/dialog/finddialog.cpp +++ b/src/dialog/finddialog.cpp @@ -25,14 +25,7 @@ #include #include -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -#include -#else -#include -#endif - -FindDialog::FindDialog(bool isBigFile, int start, int stop, bool sel, - QWidget *parent) +FindDialog::FindDialog(const FindInfo &info, QWidget *parent) : FramelessDialogBase(parent) { auto widget = new QWidget(this); auto layout = new QVBoxLayout(widget); @@ -71,30 +64,44 @@ FindDialog::FindDialog(bool isBigFile, int start, int stop, bool sel, layout->addWidget(m_hexeditor); layout->addSpacing(10); - m_hex->setChecked(true); + if (info.isStringFind) { + m_string->setChecked(true); + m_lineeditor->setEnabled(true); + m_hex->setEnabled(false); + if (!info.encoding.isEmpty()) { + m_encodings->setCurrentText(info.encoding); + } + } else { + m_hex->setChecked(true); + m_lineeditor->setEnabled(false); + m_hex->setEnabled(true); + } + + m_lineeditor->setText(info.str); + m_hexeditor->document()->_insert(0, info.buffer); auto regionw = new QWidget(this); auto regionLayout = new QHBoxLayout(regionw); regionLayout->addWidget(new QLabel(tr("Region:"), regionw)); - m_regionStart = new QSpinBox(regionw); - Q_ASSERT(stop >= start); - m_regionStart->setRange(start, stop); + m_regionStart = new QtLongLongSpinBox(regionw); + Q_ASSERT(info.stop >= info.start); + m_regionStart->setRange(info.start, info.stop); m_regionStart->setEnabled(false); - m_regionStart->setValue(start); + m_regionStart->setValue(info.start); m_regionStart->setDisplayIntegerBase(16); m_regionStart->setPrefix(QStringLiteral("0x")); regionLayout->addWidget(m_regionStart, 1); regionLayout->addWidget(new QLabel(QStringLiteral(" - "), regionw)); - m_regionStop = new QSpinBox(regionw); - m_regionStop->setRange(start, stop); - connect(m_regionStart, QOverload::of(&QSpinBox::valueChanged), - m_regionStop, &QSpinBox::setMinimum); + m_regionStop = new QtLongLongSpinBox(regionw); + m_regionStop->setRange(info.start, info.stop); + connect(m_regionStart, &QtLongLongSpinBox::valueChanged, m_regionStop, + &QtLongLongSpinBox::setMinimum); m_regionStop->setEnabled(false); - m_regionStop->setValue(qMin(start + 1024 * 1024, stop)); + m_regionStop->setValue(qMin(info.start + 1024 * 1024, info.stop)); m_regionStop->setDisplayIntegerBase(16); m_regionStop->setPrefix(QStringLiteral("0x")); regionLayout->addWidget(m_regionStop, 1); @@ -118,7 +125,7 @@ FindDialog::FindDialog(bool isBigFile, int start, int stop, bool sel, } }); group->addButton(b, id++); - b->setEnabled(!isBigFile); + b->setEnabled(!info.isBigFile); buttonLayout->addWidget(b); b = new QPushButton(tr("Region"), this); @@ -155,7 +162,7 @@ FindDialog::FindDialog(bool isBigFile, int start, int stop, bool sel, b = new QPushButton(tr("Selection"), this); b->setCheckable(true); - if (sel) { + if (info.isSel) { connect(b, &QPushButton::toggled, this, [=](bool b) { if (b) { _result.dir = SearchDirection::Selection; @@ -167,7 +174,7 @@ FindDialog::FindDialog(bool isBigFile, int start, int stop, bool sel, group->addButton(b, id++); buttonLayout->addWidget(b); - group->button(isBigFile ? 1 : 0)->setChecked(true); + group->button(info.isBigFile ? 1 : 0)->setChecked(true); layout->addWidget(btnBox); layout->addSpacing(20); @@ -184,38 +191,20 @@ FindDialog::FindDialog(bool isBigFile, int start, int stop, bool sel, this->setWindowTitle(tr("find")); } -QByteArray FindDialog::getResult(Result &result) { +FindDialog::Result FindDialog::getResult() const { return _result; } + +void FindDialog::on_accept() { _result.start = 0; _result.stop = 0; if (m_regionStart->isEnabled()) { _result.start = m_regionStart->value(); _result.stop = m_regionStop->value(); } - result = _result; - return _findarr; -} - -void FindDialog::on_accept() { - if (m_string->isChecked()) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - auto en = QStringConverter::encodingForName( - m_encodings->currentText().toUtf8()); - Q_ASSERT(en.has_value()); - QStringEncoder e(en.value()); - _findarr = e.encode(m_lineeditor->text()); -#else - auto en = QTextCodec::codecForName(m_encodings->currentText().toUtf8()); - auto e = en->makeEncoder(); - _findarr = e->fromUnicode(m_lineeditor->text()); -#endif - } else { - _findarr = - m_hexeditor->document()->read(0, int(m_hexeditor->documentBytes())); - } + _result.encoding = m_encodings->currentText(); + _result.isStringFind = m_string->isChecked(); + _result.buffer = m_hexeditor->document()->read(0); + _result.str = m_lineeditor->text(); done(1); } -void FindDialog::on_reject() { - _findarr.clear(); - done(0); -} +void FindDialog::on_reject() { done(0); } diff --git a/src/dialog/finddialog.h b/src/dialog/finddialog.h index fdce835..7170547 100644 --- a/src/dialog/finddialog.h +++ b/src/dialog/finddialog.h @@ -19,13 +19,13 @@ #define FINDDIALOG_H #include "QHexView/qhexview.h" +#include "control/qtlonglongspinbox.h" #include "framelessdialogbase.h" #include #include #include #include -#include enum class SearchDirection { None, Region, Foreword, Backword, Selection }; @@ -36,12 +36,30 @@ public: SearchDirection dir = SearchDirection::None; qsizetype start = 0; qsizetype stop = 0; + + // for searching info + bool isStringFind; + QByteArray buffer; + QString encoding; + QString str; + }; + + struct FindInfo { + bool isBigFile; + bool isStringFind; + qlonglong start; + qlonglong stop; + bool isSel; + + // for searching info + QByteArray buffer; + QString encoding; + QString str; }; public: - FindDialog(bool isBigFile, int start, int stop, bool sel = true, - QWidget *parent = nullptr); - QByteArray getResult(Result &result); + explicit FindDialog(const FindInfo &info, QWidget *parent = nullptr); + Result getResult() const; private: void on_accept(); @@ -53,10 +71,9 @@ private: QRadioButton *m_string; QRadioButton *m_hex; QComboBox *m_encodings; - QByteArray _findarr; - QSpinBox *m_regionStart; - QSpinBox *m_regionStop; + QtLongLongSpinBox *m_regionStart; + QtLongLongSpinBox *m_regionStop; Result _result; }; diff --git a/src/dialog/mainwindow.cpp b/src/dialog/mainwindow.cpp index fb7854c..e7c87bf 100644 --- a/src/dialog/mainwindow.cpp +++ b/src/dialog/mainwindow.cpp @@ -29,6 +29,7 @@ #include "class/layoutmanager.h" #include "class/logger.h" #include "class/qkeysequences.h" +#include "class/richtextitemdelegate.h" #include "class/scriptconsolemachine.h" #include "class/settingmanager.h" #include "class/wingfiledialog.h" @@ -185,6 +186,8 @@ MainWindow::MainWindow(SplashDialog *splash) : FramelessMainWindow() { // load the model Q_ASSERT(m_scriptConsole && m_scriptConsole->machine()); m_varshowtable->setModel(m_scriptConsole->consoleMachine()->model()); + + plg.angelApi()->setBindingConsole(m_scriptConsole); } // connect settings signals @@ -238,6 +241,8 @@ MainWindow::MainWindow(SplashDialog *splash) : FramelessMainWindow() { this->setUpdatesEnabled(true); + plg.dispatchEvent(IWingPlugin::RegisteredEvent::AppReady, {}); + if (splash) splash->setInfoText(tr("SetupFinished")); } @@ -484,6 +489,9 @@ MainWindow::buildUpFindResultDock(ads::CDockManager *dock, m_findresult->addAction(newAction(QStringLiteral("del"), tr("ClearFindResult"), &MainWindow::on_clearfindresult)); + + m_findresult->setItemDelegate(new RichTextItemDelegate(m_findresult)); + m_findresult->setModel(_findEmptyResult); connect(m_findresult, &QTableView::doubleClicked, this, @@ -1826,14 +1834,24 @@ void MainWindow::on_findfile() { return; } auto hexeditor = editor->hexEditor(); - FindDialog fd(editor->isBigFile(), 0, int(hexeditor->documentBytes()), - hexeditor->selectionCount() == 1, this); + + static FindDialog::FindInfo info; + info.isBigFile = editor->isBigFile(); + info.start = 0; + info.stop = hexeditor->documentBytes(); + info.isSel = hexeditor->selectionCount() == 1; + + FindDialog fd(info, this); if (fd.exec()) { - FindDialog::Result r; - auto res = fd.getResult(r); + auto r = fd.getResult(); + info.isStringFind = r.isStringFind; + info.encoding = r.encoding; + info.buffer = r.buffer; + info.str = r.str; + ExecAsync( - [this, r, res]() -> EditorView::FindError { - return currentEditor()->find(res, r); + [this, r]() -> EditorView::FindError { + return currentEditor()->find(r); }, [this](EditorView::FindError err) { switch (err) { @@ -2262,7 +2280,10 @@ void MainWindow::on_exportfindresult() { if (f.open(QFile::WriteOnly)) { QJsonObject fobj; fobj.insert(QStringLiteral("file"), editor->fileName()); - fobj.insert(QStringLiteral("data"), findresitem->lastFindData()); + + // auto d= findresitem->lastFindData(); + + // fobj.insert(QStringLiteral("data"), findresitem->lastFindData()); QJsonArray arr; for (int i = 0; i < c; i++) { auto data = findresitem->resultAt(i); @@ -2371,6 +2392,19 @@ void MainWindow::on_locChanged() { _numsitem->setNumData(NumShowModel::NumTableIndex::Char, QString()); } + auto cursor = hexeditor->cursor(); + + PluginSystem::instance().dispatchEvent( + IWingPlugin::RegisteredEvent::CursorPositionChanged, + {QVariant::fromValue(cursor->position())}); +} + +void MainWindow::on_selectionChanged() { + auto hexeditor = currentHexView(); + if (hexeditor == nullptr) { + return; + } + // 解码字符串 auto cursor = hexeditor->cursor(); QByteArrayList buffer; @@ -2402,22 +2436,8 @@ void MainWindow::on_locChanged() { // 如果不超过 10KB (默认)那么解码,防止太多卡死 if (buffer.length() <= 1024 * _decstrlim) { - auto encname = hexeditor->renderer()->encoding(); - if (encname == QStringLiteral("ASCII")) { - encname = QStringLiteral("ISO-8859-1"); - } - -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - auto enc = QStringConverter::encodingForName(encname.toUtf8()); - Q_ASSERT(enc.has_value()); - QStringDecoder dec(enc.value()); - - m_txtDecode->insertPlainText(dec.decode(b)); -#else - auto enc = QTextCodec::codecForName(encname.toUtf8()); - auto dec = enc->makeDecoder(); - m_txtDecode->setText(dec->toUnicode(b)); -#endif + m_txtDecode->insertPlainText(Utilities::decodingString( + b, hexeditor->renderer()->encoding())); m_txtDecode->insertPlainText(QStringLiteral("\n")); } else { m_txtDecode->insertHtml( @@ -2425,6 +2445,10 @@ void MainWindow::on_locChanged() { .arg(tr("TooManyBytesDecode"))); } } + + PluginSystem::instance().dispatchEvent( + IWingPlugin::RegisteredEvent::SelectionChanged, + {QVariant::fromValue(buffer), isPreview}); } void MainWindow::on_viewtxt() { @@ -2621,6 +2645,9 @@ void MainWindow::registerEditorView(EditorView *editor) { auto ta = editor->toggleViewAction(); menu->addAction(ta); ev->setEnabled(true); + + PluginSystem::instance().dispatchEvent( + IWingPlugin::RegisteredEvent::FileOpened, {editor->fileName()}); } void MainWindow::connectEditorView(EditorView *editor) { @@ -2717,6 +2744,7 @@ void MainWindow::swapEditor(EditorView *old, EditorView *cur) { if (old != nullptr) { auto hexeditor = old->hexEditor(); hexeditor->disconnect(SIGNAL(cursorLocationChanged())); + hexeditor->disconnect(SIGNAL(cursorSelectionChanged())); hexeditor->disconnect(SIGNAL(canUndoChanged(bool))); hexeditor->disconnect(SIGNAL(canRedoChanged(bool))); hexeditor->disconnect(SIGNAL(documentSaved(bool))); @@ -2731,6 +2759,8 @@ void MainWindow::swapEditor(EditorView *old, EditorView *cur) { auto hexeditor = cur->hexEditor(); connect(hexeditor, &QHexView::cursorLocationChanged, this, &MainWindow::on_locChanged); + connect(hexeditor, &QHexView::cursorSelectionChanged, this, + &MainWindow::on_selectionChanged); connect(hexeditor, &QHexView::canUndoChanged, this, [this](bool b) { m_toolBtneditors[ToolButtonIndex::UNDO_ACTION]->setEnabled(b); }); @@ -2793,6 +2823,10 @@ void MainWindow::swapEditor(EditorView *old, EditorView *cur) { m_curEditor = cur; hexeditor->getStatus(); + + PluginSystem::instance().dispatchEvent( + IWingPlugin::RegisteredEvent::FileSwitched, + {cur->fileName(), (old ? old->fileName() : QString())}); } void MainWindow::loadFindResult(EditorView *view) { @@ -2971,6 +3005,8 @@ ErrFile MainWindow::saveEditor(EditorView *editor, const QString &filename, return ErrFile::Permission; } + auto oldName = editor->fileName(); + QString workspace = m_views.value(editor); if (editor->change2WorkSpace()) { workspace = editor->fileName() + PROEXT; @@ -2979,6 +3015,10 @@ ErrFile MainWindow::saveEditor(EditorView *editor, const QString &filename, auto ret = editor->save(workspace, filename, ignoreMd5, isExport); if (ret == ErrFile::Success) { m_views[editor] = workspace; + + PluginSystem::instance().dispatchEvent( + IWingPlugin::RegisteredEvent::FileSaved, + {filename.isEmpty() ? oldName : filename, oldName}); } return ret; } @@ -3002,8 +3042,13 @@ ErrFile MainWindow::closeEditor(EditorView *editor, bool force) { m_curEditor = nullptr; _editorLock.unlock(); } + + auto fileName = editor->fileName(); PluginSystem::instance().cleanUpEditorViewHandle(editor); editor->deleteDockWidget(); + PluginSystem::instance().dispatchEvent( + IWingPlugin::RegisteredEvent::FileClosed, {fileName}); + m_toolBtneditors.value(ToolButtonIndex::EDITOR_VIEWS) ->setEnabled(m_views.size() != 0); diff --git a/src/dialog/mainwindow.h b/src/dialog/mainwindow.h index ed9be40..baab303 100644 --- a/src/dialog/mainwindow.h +++ b/src/dialog/mainwindow.h @@ -188,6 +188,7 @@ private slots: void on_clearfindresult(); void on_exportfindresult(); void on_locChanged(); + void on_selectionChanged(); void on_viewtxt(); void on_fullScreen(); diff --git a/src/model/findresultmodel.cpp b/src/model/findresultmodel.cpp index e6ba799..de02ec0 100644 --- a/src/model/findresultmodel.cpp +++ b/src/model/findresultmodel.cpp @@ -21,15 +21,20 @@ FindResultModel::FindResultModel(QObject *parent) : QAbstractTableModel(parent) {} int FindResultModel::rowCount(const QModelIndex &parent) const { + Q_UNUSED(parent); return m_results.size(); } -int FindResultModel::columnCount(const QModelIndex &parent) const { return 4; } +int FindResultModel::columnCount(const QModelIndex &parent) const { + Q_UNUSED(parent); + return 5; +} QVariant FindResultModel::data(const QModelIndex &index, int role) const { switch (role) { case Qt::DisplayRole: { - auto r = m_results.at(index.row()); + auto row = index.row(); + auto r = m_results.at(row); switch (index.column()) { case 0: // line return r.line; @@ -38,8 +43,10 @@ QVariant FindResultModel::data(const QModelIndex &index, int role) const { case 2: // offset return QStringLiteral("0x") + QString::number(r.offset, 16).toUpper(); - case 3: // value - return m_lastFindData; + case 3: // range + return m_lastFindData.at(row).findRange; + case 4: // decoding + return m_lastFindData.at(row).decoding; } return QVariant(); } @@ -62,6 +69,8 @@ QVariant FindResultModel::headerData(int section, Qt::Orientation orientation, return tr("offset"); case 3: return tr("value"); + case 4: + return tr("encoding"); } } else { return section + 1; @@ -72,7 +81,9 @@ QVariant FindResultModel::headerData(int section, Qt::Orientation orientation, QList &FindResultModel::results() { return m_results; } -QString &FindResultModel::lastFindData() { return m_lastFindData; } +QList &FindResultModel::lastFindData() { + return m_lastFindData; +} void FindResultModel::beginUpdate() { this->beginResetModel(); } diff --git a/src/model/findresultmodel.h b/src/model/findresultmodel.h index 19b9ec7..301b281 100644 --- a/src/model/findresultmodel.h +++ b/src/model/findresultmodel.h @@ -19,15 +19,22 @@ #define FINDRESULTMODEL_H #include "plugin/iwingplugin.h" + #include class FindResultModel : public QAbstractTableModel { Q_OBJECT +public: + struct FindInfo { + QString findRange; + QString decoding; + }; + public: explicit FindResultModel(QObject *parent = nullptr); QList &results(); - QString &lastFindData(); + QList &lastFindData(); void beginUpdate(); void endUpdate(); @@ -46,7 +53,7 @@ public: private: QList m_results; - QString m_lastFindData; + QList m_lastFindData; }; #endif // FINDRESULTMODEL_H diff --git a/src/plugin/iwingplugin.h b/src/plugin/iwingplugin.h index 925bc36..79e49ba 100644 --- a/src/plugin/iwingplugin.h +++ b/src/plugin/iwingplugin.h @@ -40,6 +40,7 @@ #include #include #include +#include /** * Don't try to modify this file, unless you are the dev @@ -50,7 +51,7 @@ namespace WingHex { using qusizetype = QIntegerForSizeof::Unsigned; -Q_DECL_UNUSED constexpr auto SDKVERSION = 14; +Q_DECL_UNUSED constexpr auto SDKVERSION = 15; Q_DECL_UNUSED static QString PLUGINDIR() { return QCoreApplication::applicationDirPath() + QStringLiteral("/plugin"); @@ -490,6 +491,22 @@ signals: void raiseView(); }; +struct WingDependency { + QString puid; + uint version; + QString md5; // optional, but recommend +}; + +#ifdef WING_SERVICE +#undef WING_SERVICE +#endif + +#define WING_SERVICE Q_INVOKABLE + +// for bad broken Qt API +#define WINGAPI_ARG(type, data) QArgument(#type, data) +#define WINGAPI_RETURN_ARG(type, data) QReturnArgument(#type, data) + class IWingPlugin : public QObject { Q_OBJECT public: @@ -519,6 +536,7 @@ public: MetaTypeMask = 0xFFFFF, Ref = 0x10000000, Array = 0x100000, + Map = 0x200000 }; static_assert(MetaType::MetaMax < MetaType::Array); @@ -531,11 +549,18 @@ public: enum class RegisteredEvent : uint { None, - SelectionChanged = 1u, - CursorPositionChanged = 1u << 1 + AppReady = 1u, + SelectionChanged = 1u << 1, + CursorPositionChanged = 1u << 2, + FileOpened = 1u << 3, + FileSaved = 1u << 4, + FileSwitched = 1u << 5, + FileClosed = 1u << 6 }; Q_DECLARE_FLAGS(RegisteredEvents, RegisteredEvent) + enum class PluginFileEvent { Opened, Saved, Switched, Closed }; + public: virtual int sdkVersion() const = 0; virtual const QString signature() const = 0; @@ -548,6 +573,13 @@ public: virtual uint pluginVersion() const = 0; virtual const QString pluginComment() const = 0; + virtual QList dependencies() const { return {}; } + + virtual RegisteredEvents registeredEvents() const { + return RegisteredEvent::None; + } + +public: virtual QList registeredDockWidgets() const { return {}; } @@ -564,22 +596,41 @@ public: return {}; } - // QHash - virtual QHash registeredScriptFn() { return {}; } - - virtual RegisteredEvents registeredEvents() const { - return RegisteredEvent::None; +public: + // QHash< function-name, fn > + virtual QHash registeredScriptFns() const { + return {}; } + // QHash< obj-names, decl-members > + virtual QHash registeredScriptObjs() const { + return {}; + } + +signals: + // QHash< obj-names, decl-members > + bool registerScriptObj(const QString &obj, const QStringList &members); + public: - virtual void eventSelectionChanged(const QByteArrayList &selections) { + virtual void eventSelectionChanged(const QByteArrayList &selections, + bool isPreview) { Q_UNUSED(selections); + Q_UNUSED(isPreview); } virtual void eventCursorPositionChanged(const WingHex::HexPosition &pos) { Q_UNUSED(pos); } + virtual void eventPluginFile(PluginFileEvent e, const QString &newfileName, + const QString &oldfileName) { + Q_UNUSED(e); + Q_UNUSED(newfileName); + Q_UNUSED(oldfileName); + } + + virtual void eventReady() {} + signals: // extension and exposed to WingHexAngelScript void toast(const QPixmap &icon, const QString &message); @@ -591,8 +642,72 @@ signals: // not available for AngelScript // only for plugin UI extenstion + QDialog *createDialog(QWidget *content); + bool invokeService(const QString &puid, const char *method, + Qt::ConnectionType type, QGenericReturnArgument ret, + QGenericArgument val0 = QGenericArgument(nullptr), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()); + +public: + inline bool invokeService(const QString &puid, const char *member, + QGenericReturnArgument ret, + QGenericArgument val0 = QGenericArgument(nullptr), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) { + return emit invokeService(puid, member, Qt::AutoConnection, ret, val0, + val1, val2, val3, val4, val5, val6, val7, + val8, val9); + } + + inline bool invokeService(const QString &puid, const char *member, + Qt::ConnectionType type, QGenericArgument val0, + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) { + return emit invokeService(puid, member, type, QGenericReturnArgument(), + val0, val1, val2, val3, val4, val5, val6, + val7, val8, val9); + } + + inline bool invokeService(const QString &puid, const char *member, + QGenericArgument val0, + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) { + return emit invokeService(puid, member, Qt::AutoConnection, + QGenericReturnArgument(), val0, val1, val2, + val3, val4, val5, val6, val7, val8, val9); + } + public: WingPlugin::Reader reader; WingPlugin::Controller controller; diff --git a/src/plugin/pluginsystem.cpp b/src/plugin/pluginsystem.cpp index 7b001d5..fb848d8 100644 --- a/src/plugin/pluginsystem.cpp +++ b/src/plugin/pluginsystem.cpp @@ -47,7 +47,7 @@ PluginSystem::~PluginSystem() { qApp->exit(int(CrashCode::PluginSetting)); } - for (auto &item : loadedplgs) { + for (auto &item : _loadedplgs) { auto set = std::make_unique( udir.absoluteFilePath(item->metaObject()->className()), QSettings::Format::IniFormat); @@ -56,10 +56,12 @@ PluginSystem::~PluginSystem() { } } -const QList &PluginSystem::plugins() const { return loadedplgs; } +const QList &PluginSystem::plugins() const { + return _loadedplgs; +} const IWingPlugin *PluginSystem::plugin(qsizetype index) const { - return loadedplgs.at(index); + return _loadedplgs.at(index); } void PluginSystem::loadPlugin(const QFileInfo &fileinfo, const QDir &setdir) { @@ -201,9 +203,82 @@ bool PluginSystem::closeEditor(IWingPlugin *plg, int handle, bool force) { return true; } +void PluginSystem::dispatchEvent(IWingPlugin::RegisteredEvent event, + const QVariantList ¶ms) { + switch (event) { + case WingHex::IWingPlugin::RegisteredEvent::SelectionChanged: { + Q_ASSERT(params.size() == 2 && + params.at(0).canConvert() && + params.at(1).canConvert()); + auto buffers = params.first().value(); + auto isPreview = params.at(1).toBool(); + for (auto &plg : _evplgs[event]) { + plg->eventSelectionChanged(buffers, isPreview); + } + } break; + case WingHex::IWingPlugin::RegisteredEvent::CursorPositionChanged: { + Q_ASSERT(params.size() == 1 && params.at(0).canConvert()); + auto cursor = params.at(0).value(); + HexPosition pos; + pos.line = cursor.line; + pos.column = cursor.column; + pos.nibbleindex = cursor.nibbleindex; + pos.lineWidth = cursor.lineWidth; + for (auto &plg : _evplgs[event]) { + plg->eventCursorPositionChanged(pos); + } + } break; + case WingHex::IWingPlugin::RegisteredEvent::FileOpened: { + Q_ASSERT(params.size() == 1); + auto fileName = params.first().toString(); + Q_ASSERT(!fileName.isEmpty()); + for (auto &plg : _evplgs[event]) { + plg->eventPluginFile(IWingPlugin::PluginFileEvent::Opened, fileName, + {}); + } + } break; + case WingHex::IWingPlugin::RegisteredEvent::FileSaved: { + Q_ASSERT(params.size() == 2); + auto newFileName = params.at(0).toString(); + auto oldFileName = params.at(1).toString(); + Q_ASSERT(!newFileName.isEmpty() && !oldFileName.isEmpty()); + for (auto &plg : _evplgs[event]) { + plg->eventPluginFile(IWingPlugin::PluginFileEvent::Saved, + oldFileName, newFileName); + } + } break; + case WingHex::IWingPlugin::RegisteredEvent::FileSwitched: { + Q_ASSERT(params.size() == 2); + auto newFileName = params.at(0).toString(); + auto oldFileName = params.at(1).toString(); + for (auto &plg : _evplgs[event]) { + plg->eventPluginFile(IWingPlugin::PluginFileEvent::Switched, + oldFileName, newFileName); + } + } break; + case WingHex::IWingPlugin::RegisteredEvent::AppReady: { + Q_ASSERT(params.isEmpty()); + for (auto &plg : _evplgs[event]) { + plg->eventReady(); + } + } break; + case WingHex::IWingPlugin::RegisteredEvent::FileClosed: { + Q_ASSERT(params.size() == 1); + auto fileName = params.first().toString(); + Q_ASSERT(!fileName.isEmpty()); + for (auto &plg : _evplgs[event]) { + plg->eventPluginFile(IWingPlugin::PluginFileEvent::Closed, fileName, + {}); + } + } break; + default: + break; + } +} + void PluginSystem::registerFns(IWingPlugin *plg) { Q_ASSERT(plg); - auto fns = plg->registeredScriptFn(); + auto fns = plg->registeredScriptFns(); if (fns.isEmpty()) { return; } @@ -224,9 +299,48 @@ void PluginSystem::registerFns(IWingPlugin *plg) { _angelplg->registerScriptFns(plg->metaObject()->className(), rfns); } +void PluginSystem::registerEvents(IWingPlugin *plg) { + Q_ASSERT(plg); + auto evs = plg->registeredEvents(); + using FlagInt = decltype(evs)::Int; + if (FlagInt(evs) == FlagInt(IWingPlugin::RegisteredEvent::None)) { + return; + } + + if (evs.testFlag(IWingPlugin::RegisteredEvent::SelectionChanged)) { + _evplgs[IWingPlugin::RegisteredEvent::SelectionChanged].append(plg); + } + + if (evs.testFlag(IWingPlugin::RegisteredEvent::CursorPositionChanged)) { + _evplgs[IWingPlugin::RegisteredEvent::CursorPositionChanged].append( + plg); + } + + if (evs.testFlag(IWingPlugin::RegisteredEvent::FileOpened)) { + _evplgs[IWingPlugin::RegisteredEvent::FileOpened].append(plg); + } + + if (evs.testFlag(IWingPlugin::RegisteredEvent::FileSaved)) { + _evplgs[IWingPlugin::RegisteredEvent::FileSaved].append(plg); + } + + if (evs.testFlag(IWingPlugin::RegisteredEvent::FileSwitched)) { + _evplgs[IWingPlugin::RegisteredEvent::FileSwitched].append(plg); + } + + if (evs.testFlag(IWingPlugin::RegisteredEvent::AppReady)) { + _evplgs[IWingPlugin::RegisteredEvent::AppReady].append(plg); + } + + if (evs.testFlag(IWingPlugin::RegisteredEvent::FileClosed)) { + _evplgs[IWingPlugin::RegisteredEvent::FileClosed].append(plg); + } +} + QString PluginSystem::type2AngelScriptString(IWingPlugin::MetaType type, bool isArg) { bool isArray = type & WingHex::IWingPlugin::Array; + bool isMap = type & WingHex::IWingPlugin::Map; bool isRef = type & WingHex::IWingPlugin::Ref; QString retype; @@ -282,10 +396,18 @@ QString PluginSystem::type2AngelScriptString(IWingPlugin::MetaType type, return {}; } + if (isMap && isArray) { + return {}; + } + if (isArray) { retype = QStringLiteral("array<") + retype + QStringLiteral(">"); } + if (isMap) { + retype = QStringLiteral("dictionary<") + retype + QStringLiteral(">"); + } + if (isRef) { if (isArg) { retype += QStringLiteral(" @out"); @@ -349,7 +471,7 @@ bool PluginSystem::loadPlugin(IWingPlugin *p, } auto puid = getPUID(p); - if (loadedpuid.contains(puid)) { + if (_loadedpuid.contains(puid)) { throw tr("ErrLoadLoadedPlugin"); } @@ -374,8 +496,8 @@ bool PluginSystem::loadPlugin(IWingPlugin *p, } } - loadedplgs.push_back(p); - loadedpuid << puid; + _loadedplgs.push_back(p); + _loadedpuid << puid; Logger::warning(tr("PluginName :") + p->pluginName()); Logger::warning(tr("PluginAuthor :") + p->pluginAuthor()); @@ -543,6 +665,7 @@ bool PluginSystem::loadPlugin(IWingPlugin *p, } registerFns(p); + registerEvents(p); connectInterface(p); m_plgviewMap.insert(p, nullptr); @@ -609,6 +732,82 @@ void PluginSystem::connectBaseInterface(IWingPlugin *plg) { return nullptr; } }); + connect( + plg, + QOverload::of(&IWingPlugin::invokeService), + this, + [=](const QString &puid, const char *method, Qt::ConnectionType type, + QGenericReturnArgument ret, QGenericArgument val0, + QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, + QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, + QGenericArgument val7, QGenericArgument val8, + QGenericArgument val9) -> bool { + auto r = std::find_if( + _loadedplgs.begin(), _loadedplgs.end(), + [=](IWingPlugin *plg) { return getPUID(plg) == puid; }); + if (r == _loadedplgs.end()) { + return false; + } + auto meta = (*r)->metaObject(); + + // filter the evil call and report to log + QVarLengthArray sig; + int len = qstrlen(method); + if (len <= 0) + return false; + sig.append(method, len); + sig.append('('); + const char *typeNames[] = {ret.name(), val0.name(), val1.name(), + val2.name(), val3.name(), val4.name(), + val5.name(), val6.name(), val7.name(), + val8.name(), val9.name()}; + size_t paramCount; + constexpr auto maxParamCount = + sizeof(typeNames) / sizeof(const char *); + for (paramCount = 1; paramCount < maxParamCount; ++paramCount) { + len = qstrlen(typeNames[paramCount]); + if (len <= 0) + break; + sig.append(typeNames[paramCount], len); + sig.append(','); + } + if (paramCount == 1) + sig.append(')'); // no parameters + else + sig[sig.size() - 1] = ')'; + sig.append('\0'); + + // checking + auto midx = meta->indexOfMethod(sig.constData()); + if (midx < 0) { + auto norm = QMetaObject::normalizedSignature(sig.constData()); + midx = meta->indexOfMethod(norm.constData()); + if (midx < 0) { + return false; + } + } + + auto m = meta->method(midx); + + if (m.methodType() == QMetaMethod::Signal) { + // report + Logger::warning(packLogMessage( + plg->metaObject()->className(), + tr("[EvilCall]") % + QString::fromLatin1(sig.data(), sig.length()))); + return false; + } + + auto obj = *r; + obj->setProperty("__LAST_CALLER__", plg->metaObject()->className()); + return m.invoke(obj, type, ret, val0, val1, val2, val3, val4, val5, + val6, val7, val8, val9); + }); } void PluginSystem::connectReaderInterface(IWingPlugin *plg) { @@ -873,7 +1072,7 @@ void PluginSystem::connectReaderInterface(IWingPlugin *plg) { } _rwlock.unlock(); - return toByteArray(buffer, enco); + return Utilities::decodingString(buffer, enco); } return QString(); }); @@ -1155,7 +1354,7 @@ void PluginSystem::connectControllerInterface(IWingPlugin *plg) { enco = render->encoding(); } - auto unicode = toByteArray(value, enco); + auto unicode = Utilities::encodingString(value, enco); _rwlock.lockForWrite(); auto ret = doc->insert(offset, unicode); @@ -1204,7 +1403,7 @@ void PluginSystem::connectControllerInterface(IWingPlugin *plg) { enco = render->encoding(); } - auto unicode = toByteArray(value, enco); + auto unicode = Utilities::encodingString(value, enco); _rwlock.lockForWrite(); auto ret = doc->replace(offset, unicode); _rwlock.unlock(); @@ -1251,7 +1450,7 @@ void PluginSystem::connectControllerInterface(IWingPlugin *plg) { enco = render->encoding(); } - auto unicode = toByteArray(value, enco); + auto unicode = Utilities::encodingString(value, enco); _rwlock.lockForWrite(); auto ret = doc->insert(offset, unicode); _rwlock.unlock(); @@ -2185,26 +2384,3 @@ EditorView *PluginSystem::pluginCurrentEditor(IWingPlugin *sender) const { } return nullptr; } - -QByteArray PluginSystem::toByteArray(const QString &buffer, - const QString &encoding) { - auto enn = encoding; - - if (enn.isEmpty() || enn == QStringLiteral("ASCII")) { - enn = QStringLiteral("ISO-8859-1"); - } - -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - auto en = QStringConverter::encodingForName(enn.toUtf8()); - QByteArray unicode; - if (en.has_value()) { - QStringEncoder e(en.value()); - unicode = e.encode(buffer); - } -#else - auto enc = QTextCodec::codecForName(enn.toUtf8()); - auto e = enc->makeEncoder(); - auto unicode = e->fromUnicode(buffer); -#endif - return unicode; -} diff --git a/src/plugin/pluginsystem.h b/src/plugin/pluginsystem.h index 5b21584..6415388 100644 --- a/src/plugin/pluginsystem.h +++ b/src/plugin/pluginsystem.h @@ -121,8 +121,12 @@ public: bool closeEditor(IWingPlugin *plg, int handle, bool force); + void dispatchEvent(IWingPlugin::RegisteredEvent event, + const QVariantList ¶ms); + private: void registerFns(IWingPlugin *plg); + void registerEvents(IWingPlugin *plg); QString type2AngelScriptString(IWingPlugin::MetaType type, bool isArg); @@ -146,8 +150,6 @@ private: EditorView *pluginCurrentEditor(IWingPlugin *sender) const; - QByteArray toByteArray(const QString &buffer, const QString &encoding); - private: template T readBasicTypeContent(IWingPlugin *plg, qsizetype offset) { @@ -228,8 +230,10 @@ private: private: MainWindow *_win = nullptr; - QStringList loadedpuid; - QList loadedplgs; + QStringList _loadedpuid; + QList _loadedplgs; + + QMap> _evplgs; QHash m_plgviewMap; QHash>> diff --git a/src/utilities.h b/src/utilities.h index afaa158..aaf9849 100644 --- a/src/utilities.h +++ b/src/utilities.h @@ -250,6 +250,47 @@ public: } } } + + static QByteArray encodingString(const QString &str, + const QString &enc = {}) { + auto encoding = enc; + if (encoding.isEmpty() || encoding.compare(QStringLiteral("ASCII"), + Qt::CaseInsensitive) == 0) { + encoding = QStringLiteral("ISO-8859-1"); + } + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + auto en = QStringConverter::encodingForName(encoding.toUtf8()); + Q_ASSERT(en.has_value()); + QStringEncoder e(en.value()); + return e.encode(str); +#else + auto en = QTextCodec::codecForName(encoding.toUtf8()); + auto e = en->makeEncoder(); + return e->fromUnicode(str); +#endif + } + + static QString decodingString(const QByteArray &buffer, + const QString &enc = {}) { + auto encoding = enc; + if (encoding.isEmpty() || encoding.compare(QStringLiteral("ASCII"), + Qt::CaseInsensitive) == 0) { + encoding = QStringLiteral("ISO-8859-1"); + } + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + auto en = QStringConverter::encodingForName(encoding.toUtf8()); + Q_ASSERT(en.has_value()); + QStringDecoder dec(en.value()); + + return dec.decode(buffer); +#else + auto en = QTextCodec::codecForName(encoding.toUtf8()); + auto dec = en->makeDecoder(); + return dec->toUnicode(buffer); +#endif + } }; #endif // UTILITIES_H