Compare commits

..

14 Commits

84 changed files with 5760 additions and 3171 deletions

View File

@ -40,7 +40,7 @@ jobs:
run: sudo apt install fakeroot patchelf -y
- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: '3.8'
cache: 'pip'

View File

@ -43,7 +43,7 @@ jobs:
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: '3.8'
cache: 'pip'
@ -64,4 +64,4 @@ jobs:
with:
name: WingHexExplorer2-win-release-build-cache
path: ${{github.workspace}}/mkinstaller/innoSetup/*.exe

View File

@ -374,7 +374,6 @@ QString QConsoleWidget::currentCommandLine() const {
int QConsoleWidget::currentHeaderPos() const { return inpos_; }
void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
QTextCharFormat currfmt = currentCharFormat();
QTextCursor tc = textCursor();
if (mode() == Input) {
@ -400,8 +399,8 @@ void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
inpos_ = tc.position() - inpos_;
// restore the edit pos
tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, editpos);
tc.setCharFormat({});
setTextCursor(tc);
setCurrentCharFormat(currfmt);
} else {
// in output mode messages are ed
QTextCursor tc1 = tc;
@ -416,6 +415,8 @@ void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
textCursor().insertText(message, fmt);
ensureCursorVisible();
tc.setCharFormat({});
// restore cursor if needed
if (needsRestore)
setTextCursor(tc);

View File

@ -437,44 +437,30 @@ void QHexDocument::findAllBytes(qsizetype begin, qsizetype end,
}
}
qsizetype QHexDocument::findAllBytesExt(qsizetype begin, qsizetype end,
const QString &pattern,
QList<qsizetype> &results,
const std::function<bool()> &pred) {
void QHexDocument::findAllBytesExt(qsizetype begin, qsizetype end,
const QString &pattern,
QList<qsizetype> &results,
const std::function<bool()> &pred) {
results.clear();
auto patterns = parseConvertPattern(pattern);
if (patterns.isEmpty()) {
return 0;
if (end < 0) {
end = m_buffer->length();
}
qsizetype p = begin > 0 ? begin : 0;
qsizetype e = end > begin ? end : -1;
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;
}
if (pattern.isEmpty() || end > m_buffer->length() || begin >= end) {
return;
}
while (pred()) {
p = findNextExt(p, pattern);
if (p < 0 || (e > 0 && p > e)) {
qsizetype pos = begin;
qsizetype n = pattern.size();
while (pos + n <= end && pred()) {
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;
}
if (results.size() > QHEXVIEW_FIND_LIMIT) {
break;
}
results.append(p);
p += offset;
}
return offset;
}
bool QHexDocument::insert(qsizetype offset, uchar b) {
@ -538,287 +524,92 @@ bool QHexDocument::_remove(qsizetype offset, qsizetype len) {
return true;
}
qsizetype QHexDocument::findNextExt(qsizetype begin,
const QList<FindStep> &patterns) {
auto op = [this](qsizetype &pos, const FindStep &step,
qsizetype *begin = nullptr) -> bool {
if (pos < 0 || pos >= length()) {
return false;
}
if (std::holds_alternative<QByteArray>(step)) {
auto v = std::get<QByteArray>(step);
auto len = v.length();
auto r = findNext(pos, v);
if (r >= 0) {
if (begin) {
*begin = r;
} else {
if (r != pos) {
pos = -1;
return false;
}
}
pos = r + len;
return true;
} else {
pos = -1;
bool QHexDocument::parsePattern(const QString &pattern,
QList<PatternByte> &out) {
out.clear();
QString p = pattern;
p.remove(' ');
if (p.size() % 2 != 0) {
return false;
}
for (int i = 0; i < p.size(); i += 2) {
QChar hi = p[i], lo = p[i + 1];
PatternByte pb;
// high nibble
if (hi != '?') {
int v = hex2Int(hi);
if (v < 0) {
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 < length()) {
return true;
}
pb.value |= quint8(v << 4);
pb.mask |= 0xF0;
}
return false;
};
while (begin < length()) {
auto pos = begin;
auto p = patterns.cbegin();
auto r = op(pos, *p, &begin);
if (!r) {
if (pos < 0) {
return -1;
// low nibble
if (lo != '?') {
int v = hex2Int(lo);
if (v < 0) {
return false;
}
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;
for (; p != patterns.cend(); ++p) {
auto r = op(pos, *p);
if (!r) {
for (qsizetype i = 0; i < n; ++i) {
if (!matchByte(m_buffer->at(pos + i), patterns[i])) {
ok = false;
if (pos < 0) {
return -1;
}
begin = pos;
break;
}
}
if (ok) {
return begin;
}
if (ok)
return pos;
}
return -1;
}
qsizetype QHexDocument::findPreviousExt(qsizetype begin,
const QList<FindStep> &patterns) {
auto op = [this](qsizetype &pos, const FindStep &step,
qsizetype *begin = nullptr) -> bool {
if (pos < 0 || pos >= length()) {
return false;
}
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;
const QList<PatternByte> &patterns) {
if (patterns.isEmpty() || begin < 0)
return -1;
qsizetype n = patterns.size();
qsizetype maxStart = qMin(begin, m_buffer->length() - n);
for (qsizetype pos = maxStart; pos >= 0; --pos) {
bool ok = true;
for (; p != patterns.crend(); ++p) {
auto r = op(pos, *p);
if (!r) {
for (qsizetype i = 0; i < n; ++i) {
if (!matchByte(m_buffer->at(pos + i), patterns[i])) {
ok = false;
if (pos < 0) {
return -1;
}
begin = pos;
break;
}
}
if (ok) {
return begin;
}
if (ok)
return pos;
}
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
@ -913,27 +704,31 @@ void QHexDocument::beginMarco(const QString &text) {
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) {
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) {
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) {
if (m_keepsize || m_readonly || m_islocked)
return;
if (m_keepsize || m_readonly || m_islocked) {
return false;
}
auto cmd = MakeInsert(nullptr, cursor, offset, data, nibbleindex);
if (cmd) {
m_undostack->push(cmd);
} else {
return false;
}
emit documentChanged();
return true;
}
void QHexDocument::Append(QHexCursor *cursor, uchar b, int nibbleindex) {
@ -951,15 +746,19 @@ void QHexDocument::Append(QHexCursor *cursor, const QByteArray &data,
emit documentChanged();
}
void QHexDocument::Replace(QHexCursor *cursor, qsizetype offset,
bool QHexDocument::Replace(QHexCursor *cursor, qsizetype offset,
const QByteArray &data, int nibbleindex) {
if (m_readonly || m_islocked)
return;
if (m_readonly || m_islocked) {
return false;
}
auto cmd = MakeReplace(nullptr, cursor, offset, data, nibbleindex);
if (cmd) {
m_undostack->push(cmd);
} else {
return false;
}
emit documentChanged();
return true;
}
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) {
auto patterns = parseConvertPattern(pattern);
if (patterns.isEmpty()) {
QList<PatternByte> patterns;
if (!parsePattern(pattern, patterns)) {
return -1;
}
return findNextExt(begin, patterns);
}
qsizetype QHexDocument::findPreviousExt(qsizetype begin,
const QString &pattern) {
auto patterns = parseConvertPattern(pattern);
if (patterns.isEmpty()) {
QList<PatternByte> patterns;
if (!parsePattern(pattern, patterns)) {
return -1;
}
return findPreviousExt(begin, patterns);
}

View File

@ -107,7 +107,7 @@ public:
QList<qsizetype> &results,
const std::function<bool()> &pred = [] { return true; });
qsizetype findAllBytesExt(
void findAllBytesExt(
qsizetype begin, qsizetype end, const QString &pattern,
QList<qsizetype> &results,
const std::function<bool()> &pred = [] { return true; });
@ -148,14 +148,14 @@ public slots:
void beginMarco(const QString &text);
void endMarco();
void Insert(QHexCursor *cursor, qsizetype offset, uchar b, int nibbleindex);
void Insert(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
bool Insert(QHexCursor *cursor, qsizetype offset, uchar b, int nibbleindex);
bool Insert(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
int nibbleindex);
void Append(QHexCursor *cursor, uchar b, 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);
void Replace(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
bool Replace(QHexCursor *cursor, qsizetype offset, const QByteArray &data,
int nibbleindex = 0);
bool Remove(QHexCursor *cursor, qsizetype offset, qsizetype len,
int nibbleindex = 0);
@ -204,20 +204,28 @@ public slots:
bool _remove(qsizetype offset, qsizetype len);
private:
// AB
struct HexWildItem {
uchar higher; // A
uchar lower; // B
// PatternByte: represents a byte in the pattern, with nibble-level wildcard
// support
struct PatternByte {
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 >
using FindStep = std::variant<QByteArray, HexWildItem, size_t>;
// Parse pattern string (e.g., "00 ?? AB C? 88" or "00??ABC?88") into
// PatternByte list
bool parsePattern(const QString &pattern, QList<PatternByte> &out);
QList<FindStep> parseConvertPattern(const QString &pattern);
qsizetype findNextExt(qsizetype begin, const QList<FindStep> &patterns);
qsizetype findPreviousExt(qsizetype begin, const QList<FindStep> &patterns);
// Byte match using mask
inline bool matchByte(quint8 data, const PatternByte &pb) {
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

View File

@ -707,7 +707,6 @@ void QHexRenderer::drawHex(QPainter *painter, const QRect &linerect,
if (!dis)
this->applyMetadata(textcursor, line, Hex);
this->applyBookMark(textcursor, line, Hex);
this->applySelection(textcursor, line, Hex);
this->applyCursorHex(textcursor, line);
@ -718,24 +717,76 @@ void QHexRenderer::drawHex(QPainter *painter, const QRect &linerect,
ctx.palette.setColor(QPalette::Text, m_bytesColor);
textdocument.documentLayout()->draw(painter, ctx);
this->applyBookMark(painter, textcursor, line, Hex);
painter->restore();
}
void QHexRenderer::applyBookMark(QTextCursor &textcursor, qsizetype line,
Factor factor) {
void QHexRenderer::applyBookMark(QPainter *painter, QTextCursor &textcursor,
qsizetype line, Factor factor) {
if (!m_document->lineHasBookMark(line))
return;
painter->save();
auto pos = m_document->getLineBookmarksPos(line);
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();
textcursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor,
factor - 1);
charformat.setFontWeight(QFont::Bold);
textcursor.setCharFormat(charformat);
auto textOutline = charformat.textOutline();
constexpr auto ALPHA = 180;
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,
@ -770,6 +821,7 @@ void QHexRenderer::drawString(QPainter *painter, const QRect &linerect,
ctx.palette.setColor(QPalette::Text, m_bytesColor);
textdocument.documentLayout()->draw(painter, ctx);
this->applyBookMark(painter, textcursor, line, String);
painter->restore();
}

View File

@ -144,8 +144,10 @@ private:
qsizetype lineStart, qsizetype lineEnd, Factor factor,
bool strikeOut, bool hasSelection) const;
void applyBookMark(QTextCursor &textcursor, qsizetype line,
Factor factor); // added by wingsummer
// added by wingsummer
void applyBookMark(QPainter *painter, QTextCursor &textcursor,
qsizetype line, Factor factor);
void applyCursorAscii(QTextCursor &textcursor, qsizetype line) const;
void applyCursorHex(QTextCursor &textcursor, qsizetype line) const;
void drawAddress(QPainter *painter, const QRect &linerect, qsizetype line);

View File

@ -355,7 +355,7 @@ qsizetype QHexView::findPrevious(qsizetype begin, const QByteArray &ba) {
bool QHexView::RemoveSelection(int nibbleindex) {
if (!m_cursor->hasSelection())
return false;
return true;
auto total = m_cursor->selectionCount();
m_document->beginMarco(QStringLiteral("RemoveSelection"));
@ -375,7 +375,7 @@ bool QHexView::RemoveSelection(int nibbleindex) {
bool QHexView::removeSelection() {
if (!m_cursor->hasSelection())
return false;
return true;
// We essure selections are ordered by desending
// by selection-start, so it's safe to remove
@ -417,7 +417,7 @@ QByteArrayList QHexView::selectedBytes() const {
return res;
}
void QHexView::paste(bool hex) {
bool QHexView::paste(bool hex) {
QClipboard *c = qApp->clipboard();
QByteArray data;
@ -426,19 +426,28 @@ void QHexView::paste(bool hex) {
} else {
auto d = c->mimeData();
data = d->data(QStringLiteral("application/octet-stream"));
if (data.isEmpty()) {
data = d->text().toUtf8();
}
}
if (data.isEmpty())
return;
if (data.isEmpty()) {
return true;
}
this->removeSelection();
auto ret = this->removeSelection();
if (!ret) {
return false;
}
auto pos = m_cursor->position().offset();
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
} else
m_document->replace(pos, data);
return ret;
} else {
return m_document->replace(pos, data);
}
}
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();
QByteArray data;
@ -466,19 +475,27 @@ void QHexView::Paste(bool hex, int nibbleindex) {
} else {
auto d = c->mimeData();
data = d->data(QStringLiteral("application/octet-stream"));
if (data.isEmpty()) {
data = d->text().toUtf8();
}
}
if (data.isEmpty())
return;
if (data.isEmpty()) {
return true;
}
this->RemoveSelection(nibbleindex);
auto ret = this->RemoveSelection(nibbleindex);
if (!ret) {
return false;
}
auto pos = m_cursor->position().offset();
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
return ret;
} 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; }
void QHexView::setScaleRate(qreal rate) {
if (m_scaleRate > 0) {
if (rate >= 0.2 && rate < 2.01) {
m_scaleRate = rate;
setFontSize(fontSize());
emit scaleRateChanged();
update();
}
}
@ -709,7 +727,7 @@ void QHexView::focusOutEvent(QFocusEvent *e) {
void QHexView::wheelEvent(QWheelEvent *e) {
if (qApp->keyboardModifiers() == Qt::ControlModifier) {
auto dela = e->angleDelta().y() / 1200.0 / 2;
auto dela = e->angleDelta().y() / 1200.0;
setScaleRate(scaleRate() + dela);
return;
}

View File

@ -135,10 +135,10 @@ public:
bool cut(bool hex);
bool copy(bool hex = false);
void paste(bool hex = false);
bool paste(bool hex = false);
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, const QByteArray &data, int nibbleindex = 0);

@ -1 +1 @@
Subproject commit e5cef2dcf126037ffdc57a5aaf6a3b1d3f4c70ae
Subproject commit 3a5bb0d8bc45b6150ed4a7513b0712b0a5954a74

View File

@ -8,7 +8,7 @@ set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(PROJECT_VERSION "2.2.2")
set(PROJECT_VERSION "2.2.3")
find_package(
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)
option(WINGHEX_USE_FRAMELESS ON)
option(BUILD_TEST_PLUGIN OFF)
option(BUILD_SHARED_MEM_EXT OFF)
add_definitions(-DAS_NO_THREADS)
if(BUILD_TEST_PLUGIN)
add_subdirectory(TestPlugin)
endif()
option(BUILD_SHARED_MEM_EXT OFF)
if(BUILD_SHARED_MEM_EXT)
add_subdirectory(ShareMemoryDrv)
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}/QT_VERSION" ${QT_VERSION_MAJOR})
option(WINGHEX_USE_FRAMELESS "Use borderless windows to ensure UI uniformity"
TRUE)
if(WIN32)
find_package(QT NAMES Qt6 Qt5 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_INSTALL "Install library" OFF)
add_subdirectory(3rdparty/qwindowkit)
add_definitions(-DWINGHEX_USE_FRAMELESS)
add_compile_definitions(WINGHEX_USE_FRAMELESS)
endif()
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
@ -193,8 +194,6 @@ set(CONTROL_SRC
src/control/qlistviewext.cpp
src/control/asobjtreewidget.h
src/control/asobjtreewidget.cpp
src/control/qtlonglongspinbox.cpp
src/control/qtlonglongspinbox.h
src/control/dockwidgettab.h
src/control/dockwidgettab.cpp
src/control/qhextextedit.h
@ -208,7 +207,9 @@ set(CONTROL_SRC
src/control/gotolinewidget.h
src/control/gotolinewidget.cpp
src/control/codeeditcontrolwidget.h
src/control/codeeditcontrolwidget.cpp)
src/control/codeeditcontrolwidget.cpp
src/control/scrollablelabel.h
src/control/scrollablelabel.cpp)
set(CLASS_SRC
src/class/logger.cpp
@ -354,7 +355,8 @@ set(SCRIPT_ADDON_SRC
src/scriptaddon/scriptjson.h
src/scriptaddon/scriptjson.cpp
src/scriptaddon/scriptfile.cpp
src/scriptaddon/scriptfile.h)
src/scriptaddon/scriptfile.h
src/scriptaddon/aspromise.hpp)
# localization support
file(

View File

@ -27,6 +27,7 @@
<p align="center">
<img alt="作者" src="authorband.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 align="center">
@ -115,8 +116,7 @@
1. 本软件源代码不得私自应用于闭源商业用途除非你完整开源GPL协议的要求。如果要将软件仓库的代码商用闭源必须找我购买商业授权签订合同价格私聊非诚勿扰。
2. 本软件是用我的业余时间编写,不能及时修复 Bug 或者提供技术支持,请见谅。
3. 本人非计算机专业,编写程序难免有 Bug ,欢迎提交 PR 。
4. 无论在什么时候有疑问,请详细阅读 Wiki 。
3. 无论在什么时候有疑问,请详细阅读 Wiki 。
### 打包者必读
@ -128,7 +128,7 @@
4. 包内最好含有主程序的校验和,但不强求。
5. 无论是哪种打包形式,都一定要声明其开源协议和自由属性,不得隐藏而借机收费。虽然收费我并不反感,下载服务器还是需要资金维护的,但借着信息差骗人收费,这是我极其深恶痛绝的。
6. 无论以任何为目的,如未获得我作者的授权,不得修改任意程序内指向的网络链接和软件关于信息,比如赞助和关于软件部分的内容等。
7. 不得在安装程序内插入任何含有商业推广的插件,并且严格遵守第二条内容
7. 不得在安装程序内插入任何含有商业推广的插件。
### issue 前必读
@ -174,7 +174,7 @@
## WIKI
&emsp;&emsp;如果想学习羽云十六进制编辑器的使用以及如何开发该软件的插件,请到 [该链接](https://github.com/Wing-summer/WingHexExplorer2/wiki/%E7%AE%80%E4%BB%8B) ,同时也欢迎大家指出 Wiki 的错误以及贡献优质内容。
&emsp;&emsp;如果想学习羽云十六进制编辑器的使用以及如何开发该软件的插件,请到 [该链接](https://wing-summer.github.io/WingHexExplorer2/docs/zh/credits.html) ,同时也欢迎大家指出 Wiki 的错误以及贡献优质内容。
## 插件库

View File

@ -27,6 +27,7 @@
<p align="center">
<img alt="Author" src="authorband.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 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.
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.
4. Whenever you have questions, please read the Wiki in detail.
3. Whenever you have questions, please read the Wiki in detail.
### 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.
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.
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
@ -175,7 +175,7 @@ Of course, there are other repositories as mirror for Chinese users (which will
## WIKI
&emsp;&emsp;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.
&emsp;&emsp;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

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -55,6 +55,7 @@
<item>typedef</item>
<item>while</item>
<item>xor</item>
<item>co_await</item>
</list>
<list name="types">
<item>void</item>
@ -80,7 +81,7 @@
<HlCOct attribute="Octal" context="#stay"/>
<HlCHex attribute="Hex" context="#stay"/>
<Int attribute="Decimal" context="Int Suffixes"/>
<HlCChar attribute="Char" context="#stay"/>
<DetectChar attribute="Char" context="String" char="&apos;"/>
<StringDetect attribute="Text Block" context="TextBlock" String="&quot;&quot;&quot;"/>
<DetectChar attribute="String" context="String" char="&quot;"/>
<Detect2Chars attribute="Comment" context="MatchComment" char="/" char1="/" lookAhead="true"/>
@ -105,6 +106,7 @@
<context attribute="String" lineEndContext="#pop" name="String">
<DetectChar context="StringEscapedChar" char="\" lookAhead="1"/>
<DetectChar attribute="String" context="#pop" char="&quot;"/>
<DetectChar attribute="Char" context="#pop" char="&apos;"/>
</context>
<context attribute="String" lineEndContext="#pop" name="StringEscapedChar">
<HlCStringChar attribute="String Char" context="#pop"/>
@ -144,7 +146,7 @@
<itemData name="Octal" defStyleNum="dsBaseN"/>
<itemData name="Hex" defStyleNum="dsBaseN"/>
<itemData name="Float" defStyleNum="dsFloat"/>
<itemData name="Char" defStyleNum="dsChar"/>
<itemData name="Char" defStyleNum="dsString"/>
<itemData name="String" defStyleNum="dsString"/>
<itemData name="Text Block" defStyleNum="dsString"/>
<itemData name="String Char" defStyleNum="dsSpecialChar"/>

View File

@ -25,43 +25,49 @@
AngelObjString::AngelObjString() {}
QString AngelObjString::stringToString(void *obj, asDebugger *dbg) {
QString AngelObjString::stringToString(void *obj, asDebugger *dbg, asUINT tag) {
Q_UNUSED(dbg);
// We know the received object is a string
QString val = *reinterpret_cast<QString *>(obj);
if (tag == 1) {
val.prepend('"').append('"');
}
return val;
}
QString AngelObjString::arrayToString(void *obj, asDebugger *dbg) {
QString AngelObjString::arrayToString(void *obj, asDebugger *dbg, asUINT tag) {
CScriptArray *arr = reinterpret_cast<CScriptArray *>(obj);
QString str;
QTextStream s(&str);
s << tr("(len=") << arr->GetSize() << QStringLiteral(")");
s << QStringLiteral(" [");
s << QStringLiteral("{");
for (asUINT n = 0; n < arr->GetSize(); n++) {
s << dbg->toString(arr->At(n), arr->GetElementTypeId(),
arr->GetArrayObjectType()->GetEngine());
arr->GetArrayObjectType()->GetEngine(), 1);
if (n < arr->GetSize() - 1)
s << ", ";
}
s << QStringLiteral("]");
s << QStringLiteral("}");
return str;
}
QString AngelObjString::charToString(void *obj, asDebugger *dbg) {
QString AngelObjString::charToString(void *obj, asDebugger *dbg, asUINT tag) {
Q_UNUSED(dbg);
// We know the received object is a char
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);
QString str;
@ -69,11 +75,11 @@ QString AngelObjString::dictionaryToString(void *obj, asDebugger *dbg) {
auto engine = dic->GetEngine();
s << " {";
s << QStringLiteral("{");
asUINT n = 0;
for (CScriptDictionary::CIterator it = dic->begin(); it != dic->end();
it++, n++) {
s << "[" << it.GetKey() << "] = ";
s << QStringLiteral("[") << it.GetKey() << QStringLiteral("] = ");
// Get the type and address of the value
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
// default, but in an environment where there multiple engines this
// 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)
s << ", ";
s << QStringLiteral(", ");
}
s << "}";
s << QStringLiteral("}");
return str;
}
QString AngelObjString::colorToString(void *obj, asDebugger *dbg) {
QString AngelObjString::colorToString(void *obj, asDebugger *dbg, asUINT tag) {
Q_UNUSED(dbg);
auto color = reinterpret_cast<QColor *>(obj);

View File

@ -28,16 +28,15 @@ class AngelObjString : public QObject {
Q_OBJECT
public:
// for debugger use
static QString stringToString(void *obj, asDebugger *dbg);
static QString stringToString(void *obj, asDebugger *dbg, asUINT tag);
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:
// ==================================================

View File

@ -143,8 +143,9 @@ AppManager::AppManager(int &argc, char *argv[])
}
AppManager::~AppManager() {
InspectQtLogHelper::instance().destory();
ClangFormatManager::instance().save();
ScriptMachine::instance().deleteLater();
InspectQtLogHelper::instance().destory();
CommandHistoryManager::save(QConsoleWidget::history().strings_);
delete _w;
@ -155,7 +156,7 @@ AppManager *AppManager::instance() { return _instance; }
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) {
EditorView *editor = nullptr;

View File

@ -38,7 +38,7 @@ public:
QApplication::tr("WingCloudStudio");
}
uint currentMSecsSinceEpoch();
quint64 currentMSecsSinceEpoch();
public slots:
void openFile(const QString &file, bool autoDetect = true);

View File

@ -21,7 +21,9 @@
#include "class/aspreprocesser.h"
#include "class/qascodeparser.h"
#include "class/scriptmachine.h"
#include "control/scripteditor.h"
#include "model/codecompletionmodel.h"
#include "utilities.h"
#include "wingcodeedit.h"
#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, LEFT_PARE_TRIGGER, ("("))
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, SEMI_COLON_TRIGGER, (";"))
Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, SHARP_TRIGGER, ("#"))
AsCompletion::AsCompletion(WingCodeEdit *p)
: WingCompleter(p), parser(ScriptMachine::instance().engine()) {
@ -47,7 +50,9 @@ AsCompletion::AsCompletion(WingCodeEdit *p)
// unleash the power of call tips
*LEFT_PARE_TRIGGER,
// clear the tips
*SEMI_COLON_TRIGGER});
*SEMI_COLON_TRIGGER,
// for marcos
*SHARP_TRIGGER});
setTriggerAmount(3);
connect(this, QOverload<const QModelIndex &>::of(&AsCompletion::activated),
@ -99,6 +104,15 @@ void AsCompletion::applyEmptyNsNode(QList<CodeInfoTip> &nodes,
tip.name = p.nameSpace;
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);
@ -112,7 +126,7 @@ void AsCompletion::applyClassNodes(QList<CodeInfoTip> &nodes) {
for (auto &item : n) {
if (item.type == CodeInfoTip::Type::Class) {
for (auto &c : item.children) {
if (c.type == CodeInfoTip::Type::Function) {
if (c.type == CodeInfoTip::Type::ClsFunction) {
if (!c.addinfo.contains(CodeInfoTip::RetType)) {
continue;
}
@ -129,7 +143,7 @@ void AsCompletion::applyClassNodes(QList<CodeInfoTip> &nodes) {
int AsCompletion::includeCallBack(const QString &include, bool quotedInclude,
const QString &from, AsPreprocesser *builder,
void *userParam) {
Q_UNUSED(userParam);
auto p = reinterpret_cast<AsCompletion *>(userParam);
QFileInfo info(include);
bool isAbsolute = info.isAbsolute();
@ -139,7 +153,9 @@ int AsCompletion::includeCallBack(const QString &include, bool quotedInclude,
if (isAbsolute) {
inc = include;
} 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);
}
} else {
@ -165,15 +181,59 @@ int AsCompletion::includeCallBack(const QString &include, bool quotedInclude,
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({}); }
QString AsCompletion::wordSeperators() const {
static QString eow(QStringLiteral("~!@#$%^&*()_+{}|\"<>?,/;'[]\\-="));
static QString eow(QStringLiteral("~!@$%^&*()_+{}|\"<>?,/;'[]\\-="));
return eow;
}
bool AsCompletion::processTrigger(const QString &trigger,
const QString &content) {
QList<CodeInfoTip> nodes;
if (trigger == *SHARP_TRIGGER) {
setModel(new CodeCompletionModel(parseMarcos(), this));
setCompletionPrefix({});
return true;
}
if (content.isEmpty()) {
return false;
}
@ -186,8 +246,6 @@ bool AsCompletion::processTrigger(const QString &trigger,
auto len = content.length();
auto code = content.toUtf8();
QList<CodeInfoTip> nodes;
if (!trigger.isEmpty() && trigger != *DOT_TRIGGER) {
clearFunctionTip();
}
@ -257,6 +315,7 @@ bool AsCompletion::processTrigger(const QString &trigger,
auto etoken = tokens.back();
// it can not be any trigger, so take the last as prefix
QString prefix = etoken.content;
if (etoken.type == asTC_VALUE || etoken.type == asTC_COMMENT ||
etoken.type == asTC_UNKNOWN) {
popup()->hide();
@ -268,6 +327,15 @@ bool AsCompletion::processTrigger(const QString &trigger,
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();
// 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));
setCompletionPrefix(prefix);
return true;
} else if (etoken.content == QByteArrayLiteral(")")) {
// ignore
} else {
applyEmptyNsNode(nodes, docNodes);
}
@ -302,46 +372,60 @@ bool AsCompletion::processTrigger(const QString &trigger,
}
if (trigger == *DOT_TRIGGER) {
// member type guessing ? basic match is enough. (>n<)
auto isBasicType = [](const QString &type) {
static QStringList basicType{
"int", "int8", "int16", "int32", "int64",
"uint", "uint8", "uint16", "uint32", "uint64",
"float", "double", "byte"};
if (etoken.type == asTC_IDENTIFIER) {
// member type guessing ? basic match is enough. (>n<)
auto isBasicType = [](const QByteArray &type) {
static QByteArrayList basicType{
"int", "int8", "int16", "int32", "int64",
"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
for (auto &item : docNodes) {
if (item.type == CodeInfoTip::Type::Class) {
auto name = item.nameSpace;
if (name.isEmpty()) {
name = item.name;
// filter the type we can use to auto-complete in docNodes
for (auto &item : docNodes) {
if (item.type == CodeInfoTip::Type::Class) {
auto name = item.nameSpace;
if (name.isEmpty()) {
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();
auto ns = getNamespace(tokens);
for (auto &item : docNodes) {
if (etoken.content == item.name && ns == item.nameSpace) {
auto retType = item.addinfo.value(CodeInfoTip::RetType);
tokens.removeLast();
auto ns = getNamespace(tokens);
for (auto &item : docNodes) {
if (etoken.content == item.name && ns == item.nameSpace) {
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.
// PRs will be welcomed !!!
if (isBasicType(retType)) {
popup()->hide();
return false;
// auto type inference is not supported.
// PRs will be welcomed !!!
if (isBasicType(type)) {
popup()->hide();
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
AsPreprocesser prepc(engine);
prepc.setIsCodeCompleteMode(true);
prepc.setIncludeCallback(&AsCompletion::includeCallBack, this);
auto r = prepc.loadSectionFromMemory(QStringLiteral("ASCOMPLETION"),
@ -424,17 +509,61 @@ QList<CodeInfoTip> AsCompletion::parseDocument() {
auto data = prepc.scriptData();
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) {
qsizetype offset = -1;
QList<CodeInfoTip> dd;
if (d.section == QStringLiteral("ASCOMPLETION")) {
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;
}
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,
const QByteArray &code) {
QList<CodeInfoTip> ret;

View File

@ -42,6 +42,8 @@ protected:
virtual QList<CodeInfoTip> parseDocument();
virtual QList<CodeInfoTip> parseMarcos();
QList<CodeInfoTip> parseScriptData(qsizetype offset,
const QByteArray &code);
@ -62,6 +64,21 @@ private:
const QString &from, AsPreprocesser *builder,
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:
ASDataBase parser;
};

View File

@ -87,3 +87,22 @@ QList<CodeInfoTip> AsConsoleCompletion::parseDocument() {
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;
}

View File

@ -29,7 +29,8 @@ public:
virtual ~AsConsoleCompletion() = default;
protected:
virtual QList<CodeInfoTip> parseDocument();
virtual QList<CodeInfoTip> parseDocument() override;
virtual QList<CodeInfoTip> parseMarcos() override;
private:
ScriptingConsole *_console;

View File

@ -40,7 +40,8 @@ ASDataBase::ASDataBase(asIScriptEngine *engine) {
"function", "interface", "shared", "this", "explicit", "override",
"namespace", "get", "set", "super", "mixin", "false",
"true", "null", "typename", "return", "typedef", "funcdef",
"from", "import", "not", "xor", "or", "is"};
"from", "import", "not", "xor", "or", "is",
"co_await"};
for (auto &k : kws) {
CodeInfoTip t;
t.type = CodeInfoTip::Type::KeyWord;
@ -325,6 +326,42 @@ void ASDataBase::addClassCompletion(asIScriptEngine *engine) {
obj->Release();
_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 {};
}
const QHash<QString, QList<CodeInfoTip>> &ASDataBase::classNodes() const {
return _classNodes;
}
const QList<CodeInfoTip> &ASDataBase::keywordNodes() const {
return _keywordNode;
}

View File

@ -43,6 +43,7 @@ private:
public:
const QHash<QString, QList<CodeInfoTip>> &headerNodes() const;
const QHash<QString, QList<CodeInfoTip>> &enumsNodes() const;
const QHash<QString, QList<CodeInfoTip>> &classNodes() const;
const QList<CodeInfoTip> &keywordNodes() const;
@ -57,6 +58,7 @@ private:
// <namespace, content>
QHash<QString, QList<CodeInfoTip>> _headerNodes;
QHash<QString, QList<CodeInfoTip>> _enumsNodes;
QHash<QString, QList<CodeInfoTip>> _classNodes;
QList<CodeInfoTip> _keywordNode;
};

View File

@ -16,6 +16,7 @@
*/
#include "asdebugger.h"
#include "class/appmanager.h"
#include "define.h"
#include <QApplication>
@ -78,6 +79,44 @@ void asDebugger::lineCallback(asIScriptContext *ctx) {
if (ctx == nullptr)
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>(
ctx->GetUserData(AsUserDataType::UserData_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());
Q_ASSERT(dbgContext);
@ -368,7 +401,7 @@ bool asDebugger::checkBreakPoint(asIScriptContext *ctx) {
}
QString asDebugger::toString(void *value, asUINT typeId,
asIScriptEngine *engine) {
asIScriptEngine *engine, asUINT tag) {
if (value == nullptr)
return QStringLiteral("<null>");
@ -445,7 +478,8 @@ QString asDebugger::toString(void *value, asUINT typeId,
s << name /*type->GetPropertyDeclaration(n)*/
<< QStringLiteral(" = ")
<< 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
// this type
s << it.value()(value, this);
s << it.value()(value, this, tag);
} else {
// Unknown type: type + address
s << type->GetName() << '(' << value << ')';

View File

@ -84,7 +84,7 @@ public:
virtual ~asDebugger();
// 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,
ToStringCallback callback);
@ -108,8 +108,9 @@ public:
// Line callback invoked by context
void lineCallback(asIScriptContext *ctx);
// tag = 1 : string should be printed with quotes
QString toString(void *value, asUINT typeId,
asIScriptEngine *engine = nullptr);
asIScriptEngine *engine = nullptr, asUINT tag = 0);
GCStatistic gcStatistics();

View File

@ -16,16 +16,29 @@
*/
#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 <QFileInfo>
#include <QLibraryInfo>
#include <QMetaEnum>
#include <QStack>
#include <QVersionNumber>
Q_GLOBAL_STATIC_WITH_ARGS(
QStringList, DEFAULT_MARCO,
({"__AS_ARRAY__", "__AS_ANY__", "__AS_GRID__", "__AS_HANDLE__",
QByteArrayList, DEFAULT_MARCO,
({// special marcos
"__LINE__", "__SECTION__", "__SECTION_BASE__",
// functions
"__AS_ARRAY__", "__AS_ANY__", "__AS_GRID__", "__AS_HANDLE__",
"__AS_MATH__", "__AS_WEAKREF__", "__AS_COROUTINE__", "__WING_FILE__",
"__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) {
Q_ASSERT(engine);
@ -36,7 +49,58 @@ AsPreprocesser::AsPreprocesser(asIScriptEngine *engine) : engine(engine) {
pragmaCallback = 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(); }
@ -85,10 +149,13 @@ void AsPreprocesser::setPragmaCallback(PRAGMACALLBACK_t callback,
pragmaParam = userParam;
}
void AsPreprocesser::defineWord(const QString &word) {
if (!definedWords.contains(word)) {
definedWords.append(word);
bool AsPreprocesser::defineWord(const QString &word, const QByteArray &value) {
// try to modify system marco is not allowed
if (DEFAULT_MARCO->contains(word)) {
return false;
}
definedWords.insert(word, value);
return true;
}
unsigned int AsPreprocesser::sectionCount() const {
@ -106,6 +173,9 @@ void AsPreprocesser::clearAll() { includedScripts.clear(); }
int AsPreprocesser::processScriptSection(const QByteArray &script,
const QString &sectionname) {
// #define DBG_CODEP // debug macro for pre-process
QVector<QPair<QString, bool>> includes;
QByteArray modifiedScript = script;
@ -114,8 +184,11 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
// shouldn't be compiled
QByteArray::size_type pos = 0;
int nested = 0;
QStack<std::optional<bool>> m_condtionStack;
while (pos < modifiedScript.size()) {
auto SECTION = sectionname.toUtf8();
asUINT len = 0;
asETokenClass t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
@ -128,46 +201,399 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
modifiedScript.size() - pos, &len);
Q_UNUSED(t);
QByteArray token = modifiedScript.mid(pos, len);
QByteArray token = modifiedScript.sliced(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,
modifiedScript.size() - pos, &len);
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;
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
}
if (t == asTC_IDENTIFIER) {
QByteArray word = modifiedScript.mid(pos, len);
if (isIfDef || isIfnDef) {
if (_isCodeCompleteMode) {
auto pos = modifiedScript.indexOf('\n', start);
if (pos < 0) {
overwriteCode(modifiedScript, start,
modifiedScript.size() - start - 1);
} else {
overwriteCode(modifiedScript, start, pos - start);
}
continue;
}
if (t == asTC_IDENTIFIER) {
QByteArray word = modifiedScript.sliced(pos, len);
// Overwrite the #if directive with space characters to
// avoid compiler error
pos += len;
overwriteCode(modifiedScript, start, pos - start);
// Overwrite the directive with space characters to
// avoid compiler error
pos += len;
overwriteCode(modifiedScript, start, pos - start);
// Has this identifier been defined by the application or
// not?
if (!definedWords.contains(word)) {
// Exclude all the code until and including the #endif
pos = excludeCode(modifiedScript, pos);
// Has this identifier been defined by the application
// or not?
if ((isIfDef && !definedWords.contains(word)) ||
(isIfnDef && definedWords.contains(word))) {
// 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 {
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") {
// Only remove the #endif if there was a matching #if
if (nested > 0) {
} else if (token == "else") {
if (_isCodeCompleteMode) {
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;
}
}
#ifdef DBG_CODEP
qDebug().noquote() << modifiedScript;
#endif
// Then check for pre-processor directives
pos = 0;
while (pos >= 0 && pos < modifiedScript.size()) {
@ -178,7 +604,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
pos += len;
continue;
}
QString token = modifiedScript.mid(pos, len);
QString token = modifiedScript.sliced(pos, len);
// Skip possible decorators before class and interface declarations
if (token == "shared" || token == "abstract" || token == "mixin" ||
@ -194,7 +620,7 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
t = engine->ParseToken(modifiedScript.data() + pos,
modifiedScript.size() - pos, &len);
if (t == asTC_IDENTIFIER) {
token = modifiedScript.mid(pos, len);
token = modifiedScript.sliced(pos, len);
if (token == "include") {
pos += len;
t = engine->ParseToken(modifiedScript.data() + pos,
@ -211,22 +637,22 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
modifiedScript[pos] == '\'')) {
// Get the include file
QString includefile =
modifiedScript.mid(pos + 1, len - 2);
modifiedScript.sliced(pos + 1, len - 2);
pos += len;
// Make sure the includeFile doesn't contain any
// line breaks
auto p = includefile.indexOf('\n');
if (p >= 0) {
// TODO: Show the correct line number for the
// error
auto str =
QObject::tr("Invalid file name for #include; "
"it contains a line-break: ") +
QStringLiteral("'") + includefile.left(p) +
QStringLiteral("'");
engine->WriteMessage(sectionname.toUtf8(), 0, 0,
asMSGTYPE_ERROR, str.toUtf8());
engine->WriteMessage(
sectionname.toUtf8(),
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
} else {
// Store it for later processing
includes.append({includefile, true});
@ -235,9 +661,8 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
// characters to avoid compiler error
overwriteCode(modifiedScript, start, pos - start);
}
}
if (t == asTC_KEYWORD && modifiedScript[pos] == '<') {
} else if (t == asTC_KEYWORD &&
modifiedScript[pos] == '<') {
pos += len;
// find the next '>'
@ -255,7 +680,8 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
if (found) {
QString includefile =
modifiedScript.mid(pos, rpos - pos).trimmed();
modifiedScript.sliced(pos, rpos - pos)
.trimmed();
pos = rpos + 1;
@ -264,17 +690,16 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
auto p = includefile.indexOf('\n');
auto ws = includefile.indexOf(' ');
if (!includefile.isEmpty() && p >= 0 && ws >= 0) {
// TODO: Show the correct line number for
// the error
auto str =
QObject::tr(
"Invalid file name for #include; "
"it contains a line-break: ") +
QStringLiteral("'") + includefile.left(p) +
QStringLiteral("'");
engine->WriteMessage(sectionname.toUtf8(), 0, 0,
asMSGTYPE_ERROR,
str.toUtf8());
engine->WriteMessage(
sectionname.toUtf8(),
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR, str.toUtf8());
} else {
// Store it for later processing
includes.append({includefile, false});
@ -303,22 +728,26 @@ int AsPreprocesser::processScriptSection(const QByteArray &script,
// Call the pragma callback
auto pragmaText =
modifiedScript.mid(start + 7, pos - start - 7);
modifiedScript.sliced(start + 7, pos - start - 7);
// Overwrite the pragma directive with space characters
// to avoid compiler error
overwriteCode(modifiedScript, start, pos - start);
int r = pragmaCallback
? pragmaCallback(pragmaText, this, sectionname,
pragmaParam)
: -1;
if (r < 0) {
// TODO: Report the correct line number
engine->WriteMessage(
sectionname.toUtf8(), 0, 0, asMSGTYPE_ERROR,
QObject::tr("Invalid #pragma directive").toUtf8());
return r;
if (!_isCodeCompleteMode) {
int r = pragmaCallback
? pragmaCallback(pragmaText, this,
sectionname, pragmaParam)
: -1;
if (r < 0) {
engine->WriteMessage(
sectionname.toUtf8(),
getLineCount(modifiedScript, pos), 1,
asMSGTYPE_ERROR,
QObject::tr("Invalid #pragma directive")
.toUtf8());
return r;
}
}
}
}
@ -386,9 +815,6 @@ int AsPreprocesser::loadScriptSection(const QString &filename) {
QFileInfo(filename).absoluteFilePath() + QStringLiteral("'");
engine->WriteMessage(filename.toUtf8(), 0, 0, asMSGTYPE_ERROR,
msg.toUtf8());
// TODO: Write the file where this one was included from
return -1;
}
@ -412,7 +838,7 @@ bool AsPreprocesser::includeIfNotAlreadyIncluded(const QString &filename) {
return true;
}
int AsPreprocesser::skipStatement(const QByteArray &modifiedScript, int pos) {
int AsPreprocesser::skipStatement(QByteArray &modifiedScript, int pos) {
asUINT len = 0;
// Skip until ; or { whichever comes first
@ -438,39 +864,74 @@ int AsPreprocesser::skipStatement(const QByteArray &modifiedScript, int pos) {
else if (modifiedScript[pos] == '}')
level--;
}
pos += len;
}
} else
} else {
pos += 1;
}
return pos;
}
int AsPreprocesser::excludeCode(QByteArray &modifiedScript, int pos) {
int AsPreprocesser::excludeIfCode(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] == '#') {
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.mid(pos, len);
overwriteCode(modifiedScript, pos, len);
QString token = modifiedScript.sliced(pos, len);
if (token == "if") {
if (token == "if" || token == "ifdef" || token == "ifndef") {
nested++;
} else if (token == "endif") {
} else if (token == "else") {
if (nested-- == 0) {
pos += len;
pos = sharpPos;
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') {
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 &currentSection) {
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 &section,
const QByteArray &code) {
ScriptData data;

View File

@ -35,6 +35,7 @@
#pragma warning(disable : 4786)
#endif
#include <QEventLoop>
#include <QMap>
#include <QSet>
#include <QVector>
@ -62,6 +63,16 @@ typedef int (*PRAGMACALLBACK_t)(const QByteArray &pragmaText,
// Helper class for loading and pre-processing script files to
// 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 {
public:
explicit AsPreprocesser(asIScriptEngine *engine);
@ -81,6 +92,8 @@ public:
int loadSectionFromFile(const QString &filename);
int loadSectionFromMemory(const QString &section, const QByteArray &code);
void addScriptSection(const QString &section, const QByteArray &code);
QList<ScriptData> scriptData() const;
// Returns the engine
@ -93,25 +106,42 @@ public:
void setPragmaCallback(PRAGMACALLBACK_t callback, void *userParam);
// 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
unsigned int sectionCount() 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:
void clearAll();
void addScriptSection(const QString &section, const QByteArray &code);
int processScriptSection(const QByteArray &script,
const QString &sectionname);
int loadScriptSection(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);
int getLineCount(const QByteArray &modifiedScript, int pos) const;
bool endLinePassFailed(const QByteArray &modifiedScript, int pos);
QByteArray processIfExpression(const QByteArray &codes, int currentLine,
const QByteArray &currentSection);
QByteArray findReplaceResult(const QByteArray &v);
asIScriptEngine *engine;
QList<ScriptData> modifiedScripts;
@ -124,7 +154,10 @@ protected:
QStringList includedScripts;
QStringList definedWords;
QHash<QString, QByteArray> definedWords;
private:
bool _isCodeCompleteMode = false;
};
#endif // ASPREPROCESSER_H

View File

@ -1,6 +1,8 @@
#include "inspectqtloghelper.h"
#include "utilities.h"
#include <QMenu>
#include <iostream>
#define INFOLOG(msg) "<font color=\"green\">" + msg + "</font>"

View File

@ -3126,8 +3126,8 @@ void PluginSystem::applyFunctionTables(IWingPluginBase *plg,
plg->setProperty("__CALL_POINTER__", quintptr(this));
}
QString PluginSystem::type2AngelScriptString(IWingPlugin::MetaType type,
bool isArg, bool noModifier) {
QString PluginSystem::type2AngelScriptString(uint type, bool isArg,
bool noModifier) {
auto isArray = !!(type & WingHex::IWingPlugin::Array);
auto isList = !!(type & WingHex::IWingPlugin::List);
auto isContainer = isArray || isList;

View File

@ -175,8 +175,8 @@ public:
static QString getPUID(IWingPluginBase *p);
static QString type2AngelScriptString(IWingPlugin::MetaType type,
bool isArg, bool noModifier = false);
static QString type2AngelScriptString(uint type, bool isArg,
bool noModifier = false);
private:
void loadExtPlugin();

View File

@ -53,6 +53,15 @@ QAsCodeParser::parse(const QByteArray &codes) {
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>
QAsCodeParser::parseAndIntell(qsizetype offset, const QByteArray &codes) {
return parseIntell(offset, parse(codes));
@ -572,7 +581,7 @@ QAsCodeParser::parseStatementBlock(const QByteArrayList &ns,
rewindTo(&t1);
if (isVarDecl()) {
syms.top().append(parseDeclaration(ns));
syms.top().append(parseDeclaration(end, ns));
} else {
_isSyntaxError = true;
}
@ -1069,8 +1078,8 @@ void QAsCodeParser::superficiallyParseVarInit() {
}
QList<QAsCodeParser::Symbol>
QAsCodeParser::parseDeclaration(const QByteArrayList &ns, bool isClassProp,
bool isGlobalVar) {
QAsCodeParser::parseDeclaration(qsizetype end, const QByteArrayList &ns,
bool isClassProp, bool isGlobalVar) {
QList<QAsCodeParser::Symbol> ret;
Symbol sym;
@ -1097,6 +1106,10 @@ QAsCodeParser::parseDeclaration(const QByteArrayList &ns, bool isClassProp,
for (;;) {
// Parse identifier
auto id = parseIdentifier();
if (end >= 0 && id.pos > end) {
return ret;
}
if (_isSyntaxError)
return ret;
@ -1437,8 +1450,9 @@ void QAsCodeParser::parseNamespace() {
QList<QAsCodeParser::Symbol>
QAsCodeParser::parseGlobalVarDecls(const QByteArrayList &ns,
const QByteArray &code) {
reset();
_code = code;
return parseDeclaration(ns, false, true);
return parseDeclaration(-1, ns, false, true);
}
QAsCodeParser::CodeSegment QAsCodeParser::parseFunctionMethod(Visiblity &vis) {
@ -1625,7 +1639,7 @@ QAsCodeParser::parseClassContent(qsizetype offset, const QByteArrayList &ns,
auto vp = parseVirtualPropertyDecl(true, false);
syms.append(vp);
} else if (isVarDecl()) {
auto decl = parseDeclaration(ns, true);
auto decl = parseDeclaration(offset, ns, true);
syms.append(decl);
} else if (t.type == ttEndStatement)
// Skip empty declarations
@ -2303,66 +2317,6 @@ bool QAsCodeParser::findTokenAfterType(
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) {
Q_UNUSED(t);
// TODO: don't check

View File

@ -106,12 +106,8 @@ public:
QList<Symbol> parseAndIntell(qsizetype offset, const QByteArray &codes);
public:
// utilities
static bool isConstant(int tokenType);
static bool isOperator(int tokenType);
static bool isPreOperator(int tokenType);
static bool isPostOperator(int tokenType);
static bool isAssignOperator(int tokenType);
static eTokenType getToken(asIScriptEngine *engine, const char *string,
size_t stringLength, size_t *tokenLength);
private:
QList<QAsCodeParser::CodeSegment> parseScript(bool inBlock);
@ -161,7 +157,7 @@ private:
QByteArrayList parseOptionalScope();
QList<Symbol> parseDeclaration(const QByteArrayList &ns,
QList<Symbol> parseDeclaration(qsizetype end, const QByteArrayList &ns,
bool isClassProp = false,
bool isGlobalVar = false);

View File

@ -31,19 +31,92 @@
#include "angelobjstring.h"
#include "class/appmanager.h"
#include "class/asbuilder.h"
#include "class/logger.h"
#include "class/pluginsystem.h"
#include "class/qascodeparser.h"
#include "class/settingmanager.h"
#include "define.h"
#include "scriptaddon/aspromise.hpp"
#include "scriptaddon/scriptcolor.h"
#include "scriptaddon/scriptfile.h"
#include "scriptaddon/scriptjson.h"
#include "scriptaddon/scriptqstring.h"
#include "scriptaddon/scriptregex.h"
#include <QClipboard>
#include <QMimeData>
#include <QProcess>
#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() {
if (isInited()) {
@ -72,6 +145,11 @@ bool ScriptMachine::isRunning(ConsoleMode mode) const {
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() {
if (_engine == nullptr) {
return false;
@ -138,10 +216,20 @@ bool ScriptMachine::configureEngine() {
_rtypes[RegisteredType::tColor] =
q_check_ptr(_engine->GetTypeInfoByName("color"));
_engine->SetDefaultAccessMask(0x1);
// Register a couple of extra functions for the scripts
r = _engine->RegisterGlobalFunction("void print(? &in obj)",
asMETHOD(ScriptMachine, print),
asCALL_THISCALL_ASGLOBAL, this);
r = _engine->RegisterGlobalFunction(
"void print(const ? &in obj, const ? &in = null," INS_8 ")",
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);
if (r < 0) {
return false;
@ -155,6 +243,22 @@ bool ScriptMachine::configureEngine() {
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(
"int exec(string &out output, const string &in exe, "
"const string &in params = \"\", int timeout = 3000)",
@ -198,7 +302,8 @@ bool ScriptMachine::configureEngine() {
PluginSystem::instance().angelApi()->installAPI(this);
// create module for Console
_engine->GetModule("WINGCONSOLE", asGM_ALWAYS_CREATE);
auto mod = _engine->GetModule("WINGCONSOLE", asGM_ALWAYS_CREATE);
mod->SetAccessMask(0x1);
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();
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;
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 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
auto mod = createModule(mode);
// script-running is not allowed in interactive mode
@ -326,13 +487,11 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
return false;
}
QScopeGuard guard([mod, mode]() {
if (mode != ConsoleMode::Interactive) {
// 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();
}
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();
});
asPWORD isDbg = 0;
@ -353,12 +512,17 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
builder.setPragmaCallback(&ScriptMachine::pragmaCallback, this);
builder.setIncludeCallback(&ScriptMachine::includeCallback, this);
_curMsgMode = mode;
auto r = builder.loadSectionFromFile(script.toUtf8());
if (r < 0) {
MessageInfo info;
info.mode = mode;
info.message = tr("Script failed to pre-processed");
info.type = MessageType::Error;
outputMessage(info);
return false;
}
_curMode = mode;
r = builder.build(mod);
if (r < 0) {
MessageInfo info;
@ -414,9 +578,25 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
_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),
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),
AsUserDataType::UserData_isDbg);
@ -492,9 +672,82 @@ bool ScriptMachine::executeScript(ConsoleMode mode, const QString &script,
_debugger->clearBreakPoint();
emit onDebugFinished();
}
if (retCode) {
*retCode = r;
}
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() {
if (_debugger->getEngine()) {
_debugger->runDebugAction(asDebugger::ABORT);
@ -534,7 +787,7 @@ void ScriptMachine::messageCallback(const asSMessageInfo *msg, void *param) {
return;
}
MessageInfo info;
info.mode = ins->_curMode;
info.mode = ins->_curMsgMode;
info.row = msg->row;
info.col = msg->col;
info.section = msg->section;
@ -573,29 +826,43 @@ asIScriptModule *ScriptMachine::createModule(ConsoleMode mode) {
if (isModuleExists(mode)) {
return nullptr;
}
asIScriptModule *mod = nullptr;
switch (mode) {
case Interactive:
return nullptr;
mod = nullptr;
case Scripting:
return _engine->GetModule("WINGSCRIPT", asGM_ALWAYS_CREATE);
mod = _engine->GetModule("WINGSCRIPT", asGM_ALWAYS_CREATE);
mod->SetAccessMask(0x1);
break;
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 *mod = nullptr;
switch (mode) {
case Interactive:
return _engine->GetModule("WINGCONSOLE", asGM_ONLY_IF_EXISTS);
mod = _engine->GetModule("WINGCONSOLE", asGM_ONLY_IF_EXISTS);
mod->SetAccessMask(0x1);
break;
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:
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) {
@ -643,19 +910,21 @@ void ScriptMachine::returnContextCallback(asIScriptEngine *engine,
asIScriptContext *ctx, void *param) {
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
// value) This must be done before making the context available for re-use,
// as the clean up may trigger other script executions, e.g. if a destructor
// needs to call a function.
ctx->Unprepare();
// Unprepare the context to free any objects it may still hold (e.g.
// return value) This must be done before making the context available
// for re-use, as the clean up may trigger other script executions, e.g.
// if a destructor needs to call a function.
ctx->Unprepare();
auto p = reinterpret_cast<ScriptMachine *>(param);
Q_ASSERT(p);
auto p = reinterpret_cast<ScriptMachine *>(param);
Q_ASSERT(p);
// Place the context into the pool for when it will be needed again
p->_ctxPool.push_back(ctx);
// Place the context into the pool for when it will be needed again
p->_ctxPool.push_back(ctx);
}
}
int ScriptMachine::pragmaCallback(const QByteArray &pragmaText,
@ -1728,6 +1997,8 @@ void ScriptMachine::translation() {
}
void ScriptMachine::registerEngineAddon(asIScriptEngine *engine) {
// all modules can access
engine->SetDefaultAccessMask(0x3);
RegisterScriptArray(engine, true);
RegisterQString(engine);
RegisterScriptRegex(engine);
@ -1742,9 +2013,13 @@ void ScriptMachine::registerEngineAddon(asIScriptEngine *engine) {
RegisterScriptHandle(engine);
RegisterColor(engine);
RegisterQJson(engine);
engine->SetDefaultAccessMask(0x1);
RegisterScriptFile(engine);
registerExceptionRoutines(engine);
registerEngineAssert(engine);
registerEngineClipboard(engine);
AsDirectPromise::Register(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,
const RegCallBacks &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) {
asIScriptContext *ctx = asGetActiveContext();
if (ctx) {
@ -1832,6 +2201,15 @@ asIScriptEngine *ScriptMachine::engine() const { return _engine; }
asDebugger *ScriptMachine::debugger() const { return _debugger; }
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);
_engine->SetEngineProperty(asEP_BUILD_WITHOUT_LINE_CUES, false);
@ -1841,30 +2219,34 @@ bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
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
if (std::find_if(ret.begin(), ret.end(),
[](const QAsCodeParser::CodeSegment &seg) {
switch (seg.type) {
case QAsCodeParser::SymbolType::Enum:
case QAsCodeParser::SymbolType::Class:
case QAsCodeParser::SymbolType::Function:
case QAsCodeParser::SymbolType::Interface:
case QAsCodeParser::SymbolType::Import:
case QAsCodeParser::SymbolType::Variable:
return false;
case QAsCodeParser::SymbolType::Invalid:
case QAsCodeParser::SymbolType::TypeDef:
case QAsCodeParser::SymbolType::FnDef:
return true;
}
return true;
}) != ret.end()) {
if (ret.isEmpty() ||
std::any_of(ret.begin(), ret.end(),
[](const QAsCodeParser::CodeSegment &seg) {
switch (seg.type) {
case QAsCodeParser::SymbolType::Enum:
case QAsCodeParser::SymbolType::Class:
case QAsCodeParser::SymbolType::Function:
case QAsCodeParser::SymbolType::Interface:
case QAsCodeParser::SymbolType::Import:
case QAsCodeParser::SymbolType::Variable:
return false;
case QAsCodeParser::SymbolType::Invalid:
case QAsCodeParser::SymbolType::TypeDef:
case QAsCodeParser::SymbolType::FnDef:
return true;
}
return true;
})) {
// ok, wrap the codes
ccode.prepend("void f(){").append("}");
// start to compile
_curMode = mode;
_curMsgMode = mode;
auto cr = mod->CompileFunction(nullptr, ccode, 0, 0, &func);
if (cr < 0) {
MessageInfo info;
@ -1881,12 +2263,21 @@ bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
asIScriptContext *ctx = _ctxMgr->AddContext(_engine, func, true);
_ctx[mode] = ctx;
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);
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);
ctx->SetUserData(reinterpret_cast<void *>(umode),
AsUserDataType::UserData_ContextMode);
@ -1951,7 +2342,7 @@ bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) {
return seg.type ==
QAsCodeParser::SymbolType::Variable;
})) {
_curMode = mode;
_curMsgMode = mode;
for (auto &s : ret) {
auto r = mod->CompileGlobalVar(nullptr, s.codes, 0);

View File

@ -28,6 +28,8 @@
#include <QObject>
#include <QQueue>
class CScriptArray;
class ScriptMachine : public QObject {
Q_OBJECT
private:
@ -36,9 +38,9 @@ private:
public:
// we have three console modes
enum ConsoleMode {
Interactive, // in a shell
Scripting, // in scripting dialog
Background // run codes from other way
Interactive = 1, // in a shell
Scripting, // in scripting dialog
Background, // run codes from other way
};
public:
@ -108,8 +110,7 @@ public:
asIScriptModule *module(ConsoleMode mode);
static ScriptMachine &instance();
virtual ~ScriptMachine();
void destoryMachine();
public:
bool init();
@ -118,6 +119,7 @@ public:
static void registerEngineAddon(asIScriptEngine *engine);
static void registerEngineAssert(asIScriptEngine *engine);
static void registerEngineClipboard(asIScriptEngine *engine);
void registerCallBack(ConsoleMode mode, const RegCallBacks &callbacks);
@ -126,10 +128,17 @@ public:
asIScriptEngine *engine() const;
void outputMessage(const MessageInfo &info);
public:
static void scriptAssert(bool b);
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 QString scriptGetExceptionInfo();
@ -144,7 +153,9 @@ public slots:
bool executeCode(ConsoleMode mode, const QString &code);
// only scripting mode can be debugged
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 abortScript(ConsoleMode mode);
@ -155,19 +166,21 @@ protected:
QString getCallStack(asIScriptContext *context);
void destoryMachine();
private:
void print(void *ref, int typeId);
QString getInput();
static void print(asIScriptGeneric *args);
static void println(asIScriptGeneric *args);
void outputMessage(const MessageInfo &info);
QString getInput();
bool isType(asITypeInfo *tinfo, RegisteredType type);
static int execSystemCmd(QString &out, const QString &exe,
const QString &params, int timeout);
static QString beautify(const QString &str, uint indent);
QString stringify(void *ref, int typeId);
private:
static void messageCallback(const asSMessageInfo *msg, void *param);
@ -208,7 +221,7 @@ private:
QVector<asITypeInfo *> _rtypes;
QMap<ConsoleMode, RegCallBacks> _regcalls;
QMap<ConsoleMode, asIScriptContext *> _ctx;
ConsoleMode _curMode = ConsoleMode::Background;
ConsoleMode _curMsgMode = ConsoleMode::Background;
};
Q_DECLARE_METATYPE(ScriptMachine::MessageInfo)

View File

@ -73,6 +73,11 @@ QString ScriptManager::readJsonObjString(const QJsonObject &jobj,
return {};
}
bool ScriptManager::readJsonObjBool(const QJsonObject &jobj,
const QString &key) {
return jobj.value(key).toBool();
}
QMenu *ScriptManager::buildUpScriptDirMenu(QWidget *parent,
const QStringList &files,
bool isSys) {
@ -167,6 +172,7 @@ ScriptManager::ensureDirMeta(const QFileInfo &info) {
jobj.insert(QStringLiteral("license"), QLatin1String());
jobj.insert(QStringLiteral("homepage"), QLatin1String());
jobj.insert(QStringLiteral("comment"), QLatin1String());
jobj.insert(QStringLiteral("hexmenu"), false);
QFile f(base.absoluteFilePath(QStringLiteral(".wingasmeta")));
if (f.open(QFile::WriteOnly | QFile::Text)) {
QJsonDocument jdoc(jobj);
@ -195,6 +201,8 @@ ScriptManager::ensureDirMeta(const QFileInfo &info) {
readJsonObjString(jobj, QStringLiteral("homepage"));
meta.comment =
readJsonObjString(jobj, QStringLiteral("comment"));
meta.isContextMenu =
readJsonObjBool(jobj, QStringLiteral("hexmenu"));
} else {
meta.name = info.fileName();
}
@ -217,9 +225,10 @@ ScriptManager::sysDirMeta(const QString &cat) const {
return _sysDirMetas.value(cat);
}
ScriptManager::ScriptActionMaps
ScriptManager::buildUpRibbonScriptRunner(RibbonButtonGroup *group) {
ScriptActionMaps maps;
QList<QMenu *>
ScriptManager::buildUpScriptRunnerContext(RibbonButtonGroup *group,
QWidget *parent) {
QList<QMenu *> maps;
auto &sm = ScriptManager::instance();
auto &stm = SettingManager::instance();
@ -229,10 +238,20 @@ ScriptManager::buildUpRibbonScriptRunner(RibbonButtonGroup *group) {
if (hideCats.contains(cat)) {
continue;
}
maps.sysList << addPannelAction(
group, ICONRES(QStringLiteral("scriptfolder")),
sm.sysDirMeta(cat).name,
auto meta = sm.sysDirMeta(cat);
addPannelAction(
group, ICONRES(QStringLiteral("scriptfolder")), meta.name,
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();
@ -240,10 +259,19 @@ ScriptManager::buildUpRibbonScriptRunner(RibbonButtonGroup *group) {
if (hideCats.contains(cat)) {
continue;
}
maps.usrList << addPannelAction(
group, ICONRES(QStringLiteral("scriptfolderusr")),
sm.usrDirMeta(cat).name,
auto meta = sm.usrDirMeta(cat);
addPannelAction(
group, ICONRES(QStringLiteral("scriptfolderusr")), meta.name,
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;

View File

@ -24,7 +24,6 @@
#include <QObject>
#include "QWingRibbon/ribbonbuttongroup.h"
#include "control/scriptingconsole.h"
#include "utilities.h"
class ScriptManager : public QObject {
@ -38,14 +37,10 @@ public:
QString license;
QString homepage;
QString comment;
bool isContextMenu = false;
bool isSys; // a flag
};
struct ScriptActionMaps {
QList<QToolButton *> sysList;
QList<QToolButton *> usrList;
};
public:
static ScriptManager &instance();
@ -68,7 +63,8 @@ public:
ScriptDirMeta usrDirMeta(const QString &cat) const;
ScriptDirMeta sysDirMeta(const QString &cat) const;
static ScriptActionMaps buildUpRibbonScriptRunner(RibbonButtonGroup *group);
static QList<QMenu *> buildUpScriptRunnerContext(RibbonButtonGroup *group,
QWidget *parent);
private:
static QToolButton *addPannelAction(RibbonButtonGroup *pannel,
@ -119,6 +115,8 @@ private:
QString readJsonObjString(const QJsonObject &jobj, const QString &key);
bool readJsonObjBool(const QJsonObject &jobj, const QString &key);
static QMenu *buildUpScriptDirMenu(QWidget *parent,
const QStringList &files, bool isSys);

View File

@ -33,12 +33,24 @@
_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) \
if (this->_setUnsavedConsole.testFlag(flag)) { \
WRITE_CONFIG(config, dvalue); \
_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_SIZE, ("codeedit.fontsize"))
Q_GLOBAL_STATIC_WITH_ARGS(QString, CODEEDIT_THEME, ("codeedit.theme"))
@ -200,56 +212,59 @@ void ScriptSettings::save(SETTINGS cat) {
}
void ScriptSettings::reset(SETTINGS cat) {
__reset(cat);
load();
}
void ScriptSettings::__reset(SETTINGS cat) {
HANDLE_CONFIG;
if (cat.testFlag(SETTING::EDITOR)) {
WRITE_CONFIG_EDITOR_SET(CODEEDIT_FONT, SETTING_ITEM::FONT,
_defaultFont.defaultFamily());
WRITE_CONFIG_EDITOR_SET(CODEEDIT_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
10);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_THEME, SETTING_ITEM::THEME, {});
WRITE_CONFIG_EDITOR_SET(CODEEDIT_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
4);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_INDENTATION, SETTING_ITEM::INDENTATION,
0);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_MATCH_BRACES,
SETTING_ITEM::MATCH_BRACES, true);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_WORD_WRAP, SETTING_ITEM::WORD_WRAP,
false);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_LINENUMBER,
SETTING_ITEM::SHOW_LINENUMBER, true);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_FOLDING,
SETTING_ITEM::SHOW_FOLDING, true);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_INDENTGUIDES,
SETTING_ITEM::SHOW_INDENTGUIDES, true);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_LONGLINEEDGE,
SETTING_ITEM::SHOW_LONGLINEEDGE, false);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_WHITESPACE,
SETTING_ITEM::SHOW_WHITESPACE, false);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_AUTO_CLOSE_CHAR,
SETTING_ITEM::AUTO_CLOSE_CHAR, true);
WRITE_CONFIG_EDITOR_SET(CODEEDIT_AUTO_IDEN, SETTING_ITEM::AUTO_IDEN,
true);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_FONT, SETTING_ITEM::FONT,
_defaultFont.defaultFamily());
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
10);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_THEME, SETTING_ITEM::THEME, {});
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
4);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_INDENTATION,
SETTING_ITEM::INDENTATION, 0);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_MATCH_BRACES,
SETTING_ITEM::MATCH_BRACES, true);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_WORD_WRAP, SETTING_ITEM::WORD_WRAP,
false);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_LINENUMBER,
SETTING_ITEM::SHOW_LINENUMBER, true);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_FOLDING,
SETTING_ITEM::SHOW_FOLDING, true);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_INDENTGUIDES,
SETTING_ITEM::SHOW_INDENTGUIDES, true);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_LONGLINEEDGE,
SETTING_ITEM::SHOW_LONGLINEEDGE, false);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_WHITESPACE,
SETTING_ITEM::SHOW_WHITESPACE, false);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_AUTO_CLOSE_CHAR,
SETTING_ITEM::AUTO_CLOSE_CHAR, true);
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_AUTO_IDEN, SETTING_ITEM::AUTO_IDEN,
true);
}
if (cat.testFlag(SETTING::CONSOLE)) {
WRITE_CONFIG_CONSOLE_SET(CONSOLE_FONT, SETTING_ITEM::FONT,
_defaultFont.defaultFamily());
WRITE_CONFIG_CONSOLE_SET(CONSOLE_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
10);
WRITE_CONFIG_CONSOLE_SET(CONSOLE_THEME, SETTING_ITEM::THEME, {});
WRITE_CONFIG_CONSOLE_SET(CONSOLE_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
4);
WRITE_CONFIG_CONSOLE_SET(CONSOLE_INDENTATION, SETTING_ITEM::INDENTATION,
0);
WRITE_CONFIG_CONSOLE_SET(CONSOLE_MATCH_BRACES,
SETTING_ITEM::MATCH_BRACES, true);
WRITE_CONFIG_CONSOLE_SET(CONSOLE_SHOW_WHITESPACE,
SETTING_ITEM::SHOW_WHITESPACE, false);
WRITE_CONFIG_CONSOLE_SET(CONSOLE_AUTO_CLOSE_CHAR,
SETTING_ITEM::AUTO_CLOSE_CHAR, true);
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_FONT, SETTING_ITEM::FONT,
_defaultFont.defaultFamily());
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
10);
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_THEME, SETTING_ITEM::THEME, {});
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
4);
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_INDENTATION,
SETTING_ITEM::INDENTATION, 0);
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_MATCH_BRACES,
SETTING_ITEM::MATCH_BRACES, true);
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_SHOW_WHITESPACE,
SETTING_ITEM::SHOW_WHITESPACE, false);
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_AUTO_CLOSE_CHAR,
SETTING_ITEM::AUTO_CLOSE_CHAR, true);
}
load();
}
ScriptSettings::ScriptSettings() : QObject() {

View File

@ -57,6 +57,8 @@ public:
void save(SETTINGS cat = SETTING::ALL);
void reset(SETTINGS cat = SETTING::ALL);
void __reset(SETTINGS cat);
public:
QString editorFontFamily() const;
void setEditorFontFamily(const QString &newEditorFontFamily);

View File

@ -31,6 +31,12 @@
_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, SCRIPT_DOCK_LAYOUT, ("script.layout"))
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,
("script.allowUsrScriptRoot"))
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_SYSHIDECATS, ("script.sysHideCats"))
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_INT_POSITIVE(m_editorfontSize, EDITOR_FONTSIZE,
defaultFontSize);
m_editorfontSize = qBound(5, m_editorfontSize, 25);
READ_CONFIG_BOOL(m_editorShowHeader, EDITOR_SHOW_ADDR, true);
READ_CONFIG_BOOL(m_editorShowcol, EDITOR_SHOW_COL, 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_allowUsrScriptInRoot, SCRIPT_ALLOW_USRSCRIPT_INROOT,
false);
READ_CONFIG_INT(m_scriptTimeout, SCRIPT_TIMEOUT, 10);
m_scriptTimeout = qBound(0, m_scriptTimeout, 312480);
m_usrHideCats =
READ_CONFIG(SCRIPT_USRHIDECATS, QStringList()).toStringList();
m_sysHideCats =
@ -185,6 +195,16 @@ QVariantList SettingManager::getVarList(
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; }
void SettingManager::setLogCount(qsizetype newLogCount) {
@ -392,6 +412,7 @@ void SettingManager::save(SETTINGS cat) {
}
if (cat.testFlag(SETTING::SCRIPT)) {
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_USRHIDECATS, m_usrHideCats);
WRITE_CONFIG_SET(SCRIPT_SYSHIDECATS, m_sysHideCats);
@ -409,44 +430,49 @@ void SettingManager::save(SETTINGS cat) {
}
void SettingManager::reset(SETTINGS cat) {
__reset(cat);
load();
}
void SettingManager::__reset(SETTINGS cat) {
HANDLE_CONFIG;
if (cat.testFlag(SETTING::APP)) {
WRITE_CONFIG_SET(SKIN_THEME, 0);
WRITE_CONFIG_SET(APP_LANGUAGE, QString());
WRITE_CONFIG_SET(APP_FONTFAMILY, _defaultFont.family());
WRITE_CONFIG_SET(APP_FONTSIZE, _defaultFont.pointSize());
WRITE_CONFIG_SET(APP_WINDOWSIZE, Qt::WindowMaximized);
WRITE_CONFIG_RESET(SKIN_THEME, 0);
WRITE_CONFIG_RESET(APP_LANGUAGE, QString());
WRITE_CONFIG_RESET(APP_FONTFAMILY, _defaultFont.family());
WRITE_CONFIG_RESET(APP_FONTSIZE, _defaultFont.pointSize());
WRITE_CONFIG_RESET(APP_WINDOWSIZE, Qt::WindowMaximized);
}
if (cat.testFlag(SETTING::PLUGIN)) {
WRITE_CONFIG_SET(PLUGIN_ENABLE, true);
WRITE_CONFIG_SET(PLUGIN_ENABLE_ROOT, false);
WRITE_CONFIG_RESET(PLUGIN_ENABLE, true);
WRITE_CONFIG_RESET(PLUGIN_ENABLE_ROOT, false);
}
if (cat.testFlag(SETTING::EDITOR)) {
WRITE_CONFIG_SET(EDITOR_FONTSIZE, _defaultFont.pointSize());
WRITE_CONFIG_SET(EDITOR_SHOW_ADDR, true);
WRITE_CONFIG_SET(EDITOR_SHOW_COL, true);
WRITE_CONFIG_SET(EDITOR_SHOW_TEXT, true);
WRITE_CONFIG_SET(EDITOR_FIND_MAXCOUNT, 100);
WRITE_CONFIG_SET(EDITOR_COPY_LIMIT, 100);
WRITE_CONFIG_SET(EDITOR_DECSTRLIMIT, 10);
WRITE_CONFIG_RESET(EDITOR_FONTSIZE, _defaultFont.pointSize());
WRITE_CONFIG_RESET(EDITOR_SHOW_ADDR, true);
WRITE_CONFIG_RESET(EDITOR_SHOW_COL, true);
WRITE_CONFIG_RESET(EDITOR_SHOW_TEXT, true);
WRITE_CONFIG_RESET(EDITOR_FIND_MAXCOUNT, 100);
WRITE_CONFIG_RESET(EDITOR_COPY_LIMIT, 100);
WRITE_CONFIG_RESET(EDITOR_DECSTRLIMIT, 10);
}
if (cat.testFlag(SETTING::SCRIPT)) {
WRITE_CONFIG_SET(SCRIPT_ENABLE, true);
WRITE_CONFIG_SET(SCRIPT_ALLOW_USRSCRIPT_INROOT, false);
WRITE_CONFIG_SET(SCRIPT_USRHIDECATS, QStringList());
WRITE_CONFIG_SET(SCRIPT_SYSHIDECATS, QStringList());
WRITE_CONFIG_RESET(SCRIPT_ENABLE, true);
WRITE_CONFIG_RESET(SCRIPT_TIMEOUT, 10);
WRITE_CONFIG_RESET(SCRIPT_ALLOW_USRSCRIPT_INROOT, false);
WRITE_CONFIG_RESET(SCRIPT_USRHIDECATS, QStringList());
WRITE_CONFIG_RESET(SCRIPT_SYSHIDECATS, QStringList());
}
if (cat.testFlag(SETTING::OTHER)) {
WRITE_CONFIG_SET(OTHER_USESYS_FILEDIALOG, true);
WRITE_CONFIG_RESET(OTHER_USESYS_FILEDIALOG, true);
#ifdef WINGHEX_USE_FRAMELESS
WRITE_CONFIG_SET(OTHER_USE_NATIVE_TITLEBAR, false);
WRITE_CONFIG_RESET(OTHER_USE_NATIVE_TITLEBAR, false);
#endif
WRITE_CONFIG_SET(OTHER_DONT_USE_SPLASH, false);
WRITE_CONFIG_SET(OTHER_CHECK_UPDATE, false);
WRITE_CONFIG_SET(OTHER_LOG_LEVEL, Logger::defaultLevel());
WRITE_CONFIG_SET(OTHER_LOG_COUNT, 20);
WRITE_CONFIG_RESET(OTHER_DONT_USE_SPLASH, false);
WRITE_CONFIG_RESET(OTHER_CHECK_UPDATE, false);
WRITE_CONFIG_RESET(OTHER_LOG_LEVEL, Logger::defaultLevel());
WRITE_CONFIG_RESET(OTHER_LOG_COUNT, 20);
}
load();
}
qsizetype SettingManager::decodeStrlimit() const { return m_decodeStrlimit; }

View File

@ -59,7 +59,7 @@ private:
EDITOR_SHOW_ADDR = 1u << 11,
EDITOR_SHOW_COL = 1u << 12,
EDITOR_SHOW_TEXT = 1u << 13,
// EDITOR_ENCODING = 1u << 14, // Reserved
SCRIPT_TIMEOUT = 1u << 14,
EDITOR_FIND_MAXCOUNT = 1u << 15,
EDITOR_COPY_LIMIT = 1u << 16,
EDITOR_DECSTRLIMIT = 1u << 17,
@ -119,6 +119,8 @@ public:
void save(SETTINGS cat = SETTING::ALL);
void reset(SETTINGS cat);
void __reset(SETTINGS cat);
QList<RecentFileManager::RecentInfo> recentHexFiles() const;
void
setRecentFiles(const QList<RecentFileManager::RecentInfo> &newRecentFiles);
@ -172,6 +174,9 @@ public:
qsizetype logCount() const;
void setLogCount(qsizetype newLogCount);
int scriptTimeout() const;
void setScriptTimeout(int newScriptTimeout);
public:
void checkWriteableAndWarn();
@ -229,6 +234,8 @@ private:
int m_logLevel = 0;
qsizetype m_logCount = 20;
int m_scriptTimeout = 60; // min
private:
QFont _defaultFont;
SETTING_ITEMS _setUnsaved;

View File

@ -1188,13 +1188,15 @@ QByteArray WingAngelAPI::cArray2ByteArray(const CScriptArray &array, int byteID,
return {};
}
auto len = array.GetSize();
QByteArray buffer;
buffer.reserve(array.GetSize());
buffer.resize(len);
array.AddRef();
for (asUINT i = 0; i < array.GetSize(); ++i) {
auto item = reinterpret_cast<const asBYTE *>(array.At(i));
buffer.append(*item);
}
std::memcpy(buffer.data(), const_cast<CScriptArray &>(array).GetBuffer(),
len);
array.Release();
return buffer;
}
@ -1899,20 +1901,43 @@ bool WingAngelAPI::execScriptCode(const WingHex::SenderInfo &sender,
bool WingAngelAPI::execScript(const WingHex::SenderInfo &sender,
const QString &fileName) {
auto handles = _handles;
auto ret = ScriptMachine::instance().executeScript(
ScriptMachine::Background, fileName);
cleanUpHandles(handles);
return ret;
auto exec = [this, fileName]() -> bool {
auto handles = _handles;
auto ret = ScriptMachine::instance().executeScript(
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,
const QString &code) {
auto handles = _handles;
auto ret =
ScriptMachine::instance().executeCode(ScriptMachine::Background, code);
cleanUpHandles(handles);
return ret;
auto exec = [this, code]() -> bool {
auto handles = _handles;
auto ret = ScriptMachine::instance().executeCode(
ScriptMachine::Background, code);
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,
@ -1970,9 +1995,7 @@ void *WingAngelAPI::vector2AsArray(const WingHex::SenderInfo &sender,
if (info) {
auto len = content.length();
auto arr = CScriptArray::Create(info, len);
for (decltype(len) i = 0; i < len; ++i) {
arr->SetValue(i, content.at(i));
}
std::memcpy(arr->GetBuffer(), content.data(), len);
return arr;
}
return nullptr;
@ -1980,6 +2003,10 @@ void *WingAngelAPI::vector2AsArray(const WingHex::SenderInfo &sender,
void *WingAngelAPI::list2AsArray(const WingHex::SenderInfo &sender,
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);
auto typeStr = PluginSystem::type2AngelScriptString(
MetaType(type | MetaType::Array), false, true);
@ -1998,6 +2025,7 @@ void *WingAngelAPI::list2AsArray(const WingHex::SenderInfo &sender,
return arr;
}
return nullptr;
#endif
}
void WingAngelAPI::deleteAsArray(const WingHex::SenderInfo &sender,

View File

@ -95,7 +95,7 @@ WingCStruct::WingCStruct() : WingHex::IWingPlugin() {
info.fn = std::bind(
QOverload<const QVariantList &>::of(&WingCStruct::structTypes),
this, std::placeholders::_1);
info.ret = MetaType(MetaType::String | MetaType::Array);
info.ret = MetaType::String | MetaType::Array;
_scriptInfo.insert(QStringLiteral("structTypes"), info);
}
@ -125,7 +125,7 @@ WingCStruct::WingCStruct() : WingHex::IWingPlugin() {
info.fn = std::bind(
QOverload<const QVariantList &>::of(&WingCStruct::constDefines),
this, std::placeholders::_1);
info.ret = MetaType(MetaType::String | MetaType::Array);
info.ret = MetaType::String | MetaType::Array;
_scriptInfo.insert(QStringLiteral("constDefines"), info);
}
@ -166,7 +166,7 @@ WingCStruct::WingCStruct() : WingHex::IWingPlugin() {
info.fn = std::bind(
QOverload<const QVariantList &>::of(&WingCStruct::readRaw), this,
std::placeholders::_1);
info.ret = MetaType(MetaType::Byte | MetaType::Array);
info.ret = MetaType::Byte | MetaType::Array;
info.params.append(
qMakePair(getqsizetypeMetaType(), QStringLiteral("offset")));
@ -542,7 +542,6 @@ CScriptDictionary *WingCStruct::convert2AsDictionary(const QVariantHash &hash) {
switch (type) {
case QMetaType::Bool:
case QMetaType::UChar:
case QMetaType::Char:
case QMetaType::Int:
case QMetaType::Long:
case QMetaType::LongLong:
@ -564,6 +563,13 @@ CScriptDictionary *WingCStruct::convert2AsDictionary(const QVariantHash &hash) {
case QMetaType::Float:
dic->Set(p->first, var.toDouble());
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)
case QMetaType::Char16: {
auto v = var.value<char16_t>();

View File

@ -104,11 +104,20 @@ void CodeEdit::onCompletion(const QModelIndex &index) {
if (selfdata.type == CodeInfoTip::Type::Function ||
selfdata.type == CodeInfoTip::Type::ClsFunction) {
auto args = selfdata.addinfo.value(CodeInfoTip::Args);
auto cursor = textCursor();
cursor.insertText(QStringLiteral("()"));
if (!args.isEmpty()) {
cursor.movePosition(QTextCursor::Left);
setTextCursor(cursor);
auto cur = textCursor();
cur.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
auto ch = cur.selectedText();
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; }
void CodeEdit::setContentModified(bool b) { emit contentModified(b); }
void CodeEdit::resizeEvent(QResizeEvent *event) {
if (event)
WingCodeEdit::resizeEvent(event);

View File

@ -35,6 +35,9 @@ public:
SearchReplaceWidget *searchWidget() const;
public:
void setContentModified(bool b);
signals:
void contentModified(bool b);

View File

@ -227,7 +227,7 @@ EditorView::FindError EditorView::find(const FindDialog::Result &result) {
end = cur->selectionEnd(0).offset();
} break;
default: {
begin = -1;
begin = 0;
end = -1;
} break;
}
@ -242,8 +242,12 @@ EditorView::FindError EditorView::find(const FindDialog::Result &result) {
contextLen = raw.length();
m_findResults->setEncoding(result.encoding);
d->findAllBytes(begin, end, raw, results);
m_findResults->lastFindData() = qMakePair(data, contextLen);
} 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();
@ -257,12 +261,9 @@ EditorView::FindError EditorView::find(const FindDialog::Result &result) {
r.col = r.offset % lineWidth;
m_findResults->results().append(r);
m_findResults->findData().append(
readContextFinding(ritem, contextLen, FIND_CONTEXT_SIZE,
FIND_MAX_DISPLAY_FIND_CHARS));
readContextFinding(ritem, contextLen));
}
m_findResults->lastFindData() = data;
m_findResults->endUpdate();
if (m_findResults->size() >= QHEXVIEW_FIND_LIMIT) {
@ -287,9 +288,8 @@ ErrFile EditorView::newFile(size_t index) {
if (isCloneFile()) {
return ErrFile::ClonedFile;
}
if (!m_fileName.isEmpty()) {
_watcher.removePath(m_fileName);
}
removeMonitorPaths();
auto istr = QString::number(index);
m_fileName = tr("Untitled") + istr;
this->setWindowTitle(m_fileName);
@ -326,9 +326,7 @@ ErrFile EditorView::openFile(const QString &filename) {
return ErrFile::Permission;
}
if (!m_fileName.isEmpty()) {
_watcher.removePath(m_fileName);
}
removeMonitorPaths();
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
m_hex->setLockedFile(readonly);
@ -346,7 +344,7 @@ ErrFile EditorView::openFile(const QString &filename) {
tab->setIcon(Utilities::getIconFromFile(style(), m_fileName));
tab->setToolTip(m_fileName);
_watcher.addPath(m_fileName);
addMonitorPath();
}
return ErrFile::Success;
@ -384,9 +382,7 @@ ErrFile EditorView::openExtFile(const QString &ext, const QString &file) {
return ErrFile::Error;
}
if (!m_fileName.isEmpty()) {
_watcher.removePath(m_fileName);
}
removeMonitorPaths();
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
m_hex->setLockedFile(readonly);
@ -574,19 +570,20 @@ ErrFile EditorView::save(const QString &workSpaceName, const QString &path,
return ErrFile::Permission;
}
removeMonitorPaths();
if (doc->saveTo(&file, !isExport)) {
file.close();
if (!isExport) {
if (!m_fileName.isEmpty()) {
_watcher.removePath(m_fileName);
}
m_fileName = QFileInfo(fileName).absoluteFilePath();
m_isNewFile = false;
m_docType = DocumentType::File;
doc->setDocSaved();
_watcher.addPath(m_fileName);
}
addMonitorPath();
#ifdef Q_OS_LINUX
adjustPermission();
#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; }
MetaDataModel *EditorView::metadataModel() const { return m_metadata; }
@ -804,34 +811,50 @@ bool EditorView::checkHasUnsavedState() const {
}
FindResultModel::FindInfo EditorView::readContextFinding(qsizetype offset,
qsizetype findSize,
int contextSize,
int maxDisplayBytes) {
qsizetype findSize) {
constexpr long DISPLAY_SIZE = 16;
constexpr long FIND_CONTENXT_LEN = 10;
constexpr long HT_SIZE = (DISPLAY_SIZE - FIND_CONTENXT_LEN) / 2;
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 header = doc->read(offset, qMin(findSize, halfSize));
QByteArray tailer;
if (header.size() < findSize) {
auto len = qMin(findSize, qsizetype(maxDisplayBytes) - halfSize);
tailer = doc->read(offset + findSize - len, len);
auto headerlen = HT_SIZE + rs.quot + rs.rem;
auto taillen = HT_SIZE + rs.quot;
auto begin = qMax(offset - headerlen, 0);
auto end = qMin(offset + findSize + taillen, doc->length());
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,

View File

@ -225,9 +225,7 @@ private:
bool checkHasUnsavedState() const;
FindResultModel::FindInfo readContextFinding(qsizetype offset,
qsizetype findSize,
int contextSize,
int maxDisplayBytes);
qsizetype findSize);
void applyFunctionTables(WingHex::WingEditorViewWidget *view,
const CallTable &fns);
@ -538,7 +536,10 @@ private:
parent->addAction(a);
}
private:
void connectDocSavedFlag(EditorView *editor);
void removeMonitorPaths();
void addMonitorPath();
signals:
void sigOnCutFile();

View File

@ -36,6 +36,7 @@ GotoLineWidget::GotoLineWidget(QWidget *parent)
m_sbline = new QSpinBox(this);
m_sbline->setRange(1, 1);
m_sbline->setContextMenuPolicy(Qt::NoContextMenu);
m_sbline->setMinimumWidth(120);
connect(m_sbline, &QSpinBox::valueChanged, this,
&GotoLineWidget::onGotoLine);

View File

@ -57,7 +57,7 @@ void GotoWidget::handleLineChanged() {
ui->lineEdit->setStyleSheet(QString());
emit jumpToLine(p, isline);
} else {
ui->lineEdit->setStyleSheet(QStringLiteral("color: red;"));
ui->lineEdit->setStyleSheet(QStringLiteral("QLineEdit{color: red}"));
}
}

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<width>412</width>
<height>50</height>
</rect>
</property>
@ -44,7 +44,7 @@
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -60,7 +60,7 @@
<string>GoTo:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
@ -78,6 +78,9 @@
<height>16777215</height>
</size>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ContextMenuPolicy::NoContextMenu</enum>
</property>
</widget>
</item>
<item>

View File

@ -102,3 +102,17 @@ bool QHexTextEdit::isHexMode() const { return m_isHexMode; }
void QHexTextEdit::setIsHexMode(bool 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();
}

View File

@ -28,6 +28,10 @@ public:
bool isHexMode() const;
void setIsHexMode(bool newIsHexMode);
public:
// must be 'xx xx xx' style
void setFindText(const QString &text);
protected:
void keyPressEvent(QKeyEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;

View File

@ -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());
}

View File

@ -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

View File

@ -57,6 +57,9 @@ ScriptEditor::ScriptEditor(QWidget *parent)
connect(m_editor, &CodeEdit::contentModified, this,
[this]() { processTitle(); });
connect(&_watcher, &QFileSystemWatcher::fileChanged, this,
&ScriptEditor::need2Reload);
this->setWidget(m_editor);
}
@ -75,12 +78,30 @@ bool ScriptEditor::openFile(const QString &filename) {
}
m_editor->setPlainText(QString::fromUtf8(f.readAll()));
f.close();
if (!m_fileName.isEmpty()) {
_watcher.removePath(m_fileName);
}
m_fileName = filename;
_watcher.addPath(m_fileName);
processTitle();
return true;
}
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
auto needAdjustFile = !QFile::exists(path);
#endif

View File

@ -21,6 +21,8 @@
#include "Qt-Advanced-Docking-System/src/DockWidget.h"
#include "control/codeedit.h"
#include <QFileSystemWatcher>
class asIScriptEngine;
class ScriptEditor : public ads::CDockWidget {
@ -40,6 +42,8 @@ signals:
void onToggleMark(int line);
void onFunctionTip(const QString &tip);
void need2Reload();
public slots:
void setReadOnly(bool b);
bool openFile(const QString &filename);
@ -57,6 +61,8 @@ private:
private:
CodeEdit *m_editor = nullptr;
QString m_fileName;
QFileSystemWatcher _watcher;
};
#endif // SCRIPTEDITOR_H

View File

@ -225,7 +225,7 @@ void ScriptingConsole::applyScriptSettings() {
auto dfont = QFont(set.consoleFontFamily());
dfont.setPointSize(set.consoleFontSize());
auto thname = set.editorTheme();
auto thname = set.consoleTheme();
if (thname.isEmpty()) {
switch (SkinManager::instance().currentTheme()) {
case SkinManager::Theme::Dark:
@ -255,32 +255,28 @@ void ScriptingConsole::runConsoleCommand(const QString &code) {
auto &ins = ScriptMachine::instance();
auto mod = ins.module(ScriptMachine::Interactive);
if (mod) {
QList<QPair<QByteArray, QByteArray>> vars;
auto total = mod->GetGlobalVarCount();
// generate codes to print
QString codes;
setMode(Output);
if (total == 0) {
codes = QStringLiteral("print(\"<none>\");");
stdOut("<none>");
} else {
auto &sm = ScriptMachine::instance();
for (asUINT i = 0; i < total; ++i) {
const char *name;
int typeId;
int typeID;
auto decl = mod->GetGlobalVarDeclaration(i);
if (decl && mod->GetGlobalVar(i, &name) == asSUCCESS) {
vars.emplaceBack(decl, name);
if (decl && mod->GetGlobalVar(i, &name, nullptr, &typeID) ==
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();
appendCommandPrompt();
setMode(Input);
@ -329,11 +325,13 @@ void ScriptingConsole::runConsoleCommand(const QString &code) {
auto mod = ins.module(ScriptMachine::Interactive);
if (mod) {
auto total = mod->GetGlobalVarCount();
asUINT i = total;
do {
--i;
mod->RemoveGlobalVar(i);
} while (i);
if (total) {
asUINT i = total;
do {
--i;
mod->RemoveGlobalVar(i);
} while (i);
}
}
_codes.clear();
appendCommandPrompt();
@ -391,11 +389,20 @@ void ScriptingConsole::onCompletion(const QModelIndex &index) {
if (selfdata.type == CodeInfoTip::Type::Function ||
selfdata.type == CodeInfoTip::Type::ClsFunction) {
auto args = selfdata.addinfo.value(CodeInfoTip::Args);
auto cursor = textCursor();
cursor.insertText(QStringLiteral("()"));
if (!args.isEmpty()) {
cursor.movePosition(QTextCursor::Left);
setTextCursor(cursor);
auto cur = textCursor();
cur.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
auto ch = cur.selectedText();
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();
if (!text.isEmpty()) {
if (text.indexOf('\n') < 0) {
replaceCommandLine(text);
if (isCursorInEditZone()) {
auto cursor = this->textCursor();
cursor.insertText(text);
} else {
replaceCommandLine(text);
}
} else {
auto ret = WingMessageBox::question(
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 {
QTextCursor textCursor = this->textCursor();
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
@ -448,24 +466,38 @@ QString ScriptingConsole::currentCodes() const {
void ScriptingConsole::contextMenuEvent(QContextMenuEvent *event) {
QMenu menu(this);
menu.addAction(QIcon(QStringLiteral(":/qeditor/copy.png")), tr("Copy"),
QKeySequence(QKeySequence::Copy), this,
&ScriptingConsole::copy);
menu.addAction(QIcon(QStringLiteral(":/qeditor/cut.png")), tr("Cut"),
QKeySequence(QKeySequence::Cut), this,
&ScriptingConsole::cut);
menu.addAction(QIcon(QStringLiteral(":/qeditor/paste.png")), tr("Paste"),
QKeySequence(QKeySequence::Paste), this,
&ScriptingConsole::paste);
menu.addAction(ICONRES(QStringLiteral("del")), tr("Clear"),
QKeySequence(Qt::ControlModifier | Qt::Key_L), this,
&ScriptingConsole::clearConsole);
menu.addSeparator();
menu.addAction(ICONRES(QStringLiteral("dbgstop")), tr("AbortScript"),
QKeySequence(Qt::ControlModifier | Qt::Key_Q), []() {
ScriptMachine::instance().abortScript(
ScriptMachine::Background);
});
auto a = menu.addAction(QIcon(QStringLiteral(":/qeditor/copy.png")),
tr("Copy"), QKeySequence(QKeySequence::Copy), this,
&ScriptingConsole::copy);
a->setShortcutContext(Qt::WidgetShortcut);
a = menu.addAction(QIcon(QStringLiteral(":/qeditor/cut.png")), tr("Cut"),
QKeySequence(QKeySequence::Cut), this,
&ScriptingConsole::cut);
a->setShortcutContext(Qt::WidgetShortcut);
a = menu.addAction(QIcon(QStringLiteral(":/qeditor/paste.png")),
tr("Paste"), QKeySequence(QKeySequence::Paste), this,
&ScriptingConsole::paste);
a->setShortcutContext(Qt::WidgetShortcut);
if (_isTerminal) {
a = menu.addAction(ICONRES(QStringLiteral("del")), tr("Clear"),
QKeySequence(Qt::ControlModifier | Qt::Key_L), this,
&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());
}

View File

@ -40,6 +40,10 @@ signals:
public:
QString getInput();
bool isTerminal() const;
void setIsTerminal(bool newIsTerminal);
public slots:
void init();
@ -71,6 +75,7 @@ protected slots:
private:
QString _codes;
bool _isTerminal = true;
bool _isWaitingRead = false;
std::function<QString(void)> _getInputFn;
};

View File

@ -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 "class/wingconsolehighligher.h"
#include "wingsyntaxhighlighter.h"
@ -19,18 +36,47 @@ ScriptingConsoleBase::ScriptingConsoleBase(QWidget *parent)
}
void ScriptingConsoleBase::stdOut(const QString &str) {
writeStdOut(str);
dontHighlightLastLine(true);
auto lines = str.split('\n');
if (lines.isEmpty()) {
return;
}
writeStdOut(lines.takeFirst());
dontHighlightLastLine(false);
for (auto &l : lines) {
newLine();
writeStdOut(l);
dontHighlightLastLine(true);
}
}
void ScriptingConsoleBase::stdErr(const QString &str) {
writeStdErr(str);
auto lines = str.split('\n');
if (lines.isEmpty()) {
return;
}
writeStdErr(lines.takeFirst());
dontHighlightLastLine(false);
for (auto &l : lines) {
newLine();
writeStdErr(l);
dontHighlightLastLine(false);
}
}
void ScriptingConsoleBase::stdWarn(const QString &str) {
write(str, _warnCharFmt);
auto lines = str.split('\n');
if (lines.isEmpty()) {
return;
}
write(lines.takeFirst(), _warnCharFmt);
dontHighlightLastLine(false);
for (auto &l : lines) {
newLine();
write(l, _warnCharFmt);
dontHighlightLastLine(false);
}
}
void ScriptingConsoleBase::newLine() { _s << Qt::endl; }

View File

@ -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
#define SCRIPTINGCONSOLEBASE_H

View File

@ -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);
}
}

View File

@ -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

View File

@ -16,6 +16,8 @@ enum AsUserDataType {
UserData_PluginFn,
UserData_isDbg,
UserData_ContextMode,
UserData_Timer,
UserData_TimeOut
};
}

View File

@ -83,39 +83,15 @@ FindDialog::FindDialog(const FindInfo &info, QWidget *parent)
if (info.isStringFind) {
if (!info.encoding.isEmpty()) {
m_lineeditor->setIsHexMode(false);
m_findMode->setCurrentText(info.encoding);
}
} else {
m_lineeditor->setIsHexMode(true);
m_findMode->setCurrentIndex(0);
}
m_lineeditor->setText(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);
m_lineeditor->setFindText(info.str);
auto group = new QButtonGroup(this);
group->setExclusive(true);
@ -134,18 +110,9 @@ FindDialog::FindDialog(const FindInfo &info, QWidget *parent)
}
});
group->addButton(b, id++);
b->setEnabled(!info.isBigFile);
b->setChecked(true);
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++);
buttonLayout->addWidget(b);
@ -183,7 +150,6 @@ FindDialog::FindDialog(const FindInfo &info, QWidget *parent)
group->addButton(b, id++);
buttonLayout->addWidget(b);
group->button(info.isBigFile ? 1 : 0)->setChecked(true);
layout->addWidget(btnBox);
@ -195,9 +161,6 @@ FindDialog::FindDialog(const FindInfo &info, QWidget *parent)
auto s = new QShortcut(key, this);
connect(s, &QShortcut::activated, this, &FindDialog::on_accept);
layout->addWidget(regionw);
regionw->hide();
layout->addSpacing(20);
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();
done(1);
}

View File

@ -19,7 +19,6 @@
#define FINDDIALOG_H
#include "control/qhextextedit.h"
#include "control/qtlonglongspinbox.h"
#include "framelessdialogbase.h"
#include <QComboBox>
@ -28,15 +27,13 @@
#include <QRadioButton>
#include <QTextEdit>
enum class SearchDirection { None, Region, Foreword, Backword, Selection };
enum class SearchDirection { None, Foreword, Backword, Selection };
class FindDialog : public FramelessDialogBase {
Q_OBJECT
public:
struct Result {
SearchDirection dir = SearchDirection::None;
qsizetype start = 0;
qsizetype stop = 0;
// for searching info
bool isStringFind;
@ -45,10 +42,7 @@ public:
};
struct FindInfo {
bool isBigFile;
bool isStringFind;
qlonglong start;
qlonglong stop;
bool isSel;
// for searching info
@ -70,9 +64,6 @@ private:
QComboBox *m_findMode;
QTextEdit *m_preview;
QtLongLongSpinBox *m_regionStart;
QtLongLongSpinBox *m_regionStop;
Result _result;
};

View File

@ -131,7 +131,7 @@ MainWindow::MainWindow(SplashDialog *splash) : FramelessMainWindow() {
m_status->addWidget(l);
m_status->addWidget(m_lblsellen);
_status = new QLabel(m_status);
_status = new ScrollableLabel(m_status);
m_status->addPermanentWidget(_status);
auto separator = new QFrame(m_status);
@ -607,6 +607,24 @@ ads::CDockAreaWidget *MainWindow::buildUpLogDock(ads::CDockManager *dock,
m_logbrowser->setOpenExternalLinks(true);
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 =
buildDockWidget(dock, QStringLiteral("Log"), tr("Log"), m_logbrowser);
return dock->addDockWidget(area, dw, areaw);
@ -698,10 +716,17 @@ MainWindow::buildUpFindResultDock(ads::CDockManager *dock,
cursor->moveTo(fm->resultAt(index.row()).offset);
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"),
tr("FindResult") + QStringLiteral(" (ASCII)"),
m_findresult);
@ -803,9 +828,7 @@ MainWindow::buildUpHashResultDock(ads::CDockManager *dock,
m_hashtable->setContextMenuPolicy(
Qt::ContextMenuPolicy::ActionsContextMenu);
auto a = new QAction(m_hashtable);
a->setText(tr("Copy"));
connect(a, &QAction::triggered, this, [=] {
auto a = newAction(ICONRES(QStringLiteral("copy")), tr("Copy"), [=] {
auto r = m_hashtable->currentIndex();
qApp->clipboard()->setText(
_hashModel->checkSumData(QCryptographicHash::Algorithm(r.row())));
@ -813,6 +836,9 @@ MainWindow::buildUpHashResultDock(ads::CDockManager *dock,
tr("CopyToClipBoard"));
});
m_hashtable->addAction(a);
a = newAction(QStringLiteral("del"), tr("Clear"),
[=]() { _hashModel->clearData(); });
m_hashtable->addAction(a);
connect(m_hashtable->selectionModel(),
&QItemSelectionModel::currentRowChanged, a,
[=](const QModelIndex &current, const QModelIndex &) {
@ -985,18 +1011,17 @@ MainWindow::buildUpDecodingStrShowDock(ads::CDockManager *dock,
tr("DecodeText") + QStringLiteral(" (ASCII)"),
m_txtDecode);
auto menu = m_txtDecode->createStandardContextMenu();
menu->addSeparator();
auto a = new QAction(tr("Encoding"), this);
auto a = newAction(
ICONRES("copy"), tr("Copy"), [=]() { m_logbrowser->copy(); },
QKeySequence::Copy);
a->setShortcutContext(Qt::WidgetShortcut);
m_txtDecode->addAction(a);
a = new QAction(tr("Encoding"), this);
a->setIcon(ICONRES(QStringLiteral("encoding")));
connect(a, &QAction::triggered, this, &MainWindow::on_encoding);
menu->addAction(a);
m_txtDecode->addAction(a);
m_txtDecode->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_txtDecode, &QTextBrowser::customContextMenuRequested, this,
[=](const QPoint &pos) {
menu->popup(m_txtDecode->viewport()->mapToGlobal(pos));
});
m_txtDecode->setContextMenuPolicy(Qt::ActionsContextMenu);
connect(m_txtDecode, &QTextBrowser::windowTitleChanged, dw,
&QDockWidget::setWindowTitle);
@ -1042,13 +1067,16 @@ MainWindow::buildUpScriptBgOutputDock(ads::CDockManager *dock,
auto a = newAction(
ICONRES(QStringLiteral("mStr")), tr("SelectAll"),
[this]() { m_bgScriptOutput->selectAll(); }, QKeySequence::SelectAll);
a->setShortcutContext(Qt::WidgetShortcut);
m_bgScriptOutput->addAction(a);
a = newAction(
ICONRES(QStringLiteral("copy")), tr("Copy"),
[this]() { m_bgScriptOutput->copy(); }, QKeySequence::Copy);
a->setShortcutContext(Qt::WidgetShortcut);
m_bgScriptOutput->addAction(a);
a = newAction(ICONRES(QStringLiteral("del")), tr("Clear"),
[this]() { m_bgScriptOutput->clear(); });
a->setShortcutContext(Qt::WidgetShortcut);
m_bgScriptOutput->addAction(a);
a = new QAction(this);
a->setSeparator(true);
@ -1059,6 +1087,7 @@ MainWindow::buildUpScriptBgOutputDock(ads::CDockManager *dock,
ScriptMachine::instance().abortScript(ScriptMachine::Background);
},
QKeySequence(Qt::ControlModifier | Qt::Key_Q));
a->setShortcutContext(Qt::WidgetShortcut);
m_bgScriptOutput->addAction(a);
m_bgScriptOutput->setContextMenuPolicy(Qt::ActionsContextMenu);
@ -1501,7 +1530,8 @@ RibbonTabContent *MainWindow::buildScriptPage(RibbonTabContent *tab) {
pannel->setVisible(false);
connect(pannel, &RibbonButtonGroup::emptyStatusChanged, this,
[pannel](bool isEmpty) { pannel->setVisible(!isEmpty); });
_scriptMaps = ScriptManager::buildUpRibbonScriptRunner(pannel);
_scriptContexts =
ScriptManager::buildUpScriptRunnerContext(pannel, this);
m_scriptDBGroup = pannel;
}
@ -2052,7 +2082,10 @@ void MainWindow::on_pastefile() {
if (hexeditor == nullptr) {
return;
}
hexeditor->Paste();
if (!hexeditor->Paste()) {
Toast::toast(this, NAMEICONRES(QStringLiteral("paste")),
tr("PasteFailedNote"));
}
}
void MainWindow::on_delete() {
@ -2094,9 +2127,6 @@ void MainWindow::on_findfile() {
auto hexeditor = editor->hexEditor();
static FindDialog::FindInfo info;
info.isBigFile = editor->isBigFile();
info.start = 0;
info.stop = hexeditor->documentBytes();
info.isSel = hexeditor->selectionCount() == 1;
FindDialog fd(info, this);
@ -2110,7 +2140,6 @@ void MainWindow::on_findfile() {
ExecAsync<EditorView::FindError>(
[this, r]() -> EditorView::FindError {
m_isfinding = true;
return currentEditor()->find(r);
},
[this](EditorView::FindError err) {
@ -2134,6 +2163,14 @@ void MainWindow::on_findfile() {
if (result) {
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_isfinding = false;
@ -2578,7 +2615,7 @@ void MainWindow::on_exportfindresult() {
auto d = findresitem->lastFindData();
fobj.insert(QStringLiteral("find"), d);
fobj.insert(QStringLiteral("find"), d.first);
QJsonArray arr;
for (int i = 0; i < c; 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) {
editor->registerQMenu(m);
}
@ -3207,6 +3249,8 @@ void MainWindow::connectEditorView(EditorView *editor) {
if (editor->isBigFile()) {
auto fileName = editor->fileName();
if (!QFile::exists(fileName)) {
activateWindow();
raise();
editor->raise();
WingMessageBox::critical(this, tr("Error"),
tr("FileCloseBigFile"));

View File

@ -360,7 +360,7 @@ private:
template <typename Func>
inline QAction *newAction(const QString &title, Func &&slot,
const QKeySequence &shortcut = QKeySequence()) {
auto a = new QAction;
auto a = new QAction(this);
a->setText(title);
a->setShortcutVisibleInContextMenu(true);
a->setShortcut(shortcut);
@ -466,7 +466,7 @@ signals:
private:
Ribbon *m_ribbon = nullptr;
ads::CDockManager *m_dock = nullptr;
QLabel *_status = nullptr;
ScrollableLabel *_status = nullptr;
QString m_encoding;
@ -524,7 +524,7 @@ private:
RibbonButtonGroup *m_scriptDBGroup = nullptr;
RibbonButtonGroup *m_pluginSettingsGroup = nullptr;
ScriptManager::ScriptActionMaps _scriptMaps;
QList<QMenu *> _scriptContexts;
//===================================================

View File

@ -38,6 +38,7 @@
#include <QPainter>
#include <QPicture>
#include <QStatusBar>
#include <QToolTip>
constexpr auto EMPTY_FUNC = [] {};
@ -71,7 +72,7 @@ ScriptingDialog::ScriptingDialog(QWidget *parent)
layout->addWidget(m_dock, 1);
m_status = new QStatusBar(this);
_status = new QLabel(this);
_status = new ScrollableLabel(this);
m_status->addPermanentWidget(_status);
layout->addWidget(m_status);
buildUpContent(cw);
@ -583,6 +584,7 @@ ScriptingDialog::buildUpOutputShowDock(ads::CDockManager *dock,
ads::CDockAreaWidget *areaw) {
m_consoleout = new ScriptingConsole(this);
m_consoleout->setMode(ScriptingConsole::Output);
m_consoleout->setIsTerminal(false);
auto dw = buildDockWidget(dock, QStringLiteral("ConsoleOutput"),
tr("ConsoleOutput"), m_consoleout);
return dock->addDockWidget(area, dw, areaw);
@ -799,6 +801,21 @@ void ScriptingDialog::registerEditorView(ScriptEditor *editor) {
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);
auto ev = m_Tbtneditors.value(ToolButtonIndex::EDITOR_VIEWS);
@ -873,6 +890,19 @@ void ScriptingDialog::swapEditor(ScriptEditor *old, ScriptEditor *cur) {
m_curEditor = cur;
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) {
@ -1369,7 +1399,8 @@ void ScriptingDialog::on_runscript() {
PluginSystem::instance().scriptPragmaBegin();
editor->setReadOnly(true);
// ScriptMachine::instance().executeScript(editor->fileName());
ScriptMachine::instance().executeScript(ScriptMachine::Scripting,
editor->fileName());
editor->setReadOnly(false);
updateRunDebugMode();
}

View File

@ -20,6 +20,7 @@
#include "control/asobjtreewidget.h"
#include "control/scriptingconsole.h"
#include "control/scrollablelabel.h"
#include "dialog/settingdialog.h"
#include "framelessmainwindow.h"
@ -170,7 +171,7 @@ private:
template <typename Func>
inline QAction *newAction(const QString &title, Func &&slot,
const QKeySequence &shortcut = QKeySequence()) {
auto a = new QAction;
auto a = new QAction(this);
a->setText(title);
a->setShortcutVisibleInContextMenu(true);
a->setShortcut(shortcut);
@ -301,7 +302,7 @@ private:
ScriptEditor *_DebugingEditor;
QLabel *_status = nullptr;
ScrollableLabel *_status = nullptr;
};
#endif // SCRIPTINGDIALOG_H

View File

@ -113,21 +113,26 @@ void SettingDialog::on_buttonBox_clicked(QAbstractButton *button) {
page->apply();
}
} else if (button == btnbox->button(QDialogButtonBox::RestoreDefaults)) {
for (auto &page : m_pages) {
page->reset();
auto index = ui->listWidget->currentRow();
if (index >= 0) {
m_pages.at(index)->reset();
}
toastTakeEffectReboot();
} else if (button == btnbox->button(QDialogButtonBox::Reset)) {
auto res = WingMessageBox::warning(
this, qAppName(),
tr("This will reset all settings. Are you sure to continue?"),
QMessageBox::Yes | QMessageBox::No);
if (res == QMessageBox::No)
if (res == QMessageBox::No) {
return;
}
for (auto &page : m_pages) {
page->reset();
}
toastTakeEffectReboot();
} else if (button == btnbox->button(QDialogButtonBox::Cancel)) {
for (auto &page : m_pages) {
page->cancel();

View File

@ -135,7 +135,9 @@ QList<FindResultModel::FindInfo> &FindResultModel::findData() {
return m_findData;
}
QString &FindResultModel::lastFindData() { return m_lastFindData; }
QPair<QString, qsizetype> &FindResultModel::lastFindData() {
return m_lastFindData;
}
void FindResultModel::beginUpdate() { this->beginResetModel(); }

View File

@ -42,7 +42,7 @@ public:
QList<FindResult> &results();
QList<FindInfo> &findData();
QString &lastFindData();
QPair<QString, qsizetype> &lastFindData();
void beginUpdate();
void endUpdate();
@ -67,7 +67,7 @@ public:
private:
QList<FindResult> m_results;
QList<FindInfo> m_findData;
QString m_lastFindData;
QPair<QString, qsizetype> m_lastFindData;
QString m_encoding;
};

View File

@ -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

View File

@ -27,12 +27,6 @@
// 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
#ifndef AS_NO_IMPL_OPS_WITH_STRING_AND_PRIMITIVE
#define AS_NO_IMPL_OPS_WITH_STRING_AND_PRIMITIVE 0

View File

@ -47,7 +47,8 @@ void RegisterScriptRegex(asIScriptEngine *engine) {
r = engine->RegisterObjectBehaviour(
"exp", asBEHAVE_CONSTRUCT,
"void f(string &in, PatternOptions = PatternOptions::NoPatternOption)",
"void f(const string &in, PatternOptions = "
"regex::PatternOptions::NoPatternOption)",
asFUNCTIONPR(
[](void *memory, const QString &r, Angel::PatternOptions op) {
new (memory) QRegularExpression(
@ -169,7 +170,7 @@ void RegisterScriptRegex(asIScriptEngine *engine) {
// QRegularExpression...
r = engine->RegisterObjectMethod(
"exp", "void setPattern(string &in)",
"exp", "void setPattern(const string &in)",
asMETHODPR(QRegularExpression, setPattern, (const QString &), void),
asCALL_THISCALL);
Q_ASSERT(r >= 0);

View File

@ -129,10 +129,10 @@
<item row="1" column="1">
<widget class="QSpinBox" name="sbFontSize">
<property name="minimum">
<number>10</number>
<number>5</number>
</property>
<property name="maximum">
<number>50</number>
<number>25</number>
</property>
</widget>
</item>
@ -192,7 +192,7 @@
<string notr="true">-</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
@ -212,7 +212,7 @@
<string notr="true">-</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
@ -232,7 +232,7 @@
<string notr="true">-</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
@ -252,7 +252,7 @@
<string notr="true">-</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
@ -265,7 +265,7 @@
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>

View File

@ -46,8 +46,10 @@ void ScriptSettingDialog::loadData() {
auto &set = SettingManager::instance();
this->blockSignals(true);
ui->listWidget->clear();
ui->cbEnable->setChecked(set.scriptEnabled());
ui->cbAllowUsrScript->setChecked(set.allowUsrScriptInRoot());
ui->sbTimeout->setValue(set.scriptTimeout());
this->blockSignals(false);
if (set.scriptEnabled()) {
@ -112,6 +114,7 @@ void ScriptSettingDialog::apply() {
auto &set = SettingManager::instance();
set.setScriptEnabled(ui->cbEnable->isChecked());
set.setAllowUsrScriptInRoot(ui->cbAllowUsrScript->isChecked());
set.setScriptTimeout(ui->sbTimeout->value());
set.setUsrHideCats(usrHideCats);
set.setSysHideCats(sysHideCats);
set.save(SettingManager::SCRIPT);
@ -149,6 +152,9 @@ void ScriptSettingDialog::on_listWidget_currentRowChanged(int currentRow) {
info->append(tr("Name:") + meta.name);
info->append(tr("Author:") + meta.author);
info->append(tr("License:") + meta.license);
info->append(tr("ContextMenu:") + (meta.isContextMenu
? QStringLiteral("true")
: QStringLiteral("false")));
info->append(tr("HomePage:") + meta.homepage);
info->append(tr("Comment:"));
auto cur = info->textCursor();

View File

@ -40,6 +40,63 @@
</property>
</widget>
</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>
</widget>
</item>
@ -52,7 +109,7 @@
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QListWidget" name="listWidget"/>
<widget class="QWidget" name="layoutWidget">

View File

@ -52,9 +52,6 @@
#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) {
return ":/com.wingsummer.winghex/images/" + name + ".png";
}