Compare commits
25 Commits
8449bae812
...
56304a75d0
Author | SHA1 | Date |
---|---|---|
|
56304a75d0 | |
|
57bd7b7c61 | |
|
066ab5375b | |
|
1be1f7322f | |
|
b49523e952 | |
|
960de3afb6 | |
|
3f26766575 | |
|
098f08e906 | |
|
cb2e8743af | |
|
8ad73431c1 | |
|
db60affc22 | |
|
32e27ba543 | |
|
c01ca038a3 | |
|
d8069aedde | |
|
cf3d4da8e8 | |
|
2ee3051a7d | |
|
f59755e3f0 | |
|
ef8bb9aa3a | |
|
f87c0970be | |
|
f51630c6f0 | |
|
d6680e3f11 | |
|
7597663d76 | |
|
7ee69b5ced | |
|
570f78994c | |
|
5aaa59937d |
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -109,6 +109,15 @@ QString QConsoleWidget::getCommandLine() {
|
|||
return code;
|
||||
}
|
||||
|
||||
void QConsoleWidget::paste() {
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
const QMimeData *const clipboard = QApplication::clipboard()->mimeData();
|
||||
const QString text = clipboard->text();
|
||||
if (!text.isNull()) {
|
||||
textCursor.insertText(text, channelCharFormat(StandardInput));
|
||||
}
|
||||
}
|
||||
|
||||
void QConsoleWidget::handleReturnKey() {
|
||||
QString code = getCommandLine();
|
||||
|
||||
|
@ -189,12 +198,7 @@ void QConsoleWidget::keyPressEvent(QKeyEvent *e) {
|
|||
// Allow paste only if the selection is in the interactive area ...
|
||||
if (e->key() == Qt::Key_V && e->modifiers() == Qt::ControlModifier) {
|
||||
if (selectionInEditZone || isCursorInEditZone()) {
|
||||
const QMimeData *const clipboard =
|
||||
QApplication::clipboard()->mimeData();
|
||||
const QString text = clipboard->text();
|
||||
if (!text.isNull()) {
|
||||
textCursor.insertText(text, channelCharFormat(StandardInput));
|
||||
}
|
||||
paste();
|
||||
}
|
||||
|
||||
e->accept();
|
||||
|
@ -361,16 +365,15 @@ void QConsoleWidget::replaceCommandLine(const QString &str) {
|
|||
}
|
||||
|
||||
QString QConsoleWidget::currentCommandLine() const {
|
||||
// Select the text after the last command prompt ...
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
textCursor.movePosition(QTextCursor::End);
|
||||
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
|
||||
|
||||
return textCursor.selectedText();
|
||||
}
|
||||
|
||||
int QConsoleWidget::currentHeaderPos() const { return inpos_; }
|
||||
|
||||
void QConsoleWidget::write(const QString &message, const QTextCharFormat &fmt) {
|
||||
QTextCharFormat currfmt = currentCharFormat();
|
||||
QTextCursor tc = textCursor();
|
||||
|
||||
if (mode() == Input) {
|
||||
|
@ -396,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;
|
||||
|
@ -412,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);
|
||||
|
|
|
@ -77,6 +77,10 @@ public:
|
|||
// get the current command line
|
||||
QString getCommandLine();
|
||||
|
||||
virtual void paste();
|
||||
|
||||
int currentHeaderPos() const;
|
||||
|
||||
public slots:
|
||||
|
||||
// write to StandardOutput
|
||||
|
@ -94,6 +98,7 @@ signals:
|
|||
protected:
|
||||
bool canPaste() const;
|
||||
bool canCut() const;
|
||||
|
||||
virtual void handleReturnKey();
|
||||
virtual void handleTabKey();
|
||||
// reimp QPlainTextEdit functions
|
||||
|
@ -112,7 +117,6 @@ protected:
|
|||
static History history_;
|
||||
ConsoleMode mode_;
|
||||
int inpos_;
|
||||
QString currentMultiLineCode_;
|
||||
QConsoleIODevice *iodevice_;
|
||||
QTextCharFormat chanFormat_[nConsoleChannels];
|
||||
};
|
||||
|
|
|
@ -61,7 +61,7 @@ qsizetype QHexCursor::selectionLength(qsizetype index) const {
|
|||
|
||||
qsizetype QHexCursor::currentSelectionLength() const {
|
||||
if (hasPreviewSelection() && m_preMode != SelectionRemove) {
|
||||
return qAbs(m_position - m_selection + 1);
|
||||
return qAbs(m_position - m_selection) + 1;
|
||||
}
|
||||
|
||||
qsizetype len = 0;
|
||||
|
|
|
@ -437,44 +437,30 @@ void QHexDocument::findAllBytes(qsizetype begin, qsizetype end,
|
|||
}
|
||||
}
|
||||
|
||||
qsizetype QHexDocument::findAllBytesExt(qsizetype begin, qsizetype end,
|
||||
void QHexDocument::findAllBytesExt(qsizetype begin, qsizetype end,
|
||||
const QString &pattern,
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
pb.value |= quint8(v << 4);
|
||||
pb.mask |= 0xF0;
|
||||
}
|
||||
// low nibble
|
||||
if (lo != '?') {
|
||||
int v = hex2Int(lo);
|
||||
if (v < 0) {
|
||||
return false;
|
||||
}
|
||||
pb.value |= quint8(v);
|
||||
pb.mask |= 0x0F;
|
||||
}
|
||||
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<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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
while (begin < length()) {
|
||||
auto pos = begin;
|
||||
|
||||
auto p = patterns.cbegin();
|
||||
auto r = op(pos, *p, &begin);
|
||||
if (!r) {
|
||||
if (pos < 0) {
|
||||
const QList<PatternByte> &patterns) {
|
||||
if (patterns.isEmpty() || begin < 0 ||
|
||||
begin + patterns.size() > m_buffer->length())
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
++p;
|
||||
|
||||
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) {
|
||||
const QList<PatternByte> &patterns) {
|
||||
if (patterns.isEmpty() || begin < 0)
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
++p;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 charformat = textcursor.charFormat();
|
||||
textcursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor,
|
||||
factor - 1);
|
||||
charformat.setFontWeight(QFont::Bold);
|
||||
textcursor.setCharFormat(charformat);
|
||||
auto off = item % hexLineWidth();
|
||||
|
||||
qreal begin, width;
|
||||
auto height = lineHeight();
|
||||
|
||||
// add some paddings
|
||||
if (factor == Hex) {
|
||||
begin = getCellWidth() * off * 3 + 1;
|
||||
begin += getCellWidth() / 2;
|
||||
width = getCellWidth() * 2 + 2;
|
||||
textcursor.setPosition(off * factor);
|
||||
} else {
|
||||
begin = getCellWidth() * off + 1;
|
||||
width = getCellWidth();
|
||||
textcursor.setPosition(m_cursor->currentColumn());
|
||||
}
|
||||
|
||||
auto charformat = textcursor.charFormat();
|
||||
auto 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,155 +0,0 @@
|
|||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file commonly created by Windows Explorer
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares and remote volumes
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# Thumbnail cache files created by Windows
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
|
||||
# Windows Desktop Search
|
||||
*.pst
|
||||
*.ost
|
||||
*.log
|
||||
|
||||
# Compiled Object files, Static and Dynamic libs
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
*.a
|
||||
*.class
|
||||
*.so
|
||||
*.lib
|
||||
*.dll
|
||||
*.exe
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
|
||||
# Java
|
||||
*.class
|
||||
|
||||
# Eclipse
|
||||
.project
|
||||
.classpath
|
||||
.settings/
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
.vscodium/
|
||||
|
||||
# Node.js
|
||||
node_modules/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints/
|
||||
|
||||
# Thumbnails
|
||||
Thumbs/
|
||||
Thumbs.db
|
||||
|
||||
# macOS metadata
|
||||
._*
|
||||
|
||||
# TextMate
|
||||
*.tmproj
|
||||
*.tmproject
|
||||
.tmtags
|
||||
|
||||
# Sublime Text
|
||||
*.sublime-workspace
|
||||
*.sublime-project
|
||||
|
||||
# VS Code directories
|
||||
.vscode/
|
||||
|
||||
# CodeKit
|
||||
.codekit-config.json
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Compiled files
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Logs and databases
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
*.xml
|
||||
|
||||
# Binary and source packages
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.tar
|
||||
*.zip
|
||||
*.rar
|
||||
*.bin
|
||||
*.war
|
||||
*.ear
|
||||
*.sar
|
||||
*.bbl
|
||||
*.pdf
|
||||
*.xls
|
||||
*.xlsx
|
||||
*.ppt
|
||||
*.pptx
|
||||
|
||||
# Virtual environment
|
||||
venv/
|
||||
env/
|
||||
|
||||
### Manually Entered
|
||||
vim-debug/
|
||||
**/out/bin
|
||||
**/out/lib
|
||||
**/out/share
|
||||
_deps
|
||||
.cache/
|
||||
compile_commands.json
|
||||
*.bak
|
||||
docs/
|
||||
*.old
|
||||
|
||||
# clangd cache
|
||||
.cache/
|
||||
.vim/
|
||||
build/
|
||||
debug/
|
||||
realease/
|
||||
Release/
|
||||
Debug
|
|
@ -1,26 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(
|
||||
QJsonModel
|
||||
VERSION 0.0.2
|
||||
LANGUAGES C CXX
|
||||
# Save this for later: HOMEPAGE_URL <URL>
|
||||
DESCRIPTION
|
||||
"QJsonModel is a json tree model class for Qt6/C++17 based on QAbstractItemModel. MIT License."
|
||||
)
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
|
||||
|
||||
if(MSVC)
|
||||
string(APPEND CMAKE_CXX_FLAGS " /utf-8")
|
||||
string(APPEND CMAKE_C_FLAGS " /utf-8")
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
add_library(QJsonModel STATIC include/details/QUtf8.hpp include/QJsonModel.hpp
|
||||
QJsonModel.cpp)
|
||||
|
||||
target_link_libraries(QJsonModel PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 sacha schutz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,518 +0,0 @@
|
|||
/* QJsonModel.cpp
|
||||
* Copyright (c) 2011 SCHUTZ Sacha
|
||||
* Copyright © 2024 Saul D. Beniquez
|
||||
*
|
||||
* License:
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// NOLINTBEGIN
|
||||
|
||||
#include "QJsonModel.hpp"
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QFont>
|
||||
|
||||
inline bool contains(const QStringList &list, const QString &value) {
|
||||
for (auto val : list)
|
||||
if (value.contains(val, Qt::CaseInsensitive))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonTreeItem::QJsonTreeItem(QJsonTreeItem *parent) { mParent = parent; }
|
||||
|
||||
QJsonTreeItem::~QJsonTreeItem() { qDeleteAll(mChilds); }
|
||||
|
||||
void QJsonTreeItem::appendChild(QJsonTreeItem *item) { mChilds.append(item); }
|
||||
|
||||
QJsonTreeItem *QJsonTreeItem::child(int row) { return mChilds.value(row); }
|
||||
|
||||
QJsonTreeItem *QJsonTreeItem::parent() { return mParent; }
|
||||
|
||||
int QJsonTreeItem::childCount() const { return mChilds.count(); }
|
||||
|
||||
int QJsonTreeItem::row() const {
|
||||
if (mParent)
|
||||
return mParent->mChilds.indexOf(const_cast<QJsonTreeItem *>(this));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QJsonTreeItem::setKey(const QString &key) { mKey = key; }
|
||||
|
||||
void QJsonTreeItem::setValue(const QVariant &value) { mValue = value; }
|
||||
|
||||
void QJsonTreeItem::setType(const QJsonValue::Type &type) { mType = type; }
|
||||
|
||||
QString QJsonTreeItem::key() const { return mKey; }
|
||||
|
||||
QVariant QJsonTreeItem::value() const { return mValue; }
|
||||
|
||||
QJsonValue::Type QJsonTreeItem::type() const { return mType; }
|
||||
|
||||
QJsonTreeItem *QJsonTreeItem::load(const QJsonValue &value,
|
||||
const QStringList &exceptions,
|
||||
QJsonTreeItem *parent) {
|
||||
QJsonTreeItem *rootItem = new QJsonTreeItem(parent);
|
||||
rootItem->setKey("root");
|
||||
|
||||
if (value.isObject()) {
|
||||
// Get all QJsonValue childs
|
||||
const QStringList keys =
|
||||
value.toObject().keys(); // To prevent clazy-range warning
|
||||
for (const QString &key : keys) {
|
||||
if (contains(exceptions, key)) {
|
||||
continue;
|
||||
}
|
||||
QJsonValue v = value.toObject().value(key);
|
||||
QJsonTreeItem *child = load(v, exceptions, rootItem);
|
||||
child->setKey(key);
|
||||
child->setType(v.type());
|
||||
rootItem->appendChild(child);
|
||||
}
|
||||
} else if (value.isArray()) {
|
||||
// Get all QJsonValue childs
|
||||
int index = 0;
|
||||
const QJsonArray array =
|
||||
value.toArray(); // To prevent clazy-range warning
|
||||
for (const QJsonValue &v : array) {
|
||||
QJsonTreeItem *child = load(v, exceptions, rootItem);
|
||||
child->setKey(QString::number(index));
|
||||
child->setType(v.type());
|
||||
rootItem->appendChild(child);
|
||||
++index;
|
||||
}
|
||||
} else {
|
||||
rootItem->setValue(value.toVariant());
|
||||
rootItem->setType(value.type());
|
||||
}
|
||||
|
||||
return rootItem;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
|
||||
inline uchar hexdig(uint u) { return (u < 0xa ? '0' + u : 'a' + u - 0xa); }
|
||||
|
||||
QByteArray escapedString(const QString &s) {
|
||||
QByteArray ba(s.length(), Qt::Uninitialized);
|
||||
uchar *cursor =
|
||||
reinterpret_cast<uchar *>(const_cast<char *>(ba.constData()));
|
||||
const uchar *ba_end = cursor + ba.length();
|
||||
const ushort *src = reinterpret_cast<const ushort *>(s.constBegin());
|
||||
const ushort *const end = reinterpret_cast<const ushort *>(s.constEnd());
|
||||
while (src != end) {
|
||||
if (cursor >= ba_end - 6) {
|
||||
// ensure we have enough space
|
||||
int pos = cursor - reinterpret_cast<const uchar *>(ba.constData());
|
||||
ba.resize(ba.size() * 2);
|
||||
cursor = reinterpret_cast<uchar *>(ba.data()) + pos;
|
||||
ba_end =
|
||||
reinterpret_cast<const uchar *>(ba.constData()) + ba.length();
|
||||
}
|
||||
uint u = *src++;
|
||||
if (u < 0x80) {
|
||||
if (u < 0x20 || u == 0x22 || u == 0x5c) {
|
||||
*cursor++ = '\\';
|
||||
switch (u) {
|
||||
case 0x22:
|
||||
*cursor++ = '"';
|
||||
break;
|
||||
case 0x5c:
|
||||
*cursor++ = '\\';
|
||||
break;
|
||||
case 0x8:
|
||||
*cursor++ = 'b';
|
||||
break;
|
||||
case 0xc:
|
||||
*cursor++ = 'f';
|
||||
break;
|
||||
case 0xa:
|
||||
*cursor++ = 'n';
|
||||
break;
|
||||
case 0xd:
|
||||
*cursor++ = 'r';
|
||||
break;
|
||||
case 0x9:
|
||||
*cursor++ = 't';
|
||||
break;
|
||||
default:
|
||||
*cursor++ = 'u';
|
||||
*cursor++ = '0';
|
||||
*cursor++ = '0';
|
||||
*cursor++ = hexdig(u >> 4);
|
||||
*cursor++ = hexdig(u & 0xf);
|
||||
}
|
||||
} else {
|
||||
*cursor++ = (uchar)u;
|
||||
}
|
||||
} else if (QUtf8Functions::toUtf8<QUtf8BaseTraits>(u, cursor, src,
|
||||
end) < 0) {
|
||||
// failed to get valid utf8 use JSON escape sequence
|
||||
*cursor++ = '\\';
|
||||
*cursor++ = 'u';
|
||||
*cursor++ = hexdig(u >> 12 & 0x0f);
|
||||
*cursor++ = hexdig(u >> 8 & 0x0f);
|
||||
*cursor++ = hexdig(u >> 4 & 0x0f);
|
||||
*cursor++ = hexdig(u & 0x0f);
|
||||
}
|
||||
}
|
||||
ba.resize(cursor - reinterpret_cast<const uchar *>(ba.constData()));
|
||||
return ba;
|
||||
}
|
||||
|
||||
QJsonModel::QJsonModel(QObject *parent)
|
||||
: QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} {
|
||||
mHeaders.append(tr("key"));
|
||||
mHeaders.append(tr("value"));
|
||||
}
|
||||
|
||||
QJsonModel::QJsonModel(const QString &fileName, QObject *parent)
|
||||
: QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} {
|
||||
mHeaders.append(tr("key"));
|
||||
mHeaders.append(tr("value"));
|
||||
load(fileName);
|
||||
}
|
||||
|
||||
QJsonModel::QJsonModel(QIODevice *device, QObject *parent)
|
||||
: QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} {
|
||||
mHeaders.append(tr("key"));
|
||||
mHeaders.append(tr("value"));
|
||||
load(device);
|
||||
}
|
||||
|
||||
QJsonModel::QJsonModel(const QByteArray &json, QObject *parent)
|
||||
: QAbstractItemModel(parent), mRootItem{new QJsonTreeItem} {
|
||||
mHeaders.append(tr("key"));
|
||||
mHeaders.append(tr("value"));
|
||||
loadJson(json);
|
||||
}
|
||||
|
||||
QJsonModel::~QJsonModel() { delete mRootItem; }
|
||||
|
||||
bool QJsonModel::load(const QString &fileName) {
|
||||
QFile file(fileName);
|
||||
bool success = false;
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
success = load(&file);
|
||||
file.close();
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool QJsonModel::load(QIODevice *device) { return loadJson(device->readAll()); }
|
||||
|
||||
bool QJsonModel::loadJson(const QByteArray &json) {
|
||||
QJsonParseError error;
|
||||
auto const &jdoc = QJsonDocument::fromJson(json, &error);
|
||||
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!jdoc.isNull()) {
|
||||
beginResetModel();
|
||||
delete mRootItem;
|
||||
if (jdoc.isArray()) {
|
||||
mRootItem =
|
||||
QJsonTreeItem::load(QJsonValue(jdoc.array()), mExceptions);
|
||||
mRootItem->setType(QJsonValue::Array);
|
||||
|
||||
} else {
|
||||
mRootItem =
|
||||
QJsonTreeItem::load(QJsonValue(jdoc.object()), mExceptions);
|
||||
mRootItem->setType(QJsonValue::Object);
|
||||
}
|
||||
endResetModel();
|
||||
return true;
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << "cannot load json";
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant QJsonModel::data(const QModelIndex &index, int role) const {
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
|
||||
QJsonTreeItem *item = static_cast<QJsonTreeItem *>(index.internalPointer());
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
if (index.column() == 0)
|
||||
return QString("%1").arg(item->key());
|
||||
|
||||
if (index.column() == 1)
|
||||
return item->value();
|
||||
} else if (Qt::EditRole == role) {
|
||||
if (index.column() == 1)
|
||||
return item->value();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool QJsonModel::setData(const QModelIndex &index, const QVariant &value,
|
||||
int role) {
|
||||
int col = index.column();
|
||||
if (Qt::EditRole == role) {
|
||||
if (col == 1) {
|
||||
QJsonTreeItem *item =
|
||||
static_cast<QJsonTreeItem *>(index.internalPointer());
|
||||
item->setValue(value);
|
||||
emit dataChanged(index, index, {Qt::EditRole});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant QJsonModel::headerData(int section, Qt::Orientation orientation,
|
||||
int role) const {
|
||||
if (role != Qt::DisplayRole)
|
||||
return {};
|
||||
|
||||
if (orientation == Qt::Horizontal)
|
||||
return mHeaders.value(section);
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
QModelIndex QJsonModel::index(int row, int column,
|
||||
const QModelIndex &parent) const {
|
||||
if (!hasIndex(row, column, parent))
|
||||
return {};
|
||||
|
||||
QJsonTreeItem *parentItem;
|
||||
|
||||
if (!parent.isValid())
|
||||
parentItem = mRootItem;
|
||||
else
|
||||
parentItem = static_cast<QJsonTreeItem *>(parent.internalPointer());
|
||||
|
||||
QJsonTreeItem *childItem = parentItem->child(row);
|
||||
if (childItem)
|
||||
return createIndex(row, column, childItem);
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
QModelIndex QJsonModel::parent(const QModelIndex &index) const {
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
|
||||
QJsonTreeItem *childItem =
|
||||
static_cast<QJsonTreeItem *>(index.internalPointer());
|
||||
QJsonTreeItem *parentItem = childItem->parent();
|
||||
|
||||
if (parentItem == mRootItem)
|
||||
return QModelIndex();
|
||||
|
||||
return createIndex(parentItem->row(), 0, parentItem);
|
||||
}
|
||||
|
||||
int QJsonModel::rowCount(const QModelIndex &parent) const {
|
||||
QJsonTreeItem *parentItem;
|
||||
if (parent.column() > 0)
|
||||
return 0;
|
||||
|
||||
if (!parent.isValid())
|
||||
parentItem = mRootItem;
|
||||
else
|
||||
parentItem = static_cast<QJsonTreeItem *>(parent.internalPointer());
|
||||
|
||||
return parentItem->childCount();
|
||||
}
|
||||
|
||||
int QJsonModel::columnCount(const QModelIndex &parent) const {
|
||||
Q_UNUSED(parent)
|
||||
return 2;
|
||||
}
|
||||
|
||||
Qt::ItemFlags QJsonModel::flags(const QModelIndex &index) const {
|
||||
int col = index.column();
|
||||
auto item = static_cast<QJsonTreeItem *>(index.internalPointer());
|
||||
|
||||
auto isArray = QJsonValue::Array == item->type();
|
||||
auto isObject = QJsonValue::Object == item->type();
|
||||
|
||||
if ((col == 1) && !(isArray || isObject))
|
||||
return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
|
||||
else
|
||||
return QAbstractItemModel::flags(index);
|
||||
}
|
||||
|
||||
QByteArray QJsonModel::json(bool compact) {
|
||||
auto jsonValue = genJson(mRootItem);
|
||||
QByteArray json;
|
||||
if (jsonValue.isNull())
|
||||
return json;
|
||||
|
||||
if (jsonValue.isArray())
|
||||
arrayToJson(jsonValue.toArray(), json, 0, compact);
|
||||
else
|
||||
objectToJson(jsonValue.toObject(), json, 0, compact);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
void QJsonModel::objectToJson(QJsonObject jsonObject, QByteArray &json,
|
||||
int indent, bool compact) {
|
||||
json += compact ? "{" : "{\n";
|
||||
objectContentToJson(jsonObject, json, indent + (compact ? 0 : 1), compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += compact ? "}" : "}\n";
|
||||
}
|
||||
void QJsonModel::arrayToJson(QJsonArray jsonArray, QByteArray &json, int indent,
|
||||
bool compact) {
|
||||
json += compact ? "[" : "[\n";
|
||||
arrayContentToJson(jsonArray, json, indent + (compact ? 0 : 1), compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += compact ? "]" : "]\n";
|
||||
}
|
||||
|
||||
void QJsonModel::arrayContentToJson(QJsonArray jsonArray, QByteArray &json,
|
||||
int indent, bool compact) {
|
||||
if (jsonArray.size() <= 0)
|
||||
return;
|
||||
|
||||
QByteArray indentString(4 * indent, ' ');
|
||||
int i = 0;
|
||||
while (1) {
|
||||
json += indentString;
|
||||
valueToJson(jsonArray.at(i), json, indent, compact);
|
||||
if (++i == jsonArray.size()) {
|
||||
if (!compact)
|
||||
json += '\n';
|
||||
break;
|
||||
}
|
||||
json += compact ? "," : ",\n";
|
||||
}
|
||||
}
|
||||
void QJsonModel::objectContentToJson(QJsonObject jsonObject, QByteArray &json,
|
||||
int indent, bool compact) {
|
||||
if (jsonObject.size() <= 0)
|
||||
return;
|
||||
|
||||
QByteArray indentString(4 * indent, ' ');
|
||||
int i = 0;
|
||||
while (1) {
|
||||
QString key = jsonObject.keys().at(i);
|
||||
json += indentString;
|
||||
json += '"';
|
||||
json += escapedString(key);
|
||||
json += compact ? "\":" : "\": ";
|
||||
valueToJson(jsonObject.value(key), json, indent, compact);
|
||||
if (++i == jsonObject.size()) {
|
||||
if (!compact)
|
||||
json += '\n';
|
||||
break;
|
||||
}
|
||||
json += compact ? "," : ",\n";
|
||||
}
|
||||
}
|
||||
|
||||
void QJsonModel::valueToJson(QJsonValue jsonValue, QByteArray &json, int indent,
|
||||
bool compact) {
|
||||
QJsonValue::Type type = jsonValue.type();
|
||||
switch (type) {
|
||||
case QJsonValue::Bool:
|
||||
json += jsonValue.toBool() ? "true" : "false";
|
||||
break;
|
||||
case QJsonValue::Double: {
|
||||
const double d = jsonValue.toDouble();
|
||||
if (qIsFinite(d)) {
|
||||
json += QByteArray::number(d, 'f', QLocale::FloatingPointShortest);
|
||||
} else {
|
||||
json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QJsonValue::String:
|
||||
json += '"';
|
||||
json += escapedString(jsonValue.toString());
|
||||
json += '"';
|
||||
break;
|
||||
case QJsonValue::Array:
|
||||
json += compact ? "[" : "[\n";
|
||||
arrayContentToJson(jsonValue.toArray(), json,
|
||||
indent + (compact ? 0 : 1), compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += ']';
|
||||
break;
|
||||
case QJsonValue::Object:
|
||||
json += compact ? "{" : "{\n";
|
||||
objectContentToJson(jsonValue.toObject(), json,
|
||||
indent + (compact ? 0 : 1), compact);
|
||||
json += QByteArray(4 * indent, ' ');
|
||||
json += '}';
|
||||
break;
|
||||
case QJsonValue::Null:
|
||||
default:
|
||||
json += "null";
|
||||
}
|
||||
}
|
||||
|
||||
void QJsonModel::addException(const QStringList &exceptions) {
|
||||
mExceptions = exceptions;
|
||||
}
|
||||
|
||||
QJsonValue QJsonModel::genJson(QJsonTreeItem *item) const {
|
||||
auto type = item->type();
|
||||
int nchild = item->childCount();
|
||||
|
||||
if (QJsonValue::Object == type) {
|
||||
QJsonObject jo;
|
||||
for (int i = 0; i < nchild; ++i) {
|
||||
auto ch = item->child(i);
|
||||
auto key = ch->key();
|
||||
jo.insert(key, genJson(ch));
|
||||
}
|
||||
return jo;
|
||||
} else if (QJsonValue::Array == type) {
|
||||
QJsonArray arr;
|
||||
for (int i = 0; i < nchild; ++i) {
|
||||
auto ch = item->child(i);
|
||||
arr.append(genJson(ch));
|
||||
}
|
||||
return arr;
|
||||
} else {
|
||||
QJsonValue va;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
switch (item->value().typeId()) {
|
||||
#else
|
||||
switch (item->value().userType()) {
|
||||
#endif
|
||||
case QMetaType::Bool: {
|
||||
va = item->value().toBool();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
va = item->value().toString();
|
||||
break;
|
||||
}
|
||||
return va;
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
# QJsonModel
|
||||
QJsonModel is a JSON tree model class for Qt6/C++17 based on QAbstractItemModel.
|
||||
|
||||
QJsonModel was originally written by Sacha Shutz (https://github.com/dridk).
|
||||
|
||||
This fork is also released under the MIT License.
|
||||
|
||||
|
||||

|
||||
|
||||
## Build Instructions
|
||||
|
||||
### Build Tools
|
||||
- CMake (version 3.21 or higher)
|
||||
- C++17-compatible compiler
|
||||
|
||||
### Building the Project
|
||||
1. Clone the repository:
|
||||
```
|
||||
git clone <URL>
|
||||
```
|
||||
|
||||
2. Navigate to the project directory:
|
||||
```
|
||||
cd elemental-game
|
||||
```
|
||||
3. Configure your build system:
|
||||
```bash
|
||||
cmake -B debug -G Unix Makefiles
|
||||
# or
|
||||
cmake -B debug -G Ninja # this is faster and more modern
|
||||
```
|
||||
4. Invoke your build system
|
||||
```
|
||||
cmake --build debug
|
||||
```
|
||||
### Usage - CMake
|
||||
|
||||
You can add this library to your CMake projects using FetchContent()
|
||||
or CPM_AddPackage().
|
||||
|
||||
Here's how to do it with CPM_AddPackage:
|
||||
|
||||
```
|
||||
COMING SOON
|
||||
```
|
||||
|
||||
### Usage - C++
|
||||
|
||||
####
|
||||
```cpp
|
||||
QJsonModel * model = new QJsonModel;
|
||||
QTreeView * view = new QTreeView;
|
||||
view->setModel(model);
|
||||
model->load("example.json")
|
||||
```
|
|
@ -1,115 +0,0 @@
|
|||
/* QJsonModel.hpp
|
||||
* Copyright (c) 2011 SCHUTZ Sacha
|
||||
* Copyright © 2024 Saul D. Beniquez
|
||||
*
|
||||
* License:
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QIcon>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "details/QUtf8.hpp"
|
||||
|
||||
class QJsonModel;
|
||||
class QJsonItem;
|
||||
|
||||
class QJsonTreeItem {
|
||||
public:
|
||||
QJsonTreeItem(QJsonTreeItem *parent = nullptr);
|
||||
~QJsonTreeItem();
|
||||
void appendChild(QJsonTreeItem *item);
|
||||
QJsonTreeItem *child(int row);
|
||||
QJsonTreeItem *parent();
|
||||
int childCount() const;
|
||||
int row() const;
|
||||
void setKey(const QString &key);
|
||||
void setValue(const QVariant &value);
|
||||
void setType(const QJsonValue::Type &type);
|
||||
QString key() const;
|
||||
QVariant value() const;
|
||||
QJsonValue::Type type() const;
|
||||
|
||||
static QJsonTreeItem *load(const QJsonValue &value,
|
||||
const QStringList &exceptions = {},
|
||||
QJsonTreeItem *parent = nullptr);
|
||||
|
||||
protected:
|
||||
private:
|
||||
QString mKey;
|
||||
QVariant mValue;
|
||||
QJsonValue::Type mType;
|
||||
QList<QJsonTreeItem *> mChilds;
|
||||
QJsonTreeItem *mParent = nullptr;
|
||||
};
|
||||
|
||||
//---------------------------------------------------
|
||||
|
||||
class QJsonModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QJsonModel(QObject *parent = nullptr);
|
||||
QJsonModel(const QString &fileName, QObject *parent = nullptr);
|
||||
QJsonModel(QIODevice *device, QObject *parent = nullptr);
|
||||
QJsonModel(const QByteArray &json, QObject *parent = nullptr);
|
||||
~QJsonModel();
|
||||
bool load(const QString &fileName);
|
||||
bool load(QIODevice *device);
|
||||
bool loadJson(const QByteArray &json);
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value,
|
||||
int role = Qt::EditRole) override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role) const override;
|
||||
QModelIndex index(int row, int column,
|
||||
const QModelIndex &parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex &index) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
QByteArray json(bool compact = false);
|
||||
QByteArray jsonToByte(QJsonValue jsonValue);
|
||||
void objectToJson(QJsonObject jsonObject, QByteArray &json, int indent,
|
||||
bool compact);
|
||||
void arrayToJson(QJsonArray jsonArray, QByteArray &json, int indent,
|
||||
bool compact);
|
||||
void arrayContentToJson(QJsonArray jsonArray, QByteArray &json, int indent,
|
||||
bool compact);
|
||||
void objectContentToJson(QJsonObject jsonObject, QByteArray &json,
|
||||
int indent, bool compact);
|
||||
void valueToJson(QJsonValue jsonValue, QByteArray &json, int indent,
|
||||
bool compact);
|
||||
//! List of tags to skip during JSON parsing
|
||||
void addException(const QStringList &exceptions);
|
||||
|
||||
private:
|
||||
QJsonValue genJson(QJsonTreeItem *) const;
|
||||
QJsonTreeItem *mRootItem = nullptr;
|
||||
QStringList mHeaders;
|
||||
//! List of exceptions (e.g. comments). Case insensitive, compairs on
|
||||
//! "contains".
|
||||
QStringList mExceptions;
|
||||
};
|
|
@ -1,194 +0,0 @@
|
|||
/* QUtf8.hpp
|
||||
* Copyright © 2024 Saul D. Beniquez
|
||||
* License:
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <QJsonValue>
|
||||
|
||||
namespace QUtf8Functions {
|
||||
/// returns 0 on success; errors can only happen if \a u is a surrogate:
|
||||
/// Error if \a u is a low surrogate;
|
||||
/// if \a u is a high surrogate, Error if the next isn't a low one,
|
||||
/// EndOfString if we run into the end of the string.
|
||||
template <typename Traits, typename OutputPtr, typename InputPtr>
|
||||
inline int toUtf8(ushort u, OutputPtr &dst, InputPtr &src, InputPtr end) {
|
||||
if (!Traits::skipAsciiHandling && u < 0x80) {
|
||||
// U+0000 to U+007F (US-ASCII) - one byte
|
||||
Traits::appendByte(dst, uchar(u));
|
||||
return 0;
|
||||
} else if (u < 0x0800) {
|
||||
// U+0080 to U+07FF - two bytes
|
||||
// first of two bytes
|
||||
Traits::appendByte(dst, 0xc0 | uchar(u >> 6));
|
||||
} else {
|
||||
if (!QChar::isSurrogate(u)) {
|
||||
// U+0800 to U+FFFF (except U+D800-U+DFFF) - three bytes
|
||||
if (!Traits::allowNonCharacters && QChar::isNonCharacter(u))
|
||||
return Traits::Error;
|
||||
// first of three bytes
|
||||
Traits::appendByte(dst, 0xe0 | uchar(u >> 12));
|
||||
} else {
|
||||
// U+10000 to U+10FFFF - four bytes
|
||||
// need to get one extra codepoint
|
||||
if (Traits::availableUtf16(src, end) == 0)
|
||||
return Traits::EndOfString;
|
||||
ushort low = Traits::peekUtf16(src);
|
||||
if (!QChar::isHighSurrogate(u))
|
||||
return Traits::Error;
|
||||
if (!QChar::isLowSurrogate(low))
|
||||
return Traits::Error;
|
||||
Traits::advanceUtf16(src);
|
||||
uint ucs4 = QChar::surrogateToUcs4(u, low);
|
||||
if (!Traits::allowNonCharacters && QChar::isNonCharacter(ucs4))
|
||||
return Traits::Error;
|
||||
// first byte
|
||||
Traits::appendByte(dst, 0xf0 | (uchar(ucs4 >> 18) & 0xf));
|
||||
// second of four bytes
|
||||
Traits::appendByte(dst, 0x80 | (uchar(ucs4 >> 12) & 0x3f));
|
||||
// for the rest of the bytes
|
||||
u = ushort(ucs4);
|
||||
}
|
||||
// second to last byte
|
||||
Traits::appendByte(dst, 0x80 | (uchar(u >> 6) & 0x3f));
|
||||
}
|
||||
// last byte
|
||||
Traits::appendByte(dst, 0x80 | (u & 0x3f));
|
||||
return 0;
|
||||
}
|
||||
inline bool isContinuationByte(uchar b) { return (b & 0xc0) == 0x80; }
|
||||
/// returns the number of characters consumed (including \a b) in case of
|
||||
/// success; returns negative in case of error: Traits::Error or
|
||||
/// Traits::EndOfString
|
||||
template <typename Traits, typename OutputPtr, typename InputPtr>
|
||||
inline int fromUtf8(uchar b, OutputPtr &dst, InputPtr &src, InputPtr end) {
|
||||
int charsNeeded;
|
||||
uint min_uc;
|
||||
uint uc;
|
||||
if (!Traits::skipAsciiHandling && b < 0x80) {
|
||||
// US-ASCII
|
||||
Traits::appendUtf16(dst, b);
|
||||
return 1;
|
||||
}
|
||||
if (!Traits::isTrusted && Q_UNLIKELY(b <= 0xC1)) {
|
||||
// an UTF-8 first character must be at least 0xC0
|
||||
// however, all 0xC0 and 0xC1 first bytes can only produce overlong
|
||||
// sequences
|
||||
return Traits::Error;
|
||||
} else if (b < 0xe0) {
|
||||
charsNeeded = 2;
|
||||
min_uc = 0x80;
|
||||
uc = b & 0x1f;
|
||||
} else if (b < 0xf0) {
|
||||
charsNeeded = 3;
|
||||
min_uc = 0x800;
|
||||
uc = b & 0x0f;
|
||||
} else if (b < 0xf5) {
|
||||
charsNeeded = 4;
|
||||
min_uc = 0x10000;
|
||||
uc = b & 0x07;
|
||||
} else {
|
||||
// the last Unicode character is U+10FFFF
|
||||
// it's encoded in UTF-8 as "\xF4\x8F\xBF\xBF"
|
||||
// therefore, a byte higher than 0xF4 is not the UTF-8 first byte
|
||||
return Traits::Error;
|
||||
}
|
||||
int bytesAvailable = Traits::availableBytes(src, end);
|
||||
if (Q_UNLIKELY(bytesAvailable < charsNeeded - 1)) {
|
||||
// it's possible that we have an error instead of just unfinished bytes
|
||||
if (bytesAvailable > 0 && !isContinuationByte(Traits::peekByte(src, 0)))
|
||||
return Traits::Error;
|
||||
if (bytesAvailable > 1 && !isContinuationByte(Traits::peekByte(src, 1)))
|
||||
return Traits::Error;
|
||||
return Traits::EndOfString;
|
||||
}
|
||||
// first continuation character
|
||||
b = Traits::peekByte(src, 0);
|
||||
if (!isContinuationByte(b))
|
||||
return Traits::Error;
|
||||
uc <<= 6;
|
||||
uc |= b & 0x3f;
|
||||
if (charsNeeded > 2) {
|
||||
// second continuation character
|
||||
b = Traits::peekByte(src, 1);
|
||||
if (!isContinuationByte(b))
|
||||
return Traits::Error;
|
||||
uc <<= 6;
|
||||
uc |= b & 0x3f;
|
||||
if (charsNeeded > 3) {
|
||||
// third continuation character
|
||||
b = Traits::peekByte(src, 2);
|
||||
if (!isContinuationByte(b))
|
||||
return Traits::Error;
|
||||
uc <<= 6;
|
||||
uc |= b & 0x3f;
|
||||
}
|
||||
}
|
||||
// we've decoded something; safety-check it
|
||||
if (!Traits::isTrusted) {
|
||||
if (uc < min_uc)
|
||||
return Traits::Error;
|
||||
if (QChar::isSurrogate(uc) || uc > QChar::LastValidCodePoint)
|
||||
return Traits::Error;
|
||||
if (!Traits::allowNonCharacters && QChar::isNonCharacter(uc))
|
||||
return Traits::Error;
|
||||
}
|
||||
// write the UTF-16 sequence
|
||||
if (!QChar::requiresSurrogates(uc)) {
|
||||
// UTF-8 decoded and no surrogates are required
|
||||
// detach if necessary
|
||||
Traits::appendUtf16(dst, ushort(uc));
|
||||
} else {
|
||||
// UTF-8 decoded to something that requires a surrogate pair
|
||||
Traits::appendUcs4(dst, uc);
|
||||
}
|
||||
Traits::advanceByte(src, charsNeeded - 1);
|
||||
return charsNeeded;
|
||||
}
|
||||
} // namespace QUtf8Functions
|
||||
|
||||
struct QUtf8BaseTraits {
|
||||
static const bool isTrusted = false;
|
||||
static const bool allowNonCharacters = true;
|
||||
static const bool skipAsciiHandling = false;
|
||||
static const int Error = -1;
|
||||
static const int EndOfString = -2;
|
||||
static bool isValidCharacter(uint u) { return int(u) >= 0; }
|
||||
static void appendByte(uchar *&ptr, uchar b) { *ptr++ = b; }
|
||||
static uchar peekByte(const uchar *ptr, int n = 0) { return ptr[n]; }
|
||||
static qptrdiff availableBytes(const uchar *ptr, const uchar *end) {
|
||||
return end - ptr;
|
||||
}
|
||||
static void advanceByte(const uchar *&ptr, int n = 1) { ptr += n; }
|
||||
static void appendUtf16(ushort *&ptr, ushort uc) { *ptr++ = uc; }
|
||||
static void appendUcs4(ushort *&ptr, uint uc) {
|
||||
appendUtf16(ptr, QChar::highSurrogate(uc));
|
||||
appendUtf16(ptr, QChar::lowSurrogate(uc));
|
||||
}
|
||||
static ushort peekUtf16(const ushort *ptr, int n = 0) { return ptr[n]; }
|
||||
static qptrdiff availableUtf16(const ushort *ptr, const ushort *end) {
|
||||
return end - ptr;
|
||||
}
|
||||
static void advanceUtf16(const ushort *&ptr, int n = 1) { ptr += n; }
|
||||
// it's possible to output to UCS-4 too
|
||||
static void appendUtf16(uint *&ptr, ushort uc) { *ptr++ = uc; }
|
||||
static void appendUcs4(uint *&ptr, uint uc) { *ptr++ = uc; }
|
||||
};
|
Binary file not shown.
Before Width: | Height: | Size: 50 KiB |
|
@ -1 +1 @@
|
|||
Subproject commit fe0774c1d2ff5c86832d5850c1dccbbf3a6c3c66
|
||||
Subproject commit 3a5bb0d8bc45b6150ed4a7513b0712b0a5954a74
|
|
@ -1 +1 @@
|
|||
Subproject commit fac4d08fd0473a94d99c143c6ba6b1f9e0bd7636
|
||||
Subproject commit ce639ebfcec47a7c74233b4bab50017cb34e615b
|
|
@ -33,7 +33,8 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
|
|||
#endif
|
||||
prefix = id.section(QLatin1Char('/'), -1);
|
||||
}
|
||||
prefix.remove(QRegularExpression("[^a-zA-Z]"));
|
||||
static QRegularExpression regex("[^a-zA-Z]");
|
||||
prefix.remove(regex);
|
||||
prefix.truncate(6);
|
||||
|
||||
QByteArray idc = id.toUtf8();
|
||||
|
@ -69,6 +70,8 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
|
|||
lockFile.open(QIODevice::ReadWrite);
|
||||
}
|
||||
|
||||
QtLocalPeer::~QtLocalPeer() { server->close(); }
|
||||
|
||||
bool QtLocalPeer::isClient() {
|
||||
if (lockFile.isLocked())
|
||||
return false;
|
||||
|
|
|
@ -15,6 +15,8 @@ class QtLocalPeer : public QObject {
|
|||
|
||||
public:
|
||||
QtLocalPeer(QObject *parent = nullptr, const QString &appId = QString());
|
||||
~QtLocalPeer();
|
||||
|
||||
bool isClient();
|
||||
bool sendMessage(const QByteArray &uMsg, int timeout);
|
||||
QString applicationId() const { return id; }
|
||||
|
|
|
@ -8,7 +8,7 @@ set(CMAKE_AUTORCC ON)
|
|||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(PROJECT_VERSION "2.2.1")
|
||||
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,13 +40,10 @@ 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)
|
||||
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
|
||||
add_definitions(-DNOMINMAX)
|
||||
endif()
|
||||
|
||||
if(${QT_VERSION_MAJOR} EQUAL 5)
|
||||
|
@ -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)
|
||||
|
@ -79,7 +80,6 @@ add_subdirectory(3rdparty/QHexView)
|
|||
add_subdirectory(3rdparty/WingCodeEdit)
|
||||
add_subdirectory(3rdparty/Qt-Advanced-Docking-System)
|
||||
add_subdirectory(3rdparty/AngelScript/sdk/angelscript/projects/cmake)
|
||||
add_subdirectory(3rdparty/QJsonModel)
|
||||
|
||||
set(ANGEL_SCRIPT_ADDON_ROOT
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/AngelScript/sdk/add_on")
|
||||
|
@ -194,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
|
||||
|
@ -209,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
|
||||
|
@ -239,8 +239,6 @@ set(CLASS_SRC
|
|||
src/class/settingmanager.cpp
|
||||
src/class/asdebugger.h
|
||||
src/class/asdebugger.cpp
|
||||
src/class/scriptconsolemachine.h
|
||||
src/class/scriptconsolemachine.cpp
|
||||
src/class/angelobjstring.h
|
||||
src/class/angelobjstring.cpp
|
||||
src/class/scripteditortheme.h
|
||||
|
@ -256,8 +254,6 @@ set(CLASS_SRC
|
|||
src/class/ascompletion.h
|
||||
src/class/asbuilder.h
|
||||
src/class/asbuilder.cpp
|
||||
src/class/ascontextmgr.h
|
||||
src/class/ascontextmgr.cpp
|
||||
src/class/clangformatmanager.h
|
||||
src/class/clangformatmanager.cpp
|
||||
src/class/aspreprocesser.h
|
||||
|
@ -274,7 +270,6 @@ set(CLASS_SRC
|
|||
src/class/dockcomponentsfactory.cpp
|
||||
src/class/diffutil.h
|
||||
src/class/diffutil.cpp
|
||||
src/class/clickcallback.h
|
||||
src/class/crashhandler.h
|
||||
src/class/crashhandler.cpp
|
||||
src/class/pluginsystem.h
|
||||
|
@ -317,10 +312,6 @@ set(MODEL_SRC
|
|||
src/model/metadatamodel.cpp
|
||||
src/model/checksummodel.h
|
||||
src/model/checksummodel.cpp
|
||||
src/model/qjsontablemodel.h
|
||||
src/model/qjsontablemodel.cpp
|
||||
src/model/scriptobjmodel.h
|
||||
src/model/scriptobjmodel.cpp
|
||||
src/model/dbgcallstackmodel.h
|
||||
src/model/dbgcallstackmodel.cpp
|
||||
src/model/dbgvarshowmodel.h
|
||||
|
@ -364,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(
|
||||
|
@ -384,7 +376,6 @@ endforeach()
|
|||
set(TRANSLATION_PATH
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QConsoleWidget
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QHexView
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QJsonModel
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/Qt-Advanced-Docking-System/src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QWingRibbon
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
|
@ -518,7 +509,6 @@ target_link_libraries(
|
|||
WingPlugin
|
||||
QHexView
|
||||
WingCodeEdit
|
||||
QJsonModel
|
||||
angelscript
|
||||
qtadvanceddocking-qt${QT_VERSION_MAJOR})
|
||||
|
||||
|
|
|
@ -115,8 +115,7 @@
|
|||
|
||||
1. 本软件源代码不得私自应用于闭源商业用途,除非你完整开源(GPL协议的要求)。如果要将软件仓库的代码商用闭源,必须找我购买商业授权签订合同,价格私聊,非诚勿扰。
|
||||
2. 本软件是用我的业余时间编写,不能及时修复 Bug 或者提供技术支持,请见谅。
|
||||
3. 本人非计算机专业,编写程序难免有 Bug ,欢迎提交 PR 。
|
||||
4. 无论在什么时候有疑问,请详细阅读 Wiki 。
|
||||
3. 无论在什么时候有疑问,请详细阅读 Wiki 。
|
||||
|
||||
### 打包者必读
|
||||
|
||||
|
@ -128,7 +127,7 @@
|
|||
4. 包内最好含有主程序的校验和,但不强求。
|
||||
5. 无论是哪种打包形式,都一定要声明其开源协议和自由属性,不得隐藏而借机收费。虽然收费我并不反感,下载服务器还是需要资金维护的,但借着信息差骗人收费,这是我极其深恶痛绝的。
|
||||
6. 无论以任何为目的,如未获得我作者的授权,不得修改任意程序内指向的网络链接和软件关于信息,比如赞助和关于软件部分的内容等。
|
||||
7. 不得在安装程序内插入任何含有商业推广的插件,并且严格遵守第二条内容。
|
||||
7. 不得在安装程序内插入任何含有商业推广的插件。
|
||||
|
||||
### issue 前必读
|
||||
|
||||
|
@ -174,7 +173,7 @@
|
|||
|
||||
## WIKI
|
||||
|
||||
  如果想学习羽云十六进制编辑器的使用以及如何开发该软件的插件,请到 [该链接](https://github.com/Wing-summer/WingHexExplorer2/wiki/%E7%AE%80%E4%BB%8B) ,同时也欢迎大家指出 Wiki 的错误以及贡献优质内容。
|
||||
  如果想学习羽云十六进制编辑器的使用以及如何开发该软件的插件,请到 [该链接](https://wing-summer.github.io/WingHexExplorer2/docs/zh/credits.html) ,同时也欢迎大家指出 Wiki 的错误以及贡献优质内容。
|
||||
|
||||
## 插件库
|
||||
|
||||
|
|
|
@ -115,8 +115,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 +127,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 +174,7 @@ Of course, there are other repositories as mirror for Chinese users (which will
|
|||
|
||||
## WIKI
|
||||
|
||||
  If you want to learn how to use WingHexEditor and how to develop plug-ins for the software, please go to this link: `Not available yet`. At the same time, you are also welcome to point out errors in the Wiki and contribute high-quality content.
|
||||
  If you want to learn how to use WingHexEditor and how to develop plug-ins for the software, please go to [this link](https://wing-summer.github.io/WingHexExplorer2/docs/zh/credits.html) (Chinese only). At the same time, you are also welcome to point out errors in the Wiki and contribute high-quality content.
|
||||
|
||||
## Plugins
|
||||
|
||||
|
|
|
@ -61,8 +61,6 @@ add_library(
|
|||
ctltestform.h
|
||||
ctltestform.cpp
|
||||
ctltestform.ui
|
||||
testtablemodel.h
|
||||
testtablemodel.cpp
|
||||
testsettingpage.h
|
||||
testsettingpage.cpp
|
||||
testwingeditorviewwidget.h
|
||||
|
@ -91,4 +89,5 @@ if(TEST_MODE)
|
|||
${WINGHEX_PLUGIN_PATH})
|
||||
endif()
|
||||
|
||||
target_link_libraries(TestPlugin PRIVATE Qt${QT_VERSION_MAJOR}::Widgets WingPlugin)
|
||||
target_link_libraries(TestPlugin PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
|
||||
WingPlugin)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
# You can redistribute this file and/or modify it under the terms of the BSD
|
||||
# 3-Clause.
|
||||
#
|
||||
# THIS FILE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
|
||||
# THIS FILE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
|
|
|
@ -185,12 +185,12 @@
|
|||
<translation>控制器</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../testform.ui" line="1184"/>
|
||||
<location filename="../testform.ui" line="1055"/>
|
||||
<source>Others</source>
|
||||
<translation>其他</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../testform.ui" line="1245"/>
|
||||
<location filename="../testform.ui" line="1116"/>
|
||||
<source>Other APIs should be Test With Script</source>
|
||||
<translation>其他 API 应该用脚本测试,不提供可视化方式</translation>
|
||||
</message>
|
||||
|
@ -231,7 +231,6 @@
|
|||
<location filename="../testform.ui" line="311"/>
|
||||
<location filename="../testform.ui" line="707"/>
|
||||
<location filename="../testform.ui" line="881"/>
|
||||
<location filename="../testform.ui" line="1070"/>
|
||||
<source>Type</source>
|
||||
<translation>类型</translation>
|
||||
</message>
|
||||
|
@ -266,32 +265,6 @@
|
|||
<source>Color</source>
|
||||
<translation>颜色</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../testform.ui" line="1055"/>
|
||||
<source>DataVisual</source>
|
||||
<translation>数据可视化</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../testform.cpp" line="340"/>
|
||||
<location filename="../testform.cpp" line="349"/>
|
||||
<source>UpdateTextTreeError</source>
|
||||
<translation>更新文本树失败</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../testform.cpp" line="364"/>
|
||||
<source>UpdateTextListByModelError</source>
|
||||
<translation>通过模型更新文本列表失败</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../testform.cpp" line="374"/>
|
||||
<source>UpdateTextTableByModelError</source>
|
||||
<translation>通过模型更新文本表格失败</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../testform.cpp" line="385"/>
|
||||
<source>UpdateTextTreeByModelError</source>
|
||||
<translation>通过模型更新文本树失败</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TestPlugin</name>
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
#include "ctltestform.h"
|
||||
#include "readertestform.h"
|
||||
#include "testtablemodel.h"
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QFileSystemModel>
|
||||
|
@ -37,8 +36,6 @@ TestForm::TestForm(WingHex::IWingPlugin *plg, QWidget *parent)
|
|||
: WingHex::WingPluginWidget(plg, parent), ui(new Ui::TestForm) {
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->teDataVisual->setAcceptRichText(false);
|
||||
|
||||
ui->saReader->widget()->layout()->addWidget(
|
||||
new ReaderTestForm(plg, ui->tbReaderLogger, this));
|
||||
ui->saCtl->widget()->layout()->addWidget(
|
||||
|
@ -56,10 +53,6 @@ TestForm::TestForm(WingHex::IWingPlugin *plg, QWidget *parent)
|
|||
|
||||
ui->lblauthor->setPixmap(
|
||||
WingHex::HOSTRESPIMG(QStringLiteral("author"), QStringLiteral(".jpg")));
|
||||
|
||||
_click = std::bind(&TestForm::onDVClicked, this, std::placeholders::_1);
|
||||
_dblclick =
|
||||
std::bind(&TestForm::onDVDoubleClicked, this, std::placeholders::_1);
|
||||
}
|
||||
|
||||
TestForm::~TestForm() { delete ui; }
|
||||
|
@ -153,16 +146,6 @@ QFileDialog::Options TestForm::getFileDialogOptions() const {
|
|||
return options;
|
||||
}
|
||||
|
||||
void TestForm::onDVClicked(const QModelIndex &index) {
|
||||
logWarn(QStringLiteral("[Test - Click] ") +
|
||||
index.model()->data(index).toString());
|
||||
}
|
||||
|
||||
void TestForm::onDVDoubleClicked(const QModelIndex &index) {
|
||||
msgWarning(this, QStringLiteral("Test - DoubleClick"),
|
||||
index.model()->data(index).toString());
|
||||
}
|
||||
|
||||
void TestForm::on_btnSendLog_clicked() {
|
||||
auto txt = ui->leLogText->text();
|
||||
switch (Level(ui->cbLogLevel->currentIndex())) {
|
||||
|
@ -323,69 +306,6 @@ void TestForm::on_btnGetColor_clicked() {
|
|||
}
|
||||
}
|
||||
|
||||
void TestForm::on_btnText_2_clicked() {
|
||||
dataVisualText(ui->teDataVisual->toPlainText(), QStringLiteral("TestForm"));
|
||||
}
|
||||
|
||||
void TestForm::on_btnTextList_clicked() {
|
||||
auto txts = ui->teDataVisual->toPlainText().split('\n');
|
||||
dataVisualTextList(txts, QStringLiteral("TestForm"), _click, _dblclick);
|
||||
}
|
||||
|
||||
void TestForm::on_btnTextTree_clicked() {
|
||||
auto ret =
|
||||
dataVisualTextTree(ui->teDataVisual->toPlainText(),
|
||||
QStringLiteral("TestForm"), _click, _dblclick);
|
||||
if (!ret) {
|
||||
msgCritical(this, QStringLiteral("Test"), tr("UpdateTextTreeError"));
|
||||
}
|
||||
}
|
||||
|
||||
void TestForm::on_btnTextTable_clicked() {
|
||||
auto ret = dataVisualTextTable(
|
||||
ui->teDataVisual->toPlainText(), {"wingsummer", "wingsummer"}, {},
|
||||
QStringLiteral("TestForm"), _click, _dblclick);
|
||||
if (!ret) {
|
||||
msgCritical(this, QStringLiteral("Test"), tr("UpdateTextTreeError"));
|
||||
}
|
||||
}
|
||||
|
||||
void TestForm::on_btnTextListByModel_clicked() {
|
||||
auto model = new QStringListModel;
|
||||
QStringList buffer;
|
||||
for (int i = 0; i < WingHex::SDKVERSION; ++i) {
|
||||
buffer.append("wingsummer" % QString::number(i));
|
||||
}
|
||||
model->setStringList(buffer);
|
||||
auto ret = dataVisualTextListByModel(model, QStringLiteral("TestForm"),
|
||||
_click, _dblclick);
|
||||
if (!ret) {
|
||||
msgCritical(this, QStringLiteral("Test"),
|
||||
tr("UpdateTextListByModelError"));
|
||||
}
|
||||
}
|
||||
|
||||
void TestForm::on_btnTextTableByModel_clicked() {
|
||||
auto model = new TestTableModel;
|
||||
auto ret = dataVisualTextTableByModel(model, QStringLiteral("TestForm"),
|
||||
_click, _dblclick);
|
||||
if (!ret) {
|
||||
msgCritical(this, QStringLiteral("Test"),
|
||||
tr("UpdateTextTableByModelError"));
|
||||
}
|
||||
}
|
||||
|
||||
void TestForm::on_btnTextTreeByModel_clicked() {
|
||||
auto model = new QFileSystemModel;
|
||||
model->setRootPath(QDir::currentPath());
|
||||
auto ret = dataVisualTextTreeByModel(model, QStringLiteral("TestForm"),
|
||||
_click, _dblclick);
|
||||
if (!ret) {
|
||||
msgCritical(this, QStringLiteral("Test"),
|
||||
tr("UpdateTextTreeByModelError"));
|
||||
}
|
||||
}
|
||||
|
||||
void TestForm::on_btnStatusVisible_clicked() {
|
||||
if (ui->rbLockedFile->isChecked()) {
|
||||
Q_UNUSED(setLockedFile(true));
|
||||
|
|
|
@ -85,20 +85,6 @@ private slots:
|
|||
|
||||
void on_btnGetColor_clicked();
|
||||
|
||||
void on_btnText_2_clicked();
|
||||
|
||||
void on_btnTextList_clicked();
|
||||
|
||||
void on_btnTextTree_clicked();
|
||||
|
||||
void on_btnTextTable_clicked();
|
||||
|
||||
void on_btnTextListByModel_clicked();
|
||||
|
||||
void on_btnTextTableByModel_clicked();
|
||||
|
||||
void on_btnTextTreeByModel_clicked();
|
||||
|
||||
void on_btnStatusVisible_clicked();
|
||||
|
||||
void on_btnStatusInvisible_clicked();
|
||||
|
@ -121,15 +107,8 @@ private:
|
|||
|
||||
QFileDialog::Options getFileDialogOptions() const;
|
||||
|
||||
void onDVClicked(const QModelIndex &index);
|
||||
|
||||
void onDVDoubleClicked(const QModelIndex &index);
|
||||
|
||||
private:
|
||||
Ui::TestForm *ui;
|
||||
|
||||
WingHex::ClickedCallBack _click;
|
||||
WingHex::ClickedCallBack _dblclick;
|
||||
};
|
||||
|
||||
#endif // TESTFORM_H
|
||||
|
|
|
@ -1050,135 +1050,6 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_8">
|
||||
<attribute name="title">
|
||||
<string>DataVisual</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_20">
|
||||
<item>
|
||||
<widget class="QTextEdit" name="teDataVisual"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_6">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>150</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SizeConstraint::SetMinAndMaxSize</enum>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="btnTextList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">updateTextList (LineByLine)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="btnTextTree">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">updateTextTree (Json)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="btnTextTable">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">updateTextTable (Json)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="btnTextListByModel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">updateTextListByModel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="btnText_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">updateText</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QPushButton" name="btnTextTableByModel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">updateTextTableByModel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QPushButton" name="btnTextTreeByModel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">updateTextTreeByModel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" rowspan="2">
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string notr="true">WingSummer</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_9">
|
||||
<attribute name="title">
|
||||
<string>Others</string>
|
||||
|
@ -1278,18 +1149,18 @@
|
|||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>btnClearCtl</sender>
|
||||
<sender>btnClearFile</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>tbCtlLogger</receiver>
|
||||
<receiver>tbFileLogger</receiver>
|
||||
<slot>clear()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>271</x>
|
||||
<y>387</y>
|
||||
<x>239</x>
|
||||
<y>388</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>318</x>
|
||||
<y>264</y>
|
||||
<x>290</x>
|
||||
<y>328</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
|
@ -1310,34 +1181,34 @@
|
|||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>btnClearInput</sender>
|
||||
<sender>btnClearCtl</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>tbInputLogger</receiver>
|
||||
<receiver>tbCtlLogger</receiver>
|
||||
<slot>clear()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>210</x>
|
||||
<y>388</y>
|
||||
<x>271</x>
|
||||
<y>387</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>236</x>
|
||||
<y>332</y>
|
||||
<x>318</x>
|
||||
<y>264</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>btnClearFile</sender>
|
||||
<sender>btnClearText</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>tbFileLogger</receiver>
|
||||
<receiver>leToastText</receiver>
|
||||
<slot>clear()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>239</x>
|
||||
<y>388</y>
|
||||
<x>569</x>
|
||||
<y>271</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>290</x>
|
||||
<y>328</y>
|
||||
<x>468</x>
|
||||
<y>229</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
|
@ -1358,18 +1229,18 @@
|
|||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>btnClearText</sender>
|
||||
<sender>btnClearInput</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>leToastText</receiver>
|
||||
<receiver>tbInputLogger</receiver>
|
||||
<slot>clear()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>569</x>
|
||||
<y>271</y>
|
||||
<x>210</x>
|
||||
<y>388</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>468</x>
|
||||
<y>229</y>
|
||||
<x>236</x>
|
||||
<y>332</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
/*==============================================================================
|
||||
** Copyright (C) 2024-2027 WingSummer
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is
|
||||
** furnished to do so.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
** THE SOFTWARE.
|
||||
** =============================================================================
|
||||
*/
|
||||
|
||||
#include "testtablemodel.h"
|
||||
#include "WingPlugin/iwingpluginbase.h"
|
||||
|
||||
TestTableModel::TestTableModel(QObject *parent) : QAbstractTableModel(parent) {}
|
||||
|
||||
int TestTableModel::rowCount(const QModelIndex &parent) const {
|
||||
Q_UNUSED(parent);
|
||||
return WingHex::SDKVERSION;
|
||||
}
|
||||
|
||||
int TestTableModel::columnCount(const QModelIndex &parent) const {
|
||||
Q_UNUSED(parent);
|
||||
return 5;
|
||||
}
|
||||
|
||||
QVariant TestTableModel::data(const QModelIndex &index, int role) const {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::ToolTipRole: {
|
||||
return QStringLiteral("(%1, %2)").arg(index.row()).arg(index.column());
|
||||
}
|
||||
case Qt::TextAlignmentRole:
|
||||
return Qt::AlignCenter;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant TestTableModel::headerData(int section, Qt::Orientation orientation,
|
||||
int role) const {
|
||||
Q_UNUSED(orientation);
|
||||
if (role == Qt::DisplayRole) {
|
||||
return section + 1;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*==============================================================================
|
||||
** Copyright (C) 2024-2027 WingSummer
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and associated documentation files (the "Software"), to deal
|
||||
** in the Software without restriction, including without limitation the rights
|
||||
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
** copies of the Software, and to permit persons to whom the Software is
|
||||
** furnished to do so.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
** THE SOFTWARE.
|
||||
** =============================================================================
|
||||
*/
|
||||
|
||||
#ifndef TESTTABLEMODEL_H
|
||||
#define TESTTABLEMODEL_H
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
|
||||
class TestTableModel : public QAbstractTableModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TestTableModel(QObject *parent = nullptr);
|
||||
|
||||
// QAbstractItemModel interface
|
||||
public:
|
||||
virtual int rowCount(const QModelIndex &parent) const override;
|
||||
virtual int columnCount(const QModelIndex &parent) const override;
|
||||
virtual QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role) const override;
|
||||
};
|
||||
|
||||
#endif // TESTTABLEMODEL_H
|
|
@ -1 +1 @@
|
|||
Subproject commit 95b3250011d0bf76ebd77f9905dc77fd04a46ce7
|
||||
Subproject commit 1f53c308498e529e5cfbed16c9f7f68c77a3e8e5
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -41,7 +41,7 @@ def run_command_interactive(command):
|
|||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="mkdeb.py", description="A deb installer maker for WingHexExplorer2")
|
||||
prog="mkinnopak.py", description="A InnoSetup installer maker for WingHexExplorer2")
|
||||
|
||||
parser.add_argument(
|
||||
"folder", help="A folder that has contained the binary build", type=str
|
||||
|
@ -275,7 +275,7 @@ Source: {#MyAppExePath}; DestDir: "{app}"; Flags: ignoreversion
|
|||
iss_content += r'Root: HKCR; Subkey: "WingHexExplorer2\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppExeName},1"; Flags: noerror uninsdeletevalue; ' + '\n'
|
||||
iss_content += r'Root: HKCR; Subkey: "WingHexExplorer2\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1""" ;Flags: noerror uninsdeletevalue; ' + '\n'
|
||||
|
||||
iss_content += """
|
||||
iss_content += r"""
|
||||
[Icons]
|
||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
|
||||
|
|
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
@ -55,6 +55,7 @@
|
|||
<item>typedef</item>
|
||||
<item>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="'"/>
|
||||
<StringDetect attribute="Text Block" context="TextBlock" String="""""/>
|
||||
<DetectChar attribute="String" context="String" char="""/>
|
||||
<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="""/>
|
||||
<DetectChar attribute="Char" context="#pop" char="'"/>
|
||||
</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"/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"Id": "WingAngelAPI",
|
||||
"Author": "wingsummer",
|
||||
"Version": "2.1.0",
|
||||
"Version": "2.2.2",
|
||||
"Vendor": "WingCloudStudio",
|
||||
"License": "AGPL-3.0",
|
||||
"Url": "https://github.com/Wing-summer/WingHexExplorer2"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"Id" : "WingCStruct",
|
||||
"Version" : "0.0.1",
|
||||
"Version" : "0.0.2",
|
||||
"Vendor" : "WingCloudStudio",
|
||||
"Author" : "wingsummer",
|
||||
"License" : "AGPL-v3.0",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
// ==================================================
|
||||
|
|
|
@ -45,6 +45,8 @@ AppManager::AppManager(int &argc, char *argv[])
|
|||
: QtSingleApplication(argc, argv) {
|
||||
ASSERT_SINGLETON;
|
||||
|
||||
_instance = this;
|
||||
|
||||
LanguageManager::instance();
|
||||
InspectQtLogHelper::instance().init();
|
||||
CrashHandler::instance().init();
|
||||
|
@ -102,6 +104,8 @@ AppManager::AppManager(int &argc, char *argv[])
|
|||
his.add(cmd);
|
||||
}
|
||||
|
||||
_timer.start();
|
||||
|
||||
_w = new MainWindow(splash);
|
||||
|
||||
setActivationWindow(_w);
|
||||
|
@ -133,18 +137,18 @@ AppManager::AppManager(int &argc, char *argv[])
|
|||
|
||||
connect(_w, &MainWindow::closed, this,
|
||||
[]() { AppManager::instance()->exit(); });
|
||||
_instance = this;
|
||||
|
||||
if (splash)
|
||||
splash->close();
|
||||
}
|
||||
|
||||
AppManager::~AppManager() {
|
||||
InspectQtLogHelper::instance().destory();
|
||||
ClangFormatManager::instance().save();
|
||||
// CommandHistoryManager::save(QConsoleWidget::history().strings_);
|
||||
ScriptMachine::instance().deleteLater();
|
||||
InspectQtLogHelper::instance().destory();
|
||||
CommandHistoryManager::save(QConsoleWidget::history().strings_);
|
||||
|
||||
_w->deleteLater();
|
||||
delete _w;
|
||||
_w = nullptr;
|
||||
}
|
||||
|
||||
|
@ -152,6 +156,8 @@ AppManager *AppManager::instance() { return _instance; }
|
|||
|
||||
MainWindow *AppManager::mainWindow() const { return _w; }
|
||||
|
||||
quint64 AppManager::currentMSecsSinceEpoch() { return _timer.elapsed(); }
|
||||
|
||||
void AppManager::openFile(const QString &file, bool autoDetect) {
|
||||
EditorView *editor = nullptr;
|
||||
Q_ASSERT(_w);
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "dialog/mainwindow.h"
|
||||
#include "qtsingleapplication/src/qtsingleapplication.h"
|
||||
|
||||
#include <QElapsedTimer>
|
||||
|
||||
class AppManager : public QtSingleApplication {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -36,6 +38,8 @@ public:
|
|||
QApplication::tr("WingCloudStudio");
|
||||
}
|
||||
|
||||
quint64 currentMSecsSinceEpoch();
|
||||
|
||||
public slots:
|
||||
void openFile(const QString &file, bool autoDetect = true);
|
||||
void openRawFile(const QString &file);
|
||||
|
@ -43,6 +47,8 @@ public slots:
|
|||
|
||||
private:
|
||||
MainWindow *_w = nullptr;
|
||||
QElapsedTimer _timer;
|
||||
|
||||
static AppManager *_instance;
|
||||
};
|
||||
|
||||
|
|
|
@ -24,36 +24,17 @@
|
|||
|
||||
asBuilder::asBuilder(asIScriptEngine *engine) : AsPreprocesser(engine) {}
|
||||
|
||||
asBuilder::~asBuilder() {
|
||||
if (module) {
|
||||
module->Discard();
|
||||
}
|
||||
}
|
||||
|
||||
int asBuilder::StartNewModule(const char *moduleName) {
|
||||
if (module) {
|
||||
module->Discard();
|
||||
}
|
||||
|
||||
module = engine->GetModule(moduleName, asGM_ALWAYS_CREATE);
|
||||
if (module == nullptr)
|
||||
return -1;
|
||||
|
||||
ClearAll();
|
||||
return 0;
|
||||
}
|
||||
|
||||
asIScriptModule *asBuilder::GetModule() { return module; }
|
||||
|
||||
int asBuilder::Build() {
|
||||
int asBuilder::build(asIScriptModule *module) {
|
||||
Q_ASSERT(module);
|
||||
if (module == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
module->ResetGlobalVars();
|
||||
|
||||
for (auto &mod : modifiedScripts) {
|
||||
module->AddScriptSection(mod.section.toUtf8(), mod.script.data(),
|
||||
mod.script.size(), mod.lineOffset);
|
||||
mod.script.size());
|
||||
}
|
||||
|
||||
int r = module->Build();
|
||||
|
|
|
@ -27,19 +27,10 @@
|
|||
class asBuilder : public AsPreprocesser {
|
||||
public:
|
||||
explicit asBuilder(asIScriptEngine *engine);
|
||||
virtual ~asBuilder();
|
||||
|
||||
// Start a new module
|
||||
virtual int StartNewModule(const char *moduleName);
|
||||
|
||||
// Build the added script sections
|
||||
virtual int Build();
|
||||
|
||||
// Returns the current module
|
||||
asIScriptModule *GetModule();
|
||||
|
||||
protected:
|
||||
asIScriptModule *module = nullptr;
|
||||
public:
|
||||
// build the added script sections
|
||||
int build(asIScriptModule *module);
|
||||
};
|
||||
|
||||
#endif // ASBUILDER_H
|
||||
|
|
|
@ -18,10 +18,16 @@
|
|||
#include "ascompletion.h"
|
||||
|
||||
#include "asdatabase.h"
|
||||
#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>
|
||||
#include <QApplication>
|
||||
#include <QByteArray>
|
||||
#include <QDir>
|
||||
#include <QEvent>
|
||||
|
@ -36,17 +42,17 @@ 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(asIScriptEngine *engine, WingCodeEdit *p)
|
||||
: WingCompleter(p), parser(engine), _engine(engine),
|
||||
m_parseDocument(false) {
|
||||
Q_ASSERT(engine);
|
||||
|
||||
AsCompletion::AsCompletion(WingCodeEdit *p)
|
||||
: WingCompleter(p), parser(ScriptMachine::instance().engine()) {
|
||||
setTriggerList({*DOT_TRIGGER, *DBL_COLON_TRIGGER,
|
||||
// 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),
|
||||
|
@ -63,8 +69,10 @@ AsCompletion::AsCompletion(asIScriptEngine *engine, WingCodeEdit *p)
|
|||
|
||||
AsCompletion::~AsCompletion() {}
|
||||
|
||||
void AsCompletion::applyEmptyNsNode(QList<CodeInfoTip> &nodes) {
|
||||
void AsCompletion::applyEmptyNsNode(QList<CodeInfoTip> &nodes,
|
||||
const QList<CodeInfoTip> &docNodes) {
|
||||
static QList<CodeInfoTip> emptyNsNodes;
|
||||
|
||||
if (emptyNsNodes.isEmpty()) {
|
||||
auto &hn = parser.headerNodes();
|
||||
for (auto p = hn.constKeyValueBegin(); p != hn.constKeyValueEnd();
|
||||
|
@ -80,7 +88,34 @@ void AsCompletion::applyEmptyNsNode(QList<CodeInfoTip> &nodes) {
|
|||
}
|
||||
emptyNsNodes.append(parser.keywordNodes());
|
||||
}
|
||||
nodes = emptyNsNodes;
|
||||
|
||||
nodes.clear();
|
||||
|
||||
for (auto &p : docNodes) {
|
||||
if (p.nameSpace.isEmpty()) {
|
||||
nodes.append(p);
|
||||
} else {
|
||||
if (p.dontAddGlobal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CodeInfoTip tip;
|
||||
tip.type = CodeInfoTip::Type::Group;
|
||||
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);
|
||||
}
|
||||
|
||||
void AsCompletion::applyClassNodes(QList<CodeInfoTip> &nodes) {
|
||||
|
@ -91,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;
|
||||
}
|
||||
|
@ -105,43 +140,112 @@ void AsCompletion::applyClassNodes(QList<CodeInfoTip> &nodes) {
|
|||
nodes = clsNodes;
|
||||
}
|
||||
|
||||
bool AsCompletion::parseDocument() const { return m_parseDocument; }
|
||||
int AsCompletion::includeCallBack(const QString &include, bool quotedInclude,
|
||||
const QString &from, AsPreprocesser *builder,
|
||||
void *userParam) {
|
||||
auto p = reinterpret_cast<AsCompletion *>(userParam);
|
||||
|
||||
void AsCompletion::setParseDocument(bool newParseDocument) {
|
||||
m_parseDocument = newParseDocument;
|
||||
QFileInfo info(include);
|
||||
bool isAbsolute = info.isAbsolute();
|
||||
bool hasNoExt = info.suffix().isEmpty();
|
||||
QString inc;
|
||||
if (quotedInclude) {
|
||||
if (isAbsolute) {
|
||||
inc = include;
|
||||
} else {
|
||||
auto editor = qobject_cast<ScriptEditor *>(p->widget()->parent());
|
||||
Q_ASSERT(editor);
|
||||
auto pwd = QFileInfo(editor->fileName()).absoluteDir();
|
||||
inc = pwd.absoluteFilePath(include);
|
||||
}
|
||||
} else {
|
||||
// absolute include is not allowed in #include<>
|
||||
if (isAbsolute) {
|
||||
// ignored in code completion
|
||||
return asSUCCESS;
|
||||
}
|
||||
|
||||
QDir dir(qApp->applicationDirPath());
|
||||
if (!dir.cd(QStringLiteral("aslib"))) {
|
||||
// someone crash the software, ignored
|
||||
return asSUCCESS;
|
||||
}
|
||||
inc = dir.absoluteFilePath(include);
|
||||
}
|
||||
|
||||
if (hasNoExt) {
|
||||
inc += QStringLiteral(".as");
|
||||
}
|
||||
|
||||
builder->loadSectionFromFile(inc);
|
||||
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;
|
||||
}
|
||||
|
||||
void AsCompletion::processTrigger(const QString &trigger,
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trigger == *SEMI_COLON_TRIGGER) {
|
||||
clearFunctionTip();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto len = content.length();
|
||||
auto code = content.toUtf8();
|
||||
|
||||
QList<CodeInfoTip> nodes;
|
||||
|
||||
// TODO: PRs are welcomed !!!
|
||||
// If this software is well-known or brings me lots of
|
||||
// financial support, I will implement it myself.
|
||||
// PARSING THE DOCUMENT
|
||||
if (m_parseDocument) {
|
||||
nodes.append(parseDocument());
|
||||
}
|
||||
|
||||
if (!trigger.isEmpty() && trigger != *DOT_TRIGGER) {
|
||||
clearFunctionTip();
|
||||
}
|
||||
|
@ -155,12 +259,14 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
QByteArray content;
|
||||
};
|
||||
|
||||
auto engine = ScriptMachine::instance().engine();
|
||||
|
||||
// parse the tokens
|
||||
QVector<Token> tokens;
|
||||
qsizetype pos = 0;
|
||||
for (; p < end;) {
|
||||
asUINT tokenLen = 0;
|
||||
auto tt = _engine->ParseToken(p, len, &tokenLen);
|
||||
auto tt = engine->ParseToken(p, len, &tokenLen);
|
||||
if (tt == asTC_WHITESPACE) {
|
||||
p += tokenLen;
|
||||
pos += tokenLen;
|
||||
|
@ -203,22 +309,45 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
QByteArray fn;
|
||||
if (tokens.isEmpty()) {
|
||||
popup()->hide();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
QString prefix;
|
||||
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();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trigger.isEmpty() && popup()->isVisible()) {
|
||||
setCompletionPrefix(prefix);
|
||||
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
|
||||
if (trigger.isEmpty()) {
|
||||
// it can not be any trigger, so take the last as prefix
|
||||
prefix = etoken.content;
|
||||
tokens.removeLast();
|
||||
if (tokens.isEmpty()) {
|
||||
applyEmptyNsNode(nodes);
|
||||
applyEmptyNsNode(nodes, docNodes);
|
||||
} else {
|
||||
etoken = tokens.back(); // checking later
|
||||
}
|
||||
} else {
|
||||
prefix.clear();
|
||||
}
|
||||
|
||||
if (nodes.isEmpty()) {
|
||||
|
@ -227,21 +356,82 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
if (etoken.content == *DBL_COLON_TRIGGER) {
|
||||
processTrigger(*DBL_COLON_TRIGGER, content.left(etoken.pos));
|
||||
setCompletionPrefix(prefix);
|
||||
return;
|
||||
return true;
|
||||
} else if (etoken.content == *DOT_TRIGGER) {
|
||||
processTrigger(*DOT_TRIGGER, content.left(etoken.pos));
|
||||
setCompletionPrefix(prefix);
|
||||
return;
|
||||
return true;
|
||||
} else if (etoken.content == QByteArrayLiteral(")")) {
|
||||
// ignore
|
||||
} else {
|
||||
applyEmptyNsNode(nodes);
|
||||
applyEmptyNsNode(nodes, docNodes);
|
||||
}
|
||||
} else if (etoken.type != asTC_IDENTIFIER) {
|
||||
popup()->hide();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trigger == *DOT_TRIGGER) {
|
||||
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);
|
||||
};
|
||||
|
||||
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;
|
||||
} else {
|
||||
name += QStringLiteral("::") + item.name;
|
||||
}
|
||||
clsNodes.insert(name, item.children);
|
||||
}
|
||||
// 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);
|
||||
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(type)) {
|
||||
popup()->hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
nodes.append(clsNodes.value(type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nodes.isEmpty()) {
|
||||
applyClassNodes(nodes);
|
||||
}
|
||||
} else if (etoken.content.length() >= triggerAmount()) {
|
||||
// completion for a.b.c or a::b.c or a::b::c.d or ::a::b.c
|
||||
if (trigger == *DBL_COLON_TRIGGER) {
|
||||
|
@ -250,12 +440,19 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
if (idx >= 0) {
|
||||
if (tokens.at(idx).content == *DOT_TRIGGER) {
|
||||
popup()->hide();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
nodes = parser.headerNodes().value(ns);
|
||||
nodes = parser.headerNodes().value(ns) +
|
||||
parser.enumsNodes().value(ns);
|
||||
for (auto &n : docNodes) {
|
||||
if (n.nameSpace == ns) {
|
||||
nodes.append(n);
|
||||
}
|
||||
}
|
||||
|
||||
if (nodes.isEmpty()) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
} else if (trigger == *LEFT_PARE_TRIGGER) {
|
||||
// the first is function name, an identifier
|
||||
|
@ -271,12 +468,14 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
if (idx >= 0 && idx < tokens.length()) {
|
||||
if (tokens.at(idx).content == *DOT_TRIGGER) {
|
||||
popup()->hide();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
nodes = parser.headerNodes().value(ns);
|
||||
|
||||
if (nodes.isEmpty()) {
|
||||
applyEmptyNsNode(nodes);
|
||||
applyEmptyNsNode(nodes, docNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -284,9 +483,184 @@ void AsCompletion::processTrigger(const QString &trigger,
|
|||
|
||||
setModel(new CodeCompletionModel(nodes, this));
|
||||
setCompletionPrefix(prefix);
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<CodeInfoTip> AsCompletion::parseDocument() { return {}; }
|
||||
QList<CodeInfoTip> AsCompletion::parseDocument() {
|
||||
auto editor = qobject_cast<WingCodeEdit *>(widget());
|
||||
if (editor == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto code = editor->toPlainText();
|
||||
auto engine = ScriptMachine::instance().engine();
|
||||
|
||||
// first preprocess the code
|
||||
AsPreprocesser prepc(engine);
|
||||
prepc.setIsCodeCompleteMode(true);
|
||||
prepc.setIncludeCallback(&AsCompletion::includeCallBack, this);
|
||||
|
||||
auto r = prepc.loadSectionFromMemory(QStringLiteral("ASCOMPLETION"),
|
||||
code.toUtf8());
|
||||
if (r <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
auto engine = ScriptMachine::instance().engine();
|
||||
QAsCodeParser parser(engine);
|
||||
auto syms = parser.parseAndIntell(offset, code);
|
||||
|
||||
for (auto &sym : syms) {
|
||||
CodeInfoTip tip;
|
||||
tip.name = sym.name;
|
||||
tip.nameSpace = QString::fromUtf8(sym.scope.join("::"));
|
||||
|
||||
switch (sym.symtype) {
|
||||
case QAsCodeParser::SymbolType::Function:
|
||||
case QAsCodeParser::SymbolType::FnDef:
|
||||
tip.type = CodeInfoTip::Type::Function;
|
||||
tip.addinfo.insert(CodeInfoTip::RetType,
|
||||
QString::fromUtf8(sym.type));
|
||||
tip.addinfo.insert(CodeInfoTip::Args,
|
||||
QString::fromUtf8(sym.additonalInfo));
|
||||
for (auto &var : sym.children) {
|
||||
CodeInfoTip va;
|
||||
va.dontAddGlobal = true;
|
||||
va.name = var.name;
|
||||
va.nameSpace = QString::fromUtf8(var.scope.join("::"));
|
||||
va.addinfo.insert(CodeInfoTip::RetType, var.type);
|
||||
va.type = CodeInfoTip::Type::Variable;
|
||||
ret.append(va);
|
||||
}
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::Enum:
|
||||
tip.type = CodeInfoTip::Type::Enum;
|
||||
for (auto &e : sym.children) {
|
||||
CodeInfoTip en;
|
||||
en.dontAddGlobal = true;
|
||||
en.name = e.name;
|
||||
en.nameSpace = QString::fromUtf8(e.scope.join("::"));
|
||||
en.type = CodeInfoTip::Type::Enumerater;
|
||||
if (!e.additonalInfo.isEmpty()) {
|
||||
en.addinfo.insert(CodeInfoTip::Comment,
|
||||
en.name + QStringLiteral(" = ") +
|
||||
e.additonalInfo);
|
||||
}
|
||||
ret.append(en);
|
||||
}
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::TypeDef:
|
||||
tip.type = CodeInfoTip::Type::TypeDef;
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::Variable:
|
||||
tip.addinfo.insert(CodeInfoTip::RetType, sym.type);
|
||||
tip.type = CodeInfoTip::Type::Variable;
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::Class:
|
||||
case QAsCodeParser::SymbolType::Interface:
|
||||
for (auto &mem : sym.children) {
|
||||
if (mem.vis != QAsCodeParser::Visiblity::Public) {
|
||||
continue;
|
||||
}
|
||||
CodeInfoTip ctip;
|
||||
ctip.name = mem.name;
|
||||
ctip.nameSpace = QString::fromUtf8(mem.scope.join("::"));
|
||||
if (mem.symtype == QAsCodeParser::SymbolType::Function) {
|
||||
ctip.type = CodeInfoTip::Type::Function;
|
||||
ctip.addinfo.insert(CodeInfoTip::RetType,
|
||||
QString::fromUtf8(mem.type));
|
||||
ctip.addinfo.insert(CodeInfoTip::Args,
|
||||
QString::fromUtf8(mem.additonalInfo));
|
||||
for (auto &var : mem.children) {
|
||||
CodeInfoTip va;
|
||||
va.dontAddGlobal = true;
|
||||
va.name = var.name;
|
||||
va.nameSpace = QString::fromUtf8(var.scope.join("::"));
|
||||
va.addinfo.insert(CodeInfoTip::RetType, var.type);
|
||||
va.type = CodeInfoTip::Type::Variable;
|
||||
tip.children.append(va);
|
||||
}
|
||||
tip.children.append(ctip);
|
||||
} else if (mem.symtype == QAsCodeParser::SymbolType::Variable) {
|
||||
ctip.addinfo.insert(CodeInfoTip::RetType, mem.type);
|
||||
ctip.type = CodeInfoTip::Type::Variable;
|
||||
tip.children.append(ctip);
|
||||
}
|
||||
}
|
||||
tip.type = CodeInfoTip::Type::Class;
|
||||
break;
|
||||
case QAsCodeParser::SymbolType::Invalid:
|
||||
case QAsCodeParser::SymbolType::Import:
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.append(tip);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool AsCompletion::eventFilter(QObject *watched, QEvent *event) {
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
|
|
|
@ -21,10 +21,12 @@
|
|||
#include "WingCodeEdit/wingcompleter.h"
|
||||
#include "class/asdatabase.h"
|
||||
|
||||
class AsPreprocesser;
|
||||
|
||||
class AsCompletion : public WingCompleter {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AsCompletion(asIScriptEngine *engine, WingCodeEdit *p);
|
||||
explicit AsCompletion(WingCodeEdit *p);
|
||||
|
||||
virtual ~AsCompletion();
|
||||
|
||||
|
@ -32,17 +34,19 @@ public:
|
|||
public:
|
||||
virtual QString wordSeperators() const override;
|
||||
|
||||
bool parseDocument() const;
|
||||
void setParseDocument(bool newParseDocument);
|
||||
|
||||
void clearFunctionTip();
|
||||
|
||||
protected:
|
||||
virtual void processTrigger(const QString &trigger,
|
||||
virtual bool processTrigger(const QString &trigger,
|
||||
const QString &content) override;
|
||||
|
||||
virtual QList<CodeInfoTip> parseDocument();
|
||||
|
||||
virtual QList<CodeInfoTip> parseMarcos();
|
||||
|
||||
QList<CodeInfoTip> parseScriptData(qsizetype offset,
|
||||
const QByteArray &code);
|
||||
|
||||
signals:
|
||||
void onFunctionTip(const QString &content);
|
||||
|
||||
|
@ -51,13 +55,32 @@ public:
|
|||
virtual bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
private:
|
||||
void applyEmptyNsNode(QList<CodeInfoTip> &nodes);
|
||||
void applyEmptyNsNode(QList<CodeInfoTip> &nodes,
|
||||
const QList<CodeInfoTip> &docNodes);
|
||||
void applyClassNodes(QList<CodeInfoTip> &nodes);
|
||||
|
||||
private:
|
||||
static int includeCallBack(const QString &include, bool quotedInclude,
|
||||
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;
|
||||
asIScriptEngine *_engine;
|
||||
bool m_parseDocument;
|
||||
};
|
||||
|
||||
#endif // _CPP_COMPLETION_H_
|
||||
|
|
|
@ -1,12 +1,108 @@
|
|||
/*==============================================================================
|
||||
** 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 "asconsolecompletion.h"
|
||||
|
||||
#include "AngelScript/sdk/angelscript/source/as_module.h"
|
||||
#include "class/qascodeparser.h"
|
||||
#include "control/scriptingconsole.h"
|
||||
|
||||
AsConsoleCompletion::AsConsoleCompletion(asIScriptEngine *engine,
|
||||
ScriptingConsole *p)
|
||||
: AsCompletion(engine, p), _console(p) {}
|
||||
AsConsoleCompletion::AsConsoleCompletion(ScriptingConsole *p)
|
||||
: AsCompletion(p), _console(p) {}
|
||||
|
||||
QList<CodeInfoTip> AsConsoleCompletion::parseDocument() {
|
||||
// TODO
|
||||
auto editor = _console;
|
||||
if (editor == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto code = editor->currentCodes();
|
||||
auto engine = ScriptMachine::instance().engine();
|
||||
|
||||
QAsCodeParser parser(engine);
|
||||
auto seg = parser.parse(code.toUtf8());
|
||||
if (std::find_if(seg.begin(), seg.end(),
|
||||
[](const QAsCodeParser::CodeSegment &seg) {
|
||||
return seg.isValid();
|
||||
}) == seg.end()) {
|
||||
// wrap it to let code-completion work
|
||||
code.prepend("void f(){").append('}');
|
||||
}
|
||||
|
||||
// first preprocess the code
|
||||
AsPreprocesser prepc(engine);
|
||||
|
||||
// including is not supported in console
|
||||
auto r = prepc.loadSectionFromMemory(QStringLiteral("ASConCOMPLETION"),
|
||||
code.toUtf8());
|
||||
if (r <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<CodeInfoTip> citips;
|
||||
// first, global variables should be added into
|
||||
auto rmod = ScriptMachine::instance().module(ScriptMachine::Interactive);
|
||||
// I think hijack the internal will do the better
|
||||
auto mod = dynamic_cast<asCModule *>(rmod);
|
||||
if (mod) {
|
||||
auto it = mod->m_scriptGlobals.List();
|
||||
while (it) {
|
||||
// lamda expression is great!
|
||||
auto CString2ByteArray = [](const asCString &str) -> QByteArray {
|
||||
return QByteArray(str.AddressOf(), str.GetLength());
|
||||
};
|
||||
|
||||
CodeInfoTip tip;
|
||||
tip.type = CodeInfoTip::Type::Variable;
|
||||
tip.addinfo.insert(
|
||||
CodeInfoTip::RetType,
|
||||
CString2ByteArray(it->type.Format(mod->m_defaultNamespace)));
|
||||
tip.nameSpace = CString2ByteArray(it->nameSpace->name);
|
||||
tip.name = CString2ByteArray(it->name);
|
||||
citips.append(tip);
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
auto data = prepc.scriptData();
|
||||
if (data.size() == 1) {
|
||||
auto d = data[0];
|
||||
return citips + parseScriptData(d.script.length() - 1, d.script);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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 ASCONSOLECOMPLETION_H
|
||||
#define ASCONSOLECOMPLETION_H
|
||||
|
||||
|
@ -8,12 +25,12 @@ class ScriptingConsole;
|
|||
class AsConsoleCompletion : public AsCompletion {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AsConsoleCompletion(asIScriptEngine *engine, ScriptingConsole *p);
|
||||
explicit AsConsoleCompletion(ScriptingConsole *p);
|
||||
virtual ~AsConsoleCompletion() = default;
|
||||
|
||||
// AsCompletion interface
|
||||
protected:
|
||||
virtual QList<CodeInfoTip> parseDocument() override;
|
||||
virtual QList<CodeInfoTip> parseMarcos() override;
|
||||
|
||||
private:
|
||||
ScriptingConsole *_console;
|
||||
|
|
|
@ -1,22 +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 "ascontextmgr.h"
|
||||
|
||||
asContextMgr::asContextMgr() : CContextMgr() {}
|
||||
|
||||
bool asContextMgr::isRunning() const { return !m_threads.empty(); }
|
|
@ -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;
|
||||
|
@ -177,6 +178,10 @@ const QHash<QString, QList<CodeInfoTip>> &ASDataBase::headerNodes() const {
|
|||
return _headerNodes;
|
||||
}
|
||||
|
||||
const QHash<QString, QList<CodeInfoTip>> &ASDataBase::enumsNodes() const {
|
||||
return _enumsNodes;
|
||||
}
|
||||
|
||||
void ASDataBase::addGlobalFunctionCompletion(asIScriptEngine *engine) {
|
||||
Q_ASSERT(engine);
|
||||
|
||||
|
@ -210,6 +215,13 @@ void ASDataBase::addEnumCompletion(asIScriptEngine *engine) {
|
|||
einfo.name = etype->GetName();
|
||||
einfo.type = CodeInfoTip::Type::Enum;
|
||||
|
||||
auto ens = einfo.nameSpace;
|
||||
if (ens.isEmpty()) {
|
||||
ens = einfo.name;
|
||||
} else {
|
||||
ens += QStringLiteral("::") + einfo.name;
|
||||
}
|
||||
|
||||
for (asUINT i = 0; i < etype->GetEnumValueCount(); ++i) {
|
||||
int v;
|
||||
auto e = etype->GetEnumValueByIndex(i, &v);
|
||||
|
@ -221,6 +233,7 @@ void ASDataBase::addEnumCompletion(asIScriptEngine *engine) {
|
|||
QStringLiteral(" = ") +
|
||||
QString::number(v));
|
||||
einfo.children.append(en);
|
||||
_enumsNodes[ens].append(en);
|
||||
}
|
||||
|
||||
etype->Release();
|
||||
|
@ -313,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,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;
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ 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;
|
||||
|
||||
|
@ -55,6 +57,8 @@ private:
|
|||
private:
|
||||
// <namespace, content>
|
||||
QHash<QString, QList<CodeInfoTip>> _headerNodes;
|
||||
QHash<QString, QList<CodeInfoTip>> _enumsNodes;
|
||||
QHash<QString, QList<CodeInfoTip>> _classNodes;
|
||||
QList<CodeInfoTip> _keywordNode;
|
||||
};
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
#include "asdebugger.h"
|
||||
#include "class/appmanager.h"
|
||||
#include "define.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
@ -71,10 +72,57 @@ void asDebugger::takeCommands(asIScriptContext *ctx) {
|
|||
void asDebugger::lineCallback(asIScriptContext *ctx) {
|
||||
Q_ASSERT(ctx);
|
||||
|
||||
// prevent UI freezing
|
||||
qApp->processEvents();
|
||||
|
||||
// This should never happen, but it doesn't hurt to validate it
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char *file = 0;
|
||||
int col = 0;
|
||||
int lineNbr = ctx->GetLineNumber(0, &col, &file);
|
||||
|
@ -98,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);
|
||||
|
||||
|
@ -359,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>");
|
||||
|
||||
|
@ -436,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -473,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 << ')';
|
||||
|
@ -500,6 +543,8 @@ asDebugger::GCStatistic asDebugger::gcStatistics() {
|
|||
|
||||
void asDebugger::runDebugAction(DebugAction action) { m_action = action; }
|
||||
|
||||
void asDebugger::resetState() { m_action = CONTINUE; }
|
||||
|
||||
void asDebugger::deleteDbgContextInfo(void *info) {
|
||||
if (info) {
|
||||
delete reinterpret_cast<ContextDbgInfo *>(info);
|
||||
|
|
|
@ -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,14 +108,16 @@ 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();
|
||||
|
||||
void runDebugAction(DebugAction action);
|
||||
|
||||
DebugAction currentState() const;
|
||||
void resetState();
|
||||
|
||||
static void deleteDbgContextInfo(void *info);
|
||||
|
||||
|
|
|
@ -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,19 +49,70 @@ 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(); }
|
||||
|
||||
int AsPreprocesser::AddSectionFromFile(const QString &filename) {
|
||||
int AsPreprocesser::loadSectionFromFile(const QString &filename) {
|
||||
// The file name stored in the set should be the fully resolved name because
|
||||
// it is possible to name the same file in multiple ways using relative
|
||||
// paths.
|
||||
auto fullpath = QFileInfo(filename).absoluteFilePath();
|
||||
|
||||
if (IncludeIfNotAlreadyIncluded(fullpath)) {
|
||||
int r = LoadScriptSection(fullpath);
|
||||
if (includeIfNotAlreadyIncluded(fullpath)) {
|
||||
int r = loadScriptSection(fullpath);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else
|
||||
|
@ -58,62 +122,73 @@ int AsPreprocesser::AddSectionFromFile(const QString &filename) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
QList<AsPreprocesser::ScriptData> AsPreprocesser::GetScriptData() const {
|
||||
int AsPreprocesser::loadSectionFromMemory(const QString §ion,
|
||||
const QByteArray &code) {
|
||||
int r = processScriptSection(code, section);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
QList<AsPreprocesser::ScriptData> AsPreprocesser::scriptData() const {
|
||||
return modifiedScripts;
|
||||
}
|
||||
|
||||
asIScriptEngine *AsPreprocesser::GetEngine() { return engine; }
|
||||
asIScriptEngine *AsPreprocesser::getEngine() { return engine; }
|
||||
|
||||
void AsPreprocesser::SetIncludeCallback(INCLUDECALLBACK_t callback,
|
||||
void AsPreprocesser::setIncludeCallback(INCLUDECALLBACK_t callback,
|
||||
void *userParam) {
|
||||
includeCallback = callback;
|
||||
includeParam = userParam;
|
||||
}
|
||||
|
||||
void AsPreprocesser::SetPragmaCallback(PRAGMACALLBACK_t callback,
|
||||
void AsPreprocesser::setPragmaCallback(PRAGMACALLBACK_t callback,
|
||||
void *userParam) {
|
||||
pragmaCallback = 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::GetSectionCount() const {
|
||||
unsigned int AsPreprocesser::sectionCount() const {
|
||||
return (unsigned int)(includedScripts.size());
|
||||
}
|
||||
|
||||
QString AsPreprocesser::GetSectionName(unsigned int idx) const {
|
||||
QString AsPreprocesser::sectionName(unsigned int idx) const {
|
||||
if (qsizetype(idx) >= qsizetype(includedScripts.size()))
|
||||
return {};
|
||||
|
||||
return includedScripts.at(idx);
|
||||
}
|
||||
|
||||
void AsPreprocesser::ClearAll() { includedScripts.clear(); }
|
||||
void AsPreprocesser::clearAll() { includedScripts.clear(); }
|
||||
|
||||
int AsPreprocesser::processScriptSection(const QByteArray &script,
|
||||
const QString §ionname) {
|
||||
|
||||
// #define DBG_CODEP // debug macro for pre-process
|
||||
|
||||
int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
|
||||
const QString §ionname,
|
||||
int lineOffset) {
|
||||
QVector<QPair<QString, bool>> includes;
|
||||
|
||||
QByteArray modifiedScript;
|
||||
|
||||
// Perform a superficial parsing of the script first to store the metadata
|
||||
if (length)
|
||||
modifiedScript = script.left(length);
|
||||
else
|
||||
modifiedScript = script;
|
||||
QByteArray modifiedScript = script;
|
||||
|
||||
// First perform the checks for #if directives to exclude code that
|
||||
// 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);
|
||||
|
@ -126,45 +201,398 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
|
|||
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 (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.mid(pos, len);
|
||||
QByteArray word = modifiedScript.sliced(pos, len);
|
||||
|
||||
// Overwrite the #if directive with space characters to
|
||||
// Overwrite the directive with space characters to
|
||||
// avoid compiler error
|
||||
pos += len;
|
||||
OverwriteCode(modifiedScript, start, pos - start);
|
||||
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 {
|
||||
nested++;
|
||||
m_condtionStack.push(true);
|
||||
}
|
||||
#ifdef DBG_CODEP
|
||||
qDebug().noquote() << modifiedScript;
|
||||
#endif
|
||||
} else {
|
||||
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 == "else") {
|
||||
if (_isCodeCompleteMode) {
|
||||
overwriteCode(modifiedScript, start, pos - start);
|
||||
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 (nested > 0) {
|
||||
OverwriteCode(modifiedScript, start, pos - start);
|
||||
nested--;
|
||||
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;
|
||||
|
@ -176,7 +604,7 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
|
|||
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" ||
|
||||
|
@ -192,7 +620,7 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
|
|||
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,
|
||||
|
@ -209,21 +637,21 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
|
|||
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,
|
||||
engine->WriteMessage(
|
||||
sectionname.toUtf8(),
|
||||
getLineCount(modifiedScript, pos), 1,
|
||||
asMSGTYPE_ERROR, str.toUtf8());
|
||||
} else {
|
||||
// Store it for later processing
|
||||
|
@ -231,11 +659,10 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
|
|||
|
||||
// Overwrite the include directive with space
|
||||
// characters to avoid compiler error
|
||||
OverwriteCode(modifiedScript, start, pos - start);
|
||||
overwriteCode(modifiedScript, start, pos - start);
|
||||
}
|
||||
}
|
||||
|
||||
if (t == asTC_KEYWORD && modifiedScript[pos] == '<') {
|
||||
} else if (t == asTC_KEYWORD &&
|
||||
modifiedScript[pos] == '<') {
|
||||
pos += len;
|
||||
|
||||
// find the next '>'
|
||||
|
@ -253,7 +680,8 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
|
|||
|
||||
if (found) {
|
||||
QString includefile =
|
||||
modifiedScript.mid(pos, rpos - pos).trimmed();
|
||||
modifiedScript.sliced(pos, rpos - pos)
|
||||
.trimmed();
|
||||
|
||||
pos = rpos + 1;
|
||||
|
||||
|
@ -262,24 +690,23 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
|
|||
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});
|
||||
|
||||
// Overwrite the include directive with
|
||||
// space characters to avoid compiler error
|
||||
OverwriteCode(modifiedScript, start,
|
||||
overwriteCode(modifiedScript, start,
|
||||
pos - start);
|
||||
}
|
||||
} else {
|
||||
|
@ -301,37 +728,41 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
|
|||
|
||||
// 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);
|
||||
overwriteCode(modifiedScript, start, pos - start);
|
||||
|
||||
if (!_isCodeCompleteMode) {
|
||||
int r = pragmaCallback
|
||||
? pragmaCallback(pragmaText, this, sectionname,
|
||||
pragmaParam)
|
||||
? 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());
|
||||
sectionname.toUtf8(),
|
||||
getLineCount(modifiedScript, pos), 1,
|
||||
asMSGTYPE_ERROR,
|
||||
QObject::tr("Invalid #pragma directive")
|
||||
.toUtf8());
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't search for includes within statement blocks or
|
||||
// between tokens in statements
|
||||
else {
|
||||
pos = SkipStatement(modifiedScript, pos);
|
||||
pos = skipStatement(modifiedScript, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// Build the actual script
|
||||
engine->SetEngineProperty(asEP_COPY_SCRIPT_SECTIONS, true);
|
||||
|
||||
AddScriptSection(sectionname, modifiedScript, lineOffset);
|
||||
addScriptSection(sectionname, modifiedScript);
|
||||
|
||||
if (includes.size() > 0) {
|
||||
// If the callback has been set, then call it for each included file
|
||||
|
@ -362,7 +793,7 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
|
|||
}
|
||||
|
||||
// Include the script section
|
||||
int r = AddSectionFromFile(includes[n].first);
|
||||
int r = loadSectionFromFile(includes[n].first);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -372,7 +803,7 @@ int AsPreprocesser::ProcessScriptSection(const QByteArray &script, int length,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int AsPreprocesser::LoadScriptSection(const QString &filename) {
|
||||
int AsPreprocesser::loadScriptSection(const QString &filename) {
|
||||
// Open the script file
|
||||
|
||||
QFile f(filename);
|
||||
|
@ -384,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;
|
||||
}
|
||||
|
||||
|
@ -396,10 +824,10 @@ int AsPreprocesser::LoadScriptSection(const QString &filename) {
|
|||
|
||||
// Process the script section even if it is zero length so that the name is
|
||||
// registered
|
||||
return ProcessScriptSection(code, code.length(), filename, 0);
|
||||
return processScriptSection(code, filename);
|
||||
}
|
||||
|
||||
bool AsPreprocesser::IncludeIfNotAlreadyIncluded(const QString &filename) {
|
||||
bool AsPreprocesser::includeIfNotAlreadyIncluded(const QString &filename) {
|
||||
if (includedScripts.contains(filename)) {
|
||||
// Already included
|
||||
return false;
|
||||
|
@ -410,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
|
||||
|
@ -436,41 +864,42 @@ 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);
|
||||
overwriteCode(modifiedScript, pos, len);
|
||||
}
|
||||
pos += len;
|
||||
}
|
||||
|
@ -478,7 +907,41 @@ int AsPreprocesser::ExcludeCode(QByteArray &modifiedScript, int pos) {
|
|||
return pos;
|
||||
}
|
||||
|
||||
void AsPreprocesser::OverwriteCode(QByteArray &modifiedScript, int start,
|
||||
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);
|
||||
}
|
||||
pos += len;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
void AsPreprocesser::overwriteCode(QByteArray &modifiedScript, int start,
|
||||
int len) {
|
||||
auto code = modifiedScript.data() + start;
|
||||
for (int n = 0; n < len; n++) {
|
||||
|
@ -488,11 +951,91 @@ void AsPreprocesser::OverwriteCode(QByteArray &modifiedScript, int start,
|
|||
}
|
||||
}
|
||||
|
||||
void AsPreprocesser::AddScriptSection(const QString §ion,
|
||||
const QByteArray &code, int lineOffset) {
|
||||
int AsPreprocesser::getLineCount(const QByteArray &modifiedScript,
|
||||
int pos) const {
|
||||
pos = qBound(0, pos, int(modifiedScript.size()));
|
||||
return std::count_if(modifiedScript.begin(),
|
||||
std::next(modifiedScript.begin(), pos),
|
||||
[](char ch) -> bool { return ch == '\n'; }) +
|
||||
1;
|
||||
}
|
||||
|
||||
bool AsPreprocesser::endLinePassFailed(const QByteArray &modifiedScript,
|
||||
int pos) { // ensure '\n' end line
|
||||
bool endError = false;
|
||||
asUINT len = 0;
|
||||
auto t = engine->ParseToken(modifiedScript.data() + pos,
|
||||
modifiedScript.size() - pos, &len);
|
||||
if (t == asTC_WHITESPACE) {
|
||||
if (!std::any_of(modifiedScript.data() + pos,
|
||||
modifiedScript.data() + pos + len,
|
||||
[](char ch) { return ch == '\n'; })) {
|
||||
endError = true;
|
||||
}
|
||||
} else {
|
||||
if (len != 0) {
|
||||
endError = true;
|
||||
}
|
||||
}
|
||||
return endError;
|
||||
}
|
||||
|
||||
QByteArray
|
||||
AsPreprocesser::processIfExpression(const QByteArray &codes, int currentLine,
|
||||
const QByteArray ¤tSection) {
|
||||
QByteArray ret;
|
||||
|
||||
int pos = 0;
|
||||
auto total = codes.length();
|
||||
while (pos < total) {
|
||||
asUINT len = 0;
|
||||
auto t =
|
||||
engine->ParseToken(codes.data() + pos, codes.size() - pos, &len);
|
||||
auto word = codes.sliced(pos, len);
|
||||
if (t == asTC_IDENTIFIER) {
|
||||
if (word == "__LINE__") {
|
||||
ret.append(currentLine);
|
||||
} else if (word == "__SECTION__") {
|
||||
auto s = currentSection;
|
||||
s.prepend('"').append('"');
|
||||
ret.append(s);
|
||||
} else {
|
||||
ret.append(findReplaceResult(word));
|
||||
}
|
||||
} else {
|
||||
ret.append(word);
|
||||
}
|
||||
pos += len;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QByteArray AsPreprocesser::findReplaceResult(const QByteArray &v) {
|
||||
QByteArray r = v;
|
||||
while (definedWords.contains(r)) {
|
||||
r = definedWords.value(r);
|
||||
if (r.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
bool AsPreprocesser::isCodeCompleteMode() const { return _isCodeCompleteMode; }
|
||||
|
||||
void AsPreprocesser::setIsCodeCompleteMode(bool newIsCodeCompleteMode) {
|
||||
_isCodeCompleteMode = newIsCodeCompleteMode;
|
||||
}
|
||||
|
||||
QHash<QString, QByteArray> AsPreprocesser::definedMacros() const {
|
||||
return definedWords;
|
||||
}
|
||||
|
||||
void AsPreprocesser::addScriptSection(const QString §ion,
|
||||
const QByteArray &code) {
|
||||
ScriptData data;
|
||||
data.section = section;
|
||||
data.lineOffset = lineOffset;
|
||||
data.script = code;
|
||||
modifiedScripts.append(data);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -70,7 +81,6 @@ public:
|
|||
public:
|
||||
struct ScriptData {
|
||||
QString section;
|
||||
int lineOffset = -1;
|
||||
QByteArray script;
|
||||
};
|
||||
|
||||
|
@ -79,41 +89,59 @@ public:
|
|||
// Returns 1 if the file was included
|
||||
// 0 if the file had already been included before
|
||||
// <0 on error
|
||||
int AddSectionFromFile(const QString &filename);
|
||||
int loadSectionFromFile(const QString &filename);
|
||||
int loadSectionFromMemory(const QString §ion, const QByteArray &code);
|
||||
|
||||
QList<ScriptData> GetScriptData() const;
|
||||
void addScriptSection(const QString §ion, const QByteArray &code);
|
||||
|
||||
QList<ScriptData> scriptData() const;
|
||||
|
||||
// Returns the engine
|
||||
asIScriptEngine *GetEngine();
|
||||
asIScriptEngine *getEngine();
|
||||
|
||||
// Register the callback for resolving include directive
|
||||
void SetIncludeCallback(INCLUDECALLBACK_t callback, void *userParam);
|
||||
void setIncludeCallback(INCLUDECALLBACK_t callback, void *userParam);
|
||||
|
||||
// Register the callback for resolving pragma directive
|
||||
void SetPragmaCallback(PRAGMACALLBACK_t callback, void *userParam);
|
||||
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 GetSectionCount() const;
|
||||
unsigned int sectionCount() const;
|
||||
|
||||
QString GetSectionName(unsigned int idx) const;
|
||||
QString sectionName(unsigned int idx) const;
|
||||
|
||||
bool isCodeCompleteMode() const;
|
||||
void setIsCodeCompleteMode(bool newIsCodeCompleteMode);
|
||||
|
||||
QHash<QString, QByteArray> definedMacros() const;
|
||||
|
||||
protected:
|
||||
void ClearAll();
|
||||
int ProcessScriptSection(const QByteArray &script, int length,
|
||||
const QString §ionname, int lineOffset);
|
||||
int LoadScriptSection(const QString &filename);
|
||||
bool IncludeIfNotAlreadyIncluded(const QString &filename);
|
||||
QString translate(const char *str);
|
||||
|
||||
int SkipStatement(const QByteArray &modifiedScript, int pos);
|
||||
protected:
|
||||
void clearAll();
|
||||
|
||||
int ExcludeCode(QByteArray &modifiedScript, int pos);
|
||||
void OverwriteCode(QByteArray &modifiedScript, int start, int len);
|
||||
int processScriptSection(const QByteArray &script,
|
||||
const QString §ionname);
|
||||
int loadScriptSection(const QString &filename);
|
||||
bool includeIfNotAlreadyIncluded(const QString &filename);
|
||||
|
||||
void AddScriptSection(const QString §ion, const QByteArray &code,
|
||||
int lineOffset);
|
||||
int skipStatement(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 ¤tSection);
|
||||
|
||||
QByteArray findReplaceResult(const QByteArray &v);
|
||||
|
||||
asIScriptEngine *engine;
|
||||
QList<ScriptData> modifiedScripts;
|
||||
|
@ -126,7 +154,10 @@ protected:
|
|||
|
||||
QStringList includedScripts;
|
||||
|
||||
QStringList definedWords;
|
||||
QHash<QString, QByteArray> definedWords;
|
||||
|
||||
private:
|
||||
bool _isCodeCompleteMode = false;
|
||||
};
|
||||
|
||||
#endif // ASPREPROCESSER_H
|
||||
|
|
|
@ -133,7 +133,7 @@ QString ClangFormatManager::formatCode(const QString &codes, bool &ok) {
|
|||
.arg(m_clangStyle)
|
||||
.arg(m_identWidth);
|
||||
auto ret = runClangFormat(
|
||||
{style, QStringLiteral("--assume-filename=wing.cpp")}, codes, ok);
|
||||
{style, QStringLiteral("--assume-filename=wing.cs")}, codes, ok);
|
||||
return ret;
|
||||
} else {
|
||||
return runClangFormat({}, codes, ok);
|
||||
|
|
|
@ -1,84 +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 CLICKCALLBACK_H
|
||||
#define CLICKCALLBACK_H
|
||||
|
||||
#include "WingPlugin/iwingplugin.h"
|
||||
#include "angelscript.h"
|
||||
|
||||
class ClickCallBack {
|
||||
public:
|
||||
ClickCallBack() {}
|
||||
|
||||
ClickCallBack(const WingHex::ClickedCallBack &b) : _call(b) {}
|
||||
|
||||
explicit ClickCallBack(asIScriptEngine *engine, asIScriptFunction *func)
|
||||
: _engine(engine), _func(func) {
|
||||
if (_engine && _func) {
|
||||
_func->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
~ClickCallBack() {
|
||||
if (_func) {
|
||||
_func->Release();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
ClickCallBack &operator=(const WingHex::ClickedCallBack &_Right) {
|
||||
*this = ClickCallBack(_Right);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ClickCallBack &operator=(WingHex::ClickedCallBack &&_Right) {
|
||||
*this = ClickCallBack(_Right);
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit operator bool() const noexcept {
|
||||
return _call || (_engine && _func);
|
||||
}
|
||||
|
||||
void operator()(const QModelIndex &index) {
|
||||
if (_call) {
|
||||
_call(index);
|
||||
} else {
|
||||
if (_engine && _func) {
|
||||
auto ctx = _engine->CreateContext();
|
||||
if (ctx) {
|
||||
auto r = ctx->Prepare(_func);
|
||||
if (r >= 0) {
|
||||
auto idx = index;
|
||||
ctx->SetArgObject(0, &idx);
|
||||
ctx->Execute();
|
||||
}
|
||||
ctx->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
WingHex::ClickedCallBack _call;
|
||||
asIScriptEngine *_engine = nullptr;
|
||||
asIScriptFunction *_func = nullptr;
|
||||
};
|
||||
|
||||
#endif // CLICKCALLBACK_H
|
|
@ -36,6 +36,7 @@ QIcon CodeInfoTip::getDisplayIcon(Type type, CodeInfoVisibility vis) {
|
|||
case Type::Group:
|
||||
return icon(ICON_NAMESPACE);
|
||||
case Type::Class:
|
||||
case Type::TypeDef:
|
||||
return icon(ICON_CLASS);
|
||||
case Type::ClsFunction:
|
||||
case Type::Function:
|
||||
|
|
|
@ -36,6 +36,7 @@ public:
|
|||
Variable,
|
||||
Property = Variable,
|
||||
Enumerater,
|
||||
TypeDef
|
||||
};
|
||||
|
||||
enum CacheIndex {
|
||||
|
@ -78,6 +79,7 @@ public:
|
|||
|
||||
public:
|
||||
QString name;
|
||||
bool dontAddGlobal = false;
|
||||
Type type = Type::Unknown;
|
||||
QString nameSpace;
|
||||
CodeInfoVisibility visualpo = CodeInfoVisibility::VISIBILITY_DEFAULT;
|
||||
|
|
|
@ -151,6 +151,9 @@ void CTypeParser::initialize() {
|
|||
ADD_TYPE(longlong, QMetaType::LongLong);
|
||||
ADD_TYPE_U(ulonglong, QMetaType::ULongLong);
|
||||
|
||||
using uint = unsigned int;
|
||||
ADD_TYPE_U(uint, QMetaType::UInt);
|
||||
|
||||
using BOOL = bool;
|
||||
using BYTE = byte;
|
||||
using WORD = uint16;
|
||||
|
@ -242,6 +245,8 @@ void CTypeParser::initialize() {
|
|||
|
||||
#undef ADD_TYPE
|
||||
#undef ADD_TYPE_S
|
||||
|
||||
base_types_ = type_maps_.keys();
|
||||
}
|
||||
|
||||
void CTypeParser::setIncludePaths(const QStringList &paths) {
|
||||
|
@ -451,7 +456,7 @@ void CTypeParser::stripComments(QStringList &lines) const {
|
|||
QStringLiteral("]");
|
||||
|
||||
// search comment start
|
||||
while ((pos = line.indexOf(kSlash, pos)) != -1) {
|
||||
while ((pos = line.indexOf(char(kSlash), pos)) != -1) {
|
||||
if (line.length() <= pos + 1)
|
||||
break; // the 1st '/' is at the end of line, so not a comment
|
||||
|
||||
|
@ -627,7 +632,7 @@ TokenTypes CTypeParser::getTokenType(const QString &token) const {
|
|||
return keywords_.value(token);
|
||||
} else if (qualifiers_.contains(token)) {
|
||||
return kQualifier;
|
||||
} else if (type_maps_.contains(token)) {
|
||||
} else if (base_types_.contains(token)) {
|
||||
return kBasicDataType;
|
||||
} else if (struct_defs_.contains(token)) {
|
||||
return kStructName;
|
||||
|
@ -1065,7 +1070,7 @@ bool CTypeParser::parseDeclaration(const QString &line,
|
|||
if (line.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (line[line.length() - 1] != kSemicolon)
|
||||
if (line[line.length() - 1] != char(kSemicolon))
|
||||
return false;
|
||||
|
||||
QStringList tokens;
|
||||
|
@ -1085,7 +1090,7 @@ bool CTypeParser::parseDeclaration(const QString &line,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (tokens[++index].at(0) == kAsterisk) {
|
||||
if (tokens[++index].at(0) == char(kAsterisk)) {
|
||||
decl.is_pointer = true;
|
||||
// size of a pointer is 4 types on a 32-bit system
|
||||
length =
|
||||
|
@ -1143,14 +1148,14 @@ bool CTypeParser::parseEnumDeclaration(const QString &line, int &last_value,
|
|||
break;
|
||||
|
||||
case 2:
|
||||
if (kComma != tokens[1].at(0)) {
|
||||
if (char(kComma) != tokens[1].at(0)) {
|
||||
return false;
|
||||
}
|
||||
decl.second = ++last_value;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (kEqual != tokens[1].at(0)) {
|
||||
if (char(kEqual) != tokens[1].at(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1166,7 +1171,8 @@ bool CTypeParser::parseEnumDeclaration(const QString &line, int &last_value,
|
|||
break;
|
||||
|
||||
case 4:
|
||||
if (!(kEqual == tokens[1].at(0) && kComma == tokens[3].at(0))) {
|
||||
if (!(char(kEqual) == tokens[1].at(0) &&
|
||||
char(kComma) == tokens[3].at(0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1203,8 +1209,8 @@ bool CTypeParser::parseAssignExpression(const QString &line) {
|
|||
|
||||
// only 4 tokens for an assignment expression: var = number;
|
||||
if (4 == splitLineIntoTokens(line, tokens) &&
|
||||
kEqual == tokens[1].at(tokens[1].length() - 1) &&
|
||||
kSemicolon == tokens[3].at(tokens[3].length() - 1) &&
|
||||
char(kEqual) == tokens[1].at(tokens[1].length() - 1) &&
|
||||
char(kSemicolon) == tokens[3].at(tokens[3].length() - 1) &&
|
||||
isNumericToken(tokens[2], number)) {
|
||||
const_defs_.insert(tokens[0], number);
|
||||
return true;
|
||||
|
@ -1239,7 +1245,7 @@ bool CTypeParser::parsePreProcDirective(const QString &src, qsizetype &pos) {
|
|||
}
|
||||
|
||||
// only handle header file included with ""
|
||||
if (kQuotation == token[token.length() - 1]) {
|
||||
if (char(kQuotation) == token[token.length() - 1]) {
|
||||
// get included header file name
|
||||
if (!getNextToken(src, pos, token, false)) {
|
||||
return false;
|
||||
|
@ -1366,7 +1372,7 @@ bool CTypeParser::parseStructUnion(const bool is_struct, const bool is_typedef,
|
|||
if (is_typedef) {
|
||||
// format 1
|
||||
if (!(getNextToken(src, pos, next_token) &&
|
||||
kSemicolon == next_token.at(0))) {
|
||||
char(kSemicolon) == next_token.at(0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1387,7 +1393,7 @@ bool CTypeParser::parseStructUnion(const bool is_struct, const bool is_typedef,
|
|||
type_maps_[type_name] = type_maps_[type_alias];
|
||||
}
|
||||
} else { // non-typedef
|
||||
if (kSemicolon == token.at(0)) {
|
||||
if (char(kSemicolon) == token.at(0)) {
|
||||
// format 2
|
||||
is_decl = false;
|
||||
if (type_name.isEmpty()) {
|
||||
|
@ -1542,7 +1548,7 @@ bool CTypeParser::parseEnum(const bool is_typedef, const QString &src,
|
|||
if (is_typedef) {
|
||||
// format 1
|
||||
if (!(getNextToken(src, pos, next_token) &&
|
||||
kSemicolon == next_token.at(0))) {
|
||||
char(kSemicolon) == next_token.at(0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1559,7 +1565,7 @@ bool CTypeParser::parseEnum(const bool is_typedef, const QString &src,
|
|||
;
|
||||
}
|
||||
} else { // non-typedef
|
||||
if (kSemicolon == token.at(0)) {
|
||||
if (char(kSemicolon) == token.at(0)) {
|
||||
// format 2
|
||||
is_decl = false;
|
||||
if (type_name.isEmpty()) {
|
||||
|
|
|
@ -211,6 +211,9 @@ private:
|
|||
/// @note All enum types have fixed size, so they're not stored
|
||||
QHash<QString, QPair<QMetaType::Type, qsizetype>> type_maps_;
|
||||
|
||||
/// basic types
|
||||
QStringList base_types_;
|
||||
|
||||
/// unsigned types
|
||||
QStringList unsigned_types_;
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "inspectqtloghelper.h"
|
||||
#include "utilities.h"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define INFOLOG(msg) "<font color=\"green\">" + msg + "</font>"
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
|
||||
#include "pluginsystem.h"
|
||||
#include "AngelScript/sdk/angelscript/source/as_builder.h"
|
||||
#include "QJsonModel/include/QJsonModel.hpp"
|
||||
#include "Qt-Advanced-Docking-System/src/DockAreaWidget.h"
|
||||
#include "class/languagemanager.h"
|
||||
#include "class/logger.h"
|
||||
#include "class/scriptmachine.h"
|
||||
#include "class/settingmanager.h"
|
||||
#include "class/skinmanager.h"
|
||||
#include "class/wingcstruct.h"
|
||||
|
@ -32,10 +32,11 @@
|
|||
#include "dialog/colorpickerdialog.h"
|
||||
#include "dialog/framelessdialogbase.h"
|
||||
#include "dialog/mainwindow.h"
|
||||
#include "model/qjsontablemodel.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfoList>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QMessageBox>
|
||||
#include <QPluginLoader>
|
||||
#include <QScopeGuard>
|
||||
|
@ -1786,99 +1787,6 @@ bool PluginSystem::closeAllFiles(QObject *sender) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool PluginSystem::dataVisualText(QObject *sender, const QString &data,
|
||||
const QString &title) {
|
||||
auto plg = checkPluginAndReport(sender, __func__);
|
||||
if (plg == nullptr || !checkThreadAff()) {
|
||||
return false;
|
||||
}
|
||||
_win->m_infotxt->setProperty("__TITLE__", title);
|
||||
_win->m_infotxt->setText(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PluginSystem::dataVisualTextList(QObject *sender, const QStringList &data,
|
||||
const QString &title,
|
||||
ClickedCallBack clicked,
|
||||
ClickedCallBack dblClicked) {
|
||||
auto plg = checkPluginAndReport(sender, __func__);
|
||||
if (plg == nullptr || !checkThreadAff()) {
|
||||
return false;
|
||||
}
|
||||
return updateTextList_API(data, title, clicked, dblClicked);
|
||||
}
|
||||
|
||||
bool PluginSystem::dataVisualTextTree(QObject *sender, const QString &json,
|
||||
const QString &title,
|
||||
ClickedCallBack clicked,
|
||||
ClickedCallBack dblClicked) {
|
||||
auto plg = checkPluginAndReport(sender, __func__);
|
||||
if (plg == nullptr || !checkThreadAff()) {
|
||||
return false;
|
||||
}
|
||||
return updateTextTree_API(json, title, clicked, dblClicked);
|
||||
}
|
||||
|
||||
bool PluginSystem::dataVisualTextTable(QObject *sender, const QString &json,
|
||||
const QStringList &headers,
|
||||
const QStringList &headerNames,
|
||||
const QString &title,
|
||||
ClickedCallBack clicked,
|
||||
ClickedCallBack dblClicked) {
|
||||
auto plg = checkPluginAndReport(sender, __func__);
|
||||
if (plg == nullptr || !checkThreadAff()) {
|
||||
return false;
|
||||
}
|
||||
return updateTextTable_API(json, headers, headerNames, title, clicked,
|
||||
dblClicked);
|
||||
}
|
||||
|
||||
bool PluginSystem::dataVisualTextListByModel(QObject *sender,
|
||||
QAbstractItemModel *model,
|
||||
const QString &title,
|
||||
ClickedCallBack clicked,
|
||||
ClickedCallBack dblClicked) {
|
||||
auto plg = checkPluginAndReport(sender, __func__);
|
||||
if (plg == nullptr || !checkThreadAff()) {
|
||||
return false;
|
||||
}
|
||||
if (model) {
|
||||
auto oldmodel = _win->m_infolist->model();
|
||||
if (oldmodel) {
|
||||
oldmodel->deleteLater();
|
||||
}
|
||||
_win->m_infolist->setProperty("__TITLE__", title);
|
||||
_win->m_infolist->setModel(model);
|
||||
_win->m_infoclickfn = clicked;
|
||||
_win->m_infodblclickfn = dblClicked;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PluginSystem::dataVisualTextTreeByModel(QObject *sender,
|
||||
QAbstractItemModel *model,
|
||||
const QString &title,
|
||||
ClickedCallBack clicked,
|
||||
ClickedCallBack dblClicked) {
|
||||
auto plg = checkPluginAndReport(sender, __func__);
|
||||
if (plg == nullptr || !checkThreadAff()) {
|
||||
return false;
|
||||
}
|
||||
if (model) {
|
||||
auto oldmodel = _win->m_infotree->model();
|
||||
if (oldmodel) {
|
||||
oldmodel->deleteLater();
|
||||
}
|
||||
_win->m_infotree->setProperty("__TITLE__", title);
|
||||
_win->m_infotree->setModel(model);
|
||||
_win->m_infotreeclickfn = clicked;
|
||||
_win->m_infotreedblclickfn = dblClicked;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PluginSystem::checkErrAllAllowAndReport(QObject *sender,
|
||||
const char *func) {
|
||||
auto p = qobject_cast<WingHex::IWingPlugin *>(sender);
|
||||
|
@ -1903,29 +1811,6 @@ IWingPlugin *PluginSystem::checkPluginAndReport(QObject *sender,
|
|||
return p;
|
||||
}
|
||||
|
||||
bool PluginSystem::dataVisualTextTableByModel(QObject *sender,
|
||||
QAbstractItemModel *model,
|
||||
const QString &title,
|
||||
ClickedCallBack clicked,
|
||||
ClickedCallBack dblClicked) {
|
||||
auto plg = checkPluginAndReport(sender, __func__);
|
||||
if (plg == nullptr || !checkThreadAff()) {
|
||||
return false;
|
||||
}
|
||||
if (model) {
|
||||
auto oldmodel = _win->m_infotable->model();
|
||||
if (oldmodel) {
|
||||
oldmodel->deleteLater();
|
||||
}
|
||||
_win->m_infotable->setProperty("__TITLE__", title);
|
||||
_win->m_infotable->setModel(model);
|
||||
_win->m_infotableclickfn = clicked;
|
||||
_win->m_infotabledblclickfn = dblClicked;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ErrFile PluginSystem::saveAsCurrent(QObject *sender, const QString &savename) {
|
||||
auto plg = checkPluginAndReport(sender, __func__);
|
||||
if (plg == nullptr) {
|
||||
|
@ -2955,6 +2840,10 @@ void PluginSystem::checkDirRootSafe(const QDir &dir) {
|
|||
}
|
||||
|
||||
void PluginSystem::registerFns(IWingPlugin *plg) {
|
||||
if (_angelplg == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(plg);
|
||||
auto fns = plg->registeredScriptFns();
|
||||
if (fns.isEmpty()) {
|
||||
|
@ -3027,6 +2916,10 @@ void PluginSystem::registerFns(IWingPlugin *plg) {
|
|||
}
|
||||
|
||||
void PluginSystem::registerUnSafeFns(IWingPlugin *plg) {
|
||||
if (_angelplg == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(plg);
|
||||
|
||||
auto fns = plg->registeredScriptUnsafeFns();
|
||||
|
@ -3233,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;
|
||||
|
@ -3715,77 +3608,6 @@ void PluginSystem::registerMarcoDevice(IWingDevice *plg) {
|
|||
_scriptMarcos.append(sep + id + sep);
|
||||
}
|
||||
|
||||
bool PluginSystem::updateTextList_API(const QStringList &data,
|
||||
const QString &title,
|
||||
const ClickCallBack &click,
|
||||
const ClickCallBack &dblclick) {
|
||||
auto oldmodel = _win->m_infolist->model();
|
||||
if (oldmodel) {
|
||||
oldmodel->deleteLater();
|
||||
}
|
||||
_win->m_infolist->setProperty("__TITLE__", title);
|
||||
auto model = new QStringListModel(data);
|
||||
_win->m_infolist->setModel(model);
|
||||
_win->m_infoclickfn = click;
|
||||
_win->m_infodblclickfn = dblclick;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PluginSystem::updateTextTree_API(const QString &json, const QString &title,
|
||||
const ClickCallBack &click,
|
||||
const ClickCallBack &dblclick) {
|
||||
auto oldmodel = _win->m_infotree->model();
|
||||
if (oldmodel) {
|
||||
oldmodel->deleteLater();
|
||||
}
|
||||
_win->m_infotree->setProperty("__TITLE__", title);
|
||||
auto model = new QJsonModel;
|
||||
if (model->loadJson(json.toUtf8())) {
|
||||
_win->m_infotree->setModel(model);
|
||||
_win->m_infotreeclickfn = click;
|
||||
_win->m_infotreedblclickfn = dblclick;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PluginSystem::updateTextTable_API(const QString &json,
|
||||
const QStringList &headers,
|
||||
const QStringList &headerNames,
|
||||
const QString &title,
|
||||
const ClickCallBack &click,
|
||||
const ClickCallBack &dblclick) {
|
||||
auto oldmodel = _win->m_infotable->model();
|
||||
if (oldmodel) {
|
||||
oldmodel->deleteLater();
|
||||
}
|
||||
|
||||
QJsonTableModel::Header header;
|
||||
if (headers.size() > headerNames.size()) {
|
||||
for (auto &name : headers) {
|
||||
QJsonTableModel::Heading heading;
|
||||
heading["index"] = name;
|
||||
heading["title"] = name;
|
||||
header.append(heading);
|
||||
}
|
||||
} else {
|
||||
auto np = headerNames.cbegin();
|
||||
for (auto p = headers.cbegin(); p != headers.cend(); ++p, ++np) {
|
||||
QJsonTableModel::Heading heading;
|
||||
heading["index"] = *p;
|
||||
heading["title"] = *np;
|
||||
header.append(heading);
|
||||
}
|
||||
}
|
||||
_win->m_infotable->setProperty("__TITLE__", title);
|
||||
auto model = new QJsonTableModel(header);
|
||||
model->setJson(QJsonDocument::fromJson(json.toUtf8()));
|
||||
_win->m_infotable->setModel(model);
|
||||
_win->m_infotableclickfn = click;
|
||||
_win->m_infotabledblclickfn = dblclick;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PluginSystem::checkThreadAff() {
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
Logger::warning(tr("Not allowed operation in non-UI thread"));
|
||||
|
@ -3886,6 +3708,8 @@ void PluginSystem::loadAllPlugin() {
|
|||
}
|
||||
}
|
||||
|
||||
Logger::newLine();
|
||||
|
||||
// loading finished, delete the checking engine
|
||||
finalizeCheckingEngine();
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#include <QVariant>
|
||||
|
||||
#include "WingPlugin/iwingdevice.h"
|
||||
#include "class/clickcallback.h"
|
||||
#include "class/wingangelapi.h"
|
||||
#include "control/editorview.h"
|
||||
|
||||
|
@ -176,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();
|
||||
|
@ -258,18 +257,6 @@ private:
|
|||
void registerPluginDockWidgets(IWingPluginBase *p);
|
||||
void registerPluginPages(IWingPluginBase *p);
|
||||
|
||||
public:
|
||||
bool updateTextList_API(const QStringList &data, const QString &title,
|
||||
const ClickCallBack &click,
|
||||
const ClickCallBack &dblclick);
|
||||
bool updateTextTree_API(const QString &json, const QString &title,
|
||||
const ClickCallBack &click,
|
||||
const ClickCallBack &dblclick);
|
||||
bool updateTextTable_API(const QString &json, const QStringList &headers,
|
||||
const QStringList &headerNames,
|
||||
const QString &title, const ClickCallBack &click,
|
||||
const ClickCallBack &dblclick);
|
||||
|
||||
public:
|
||||
// fpr crash checking
|
||||
QString currentLoadingPlugin() const;
|
||||
|
@ -735,41 +722,6 @@ public:
|
|||
// extension
|
||||
WING_SERVICE bool closeAllFiles(QObject *sender);
|
||||
|
||||
public:
|
||||
WING_SERVICE bool dataVisualText(QObject *sender, const QString &data,
|
||||
const QString &title);
|
||||
|
||||
WING_SERVICE bool dataVisualTextList(QObject *sender,
|
||||
const QStringList &data,
|
||||
const QString &title,
|
||||
WingHex::ClickedCallBack clicked,
|
||||
WingHex::ClickedCallBack dblClicked);
|
||||
|
||||
WING_SERVICE bool dataVisualTextTree(QObject *sender, const QString &json,
|
||||
const QString &title,
|
||||
WingHex::ClickedCallBack clicked,
|
||||
WingHex::ClickedCallBack dblClicked);
|
||||
|
||||
WING_SERVICE bool dataVisualTextTable(QObject *sender, const QString &json,
|
||||
const QStringList &headers,
|
||||
const QStringList &headerNames,
|
||||
const QString &title,
|
||||
WingHex::ClickedCallBack clicked,
|
||||
WingHex::ClickedCallBack dblClicked);
|
||||
|
||||
// API for Qt Plugin Only
|
||||
WING_SERVICE bool dataVisualTextListByModel(
|
||||
QObject *sender, QAbstractItemModel *model, const QString &title,
|
||||
WingHex::ClickedCallBack clicked, WingHex::ClickedCallBack dblClicked);
|
||||
|
||||
WING_SERVICE bool dataVisualTextTableByModel(
|
||||
QObject *sender, QAbstractItemModel *model, const QString &title,
|
||||
WingHex::ClickedCallBack clicked, WingHex::ClickedCallBack dblClicked);
|
||||
|
||||
WING_SERVICE bool dataVisualTextTreeByModel(
|
||||
QObject *sender, QAbstractItemModel *model, const QString &title,
|
||||
WingHex::ClickedCallBack clicked, WingHex::ClickedCallBack dblClicked);
|
||||
|
||||
private:
|
||||
WingHex::IWingPlugin *checkPluginAndReport(QObject *sender,
|
||||
const char *func);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -24,11 +24,10 @@
|
|||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
// This class is the modification of as_parser.
|
||||
// This class comes from as_parser.h .
|
||||
// You can modified it to support more features.
|
||||
/** It's a complex thing to fully support AngelScript code intellisense.
|
||||
** I just support basic code completion like local or global
|
||||
* variables/functions.
|
||||
** I just support basic code completion.
|
||||
** If you are interested in implement a well-featured intellisense like
|
||||
* Qt creator or Visual Studio, PRs are welcomed !!!
|
||||
*/
|
||||
|
@ -42,195 +41,192 @@ public:
|
|||
public:
|
||||
enum class SymbolType {
|
||||
Invalid,
|
||||
FnDecl,
|
||||
Import,
|
||||
Value, // a common value
|
||||
Variable, // a variable
|
||||
Variable, // variable or property in class
|
||||
Enum, // an enum
|
||||
Class, // a class type
|
||||
Function, // a function
|
||||
TypeDef, // a typedef
|
||||
FnDef, // a funcdef
|
||||
Property, // a property
|
||||
Interface, // an interface
|
||||
Import, // import but not supported
|
||||
};
|
||||
|
||||
enum class Visiblity { Public, Private, Protected };
|
||||
|
||||
struct Symbol;
|
||||
|
||||
/**
|
||||
* @brief The CodeSegment class
|
||||
*/
|
||||
struct CodeSegment {
|
||||
bool valid = false;
|
||||
// QByteArrayList ns;
|
||||
QByteArray ret;
|
||||
QByteArrayList scope;
|
||||
QByteArray name;
|
||||
qsizetype nameInSrc = -1;
|
||||
QList<Symbol> args;
|
||||
QByteArray code;
|
||||
SymbolType type = QAsCodeParser::SymbolType::Invalid;
|
||||
qsizetype offset = -1;
|
||||
QByteArray codes;
|
||||
|
||||
bool isValid() const { return valid; }
|
||||
QByteArrayList additonalInfos; // for other additonal infos
|
||||
|
||||
public:
|
||||
inline bool isValid() const {
|
||||
return offset >= 0 && type != SymbolType::Invalid;
|
||||
}
|
||||
|
||||
inline qsizetype end() const {
|
||||
if (offset < 0) {
|
||||
return -1;
|
||||
}
|
||||
return offset + codes.length();
|
||||
}
|
||||
|
||||
inline qsizetype length() const { return codes.length(); }
|
||||
|
||||
inline bool hasCodes() const { return !codes.isEmpty(); }
|
||||
};
|
||||
|
||||
struct Symbol {
|
||||
SymbolType type = SymbolType::Invalid;
|
||||
QString name;
|
||||
qsizetype nameInSrc = -1;
|
||||
QString typeStr;
|
||||
Visiblity vis = Visiblity::Public;
|
||||
QList<Symbol> content;
|
||||
SymbolType symtype = QAsCodeParser::SymbolType::Invalid;
|
||||
QByteArrayList scope;
|
||||
QByteArray name;
|
||||
QByteArray type;
|
||||
int offset = -1;
|
||||
Visiblity vis = QAsCodeParser::Visiblity::Public;
|
||||
QByteArray additonalInfo; // for other additonal info
|
||||
|
||||
// QByteArrayList ns; // namespaces
|
||||
QMap<qsizetype, CodeSegment> codesegs; // used in class
|
||||
|
||||
// size_t scope = 0; // 0 for all
|
||||
|
||||
bool isValid() const { return type != SymbolType::Invalid; }
|
||||
QByteArrayList inherit;
|
||||
QList<Symbol> children;
|
||||
};
|
||||
|
||||
using SymbolTable = QMap<size_t, Symbol>;
|
||||
public:
|
||||
// First, we should parse and split the code into segments
|
||||
QList<QAsCodeParser::CodeSegment> parse(const QByteArray &codes);
|
||||
|
||||
private:
|
||||
// QMap< offset , CodeSegment>
|
||||
QMap<qsizetype, CodeSegment> m_segs; // global functions
|
||||
// Then, we can deep parsing for code completion
|
||||
QList<Symbol> parseIntell(qsizetype offset,
|
||||
const QList<QAsCodeParser::CodeSegment> &segs);
|
||||
|
||||
// so, a helper function?
|
||||
QList<Symbol> parseAndIntell(qsizetype offset, const QByteArray &codes);
|
||||
|
||||
public:
|
||||
SymbolTable parse(const QByteArray &codes);
|
||||
|
||||
SymbolTable parseIntell(qsizetype offset, const QByteArray &codes);
|
||||
static eTokenType getToken(asIScriptEngine *engine, const char *string,
|
||||
size_t stringLength, size_t *tokenLength);
|
||||
|
||||
private:
|
||||
void ParseScript(bool inBlock);
|
||||
QList<QAsCodeParser::CodeSegment> parseScript(bool inBlock);
|
||||
|
||||
void skipCodeBlock();
|
||||
QByteArray getSymbolString(const sToken &t);
|
||||
|
||||
QByteArrayList getRealNamespace(const QByteArrayList &ns);
|
||||
|
||||
// parse tokens
|
||||
sToken ParseIdentifier();
|
||||
sToken ParseToken(int token);
|
||||
sToken ParseRealType();
|
||||
sToken ParseDataType(bool allowVariableType = false,
|
||||
bool allowAuto = false);
|
||||
sToken ParseOneOf(int *tokens, int count);
|
||||
sToken ParseType(bool allowConst, bool allowVariableType = false,
|
||||
bool allowAuto = false);
|
||||
|
||||
// Statements
|
||||
sToken SuperficiallyParseVarInit();
|
||||
void superficiallyParseVarInit();
|
||||
sToken superficiallyParseStatementBlock();
|
||||
void superficiallyParseExpression();
|
||||
void superficiallyParseTemplateList();
|
||||
|
||||
// parse and get symbols
|
||||
Symbol ParseImport();
|
||||
void ParseEnumeration();
|
||||
void ParseTypedef();
|
||||
void ParseClass();
|
||||
void ParseMixin();
|
||||
void ParseInterface();
|
||||
Symbol ParseFuncDef();
|
||||
void ParseNamespace();
|
||||
void ParseReturn();
|
||||
void ParseBreak();
|
||||
void ParseContinue();
|
||||
void ParseTryCatch();
|
||||
void ParseIf();
|
||||
void ParseLambda();
|
||||
void ParseStatement();
|
||||
CodeSegment ParseFunction(bool isMethod = false);
|
||||
void ParseExpressionStatement();
|
||||
void ParseListPattern();
|
||||
void ParseStatementBlock();
|
||||
|
||||
void ParseAssignment();
|
||||
void ParseAssignOperator();
|
||||
void ParseSwitch();
|
||||
void ParseCase();
|
||||
void ParseFor();
|
||||
void ParseWhile();
|
||||
void ParseDoWhile();
|
||||
void ParseCondition();
|
||||
void ParseExpression();
|
||||
void ParseExprTerm();
|
||||
void ParseExprOperator();
|
||||
void ParseExprPreOp();
|
||||
void ParseExprPostOp();
|
||||
void ParseExprValue();
|
||||
|
||||
QByteArrayList ParseOptionalScope();
|
||||
|
||||
Symbol ParseVirtualPropertyDecl(bool isMethod, bool isInterface);
|
||||
QList<Symbol> ParseParameterList();
|
||||
|
||||
// parse but not get symbols
|
||||
void ParseTypeMod(bool isParam);
|
||||
void ParseFunctionCall();
|
||||
void SuperficiallyParseStatementBlock();
|
||||
void ParseInitList();
|
||||
void ParseCast();
|
||||
void ParseVariableAccess();
|
||||
void ParseConstructCall();
|
||||
void ParseConstant();
|
||||
void parseMethodAttributes();
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
|
||||
bool IsVarDecl();
|
||||
bool IsVirtualPropertyDecl();
|
||||
bool IsFuncDecl(bool isMethod);
|
||||
bool IsLambda();
|
||||
bool IsFunctionCall();
|
||||
|
||||
void GetToken(sToken *token);
|
||||
void RewindTo(const sToken *token);
|
||||
void RewindErrorTo(sToken *token);
|
||||
void SetPos(size_t pos);
|
||||
|
||||
bool IsRealType(int tokenType);
|
||||
bool IdentifierIs(const sToken &t, const char *str);
|
||||
bool IsDataType(const sToken &token);
|
||||
CodeSegment parseEnumeration();
|
||||
CodeSegment parseTypedef();
|
||||
CodeSegment parseClass();
|
||||
CodeSegment parseMixin();
|
||||
CodeSegment parseInterface();
|
||||
CodeSegment parseFuncDef();
|
||||
CodeSegment parseFunction();
|
||||
CodeSegment parseFunctionMethod(Visiblity &vis);
|
||||
|
||||
private:
|
||||
bool CheckTemplateType(const sToken &t);
|
||||
bool ParseTemplTypeList(bool required = true);
|
||||
void ParseMethodAttributes();
|
||||
// parse tokens
|
||||
sToken parseIdentifier();
|
||||
sToken parseToken(int token);
|
||||
sToken parseRealType();
|
||||
sToken parseDataType(bool allowVariableType = false,
|
||||
bool allowAuto = false);
|
||||
sToken parseOneOf(int *tokens, int count);
|
||||
|
||||
void ParseArgList(bool withParenthesis = true);
|
||||
|
||||
void SuperficiallyParseExpression();
|
||||
void parseNamespace();
|
||||
|
||||
private:
|
||||
bool FindTokenAfterType(sToken &nextToken);
|
||||
bool FindIdentifierAfterScope(sToken &nextToken);
|
||||
bool IsConstant(int tokenType);
|
||||
bool IsOperator(int tokenType);
|
||||
bool IsPreOperator(int tokenType);
|
||||
bool IsPostOperator(int tokenType);
|
||||
bool IsAssignOperator(int tokenType);
|
||||
// deep parsing
|
||||
QList<Symbol> parseEnumerationContent(const QByteArrayList &ns,
|
||||
const QByteArray &name,
|
||||
const QByteArray &code);
|
||||
Symbol parseTypedefContent(const QByteArrayList &ns,
|
||||
const QByteArray &code);
|
||||
|
||||
bool DoesTypeExist(const QString &t);
|
||||
QList<Symbol> parseGlobalVarDecls(const QByteArrayList &ns,
|
||||
const QByteArray &code);
|
||||
|
||||
Symbol ParseFunctionDefinition();
|
||||
QByteArrayList parseOptionalScope();
|
||||
|
||||
QList<Symbol> ParseDeclaration(bool isClassProp = false,
|
||||
QList<Symbol> parseDeclaration(qsizetype end, const QByteArrayList &ns,
|
||||
bool isClassProp = false,
|
||||
bool isGlobalVar = false);
|
||||
|
||||
Symbol ParseInterfaceMethod();
|
||||
QByteArray parseType(bool allowConst, bool allowVariableType = false,
|
||||
bool allowAuto = false);
|
||||
|
||||
void ParseStringConstant();
|
||||
Symbol parseFuncDefContent(const QByteArrayList &ns,
|
||||
const QByteArray &code);
|
||||
|
||||
Symbol parseFuncDefContent(const QByteArrayList &ns);
|
||||
|
||||
QPair<QByteArrayList, QList<Symbol>>
|
||||
parseClassContent(qsizetype offset, const QByteArrayList &ns,
|
||||
const QByteArray &code);
|
||||
|
||||
QPair<QByteArrayList, QList<Symbol>>
|
||||
parseInterfaceContent(qsizetype offset, const QByteArrayList &ns,
|
||||
const QByteArray &code);
|
||||
|
||||
QList<Symbol> parseStatementBlock(const QByteArrayList &ns,
|
||||
const QByteArray &code, qsizetype end);
|
||||
|
||||
private:
|
||||
bool errorWhileParsing;
|
||||
bool isSyntaxError;
|
||||
bool checkValidTypes;
|
||||
bool isParsingAppInterface;
|
||||
Symbol parseVirtualPropertyDecl(bool isMethod, bool isInterface);
|
||||
QList<Symbol> parseParameterListContent();
|
||||
|
||||
// parse but not get symbols
|
||||
void parseTypeMod(bool isParam);
|
||||
|
||||
private:
|
||||
void reset();
|
||||
|
||||
bool isVarDecl();
|
||||
bool isVirtualPropertyDecl();
|
||||
bool isFuncDecl(bool isMethod);
|
||||
|
||||
void getToken(sToken *token);
|
||||
void rewindTo(const sToken *token);
|
||||
void rewindErrorTo(sToken *token);
|
||||
void setPos(size_t pos);
|
||||
|
||||
bool isPrimType(int tokenType);
|
||||
bool identifierIs(const sToken &t, const char *str);
|
||||
bool isDataType(const sToken &token);
|
||||
|
||||
private:
|
||||
bool checkTemplateType(const sToken &t);
|
||||
|
||||
private:
|
||||
bool findTokenAfterType(sToken &nextToken);
|
||||
|
||||
bool typeExist(const QString &t);
|
||||
|
||||
Symbol parseInterfaceMethod();
|
||||
|
||||
private:
|
||||
bool _errorWhileParsing;
|
||||
bool _isSyntaxError;
|
||||
bool _checkValidTypes;
|
||||
bool _isParsingAppInterface;
|
||||
|
||||
asCScriptEngine *engine;
|
||||
|
||||
// size_t _curscope = 0;
|
||||
SymbolTable _symtable;
|
||||
QByteArray _code;
|
||||
|
||||
QByteArray code;
|
||||
QByteArray tempString; // Used for reduzing amount of dynamic allocations
|
||||
sToken _lastToken;
|
||||
size_t _sourcePos;
|
||||
|
||||
sToken lastToken;
|
||||
size_t sourcePos;
|
||||
QByteArrayList currentNs;
|
||||
};
|
||||
|
||||
#endif // QASCODEPARSER_H
|
||||
|
|
|
@ -1,157 +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 "scriptconsolemachine.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QTextStream>
|
||||
|
||||
ScriptConsoleMachine::ScriptConsoleMachine(
|
||||
std::function<QString(void)> &getInputFn, QObject *parent)
|
||||
: ScriptMachine(getInputFn, parent) {
|
||||
if (!ScriptConsoleMachine::configureEngine(engine())) {
|
||||
destoryMachine();
|
||||
}
|
||||
if (engine()) {
|
||||
_model = new ScriptObjModel(engine(), debugger(), this);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptConsoleMachine::~ScriptConsoleMachine() {}
|
||||
|
||||
bool ScriptConsoleMachine::executeCode(const QString &code) {
|
||||
return execString(engine(), code);
|
||||
}
|
||||
|
||||
ScriptObjModel *ScriptConsoleMachine::model() const { return _model; }
|
||||
|
||||
bool ScriptConsoleMachine::configureEngine(asIScriptEngine *engine) {
|
||||
_clsfn = std::bind(&ScriptConsoleMachine::onClearConsole, this);
|
||||
auto r = engine->RegisterGlobalFunction(
|
||||
"void clear()", asMETHOD(decltype(_clsfn), operator()),
|
||||
asCALL_THISCALL_ASGLOBAL, &_clsfn);
|
||||
Q_ASSERT(r >= 0);
|
||||
return r >= 0;
|
||||
}
|
||||
|
||||
bool ScriptConsoleMachine::execString(asIScriptEngine *engine,
|
||||
const QString &code) {
|
||||
auto mod = engine->GetModule("Console", asGM_CREATE_IF_NOT_EXISTS);
|
||||
if (code.startsWith(QStringLiteral("addvar "))) {
|
||||
auto varcmd = code.mid(7) + QStringLiteral(";");
|
||||
if (varcmd.length() == 1) {
|
||||
MessageInfo info;
|
||||
info.message = tr("EmptyDecl");
|
||||
emit onOutput(MessageType::Error, info);
|
||||
return false;
|
||||
}
|
||||
auto ret = mod->CompileGlobalVar("addvar", varcmd.toUtf8(), 0) >= 0;
|
||||
_model->updateData();
|
||||
return ret;
|
||||
} else if (code.startsWith(QStringLiteral("rmvar "))) {
|
||||
if (mod == nullptr || mod->GetGlobalVarCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove the tailing semi-colons
|
||||
static QRegularExpression re(QStringLiteral(";+$"));
|
||||
|
||||
auto varcmd = code.mid(6).remove(re);
|
||||
for (auto &var : varcmd.split(' ', Qt::SkipEmptyParts)) {
|
||||
int index = mod->GetGlobalVarIndexByName(var.toUtf8());
|
||||
if (index >= 0) {
|
||||
mod->RemoveGlobalVar(index);
|
||||
}
|
||||
}
|
||||
_model->updateData();
|
||||
return true;
|
||||
} else if (code.startsWith(QStringLiteral("lsfn"))) {
|
||||
if (code.trimmed().length() != 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString str;
|
||||
QTextStream s(&str);
|
||||
asUINT n;
|
||||
|
||||
// List the application registered functions
|
||||
s << tr("Application functions:") << Qt::endl;
|
||||
for (n = 0; n < engine->GetGlobalFunctionCount(); n++) {
|
||||
asIScriptFunction *func = engine->GetGlobalFunctionByIndex(n);
|
||||
|
||||
// Skip the functions that start with _ as these are not meant to be
|
||||
// called explicitly by the user
|
||||
if (func->GetName()[0] != '_')
|
||||
s << QStringLiteral(" ") << func->GetDeclaration() << Qt::endl;
|
||||
}
|
||||
|
||||
// List the user functions in the module
|
||||
if (mod) {
|
||||
s << Qt::endl;
|
||||
s << tr("User functions:") << Qt::endl;
|
||||
for (n = 0; n < mod->GetFunctionCount(); n++) {
|
||||
asIScriptFunction *func = mod->GetFunctionByIndex(n);
|
||||
s << QStringLiteral(" ") << func->GetDeclaration() << Qt::endl;
|
||||
}
|
||||
}
|
||||
|
||||
MessageInfo info;
|
||||
info.message = str;
|
||||
emit onOutput(MessageType::Info, info);
|
||||
return true;
|
||||
} else if (code.startsWith(QStringLiteral("lsvar"))) {
|
||||
if (code.trimmed().length() != 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString str;
|
||||
QTextStream s(&str);
|
||||
asUINT n;
|
||||
|
||||
// List the application registered variables
|
||||
s << tr("Application variables:") << Qt::endl;
|
||||
for (n = 0; n < engine->GetGlobalPropertyCount(); n++) {
|
||||
const char *name;
|
||||
int typeId;
|
||||
bool isConst;
|
||||
engine->GetGlobalPropertyByIndex(n, &name, 0, &typeId, &isConst);
|
||||
auto decl =
|
||||
isConst ? QStringLiteral(" const ") : QStringLiteral(" ");
|
||||
decl += engine->GetTypeDeclaration(typeId);
|
||||
decl += QStringLiteral(" ");
|
||||
decl += name;
|
||||
s << decl << Qt::endl;
|
||||
}
|
||||
|
||||
// List the user variables in the module
|
||||
if (mod) {
|
||||
s << Qt::endl;
|
||||
s << tr("User variables:") << Qt::endl;
|
||||
for (n = 0; n < (asUINT)mod->GetGlobalVarCount(); n++) {
|
||||
s << QStringLiteral(" ") << mod->GetGlobalVarDeclaration(n)
|
||||
<< Qt::endl;
|
||||
}
|
||||
}
|
||||
|
||||
MessageInfo info;
|
||||
info.message = str;
|
||||
emit onOutput(MessageType::Info, info);
|
||||
return true;
|
||||
} else {
|
||||
return ScriptMachine::executeCode(code);
|
||||
}
|
||||
}
|
|
@ -1,50 +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 SCRIPTCONSOLEMACHINE_H
|
||||
#define SCRIPTCONSOLEMACHINE_H
|
||||
|
||||
#include "model/scriptobjmodel.h"
|
||||
#include "scriptmachine.h"
|
||||
|
||||
class ScriptConsoleMachine : public ScriptMachine {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ScriptConsoleMachine(std::function<QString()> &getInputFn,
|
||||
QObject *parent = nullptr);
|
||||
virtual ~ScriptConsoleMachine();
|
||||
|
||||
virtual bool executeCode(const QString &code) override;
|
||||
|
||||
ScriptObjModel *model() const;
|
||||
|
||||
signals:
|
||||
void onClearConsole();
|
||||
|
||||
protected:
|
||||
virtual bool configureEngine(asIScriptEngine *engine) override;
|
||||
|
||||
private:
|
||||
bool execString(asIScriptEngine *engine, const QString &code);
|
||||
|
||||
private:
|
||||
ScriptObjModel *_model = nullptr;
|
||||
|
||||
std::function<void(void)> _clsfn;
|
||||
};
|
||||
|
||||
#endif // SCRIPTCONSOLEMACHINE_H
|
File diff suppressed because it is too large
Load Diff
|
@ -18,27 +18,40 @@
|
|||
#ifndef SCRIPTMACHINE_H
|
||||
#define SCRIPTMACHINE_H
|
||||
|
||||
#include "AngelScript/sdk/add_on/contextmgr/contextmgr.h"
|
||||
#include "AngelScript/sdk/angelscript/include/angelscript.h"
|
||||
|
||||
#include "class/aspreprocesser.h"
|
||||
|
||||
#include "asdebugger.h"
|
||||
#include "class/ascontextmgr.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QQueue>
|
||||
|
||||
class CScriptArray;
|
||||
|
||||
class ScriptMachine : public QObject {
|
||||
Q_OBJECT
|
||||
private:
|
||||
using TranslateFunc = std::function<QString(const QStringList &)>;
|
||||
|
||||
public:
|
||||
// we have three console modes
|
||||
enum ConsoleMode {
|
||||
Interactive = 1, // in a shell
|
||||
Scripting, // in scripting dialog
|
||||
Background, // run codes from other way
|
||||
};
|
||||
|
||||
public:
|
||||
enum class MessageType { Info, Warn, Error, Print };
|
||||
|
||||
struct MessageInfo {
|
||||
ConsoleMode mode = ConsoleMode::Background;
|
||||
QString section;
|
||||
int row = -1;
|
||||
int col = -1;
|
||||
asEMsgType type = asEMsgType::asMSGTYPE_INFORMATION;
|
||||
MessageType type = MessageType::Info;
|
||||
QString message;
|
||||
};
|
||||
|
||||
|
@ -78,29 +91,54 @@ public:
|
|||
Q_ENUM(asEContextState)
|
||||
|
||||
public:
|
||||
explicit ScriptMachine(const std::function<QString()> &getInputFn,
|
||||
QObject *parent = nullptr);
|
||||
struct RegCallBacks {
|
||||
std::function<QString()> getInputFn;
|
||||
std::function<void()> clearFn;
|
||||
std::function<void(const ScriptMachine::MessageInfo &)> printMsgFn;
|
||||
};
|
||||
|
||||
virtual ~ScriptMachine();
|
||||
private:
|
||||
explicit ScriptMachine();
|
||||
Q_DISABLE_COPY_MOVE(ScriptMachine)
|
||||
|
||||
bool inited();
|
||||
private:
|
||||
asIScriptModule *createModule(ConsoleMode mode);
|
||||
asIScriptModule *createModuleIfNotExist(ConsoleMode mode);
|
||||
bool isModuleExists(ConsoleMode mode);
|
||||
|
||||
bool isRunning() const;
|
||||
public:
|
||||
asIScriptModule *module(ConsoleMode mode);
|
||||
|
||||
static ScriptMachine &instance();
|
||||
void destoryMachine();
|
||||
|
||||
public:
|
||||
bool init();
|
||||
bool isInited() const;
|
||||
bool isRunning(ConsoleMode mode) const;
|
||||
|
||||
static void registerEngineAddon(asIScriptEngine *engine);
|
||||
static void registerEngineAssert(asIScriptEngine *engine);
|
||||
static void registerEngineClipboard(asIScriptEngine *engine);
|
||||
|
||||
void registerCallBack(ConsoleMode mode, const RegCallBacks &callbacks);
|
||||
|
||||
public:
|
||||
asDebugger *debugger() const;
|
||||
|
||||
asIScriptEngine *engine() const;
|
||||
|
||||
bool insteadFoundDisabled() const;
|
||||
void setInsteadFoundDisabled(bool newInsteadFoundDisabled);
|
||||
|
||||
static void registerEngineAddon(asIScriptEngine *engine);
|
||||
static void registerEngineAssert(asIScriptEngine *engine);
|
||||
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();
|
||||
|
@ -109,27 +147,28 @@ public:
|
|||
|
||||
public:
|
||||
// debug or release?
|
||||
bool isDebugMode() const;
|
||||
void setDebugMode(bool isDbg);
|
||||
bool isDebugMode(ConsoleMode mode = Scripting);
|
||||
|
||||
public slots:
|
||||
virtual bool executeCode(const QString &code);
|
||||
virtual bool executeScript(const QString &script, bool isInDebug = false);
|
||||
bool executeCode(ConsoleMode mode, const QString &code);
|
||||
// only scripting mode can be debugged
|
||||
bool executeScript(ConsoleMode mode, const QString &script,
|
||||
bool isInDebug = false, int *retCode = nullptr);
|
||||
|
||||
int evaluateDefine(const QString &code, bool &result);
|
||||
|
||||
void abortDbgScript();
|
||||
void abortScript(ConsoleMode mode);
|
||||
void abortScript();
|
||||
|
||||
private:
|
||||
bool execute(const QString &code, bool isInDebug = false);
|
||||
|
||||
protected:
|
||||
virtual bool configureEngine(asIScriptEngine *engine);
|
||||
bool configureEngine();
|
||||
|
||||
QString getCallStack(asIScriptContext *context);
|
||||
|
||||
void destoryMachine();
|
||||
|
||||
private:
|
||||
void print(void *ref, int typeId);
|
||||
static void print(asIScriptGeneric *args);
|
||||
static void println(asIScriptGeneric *args);
|
||||
|
||||
QString getInput();
|
||||
|
||||
|
@ -138,6 +177,10 @@ private:
|
|||
static int execSystemCmd(QString &out, const QString &exe,
|
||||
const QString ¶ms, 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);
|
||||
|
||||
|
@ -166,21 +209,19 @@ private:
|
|||
Q_DECL_UNUSED void translation();
|
||||
|
||||
signals:
|
||||
void onOutput(MessageType type, const MessageInfo &message);
|
||||
|
||||
void onDebugFinished();
|
||||
|
||||
private:
|
||||
asIScriptEngine *_engine = nullptr;
|
||||
asDebugger *_debugger = nullptr;
|
||||
asContextMgr *_ctxMgr = nullptr;
|
||||
QVector<asIScriptContext *> _ctxPool;
|
||||
std::function<void(void *ref, int typeId)> _printFn;
|
||||
CContextMgr *_ctxMgr = nullptr;
|
||||
|
||||
QQueue<asIScriptContext *> _ctxPool;
|
||||
|
||||
QVector<asITypeInfo *> _rtypes;
|
||||
std::function<QString(void)> _getInputFn;
|
||||
|
||||
bool m_insteadFoundDisabled = false;
|
||||
QMap<ConsoleMode, RegCallBacks> _regcalls;
|
||||
QMap<ConsoleMode, asIScriptContext *> _ctx;
|
||||
ConsoleMode _curMsgMode = ConsoleMode::Background;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ScriptMachine::MessageInfo)
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <QJsonObject>
|
||||
#include <QMenu>
|
||||
|
||||
#include "scriptmachine.h"
|
||||
#include "settingmanager.h"
|
||||
#include "utilities.h"
|
||||
#include "wingmessagebox.h"
|
||||
|
@ -50,7 +51,7 @@ ScriptManager::ScriptManager() : QObject() {
|
|||
refresh();
|
||||
}
|
||||
|
||||
ScriptManager::~ScriptManager() { detach(); }
|
||||
ScriptManager::~ScriptManager() {}
|
||||
|
||||
QStringList ScriptManager::getScriptFileNames(const QDir &dir) const {
|
||||
if (!dir.exists()) {
|
||||
|
@ -72,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) {
|
||||
|
@ -166,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);
|
||||
|
@ -194,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();
|
||||
}
|
||||
|
@ -206,14 +215,6 @@ ScriptManager::ensureDirMeta(const QFileInfo &info) {
|
|||
return meta;
|
||||
}
|
||||
|
||||
void ScriptManager::attach(ScriptingConsole *console) {
|
||||
if (console) {
|
||||
_console = console;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::detach() { _console = nullptr; }
|
||||
|
||||
ScriptManager::ScriptDirMeta
|
||||
ScriptManager::usrDirMeta(const QString &cat) const {
|
||||
return _usrDirMetas.value(cat);
|
||||
|
@ -224,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();
|
||||
|
@ -236,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();
|
||||
|
@ -247,23 +259,37 @@ 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;
|
||||
}
|
||||
|
||||
void ScriptManager::runScript(const QString &filename) {
|
||||
Q_ASSERT(_console);
|
||||
_console->setMode(ScriptingConsole::Output);
|
||||
_console->stdWarn(tr("Excuting:") + filename);
|
||||
_console->newLine();
|
||||
_console->machine()->executeScript(filename);
|
||||
_console->appendCommandPrompt();
|
||||
_console->setMode(ScriptingConsole::Input);
|
||||
auto &ins = ScriptMachine::instance();
|
||||
if (ins.isRunning(ScriptMachine::Background)) {
|
||||
auto ret = QMessageBox::question(nullptr, tr("ScriptRunning"),
|
||||
tr("ScriptRunningRequestLastStop?"));
|
||||
if (ret == QMessageBox::Yes) {
|
||||
ins.abortScript(ScriptMachine::Background);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ins.executeScript(ScriptMachine::Background, filename);
|
||||
}
|
||||
|
||||
QStringList ScriptManager::usrScriptsDbCats() const {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
@ -65,13 +60,11 @@ public:
|
|||
void refreshUsrScriptsDbCats();
|
||||
void refreshSysScriptsDbCats();
|
||||
|
||||
void attach(ScriptingConsole *console);
|
||||
void detach();
|
||||
|
||||
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,
|
||||
|
@ -122,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);
|
||||
|
||||
|
@ -133,8 +128,6 @@ private:
|
|||
|
||||
QHash<QString, ScriptDirMeta> _usrDirMetas;
|
||||
QHash<QString, ScriptDirMeta> _sysDirMetas;
|
||||
|
||||
ScriptingConsole *_console = nullptr;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ScriptManager::ScriptDirMeta)
|
||||
|
|
|
@ -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,
|
||||
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_FONT, SETTING_ITEM::FONT,
|
||||
_defaultFont.defaultFamily());
|
||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
|
||||
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
|
||||
10);
|
||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_THEME, SETTING_ITEM::THEME, {});
|
||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
|
||||
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_THEME, SETTING_ITEM::THEME, {});
|
||||
WRITE_CONFIG_EDITOR_RESET(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,
|
||||
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_SET(CODEEDIT_WORD_WRAP, SETTING_ITEM::WORD_WRAP,
|
||||
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_WORD_WRAP, SETTING_ITEM::WORD_WRAP,
|
||||
false);
|
||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_LINENUMBER,
|
||||
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_LINENUMBER,
|
||||
SETTING_ITEM::SHOW_LINENUMBER, true);
|
||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_FOLDING,
|
||||
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_FOLDING,
|
||||
SETTING_ITEM::SHOW_FOLDING, true);
|
||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_INDENTGUIDES,
|
||||
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_INDENTGUIDES,
|
||||
SETTING_ITEM::SHOW_INDENTGUIDES, true);
|
||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_LONGLINEEDGE,
|
||||
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_LONGLINEEDGE,
|
||||
SETTING_ITEM::SHOW_LONGLINEEDGE, false);
|
||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_SHOW_WHITESPACE,
|
||||
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_SHOW_WHITESPACE,
|
||||
SETTING_ITEM::SHOW_WHITESPACE, false);
|
||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_AUTO_CLOSE_CHAR,
|
||||
WRITE_CONFIG_EDITOR_RESET(CODEEDIT_AUTO_CLOSE_CHAR,
|
||||
SETTING_ITEM::AUTO_CLOSE_CHAR, true);
|
||||
WRITE_CONFIG_EDITOR_SET(CODEEDIT_AUTO_IDEN, SETTING_ITEM::AUTO_IDEN,
|
||||
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,
|
||||
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_FONT, SETTING_ITEM::FONT,
|
||||
_defaultFont.defaultFamily());
|
||||
WRITE_CONFIG_CONSOLE_SET(CONSOLE_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
|
||||
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_FONT_SIZE, SETTING_ITEM::FONT_SIZE,
|
||||
10);
|
||||
WRITE_CONFIG_CONSOLE_SET(CONSOLE_THEME, SETTING_ITEM::THEME, {});
|
||||
WRITE_CONFIG_CONSOLE_SET(CONSOLE_TABS_WIDTH, SETTING_ITEM::TAB_WIDTH,
|
||||
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_THEME, SETTING_ITEM::THEME, {});
|
||||
WRITE_CONFIG_CONSOLE_RESET(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,
|
||||
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_SET(CONSOLE_SHOW_WHITESPACE,
|
||||
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_SHOW_WHITESPACE,
|
||||
SETTING_ITEM::SHOW_WHITESPACE, false);
|
||||
WRITE_CONFIG_CONSOLE_SET(CONSOLE_AUTO_CLOSE_CHAR,
|
||||
WRITE_CONFIG_CONSOLE_RESET(CONSOLE_AUTO_CLOSE_CHAR,
|
||||
SETTING_ITEM::AUTO_CLOSE_CHAR, true);
|
||||
}
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
ScriptSettings::ScriptSettings() : QObject() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,9 +23,7 @@
|
|||
|
||||
#include <QFileDialog>
|
||||
|
||||
#include <any>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
class asIScriptEngine;
|
||||
class ScriptMachine;
|
||||
|
@ -73,9 +71,6 @@ public:
|
|||
void installAPI(ScriptMachine *machine);
|
||||
void installBasicTypes(asIScriptEngine *engine);
|
||||
|
||||
void setBindingConsole(ScriptingConsole *console);
|
||||
void unBindConsole();
|
||||
|
||||
static QString qvariantCastASString(const QMetaType::Type &id);
|
||||
|
||||
static int qvariantCastASID(asIScriptEngine *engine,
|
||||
|
@ -100,25 +95,13 @@ private:
|
|||
void installHexBaseType(asIScriptEngine *engine);
|
||||
void installHexReaderAPI(asIScriptEngine *engine);
|
||||
void installHexControllerAPI(asIScriptEngine *engine);
|
||||
void installDataVisualAPI(asIScriptEngine *engine);
|
||||
void installScriptFns(asIScriptEngine *engine);
|
||||
void installScriptUnSafeFns(asIScriptEngine *engine);
|
||||
void installScriptEnums(asIScriptEngine *engine);
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
void registerAPI(asIScriptEngine *engine, const std::function<T> &fn,
|
||||
const char *sig) {
|
||||
_fnbuffer.push_back(fn);
|
||||
auto r = engine->RegisterGlobalFunction(
|
||||
sig, asMETHOD(std::function<T>, operator()),
|
||||
asCALL_THISCALL_ASGLOBAL,
|
||||
std::any_cast<std::function<T>>(&_fnbuffer.back()));
|
||||
Q_ASSERT(r >= 0);
|
||||
Q_UNUSED(r);
|
||||
}
|
||||
|
||||
using WrapperFn = std::function<void(asIScriptGeneric *)>;
|
||||
void registerAPI(asIScriptEngine *engine, const asSFuncPtr &fn,
|
||||
const char *sig);
|
||||
|
||||
private:
|
||||
QStringList cArray2QStringList(const CScriptArray &array, int stringID,
|
||||
|
@ -203,22 +186,9 @@ private:
|
|||
|
||||
// =========================================================
|
||||
|
||||
QString getSenderHeader(const WingHex::SenderInfo &sender);
|
||||
|
||||
void cleanUpHandles(const QVector<int> &handles);
|
||||
|
||||
private:
|
||||
QString _InputBox_getItem(const QString &title, const QString &label,
|
||||
const CScriptArray &items, int current,
|
||||
bool editable, bool *ok,
|
||||
Qt::InputMethodHints inputMethodHints);
|
||||
|
||||
CScriptArray *_FileDialog_getOpenFileNames(const QString &caption,
|
||||
const QString &dir,
|
||||
const QString &filter,
|
||||
QString *selectedFilter,
|
||||
QFileDialog::Options options);
|
||||
|
||||
CScriptArray *_HexReader_selectedBytes(qsizetype index);
|
||||
|
||||
CScriptArray *_HexReader_selectionBytes();
|
||||
|
@ -235,34 +205,92 @@ private:
|
|||
|
||||
bool _HexController_appendBytes(const CScriptArray &ba);
|
||||
|
||||
private:
|
||||
bool _DataVisual_updateTextList(const CScriptArray &data,
|
||||
const QString &title,
|
||||
asIScriptFunction *click,
|
||||
asIScriptFunction *dblclick);
|
||||
void _UI_Toast(const QString &message, const QString &icon);
|
||||
|
||||
bool _DataVisual_updateTextTree(const QString &json, const QString &title,
|
||||
asIScriptFunction *click,
|
||||
asIScriptFunction *dblclick);
|
||||
|
||||
bool _DataVisual_updateTextTable(const QString &json,
|
||||
const CScriptArray &headers,
|
||||
const CScriptArray &headerNames,
|
||||
const QString &title,
|
||||
asIScriptFunction *click,
|
||||
asIScriptFunction *dblclick);
|
||||
QColor _Color_get(const QString &caption);
|
||||
|
||||
private:
|
||||
std::vector<std::any> _fnbuffer;
|
||||
void _MSG_AboutQt(const QString &title);
|
||||
|
||||
QMessageBox::StandardButton
|
||||
_MSG_Information(const QString &title, const QString &text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton defaultButton);
|
||||
|
||||
QMessageBox::StandardButton
|
||||
_MSG_Question(const QString &title, const QString &text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton defaultButton);
|
||||
|
||||
QMessageBox::StandardButton
|
||||
_MSG_Warning(const QString &title, const QString &text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton defaultButton);
|
||||
|
||||
QMessageBox::StandardButton
|
||||
_MSG_Critical(const QString &title, const QString &text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton defaultButton);
|
||||
|
||||
void _MSG_About(const QString &title, const QString &text);
|
||||
|
||||
QMessageBox::StandardButton
|
||||
_MSG_msgbox(QMessageBox::Icon icon, const QString &title,
|
||||
const QString &text, QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton defaultButton);
|
||||
|
||||
private:
|
||||
QString _InputBox_GetText(const QString &title, const QString &label,
|
||||
QLineEdit::EchoMode echo, const QString &text,
|
||||
bool *ok, Qt::InputMethodHints inputMethodHints);
|
||||
|
||||
QString _InputBox_GetMultiLineText(const QString &title,
|
||||
const QString &label,
|
||||
const QString &text, bool *ok,
|
||||
Qt::InputMethodHints inputMethodHints);
|
||||
|
||||
QString _InputBox_getItem(const QString &title, const QString &label,
|
||||
const CScriptArray &items, int current,
|
||||
bool editable, bool *ok,
|
||||
Qt::InputMethodHints inputMethodHints);
|
||||
|
||||
int _InputBox_GetInt(const QString &title, const QString &label, int value,
|
||||
int minValue, int maxValue, int step, bool *ok);
|
||||
|
||||
double _InputBox_GetDouble(const QString &title, const QString &label,
|
||||
double value, double minValue, double maxValue,
|
||||
int decimals, bool *ok, double step);
|
||||
|
||||
private:
|
||||
QString _FileDialog_GetExistingDirectory(const QString &caption,
|
||||
const QString &dir,
|
||||
QFileDialog::Options options);
|
||||
|
||||
QString _FileDialog_GetOpenFileName(const QString &caption,
|
||||
const QString &dir,
|
||||
const QString &filter,
|
||||
QString *selectedFilter,
|
||||
QFileDialog::Options options);
|
||||
|
||||
CScriptArray *_FileDialog_getOpenFileNames(const QString &caption,
|
||||
const QString &dir,
|
||||
const QString &filter,
|
||||
QString *selectedFilter,
|
||||
QFileDialog::Options options);
|
||||
|
||||
QString _FileDialog_GetSaveFileName(const QString &caption,
|
||||
const QString &dir,
|
||||
const QString &filter,
|
||||
QString *selectedFilter,
|
||||
QFileDialog::Options options);
|
||||
|
||||
private:
|
||||
QVector<IWingPlugin::ScriptFnInfo> _sfns;
|
||||
QHash<QString, QHash<QString, qsizetype>> _rfns;
|
||||
|
||||
QVector<IWingPlugin::UNSAFE_SCFNPTR> _usfns;
|
||||
QHash<QString, QHash<QString, qsizetype>> _urfns;
|
||||
|
||||
ScriptingConsole *_console = nullptr;
|
||||
|
||||
QHash<QString, QHash<QString, QList<QPair<QString, int>>>> _objs;
|
||||
|
||||
QVector<int> _handles;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "wingconsolehighligher.h"
|
||||
|
||||
#include <KSyntaxHighlighting/FoldingRegion>
|
||||
#include <KSyntaxHighlighting/Format>
|
||||
#include <KSyntaxHighlighting/Theme>
|
||||
|
||||
WingConsoleHighligher::WingConsoleHighligher(QTextDocument *document)
|
||||
: WingSyntaxHighlighter(document) {}
|
||||
|
@ -18,11 +20,15 @@ void WingConsoleHighligher::applyFormat(
|
|||
auto off = offsetv.toInt(&b);
|
||||
if (b) {
|
||||
if (off < 0) {
|
||||
// don't highlight
|
||||
return;
|
||||
if (off == -2) {
|
||||
WingSyntaxHighlighter::applyFormat(
|
||||
offset, length, KSyntaxHighlighting::Format());
|
||||
}
|
||||
} else {
|
||||
if (offset <= off) {
|
||||
auto div = off - offset;
|
||||
WingSyntaxHighlighter::applyFormat(
|
||||
offset, div, KSyntaxHighlighting::Format());
|
||||
auto rest = length - div;
|
||||
if (rest <= 0) {
|
||||
return;
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "utilities.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPushButton>
|
||||
#include <QResizeEvent>
|
||||
|
||||
WingMessageBox::WingMessageBox() {}
|
||||
|
@ -161,5 +162,10 @@ WingMessageBox::msgbox(QWidget *parent, QMessageBox::Icon icon,
|
|||
QObject::connect(msgbox, &QMessageBox::finished, &d,
|
||||
&FramelessDialogBase::done);
|
||||
|
||||
return static_cast<QMessageBox::StandardButton>(d.exec());
|
||||
auto ret = d.exec();
|
||||
if (ret == 0) {
|
||||
return msgbox->standardButton(msgbox->defaultButton());
|
||||
}
|
||||
|
||||
return static_cast<QMessageBox::StandardButton>(ret);
|
||||
}
|
||||
|
|
|
@ -13,5 +13,4 @@ The following components are all third-party components used by the software. Th
|
|||
* [cpptrace](https://github.com/jeremy-rifkin/cpptrace) (MIT)
|
||||
* [QConsoleWidget](https://github.com/gapost/qconsolewidget) (MIT, **FORK** -> AGPL-3.0)
|
||||
* [QColorPicker](https://github.com/arsdever/qcolorpicker) (MIT)
|
||||
* [QtJsonModel](https://github.com/dridk/QJsonmodel) (MIT)
|
||||
* [Qt](https://www.qt.io/) (LGPL)
|
||||
|
|
|
@ -104,12 +104,21 @@ 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 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);
|
||||
|
|
|
@ -35,6 +35,9 @@ public:
|
|||
|
||||
SearchReplaceWidget *searchWidget() const;
|
||||
|
||||
public:
|
||||
void setContentModified(bool b);
|
||||
|
||||
signals:
|
||||
void contentModified(bool b);
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
constexpr qsizetype FILE_MAX_BUFFER = 0x6400000; // 100MB
|
||||
constexpr qsizetype FILE_MAX_BUFFER = 0x32000000; // 800MB
|
||||
constexpr auto CLONE_LIMIT = 3;
|
||||
|
||||
constexpr auto VIEW_PROPERTY = "__VIEW__";
|
||||
|
@ -121,6 +121,9 @@ EditorView::EditorView(QWidget *parent)
|
|||
m_metadata->setDocument(doc);
|
||||
});
|
||||
|
||||
connect(&_watcher, &QFileSystemWatcher::fileChanged, this,
|
||||
&EditorView::need2Reload);
|
||||
|
||||
applySettings();
|
||||
|
||||
// build up call tables
|
||||
|
@ -224,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;
|
||||
}
|
||||
|
@ -239,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();
|
||||
|
@ -254,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) {
|
||||
|
@ -284,6 +288,8 @@ ErrFile EditorView::newFile(size_t index) {
|
|||
if (isCloneFile()) {
|
||||
return ErrFile::ClonedFile;
|
||||
}
|
||||
|
||||
removeMonitorPaths();
|
||||
auto istr = QString::number(index);
|
||||
m_fileName = tr("Untitled") + istr;
|
||||
this->setWindowTitle(m_fileName);
|
||||
|
@ -320,6 +326,8 @@ ErrFile EditorView::openFile(const QString &filename) {
|
|||
return ErrFile::Permission;
|
||||
}
|
||||
|
||||
removeMonitorPaths();
|
||||
|
||||
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
|
||||
m_hex->setLockedFile(readonly);
|
||||
m_hex->setKeepSize(true);
|
||||
|
@ -335,6 +343,8 @@ ErrFile EditorView::openFile(const QString &filename) {
|
|||
auto tab = this->tabWidget();
|
||||
tab->setIcon(Utilities::getIconFromFile(style(), m_fileName));
|
||||
tab->setToolTip(m_fileName);
|
||||
|
||||
addMonitorPath();
|
||||
}
|
||||
|
||||
return ErrFile::Success;
|
||||
|
@ -372,6 +382,8 @@ ErrFile EditorView::openExtFile(const QString &ext, const QString &file) {
|
|||
return ErrFile::Error;
|
||||
}
|
||||
|
||||
removeMonitorPaths();
|
||||
|
||||
m_hex->setDocument(QSharedPointer<QHexDocument>(p));
|
||||
m_hex->setLockedFile(readonly);
|
||||
m_hex->setKeepSize(true);
|
||||
|
@ -558,6 +570,8 @@ ErrFile EditorView::save(const QString &workSpaceName, const QString &path,
|
|||
return ErrFile::Permission;
|
||||
}
|
||||
|
||||
removeMonitorPaths();
|
||||
|
||||
if (doc->saveTo(&file, !isExport)) {
|
||||
file.close();
|
||||
|
||||
|
@ -567,6 +581,9 @@ ErrFile EditorView::save(const QString &workSpaceName, const QString &path,
|
|||
m_docType = DocumentType::File;
|
||||
doc->setDocSaved();
|
||||
}
|
||||
|
||||
addMonitorPath();
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
adjustPermission();
|
||||
#endif
|
||||
|
@ -717,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; }
|
||||
|
@ -784,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 left = qsizetype(maxDisplayBytes) - header.size() - tailer.size();
|
||||
auto begin = qMax(offset - headerlen, 0);
|
||||
auto end = qMin(offset + findSize + taillen, doc->length());
|
||||
auto boff = offset - begin;
|
||||
|
||||
// append to contextSize
|
||||
contextSize += (left / 2);
|
||||
|
||||
auto cheader = doc->read(offset - contextSize, contextSize);
|
||||
auto ctailer = doc->read(offset + findSize, contextSize);
|
||||
auto buffer = doc->read(begin, end - begin + 1);
|
||||
|
||||
FindResultModel::FindInfo info;
|
||||
info.cheader = cheader;
|
||||
info.hbuffer = header;
|
||||
info.tbuffer = tailer;
|
||||
info.ctailer = ctailer;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorView::applyFunctionTables(WingEditorViewWidget *view,
|
||||
|
@ -2033,6 +2076,19 @@ EditorView *EditorView::clone() {
|
|||
this->m_cloneChildren[this->m_cloneChildren.indexOf(ev)] = nullptr;
|
||||
});
|
||||
|
||||
connect(ev, &EditorView::sigOnCutFile, this, &EditorView::sigOnCutFile);
|
||||
connect(ev, &EditorView::sigOnCutHex, this, &EditorView::sigOnCutHex);
|
||||
connect(ev, &EditorView::sigOnCopyFile, this, &EditorView::sigOnCopyFile);
|
||||
connect(ev, &EditorView::sigOnCopyHex, this, &EditorView::sigOnCopyHex);
|
||||
connect(ev, &EditorView::sigOnPasteFile, this, &EditorView::sigOnPasteFile);
|
||||
connect(ev, &EditorView::sigOnPasteHex, this, &EditorView::sigOnPasteHex);
|
||||
connect(ev, &EditorView::sigOnDelete, this, &EditorView::sigOnDelete);
|
||||
connect(ev, &EditorView::sigOnFindFile, this, &EditorView::sigOnFindFile);
|
||||
connect(ev, &EditorView::sigOnGoToLine, this, &EditorView::sigOnGoToLine);
|
||||
connect(ev, &EditorView::sigOnFill, this, &EditorView::sigOnFill);
|
||||
connect(ev, &EditorView::sigOnMetadata, this, &EditorView::sigOnMetadata);
|
||||
connect(ev, &EditorView::sigOnBookMark, this, &EditorView::sigOnBookMark);
|
||||
|
||||
auto doc = this->m_hex->document();
|
||||
|
||||
ev->m_cloneParent = this;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#ifndef EDITORVIEW_H
|
||||
#define EDITORVIEW_H
|
||||
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QReadWriteLock>
|
||||
#include <QStackedWidget>
|
||||
|
||||
|
@ -224,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);
|
||||
|
@ -537,11 +536,12 @@ private:
|
|||
parent->addAction(a);
|
||||
}
|
||||
|
||||
private:
|
||||
void connectDocSavedFlag(EditorView *editor);
|
||||
void removeMonitorPaths();
|
||||
void addMonitorPath();
|
||||
|
||||
signals:
|
||||
void sigFileSaved(QString filename, QString oldFileName);
|
||||
|
||||
void sigOnCutFile();
|
||||
void sigOnCutHex();
|
||||
void sigOnCopyFile();
|
||||
|
@ -555,6 +555,8 @@ signals:
|
|||
void sigOnMetadata();
|
||||
void sigOnBookMark();
|
||||
|
||||
void need2Reload();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
|
@ -591,6 +593,7 @@ private:
|
|||
|
||||
QReadWriteLock _rwlock;
|
||||
CallTable _viewFns;
|
||||
QFileSystemWatcher _watcher;
|
||||
};
|
||||
|
||||
#endif // EDITORVIEW_H
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,257 +0,0 @@
|
|||
/*==============================================================================
|
||||
** Copyright (C) 2024-2027 WingSummer
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify it under
|
||||
** the terms of the GNU Affero General Public License as published by the Free
|
||||
** Software Foundation, version 3.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
** details.
|
||||
**
|
||||
** You should have received a copy of the GNU Affero General Public License
|
||||
** along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
** =============================================================================
|
||||
*/
|
||||
|
||||
#include "qtlonglongspinbox.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QLineEdit>
|
||||
#include <limits>
|
||||
|
||||
QtLongLongSpinBox::QtLongLongSpinBox(QWidget *parent)
|
||||
: QAbstractSpinBox(parent) {
|
||||
m_minimum = std::numeric_limits<qlonglong>::min();
|
||||
m_maximum = std::numeric_limits<qlonglong>::max();
|
||||
m_value = 0;
|
||||
m_singleStep = 1;
|
||||
m_base = 10;
|
||||
|
||||
setValue(m_value);
|
||||
}
|
||||
|
||||
qlonglong QtLongLongSpinBox::value() const { return m_value; }
|
||||
|
||||
void QtLongLongSpinBox::setValue(qlonglong expectedNewValue) {
|
||||
const qlonglong newValue = qBound(m_minimum, expectedNewValue, m_maximum);
|
||||
const QString newValueString = QString::number(newValue, m_base);
|
||||
lineEdit()->setText(m_prefix + newValueString + m_suffix);
|
||||
if (m_value != newValue) {
|
||||
m_value = newValue;
|
||||
emit valueChanged(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
QString QtLongLongSpinBox::prefix() const { return m_prefix; }
|
||||
|
||||
void QtLongLongSpinBox::setPrefix(const QString &prefix) {
|
||||
m_prefix = prefix;
|
||||
|
||||
setValue(m_value);
|
||||
}
|
||||
|
||||
QString QtLongLongSpinBox::suffix() const { return m_suffix; }
|
||||
|
||||
void QtLongLongSpinBox::setSuffix(const QString &suffix) {
|
||||
m_suffix = suffix;
|
||||
|
||||
setValue(m_value);
|
||||
}
|
||||
|
||||
QString QtLongLongSpinBox::cleanText() const {
|
||||
return QString::number(m_value);
|
||||
}
|
||||
|
||||
qlonglong QtLongLongSpinBox::singleStep() const { return m_singleStep; }
|
||||
|
||||
void QtLongLongSpinBox::setSingleStep(qlonglong step) { m_singleStep = step; }
|
||||
|
||||
qlonglong QtLongLongSpinBox::minimum() const { return m_minimum; }
|
||||
|
||||
void QtLongLongSpinBox::setMinimum(qlonglong min) {
|
||||
m_minimum = min;
|
||||
if (m_maximum < m_minimum) {
|
||||
m_maximum = m_minimum;
|
||||
}
|
||||
|
||||
setValue(m_value);
|
||||
}
|
||||
|
||||
qlonglong QtLongLongSpinBox::maximum() const { return m_maximum; }
|
||||
|
||||
void QtLongLongSpinBox::setMaximum(qlonglong max) {
|
||||
m_maximum = max;
|
||||
if (m_maximum < m_minimum) {
|
||||
m_maximum = m_minimum;
|
||||
}
|
||||
|
||||
setValue(m_value);
|
||||
}
|
||||
|
||||
void QtLongLongSpinBox::setRange(qlonglong min, qlonglong max) {
|
||||
if (min < max) {
|
||||
m_minimum = min;
|
||||
m_maximum = max;
|
||||
} else {
|
||||
m_minimum = max;
|
||||
m_maximum = min;
|
||||
}
|
||||
|
||||
setValue(m_value);
|
||||
}
|
||||
|
||||
void QtLongLongSpinBox::setDisplayIntegerBase(int base) {
|
||||
if (m_base != base) {
|
||||
m_base = base;
|
||||
setValue(m_value);
|
||||
}
|
||||
}
|
||||
|
||||
int QtLongLongSpinBox::displayIntegerBase() const { return m_base; }
|
||||
|
||||
void QtLongLongSpinBox::keyPressEvent(QKeyEvent *event) {
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
selectCleanText();
|
||||
lineEditEditingFinalize();
|
||||
}
|
||||
|
||||
QAbstractSpinBox::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void QtLongLongSpinBox::focusOutEvent(QFocusEvent *event) {
|
||||
lineEditEditingFinalize();
|
||||
|
||||
QAbstractSpinBox::focusOutEvent(event);
|
||||
}
|
||||
|
||||
QAbstractSpinBox::StepEnabled QtLongLongSpinBox::stepEnabled() const {
|
||||
if (isReadOnly()) {
|
||||
return StepNone;
|
||||
}
|
||||
|
||||
StepEnabled se = StepNone;
|
||||
if (wrapping() || m_value < m_maximum) {
|
||||
se |= StepUpEnabled;
|
||||
}
|
||||
if (wrapping() || m_value > m_minimum) {
|
||||
se |= StepDownEnabled;
|
||||
}
|
||||
|
||||
return se;
|
||||
}
|
||||
|
||||
void QtLongLongSpinBox::stepBy(int steps) {
|
||||
if (isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_prefix + QString::number(m_value) + m_suffix != lineEdit()->text()) {
|
||||
lineEditEditingFinalize();
|
||||
}
|
||||
|
||||
qlonglong newValue = m_value + (steps * m_singleStep);
|
||||
if (wrapping()) {
|
||||
// emulating the behavior of QSpinBox
|
||||
if (newValue > m_maximum) {
|
||||
if (m_value == m_maximum) {
|
||||
newValue = m_minimum;
|
||||
} else {
|
||||
newValue = m_maximum;
|
||||
}
|
||||
} else if (newValue < m_minimum) {
|
||||
if (m_value == m_minimum) {
|
||||
newValue = m_maximum;
|
||||
} else {
|
||||
newValue = m_minimum;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newValue = qBound(m_minimum, newValue, m_maximum);
|
||||
}
|
||||
|
||||
setValue(newValue);
|
||||
selectCleanText();
|
||||
}
|
||||
|
||||
QValidator::State QtLongLongSpinBox::validate(QString &input, int &pos) const {
|
||||
// first, we try to interpret as a number without prefixes
|
||||
bool ok;
|
||||
const qlonglong value = input.toLongLong(&ok);
|
||||
if (input.isEmpty() || (ok && value <= m_maximum)) {
|
||||
input = m_prefix + input + m_suffix;
|
||||
pos += m_prefix.length();
|
||||
return QValidator::Acceptable;
|
||||
}
|
||||
|
||||
// if string of text editor aren't simple number, try to interpret it
|
||||
// as a number with prefix and suffix
|
||||
bool valid = true;
|
||||
if (!m_prefix.isEmpty() && !input.startsWith(m_prefix)) {
|
||||
valid = false;
|
||||
}
|
||||
if (!m_suffix.isEmpty() && !input.endsWith(m_suffix)) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
const int start = m_prefix.length();
|
||||
const int length = input.length() - start - m_suffix.length();
|
||||
|
||||
bool ok;
|
||||
const QString number = input.mid(start, length);
|
||||
const qlonglong value = number.toLongLong(&ok);
|
||||
if (number.isEmpty() || (ok && value <= m_maximum)) {
|
||||
return QValidator::Acceptable;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise not acceptable
|
||||
return QValidator::Invalid;
|
||||
}
|
||||
|
||||
void QtLongLongSpinBox::lineEditEditingFinalize() {
|
||||
const QString text = lineEdit()->text();
|
||||
|
||||
// first, we try to read as a number without prefixes
|
||||
bool ok;
|
||||
qlonglong value = text.toLongLong(&ok);
|
||||
if (ok) {
|
||||
setValue(value);
|
||||
return;
|
||||
}
|
||||
|
||||
// if string of text editor aren't simple number, try to interpret it
|
||||
// as a number with prefix and suffix
|
||||
bool valid = true;
|
||||
if (!m_prefix.isEmpty() && !text.startsWith(m_prefix)) {
|
||||
valid = false;
|
||||
} else if (!m_suffix.isEmpty() && !text.endsWith(m_suffix)) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
const int start = m_prefix.length();
|
||||
const int length = text.length() - start - m_suffix.length();
|
||||
|
||||
bool ok;
|
||||
const qlonglong value = text.mid(start, length).toLongLong(&ok);
|
||||
if (ok) {
|
||||
setValue(value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise set old value
|
||||
setValue(m_value);
|
||||
}
|
||||
|
||||
void QtLongLongSpinBox::selectCleanText() {
|
||||
lineEdit()->setSelection(m_prefix.length(), lineEdit()->text().length() -
|
||||
m_prefix.length() -
|
||||
m_suffix.length());
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*==============================================================================
|
||||
** Copyright (C) 2024-2027 WingSummer
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify it under
|
||||
** the terms of the GNU Affero General Public License as published by the Free
|
||||
** Software Foundation, version 3.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
** FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
** details.
|
||||
**
|
||||
** You should have received a copy of the GNU Affero General Public License
|
||||
** along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
** =============================================================================
|
||||
*/
|
||||
|
||||
#ifndef QTLONGLONGSPINBOX_H
|
||||
#define QTLONGLONGSPINBOX_H
|
||||
|
||||
#include <QAbstractSpinBox>
|
||||
#include <QtGlobal>
|
||||
|
||||
class QtLongLongSpinBox : public QAbstractSpinBox {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QtLongLongSpinBox(QWidget *parent = 0);
|
||||
|
||||
qlonglong value() const;
|
||||
|
||||
QString prefix() const;
|
||||
void setPrefix(const QString &prefix);
|
||||
|
||||
QString suffix() const;
|
||||
void setSuffix(const QString &suffix);
|
||||
|
||||
QString cleanText() const;
|
||||
|
||||
qlonglong singleStep() const;
|
||||
void setSingleStep(qlonglong val);
|
||||
|
||||
qlonglong minimum() const;
|
||||
void setMinimum(qlonglong min);
|
||||
|
||||
qlonglong maximum() const;
|
||||
void setMaximum(qlonglong max);
|
||||
|
||||
void setRange(qlonglong min, qlonglong max);
|
||||
|
||||
void setDisplayIntegerBase(int base);
|
||||
|
||||
int displayIntegerBase() const;
|
||||
|
||||
public slots:
|
||||
void setValue(qlonglong value);
|
||||
|
||||
signals:
|
||||
void valueChanged(qlonglong i);
|
||||
|
||||
protected:
|
||||
virtual void keyPressEvent(QKeyEvent *event);
|
||||
virtual void focusOutEvent(QFocusEvent *event);
|
||||
virtual void stepBy(int steps);
|
||||
virtual StepEnabled stepEnabled() const;
|
||||
virtual QValidator::State validate(QString &input, int &pos) const;
|
||||
|
||||
private:
|
||||
void lineEditEditingFinalize();
|
||||
void selectCleanText();
|
||||
|
||||
private:
|
||||
QString m_prefix;
|
||||
QString m_suffix;
|
||||
qlonglong m_singleStep;
|
||||
qlonglong m_minimum;
|
||||
qlonglong m_maximum;
|
||||
qlonglong m_value;
|
||||
|
||||
int m_base;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QtLongLongSpinBox)
|
||||
};
|
||||
|
||||
#endif // QTLONGLONGSPINBOX_H
|
|
@ -35,7 +35,7 @@
|
|||
#include "class/ascompletion.h"
|
||||
#include "class/clangformatmanager.h"
|
||||
|
||||
ScriptEditor::ScriptEditor(asIScriptEngine *engine, QWidget *parent)
|
||||
ScriptEditor::ScriptEditor(QWidget *parent)
|
||||
: ads::CDockWidget(nullptr, QString(), parent) {
|
||||
this->setFeatures(
|
||||
CDockWidget::DockWidgetFocusable | CDockWidget::DockWidgetMovable |
|
||||
|
@ -48,7 +48,7 @@ ScriptEditor::ScriptEditor(asIScriptEngine *engine, QWidget *parent)
|
|||
m_editor->setSyntax(
|
||||
m_editor->syntaxRepo().definitionForName("AngelScript"));
|
||||
|
||||
auto cm = new AsCompletion(engine, m_editor);
|
||||
auto cm = new AsCompletion(m_editor);
|
||||
connect(cm, &AsCompletion::onFunctionTip, this,
|
||||
&ScriptEditor::onFunctionTip);
|
||||
|
||||
|
@ -57,6 +57,9 @@ ScriptEditor::ScriptEditor(asIScriptEngine *engine, 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
|
||||
|
|
|
@ -21,13 +21,15 @@
|
|||
#include "Qt-Advanced-Docking-System/src/DockWidget.h"
|
||||
#include "control/codeedit.h"
|
||||
|
||||
#include <QFileSystemWatcher>
|
||||
|
||||
class asIScriptEngine;
|
||||
|
||||
class ScriptEditor : public ads::CDockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ScriptEditor(asIScriptEngine *engine, QWidget *parent = nullptr);
|
||||
explicit ScriptEditor(QWidget *parent = nullptr);
|
||||
virtual ~ScriptEditor();
|
||||
|
||||
QString fileName() const;
|
||||
|
@ -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
|
||||
|
|
|
@ -17,17 +17,20 @@
|
|||
|
||||
#include "scriptingconsole.h"
|
||||
#include "QConsoleWidget/QConsoleIODevice.h"
|
||||
#include "class/logger.h"
|
||||
#include "class/scriptconsolemachine.h"
|
||||
#include "class/scriptmachine.h"
|
||||
#include "class/scriptsettings.h"
|
||||
#include "class/skinmanager.h"
|
||||
#include "class/wingmessagebox.h"
|
||||
#include "model/codecompletionmodel.h"
|
||||
#include "utilities.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QColor>
|
||||
#include <QIcon>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QMimeData>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextBlock>
|
||||
|
||||
|
@ -68,7 +71,7 @@ void ScriptingConsole::handleReturnKey() {
|
|||
|
||||
// start new block
|
||||
appendPlainText(QString());
|
||||
dontHighlightLastLine();
|
||||
dontHighlightLastLine(true);
|
||||
setMode(Output);
|
||||
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
|
@ -84,33 +87,68 @@ void ScriptingConsole::handleReturnKey() {
|
|||
if (iodevice_->isOpen())
|
||||
iodevice_->consoleWidgetInput(code);
|
||||
|
||||
if (!_isWaitingRead) {
|
||||
emit consoleCommand(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingConsole::init() {
|
||||
_getInputFn = std::bind(&ScriptingConsole::getInput, this);
|
||||
|
||||
_sp = new ScriptConsoleMachine(_getInputFn, this);
|
||||
connect(_sp, &ScriptConsoleMachine::onClearConsole, this,
|
||||
&ScriptingConsole::clear);
|
||||
connect(this, &ScriptingConsole::abortEvaluation, _sp,
|
||||
&ScriptConsoleMachine::abortScript);
|
||||
connect(this, &QConsoleWidget::consoleCommand, this,
|
||||
&ScriptingConsole::runConsoleCommand);
|
||||
|
||||
connect(
|
||||
_sp, &ScriptConsoleMachine::onOutput, this,
|
||||
[=](ScriptConsoleMachine::MessageType type,
|
||||
const ScriptConsoleMachine::MessageInfo &message) {
|
||||
auto cm = new AsConsoleCompletion(this);
|
||||
connect(cm, &AsCompletion::onFunctionTip, this,
|
||||
&ScriptingConsole::onFunctionTip);
|
||||
}
|
||||
|
||||
void ScriptingConsole::clearConsole() {
|
||||
setMode(Output);
|
||||
|
||||
auto cur = this->textCursor();
|
||||
auto off = cur.position() - this->currentHeaderPos();
|
||||
auto lastCmd = this->currentCommandLine();
|
||||
auto dis = lastCmd.length() - off;
|
||||
|
||||
clear();
|
||||
|
||||
if (lastCommandPrompt()) {
|
||||
auto lines = _codes.split('\n');
|
||||
auto pl = lines.begin();
|
||||
appendCommandPrompt(false);
|
||||
writeStdOut(*pl);
|
||||
pl++;
|
||||
for (; pl != lines.end(); pl++) {
|
||||
appendCommandPrompt(true);
|
||||
writeStdOut(*pl);
|
||||
}
|
||||
appendCommandPrompt(true);
|
||||
} else {
|
||||
appendCommandPrompt(false);
|
||||
}
|
||||
|
||||
setMode(Input);
|
||||
replaceCommandLine(lastCmd);
|
||||
cur = this->textCursor();
|
||||
cur.movePosition(QTextCursor::EndOfBlock);
|
||||
cur.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, dis);
|
||||
setTextCursor(cur);
|
||||
}
|
||||
|
||||
void ScriptingConsole::processKeyEvent(QKeyEvent *e) { keyPressEvent(e); }
|
||||
|
||||
void ScriptingConsole::onOutput(const ScriptMachine::MessageInfo &message) {
|
||||
// <type, <row, col>>
|
||||
static QPair<ScriptConsoleMachine::MessageType, QPair<int, int>>
|
||||
lastInfo{ScriptConsoleMachine::MessageType::Print, {-1, -1}};
|
||||
static QPair<ScriptMachine::MessageType, QPair<int, int>> lastInfo{
|
||||
ScriptMachine::MessageType::Print, {-1, -1}};
|
||||
|
||||
auto doc = this->document();
|
||||
auto lastLine = doc->lastBlock();
|
||||
auto isNotBlockStart = !lastLine.text().isEmpty();
|
||||
|
||||
auto fmtMsg = [](const ScriptConsoleMachine::MessageInfo &message)
|
||||
-> QString {
|
||||
auto fmtMsg = [](const ScriptMachine::MessageInfo &message) -> QString {
|
||||
if (message.row <= 0 || message.col <= 0) {
|
||||
return message.message;
|
||||
} else {
|
||||
|
@ -120,20 +158,18 @@ void ScriptingConsole::init() {
|
|||
}
|
||||
};
|
||||
|
||||
auto isMatchLast =
|
||||
[](ScriptConsoleMachine::MessageType type,
|
||||
const ScriptConsoleMachine::MessageInfo &message) -> bool {
|
||||
auto isMatchLast = [](const ScriptMachine::MessageInfo &message) -> bool {
|
||||
if (message.row < 0 || message.col < 0) {
|
||||
return false;
|
||||
}
|
||||
return lastInfo.first == type &&
|
||||
return lastInfo.first == message.type &&
|
||||
lastInfo.second.first == message.row &&
|
||||
lastInfo.second.second == message.col;
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
switch (message.type) {
|
||||
case ScriptMachine::MessageType::Info:
|
||||
if (isMatchLast(type, message)) {
|
||||
if (isMatchLast(message)) {
|
||||
stdOut(message.message);
|
||||
} else {
|
||||
if (isNotBlockStart) {
|
||||
|
@ -144,7 +180,7 @@ void ScriptingConsole::init() {
|
|||
flush();
|
||||
break;
|
||||
case ScriptMachine::MessageType::Warn:
|
||||
if (isMatchLast(type, message)) {
|
||||
if (isMatchLast(message)) {
|
||||
stdWarn(message.message);
|
||||
} else {
|
||||
if (isNotBlockStart) {
|
||||
|
@ -155,7 +191,7 @@ void ScriptingConsole::init() {
|
|||
flush();
|
||||
break;
|
||||
case ScriptMachine::MessageType::Error:
|
||||
if (isMatchLast(type, message)) {
|
||||
if (isMatchLast(message)) {
|
||||
stdErr(message.message);
|
||||
} else {
|
||||
if (isNotBlockStart) {
|
||||
|
@ -166,47 +202,30 @@ void ScriptingConsole::init() {
|
|||
flush();
|
||||
break;
|
||||
case ScriptMachine::MessageType::Print:
|
||||
if (lastInfo.first != type) {
|
||||
if (lastInfo.first != message.type && isNotBlockStart) {
|
||||
newLine();
|
||||
}
|
||||
// If running ouput in the console,
|
||||
// otherwise logging.
|
||||
if (_sp->isRunning()) {
|
||||
stdOut(message.message);
|
||||
} else {
|
||||
Logger::logPrint(Logger::packDebugStr(
|
||||
packUpLoggingStr(message.message)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
lastInfo.first = type;
|
||||
lastInfo.first = message.type;
|
||||
lastInfo.second = qMakePair(message.row, message.col);
|
||||
});
|
||||
|
||||
connect(this, &QConsoleWidget::consoleCommand, this,
|
||||
&ScriptingConsole::runConsoleCommand);
|
||||
|
||||
auto cm = new AsConsoleCompletion(_sp->engine(), this);
|
||||
connect(cm, &AsCompletion::onFunctionTip, this,
|
||||
&ScriptingConsole::onFunctionTip);
|
||||
}
|
||||
|
||||
void ScriptingConsole::clearConsole() {
|
||||
void ScriptingConsole::abortCurrentCode() {
|
||||
setMode(Output);
|
||||
clear();
|
||||
appendCommandPrompt(lastCommandPrompt());
|
||||
_codes.clear();
|
||||
appendCommandPrompt();
|
||||
setMode(Input);
|
||||
}
|
||||
|
||||
void ScriptingConsole::processKeyEvent(QKeyEvent *e) { keyPressEvent(e); }
|
||||
|
||||
void ScriptingConsole::applyScriptSettings() {
|
||||
auto &set = ScriptSettings::instance();
|
||||
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:
|
||||
|
@ -232,8 +251,94 @@ void ScriptingConsole::applyScriptSettings() {
|
|||
|
||||
void ScriptingConsole::runConsoleCommand(const QString &code) {
|
||||
auto exec = code.trimmed();
|
||||
if (exec.endsWith('\\')) {
|
||||
if (exec == QStringLiteral("#ls")) {
|
||||
auto &ins = ScriptMachine::instance();
|
||||
auto mod = ins.module(ScriptMachine::Interactive);
|
||||
if (mod) {
|
||||
auto total = mod->GetGlobalVarCount();
|
||||
|
||||
setMode(Output);
|
||||
|
||||
if (total == 0) {
|
||||
stdOut("<none>");
|
||||
} else {
|
||||
auto &sm = ScriptMachine::instance();
|
||||
for (asUINT i = 0; i < total; ++i) {
|
||||
const char *name;
|
||||
int typeID;
|
||||
auto decl = mod->GetGlobalVarDeclaration(i);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_codes.clear();
|
||||
appendCommandPrompt();
|
||||
setMode(Input);
|
||||
}
|
||||
} else if (exec.startsWith(QStringLiteral("#del"))) {
|
||||
// this is special command
|
||||
auto &ins = ScriptMachine::instance();
|
||||
auto mod = ins.module(ScriptMachine::Interactive);
|
||||
if (mod) {
|
||||
// first check whether contains \n
|
||||
auto idx = exec.indexOf('\n');
|
||||
if (idx >= 0) {
|
||||
setMode(Output);
|
||||
stdErr(tr("InvalidDelCmd"));
|
||||
} else {
|
||||
// ok, then tokens should be devided by the space
|
||||
exec.remove(0, 4);
|
||||
auto vars = exec.split(' ', Qt::SkipEmptyParts);
|
||||
|
||||
QList<asUINT> indices;
|
||||
|
||||
// then check
|
||||
setMode(Output);
|
||||
for (auto &v : vars) {
|
||||
auto idx = mod->GetGlobalVarIndexByName(v.toUtf8());
|
||||
if (idx >= 0) {
|
||||
indices.append(idx);
|
||||
} else {
|
||||
stdWarn(tr("NotFoundIgnore:") + v);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(indices.begin(), indices.end(), std::greater<int>());
|
||||
|
||||
// ok, remove
|
||||
for (auto &i : indices) {
|
||||
mod->RemoveGlobalVar(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
_codes.clear();
|
||||
appendCommandPrompt();
|
||||
setMode(Input);
|
||||
} else if (exec == QStringLiteral("#cls")) {
|
||||
auto &ins = ScriptMachine::instance();
|
||||
auto mod = ins.module(ScriptMachine::Interactive);
|
||||
if (mod) {
|
||||
auto total = mod->GetGlobalVarCount();
|
||||
if (total) {
|
||||
asUINT i = total;
|
||||
do {
|
||||
--i;
|
||||
mod->RemoveGlobalVar(i);
|
||||
} while (i);
|
||||
}
|
||||
}
|
||||
_codes.clear();
|
||||
appendCommandPrompt();
|
||||
setMode(Input);
|
||||
} else if (exec.endsWith('\\')) {
|
||||
static QRegularExpression ex(QStringLiteral("[\\\\\\s]+$"));
|
||||
_codes.append('\n');
|
||||
_codes += exec.remove(ex);
|
||||
setMode(Output);
|
||||
appendCommandPrompt(true);
|
||||
|
@ -241,8 +346,8 @@ void ScriptingConsole::runConsoleCommand(const QString &code) {
|
|||
} else {
|
||||
setMode(Output);
|
||||
_codes += exec;
|
||||
if (!_sp->executeCode(_codes)) {
|
||||
}
|
||||
ScriptMachine::instance().executeCode(ScriptMachine::Interactive,
|
||||
_codes);
|
||||
_codes.clear();
|
||||
appendCommandPrompt();
|
||||
setMode(Input);
|
||||
|
@ -250,11 +355,18 @@ void ScriptingConsole::runConsoleCommand(const QString &code) {
|
|||
}
|
||||
|
||||
QString ScriptingConsole::getInput() {
|
||||
auto &s = consoleStream();
|
||||
appendCommandPrompt(true);
|
||||
setMode(Input);
|
||||
consoleStream().device()->waitForReadyRead(-1);
|
||||
s.status();
|
||||
auto d = s.device();
|
||||
auto ba = d->bytesAvailable();
|
||||
d->skip(ba);
|
||||
_isWaitingRead = true;
|
||||
d->waitForReadyRead(-1);
|
||||
QString instr;
|
||||
consoleStream() >> instr;
|
||||
s >> instr;
|
||||
_isWaitingRead = false;
|
||||
setMode(Output);
|
||||
return instr;
|
||||
}
|
||||
|
@ -277,35 +389,115 @@ 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 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("("));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingConsole::paste() {
|
||||
if (ScriptMachine::instance().isRunning(ScriptMachine::Interactive)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QMimeData *const clipboard = QApplication::clipboard()->mimeData();
|
||||
const QString text = clipboard->text();
|
||||
if (!text.isEmpty()) {
|
||||
if (text.indexOf('\n') < 0) {
|
||||
if (isCursorInEditZone()) {
|
||||
auto cursor = this->textCursor();
|
||||
cursor.insertText(text);
|
||||
} else {
|
||||
replaceCommandLine(text);
|
||||
}
|
||||
} else {
|
||||
auto ret = WingMessageBox::question(
|
||||
nullptr, tr("MultiCodeCanNotUndo"), text);
|
||||
if (ret == QMessageBox::No) {
|
||||
return;
|
||||
}
|
||||
auto lines = text.split('\n');
|
||||
if (lines.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setMode(Output);
|
||||
auto pl = lines.begin();
|
||||
auto pend = std::prev(lines.end());
|
||||
writeStdOut(*pl);
|
||||
pl++;
|
||||
for (; pl != pend; pl++) {
|
||||
appendCommandPrompt(true);
|
||||
writeStdOut(*pl);
|
||||
}
|
||||
appendCommandPrompt(true);
|
||||
setMode(Input);
|
||||
replaceCommandLine(*pl);
|
||||
lines.removeLast();
|
||||
_codes = lines.join('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptingConsole::isTerminal() const { return _isTerminal; }
|
||||
|
||||
void ScriptingConsole::setIsTerminal(bool newIsTerminal) {
|
||||
_isTerminal = newIsTerminal;
|
||||
}
|
||||
|
||||
QString ScriptingConsole::currentCodes() const {
|
||||
return _codes + currentCommandLine();
|
||||
QTextCursor textCursor = this->textCursor();
|
||||
textCursor.setPosition(inpos_, QTextCursor::KeepAnchor);
|
||||
return _codes + textCursor.selectedText();
|
||||
}
|
||||
|
||||
ScriptMachine *ScriptingConsole::machine() const { return _sp; }
|
||||
|
||||
ScriptConsoleMachine *ScriptingConsole::consoleMachine() const { return _sp; }
|
||||
|
||||
void ScriptingConsole::contextMenuEvent(QContextMenuEvent *event) {
|
||||
QMenu menu(this);
|
||||
|
||||
menu.addAction(QIcon(QStringLiteral(":/qeditor/copy.png")), tr("Copy"),
|
||||
QKeySequence(QKeySequence::Copy), this,
|
||||
auto a = menu.addAction(QIcon(QStringLiteral(":/qeditor/copy.png")),
|
||||
tr("Copy"), QKeySequence(QKeySequence::Copy), this,
|
||||
&ScriptingConsole::copy);
|
||||
menu.addAction(QIcon(QStringLiteral(":/qeditor/cut.png")), tr("Cut"),
|
||||
a->setShortcutContext(Qt::WidgetShortcut);
|
||||
a = 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,
|
||||
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());
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue