feat: 十六进制编辑区注册的菜单支持编辑器上下文;用户交互优化;

This commit is contained in:
寂静的羽夏 2025-07-26 19:34:51 +08:00
parent af15ffc8aa
commit 6678c7b9ff
24 changed files with 1351 additions and 1001 deletions

View File

@ -25,6 +25,7 @@ option(BUILD_TEST_PLUGIN OFF)
option(BUILD_SHARED_MEM_EXT OFF)
add_definitions(-DAS_NO_THREADS)
add_definitions(-DWING_SYSTEM_NAME="${CMAKE_SYSTEM_NAME}")
if(BUILD_TEST_PLUGIN)
add_subdirectory(TestPlugin)
@ -308,7 +309,9 @@ set(CLASS_SRC
src/class/changedstringlist.h
src/class/changedstringlist.cpp
src/class/editorviewcontext.h
src/class/editorviewcontext.cpp)
src/class/editorviewcontext.cpp
src/class/consolehighlighanim.h
src/class/consolehighlighanim.cpp)
set(INTERNAL_PLG_SRC
src/class/wingangelapi.h src/class/wingangelapi.cpp

View File

@ -31,6 +31,7 @@ TestHexExt::TestHexExt() : WingHex::IWingHexEditorPlugin() {
a->setChecked(true);
connect(a, &QAction::toggled, this, &TestHexExt::setColVisible);
m_context->addAction(a);
m_aVisCol = a;
}
bool TestHexExt::init(const std::unique_ptr<QSettings> &set) {
@ -52,7 +53,8 @@ TestHexExt::registeredRibbonTools() const {
}
QMargins TestHexExt::contentMargins(WingHex::HexEditorContext *context) const {
if (!_visCol) {
auto visCol = context->property("TestHexExt.colVis").toBool();
if (!visCol) {
return {};
}
auto lines = context->documentLines();
@ -65,9 +67,23 @@ QMargins TestHexExt::contentMargins(WingHex::HexEditorContext *context) const {
return {int(colLen) + 1, 0, 0, 0};
}
void TestHexExt::prepareCallEditorContext(WingHex::HexEditorContext *context) {
_curContext = context;
auto b = isShowLinePannel(context);
m_aVisCol->blockSignals(true);
m_aVisCol->setChecked(b);
m_aVisCol->blockSignals(false);
}
void TestHexExt::finishCallEditorContext(WingHex::HexEditorContext *context) {
Q_UNUSED(context);
_curContext = nullptr;
}
void TestHexExt::onPaintEvent(QPainter *painter, const QWidget *w,
WingHex::HexEditorContext *context) {
if (!_visCol) {
auto visCol = isShowLinePannel(context);
if (!visCol) {
return;
}
painter->save();
@ -104,4 +120,17 @@ void TestHexExt::onPaintEvent(QPainter *painter, const QWidget *w,
painter->restore();
}
void TestHexExt::setColVisible(bool b) { _visCol = b; }
bool TestHexExt::isShowLinePannel(WingHex::HexEditorContext *context) {
auto pp = context->property("TestHexExt.colVis");
if (pp.isNull()) {
context->setProperty("TestHexExt.colVis", true);
return true;
}
return pp.toBool();
}
void TestHexExt::setColVisible(bool b) {
if (_curContext) {
_curContext->setProperty("TestHexExt.colVis", b);
}
}

View File

@ -52,16 +52,26 @@ public:
virtual QMargins
contentMargins(WingHex::HexEditorContext *context) const override;
public:
virtual void
prepareCallEditorContext(WingHex::HexEditorContext *context) override;
virtual void
finishCallEditorContext(WingHex::HexEditorContext *context) override;
public:
virtual void onPaintEvent(QPainter *painter, const QWidget *w,
WingHex::HexEditorContext *context) override;
private:
bool isShowLinePannel(WingHex::HexEditorContext *context);
private slots:
void setColVisible(bool b);
private:
QMenu *m_context;
bool _visCol = true;
QAction *m_aVisCol;
WingHex::HexEditorContext *_curContext = nullptr;
};
#endif // TESTHEXEXT_H

@ -1 +1 @@
Subproject commit fad774858664d6a137c21b0c2af73ca209cc2f47
Subproject commit 4492bf2aaba267b80ec324c456f0584dcd0efc03

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

147
main.cpp
View File

@ -1,8 +1,118 @@
#include "class/appmanager.h"
#include "class/languagemanager.h"
#include "class/settingmanager.h"
#include "class/wingmessagebox.h"
#include "define.h"
#include <QDebug>
#include <QProcessEnvironment>
#include <QSettings>
void loadEnvConfig(int argc, char *argv[]) {
QFileInfo info(argv[0]);
QDir appDir(info.absoluteDir());
if (!appDir.exists(QStringLiteral("config.ini"))) {
return;
}
auto path = appDir.absoluteFilePath(QStringLiteral("config.ini"));
QSettings set(path, QSettings::IniFormat);
// General
for (auto &kv : set.childKeys()) {
qputenv(qPrintable(kv), set.value(kv).toByteArray());
}
auto groups = set.childGroups();
auto evaluate = [](const QProcessEnvironment &env,
const QString &statement) {
// Parse and evaluate statements:
// $NAME -> check existence
// $NAME=VALUE -> ignore-case full match
// $NAME==VALUE -> case-sensitive full match
// $NAME:=VALUE -> ignore-case contains
// $NAME::=VALUE -> case-sensitive contains
// VALUE: unless pure digits, must be enclosed in "" or ''
static const QRegularExpression re(
R"(^\$([A-Za-z_][A-Za-z0-9_]*)(?:\s*(==|::=|:=|=)\s*(\d+|"[^"]*"|'[^']*'))?$)");
auto match = re.match(statement);
if (!match.hasMatch()) {
qWarning("[main::loadEnvConfig] Invalid syntax: %s",
qUtf8Printable(statement));
return false;
}
auto name = match.captured(1);
auto op = match.captured(2);
auto value = match.captured(3);
// Existence check: no operator provided
if (op.isEmpty()) {
return env.contains(name);
}
if (!value.isEmpty() &&
((value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith('\'') && value.endsWith('\'')))) {
value.removeFirst().removeLast();
}
auto var = env.value(name);
// Evaluate based on operator
if (op == QStringLiteral(":=") || op == QStringLiteral("::=")) {
const QStringList items = var.split(QDir::listSeparator());
for (const QString &item : items) {
if (op == QStringLiteral(":=")) {
if (item.contains(value, Qt::CaseInsensitive)) {
return true;
}
} else {
if (item.contains(value, Qt::CaseSensitive)) {
return true;
}
}
}
return false;
}
if (op == "=") {
return QString::compare(var, value, Qt::CaseInsensitive) == 0;
} else if (op == "==") {
return var == value;
} else {
qWarning("[main::loadEnvConfig] Unknown operator: %s",
qUtf8Printable(op));
}
return false;
};
auto env = QProcessEnvironment::systemEnvironment();
constexpr auto syslen = std::char_traits<char>::length(WING_SYSTEM_NAME);
if (syslen) {
set.beginGroup(WING_SYSTEM_NAME);
for (auto &kv : set.childKeys()) {
qputenv(qPrintable(kv), set.value(kv).toByteArray());
}
set.endGroup();
}
for (auto &g : groups) {
if (evaluate(env, g)) {
set.beginGroup(g);
for (auto &kv : set.childKeys()) {
qputenv(qPrintable(kv), set.value(kv).toByteArray());
}
set.endGroup();
}
}
}
int main(int argc, char *argv[]) {
/* 有关对在 QT5 的 Win 平台禁用高 dpi 支持
*
@ -18,23 +128,13 @@ int main(int argc, char *argv[]) {
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
loadEnvConfig(argc, argv);
QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
#ifdef Q_OS_LINUX
// fix wayland issue (a workaround): floating dock not work
// reference:
// https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/
// issues/714#issuecomment-2802752677
auto denv = qgetenv("XDG_SESSION_TYPE");
if (denv.isEmpty() ||
denv.compare(QByteArrayLiteral("wayland"), Qt::CaseInsensitive) == 0) {
qputenv("QT_QPA_PLATFORM", "xcb");
}
#endif
QApplication::setApplicationName(APP_NAME);
QApplication::setOrganizationName(APP_ORG);
QApplication::setApplicationVersion(WINGHEX_VERSION);
QApplication::setApplicationName(QStringLiteral(APP_NAME));
QApplication::setOrganizationName(QStringLiteral(APP_ORG));
QApplication::setApplicationVersion(QStringLiteral(WINGHEX_VERSION));
try {
AppManager a(argc, argv);
@ -61,5 +161,22 @@ int main(int argc, char *argv[]) {
return a.exec();
} catch (CrashCode errCode) {
return int(errCode);
} catch (const std::bad_alloc &) {
auto &lang = LanguageManager::instance();
auto df = lang.defaultLocale();
// this exception can only occur when your memory are too limit or
// you are writing more than 2GB with QByteArray on 32-bit operating
// system.
if (QLocale::China == df.territory()) {
WingMessageBox::critical(
nullptr, QStringLiteral(APP_NAME),
QStringLiteral("崩溃啦!发生内存溢出异常!"));
} else {
WingMessageBox::critical(
nullptr, QStringLiteral(APP_NAME),
QStringLiteral("WingHexExplorer2 is out of memory. Crashed!"));
}
return int(CrashCode::OutofMemory);
}
}

60
mkinstaller/config.ini Normal file
View File

@ -0,0 +1,60 @@
; ========= 羽云十六进制编辑器2应用环境配置文件说明 ==========
;
; 如果区块名开头没有 $ 则认为普通区块General 会在所有的操作系统生效
; 如果你想在某些操作系统生效可以指定区块名为Windows, Linux 和 Darwin
; 对于 Linux 系统,如果包含 uname 工具,则区块名为 uname -s 的输出结果
;
; ================= 配置文件区块扩展语法说明 =================
;
; 语句格式:
; $NAME [OPERATOR VALUE]
;
; 各字段说明:
; $ • 语句起始标记,必需
;
; NAME • 环境变量名
; • 以字母或“_”开头后续可包含字母、数字或“_”
;
; OPERATOR • 可选运算符:
; = 忽略大小写全匹配
; == 区分大小写全匹配
; := 忽略大小写子串包含
; ::= 区分大小写子串包含
;
; VALUE • 可选比较值,仅在 OPERATOR 存在时使用
; • 格式必须是:
; — 纯数字(例如 12345
; — 或用双引号包裹的字符串("text"
; — 或用单引号包裹的字符串('text'
;
; 存在性检查:
; 若仅写 “$NAME”无 OPERATOR 和 VALUE即检查该环境变量是否已定义
;
; --------------------- 示例 ---------------------
; $XDG_SESSION_TYPE="wayland"
; → 忽略大小写地检查 XDG_SESSION_TYPE 是否等于 “wayland”
;
; $PATH:="/usr/local/bin"
; → 忽略大小写地检查 PATH 中是否包含 “/usr/local/bin”
;
; $HOME==12345
; → 区分大小写地检查 HOME 是否等于数字 12345
;
; $LD_LIBRARY_PATH::="/lib64"
; → 区分大小写地检查 LD_LIBRARY_PATH 中是否包含 “/lib64”
;
; $HOME
; → 检查 HOME 是否已定义(存在性检查)
; =====================================================
[General]
WING_DISABLE_PLUGIN_SYSTEM=0
WING_DISABLE_EXTDRV=0
WING_DISABLE_PLUGIN=0
WING_DEBUG=0
[$XDG_SESSION_TYPE="wayland"]
QT_QPA_PLATFORM=xcb

View File

@ -497,7 +497,6 @@ QList<CodeInfoTip> AsCompletion::parseDocument() {
// first preprocess the code
AsPreprocesser prepc(engine);
prepc.setIsCodeCompleteMode(true);
prepc.setIncludeCallback(&AsCompletion::includeCallBack, this);
auto r = prepc.loadSectionFromMemory(QStringLiteral("ASCOMPLETION"),

View File

@ -186,13 +186,15 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
QStack<std::optional<bool>> m_condtionStack;
bool isEndLine = true;
while (pos < modifiedScript.size()) {
auto SECTION = sectionname.toUtf8();
asUINT len = 0;
asETokenClass t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
if (t == asTC_UNKNOWN && modifiedScript[pos] == '#' &&
if (isEndLine && t == asTC_UNKNOWN && modifiedScript[pos] == '#' &&
(pos + 1 < modifiedScript.size())) {
int start = pos++;
@ -217,12 +219,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
if (std::any_of(modifiedScript.data() + pos,
modifiedScript.data() + pos + len,
[](char ch) { return ch == '\n'; })) {
if (_isCodeCompleteMode) {
pos = modifiedScript.indexOf('\n', pos);
continue;
}
auto str = QObject::tr("IfDefNoWord");
auto str = tr("IfDefNoWord");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
@ -234,16 +231,6 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
}
if (isIfDef || isIfnDef) {
if (_isCodeCompleteMode) {
auto pos = modifiedScript.indexOf('\n', start);
if (pos < 0) {
overwriteCode(modifiedScript, start,
modifiedScript.size() - start - 1);
} else {
overwriteCode(modifiedScript, start, pos - start);
}
continue;
}
if (t == asTC_IDENTIFIER) {
QByteArray word = modifiedScript.sliced(pos, len);
@ -267,23 +254,13 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
qDebug().noquote() << modifiedScript;
#endif
} else {
auto str = QObject::tr("IfDefInvalidWord");
auto str = tr("IfDefInvalidWord");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
} else if (isIf) {
if (_isCodeCompleteMode) {
auto pos = modifiedScript.indexOf('\n', start);
if (pos < 0) {
overwriteCode(modifiedScript, start,
modifiedScript.size() - start - 1);
} else {
overwriteCode(modifiedScript, start, pos - start);
}
continue;
}
// evalutate the string
auto npos = modifiedScript.indexOf('\n', pos);
QByteArray codes;
@ -303,7 +280,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
ok);
if (ret < 0) {
auto str = QObject::tr("CalIfFailed");
auto str = tr("CalIfFailed");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
@ -324,20 +301,6 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
QByteArray word = modifiedScript.sliced(pos, len);
pos += len;
if (_isCodeCompleteMode) {
defineWord(word, {});
auto pos = modifiedScript.indexOf('\n', start);
if (pos < 0) {
overwriteCode(modifiedScript, start,
modifiedScript.size() - start -
1);
} else {
overwriteCode(modifiedScript, start,
pos - start);
}
continue;
}
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos,
&len);
@ -366,7 +329,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
defineWord(word, v);
} break;
default:
auto str = QObject::tr("UnexceptedToken");
auto str = tr("UnexceptedToken");
engine->WriteMessage(
SECTION,
getLineCount(modifiedScript, pos), 1,
@ -381,42 +344,20 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
// ensure end line
if (endLinePassFailed(modifiedScript, pos)) {
auto str = QObject::tr("UnexceptedToken");
auto str = tr("UnexceptedToken");
engine->WriteMessage(
SECTION, getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
} else {
if (_isCodeCompleteMode) {
auto pos = modifiedScript.indexOf('\n', start);
if (pos < 0) {
overwriteCode(modifiedScript, start,
modifiedScript.size() - start -
1);
} else {
overwriteCode(modifiedScript, start,
pos - start);
}
continue;
}
auto str = QObject::tr("InvalidDef");
auto str = tr("InvalidDef");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
} else if (isUnDef) {
if (_isCodeCompleteMode) {
auto pos = modifiedScript.indexOf('\n', start);
if (pos < 0) {
overwriteCode(modifiedScript, start,
modifiedScript.size() - start - 1);
} else {
overwriteCode(modifiedScript, start, pos - start);
}
continue;
}
if (t == asTC_IDENTIFIER) {
QByteArray word = modifiedScript.sliced(pos, len);
@ -427,13 +368,13 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
constexpr auto PREFIX = "__";
if (word.startsWith(PREFIX) && word.endsWith(PREFIX)) {
// Warning
auto str = QObject::tr("ReservedMarcoType");
auto str = tr("ReservedMarcoType");
engine->WriteMessage(
SECTION, getLineCount(modifiedScript, pos), 1,
asMSGTYPE_WARNING, str.toUtf8());
} else {
if (!definedWords.remove(word)) {
auto str = QObject::tr("MarcoNotFound:") + word;
auto str = tr("MarcoNotFound:") + word;
engine->WriteMessage(
SECTION, getLineCount(modifiedScript, pos),
1, asMSGTYPE_WARNING, str.toUtf8());
@ -442,14 +383,14 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
// ensure end line
if (endLinePassFailed(modifiedScript, pos)) {
auto str = QObject::tr("UnexceptedToken");
auto str = tr("UnexceptedToken");
engine->WriteMessage(
SECTION, getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
} else {
auto str = QObject::tr("InvalidDef");
auto str = tr("InvalidDef");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
@ -457,12 +398,8 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
}
}
} else if (token == "else") {
if (_isCodeCompleteMode) {
overwriteCode(modifiedScript, start, pos - start);
continue;
}
if (m_condtionStack.isEmpty()) {
auto str = QObject::tr("NoMatchingIf");
auto str = tr("NoMatchingIf");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
@ -478,14 +415,14 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
// ensure end line
if (endLinePassFailed(modifiedScript, pos - 1)) {
auto str = QObject::tr("UnexceptedToken");
auto str = tr("UnexceptedToken");
engine->WriteMessage(
SECTION, getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
}
} else {
auto str = QObject::tr("DupElseDef");
auto str = tr("DupElseDef");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
@ -496,13 +433,9 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
qDebug().noquote() << modifiedScript;
#endif
} else if (token == "endif") {
if (_isCodeCompleteMode) {
overwriteCode(modifiedScript, start, pos - start);
continue;
}
// Only remove the #endif if there was a matching #if
if (m_condtionStack.isEmpty()) {
auto str = QObject::tr("NoMatchingIf");
auto str = tr("NoMatchingIf");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
@ -514,7 +447,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
// ensure end line
if (endLinePassFailed(modifiedScript, pos)) {
auto str = QObject::tr("UnexceptedToken");
auto str = tr("UnexceptedToken");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
@ -524,17 +457,18 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
qDebug().noquote() << modifiedScript;
#endif
}
isEndLine = true;
} else {
if (!_isCodeCompleteMode) {
if (t == asTC_IDENTIFIER) {
// define replace
auto word = modifiedScript.sliced(pos, len);
if (word == PROMISE_AWAIT) {
auto npos = pos + len;
asUINT total = 0;
auto t = engine->ParseToken(
modifiedScript.data() + npos,
modifiedScript.size() - npos, &total);
auto t = engine->ParseToken(modifiedScript.data() + npos,
modifiedScript.size() - npos,
&total);
if (t == asTC_WHITESPACE) {
npos += total;
t = engine->ParseToken(modifiedScript.data() + npos,
@ -544,8 +478,8 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
// ok
auto word = modifiedScript.sliced(npos, total);
auto data = "(" + word +
")." PROMISE_YIELD
"()." PROMISE_UNWRAP "()";
")." PROMISE_YIELD "()." PROMISE_UNWRAP
"()";
auto oldLen = npos - pos + word.length();
modifiedScript.replace(pos, oldLen, data);
pos = npos;
@ -554,14 +488,14 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
}
}
auto str = QObject::tr("UnexceptedToken");
auto str = tr("UnexceptedToken");
engine->WriteMessage(SECTION,
getLineCount(modifiedScript, pos),
1, asMSGTYPE_ERROR, str.toUtf8());
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
return asERROR;
} else if (word == "__LINE__") {
auto data = QByteArray::number(
getLineCount(modifiedScript, pos));
auto data =
QByteArray::number(getLineCount(modifiedScript, pos));
modifiedScript.replace(pos, len, data);
pos += data.length();
continue;
@ -584,7 +518,10 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
len = rword.length();
}
}
}
} else if (t == asTC_WHITESPACE) {
isEndLine = std::any_of(modifiedScript.data() + pos,
modifiedScript.data() + pos + len,
[](char ch) { return ch == '\n'; });
}
pos += len;
}
@ -644,10 +581,10 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
// line breaks
auto p = includefile.indexOf('\n');
if (p >= 0) {
auto str =
QObject::tr("Invalid file name for #include; "
auto str = tr("Invalid file name for #include; "
"it contains a line-break: ") +
QStringLiteral("'") + includefile.left(p) +
QStringLiteral("'") +
includefile.left(p) +
QStringLiteral("'");
engine->WriteMessage(
sectionname.toUtf8(),
@ -690,11 +627,10 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
auto p = includefile.indexOf('\n');
auto ws = includefile.indexOf(' ');
if (!includefile.isEmpty() && p >= 0 && ws >= 0) {
auto str =
QObject::tr(
"Invalid file name for #include; "
auto str = tr("Invalid file name for #include; "
"it contains a line-break: ") +
QStringLiteral("'") + includefile.left(p) +
QStringLiteral("'") +
includefile.left(p) +
QStringLiteral("'");
engine->WriteMessage(
sectionname.toUtf8(),
@ -710,8 +646,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
pos - start);
}
} else {
auto str =
QObject::tr("Invalid file name for #include; "
auto str = tr("Invalid file name for #include; "
"it contains a line-break or "
"unpaired symbol");
engine->WriteMessage(sectionname.toUtf8(), 0, 0,
@ -734,24 +669,21 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
// to avoid compiler error
overwriteCode(modifiedScript, start, pos - start);
if (!_isCodeCompleteMode) {
int r = pragmaCallback
? pragmaCallback(pragmaText, this,
sectionname, pragmaParam)
? pragmaCallback(pragmaText, this, sectionname,
pragmaParam)
: -1;
if (r < 0) {
engine->WriteMessage(
sectionname.toUtf8(),
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR,
QObject::tr("Invalid #pragma directive")
.toUtf8());
tr("Invalid #pragma directive").toUtf8());
return r;
}
}
}
}
}
// Don't search for includes within statement blocks or
// between tokens in statements
else {
@ -810,8 +742,7 @@ int AsPreprocesser::loadScriptSection(const QString &filename) {
if (!f.open(QFile::ReadOnly)) {
// Write a message to the engine's message callback
auto msg = QObject::tr("Failed to open script file ") +
QStringLiteral("'") +
auto msg = tr("Failed to open script file ") + QStringLiteral("'") +
QFileInfo(filename).absoluteFilePath() + QStringLiteral("'");
engine->WriteMessage(filename.toUtf8(), 0, 0, asMSGTYPE_ERROR,
msg.toUtf8());
@ -1022,12 +953,6 @@ QByteArray AsPreprocesser::findReplaceResult(const QByteArray &v) {
return r;
}
bool AsPreprocesser::isCodeCompleteMode() const { return _isCodeCompleteMode; }
void AsPreprocesser::setIsCodeCompleteMode(bool newIsCodeCompleteMode) {
_isCodeCompleteMode = newIsCodeCompleteMode;
}
QHash<QString, QByteArray> AsPreprocesser::definedMacros() const {
return definedWords;
}

View File

@ -35,6 +35,7 @@
#pragma warning(disable : 4786)
#endif
#include <QApplication>
#include <QEventLoop>
#include <QMap>
#include <QSet>
@ -74,6 +75,7 @@ typedef int (*PRAGMACALLBACK_t)(const QByteArray &pragmaText,
* * #ifndef <word>
*/
class AsPreprocesser {
Q_DECLARE_TR_FUNCTIONS(AsPreprocesser)
public:
explicit AsPreprocesser(asIScriptEngine *engine);
virtual ~AsPreprocesser();
@ -113,9 +115,6 @@ public:
QString sectionName(unsigned int idx) const;
bool isCodeCompleteMode() const;
void setIsCodeCompleteMode(bool newIsCodeCompleteMode);
QHash<QString, QByteArray> definedMacros() const;
protected:
@ -155,9 +154,6 @@ protected:
QStringList includedScripts;
QHash<QString, QByteArray> definedWords;
private:
bool _isCodeCompleteMode = false;
};
#endif // ASPREPROCESSER_H

View File

@ -0,0 +1,98 @@
/*==============================================================================
** 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 <https://www.gnu.org/licenses/>.
** =============================================================================
*/
#include "consolehighlighanim.h"
#include <QApplication>
#include <QPalette>
#include <QWidget>
ConsoleHighlighAnim::ConsoleHighlighAnim(QObject *parent)
: QObject(parent), m_highlightColor(qApp->palette().highlight().color()),
m_alpha(0), _w(nullptr), _anim(nullptr) {
_anim = new QPropertyAnimation(this, QByteArrayLiteral("alpha"), this);
_anim->setStartValue(10);
_anim->setEndValue(100);
_anim->setDuration(1000);
_anim->setEasingCurve(QEasingCurve::InOutQuad);
_anim->setLoopCount(5);
connect(_anim, &QPropertyAnimation::finished, this, [this]() {
if (_w) {
_w->setStyleSheet({});
}
});
}
int ConsoleHighlighAnim::getAlpha() const { return m_alpha; }
void ConsoleHighlighAnim::setAlpha(int newAlpha) {
if (m_alpha == newAlpha)
return;
m_alpha = newAlpha;
auto clsname = _w->metaObject()->className();
auto ss = QStringLiteral("background-color: rgba(%1, %2, %3, %4);")
.arg(m_highlightColor.red())
.arg(m_highlightColor.green())
.arg(m_highlightColor.blue())
.arg(m_alpha);
if (clsname) {
ss.prepend('{').prepend(clsname).append('}');
}
if (_w) {
_w->setStyleSheet(ss);
}
Q_EMIT alphaChanged();
}
QWidget *ConsoleHighlighAnim::widget() const { return _w; }
void ConsoleHighlighAnim::setWidget(QWidget *newW) {
if (_w) {
_w->setStyleSheet({});
}
_w = newW;
auto ss = QStringLiteral("background-color: rgba(%1, %2, %3, %4);")
.arg(m_highlightColor.red())
.arg(m_highlightColor.green())
.arg(m_highlightColor.blue())
.arg(m_alpha);
if (_w) {
_w->setStyleSheet(ss);
}
}
void ConsoleHighlighAnim::start() {
if (_w == nullptr) {
return;
}
if (_anim->state() == QPropertyAnimation::Stopped) {
_anim->start();
}
}
void ConsoleHighlighAnim::stop() {
if (_anim->state() == QPropertyAnimation::Running) {
_anim->stop();
if (_w) {
_w->setStyleSheet({});
}
}
}

View File

@ -0,0 +1,55 @@
/*==============================================================================
** 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 <https://www.gnu.org/licenses/>.
** =============================================================================
*/
#ifndef CONSOLEHIGHLIGHANIM_H
#define CONSOLEHIGHLIGHANIM_H
#include <QColor>
#include <QObject>
#include <QPropertyAnimation>
class ConsoleHighlighAnim : public QObject {
Q_OBJECT
Q_PROPERTY(int alpha READ getAlpha WRITE setAlpha NOTIFY alphaChanged FINAL)
public:
explicit ConsoleHighlighAnim(QObject *parent = nullptr);
public:
int getAlpha() const;
void setAlpha(int newAlpha);
QWidget *widget() const;
void setWidget(QWidget *newW);
public:
void start();
// 停止动画并清除样式
void stop();
signals:
void alphaChanged();
private:
QColor m_highlightColor;
int m_alpha;
QWidget *_w;
QPropertyAnimation *_anim;
};
#endif // CONSOLEHIGHLIGHANIM_H

View File

@ -18,10 +18,14 @@
#include "editorviewcontext.h"
EditorViewContext::EditorViewContext(QHexView *view)
: WingHex::HexEditorContext(), _view(view) {
: WingHex::HexEditorContext(view), _view(view) {
Q_ASSERT(view);
}
QString EditorViewContext::docFileName() const {
return _view->windowFilePath();
}
QFontMetricsF EditorViewContext::fontMetrics() const {
return _view->fontMetrics();
}

View File

@ -27,6 +27,7 @@ public:
// HexEditorPalette interface
public:
virtual QString docFileName() const override;
virtual QFontMetricsF fontMetrics() const override;
virtual QColor headerColor() const override;
virtual QColor addressColor() const override;

View File

@ -4063,12 +4063,7 @@ void PluginSystem::try2LoadHexExtPlugin() {
_manHexInfo = m;
{
auto menu = p->registeredHexContextMenu();
if (menu) {
_win->m_hexContextMenu.append(menu);
}
}
registerHexContextMenu(p);
registerRibbonTools(p->registeredRibbonTools());
registeredSettingPages(QVariant::fromValue(p),
p->registeredSettingPages());
@ -4142,6 +4137,30 @@ void PluginSystem::registerEvents(IWingPlugin *plg) {
}
}
void PluginSystem::registerHexContextMenu(IWingHexEditorInterface *inter) {
Q_ASSERT(inter);
auto menu = inter->registeredHexContextMenu();
if (menu) {
_win->m_hexContextMenu.append(menu);
connect(menu, &QMenu::aboutToShow, this, [menu, inter]() {
auto pp = menu->property("__CONTEXT__");
auto ptr =
reinterpret_cast<HexEditorContext *>(pp.value<quintptr>());
if (ptr) {
inter->prepareCallEditorContext(ptr);
}
});
connect(menu, &QMenu::triggered, this, [menu, inter]() {
auto pp = menu->property("__CONTEXT__");
auto ptr =
reinterpret_cast<HexEditorContext *>(pp.value<quintptr>());
if (ptr) {
inter->finishCallEditorContext(ptr);
}
});
}
}
void PluginSystem::applyFunctionTables(QObject *plg, const CallTable &fns) {
plg->setProperty("__CALL_TABLE__", QVariant::fromValue(fns));
plg->setProperty("__CALL_POINTER__", quintptr(this));
@ -4217,13 +4236,7 @@ void PluginSystem::loadPlugin(IWingPlugin *p, PluginInfo &meta,
// ensure call only once
registerRibbonTools(p->registeredRibbonTools());
registerPluginDockWidgets(p);
{
auto menu = p->registeredHexContextMenu();
if (menu) {
_win->m_hexContextMenu.append(menu);
}
}
registerHexContextMenu(p);
{
auto vieww = p->registeredEditorViewWidgets();

View File

@ -224,6 +224,8 @@ private:
private:
void registerEvents(IWingPlugin *plg);
void registerHexContextMenu(IWingHexEditorInterface *inter);
void applyFunctionTables(QObject *plg, const CallTable &fns);
bool isPluginLoaded(const WingDependency &d);

View File

@ -333,8 +333,9 @@ ErrFile EditorView::newFile(size_t index) {
removeMonitorPaths();
auto istr = QString::number(index);
m_fileName = tr("Untitled") + istr;
this->setWindowTitle(m_fileName);
auto fname = tr("Untitled") + istr;
m_hex->setWindowFilePath(fname);
this->setWindowTitle(fname);
m_docType = DocumentType::File;
m_isWorkSpace = false;
m_isNewFile = true;
@ -371,7 +372,8 @@ ErrFile EditorView::openFile(const QString &filename) {
m_hex->setKeepSize(true);
m_docType = DocumentType::File;
m_fileName = info.absoluteFilePath();
auto fName = info.absoluteFilePath();
m_hex->setWindowFilePath(fName);
m_isNewFile = false;
p->setDocSaved();
@ -379,8 +381,8 @@ ErrFile EditorView::openFile(const QString &filename) {
connectDocSavedFlag(this);
auto tab = this->tabWidget();
tab->setIcon(Utilities::getIconFromFile(style(), m_fileName));
tab->setToolTip(m_fileName);
tab->setIcon(Utilities::getIconFromFile(style(), fName));
tab->setToolTip(fName);
addMonitorPath();
}
@ -435,7 +437,7 @@ ErrFile EditorView::openExtFile(const QString &ext, const QString &file) {
_file = file;
m_docType = DocumentType::Extension;
m_fileName = fileName;
m_hex->setWindowFilePath(fileName);
m_isNewFile = false;
p->setDocSaved();
@ -513,7 +515,7 @@ ErrFile EditorView::save(const QString &workSpaceName, const QString &path,
return this->cloneParent()->save(workSpaceName, path, isExport,
workSpaceAttr);
}
auto fileName = path.isEmpty() ? m_fileName : path;
auto fileName = path.isEmpty() ? m_hex->windowFilePath() : path;
auto doc = m_hex->document();
#ifdef Q_OS_LINUX
@ -589,7 +591,8 @@ ErrFile EditorView::save(const QString &workSpaceName, const QString &path,
}
}
if (!doc->isDocSaved() || m_fileName != fileName || isNewFile()) {
if (!doc->isDocSaved() || m_hex->windowFilePath() != fileName ||
isNewFile()) {
if (m_docType == DocumentType::Extension) {
if (_dev->isOpen()) {
_dev->close();
@ -611,7 +614,8 @@ ErrFile EditorView::save(const QString &workSpaceName, const QString &path,
if (doc->saveTo(&file, !isExport)) {
file.close();
if (!isExport) {
m_fileName = QFileInfo(fileName).absoluteFilePath();
m_hex->setWindowFilePath(
QFileInfo(fileName).absoluteFilePath());
if (isNewFile()) {
auto buffer = new QFileBuffer;
@ -660,7 +664,7 @@ ErrFile EditorView::reload() {
switch (documentType()) {
case DocumentType::File:
return openFile(m_fileName);
return openFile(m_hex->windowFilePath());
case DocumentType::Extension:
return openExtFile(_ext, _file);
default:
@ -741,13 +745,14 @@ void EditorView::connectDocSavedFlag(EditorView *editor) {
connect(editor->m_hex->document().get(), &QHexDocument::documentSaved, this,
[=](bool b) {
QString fileName;
auto fName = m_hex->windowFilePath();
if (editor->isNewFile()) {
fileName = m_fileName;
fileName = fName;
} else if (editor->isExtensionFile()) {
auto idx = m_fileName.indexOf('}');
fileName = m_fileName.mid(idx);
auto idx = fName.indexOf('}');
fileName = fName.mid(idx);
} else {
fileName = QFileInfo(m_fileName).fileName();
fileName = QFileInfo(fName).fileName();
}
QString content;
@ -770,7 +775,7 @@ void EditorView::connectDocSavedFlag(EditorView *editor) {
auto tab = this->tabWidget();
if (tab->icon().isNull()) {
tab->setIcon(
Utilities::getIconFromFile(style(), m_fileName));
Utilities::getIconFromFile(style(), fName));
}
}
});
@ -784,7 +789,7 @@ void EditorView::removeMonitorPaths() {
_watcher.removePaths(files);
}
void EditorView::addMonitorPath() { _watcher.addPath(m_fileName); }
void EditorView::addMonitorPath() { _watcher.addPath(m_hex->windowFilePath()); }
BookMarksModel *EditorView::bookmarksModel() const { return m_bookmarks; }
@ -2144,12 +2149,12 @@ EditorView *EditorView::clone() {
connect(ev, &EditorView::sigOnMetadata, this, &EditorView::sigOnMetadata);
connect(ev, &EditorView::sigOnBookMark, this, &EditorView::sigOnBookMark);
auto doc = this->m_hex->document();
auto doc = m_hex->document();
ev->m_cloneParent = this;
ev->m_hex->setDocument(doc, ev->m_hex->cursor());
ev->m_fileName = this->m_fileName;
ev->m_hex->setWindowFilePath(m_hex->windowFilePath());
ev->setWindowTitle(this->windowTitle() + QStringLiteral(" : ") +
QString::number(cloneIndex + 1));
@ -2234,7 +2239,7 @@ QString EditorView::fileName() const {
if (isCloneFile()) {
return this->cloneParent()->fileName();
}
return m_fileName;
return m_hex->windowFilePath();
}
bool EditorView::eventFilter(QObject *watched, QEvent *event) {
@ -2247,3 +2252,5 @@ bool EditorView::eventFilter(QObject *watched, QEvent *event) {
}
return ads::CDockWidget::eventFilter(watched, event);
}
EditorViewContext *EditorView::editorContext() const { return _context; }

View File

@ -217,6 +217,8 @@ public:
void applySettings();
EditorViewContext *editorContext() const;
private:
inline qsizetype findAvailCloneIndex();
@ -570,7 +572,6 @@ private:
QMenu *m_menu = nullptr;
QHash<QString, WingEditorViewWidget *> m_others;
QString m_fileName;
bool m_isNewFile = true;
QByteArray m_md5;

View File

@ -6,7 +6,8 @@ enum class CrashCode : int {
LanguageFile,
PluginSetting,
ScriptInitFailed,
GenericCallNotSupported
GenericCallNotSupported,
OutofMemory
};
namespace AsUserDataType {

View File

@ -465,6 +465,9 @@ void MainWindow::buildUpDockSystem(QWidget *container) {
swapEditor(m_curEditor, editview);
_editorLock.unlock();
} else {
for (auto &menu : m_hexContextMenu) {
menu->setProperty("__CONTEXT__", {});
}
m_findresult->setModel(_findEmptyResult);
m_bookmarks->setModel(_bookMarkEmpty);
m_metadatas->setModel(_metadataEmpty);
@ -1083,10 +1086,13 @@ ads::CDockAreaWidget *
MainWindow::buildUpScriptBgOutputDock(ads::CDockManager *dock,
ads::DockWidgetArea area,
ads::CDockAreaWidget *areaw) {
m_bgScriptOutput = new QPlainTextEdit(this);
m_bgScriptOutput->setPlaceholderText(tr("BgScriptOutputHere"));
m_bgScriptOutput->setReadOnly(true);
_hlAnim = new ConsoleHighlighAnim(m_bgScriptOutput);
auto a = newAction(
ICONRES(QStringLiteral("mStr")), tr("SelectAll"),
[this]() { m_bgScriptOutput->selectAll(); }, QKeySequence::SelectAll);
@ -1116,6 +1122,13 @@ MainWindow::buildUpScriptBgOutputDock(ads::CDockManager *dock,
auto dw = buildDockWidget(dock, QStringLiteral("BgScriptOutput"),
tr("BgScriptOutput"), m_bgScriptOutput);
_hlAnim->setWidget(dw->tabWidget());
auto e = new EventFilter(QEvent::Show, dw);
connect(e, &EventFilter::eventTriggered, this,
[this]() { _hlAnim->stop(); });
m_bgScriptOutput->installEventFilter(e);
return dock->addDockWidget(area, dw, areaw);
}
@ -3333,7 +3346,6 @@ void MainWindow::swapEditor(EditorView *old, EditorView *cur) {
doc->disconnect(m_aShowMetaComment);
}
Q_ASSERT(cur);
auto hexeditor = cur->hexEditor();
auto needReload = cur->property("__RELOAD__").toBool();
if (needReload) {
@ -3434,6 +3446,10 @@ void MainWindow::swapEditor(EditorView *old, EditorView *cur) {
m_metadatas->selectionModel()->hasSelection());
});
for (auto &menu : m_hexContextMenu) {
menu->setProperty("__CONTEXT__", quintptr(cur->editorContext()));
}
_undoView->setStack(doc->undoStack());
m_curEditor = cur;
@ -3959,6 +3975,10 @@ void MainWindow::onOutputBgScriptOutput(
lastInfo.first = message.type;
lastInfo.second = qMakePair(message.row, message.col);
if (!m_bgScriptOutput->isVisible()) {
_hlAnim->start();
}
}
void MainWindow::closeEvent(QCloseEvent *event) {

View File

@ -44,6 +44,7 @@
#include "Qt-Advanced-Docking-System/src/DockManager.h"
#include "Qt-Advanced-Docking-System/src/DockWidget.h"
#include "WingPlugin/iwingplugin.h"
#include "class/consolehighlighanim.h"
#include "class/recentfilemanager.h"
#include "control/editorview.h"
#include "control/qtableviewext.h"
@ -479,6 +480,8 @@ private:
ScriptingConsole *m_scriptConsole = nullptr;
QPlainTextEdit *m_bgScriptOutput = nullptr;
ConsoleHighlighAnim *_hlAnim = nullptr;
bool m_isfinding = false;
ads::CDockWidget *m_find = nullptr;
QMenu *m_menuFind = nullptr;

View File

@ -2741,12 +2741,12 @@ ads--CDockWidgetTab[activeTab="true"]
ads--CDockWidgetTab QLabel
{
background-color: #1b1d20;
background-color: transparent;
}
ads--CDockWidgetTab[activeTab="true"] QLabel
ads--CDockWidgetTab[activeTab="false"]:hover
{
background-color: #202326;
background-color: rgba(61, 173, 232, 0.1);
}
ads--CDockContainerWidget > QSplitter{

View File

@ -2741,12 +2741,12 @@ ads--CDockWidgetTab[activeTab="true"]
ads--CDockWidgetTab QLabel
{
background-color: #d9d8d7;
background-color: transparent;
}
ads--CDockWidgetTab[activeTab="true"] QLabel
ads--CDockWidgetTab[activeTab="false"]:hover
{
background-color: #eff0f1;
background-color: rgba(61, 173, 232, 0.2);
}
ads--CDockContainerWidget > QSplitter{