diff --git a/3rdparty/QHexView/qhexview.h b/3rdparty/QHexView/qhexview.h index d9fe8d2..0f23e70 100644 --- a/3rdparty/QHexView/qhexview.h +++ b/3rdparty/QHexView/qhexview.h @@ -128,7 +128,7 @@ private: signals: void documentChanged(QHexDocument *doc); void cursorLocationChanged(); - void cursorSelectionChanged(); // TODO + void cursorSelectionChanged(); void canUndoChanged(bool canUndo); void canRedoChanged(bool canRedo); void documentSaved(bool saved); diff --git a/TestPlugin/lang/TestPlugin_zh_CN.ts b/TestPlugin/lang/TestPlugin_zh_CN.ts index b7f974f..ea3cb8a 100644 --- a/TestPlugin/lang/TestPlugin_zh_CN.ts +++ b/TestPlugin/lang/TestPlugin_zh_CN.ts @@ -287,28 +287,28 @@ 数据可视化 - - + + UpdateTextTreeError 更新文本树失败 - + UpdateTextListByModelError 通过模型更新文本列表失败 - + UpdateTextTableByModelError 通过模型更新文本表格失败 - + UpdateTextTreeByModelError 通过模型更新文本树失败 - + Choose 选择 diff --git a/TestPlugin/testform.cpp b/TestPlugin/testform.cpp index ba2e81f..71866f2 100644 --- a/TestPlugin/testform.cpp +++ b/TestPlugin/testform.cpp @@ -329,16 +329,19 @@ void TestForm::on_btnGetColor_clicked() { } void TestForm::on_btnText_2_clicked() { - emit _plg->visual.updateText(ui->teDataVisual->toPlainText()); + emit _plg->visual.updateText(ui->teDataVisual->toPlainText(), + QStringLiteral("TestForm")); } void TestForm::on_btnTextList_clicked() { auto txts = ui->teDataVisual->toPlainText().split('\n'); - emit _plg->visual.updateTextList(txts, _click, _dblclick); + emit _plg->visual.updateTextList(txts, QStringLiteral("TestForm"), _click, + _dblclick); } void TestForm::on_btnTextTree_clicked() { auto ret = emit _plg->visual.updateTextTree(ui->teDataVisual->toPlainText(), + QStringLiteral("TestForm"), _click, _dblclick); if (!ret) { emit _plg->msgbox.critical(this, QStringLiteral("Test"), @@ -349,7 +352,8 @@ void TestForm::on_btnTextTree_clicked() { void TestForm::on_btnTextTable_clicked() { auto ret = emit _plg->visual.updateTextTable( ui->teDataVisual->toPlainText(), - {WingHex::WINGSUMMER, WingHex::WINGSUMMER}, {}, _click, _dblclick); + {WingHex::WINGSUMMER, WingHex::WINGSUMMER}, {}, + QStringLiteral("TestForm"), _click, _dblclick); if (!ret) { emit _plg->msgbox.critical(this, QStringLiteral("Test"), tr("UpdateTextTreeError")); @@ -363,8 +367,8 @@ void TestForm::on_btnTextListByModel_clicked() { buffer.append(WingHex::WINGSUMMER % QString::number(i)); } model->setStringList(buffer); - auto ret = - emit _plg->visual.updateTextListByModel(model, _click, _dblclick); + auto ret = emit _plg->visual.updateTextListByModel( + model, QStringLiteral("TestForm"), _click, _dblclick); if (!ret) { emit _plg->msgbox.critical(this, QStringLiteral("Test"), tr("UpdateTextListByModelError")); @@ -373,8 +377,8 @@ void TestForm::on_btnTextListByModel_clicked() { void TestForm::on_btnTextTableByModel_clicked() { auto model = new TestTableModel; - auto ret = - emit _plg->visual.updateTextTableByModel(model, _click, _dblclick); + auto ret = emit _plg->visual.updateTextTableByModel( + model, QStringLiteral("TestForm"), _click, _dblclick); if (!ret) { emit _plg->msgbox.critical(this, QStringLiteral("Test"), tr("UpdateTextTableByModelError")); @@ -384,8 +388,8 @@ void TestForm::on_btnTextTableByModel_clicked() { void TestForm::on_btnTextTreeByModel_clicked() { auto model = new QFileSystemModel; model->setRootPath(QDir::currentPath()); - auto ret = - emit _plg->visual.updateTextTreeByModel(model, _click, _dblclick); + auto ret = emit _plg->visual.updateTextTreeByModel( + model, QStringLiteral("TestForm"), _click, _dblclick); if (!ret) { emit _plg->msgbox.critical(this, QStringLiteral("Test"), tr("UpdateTextTreeByModelError")); diff --git a/lang/zh_CN/winghex.ts b/lang/zh_CN/winghex.ts index 2891a81..d6a146c 100644 --- a/lang/zh_CN/winghex.ts +++ b/lang/zh_CN/winghex.ts @@ -442,7 +442,7 @@ 编码 - + Untitled 未命名 @@ -589,29 +589,29 @@ FindResultModel - + line - + col - + offset 偏移 - + value - + encoding - + 解码 @@ -851,978 +851,982 @@ 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 打开文件出现错误(由于权限不足),如下为打开错误的文件: @@ -2033,92 +2037,127 @@ PluginSystem - + LoadingPlugin 正在加载插件: - + RegisteredFnDup 注册重复函数对象 - + ErrLoadPluginSign 插件加载失败:非法插件签名! - + ErrLoadPluginSDKVersion 插件加载失败:非法插件 SDK 版本! - + ErrLoadPluginNoName 插件加载失败:非法插件名称! - + + ErrLoadInvalidPUID + 插件加载失败:非法插件唯一标志符! + + + ErrLoadLoadedPlugin 插件加载失败:重复加载插件! - + ErrLoadInitPlugin 插件加载失败:初始化插件失败! - + PluginName : 插件名: - + PluginAuthor : 插件作者: - + PluginWidgetRegister 注册插件对象中…… - + EmptyNameDockWidget: 空的贴边组件名: - + InvalidNameDockWidget: 无效贴边组件名: - + InvalidNullDockWidget: 无效空贴边组件: - + [EvilCall] 【恶意调用】 - + Not allowed operation in non-UI thread 该操作在非 UI 线程非法 - + UnsafePluginDir 不安全的插件目录,请将插件目录设置为仅管理员账户可写 - + FoundPluginCount 总计发现插件数目: - + + PluginLoadingFailedSummary + 有依赖插件加载失败总结 + + + + - PluginName: + - 插件名: + + + + - Dependencies: + - 依赖: + + + + PUID: + 插件唯一标志符: + + + + Version: + 版本: + + + + MD5: + MD5 校验和: + + + PluginLoadingFinished 加载插件完毕! @@ -2159,8 +2198,8 @@ QApplication - - + + OptionNeedRestart 该设置需要程序重启后生效 @@ -4840,88 +4879,88 @@ Do you wish to keep up to date by reloading the file? 栈跟踪 - + Symbol 符号 - - - - - - + + + + + + Error 错误 - + Too much opened files 打开的文件过多,无法继续操作! - + ConfirmSave 正在关闭未保存的脚本文件,你确定保存吗? - + ScriptSaveFailedClose 脚本保存失败,你仍确认关闭吗? - - + + ChooseFile 选择文件 - - - + + + FilePermission 因文件权限无法继续! - + ReloadSuccessfully 文件重新加载成功! - + ReloadUnSuccessfully 文件重新加载失败! - + ChooseSaveFile 请选择保存文件路径: - - + + SaveSuccessfully 保存成功! - + SaveUnSuccessfully 保存失败! - + FormatCodeFailed 代码格式化失败 - - + + CannotSave2RunScript 无法保存,故无法继续运行脚本。 - - + + ScriptStillRunning 脚本仍在运行,你确定要退出吗? @@ -5133,12 +5172,22 @@ Do you wish to keep up to date by reloading the file? 为 AngelScript 脚本提供调用主机 API 能力的内部插件。 - + RegisterScriptFnInvalidSig: 因脚本函数签名非法而注册失败: - + + InvalidEnumName: + 非法枚举名: + + + + InvalidEnumValue: + 非法枚举值: + + + NotSupportedQMetaType: 不支持的 QT 数据元类型: diff --git a/src/class/ascompletion.cpp b/src/class/ascompletion.cpp index 573a337..eae1cff 100644 --- a/src/class/ascompletion.cpp +++ b/src/class/ascompletion.cpp @@ -186,7 +186,7 @@ void AsCompletion::complete(const QDocumentCursor &c, const QString &trigger) { return; } } else if (rbegin->content == DOT_TRIGGER) { - // TODO + // TODO only PR } else { applyEmptyNsNode(nodes); } diff --git a/src/class/richtextitemdelegate.cpp b/src/class/richtextitemdelegate.cpp index e2e707a..34ab0e3 100644 --- a/src/class/richtextitemdelegate.cpp +++ b/src/class/richtextitemdelegate.cpp @@ -1,3 +1,20 @@ +/*============================================================================== +** 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 "richtextitemdelegate.h" #include @@ -11,44 +28,64 @@ RichTextItemDelegate::RichTextItemDelegate(QObject *parent) void RichTextItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { - auto options = option; - initStyleOption(&options, index); + if (!index.isValid()) + return; - painter->save(); + // Get the rich text content + QString text = index.data(Qt::DisplayRole).toString(); + if (text.isEmpty()) + return; + // Get alignment + QVariant alignmentVariant = index.data(Qt::TextAlignmentRole); + Qt::Alignment alignment = alignmentVariant.isValid() + ? Qt::Alignment(alignmentVariant.toInt()) + : Qt::AlignLeft; + + // Set up a QTextDocument to render the HTML content QTextDocument doc; - doc.setHtml(options.text); + doc.setHtml(text); - options.text.clear(); - options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, - painter); + // Disable word wrapping + QTextOption textOption; + textOption.setWrapMode(QTextOption::NoWrap); + doc.setDefaultTextOption(textOption); - // 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()); + // Clip the painter to the cell rectangle + painter->save(); + painter->setClipRect(option.rect); - 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); + // Calculate the rendering rectangle based on alignment + QRect rect = option.rect; + QSize contentSize = doc.size().toSize(); + if (alignment & Qt::AlignHCenter) { + rect.setLeft(rect.left() + (rect.width() - contentSize.width()) / 2); + } else if (alignment & Qt::AlignRight) { + rect.setLeft(rect.right() - contentSize.width()); + } - ctx.clip = clip; - doc.documentLayout()->draw(painter, ctx); + if (alignment & Qt::AlignVCenter) { + rect.setTop(rect.top() + (rect.height() - contentSize.height()) / 2); + } else if (alignment & Qt::AlignBottom) { + rect.setTop(rect.bottom() - contentSize.height()); + } + + // Render the document in the adjusted rectangle + painter->translate(rect.topLeft()); + doc.setTextWidth(option.rect.width()); + doc.drawContents(painter); painter->restore(); } QSize RichTextItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { - auto options = option; - initStyleOption(&options, index); + QString text = index.data(Qt::DisplayRole).toString(); + if (text.isEmpty()) + return QStyledItemDelegate::sizeHint(option, index); QTextDocument doc; - doc.setHtml(options.text); - doc.setTextWidth(options.rect.width()); - return QSize(doc.idealWidth(), doc.size().height()); + doc.setHtml(text); + doc.setTextWidth(option.rect.width()); + return doc.size().toSize(); } diff --git a/src/class/richtextitemdelegate.h b/src/class/richtextitemdelegate.h index 571fb4c..3be18ae 100644 --- a/src/class/richtextitemdelegate.h +++ b/src/class/richtextitemdelegate.h @@ -1,3 +1,20 @@ +/*============================================================================== +** 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 RICHTEXTITEMDELEGATE_H #define RICHTEXTITEMDELEGATE_H diff --git a/src/class/scriptconsolemachine.cpp b/src/class/scriptconsolemachine.cpp index 09df21a..319ccc1 100644 --- a/src/class/scriptconsolemachine.cpp +++ b/src/class/scriptconsolemachine.cpp @@ -186,9 +186,6 @@ bool ScriptConsoleMachine::execString(asIScriptEngine *engine, info.message = str; emit onOutput(MessageType::Info, info); return true; - } else if (code.startsWith(QStringLiteral("import "))) { - // TODO - return true; } else { return ExecuteString(engine, code.toUtf8(), mod, immediateContext()) >= 0; diff --git a/src/class/wingangelapi.cpp b/src/class/wingangelapi.cpp index b4f6a02..7fbadc9 100644 --- a/src/class/wingangelapi.cpp +++ b/src/class/wingangelapi.cpp @@ -80,6 +80,17 @@ const QString WingAngelAPI::pluginComment() const { "ability to call the host API."); } +void WingAngelAPI::registerScriptEnums( + const QString &ns, const QHash>> &objs) { + Q_ASSERT(!ns.isEmpty()); + if (objs.isEmpty()) { + return; + } + + // check it later + _objs.insert(ns, objs); +} + void WingAngelAPI::registerScriptFns(const QString &ns, const QHash &rfns) { Q_ASSERT(!ns.isEmpty()); @@ -115,6 +126,7 @@ void WingAngelAPI::installAPI(ScriptMachine *machine) { installDataVisualAPI(engine, stringTypeID); installScriptFns(engine); + installScriptEnums(engine); } void WingAngelAPI::installLogAPI(asIScriptEngine *engine) { @@ -1107,34 +1119,35 @@ void WingAngelAPI::installDataVisualAPI(asIScriptEngine *engine, int stringID) { auto datavis = &this->visual; - registerAPI( + registerAPI( engine, std::bind(&WingHex::WingPlugin::DataVisual::updateText, datavis, - std::placeholders::_1), - "bool updateText(string &in data)"); + std::placeholders::_1, std::placeholders::_2), + "bool updateText(string &in data, string &in title=\"\")"); - registerAPI( + registerAPI( engine, std::bind(&WingAngelAPI::_DataVisual_updateTextList, this, stringID, - std::placeholders::_1), - "bool updateTextList(array &in data)"); + std::placeholders::_1, std::placeholders::_2), + "bool updateTextList(array &in data, string &in title=\"\")"); - registerAPI( + registerAPI( engine, std::bind(&WingHex::WingPlugin::DataVisual::updateTextTree, datavis, - std::placeholders::_1, + std::placeholders::_1, std::placeholders::_2, WingHex::WingPlugin::DataVisual::ClickedCallBack(), WingHex::WingPlugin::DataVisual::DoubleClickedCallBack()), - "bool updateTextTree(string &in json)"); + "bool updateTextTree(string &in json, string &in title=\"\")"); registerAPI( + const CScriptArray &, const QString &)>( engine, std::bind(&WingAngelAPI::_DataVisual_updateTextTable, this, stringID, std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3), + std::placeholders::_3, std::placeholders::_4), "bool updateTextTable(string &in json, array &in headers, " - "array &in headerNames = array())"); + "array &in headerNames = array(), string &in " + "title=\"\")"); engine->SetDefaultNamespace(""); } @@ -1169,6 +1182,40 @@ void WingAngelAPI::installScriptFns(asIScriptEngine *engine) { } } +void WingAngelAPI::installScriptEnums(asIScriptEngine *engine) { + for (auto pobjs = _objs.constKeyValueBegin(); + pobjs != _objs.constKeyValueEnd(); ++pobjs) { + auto ns = pobjs->first; + int r = engine->SetDefaultNamespace(ns.toUtf8()); + Q_ASSERT(r >= 0); + Q_UNUSED(r); + + auto &pobj = pobjs->second; + for (auto p = pobj.constKeyValueBegin(); p != pobj.constKeyValueEnd(); + p++) { + auto en = p->first.toUtf8(); + r = engine->RegisterEnum(en.data()); + if (r < 0) { + emit warn(tr("InvalidEnumName:") + p->first); + continue; + } + + for (auto &e : p->second) { + auto ev = e.first.toUtf8(); + r = engine->RegisterEnumValue(en.data(), ev.data(), e.second); + if (r < 0) { + emit warn(tr("InvalidEnumValue:") % p->first % + QStringLiteral("::") % e.first % + QStringLiteral(" (") % QString::number(e.second) % + QStringLiteral(")")); + continue; + } + } + } + engine->SetDefaultNamespace(""); + } +} + QStringList WingAngelAPI::cArray2QStringList(const CScriptArray &array, int stringID, bool *ok) { bool b = array.GetElementTypeId() == stringID; @@ -1653,7 +1700,8 @@ void WingAngelAPI::script_call(asIScriptGeneric *gen) { std::bind(op, gen, std::placeholders::_1, std::placeholders::_2)); } -bool WingAngelAPI::execScriptCode(const QString &code) { +bool WingAngelAPI::execScriptCode(const WingHex::SenderInfo &sender, + const QString &code) { if (code.isEmpty()) { return true; } @@ -1666,9 +1714,7 @@ bool WingAngelAPI::execScriptCode(const QString &code) { } _console->setMode(ScriptingConsole::Output); - _console->write(QStringLiteral("(") % - property("__LAST_CALLER__").toString() % - QStringLiteral(") ")); + _console->write(getSenderHeader(sender)); _console->machine()->executeScript(f.fileName()); _console->appendCommandPrompt(); _console->setMode(ScriptingConsole::Input); @@ -1677,28 +1723,31 @@ bool WingAngelAPI::execScriptCode(const QString &code) { return false; } -bool WingAngelAPI::execScript(const QString &fileName) { +bool WingAngelAPI::execScript(const WingHex::SenderInfo &sender, + const QString &fileName) { _console->setMode(ScriptingConsole::Output); - _console->write(QStringLiteral("(") % - property("__LAST_CALLER__").toString() % - QStringLiteral(") ")); + _console->write(getSenderHeader(sender)); auto ret = _console->machine()->executeScript(fileName); _console->appendCommandPrompt(); _console->setMode(ScriptingConsole::Input); return ret; } -bool WingAngelAPI::execCode(const QString &code) { +bool WingAngelAPI::execCode(const WingHex::SenderInfo &sender, + const QString &code) { _console->setMode(ScriptingConsole::Output); - _console->write(QStringLiteral("(") % - property("__LAST_CALLER__").toString() % - QStringLiteral(") ")); + _console->write(getSenderHeader(sender)); auto ret = _console->machine()->executeCode(code); _console->appendCommandPrompt(); _console->setMode(ScriptingConsole::Input); return ret; } +QString WingAngelAPI::getSenderHeader(const WingHex::SenderInfo &sender) { + return QStringLiteral("(") % sender.puid % QStringLiteral("::") % + sender.plgcls % QStringLiteral(") "); +} + QString WingAngelAPI::_InputBox_getItem(int stringID, const QString &title, const QString &label, const CScriptArray &items, int current, @@ -1923,25 +1972,28 @@ bool WingAngelAPI::_HexController_appendBytes(const CScriptArray &ba) { } bool WingAngelAPI::_DataVisual_updateTextList(int stringID, - const CScriptArray &data) { + const CScriptArray &data, + const QString &title) { bool o = false; auto ret = cArray2QStringList(data, stringID, &o); if (o) { - return emit visual.updateTextList(ret); + return emit visual.updateTextList(ret, title); } else { return false; } } -bool WingAngelAPI::_DataVisual_updateTextTable( - int stringID, const QString &json, const CScriptArray &headers, - const CScriptArray &headerNames) { +bool WingAngelAPI::_DataVisual_updateTextTable(int stringID, + const QString &json, + const CScriptArray &headers, + const CScriptArray &headerNames, + const QString &title) { bool o = false; auto h = cArray2QStringList(headers, stringID, &o); if (o) { auto hn = cArray2QStringList(headerNames, stringID, &o); if (o) { - return emit visual.updateTextTable(json, h, hn); + return emit visual.updateTextTable(json, h, hn, title); } else { return false; } diff --git a/src/class/wingangelapi.h b/src/class/wingangelapi.h index 461b590..5ece4dd 100644 --- a/src/class/wingangelapi.h +++ b/src/class/wingangelapi.h @@ -52,6 +52,10 @@ public: registerScriptFns(const QString &ns, const QHash &rfns); + void + registerScriptEnums(const QString &ns, + const QHash>> &objs); + void installAPI(ScriptMachine *machine); ScriptingConsole *bindingConsole() const; @@ -69,6 +73,7 @@ private: void installHexControllerAPI(asIScriptEngine *engine); void installDataVisualAPI(asIScriptEngine *engine, int stringID); void installScriptFns(asIScriptEngine *engine); + void installScriptEnums(asIScriptEngine *engine); private: template @@ -122,9 +127,14 @@ 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); + WING_SERVICE bool execScriptCode(const WingHex::SenderInfo &sender, + const QString &code); + WING_SERVICE bool execScript(const WingHex::SenderInfo &sender, + const QString &fileName); + WING_SERVICE bool execCode(const WingHex::SenderInfo &sender, + const QString &code); + + QString getSenderHeader(const WingHex::SenderInfo &sender); private: QString _InputBox_getItem(int stringID, const QString &title, @@ -164,11 +174,13 @@ private: bool _HexController_appendBytes(const CScriptArray &ba); - bool _DataVisual_updateTextList(int stringID, const CScriptArray &data); + bool _DataVisual_updateTextList(int stringID, const CScriptArray &data, + const QString &title); bool _DataVisual_updateTextTable(int stringID, const QString &json, const CScriptArray &headers, - const CScriptArray &headerNames); + const CScriptArray &headerNames, + const QString &title); private: std::vector _fnbuffer; @@ -177,6 +189,7 @@ private: ScriptingConsole *_console = nullptr; QHash> _rfns; + QHash>>> _objs; }; #endif // WINGANGELAPI_H diff --git a/src/control/editorview.cpp b/src/control/editorview.cpp index e9f0591..654d60c 100644 --- a/src/control/editorview.cpp +++ b/src/control/editorview.cpp @@ -210,26 +210,20 @@ EditorView::FindError EditorView::find(const FindDialog::Result &result) { m_findResults->clear(); auto lineWidth = m_hex->renderer()->hexLineWidth(); + auto docLen = d->length(); for (auto &ritem : results) { FindResult r; 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->findData().append( + readContextFinding(ritem, data.size(), FIND_CONTEXT_SIZE, + FIND_MAX_DISPLAY_FIND_CHARS)); } + m_findResults->lastFindData() = data; + m_findResults->endUpdate(); if (m_findResults->size() == @@ -612,6 +606,37 @@ QHash EditorView::savePluginData() { return ret; } +FindResultModel::FindInfo EditorView::readContextFinding(qsizetype offset, + qsizetype findSize, + int contextSize, + int maxDisplayBytes) { + auto doc = m_hex->document(); + + qsizetype halfSize = maxDisplayBytes / 2; + auto header = doc->read(offset, qMin(findSize, halfSize)); + QByteArray tailer; + if (header.size() < findSize) { + tailer = doc->read( + offset, qMin(findSize, qsizetype(maxDisplayBytes) - halfSize)); + } + + auto left = qsizetype(maxDisplayBytes) - header.size() - tailer.size(); + + // append to contextSize + contextSize += (left / 2); + + auto cheader = doc->read(offset - contextSize, contextSize); + auto ctailer = doc->read(offset + findSize, contextSize); + + FindResultModel::FindInfo info; + info.cheader = cheader; + info.hbuffer = header; + info.tbuffer = tailer; + info.ctailer = ctailer; + + return info; +} + EditorView *EditorView::cloneParent() const { return m_cloneParent; } bool EditorView::isCloned() const { return m_cloneParent != nullptr; } diff --git a/src/control/editorview.h b/src/control/editorview.h index 41842fb..9b2caa2 100644 --- a/src/control/editorview.h +++ b/src/control/editorview.h @@ -125,6 +125,11 @@ private: void applyPluginData(const QHash &data); QHash savePluginData(); + FindResultModel::FindInfo readContextFinding(qsizetype offset, + qsizetype findSize, + int contextSize, + int maxDisplayBytes); + private: template inline void newAction(QWidget *parent, const QString &icon, diff --git a/src/dialog/finddialog.cpp b/src/dialog/finddialog.cpp index a0490f9..f529cf9 100644 --- a/src/dialog/finddialog.cpp +++ b/src/dialog/finddialog.cpp @@ -67,14 +67,14 @@ FindDialog::FindDialog(const FindInfo &info, QWidget *parent) if (info.isStringFind) { m_string->setChecked(true); m_lineeditor->setEnabled(true); - m_hex->setEnabled(false); + m_hexeditor->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_hexeditor->setEnabled(true); } m_lineeditor->setText(info.str); diff --git a/src/dialog/mainwindow.cpp b/src/dialog/mainwindow.cpp index e7c87bf..9c597ed 100644 --- a/src/dialog/mainwindow.cpp +++ b/src/dialog/mainwindow.cpp @@ -24,6 +24,7 @@ #include "aboutsoftwaredialog.h" #include "checksumdialog.h" #include "class/appmanager.h" +#include "class/eventfilter.h" #include "class/langservice.h" #include "class/languagemanager.h" #include "class/layoutmanager.h" @@ -480,6 +481,7 @@ MainWindow::buildUpFindResultDock(ads::CDockManager *dock, m_findresult->setProperty("EditorView", quintptr(0)); Utilities::applyTableViewProperty(m_findresult); + auto header = m_findresult->horizontalHeader(); m_findresult->setContextMenuPolicy( Qt::ContextMenuPolicy::ActionsContextMenu); @@ -494,6 +496,9 @@ MainWindow::buildUpFindResultDock(ads::CDockManager *dock, m_findresult->setModel(_findEmptyResult); + header->setSectionResizeMode(3, QHeaderView::Stretch); + header->setSectionResizeMode(4, QHeaderView::Stretch); + connect(m_findresult, &QTableView::doubleClicked, this, [=](const QModelIndex &index) { auto editor = @@ -779,6 +784,29 @@ MainWindow::buildUpVisualDataDock(ads::CDockManager *dock, ads::CDockAreaWidget *areaw) { using namespace ads; + auto efilter = new EventFilter(QEvent::DynamicPropertyChange, this); + connect(efilter, &EventFilter::eventTriggered, this, + [this](QObject *obj, QEvent *event) { + auto e = static_cast(event); + constexpr auto ppname = "__TITLE__"; + if (e->propertyName() == QByteArray(ppname)) { + auto title = obj->property(ppname).toString(); + auto display = obj->property("__DISPLAY__").toString(); + auto dock = reinterpret_cast( + obj->property("__DOCK__").value()); + if (dock) { + if (!title.isEmpty()) { + display += QStringLiteral("(") % title % + QStringLiteral(")"); + } + dock->setWindowTitle(display); + } + } + }); + + constexpr auto dpname = "__DISPLAY__"; + constexpr auto dockpname = "__DOCK__"; + m_infolist = new QListView(this); m_infolist->setEditTriggers(QListView::EditTrigger::NoEditTriggers); connect(m_infolist, &QListView::clicked, this, @@ -795,6 +823,10 @@ MainWindow::buildUpVisualDataDock(ads::CDockManager *dock, }); auto dw = buildDockWidget(dock, QStringLiteral("DVList"), tr("DVList"), m_infolist); + m_infolist->setProperty(dpname, tr("DVList")); + m_infolist->setProperty(dockpname, quintptr(dw)); + m_infolist->installEventFilter(efilter); + auto ar = dock->addDockWidget(area, dw, areaw); m_infotree = new QTreeView(this); @@ -813,6 +845,9 @@ MainWindow::buildUpVisualDataDock(ads::CDockManager *dock, }); dw = buildDockWidget(dock, QStringLiteral("DVTree"), tr("DVTree"), m_infotree); + m_infotree->setProperty(dpname, tr("DVTree")); + m_infotree->setProperty(dockpname, quintptr(dw)); + m_infotree->installEventFilter(efilter); dock->addDockWidget(CenterDockWidgetArea, dw, ar); m_infotable = new QTableView(this); @@ -831,11 +866,17 @@ MainWindow::buildUpVisualDataDock(ads::CDockManager *dock, }); dw = buildDockWidget(dock, QStringLiteral("DVTable"), tr("DVTable"), m_infotable); + m_infotable->setProperty(dpname, tr("DVTable")); + m_infotable->setProperty(dockpname, quintptr(dw)); + m_infotable->installEventFilter(efilter); dock->addDockWidget(CenterDockWidgetArea, dw, ar); m_infotxt = new QTextBrowser(this); dw = buildDockWidget(dock, QStringLiteral("DVText"), tr("DVText"), m_infotxt); + m_infotxt->setProperty(dpname, tr("DVText")); + m_infotxt->setProperty(dockpname, quintptr(dw)); + m_infotxt->installEventFilter(efilter); dock->addDockWidget(CenterDockWidgetArea, dw, ar); return ar; @@ -2270,8 +2311,8 @@ void MainWindow::on_exportfindresult() { tr("EmptyFindResult")); return; } - auto filename = WingFileDialog::getSaveFileName(this, tr("ChooseSaveFile"), - m_lastusedpath); + auto filename = WingFileDialog::getSaveFileName( + this, tr("ChooseSaveFile"), m_lastusedpath, {"Json (*.json)"}); if (filename.isEmpty()) return; m_lastusedpath = QFileInfo(filename).absoluteDir().absolutePath(); @@ -2281,9 +2322,10 @@ void MainWindow::on_exportfindresult() { QJsonObject fobj; fobj.insert(QStringLiteral("file"), editor->fileName()); - // auto d= findresitem->lastFindData(); + auto d = findresitem->lastFindData(); - // fobj.insert(QStringLiteral("data"), findresitem->lastFindData()); + fobj.insert(QStringLiteral("find"), + QString::fromLatin1(d.toHex(' ').toUpper())); QJsonArray arr; for (int i = 0; i < c; i++) { auto data = findresitem->resultAt(i); @@ -2291,6 +2333,18 @@ void MainWindow::on_exportfindresult() { jobj.insert(QStringLiteral("line"), QString::number(data.line)); jobj.insert(QStringLiteral("col"), QString::number(data.col)); jobj.insert(QStringLiteral("offset"), QString::number(data.offset)); + + QTextDocument doc; + doc.setHtml( + findresitem->data(findresitem->index(i, 3), Qt::DisplayRole) + .toString()); + jobj.insert(QStringLiteral("range"), doc.toPlainText()); + + doc.setHtml( + findresitem->data(findresitem->index(i, 4), Qt::DisplayRole) + .toString()); + jobj.insert(QStringLiteral("encoding"), doc.toPlainText()); + arr.append(jobj); } fobj.insert(QStringLiteral("data"), arr); diff --git a/src/dialog/scriptingdialog.cpp b/src/dialog/scriptingdialog.cpp index 919fcc1..7e5bd3b 100644 --- a/src/dialog/scriptingdialog.cpp +++ b/src/dialog/scriptingdialog.cpp @@ -629,6 +629,7 @@ ScriptingDialog::buildSymbolShowDock(ads::CDockManager *dock, ads::CDockAreaWidget *areaw) { Q_ASSERT(m_consoleout); m_sym = new ASObjTreeWidget(this); + m_sym->header()->setSectionResizeMode(QHeaderView::Stretch); auto dw = buildDockWidget(dock, QStringLiteral("Symbol"), tr("Symbol"), m_sym); return dock->addDockWidget(area, dw, areaw); diff --git a/src/model/findresultmodel.cpp b/src/model/findresultmodel.cpp index de02ec0..47d594e 100644 --- a/src/model/findresultmodel.cpp +++ b/src/model/findresultmodel.cpp @@ -16,9 +16,10 @@ */ #include "findresultmodel.h" +#include "utilities.h" FindResultModel::FindResultModel(QObject *parent) - : QAbstractTableModel(parent) {} + : QAbstractTableModel(parent), m_encoding(QStringLiteral("ASCII")) {} int FindResultModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); @@ -43,15 +44,53 @@ QVariant FindResultModel::data(const QModelIndex &index, int role) const { case 2: // offset return QStringLiteral("0x") + QString::number(r.offset, 16).toUpper(); - case 3: // range - return m_lastFindData.at(row).findRange; - case 4: // decoding - return m_lastFindData.at(row).decoding; + case 3: { + // range + auto data = m_findData.at(row); + QString buffer = + data.cheader.toHex(' ').toUpper() % QStringLiteral(" "); + if (!data.hbuffer.isEmpty()) { + buffer += data.hbuffer.toHex(' ').toUpper(); + if (!data.tbuffer.isEmpty()) { + buffer += QStringLiteral(" .. "); + } + } + + buffer += data.tbuffer.toHex(' ').toUpper() % + QStringLiteral(" ") % + data.ctailer.toHex(' ').toUpper(); + + return buffer; + } + case 4: { // decoding + auto data = m_findData.at(row); + QString buffer = + Utilities::decodingString(data.cheader, m_encoding) % + QStringLiteral(" "); + if (!data.hbuffer.isEmpty()) { + buffer += Utilities::decodingString(data.hbuffer); + if (!data.tbuffer.isEmpty()) { + buffer += QStringLiteral(" ... "); + } + } + + buffer += Utilities::decodingString(data.tbuffer) % + QStringLiteral(" ") % + Utilities::decodingString(data.ctailer); + + return buffer; + } } return QVariant(); } case Qt::TextAlignmentRole: - return Qt::AlignCenter; + switch (index.column()) { + case 3: + case 4: + return Qt::AlignLeft; + default: + return Qt::AlignCenter; + } } return QVariant(); } @@ -79,12 +118,20 @@ QVariant FindResultModel::headerData(int section, Qt::Orientation orientation, return QVariant(); } +QString FindResultModel::encoding() const { return m_encoding; } + +void FindResultModel::setEncoding(const QString &newEncoding) { + m_encoding = newEncoding; +} + QList &FindResultModel::results() { return m_results; } -QList &FindResultModel::lastFindData() { - return m_lastFindData; +QList &FindResultModel::findData() { + return m_findData; } +QByteArray &FindResultModel::lastFindData() { return m_lastFindData; } + void FindResultModel::beginUpdate() { this->beginResetModel(); } void FindResultModel::endUpdate() { this->endResetModel(); } @@ -95,7 +142,7 @@ WingHex::FindResult FindResultModel::resultAt(qsizetype index) const { void FindResultModel::clear() { m_results.clear(); - m_lastFindData.clear(); + m_findData.clear(); } QList::size_type FindResultModel::size() const { diff --git a/src/model/findresultmodel.h b/src/model/findresultmodel.h index 301b281..a549ca9 100644 --- a/src/model/findresultmodel.h +++ b/src/model/findresultmodel.h @@ -24,17 +24,21 @@ class FindResultModel : public QAbstractTableModel { Q_OBJECT + public: struct FindInfo { - QString findRange; - QString decoding; + QByteArray cheader; + QByteArray hbuffer; + QByteArray tbuffer; + QByteArray ctailer; }; public: explicit FindResultModel(QObject *parent = nullptr); QList &results(); - QList &lastFindData(); + QList &findData(); + QByteArray &lastFindData(); void beginUpdate(); void endUpdate(); @@ -51,9 +55,15 @@ public: virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QString encoding() const; + void setEncoding(const QString &newEncoding); + private: QList m_results; - QList m_lastFindData; + QList m_findData; + QByteArray m_lastFindData; + + QString m_encoding; }; #endif // FINDRESULTMODEL_H diff --git a/src/plugin/iwingplugin.h b/src/plugin/iwingplugin.h index 79e49ba..09a6139 100644 --- a/src/plugin/iwingplugin.h +++ b/src/plugin/iwingplugin.h @@ -401,30 +401,32 @@ public: typedef ClickedCallBack DoubleClickedCallBack; signals: - bool updateText(const QString &data); - bool updateTextList(const QStringList &data, ClickedCallBack clicked = {}, + bool updateText(const QString &data, const QString &title = {}); + bool updateTextList(const QStringList &data, const QString &title = {}, + ClickedCallBack clicked = {}, DoubleClickedCallBack dblClicked = {}); Q_REQUIRED_RESULT bool - updateTextTree(const QString &json, ClickedCallBack clicked = {}, + updateTextTree(const QString &json, const QString &title = {}, + ClickedCallBack clicked = {}, DoubleClickedCallBack dblClicked = {}); Q_REQUIRED_RESULT bool updateTextTable(const QString &json, const QStringList &headers, const QStringList &headerNames = {}, - ClickedCallBack clicked = {}, + const QString &title = {}, ClickedCallBack clicked = {}, DoubleClickedCallBack dblClicked = {}); // API for Qt Plugin Only Q_REQUIRED_RESULT bool - updateTextListByModel(QAbstractItemModel *model, + updateTextListByModel(QAbstractItemModel *model, const QString &title = {}, ClickedCallBack clicked = {}, DoubleClickedCallBack dblClicked = {}); Q_REQUIRED_RESULT bool - updateTextTableByModel(QAbstractItemModel *model, + updateTextTableByModel(QAbstractItemModel *model, const QString &title = {}, ClickedCallBack clicked = {}, DoubleClickedCallBack dblClicked = {}); Q_REQUIRED_RESULT bool - updateTextTreeByModel(QAbstractItemModel *model, + updateTextTreeByModel(QAbstractItemModel *model, const QString &title = {}, ClickedCallBack clicked = {}, DoubleClickedCallBack dblClicked = {}); }; @@ -494,7 +496,13 @@ signals: struct WingDependency { QString puid; uint version; - QString md5; // optional, but recommend + QByteArray md5; // optional, but recommend +}; + +struct SenderInfo { + QString plgcls; + QString puid; + QVariant meta; }; #ifdef WING_SERVICE @@ -602,8 +610,9 @@ public: return {}; } - // QHash< obj-names, decl-members > - virtual QHash registeredScriptObjs() const { + // QHash< enum , members > + virtual QHash>> + registeredScriptEnums() const { return {}; } @@ -655,8 +664,7 @@ signals: QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), - QGenericArgument val8 = QGenericArgument(), - QGenericArgument val9 = QGenericArgument()); + QGenericArgument val8 = QGenericArgument()); public: inline bool invokeService(const QString &puid, const char *member, @@ -669,11 +677,10 @@ public: QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), - QGenericArgument val8 = QGenericArgument(), - QGenericArgument val9 = QGenericArgument()) { + QGenericArgument val8 = QGenericArgument()) { return emit invokeService(puid, member, Qt::AutoConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, - val8, val9); + val8); } inline bool invokeService(const QString &puid, const char *member, @@ -685,11 +692,10 @@ public: QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), - QGenericArgument val8 = QGenericArgument(), - QGenericArgument val9 = QGenericArgument()) { + QGenericArgument val8 = QGenericArgument()) { return emit invokeService(puid, member, type, QGenericReturnArgument(), val0, val1, val2, val3, val4, val5, val6, - val7, val8, val9); + val7, val8); } inline bool invokeService(const QString &puid, const char *member, @@ -701,11 +707,10 @@ public: QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), - QGenericArgument val8 = QGenericArgument(), - QGenericArgument val9 = QGenericArgument()) { + QGenericArgument val8 = QGenericArgument()) { return emit invokeService(puid, member, Qt::AutoConnection, QGenericReturnArgument(), val0, val1, val2, - val3, val4, val5, val6, val7, val8, val9); + val3, val4, val5, val6, val7, val8); } public: @@ -720,6 +725,7 @@ public: } // namespace WingHex +Q_DECLARE_METATYPE(WingHex::SenderInfo) Q_DECLARE_INTERFACE(WingHex::IWingPlugin, "com.wingsummer.iwingplugin") #endif // IWINGPLUGIN_H diff --git a/src/plugin/pluginsystem.cpp b/src/plugin/pluginsystem.cpp index fb848d8..331ee66 100644 --- a/src/plugin/pluginsystem.cpp +++ b/src/plugin/pluginsystem.cpp @@ -68,15 +68,14 @@ void PluginSystem::loadPlugin(const QFileInfo &fileinfo, const QDir &setdir) { Q_ASSERT(_win); if (fileinfo.exists()) { - QPluginLoader loader(fileinfo.absoluteFilePath(), this); + auto fileName = fileinfo.absoluteFilePath(); + QPluginLoader loader(fileName, this); Logger::info(tr("LoadingPlugin") + fileinfo.fileName()); auto p = qobject_cast(loader.instance()); if (Q_UNLIKELY(p == nullptr)) { Logger::critical(loader.errorString()); } else { - if (!loadPlugin(p, setdir)) { - loader.unload(); - } + loadPlugin(p, fileName, setdir); } Logger::_log(""); } @@ -299,6 +298,17 @@ void PluginSystem::registerFns(IWingPlugin *plg) { _angelplg->registerScriptFns(plg->metaObject()->className(), rfns); } +void PluginSystem::registerEnums(IWingPlugin *plg) { + Q_ASSERT(plg); + auto objs = plg->registeredScriptEnums(); + if (objs.isEmpty()) { + return; + } + + Q_ASSERT(_angelplg); + _angelplg->registerScriptEnums(plg->metaObject()->className(), objs); +} + void PluginSystem::registerEvents(IWingPlugin *plg) { Q_ASSERT(plg); auto evs = plg->registeredEvents(); @@ -445,15 +455,35 @@ QString PluginSystem::getScriptFnSig(const QString &fnName, QString PluginSystem::getPUID(IWingPlugin *p) { if (p) { + constexpr auto puid_limit = 36; // same as uuid length, so enough auto prop = p->property("puid").toString().trimmed(); + if (prop.length() > puid_limit) { + return {}; + } auto pid = QString(p->metaObject()->className()); + if (pid.length() > puid_limit) { + return {}; + } return prop.isEmpty() ? pid : prop; } else { return {}; } } -bool PluginSystem::loadPlugin(IWingPlugin *p, +bool PluginSystem::isPluginLoaded(const WingDependency &d) { + for (auto &info : _loadedplginfo) { + if (info.version >= d.version && info.puid == d.puid) { + if (d.md5.isEmpty()) { + return true; + } else { + return d.md5.compare(info.md5, Qt::CaseInsensitive) == 0; + } + } + } + return false; +} + +void PluginSystem::loadPlugin(IWingPlugin *p, const QString &fileName, const std::optional &setdir) { QTranslator *p_tr = nullptr; @@ -471,8 +501,25 @@ bool PluginSystem::loadPlugin(IWingPlugin *p, } auto puid = getPUID(p); - if (_loadedpuid.contains(puid)) { - throw tr("ErrLoadLoadedPlugin"); + if (puid.isEmpty()) { + throw tr("ErrLoadInvalidPUID"); + } + + for (auto &uid : _loadedplginfo) { + if (uid.puid == puid) { + throw tr("ErrLoadLoadedPlugin"); + } + } + + // check dependencise + auto dps = p->dependencies(); + if (!dps.isEmpty()) { + for (auto &d : dps) { + if (!isPluginLoaded(d)) { + _lazyplgs.append(qMakePair(p, fileName)); + return; + } + } } emit pluginLoading(p->pluginName()); @@ -496,8 +543,14 @@ bool PluginSystem::loadPlugin(IWingPlugin *p, } } - _loadedplgs.push_back(p); - _loadedpuid << puid; + _loadedplgs.append(p); + + WingDependency de; + de.puid = puid; + de.version = p->pluginVersion(); + de.md5 = Utilities::getMd5(fileName); + + _loadedplginfo.append(de); Logger::warning(tr("PluginName :") + p->pluginName()); Logger::warning(tr("PluginAuthor :") + p->pluginAuthor()); @@ -665,6 +718,7 @@ bool PluginSystem::loadPlugin(IWingPlugin *p, } registerFns(p); + registerEnums(p); registerEvents(p); connectInterface(p); @@ -674,9 +728,7 @@ bool PluginSystem::loadPlugin(IWingPlugin *p, if (p_tr) { p_tr->deleteLater(); } - return false; } - return true; } void PluginSystem::connectInterface(IWingPlugin *plg) { @@ -738,22 +790,22 @@ void PluginSystem::connectBaseInterface(IWingPlugin *plg) { QGenericReturnArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, - QGenericArgument, QGenericArgument>::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 { + QGenericArgument val7, QGenericArgument val8) -> 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(); + + auto obj = *r; + auto meta = obj->metaObject(); // filter the evil call and report to log QVarLengthArray sig; @@ -762,10 +814,13 @@ void PluginSystem::connectBaseInterface(IWingPlugin *plg) { 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()}; + + auto sname = QMetaType::fromType().name(); + + const char *typeNames[] = {ret.name(), sname, val0.name(), + val1.name(), val2.name(), val3.name(), + val4.name(), val5.name(), val6.name(), + val7.name(), val8.name()}; size_t paramCount; constexpr auto maxParamCount = sizeof(typeNames) / sizeof(const char *); @@ -803,10 +858,26 @@ void PluginSystem::connectBaseInterface(IWingPlugin *plg) { 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); + meta = plg->metaObject(); + SenderInfo info; + info.plgcls = meta->className(); + info.puid = getPUID(plg); + + auto meta_name = "WING_META"; + // property first + auto var = plg->property(meta_name); + if (var.isValid()) { + info.meta = var; + } else { + auto iidx = meta->indexOfClassInfo(meta_name); + if (iidx >= 0) { + info.meta = QString(meta->classInfo(iidx).value()); + } + } + + return m.invoke(obj, type, ret, + WINGAPI_ARG(WingHex::SenderInfo, info), val0, val1, + val2, val3, val4, val5, val6, val7, val8); }); } @@ -2152,13 +2223,14 @@ void PluginSystem::connectUIInterface(IWingPlugin *plg) { auto visual = &plg->visual; connect(visual, &WingPlugin::DataVisual::updateText, _win, - [=](const QString &txt) -> bool { + [=](const QString &txt, const QString &title) -> bool { + _win->m_infotxt->setProperty("__TITLE__", title); _win->m_infotxt->setText(txt); return true; }); connect( visual, &WingPlugin::DataVisual::updateTextList, _win, - [=](const QStringList &data, + [=](const QStringList &data, const QString &title, WingHex::WingPlugin::DataVisual::ClickedCallBack clicked, WingHex::WingPlugin::DataVisual::DoubleClickedCallBack dblClicked) -> bool { @@ -2166,6 +2238,7 @@ void PluginSystem::connectUIInterface(IWingPlugin *plg) { if (oldmodel) { oldmodel->deleteLater(); } + _win->m_infolist->setProperty("__TITLE__", title); auto model = new QStringListModel(data); _win->m_infolist->setModel(model); _win->m_infoclickfn = clicked; @@ -2174,7 +2247,7 @@ void PluginSystem::connectUIInterface(IWingPlugin *plg) { }); connect( visual, &WingPlugin::DataVisual::updateTextListByModel, _win, - [=](QAbstractItemModel *model, + [=](QAbstractItemModel *model, const QString &title, WingHex::WingPlugin::DataVisual::ClickedCallBack clicked, WingHex::WingPlugin::DataVisual::DoubleClickedCallBack dblClicked) -> bool { @@ -2183,6 +2256,7 @@ void PluginSystem::connectUIInterface(IWingPlugin *plg) { if (oldmodel) { oldmodel->deleteLater(); } + _win->m_infolist->setProperty("__TITLE__", title); _win->m_infolist->setModel(model); _win->m_infoclickfn = clicked; _win->m_infodblclickfn = dblClicked; @@ -2192,7 +2266,7 @@ void PluginSystem::connectUIInterface(IWingPlugin *plg) { }); connect( visual, &WingPlugin::DataVisual::updateTextTree, _win, - [=](const QString &json, + [=](const QString &json, const QString &title, WingHex::WingPlugin::DataVisual::ClickedCallBack clicked, WingHex::WingPlugin::DataVisual::DoubleClickedCallBack dblClicked) -> bool { @@ -2200,6 +2274,7 @@ void PluginSystem::connectUIInterface(IWingPlugin *plg) { if (oldmodel) { oldmodel->deleteLater(); } + _win->m_infotree->setProperty("__TITLE__", title); auto model = new QJsonModel; if (model->loadJson(json.toUtf8())) { _win->m_infotree->setModel(model); @@ -2211,7 +2286,7 @@ void PluginSystem::connectUIInterface(IWingPlugin *plg) { }); connect( visual, &WingPlugin::DataVisual::updateTextTreeByModel, _win, - [=](QAbstractItemModel *model, + [=](QAbstractItemModel *model, const QString &title, WingHex::WingPlugin::DataVisual::ClickedCallBack clicked, WingHex::WingPlugin::DataVisual::DoubleClickedCallBack dblClicked) -> bool { @@ -2220,6 +2295,7 @@ void PluginSystem::connectUIInterface(IWingPlugin *plg) { if (oldmodel) { oldmodel->deleteLater(); } + _win->m_infotree->setProperty("__TITLE__", title); _win->m_infotree->setModel(model); _win->m_infotreeclickfn = clicked; _win->m_infotreedblclickfn = dblClicked; @@ -2230,7 +2306,7 @@ void PluginSystem::connectUIInterface(IWingPlugin *plg) { connect( visual, &WingPlugin::DataVisual::updateTextTable, _win, [=](const QString &json, const QStringList &headers, - const QStringList &headerNames, + const QStringList &headerNames, const QString &title, WingHex::WingPlugin::DataVisual::ClickedCallBack clicked, WingHex::WingPlugin::DataVisual::DoubleClickedCallBack dblClicked) -> bool { @@ -2257,7 +2333,7 @@ void PluginSystem::connectUIInterface(IWingPlugin *plg) { header.append(heading); } } - + _win->m_infotable->setProperty("__TITLE__", title); auto model = new QJsonTableModel(header); model->setJson(QJsonDocument::fromJson(json.toUtf8())); _win->m_infotable->setModel(model); @@ -2267,7 +2343,7 @@ void PluginSystem::connectUIInterface(IWingPlugin *plg) { }); connect( visual, &WingPlugin::DataVisual::updateTextTableByModel, _win, - [=](QAbstractItemModel *model, + [=](QAbstractItemModel *model, const QString &title, WingHex::WingPlugin::DataVisual::ClickedCallBack clicked, WingHex::WingPlugin::DataVisual::DoubleClickedCallBack dblClicked) -> bool { @@ -2276,6 +2352,7 @@ void PluginSystem::connectUIInterface(IWingPlugin *plg) { if (oldmodel) { oldmodel->deleteLater(); } + _win->m_infotable->setProperty("__TITLE__", title); _win->m_infotable->setModel(model); _win->m_infotableclickfn = clicked; _win->m_infotabledblclickfn = dblClicked; @@ -2313,9 +2390,7 @@ void PluginSystem::LoadPlugin() { if (set.scriptEnabled()) { _angelplg = new WingAngelAPI; - auto ret = loadPlugin(_angelplg, std::nullopt); - Q_ASSERT(ret); - Q_UNUSED(ret); + loadPlugin(_angelplg, {}, std::nullopt); } bool ok; @@ -2370,6 +2445,33 @@ void PluginSystem::LoadPlugin() { loadPlugin(item, udir); } + if (!_lazyplgs.isEmpty()) { + decltype(_lazyplgs) lazyplgs; + lazyplgs.swap(_lazyplgs); + + for (auto &item : lazyplgs) { + loadPlugin(item.first, item.second, udir); + } + } + + if (!_lazyplgs.isEmpty()) { + Logger::critical(tr("PluginLoadingFailedSummary")); + Logger::_log({}); + for (auto &lplg : _lazyplgs) { + auto plg = lplg.first; + Logger::critical(tr("- PluginName:") + plg->pluginName()); + Logger::critical(tr("- Dependencies:")); + for (auto &d : plg->dependencies()) { + Logger::critical(QString(4, ' ') + tr("PUID:") + d.puid); + Logger::critical(QString(4, ' ') + tr("Version:") + + QString::number(d.version)); + Logger::critical(QString(4, ' ') + tr("MD5:") + d.md5); + } + plg->deleteLater(); + } + _lazyplgs.clear(); + } + Logger::info(tr("PluginLoadingFinished")); } diff --git a/src/plugin/pluginsystem.h b/src/plugin/pluginsystem.h index 6415388..9da89ea 100644 --- a/src/plugin/pluginsystem.h +++ b/src/plugin/pluginsystem.h @@ -126,6 +126,7 @@ public: private: void registerFns(IWingPlugin *plg); + void registerEnums(IWingPlugin *plg); void registerEvents(IWingPlugin *plg); QString type2AngelScriptString(IWingPlugin::MetaType type, bool isArg); @@ -135,8 +136,11 @@ private: QString getPUID(IWingPlugin *p); + bool isPluginLoaded(const WingDependency &d); + private: - bool loadPlugin(IWingPlugin *p, const std::optional &setdir); + void loadPlugin(IWingPlugin *p, const QString &fileName, + const std::optional &setdir); void connectInterface(IWingPlugin *plg); void connectLoadingInterface(IWingPlugin *plg); @@ -230,8 +234,9 @@ private: private: MainWindow *_win = nullptr; - QStringList _loadedpuid; + QList _loadedplginfo; QList _loadedplgs; + QList> _lazyplgs; QMap> _evplgs; diff --git a/src/utilities.h b/src/utilities.h index aaf9849..61444b9 100644 --- a/src/utilities.h +++ b/src/utilities.h @@ -50,6 +50,9 @@ #define PROEXT ".wingpro" +constexpr auto FIND_CONTEXT_SIZE = 3; +constexpr auto FIND_MAX_DISPLAY_FIND_CHARS = 8; + Q_DECL_UNUSED static inline QString NAMEICONRES(const QString &name) { return ":/com.wingsummer.winghex/images/" + name + ".png"; } @@ -156,6 +159,10 @@ public: } static QByteArray getMd5(QString filename) { + if (filename.isEmpty()) { + return {}; + } + QFile sourceFile(filename); if (sourceFile.open(QIODevice::ReadOnly)) { QCryptographicHash hash(QCryptographicHash::Md5); @@ -163,7 +170,7 @@ public: return hash.result(); } } - return QByteArray(); + return {}; } static bool checkIsLittleEndian() {