Compare commits
14 Commits
d8069aedde
...
f299b75198
Author | SHA1 | Date |
---|---|---|
|
f299b75198 | |
|
56304a75d0 | |
|
57bd7b7c61 | |
|
066ab5375b | |
|
1be1f7322f | |
|
b49523e952 | |
|
960de3afb6 | |
|
3f26766575 | |
|
098f08e906 | |
|
cb2e8743af | |
|
8ad73431c1 | |
|
db60affc22 | |
|
32e27ba543 | |
|
c01ca038a3 |
|
@ -40,7 +40,7 @@ jobs:
|
||||||
run: sudo apt install fakeroot patchelf -y
|
run: sudo apt install fakeroot patchelf -y
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.8'
|
python-version: '3.8'
|
||||||
cache: 'pip'
|
cache: 'pip'
|
||||||
|
|
|
@ -43,7 +43,7 @@ jobs:
|
||||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.8'
|
python-version: '3.8'
|
||||||
cache: 'pip'
|
cache: 'pip'
|
||||||
|
@ -64,4 +64,4 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: WingHexExplorer2-win-release-build-cache
|
name: WingHexExplorer2-win-release-build-cache
|
||||||
path: ${{github.workspace}}/mkinstaller/innoSetup/*.exe
|
path: ${{github.workspace}}/mkinstaller/innoSetup/*.exe
|
||||||
|
|
||||||
|
|
|
@ -374,7 +374,6 @@ QString QConsoleWidget::currentCommandLine() const {
|
||||||
int QConsoleWidget::currentHeaderPos() const { return inpos_; }
|
int QConsoleWidget::currentHeaderPos() const { return inpos_; }
|
||||||
|
|
||||||
void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
|
void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
|
||||||
QTextCharFormat currfmt = currentCharFormat();
|
|
||||||
QTextCursor tc = textCursor();
|
QTextCursor tc = textCursor();
|
||||||
|
|
||||||
if (mode() == Input) {
|
if (mode() == Input) {
|
||||||
|
@ -400,8 +399,8 @@ void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
|
||||||
inpos_ = tc.position() - inpos_;
|
inpos_ = tc.position() - inpos_;
|
||||||
// restore the edit pos
|
// restore the edit pos
|
||||||
tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, editpos);
|
tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, editpos);
|
||||||
|
tc.setCharFormat({});
|
||||||
setTextCursor(tc);
|
setTextCursor(tc);
|
||||||
setCurrentCharFormat(currfmt);
|
|
||||||
} else {
|
} else {
|
||||||
// in output mode messages are ed
|
// in output mode messages are ed
|
||||||
QTextCursor tc1 = tc;
|
QTextCursor tc1 = tc;
|
||||||
|
@ -416,6 +415,8 @@ void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
|
||||||
textCursor().insertText(message, fmt);
|
textCursor().insertText(message, fmt);
|
||||||
ensureCursorVisible();
|
ensureCursorVisible();
|
||||||
|
|
||||||
|
tc.setCharFormat({});
|
||||||
|
|
||||||
// restore cursor if needed
|
// restore cursor if needed
|
||||||
if (needsRestore)
|
if (needsRestore)
|
||||||
setTextCursor(tc);
|
setTextCursor(tc);
|
||||||
|
|
|
@ -437,44 +437,30 @@ void QHexDocument::findAllBytes(qsizetype begin, qsizetype end,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qsizetype QHexDocument::findAllBytesExt(qsizetype begin, qsizetype end,
|
void QHexDocument::findAllBytesExt(qsizetype begin, qsizetype end,
|
||||||
const QString &pattern,
|
const QString &pattern,
|
||||||
QList<qsizetype> &results,
|
QList<qsizetype> &results,
|
||||||
const std::function<bool()> &pred) {
|
const std::function<bool()> &pred) {
|
||||||
results.clear();
|
results.clear();
|
||||||
auto patterns = parseConvertPattern(pattern);
|
if (end < 0) {
|
||||||
if (patterns.isEmpty()) {
|
end = m_buffer->length();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qsizetype p = begin > 0 ? begin : 0;
|
if (pattern.isEmpty() || end > m_buffer->length() || begin >= end) {
|
||||||
qsizetype e = end > begin ? end : -1;
|
return;
|
||||||
|
|
||||||
qsizetype offset = 0;
|
|
||||||
for (auto &p : patterns) {
|
|
||||||
if (std::holds_alternative<QByteArray>(p)) {
|
|
||||||
offset += std::get<QByteArray>(p).length();
|
|
||||||
} else if (std::holds_alternative<size_t>(p)) {
|
|
||||||
offset += std::get<size_t>(p);
|
|
||||||
} else if (std::holds_alternative<HexWildItem>(p)) {
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
qsizetype pos = begin;
|
||||||
while (pred()) {
|
qsizetype n = pattern.size();
|
||||||
p = findNextExt(p, pattern);
|
while (pos + n <= end && pred()) {
|
||||||
if (p < 0 || (e > 0 && p > e)) {
|
qsizetype f = findNextExt(pos, pattern);
|
||||||
|
if (f < 0 || f + n > end)
|
||||||
|
break;
|
||||||
|
results.append(f);
|
||||||
|
pos = f + 1;
|
||||||
|
if (results.size() >= QHEXVIEW_FIND_LIMIT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (results.size() > QHEXVIEW_FIND_LIMIT) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
results.append(p);
|
|
||||||
p += offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QHexDocument::insert(qsizetype offset, uchar b) {
|
bool QHexDocument::insert(qsizetype offset, uchar b) {
|
||||||
|
@ -538,287 +524,92 @@ bool QHexDocument::_remove(qsizetype offset, qsizetype len) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
qsizetype QHexDocument::findNextExt(qsizetype begin,
|
bool QHexDocument::parsePattern(const QString &pattern,
|
||||||
const QList<FindStep> &patterns) {
|
QList<PatternByte> &out) {
|
||||||
auto op = [this](qsizetype &pos, const FindStep &step,
|
out.clear();
|
||||||
qsizetype *begin = nullptr) -> bool {
|
QString p = pattern;
|
||||||
if (pos < 0 || pos >= length()) {
|
p.remove(' ');
|
||||||
return false;
|
if (p.size() % 2 != 0) {
|
||||||
}
|
return false;
|
||||||
if (std::holds_alternative<QByteArray>(step)) {
|
}
|
||||||
auto v = std::get<QByteArray>(step);
|
for (int i = 0; i < p.size(); i += 2) {
|
||||||
auto len = v.length();
|
QChar hi = p[i], lo = p[i + 1];
|
||||||
auto r = findNext(pos, v);
|
PatternByte pb;
|
||||||
if (r >= 0) {
|
// high nibble
|
||||||
if (begin) {
|
if (hi != '?') {
|
||||||
*begin = r;
|
int v = hex2Int(hi);
|
||||||
} else {
|
if (v < 0) {
|
||||||
if (r != pos) {
|
|
||||||
pos = -1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos = r + len;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
pos = -1;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (std::holds_alternative<HexWildItem>(step)) {
|
pb.value |= quint8(v << 4);
|
||||||
auto v = std::get<HexWildItem>(step);
|
pb.mask |= 0xF0;
|
||||||
auto wc = uchar(at(pos));
|
|
||||||
pos += 1;
|
|
||||||
|
|
||||||
if (v.higher == '?') {
|
|
||||||
if ((wc & 0xf) == v.lower) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((wc >> 4) == v.higher) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (std::holds_alternative<size_t>(step)) {
|
|
||||||
auto v = std::get<size_t>(step);
|
|
||||||
pos += v;
|
|
||||||
if (v + pos < length()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
// low nibble
|
||||||
};
|
if (lo != '?') {
|
||||||
|
int v = hex2Int(lo);
|
||||||
while (begin < length()) {
|
if (v < 0) {
|
||||||
auto pos = begin;
|
return false;
|
||||||
|
|
||||||
auto p = patterns.cbegin();
|
|
||||||
auto r = op(pos, *p, &begin);
|
|
||||||
if (!r) {
|
|
||||||
if (pos < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
continue;
|
pb.value |= quint8(v);
|
||||||
|
pb.mask |= 0x0F;
|
||||||
}
|
}
|
||||||
++p;
|
out.append(pb);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QHexDocument::hex2Int(const QChar &c) {
|
||||||
|
ushort u = c.unicode();
|
||||||
|
if (u >= '0' && u <= '9')
|
||||||
|
return u - '0';
|
||||||
|
else if (u >= 'A' && u <= 'F')
|
||||||
|
return 10 + (u - 'A');
|
||||||
|
else if (u >= 'a' && u <= 'f')
|
||||||
|
return 10 + (u - 'a');
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qsizetype QHexDocument::findNextExt(qsizetype begin,
|
||||||
|
const QList<PatternByte> &patterns) {
|
||||||
|
if (patterns.isEmpty() || begin < 0 ||
|
||||||
|
begin + patterns.size() > m_buffer->length())
|
||||||
|
return -1;
|
||||||
|
qsizetype n = patterns.size();
|
||||||
|
for (qsizetype pos = begin; pos + n <= m_buffer->length(); ++pos) {
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
for (; p != patterns.cend(); ++p) {
|
for (qsizetype i = 0; i < n; ++i) {
|
||||||
auto r = op(pos, *p);
|
if (!matchByte(m_buffer->at(pos + i), patterns[i])) {
|
||||||
if (!r) {
|
|
||||||
ok = false;
|
ok = false;
|
||||||
if (pos < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
begin = pos;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ok)
|
||||||
if (ok) {
|
return pos;
|
||||||
return begin;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
qsizetype QHexDocument::findPreviousExt(qsizetype begin,
|
qsizetype QHexDocument::findPreviousExt(qsizetype begin,
|
||||||
const QList<FindStep> &patterns) {
|
const QList<PatternByte> &patterns) {
|
||||||
auto op = [this](qsizetype &pos, const FindStep &step,
|
if (patterns.isEmpty() || begin < 0)
|
||||||
qsizetype *begin = nullptr) -> bool {
|
return -1;
|
||||||
if (pos < 0 || pos >= length()) {
|
qsizetype n = patterns.size();
|
||||||
return false;
|
qsizetype maxStart = qMin(begin, m_buffer->length() - n);
|
||||||
}
|
for (qsizetype pos = maxStart; pos >= 0; --pos) {
|
||||||
if (std::holds_alternative<QByteArray>(step)) {
|
|
||||||
auto v = std::get<QByteArray>(step);
|
|
||||||
auto len = v.length();
|
|
||||||
auto r = findPrevious(pos, v);
|
|
||||||
if (r >= 0) {
|
|
||||||
if (begin) {
|
|
||||||
*begin = r;
|
|
||||||
} else {
|
|
||||||
if (r + len != pos) {
|
|
||||||
pos = -1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos = r - len;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
pos = -1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (std::holds_alternative<HexWildItem>(step)) {
|
|
||||||
auto v = std::get<HexWildItem>(step);
|
|
||||||
auto wc = uchar(at(pos));
|
|
||||||
pos -= 1;
|
|
||||||
|
|
||||||
if (v.higher == '?') {
|
|
||||||
if ((wc & 0xf) == v.lower) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((wc >> 4) == v.higher) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (std::holds_alternative<size_t>(step)) {
|
|
||||||
auto v = std::get<size_t>(step);
|
|
||||||
pos -= v;
|
|
||||||
if (v - pos >= 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
while (begin >= 0) {
|
|
||||||
auto pos = begin;
|
|
||||||
|
|
||||||
auto p = patterns.crbegin();
|
|
||||||
auto r = op(pos, *p, &begin);
|
|
||||||
if (!r) {
|
|
||||||
if (pos < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
++p;
|
|
||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
for (; p != patterns.crend(); ++p) {
|
for (qsizetype i = 0; i < n; ++i) {
|
||||||
auto r = op(pos, *p);
|
if (!matchByte(m_buffer->at(pos + i), patterns[i])) {
|
||||||
if (!r) {
|
|
||||||
ok = false;
|
ok = false;
|
||||||
if (pos < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
begin = pos;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ok)
|
||||||
if (ok) {
|
return pos;
|
||||||
return begin;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QHexDocument::FindStep>
|
|
||||||
QHexDocument::parseConvertPattern(const QString &pattern) {
|
|
||||||
// process hex pattern
|
|
||||||
QList<HexWildItem> words;
|
|
||||||
std::optional<uchar> higher;
|
|
||||||
for (auto pchar = pattern.cbegin(); pchar != pattern.cend(); ++pchar) {
|
|
||||||
if (pchar->isSpace()) {
|
|
||||||
if (higher) {
|
|
||||||
return {};
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto c = pchar->unicode();
|
|
||||||
if (c >= '0' && c <= '9') {
|
|
||||||
if (higher) {
|
|
||||||
HexWildItem item;
|
|
||||||
item.higher = higher.value();
|
|
||||||
item.lower = uchar(c) - '0';
|
|
||||||
words.append(item);
|
|
||||||
higher.reset();
|
|
||||||
} else {
|
|
||||||
higher = uchar(c) - '0';
|
|
||||||
}
|
|
||||||
} else if (c >= 'A' && c <= 'F') {
|
|
||||||
if (higher) {
|
|
||||||
HexWildItem item;
|
|
||||||
item.higher = higher.value();
|
|
||||||
item.lower = uchar(c) - 'A' + 10;
|
|
||||||
words.append(item);
|
|
||||||
higher.reset();
|
|
||||||
} else {
|
|
||||||
higher = uchar(c) - 'A' + 10;
|
|
||||||
}
|
|
||||||
} else if (c >= 'a' && c <= 'f') {
|
|
||||||
if (higher) {
|
|
||||||
HexWildItem item;
|
|
||||||
item.higher = higher.value();
|
|
||||||
item.lower = uchar(c) - 'a' + 10;
|
|
||||||
words.append(item);
|
|
||||||
higher.reset();
|
|
||||||
} else {
|
|
||||||
higher = uchar(c) - 'a' + 10;
|
|
||||||
}
|
|
||||||
} else if (c == '?') {
|
|
||||||
if (higher) {
|
|
||||||
HexWildItem item;
|
|
||||||
item.higher = higher.value();
|
|
||||||
item.lower = '?';
|
|
||||||
words.append(item);
|
|
||||||
higher.reset();
|
|
||||||
} else {
|
|
||||||
higher = '?';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (higher) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!words.isEmpty()) {
|
|
||||||
QList<FindStep> steps;
|
|
||||||
|
|
||||||
// parsing...
|
|
||||||
QByteArray buffer;
|
|
||||||
size_t len = 0;
|
|
||||||
for (auto pw = words.cbegin(); pw != words.cend(); ++pw) {
|
|
||||||
auto higher = pw->higher;
|
|
||||||
auto lower = pw->lower;
|
|
||||||
if (higher == '?' || lower == '?') {
|
|
||||||
if (higher == '?' && lower == '?') {
|
|
||||||
if (!buffer.isEmpty()) {
|
|
||||||
steps.append(buffer);
|
|
||||||
buffer.clear();
|
|
||||||
}
|
|
||||||
len++;
|
|
||||||
} else {
|
|
||||||
if (len != 0) {
|
|
||||||
steps.append(len);
|
|
||||||
len = 0;
|
|
||||||
}
|
|
||||||
if (!buffer.isEmpty()) {
|
|
||||||
steps.append(buffer);
|
|
||||||
buffer.clear();
|
|
||||||
}
|
|
||||||
HexWildItem item;
|
|
||||||
item.higher = higher;
|
|
||||||
item.lower = lower;
|
|
||||||
steps.append(item);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (len != 0) {
|
|
||||||
steps.append(len);
|
|
||||||
len = 0;
|
|
||||||
}
|
|
||||||
buffer.append(char(pw->higher << 4 | pw->lower));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
if (len != 0) {
|
|
||||||
steps.append(len);
|
|
||||||
}
|
|
||||||
if (!buffer.isEmpty()) {
|
|
||||||
steps.append(buffer);
|
|
||||||
}
|
|
||||||
return steps;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*======================*/
|
/*======================*/
|
||||||
|
|
||||||
// modified by wingsummer
|
// modified by wingsummer
|
||||||
|
@ -913,27 +704,31 @@ void QHexDocument::beginMarco(const QString &text) {
|
||||||
|
|
||||||
void QHexDocument::endMarco() { m_undostack->endMacro(); }
|
void QHexDocument::endMarco() { m_undostack->endMacro(); }
|
||||||
|
|
||||||
void QHexDocument::Insert(QHexCursor *cursor, qsizetype offset, uchar b,
|
bool QHexDocument::Insert(QHexCursor *cursor, qsizetype offset, uchar b,
|
||||||
int nibbleindex) {
|
int nibbleindex) {
|
||||||
this->Insert(cursor, offset, QByteArray(1, char(b)), nibbleindex);
|
return this->Insert(cursor, offset, QByteArray(1, char(b)), nibbleindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHexDocument::Replace(QHexCursor *cursor, qsizetype offset, uchar b,
|
bool QHexDocument::Replace(QHexCursor *cursor, qsizetype offset, uchar b,
|
||||||
int nibbleindex) {
|
int nibbleindex) {
|
||||||
this->Replace(cursor, offset, QByteArray(1, char(b)), nibbleindex);
|
return this->Replace(cursor, offset, QByteArray(1, char(b)), nibbleindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHexDocument::Insert(QHexCursor *cursor, qsizetype offset,
|
bool QHexDocument::Insert(QHexCursor *cursor, qsizetype offset,
|
||||||
const QByteArray &data, int nibbleindex) {
|
const QByteArray &data, int nibbleindex) {
|
||||||
if (m_keepsize || m_readonly || m_islocked)
|
if (m_keepsize || m_readonly || m_islocked) {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto cmd = MakeInsert(nullptr, cursor, offset, data, nibbleindex);
|
auto cmd = MakeInsert(nullptr, cursor, offset, data, nibbleindex);
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
m_undostack->push(cmd);
|
m_undostack->push(cmd);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit documentChanged();
|
emit documentChanged();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHexDocument::Append(QHexCursor *cursor, uchar b, int nibbleindex) {
|
void QHexDocument::Append(QHexCursor *cursor, uchar b, int nibbleindex) {
|
||||||
|
@ -951,15 +746,19 @@ void QHexDocument::Append(QHexCursor *cursor, const QByteArray &data,
|
||||||
emit documentChanged();
|
emit documentChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHexDocument::Replace(QHexCursor *cursor, qsizetype offset,
|
bool QHexDocument::Replace(QHexCursor *cursor, qsizetype offset,
|
||||||
const QByteArray &data, int nibbleindex) {
|
const QByteArray &data, int nibbleindex) {
|
||||||
if (m_readonly || m_islocked)
|
if (m_readonly || m_islocked) {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
auto cmd = MakeReplace(nullptr, cursor, offset, data, nibbleindex);
|
auto cmd = MakeReplace(nullptr, cursor, offset, data, nibbleindex);
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
m_undostack->push(cmd);
|
m_undostack->push(cmd);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
emit documentChanged();
|
emit documentChanged();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QHexDocument::Remove(QHexCursor *cursor, qsizetype offset, qsizetype len,
|
bool QHexDocument::Remove(QHexCursor *cursor, qsizetype offset, qsizetype len,
|
||||||
|
@ -1063,21 +862,19 @@ qsizetype QHexDocument::findPrevious(qsizetype begin, const QByteArray &ba) {
|
||||||
}
|
}
|
||||||
|
|
||||||
qsizetype QHexDocument::findNextExt(qsizetype begin, const QString &pattern) {
|
qsizetype QHexDocument::findNextExt(qsizetype begin, const QString &pattern) {
|
||||||
auto patterns = parseConvertPattern(pattern);
|
QList<PatternByte> patterns;
|
||||||
if (patterns.isEmpty()) {
|
if (!parsePattern(pattern, patterns)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return findNextExt(begin, patterns);
|
return findNextExt(begin, patterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
qsizetype QHexDocument::findPreviousExt(qsizetype begin,
|
qsizetype QHexDocument::findPreviousExt(qsizetype begin,
|
||||||
const QString &pattern) {
|
const QString &pattern) {
|
||||||
auto patterns = parseConvertPattern(pattern);
|
QList<PatternByte> patterns;
|
||||||
if (patterns.isEmpty()) {
|
if (!parsePattern(pattern, patterns)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return findPreviousExt(begin, patterns);
|
return findPreviousExt(begin, patterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ public:
|
||||||
QList<qsizetype> &results,
|
QList<qsizetype> &results,
|
||||||
const std::function<bool()> &pred = [] { return true; });
|
const std::function<bool()> &pred = [] { return true; });
|
||||||
|
|
||||||
qsizetype findAllBytesExt(
|
void findAllBytesExt(
|
||||||
qsizetype begin, qsizetype end, const QString &pattern,
|
qsizetype begin, qsizetype end, const QString &pattern,
|
||||||
QList<qsizetype> &results,
|
QList<qsizetype> &results,
|
||||||
const std::function<bool()> &pred = [] { return true; });
|
const std::function<bool()> &pred = [] { return true; });
|
||||||
|
@ -148,14 +148,14 @@ public slots:
|
||||||
void beginMarco(const QString &text);
|
void beginMarco(const QString &text);
|
||||||
void endMarco();
|
void endMarco();
|
||||||
|
|
||||||
void Insert(QHexCursor *cursor, qsizetype offset, uchar b, int nibbleindex);
|
bool Insert(QHexCursor *cursor, qsizetype offset, uchar b, int nibbleindex);
|
||||||
void Insert(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
|
bool Insert(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
|
||||||
int nibbleindex);
|
int nibbleindex);
|
||||||
void Append(QHexCursor *cursor, uchar b, int nibbleindex);
|
void Append(QHexCursor *cursor, uchar b, int nibbleindex);
|
||||||
void Append(QHexCursor *cursor, const QByteArray &data, int nibbleindex);
|
void Append(QHexCursor *cursor, const QByteArray &data, int nibbleindex);
|
||||||
void Replace(QHexCursor *cursor, qsizetype offset, uchar b,
|
bool Replace(QHexCursor *cursor, qsizetype offset, uchar b,
|
||||||
int nibbleindex);
|
int nibbleindex);
|
||||||
void Replace(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
|
bool Replace(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
|
||||||
int nibbleindex = 0);
|
int nibbleindex = 0);
|
||||||
bool Remove(QHexCursor *cursor, qsizetype offset, qsizetype len,
|
bool Remove(QHexCursor *cursor, qsizetype offset, qsizetype len,
|
||||||
int nibbleindex = 0);
|
int nibbleindex = 0);
|
||||||
|
@ -204,20 +204,28 @@ public slots:
|
||||||
bool _remove(qsizetype offset, qsizetype len);
|
bool _remove(qsizetype offset, qsizetype len);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// AB
|
// PatternByte: represents a byte in the pattern, with nibble-level wildcard
|
||||||
struct HexWildItem {
|
// support
|
||||||
uchar higher; // A
|
struct PatternByte {
|
||||||
uchar lower; // B
|
quint8 value = 0; // fixed bits
|
||||||
|
quint8 mask =
|
||||||
|
0; // mask: which bits to match (1 = must match, 0 = wildcard)
|
||||||
};
|
};
|
||||||
|
|
||||||
// std::variant< find-content, hex with wildcard, all-wildcards >
|
// Parse pattern string (e.g., "00 ?? AB C? 88" or "00??ABC?88") into
|
||||||
using FindStep = std::variant<QByteArray, HexWildItem, size_t>;
|
// PatternByte list
|
||||||
|
bool parsePattern(const QString &pattern, QList<PatternByte> &out);
|
||||||
|
|
||||||
QList<FindStep> parseConvertPattern(const QString &pattern);
|
// Byte match using mask
|
||||||
qsizetype findNextExt(qsizetype begin, const QList<FindStep> &patterns);
|
inline bool matchByte(quint8 data, const PatternByte &pb) {
|
||||||
qsizetype findPreviousExt(qsizetype begin, const QList<FindStep> &patterns);
|
return (data & pb.mask) == (pb.value & pb.mask);
|
||||||
|
}
|
||||||
|
|
||||||
/*================================*/
|
int hex2Int(const QChar &c);
|
||||||
|
|
||||||
|
qsizetype findNextExt(qsizetype begin, const QList<PatternByte> &patterns);
|
||||||
|
qsizetype findPreviousExt(qsizetype begin,
|
||||||
|
const QList<PatternByte> &patterns);
|
||||||
|
|
||||||
/*================================*/
|
/*================================*/
|
||||||
// modified by wingsummer
|
// modified by wingsummer
|
||||||
|
|
|
@ -707,7 +707,6 @@ void QHexRenderer::drawHex(QPainter *painter, const QRect &linerect,
|
||||||
if (!dis)
|
if (!dis)
|
||||||
this->applyMetadata(textcursor, line, Hex);
|
this->applyMetadata(textcursor, line, Hex);
|
||||||
|
|
||||||
this->applyBookMark(textcursor, line, Hex);
|
|
||||||
this->applySelection(textcursor, line, Hex);
|
this->applySelection(textcursor, line, Hex);
|
||||||
this->applyCursorHex(textcursor, line);
|
this->applyCursorHex(textcursor, line);
|
||||||
|
|
||||||
|
@ -718,24 +717,76 @@ void QHexRenderer::drawHex(QPainter *painter, const QRect &linerect,
|
||||||
ctx.palette.setColor(QPalette::Text, m_bytesColor);
|
ctx.palette.setColor(QPalette::Text, m_bytesColor);
|
||||||
textdocument.documentLayout()->draw(painter, ctx);
|
textdocument.documentLayout()->draw(painter, ctx);
|
||||||
|
|
||||||
|
this->applyBookMark(painter, textcursor, line, Hex);
|
||||||
painter->restore();
|
painter->restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHexRenderer::applyBookMark(QTextCursor &textcursor, qsizetype line,
|
void QHexRenderer::applyBookMark(QPainter *painter, QTextCursor &textcursor,
|
||||||
Factor factor) {
|
qsizetype line, Factor factor) {
|
||||||
|
|
||||||
if (!m_document->lineHasBookMark(line))
|
if (!m_document->lineHasBookMark(line))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
painter->save();
|
||||||
|
|
||||||
auto pos = m_document->getLineBookmarksPos(line);
|
auto pos = m_document->getLineBookmarksPos(line);
|
||||||
for (auto &item : pos) {
|
for (auto &item : pos) {
|
||||||
textcursor.setPosition(int((item % hexLineWidth()) * factor) + 2);
|
auto off = item % hexLineWidth();
|
||||||
|
|
||||||
|
qreal begin, width;
|
||||||
|
auto height = lineHeight();
|
||||||
|
|
||||||
|
// add some paddings
|
||||||
|
if (factor == Hex) {
|
||||||
|
begin = getCellWidth() * off * 3 + 1;
|
||||||
|
begin += getCellWidth() / 2;
|
||||||
|
width = getCellWidth() * 2 + 2;
|
||||||
|
textcursor.setPosition(off * factor);
|
||||||
|
} else {
|
||||||
|
begin = getCellWidth() * off + 1;
|
||||||
|
width = getCellWidth();
|
||||||
|
textcursor.setPosition(m_cursor->currentColumn());
|
||||||
|
}
|
||||||
|
|
||||||
auto charformat = textcursor.charFormat();
|
auto charformat = textcursor.charFormat();
|
||||||
textcursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor,
|
auto textOutline = charformat.textOutline();
|
||||||
factor - 1);
|
|
||||||
charformat.setFontWeight(QFont::Bold);
|
constexpr auto ALPHA = 180;
|
||||||
textcursor.setCharFormat(charformat);
|
|
||||||
|
if (textOutline.style() != Qt::NoPen) {
|
||||||
|
auto outColor = textOutline.color();
|
||||||
|
outColor.setAlpha(ALPHA);
|
||||||
|
QPen pen(outColor, 1, Qt::DotLine);
|
||||||
|
painter->setPen(pen);
|
||||||
|
} else {
|
||||||
|
if (m_cursor->currentLine() == line &&
|
||||||
|
m_cursor->currentColumn() == off) {
|
||||||
|
auto color = m_bytesColor;
|
||||||
|
color.setAlpha(ALPHA);
|
||||||
|
QPen pen(color, 1, Qt::DotLine);
|
||||||
|
painter->setPen(pen);
|
||||||
|
} else {
|
||||||
|
auto foreground = charformat.foreground();
|
||||||
|
if (foreground.style() != Qt::NoBrush) {
|
||||||
|
auto textColor = foreground.color();
|
||||||
|
textColor.setAlpha(ALPHA);
|
||||||
|
QPen pen(textColor, 1, Qt::DotLine);
|
||||||
|
painter->setPen(pen);
|
||||||
|
} else {
|
||||||
|
auto color = m_bytesColor;
|
||||||
|
color.setAlpha(ALPHA);
|
||||||
|
QPen pen(color, 1, Qt::DotLine);
|
||||||
|
painter->setPen(pen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
painter->setBrush(Qt::transparent);
|
||||||
|
painter->setBackground(Qt::transparent);
|
||||||
|
painter->drawRect(begin, 0, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
painter->restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHexRenderer::drawString(QPainter *painter, const QRect &linerect,
|
void QHexRenderer::drawString(QPainter *painter, const QRect &linerect,
|
||||||
|
@ -770,6 +821,7 @@ void QHexRenderer::drawString(QPainter *painter, const QRect &linerect,
|
||||||
ctx.palette.setColor(QPalette::Text, m_bytesColor);
|
ctx.palette.setColor(QPalette::Text, m_bytesColor);
|
||||||
textdocument.documentLayout()->draw(painter, ctx);
|
textdocument.documentLayout()->draw(painter, ctx);
|
||||||
|
|
||||||
|
this->applyBookMark(painter, textcursor, line, String);
|
||||||
painter->restore();
|
painter->restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,8 +144,10 @@ private:
|
||||||
qsizetype lineStart, qsizetype lineEnd, Factor factor,
|
qsizetype lineStart, qsizetype lineEnd, Factor factor,
|
||||||
bool strikeOut, bool hasSelection) const;
|
bool strikeOut, bool hasSelection) const;
|
||||||
|
|
||||||
void applyBookMark(QTextCursor &textcursor, qsizetype line,
|
// added by wingsummer
|
||||||
Factor factor); // added by wingsummer
|
void applyBookMark(QPainter *painter, QTextCursor &textcursor,
|
||||||
|
qsizetype line, Factor factor);
|
||||||
|
|
||||||
void applyCursorAscii(QTextCursor &textcursor, qsizetype line) const;
|
void applyCursorAscii(QTextCursor &textcursor, qsizetype line) const;
|
||||||
void applyCursorHex(QTextCursor &textcursor, qsizetype line) const;
|
void applyCursorHex(QTextCursor &textcursor, qsizetype line) const;
|
||||||
void drawAddress(QPainter *painter, const QRect &linerect, qsizetype line);
|
void drawAddress(QPainter *painter, const QRect &linerect, qsizetype line);
|
||||||
|
|
|
@ -355,7 +355,7 @@ qsizetype QHexView::findPrevious(qsizetype begin, const QByteArray &ba) {
|
||||||
|
|
||||||
bool QHexView::RemoveSelection(int nibbleindex) {
|
bool QHexView::RemoveSelection(int nibbleindex) {
|
||||||
if (!m_cursor->hasSelection())
|
if (!m_cursor->hasSelection())
|
||||||
return false;
|
return true;
|
||||||
|
|
||||||
auto total = m_cursor->selectionCount();
|
auto total = m_cursor->selectionCount();
|
||||||
m_document->beginMarco(QStringLiteral("RemoveSelection"));
|
m_document->beginMarco(QStringLiteral("RemoveSelection"));
|
||||||
|
@ -375,7 +375,7 @@ bool QHexView::RemoveSelection(int nibbleindex) {
|
||||||
|
|
||||||
bool QHexView::removeSelection() {
|
bool QHexView::removeSelection() {
|
||||||
if (!m_cursor->hasSelection())
|
if (!m_cursor->hasSelection())
|
||||||
return false;
|
return true;
|
||||||
|
|
||||||
// We essure selections are ordered by desending
|
// We essure selections are ordered by desending
|
||||||
// by selection-start, so it's safe to remove
|
// by selection-start, so it's safe to remove
|
||||||
|
@ -417,7 +417,7 @@ QByteArrayList QHexView::selectedBytes() const {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHexView::paste(bool hex) {
|
bool QHexView::paste(bool hex) {
|
||||||
QClipboard *c = qApp->clipboard();
|
QClipboard *c = qApp->clipboard();
|
||||||
|
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
|
@ -426,19 +426,28 @@ void QHexView::paste(bool hex) {
|
||||||
} else {
|
} else {
|
||||||
auto d = c->mimeData();
|
auto d = c->mimeData();
|
||||||
data = d->data(QStringLiteral("application/octet-stream"));
|
data = d->data(QStringLiteral("application/octet-stream"));
|
||||||
|
if (data.isEmpty()) {
|
||||||
|
data = d->text().toUtf8();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.isEmpty())
|
if (data.isEmpty()) {
|
||||||
return;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
this->removeSelection();
|
auto ret = this->removeSelection();
|
||||||
|
if (!ret) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto pos = m_cursor->position().offset();
|
auto pos = m_cursor->position().offset();
|
||||||
if (!m_document->isKeepSize()) {
|
if (!m_document->isKeepSize()) {
|
||||||
m_document->insert(pos, data);
|
bool ret = m_document->insert(pos, data);
|
||||||
m_cursor->moveTo(pos + data.length()); // added by wingsummer
|
m_cursor->moveTo(pos + data.length()); // added by wingsummer
|
||||||
} else
|
return ret;
|
||||||
m_document->replace(pos, data);
|
} else {
|
||||||
|
return m_document->replace(pos, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QHexView::Cut(bool hex, int nibbleindex) {
|
bool QHexView::Cut(bool hex, int nibbleindex) {
|
||||||
|
@ -457,7 +466,7 @@ bool QHexView::Cut(bool hex, int nibbleindex) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QHexView::Paste(bool hex, int nibbleindex) {
|
bool QHexView::Paste(bool hex, int nibbleindex) {
|
||||||
QClipboard *c = qApp->clipboard();
|
QClipboard *c = qApp->clipboard();
|
||||||
|
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
|
@ -466,19 +475,27 @@ void QHexView::Paste(bool hex, int nibbleindex) {
|
||||||
} else {
|
} else {
|
||||||
auto d = c->mimeData();
|
auto d = c->mimeData();
|
||||||
data = d->data(QStringLiteral("application/octet-stream"));
|
data = d->data(QStringLiteral("application/octet-stream"));
|
||||||
|
if (data.isEmpty()) {
|
||||||
|
data = d->text().toUtf8();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.isEmpty())
|
if (data.isEmpty()) {
|
||||||
return;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
this->RemoveSelection(nibbleindex);
|
auto ret = this->RemoveSelection(nibbleindex);
|
||||||
|
if (!ret) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto pos = m_cursor->position().offset();
|
auto pos = m_cursor->position().offset();
|
||||||
if (m_cursor->insertionMode() == QHexCursor::InsertionMode::InsertMode) {
|
if (m_cursor->insertionMode() == QHexCursor::InsertionMode::InsertMode) {
|
||||||
m_document->Insert(m_cursor, pos, data, nibbleindex);
|
auto ret = m_document->Insert(m_cursor, pos, data, nibbleindex);
|
||||||
m_cursor->moveTo(pos + data.length()); // added by wingsummer
|
m_cursor->moveTo(pos + data.length()); // added by wingsummer
|
||||||
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
m_document->Replace(m_cursor, pos, data, nibbleindex);
|
return m_document->Replace(m_cursor, pos, data, nibbleindex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,10 +554,11 @@ bool QHexView::copy(bool hex) {
|
||||||
qreal QHexView::fontSize() const { return m_fontSize; }
|
qreal QHexView::fontSize() const { return m_fontSize; }
|
||||||
|
|
||||||
void QHexView::setScaleRate(qreal rate) {
|
void QHexView::setScaleRate(qreal rate) {
|
||||||
if (m_scaleRate > 0) {
|
if (rate >= 0.2 && rate < 2.01) {
|
||||||
m_scaleRate = rate;
|
m_scaleRate = rate;
|
||||||
setFontSize(fontSize());
|
setFontSize(fontSize());
|
||||||
emit scaleRateChanged();
|
emit scaleRateChanged();
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -709,7 +727,7 @@ void QHexView::focusOutEvent(QFocusEvent *e) {
|
||||||
|
|
||||||
void QHexView::wheelEvent(QWheelEvent *e) {
|
void QHexView::wheelEvent(QWheelEvent *e) {
|
||||||
if (qApp->keyboardModifiers() == Qt::ControlModifier) {
|
if (qApp->keyboardModifiers() == Qt::ControlModifier) {
|
||||||
auto dela = e->angleDelta().y() / 1200.0 / 2;
|
auto dela = e->angleDelta().y() / 1200.0;
|
||||||
setScaleRate(scaleRate() + dela);
|
setScaleRate(scaleRate() + dela);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,10 +135,10 @@ public:
|
||||||
|
|
||||||
bool cut(bool hex);
|
bool cut(bool hex);
|
||||||
bool copy(bool hex = false);
|
bool copy(bool hex = false);
|
||||||
void paste(bool hex = false);
|
bool paste(bool hex = false);
|
||||||
|
|
||||||
bool Cut(bool hex = false, int nibbleindex = 0);
|
bool Cut(bool hex = false, int nibbleindex = 0);
|
||||||
void Paste(bool hex = false, int nibbleindex = 0);
|
bool Paste(bool hex = false, int nibbleindex = 0);
|
||||||
void Replace(qsizetype offset, uchar b, int nibbleindex);
|
void Replace(qsizetype offset, uchar b, int nibbleindex);
|
||||||
void Replace(qsizetype offset, const QByteArray &data, int nibbleindex = 0);
|
void Replace(qsizetype offset, const QByteArray &data, int nibbleindex = 0);
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit e5cef2dcf126037ffdc57a5aaf6a3b1d3f4c70ae
|
Subproject commit 3a5bb0d8bc45b6150ed4a7513b0712b0a5954a74
|
|
@ -8,7 +8,7 @@ set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(PROJECT_VERSION "2.2.2")
|
set(PROJECT_VERSION "2.2.3")
|
||||||
|
|
||||||
find_package(
|
find_package(
|
||||||
QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network Concurrent
|
QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network Concurrent
|
||||||
|
@ -21,12 +21,16 @@ message("Build ${PROJECT_NAME} with ${CMAKE_BUILD_TYPE}.")
|
||||||
|
|
||||||
install(CODE "set(CMAKE_INSTALL_LOCAL_ONLY TRUE)" ALL_COMPONENTS)
|
install(CODE "set(CMAKE_INSTALL_LOCAL_ONLY TRUE)" ALL_COMPONENTS)
|
||||||
|
|
||||||
|
option(WINGHEX_USE_FRAMELESS ON)
|
||||||
option(BUILD_TEST_PLUGIN OFF)
|
option(BUILD_TEST_PLUGIN OFF)
|
||||||
|
option(BUILD_SHARED_MEM_EXT OFF)
|
||||||
|
|
||||||
|
add_definitions(-DAS_NO_THREADS)
|
||||||
|
|
||||||
if(BUILD_TEST_PLUGIN)
|
if(BUILD_TEST_PLUGIN)
|
||||||
add_subdirectory(TestPlugin)
|
add_subdirectory(TestPlugin)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
option(BUILD_SHARED_MEM_EXT OFF)
|
|
||||||
if(BUILD_SHARED_MEM_EXT)
|
if(BUILD_SHARED_MEM_EXT)
|
||||||
add_subdirectory(ShareMemoryDrv)
|
add_subdirectory(ShareMemoryDrv)
|
||||||
endif()
|
endif()
|
||||||
|
@ -36,9 +40,6 @@ add_definitions(-DWINGHEX_VERSION="${PROJECT_VERSION}"
|
||||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/WINGHEX_VERSION" ${PROJECT_VERSION})
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/WINGHEX_VERSION" ${PROJECT_VERSION})
|
||||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/QT_VERSION" ${QT_VERSION_MAJOR})
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/QT_VERSION" ${QT_VERSION_MAJOR})
|
||||||
|
|
||||||
option(WINGHEX_USE_FRAMELESS "Use borderless windows to ensure UI uniformity"
|
|
||||||
TRUE)
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
find_package(QT NAMES Qt6 Qt5 REQUIRED AxContainer)
|
find_package(QT NAMES Qt6 Qt5 REQUIRED AxContainer)
|
||||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED AxContainer)
|
find_package(Qt${QT_VERSION_MAJOR} REQUIRED AxContainer)
|
||||||
|
@ -66,7 +67,7 @@ if(WINGHEX_USE_FRAMELESS)
|
||||||
option(QWINDOWKIT_BUILD_STATIC "Build static libraries" TRUE)
|
option(QWINDOWKIT_BUILD_STATIC "Build static libraries" TRUE)
|
||||||
option(QWINDOWKIT_INSTALL "Install library" OFF)
|
option(QWINDOWKIT_INSTALL "Install library" OFF)
|
||||||
add_subdirectory(3rdparty/qwindowkit)
|
add_subdirectory(3rdparty/qwindowkit)
|
||||||
add_definitions(-DWINGHEX_USE_FRAMELESS)
|
add_compile_definitions(WINGHEX_USE_FRAMELESS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||||
|
@ -193,8 +194,6 @@ set(CONTROL_SRC
|
||||||
src/control/qlistviewext.cpp
|
src/control/qlistviewext.cpp
|
||||||
src/control/asobjtreewidget.h
|
src/control/asobjtreewidget.h
|
||||||
src/control/asobjtreewidget.cpp
|
src/control/asobjtreewidget.cpp
|
||||||
src/control/qtlonglongspinbox.cpp
|
|
||||||
src/control/qtlonglongspinbox.h
|
|
||||||
src/control/dockwidgettab.h
|
src/control/dockwidgettab.h
|
||||||
src/control/dockwidgettab.cpp
|
src/control/dockwidgettab.cpp
|
||||||
src/control/qhextextedit.h
|
src/control/qhextextedit.h
|
||||||
|
@ -208,7 +207,9 @@ set(CONTROL_SRC
|
||||||
src/control/gotolinewidget.h
|
src/control/gotolinewidget.h
|
||||||
src/control/gotolinewidget.cpp
|
src/control/gotolinewidget.cpp
|
||||||
src/control/codeeditcontrolwidget.h
|
src/control/codeeditcontrolwidget.h
|
||||||
src/control/codeeditcontrolwidget.cpp)
|
src/control/codeeditcontrolwidget.cpp
|
||||||
|
src/control/scrollablelabel.h
|
||||||
|
src/control/scrollablelabel.cpp)
|
||||||
|
|
||||||
set(CLASS_SRC
|
set(CLASS_SRC
|
||||||
src/class/logger.cpp
|
src/class/logger.cpp
|
||||||
|
@ -354,7 +355,8 @@ set(SCRIPT_ADDON_SRC
|
||||||
src/scriptaddon/scriptjson.h
|
src/scriptaddon/scriptjson.h
|
||||||
src/scriptaddon/scriptjson.cpp
|
src/scriptaddon/scriptjson.cpp
|
||||||
src/scriptaddon/scriptfile.cpp
|
src/scriptaddon/scriptfile.cpp
|
||||||
src/scriptaddon/scriptfile.h)
|
src/scriptaddon/scriptfile.h
|
||||||
|
src/scriptaddon/aspromise.hpp)
|
||||||
|
|
||||||
# localization support
|
# localization support
|
||||||
file(
|
file(
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="作者" src="authorband.svg" />
|
<img alt="作者" src="authorband.svg" />
|
||||||
<img alt="协议" src="licenseband.svg" />
|
<img alt="协议" src="licenseband.svg" />
|
||||||
|
<a href="https://deepwiki.com/Wing-summer/WingHexExplorer2"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
@ -115,8 +116,7 @@
|
||||||
|
|
||||||
1. 本软件源代码不得私自应用于闭源商业用途,除非你完整开源(GPL协议的要求)。如果要将软件仓库的代码商用闭源,必须找我购买商业授权签订合同,价格私聊,非诚勿扰。
|
1. 本软件源代码不得私自应用于闭源商业用途,除非你完整开源(GPL协议的要求)。如果要将软件仓库的代码商用闭源,必须找我购买商业授权签订合同,价格私聊,非诚勿扰。
|
||||||
2. 本软件是用我的业余时间编写,不能及时修复 Bug 或者提供技术支持,请见谅。
|
2. 本软件是用我的业余时间编写,不能及时修复 Bug 或者提供技术支持,请见谅。
|
||||||
3. 本人非计算机专业,编写程序难免有 Bug ,欢迎提交 PR 。
|
3. 无论在什么时候有疑问,请详细阅读 Wiki 。
|
||||||
4. 无论在什么时候有疑问,请详细阅读 Wiki 。
|
|
||||||
|
|
||||||
### 打包者必读
|
### 打包者必读
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@
|
||||||
4. 包内最好含有主程序的校验和,但不强求。
|
4. 包内最好含有主程序的校验和,但不强求。
|
||||||
5. 无论是哪种打包形式,都一定要声明其开源协议和自由属性,不得隐藏而借机收费。虽然收费我并不反感,下载服务器还是需要资金维护的,但借着信息差骗人收费,这是我极其深恶痛绝的。
|
5. 无论是哪种打包形式,都一定要声明其开源协议和自由属性,不得隐藏而借机收费。虽然收费我并不反感,下载服务器还是需要资金维护的,但借着信息差骗人收费,这是我极其深恶痛绝的。
|
||||||
6. 无论以任何为目的,如未获得我作者的授权,不得修改任意程序内指向的网络链接和软件关于信息,比如赞助和关于软件部分的内容等。
|
6. 无论以任何为目的,如未获得我作者的授权,不得修改任意程序内指向的网络链接和软件关于信息,比如赞助和关于软件部分的内容等。
|
||||||
7. 不得在安装程序内插入任何含有商业推广的插件,并且严格遵守第二条内容。
|
7. 不得在安装程序内插入任何含有商业推广的插件。
|
||||||
|
|
||||||
### issue 前必读
|
### issue 前必读
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@
|
||||||
|
|
||||||
## WIKI
|
## WIKI
|
||||||
|
|
||||||
  如果想学习羽云十六进制编辑器的使用以及如何开发该软件的插件,请到 [该链接](https://github.com/Wing-summer/WingHexExplorer2/wiki/%E7%AE%80%E4%BB%8B) ,同时也欢迎大家指出 Wiki 的错误以及贡献优质内容。
|
  如果想学习羽云十六进制编辑器的使用以及如何开发该软件的插件,请到 [该链接](https://wing-summer.github.io/WingHexExplorer2/docs/zh/credits.html) ,同时也欢迎大家指出 Wiki 的错误以及贡献优质内容。
|
||||||
|
|
||||||
## 插件库
|
## 插件库
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="Author" src="authorband.svg" />
|
<img alt="Author" src="authorband.svg" />
|
||||||
<img alt="License" src="licenseband.svg" />
|
<img alt="License" src="licenseband.svg" />
|
||||||
|
<a href="https://deepwiki.com/Wing-summer/WingHexExplorer2"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
@ -115,8 +116,7 @@ For plugin development, the corresponding open source agreements are different.
|
||||||
|
|
||||||
1. The source code of this software shall not be used for closed-source commercial purposes unless you open source it completely (as required by the GPL agreement). If you want to commercially close the code of the software warehouse, you must contact me to purchase a commercial license and sign a contract. Please contact me for the price. Please do not disturb me if you are not serious.
|
1. The source code of this software shall not be used for closed-source commercial purposes unless you open source it completely (as required by the GPL agreement). If you want to commercially close the code of the software warehouse, you must contact me to purchase a commercial license and sign a contract. Please contact me for the price. Please do not disturb me if you are not serious.
|
||||||
2. This software was written in my spare time. Please forgive me for not being able to fix bugs or provide technical support in time.
|
2. This software was written in my spare time. Please forgive me for not being able to fix bugs or provide technical support in time.
|
||||||
3. I am not a computer major, and there are inevitably bugs in the program I write. Welcome to submit PR.
|
3. Whenever you have questions, please read the Wiki in detail.
|
||||||
4. Whenever you have questions, please read the Wiki in detail.
|
|
||||||
|
|
||||||
### For Packagers
|
### For Packagers
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ First of all, I would like to express my sincere tanks for your enthusiastic hel
|
||||||
4. It is best to contain the checksum of the main program in the package, but it is not mandatory.
|
4. It is best to contain the checksum of the main program in the package, but it is not mandatory.
|
||||||
5. Regardless of the packaging format, its open source agreement and free attributes must be stated, and it is not allowed to hide and charge for it. Although I don't mind charging, the download server still needs money to maintain, but I hate charging by taking advantage of the information gap.
|
5. Regardless of the packaging format, its open source agreement and free attributes must be stated, and it is not allowed to hide and charge for it. Although I don't mind charging, the download server still needs money to maintain, but I hate charging by taking advantage of the information gap.
|
||||||
6. Regardless of the purpose, if you do not obtain the authorization of me, you may not modify any network links pointed to and the ABOUT contents in the program, such as sponsorship and content about the software.
|
6. Regardless of the purpose, if you do not obtain the authorization of me, you may not modify any network links pointed to and the ABOUT contents in the program, such as sponsorship and content about the software.
|
||||||
7. You may not insert any plug-in containing commercial promotion in the installation program, and strictly abide by the content of the second article.
|
7. You may not insert any plug-in containing commercial promotion in the installation program.
|
||||||
|
|
||||||
### Issue
|
### Issue
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ Of course, there are other repositories as mirror for Chinese users (which will
|
||||||
|
|
||||||
## WIKI
|
## WIKI
|
||||||
|
|
||||||
  If you want to learn how to use WingHexEditor and how to develop plug-ins for the software, please go to this link: `Not available yet`. At the same time, you are also welcome to point out errors in the Wiki and contribute high-quality content.
|
  If you want to learn how to use WingHexEditor and how to develop plug-ins for the software, please go to [this link](https://wing-summer.github.io/WingHexExplorer2/docs/zh/credits.html) (Chinese only). At the same time, you are also welcome to point out errors in the Wiki and contribute high-quality content.
|
||||||
|
|
||||||
## Plugins
|
## Plugins
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8d86c185041572bd4735b272669d4117765c2176
|
Subproject commit 1f53c308498e529e5cfbed16c9f7f68c77a3e8e5
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
@ -55,6 +55,7 @@
|
||||||
<item>typedef</item>
|
<item>typedef</item>
|
||||||
<item>while</item>
|
<item>while</item>
|
||||||
<item>xor</item>
|
<item>xor</item>
|
||||||
|
<item>co_await</item>
|
||||||
</list>
|
</list>
|
||||||
<list name="types">
|
<list name="types">
|
||||||
<item>void</item>
|
<item>void</item>
|
||||||
|
@ -80,7 +81,7 @@
|
||||||
<HlCOct attribute="Octal" context="#stay"/>
|
<HlCOct attribute="Octal" context="#stay"/>
|
||||||
<HlCHex attribute="Hex" context="#stay"/>
|
<HlCHex attribute="Hex" context="#stay"/>
|
||||||
<Int attribute="Decimal" context="Int Suffixes"/>
|
<Int attribute="Decimal" context="Int Suffixes"/>
|
||||||
<HlCChar attribute="Char" context="#stay"/>
|
<DetectChar attribute="Char" context="String" char="'"/>
|
||||||
<StringDetect attribute="Text Block" context="TextBlock" String="""""/>
|
<StringDetect attribute="Text Block" context="TextBlock" String="""""/>
|
||||||
<DetectChar attribute="String" context="String" char="""/>
|
<DetectChar attribute="String" context="String" char="""/>
|
||||||
<Detect2Chars attribute="Comment" context="MatchComment" char="/" char1="/" lookAhead="true"/>
|
<Detect2Chars attribute="Comment" context="MatchComment" char="/" char1="/" lookAhead="true"/>
|
||||||
|
@ -105,6 +106,7 @@
|
||||||
<context attribute="String" lineEndContext="#pop" name="String">
|
<context attribute="String" lineEndContext="#pop" name="String">
|
||||||
<DetectChar context="StringEscapedChar" char="\" lookAhead="1"/>
|
<DetectChar context="StringEscapedChar" char="\" lookAhead="1"/>
|
||||||
<DetectChar attribute="String" context="#pop" char="""/>
|
<DetectChar attribute="String" context="#pop" char="""/>
|
||||||
|
<DetectChar attribute="Char" context="#pop" char="'"/>
|
||||||
</context>
|
</context>
|
||||||
<context attribute="String" lineEndContext="#pop" name="StringEscapedChar">
|
<context attribute="String" lineEndContext="#pop" name="StringEscapedChar">
|
||||||
<HlCStringChar attribute="String Char" context="#pop"/>
|
<HlCStringChar attribute="String Char" context="#pop"/>
|
||||||
|
@ -144,7 +146,7 @@
|
||||||
<itemData name="Octal" defStyleNum="dsBaseN"/>
|
<itemData name="Octal" defStyleNum="dsBaseN"/>
|
||||||
<itemData name="Hex" defStyleNum="dsBaseN"/>
|
<itemData name="Hex" defStyleNum="dsBaseN"/>
|
||||||
<itemData name="Float" defStyleNum="dsFloat"/>
|
<itemData name="Float" defStyleNum="dsFloat"/>
|
||||||
<itemData name="Char" defStyleNum="dsChar"/>
|
<itemData name="Char" defStyleNum="dsString"/>
|
||||||
<itemData name="String" defStyleNum="dsString"/>
|
<itemData name="String" defStyleNum="dsString"/>
|
||||||
<itemData name="Text Block" defStyleNum="dsString"/>
|
<itemData name="Text Block" defStyleNum="dsString"/>
|
||||||
<itemData name="String Char" defStyleNum="dsSpecialChar"/>
|
<itemData name="String Char" defStyleNum="dsSpecialChar"/>
|
||||||
|
|
|
@ -25,43 +25,49 @@
|
||||||
|
|
||||||
AngelObjString::AngelObjString() {}
|
AngelObjString::AngelObjString() {}
|
||||||
|
|
||||||
QString AngelObjString::stringToString(void *obj, asDebugger *dbg) {
|
QString AngelObjString::stringToString(void *obj, asDebugger *dbg, asUINT tag) {
|
||||||
Q_UNUSED(dbg);
|
Q_UNUSED(dbg);
|
||||||
|
|
||||||
// We know the received object is a string
|
// We know the received object is a string
|
||||||
QString val = *reinterpret_cast<QString *>(obj);
|
QString val = *reinterpret_cast<QString *>(obj);
|
||||||
|
if (tag == 1) {
|
||||||
|
val.prepend('"').append('"');
|
||||||
|
}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AngelObjString::arrayToString(void *obj, asDebugger *dbg) {
|
QString AngelObjString::arrayToString(void *obj, asDebugger *dbg, asUINT tag) {
|
||||||
CScriptArray *arr = reinterpret_cast<CScriptArray *>(obj);
|
CScriptArray *arr = reinterpret_cast<CScriptArray *>(obj);
|
||||||
|
|
||||||
QString str;
|
QString str;
|
||||||
QTextStream s(&str);
|
QTextStream s(&str);
|
||||||
s << tr("(len=") << arr->GetSize() << QStringLiteral(")");
|
s << QStringLiteral("{");
|
||||||
|
|
||||||
s << QStringLiteral(" [");
|
|
||||||
for (asUINT n = 0; n < arr->GetSize(); n++) {
|
for (asUINT n = 0; n < arr->GetSize(); n++) {
|
||||||
s << dbg->toString(arr->At(n), arr->GetElementTypeId(),
|
s << dbg->toString(arr->At(n), arr->GetElementTypeId(),
|
||||||
arr->GetArrayObjectType()->GetEngine());
|
arr->GetArrayObjectType()->GetEngine(), 1);
|
||||||
if (n < arr->GetSize() - 1)
|
if (n < arr->GetSize() - 1)
|
||||||
s << ", ";
|
s << ", ";
|
||||||
}
|
}
|
||||||
s << QStringLiteral("]");
|
s << QStringLiteral("}");
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AngelObjString::charToString(void *obj, asDebugger *dbg) {
|
QString AngelObjString::charToString(void *obj, asDebugger *dbg, asUINT tag) {
|
||||||
|
|
||||||
Q_UNUSED(dbg);
|
Q_UNUSED(dbg);
|
||||||
|
|
||||||
// We know the received object is a char
|
// We know the received object is a char
|
||||||
QChar *val = reinterpret_cast<QChar *>(obj);
|
QChar *val = reinterpret_cast<QChar *>(obj);
|
||||||
return QString(*val);
|
auto ret = QString(*val);
|
||||||
|
if (tag == 1) {
|
||||||
|
ret.prepend('\'').append('\'');
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
|
QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg,
|
||||||
|
asUINT tag) {
|
||||||
CScriptDictionary *dic = reinterpret_cast<CScriptDictionary *>(obj);
|
CScriptDictionary *dic = reinterpret_cast<CScriptDictionary *>(obj);
|
||||||
|
|
||||||
QString str;
|
QString str;
|
||||||
|
@ -69,11 +75,11 @@ QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
|
||||||
|
|
||||||
auto engine = dic->GetEngine();
|
auto engine = dic->GetEngine();
|
||||||
|
|
||||||
s << " {";
|
s << QStringLiteral("{");
|
||||||
asUINT n = 0;
|
asUINT n = 0;
|
||||||
for (CScriptDictionary::CIterator it = dic->begin(); it != dic->end();
|
for (CScriptDictionary::CIterator it = dic->begin(); it != dic->end();
|
||||||
it++, n++) {
|
it++, n++) {
|
||||||
s << "[" << it.GetKey() << "] = ";
|
s << QStringLiteral("[") << it.GetKey() << QStringLiteral("] = ");
|
||||||
|
|
||||||
// Get the type and address of the value
|
// Get the type and address of the value
|
||||||
const void *val = it.GetAddressOfValue();
|
const void *val = it.GetAddressOfValue();
|
||||||
|
@ -83,17 +89,17 @@ QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
|
||||||
// active, the debugger will use the engine held inside it by
|
// active, the debugger will use the engine held inside it by
|
||||||
// default, but in an environment where there multiple engines this
|
// default, but in an environment where there multiple engines this
|
||||||
// might not be the correct instance).
|
// might not be the correct instance).
|
||||||
s << dbg->toString(const_cast<void *>(val), typeId, engine);
|
s << dbg->toString(const_cast<void *>(val), typeId, engine, 1);
|
||||||
|
|
||||||
if (n < dic->GetSize() - 1)
|
if (n < dic->GetSize() - 1)
|
||||||
s << ", ";
|
s << QStringLiteral(", ");
|
||||||
}
|
}
|
||||||
s << "}";
|
s << QStringLiteral("}");
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AngelObjString::colorToString(void *obj, asDebugger *dbg) {
|
QString AngelObjString::colorToString(void *obj, asDebugger *dbg, asUINT tag) {
|
||||||
Q_UNUSED(dbg);
|
Q_UNUSED(dbg);
|
||||||
|
|
||||||
auto color = reinterpret_cast<QColor *>(obj);
|
auto color = reinterpret_cast<QColor *>(obj);
|
||||||
|
|
|
@ -28,16 +28,15 @@ class AngelObjString : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// for debugger use
|
static QString stringToString(void *obj, asDebugger *dbg, asUINT tag);
|
||||||
static QString stringToString(void *obj, asDebugger *dbg);
|
|
||||||
|
|
||||||
static QString arrayToString(void *obj, asDebugger *dbg);
|
static QString arrayToString(void *obj, asDebugger *dbg, asUINT tag);
|
||||||
|
|
||||||
static QString charToString(void *obj, asDebugger *dbg);
|
static QString charToString(void *obj, asDebugger *dbg, asUINT tag);
|
||||||
|
|
||||||
static QString dictionaryToString(void *obj, asDebugger *dbg);
|
static QString dictionaryToString(void *obj, asDebugger *dbg, asUINT tag);
|
||||||
|
|
||||||
static QString colorToString(void *obj, asDebugger *dbg);
|
static QString colorToString(void *obj, asDebugger *dbg, asUINT tag);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// ==================================================
|
// ==================================================
|
||||||
|
|
|
@ -143,8 +143,9 @@ AppManager::AppManager(int &argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
AppManager::~AppManager() {
|
AppManager::~AppManager() {
|
||||||
InspectQtLogHelper::instance().destory();
|
|
||||||
ClangFormatManager::instance().save();
|
ClangFormatManager::instance().save();
|
||||||
|
ScriptMachine::instance().deleteLater();
|
||||||
|
InspectQtLogHelper::instance().destory();
|
||||||
CommandHistoryManager::save(QConsoleWidget::history().strings_);
|
CommandHistoryManager::save(QConsoleWidget::history().strings_);
|
||||||
|
|
||||||
delete _w;
|
delete _w;
|
||||||
|
@ -155,7 +156,7 @@ AppManager *AppManager::instance() { return _instance; }
|
||||||
|
|
||||||
MainWindow *AppManager::mainWindow() const { return _w; }
|
MainWindow *AppManager::mainWindow() const { return _w; }
|
||||||
|
|
||||||
uint AppManager::currentMSecsSinceEpoch() { return _timer.elapsed(); }
|
quint64 AppManager::currentMSecsSinceEpoch() { return _timer.elapsed(); }
|
||||||
|
|
||||||
void AppManager::openFile(const QString &file, bool autoDetect) {
|
void AppManager::openFile(const QString &file, bool autoDetect) {
|
||||||
EditorView *editor = nullptr;
|
EditorView *editor = nullptr;
|
||||||
|
|
|
@ -38,7 +38,7 @@ public:
|
||||||
QApplication::tr("WingCloudStudio");
|
QApplication::tr("WingCloudStudio");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint currentMSecsSinceEpoch();
|
quint64 currentMSecsSinceEpoch();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void openFile(const QString &file, bool autoDetect = true);
|
void openFile(const QString &file, bool autoDetect = true);
|
||||||
|
|
|
@ -21,7 +21,9 @@
|
||||||
#include "class/aspreprocesser.h"
|
#include "class/aspreprocesser.h"
|
||||||
#include "class/qascodeparser.h"
|
#include "class/qascodeparser.h"
|
||||||
#include "class/scriptmachine.h"
|
#include "class/scriptmachine.h"
|
||||||
|
#include "control/scripteditor.h"
|
||||||
#include "model/codecompletionmodel.h"
|
#include "model/codecompletionmodel.h"
|
||||||
|
#include "utilities.h"
|
||||||
#include "wingcodeedit.h"
|
#include "wingcodeedit.h"
|
||||||
|
|
||||||
#include <QAbstractItemView>
|
#include <QAbstractItemView>
|
||||||
|
@ -40,6 +42,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, DOT_TRIGGER, ("."))
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, DBL_COLON_TRIGGER, ("::"))
|
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, DBL_COLON_TRIGGER, ("::"))
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, LEFT_PARE_TRIGGER, ("("))
|
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, LEFT_PARE_TRIGGER, ("("))
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, SEMI_COLON_TRIGGER, (";"))
|
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, SEMI_COLON_TRIGGER, (";"))
|
||||||
|
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, SHARP_TRIGGER, ("#"))
|
||||||
|
|
||||||
AsCompletion::AsCompletion(WingCodeEdit *p)
|
AsCompletion::AsCompletion(WingCodeEdit *p)
|
||||||
: WingCompleter(p), parser(ScriptMachine::instance().engine()) {
|
: WingCompleter(p), parser(ScriptMachine::instance().engine()) {
|
||||||
|
@ -47,7 +50,9 @@ AsCompletion::AsCompletion(WingCodeEdit *p)
|
||||||
// unleash the power of call tips
|
// unleash the power of call tips
|
||||||
*LEFT_PARE_TRIGGER,
|
*LEFT_PARE_TRIGGER,
|
||||||
// clear the tips
|
// clear the tips
|
||||||
*SEMI_COLON_TRIGGER});
|
*SEMI_COLON_TRIGGER,
|
||||||
|
// for marcos
|
||||||
|
*SHARP_TRIGGER});
|
||||||
setTriggerAmount(3);
|
setTriggerAmount(3);
|
||||||
|
|
||||||
connect(this, QOverload<const QModelIndex &>::of(&AsCompletion::activated),
|
connect(this, QOverload<const QModelIndex &>::of(&AsCompletion::activated),
|
||||||
|
@ -99,6 +104,15 @@ void AsCompletion::applyEmptyNsNode(QList<CodeInfoTip> &nodes,
|
||||||
tip.name = p.nameSpace;
|
tip.name = p.nameSpace;
|
||||||
nodes.append(tip);
|
nodes.append(tip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p.type == CodeInfoTip::Type::Class) {
|
||||||
|
for (auto &c : p.children) {
|
||||||
|
if (c.type == CodeInfoTip::Type::ClsFunction ||
|
||||||
|
c.type == CodeInfoTip::Type::Property) {
|
||||||
|
nodes.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes.append(emptyNsNodes);
|
nodes.append(emptyNsNodes);
|
||||||
|
@ -112,7 +126,7 @@ void AsCompletion::applyClassNodes(QList<CodeInfoTip> &nodes) {
|
||||||
for (auto &item : n) {
|
for (auto &item : n) {
|
||||||
if (item.type == CodeInfoTip::Type::Class) {
|
if (item.type == CodeInfoTip::Type::Class) {
|
||||||
for (auto &c : item.children) {
|
for (auto &c : item.children) {
|
||||||
if (c.type == CodeInfoTip::Type::Function) {
|
if (c.type == CodeInfoTip::Type::ClsFunction) {
|
||||||
if (!c.addinfo.contains(CodeInfoTip::RetType)) {
|
if (!c.addinfo.contains(CodeInfoTip::RetType)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -129,7 +143,7 @@ void AsCompletion::applyClassNodes(QList<CodeInfoTip> &nodes) {
|
||||||
int AsCompletion::includeCallBack(const QString &include, bool quotedInclude,
|
int AsCompletion::includeCallBack(const QString &include, bool quotedInclude,
|
||||||
const QString &from, AsPreprocesser *builder,
|
const QString &from, AsPreprocesser *builder,
|
||||||
void *userParam) {
|
void *userParam) {
|
||||||
Q_UNUSED(userParam);
|
auto p = reinterpret_cast<AsCompletion *>(userParam);
|
||||||
|
|
||||||
QFileInfo info(include);
|
QFileInfo info(include);
|
||||||
bool isAbsolute = info.isAbsolute();
|
bool isAbsolute = info.isAbsolute();
|
||||||
|
@ -139,7 +153,9 @@ int AsCompletion::includeCallBack(const QString &include, bool quotedInclude,
|
||||||
if (isAbsolute) {
|
if (isAbsolute) {
|
||||||
inc = include;
|
inc = include;
|
||||||
} else {
|
} else {
|
||||||
auto pwd = QFileInfo(from).absoluteDir();
|
auto editor = qobject_cast<ScriptEditor *>(p->widget()->parent());
|
||||||
|
Q_ASSERT(editor);
|
||||||
|
auto pwd = QFileInfo(editor->fileName()).absoluteDir();
|
||||||
inc = pwd.absoluteFilePath(include);
|
inc = pwd.absoluteFilePath(include);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -165,15 +181,59 @@ int AsCompletion::includeCallBack(const QString &include, bool quotedInclude,
|
||||||
return asSUCCESS;
|
return asSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AsCompletion::pushCompleteDBData(const QString &fileName,
|
||||||
|
const QList<CodeInfoTip> &data) {
|
||||||
|
if (!QFile::exists(fileName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompleteDB c;
|
||||||
|
c.data = data;
|
||||||
|
c.md5 = Utilities::getMd5(fileName);
|
||||||
|
c.time = 3;
|
||||||
|
|
||||||
|
comdb.insert(fileName, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsCompletion::remoteCompleteDBData(const QString &fileName) {
|
||||||
|
comdb.remove(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AsCompletion::CompleteDB>
|
||||||
|
AsCompletion::getCompleteDBData(const QString &fileName) {
|
||||||
|
if (comdb.contains(fileName)) {
|
||||||
|
auto ret = comdb[fileName];
|
||||||
|
ret.time++;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsCompletion::clearCompleteDBUnused() {
|
||||||
|
for (auto &c : comdb) {
|
||||||
|
c.time--;
|
||||||
|
}
|
||||||
|
comdb.removeIf(
|
||||||
|
[](const QPair<QString, CompleteDB> &c) { return c.second.time == 0; });
|
||||||
|
}
|
||||||
|
|
||||||
void AsCompletion::clearFunctionTip() { emit onFunctionTip({}); }
|
void AsCompletion::clearFunctionTip() { emit onFunctionTip({}); }
|
||||||
|
|
||||||
QString AsCompletion::wordSeperators() const {
|
QString AsCompletion::wordSeperators() const {
|
||||||
static QString eow(QStringLiteral("~!@#$%^&*()_+{}|\"<>?,/;'[]\\-="));
|
static QString eow(QStringLiteral("~!@$%^&*()_+{}|\"<>?,/;'[]\\-="));
|
||||||
return eow;
|
return eow;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsCompletion::processTrigger(const QString &trigger,
|
bool AsCompletion::processTrigger(const QString &trigger,
|
||||||
const QString &content) {
|
const QString &content) {
|
||||||
|
QList<CodeInfoTip> nodes;
|
||||||
|
|
||||||
|
if (trigger == *SHARP_TRIGGER) {
|
||||||
|
setModel(new CodeCompletionModel(parseMarcos(), this));
|
||||||
|
setCompletionPrefix({});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (content.isEmpty()) {
|
if (content.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -186,8 +246,6 @@ bool AsCompletion::processTrigger(const QString &trigger,
|
||||||
auto len = content.length();
|
auto len = content.length();
|
||||||
auto code = content.toUtf8();
|
auto code = content.toUtf8();
|
||||||
|
|
||||||
QList<CodeInfoTip> nodes;
|
|
||||||
|
|
||||||
if (!trigger.isEmpty() && trigger != *DOT_TRIGGER) {
|
if (!trigger.isEmpty() && trigger != *DOT_TRIGGER) {
|
||||||
clearFunctionTip();
|
clearFunctionTip();
|
||||||
}
|
}
|
||||||
|
@ -257,6 +315,7 @@ bool AsCompletion::processTrigger(const QString &trigger,
|
||||||
auto etoken = tokens.back();
|
auto etoken = tokens.back();
|
||||||
// it can not be any trigger, so take the last as prefix
|
// it can not be any trigger, so take the last as prefix
|
||||||
QString prefix = etoken.content;
|
QString prefix = etoken.content;
|
||||||
|
|
||||||
if (etoken.type == asTC_VALUE || etoken.type == asTC_COMMENT ||
|
if (etoken.type == asTC_VALUE || etoken.type == asTC_COMMENT ||
|
||||||
etoken.type == asTC_UNKNOWN) {
|
etoken.type == asTC_UNKNOWN) {
|
||||||
popup()->hide();
|
popup()->hide();
|
||||||
|
@ -268,6 +327,15 @@ bool AsCompletion::processTrigger(const QString &trigger,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trigger.isEmpty() && tokens.length() > 1) {
|
||||||
|
auto t = std::next(tokens.rbegin());
|
||||||
|
if (t->content == "#") {
|
||||||
|
setModel(new CodeCompletionModel(parseMarcos(), this));
|
||||||
|
setCompletionPrefix(prefix);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QList<CodeInfoTip> docNodes = parseDocument();
|
QList<CodeInfoTip> docNodes = parseDocument();
|
||||||
|
|
||||||
// if trigger is empty, it's making editing
|
// if trigger is empty, it's making editing
|
||||||
|
@ -293,6 +361,8 @@ bool AsCompletion::processTrigger(const QString &trigger,
|
||||||
processTrigger(*DOT_TRIGGER, content.left(etoken.pos));
|
processTrigger(*DOT_TRIGGER, content.left(etoken.pos));
|
||||||
setCompletionPrefix(prefix);
|
setCompletionPrefix(prefix);
|
||||||
return true;
|
return true;
|
||||||
|
} else if (etoken.content == QByteArrayLiteral(")")) {
|
||||||
|
// ignore
|
||||||
} else {
|
} else {
|
||||||
applyEmptyNsNode(nodes, docNodes);
|
applyEmptyNsNode(nodes, docNodes);
|
||||||
}
|
}
|
||||||
|
@ -302,46 +372,60 @@ bool AsCompletion::processTrigger(const QString &trigger,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trigger == *DOT_TRIGGER) {
|
if (trigger == *DOT_TRIGGER) {
|
||||||
// member type guessing ? basic match is enough. (>n<)
|
if (etoken.type == asTC_IDENTIFIER) {
|
||||||
auto isBasicType = [](const QString &type) {
|
// member type guessing ? basic match is enough. (>n<)
|
||||||
static QStringList basicType{
|
auto isBasicType = [](const QByteArray &type) {
|
||||||
"int", "int8", "int16", "int32", "int64",
|
static QByteArrayList basicType{
|
||||||
"uint", "uint8", "uint16", "uint32", "uint64",
|
"int", "int8", "int16", "int32", "int64",
|
||||||
"float", "double", "byte"};
|
"uint", "uint8", "uint16", "uint32", "uint64",
|
||||||
|
"float", "double", "byte"};
|
||||||
|
|
||||||
return basicType.contains(type);
|
return basicType.contains(type);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto clsNodes = parser.headerNodes();
|
auto clsNodes = parser.classNodes();
|
||||||
|
|
||||||
// filter the type we can use to auto-complete in docNodes
|
// filter the type we can use to auto-complete in docNodes
|
||||||
for (auto &item : docNodes) {
|
for (auto &item : docNodes) {
|
||||||
if (item.type == CodeInfoTip::Type::Class) {
|
if (item.type == CodeInfoTip::Type::Class) {
|
||||||
auto name = item.nameSpace;
|
auto name = item.nameSpace;
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty()) {
|
||||||
name = item.name;
|
name = item.name;
|
||||||
|
} else {
|
||||||
|
name += QStringLiteral("::") + item.name;
|
||||||
|
}
|
||||||
|
clsNodes.insert(name, item.children);
|
||||||
}
|
}
|
||||||
clsNodes.insert(name, item.children);
|
// a typedef can only be used to define an alias
|
||||||
|
// for primitive types, so NO NEED for auto-completing
|
||||||
}
|
}
|
||||||
// a typedef can only be used to define an alias
|
|
||||||
// for primitive types, so NO NEED for auto-completing
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens.removeLast();
|
tokens.removeLast();
|
||||||
auto ns = getNamespace(tokens);
|
auto ns = getNamespace(tokens);
|
||||||
for (auto &item : docNodes) {
|
for (auto &item : docNodes) {
|
||||||
if (etoken.content == item.name && ns == item.nameSpace) {
|
if (etoken.content == item.name && ns == item.nameSpace) {
|
||||||
auto retType = item.addinfo.value(CodeInfoTip::RetType);
|
auto retType = item.addinfo.value(CodeInfoTip::RetType);
|
||||||
|
auto decl = engine->GetTypeInfoByDecl(retType.toUtf8());
|
||||||
|
if (decl) {
|
||||||
|
QByteArray type = decl->GetNamespace();
|
||||||
|
if (type.isEmpty()) {
|
||||||
|
type = decl->GetName();
|
||||||
|
} else {
|
||||||
|
type +=
|
||||||
|
(QByteArrayLiteral("::") + decl->GetName());
|
||||||
|
}
|
||||||
|
|
||||||
// auto type inference is not supported.
|
// auto type inference is not supported.
|
||||||
// PRs will be welcomed !!!
|
// PRs will be welcomed !!!
|
||||||
if (isBasicType(retType)) {
|
if (isBasicType(type)) {
|
||||||
popup()->hide();
|
popup()->hide();
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes.append(clsNodes.value(type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes.append(clsNodes.value(retType));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,6 +497,7 @@ QList<CodeInfoTip> AsCompletion::parseDocument() {
|
||||||
|
|
||||||
// first preprocess the code
|
// first preprocess the code
|
||||||
AsPreprocesser prepc(engine);
|
AsPreprocesser prepc(engine);
|
||||||
|
prepc.setIsCodeCompleteMode(true);
|
||||||
prepc.setIncludeCallback(&AsCompletion::includeCallBack, this);
|
prepc.setIncludeCallback(&AsCompletion::includeCallBack, this);
|
||||||
|
|
||||||
auto r = prepc.loadSectionFromMemory(QStringLiteral("ASCOMPLETION"),
|
auto r = prepc.loadSectionFromMemory(QStringLiteral("ASCOMPLETION"),
|
||||||
|
@ -424,17 +509,61 @@ QList<CodeInfoTip> AsCompletion::parseDocument() {
|
||||||
auto data = prepc.scriptData();
|
auto data = prepc.scriptData();
|
||||||
QList<CodeInfoTip> ret;
|
QList<CodeInfoTip> ret;
|
||||||
|
|
||||||
|
auto marcos = prepc.definedMacros();
|
||||||
|
for (auto pkey = marcos.keyBegin(); pkey != marcos.keyEnd(); pkey++) {
|
||||||
|
CodeInfoTip tip;
|
||||||
|
tip.type = CodeInfoTip::Type::KeyWord;
|
||||||
|
tip.dontAddGlobal = true;
|
||||||
|
tip.name = *pkey;
|
||||||
|
ret.append(tip);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &d : data) {
|
for (auto &d : data) {
|
||||||
qsizetype offset = -1;
|
qsizetype offset = -1;
|
||||||
|
QList<CodeInfoTip> dd;
|
||||||
|
|
||||||
if (d.section == QStringLiteral("ASCOMPLETION")) {
|
if (d.section == QStringLiteral("ASCOMPLETION")) {
|
||||||
offset = editor->textCursor().position();
|
offset = editor->textCursor().position();
|
||||||
|
} else {
|
||||||
|
auto r = getCompleteDBData(d.section);
|
||||||
|
if (r) {
|
||||||
|
auto md5 = r->md5;
|
||||||
|
if (Utilities::getMd5(d.section) == md5) {
|
||||||
|
dd = r->data;
|
||||||
|
} else {
|
||||||
|
remoteCompleteDBData(d.section);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ret.append(parseScriptData(offset, d.script));
|
|
||||||
|
if (dd.isEmpty()) {
|
||||||
|
dd = parseScriptData(offset, d.script);
|
||||||
|
if (offset < 0) {
|
||||||
|
pushCompleteDBData(d.section, dd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret.append(dd);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<CodeInfoTip> AsCompletion::parseMarcos() {
|
||||||
|
static QList<CodeInfoTip> marcos;
|
||||||
|
if (marcos.isEmpty()) {
|
||||||
|
QStringList m{"define", "undef", "if", "else", "endif",
|
||||||
|
"ifdef", "ifndef", "include", "pragma"};
|
||||||
|
for (auto &i : m) {
|
||||||
|
CodeInfoTip tip;
|
||||||
|
tip.name = i;
|
||||||
|
tip.dontAddGlobal = true;
|
||||||
|
tip.type = CodeInfoTip::Type::KeyWord;
|
||||||
|
marcos.append(tip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return marcos;
|
||||||
|
}
|
||||||
|
|
||||||
QList<CodeInfoTip> AsCompletion::parseScriptData(qsizetype offset,
|
QList<CodeInfoTip> AsCompletion::parseScriptData(qsizetype offset,
|
||||||
const QByteArray &code) {
|
const QByteArray &code) {
|
||||||
QList<CodeInfoTip> ret;
|
QList<CodeInfoTip> ret;
|
||||||
|
|
|
@ -42,6 +42,8 @@ protected:
|
||||||
|
|
||||||
virtual QList<CodeInfoTip> parseDocument();
|
virtual QList<CodeInfoTip> parseDocument();
|
||||||
|
|
||||||
|
virtual QList<CodeInfoTip> parseMarcos();
|
||||||
|
|
||||||
QList<CodeInfoTip> parseScriptData(qsizetype offset,
|
QList<CodeInfoTip> parseScriptData(qsizetype offset,
|
||||||
const QByteArray &code);
|
const QByteArray &code);
|
||||||
|
|
||||||
|
@ -62,6 +64,21 @@ private:
|
||||||
const QString &from, AsPreprocesser *builder,
|
const QString &from, AsPreprocesser *builder,
|
||||||
void *userParam);
|
void *userParam);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct CompleteDB {
|
||||||
|
QList<CodeInfoTip> data;
|
||||||
|
QByteArray md5;
|
||||||
|
uint time = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
QHash<QString, CompleteDB> comdb;
|
||||||
|
|
||||||
|
void pushCompleteDBData(const QString &fileName,
|
||||||
|
const QList<CodeInfoTip> &data);
|
||||||
|
std::optional<CompleteDB> getCompleteDBData(const QString &fileName);
|
||||||
|
void remoteCompleteDBData(const QString &fileName);
|
||||||
|
void clearCompleteDBUnused();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ASDataBase parser;
|
ASDataBase parser;
|
||||||
};
|
};
|
||||||
|
|
|
@ -87,3 +87,22 @@ QList<CodeInfoTip> AsConsoleCompletion::parseDocument() {
|
||||||
return citips;
|
return citips;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<CodeInfoTip> AsConsoleCompletion::parseMarcos() {
|
||||||
|
static QList<CodeInfoTip> marcos;
|
||||||
|
if (marcos.isEmpty()) {
|
||||||
|
QStringList m{
|
||||||
|
"ls",
|
||||||
|
"del",
|
||||||
|
"cls",
|
||||||
|
};
|
||||||
|
for (auto &i : m) {
|
||||||
|
CodeInfoTip tip;
|
||||||
|
tip.name = i;
|
||||||
|
tip.dontAddGlobal = true;
|
||||||
|
tip.type = CodeInfoTip::Type::KeyWord;
|
||||||
|
marcos.append(tip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return marcos;
|
||||||
|
}
|
||||||
|
|
|
@ -29,7 +29,8 @@ public:
|
||||||
virtual ~AsConsoleCompletion() = default;
|
virtual ~AsConsoleCompletion() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual QList<CodeInfoTip> parseDocument();
|
virtual QList<CodeInfoTip> parseDocument() override;
|
||||||
|
virtual QList<CodeInfoTip> parseMarcos() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ScriptingConsole *_console;
|
ScriptingConsole *_console;
|
||||||
|
|
|
@ -40,7 +40,8 @@ ASDataBase::ASDataBase(asIScriptEngine *engine) {
|
||||||
"function", "interface", "shared", "this", "explicit", "override",
|
"function", "interface", "shared", "this", "explicit", "override",
|
||||||
"namespace", "get", "set", "super", "mixin", "false",
|
"namespace", "get", "set", "super", "mixin", "false",
|
||||||
"true", "null", "typename", "return", "typedef", "funcdef",
|
"true", "null", "typename", "return", "typedef", "funcdef",
|
||||||
"from", "import", "not", "xor", "or", "is"};
|
"from", "import", "not", "xor", "or", "is",
|
||||||
|
"co_await"};
|
||||||
for (auto &k : kws) {
|
for (auto &k : kws) {
|
||||||
CodeInfoTip t;
|
CodeInfoTip t;
|
||||||
t.type = CodeInfoTip::Type::KeyWord;
|
t.type = CodeInfoTip::Type::KeyWord;
|
||||||
|
@ -325,6 +326,42 @@ void ASDataBase::addClassCompletion(asIScriptEngine *engine) {
|
||||||
obj->Release();
|
obj->Release();
|
||||||
|
|
||||||
_headerNodes[ns].append(cls);
|
_headerNodes[ns].append(cls);
|
||||||
|
|
||||||
|
auto cat = cls.nameSpace;
|
||||||
|
if (cat.isEmpty()) {
|
||||||
|
cat = cls.name;
|
||||||
|
} else {
|
||||||
|
cat += QStringLiteral("::") + cls.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = cls.children;
|
||||||
|
data.removeIf([](const CodeInfoTip &tip) {
|
||||||
|
static QStringList opList{
|
||||||
|
"opNeg", "opCom", "opPreInc", "opPreDec",
|
||||||
|
"opPostInc", "opPostDec", "opEquals", "opCmp",
|
||||||
|
"opEquals", "opAssign", "opAddAssign", "opSubAssign",
|
||||||
|
"opMulAssign", "opDivAssign", "opModAssign", "opPowAssign",
|
||||||
|
"opAndAssign", "opOrAssign", "opXorAssign", "opShlAssign",
|
||||||
|
"opShrAssign", "opUShrAssign", "opAdd", "opAdd_r",
|
||||||
|
"opSub", "opSub_r", "opMul", "opMul_r",
|
||||||
|
"opDiv", "opDiv_r", "opMod", "opMod_r",
|
||||||
|
"opPow", "opPow_r", "opAnd", "opAnd_r",
|
||||||
|
"opOr", "opOr_r", "opXor", "opXor_r",
|
||||||
|
"opShl", "opShl_r", "opShr", "opShr_r",
|
||||||
|
"opUShr", "opUShr_r", "opIndex", "opCall",
|
||||||
|
"opConv", "opImplConv", "opCast", "opImplCast"};
|
||||||
|
return tip.addinfo.value(CodeInfoTip::RetType).isEmpty() ||
|
||||||
|
opList.contains(tip.name);
|
||||||
|
});
|
||||||
|
for (auto &d : data) {
|
||||||
|
d.addinfo.insert(
|
||||||
|
CodeInfoTip::Comment,
|
||||||
|
d.addinfo.value(CodeInfoTip::RetType) + QStringLiteral(" ") +
|
||||||
|
d.name + QStringLiteral("(") +
|
||||||
|
d.addinfo.value(CodeInfoTip::Args) + QStringLiteral(") ") +
|
||||||
|
d.addinfo.value(CodeInfoTip::SuffixQualifier));
|
||||||
|
}
|
||||||
|
_classNodes[cat].append(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,6 +372,10 @@ QString ASDataBase::getSuffixQualifier(asIScriptFunction *fn) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QHash<QString, QList<CodeInfoTip>> &ASDataBase::classNodes() const {
|
||||||
|
return _classNodes;
|
||||||
|
}
|
||||||
|
|
||||||
const QList<CodeInfoTip> &ASDataBase::keywordNodes() const {
|
const QList<CodeInfoTip> &ASDataBase::keywordNodes() const {
|
||||||
return _keywordNode;
|
return _keywordNode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ private:
|
||||||
public:
|
public:
|
||||||
const QHash<QString, QList<CodeInfoTip>> &headerNodes() const;
|
const QHash<QString, QList<CodeInfoTip>> &headerNodes() const;
|
||||||
const QHash<QString, QList<CodeInfoTip>> &enumsNodes() const;
|
const QHash<QString, QList<CodeInfoTip>> &enumsNodes() const;
|
||||||
|
const QHash<QString, QList<CodeInfoTip>> &classNodes() const;
|
||||||
|
|
||||||
const QList<CodeInfoTip> &keywordNodes() const;
|
const QList<CodeInfoTip> &keywordNodes() const;
|
||||||
|
|
||||||
|
@ -57,6 +58,7 @@ private:
|
||||||
// <namespace, content>
|
// <namespace, content>
|
||||||
QHash<QString, QList<CodeInfoTip>> _headerNodes;
|
QHash<QString, QList<CodeInfoTip>> _headerNodes;
|
||||||
QHash<QString, QList<CodeInfoTip>> _enumsNodes;
|
QHash<QString, QList<CodeInfoTip>> _enumsNodes;
|
||||||
|
QHash<QString, QList<CodeInfoTip>> _classNodes;
|
||||||
QList<CodeInfoTip> _keywordNode;
|
QList<CodeInfoTip> _keywordNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "asdebugger.h"
|
#include "asdebugger.h"
|
||||||
|
#include "class/appmanager.h"
|
||||||
#include "define.h"
|
#include "define.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
@ -78,6 +79,44 @@ void asDebugger::lineCallback(asIScriptContext *ctx) {
|
||||||
if (ctx == nullptr)
|
if (ctx == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// By default we ignore callbacks when the context is not active.
|
||||||
|
// An application might override this to for example disconnect the
|
||||||
|
// debugger as the execution finished.
|
||||||
|
if (ctx->GetState() != asEXECUTION_ACTIVE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto now = AppManager::instance()->currentMSecsSinceEpoch();
|
||||||
|
auto timer = reinterpret_cast<asPWORD>(
|
||||||
|
ctx->GetUserData(AsUserDataType::UserData_Timer));
|
||||||
|
auto timeOutTime = reinterpret_cast<asPWORD>(
|
||||||
|
ctx->GetUserData(AsUserDataType::UserData_TimeOut));
|
||||||
|
auto mode = reinterpret_cast<asPWORD>(
|
||||||
|
ctx->GetUserData(AsUserDataType::UserData_ContextMode));
|
||||||
|
|
||||||
|
bool timeOut = false;
|
||||||
|
if (timer < 0) {
|
||||||
|
timeOut = true;
|
||||||
|
} else {
|
||||||
|
if (mode == 0) {
|
||||||
|
timeOut = (now - timer) > 3000; // 3 s
|
||||||
|
} else {
|
||||||
|
if (timeOutTime) {
|
||||||
|
timeOut = (now - timer) > timeOutTime; // 10 min
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeOut && mode) {
|
||||||
|
auto timeOut = tr("ScriptTimedOut");
|
||||||
|
ScriptMachine::MessageInfo info;
|
||||||
|
info.message = timeOut;
|
||||||
|
info.mode = ScriptMachine::ConsoleMode(mode);
|
||||||
|
info.type = ScriptMachine::MessageType::Error;
|
||||||
|
ScriptMachine::instance().outputMessage(info);
|
||||||
|
ctx->Abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto isDbg = reinterpret_cast<asPWORD>(
|
auto isDbg = reinterpret_cast<asPWORD>(
|
||||||
ctx->GetUserData(AsUserDataType::UserData_isDbg));
|
ctx->GetUserData(AsUserDataType::UserData_isDbg));
|
||||||
if (!isDbg) {
|
if (!isDbg) {
|
||||||
|
@ -107,12 +146,6 @@ void asDebugger::lineCallback(asIScriptContext *ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default we ignore callbacks when the context is not active.
|
|
||||||
// An application might override this to for example disconnect the
|
|
||||||
// debugger as the execution finished.
|
|
||||||
if (ctx->GetState() != asEXECUTION_ACTIVE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto dbgContext = reinterpret_cast<ContextDbgInfo *>(ctx->GetUserData());
|
auto dbgContext = reinterpret_cast<ContextDbgInfo *>(ctx->GetUserData());
|
||||||
Q_ASSERT(dbgContext);
|
Q_ASSERT(dbgContext);
|
||||||
|
|
||||||
|
@ -368,7 +401,7 @@ bool asDebugger::checkBreakPoint(asIScriptContext *ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString asDebugger::toString(void *value, asUINT typeId,
|
QString asDebugger::toString(void *value, asUINT typeId,
|
||||||
asIScriptEngine *engine) {
|
asIScriptEngine *engine, asUINT tag) {
|
||||||
if (value == nullptr)
|
if (value == nullptr)
|
||||||
return QStringLiteral("<null>");
|
return QStringLiteral("<null>");
|
||||||
|
|
||||||
|
@ -445,7 +478,8 @@ QString asDebugger::toString(void *value, asUINT typeId,
|
||||||
s << name /*type->GetPropertyDeclaration(n)*/
|
s << name /*type->GetPropertyDeclaration(n)*/
|
||||||
<< QStringLiteral(" = ")
|
<< QStringLiteral(" = ")
|
||||||
<< toString(obj->GetAddressOfProperty(n),
|
<< toString(obj->GetAddressOfProperty(n),
|
||||||
obj->GetPropertyTypeId(n), type->GetEngine());
|
obj->GetPropertyTypeId(n), type->GetEngine(),
|
||||||
|
1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -482,7 +516,7 @@ QString asDebugger::toString(void *value, asUINT typeId,
|
||||||
|
|
||||||
// Invoke the callback to get the string representation of
|
// Invoke the callback to get the string representation of
|
||||||
// this type
|
// this type
|
||||||
s << it.value()(value, this);
|
s << it.value()(value, this, tag);
|
||||||
} else {
|
} else {
|
||||||
// Unknown type: type + address
|
// Unknown type: type + address
|
||||||
s << type->GetName() << '(' << value << ')';
|
s << type->GetName() << '(' << value << ')';
|
||||||
|
|
|
@ -84,7 +84,7 @@ public:
|
||||||
virtual ~asDebugger();
|
virtual ~asDebugger();
|
||||||
|
|
||||||
// Register callbacks to handle to-string conversions of application types
|
// Register callbacks to handle to-string conversions of application types
|
||||||
typedef QString (*ToStringCallback)(void *obj, asDebugger *dbg);
|
typedef QString (*ToStringCallback)(void *obj, asDebugger *dbg, asUINT tag);
|
||||||
|
|
||||||
void registerToStringCallback(const asITypeInfo *ti,
|
void registerToStringCallback(const asITypeInfo *ti,
|
||||||
ToStringCallback callback);
|
ToStringCallback callback);
|
||||||
|
@ -108,8 +108,9 @@ public:
|
||||||
// Line callback invoked by context
|
// Line callback invoked by context
|
||||||
void lineCallback(asIScriptContext *ctx);
|
void lineCallback(asIScriptContext *ctx);
|
||||||
|
|
||||||
|
// tag = 1 : string should be printed with quotes
|
||||||
QString toString(void *value, asUINT typeId,
|
QString toString(void *value, asUINT typeId,
|
||||||
asIScriptEngine *engine = nullptr);
|
asIScriptEngine *engine = nullptr, asUINT tag = 0);
|
||||||
|
|
||||||
GCStatistic gcStatistics();
|
GCStatistic gcStatistics();
|
||||||
|
|
||||||
|
|
|
@ -16,16 +16,29 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "aspreprocesser.h"
|
#include "aspreprocesser.h"
|
||||||
|
#include "class/languagemanager.h"
|
||||||
|
#include "class/qascodeparser.h"
|
||||||
|
#include "class/scriptmachine.h"
|
||||||
|
#include "class/skinmanager.h"
|
||||||
|
#include "scriptaddon/aspromise.hpp"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QLibraryInfo>
|
||||||
|
#include <QMetaEnum>
|
||||||
|
#include <QStack>
|
||||||
|
#include <QVersionNumber>
|
||||||
|
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(
|
Q_GLOBAL_STATIC_WITH_ARGS(
|
||||||
QStringList, DEFAULT_MARCO,
|
QByteArrayList, DEFAULT_MARCO,
|
||||||
({"__AS_ARRAY__", "__AS_ANY__", "__AS_GRID__", "__AS_HANDLE__",
|
({// special marcos
|
||||||
|
"__LINE__", "__SECTION__", "__SECTION_BASE__",
|
||||||
|
// functions
|
||||||
|
"__AS_ARRAY__", "__AS_ANY__", "__AS_GRID__", "__AS_HANDLE__",
|
||||||
"__AS_MATH__", "__AS_WEAKREF__", "__AS_COROUTINE__", "__WING_FILE__",
|
"__AS_MATH__", "__AS_WEAKREF__", "__AS_COROUTINE__", "__WING_FILE__",
|
||||||
"__WING_STRING__", "__WING_COLOR__", "__WING_JSON__", "__WING_REGEX__",
|
"__WING_STRING__", "__WING_COLOR__", "__WING_JSON__", "__WING_REGEX__",
|
||||||
"__WING_DICTIONARY__"}));
|
"__WING_DICTIONARY__", "__WING_PRINT_VAR__", "__WING_PRINT_LN__",
|
||||||
|
"__AS_PROMISE__", "__WING_CLIPBOARD__"}));
|
||||||
|
|
||||||
AsPreprocesser::AsPreprocesser(asIScriptEngine *engine) : engine(engine) {
|
AsPreprocesser::AsPreprocesser(asIScriptEngine *engine) : engine(engine) {
|
||||||
Q_ASSERT(engine);
|
Q_ASSERT(engine);
|
||||||
|
@ -36,7 +49,58 @@ AsPreprocesser::AsPreprocesser(asIScriptEngine *engine) : engine(engine) {
|
||||||
pragmaCallback = nullptr;
|
pragmaCallback = nullptr;
|
||||||
pragmaParam = nullptr;
|
pragmaParam = nullptr;
|
||||||
|
|
||||||
definedWords = *DEFAULT_MARCO;
|
for (auto &m : *DEFAULT_MARCO) {
|
||||||
|
definedWords.insert(m, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
static QHash<QString, QByteArray> addInfos;
|
||||||
|
if (addInfos.isEmpty()) {
|
||||||
|
// software infos
|
||||||
|
auto ver = QVersionNumber::fromString(WINGHEX_VERSION);
|
||||||
|
addInfos.insert(QStringLiteral("__WING_VERSION__"),
|
||||||
|
"\"" WINGHEX_VERSION "\"");
|
||||||
|
addInfos.insert(QStringLiteral("__WING_VERSION_MAJOR__"),
|
||||||
|
QByteArray::number(ver.majorVersion()));
|
||||||
|
addInfos.insert(QStringLiteral("__WING_VERSION_MINOR__"),
|
||||||
|
QByteArray::number(ver.minorVersion()));
|
||||||
|
addInfos.insert(QStringLiteral("__WING_VERSION_PATCH__"),
|
||||||
|
QByteArray::number(ver.microVersion()));
|
||||||
|
addInfos.insert(QStringLiteral("__ANGELSCRIPT_VERSION__"),
|
||||||
|
"\"" ANGELSCRIPT_VERSION_STRING "\"");
|
||||||
|
addInfos.insert(QStringLiteral("__ANGELSCRIPT_VERSION_MAJOR__"),
|
||||||
|
QByteArray::number(ANGELSCRIPT_VERSION / 10000));
|
||||||
|
addInfos.insert(QStringLiteral("__ANGELSCRIPT_VERSION_MINOR__"),
|
||||||
|
QByteArray::number((ANGELSCRIPT_VERSION / 100) % 100));
|
||||||
|
addInfos.insert(QStringLiteral("__ANGELSCRIPT_VERSION_PATCH__"),
|
||||||
|
QByteArray::number(ANGELSCRIPT_VERSION % 100));
|
||||||
|
addInfos.insert(QStringLiteral("__WINGHEX_APPNAME__"),
|
||||||
|
"\"" APP_NAME "\"");
|
||||||
|
addInfos.insert(QStringLiteral("__WINGHEX_AUTHOR__"), "\"wingsummer\"");
|
||||||
|
ver = QLibraryInfo::version();
|
||||||
|
addInfos.insert(QStringLiteral("__QT_VERSION__"),
|
||||||
|
ver.toString().toUtf8().prepend('"').append('"'));
|
||||||
|
addInfos.insert(QStringLiteral("__QT_VERSION_MAJOR__"),
|
||||||
|
QByteArray::number(ver.majorVersion()));
|
||||||
|
addInfos.insert(QStringLiteral("__QT_VERSION_MINOR__"),
|
||||||
|
QByteArray::number(ver.minorVersion()));
|
||||||
|
addInfos.insert(QStringLiteral("__QT_VERSION_PATCH__"),
|
||||||
|
QByteArray::number(ver.microVersion()));
|
||||||
|
|
||||||
|
// UI
|
||||||
|
addInfos.insert(QStringLiteral("__LANG__"), LanguageManager::instance()
|
||||||
|
.defaultLocale()
|
||||||
|
.name()
|
||||||
|
.toUtf8()
|
||||||
|
.prepend('"')
|
||||||
|
.append('"'));
|
||||||
|
auto theme = SkinManager::instance().currentTheme();
|
||||||
|
auto te = QMetaEnum::fromType<SkinManager::Theme>();
|
||||||
|
addInfos.insert(
|
||||||
|
QStringLiteral("__THEME__"),
|
||||||
|
QByteArray(te.valueToKey(int(theme))).prepend('"').append('"'));
|
||||||
|
}
|
||||||
|
|
||||||
|
definedWords.insert(addInfos);
|
||||||
}
|
}
|
||||||
|
|
||||||
AsPreprocesser::~AsPreprocesser() { void ClearAll(); }
|
AsPreprocesser::~AsPreprocesser() { void ClearAll(); }
|
||||||
|
@ -85,10 +149,13 @@ void AsPreprocesser::setPragmaCallback(PRAGMACALLBACK_t callback,
|
||||||
pragmaParam = userParam;
|
pragmaParam = userParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsPreprocesser::defineWord(const QString &word) {
|
bool AsPreprocesser::defineWord(const QString &word, const QByteArray &value) {
|
||||||
if (!definedWords.contains(word)) {
|
// try to modify system marco is not allowed
|
||||||
definedWords.append(word);
|
if (DEFAULT_MARCO->contains(word)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
definedWords.insert(word, value);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int AsPreprocesser::sectionCount() const {
|
unsigned int AsPreprocesser::sectionCount() const {
|
||||||
|
@ -106,6 +173,9 @@ void AsPreprocesser::clearAll() { includedScripts.clear(); }
|
||||||
|
|
||||||
int AsPreprocesser::processScriptSection(const QByteArray &script,
|
int AsPreprocesser::processScriptSection(const QByteArray &script,
|
||||||
const QString §ionname) {
|
const QString §ionname) {
|
||||||
|
|
||||||
|
// #define DBG_CODEP // debug macro for pre-process
|
||||||
|
|
||||||
QVector<QPair<QString, bool>> includes;
|
QVector<QPair<QString, bool>> includes;
|
||||||
|
|
||||||
QByteArray modifiedScript = script;
|
QByteArray modifiedScript = script;
|
||||||
|
@ -114,8 +184,11 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
|
||||||
// shouldn't be compiled
|
// shouldn't be compiled
|
||||||
QByteArray::size_type pos = 0;
|
QByteArray::size_type pos = 0;
|
||||||
|
|
||||||
int nested = 0;
|
QStack<std::optional<bool>> m_condtionStack;
|
||||||
|
|
||||||
while (pos < modifiedScript.size()) {
|
while (pos < modifiedScript.size()) {
|
||||||
|
auto SECTION = sectionname.toUtf8();
|
||||||
|
|
||||||
asUINT len = 0;
|
asUINT len = 0;
|
||||||
asETokenClass t = engine->ParseToken(modifiedScript.data() + pos,
|
asETokenClass t = engine->ParseToken(modifiedScript.data() + pos,
|
||||||
modifiedScript.size() - pos, &len);
|
modifiedScript.size() - pos, &len);
|
||||||
|
@ -128,46 +201,399 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
|
||||||
modifiedScript.size() - pos, &len);
|
modifiedScript.size() - pos, &len);
|
||||||
Q_UNUSED(t);
|
Q_UNUSED(t);
|
||||||
|
|
||||||
QByteArray token = modifiedScript.mid(pos, len);
|
QByteArray token = modifiedScript.sliced(pos, len);
|
||||||
pos += len;
|
pos += len;
|
||||||
|
|
||||||
if (token == "if") {
|
bool isIf = token == QStringLiteral("if");
|
||||||
|
bool isIfDef = token == QStringLiteral("ifdef");
|
||||||
|
bool isIfnDef = token == QStringLiteral("ifndef");
|
||||||
|
bool isDef = token == QStringLiteral("define");
|
||||||
|
bool isUnDef = token == QStringLiteral("undef");
|
||||||
|
|
||||||
|
if (isIf || isIfDef || isIfnDef || isDef || isUnDef) {
|
||||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||||
modifiedScript.size() - pos, &len);
|
modifiedScript.size() - pos, &len);
|
||||||
if (t == asTC_WHITESPACE) {
|
if (t == asTC_WHITESPACE) {
|
||||||
|
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");
|
||||||
|
engine->WriteMessage(SECTION,
|
||||||
|
getLineCount(modifiedScript, pos),
|
||||||
|
1, asMSGTYPE_ERROR, str.toUtf8());
|
||||||
|
return asERROR;
|
||||||
|
}
|
||||||
pos += len;
|
pos += len;
|
||||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||||
modifiedScript.size() - pos, &len);
|
modifiedScript.size() - pos, &len);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t == asTC_IDENTIFIER) {
|
if (isIfDef || isIfnDef) {
|
||||||
QByteArray word = modifiedScript.mid(pos, len);
|
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);
|
||||||
|
|
||||||
// Overwrite the #if directive with space characters to
|
// Overwrite the directive with space characters to
|
||||||
// avoid compiler error
|
// avoid compiler error
|
||||||
pos += len;
|
pos += len;
|
||||||
overwriteCode(modifiedScript, start, pos - start);
|
overwriteCode(modifiedScript, start, pos - start);
|
||||||
|
|
||||||
// Has this identifier been defined by the application or
|
// Has this identifier been defined by the application
|
||||||
// not?
|
// or not?
|
||||||
if (!definedWords.contains(word)) {
|
if ((isIfDef && !definedWords.contains(word)) ||
|
||||||
// Exclude all the code until and including the #endif
|
(isIfnDef && definedWords.contains(word))) {
|
||||||
pos = excludeCode(modifiedScript, pos);
|
// Exclude all the code until and including the
|
||||||
|
// #endif
|
||||||
|
pos = excludeIfCode(modifiedScript, pos);
|
||||||
|
m_condtionStack.push(false);
|
||||||
|
} else {
|
||||||
|
m_condtionStack.push(true);
|
||||||
|
}
|
||||||
|
#ifdef DBG_CODEP
|
||||||
|
qDebug().noquote() << modifiedScript;
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
nested++;
|
auto str = QObject::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;
|
||||||
|
if (npos >= 0) {
|
||||||
|
codes = modifiedScript.sliced(pos, npos - pos);
|
||||||
|
} else {
|
||||||
|
codes = modifiedScript.sliced(pos);
|
||||||
|
}
|
||||||
|
overwriteCode(modifiedScript, start, npos - start);
|
||||||
|
|
||||||
|
auto &sm = ScriptMachine::instance();
|
||||||
|
bool ok = false;
|
||||||
|
// if expression should processed the marcos
|
||||||
|
auto ret = sm.evaluateDefine(
|
||||||
|
processIfExpression(
|
||||||
|
codes, getLineCount(modifiedScript, pos), SECTION),
|
||||||
|
ok);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
auto str = QObject::tr("CalIfFailed");
|
||||||
|
engine->WriteMessage(SECTION,
|
||||||
|
getLineCount(modifiedScript, pos),
|
||||||
|
1, asMSGTYPE_ERROR, str.toUtf8());
|
||||||
|
return asERROR;
|
||||||
|
} else {
|
||||||
|
if (ok) {
|
||||||
|
m_condtionStack.push(true);
|
||||||
|
} else {
|
||||||
|
pos = excludeIfCode(modifiedScript, npos);
|
||||||
|
m_condtionStack.push(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef DBG_CODEP
|
||||||
|
qDebug().noquote() << modifiedScript;
|
||||||
|
#endif
|
||||||
|
} else if (isDef) {
|
||||||
|
if (t == asTC_IDENTIFIER) {
|
||||||
|
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);
|
||||||
|
if (t == asTC_WHITESPACE) {
|
||||||
|
// line break?
|
||||||
|
if (std::any_of(
|
||||||
|
modifiedScript.data() + pos,
|
||||||
|
modifiedScript.data() + pos + len,
|
||||||
|
[](char ch) { return ch == '\n'; })) {
|
||||||
|
defineWord(word, {});
|
||||||
|
} else {
|
||||||
|
pos += len;
|
||||||
|
size_t total = 0;
|
||||||
|
auto v = QAsCodeParser::getToken(
|
||||||
|
engine, modifiedScript.data() + pos,
|
||||||
|
modifiedScript.size() - pos, &total);
|
||||||
|
// only support these things
|
||||||
|
switch (v) {
|
||||||
|
case ttIdentifier:
|
||||||
|
case ttIntConstant:
|
||||||
|
case ttFloatConstant:
|
||||||
|
case ttDoubleConstant:
|
||||||
|
case ttBitsConstant:
|
||||||
|
case ttStringConstant: {
|
||||||
|
auto v = modifiedScript.sliced(pos, total);
|
||||||
|
defineWord(word, v);
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
auto str = QObject::tr("UnexceptedToken");
|
||||||
|
engine->WriteMessage(
|
||||||
|
SECTION,
|
||||||
|
getLineCount(modifiedScript, pos), 1,
|
||||||
|
asMSGTYPE_ERROR, str.toUtf8());
|
||||||
|
return asERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += total;
|
||||||
|
}
|
||||||
|
overwriteCode(modifiedScript, start, pos - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure end line
|
||||||
|
if (endLinePassFailed(modifiedScript, pos)) {
|
||||||
|
auto str = QObject::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");
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Overwrite the directive with space characters to
|
||||||
|
// avoid compiler error
|
||||||
|
pos += len;
|
||||||
|
overwriteCode(modifiedScript, start, pos - start);
|
||||||
|
constexpr auto PREFIX = "__";
|
||||||
|
if (word.startsWith(PREFIX) && word.endsWith(PREFIX)) {
|
||||||
|
// Warning
|
||||||
|
auto str = QObject::tr("ReservedMarcoType");
|
||||||
|
engine->WriteMessage(
|
||||||
|
SECTION, getLineCount(modifiedScript, pos), 1,
|
||||||
|
asMSGTYPE_WARNING, str.toUtf8());
|
||||||
|
} else {
|
||||||
|
if (!definedWords.remove(word)) {
|
||||||
|
auto str = QObject::tr("MarcoNotFound:") + word;
|
||||||
|
engine->WriteMessage(
|
||||||
|
SECTION, getLineCount(modifiedScript, pos),
|
||||||
|
1, asMSGTYPE_WARNING, str.toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure end line
|
||||||
|
if (endLinePassFailed(modifiedScript, pos)) {
|
||||||
|
auto str = QObject::tr("UnexceptedToken");
|
||||||
|
engine->WriteMessage(
|
||||||
|
SECTION, getLineCount(modifiedScript, pos), 1,
|
||||||
|
asMSGTYPE_ERROR, str.toUtf8());
|
||||||
|
return asERROR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto str = QObject::tr("InvalidDef");
|
||||||
|
engine->WriteMessage(SECTION,
|
||||||
|
getLineCount(modifiedScript, pos),
|
||||||
|
1, asMSGTYPE_ERROR, str.toUtf8());
|
||||||
|
return asERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (token == "endif") {
|
} else if (token == "else") {
|
||||||
// Only remove the #endif if there was a matching #if
|
if (_isCodeCompleteMode) {
|
||||||
if (nested > 0) {
|
|
||||||
overwriteCode(modifiedScript, start, pos - start);
|
overwriteCode(modifiedScript, start, pos - start);
|
||||||
nested--;
|
continue;
|
||||||
|
}
|
||||||
|
if (m_condtionStack.isEmpty()) {
|
||||||
|
auto str = QObject::tr("NoMatchingIf");
|
||||||
|
engine->WriteMessage(SECTION,
|
||||||
|
getLineCount(modifiedScript, pos), 1,
|
||||||
|
asMSGTYPE_ERROR, str.toUtf8());
|
||||||
|
return asERROR;
|
||||||
|
} else {
|
||||||
|
overwriteCode(modifiedScript, start, pos - start);
|
||||||
|
auto opBool = m_condtionStack.top();
|
||||||
|
if (opBool) {
|
||||||
|
if (opBool.value()) {
|
||||||
|
pos = excludeElseCode(modifiedScript, pos);
|
||||||
|
m_condtionStack.top().reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure end line
|
||||||
|
if (endLinePassFailed(modifiedScript, pos - 1)) {
|
||||||
|
auto str = QObject::tr("UnexceptedToken");
|
||||||
|
engine->WriteMessage(
|
||||||
|
SECTION, getLineCount(modifiedScript, pos), 1,
|
||||||
|
asMSGTYPE_ERROR, str.toUtf8());
|
||||||
|
return asERROR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto str = QObject::tr("DupElseDef");
|
||||||
|
engine->WriteMessage(SECTION,
|
||||||
|
getLineCount(modifiedScript, pos),
|
||||||
|
1, asMSGTYPE_ERROR, str.toUtf8());
|
||||||
|
return asERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef DBG_CODEP
|
||||||
|
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");
|
||||||
|
engine->WriteMessage(SECTION,
|
||||||
|
getLineCount(modifiedScript, pos), 1,
|
||||||
|
asMSGTYPE_ERROR, str.toUtf8());
|
||||||
|
return asERROR;
|
||||||
|
} else {
|
||||||
|
overwriteCode(modifiedScript, start, pos - start);
|
||||||
|
m_condtionStack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure end line
|
||||||
|
if (endLinePassFailed(modifiedScript, pos)) {
|
||||||
|
auto str = QObject::tr("UnexceptedToken");
|
||||||
|
engine->WriteMessage(SECTION,
|
||||||
|
getLineCount(modifiedScript, pos), 1,
|
||||||
|
asMSGTYPE_ERROR, str.toUtf8());
|
||||||
|
return asERROR;
|
||||||
|
}
|
||||||
|
#ifdef DBG_CODEP
|
||||||
|
qDebug().noquote() << modifiedScript;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
|
if (t == asTC_WHITESPACE) {
|
||||||
|
npos += total;
|
||||||
|
t = engine->ParseToken(modifiedScript.data() + npos,
|
||||||
|
modifiedScript.size() - npos,
|
||||||
|
&total);
|
||||||
|
if (t == asTC_IDENTIFIER) {
|
||||||
|
// ok
|
||||||
|
auto word = modifiedScript.sliced(npos, total);
|
||||||
|
auto data = "(" + word +
|
||||||
|
")." PROMISE_YIELD
|
||||||
|
"()." PROMISE_UNWRAP "()";
|
||||||
|
auto oldLen = npos - pos + word.length();
|
||||||
|
modifiedScript.replace(pos, oldLen, data);
|
||||||
|
pos = npos;
|
||||||
|
pos += data.length() - oldLen + word.length();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto str = QObject::tr("UnexceptedToken");
|
||||||
|
engine->WriteMessage(SECTION,
|
||||||
|
getLineCount(modifiedScript, pos),
|
||||||
|
1, asMSGTYPE_ERROR, str.toUtf8());
|
||||||
|
return asERROR;
|
||||||
|
} else if (word == "__LINE__") {
|
||||||
|
auto data = QByteArray::number(
|
||||||
|
getLineCount(modifiedScript, pos));
|
||||||
|
modifiedScript.replace(pos, len, data);
|
||||||
|
pos += data.length();
|
||||||
|
continue;
|
||||||
|
} else if (word == "__SECTION__") {
|
||||||
|
auto data = SECTION;
|
||||||
|
data.prepend('"').append('"');
|
||||||
|
modifiedScript.replace(pos, len, data);
|
||||||
|
pos += data.length();
|
||||||
|
continue;
|
||||||
|
} else if (word == "__SECTION_BASE__") {
|
||||||
|
auto data = QFileInfo(SECTION).baseName().toUtf8();
|
||||||
|
data.prepend('"').append('"');
|
||||||
|
modifiedScript.replace(pos, len, data);
|
||||||
|
pos += data.length();
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
auto rword = findReplaceResult(word);
|
||||||
|
if (word != rword) {
|
||||||
|
modifiedScript.replace(pos, len, rword);
|
||||||
|
len = rword.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
|
||||||
pos += len;
|
pos += len;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DBG_CODEP
|
||||||
|
qDebug().noquote() << modifiedScript;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Then check for pre-processor directives
|
// Then check for pre-processor directives
|
||||||
pos = 0;
|
pos = 0;
|
||||||
while (pos >= 0 && pos < modifiedScript.size()) {
|
while (pos >= 0 && pos < modifiedScript.size()) {
|
||||||
|
@ -178,7 +604,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
|
||||||
pos += len;
|
pos += len;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QString token = modifiedScript.mid(pos, len);
|
QString token = modifiedScript.sliced(pos, len);
|
||||||
|
|
||||||
// Skip possible decorators before class and interface declarations
|
// Skip possible decorators before class and interface declarations
|
||||||
if (token == "shared" || token == "abstract" || token == "mixin" ||
|
if (token == "shared" || token == "abstract" || token == "mixin" ||
|
||||||
|
@ -194,7 +620,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
|
||||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||||
modifiedScript.size() - pos, &len);
|
modifiedScript.size() - pos, &len);
|
||||||
if (t == asTC_IDENTIFIER) {
|
if (t == asTC_IDENTIFIER) {
|
||||||
token = modifiedScript.mid(pos, len);
|
token = modifiedScript.sliced(pos, len);
|
||||||
if (token == "include") {
|
if (token == "include") {
|
||||||
pos += len;
|
pos += len;
|
||||||
t = engine->ParseToken(modifiedScript.data() + pos,
|
t = engine->ParseToken(modifiedScript.data() + pos,
|
||||||
|
@ -211,22 +637,22 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
|
||||||
modifiedScript[pos] == '\'')) {
|
modifiedScript[pos] == '\'')) {
|
||||||
// Get the include file
|
// Get the include file
|
||||||
QString includefile =
|
QString includefile =
|
||||||
modifiedScript.mid(pos + 1, len - 2);
|
modifiedScript.sliced(pos + 1, len - 2);
|
||||||
pos += len;
|
pos += len;
|
||||||
|
|
||||||
// Make sure the includeFile doesn't contain any
|
// Make sure the includeFile doesn't contain any
|
||||||
// line breaks
|
// line breaks
|
||||||
auto p = includefile.indexOf('\n');
|
auto p = includefile.indexOf('\n');
|
||||||
if (p >= 0) {
|
if (p >= 0) {
|
||||||
// TODO: Show the correct line number for the
|
|
||||||
// error
|
|
||||||
auto str =
|
auto str =
|
||||||
QObject::tr("Invalid file name for #include; "
|
QObject::tr("Invalid file name for #include; "
|
||||||
"it contains a line-break: ") +
|
"it contains a line-break: ") +
|
||||||
QStringLiteral("'") + includefile.left(p) +
|
QStringLiteral("'") + includefile.left(p) +
|
||||||
QStringLiteral("'");
|
QStringLiteral("'");
|
||||||
engine->WriteMessage(sectionname.toUtf8(), 0, 0,
|
engine->WriteMessage(
|
||||||
asMSGTYPE_ERROR, str.toUtf8());
|
sectionname.toUtf8(),
|
||||||
|
getLineCount(modifiedScript, pos), 1,
|
||||||
|
asMSGTYPE_ERROR, str.toUtf8());
|
||||||
} else {
|
} else {
|
||||||
// Store it for later processing
|
// Store it for later processing
|
||||||
includes.append({includefile, true});
|
includes.append({includefile, true});
|
||||||
|
@ -235,9 +661,8 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
|
||||||
// characters to avoid compiler error
|
// characters to avoid compiler error
|
||||||
overwriteCode(modifiedScript, start, pos - start);
|
overwriteCode(modifiedScript, start, pos - start);
|
||||||
}
|
}
|
||||||
}
|
} else if (t == asTC_KEYWORD &&
|
||||||
|
modifiedScript[pos] == '<') {
|
||||||
if (t == asTC_KEYWORD && modifiedScript[pos] == '<') {
|
|
||||||
pos += len;
|
pos += len;
|
||||||
|
|
||||||
// find the next '>'
|
// find the next '>'
|
||||||
|
@ -255,7 +680,8 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
QString includefile =
|
QString includefile =
|
||||||
modifiedScript.mid(pos, rpos - pos).trimmed();
|
modifiedScript.sliced(pos, rpos - pos)
|
||||||
|
.trimmed();
|
||||||
|
|
||||||
pos = rpos + 1;
|
pos = rpos + 1;
|
||||||
|
|
||||||
|
@ -264,17 +690,16 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
|
||||||
auto p = includefile.indexOf('\n');
|
auto p = includefile.indexOf('\n');
|
||||||
auto ws = includefile.indexOf(' ');
|
auto ws = includefile.indexOf(' ');
|
||||||
if (!includefile.isEmpty() && p >= 0 && ws >= 0) {
|
if (!includefile.isEmpty() && p >= 0 && ws >= 0) {
|
||||||
// TODO: Show the correct line number for
|
|
||||||
// the error
|
|
||||||
auto str =
|
auto str =
|
||||||
QObject::tr(
|
QObject::tr(
|
||||||
"Invalid file name for #include; "
|
"Invalid file name for #include; "
|
||||||
"it contains a line-break: ") +
|
"it contains a line-break: ") +
|
||||||
QStringLiteral("'") + includefile.left(p) +
|
QStringLiteral("'") + includefile.left(p) +
|
||||||
QStringLiteral("'");
|
QStringLiteral("'");
|
||||||
engine->WriteMessage(sectionname.toUtf8(), 0, 0,
|
engine->WriteMessage(
|
||||||
asMSGTYPE_ERROR,
|
sectionname.toUtf8(),
|
||||||
str.toUtf8());
|
getLineCount(modifiedScript, pos), 1,
|
||||||
|
asMSGTYPE_ERROR, str.toUtf8());
|
||||||
} else {
|
} else {
|
||||||
// Store it for later processing
|
// Store it for later processing
|
||||||
includes.append({includefile, false});
|
includes.append({includefile, false});
|
||||||
|
@ -303,22 +728,26 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
|
||||||
|
|
||||||
// Call the pragma callback
|
// Call the pragma callback
|
||||||
auto pragmaText =
|
auto pragmaText =
|
||||||
modifiedScript.mid(start + 7, pos - start - 7);
|
modifiedScript.sliced(start + 7, pos - start - 7);
|
||||||
|
|
||||||
// Overwrite the pragma directive with space characters
|
// Overwrite the pragma directive with space characters
|
||||||
// to avoid compiler error
|
// to avoid compiler error
|
||||||
overwriteCode(modifiedScript, start, pos - start);
|
overwriteCode(modifiedScript, start, pos - start);
|
||||||
|
|
||||||
int r = pragmaCallback
|
if (!_isCodeCompleteMode) {
|
||||||
? pragmaCallback(pragmaText, this, sectionname,
|
int r = pragmaCallback
|
||||||
pragmaParam)
|
? pragmaCallback(pragmaText, this,
|
||||||
: -1;
|
sectionname, pragmaParam)
|
||||||
if (r < 0) {
|
: -1;
|
||||||
// TODO: Report the correct line number
|
if (r < 0) {
|
||||||
engine->WriteMessage(
|
engine->WriteMessage(
|
||||||
sectionname.toUtf8(), 0, 0, asMSGTYPE_ERROR,
|
sectionname.toUtf8(),
|
||||||
QObject::tr("Invalid #pragma directive").toUtf8());
|
getLineCount(modifiedScript, pos), 1,
|
||||||
return r;
|
asMSGTYPE_ERROR,
|
||||||
|
QObject::tr("Invalid #pragma directive")
|
||||||
|
.toUtf8());
|
||||||
|
return r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -386,9 +815,6 @@ int AsPreprocesser::loadScriptSection(const QString &filename) {
|
||||||
QFileInfo(filename).absoluteFilePath() + QStringLiteral("'");
|
QFileInfo(filename).absoluteFilePath() + QStringLiteral("'");
|
||||||
engine->WriteMessage(filename.toUtf8(), 0, 0, asMSGTYPE_ERROR,
|
engine->WriteMessage(filename.toUtf8(), 0, 0, asMSGTYPE_ERROR,
|
||||||
msg.toUtf8());
|
msg.toUtf8());
|
||||||
|
|
||||||
// TODO: Write the file where this one was included from
|
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,7 +838,7 @@ bool AsPreprocesser::includeIfNotAlreadyIncluded(const QString &filename) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int AsPreprocesser::skipStatement(const QByteArray &modifiedScript, int pos) {
|
int AsPreprocesser::skipStatement(QByteArray &modifiedScript, int pos) {
|
||||||
asUINT len = 0;
|
asUINT len = 0;
|
||||||
|
|
||||||
// Skip until ; or { whichever comes first
|
// Skip until ; or { whichever comes first
|
||||||
|
@ -438,39 +864,74 @@ int AsPreprocesser::skipStatement(const QByteArray &modifiedScript, int pos) {
|
||||||
else if (modifiedScript[pos] == '}')
|
else if (modifiedScript[pos] == '}')
|
||||||
level--;
|
level--;
|
||||||
}
|
}
|
||||||
|
|
||||||
pos += len;
|
pos += len;
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
pos += 1;
|
pos += 1;
|
||||||
|
}
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
int AsPreprocesser::excludeCode(QByteArray &modifiedScript, int pos) {
|
int AsPreprocesser::excludeIfCode(QByteArray &modifiedScript, int pos) {
|
||||||
asUINT len = 0;
|
asUINT len = 0;
|
||||||
int nested = 0;
|
int nested = 0;
|
||||||
while (pos < (int)modifiedScript.size()) {
|
while (pos < (int)modifiedScript.size()) {
|
||||||
engine->ParseToken(modifiedScript.data() + pos,
|
engine->ParseToken(modifiedScript.data() + pos,
|
||||||
modifiedScript.size() - pos, &len);
|
modifiedScript.size() - pos, &len);
|
||||||
if (modifiedScript[pos] == '#') {
|
if (modifiedScript[pos] == '#') {
|
||||||
modifiedScript[pos] = ' ';
|
auto sharpPos = pos;
|
||||||
pos++;
|
pos++;
|
||||||
|
|
||||||
// Is it an #if or #endif directive?
|
// Is it an #if or #endif directive?
|
||||||
engine->ParseToken(modifiedScript.data() + pos,
|
engine->ParseToken(modifiedScript.data() + pos,
|
||||||
modifiedScript.size() - pos, &len);
|
modifiedScript.size() - pos, &len);
|
||||||
QString token = modifiedScript.mid(pos, len);
|
QString token = modifiedScript.sliced(pos, len);
|
||||||
overwriteCode(modifiedScript, pos, len);
|
|
||||||
|
|
||||||
if (token == "if") {
|
if (token == "if" || token == "ifdef" || token == "ifndef") {
|
||||||
nested++;
|
nested++;
|
||||||
} else if (token == "endif") {
|
} else if (token == "else") {
|
||||||
if (nested-- == 0) {
|
if (nested-- == 0) {
|
||||||
pos += len;
|
pos = sharpPos;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
modifiedScript[sharpPos] = ' ';
|
||||||
|
overwriteCode(modifiedScript, pos, len);
|
||||||
|
} else if (modifiedScript[pos] != '\n') {
|
||||||
|
overwriteCode(modifiedScript, pos, len);
|
||||||
|
}
|
||||||
|
pos += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AsPreprocesser::excludeElseCode(QByteArray &modifiedScript, int pos) {
|
||||||
|
asUINT len = 0;
|
||||||
|
int nested = 0;
|
||||||
|
while (pos < (int)modifiedScript.size()) {
|
||||||
|
engine->ParseToken(modifiedScript.data() + pos,
|
||||||
|
modifiedScript.size() - pos, &len);
|
||||||
|
if (modifiedScript[pos] == '#') {
|
||||||
|
auto sharpPos = pos;
|
||||||
|
pos++;
|
||||||
|
// Is it an #if or #endif directive?
|
||||||
|
engine->ParseToken(modifiedScript.data() + pos,
|
||||||
|
modifiedScript.size() - pos, &len);
|
||||||
|
QString token = modifiedScript.sliced(pos, len);
|
||||||
|
|
||||||
|
if (token == "if" || token == "ifdef" || token == "ifndef") {
|
||||||
|
nested++;
|
||||||
|
} else if (token == "endif") {
|
||||||
|
if (nested-- == 0) {
|
||||||
|
pos = sharpPos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modifiedScript[sharpPos] = ' ';
|
||||||
|
overwriteCode(modifiedScript, pos, len);
|
||||||
} else if (modifiedScript[pos] != '\n') {
|
} else if (modifiedScript[pos] != '\n') {
|
||||||
overwriteCode(modifiedScript, pos, len);
|
overwriteCode(modifiedScript, pos, len);
|
||||||
}
|
}
|
||||||
|
@ -490,6 +951,87 @@ void AsPreprocesser::overwriteCode(QByteArray &modifiedScript, int start,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AsPreprocesser::getLineCount(const QByteArray &modifiedScript,
|
||||||
|
int pos) const {
|
||||||
|
pos = qBound(0, pos, int(modifiedScript.size()));
|
||||||
|
return std::count_if(modifiedScript.begin(),
|
||||||
|
std::next(modifiedScript.begin(), pos),
|
||||||
|
[](char ch) -> bool { return ch == '\n'; }) +
|
||||||
|
1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsPreprocesser::endLinePassFailed(const QByteArray &modifiedScript,
|
||||||
|
int pos) { // ensure '\n' end line
|
||||||
|
bool endError = false;
|
||||||
|
asUINT len = 0;
|
||||||
|
auto t = engine->ParseToken(modifiedScript.data() + pos,
|
||||||
|
modifiedScript.size() - pos, &len);
|
||||||
|
if (t == asTC_WHITESPACE) {
|
||||||
|
if (!std::any_of(modifiedScript.data() + pos,
|
||||||
|
modifiedScript.data() + pos + len,
|
||||||
|
[](char ch) { return ch == '\n'; })) {
|
||||||
|
endError = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (len != 0) {
|
||||||
|
endError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return endError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray
|
||||||
|
AsPreprocesser::processIfExpression(const QByteArray &codes, int currentLine,
|
||||||
|
const QByteArray ¤tSection) {
|
||||||
|
QByteArray ret;
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
auto total = codes.length();
|
||||||
|
while (pos < total) {
|
||||||
|
asUINT len = 0;
|
||||||
|
auto t =
|
||||||
|
engine->ParseToken(codes.data() + pos, codes.size() - pos, &len);
|
||||||
|
auto word = codes.sliced(pos, len);
|
||||||
|
if (t == asTC_IDENTIFIER) {
|
||||||
|
if (word == "__LINE__") {
|
||||||
|
ret.append(currentLine);
|
||||||
|
} else if (word == "__SECTION__") {
|
||||||
|
auto s = currentSection;
|
||||||
|
s.prepend('"').append('"');
|
||||||
|
ret.append(s);
|
||||||
|
} else {
|
||||||
|
ret.append(findReplaceResult(word));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret.append(word);
|
||||||
|
}
|
||||||
|
pos += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray AsPreprocesser::findReplaceResult(const QByteArray &v) {
|
||||||
|
QByteArray r = v;
|
||||||
|
while (definedWords.contains(r)) {
|
||||||
|
r = definedWords.value(r);
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsPreprocesser::isCodeCompleteMode() const { return _isCodeCompleteMode; }
|
||||||
|
|
||||||
|
void AsPreprocesser::setIsCodeCompleteMode(bool newIsCodeCompleteMode) {
|
||||||
|
_isCodeCompleteMode = newIsCodeCompleteMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<QString, QByteArray> AsPreprocesser::definedMacros() const {
|
||||||
|
return definedWords;
|
||||||
|
}
|
||||||
|
|
||||||
void AsPreprocesser::addScriptSection(const QString §ion,
|
void AsPreprocesser::addScriptSection(const QString §ion,
|
||||||
const QByteArray &code) {
|
const QByteArray &code) {
|
||||||
ScriptData data;
|
ScriptData data;
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#pragma warning(disable : 4786)
|
#pragma warning(disable : 4786)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <QEventLoop>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
@ -62,6 +63,16 @@ typedef int (*PRAGMACALLBACK_t)(const QByteArray &pragmaText,
|
||||||
// Helper class for loading and pre-processing script files to
|
// Helper class for loading and pre-processing script files to
|
||||||
// support include directives declarations
|
// support include directives declarations
|
||||||
|
|
||||||
|
/** for macros, we support:
|
||||||
|
* * #if <conditions>
|
||||||
|
* * #else
|
||||||
|
* * #endif
|
||||||
|
* * #define <word>
|
||||||
|
* * #define <word> <string|int64|double>
|
||||||
|
* * #undef <word>
|
||||||
|
* * #ifdef <word>
|
||||||
|
* * #ifndef <word>
|
||||||
|
*/
|
||||||
class AsPreprocesser {
|
class AsPreprocesser {
|
||||||
public:
|
public:
|
||||||
explicit AsPreprocesser(asIScriptEngine *engine);
|
explicit AsPreprocesser(asIScriptEngine *engine);
|
||||||
|
@ -81,6 +92,8 @@ public:
|
||||||
int loadSectionFromFile(const QString &filename);
|
int loadSectionFromFile(const QString &filename);
|
||||||
int loadSectionFromMemory(const QString §ion, const QByteArray &code);
|
int loadSectionFromMemory(const QString §ion, const QByteArray &code);
|
||||||
|
|
||||||
|
void addScriptSection(const QString §ion, const QByteArray &code);
|
||||||
|
|
||||||
QList<ScriptData> scriptData() const;
|
QList<ScriptData> scriptData() const;
|
||||||
|
|
||||||
// Returns the engine
|
// Returns the engine
|
||||||
|
@ -93,25 +106,42 @@ public:
|
||||||
void setPragmaCallback(PRAGMACALLBACK_t callback, void *userParam);
|
void setPragmaCallback(PRAGMACALLBACK_t callback, void *userParam);
|
||||||
|
|
||||||
// Add a pre-processor define for conditional compilation
|
// Add a pre-processor define for conditional compilation
|
||||||
void defineWord(const QString &word);
|
bool defineWord(const QString &word, const QByteArray &value = {});
|
||||||
|
|
||||||
// Enumerate included script sections
|
// Enumerate included script sections
|
||||||
unsigned int sectionCount() const;
|
unsigned int sectionCount() const;
|
||||||
|
|
||||||
QString sectionName(unsigned int idx) const;
|
QString sectionName(unsigned int idx) const;
|
||||||
|
|
||||||
|
bool isCodeCompleteMode() const;
|
||||||
|
void setIsCodeCompleteMode(bool newIsCodeCompleteMode);
|
||||||
|
|
||||||
|
QHash<QString, QByteArray> definedMacros() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString translate(const char *str);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void clearAll();
|
void clearAll();
|
||||||
void addScriptSection(const QString §ion, const QByteArray &code);
|
|
||||||
int processScriptSection(const QByteArray &script,
|
int processScriptSection(const QByteArray &script,
|
||||||
const QString §ionname);
|
const QString §ionname);
|
||||||
int loadScriptSection(const QString &filename);
|
int loadScriptSection(const QString &filename);
|
||||||
bool includeIfNotAlreadyIncluded(const QString &filename);
|
bool includeIfNotAlreadyIncluded(const QString &filename);
|
||||||
|
|
||||||
int skipStatement(const QByteArray &modifiedScript, int pos);
|
int skipStatement(QByteArray &modifiedScript, int pos);
|
||||||
|
|
||||||
int excludeCode(QByteArray &modifiedScript, int pos);
|
int excludeIfCode(QByteArray &modifiedScript, int pos);
|
||||||
|
int excludeElseCode(QByteArray &modifiedScript, int pos);
|
||||||
void overwriteCode(QByteArray &modifiedScript, int start, int len);
|
void overwriteCode(QByteArray &modifiedScript, int start, int len);
|
||||||
|
int getLineCount(const QByteArray &modifiedScript, int pos) const;
|
||||||
|
|
||||||
|
bool endLinePassFailed(const QByteArray &modifiedScript, int pos);
|
||||||
|
|
||||||
|
QByteArray processIfExpression(const QByteArray &codes, int currentLine,
|
||||||
|
const QByteArray ¤tSection);
|
||||||
|
|
||||||
|
QByteArray findReplaceResult(const QByteArray &v);
|
||||||
|
|
||||||
asIScriptEngine *engine;
|
asIScriptEngine *engine;
|
||||||
QList<ScriptData> modifiedScripts;
|
QList<ScriptData> modifiedScripts;
|
||||||
|
@ -124,7 +154,10 @@ protected:
|
||||||
|
|
||||||
QStringList includedScripts;
|
QStringList includedScripts;
|
||||||
|
|
||||||
QStringList definedWords;
|
QHash<QString, QByteArray> definedWords;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _isCodeCompleteMode = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ASPREPROCESSER_H
|
#endif // ASPREPROCESSER_H
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "inspectqtloghelper.h"
|
#include "inspectqtloghelper.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#define INFOLOG(msg) "<font color=\"green\">" + msg + "</font>"
|
#define INFOLOG(msg) "<font color=\"green\">" + msg + "</font>"
|
||||||
|
|
|
@ -3126,8 +3126,8 @@ void PluginSystem::applyFunctionTables(IWingPluginBase *plg,
|
||||||
plg->setProperty("__CALL_POINTER__", quintptr(this));
|
plg->setProperty("__CALL_POINTER__", quintptr(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString PluginSystem::type2AngelScriptString(IWingPlugin::MetaType type,
|
QString PluginSystem::type2AngelScriptString(uint type, bool isArg,
|
||||||
bool isArg, bool noModifier) {
|
bool noModifier) {
|
||||||
auto isArray = !!(type & WingHex::IWingPlugin::Array);
|
auto isArray = !!(type & WingHex::IWingPlugin::Array);
|
||||||
auto isList = !!(type & WingHex::IWingPlugin::List);
|
auto isList = !!(type & WingHex::IWingPlugin::List);
|
||||||
auto isContainer = isArray || isList;
|
auto isContainer = isArray || isList;
|
||||||
|
|
|
@ -175,8 +175,8 @@ public:
|
||||||
|
|
||||||
static QString getPUID(IWingPluginBase *p);
|
static QString getPUID(IWingPluginBase *p);
|
||||||
|
|
||||||
static QString type2AngelScriptString(IWingPlugin::MetaType type,
|
static QString type2AngelScriptString(uint type, bool isArg,
|
||||||
bool isArg, bool noModifier = false);
|
bool noModifier = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadExtPlugin();
|
void loadExtPlugin();
|
||||||
|
|
|
@ -53,6 +53,15 @@ QAsCodeParser::parse(const QByteArray &codes) {
|
||||||
return parseScript(false);
|
return parseScript(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eTokenType QAsCodeParser::getToken(asIScriptEngine *engine, const char *string,
|
||||||
|
size_t stringLength, size_t *tokenLength) {
|
||||||
|
auto e = dynamic_cast<asCScriptEngine *>(engine);
|
||||||
|
if (e) {
|
||||||
|
return e->tok.GetToken(string, stringLength, tokenLength, nullptr);
|
||||||
|
}
|
||||||
|
return ttUnrecognizedToken;
|
||||||
|
}
|
||||||
|
|
||||||
QList<QAsCodeParser::Symbol>
|
QList<QAsCodeParser::Symbol>
|
||||||
QAsCodeParser::parseAndIntell(qsizetype offset, const QByteArray &codes) {
|
QAsCodeParser::parseAndIntell(qsizetype offset, const QByteArray &codes) {
|
||||||
return parseIntell(offset, parse(codes));
|
return parseIntell(offset, parse(codes));
|
||||||
|
@ -572,7 +581,7 @@ QAsCodeParser::parseStatementBlock(const QByteArrayList &ns,
|
||||||
rewindTo(&t1);
|
rewindTo(&t1);
|
||||||
|
|
||||||
if (isVarDecl()) {
|
if (isVarDecl()) {
|
||||||
syms.top().append(parseDeclaration(ns));
|
syms.top().append(parseDeclaration(end, ns));
|
||||||
} else {
|
} else {
|
||||||
_isSyntaxError = true;
|
_isSyntaxError = true;
|
||||||
}
|
}
|
||||||
|
@ -1069,8 +1078,8 @@ void QAsCodeParser::superficiallyParseVarInit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QAsCodeParser::Symbol>
|
QList<QAsCodeParser::Symbol>
|
||||||
QAsCodeParser::parseDeclaration(const QByteArrayList &ns, bool isClassProp,
|
QAsCodeParser::parseDeclaration(qsizetype end, const QByteArrayList &ns,
|
||||||
bool isGlobalVar) {
|
bool isClassProp, bool isGlobalVar) {
|
||||||
QList<QAsCodeParser::Symbol> ret;
|
QList<QAsCodeParser::Symbol> ret;
|
||||||
Symbol sym;
|
Symbol sym;
|
||||||
|
|
||||||
|
@ -1097,6 +1106,10 @@ QAsCodeParser::parseDeclaration(const QByteArrayList &ns, bool isClassProp,
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// Parse identifier
|
// Parse identifier
|
||||||
auto id = parseIdentifier();
|
auto id = parseIdentifier();
|
||||||
|
if (end >= 0 && id.pos > end) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (_isSyntaxError)
|
if (_isSyntaxError)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -1437,8 +1450,9 @@ void QAsCodeParser::parseNamespace() {
|
||||||
QList<QAsCodeParser::Symbol>
|
QList<QAsCodeParser::Symbol>
|
||||||
QAsCodeParser::parseGlobalVarDecls(const QByteArrayList &ns,
|
QAsCodeParser::parseGlobalVarDecls(const QByteArrayList &ns,
|
||||||
const QByteArray &code) {
|
const QByteArray &code) {
|
||||||
|
reset();
|
||||||
_code = code;
|
_code = code;
|
||||||
return parseDeclaration(ns, false, true);
|
return parseDeclaration(-1, ns, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
QAsCodeParser::CodeSegment QAsCodeParser::parseFunctionMethod(Visiblity &vis) {
|
QAsCodeParser::CodeSegment QAsCodeParser::parseFunctionMethod(Visiblity &vis) {
|
||||||
|
@ -1625,7 +1639,7 @@ QAsCodeParser::parseClassContent(qsizetype offset, const QByteArrayList &ns,
|
||||||
auto vp = parseVirtualPropertyDecl(true, false);
|
auto vp = parseVirtualPropertyDecl(true, false);
|
||||||
syms.append(vp);
|
syms.append(vp);
|
||||||
} else if (isVarDecl()) {
|
} else if (isVarDecl()) {
|
||||||
auto decl = parseDeclaration(ns, true);
|
auto decl = parseDeclaration(offset, ns, true);
|
||||||
syms.append(decl);
|
syms.append(decl);
|
||||||
} else if (t.type == ttEndStatement)
|
} else if (t.type == ttEndStatement)
|
||||||
// Skip empty declarations
|
// Skip empty declarations
|
||||||
|
@ -2303,66 +2317,6 @@ bool QAsCodeParser::findTokenAfterType(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QAsCodeParser::isConstant(int tokenType) {
|
|
||||||
if (tokenType == ttIntConstant || tokenType == ttFloatConstant ||
|
|
||||||
tokenType == ttDoubleConstant || tokenType == ttStringConstant ||
|
|
||||||
tokenType == ttMultilineStringConstant ||
|
|
||||||
tokenType == ttHeredocStringConstant || tokenType == ttTrue ||
|
|
||||||
tokenType == ttFalse || tokenType == ttBitsConstant ||
|
|
||||||
tokenType == ttNull)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QAsCodeParser::isOperator(int tokenType) {
|
|
||||||
if (tokenType == ttPlus || tokenType == ttMinus || tokenType == ttStar ||
|
|
||||||
tokenType == ttSlash || tokenType == ttPercent ||
|
|
||||||
tokenType == ttStarStar || tokenType == ttAnd || tokenType == ttOr ||
|
|
||||||
tokenType == ttXor || tokenType == ttEqual || tokenType == ttNotEqual ||
|
|
||||||
tokenType == ttLessThan || tokenType == ttLessThanOrEqual ||
|
|
||||||
tokenType == ttGreaterThan || tokenType == ttGreaterThanOrEqual ||
|
|
||||||
tokenType == ttAmp || tokenType == ttBitOr || tokenType == ttBitXor ||
|
|
||||||
tokenType == ttBitShiftLeft || tokenType == ttBitShiftRight ||
|
|
||||||
tokenType == ttBitShiftRightArith || tokenType == ttIs ||
|
|
||||||
tokenType == ttNotIs)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QAsCodeParser::isPreOperator(int tokenType) {
|
|
||||||
if (tokenType == ttMinus || tokenType == ttPlus || tokenType == ttNot ||
|
|
||||||
tokenType == ttInc || tokenType == ttDec || tokenType == ttBitNot ||
|
|
||||||
tokenType == ttHandle)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QAsCodeParser::isPostOperator(int tokenType) {
|
|
||||||
if (tokenType == ttInc || // post increment
|
|
||||||
tokenType == ttDec || // post decrement
|
|
||||||
tokenType == ttDot || // member access
|
|
||||||
tokenType == ttOpenBracket || // index operator
|
|
||||||
tokenType ==
|
|
||||||
ttOpenParenthesis) // argument list for call on function pointer
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QAsCodeParser::isAssignOperator(int tokenType) {
|
|
||||||
if (tokenType == ttAssignment || tokenType == ttAddAssign ||
|
|
||||||
tokenType == ttSubAssign || tokenType == ttMulAssign ||
|
|
||||||
tokenType == ttDivAssign || tokenType == ttModAssign ||
|
|
||||||
tokenType == ttPowAssign || tokenType == ttAndAssign ||
|
|
||||||
tokenType == ttOrAssign || tokenType == ttXorAssign ||
|
|
||||||
tokenType == ttShiftLeftAssign || tokenType == ttShiftRightLAssign ||
|
|
||||||
tokenType == ttShiftRightAAssign)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QAsCodeParser::typeExist(const QString &t) {
|
bool QAsCodeParser::typeExist(const QString &t) {
|
||||||
Q_UNUSED(t);
|
Q_UNUSED(t);
|
||||||
// TODO: don't check
|
// TODO: don't check
|
||||||
|
|
|
@ -106,12 +106,8 @@ public:
|
||||||
QList<Symbol> parseAndIntell(qsizetype offset, const QByteArray &codes);
|
QList<Symbol> parseAndIntell(qsizetype offset, const QByteArray &codes);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// utilities
|
static eTokenType getToken(asIScriptEngine *engine, const char *string,
|
||||||
static bool isConstant(int tokenType);
|
size_t stringLength, size_t *tokenLength);
|
||||||
static bool isOperator(int tokenType);
|
|
||||||
static bool isPreOperator(int tokenType);
|
|
||||||
static bool isPostOperator(int tokenType);
|
|
||||||
static bool isAssignOperator(int tokenType);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<QAsCodeParser::CodeSegment> parseScript(bool inBlock);
|
QList<QAsCodeParser::CodeSegment> parseScript(bool inBlock);
|
||||||
|
@ -161,7 +157,7 @@ private:
|
||||||
|
|
||||||
QByteArrayList parseOptionalScope();
|
QByteArrayList parseOptionalScope();
|
||||||
|
|
||||||
QList<Symbol> parseDeclaration(const QByteArrayList &ns,
|
QList<Symbol> parseDeclaration(qsizetype end, const QByteArrayList &ns,
|
||||||
bool isClassProp = false,
|
bool isClassProp = false,
|
||||||
bool isGlobalVar = false);
|
bool isGlobalVar = false);
|
||||||
|
|
||||||
|
|
|
@ -31,19 +31,92 @@
|
||||||
#include "angelobjstring.h"
|
#include "angelobjstring.h"
|
||||||
#include "class/appmanager.h"
|
#include "class/appmanager.h"
|
||||||
#include "class/asbuilder.h"
|
#include "class/asbuilder.h"
|
||||||
|
#include "class/logger.h"
|
||||||
#include "class/pluginsystem.h"
|
#include "class/pluginsystem.h"
|
||||||
#include "class/qascodeparser.h"
|
#include "class/qascodeparser.h"
|
||||||
|
#include "class/settingmanager.h"
|
||||||
#include "define.h"
|
#include "define.h"
|
||||||
|
#include "scriptaddon/aspromise.hpp"
|
||||||
#include "scriptaddon/scriptcolor.h"
|
#include "scriptaddon/scriptcolor.h"
|
||||||
#include "scriptaddon/scriptfile.h"
|
#include "scriptaddon/scriptfile.h"
|
||||||
#include "scriptaddon/scriptjson.h"
|
#include "scriptaddon/scriptjson.h"
|
||||||
#include "scriptaddon/scriptqstring.h"
|
#include "scriptaddon/scriptqstring.h"
|
||||||
#include "scriptaddon/scriptregex.h"
|
#include "scriptaddon/scriptregex.h"
|
||||||
|
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QMimeData>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QScopeGuard>
|
#include <QScopeGuard>
|
||||||
|
|
||||||
ScriptMachine::~ScriptMachine() { destoryMachine(); }
|
class StringFormatter {
|
||||||
|
public:
|
||||||
|
static QString format(const QString &input, uint indentSize = 4) {
|
||||||
|
QString output;
|
||||||
|
int level = 0;
|
||||||
|
bool inString = false;
|
||||||
|
QChar stringDelim;
|
||||||
|
bool escape = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < input.size(); ++i) {
|
||||||
|
QChar c = input[i];
|
||||||
|
|
||||||
|
if (inString) {
|
||||||
|
output += c;
|
||||||
|
if (escape) {
|
||||||
|
escape = false;
|
||||||
|
} else if (c == '\\') {
|
||||||
|
escape = true;
|
||||||
|
} else if (c == stringDelim) {
|
||||||
|
inString = false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isQuote(c)) {
|
||||||
|
inString = true;
|
||||||
|
stringDelim = c;
|
||||||
|
output += c;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c.unicode()) {
|
||||||
|
case '{':
|
||||||
|
output += QStringLiteral("{\n");
|
||||||
|
++level;
|
||||||
|
output += QString(level * indentSize, ' ');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '}':
|
||||||
|
output += QStringLiteral("\n");
|
||||||
|
--level;
|
||||||
|
output +=
|
||||||
|
QString(level * indentSize, ' ') + QStringLiteral("}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ',':
|
||||||
|
output +=
|
||||||
|
QStringLiteral(",\n") + QString(level * indentSize, ' ');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (c.isSpace()) {
|
||||||
|
// collapse multiple spaces outside strings
|
||||||
|
if (!output.isEmpty() && !output.endsWith(' ')) {
|
||||||
|
output += ' ';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output += c;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool isQuote(QChar c) { return c == '"' || c == '\''; }
|
||||||
|
};
|
||||||
|
|
||||||
bool ScriptMachine::init() {
|
bool ScriptMachine::init() {
|
||||||
if (isInited()) {
|
if (isInited()) {
|
||||||
|
@ -72,6 +145,11 @@ bool ScriptMachine::isRunning(ConsoleMode mode) const {
|
||||||
return _ctx.value(mode) != nullptr;
|
return _ctx.value(mode) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define INS_1 "const ?&in = null"
|
||||||
|
#define INS_2 INS_1 ", " INS_1
|
||||||
|
#define INS_4 INS_2 ", " INS_2
|
||||||
|
#define INS_8 INS_4 ", " INS_4
|
||||||
|
|
||||||
bool ScriptMachine::configureEngine() {
|
bool ScriptMachine::configureEngine() {
|
||||||
if (_engine == nullptr) {
|
if (_engine == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -138,10 +216,20 @@ bool ScriptMachine::configureEngine() {
|
||||||
_rtypes[RegisteredType::tColor] =
|
_rtypes[RegisteredType::tColor] =
|
||||||
q_check_ptr(_engine->GetTypeInfoByName("color"));
|
q_check_ptr(_engine->GetTypeInfoByName("color"));
|
||||||
|
|
||||||
|
_engine->SetDefaultAccessMask(0x1);
|
||||||
|
|
||||||
// Register a couple of extra functions for the scripts
|
// Register a couple of extra functions for the scripts
|
||||||
r = _engine->RegisterGlobalFunction("void print(? &in obj)",
|
r = _engine->RegisterGlobalFunction(
|
||||||
asMETHOD(ScriptMachine, print),
|
"void print(const ? &in obj, const ? &in = null," INS_8 ")",
|
||||||
asCALL_THISCALL_ASGLOBAL, this);
|
asFUNCTION(print), asCALL_GENERIC);
|
||||||
|
Q_ASSERT(r >= 0);
|
||||||
|
if (r < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = _engine->RegisterGlobalFunction(
|
||||||
|
"void println(const ? &in obj, const ? &in = null," INS_8 ")",
|
||||||
|
asFUNCTION(println), asCALL_GENERIC);
|
||||||
Q_ASSERT(r >= 0);
|
Q_ASSERT(r >= 0);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -155,6 +243,22 @@ bool ScriptMachine::configureEngine() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r = _engine->RegisterGlobalFunction("string stringify(? &in obj)",
|
||||||
|
asMETHOD(ScriptMachine, stringify),
|
||||||
|
asCALL_THISCALL_ASGLOBAL, this);
|
||||||
|
Q_ASSERT(r >= 0);
|
||||||
|
if (r < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = _engine->RegisterGlobalFunction(
|
||||||
|
"string beautify(const string &in str, uint indent = 4)",
|
||||||
|
asFUNCTION(beautify), asCALL_CDECL);
|
||||||
|
Q_ASSERT(r >= 0);
|
||||||
|
if (r < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
r = _engine->RegisterGlobalFunction(
|
r = _engine->RegisterGlobalFunction(
|
||||||
"int exec(string &out output, const string &in exe, "
|
"int exec(string &out output, const string &in exe, "
|
||||||
"const string &in params = \"\", int timeout = 3000)",
|
"const string &in params = \"\", int timeout = 3000)",
|
||||||
|
@ -198,7 +302,8 @@ bool ScriptMachine::configureEngine() {
|
||||||
PluginSystem::instance().angelApi()->installAPI(this);
|
PluginSystem::instance().angelApi()->installAPI(this);
|
||||||
|
|
||||||
// create module for Console
|
// create module for Console
|
||||||
_engine->GetModule("WINGCONSOLE", asGM_ALWAYS_CREATE);
|
auto mod = _engine->GetModule("WINGCONSOLE", asGM_ALWAYS_CREATE);
|
||||||
|
mod->SetAccessMask(0x1);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -252,18 +357,56 @@ void ScriptMachine::exceptionCallback(asIScriptContext *context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptMachine::print(void *ref, int typeId) {
|
void ScriptMachine::print(asIScriptGeneric *args) {
|
||||||
auto context = asGetActiveContext();
|
auto context = asGetActiveContext();
|
||||||
if (context) {
|
if (context) {
|
||||||
ConsoleMode mode = ConsoleMode(reinterpret_cast<asPWORD>(
|
ConsoleMode mode = ConsoleMode(reinterpret_cast<asPWORD>(
|
||||||
context->GetUserData(AsUserDataType::UserData_ContextMode)));
|
context->GetUserData(AsUserDataType::UserData_ContextMode)));
|
||||||
|
|
||||||
|
auto &m = ScriptMachine::instance();
|
||||||
|
|
||||||
MessageInfo info;
|
MessageInfo info;
|
||||||
info.mode = mode;
|
info.mode = mode;
|
||||||
info.type = MessageType::Print;
|
info.type = MessageType::Print;
|
||||||
info.message = _debugger->toString(ref, typeId, _engine);
|
|
||||||
|
|
||||||
outputMessage(info);
|
for (int i = 0; i < args->GetArgCount(); ++i) {
|
||||||
|
void *ref = args->GetArgAddress(i);
|
||||||
|
int typeId = args->GetArgTypeId(i);
|
||||||
|
|
||||||
|
if (typeId) {
|
||||||
|
info.message.append(
|
||||||
|
m.debugger()->toString(ref, typeId, m.engine()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.outputMessage(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptMachine::println(asIScriptGeneric *args) {
|
||||||
|
auto context = asGetActiveContext();
|
||||||
|
if (context) {
|
||||||
|
ConsoleMode mode = ConsoleMode(reinterpret_cast<asPWORD>(
|
||||||
|
context->GetUserData(AsUserDataType::UserData_ContextMode)));
|
||||||
|
|
||||||
|
auto &m = ScriptMachine::instance();
|
||||||
|
|
||||||
|
MessageInfo info;
|
||||||
|
info.mode = mode;
|
||||||
|
info.type = MessageType::Print;
|
||||||
|
|
||||||
|
for (int i = 0; i < args->GetArgCount(); ++i) {
|
||||||
|
void *ref = args->GetArgAddress(i);
|
||||||
|
int typeId = args->GetArgTypeId(i);
|
||||||
|
|
||||||
|
if (typeId) {
|
||||||
|
info.message
|
||||||
|
.append(m.debugger()->toString(ref, typeId, m.engine()))
|
||||||
|
.append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.outputMessage(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,8 +460,26 @@ int ScriptMachine::execSystemCmd(QString &out, const QString &exe,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ScriptMachine::beautify(const QString &str, uint indent) {
|
||||||
|
return StringFormatter::format(str, indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ScriptMachine::stringify(void *ref, int typeId) {
|
||||||
|
return _debugger->toString(ref, typeId, _engine);
|
||||||
|
}
|
||||||
|
|
||||||
bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
|
bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
|
||||||
bool isInDebug) {
|
bool isInDebug, int *retCode) {
|
||||||
|
Q_ASSERT(mode != Interactive);
|
||||||
|
if (QThread::currentThread() != qApp->thread()) {
|
||||||
|
Logger::warning(tr("Code must be exec in the main thread"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (script.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Compile the script
|
// Compile the script
|
||||||
auto mod = createModule(mode);
|
auto mod = createModule(mode);
|
||||||
// script-running is not allowed in interactive mode
|
// script-running is not allowed in interactive mode
|
||||||
|
@ -326,13 +487,11 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QScopeGuard guard([mod, mode]() {
|
QScopeGuard guard([mod]() {
|
||||||
if (mode != ConsoleMode::Interactive) {
|
// Before leaving, allow the engine to clean up remaining objects by
|
||||||
// Before leaving, allow the engine to clean up remaining objects by
|
// discarding the module and doing a full garbage collection so that
|
||||||
// discarding the module and doing a full garbage collection so that
|
// this can also be debugged if desired
|
||||||
// this can also be debugged if desired
|
mod->Discard();
|
||||||
mod->Discard();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
asPWORD isDbg = 0;
|
asPWORD isDbg = 0;
|
||||||
|
@ -353,12 +512,17 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
|
||||||
builder.setPragmaCallback(&ScriptMachine::pragmaCallback, this);
|
builder.setPragmaCallback(&ScriptMachine::pragmaCallback, this);
|
||||||
builder.setIncludeCallback(&ScriptMachine::includeCallback, this);
|
builder.setIncludeCallback(&ScriptMachine::includeCallback, this);
|
||||||
|
|
||||||
|
_curMsgMode = mode;
|
||||||
auto r = builder.loadSectionFromFile(script.toUtf8());
|
auto r = builder.loadSectionFromFile(script.toUtf8());
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
|
MessageInfo info;
|
||||||
|
info.mode = mode;
|
||||||
|
info.message = tr("Script failed to pre-processed");
|
||||||
|
info.type = MessageType::Error;
|
||||||
|
outputMessage(info);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_curMode = mode;
|
|
||||||
r = builder.build(mod);
|
r = builder.build(mod);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
MessageInfo info;
|
MessageInfo info;
|
||||||
|
@ -414,9 +578,25 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
|
||||||
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
|
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
|
||||||
_ctx[mode] = ctx;
|
_ctx[mode] = ctx;
|
||||||
|
|
||||||
|
if (mode == Background) {
|
||||||
|
MessageInfo info;
|
||||||
|
info.mode = mode;
|
||||||
|
info.message = tr("Run:") + script;
|
||||||
|
info.type = MessageType::Info;
|
||||||
|
outputMessage(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->SetUserData(reinterpret_cast<void *>(
|
||||||
|
AppManager::instance()->currentMSecsSinceEpoch()),
|
||||||
|
AsUserDataType::UserData_Timer);
|
||||||
ctx->SetUserData(reinterpret_cast<void *>(isDbg),
|
ctx->SetUserData(reinterpret_cast<void *>(isDbg),
|
||||||
AsUserDataType::UserData_isDbg);
|
AsUserDataType::UserData_isDbg);
|
||||||
|
|
||||||
|
auto timeOutRaw = SettingManager::instance().scriptTimeout();
|
||||||
|
auto timeOut = asPWORD(timeOutRaw) * 6000; // min -> ms
|
||||||
|
ctx->SetUserData(reinterpret_cast<void *>(timeOut),
|
||||||
|
AsUserDataType::UserData_TimeOut);
|
||||||
|
|
||||||
mod->SetUserData(reinterpret_cast<void *>(isDbg),
|
mod->SetUserData(reinterpret_cast<void *>(isDbg),
|
||||||
AsUserDataType::UserData_isDbg);
|
AsUserDataType::UserData_isDbg);
|
||||||
|
|
||||||
|
@ -492,9 +672,82 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
|
||||||
_debugger->clearBreakPoint();
|
_debugger->clearBreakPoint();
|
||||||
emit onDebugFinished();
|
emit onDebugFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (retCode) {
|
||||||
|
*retCode = r;
|
||||||
|
}
|
||||||
|
|
||||||
return r >= 0;
|
return r >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ScriptMachine::evaluateDefine(const QString &code, bool &result) {
|
||||||
|
QByteArray name =
|
||||||
|
QByteArrayLiteral("WINGDEF") +
|
||||||
|
QByteArray::number(AppManager::instance()->currentMSecsSinceEpoch());
|
||||||
|
|
||||||
|
auto mod = _engine->GetModule(name.data(), asGM_ALWAYS_CREATE);
|
||||||
|
mod->SetAccessMask(0x2);
|
||||||
|
|
||||||
|
asIScriptFunction *func = nullptr;
|
||||||
|
QScopeGuard guard([mod]() {
|
||||||
|
// Before leaving, allow the engine to clean up remaining objects by
|
||||||
|
// discarding the module and doing a full garbage collection so that
|
||||||
|
// this can also be debugged if desired
|
||||||
|
mod->Discard();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto ccode = code;
|
||||||
|
ccode.prepend("bool f(){ return bool(").append(");}");
|
||||||
|
// start to compile
|
||||||
|
|
||||||
|
auto cr = mod->CompileFunction(nullptr, ccode.toUtf8(), 0, 0, &func);
|
||||||
|
if (cr < 0) {
|
||||||
|
return cr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up a context to execute the script
|
||||||
|
// The context manager will request the context from the
|
||||||
|
// pool, which will automatically attach the debugger
|
||||||
|
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
|
||||||
|
|
||||||
|
ctx->SetUserData(reinterpret_cast<void *>(asPWORD(
|
||||||
|
AppManager::instance()->currentMSecsSinceEpoch())),
|
||||||
|
AsUserDataType::UserData_Timer);
|
||||||
|
|
||||||
|
asPWORD isDbg = 0;
|
||||||
|
ctx->SetUserData(reinterpret_cast<void *>(isDbg),
|
||||||
|
AsUserDataType::UserData_isDbg);
|
||||||
|
mod->SetUserData(reinterpret_cast<void *>(isDbg),
|
||||||
|
AsUserDataType::UserData_isDbg);
|
||||||
|
ctx->SetUserData(0, AsUserDataType::UserData_ContextMode);
|
||||||
|
|
||||||
|
ctx->SetExceptionCallback(asMETHOD(ScriptMachine, exceptionCallback), this,
|
||||||
|
asCALL_THISCALL);
|
||||||
|
|
||||||
|
// Execute the script until completion
|
||||||
|
// The script may create co-routines. These will automatically
|
||||||
|
// be managed by the context manager
|
||||||
|
while (_ctxMgr->ExecuteScripts()) {
|
||||||
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the main script finished normally
|
||||||
|
int r = ctx->GetState();
|
||||||
|
if (r != asEXECUTION_FINISHED) {
|
||||||
|
r = -1;
|
||||||
|
} else {
|
||||||
|
result = bool(ctx->GetReturnByte());
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func->Release();
|
||||||
|
|
||||||
|
// Return the context after retrieving the return value
|
||||||
|
_ctxMgr->DoneWithContext(ctx);
|
||||||
|
_engine->GarbageCollect();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptMachine::abortDbgScript() {
|
void ScriptMachine::abortDbgScript() {
|
||||||
if (_debugger->getEngine()) {
|
if (_debugger->getEngine()) {
|
||||||
_debugger->runDebugAction(asDebugger::ABORT);
|
_debugger->runDebugAction(asDebugger::ABORT);
|
||||||
|
@ -534,7 +787,7 @@ void ScriptMachine::messageCallback(const asSMessageInfo *msg, void *param) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MessageInfo info;
|
MessageInfo info;
|
||||||
info.mode = ins->_curMode;
|
info.mode = ins->_curMsgMode;
|
||||||
info.row = msg->row;
|
info.row = msg->row;
|
||||||
info.col = msg->col;
|
info.col = msg->col;
|
||||||
info.section = msg->section;
|
info.section = msg->section;
|
||||||
|
@ -573,29 +826,43 @@ asIScriptModule *ScriptMachine::createModule(ConsoleMode mode) {
|
||||||
if (isModuleExists(mode)) {
|
if (isModuleExists(mode)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asIScriptModule *mod = nullptr;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case Interactive:
|
case Interactive:
|
||||||
return nullptr;
|
mod = nullptr;
|
||||||
case Scripting:
|
case Scripting:
|
||||||
return _engine->GetModule("WINGSCRIPT", asGM_ALWAYS_CREATE);
|
mod = _engine->GetModule("WINGSCRIPT", asGM_ALWAYS_CREATE);
|
||||||
|
mod->SetAccessMask(0x1);
|
||||||
|
break;
|
||||||
case Background:
|
case Background:
|
||||||
return _engine->GetModule("WINGSRV", asGM_ALWAYS_CREATE);
|
mod = _engine->GetModule("WINGSRV", asGM_ALWAYS_CREATE);
|
||||||
|
mod->SetAccessMask(0x1);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// should not go there
|
|
||||||
return nullptr;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
asIScriptModule *ScriptMachine::createModuleIfNotExist(ConsoleMode mode) {
|
asIScriptModule *ScriptMachine::createModuleIfNotExist(ConsoleMode mode) {
|
||||||
|
asIScriptModule *mod = nullptr;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case Interactive:
|
case Interactive:
|
||||||
return _engine->GetModule("WINGCONSOLE", asGM_ONLY_IF_EXISTS);
|
mod = _engine->GetModule("WINGCONSOLE", asGM_ONLY_IF_EXISTS);
|
||||||
|
mod->SetAccessMask(0x1);
|
||||||
|
break;
|
||||||
case Scripting:
|
case Scripting:
|
||||||
return _engine->GetModule("WINGSCRIPT", asGM_CREATE_IF_NOT_EXISTS);
|
mod = _engine->GetModule("WINGSCRIPT", asGM_CREATE_IF_NOT_EXISTS);
|
||||||
|
mod->SetAccessMask(0x1);
|
||||||
|
break;
|
||||||
case Background:
|
case Background:
|
||||||
return _engine->GetModule("WINGSRV", asGM_CREATE_IF_NOT_EXISTS);
|
mod = _engine->GetModule("WINGSRV", asGM_CREATE_IF_NOT_EXISTS);
|
||||||
|
mod->SetAccessMask(0x1);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// should not go there
|
|
||||||
return nullptr;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
asIScriptModule *ScriptMachine::module(ConsoleMode mode) {
|
asIScriptModule *ScriptMachine::module(ConsoleMode mode) {
|
||||||
|
@ -643,19 +910,21 @@ void ScriptMachine::returnContextCallback(asIScriptEngine *engine,
|
||||||
asIScriptContext *ctx, void *param) {
|
asIScriptContext *ctx, void *param) {
|
||||||
Q_UNUSED(engine);
|
Q_UNUSED(engine);
|
||||||
|
|
||||||
// We can also check for possible script exceptions here if so desired
|
if (ctx) {
|
||||||
|
// We can also check for possible script exceptions here if so desired
|
||||||
|
|
||||||
// Unprepare the context to free any objects it may still hold (e.g. return
|
// Unprepare the context to free any objects it may still hold (e.g.
|
||||||
// value) This must be done before making the context available for re-use,
|
// return value) This must be done before making the context available
|
||||||
// as the clean up may trigger other script executions, e.g. if a destructor
|
// for re-use, as the clean up may trigger other script executions, e.g.
|
||||||
// needs to call a function.
|
// if a destructor needs to call a function.
|
||||||
ctx->Unprepare();
|
ctx->Unprepare();
|
||||||
|
|
||||||
auto p = reinterpret_cast<ScriptMachine *>(param);
|
auto p = reinterpret_cast<ScriptMachine *>(param);
|
||||||
Q_ASSERT(p);
|
Q_ASSERT(p);
|
||||||
|
|
||||||
// Place the context into the pool for when it will be needed again
|
// Place the context into the pool for when it will be needed again
|
||||||
p->_ctxPool.push_back(ctx);
|
p->_ctxPool.push_back(ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ScriptMachine::pragmaCallback(const QByteArray &pragmaText,
|
int ScriptMachine::pragmaCallback(const QByteArray &pragmaText,
|
||||||
|
@ -1728,6 +1997,8 @@ void ScriptMachine::translation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptMachine::registerEngineAddon(asIScriptEngine *engine) {
|
void ScriptMachine::registerEngineAddon(asIScriptEngine *engine) {
|
||||||
|
// all modules can access
|
||||||
|
engine->SetDefaultAccessMask(0x3);
|
||||||
RegisterScriptArray(engine, true);
|
RegisterScriptArray(engine, true);
|
||||||
RegisterQString(engine);
|
RegisterQString(engine);
|
||||||
RegisterScriptRegex(engine);
|
RegisterScriptRegex(engine);
|
||||||
|
@ -1742,9 +2013,13 @@ void ScriptMachine::registerEngineAddon(asIScriptEngine *engine) {
|
||||||
RegisterScriptHandle(engine);
|
RegisterScriptHandle(engine);
|
||||||
RegisterColor(engine);
|
RegisterColor(engine);
|
||||||
RegisterQJson(engine);
|
RegisterQJson(engine);
|
||||||
|
|
||||||
|
engine->SetDefaultAccessMask(0x1);
|
||||||
RegisterScriptFile(engine);
|
RegisterScriptFile(engine);
|
||||||
registerExceptionRoutines(engine);
|
registerExceptionRoutines(engine);
|
||||||
registerEngineAssert(engine);
|
registerEngineAssert(engine);
|
||||||
|
registerEngineClipboard(engine);
|
||||||
|
AsDirectPromise::Register(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptMachine::registerEngineAssert(asIScriptEngine *engine) {
|
void ScriptMachine::registerEngineAssert(asIScriptEngine *engine) {
|
||||||
|
@ -1780,6 +2055,57 @@ void ScriptMachine::registerEngineAssert(asIScriptEngine *engine) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptMachine::registerEngineClipboard(asIScriptEngine *engine) {
|
||||||
|
int r = engine->SetDefaultNamespace("clipboard");
|
||||||
|
Q_ASSERT(r >= 0);
|
||||||
|
Q_UNUSED(r);
|
||||||
|
|
||||||
|
// The string type must be available
|
||||||
|
Q_ASSERT(engine->GetTypeInfoByDecl("string"));
|
||||||
|
|
||||||
|
if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") == 0) {
|
||||||
|
r = engine->RegisterGlobalFunction(
|
||||||
|
"void setText(const string &in text)", asFUNCTION(clip_setText),
|
||||||
|
asCALL_CDECL);
|
||||||
|
Q_ASSERT(r >= 0);
|
||||||
|
Q_UNUSED(r);
|
||||||
|
r = engine->RegisterGlobalFunction(
|
||||||
|
"string text()", asFUNCTION(clip_getText), asCALL_CDECL);
|
||||||
|
Q_ASSERT(r >= 0);
|
||||||
|
Q_UNUSED(r);
|
||||||
|
r = engine->RegisterGlobalFunction(
|
||||||
|
"void setBinary(const uint8[]@ data)", asFUNCTION(clip_setBinary),
|
||||||
|
asCALL_CDECL);
|
||||||
|
Q_ASSERT(r >= 0);
|
||||||
|
Q_UNUSED(r);
|
||||||
|
r = engine->RegisterGlobalFunction(
|
||||||
|
"uint8[]@ getBinary()", asFUNCTION(clip_getBinary), asCALL_CDECL);
|
||||||
|
Q_ASSERT(r >= 0);
|
||||||
|
Q_UNUSED(r);
|
||||||
|
} else {
|
||||||
|
r = engine->RegisterGlobalFunction(
|
||||||
|
"void setText(const string &in text)", WRAP_FN(clip_setText),
|
||||||
|
asCALL_GENERIC);
|
||||||
|
Q_ASSERT(r >= 0);
|
||||||
|
Q_UNUSED(r);
|
||||||
|
r = engine->RegisterGlobalFunction(
|
||||||
|
"string text()", asFUNCTION(clip_getText), asCALL_GENERIC);
|
||||||
|
Q_ASSERT(r >= 0);
|
||||||
|
Q_UNUSED(r);
|
||||||
|
r = engine->RegisterGlobalFunction(
|
||||||
|
"void setBinary(const uint8[]@ data)", WRAP_FN(clip_setBinary),
|
||||||
|
asCALL_GENERIC);
|
||||||
|
Q_ASSERT(r >= 0);
|
||||||
|
Q_UNUSED(r);
|
||||||
|
r = engine->RegisterGlobalFunction(
|
||||||
|
"uint8[]@ getBinary()", WRAP_FN(clip_getBinary), asCALL_GENERIC);
|
||||||
|
Q_ASSERT(r >= 0);
|
||||||
|
Q_UNUSED(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
engine->SetDefaultNamespace("");
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptMachine::registerCallBack(ConsoleMode mode,
|
void ScriptMachine::registerCallBack(ConsoleMode mode,
|
||||||
const RegCallBacks &callbacks) {
|
const RegCallBacks &callbacks) {
|
||||||
_regcalls.insert(mode, callbacks);
|
_regcalls.insert(mode, callbacks);
|
||||||
|
@ -1808,6 +2134,49 @@ void ScriptMachine::scriptAssert_X(bool b, const QString &msg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptMachine::clip_setText(const QString &text) {
|
||||||
|
qApp->clipboard()->setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptMachine::clip_setBinary(const CScriptArray &array) {
|
||||||
|
QByteArray buffer;
|
||||||
|
buffer.reserve(array.GetSize());
|
||||||
|
array.AddRef();
|
||||||
|
for (asUINT i = 0; i < array.GetSize(); ++i) {
|
||||||
|
auto item = reinterpret_cast<const asBYTE *>(array.At(i));
|
||||||
|
buffer.append(*item);
|
||||||
|
}
|
||||||
|
array.Release();
|
||||||
|
|
||||||
|
auto c = qApp->clipboard();
|
||||||
|
auto mime = new QMimeData;
|
||||||
|
mime->setData(QStringLiteral("application/octet-stream"),
|
||||||
|
buffer); // don't use setText()
|
||||||
|
c->setMimeData(mime);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ScriptMachine::clip_getText() { return qApp->clipboard()->text(); }
|
||||||
|
|
||||||
|
CScriptArray *ScriptMachine::clip_getBinary() {
|
||||||
|
QClipboard *c = qApp->clipboard();
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
auto d = c->mimeData();
|
||||||
|
data = d->data(QStringLiteral("application/octet-stream"));
|
||||||
|
|
||||||
|
auto engine = ScriptMachine::instance().engine();
|
||||||
|
auto len = data.size();
|
||||||
|
auto arr =
|
||||||
|
CScriptArray::Create(engine->GetTypeInfoByDecl("array<uint8>"), len);
|
||||||
|
arr->AddRef();
|
||||||
|
for (int i = 0; i < len; ++i) {
|
||||||
|
auto addr = arr->At(i);
|
||||||
|
*reinterpret_cast<char *>(addr) = data.at(i);
|
||||||
|
}
|
||||||
|
arr->Release();
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptMachine::scriptThrow(const QString &msg) {
|
void ScriptMachine::scriptThrow(const QString &msg) {
|
||||||
asIScriptContext *ctx = asGetActiveContext();
|
asIScriptContext *ctx = asGetActiveContext();
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
|
@ -1832,6 +2201,15 @@ asIScriptEngine *ScriptMachine::engine() const { return _engine; }
|
||||||
asDebugger *ScriptMachine::debugger() const { return _debugger; }
|
asDebugger *ScriptMachine::debugger() const { return _debugger; }
|
||||||
|
|
||||||
bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
|
bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
|
||||||
|
if (QThread::currentThread() != qApp->thread()) {
|
||||||
|
Logger::warning(tr("Code must be exec in the main thread"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
asIScriptModule *mod = createModuleIfNotExist(mode);
|
asIScriptModule *mod = createModuleIfNotExist(mode);
|
||||||
_engine->SetEngineProperty(asEP_BUILD_WITHOUT_LINE_CUES, false);
|
_engine->SetEngineProperty(asEP_BUILD_WITHOUT_LINE_CUES, false);
|
||||||
|
|
||||||
|
@ -1841,30 +2219,34 @@ bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
|
||||||
|
|
||||||
asIScriptFunction *func = nullptr;
|
asIScriptFunction *func = nullptr;
|
||||||
|
|
||||||
auto ret = parser.parse(ccode);
|
QList<QAsCodeParser::CodeSegment> ret;
|
||||||
|
if (mode > 0) {
|
||||||
|
ret = parser.parse(ccode);
|
||||||
|
}
|
||||||
|
|
||||||
// check whether there is any enum/class
|
// check whether there is any enum/class
|
||||||
if (std::find_if(ret.begin(), ret.end(),
|
if (ret.isEmpty() ||
|
||||||
[](const QAsCodeParser::CodeSegment &seg) {
|
std::any_of(ret.begin(), ret.end(),
|
||||||
switch (seg.type) {
|
[](const QAsCodeParser::CodeSegment &seg) {
|
||||||
case QAsCodeParser::SymbolType::Enum:
|
switch (seg.type) {
|
||||||
case QAsCodeParser::SymbolType::Class:
|
case QAsCodeParser::SymbolType::Enum:
|
||||||
case QAsCodeParser::SymbolType::Function:
|
case QAsCodeParser::SymbolType::Class:
|
||||||
case QAsCodeParser::SymbolType::Interface:
|
case QAsCodeParser::SymbolType::Function:
|
||||||
case QAsCodeParser::SymbolType::Import:
|
case QAsCodeParser::SymbolType::Interface:
|
||||||
case QAsCodeParser::SymbolType::Variable:
|
case QAsCodeParser::SymbolType::Import:
|
||||||
return false;
|
case QAsCodeParser::SymbolType::Variable:
|
||||||
case QAsCodeParser::SymbolType::Invalid:
|
return false;
|
||||||
case QAsCodeParser::SymbolType::TypeDef:
|
case QAsCodeParser::SymbolType::Invalid:
|
||||||
case QAsCodeParser::SymbolType::FnDef:
|
case QAsCodeParser::SymbolType::TypeDef:
|
||||||
return true;
|
case QAsCodeParser::SymbolType::FnDef:
|
||||||
}
|
return true;
|
||||||
return true;
|
}
|
||||||
}) != ret.end()) {
|
return true;
|
||||||
|
})) {
|
||||||
// ok, wrap the codes
|
// ok, wrap the codes
|
||||||
ccode.prepend("void f(){").append("}");
|
ccode.prepend("void f(){").append("}");
|
||||||
// start to compile
|
// start to compile
|
||||||
_curMode = mode;
|
_curMsgMode = mode;
|
||||||
auto cr = mod->CompileFunction(nullptr, ccode, 0, 0, &func);
|
auto cr = mod->CompileFunction(nullptr, ccode, 0, 0, &func);
|
||||||
if (cr < 0) {
|
if (cr < 0) {
|
||||||
MessageInfo info;
|
MessageInfo info;
|
||||||
|
@ -1881,12 +2263,21 @@ bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
|
||||||
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
|
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
|
||||||
_ctx[mode] = ctx;
|
_ctx[mode] = ctx;
|
||||||
|
|
||||||
|
ctx->SetUserData(reinterpret_cast<void *>(asPWORD(
|
||||||
|
AppManager::instance()->currentMSecsSinceEpoch())),
|
||||||
|
AsUserDataType::UserData_Timer);
|
||||||
|
|
||||||
asPWORD isDbg = 0;
|
asPWORD isDbg = 0;
|
||||||
ctx->SetUserData(reinterpret_cast<void *>(isDbg),
|
ctx->SetUserData(reinterpret_cast<void *>(isDbg),
|
||||||
AsUserDataType::UserData_isDbg);
|
AsUserDataType::UserData_isDbg);
|
||||||
mod->SetUserData(reinterpret_cast<void *>(isDbg),
|
mod->SetUserData(reinterpret_cast<void *>(isDbg),
|
||||||
AsUserDataType::UserData_isDbg);
|
AsUserDataType::UserData_isDbg);
|
||||||
|
|
||||||
|
auto timeOutRaw = SettingManager::instance().scriptTimeout();
|
||||||
|
auto timeOut = asPWORD(timeOutRaw) * 6000; // min -> ms
|
||||||
|
ctx->SetUserData(reinterpret_cast<void *>(timeOut),
|
||||||
|
AsUserDataType::UserData_TimeOut);
|
||||||
|
|
||||||
asPWORD umode = asPWORD(mode);
|
asPWORD umode = asPWORD(mode);
|
||||||
ctx->SetUserData(reinterpret_cast<void *>(umode),
|
ctx->SetUserData(reinterpret_cast<void *>(umode),
|
||||||
AsUserDataType::UserData_ContextMode);
|
AsUserDataType::UserData_ContextMode);
|
||||||
|
@ -1951,7 +2342,7 @@ bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
|
||||||
return seg.type ==
|
return seg.type ==
|
||||||
QAsCodeParser::SymbolType::Variable;
|
QAsCodeParser::SymbolType::Variable;
|
||||||
})) {
|
})) {
|
||||||
_curMode = mode;
|
_curMsgMode = mode;
|
||||||
|
|
||||||
for (auto &s : ret) {
|
for (auto &s : ret) {
|
||||||
auto r = mod->CompileGlobalVar(nullptr, s.codes, 0);
|
auto r = mod->CompileGlobalVar(nullptr, s.codes, 0);
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QQueue>
|
#include <QQueue>
|
||||||
|
|
||||||
|
class CScriptArray;
|
||||||
|
|
||||||
class ScriptMachine : public QObject {
|
class ScriptMachine : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private:
|
private:
|
||||||
|
@ -36,9 +38,9 @@ private:
|
||||||
public:
|
public:
|
||||||
// we have three console modes
|
// we have three console modes
|
||||||
enum ConsoleMode {
|
enum ConsoleMode {
|
||||||
Interactive, // in a shell
|
Interactive = 1, // in a shell
|
||||||
Scripting, // in scripting dialog
|
Scripting, // in scripting dialog
|
||||||
Background // run codes from other way
|
Background, // run codes from other way
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -108,8 +110,7 @@ public:
|
||||||
asIScriptModule *module(ConsoleMode mode);
|
asIScriptModule *module(ConsoleMode mode);
|
||||||
|
|
||||||
static ScriptMachine &instance();
|
static ScriptMachine &instance();
|
||||||
|
void destoryMachine();
|
||||||
virtual ~ScriptMachine();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool init();
|
bool init();
|
||||||
|
@ -118,6 +119,7 @@ public:
|
||||||
|
|
||||||
static void registerEngineAddon(asIScriptEngine *engine);
|
static void registerEngineAddon(asIScriptEngine *engine);
|
||||||
static void registerEngineAssert(asIScriptEngine *engine);
|
static void registerEngineAssert(asIScriptEngine *engine);
|
||||||
|
static void registerEngineClipboard(asIScriptEngine *engine);
|
||||||
|
|
||||||
void registerCallBack(ConsoleMode mode, const RegCallBacks &callbacks);
|
void registerCallBack(ConsoleMode mode, const RegCallBacks &callbacks);
|
||||||
|
|
||||||
|
@ -126,10 +128,17 @@ public:
|
||||||
|
|
||||||
asIScriptEngine *engine() const;
|
asIScriptEngine *engine() const;
|
||||||
|
|
||||||
|
void outputMessage(const MessageInfo &info);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void scriptAssert(bool b);
|
static void scriptAssert(bool b);
|
||||||
static void scriptAssert_X(bool b, const QString &msg);
|
static void scriptAssert_X(bool b, const QString &msg);
|
||||||
|
|
||||||
|
static void clip_setText(const QString &text);
|
||||||
|
static void clip_setBinary(const CScriptArray &array);
|
||||||
|
static QString clip_getText();
|
||||||
|
static CScriptArray *clip_getBinary();
|
||||||
|
|
||||||
static void scriptThrow(const QString &msg);
|
static void scriptThrow(const QString &msg);
|
||||||
|
|
||||||
static QString scriptGetExceptionInfo();
|
static QString scriptGetExceptionInfo();
|
||||||
|
@ -144,7 +153,9 @@ public slots:
|
||||||
bool executeCode(ConsoleMode mode, const QString &code);
|
bool executeCode(ConsoleMode mode, const QString &code);
|
||||||
// only scripting mode can be debugged
|
// only scripting mode can be debugged
|
||||||
bool executeScript(ConsoleMode mode, const QString &script,
|
bool executeScript(ConsoleMode mode, const QString &script,
|
||||||
bool isInDebug = false);
|
bool isInDebug = false, int *retCode = nullptr);
|
||||||
|
|
||||||
|
int evaluateDefine(const QString &code, bool &result);
|
||||||
|
|
||||||
void abortDbgScript();
|
void abortDbgScript();
|
||||||
void abortScript(ConsoleMode mode);
|
void abortScript(ConsoleMode mode);
|
||||||
|
@ -155,19 +166,21 @@ protected:
|
||||||
|
|
||||||
QString getCallStack(asIScriptContext *context);
|
QString getCallStack(asIScriptContext *context);
|
||||||
|
|
||||||
void destoryMachine();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void print(void *ref, int typeId);
|
static void print(asIScriptGeneric *args);
|
||||||
QString getInput();
|
static void println(asIScriptGeneric *args);
|
||||||
|
|
||||||
void outputMessage(const MessageInfo &info);
|
QString getInput();
|
||||||
|
|
||||||
bool isType(asITypeInfo *tinfo, RegisteredType type);
|
bool isType(asITypeInfo *tinfo, RegisteredType type);
|
||||||
|
|
||||||
static int execSystemCmd(QString &out, const QString &exe,
|
static int execSystemCmd(QString &out, const QString &exe,
|
||||||
const QString ¶ms, int timeout);
|
const QString ¶ms, int timeout);
|
||||||
|
|
||||||
|
static QString beautify(const QString &str, uint indent);
|
||||||
|
|
||||||
|
QString stringify(void *ref, int typeId);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void messageCallback(const asSMessageInfo *msg, void *param);
|
static void messageCallback(const asSMessageInfo *msg, void *param);
|
||||||
|
|
||||||
|
@ -208,7 +221,7 @@ private:
|
||||||
QVector<asITypeInfo *> _rtypes;
|
QVector<asITypeInfo *> _rtypes;
|
||||||
QMap<ConsoleMode, RegCallBacks> _regcalls;
|
QMap<ConsoleMode, RegCallBacks> _regcalls;
|
||||||
QMap<ConsoleMode, asIScriptContext *> _ctx;
|
QMap<ConsoleMode, asIScriptContext *> _ctx;
|
||||||
ConsoleMode _curMode = ConsoleMode::Background;
|
ConsoleMode _curMsgMode = ConsoleMode::Background;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(ScriptMachine::MessageInfo)
|
Q_DECLARE_METATYPE(ScriptMachine::MessageInfo)
|
||||||
|
|
|
@ -73,6 +73,11 @@ QString ScriptManager::readJsonObjString(const QJsonObject &jobj,
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ScriptManager::readJsonObjBool(const QJsonObject &jobj,
|
||||||
|
const QString &key) {
|
||||||
|
return jobj.value(key).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
QMenu *ScriptManager::buildUpScriptDirMenu(QWidget *parent,
|
QMenu *ScriptManager::buildUpScriptDirMenu(QWidget *parent,
|
||||||
const QStringList &files,
|
const QStringList &files,
|
||||||
bool isSys) {
|
bool isSys) {
|
||||||
|
@ -167,6 +172,7 @@ ScriptManager::ensureDirMeta(const QFileInfo &info) {
|
||||||
jobj.insert(QStringLiteral("license"), QLatin1String());
|
jobj.insert(QStringLiteral("license"), QLatin1String());
|
||||||
jobj.insert(QStringLiteral("homepage"), QLatin1String());
|
jobj.insert(QStringLiteral("homepage"), QLatin1String());
|
||||||
jobj.insert(QStringLiteral("comment"), QLatin1String());
|
jobj.insert(QStringLiteral("comment"), QLatin1String());
|
||||||
|
jobj.insert(QStringLiteral("hexmenu"), false);
|
||||||
QFile f(base.absoluteFilePath(QStringLiteral(".wingasmeta")));
|
QFile f(base.absoluteFilePath(QStringLiteral(".wingasmeta")));
|
||||||
if (f.open(QFile::WriteOnly | QFile::Text)) {
|
if (f.open(QFile::WriteOnly | QFile::Text)) {
|
||||||
QJsonDocument jdoc(jobj);
|
QJsonDocument jdoc(jobj);
|
||||||
|
@ -195,6 +201,8 @@ ScriptManager::ensureDirMeta(const QFileInfo &info) {
|
||||||
readJsonObjString(jobj, QStringLiteral("homepage"));
|
readJsonObjString(jobj, QStringLiteral("homepage"));
|
||||||
meta.comment =
|
meta.comment =
|
||||||
readJsonObjString(jobj, QStringLiteral("comment"));
|
readJsonObjString(jobj, QStringLiteral("comment"));
|
||||||
|
meta.isContextMenu =
|
||||||
|
readJsonObjBool(jobj, QStringLiteral("hexmenu"));
|
||||||
} else {
|
} else {
|
||||||
meta.name = info.fileName();
|
meta.name = info.fileName();
|
||||||
}
|
}
|
||||||
|
@ -217,9 +225,10 @@ ScriptManager::sysDirMeta(const QString &cat) const {
|
||||||
return _sysDirMetas.value(cat);
|
return _sysDirMetas.value(cat);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptManager::ScriptActionMaps
|
QList<QMenu *>
|
||||||
ScriptManager::buildUpRibbonScriptRunner(RibbonButtonGroup *group) {
|
ScriptManager::buildUpScriptRunnerContext(RibbonButtonGroup *group,
|
||||||
ScriptActionMaps maps;
|
QWidget *parent) {
|
||||||
|
QList<QMenu *> maps;
|
||||||
|
|
||||||
auto &sm = ScriptManager::instance();
|
auto &sm = ScriptManager::instance();
|
||||||
auto &stm = SettingManager::instance();
|
auto &stm = SettingManager::instance();
|
||||||
|
@ -229,10 +238,20 @@ ScriptManager::buildUpRibbonScriptRunner(RibbonButtonGroup *group) {
|
||||||
if (hideCats.contains(cat)) {
|
if (hideCats.contains(cat)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
maps.sysList << addPannelAction(
|
|
||||||
group, ICONRES(QStringLiteral("scriptfolder")),
|
auto meta = sm.sysDirMeta(cat);
|
||||||
sm.sysDirMeta(cat).name,
|
|
||||||
|
addPannelAction(
|
||||||
|
group, ICONRES(QStringLiteral("scriptfolder")), meta.name,
|
||||||
buildUpScriptDirMenu(group, sm.getSysScriptFileNames(cat), true));
|
buildUpScriptDirMenu(group, sm.getSysScriptFileNames(cat), true));
|
||||||
|
|
||||||
|
if (meta.isContextMenu) {
|
||||||
|
auto m = buildUpScriptDirMenu(parent, sm.getSysScriptFileNames(cat),
|
||||||
|
true);
|
||||||
|
m->setTitle(meta.name);
|
||||||
|
m->setIcon(ICONRES(QStringLiteral("scriptfolder")));
|
||||||
|
maps << m;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hideCats = stm.usrHideCats();
|
hideCats = stm.usrHideCats();
|
||||||
|
@ -240,10 +259,19 @@ ScriptManager::buildUpRibbonScriptRunner(RibbonButtonGroup *group) {
|
||||||
if (hideCats.contains(cat)) {
|
if (hideCats.contains(cat)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
maps.usrList << addPannelAction(
|
auto meta = sm.usrDirMeta(cat);
|
||||||
group, ICONRES(QStringLiteral("scriptfolderusr")),
|
|
||||||
sm.usrDirMeta(cat).name,
|
addPannelAction(
|
||||||
|
group, ICONRES(QStringLiteral("scriptfolderusr")), meta.name,
|
||||||
buildUpScriptDirMenu(group, sm.getUsrScriptFileNames(cat), false));
|
buildUpScriptDirMenu(group, sm.getUsrScriptFileNames(cat), false));
|
||||||
|
|
||||||
|
if (meta.isContextMenu) {
|
||||||
|
auto m = buildUpScriptDirMenu(parent, sm.getSysScriptFileNames(cat),
|
||||||
|
true);
|
||||||
|
m->setTitle(meta.name);
|
||||||
|
m->setIcon(ICONRES(QStringLiteral("scriptfolderusr")));
|
||||||
|
maps << m;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return maps;
|
return maps;
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "QWingRibbon/ribbonbuttongroup.h"
|
#include "QWingRibbon/ribbonbuttongroup.h"
|
||||||
#include "control/scriptingconsole.h"
|
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
class ScriptManager : public QObject {
|
class ScriptManager : public QObject {
|
||||||
|
@ -38,14 +37,10 @@ public:
|
||||||
QString license;
|
QString license;
|
||||||
QString homepage;
|
QString homepage;
|
||||||
QString comment;
|
QString comment;
|
||||||
|
bool isContextMenu = false;
|
||||||
bool isSys; // a flag
|
bool isSys; // a flag
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ScriptActionMaps {
|
|
||||||
QList<QToolButton *> sysList;
|
|
||||||
QList<QToolButton *> usrList;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static ScriptManager &instance();
|
static ScriptManager &instance();
|
||||||
|
|
||||||
|
@ -68,7 +63,8 @@ public:
|
||||||
ScriptDirMeta usrDirMeta(const QString &cat) const;
|
ScriptDirMeta usrDirMeta(const QString &cat) const;
|
||||||
ScriptDirMeta sysDirMeta(const QString &cat) const;
|
ScriptDirMeta sysDirMeta(const QString &cat) const;
|
||||||
|
|
||||||
static ScriptActionMaps buildUpRibbonScriptRunner(RibbonButtonGroup *group);
|
static QList<QMenu *> buildUpScriptRunnerContext(RibbonButtonGroup *group,
|
||||||
|
QWidget *parent);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QToolButton *addPannelAction(RibbonButtonGroup *pannel,
|
static QToolButton *addPannelAction(RibbonButtonGroup *pannel,
|
||||||
|
@ -119,6 +115,8 @@ private:
|
||||||
|
|
||||||
QString readJsonObjString(const QJsonObject &jobj, const QString &key);
|
QString readJsonObjString(const QJsonObject &jobj, const QString &key);
|
||||||
|
|
||||||
|
bool readJsonObjBool(const QJsonObject &jobj, const QString &key);
|
||||||
|
|
||||||
static QMenu *buildUpScriptDirMenu(QWidget *parent,
|
static QMenu *buildUpScriptDirMenu(QWidget *parent,
|
||||||
const QStringList &files, bool isSys);
|
const QStringList &files, bool isSys);
|
||||||
|
|
||||||
|
|
|
@ -33,12 +33,24 @@
|
||||||
_setUnsavedEditor.setFlag(flag, false); \
|
_setUnsavedEditor.setFlag(flag, false); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define WRITE_CONFIG_EDITOR_RESET(config, flag, dvalue) \
|
||||||
|
do { \
|
||||||
|
WRITE_CONFIG(config, dvalue); \
|
||||||
|
_setUnsavedEditor.setFlag(flag, false); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
#define WRITE_CONFIG_CONSOLE_SET(config, flag, dvalue) \
|
#define WRITE_CONFIG_CONSOLE_SET(config, flag, dvalue) \
|
||||||
if (this->_setUnsavedConsole.testFlag(flag)) { \
|
if (this->_setUnsavedConsole.testFlag(flag)) { \
|
||||||
WRITE_CONFIG(config, dvalue); \
|
WRITE_CONFIG(config, dvalue); \
|
||||||
_setUnsavedConsole.setFlag(flag, false); \
|
_setUnsavedConsole.setFlag(flag, false); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define WRITE_CONFIG_CONSOLE_RESET(config, flag, dvalue) \
|
||||||
|
do { \
|
||||||
|
WRITE_CONFIG(config, dvalue); \
|
||||||
|
_setUnsavedConsole.setFlag(flag, false); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QString, CODEEDIT_FONT, ("codeedit.font"))
|
Q_GLOBAL_STATIC_WITH_ARGS(QString, CODEEDIT_FONT, ("codeedit.font"))
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QString, CODEEDIT_FONT_SIZE, ("codeedit.fontsize"))
|
Q_GLOBAL_STATIC_WITH_ARGS(QString, CODEEDIT_FONT_SIZE, ("codeedit.fontsize"))
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QString, CODEEDIT_THEME, ("codeedit.theme"))
|
Q_GLOBAL_STATIC_WITH_ARGS(QString, CODEEDIT_THEME, ("codeedit.theme"))
|
||||||
|
@ -200,56 +212,59 @@ void ScriptSettings::save(SETTINGS cat) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptSettings::reset(SETTINGS cat) {
|
void ScriptSettings::reset(SETTINGS cat) {
|
||||||
|
__reset(cat);
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptSettings::__reset(SETTINGS cat) {
|
||||||
HANDLE_CONFIG;
|
HANDLE_CONFIG;
|
||||||
if (cat.testFlag(SETTING::EDITOR)) {
|
if (cat.testFlag(SETTING::EDITOR)) {
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_FONT, SETTING_ITEM::FONT,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_FONT, SETTING_ITEM::FONT,
|
||||||
_defaultFont.defaultFamily());
|
_defaultFont.defaultFamily());
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
|
||||||
10);
|
10);
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_THEME, SETTING_ITEM::THEME, {});
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_THEME, SETTING_ITEM::THEME, {});
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
|
||||||
4);
|
4);
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_INDENTATION, SETTING_ITEM::INDENTATION,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_INDENTATION,
|
||||||
0);
|
SETTING_ITEM::INDENTATION, 0);
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_MATCH_BRACES,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_MATCH_BRACES,
|
||||||
SETTING_ITEM::MATCH_BRACES, true);
|
SETTING_ITEM::MATCH_BRACES, true);
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_WORD_WRAP, SETTING_ITEM::WORD_WRAP,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_WORD_WRAP, SETTING_ITEM::WORD_WRAP,
|
||||||
false);
|
false);
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_LINENUMBER,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_LINENUMBER,
|
||||||
SETTING_ITEM::SHOW_LINENUMBER, true);
|
SETTING_ITEM::SHOW_LINENUMBER, true);
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_FOLDING,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_FOLDING,
|
||||||
SETTING_ITEM::SHOW_FOLDING, true);
|
SETTING_ITEM::SHOW_FOLDING, true);
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_INDENTGUIDES,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_INDENTGUIDES,
|
||||||
SETTING_ITEM::SHOW_INDENTGUIDES, true);
|
SETTING_ITEM::SHOW_INDENTGUIDES, true);
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_LONGLINEEDGE,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_LONGLINEEDGE,
|
||||||
SETTING_ITEM::SHOW_LONGLINEEDGE, false);
|
SETTING_ITEM::SHOW_LONGLINEEDGE, false);
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_WHITESPACE,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_WHITESPACE,
|
||||||
SETTING_ITEM::SHOW_WHITESPACE, false);
|
SETTING_ITEM::SHOW_WHITESPACE, false);
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_AUTO_CLOSE_CHAR,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_AUTO_CLOSE_CHAR,
|
||||||
SETTING_ITEM::AUTO_CLOSE_CHAR, true);
|
SETTING_ITEM::AUTO_CLOSE_CHAR, true);
|
||||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_AUTO_IDEN, SETTING_ITEM::AUTO_IDEN,
|
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_AUTO_IDEN, SETTING_ITEM::AUTO_IDEN,
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cat.testFlag(SETTING::CONSOLE)) {
|
if (cat.testFlag(SETTING::CONSOLE)) {
|
||||||
WRITE_CONFIG_CONSOLE_SET(CONSOLE_FONT, SETTING_ITEM::FONT,
|
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_FONT, SETTING_ITEM::FONT,
|
||||||
_defaultFont.defaultFamily());
|
_defaultFont.defaultFamily());
|
||||||
WRITE_CONFIG_CONSOLE_SET(CONSOLE_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
|
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
|
||||||
10);
|
10);
|
||||||
WRITE_CONFIG_CONSOLE_SET(CONSOLE_THEME, SETTING_ITEM::THEME, {});
|
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_THEME, SETTING_ITEM::THEME, {});
|
||||||
WRITE_CONFIG_CONSOLE_SET(CONSOLE_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
|
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
|
||||||
4);
|
4);
|
||||||
WRITE_CONFIG_CONSOLE_SET(CONSOLE_INDENTATION, SETTING_ITEM::INDENTATION,
|
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_INDENTATION,
|
||||||
0);
|
SETTING_ITEM::INDENTATION, 0);
|
||||||
WRITE_CONFIG_CONSOLE_SET(CONSOLE_MATCH_BRACES,
|
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_MATCH_BRACES,
|
||||||
SETTING_ITEM::MATCH_BRACES, true);
|
SETTING_ITEM::MATCH_BRACES, true);
|
||||||
WRITE_CONFIG_CONSOLE_SET(CONSOLE_SHOW_WHITESPACE,
|
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_SHOW_WHITESPACE,
|
||||||
SETTING_ITEM::SHOW_WHITESPACE, false);
|
SETTING_ITEM::SHOW_WHITESPACE, false);
|
||||||
WRITE_CONFIG_CONSOLE_SET(CONSOLE_AUTO_CLOSE_CHAR,
|
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_AUTO_CLOSE_CHAR,
|
||||||
SETTING_ITEM::AUTO_CLOSE_CHAR, true);
|
SETTING_ITEM::AUTO_CLOSE_CHAR, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
load();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptSettings::ScriptSettings() : QObject() {
|
ScriptSettings::ScriptSettings() : QObject() {
|
||||||
|
|
|
@ -57,6 +57,8 @@ public:
|
||||||
void save(SETTINGS cat = SETTING::ALL);
|
void save(SETTINGS cat = SETTING::ALL);
|
||||||
void reset(SETTINGS cat = SETTING::ALL);
|
void reset(SETTINGS cat = SETTING::ALL);
|
||||||
|
|
||||||
|
void __reset(SETTINGS cat);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QString editorFontFamily() const;
|
QString editorFontFamily() const;
|
||||||
void setEditorFontFamily(const QString &newEditorFontFamily);
|
void setEditorFontFamily(const QString &newEditorFontFamily);
|
||||||
|
|
|
@ -31,6 +31,12 @@
|
||||||
_setUnsaved.setFlag(SettingManager::SETTING_ITEM::config, false); \
|
_setUnsaved.setFlag(SettingManager::SETTING_ITEM::config, false); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define WRITE_CONFIG_RESET(config, dvalue) \
|
||||||
|
do { \
|
||||||
|
WRITE_CONFIG(config, dvalue); \
|
||||||
|
_setUnsaved.setFlag(SettingManager::SETTING_ITEM::config, false); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QString, DOCK_LAYOUT, ("dock.layout"))
|
Q_GLOBAL_STATIC_WITH_ARGS(QString, DOCK_LAYOUT, ("dock.layout"))
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_DOCK_LAYOUT, ("script.layout"))
|
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_DOCK_LAYOUT, ("script.layout"))
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QString, APP_LASTUSED_PATH, ("app.lastusedpath"))
|
Q_GLOBAL_STATIC_WITH_ARGS(QString, APP_LASTUSED_PATH, ("app.lastusedpath"))
|
||||||
|
@ -59,6 +65,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_RECENTFILES, ("script.recentfiles"))
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_ALLOW_USRSCRIPT_INROOT,
|
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_ALLOW_USRSCRIPT_INROOT,
|
||||||
("script.allowUsrScriptRoot"))
|
("script.allowUsrScriptRoot"))
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_ENABLE, ("script.enable"))
|
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_ENABLE, ("script.enable"))
|
||||||
|
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_TIMEOUT, ("script.timeout"))
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_USRHIDECATS, ("script.usrHideCats"))
|
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_USRHIDECATS, ("script.usrHideCats"))
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_SYSHIDECATS, ("script.sysHideCats"))
|
Q_GLOBAL_STATIC_WITH_ARGS(QString, SCRIPT_SYSHIDECATS, ("script.sysHideCats"))
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(QString, OTHER_USESYS_FILEDIALOG,
|
Q_GLOBAL_STATIC_WITH_ARGS(QString, OTHER_USESYS_FILEDIALOG,
|
||||||
|
@ -113,6 +120,7 @@ void SettingManager::load() {
|
||||||
READ_CONFIG_BOOL(m_enablePlgInRoot, PLUGIN_ENABLE_ROOT, false);
|
READ_CONFIG_BOOL(m_enablePlgInRoot, PLUGIN_ENABLE_ROOT, false);
|
||||||
READ_CONFIG_INT_POSITIVE(m_editorfontSize, EDITOR_FONTSIZE,
|
READ_CONFIG_INT_POSITIVE(m_editorfontSize, EDITOR_FONTSIZE,
|
||||||
defaultFontSize);
|
defaultFontSize);
|
||||||
|
m_editorfontSize = qBound(5, m_editorfontSize, 25);
|
||||||
READ_CONFIG_BOOL(m_editorShowHeader, EDITOR_SHOW_ADDR, true);
|
READ_CONFIG_BOOL(m_editorShowHeader, EDITOR_SHOW_ADDR, true);
|
||||||
READ_CONFIG_BOOL(m_editorShowcol, EDITOR_SHOW_COL, true);
|
READ_CONFIG_BOOL(m_editorShowcol, EDITOR_SHOW_COL, true);
|
||||||
READ_CONFIG_BOOL(m_editorShowtext, EDITOR_SHOW_TEXT, true);
|
READ_CONFIG_BOOL(m_editorShowtext, EDITOR_SHOW_TEXT, true);
|
||||||
|
@ -146,6 +154,8 @@ void SettingManager::load() {
|
||||||
READ_CONFIG_BOOL(m_scriptEnabled, SCRIPT_ENABLE, true);
|
READ_CONFIG_BOOL(m_scriptEnabled, SCRIPT_ENABLE, true);
|
||||||
READ_CONFIG_BOOL(m_allowUsrScriptInRoot, SCRIPT_ALLOW_USRSCRIPT_INROOT,
|
READ_CONFIG_BOOL(m_allowUsrScriptInRoot, SCRIPT_ALLOW_USRSCRIPT_INROOT,
|
||||||
false);
|
false);
|
||||||
|
READ_CONFIG_INT(m_scriptTimeout, SCRIPT_TIMEOUT, 10);
|
||||||
|
m_scriptTimeout = qBound(0, m_scriptTimeout, 312480);
|
||||||
m_usrHideCats =
|
m_usrHideCats =
|
||||||
READ_CONFIG(SCRIPT_USRHIDECATS, QStringList()).toStringList();
|
READ_CONFIG(SCRIPT_USRHIDECATS, QStringList()).toStringList();
|
||||||
m_sysHideCats =
|
m_sysHideCats =
|
||||||
|
@ -185,6 +195,16 @@ QVariantList SettingManager::getVarList(
|
||||||
return varlist;
|
return varlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SettingManager::scriptTimeout() const { return m_scriptTimeout; }
|
||||||
|
|
||||||
|
void SettingManager::setScriptTimeout(int newScriptTimeout) {
|
||||||
|
newScriptTimeout = qBound(0, newScriptTimeout, 312480);
|
||||||
|
if (m_scriptTimeout != newScriptTimeout) {
|
||||||
|
m_scriptTimeout = newScriptTimeout;
|
||||||
|
_setUnsaved.setFlag(SETTING_ITEM::SCRIPT_TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
qsizetype SettingManager::logCount() const { return m_logCount; }
|
qsizetype SettingManager::logCount() const { return m_logCount; }
|
||||||
|
|
||||||
void SettingManager::setLogCount(qsizetype newLogCount) {
|
void SettingManager::setLogCount(qsizetype newLogCount) {
|
||||||
|
@ -392,6 +412,7 @@ void SettingManager::save(SETTINGS cat) {
|
||||||
}
|
}
|
||||||
if (cat.testFlag(SETTING::SCRIPT)) {
|
if (cat.testFlag(SETTING::SCRIPT)) {
|
||||||
WRITE_CONFIG_SET(SCRIPT_ENABLE, m_scriptEnabled);
|
WRITE_CONFIG_SET(SCRIPT_ENABLE, m_scriptEnabled);
|
||||||
|
WRITE_CONFIG_SET(SCRIPT_TIMEOUT, m_scriptTimeout);
|
||||||
WRITE_CONFIG_SET(SCRIPT_ALLOW_USRSCRIPT_INROOT, m_allowUsrScriptInRoot);
|
WRITE_CONFIG_SET(SCRIPT_ALLOW_USRSCRIPT_INROOT, m_allowUsrScriptInRoot);
|
||||||
WRITE_CONFIG_SET(SCRIPT_USRHIDECATS, m_usrHideCats);
|
WRITE_CONFIG_SET(SCRIPT_USRHIDECATS, m_usrHideCats);
|
||||||
WRITE_CONFIG_SET(SCRIPT_SYSHIDECATS, m_sysHideCats);
|
WRITE_CONFIG_SET(SCRIPT_SYSHIDECATS, m_sysHideCats);
|
||||||
|
@ -409,44 +430,49 @@ void SettingManager::save(SETTINGS cat) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingManager::reset(SETTINGS cat) {
|
void SettingManager::reset(SETTINGS cat) {
|
||||||
|
__reset(cat);
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingManager::__reset(SETTINGS cat) {
|
||||||
HANDLE_CONFIG;
|
HANDLE_CONFIG;
|
||||||
if (cat.testFlag(SETTING::APP)) {
|
if (cat.testFlag(SETTING::APP)) {
|
||||||
WRITE_CONFIG_SET(SKIN_THEME, 0);
|
WRITE_CONFIG_RESET(SKIN_THEME, 0);
|
||||||
WRITE_CONFIG_SET(APP_LANGUAGE, QString());
|
WRITE_CONFIG_RESET(APP_LANGUAGE, QString());
|
||||||
WRITE_CONFIG_SET(APP_FONTFAMILY, _defaultFont.family());
|
WRITE_CONFIG_RESET(APP_FONTFAMILY, _defaultFont.family());
|
||||||
WRITE_CONFIG_SET(APP_FONTSIZE, _defaultFont.pointSize());
|
WRITE_CONFIG_RESET(APP_FONTSIZE, _defaultFont.pointSize());
|
||||||
WRITE_CONFIG_SET(APP_WINDOWSIZE, Qt::WindowMaximized);
|
WRITE_CONFIG_RESET(APP_WINDOWSIZE, Qt::WindowMaximized);
|
||||||
}
|
}
|
||||||
if (cat.testFlag(SETTING::PLUGIN)) {
|
if (cat.testFlag(SETTING::PLUGIN)) {
|
||||||
WRITE_CONFIG_SET(PLUGIN_ENABLE, true);
|
WRITE_CONFIG_RESET(PLUGIN_ENABLE, true);
|
||||||
WRITE_CONFIG_SET(PLUGIN_ENABLE_ROOT, false);
|
WRITE_CONFIG_RESET(PLUGIN_ENABLE_ROOT, false);
|
||||||
}
|
}
|
||||||
if (cat.testFlag(SETTING::EDITOR)) {
|
if (cat.testFlag(SETTING::EDITOR)) {
|
||||||
WRITE_CONFIG_SET(EDITOR_FONTSIZE, _defaultFont.pointSize());
|
WRITE_CONFIG_RESET(EDITOR_FONTSIZE, _defaultFont.pointSize());
|
||||||
WRITE_CONFIG_SET(EDITOR_SHOW_ADDR, true);
|
WRITE_CONFIG_RESET(EDITOR_SHOW_ADDR, true);
|
||||||
WRITE_CONFIG_SET(EDITOR_SHOW_COL, true);
|
WRITE_CONFIG_RESET(EDITOR_SHOW_COL, true);
|
||||||
WRITE_CONFIG_SET(EDITOR_SHOW_TEXT, true);
|
WRITE_CONFIG_RESET(EDITOR_SHOW_TEXT, true);
|
||||||
WRITE_CONFIG_SET(EDITOR_FIND_MAXCOUNT, 100);
|
WRITE_CONFIG_RESET(EDITOR_FIND_MAXCOUNT, 100);
|
||||||
WRITE_CONFIG_SET(EDITOR_COPY_LIMIT, 100);
|
WRITE_CONFIG_RESET(EDITOR_COPY_LIMIT, 100);
|
||||||
WRITE_CONFIG_SET(EDITOR_DECSTRLIMIT, 10);
|
WRITE_CONFIG_RESET(EDITOR_DECSTRLIMIT, 10);
|
||||||
}
|
}
|
||||||
if (cat.testFlag(SETTING::SCRIPT)) {
|
if (cat.testFlag(SETTING::SCRIPT)) {
|
||||||
WRITE_CONFIG_SET(SCRIPT_ENABLE, true);
|
WRITE_CONFIG_RESET(SCRIPT_ENABLE, true);
|
||||||
WRITE_CONFIG_SET(SCRIPT_ALLOW_USRSCRIPT_INROOT, false);
|
WRITE_CONFIG_RESET(SCRIPT_TIMEOUT, 10);
|
||||||
WRITE_CONFIG_SET(SCRIPT_USRHIDECATS, QStringList());
|
WRITE_CONFIG_RESET(SCRIPT_ALLOW_USRSCRIPT_INROOT, false);
|
||||||
WRITE_CONFIG_SET(SCRIPT_SYSHIDECATS, QStringList());
|
WRITE_CONFIG_RESET(SCRIPT_USRHIDECATS, QStringList());
|
||||||
|
WRITE_CONFIG_RESET(SCRIPT_SYSHIDECATS, QStringList());
|
||||||
}
|
}
|
||||||
if (cat.testFlag(SETTING::OTHER)) {
|
if (cat.testFlag(SETTING::OTHER)) {
|
||||||
WRITE_CONFIG_SET(OTHER_USESYS_FILEDIALOG, true);
|
WRITE_CONFIG_RESET(OTHER_USESYS_FILEDIALOG, true);
|
||||||
#ifdef WINGHEX_USE_FRAMELESS
|
#ifdef WINGHEX_USE_FRAMELESS
|
||||||
WRITE_CONFIG_SET(OTHER_USE_NATIVE_TITLEBAR, false);
|
WRITE_CONFIG_RESET(OTHER_USE_NATIVE_TITLEBAR, false);
|
||||||
#endif
|
#endif
|
||||||
WRITE_CONFIG_SET(OTHER_DONT_USE_SPLASH, false);
|
WRITE_CONFIG_RESET(OTHER_DONT_USE_SPLASH, false);
|
||||||
WRITE_CONFIG_SET(OTHER_CHECK_UPDATE, false);
|
WRITE_CONFIG_RESET(OTHER_CHECK_UPDATE, false);
|
||||||
WRITE_CONFIG_SET(OTHER_LOG_LEVEL, Logger::defaultLevel());
|
WRITE_CONFIG_RESET(OTHER_LOG_LEVEL, Logger::defaultLevel());
|
||||||
WRITE_CONFIG_SET(OTHER_LOG_COUNT, 20);
|
WRITE_CONFIG_RESET(OTHER_LOG_COUNT, 20);
|
||||||
}
|
}
|
||||||
load();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qsizetype SettingManager::decodeStrlimit() const { return m_decodeStrlimit; }
|
qsizetype SettingManager::decodeStrlimit() const { return m_decodeStrlimit; }
|
||||||
|
|
|
@ -59,7 +59,7 @@ private:
|
||||||
EDITOR_SHOW_ADDR = 1u << 11,
|
EDITOR_SHOW_ADDR = 1u << 11,
|
||||||
EDITOR_SHOW_COL = 1u << 12,
|
EDITOR_SHOW_COL = 1u << 12,
|
||||||
EDITOR_SHOW_TEXT = 1u << 13,
|
EDITOR_SHOW_TEXT = 1u << 13,
|
||||||
// EDITOR_ENCODING = 1u << 14, // Reserved
|
SCRIPT_TIMEOUT = 1u << 14,
|
||||||
EDITOR_FIND_MAXCOUNT = 1u << 15,
|
EDITOR_FIND_MAXCOUNT = 1u << 15,
|
||||||
EDITOR_COPY_LIMIT = 1u << 16,
|
EDITOR_COPY_LIMIT = 1u << 16,
|
||||||
EDITOR_DECSTRLIMIT = 1u << 17,
|
EDITOR_DECSTRLIMIT = 1u << 17,
|
||||||
|
@ -119,6 +119,8 @@ public:
|
||||||
void save(SETTINGS cat = SETTING::ALL);
|
void save(SETTINGS cat = SETTING::ALL);
|
||||||
void reset(SETTINGS cat);
|
void reset(SETTINGS cat);
|
||||||
|
|
||||||
|
void __reset(SETTINGS cat);
|
||||||
|
|
||||||
QList<RecentFileManager::RecentInfo> recentHexFiles() const;
|
QList<RecentFileManager::RecentInfo> recentHexFiles() const;
|
||||||
void
|
void
|
||||||
setRecentFiles(const QList<RecentFileManager::RecentInfo> &newRecentFiles);
|
setRecentFiles(const QList<RecentFileManager::RecentInfo> &newRecentFiles);
|
||||||
|
@ -172,6 +174,9 @@ public:
|
||||||
qsizetype logCount() const;
|
qsizetype logCount() const;
|
||||||
void setLogCount(qsizetype newLogCount);
|
void setLogCount(qsizetype newLogCount);
|
||||||
|
|
||||||
|
int scriptTimeout() const;
|
||||||
|
void setScriptTimeout(int newScriptTimeout);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void checkWriteableAndWarn();
|
void checkWriteableAndWarn();
|
||||||
|
|
||||||
|
@ -229,6 +234,8 @@ private:
|
||||||
int m_logLevel = 0;
|
int m_logLevel = 0;
|
||||||
qsizetype m_logCount = 20;
|
qsizetype m_logCount = 20;
|
||||||
|
|
||||||
|
int m_scriptTimeout = 60; // min
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QFont _defaultFont;
|
QFont _defaultFont;
|
||||||
SETTING_ITEMS _setUnsaved;
|
SETTING_ITEMS _setUnsaved;
|
||||||
|
|
|
@ -1188,13 +1188,15 @@ QByteArray WingAngelAPI::cArray2ByteArray(const CScriptArray &array, int byteID,
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto len = array.GetSize();
|
||||||
|
|
||||||
QByteArray buffer;
|
QByteArray buffer;
|
||||||
buffer.reserve(array.GetSize());
|
buffer.resize(len);
|
||||||
array.AddRef();
|
array.AddRef();
|
||||||
for (asUINT i = 0; i < array.GetSize(); ++i) {
|
|
||||||
auto item = reinterpret_cast<const asBYTE *>(array.At(i));
|
std::memcpy(buffer.data(), const_cast<CScriptArray &>(array).GetBuffer(),
|
||||||
buffer.append(*item);
|
len);
|
||||||
}
|
|
||||||
array.Release();
|
array.Release();
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
@ -1899,20 +1901,43 @@ bool WingAngelAPI::execScriptCode(const WingHex::SenderInfo &sender,
|
||||||
|
|
||||||
bool WingAngelAPI::execScript(const WingHex::SenderInfo &sender,
|
bool WingAngelAPI::execScript(const WingHex::SenderInfo &sender,
|
||||||
const QString &fileName) {
|
const QString &fileName) {
|
||||||
auto handles = _handles;
|
|
||||||
auto ret = ScriptMachine::instance().executeScript(
|
auto exec = [this, fileName]() -> bool {
|
||||||
ScriptMachine::Background, fileName);
|
auto handles = _handles;
|
||||||
cleanUpHandles(handles);
|
auto ret = ScriptMachine::instance().executeScript(
|
||||||
return ret;
|
ScriptMachine::Background, fileName);
|
||||||
|
cleanUpHandles(handles);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (QThread::currentThread() != qApp->thread()) {
|
||||||
|
bool ret = false;
|
||||||
|
QMetaObject::invokeMethod(qApp, exec, Qt::BlockingQueuedConnection,
|
||||||
|
&ret);
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
return exec();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WingAngelAPI::execCode(const WingHex::SenderInfo &sender,
|
bool WingAngelAPI::execCode(const WingHex::SenderInfo &sender,
|
||||||
const QString &code) {
|
const QString &code) {
|
||||||
auto handles = _handles;
|
auto exec = [this, code]() -> bool {
|
||||||
auto ret =
|
auto handles = _handles;
|
||||||
ScriptMachine::instance().executeCode(ScriptMachine::Background, code);
|
auto ret = ScriptMachine::instance().executeCode(
|
||||||
cleanUpHandles(handles);
|
ScriptMachine::Background, code);
|
||||||
return ret;
|
cleanUpHandles(handles);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (QThread::currentThread() != qApp->thread()) {
|
||||||
|
bool ret = false;
|
||||||
|
QMetaObject::invokeMethod(qApp, exec, Qt::BlockingQueuedConnection,
|
||||||
|
&ret);
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
return exec();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<void *> WingAngelAPI::retriveAsCArray(const WingHex::SenderInfo &sender,
|
QVector<void *> WingAngelAPI::retriveAsCArray(const WingHex::SenderInfo &sender,
|
||||||
|
@ -1970,9 +1995,7 @@ void *WingAngelAPI::vector2AsArray(const WingHex::SenderInfo &sender,
|
||||||
if (info) {
|
if (info) {
|
||||||
auto len = content.length();
|
auto len = content.length();
|
||||||
auto arr = CScriptArray::Create(info, len);
|
auto arr = CScriptArray::Create(info, len);
|
||||||
for (decltype(len) i = 0; i < len; ++i) {
|
std::memcpy(arr->GetBuffer(), content.data(), len);
|
||||||
arr->SetValue(i, content.at(i));
|
|
||||||
}
|
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1980,6 +2003,10 @@ void *WingAngelAPI::vector2AsArray(const WingHex::SenderInfo &sender,
|
||||||
|
|
||||||
void *WingAngelAPI::list2AsArray(const WingHex::SenderInfo &sender,
|
void *WingAngelAPI::list2AsArray(const WingHex::SenderInfo &sender,
|
||||||
MetaType type, const QList<void *> &content) {
|
MetaType type, const QList<void *> &content) {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
static_assert(std::is_same_v<QList<int>, QVector<int>>);
|
||||||
|
return vector2AsArray(sender, type, content);
|
||||||
|
#else
|
||||||
Q_UNUSED(sender);
|
Q_UNUSED(sender);
|
||||||
auto typeStr = PluginSystem::type2AngelScriptString(
|
auto typeStr = PluginSystem::type2AngelScriptString(
|
||||||
MetaType(type | MetaType::Array), false, true);
|
MetaType(type | MetaType::Array), false, true);
|
||||||
|
@ -1998,6 +2025,7 @@ void *WingAngelAPI::list2AsArray(const WingHex::SenderInfo &sender,
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void WingAngelAPI::deleteAsArray(const WingHex::SenderInfo &sender,
|
void WingAngelAPI::deleteAsArray(const WingHex::SenderInfo &sender,
|
||||||
|
|
|
@ -95,7 +95,7 @@ WingCStruct::WingCStruct() : WingHex::IWingPlugin() {
|
||||||
info.fn = std::bind(
|
info.fn = std::bind(
|
||||||
QOverload<const QVariantList &>::of(&WingCStruct::structTypes),
|
QOverload<const QVariantList &>::of(&WingCStruct::structTypes),
|
||||||
this, std::placeholders::_1);
|
this, std::placeholders::_1);
|
||||||
info.ret = MetaType(MetaType::String | MetaType::Array);
|
info.ret = MetaType::String | MetaType::Array;
|
||||||
|
|
||||||
_scriptInfo.insert(QStringLiteral("structTypes"), info);
|
_scriptInfo.insert(QStringLiteral("structTypes"), info);
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ WingCStruct::WingCStruct() : WingHex::IWingPlugin() {
|
||||||
info.fn = std::bind(
|
info.fn = std::bind(
|
||||||
QOverload<const QVariantList &>::of(&WingCStruct::constDefines),
|
QOverload<const QVariantList &>::of(&WingCStruct::constDefines),
|
||||||
this, std::placeholders::_1);
|
this, std::placeholders::_1);
|
||||||
info.ret = MetaType(MetaType::String | MetaType::Array);
|
info.ret = MetaType::String | MetaType::Array;
|
||||||
|
|
||||||
_scriptInfo.insert(QStringLiteral("constDefines"), info);
|
_scriptInfo.insert(QStringLiteral("constDefines"), info);
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ WingCStruct::WingCStruct() : WingHex::IWingPlugin() {
|
||||||
info.fn = std::bind(
|
info.fn = std::bind(
|
||||||
QOverload<const QVariantList &>::of(&WingCStruct::readRaw), this,
|
QOverload<const QVariantList &>::of(&WingCStruct::readRaw), this,
|
||||||
std::placeholders::_1);
|
std::placeholders::_1);
|
||||||
info.ret = MetaType(MetaType::Byte | MetaType::Array);
|
info.ret = MetaType::Byte | MetaType::Array;
|
||||||
|
|
||||||
info.params.append(
|
info.params.append(
|
||||||
qMakePair(getqsizetypeMetaType(), QStringLiteral("offset")));
|
qMakePair(getqsizetypeMetaType(), QStringLiteral("offset")));
|
||||||
|
@ -542,7 +542,6 @@ CScriptDictionary *WingCStruct::convert2AsDictionary(const QVariantHash &hash) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case QMetaType::Bool:
|
case QMetaType::Bool:
|
||||||
case QMetaType::UChar:
|
case QMetaType::UChar:
|
||||||
case QMetaType::Char:
|
|
||||||
case QMetaType::Int:
|
case QMetaType::Int:
|
||||||
case QMetaType::Long:
|
case QMetaType::Long:
|
||||||
case QMetaType::LongLong:
|
case QMetaType::LongLong:
|
||||||
|
@ -564,6 +563,13 @@ CScriptDictionary *WingCStruct::convert2AsDictionary(const QVariantHash &hash) {
|
||||||
case QMetaType::Float:
|
case QMetaType::Float:
|
||||||
dic->Set(p->first, var.toDouble());
|
dic->Set(p->first, var.toDouble());
|
||||||
break;
|
break;
|
||||||
|
case QMetaType::Char: {
|
||||||
|
auto v = var.value<char>();
|
||||||
|
auto ch = new QChar(v);
|
||||||
|
auto id = engine->GetTypeIdByDecl("char");
|
||||||
|
dic->Set(p->first, ch, id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
case QMetaType::Char16: {
|
case QMetaType::Char16: {
|
||||||
auto v = var.value<char16_t>();
|
auto v = var.value<char16_t>();
|
||||||
|
|
|
@ -104,11 +104,20 @@ void CodeEdit::onCompletion(const QModelIndex &index) {
|
||||||
if (selfdata.type == CodeInfoTip::Type::Function ||
|
if (selfdata.type == CodeInfoTip::Type::Function ||
|
||||||
selfdata.type == CodeInfoTip::Type::ClsFunction) {
|
selfdata.type == CodeInfoTip::Type::ClsFunction) {
|
||||||
auto args = selfdata.addinfo.value(CodeInfoTip::Args);
|
auto args = selfdata.addinfo.value(CodeInfoTip::Args);
|
||||||
auto cursor = textCursor();
|
|
||||||
cursor.insertText(QStringLiteral("()"));
|
auto cur = textCursor();
|
||||||
if (!args.isEmpty()) {
|
cur.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
|
||||||
cursor.movePosition(QTextCursor::Left);
|
auto ch = cur.selectedText();
|
||||||
setTextCursor(cursor);
|
if (ch.isEmpty() || ch.front().isSpace()) {
|
||||||
|
auto cursor = textCursor();
|
||||||
|
cursor.insertText(QStringLiteral("()"));
|
||||||
|
if (!args.isEmpty()) {
|
||||||
|
cursor.movePosition(QTextCursor::Left);
|
||||||
|
setTextCursor(cursor);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto cursor = textCursor();
|
||||||
|
cursor.insertText(QStringLiteral("("));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,6 +189,8 @@ void CodeEdit::applyEditorSetStyle() {
|
||||||
|
|
||||||
SearchReplaceWidget *CodeEdit::searchWidget() const { return m_searchWidget; }
|
SearchReplaceWidget *CodeEdit::searchWidget() const { return m_searchWidget; }
|
||||||
|
|
||||||
|
void CodeEdit::setContentModified(bool b) { emit contentModified(b); }
|
||||||
|
|
||||||
void CodeEdit::resizeEvent(QResizeEvent *event) {
|
void CodeEdit::resizeEvent(QResizeEvent *event) {
|
||||||
if (event)
|
if (event)
|
||||||
WingCodeEdit::resizeEvent(event);
|
WingCodeEdit::resizeEvent(event);
|
||||||
|
|
|
@ -35,6 +35,9 @@ public:
|
||||||
|
|
||||||
SearchReplaceWidget *searchWidget() const;
|
SearchReplaceWidget *searchWidget() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setContentModified(bool b);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void contentModified(bool b);
|
void contentModified(bool b);
|
||||||
|
|
||||||
|
|
|
@ -227,7 +227,7 @@ EditorView::FindError EditorView::find(const FindDialog::Result &result) {
|
||||||
end = cur->selectionEnd(0).offset();
|
end = cur->selectionEnd(0).offset();
|
||||||
} break;
|
} break;
|
||||||
default: {
|
default: {
|
||||||
begin = -1;
|
begin = 0;
|
||||||
end = -1;
|
end = -1;
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
@ -242,8 +242,12 @@ EditorView::FindError EditorView::find(const FindDialog::Result &result) {
|
||||||
contextLen = raw.length();
|
contextLen = raw.length();
|
||||||
m_findResults->setEncoding(result.encoding);
|
m_findResults->setEncoding(result.encoding);
|
||||||
d->findAllBytes(begin, end, raw, results);
|
d->findAllBytes(begin, end, raw, results);
|
||||||
|
m_findResults->lastFindData() = qMakePair(data, contextLen);
|
||||||
} else {
|
} else {
|
||||||
contextLen = d->findAllBytesExt(begin, end, result.str, results);
|
// assuming the find pattern is 'xx xx xx xx'
|
||||||
|
contextLen = std::count(data.begin(), data.end(), ' ') + 1;
|
||||||
|
d->findAllBytesExt(begin, end, result.str, results);
|
||||||
|
m_findResults->lastFindData() = qMakePair(data, contextLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_findResults->beginUpdate();
|
m_findResults->beginUpdate();
|
||||||
|
@ -257,12 +261,9 @@ EditorView::FindError EditorView::find(const FindDialog::Result &result) {
|
||||||
r.col = r.offset % lineWidth;
|
r.col = r.offset % lineWidth;
|
||||||
m_findResults->results().append(r);
|
m_findResults->results().append(r);
|
||||||
m_findResults->findData().append(
|
m_findResults->findData().append(
|
||||||
readContextFinding(ritem, contextLen, FIND_CONTEXT_SIZE,
|
readContextFinding(ritem, contextLen));
|
||||||
FIND_MAX_DISPLAY_FIND_CHARS));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_findResults->lastFindData() = data;
|
|
||||||
|
|
||||||
m_findResults->endUpdate();
|
m_findResults->endUpdate();
|
||||||
|
|
||||||
if (m_findResults->size() >= QHEXVIEW_FIND_LIMIT) {
|
if (m_findResults->size() >= QHEXVIEW_FIND_LIMIT) {
|
||||||
|
@ -287,9 +288,8 @@ ErrFile EditorView::newFile(size_t index) {
|
||||||
if (isCloneFile()) {
|
if (isCloneFile()) {
|
||||||
return ErrFile::ClonedFile;
|
return ErrFile::ClonedFile;
|
||||||
}
|
}
|
||||||
if (!m_fileName.isEmpty()) {
|
|
||||||
_watcher.removePath(m_fileName);
|
removeMonitorPaths();
|
||||||
}
|
|
||||||
auto istr = QString::number(index);
|
auto istr = QString::number(index);
|
||||||
m_fileName = tr("Untitled") + istr;
|
m_fileName = tr("Untitled") + istr;
|
||||||
this->setWindowTitle(m_fileName);
|
this->setWindowTitle(m_fileName);
|
||||||
|
@ -326,9 +326,7 @@ ErrFile EditorView::openFile(const QString &filename) {
|
||||||
return ErrFile::Permission;
|
return ErrFile::Permission;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_fileName.isEmpty()) {
|
removeMonitorPaths();
|
||||||
_watcher.removePath(m_fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
|
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
|
||||||
m_hex->setLockedFile(readonly);
|
m_hex->setLockedFile(readonly);
|
||||||
|
@ -346,7 +344,7 @@ ErrFile EditorView::openFile(const QString &filename) {
|
||||||
tab->setIcon(Utilities::getIconFromFile(style(), m_fileName));
|
tab->setIcon(Utilities::getIconFromFile(style(), m_fileName));
|
||||||
tab->setToolTip(m_fileName);
|
tab->setToolTip(m_fileName);
|
||||||
|
|
||||||
_watcher.addPath(m_fileName);
|
addMonitorPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrFile::Success;
|
return ErrFile::Success;
|
||||||
|
@ -384,9 +382,7 @@ ErrFile EditorView::openExtFile(const QString &ext, const QString &file) {
|
||||||
return ErrFile::Error;
|
return ErrFile::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_fileName.isEmpty()) {
|
removeMonitorPaths();
|
||||||
_watcher.removePath(m_fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
|
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
|
||||||
m_hex->setLockedFile(readonly);
|
m_hex->setLockedFile(readonly);
|
||||||
|
@ -574,19 +570,20 @@ ErrFile EditorView::save(const QString &workSpaceName, const QString &path,
|
||||||
return ErrFile::Permission;
|
return ErrFile::Permission;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeMonitorPaths();
|
||||||
|
|
||||||
if (doc->saveTo(&file, !isExport)) {
|
if (doc->saveTo(&file, !isExport)) {
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
if (!isExport) {
|
if (!isExport) {
|
||||||
if (!m_fileName.isEmpty()) {
|
|
||||||
_watcher.removePath(m_fileName);
|
|
||||||
}
|
|
||||||
m_fileName = QFileInfo(fileName).absoluteFilePath();
|
m_fileName = QFileInfo(fileName).absoluteFilePath();
|
||||||
m_isNewFile = false;
|
m_isNewFile = false;
|
||||||
m_docType = DocumentType::File;
|
m_docType = DocumentType::File;
|
||||||
doc->setDocSaved();
|
doc->setDocSaved();
|
||||||
_watcher.addPath(m_fileName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addMonitorPath();
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
adjustPermission();
|
adjustPermission();
|
||||||
#endif
|
#endif
|
||||||
|
@ -737,6 +734,16 @@ void EditorView::connectDocSavedFlag(EditorView *editor) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorView::removeMonitorPaths() {
|
||||||
|
auto files = _watcher.files();
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_watcher.removePaths(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorView::addMonitorPath() { _watcher.addPath(m_fileName); }
|
||||||
|
|
||||||
BookMarksModel *EditorView::bookmarksModel() const { return m_bookmarks; }
|
BookMarksModel *EditorView::bookmarksModel() const { return m_bookmarks; }
|
||||||
|
|
||||||
MetaDataModel *EditorView::metadataModel() const { return m_metadata; }
|
MetaDataModel *EditorView::metadataModel() const { return m_metadata; }
|
||||||
|
@ -804,34 +811,50 @@ bool EditorView::checkHasUnsavedState() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
FindResultModel::FindInfo EditorView::readContextFinding(qsizetype offset,
|
FindResultModel::FindInfo EditorView::readContextFinding(qsizetype offset,
|
||||||
qsizetype findSize,
|
qsizetype findSize) {
|
||||||
int contextSize,
|
constexpr long DISPLAY_SIZE = 16;
|
||||||
int maxDisplayBytes) {
|
constexpr long FIND_CONTENXT_LEN = 10;
|
||||||
|
constexpr long HT_SIZE = (DISPLAY_SIZE - FIND_CONTENXT_LEN) / 2;
|
||||||
|
|
||||||
auto doc = m_hex->document();
|
auto doc = m_hex->document();
|
||||||
|
if (findSize <= FIND_CONTENXT_LEN) {
|
||||||
|
long leftsize = FIND_CONTENXT_LEN - findSize;
|
||||||
|
auto rs = std::div(leftsize, 2l);
|
||||||
|
|
||||||
qsizetype halfSize = maxDisplayBytes / 2;
|
auto headerlen = HT_SIZE + rs.quot + rs.rem;
|
||||||
auto header = doc->read(offset, qMin(findSize, halfSize));
|
auto taillen = HT_SIZE + rs.quot;
|
||||||
QByteArray tailer;
|
|
||||||
if (header.size() < findSize) {
|
auto begin = qMax(offset - headerlen, 0);
|
||||||
auto len = qMin(findSize, qsizetype(maxDisplayBytes) - halfSize);
|
auto end = qMin(offset + findSize + taillen, doc->length());
|
||||||
tailer = doc->read(offset + findSize - len, len);
|
auto boff = offset - begin;
|
||||||
|
|
||||||
|
auto buffer = doc->read(begin, end - begin + 1);
|
||||||
|
|
||||||
|
FindResultModel::FindInfo info;
|
||||||
|
info.cheader = buffer.left(boff);
|
||||||
|
info.hbuffer = buffer.sliced(boff, findSize);
|
||||||
|
info.tbuffer = {};
|
||||||
|
info.ctailer = buffer.sliced(boff + findSize);
|
||||||
|
return info;
|
||||||
|
} else {
|
||||||
|
constexpr long FIND_CONTENXT_HALF = FIND_CONTENXT_LEN / 2;
|
||||||
|
|
||||||
|
auto hbegin = qMax(offset - HT_SIZE, 0);
|
||||||
|
auto hlen = offset - hbegin + FIND_CONTENXT_HALF;
|
||||||
|
auto header = doc->read(hbegin, hlen);
|
||||||
|
auto toff = offset - hbegin;
|
||||||
|
|
||||||
|
auto tbegin = offset + findSize - FIND_CONTENXT_HALF;
|
||||||
|
auto tlen = HT_SIZE + FIND_CONTENXT_HALF;
|
||||||
|
auto tailer = doc->read(tbegin, tlen);
|
||||||
|
|
||||||
|
FindResultModel::FindInfo info;
|
||||||
|
info.cheader = header.left(toff);
|
||||||
|
info.hbuffer = header.sliced(toff);
|
||||||
|
info.tbuffer = tailer.left(FIND_CONTENXT_HALF);
|
||||||
|
info.ctailer = tailer.sliced(FIND_CONTENXT_HALF);
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorView::applyFunctionTables(WingEditorViewWidget *view,
|
void EditorView::applyFunctionTables(WingEditorViewWidget *view,
|
||||||
|
|
|
@ -225,9 +225,7 @@ private:
|
||||||
bool checkHasUnsavedState() const;
|
bool checkHasUnsavedState() const;
|
||||||
|
|
||||||
FindResultModel::FindInfo readContextFinding(qsizetype offset,
|
FindResultModel::FindInfo readContextFinding(qsizetype offset,
|
||||||
qsizetype findSize,
|
qsizetype findSize);
|
||||||
int contextSize,
|
|
||||||
int maxDisplayBytes);
|
|
||||||
|
|
||||||
void applyFunctionTables(WingHex::WingEditorViewWidget *view,
|
void applyFunctionTables(WingHex::WingEditorViewWidget *view,
|
||||||
const CallTable &fns);
|
const CallTable &fns);
|
||||||
|
@ -538,7 +536,10 @@ private:
|
||||||
parent->addAction(a);
|
parent->addAction(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
void connectDocSavedFlag(EditorView *editor);
|
void connectDocSavedFlag(EditorView *editor);
|
||||||
|
void removeMonitorPaths();
|
||||||
|
void addMonitorPath();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sigOnCutFile();
|
void sigOnCutFile();
|
||||||
|
|
|
@ -36,6 +36,7 @@ GotoLineWidget::GotoLineWidget(QWidget *parent)
|
||||||
|
|
||||||
m_sbline = new QSpinBox(this);
|
m_sbline = new QSpinBox(this);
|
||||||
m_sbline->setRange(1, 1);
|
m_sbline->setRange(1, 1);
|
||||||
|
m_sbline->setContextMenuPolicy(Qt::NoContextMenu);
|
||||||
m_sbline->setMinimumWidth(120);
|
m_sbline->setMinimumWidth(120);
|
||||||
connect(m_sbline, &QSpinBox::valueChanged, this,
|
connect(m_sbline, &QSpinBox::valueChanged, this,
|
||||||
&GotoLineWidget::onGotoLine);
|
&GotoLineWidget::onGotoLine);
|
||||||
|
|
|
@ -57,7 +57,7 @@ void GotoWidget::handleLineChanged() {
|
||||||
ui->lineEdit->setStyleSheet(QString());
|
ui->lineEdit->setStyleSheet(QString());
|
||||||
emit jumpToLine(p, isline);
|
emit jumpToLine(p, isline);
|
||||||
} else {
|
} else {
|
||||||
ui->lineEdit->setStyleSheet(QStringLiteral("color: red;"));
|
ui->lineEdit->setStyleSheet(QStringLiteral("QLineEdit{color: red}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>400</width>
|
<width>412</width>
|
||||||
<height>50</height>
|
<height>50</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
<string>GoTo:</string>
|
<string>GoTo:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignCenter</set>
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -78,6 +78,9 @@
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="contextMenuPolicy">
|
||||||
|
<enum>Qt::ContextMenuPolicy::NoContextMenu</enum>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -102,3 +102,17 @@ bool QHexTextEdit::isHexMode() const { return m_isHexMode; }
|
||||||
void QHexTextEdit::setIsHexMode(bool newIsHexMode) {
|
void QHexTextEdit::setIsHexMode(bool newIsHexMode) {
|
||||||
m_isHexMode = newIsHexMode;
|
m_isHexMode = newIsHexMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QHexTextEdit::setFindText(const QString &text) {
|
||||||
|
mText = text;
|
||||||
|
mText = mText
|
||||||
|
.removeIf([](const QChar &ch) {
|
||||||
|
return !std::isalnum(ch.unicode()) && ch != '?';
|
||||||
|
})
|
||||||
|
.toUpper();
|
||||||
|
setText(text);
|
||||||
|
auto cur = this->textCursor();
|
||||||
|
cur.movePosition(QTextCursor::End);
|
||||||
|
setTextCursor(cur);
|
||||||
|
mCurserPositionPre = cur.position();
|
||||||
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ public:
|
||||||
bool isHexMode() const;
|
bool isHexMode() const;
|
||||||
void setIsHexMode(bool newIsHexMode);
|
void setIsHexMode(bool newIsHexMode);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// must be 'xx xx xx' style
|
||||||
|
void setFindText(const QString &text);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
|
|
@ -1,257 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
** 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 "qtlonglongspinbox.h"
|
|
||||||
|
|
||||||
#include <QEvent>
|
|
||||||
#include <QKeyEvent>
|
|
||||||
#include <QLineEdit>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
QtLongLongSpinBox::QtLongLongSpinBox(QWidget *parent)
|
|
||||||
: QAbstractSpinBox(parent) {
|
|
||||||
m_minimum = std::numeric_limits<qlonglong>::min();
|
|
||||||
m_maximum = std::numeric_limits<qlonglong>::max();
|
|
||||||
m_value = 0;
|
|
||||||
m_singleStep = 1;
|
|
||||||
m_base = 10;
|
|
||||||
|
|
||||||
setValue(m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
qlonglong QtLongLongSpinBox::value() const { return m_value; }
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::setValue(qlonglong expectedNewValue) {
|
|
||||||
const qlonglong newValue = qBound(m_minimum, expectedNewValue, m_maximum);
|
|
||||||
const QString newValueString = QString::number(newValue, m_base);
|
|
||||||
lineEdit()->setText(m_prefix + newValueString + m_suffix);
|
|
||||||
if (m_value != newValue) {
|
|
||||||
m_value = newValue;
|
|
||||||
emit valueChanged(newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QtLongLongSpinBox::prefix() const { return m_prefix; }
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::setPrefix(const QString &prefix) {
|
|
||||||
m_prefix = prefix;
|
|
||||||
|
|
||||||
setValue(m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QtLongLongSpinBox::suffix() const { return m_suffix; }
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::setSuffix(const QString &suffix) {
|
|
||||||
m_suffix = suffix;
|
|
||||||
|
|
||||||
setValue(m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QtLongLongSpinBox::cleanText() const {
|
|
||||||
return QString::number(m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
qlonglong QtLongLongSpinBox::singleStep() const { return m_singleStep; }
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::setSingleStep(qlonglong step) { m_singleStep = step; }
|
|
||||||
|
|
||||||
qlonglong QtLongLongSpinBox::minimum() const { return m_minimum; }
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::setMinimum(qlonglong min) {
|
|
||||||
m_minimum = min;
|
|
||||||
if (m_maximum < m_minimum) {
|
|
||||||
m_maximum = m_minimum;
|
|
||||||
}
|
|
||||||
|
|
||||||
setValue(m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
qlonglong QtLongLongSpinBox::maximum() const { return m_maximum; }
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::setMaximum(qlonglong max) {
|
|
||||||
m_maximum = max;
|
|
||||||
if (m_maximum < m_minimum) {
|
|
||||||
m_maximum = m_minimum;
|
|
||||||
}
|
|
||||||
|
|
||||||
setValue(m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::setRange(qlonglong min, qlonglong max) {
|
|
||||||
if (min < max) {
|
|
||||||
m_minimum = min;
|
|
||||||
m_maximum = max;
|
|
||||||
} else {
|
|
||||||
m_minimum = max;
|
|
||||||
m_maximum = min;
|
|
||||||
}
|
|
||||||
|
|
||||||
setValue(m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::setDisplayIntegerBase(int base) {
|
|
||||||
if (m_base != base) {
|
|
||||||
m_base = base;
|
|
||||||
setValue(m_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int QtLongLongSpinBox::displayIntegerBase() const { return m_base; }
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::keyPressEvent(QKeyEvent *event) {
|
|
||||||
switch (event->key()) {
|
|
||||||
case Qt::Key_Enter:
|
|
||||||
case Qt::Key_Return:
|
|
||||||
selectCleanText();
|
|
||||||
lineEditEditingFinalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
QAbstractSpinBox::keyPressEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::focusOutEvent(QFocusEvent *event) {
|
|
||||||
lineEditEditingFinalize();
|
|
||||||
|
|
||||||
QAbstractSpinBox::focusOutEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
QAbstractSpinBox::StepEnabled QtLongLongSpinBox::stepEnabled() const {
|
|
||||||
if (isReadOnly()) {
|
|
||||||
return StepNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
StepEnabled se = StepNone;
|
|
||||||
if (wrapping() || m_value < m_maximum) {
|
|
||||||
se |= StepUpEnabled;
|
|
||||||
}
|
|
||||||
if (wrapping() || m_value > m_minimum) {
|
|
||||||
se |= StepDownEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
return se;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::stepBy(int steps) {
|
|
||||||
if (isReadOnly()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_prefix + QString::number(m_value) + m_suffix != lineEdit()->text()) {
|
|
||||||
lineEditEditingFinalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
qlonglong newValue = m_value + (steps * m_singleStep);
|
|
||||||
if (wrapping()) {
|
|
||||||
// emulating the behavior of QSpinBox
|
|
||||||
if (newValue > m_maximum) {
|
|
||||||
if (m_value == m_maximum) {
|
|
||||||
newValue = m_minimum;
|
|
||||||
} else {
|
|
||||||
newValue = m_maximum;
|
|
||||||
}
|
|
||||||
} else if (newValue < m_minimum) {
|
|
||||||
if (m_value == m_minimum) {
|
|
||||||
newValue = m_maximum;
|
|
||||||
} else {
|
|
||||||
newValue = m_minimum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newValue = qBound(m_minimum, newValue, m_maximum);
|
|
||||||
}
|
|
||||||
|
|
||||||
setValue(newValue);
|
|
||||||
selectCleanText();
|
|
||||||
}
|
|
||||||
|
|
||||||
QValidator::State QtLongLongSpinBox::validate(QString &input, int &pos) const {
|
|
||||||
// first, we try to interpret as a number without prefixes
|
|
||||||
bool ok;
|
|
||||||
const qlonglong value = input.toLongLong(&ok);
|
|
||||||
if (input.isEmpty() || (ok && value <= m_maximum)) {
|
|
||||||
input = m_prefix + input + m_suffix;
|
|
||||||
pos += m_prefix.length();
|
|
||||||
return QValidator::Acceptable;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if string of text editor aren't simple number, try to interpret it
|
|
||||||
// as a number with prefix and suffix
|
|
||||||
bool valid = true;
|
|
||||||
if (!m_prefix.isEmpty() && !input.startsWith(m_prefix)) {
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
if (!m_suffix.isEmpty() && !input.endsWith(m_suffix)) {
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valid) {
|
|
||||||
const int start = m_prefix.length();
|
|
||||||
const int length = input.length() - start - m_suffix.length();
|
|
||||||
|
|
||||||
bool ok;
|
|
||||||
const QString number = input.mid(start, length);
|
|
||||||
const qlonglong value = number.toLongLong(&ok);
|
|
||||||
if (number.isEmpty() || (ok && value <= m_maximum)) {
|
|
||||||
return QValidator::Acceptable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise not acceptable
|
|
||||||
return QValidator::Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::lineEditEditingFinalize() {
|
|
||||||
const QString text = lineEdit()->text();
|
|
||||||
|
|
||||||
// first, we try to read as a number without prefixes
|
|
||||||
bool ok;
|
|
||||||
qlonglong value = text.toLongLong(&ok);
|
|
||||||
if (ok) {
|
|
||||||
setValue(value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if string of text editor aren't simple number, try to interpret it
|
|
||||||
// as a number with prefix and suffix
|
|
||||||
bool valid = true;
|
|
||||||
if (!m_prefix.isEmpty() && !text.startsWith(m_prefix)) {
|
|
||||||
valid = false;
|
|
||||||
} else if (!m_suffix.isEmpty() && !text.endsWith(m_suffix)) {
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valid) {
|
|
||||||
const int start = m_prefix.length();
|
|
||||||
const int length = text.length() - start - m_suffix.length();
|
|
||||||
|
|
||||||
bool ok;
|
|
||||||
const qlonglong value = text.mid(start, length).toLongLong(&ok);
|
|
||||||
if (ok) {
|
|
||||||
setValue(value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise set old value
|
|
||||||
setValue(m_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QtLongLongSpinBox::selectCleanText() {
|
|
||||||
lineEdit()->setSelection(m_prefix.length(), lineEdit()->text().length() -
|
|
||||||
m_prefix.length() -
|
|
||||||
m_suffix.length());
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
/*==============================================================================
|
|
||||||
** 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 QTLONGLONGSPINBOX_H
|
|
||||||
#define QTLONGLONGSPINBOX_H
|
|
||||||
|
|
||||||
#include <QAbstractSpinBox>
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
class QtLongLongSpinBox : public QAbstractSpinBox {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit QtLongLongSpinBox(QWidget *parent = 0);
|
|
||||||
|
|
||||||
qlonglong value() const;
|
|
||||||
|
|
||||||
QString prefix() const;
|
|
||||||
void setPrefix(const QString &prefix);
|
|
||||||
|
|
||||||
QString suffix() const;
|
|
||||||
void setSuffix(const QString &suffix);
|
|
||||||
|
|
||||||
QString cleanText() const;
|
|
||||||
|
|
||||||
qlonglong singleStep() const;
|
|
||||||
void setSingleStep(qlonglong val);
|
|
||||||
|
|
||||||
qlonglong minimum() const;
|
|
||||||
void setMinimum(qlonglong min);
|
|
||||||
|
|
||||||
qlonglong maximum() const;
|
|
||||||
void setMaximum(qlonglong max);
|
|
||||||
|
|
||||||
void setRange(qlonglong min, qlonglong max);
|
|
||||||
|
|
||||||
void setDisplayIntegerBase(int base);
|
|
||||||
|
|
||||||
int displayIntegerBase() const;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void setValue(qlonglong value);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void valueChanged(qlonglong i);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void keyPressEvent(QKeyEvent *event);
|
|
||||||
virtual void focusOutEvent(QFocusEvent *event);
|
|
||||||
virtual void stepBy(int steps);
|
|
||||||
virtual StepEnabled stepEnabled() const;
|
|
||||||
virtual QValidator::State validate(QString &input, int &pos) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void lineEditEditingFinalize();
|
|
||||||
void selectCleanText();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString m_prefix;
|
|
||||||
QString m_suffix;
|
|
||||||
qlonglong m_singleStep;
|
|
||||||
qlonglong m_minimum;
|
|
||||||
qlonglong m_maximum;
|
|
||||||
qlonglong m_value;
|
|
||||||
|
|
||||||
int m_base;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Q_DISABLE_COPY(QtLongLongSpinBox)
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // QTLONGLONGSPINBOX_H
|
|
|
@ -57,6 +57,9 @@ ScriptEditor::ScriptEditor(QWidget *parent)
|
||||||
connect(m_editor, &CodeEdit::contentModified, this,
|
connect(m_editor, &CodeEdit::contentModified, this,
|
||||||
[this]() { processTitle(); });
|
[this]() { processTitle(); });
|
||||||
|
|
||||||
|
connect(&_watcher, &QFileSystemWatcher::fileChanged, this,
|
||||||
|
&ScriptEditor::need2Reload);
|
||||||
|
|
||||||
this->setWidget(m_editor);
|
this->setWidget(m_editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,12 +78,30 @@ bool ScriptEditor::openFile(const QString &filename) {
|
||||||
}
|
}
|
||||||
m_editor->setPlainText(QString::fromUtf8(f.readAll()));
|
m_editor->setPlainText(QString::fromUtf8(f.readAll()));
|
||||||
f.close();
|
f.close();
|
||||||
|
|
||||||
|
if (!m_fileName.isEmpty()) {
|
||||||
|
_watcher.removePath(m_fileName);
|
||||||
|
}
|
||||||
|
|
||||||
m_fileName = filename;
|
m_fileName = filename;
|
||||||
|
_watcher.addPath(m_fileName);
|
||||||
|
|
||||||
processTitle();
|
processTitle();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptEditor::save(const QString &path) {
|
bool ScriptEditor::save(const QString &path) {
|
||||||
|
if (!m_fileName.isEmpty()) {
|
||||||
|
_watcher.removePath(m_fileName);
|
||||||
|
}
|
||||||
|
QScopeGuard guard([this, path]() {
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
_watcher.addPath(m_fileName);
|
||||||
|
} else {
|
||||||
|
_watcher.addPath(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
auto needAdjustFile = !QFile::exists(path);
|
auto needAdjustFile = !QFile::exists(path);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include "Qt-Advanced-Docking-System/src/DockWidget.h"
|
#include "Qt-Advanced-Docking-System/src/DockWidget.h"
|
||||||
#include "control/codeedit.h"
|
#include "control/codeedit.h"
|
||||||
|
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
|
||||||
class asIScriptEngine;
|
class asIScriptEngine;
|
||||||
|
|
||||||
class ScriptEditor : public ads::CDockWidget {
|
class ScriptEditor : public ads::CDockWidget {
|
||||||
|
@ -40,6 +42,8 @@ signals:
|
||||||
void onToggleMark(int line);
|
void onToggleMark(int line);
|
||||||
void onFunctionTip(const QString &tip);
|
void onFunctionTip(const QString &tip);
|
||||||
|
|
||||||
|
void need2Reload();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setReadOnly(bool b);
|
void setReadOnly(bool b);
|
||||||
bool openFile(const QString &filename);
|
bool openFile(const QString &filename);
|
||||||
|
@ -57,6 +61,8 @@ private:
|
||||||
private:
|
private:
|
||||||
CodeEdit *m_editor = nullptr;
|
CodeEdit *m_editor = nullptr;
|
||||||
QString m_fileName;
|
QString m_fileName;
|
||||||
|
|
||||||
|
QFileSystemWatcher _watcher;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SCRIPTEDITOR_H
|
#endif // SCRIPTEDITOR_H
|
||||||
|
|
|
@ -225,7 +225,7 @@ void ScriptingConsole::applyScriptSettings() {
|
||||||
auto dfont = QFont(set.consoleFontFamily());
|
auto dfont = QFont(set.consoleFontFamily());
|
||||||
dfont.setPointSize(set.consoleFontSize());
|
dfont.setPointSize(set.consoleFontSize());
|
||||||
|
|
||||||
auto thname = set.editorTheme();
|
auto thname = set.consoleTheme();
|
||||||
if (thname.isEmpty()) {
|
if (thname.isEmpty()) {
|
||||||
switch (SkinManager::instance().currentTheme()) {
|
switch (SkinManager::instance().currentTheme()) {
|
||||||
case SkinManager::Theme::Dark:
|
case SkinManager::Theme::Dark:
|
||||||
|
@ -255,32 +255,28 @@ void ScriptingConsole::runConsoleCommand(const QString &code) {
|
||||||
auto &ins = ScriptMachine::instance();
|
auto &ins = ScriptMachine::instance();
|
||||||
auto mod = ins.module(ScriptMachine::Interactive);
|
auto mod = ins.module(ScriptMachine::Interactive);
|
||||||
if (mod) {
|
if (mod) {
|
||||||
QList<QPair<QByteArray, QByteArray>> vars;
|
|
||||||
auto total = mod->GetGlobalVarCount();
|
auto total = mod->GetGlobalVarCount();
|
||||||
|
|
||||||
// generate codes to print
|
setMode(Output);
|
||||||
QString codes;
|
|
||||||
if (total == 0) {
|
if (total == 0) {
|
||||||
codes = QStringLiteral("print(\"<none>\");");
|
stdOut("<none>");
|
||||||
} else {
|
} else {
|
||||||
|
auto &sm = ScriptMachine::instance();
|
||||||
for (asUINT i = 0; i < total; ++i) {
|
for (asUINT i = 0; i < total; ++i) {
|
||||||
const char *name;
|
const char *name;
|
||||||
int typeId;
|
int typeID;
|
||||||
auto decl = mod->GetGlobalVarDeclaration(i);
|
auto decl = mod->GetGlobalVarDeclaration(i);
|
||||||
if (decl && mod->GetGlobalVar(i, &name) == asSUCCESS) {
|
if (decl && mod->GetGlobalVar(i, &name, nullptr, &typeID) ==
|
||||||
vars.emplaceBack(decl, name);
|
asSUCCESS) {
|
||||||
|
auto value = sm.debugger()->toString(
|
||||||
|
mod->GetAddressOfGlobalVar(i), typeID, sm.engine(),
|
||||||
|
1);
|
||||||
|
stdOut(decl + QStringLiteral(" = ") + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &var : vars) {
|
|
||||||
codes.append("print(\"" + var.first + " = \");print(" +
|
|
||||||
var.second + ");print(\";\\n\");");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setMode(Output);
|
|
||||||
ScriptMachine::instance().executeCode(ScriptMachine::Interactive,
|
|
||||||
codes);
|
|
||||||
_codes.clear();
|
_codes.clear();
|
||||||
appendCommandPrompt();
|
appendCommandPrompt();
|
||||||
setMode(Input);
|
setMode(Input);
|
||||||
|
@ -329,11 +325,13 @@ void ScriptingConsole::runConsoleCommand(const QString &code) {
|
||||||
auto mod = ins.module(ScriptMachine::Interactive);
|
auto mod = ins.module(ScriptMachine::Interactive);
|
||||||
if (mod) {
|
if (mod) {
|
||||||
auto total = mod->GetGlobalVarCount();
|
auto total = mod->GetGlobalVarCount();
|
||||||
asUINT i = total;
|
if (total) {
|
||||||
do {
|
asUINT i = total;
|
||||||
--i;
|
do {
|
||||||
mod->RemoveGlobalVar(i);
|
--i;
|
||||||
} while (i);
|
mod->RemoveGlobalVar(i);
|
||||||
|
} while (i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_codes.clear();
|
_codes.clear();
|
||||||
appendCommandPrompt();
|
appendCommandPrompt();
|
||||||
|
@ -391,11 +389,20 @@ void ScriptingConsole::onCompletion(const QModelIndex &index) {
|
||||||
if (selfdata.type == CodeInfoTip::Type::Function ||
|
if (selfdata.type == CodeInfoTip::Type::Function ||
|
||||||
selfdata.type == CodeInfoTip::Type::ClsFunction) {
|
selfdata.type == CodeInfoTip::Type::ClsFunction) {
|
||||||
auto args = selfdata.addinfo.value(CodeInfoTip::Args);
|
auto args = selfdata.addinfo.value(CodeInfoTip::Args);
|
||||||
auto cursor = textCursor();
|
|
||||||
cursor.insertText(QStringLiteral("()"));
|
auto cur = textCursor();
|
||||||
if (!args.isEmpty()) {
|
cur.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
|
||||||
cursor.movePosition(QTextCursor::Left);
|
auto ch = cur.selectedText();
|
||||||
setTextCursor(cursor);
|
if (ch.isEmpty() || ch.front().isSpace()) {
|
||||||
|
auto cursor = textCursor();
|
||||||
|
cursor.insertText(QStringLiteral("()"));
|
||||||
|
if (!args.isEmpty()) {
|
||||||
|
cursor.movePosition(QTextCursor::Left);
|
||||||
|
setTextCursor(cursor);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto cursor = textCursor();
|
||||||
|
cursor.insertText(QStringLiteral("("));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,7 +416,12 @@ void ScriptingConsole::paste() {
|
||||||
const QString text = clipboard->text();
|
const QString text = clipboard->text();
|
||||||
if (!text.isEmpty()) {
|
if (!text.isEmpty()) {
|
||||||
if (text.indexOf('\n') < 0) {
|
if (text.indexOf('\n') < 0) {
|
||||||
replaceCommandLine(text);
|
if (isCursorInEditZone()) {
|
||||||
|
auto cursor = this->textCursor();
|
||||||
|
cursor.insertText(text);
|
||||||
|
} else {
|
||||||
|
replaceCommandLine(text);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
auto ret = WingMessageBox::question(
|
auto ret = WingMessageBox::question(
|
||||||
nullptr, tr("MultiCodeCanNotUndo"), text);
|
nullptr, tr("MultiCodeCanNotUndo"), text);
|
||||||
|
@ -439,6 +451,12 @@ void ScriptingConsole::paste() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ScriptingConsole::isTerminal() const { return _isTerminal; }
|
||||||
|
|
||||||
|
void ScriptingConsole::setIsTerminal(bool newIsTerminal) {
|
||||||
|
_isTerminal = newIsTerminal;
|
||||||
|
}
|
||||||
|
|
||||||
QString ScriptingConsole::currentCodes() const {
|
QString ScriptingConsole::currentCodes() const {
|
||||||
QTextCursor textCursor = this->textCursor();
|
QTextCursor textCursor = this->textCursor();
|
||||||
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
|
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
|
||||||
|
@ -448,24 +466,38 @@ QString ScriptingConsole::currentCodes() const {
|
||||||
void ScriptingConsole::contextMenuEvent(QContextMenuEvent *event) {
|
void ScriptingConsole::contextMenuEvent(QContextMenuEvent *event) {
|
||||||
QMenu menu(this);
|
QMenu menu(this);
|
||||||
|
|
||||||
menu.addAction(QIcon(QStringLiteral(":/qeditor/copy.png")), tr("Copy"),
|
auto a = menu.addAction(QIcon(QStringLiteral(":/qeditor/copy.png")),
|
||||||
QKeySequence(QKeySequence::Copy), this,
|
tr("Copy"), QKeySequence(QKeySequence::Copy), this,
|
||||||
&ScriptingConsole::copy);
|
&ScriptingConsole::copy);
|
||||||
menu.addAction(QIcon(QStringLiteral(":/qeditor/cut.png")), tr("Cut"),
|
a->setShortcutContext(Qt::WidgetShortcut);
|
||||||
QKeySequence(QKeySequence::Cut), this,
|
a = menu.addAction(QIcon(QStringLiteral(":/qeditor/cut.png")), tr("Cut"),
|
||||||
&ScriptingConsole::cut);
|
QKeySequence(QKeySequence::Cut), this,
|
||||||
menu.addAction(QIcon(QStringLiteral(":/qeditor/paste.png")), tr("Paste"),
|
&ScriptingConsole::cut);
|
||||||
QKeySequence(QKeySequence::Paste), this,
|
a->setShortcutContext(Qt::WidgetShortcut);
|
||||||
&ScriptingConsole::paste);
|
a = menu.addAction(QIcon(QStringLiteral(":/qeditor/paste.png")),
|
||||||
menu.addAction(ICONRES(QStringLiteral("del")), tr("Clear"),
|
tr("Paste"), QKeySequence(QKeySequence::Paste), this,
|
||||||
QKeySequence(Qt::ControlModifier | Qt::Key_L), this,
|
&ScriptingConsole::paste);
|
||||||
&ScriptingConsole::clearConsole);
|
a->setShortcutContext(Qt::WidgetShortcut);
|
||||||
menu.addSeparator();
|
|
||||||
menu.addAction(ICONRES(QStringLiteral("dbgstop")), tr("AbortScript"),
|
if (_isTerminal) {
|
||||||
QKeySequence(Qt::ControlModifier | Qt::Key_Q), []() {
|
a = menu.addAction(ICONRES(QStringLiteral("del")), tr("Clear"),
|
||||||
ScriptMachine::instance().abortScript(
|
QKeySequence(Qt::ControlModifier | Qt::Key_L), this,
|
||||||
ScriptMachine::Background);
|
&ScriptingConsole::clearConsole);
|
||||||
});
|
a->setShortcutContext(Qt::WidgetShortcut);
|
||||||
|
menu.addSeparator();
|
||||||
|
a = menu.addAction(ICONRES(QStringLiteral("dbgstop")),
|
||||||
|
tr("AbortScript"),
|
||||||
|
QKeySequence(Qt::ControlModifier | Qt::Key_Q), []() {
|
||||||
|
ScriptMachine::instance().abortScript(
|
||||||
|
ScriptMachine::Background);
|
||||||
|
});
|
||||||
|
a->setShortcutContext(Qt::WidgetShortcut);
|
||||||
|
} else {
|
||||||
|
a = menu.addAction(ICONRES(QStringLiteral("del")), tr("Clear"),
|
||||||
|
QKeySequence(Qt::ControlModifier | Qt::Key_L), this,
|
||||||
|
&ScriptingConsole::clear);
|
||||||
|
a->setShortcutContext(Qt::WidgetShortcut);
|
||||||
|
}
|
||||||
|
|
||||||
menu.exec(event->globalPos());
|
menu.exec(event->globalPos());
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,10 @@ signals:
|
||||||
public:
|
public:
|
||||||
QString getInput();
|
QString getInput();
|
||||||
|
|
||||||
|
bool isTerminal() const;
|
||||||
|
|
||||||
|
void setIsTerminal(bool newIsTerminal);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
|
@ -71,6 +75,7 @@ protected slots:
|
||||||
private:
|
private:
|
||||||
QString _codes;
|
QString _codes;
|
||||||
|
|
||||||
|
bool _isTerminal = true;
|
||||||
bool _isWaitingRead = false;
|
bool _isWaitingRead = false;
|
||||||
std::function<QString(void)> _getInputFn;
|
std::function<QString(void)> _getInputFn;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
** =============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
#include "scriptingconsolebase.h"
|
#include "scriptingconsolebase.h"
|
||||||
#include "class/wingconsolehighligher.h"
|
#include "class/wingconsolehighligher.h"
|
||||||
#include "wingsyntaxhighlighter.h"
|
#include "wingsyntaxhighlighter.h"
|
||||||
|
@ -19,18 +36,47 @@ ScriptingConsoleBase::ScriptingConsoleBase(QWidget *parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptingConsoleBase::stdOut(const QString &str) {
|
void ScriptingConsoleBase::stdOut(const QString &str) {
|
||||||
writeStdOut(str);
|
auto lines = str.split('\n');
|
||||||
dontHighlightLastLine(true);
|
if (lines.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writeStdOut(lines.takeFirst());
|
||||||
|
dontHighlightLastLine(false);
|
||||||
|
for (auto &l : lines) {
|
||||||
|
newLine();
|
||||||
|
writeStdOut(l);
|
||||||
|
dontHighlightLastLine(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptingConsoleBase::stdErr(const QString &str) {
|
void ScriptingConsoleBase::stdErr(const QString &str) {
|
||||||
writeStdErr(str);
|
auto lines = str.split('\n');
|
||||||
|
if (lines.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeStdErr(lines.takeFirst());
|
||||||
dontHighlightLastLine(false);
|
dontHighlightLastLine(false);
|
||||||
|
for (auto &l : lines) {
|
||||||
|
newLine();
|
||||||
|
writeStdErr(l);
|
||||||
|
dontHighlightLastLine(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptingConsoleBase::stdWarn(const QString &str) {
|
void ScriptingConsoleBase::stdWarn(const QString &str) {
|
||||||
write(str, _warnCharFmt);
|
auto lines = str.split('\n');
|
||||||
|
if (lines.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
write(lines.takeFirst(), _warnCharFmt);
|
||||||
dontHighlightLastLine(false);
|
dontHighlightLastLine(false);
|
||||||
|
for (auto &l : lines) {
|
||||||
|
newLine();
|
||||||
|
write(l, _warnCharFmt);
|
||||||
|
dontHighlightLastLine(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptingConsoleBase::newLine() { _s << Qt::endl; }
|
void ScriptingConsoleBase::newLine() { _s << Qt::endl; }
|
||||||
|
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
** =============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef SCRIPTINGCONSOLEBASE_H
|
#ifndef SCRIPTINGCONSOLEBASE_H
|
||||||
#define SCRIPTINGCONSOLEBASE_H
|
#define SCRIPTINGCONSOLEBASE_H
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*==============================================================================
|
||||||
|
** 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 "scrollablelabel.h"
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QWheelEvent>
|
||||||
|
|
||||||
|
ScrollableLabel::ScrollableLabel(QWidget *parent) : QScrollArea(parent) {
|
||||||
|
setupUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollableLabel::setText(const QString &text) {
|
||||||
|
label.setText(text);
|
||||||
|
updateLabelSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ScrollableLabel::sizeHint() const { return label.sizeHint(); }
|
||||||
|
|
||||||
|
QSize ScrollableLabel::minimumSizeHint() const {
|
||||||
|
return label.minimumSizeHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollableLabel::wheelEvent(QWheelEvent *event) {
|
||||||
|
if (shouldScroll()) {
|
||||||
|
QPoint delta = event->angleDelta();
|
||||||
|
if (!delta.isNull()) {
|
||||||
|
horizontalScrollBar()->setValue(horizontalScrollBar()->value() -
|
||||||
|
delta.y());
|
||||||
|
event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event->ignore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollableLabel::resizeEvent(QResizeEvent *event) {
|
||||||
|
QScrollArea::resizeEvent(event);
|
||||||
|
updateLabelSize();
|
||||||
|
adjustDisplayLogic();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollableLabel::setupUI() {
|
||||||
|
setWidgetResizable(false);
|
||||||
|
setFrameShape(QFrame::NoFrame);
|
||||||
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
|
||||||
|
label.setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||||
|
label.setWordWrap(false);
|
||||||
|
label.setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||||
|
|
||||||
|
setWidget(&label);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollableLabel::updateLabelSize() {
|
||||||
|
QFontMetrics fm(label.font());
|
||||||
|
const int textWidth = fm.horizontalAdvance(label.text());
|
||||||
|
const int textHeight = fm.height();
|
||||||
|
|
||||||
|
if (textWidth > width()) {
|
||||||
|
label.setFixedSize(textWidth, textHeight);
|
||||||
|
} else {
|
||||||
|
label.setFixedSize(width(), textHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrollableLabel::shouldScroll() const {
|
||||||
|
return label.width() > viewport()->width();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollableLabel::adjustDisplayLogic() {
|
||||||
|
QFontMetrics fm(label.font());
|
||||||
|
const int contentWidth = fm.horizontalAdvance(label.text());
|
||||||
|
const int containerWidth = width();
|
||||||
|
|
||||||
|
if (contentWidth > containerWidth) {
|
||||||
|
label.setFixedSize(contentWidth, fm.height());
|
||||||
|
label.setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||||
|
horizontalScrollBar()->setValue(0);
|
||||||
|
} else {
|
||||||
|
label.setFixedSize(containerWidth, fm.height());
|
||||||
|
label.setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*==============================================================================
|
||||||
|
** 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 SCROLLABLELABEL_H
|
||||||
|
#define SCROLLABLELABEL_H
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QScrollArea>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
class ScrollableLabel : public QScrollArea {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ScrollableLabel(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void setText(const QString &text);
|
||||||
|
|
||||||
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
|
QSize minimumSizeHint() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void wheelEvent(QWheelEvent *event) override;
|
||||||
|
|
||||||
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLabel label;
|
||||||
|
|
||||||
|
void setupUI();
|
||||||
|
|
||||||
|
void updateLabelSize();
|
||||||
|
|
||||||
|
bool shouldScroll() const;
|
||||||
|
|
||||||
|
void adjustDisplayLogic();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MARQUEELABEL_H
|
|
@ -16,6 +16,8 @@ enum AsUserDataType {
|
||||||
UserData_PluginFn,
|
UserData_PluginFn,
|
||||||
UserData_isDbg,
|
UserData_isDbg,
|
||||||
UserData_ContextMode,
|
UserData_ContextMode,
|
||||||
|
UserData_Timer,
|
||||||
|
UserData_TimeOut
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,39 +83,15 @@ FindDialog::FindDialog(const FindInfo &info, QWidget *parent)
|
||||||
|
|
||||||
if (info.isStringFind) {
|
if (info.isStringFind) {
|
||||||
if (!info.encoding.isEmpty()) {
|
if (!info.encoding.isEmpty()) {
|
||||||
|
m_lineeditor->setIsHexMode(false);
|
||||||
m_findMode->setCurrentText(info.encoding);
|
m_findMode->setCurrentText(info.encoding);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
m_lineeditor->setIsHexMode(true);
|
||||||
m_findMode->setCurrentIndex(0);
|
m_findMode->setCurrentIndex(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_lineeditor->setText(info.str);
|
m_lineeditor->setFindText(info.str);
|
||||||
|
|
||||||
auto regionw = new QWidget(this);
|
|
||||||
auto regionLayout = new QHBoxLayout(regionw);
|
|
||||||
|
|
||||||
regionLayout->addWidget(new QLabel(tr("Region:"), regionw));
|
|
||||||
|
|
||||||
m_regionStart = new QtLongLongSpinBox(regionw);
|
|
||||||
Q_ASSERT(info.stop >= info.start);
|
|
||||||
m_regionStart->setRange(info.start, info.stop);
|
|
||||||
m_regionStart->setEnabled(false);
|
|
||||||
m_regionStart->setValue(info.start);
|
|
||||||
m_regionStart->setDisplayIntegerBase(16);
|
|
||||||
m_regionStart->setPrefix(QStringLiteral("0x"));
|
|
||||||
regionLayout->addWidget(m_regionStart, 1);
|
|
||||||
|
|
||||||
regionLayout->addWidget(new QLabel(QStringLiteral(" - "), regionw));
|
|
||||||
|
|
||||||
m_regionStop = new QtLongLongSpinBox(regionw);
|
|
||||||
m_regionStop->setRange(info.start, info.stop);
|
|
||||||
connect(m_regionStart, &QtLongLongSpinBox::valueChanged, m_regionStop,
|
|
||||||
&QtLongLongSpinBox::setMinimum);
|
|
||||||
m_regionStop->setEnabled(false);
|
|
||||||
m_regionStop->setValue(qMin(info.start + 1024 * 1024, info.stop));
|
|
||||||
m_regionStop->setDisplayIntegerBase(16);
|
|
||||||
m_regionStop->setPrefix(QStringLiteral("0x"));
|
|
||||||
regionLayout->addWidget(m_regionStop, 1);
|
|
||||||
|
|
||||||
auto group = new QButtonGroup(this);
|
auto group = new QButtonGroup(this);
|
||||||
group->setExclusive(true);
|
group->setExclusive(true);
|
||||||
|
@ -134,18 +110,9 @@ FindDialog::FindDialog(const FindInfo &info, QWidget *parent)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
group->addButton(b, id++);
|
group->addButton(b, id++);
|
||||||
b->setEnabled(!info.isBigFile);
|
b->setChecked(true);
|
||||||
buttonLayout->addWidget(b);
|
buttonLayout->addWidget(b);
|
||||||
|
|
||||||
b = new QPushButton(tr("Region"), this);
|
|
||||||
b->setCheckable(true);
|
|
||||||
connect(b, &QPushButton::toggled, this, [=](bool b) {
|
|
||||||
if (b) {
|
|
||||||
_result.dir = SearchDirection::Region;
|
|
||||||
}
|
|
||||||
regionw->setVisible(b);
|
|
||||||
regionw->setEnabled(b);
|
|
||||||
});
|
|
||||||
group->addButton(b, id++);
|
group->addButton(b, id++);
|
||||||
buttonLayout->addWidget(b);
|
buttonLayout->addWidget(b);
|
||||||
|
|
||||||
|
@ -183,7 +150,6 @@ FindDialog::FindDialog(const FindInfo &info, QWidget *parent)
|
||||||
|
|
||||||
group->addButton(b, id++);
|
group->addButton(b, id++);
|
||||||
buttonLayout->addWidget(b);
|
buttonLayout->addWidget(b);
|
||||||
group->button(info.isBigFile ? 1 : 0)->setChecked(true);
|
|
||||||
|
|
||||||
layout->addWidget(btnBox);
|
layout->addWidget(btnBox);
|
||||||
|
|
||||||
|
@ -195,9 +161,6 @@ FindDialog::FindDialog(const FindInfo &info, QWidget *parent)
|
||||||
auto s = new QShortcut(key, this);
|
auto s = new QShortcut(key, this);
|
||||||
connect(s, &QShortcut::activated, this, &FindDialog::on_accept);
|
connect(s, &QShortcut::activated, this, &FindDialog::on_accept);
|
||||||
|
|
||||||
layout->addWidget(regionw);
|
|
||||||
regionw->hide();
|
|
||||||
|
|
||||||
layout->addSpacing(20);
|
layout->addSpacing(20);
|
||||||
layout->addWidget(dbbox);
|
layout->addWidget(dbbox);
|
||||||
|
|
||||||
|
@ -221,14 +184,6 @@ void FindDialog::on_accept() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_regionStart->isEnabled()) {
|
|
||||||
_result.start = m_regionStart->value();
|
|
||||||
_result.stop = m_regionStop->value();
|
|
||||||
} else {
|
|
||||||
_result.start = 0;
|
|
||||||
_result.stop = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_result.encoding = m_findMode->currentText();
|
_result.encoding = m_findMode->currentText();
|
||||||
done(1);
|
done(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#define FINDDIALOG_H
|
#define FINDDIALOG_H
|
||||||
|
|
||||||
#include "control/qhextextedit.h"
|
#include "control/qhextextedit.h"
|
||||||
#include "control/qtlonglongspinbox.h"
|
|
||||||
#include "framelessdialogbase.h"
|
#include "framelessdialogbase.h"
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
@ -28,15 +27,13 @@
|
||||||
#include <QRadioButton>
|
#include <QRadioButton>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
|
|
||||||
enum class SearchDirection { None, Region, Foreword, Backword, Selection };
|
enum class SearchDirection { None, Foreword, Backword, Selection };
|
||||||
|
|
||||||
class FindDialog : public FramelessDialogBase {
|
class FindDialog : public FramelessDialogBase {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
struct Result {
|
struct Result {
|
||||||
SearchDirection dir = SearchDirection::None;
|
SearchDirection dir = SearchDirection::None;
|
||||||
qsizetype start = 0;
|
|
||||||
qsizetype stop = 0;
|
|
||||||
|
|
||||||
// for searching info
|
// for searching info
|
||||||
bool isStringFind;
|
bool isStringFind;
|
||||||
|
@ -45,10 +42,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FindInfo {
|
struct FindInfo {
|
||||||
bool isBigFile;
|
|
||||||
bool isStringFind;
|
bool isStringFind;
|
||||||
qlonglong start;
|
|
||||||
qlonglong stop;
|
|
||||||
bool isSel;
|
bool isSel;
|
||||||
|
|
||||||
// for searching info
|
// for searching info
|
||||||
|
@ -70,9 +64,6 @@ private:
|
||||||
QComboBox *m_findMode;
|
QComboBox *m_findMode;
|
||||||
QTextEdit *m_preview;
|
QTextEdit *m_preview;
|
||||||
|
|
||||||
QtLongLongSpinBox *m_regionStart;
|
|
||||||
QtLongLongSpinBox *m_regionStop;
|
|
||||||
|
|
||||||
Result _result;
|
Result _result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ MainWindow::MainWindow(SplashDialog *splash) : FramelessMainWindow() {
|
||||||
m_status->addWidget(l);
|
m_status->addWidget(l);
|
||||||
m_status->addWidget(m_lblsellen);
|
m_status->addWidget(m_lblsellen);
|
||||||
|
|
||||||
_status = new QLabel(m_status);
|
_status = new ScrollableLabel(m_status);
|
||||||
m_status->addPermanentWidget(_status);
|
m_status->addPermanentWidget(_status);
|
||||||
|
|
||||||
auto separator = new QFrame(m_status);
|
auto separator = new QFrame(m_status);
|
||||||
|
@ -607,6 +607,24 @@ ads::CDockAreaWidget *MainWindow::buildUpLogDock(ads::CDockManager *dock,
|
||||||
m_logbrowser->setOpenExternalLinks(true);
|
m_logbrowser->setOpenExternalLinks(true);
|
||||||
m_logbrowser->setUndoRedoEnabled(false);
|
m_logbrowser->setUndoRedoEnabled(false);
|
||||||
|
|
||||||
|
auto a = newAction(
|
||||||
|
ICONRES("copy"), tr("Copy"), [=]() { m_logbrowser->copy(); },
|
||||||
|
QKeySequence::Copy);
|
||||||
|
a->setShortcutContext(Qt::WidgetShortcut);
|
||||||
|
m_logbrowser->addAction(a);
|
||||||
|
|
||||||
|
a = new QAction(this);
|
||||||
|
a->setSeparator(true);
|
||||||
|
m_logbrowser->addAction(a);
|
||||||
|
|
||||||
|
m_logbrowser->addAction(newAction(ICONRES(QStringLiteral("log")),
|
||||||
|
tr("ExportLog"),
|
||||||
|
&MainWindow::on_exportlog));
|
||||||
|
m_logbrowser->addAction(newAction(ICONRES(QStringLiteral("clearhis")),
|
||||||
|
tr("ClearLog"), &MainWindow::on_clslog));
|
||||||
|
|
||||||
|
m_logbrowser->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||||
|
|
||||||
auto dw =
|
auto dw =
|
||||||
buildDockWidget(dock, QStringLiteral("Log"), tr("Log"), m_logbrowser);
|
buildDockWidget(dock, QStringLiteral("Log"), tr("Log"), m_logbrowser);
|
||||||
return dock->addDockWidget(area, dw, areaw);
|
return dock->addDockWidget(area, dw, areaw);
|
||||||
|
@ -698,10 +716,17 @@ MainWindow::buildUpFindResultDock(ads::CDockManager *dock,
|
||||||
|
|
||||||
cursor->moveTo(fm->resultAt(index.row()).offset);
|
cursor->moveTo(fm->resultAt(index.row()).offset);
|
||||||
if (cursor->selectionCount() <= 1 && index.column() >= 3) {
|
if (cursor->selectionCount() <= 1 && index.column() >= 3) {
|
||||||
cursor->select(fm->lastFindData().length());
|
cursor->select(fm->lastFindData().second);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto header = m_findresult->horizontalHeader();
|
||||||
|
auto font = QFontMetrics(m_findresult->font());
|
||||||
|
auto len = font.horizontalAdvance('F') * (15 + 16 * 2);
|
||||||
|
if (header->sectionSize(3) < len) {
|
||||||
|
header->resizeSection(3, len);
|
||||||
|
}
|
||||||
|
|
||||||
auto dw = buildDockWidget(dock, QStringLiteral("FindResult"),
|
auto dw = buildDockWidget(dock, QStringLiteral("FindResult"),
|
||||||
tr("FindResult") + QStringLiteral(" (ASCII)"),
|
tr("FindResult") + QStringLiteral(" (ASCII)"),
|
||||||
m_findresult);
|
m_findresult);
|
||||||
|
@ -803,9 +828,7 @@ MainWindow::buildUpHashResultDock(ads::CDockManager *dock,
|
||||||
m_hashtable->setContextMenuPolicy(
|
m_hashtable->setContextMenuPolicy(
|
||||||
Qt::ContextMenuPolicy::ActionsContextMenu);
|
Qt::ContextMenuPolicy::ActionsContextMenu);
|
||||||
|
|
||||||
auto a = new QAction(m_hashtable);
|
auto a = newAction(ICONRES(QStringLiteral("copy")), tr("Copy"), [=] {
|
||||||
a->setText(tr("Copy"));
|
|
||||||
connect(a, &QAction::triggered, this, [=] {
|
|
||||||
auto r = m_hashtable->currentIndex();
|
auto r = m_hashtable->currentIndex();
|
||||||
qApp->clipboard()->setText(
|
qApp->clipboard()->setText(
|
||||||
_hashModel->checkSumData(QCryptographicHash::Algorithm(r.row())));
|
_hashModel->checkSumData(QCryptographicHash::Algorithm(r.row())));
|
||||||
|
@ -813,6 +836,9 @@ MainWindow::buildUpHashResultDock(ads::CDockManager *dock,
|
||||||
tr("CopyToClipBoard"));
|
tr("CopyToClipBoard"));
|
||||||
});
|
});
|
||||||
m_hashtable->addAction(a);
|
m_hashtable->addAction(a);
|
||||||
|
a = newAction(QStringLiteral("del"), tr("Clear"),
|
||||||
|
[=]() { _hashModel->clearData(); });
|
||||||
|
m_hashtable->addAction(a);
|
||||||
connect(m_hashtable->selectionModel(),
|
connect(m_hashtable->selectionModel(),
|
||||||
&QItemSelectionModel::currentRowChanged, a,
|
&QItemSelectionModel::currentRowChanged, a,
|
||||||
[=](const QModelIndex ¤t, const QModelIndex &) {
|
[=](const QModelIndex ¤t, const QModelIndex &) {
|
||||||
|
@ -985,18 +1011,17 @@ MainWindow::buildUpDecodingStrShowDock(ads::CDockManager *dock,
|
||||||
tr("DecodeText") + QStringLiteral(" (ASCII)"),
|
tr("DecodeText") + QStringLiteral(" (ASCII)"),
|
||||||
m_txtDecode);
|
m_txtDecode);
|
||||||
|
|
||||||
auto menu = m_txtDecode->createStandardContextMenu();
|
auto a = newAction(
|
||||||
menu->addSeparator();
|
ICONRES("copy"), tr("Copy"), [=]() { m_logbrowser->copy(); },
|
||||||
auto a = new QAction(tr("Encoding"), this);
|
QKeySequence::Copy);
|
||||||
|
a->setShortcutContext(Qt::WidgetShortcut);
|
||||||
|
m_txtDecode->addAction(a);
|
||||||
|
a = new QAction(tr("Encoding"), this);
|
||||||
a->setIcon(ICONRES(QStringLiteral("encoding")));
|
a->setIcon(ICONRES(QStringLiteral("encoding")));
|
||||||
connect(a, &QAction::triggered, this, &MainWindow::on_encoding);
|
connect(a, &QAction::triggered, this, &MainWindow::on_encoding);
|
||||||
menu->addAction(a);
|
m_txtDecode->addAction(a);
|
||||||
|
|
||||||
m_txtDecode->setContextMenuPolicy(Qt::CustomContextMenu);
|
m_txtDecode->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||||
connect(m_txtDecode, &QTextBrowser::customContextMenuRequested, this,
|
|
||||||
[=](const QPoint &pos) {
|
|
||||||
menu->popup(m_txtDecode->viewport()->mapToGlobal(pos));
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_txtDecode, &QTextBrowser::windowTitleChanged, dw,
|
connect(m_txtDecode, &QTextBrowser::windowTitleChanged, dw,
|
||||||
&QDockWidget::setWindowTitle);
|
&QDockWidget::setWindowTitle);
|
||||||
|
@ -1042,13 +1067,16 @@ MainWindow::buildUpScriptBgOutputDock(ads::CDockManager *dock,
|
||||||
auto a = newAction(
|
auto a = newAction(
|
||||||
ICONRES(QStringLiteral("mStr")), tr("SelectAll"),
|
ICONRES(QStringLiteral("mStr")), tr("SelectAll"),
|
||||||
[this]() { m_bgScriptOutput->selectAll(); }, QKeySequence::SelectAll);
|
[this]() { m_bgScriptOutput->selectAll(); }, QKeySequence::SelectAll);
|
||||||
|
a->setShortcutContext(Qt::WidgetShortcut);
|
||||||
m_bgScriptOutput->addAction(a);
|
m_bgScriptOutput->addAction(a);
|
||||||
a = newAction(
|
a = newAction(
|
||||||
ICONRES(QStringLiteral("copy")), tr("Copy"),
|
ICONRES(QStringLiteral("copy")), tr("Copy"),
|
||||||
[this]() { m_bgScriptOutput->copy(); }, QKeySequence::Copy);
|
[this]() { m_bgScriptOutput->copy(); }, QKeySequence::Copy);
|
||||||
|
a->setShortcutContext(Qt::WidgetShortcut);
|
||||||
m_bgScriptOutput->addAction(a);
|
m_bgScriptOutput->addAction(a);
|
||||||
a = newAction(ICONRES(QStringLiteral("del")), tr("Clear"),
|
a = newAction(ICONRES(QStringLiteral("del")), tr("Clear"),
|
||||||
[this]() { m_bgScriptOutput->clear(); });
|
[this]() { m_bgScriptOutput->clear(); });
|
||||||
|
a->setShortcutContext(Qt::WidgetShortcut);
|
||||||
m_bgScriptOutput->addAction(a);
|
m_bgScriptOutput->addAction(a);
|
||||||
a = new QAction(this);
|
a = new QAction(this);
|
||||||
a->setSeparator(true);
|
a->setSeparator(true);
|
||||||
|
@ -1059,6 +1087,7 @@ MainWindow::buildUpScriptBgOutputDock(ads::CDockManager *dock,
|
||||||
ScriptMachine::instance().abortScript(ScriptMachine::Background);
|
ScriptMachine::instance().abortScript(ScriptMachine::Background);
|
||||||
},
|
},
|
||||||
QKeySequence(Qt::ControlModifier | Qt::Key_Q));
|
QKeySequence(Qt::ControlModifier | Qt::Key_Q));
|
||||||
|
a->setShortcutContext(Qt::WidgetShortcut);
|
||||||
m_bgScriptOutput->addAction(a);
|
m_bgScriptOutput->addAction(a);
|
||||||
m_bgScriptOutput->setContextMenuPolicy(Qt::ActionsContextMenu);
|
m_bgScriptOutput->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||||
|
|
||||||
|
@ -1501,7 +1530,8 @@ RibbonTabContent *MainWindow::buildScriptPage(RibbonTabContent *tab) {
|
||||||
pannel->setVisible(false);
|
pannel->setVisible(false);
|
||||||
connect(pannel, &RibbonButtonGroup::emptyStatusChanged, this,
|
connect(pannel, &RibbonButtonGroup::emptyStatusChanged, this,
|
||||||
[pannel](bool isEmpty) { pannel->setVisible(!isEmpty); });
|
[pannel](bool isEmpty) { pannel->setVisible(!isEmpty); });
|
||||||
_scriptMaps = ScriptManager::buildUpRibbonScriptRunner(pannel);
|
_scriptContexts =
|
||||||
|
ScriptManager::buildUpScriptRunnerContext(pannel, this);
|
||||||
m_scriptDBGroup = pannel;
|
m_scriptDBGroup = pannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2052,7 +2082,10 @@ void MainWindow::on_pastefile() {
|
||||||
if (hexeditor == nullptr) {
|
if (hexeditor == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hexeditor->Paste();
|
if (!hexeditor->Paste()) {
|
||||||
|
Toast::toast(this, NAMEICONRES(QStringLiteral("paste")),
|
||||||
|
tr("PasteFailedNote"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_delete() {
|
void MainWindow::on_delete() {
|
||||||
|
@ -2094,9 +2127,6 @@ void MainWindow::on_findfile() {
|
||||||
auto hexeditor = editor->hexEditor();
|
auto hexeditor = editor->hexEditor();
|
||||||
|
|
||||||
static FindDialog::FindInfo info;
|
static FindDialog::FindInfo info;
|
||||||
info.isBigFile = editor->isBigFile();
|
|
||||||
info.start = 0;
|
|
||||||
info.stop = hexeditor->documentBytes();
|
|
||||||
info.isSel = hexeditor->selectionCount() == 1;
|
info.isSel = hexeditor->selectionCount() == 1;
|
||||||
|
|
||||||
FindDialog fd(info, this);
|
FindDialog fd(info, this);
|
||||||
|
@ -2110,7 +2140,6 @@ void MainWindow::on_findfile() {
|
||||||
ExecAsync<EditorView::FindError>(
|
ExecAsync<EditorView::FindError>(
|
||||||
[this, r]() -> EditorView::FindError {
|
[this, r]() -> EditorView::FindError {
|
||||||
m_isfinding = true;
|
m_isfinding = true;
|
||||||
|
|
||||||
return currentEditor()->find(r);
|
return currentEditor()->find(r);
|
||||||
},
|
},
|
||||||
[this](EditorView::FindError err) {
|
[this](EditorView::FindError err) {
|
||||||
|
@ -2134,6 +2163,14 @@ void MainWindow::on_findfile() {
|
||||||
if (result) {
|
if (result) {
|
||||||
m_findEncoding.value(result->encoding())->setChecked(true);
|
m_findEncoding.value(result->encoding())->setChecked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto header = m_findresult->horizontalHeader();
|
||||||
|
auto font = QFontMetrics(m_findresult->font());
|
||||||
|
auto len = font.horizontalAdvance('F') * (15 + 16 * 2);
|
||||||
|
if (header->sectionSize(3) < len) {
|
||||||
|
header->resizeSection(3, len);
|
||||||
|
}
|
||||||
|
|
||||||
m_find->raise();
|
m_find->raise();
|
||||||
|
|
||||||
m_isfinding = false;
|
m_isfinding = false;
|
||||||
|
@ -2578,7 +2615,7 @@ void MainWindow::on_exportfindresult() {
|
||||||
|
|
||||||
auto d = findresitem->lastFindData();
|
auto d = findresitem->lastFindData();
|
||||||
|
|
||||||
fobj.insert(QStringLiteral("find"), d);
|
fobj.insert(QStringLiteral("find"), d.first);
|
||||||
QJsonArray arr;
|
QJsonArray arr;
|
||||||
for (int i = 0; i < c; i++) {
|
for (int i = 0; i < c; i++) {
|
||||||
auto data = findresitem->resultAt(i);
|
auto data = findresitem->resultAt(i);
|
||||||
|
@ -3019,6 +3056,11 @@ void MainWindow::registerEditorView(EditorView *editor, const QString &ws) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto &m : _scriptContexts) {
|
||||||
|
editor->registerQMenu(m);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &m : m_hexContextMenu) {
|
for (auto &m : m_hexContextMenu) {
|
||||||
editor->registerQMenu(m);
|
editor->registerQMenu(m);
|
||||||
}
|
}
|
||||||
|
@ -3207,6 +3249,8 @@ void MainWindow::connectEditorView(EditorView *editor) {
|
||||||
if (editor->isBigFile()) {
|
if (editor->isBigFile()) {
|
||||||
auto fileName = editor->fileName();
|
auto fileName = editor->fileName();
|
||||||
if (!QFile::exists(fileName)) {
|
if (!QFile::exists(fileName)) {
|
||||||
|
activateWindow();
|
||||||
|
raise();
|
||||||
editor->raise();
|
editor->raise();
|
||||||
WingMessageBox::critical(this, tr("Error"),
|
WingMessageBox::critical(this, tr("Error"),
|
||||||
tr("FileCloseBigFile"));
|
tr("FileCloseBigFile"));
|
||||||
|
|
|
@ -360,7 +360,7 @@ private:
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
inline QAction *newAction(const QString &title, Func &&slot,
|
inline QAction *newAction(const QString &title, Func &&slot,
|
||||||
const QKeySequence &shortcut = QKeySequence()) {
|
const QKeySequence &shortcut = QKeySequence()) {
|
||||||
auto a = new QAction;
|
auto a = new QAction(this);
|
||||||
a->setText(title);
|
a->setText(title);
|
||||||
a->setShortcutVisibleInContextMenu(true);
|
a->setShortcutVisibleInContextMenu(true);
|
||||||
a->setShortcut(shortcut);
|
a->setShortcut(shortcut);
|
||||||
|
@ -466,7 +466,7 @@ signals:
|
||||||
private:
|
private:
|
||||||
Ribbon *m_ribbon = nullptr;
|
Ribbon *m_ribbon = nullptr;
|
||||||
ads::CDockManager *m_dock = nullptr;
|
ads::CDockManager *m_dock = nullptr;
|
||||||
QLabel *_status = nullptr;
|
ScrollableLabel *_status = nullptr;
|
||||||
|
|
||||||
QString m_encoding;
|
QString m_encoding;
|
||||||
|
|
||||||
|
@ -524,7 +524,7 @@ private:
|
||||||
|
|
||||||
RibbonButtonGroup *m_scriptDBGroup = nullptr;
|
RibbonButtonGroup *m_scriptDBGroup = nullptr;
|
||||||
RibbonButtonGroup *m_pluginSettingsGroup = nullptr;
|
RibbonButtonGroup *m_pluginSettingsGroup = nullptr;
|
||||||
ScriptManager::ScriptActionMaps _scriptMaps;
|
QList<QMenu *> _scriptContexts;
|
||||||
|
|
||||||
//===================================================
|
//===================================================
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QPicture>
|
#include <QPicture>
|
||||||
#include <QStatusBar>
|
#include <QStatusBar>
|
||||||
|
#include <QToolTip>
|
||||||
|
|
||||||
constexpr auto EMPTY_FUNC = [] {};
|
constexpr auto EMPTY_FUNC = [] {};
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ ScriptingDialog::ScriptingDialog(QWidget *parent)
|
||||||
layout->addWidget(m_dock, 1);
|
layout->addWidget(m_dock, 1);
|
||||||
|
|
||||||
m_status = new QStatusBar(this);
|
m_status = new QStatusBar(this);
|
||||||
_status = new QLabel(this);
|
_status = new ScrollableLabel(this);
|
||||||
m_status->addPermanentWidget(_status);
|
m_status->addPermanentWidget(_status);
|
||||||
layout->addWidget(m_status);
|
layout->addWidget(m_status);
|
||||||
buildUpContent(cw);
|
buildUpContent(cw);
|
||||||
|
@ -583,6 +584,7 @@ ScriptingDialog::buildUpOutputShowDock(ads::CDockManager *dock,
|
||||||
ads::CDockAreaWidget *areaw) {
|
ads::CDockAreaWidget *areaw) {
|
||||||
m_consoleout = new ScriptingConsole(this);
|
m_consoleout = new ScriptingConsole(this);
|
||||||
m_consoleout->setMode(ScriptingConsole::Output);
|
m_consoleout->setMode(ScriptingConsole::Output);
|
||||||
|
m_consoleout->setIsTerminal(false);
|
||||||
auto dw = buildDockWidget(dock, QStringLiteral("ConsoleOutput"),
|
auto dw = buildDockWidget(dock, QStringLiteral("ConsoleOutput"),
|
||||||
tr("ConsoleOutput"), m_consoleout);
|
tr("ConsoleOutput"), m_consoleout);
|
||||||
return dock->addDockWidget(area, dw, areaw);
|
return dock->addDockWidget(area, dw, areaw);
|
||||||
|
@ -799,6 +801,21 @@ void ScriptingDialog::registerEditorView(ScriptEditor *editor) {
|
||||||
message + QStringLiteral("</font></b>"));
|
message + QStringLiteral("</font></b>"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(editor, &ScriptEditor::need2Reload, this, [editor, this]() {
|
||||||
|
editor->editor()->setContentModified(true);
|
||||||
|
if (currentEditor() == editor) {
|
||||||
|
activateWindow();
|
||||||
|
raise();
|
||||||
|
auto ret = WingMessageBox::question(this, tr("Reload"),
|
||||||
|
tr("ReloadNeededYesOrNo"));
|
||||||
|
if (ret == QMessageBox::Yes) {
|
||||||
|
editor->reload();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
editor->setProperty("__RELOAD__", true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
m_views.append(editor);
|
m_views.append(editor);
|
||||||
|
|
||||||
auto ev = m_Tbtneditors.value(ToolButtonIndex::EDITOR_VIEWS);
|
auto ev = m_Tbtneditors.value(ToolButtonIndex::EDITOR_VIEWS);
|
||||||
|
@ -873,6 +890,19 @@ void ScriptingDialog::swapEditor(ScriptEditor *old, ScriptEditor *cur) {
|
||||||
|
|
||||||
m_curEditor = cur;
|
m_curEditor = cur;
|
||||||
updateCursorPosition();
|
updateCursorPosition();
|
||||||
|
|
||||||
|
if (cur) {
|
||||||
|
auto needReload = cur->property("__RELOAD__").toBool();
|
||||||
|
if (needReload) {
|
||||||
|
auto ret = WingMessageBox::question(this, tr("Reload"),
|
||||||
|
tr("ReloadNeededYesOrNo"));
|
||||||
|
if (ret == QMessageBox::Yes) {
|
||||||
|
cur->reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
cur->setProperty("__RELOAD__", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptingDialog::updateRunDebugMode(bool disable) {
|
void ScriptingDialog::updateRunDebugMode(bool disable) {
|
||||||
|
@ -1369,7 +1399,8 @@ void ScriptingDialog::on_runscript() {
|
||||||
PluginSystem::instance().scriptPragmaBegin();
|
PluginSystem::instance().scriptPragmaBegin();
|
||||||
|
|
||||||
editor->setReadOnly(true);
|
editor->setReadOnly(true);
|
||||||
// ScriptMachine::instance().executeScript(editor->fileName());
|
ScriptMachine::instance().executeScript(ScriptMachine::Scripting,
|
||||||
|
editor->fileName());
|
||||||
editor->setReadOnly(false);
|
editor->setReadOnly(false);
|
||||||
updateRunDebugMode();
|
updateRunDebugMode();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include "control/asobjtreewidget.h"
|
#include "control/asobjtreewidget.h"
|
||||||
#include "control/scriptingconsole.h"
|
#include "control/scriptingconsole.h"
|
||||||
|
#include "control/scrollablelabel.h"
|
||||||
#include "dialog/settingdialog.h"
|
#include "dialog/settingdialog.h"
|
||||||
#include "framelessmainwindow.h"
|
#include "framelessmainwindow.h"
|
||||||
|
|
||||||
|
@ -170,7 +171,7 @@ private:
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
inline QAction *newAction(const QString &title, Func &&slot,
|
inline QAction *newAction(const QString &title, Func &&slot,
|
||||||
const QKeySequence &shortcut = QKeySequence()) {
|
const QKeySequence &shortcut = QKeySequence()) {
|
||||||
auto a = new QAction;
|
auto a = new QAction(this);
|
||||||
a->setText(title);
|
a->setText(title);
|
||||||
a->setShortcutVisibleInContextMenu(true);
|
a->setShortcutVisibleInContextMenu(true);
|
||||||
a->setShortcut(shortcut);
|
a->setShortcut(shortcut);
|
||||||
|
@ -301,7 +302,7 @@ private:
|
||||||
|
|
||||||
ScriptEditor *_DebugingEditor;
|
ScriptEditor *_DebugingEditor;
|
||||||
|
|
||||||
QLabel *_status = nullptr;
|
ScrollableLabel *_status = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SCRIPTINGDIALOG_H
|
#endif // SCRIPTINGDIALOG_H
|
||||||
|
|
|
@ -113,21 +113,26 @@ void SettingDialog::on_buttonBox_clicked(QAbstractButton *button) {
|
||||||
page->apply();
|
page->apply();
|
||||||
}
|
}
|
||||||
} else if (button == btnbox->button(QDialogButtonBox::RestoreDefaults)) {
|
} else if (button == btnbox->button(QDialogButtonBox::RestoreDefaults)) {
|
||||||
for (auto &page : m_pages) {
|
auto index = ui->listWidget->currentRow();
|
||||||
page->reset();
|
if (index >= 0) {
|
||||||
|
m_pages.at(index)->reset();
|
||||||
}
|
}
|
||||||
|
toastTakeEffectReboot();
|
||||||
} else if (button == btnbox->button(QDialogButtonBox::Reset)) {
|
} else if (button == btnbox->button(QDialogButtonBox::Reset)) {
|
||||||
auto res = WingMessageBox::warning(
|
auto res = WingMessageBox::warning(
|
||||||
this, qAppName(),
|
this, qAppName(),
|
||||||
tr("This will reset all settings. Are you sure to continue?"),
|
tr("This will reset all settings. Are you sure to continue?"),
|
||||||
QMessageBox::Yes | QMessageBox::No);
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
if (res == QMessageBox::No)
|
|
||||||
|
if (res == QMessageBox::No) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &page : m_pages) {
|
for (auto &page : m_pages) {
|
||||||
page->reset();
|
page->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toastTakeEffectReboot();
|
||||||
} else if (button == btnbox->button(QDialogButtonBox::Cancel)) {
|
} else if (button == btnbox->button(QDialogButtonBox::Cancel)) {
|
||||||
for (auto &page : m_pages) {
|
for (auto &page : m_pages) {
|
||||||
page->cancel();
|
page->cancel();
|
||||||
|
|
|
@ -135,7 +135,9 @@ QList<FindResultModel::FindInfo> &FindResultModel::findData() {
|
||||||
return m_findData;
|
return m_findData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString &FindResultModel::lastFindData() { return m_lastFindData; }
|
QPair<QString, qsizetype> &FindResultModel::lastFindData() {
|
||||||
|
return m_lastFindData;
|
||||||
|
}
|
||||||
|
|
||||||
void FindResultModel::beginUpdate() { this->beginResetModel(); }
|
void FindResultModel::beginUpdate() { this->beginResetModel(); }
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ public:
|
||||||
|
|
||||||
QList<FindResult> &results();
|
QList<FindResult> &results();
|
||||||
QList<FindInfo> &findData();
|
QList<FindInfo> &findData();
|
||||||
QString &lastFindData();
|
QPair<QString, qsizetype> &lastFindData();
|
||||||
|
|
||||||
void beginUpdate();
|
void beginUpdate();
|
||||||
void endUpdate();
|
void endUpdate();
|
||||||
|
@ -67,7 +67,7 @@ public:
|
||||||
private:
|
private:
|
||||||
QList<FindResult> m_results;
|
QList<FindResult> m_results;
|
||||||
QList<FindInfo> m_findData;
|
QList<FindInfo> m_findData;
|
||||||
QString m_lastFindData;
|
QPair<QString, qsizetype> m_lastFindData;
|
||||||
|
|
||||||
QString m_encoding;
|
QString m_encoding;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,854 @@
|
||||||
|
// url: https://github.com/romanpunia/aspromise
|
||||||
|
// Licensed under the MIT license. Free for any type of use.
|
||||||
|
|
||||||
|
#ifndef AS_PROMISE_HPP
|
||||||
|
#define AS_PROMISE_HPP
|
||||||
|
|
||||||
|
#ifndef PROMISE_CONFIG
|
||||||
|
#define PROMISE_CONFIG
|
||||||
|
#define PROMISE_TYPENAME "promise" // promise type
|
||||||
|
#define PROMISE_VOIDPOSTFIX "_v" // promise<void> type (promise_v)
|
||||||
|
#define PROMISE_WRAP "wrap" // promise setter function
|
||||||
|
#define PROMISE_UNWRAP "unwrap" // promise getter function
|
||||||
|
#define PROMISE_YIELD "yield" // promise awaiter function
|
||||||
|
#define PROMISE_WHEN "when" // promise callback function
|
||||||
|
#define PROMISE_EVENT "when_callback" // promise funcdef name
|
||||||
|
#define PROMISE_PENDING "pending" // promise status checker
|
||||||
|
#define PROMISE_AWAIT \
|
||||||
|
"co_await" // keyword for await (C++20 coroutines one love)
|
||||||
|
#define PROMISE_USERID 559 // promise user data identifier (any value)
|
||||||
|
#define PROMISE_NULLID -1 // empty promise type id
|
||||||
|
#define PROMISE_CALLBACKS true // allow <when> listener
|
||||||
|
#endif
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define PROMISE_ASSERT(Expression, Message) assert((Expression) && Message)
|
||||||
|
#define PROMISE_CHECK(Expression) (assert((Expression) >= 0))
|
||||||
|
#else
|
||||||
|
#define PROMISE_ASSERT(Expression, Message)
|
||||||
|
#define PROMISE_CHECK(Expression) (Expression)
|
||||||
|
#endif
|
||||||
|
#ifndef ANGELSCRIPT_H
|
||||||
|
#include <angelscript.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cctype>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <cstring>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#ifndef AS_PROMISE_NO_HELPERS
|
||||||
|
/* Helper function to cleanup the script function */
|
||||||
|
static void AsClearCallback(asIScriptFunction *Callback) {
|
||||||
|
void *DelegateObject = Callback->GetDelegateObject();
|
||||||
|
if (DelegateObject != nullptr)
|
||||||
|
Callback->GetEngine()->ReleaseScriptObject(
|
||||||
|
DelegateObject, Callback->GetDelegateObjectType());
|
||||||
|
Callback->Release();
|
||||||
|
}
|
||||||
|
/* Helper function to check if context is awaiting on promise */
|
||||||
|
static bool IsAsyncContextPending(asIScriptContext *Context) {
|
||||||
|
return Context->GetUserData(PROMISE_USERID) != nullptr ||
|
||||||
|
Context->GetState() == asEXECUTION_SUSPENDED;
|
||||||
|
}
|
||||||
|
/* Helper function to check if context is awaiting on promise or active */
|
||||||
|
static bool IsAsyncContextBusy(asIScriptContext *Context) {
|
||||||
|
return IsAsyncContextPending(Context) ||
|
||||||
|
Context->GetState() == asEXECUTION_ACTIVE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Basic promise class that can be used for non-blocking asynchronous
|
||||||
|
operation data exchange between AngelScript and C++ and vice-versa.
|
||||||
|
*/
|
||||||
|
template <typename Executor>
|
||||||
|
class AsBasicPromise {
|
||||||
|
private:
|
||||||
|
/* Basically used from <any> class */
|
||||||
|
struct Dynamic {
|
||||||
|
union {
|
||||||
|
asINT64 Integer;
|
||||||
|
double Number;
|
||||||
|
void *Object;
|
||||||
|
};
|
||||||
|
|
||||||
|
int TypeId = PROMISE_NULLID;
|
||||||
|
};
|
||||||
|
#if PROMISE_CALLBACKS
|
||||||
|
/* Callbacks storage */
|
||||||
|
struct {
|
||||||
|
std::function<void(AsBasicPromise<Executor> *)> Native;
|
||||||
|
asIScriptFunction *Wrapper = nullptr;
|
||||||
|
} Callbacks;
|
||||||
|
#else
|
||||||
|
std::condition_variable Ready;
|
||||||
|
#endif
|
||||||
|
private:
|
||||||
|
asIScriptEngine *Engine;
|
||||||
|
asIScriptContext *Context;
|
||||||
|
std::atomic<uint32_t> RefCount;
|
||||||
|
std::atomic<uint32_t> RefMark;
|
||||||
|
std::mutex Update;
|
||||||
|
Dynamic Value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* Thread safe release */
|
||||||
|
void Release() {
|
||||||
|
PROMISE_ASSERT(RefCount > 0, "promise is already released");
|
||||||
|
RefMark = 0;
|
||||||
|
if (!--RefCount) {
|
||||||
|
ReleaseReferences(nullptr);
|
||||||
|
this->~AsBasicPromise();
|
||||||
|
asFreeMem((void *)this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Thread safe add reference */
|
||||||
|
void AddRef() {
|
||||||
|
PROMISE_ASSERT(RefCount < std::numeric_limits<uint32_t>::max(),
|
||||||
|
"too many references to this promise");
|
||||||
|
RefMark = 0;
|
||||||
|
++RefCount;
|
||||||
|
}
|
||||||
|
/* For garbage collector to detect references */
|
||||||
|
void EnumReferences(asIScriptEngine *OtherEngine) {
|
||||||
|
if (Value.Object != nullptr && (Value.TypeId & asTYPEID_MASK_OBJECT)) {
|
||||||
|
asITypeInfo *SubType = Engine->GetTypeInfoById(Value.TypeId);
|
||||||
|
if ((SubType->GetFlags() & asOBJ_REF))
|
||||||
|
OtherEngine->GCEnumCallback(Value.Object);
|
||||||
|
else if ((SubType->GetFlags() & asOBJ_VALUE) &&
|
||||||
|
(SubType->GetFlags() & asOBJ_GC))
|
||||||
|
Engine->ForwardGCEnumReferences(Value.Object, SubType);
|
||||||
|
|
||||||
|
asITypeInfo *Type = OtherEngine->GetTypeInfoById(Value.TypeId);
|
||||||
|
if (Type != nullptr)
|
||||||
|
OtherEngine->GCEnumCallback(Type);
|
||||||
|
}
|
||||||
|
#if PROMISE_CALLBACKS
|
||||||
|
if (Callbacks.Wrapper != nullptr) {
|
||||||
|
void *DelegateObject = Callbacks.Wrapper->GetDelegateObject();
|
||||||
|
if (DelegateObject != nullptr)
|
||||||
|
OtherEngine->GCEnumCallback(DelegateObject);
|
||||||
|
OtherEngine->GCEnumCallback(Callbacks.Wrapper);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/* For garbage collector to release references */
|
||||||
|
void ReleaseReferences(asIScriptEngine *) {
|
||||||
|
if (Value.TypeId & asTYPEID_MASK_OBJECT) {
|
||||||
|
asITypeInfo *Type = Engine->GetTypeInfoById(Value.TypeId);
|
||||||
|
Engine->ReleaseScriptObject(Value.Object, Type);
|
||||||
|
if (Type != nullptr)
|
||||||
|
Type->Release();
|
||||||
|
Clean();
|
||||||
|
}
|
||||||
|
#if PROMISE_CALLBACKS
|
||||||
|
if (Callbacks.Wrapper != nullptr) {
|
||||||
|
AsClearCallback(Callbacks.Wrapper);
|
||||||
|
Callbacks.Wrapper = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/* For garbage collector to mark */
|
||||||
|
void MarkRef() { RefMark = 0; }
|
||||||
|
/* For garbage collector to check mark */
|
||||||
|
bool IsRefMarked() { return RefMark == 1; }
|
||||||
|
/* For garbage collector to check reference count */
|
||||||
|
uint32_t GetRefCount() { return RefCount; }
|
||||||
|
/* Receive stored type id of future value */
|
||||||
|
int GetTypeIdOfObject() { return Value.TypeId; }
|
||||||
|
/* Provide a native callback that should be fired when promise will be
|
||||||
|
* settled */
|
||||||
|
void When(std::function<void(AsBasicPromise<Executor> *)> &&NewCallback) {
|
||||||
|
#if PROMISE_CALLBACKS
|
||||||
|
std::unique_lock<std::mutex> Unique(Update);
|
||||||
|
Callbacks.Native = std::move(NewCallback);
|
||||||
|
if (Callbacks.Native && !IsPending()) {
|
||||||
|
auto Callback = std::move(Callbacks.Native);
|
||||||
|
Unique.unlock();
|
||||||
|
Callback(this);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
PROMISE_ASSERT(false,
|
||||||
|
"native callback binder for <when> is not allowed");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/* Provide a script callback that should be fired when promise will be
|
||||||
|
* settled */
|
||||||
|
void When(asIScriptFunction *NewCallback) {
|
||||||
|
#if PROMISE_CALLBACKS
|
||||||
|
std::unique_lock<std::mutex> Unique(Update);
|
||||||
|
if (Callbacks.Wrapper != nullptr)
|
||||||
|
AsClearCallback(Callbacks.Wrapper);
|
||||||
|
|
||||||
|
Callbacks.Wrapper = NewCallback;
|
||||||
|
if (Callbacks.Wrapper != nullptr) {
|
||||||
|
void *DelegateObject = Callbacks.Wrapper->GetDelegateObject();
|
||||||
|
if (DelegateObject != nullptr)
|
||||||
|
Context->GetEngine()->AddRefScriptObject(
|
||||||
|
DelegateObject, Callbacks.Wrapper->GetDelegateObjectType());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Callbacks.Wrapper != nullptr && !IsPending()) {
|
||||||
|
Callbacks.Wrapper = nullptr;
|
||||||
|
Unique.unlock();
|
||||||
|
Executor()(this, Context, NewCallback);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
PROMISE_ASSERT(false,
|
||||||
|
"script callback binder for <when> is not allowed");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Thread safe store function, this is used as promise resolver
|
||||||
|
function, will either only store the result or store result and execute
|
||||||
|
callback that will resume suspended context and then release the promise
|
||||||
|
(won't destroy)
|
||||||
|
*/
|
||||||
|
void Store(void *RefPointer, int RefTypeId) {
|
||||||
|
std::unique_lock<std::mutex> Unique(Update);
|
||||||
|
PROMISE_ASSERT(Value.TypeId == PROMISE_NULLID,
|
||||||
|
"promise should be settled only once");
|
||||||
|
PROMISE_ASSERT(RefPointer != nullptr || RefTypeId == asTYPEID_VOID,
|
||||||
|
"input pointer should not be null");
|
||||||
|
PROMISE_ASSERT(Engine != nullptr,
|
||||||
|
"promise is malformed (engine is null)");
|
||||||
|
PROMISE_ASSERT(Context != nullptr,
|
||||||
|
"promise is malformed (context is null)");
|
||||||
|
|
||||||
|
if (Value.TypeId != PROMISE_NULLID) {
|
||||||
|
asIScriptContext *ThisContext = asGetActiveContext();
|
||||||
|
if (!ThisContext)
|
||||||
|
ThisContext = Context;
|
||||||
|
ThisContext->SetException("promise is already fulfilled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((RefTypeId & asTYPEID_MASK_OBJECT)) {
|
||||||
|
asITypeInfo *Type = Engine->GetTypeInfoById(RefTypeId);
|
||||||
|
if (Type != nullptr)
|
||||||
|
Type->AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Value.TypeId = RefTypeId;
|
||||||
|
if (Value.TypeId & asTYPEID_OBJHANDLE) {
|
||||||
|
Value.Object = *(void **)RefPointer;
|
||||||
|
} else if (Value.TypeId & asTYPEID_MASK_OBJECT) {
|
||||||
|
Value.Object = Engine->CreateScriptObjectCopy(
|
||||||
|
RefPointer, Engine->GetTypeInfoById(Value.TypeId));
|
||||||
|
} else if (RefPointer != nullptr) {
|
||||||
|
Value.Integer = 0;
|
||||||
|
int Size = Engine->GetSizeOfPrimitiveType(Value.TypeId);
|
||||||
|
memcpy(&Value.Integer, RefPointer, Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SuspendOwned =
|
||||||
|
Context->GetUserData(PROMISE_USERID) == (void *)this;
|
||||||
|
if (SuspendOwned)
|
||||||
|
Context->SetUserData(nullptr, PROMISE_USERID);
|
||||||
|
|
||||||
|
bool WantsResume =
|
||||||
|
(Context->GetState() == asEXECUTION_SUSPENDED && SuspendOwned);
|
||||||
|
#if PROMISE_CALLBACKS
|
||||||
|
auto NativeCallback = std::move(Callbacks.Native);
|
||||||
|
auto *WrapperCallback = Callbacks.Wrapper;
|
||||||
|
Callbacks.Wrapper = nullptr;
|
||||||
|
Unique.unlock();
|
||||||
|
|
||||||
|
if (NativeCallback != nullptr)
|
||||||
|
NativeCallback(this);
|
||||||
|
|
||||||
|
if (WrapperCallback != nullptr)
|
||||||
|
Executor()(this, Context, WrapperCallback);
|
||||||
|
#else
|
||||||
|
Ready.notify_all();
|
||||||
|
Unique.unlock();
|
||||||
|
#endif
|
||||||
|
if (WantsResume)
|
||||||
|
Executor()(this, Context);
|
||||||
|
}
|
||||||
|
/* Thread safe store function, a little easier for C++ usage */
|
||||||
|
void Store(void *RefPointer, const char *TypeName) {
|
||||||
|
PROMISE_ASSERT(Engine != nullptr,
|
||||||
|
"promise is malformed (engine is null)");
|
||||||
|
PROMISE_ASSERT(TypeName != nullptr, "typename should not be null");
|
||||||
|
Store(RefPointer, Engine->GetTypeIdByDecl(TypeName));
|
||||||
|
}
|
||||||
|
/* Thread safe store function, for promise<void> */
|
||||||
|
void StoreVoid() { Store(nullptr, asTYPEID_VOID); }
|
||||||
|
/* Thread safe retrieve function, non-blocking try-retrieve future value */
|
||||||
|
bool Retrieve(void *RefPointer, int RefTypeId) {
|
||||||
|
PROMISE_ASSERT(Engine != nullptr,
|
||||||
|
"promise is malformed (engine is null)");
|
||||||
|
PROMISE_ASSERT(RefPointer != nullptr,
|
||||||
|
"output pointer should not be null");
|
||||||
|
if (Value.TypeId == PROMISE_NULLID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (RefTypeId & asTYPEID_OBJHANDLE) {
|
||||||
|
if ((Value.TypeId & asTYPEID_MASK_OBJECT)) {
|
||||||
|
if ((Value.TypeId & asTYPEID_HANDLETOCONST) &&
|
||||||
|
!(RefTypeId & asTYPEID_HANDLETOCONST))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Engine->RefCastObject(Value.Object,
|
||||||
|
Engine->GetTypeInfoById(Value.TypeId),
|
||||||
|
Engine->GetTypeInfoById(RefTypeId),
|
||||||
|
reinterpret_cast<void **>(RefPointer));
|
||||||
|
if (*(asPWORD *)RefPointer == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (RefTypeId & asTYPEID_MASK_OBJECT) {
|
||||||
|
if (Value.TypeId == RefTypeId) {
|
||||||
|
Engine->AssignScriptObject(
|
||||||
|
RefPointer, Value.Object,
|
||||||
|
Engine->GetTypeInfoById(Value.TypeId));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int Size1 = Engine->GetSizeOfPrimitiveType(Value.TypeId);
|
||||||
|
int Size2 = Engine->GetSizeOfPrimitiveType(RefTypeId);
|
||||||
|
PROMISE_ASSERT(Size1 == Size2,
|
||||||
|
"cannot map incompatible primitive types");
|
||||||
|
|
||||||
|
if (Size1 == Size2) {
|
||||||
|
memcpy(RefPointer, &Value.Integer, Size1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* Thread safe retrieve function, also non-blocking, another syntax is used
|
||||||
|
*/
|
||||||
|
void *Retrieve() {
|
||||||
|
RetrieveVoid();
|
||||||
|
if (Value.TypeId == PROMISE_NULLID)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (Value.TypeId & asTYPEID_OBJHANDLE)
|
||||||
|
return &Value.Object;
|
||||||
|
else if (Value.TypeId & asTYPEID_MASK_OBJECT)
|
||||||
|
return Value.Object;
|
||||||
|
else if (Value.TypeId <= asTYPEID_DOUBLE ||
|
||||||
|
Value.TypeId & asTYPEID_MASK_SEQNBR)
|
||||||
|
return &Value.Integer;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
/* Thread safe retrieve function */
|
||||||
|
void RetrieveVoid() {
|
||||||
|
std::unique_lock<std::mutex> Unique(Update);
|
||||||
|
asIScriptContext *ThisContext = asGetActiveContext();
|
||||||
|
if (ThisContext != nullptr && IsPending())
|
||||||
|
ThisContext->SetException("promise is still pending");
|
||||||
|
}
|
||||||
|
/* Can be used to check if promise is still pending */
|
||||||
|
bool IsPending() { return Value.TypeId == PROMISE_NULLID; }
|
||||||
|
/*
|
||||||
|
This function should be called before retrieving the value
|
||||||
|
from promise, it will either suspend current context and add
|
||||||
|
reference to this promise if it is still pending or do nothing
|
||||||
|
if promise was already settled
|
||||||
|
*/
|
||||||
|
AsBasicPromise *YieldIf() {
|
||||||
|
std::unique_lock<std::mutex> Unique(Update);
|
||||||
|
if (Value.TypeId == PROMISE_NULLID && Context != nullptr &&
|
||||||
|
Context->Suspend() >= 0)
|
||||||
|
Context->SetUserData(this, PROMISE_USERID);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
This function can be used to await for promise
|
||||||
|
within C++ code (blocking style)
|
||||||
|
*/
|
||||||
|
AsBasicPromise *WaitIf() {
|
||||||
|
if (!IsPending())
|
||||||
|
return this;
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> Unique(Update);
|
||||||
|
#if PROMISE_CALLBACKS
|
||||||
|
if (IsPending()) {
|
||||||
|
std::condition_variable Ready;
|
||||||
|
Callbacks.Native = [&Ready](AsBasicPromise<Executor> *) {
|
||||||
|
Ready.notify_all();
|
||||||
|
};
|
||||||
|
Ready.wait(Unique, [this]() { return !IsPending(); });
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (IsPending())
|
||||||
|
Ready.wait(Unique, [this]() { return !IsPending(); });
|
||||||
|
#endif
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*
|
||||||
|
Construct a promise, notify GC, set value to none,
|
||||||
|
grab a reference to script context
|
||||||
|
*/
|
||||||
|
AsBasicPromise(asIScriptContext *NewContext) noexcept
|
||||||
|
: Engine(nullptr), Context(NewContext), RefCount(1), RefMark(0) {
|
||||||
|
PROMISE_ASSERT(Context != nullptr, "context should not be null");
|
||||||
|
Engine = Context->GetEngine();
|
||||||
|
Engine->NotifyGarbageCollectorOfNewObject(
|
||||||
|
this, Engine->GetTypeInfoByName(PROMISE_TYPENAME));
|
||||||
|
Clean();
|
||||||
|
}
|
||||||
|
/* Reset value to none */
|
||||||
|
void Clean() {
|
||||||
|
memset(&Value, 0, sizeof(Value));
|
||||||
|
Value.TypeId = PROMISE_NULLID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* AsBasicPromise creation function, for use within C++ */
|
||||||
|
static AsBasicPromise *
|
||||||
|
Create(asIScriptContext *Context = asGetActiveContext()) {
|
||||||
|
return new (asAllocMem(sizeof(AsBasicPromise))) AsBasicPromise(Context);
|
||||||
|
}
|
||||||
|
/* AsBasicPromise creation function, for use within AngelScript */
|
||||||
|
static AsBasicPromise *CreateFactory(void *_Ref, int TypeId) {
|
||||||
|
AsBasicPromise *Future = new (asAllocMem(sizeof(AsBasicPromise)))
|
||||||
|
AsBasicPromise(asGetActiveContext());
|
||||||
|
if (TypeId != asTYPEID_VOID)
|
||||||
|
Future->Store(_Ref, TypeId);
|
||||||
|
|
||||||
|
return Future;
|
||||||
|
}
|
||||||
|
/* AsBasicPromise creation function, for use within AngelScript (void
|
||||||
|
* promise) */
|
||||||
|
static AsBasicPromise *CreateFactoryVoid(void *_Ref, int TypeId) {
|
||||||
|
return Create();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Interface registration, note: promise<void> is not supported,
|
||||||
|
instead use promise_v when internal datatype is not intended,
|
||||||
|
promise will be an object handle with GC behaviours, default
|
||||||
|
constructed promise will be pending otherwise early settled
|
||||||
|
*/
|
||||||
|
static void Register(asIScriptEngine *Engine) {
|
||||||
|
using Type = AsBasicPromise<Executor>;
|
||||||
|
PROMISE_ASSERT(Engine != nullptr, "script engine should not be null");
|
||||||
|
PROMISE_CHECK(
|
||||||
|
Engine->RegisterObjectType(PROMISE_TYPENAME "<class T>", 0,
|
||||||
|
asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME "<T>", asBEHAVE_FACTORY,
|
||||||
|
PROMISE_TYPENAME "<T>@ f(?&in)", asFUNCTION(Type::CreateFactory),
|
||||||
|
asCALL_CDECL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME "<T>", asBEHAVE_TEMPLATE_CALLBACK,
|
||||||
|
"bool f(int&in, bool&out)", asFUNCTION(Type::TemplateCallback),
|
||||||
|
asCALL_CDECL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME "<T>", asBEHAVE_ADDREF, "void f()",
|
||||||
|
asMETHOD(Type, AddRef), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME "<T>", asBEHAVE_RELEASE, "void f()",
|
||||||
|
asMETHOD(Type, Release), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME "<T>", asBEHAVE_SETGCFLAG, "void f()",
|
||||||
|
asMETHOD(Type, MarkRef), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME "<T>", asBEHAVE_GETGCFLAG, "bool f()",
|
||||||
|
asMETHOD(Type, IsRefMarked), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME "<T>", asBEHAVE_GETREFCOUNT, "int f()",
|
||||||
|
asMETHOD(Type, GetRefCount), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME "<T>", asBEHAVE_ENUMREFS, "void f(int&in)",
|
||||||
|
asMETHOD(Type, EnumReferences), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME "<T>", asBEHAVE_RELEASEREFS, "void f(int&in)",
|
||||||
|
asMETHOD(Type, ReleaseReferences), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectMethod(
|
||||||
|
PROMISE_TYPENAME "<T>", "void " PROMISE_WRAP "(?&in)",
|
||||||
|
asMETHODPR(Type, Store, (void *, int), void), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectMethod(
|
||||||
|
PROMISE_TYPENAME "<T>", "T& " PROMISE_UNWRAP "()",
|
||||||
|
asMETHODPR(Type, Retrieve, (), void *), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectMethod(
|
||||||
|
PROMISE_TYPENAME "<T>",
|
||||||
|
PROMISE_TYPENAME "<T>@+ " PROMISE_YIELD "()",
|
||||||
|
asMETHOD(Type, YieldIf), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectMethod(
|
||||||
|
PROMISE_TYPENAME "<T>", "bool " PROMISE_PENDING "()",
|
||||||
|
asMETHOD(Type, IsPending), asCALL_THISCALL));
|
||||||
|
#if PROMISE_CALLBACKS
|
||||||
|
PROMISE_CHECK(Engine->RegisterFuncdef(
|
||||||
|
"void " PROMISE_TYPENAME "<T>::" PROMISE_EVENT "(promise<T>@+)"));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectMethod(
|
||||||
|
PROMISE_TYPENAME "<T>", "void " PROMISE_WHEN "(" PROMISE_EVENT "@)",
|
||||||
|
asMETHODPR(Type, When, (asIScriptFunction *), void),
|
||||||
|
asCALL_THISCALL));
|
||||||
|
#endif
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectType(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX, 0, asOBJ_REF | asOBJ_GC));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX, asBEHAVE_FACTORY,
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX "@ f()",
|
||||||
|
asFUNCTION(Type::CreateFactoryVoid), asCALL_CDECL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX, asBEHAVE_ADDREF, "void f()",
|
||||||
|
asMETHOD(Type, AddRef), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX, asBEHAVE_RELEASE, "void f()",
|
||||||
|
asMETHOD(Type, Release), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX, asBEHAVE_SETGCFLAG,
|
||||||
|
"void f()", asMETHOD(Type, MarkRef), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX, asBEHAVE_GETGCFLAG,
|
||||||
|
"bool f()", asMETHOD(Type, IsRefMarked), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX, asBEHAVE_GETREFCOUNT,
|
||||||
|
"int f()", asMETHOD(Type, GetRefCount), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX, asBEHAVE_ENUMREFS,
|
||||||
|
"void f(int&in)", asMETHOD(Type, EnumReferences), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectBehaviour(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX, asBEHAVE_RELEASEREFS,
|
||||||
|
"void f(int&in)", asMETHOD(Type, ReleaseReferences),
|
||||||
|
asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectMethod(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX, "void " PROMISE_WRAP "()",
|
||||||
|
asMETHODPR(Type, StoreVoid, (), void), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectMethod(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX, "void " PROMISE_UNWRAP "()",
|
||||||
|
asMETHODPR(Type, RetrieveVoid, (), void), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectMethod(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX,
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX "@+ " PROMISE_YIELD "()",
|
||||||
|
asMETHOD(Type, YieldIf), asCALL_THISCALL));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectMethod(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX, "bool " PROMISE_PENDING "()",
|
||||||
|
asMETHOD(Type, IsPending), asCALL_THISCALL));
|
||||||
|
#if PROMISE_CALLBACKS
|
||||||
|
PROMISE_CHECK(
|
||||||
|
Engine->RegisterFuncdef("void " PROMISE_TYPENAME PROMISE_VOIDPOSTFIX
|
||||||
|
"::" PROMISE_EVENT "(promise_v@+)"));
|
||||||
|
PROMISE_CHECK(Engine->RegisterObjectMethod(
|
||||||
|
PROMISE_TYPENAME PROMISE_VOIDPOSTFIX,
|
||||||
|
"void " PROMISE_WHEN "(" PROMISE_EVENT "@)",
|
||||||
|
asMETHODPR(Type, When, (asIScriptFunction *), void),
|
||||||
|
asCALL_THISCALL));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* Template callback function for compiler, copy-paste from <array> class */
|
||||||
|
static bool TemplateCallback(asITypeInfo *Info, bool &DontGarbageCollect) {
|
||||||
|
int TypeId = Info->GetSubTypeId();
|
||||||
|
if (TypeId == asTYPEID_VOID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((TypeId & asTYPEID_MASK_OBJECT) && !(TypeId & asTYPEID_OBJHANDLE)) {
|
||||||
|
asIScriptEngine *Engine = Info->GetEngine();
|
||||||
|
asITypeInfo *SubType = Engine->GetTypeInfoById(TypeId);
|
||||||
|
asDWORD Flags = SubType->GetFlags();
|
||||||
|
|
||||||
|
if ((Flags & asOBJ_VALUE) && !(Flags & asOBJ_POD)) {
|
||||||
|
bool Found = false;
|
||||||
|
for (size_t i = 0; i < SubType->GetBehaviourCount(); i++) {
|
||||||
|
asEBehaviours Behaviour;
|
||||||
|
asIScriptFunction *Func =
|
||||||
|
SubType->GetBehaviourByIndex((int)i, &Behaviour);
|
||||||
|
if (Behaviour != asBEHAVE_CONSTRUCT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Func->GetParamCount() == 0) {
|
||||||
|
Found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Found) {
|
||||||
|
Engine->WriteMessage(
|
||||||
|
PROMISE_TYPENAME, 0, 0, asMSGTYPE_ERROR,
|
||||||
|
"The subtype has no default constructor");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if ((Flags & asOBJ_REF)) {
|
||||||
|
bool Found = false;
|
||||||
|
if (!Engine->GetEngineProperty(
|
||||||
|
asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE)) {
|
||||||
|
for (size_t i = 0; i < SubType->GetFactoryCount(); i++) {
|
||||||
|
asIScriptFunction *Function =
|
||||||
|
SubType->GetFactoryByIndex((int)i);
|
||||||
|
if (Function->GetParamCount() == 0) {
|
||||||
|
Found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Found) {
|
||||||
|
Engine->WriteMessage(PROMISE_TYPENAME, 0, 0,
|
||||||
|
asMSGTYPE_ERROR,
|
||||||
|
"The subtype has no default factory");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Flags & asOBJ_GC))
|
||||||
|
DontGarbageCollect = true;
|
||||||
|
} else if (!(TypeId & asTYPEID_OBJHANDLE)) {
|
||||||
|
DontGarbageCollect = true;
|
||||||
|
} else {
|
||||||
|
asITypeInfo *SubType = Info->GetEngine()->GetTypeInfoById(TypeId);
|
||||||
|
asDWORD Flags = SubType->GetFlags();
|
||||||
|
|
||||||
|
if (!(Flags & asOBJ_GC)) {
|
||||||
|
if ((Flags & asOBJ_SCRIPT_OBJECT)) {
|
||||||
|
if ((Flags & asOBJ_NOINHERIT))
|
||||||
|
DontGarbageCollect = true;
|
||||||
|
} else
|
||||||
|
DontGarbageCollect = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef AS_PROMISE_NO_GENERATOR
|
||||||
|
/*
|
||||||
|
A fast and minimal code generator function for custom syntax of promise
|
||||||
|
class, it takes raw code input with <await> syntax and returns code that use
|
||||||
|
un-wrappers.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
AsGeneratePromiseEntrypoints(const char *Text, size_t *InoutTextSize,
|
||||||
|
void *(*AllocateMemory)(size_t) = &asAllocMem,
|
||||||
|
void (*FreeMemory)(void *) = &asFreeMem) {
|
||||||
|
PROMISE_ASSERT(Text != nullptr, "script code should not be null");
|
||||||
|
PROMISE_ASSERT(InoutTextSize != nullptr,
|
||||||
|
"script code size should not be null");
|
||||||
|
PROMISE_ASSERT(AllocateMemory != nullptr,
|
||||||
|
"memory allocation function should not be null");
|
||||||
|
PROMISE_ASSERT(FreeMemory != nullptr,
|
||||||
|
"memory deallocation function should not be null");
|
||||||
|
const char Match[] = PROMISE_AWAIT " ";
|
||||||
|
size_t Size = *InoutTextSize;
|
||||||
|
char *Code = (char *)AllocateMemory(Size + 1);
|
||||||
|
size_t MatchSize = sizeof(Match) - 1;
|
||||||
|
size_t Offset = 0;
|
||||||
|
memcpy(Code, Text, Size);
|
||||||
|
Code[Size] = '\0';
|
||||||
|
|
||||||
|
while (Offset < Size) {
|
||||||
|
char U = Code[Offset];
|
||||||
|
if (U == '/' && Offset + 1 < Size &&
|
||||||
|
(Code[Offset + 1] == '/' || Code[Offset + 1] == '*')) {
|
||||||
|
if (Code[++Offset] == '*') {
|
||||||
|
while (Offset + 1 < Size) {
|
||||||
|
char N = Code[Offset++];
|
||||||
|
if (N == '*' && Code[Offset++] == '/')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (Offset < Size) {
|
||||||
|
char N = Code[Offset++];
|
||||||
|
if (N == '\r' || N == '\n')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} else if (U == '\"' || U == '\'') {
|
||||||
|
++Offset;
|
||||||
|
while (Offset < Size) {
|
||||||
|
size_t LastOffset = Offset++;
|
||||||
|
if (Code[LastOffset] != U)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (LastOffset < 1 || Code[LastOffset - 1] != '\\')
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (LastOffset > 1 && Code[LastOffset - 2] == '\\')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} else if (Size - Offset < MatchSize ||
|
||||||
|
memcmp(Code + Offset, Match, MatchSize) != 0) {
|
||||||
|
++Offset;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Start = Offset + MatchSize;
|
||||||
|
while (Start < Size) {
|
||||||
|
if (!isspace((uint8_t)Code[Start]))
|
||||||
|
break;
|
||||||
|
++Start;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t Brackets = 0;
|
||||||
|
size_t End = Start;
|
||||||
|
while (End < Size) {
|
||||||
|
char V = Code[End];
|
||||||
|
if (V == ')') {
|
||||||
|
if (--Brackets < 0)
|
||||||
|
break;
|
||||||
|
} else if (V == '\"' || V == '\'') {
|
||||||
|
++End;
|
||||||
|
while (End < Size) {
|
||||||
|
size_t LastEnd = End++;
|
||||||
|
if (Code[LastEnd] != V)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (LastEnd < 1 || Code[LastEnd - 1] != '\\')
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (LastEnd > 1 && Code[LastEnd - 2] == '\\')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
--End;
|
||||||
|
} else if (V == ';')
|
||||||
|
break;
|
||||||
|
else if (V == '(')
|
||||||
|
++Brackets;
|
||||||
|
End++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (End == Start) {
|
||||||
|
Offset = End;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char Generator[] = ")." PROMISE_YIELD "()." PROMISE_UNWRAP "()";
|
||||||
|
char *Left = Code, *Middle = Code + Start, *Right = Code + End;
|
||||||
|
size_t LeftSize = Offset;
|
||||||
|
size_t MiddleSize = End - Start;
|
||||||
|
size_t GeneratorSize = sizeof(Generator) - 1;
|
||||||
|
size_t RightSize = Size - Offset;
|
||||||
|
size_t SubstringSize =
|
||||||
|
LeftSize + MiddleSize + GeneratorSize + RightSize;
|
||||||
|
size_t PrevSize = End - Offset;
|
||||||
|
size_t NewSize = MiddleSize + GeneratorSize + 1;
|
||||||
|
|
||||||
|
char *Substring = (char *)AllocateMemory(SubstringSize + 1);
|
||||||
|
memcpy(Substring, Left, LeftSize);
|
||||||
|
memcpy(Substring + LeftSize, "(", 1);
|
||||||
|
memcpy(Substring + LeftSize + 1, Middle, MiddleSize);
|
||||||
|
memcpy(Substring + LeftSize + 1 + MiddleSize, Generator, GeneratorSize);
|
||||||
|
memcpy(Substring + LeftSize + 1 + MiddleSize + GeneratorSize, Right,
|
||||||
|
RightSize);
|
||||||
|
Substring[SubstringSize] = '\0';
|
||||||
|
FreeMemory(Code);
|
||||||
|
|
||||||
|
size_t NestedSize = Offset + MiddleSize;
|
||||||
|
char Prev = Substring[NestedSize];
|
||||||
|
Substring[NestedSize] = '\0';
|
||||||
|
bool IsRecursive =
|
||||||
|
(strstr(Substring + Offset, PROMISE_AWAIT) != nullptr);
|
||||||
|
Substring[NestedSize] = Prev;
|
||||||
|
|
||||||
|
Code = Substring;
|
||||||
|
Size -= PrevSize;
|
||||||
|
Size += NewSize;
|
||||||
|
if (!IsRecursive)
|
||||||
|
Offset += MiddleSize + GeneratorSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
*InoutTextSize = Size;
|
||||||
|
return Code;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifndef AS_PROMISE_NO_DEFAULTS
|
||||||
|
/*
|
||||||
|
Basic promise settle executor, will
|
||||||
|
resume context at thread that has
|
||||||
|
settled the promise.
|
||||||
|
*/
|
||||||
|
struct AsDirectExecutor {
|
||||||
|
/* Called after suspend, this method will probably be inlined anyways */
|
||||||
|
inline void operator()(AsBasicPromise<AsDirectExecutor> *Promise,
|
||||||
|
asIScriptContext *Context) {
|
||||||
|
/*
|
||||||
|
Context should be suspended at this moment but if for
|
||||||
|
some reason it went active between function calls
|
||||||
|
(multithreaded) then user is responsible for this task to be properly
|
||||||
|
queued or exception should thrown if possible
|
||||||
|
*/
|
||||||
|
Context->Execute();
|
||||||
|
}
|
||||||
|
/* Called after suspend, for callback execution */
|
||||||
|
inline void operator()(AsBasicPromise<AsDirectExecutor> *Promise,
|
||||||
|
asIScriptContext *Context,
|
||||||
|
asIScriptFunction *Callback) {
|
||||||
|
/*
|
||||||
|
Callback control flow:
|
||||||
|
If main context is active: execute nested call on
|
||||||
|
current context If main context is suspended: execute on newly
|
||||||
|
created context Otherwise: execute on current context
|
||||||
|
*/
|
||||||
|
asEContextState State = Context->GetState();
|
||||||
|
auto Execute = [&Promise, &Context, &Callback]() {
|
||||||
|
PROMISE_CHECK(Context->Prepare(Callback));
|
||||||
|
PROMISE_CHECK(Context->SetArgObject(0, Promise));
|
||||||
|
Context->Execute();
|
||||||
|
};
|
||||||
|
if (State == asEXECUTION_ACTIVE) {
|
||||||
|
PROMISE_CHECK(Context->PushState());
|
||||||
|
Execute();
|
||||||
|
PROMISE_CHECK(Context->PopState());
|
||||||
|
} else if (State == asEXECUTION_SUSPENDED) {
|
||||||
|
asIScriptEngine *Engine = Context->GetEngine();
|
||||||
|
Context = Engine->RequestContext();
|
||||||
|
Execute();
|
||||||
|
Engine->ReturnContext(Context);
|
||||||
|
} else
|
||||||
|
Execute();
|
||||||
|
|
||||||
|
/* Cleanup referenced resources */
|
||||||
|
AsClearCallback(Callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Executor that notifies prepared context
|
||||||
|
whenever promise settles.
|
||||||
|
*/
|
||||||
|
struct AsReactiveExecutor {
|
||||||
|
typedef std::function<void(AsBasicPromise<AsReactiveExecutor> *,
|
||||||
|
asIScriptFunction *)>
|
||||||
|
ReactiveCallback;
|
||||||
|
|
||||||
|
/* Called after suspend, this method will probably be inlined anyways */
|
||||||
|
inline void operator()(AsBasicPromise<AsReactiveExecutor> *Promise,
|
||||||
|
asIScriptContext *Context) {
|
||||||
|
ReactiveCallback &Execute = GetCallback(Context);
|
||||||
|
Execute(Promise, nullptr);
|
||||||
|
}
|
||||||
|
/* Called after suspend, for callback execution */
|
||||||
|
inline void operator()(AsBasicPromise<AsReactiveExecutor> *Promise,
|
||||||
|
asIScriptContext *Context,
|
||||||
|
asIScriptFunction *Callback) {
|
||||||
|
ReactiveCallback &Execute = GetCallback(Context);
|
||||||
|
Execute(Promise, Callback);
|
||||||
|
}
|
||||||
|
static void SetCallback(asIScriptContext *Context,
|
||||||
|
ReactiveCallback *Callback) {
|
||||||
|
PROMISE_ASSERT(!Callback || *Callback, "invalid reactive callback");
|
||||||
|
Context->SetUserData((void *)Callback, 1022);
|
||||||
|
}
|
||||||
|
static ReactiveCallback &GetCallback(asIScriptContext *Context) {
|
||||||
|
ReactiveCallback *Callback =
|
||||||
|
(ReactiveCallback *)Context->GetUserData(1022);
|
||||||
|
PROMISE_ASSERT(Callback != nullptr,
|
||||||
|
"missing reactive callback on context");
|
||||||
|
return *Callback;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using AsDirectPromise = AsBasicPromise<AsDirectExecutor>;
|
||||||
|
using AsReactivePromise = AsBasicPromise<AsReactiveExecutor>;
|
||||||
|
#endif
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -27,12 +27,6 @@
|
||||||
// Compilation settings
|
// Compilation settings
|
||||||
//
|
//
|
||||||
|
|
||||||
// Some prefer to use property accessors to get/set the length of the string
|
|
||||||
// This option registers the accessors instead of the method length()
|
|
||||||
#ifndef AS_USE_ACCESSORS
|
|
||||||
#define AS_USE_ACCESSORS 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// This option disables the implicit operators with primitives
|
// This option disables the implicit operators with primitives
|
||||||
#ifndef AS_NO_IMPL_OPS_WITH_STRING_AND_PRIMITIVE
|
#ifndef AS_NO_IMPL_OPS_WITH_STRING_AND_PRIMITIVE
|
||||||
#define AS_NO_IMPL_OPS_WITH_STRING_AND_PRIMITIVE 0
|
#define AS_NO_IMPL_OPS_WITH_STRING_AND_PRIMITIVE 0
|
||||||
|
|
|
@ -47,7 +47,8 @@ void RegisterScriptRegex(asIScriptEngine *engine) {
|
||||||
|
|
||||||
r = engine->RegisterObjectBehaviour(
|
r = engine->RegisterObjectBehaviour(
|
||||||
"exp", asBEHAVE_CONSTRUCT,
|
"exp", asBEHAVE_CONSTRUCT,
|
||||||
"void f(string &in, PatternOptions = PatternOptions::NoPatternOption)",
|
"void f(const string &in, PatternOptions = "
|
||||||
|
"regex::PatternOptions::NoPatternOption)",
|
||||||
asFUNCTIONPR(
|
asFUNCTIONPR(
|
||||||
[](void *memory, const QString &r, Angel::PatternOptions op) {
|
[](void *memory, const QString &r, Angel::PatternOptions op) {
|
||||||
new (memory) QRegularExpression(
|
new (memory) QRegularExpression(
|
||||||
|
@ -169,7 +170,7 @@ void RegisterScriptRegex(asIScriptEngine *engine) {
|
||||||
|
|
||||||
// QRegularExpression...
|
// QRegularExpression...
|
||||||
r = engine->RegisterObjectMethod(
|
r = engine->RegisterObjectMethod(
|
||||||
"exp", "void setPattern(string &in)",
|
"exp", "void setPattern(const string &in)",
|
||||||
asMETHODPR(QRegularExpression, setPattern, (const QString &), void),
|
asMETHODPR(QRegularExpression, setPattern, (const QString &), void),
|
||||||
asCALL_THISCALL);
|
asCALL_THISCALL);
|
||||||
Q_ASSERT(r >= 0);
|
Q_ASSERT(r >= 0);
|
||||||
|
|
|
@ -129,10 +129,10 @@
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QSpinBox" name="sbFontSize">
|
<widget class="QSpinBox" name="sbFontSize">
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>10</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>50</number>
|
<number>25</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -192,7 +192,7 @@
|
||||||
<string notr="true">-</string>
|
<string notr="true">-</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignCenter</set>
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -212,7 +212,7 @@
|
||||||
<string notr="true">-</string>
|
<string notr="true">-</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignCenter</set>
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -232,7 +232,7 @@
|
||||||
<string notr="true">-</string>
|
<string notr="true">-</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignCenter</set>
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -252,7 +252,7 @@
|
||||||
<string notr="true">-</string>
|
<string notr="true">-</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignCenter</set>
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -265,7 +265,7 @@
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
|
|
|
@ -46,8 +46,10 @@ void ScriptSettingDialog::loadData() {
|
||||||
auto &set = SettingManager::instance();
|
auto &set = SettingManager::instance();
|
||||||
|
|
||||||
this->blockSignals(true);
|
this->blockSignals(true);
|
||||||
|
ui->listWidget->clear();
|
||||||
ui->cbEnable->setChecked(set.scriptEnabled());
|
ui->cbEnable->setChecked(set.scriptEnabled());
|
||||||
ui->cbAllowUsrScript->setChecked(set.allowUsrScriptInRoot());
|
ui->cbAllowUsrScript->setChecked(set.allowUsrScriptInRoot());
|
||||||
|
ui->sbTimeout->setValue(set.scriptTimeout());
|
||||||
this->blockSignals(false);
|
this->blockSignals(false);
|
||||||
|
|
||||||
if (set.scriptEnabled()) {
|
if (set.scriptEnabled()) {
|
||||||
|
@ -112,6 +114,7 @@ void ScriptSettingDialog::apply() {
|
||||||
auto &set = SettingManager::instance();
|
auto &set = SettingManager::instance();
|
||||||
set.setScriptEnabled(ui->cbEnable->isChecked());
|
set.setScriptEnabled(ui->cbEnable->isChecked());
|
||||||
set.setAllowUsrScriptInRoot(ui->cbAllowUsrScript->isChecked());
|
set.setAllowUsrScriptInRoot(ui->cbAllowUsrScript->isChecked());
|
||||||
|
set.setScriptTimeout(ui->sbTimeout->value());
|
||||||
set.setUsrHideCats(usrHideCats);
|
set.setUsrHideCats(usrHideCats);
|
||||||
set.setSysHideCats(sysHideCats);
|
set.setSysHideCats(sysHideCats);
|
||||||
set.save(SettingManager::SCRIPT);
|
set.save(SettingManager::SCRIPT);
|
||||||
|
@ -149,6 +152,9 @@ void ScriptSettingDialog::on_listWidget_currentRowChanged(int currentRow) {
|
||||||
info->append(tr("Name:") + meta.name);
|
info->append(tr("Name:") + meta.name);
|
||||||
info->append(tr("Author:") + meta.author);
|
info->append(tr("Author:") + meta.author);
|
||||||
info->append(tr("License:") + meta.license);
|
info->append(tr("License:") + meta.license);
|
||||||
|
info->append(tr("ContextMenu:") + (meta.isContextMenu
|
||||||
|
? QStringLiteral("true")
|
||||||
|
: QStringLiteral("false")));
|
||||||
info->append(tr("HomePage:") + meta.homepage);
|
info->append(tr("HomePage:") + meta.homepage);
|
||||||
info->append(tr("Comment:"));
|
info->append(tr("Comment:"));
|
||||||
auto cur = info->textCursor();
|
auto cur = info->textCursor();
|
||||||
|
|
|
@ -40,6 +40,63 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="widget" native="true">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lblTimeout">
|
||||||
|
<property name="text">
|
||||||
|
<string>Timeout</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="sbTimeout">
|
||||||
|
<property name="suffix">
|
||||||
|
<string notr="true"> min</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>312480</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -52,7 +109,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSplitter" name="splitter">
|
<widget class="QSplitter" name="splitter">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QListWidget" name="listWidget"/>
|
<widget class="QListWidget" name="listWidget"/>
|
||||||
<widget class="QWidget" name="layoutWidget">
|
<widget class="QWidget" name="layoutWidget">
|
||||||
|
|
|
@ -52,9 +52,6 @@
|
||||||
|
|
||||||
#define PROEXT ".wingpro"
|
#define PROEXT ".wingpro"
|
||||||
|
|
||||||
Q_DECL_UNUSED constexpr auto FIND_CONTEXT_SIZE = 3;
|
|
||||||
Q_DECL_UNUSED constexpr auto FIND_MAX_DISPLAY_FIND_CHARS = 8;
|
|
||||||
|
|
||||||
Q_DECL_UNUSED static inline QString NAMEICONRES(const QString &name) {
|
Q_DECL_UNUSED static inline QString NAMEICONRES(const QString &name) {
|
||||||
return ":/com.wingsummer.winghex/images/" + name + ".png";
|
return ":/com.wingsummer.winghex/images/" + name + ".png";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue