From 1bd978e2b29a57e1b44953f7037cc499baa6da98 Mon Sep 17 00:00:00 2001 From: wingsummer <1326224942@qq.com> Date: Mon, 6 Jan 2025 09:36:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=EF=BC=9B=E7=A7=BB=E9=99=A4=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E5=85=83=E6=95=B0=E6=8D=AE=E6=94=AF=E6=8C=81?= =?UTF-8?q?=EF=BC=9B=E5=AE=8C=E5=96=84=E6=8F=92=E4=BB=B6=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdparty/QHexView/CMakeLists.txt | 1 + .../document/commands/meta/metaaddcommand.cpp | 12 +- .../document/commands/meta/metaaddcommand.h | 3 + .../commands/meta/metaremoveposcommand.cpp | 10 +- .../commands/meta/metaremoveposcommand.h | 2 +- 3rdparty/QHexView/document/qhexcursor.cpp | 87 +-- 3rdparty/QHexView/document/qhexcursor.h | 126 +---- 3rdparty/QHexView/document/qhexmetadata.cpp | 111 +++- 3rdparty/QHexView/document/qhexmetadata.h | 62 +- 3rdparty/QHexView/document/qhexregionobject.h | 468 ++++++++++++++++ 3rdparty/QHexView/document/qhexrenderer.cpp | 95 +++- 3rdparty/QHexView/qhexview.cpp | 12 +- lang/zh_CN/winghex.ts | 528 ++++++++++-------- src/class/qasparser.cpp | 19 +- src/control/editorview.cpp | 21 +- src/dialog/mainwindow.cpp | 377 +++++++++++-- src/dialog/mainwindow.h | 7 + src/model/findresultmodel.cpp | 54 +- src/model/findresultmodel.h | 2 + src/plugin/iwingplugin.h | 16 +- src/plugin/pluginsystem.cpp | 9 + src/plugin/pluginsystem.h | 1 + src/scriptaddon/scriptjson.cpp | 4 +- 23 files changed, 1508 insertions(+), 519 deletions(-) create mode 100644 3rdparty/QHexView/document/qhexregionobject.h diff --git a/3rdparty/QHexView/CMakeLists.txt b/3rdparty/QHexView/CMakeLists.txt index 2d3a7f1..c9027bc 100644 --- a/3rdparty/QHexView/CMakeLists.txt +++ b/3rdparty/QHexView/CMakeLists.txt @@ -67,6 +67,7 @@ add_library( document/qhexmetadata.h document/qhexrenderer.cpp document/qhexrenderer.h + document/qhexregionobject.h QHexEdit2/chunks.cpp QHexEdit2/chunks.h qhexview.h diff --git a/3rdparty/QHexView/document/commands/meta/metaaddcommand.cpp b/3rdparty/QHexView/document/commands/meta/metaaddcommand.cpp index 1ff801c..d93ee9c 100644 --- a/3rdparty/QHexView/document/commands/meta/metaaddcommand.cpp +++ b/3rdparty/QHexView/document/commands/meta/metaaddcommand.cpp @@ -3,11 +3,19 @@ MetaAddCommand::MetaAddCommand(QHexMetadata *hexmeta, const QHexMetadataItem &meta, QUndoCommand *parent) - : MetaCommand(hexmeta, meta, parent) {} + : MetaCommand(hexmeta, meta, parent) { + _brokenMetas = m_hexmeta->mayBrokenMetaData(meta.begin, meta.end); +} void MetaAddCommand::redo() { m_hexmeta->metadata(m_meta.begin, m_meta.end, m_meta.foreground, m_meta.background, m_meta.comment); } -void MetaAddCommand::undo() { m_hexmeta->removeMetadata(m_meta); } +void MetaAddCommand::undo() { + m_hexmeta->removeMetadata(m_meta.begin, m_meta.end); + for (auto &meta : _brokenMetas) { + m_hexmeta->metadata(meta.begin, meta.end, meta.foreground, + meta.background, meta.comment); + } +} diff --git a/3rdparty/QHexView/document/commands/meta/metaaddcommand.h b/3rdparty/QHexView/document/commands/meta/metaaddcommand.h index ff652d7..edd12b7 100644 --- a/3rdparty/QHexView/document/commands/meta/metaaddcommand.h +++ b/3rdparty/QHexView/document/commands/meta/metaaddcommand.h @@ -10,6 +10,9 @@ public: QUndoCommand *parent = nullptr); void undo() override; void redo() override; + +private: + QVector _brokenMetas; }; #endif // METAADDCOMMAND_H diff --git a/3rdparty/QHexView/document/commands/meta/metaremoveposcommand.cpp b/3rdparty/QHexView/document/commands/meta/metaremoveposcommand.cpp index 320bfbb..9a13d65 100644 --- a/3rdparty/QHexView/document/commands/meta/metaremoveposcommand.cpp +++ b/3rdparty/QHexView/document/commands/meta/metaremoveposcommand.cpp @@ -3,13 +3,15 @@ MetaRemovePosCommand::MetaRemovePosCommand(QHexMetadata *hexmeta, qsizetype pos, QUndoCommand *parent) : QUndoCommand(parent), m_hexmeta(hexmeta), m_pos(pos) { - olditems = m_hexmeta->gets(pos); + auto po = m_hexmeta->get(pos); + if (po.has_value()) { + oldmeta = po.value(); + } } void MetaRemovePosCommand::redo() { m_hexmeta->removeMetadata(m_pos); } void MetaRemovePosCommand::undo() { - for (auto &item : olditems) - m_hexmeta->metadata(item.begin, item.end, item.foreground, - item.background, item.comment); + m_hexmeta->metadata(oldmeta.begin, oldmeta.end, oldmeta.foreground, + oldmeta.background, oldmeta.comment); } diff --git a/3rdparty/QHexView/document/commands/meta/metaremoveposcommand.h b/3rdparty/QHexView/document/commands/meta/metaremoveposcommand.h index 64b15c3..7fc0f47 100644 --- a/3rdparty/QHexView/document/commands/meta/metaremoveposcommand.h +++ b/3rdparty/QHexView/document/commands/meta/metaremoveposcommand.h @@ -18,7 +18,7 @@ public: protected: QHexMetadata *m_hexmeta; qsizetype m_pos; - QVector olditems; + QHexMetadataItem oldmeta; }; #endif // METAREMOVEPOSCOMMAND_H diff --git a/3rdparty/QHexView/document/qhexcursor.cpp b/3rdparty/QHexView/document/qhexcursor.cpp index 5f2eb9f..5483b92 100644 --- a/3rdparty/QHexView/document/qhexcursor.cpp +++ b/3rdparty/QHexView/document/qhexcursor.cpp @@ -1,8 +1,6 @@ #include "qhexcursor.h" #include -#include - QHexCursor::QHexCursor(QObject *parent) : QObject(parent), m_insertionmode(QHexCursor::OverwriteMode) { m_position.line = m_position.column = 0; @@ -17,7 +15,7 @@ QHexCursor::QHexCursor(QObject *parent) } const QHexPosition &QHexCursor::selectionStart(qsizetype index) const { - return m_sels.at(index).start; + return m_sels.at(index).begin; } const QHexPosition &QHexCursor::selectionEnd(qsizetype index) const { @@ -103,7 +101,7 @@ void QHexCursor::moveTo(const QHexPosition &pos, bool clearSelection) { } void QHexCursor::select(const QHexPosition &pos, QHexCursor::SelectionModes mode) { - this->select(pos.line, pos.column, pos.nibbleindex, mode); + this->select(pos.line, pos.column, mode); } void QHexCursor::moveTo(qsizetype line, int column, int nibbleindex, @@ -121,38 +119,38 @@ void QHexCursor::moveTo(qsizetype line, int column, int nibbleindex, emit positionChanged(); } -void QHexCursor::select(qsizetype line, int column, int nibbleindex, - SelectionModes modes) { +void QHexCursor::select(qsizetype line, int column, SelectionModes modes) { if (modes.testFlag(SelectionPreview)) { m_selection.line = line; m_selection.column = qMax(0, column); // fix the bug by wingsummer m_selection.lineWidth = m_position.lineWidth; - m_selection.nibbleindex = nibbleindex; + m_selection.nibbleindex = 0; modes.setFlag(SelectionPreview, false); m_preMode = SelectionMode(int(modes)); } else { QHexSelection sel; - sel.start = m_position; + sel.begin = m_position; + sel.begin.nibbleindex = 1; sel.end.line = line; sel.end.column = column; sel.end.lineWidth = m_position.lineWidth; - sel.end.nibbleindex = nibbleindex; + sel.end.nibbleindex = 0; sel.normalize(); switch (modes) { case SelectionAdd: - mergeAdd(sel); + m_sels.mergeAdd(sel); break; case SelectionNormal: m_sels.clear(); m_sels.append(sel); break; case SelectionRemove: - mergeRemove(sel); + m_sels.mergeRemove(sel); break; } } @@ -173,14 +171,9 @@ void QHexCursor::setPos(qsizetype offset, int nibbleindex, } void QHexCursor::select(qsizetype length, QHexCursor::SelectionModes mode) { - this->select(m_position.line, - std::min(m_lineWidth - 1, int(m_position.column + length - 1)), - 1, mode); -} - -void QHexCursor::selectOffset(qsizetype offset, qsizetype length) { - this->moveTo(offset); - this->select(length); + auto div = std::div(qsizetype(m_position.column + length - 1), + qsizetype(m_lineWidth)); + this->select(m_position.line + div.quot, div.rem, mode); } void QHexCursor::setInsertionMode(QHexCursor::InsertionMode mode) { @@ -197,7 +190,7 @@ void QHexCursor::setLineWidth(quint8 width) { m_selection.lineWidth = width; for (auto &sel : m_sels) { - sel.start.lineWidth = width; + sel.begin.lineWidth = width; sel.end.lineWidth = width; } } @@ -215,52 +208,10 @@ bool QHexCursor::hasPreviewSelection() const { return m_selection != m_position; } -void QHexCursor::mergeRemove(const QHexSelection &sel) { - Q_ASSERT(sel.isNormalized()); - - QList buffer; - QMutex locker; - QtConcurrent::blockingMap(m_sels, - [&buffer, &locker, &sel](QHexSelection &s) { - auto r = s.removeSelection(sel); - if (r.has_value()) { - QMutexLocker l(&locker); - buffer.append(r.value()); - } - }); - - // clean up invalid selections - auto cleanup = [](const QHexSelection &s) { return s.start == s.end; }; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - m_sels.removeIf(cleanup); -#else - m_sels.erase(std::remove_if(m_sels.begin(), m_sels.end(), cleanup)); -#endif - - QtConcurrent::blockingMap( - buffer, [&locker, this](QHexSelection &s) { mergeAdd(s, &locker); }); -} - -void QHexCursor::mergeAdd(const QHexSelection &sel, QMutex *locker) { - bool merged = false; - Q_ASSERT(sel.isNormalized()); - - for (auto p = m_sels.begin(); p != m_sels.end(); ++p) { - merged = p->mergeSelection(sel, locker); - if (merged) { - break; - } - } - - if (!merged) { - m_sels.append(sel); - } -} - bool QHexCursor::isLineSelected(const QHexSelection &sel, qsizetype line) const { - auto first = std::min(sel.start.line, sel.end.line); - auto last = std::max(sel.start.line, sel.end.line); + auto first = std::min(sel.begin.line, sel.end.line); + auto last = std::max(sel.begin.line, sel.end.line); if ((line >= first) && (line <= last)) return true; @@ -270,7 +221,7 @@ bool QHexCursor::isLineSelected(const QHexSelection &sel, QHexSelection QHexCursor::previewSelection() const { QHexSelection sel; - sel.start = m_position; + sel.begin = m_position; sel.end = m_selection; return sel; } @@ -284,7 +235,7 @@ QHexCursor::SelectionMode QHexCursor::previewSelectionMode() const { } void QHexCursor::mergePreviewSelection() { - auto ss = QHexSelection{m_position, m_selection}.normalized(); + auto ss = QHexSelection(m_position, m_selection).normalized(); switch (m_preMode) { case SelectionNormal: if (m_sels.isEmpty()) { @@ -292,10 +243,10 @@ void QHexCursor::mergePreviewSelection() { } break; case SelectionAdd: - mergeAdd(ss); + m_sels.mergeAdd(ss); break; case SelectionRemove: - mergeRemove(ss); + m_sels.mergeRemove(ss); break; case SelectionSingle: m_sels.clear(); diff --git a/3rdparty/QHexView/document/qhexcursor.h b/3rdparty/QHexView/document/qhexcursor.h index c9fe6f7..55c7482 100644 --- a/3rdparty/QHexView/document/qhexcursor.h +++ b/3rdparty/QHexView/document/qhexcursor.h @@ -4,7 +4,7 @@ #include #include -#include +#include "qhexregionobject.h" #define DEFAULT_HEX_LINE_LENGTH 0x10 #define DEFAULT_AREA_IDENTATION 0x01 @@ -36,6 +36,7 @@ struct QHexPosition { this->column = lineWidth - 1; } } + inline bool operator==(const QHexPosition &rhs) const { return (line == rhs.line) && (column == rhs.column) && (nibbleindex == rhs.nibbleindex); @@ -59,120 +60,21 @@ struct QHexPosition { }; Q_DECLARE_METATYPE(QHexPosition) -struct QHexSelection { - QHexPosition start; - QHexPosition end; +struct QHexSelection : QHexRegionObject { + QHexSelection() { setAdjusted(true); }; - void normalize(QMutex *locker = nullptr) { - if (locker) { - locker->lock(); - } - if (end < start) { - std::swap(start, end); - } - if (locker) { - locker->unlock(); - } - } - - qsizetype length() const { return qAbs(end - start) + 1; } - - bool contains(const QHexSelection &sel) const { - Q_ASSERT(isNormalized()); - return this->start <= sel.start && this->end >= sel.end; + explicit QHexSelection(const QHexPosition &begin, const QHexPosition &end) { + setAdjusted(true); + this->begin = begin; + this->end = end; } bool isLineSelected(qsizetype line) const { Q_ASSERT(isNormalized()); - if (this->start.line == line || this->end.line == line) { + if (this->begin.line == line || this->end.line == line) { return true; } - return this->start.line < line && line < this->end.line; - } - - bool isNormalized() const { return end >= start; } - - QHexSelection normalized() const { - QHexSelection sel = *this; - if (end < start) { - std::swap(sel.start, sel.end); - } - return sel; - } - - bool isIntersected(const QHexSelection &sel) const { - Q_ASSERT(isNormalized()); - return !(sel.end < this->start || sel.start > this->end); - } - - void intersect(const QHexSelection &sel, QMutex *locker = nullptr) { - Q_ASSERT(isNormalized()); - auto s = sel.normalized(); - if (locker) { - locker->lock(); - } - this->start = qMax(this->start, s.start); - this->end = qMin(this->end, s.end); - if (locker) { - locker->unlock(); - } - } - - Q_REQUIRED_RESULT std::optional - removeSelection(const QHexSelection &sel, QMutex *locker = nullptr) { - Q_ASSERT(isNormalized()); - Q_ASSERT(sel.isNormalized()); - if (locker) { - locker->lock(); - } - - if (sel.start <= this->start) { - if (sel.end >= this->start) { - if (sel.end < this->end) { - this->start = sel.end; - ++this->start; - } else { - // makes it invalid, delete later - this->end = this->start; - } - } - } else if (sel.start > this->start && sel.start < this->end) { - this->end = sel.start; - --this->end; - if (sel.end < this->end) { - // break into two ranges - QHexSelection sel; - sel.start = sel.end; - sel.end = this->end; - - if (locker) { - locker->unlock(); - } - - return sel; - } - } - - if (locker) { - locker->unlock(); - } - return {}; - } - - bool mergeSelection(const QHexSelection &sel, QMutex *locker = nullptr) { - Q_ASSERT(isNormalized()); - if (isIntersected(sel)) { - if (locker) { - locker->lock(); - } - this->start = qMin(this->start, sel.start); - this->end = qMax(this->end, sel.end); - if (locker) { - locker->unlock(); - } - return true; - } - return false; + return this->begin.line < line && line < this->end.line; } }; @@ -231,12 +133,11 @@ public: void select(const QHexPosition &pos, QHexCursor::SelectionModes mode = SelectionNormal); - void select(qsizetype line, int column, int nibbleindex = 1, + void select(qsizetype line, int column, QHexCursor::SelectionModes mode = SelectionNormal); void select(qsizetype length, QHexCursor::SelectionModes mode = SelectionNormal); - void selectOffset(qsizetype offset, qsizetype length); void setInsertionMode(InsertionMode mode); void setLineWidth(quint8 width); void switchInsertionMode(); @@ -248,9 +149,6 @@ public: void mergePreviewSelection(); private: - void mergeRemove(const QHexSelection &sel); - void mergeAdd(const QHexSelection &sel, QMutex *locker = nullptr); - bool isLineSelected(const QHexSelection &sel, qsizetype line) const; signals: @@ -264,7 +162,7 @@ private: QHexPosition m_position, m_selection; SelectionMode m_preMode; - QList m_sels; + QHexRegionObjectList m_sels; }; #endif // QHEXCURSOR_H diff --git a/3rdparty/QHexView/document/qhexmetadata.cpp b/3rdparty/QHexView/document/qhexmetadata.cpp index eb9c01a..d02274f 100644 --- a/3rdparty/QHexView/document/qhexmetadata.cpp +++ b/3rdparty/QHexView/document/qhexmetadata.cpp @@ -53,7 +53,7 @@ void QHexMetadata::RemoveMetadata(qsizetype offset) { void QHexMetadata::Metadata(qsizetype begin, qsizetype end, const QColor &fgcolor, const QColor &bgcolor, const QString &comment) { - QHexMetadataItem absi{begin, end, fgcolor, bgcolor, comment}; + QHexMetadataItem absi(begin, end, fgcolor, bgcolor, comment); m_undo->push(new MetaAddCommand(this, absi)); } @@ -78,7 +78,20 @@ bool QHexMetadata::removeMetadata(const QHexMetadataItem &item) { if (index < 0) { return false; } - m_metadata.removeAt(index); + + m_metadata.takeAt(index); + auto ret = removeLineMetadata(item); + return ret; +} + +void QHexMetadata::removeMetadata(qsizetype begin, qsizetype end) { + auto broken = mayBrokenMetaData(begin, end); + for (auto &item : broken) { + removeMetadata(item); + } +} + +bool QHexMetadata::removeLineMetadata(const QHexMetadataItem &item) { for (auto &l : m_linemeta) { l.remove(item); } @@ -124,24 +137,66 @@ QVector QHexMetadata::getAllMetadata() const { return m_metadata; } -QVector QHexMetadata::gets(qsizetype offset) { - QVector ret; +std::optional QHexMetadata::get(qsizetype offset) { + auto r = std::find_if(m_metadata.begin(), m_metadata.end(), + [offset](const QHexMetadataItem &item) { + return offset >= item.begin && offset <= item.end; + }); + if (r == m_metadata.end()) { + return {}; + } + return *r; +} - std::copy_if( -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - m_metadata.begin(), m_metadata.end(), -#else - m_metadata.constBegin(), m_metadata.constEnd(), -#endif - std::back_inserter(ret), [offset](const QHexMetadataItem &item) { - return offset >= item.begin && offset <= item.end; - }); +QHexLineMetadata QHexMetadata::gets(qsizetype line) { + QHexLineMetadata ret; + + if (!m_linemeta.contains(line)) { + return {}; + } + + for (auto &lms : m_linemeta[line]) { + ret.append(lms); + } return ret; } +QPair QHexMetadata::getRealMetaRange(qsizetype begin, + qsizetype end) { + Q_ASSERT(begin <= end); + + using QHexRegionGadget = QHexRegionGadget; + + QList items; + for (auto &meta : m_metadata) { + if (!(end < meta.begin || begin > meta.end)) { + items.append(QHexRegionGadget(meta.begin, meta.end)); + } + } + + if (items.isEmpty()) { + return qMakePair(-1, -1); + } else { + auto pitem = items.first(); + for (auto meta = std::next(items.constBegin()); + meta != items.constEnd(); ++meta) { + pitem.mergeRegion(*meta); + } + + QHexRegionGadget g(begin, end); + auto ret = g.intersect(pitem); + if (!ret) { + return qMakePair(-1, -1); + } + return qMakePair(g.begin, g.end); + } +} + void QHexMetadata::applyMetas(const QVector &metas) { - m_metadata = metas; + for (auto &meta : metas) { + m_metadata.mergeAdd(meta); + } } bool QHexMetadata::hasMetadata() { return m_metadata.count() > 0; } @@ -201,7 +256,7 @@ bool QHexMetadata::metadata(qsizetype begin, qsizetype end, } } - QHexMetadataItem absi{begin, end, fgcolor, bgcolor, comment}; + QHexMetadataItem absi(begin, end, fgcolor, bgcolor, comment); addMetadata(absi); emit metadataChanged(); return true; @@ -294,9 +349,33 @@ bool QHexMetadata::comment(qsizetype begin, qsizetype end, return this->metadata(begin, end, QColor(), QColor(), comment); } +QVector QHexMetadata::mayBrokenMetaData(qsizetype begin, + qsizetype end) { + QVector ret; + std::copy_if( +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + m_metadata.begin(), m_metadata.end(), +#else + m_metadata.constBegin(), m_metadata.constEnd(), +#endif + std::back_inserter(ret), [begin, end](const QHexMetadataItem &item) { + return !(end < item.begin || begin > item.end); + }); + return ret; +} + void QHexMetadata::addMetadata(const QHexMetadataItem &mi) { + auto old = m_metadata; + auto idx = m_metadata.mergeAdd(mi); + if (idx >= 0) { + auto meta = m_metadata.at(idx); + auto lastMeta = m_metadata.last(); + removeLineMetadata(old.at(idx)); + addMetaLines(meta); + addMetaLines(lastMeta); + } + addMetaLines(mi); - m_metadata << mi; } void QHexMetadata::addMetaLines(const QHexMetadataItem &mi) { diff --git a/3rdparty/QHexView/document/qhexmetadata.h b/3rdparty/QHexView/document/qhexmetadata.h index 0fc069f..a398e62 100644 --- a/3rdparty/QHexView/document/qhexmetadata.h +++ b/3rdparty/QHexView/document/qhexmetadata.h @@ -1,6 +1,8 @@ #ifndef QHEXMETADATA_H #define QHEXMETADATA_H +#include "qhexregionobject.h" + #include #include @@ -41,19 +43,56 @@ qHashMulti(qhash_result_t seed, const T &...args) noexcept( using qhash_result_t = size_t; #endif -struct QHexMetadataItem { - qsizetype begin = -1; - qsizetype end = -1; +struct QHexMetadataItem : QHexRegionObject { QColor foreground, background; QString comment; bool flag = false; + QHexMetadataItem() { + this->begin = -1; + this->end = -1; + } + + explicit QHexMetadataItem(qsizetype begin, qsizetype end, QColor foreground, + QColor background, const QString comment) + : foreground(foreground), background(background), comment(comment) { + this->begin = begin; + this->end = end; + } + // added by wingsummer bool operator==(const QHexMetadataItem &item) const { return begin == item.begin && end == item.end && foreground == item.foreground && background == item.background && comment == item.comment; } + + // QHexRegionObject interface +public: + std::variant> + mergeRegion(const QHexMetadataItem &sel, + QMutex *locker = nullptr) override { + if (sel.foreground == this->foreground && + sel.background == this->background && + sel.comment == this->comment) { + auto ret = Super::mergeRegion(sel, locker); + if (std::get(ret)) { + return std::nullopt; + } + return false; + } else { + auto ret = removeRegion(sel, locker); + if (std::holds_alternative(ret)) { + return std::get(ret); + } else { + auto r = std::get(ret); + if (r) { + return std::nullopt; + } + return false; + } + } + } }; inline qhash_result_t qHash(const QHexMetadataItem &c, @@ -75,6 +114,9 @@ typedef QList QHexLineMetadata; class QHexMetadata : public QObject { Q_OBJECT +public: + enum class MetaOpError { Error = -2 }; + public: explicit QHexMetadata(QUndoStack *undo, QObject *parent = nullptr); QHexLineMetadata get(qsizetype line) const; @@ -102,10 +144,17 @@ public: bool modifyMetadata(const QHexMetadataItem &newmeta, const QHexMetadataItem &oldmeta); bool removeMetadata(const QHexMetadataItem &item); + void removeMetadata(qsizetype begin, qsizetype end); + bool removeLineMetadata(const QHexMetadataItem &item); void removeMetadata(qsizetype offset); QVector getAllMetadata() const; - QVector gets(qsizetype offset); + + std::optional get(qsizetype offset); + QHexLineMetadata gets(qsizetype line); + QPair getRealMetaRange(qsizetype begin, + qsizetype end); + void applyMetas(const QVector &metas); bool hasMetadata(); @@ -129,6 +178,8 @@ public: bool background(qsizetype begin, qsizetype end, const QColor &bgcolor); bool comment(qsizetype begin, qsizetype end, const QString &comment); + QVector mayBrokenMetaData(qsizetype begin, qsizetype end); + private: void addMetadata(const QHexMetadataItem &mi); @@ -141,7 +192,8 @@ private: quint8 m_lineWidth; QMap> m_linemeta; - QVector m_metadata; + + QHexRegionObjectList m_metadata; QUndoStack *m_undo = nullptr; // added by wingsummer }; diff --git a/3rdparty/QHexView/document/qhexregionobject.h b/3rdparty/QHexView/document/qhexregionobject.h new file mode 100644 index 0000000..a502d5e --- /dev/null +++ b/3rdparty/QHexView/document/qhexregionobject.h @@ -0,0 +1,468 @@ +#ifndef QHEXREGIONOBJECT_H +#define QHEXREGIONOBJECT_H + +namespace REQUIRE_CHECK { + +struct NO_OP {}; + +template +NO_OP &operator>(const T &, const Arg &); +template +NO_OP &operator>=(const T &, const Arg &); +template +NO_OP &operator<(const T &, const Arg &); +template +NO_OP &operator<=(const T &, const Arg &); +template +NO_OP &operator==(const T &, const Arg &); +template +NO_OP &operator!=(const T &, const Arg &); +int optest(NO_OP const &); + +template +char optest(T const &); + +template +struct GreatThanExists { + enum { value = sizeof(optest(*(T *)(0) > *(Arg *)(0))) == sizeof(char) }; +}; +template +struct LessThanExists { + enum { value = sizeof(optest(*(T *)(0) < *(Arg *)(0))) == sizeof(char) }; +}; +template +struct GreatEqualThanExists { + enum { value = sizeof(optest(*(T *)(0) >= *(Arg *)(0))) == sizeof(char) }; +}; +template +struct LessEqualThanExists { + enum { value = sizeof(optest(*(T *)(0) <= *(Arg *)(0))) == sizeof(char) }; +}; +template +struct EqualExists { + enum { value = sizeof(optest(*(T *)(0) == *(Arg *)(0))) == sizeof(char) }; +}; +template +struct NotEqualExists { + enum { value = sizeof(optest(*(T *)(0) != *(Arg *)(0))) == sizeof(char) }; +}; + +} // namespace REQUIRE_CHECK + +#include +#include + +#include + +template +struct QHexRegionGadget final { + static_assert(REQUIRE_CHECK::GreatThanExists::value, + "Operator > is required"); + static_assert(REQUIRE_CHECK::LessThanExists::value, + "Operator < is required"); + static_assert(REQUIRE_CHECK::GreatEqualThanExists::value, + "Operator >= is required"); + static_assert(REQUIRE_CHECK::LessEqualThanExists::value, + "Operator <= is required"); + static_assert(REQUIRE_CHECK::EqualExists::value, + "Operator == is required"); + static_assert(REQUIRE_CHECK::NotEqualExists::value, + "Operator != is required"); + + bool _valid = true; + +public: + T begin; + T end; + + QHexRegionGadget() : begin(T()), end(T()) {} + + explicit QHexRegionGadget(const T &begin, const T &end) + : begin(begin), end(end) {} + + void normalize() { + Q_ASSERT(isValid()); + if (end < begin) { + std::swap(begin, end); + } + } + + qsizetype length() const { + Q_ASSERT(isValid()); + return qAbs(end - begin) + 1; + } + + bool contains(const QHexRegionGadget &sel) const { + Q_ASSERT(isValid()); + Q_ASSERT(isNormalized()); + return this->begin <= sel.begin && this->end >= sel.end; + } + + bool isNormalized() const { + Q_ASSERT(isValid()); + return end >= begin; + } + + QHexRegionGadget normalized() const { + Q_ASSERT(isValid()); + QHexRegionGadget sel = *this; + if (end < begin) { + std::swap(sel.begin, sel.end); + } + return sel; + } + + bool isIntersected(const QHexRegionGadget &sel) const { + Q_ASSERT(isValid()); + Q_ASSERT(isNormalized()); + return !(sel.end < begin || sel.begin > end); + } + + bool intersect(const QHexRegionGadget &sel) { + Q_ASSERT(isValid()); + Q_ASSERT(isNormalized()); + + if (!isIntersected(sel)) { + _valid = false; + return false; + } + + auto s = sel.normalized(); + + this->begin = qMax(this->begin, s.begin); + this->end = qMin(this->end, s.end); + + return true; + } + + Q_REQUIRED_RESULT std::variant + removeRegion(const QHexRegionGadget &sel) { + Q_ASSERT(isValid()); + Q_ASSERT(isNormalized()); + Q_ASSERT(sel.isNormalized()); + + std::variant result = false; + if (sel.begin <= this->begin) { + if (sel.end >= this->begin) { + if (sel.end < this->end) { + this->begin = sel.end; + } else { + // makes it invalid, delete later + _valid = false; + } + result = true; + } + } else if (sel.begin > this->begin && sel.begin < this->end) { + this->end = sel.begin; + result = true; + + if (sel.end < this->end) { + // break into two ranges + QHexRegionGadget sel; + sel.begin = sel.end; + sel.end = this->end; + + return sel; + } + } + + return result; + } + + std::variant> + mergeRegion(const QHexRegionGadget &sel) { + Q_ASSERT(isValid()); + Q_ASSERT(isNormalized()); + if (isIntersected(sel)) { + this->begin = qMin(this->begin, sel.begin); + this->end = qMax(this->end, sel.end); + return true; + } + return false; + }; + + bool isValid() const { return _valid; } +}; + +template +struct QHexRegionObject { + static_assert(REQUIRE_CHECK::GreatThanExists::value, + "Operator > is required"); + static_assert(REQUIRE_CHECK::LessThanExists::value, + "Operator < is required"); + static_assert(REQUIRE_CHECK::GreatEqualThanExists::value, + "Operator >= is required"); + static_assert(REQUIRE_CHECK::LessEqualThanExists::value, + "Operator <= is required"); + static_assert(REQUIRE_CHECK::EqualExists::value, + "Operator == is required"); + static_assert(REQUIRE_CHECK::NotEqualExists::value, + "Operator != is required"); + + using Super = QHexRegionObject; + +private: + bool _adjusted = false; + + bool _valid = true; + + T next(const T &obj) const { + T ret(obj); + ++ret; + return ret; + } + +public: + T begin; + T end; + + void normalize(QMutex *locker = nullptr) { + Q_ASSERT(isValid()); + if (locker) { + locker->lock(); + } + if (end < begin) { + std::swap(begin, end); + } + if (locker) { + locker->unlock(); + } + } + + qsizetype length() const { + Q_ASSERT(isValid()); + return qAbs(end - begin) + 1; + } + + bool contains(const P &sel) const { + static_assert(std::is_base_of_v, P>); + Q_ASSERT(isValid()); + Q_ASSERT(isNormalized()); + return this->begin <= sel.begin && this->end >= sel.end; + } + + bool isNormalized() const { + Q_ASSERT(isValid()); + return end >= begin; + } + + P normalized() const { + static_assert(std::is_base_of_v, P>); + Q_ASSERT(isValid()); + P sel = *reinterpret_cast(this); + if (end < begin) { + std::swap(sel.begin, sel.end); + } + return sel; + } + + bool isIntersected(const P &sel) const { + static_assert(std::is_base_of_v, P>); + Q_ASSERT(isValid()); + Q_ASSERT(isNormalized()); + if (_adjusted) { + return !(next(sel.end) < this->begin || + sel.begin > next(this->end)); + } + return !(sel.end < begin || sel.begin > end); + } + + bool intersect(const P &sel, QMutex *locker = nullptr) { + static_assert(std::is_base_of_v, P>); + Q_ASSERT(isValid()); + Q_ASSERT(isNormalized()); + if (!isIntersected(sel)) { + return false; + } + auto s = sel.normalized(); + if (locker) { + locker->lock(); + } + if (_adjusted) { + if (this->begin < s.end) { + this->begin = qMax(this->begin, next(s.begin)); + this->end = qMin(next(this->end), s.end); + } else { + this->begin = qMax(next(this->begin), s.begin); + this->end = qMin(this->end, next(s.end)); + } + } else { + this->begin = qMax(this->begin, s.begin); + this->end = qMin(this->end, s.end); + } + if (locker) { + locker->unlock(); + } + return true; + } + + Q_REQUIRED_RESULT virtual std::variant + removeRegion(const P &sel, QMutex *locker = nullptr) { + Q_ASSERT(isValid()); + Q_ASSERT(isNormalized()); + Q_ASSERT(sel.isNormalized()); + + std::variant result = false; + + if (locker) { + locker->lock(); + } + + if (sel.begin <= this->begin) { + if (sel.end >= this->begin) { + if (sel.end < this->end) { + this->begin = sel.end; + if (_adjusted) { + ++this->begin; + } + } else { + // makes it invalid, delete later + _valid = false; + } + result = true; + } + } else if (sel.begin > this->begin && sel.begin < this->end) { + this->end = sel.begin; + if (_adjusted) { + --this->end; + } + result = true; + + if (sel.end < this->end) { + // break into two ranges + P sel; + sel.begin = sel.end; + sel.end = this->end; + + if (locker) { + locker->unlock(); + } + + return sel; + } + } + + if (locker) { + locker->unlock(); + } + return result; + } + + virtual std::variant> + mergeRegion(const P &sel, QMutex *locker = nullptr) { + Q_ASSERT(isValid()); + Q_ASSERT(isNormalized()); + if (isIntersected(sel)) { + if (locker) { + locker->lock(); + } + if (_adjusted) { + if (this->begin < sel.end) { + this->begin = qMin(this->begin, next(sel.begin)); + this->end = qMax(next(this->end), sel.end); + } else { + this->begin = qMin(next(this->begin), sel.begin); + this->end = qMax(this->end, next(sel.end)); + } + } else { + this->begin = qMin(this->begin, sel.begin); + this->end = qMax(this->end, sel.end); + } + if (locker) { + locker->unlock(); + } + return true; + } + return false; + }; + + bool adjusted() const { + Q_ASSERT(isValid()); + return _adjusted; + }; + + void setAdjusted(bool newAdjusted) { + Q_ASSERT(isValid()); + _adjusted = newAdjusted; + }; + + bool isValid() const { return _valid; } +}; + +#include + +template +class QHexRegionObjectList : public QVector

{ + static_assert(std::is_base_of_v, P>); + +public: + QHexRegionObjectList() = default; + + void mergeRemove(const P &sel) { + Q_ASSERT(sel.isNormalized()); + + QList

buffer; + QMutex locker; + QtConcurrent::blockingMap(*this, [&buffer, &locker, &sel](P &s) { + auto r = s.removeRegion(sel); + if (std::holds_alternative

(r)) { + auto region = std::get

(r); + QMutexLocker l(&locker); + buffer.append(region); + } + }); + + // clean up invalid selections + auto cleanup = [](const P &s) { return !s.isValid(); }; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + this->removeIf(cleanup); +#else + this->erase(std::remove_if(this->begin(), this->end(), cleanup)); +#endif + + QtConcurrent::blockingMap( + buffer, [&locker, this](P &s) { mergeAdd(s, &locker); }); + } + + qsizetype mergeAdd(const P &sel, QMutex *locker = nullptr) { + std::variant> res = false; + Q_ASSERT(sel.isNormalized()); + + qsizetype idx = -1; + + auto p = this->begin(); + for (; p != this->end(); ++p) { + res = p->mergeRegion(sel, locker); + if (std::holds_alternative(res)) { + auto merged = std::get(res); + if (merged) { + break; + } + } else { + auto region = std::get>(res); + idx = std::distance(this->begin(), p); + if (region.has_value()) { + this->append(region.value()); + } + break; + } + } + + if (std::holds_alternative(res)) { + auto merged = std::get(res); + if (merged) { + auto idx = std::distance(this->begin(), p); + auto m = this->takeAt(idx); + mergeAdd(m, locker); + } else { + this->append(sel); + } + } else { + this->append(sel); + } + + return idx; + } +}; + +#endif // QHEXREGIONOBJECT_H diff --git a/3rdparty/QHexView/document/qhexrenderer.cpp b/3rdparty/QHexView/document/qhexrenderer.cpp index 1616717..9d13295 100644 --- a/3rdparty/QHexView/document/qhexrenderer.cpp +++ b/3rdparty/QHexView/document/qhexrenderer.cpp @@ -404,7 +404,7 @@ void QHexRenderer::applyMetadata(QTextCursor &textcursor, qsizetype line, if (!metadata->lineHasMetadata(line)) return; - const QHexLineMetadata &linemetadata = metadata->get(line); + const QHexLineMetadata &linemetadata = metadata->gets(line); for (auto &mi : linemetadata) { QTextCharFormat charformat; if (m_document->metabgVisible() && mi.background.isValid() && @@ -452,14 +452,82 @@ void QHexRenderer::applySelection(const QHexSelection &selection, return; } - const QHexPosition &startsel = selection.start; + const QHexPosition &startsel = selection.begin; const QHexPosition &endsel = selection.end; + QTextCharFormat charfmt; + charfmt.setBackground(strikeOut || hasSelection + ? m_selBackgroundColor.darker() + : m_selBackgroundColor); + charfmt.setForeground(strikeOut ? m_selectionColor.darker() + : m_selectionColor); + charfmt.setFontStrikeOut(strikeOut); + charfmt.setFontItalic(strikeOut); + + QTextCharFormat charfmt_meta = charfmt; + charfmt_meta.setFontWeight(QFont::Bold); + charfmt_meta.setFontUnderline(true); + charfmt_meta.setFontItalic(true); + if (startsel.line == endsel.line) { - textcursor.setPosition(startsel.column * factor); - textcursor.movePosition( - QTextCursor::Right, QTextCursor::KeepAnchor, - ((endsel.column - startsel.column + 1) * factor)); + auto selbegin = startsel.offset(); + auto len = endsel.column - startsel.column + 1; + auto selend = selbegin + len; + + auto meta = m_document->metadata()->getRealMetaRange(selbegin, selend); + + if (meta.first >= 0) { + auto begin = meta.first - startsel.lineWidth * startsel.line; + auto mlen = meta.second - meta.first; + + textcursor.setPosition(startsel.column * factor); + textcursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, + (begin - startsel.column) * factor); + if (factor == Hex) + textcursor.movePosition(QTextCursor::Left, + QTextCursor::KeepAnchor, 1); + + textcursor.mergeCharFormat(charfmt); + len -= (begin - startsel.column); + textcursor.clearSelection(); + + if (factor == Hex) + textcursor.movePosition(QTextCursor::Right, + QTextCursor::MoveAnchor, 1); + + textcursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, + mlen * factor); + + if (factor == Hex) + textcursor.movePosition(QTextCursor::Left, + QTextCursor::KeepAnchor, 1); + + textcursor.mergeCharFormat(charfmt_meta); + textcursor.clearSelection(); + + len -= mlen; + if (len > 0) { + if (factor == Hex) + textcursor.movePosition(QTextCursor::Right, + QTextCursor::MoveAnchor, 1); + + textcursor.movePosition(QTextCursor::Right, + QTextCursor::KeepAnchor, len * factor); + if (factor == Hex) + textcursor.movePosition(QTextCursor::Left, + QTextCursor::KeepAnchor, 1); + + textcursor.mergeCharFormat(charfmt); + } + } else { + textcursor.setPosition(startsel.column * factor); + textcursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, + len * factor); + if (factor == Hex) + textcursor.movePosition(QTextCursor::Left, + QTextCursor::KeepAnchor, 1); + textcursor.mergeCharFormat(charfmt); + } } else { if (line == startsel.line) textcursor.setPosition(startsel.column * factor); @@ -472,20 +540,9 @@ void QHexRenderer::applySelection(const QHexSelection &selection, else textcursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); + + textcursor.mergeCharFormat(charfmt); } - - if (factor == Hex) - textcursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1); - - QTextCharFormat charformat; - charformat.setBackground(strikeOut || hasSelection - ? m_selBackgroundColor.darker() - : m_selBackgroundColor); - charformat.setForeground(strikeOut ? m_selectionColor.darker() - : m_selectionColor); - charformat.setFontStrikeOut(strikeOut); - charformat.setFontItalic(strikeOut); - textcursor.mergeCharFormat(charformat); } void QHexRenderer::applyCursorAscii(QTextCursor &textcursor, diff --git a/3rdparty/QHexView/qhexview.cpp b/3rdparty/QHexView/qhexview.cpp index db6a1b7..2c80383 100644 --- a/3rdparty/QHexView/qhexview.cpp +++ b/3rdparty/QHexView/qhexview.cpp @@ -345,7 +345,7 @@ QByteArray QHexView::selectedBytes(qsizetype index) const { QByteArray QHexView::previewSelectedBytes() const { auto sel = m_cursor->previewSelection().normalized(); - return m_document->read(sel.start.offset(), sel.length()); + return m_document->read(sel.begin.offset(), sel.length()); } QByteArrayList QHexView::selectedBytes() const { @@ -591,7 +591,7 @@ void QHexView::mouseMoveEvent(QMouseEvent *e) { if (!m_renderer->hitTest(abspos, &position, this->firstVisibleLine())) return; - cursor->select(position.line, position.column, 0, + cursor->select(position.line, position.column, QHexCursor::SelectionModes( getSelectionMode() | QHexCursor::SelectionPreview)); e->accept(); @@ -760,7 +760,7 @@ void QHexView::moveNext(bool select) { if (select) cur->select(line, std::min(m_renderer->hexLineWidth() - 1, int(column)), - nibbleindex, QHexCursor::SelectionAdd); + QHexCursor::SelectionAdd); cur->moveTo(line, std::min(m_renderer->hexLineWidth() - 1, int(column)), nibbleindex); @@ -798,8 +798,7 @@ void QHexView::movePrevious(bool select) { } if (select) - cur->select(line, std::max(0, column), nibbleindex, - QHexCursor::SelectionAdd); + cur->select(line, std::max(0, column), QHexCursor::SelectionAdd); cur->moveTo(line, std::max(0, column), nibbleindex); } @@ -1003,8 +1002,7 @@ bool QHexView::processMove(QHexCursor *cur, QKeyEvent *e) { cur->select(cur->currentLine(), m_renderer->documentLastColumn()); else - cur->select(cur->currentLine(), m_renderer->hexLineWidth() - 1, - 0); + cur->select(cur->currentLine(), m_renderer->hexLineWidth() - 1); } } else return false; diff --git a/lang/zh_CN/winghex.ts b/lang/zh_CN/winghex.ts index f7d0a07..e88e431 100644 --- a/lang/zh_CN/winghex.ts +++ b/lang/zh_CN/winghex.ts @@ -442,7 +442,7 @@ 编码 - + Untitled 未命名 @@ -857,7 +857,7 @@ - + View 视图 @@ -894,8 +894,8 @@ - - + + Plugin 插件 @@ -906,318 +906,327 @@ - + Log 日志 - + ExportFindResult 导出搜索结果 - + ClearFindResult 清空记录 - + FindResult 搜索结果 - - - + + + + + + + Copy 复制 - - - - + + + + + + + + CopyToClipBoard 数据已拷贝到粘贴板 - + LittleEndian 小端 - + BigEndian 大端 - + Number 数值 - - + + CheckSum 校验和 - - + + DeleteBookMark 删除书签 - - + + ClearBookMark 清空书签 - - - - + + + + BookMark 书签 - + DecodeText 解码字符串 - + ScriptConsole 脚本控制台 - - + + DVList 可视化列表 - - + + DVTree 可视化树数据 - - + + DVTable 可视化表格 - - + + DVText 可视化文本 - - + + Basic 基础 - + New 新建 - + OpenF 打开文件 - + OpenFR 打开局部文件 - + OpenWorkSpace 打开工作区 - + OpenD 打开驱动器 - + RecentFiles 最近打开 - + Reload 重新加载 - - + + Save 保存 - + SaveAs 另存为 - + Export 导出 - + SaveSel 保存选区字节 - - - - + + + + General 基本 - + Undo 撤销 - + Redo 恢复 - + Cut 剪切 - + Paste 粘贴 - + Delete 删除 - + Clone 克隆 - + Lookup 查询 - + Find 查找 - + Goto 跳转 - + + Encoding 编码 - + FileInfo 文件信息 - - + + Hex 十六进制 - + CutHex 剪切(十六进制) - + CopyHex 复制(十六进制) - + PasteHex 粘贴(十六进制) - - + + Fill 填充 - + FillZero 填充零 - - - - - + + + + + MetaData 标注 - + DeleteMetadata 删除标注 - + ClearMetadata 清空标注 - + MetaDataEdit 编辑标注 - + DeleteMetaData 删除标注 - + ClearMetaData 清空标注 - + Display 显示 - + ViewText 文本预览 - + Scale 缩放 @@ -1297,536 +1306,583 @@ 启动完毕 - + + + + + ExportResult + 导出结果 + + + + + + NothingToSave + 没有保存的数据模型 + + + + + + + ClearResult + 清空结果 + + + ResetScale 重置缩放 - + ShowMetafg 标注前景色 - + ShowMetabg 标注背景色 - + ShowMetaComment 批注 - + MetaShowAll 显示所有标注 - + MetaHideAll 隐藏所有标注 - + FileStatus 文件状态 - + InfoSave 是否保存 - + ReadOnly 可读写 - + SetLocked 启用/禁用锁定编辑 - + ErrUnLock 锁定编辑失败 - + SetOver 启用/禁用改变大小 - + ErrUnOver 锁定文件大小失败 - + InfoCanOverLimit 当前编辑处于受限模式! - + Window 窗体 - + Editor 编辑器 - + Tools 工具 - + HexEditorLayout 编辑器布局 - + SetBaseAddr 设置基址 - + addressBase 基址 - + inputAddressBase 请输入基址 - + WarnBigBaseAddress 基址过大,你得到的地址将会不正确! - + ErrBaseAddress 非法基址输入 - + SetColInfo 显示/隐藏地址栏 - + SetHeaderInfo 显示/隐藏表头 - + SetAsciiString 显示/隐藏解码字符串 - + Layout 布局 - + Fullscreen 全屏 - + Default 默认 - + RestoreLayout 恢复布局 - - + + SaveLayout 保存布局 - + ExportLog 导出日志 - + ClearLog 清空日志 - + ScriptEditor 脚本编辑器 - + Scripts 脚本仓库 - + PluginFunctions 插件功能 - + ScriptSetting 脚本设置 - + PluginSettings 插件设置 - + Info 信息 - + Software 软件 - + Sponsor 赞助 - + CheckUpdate 检查更新 - + Wiki 网页 Wiki - + AboutQT 关于 QT - + SetPageIDEmptyTryUseName 设置页 ID 为空,尝试使用名称作为 ID - + SetPageDupNameIgnored 设置页重复的 ID 名称,已忽略加载 - + Plugin %1 contains a duplicate ID (%2) that is already registered by plugin %3 插件 %1 包含重复 ID (%2),该 ID 已被插件 %3 注册 - - + + ChooseFile 选择文件 - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + Error 错误 - - - - + + + + FileNotExist 文件不存在! - - - - - + + + + + + + + + + FilePermission 因文件权限无法继续! - + ProjectFile (*.wingpro) 项目文件 (*.wingpro) - + Root Required! 需要管理员权限继续操作! - + ReloadSuccessfully 文件重新加载成功! - + ReloadUnSuccessfully 文件重新加载失败! - - - + + + + + + + ChooseSaveFile 请选择保存文件路径: - + NoMoreClone 克隆已到上限,无法继续操作! - + FindFininishBusy 查找任务繁忙,请勿重复查找! - + MayTooMuchFindResult 搜索数量已到达上限,结果可能不全,建议请按区段搜索。 - + SaveLayoutSuccess 保存布局成功 - + SaveLayoutError 保存布局失败 - + ConfirmSave 正在关闭未保存的文件或工作区,你确定保存吗? - + + Column %1 + 列 %1 + + + ConfirmAPPSave 你尝试关闭程序,但仍存在未保存的文件或工作区,你确定保存这些更改吗? - - + + + + + + SaveSuccessfully 保存成功! - - + + SaveWSError 保存工作区错误! - - - + + + Warn 警告 - + ScriptObjShow 脚本对象 - - - + + + SourceChanged 局部打开原始文件更改! - - + + SaveSourceFileError 由于原文件更改,保存文件失败! - + SaveUnSuccessfully 保存失败! - + ChooseExportFile 请选择导出文件路径: - + ExportSuccessfully 导出成功! - + ExportSourceFileError 由于原文件更改,导出文件失败! - + ExportUnSuccessfully 导出失败! - + SaveSelSuccess 保存选区字节成功! - + SaveSelError 保存选区字节失败,因文件不具有可写权限! - - + + CutToClipBoard 数据已剪切到粘贴板! - - + + UnCutToClipBoard 由于保持大小限制,数据剪切到粘贴板失败! - - + + UnCopyToClipBoard 由于保持大小限制,数据剪切到复制板失败! - + FindFininish 查找结果完毕! - + PleaseInputFill 请输入填充字节值 - + FillInputError 填充字节输入错误 - - + + InputComment 请输入批注: - - - + + + NoSelection 没有选区,无法继续的操作! - + NoMetaData 无可编辑标记 - + + EmptyFindResult 没有可导出的搜索结果! - + SaveFindResult 导出搜索结果成功! - + SaveFindResultError 导出结果失败! - + TooManyBytesDecode 超出解码字节限制…… - + ExportLogError 导出日志失败! - + ExportLogSuccess 导出日志成功,路径: - + ClearLogSuccess 清空日志成功! - + BadNetwork 无法与远程服务器的更新检查建立连接,请检查网络。 - + NewestVersion 当前软件为最新版本 - + OlderVersion 你使用的软件为老版本,建议到 Github 和 Gitee 的仓库发行版下载更新。 - + CheckingUpdate 检查更新中…… - + Too much opened files 打开的文件过多,无法继续操作! - + CopyLimit 拷贝字节超出限制 - + ErrOpenFileBelow 打开文件出现错误(由于权限不足),如下为打开错误的文件: @@ -2107,57 +2163,57 @@ 无效空贴边组件: - + [EvilCall] 【恶意调用】 - + Not allowed operation in non-UI thread 该操作在非 UI 线程非法 - + UnsafePluginDir 不安全的插件目录,请将插件目录设置为仅管理员账户可写 - + FoundPluginCount 总计发现插件数目: - + PluginLoadingFailedSummary 有依赖插件加载失败总结 - + - PluginName: - 插件名: - + - Dependencies: - 依赖: - + PUID: 插件唯一标志符: - + Version: 版本: - + MD5: MD5 校验和: - + PluginLoadingFinished 加载插件完毕! diff --git a/src/class/qasparser.cpp b/src/class/qasparser.cpp index b40fd88..0e7cedd 100644 --- a/src/class/qasparser.cpp +++ b/src/class/qasparser.cpp @@ -17,8 +17,10 @@ #include "qasparser.h" -#include "AngelScript/sdk/angelscript/source/as_builder.h" -#include "AngelScript/sdk/angelscript/source/as_parser.h" +#include "AngelScript/sdk/angelscript/source/as_objecttype.h" +#include "AngelScript/sdk/angelscript/source/as_scriptcode.h" +#include "AngelScript/sdk/angelscript/source/as_scriptengine.h" +#include "AngelScript/sdk/angelscript/source/as_scriptfunction.h" #include "class/qcodenode.h" #include @@ -115,7 +117,7 @@ QByteArray QAsParser::getFnParamDeclString(asIScriptFunction *fn, } } - return QByteArray(str.AddressOf(), str.GetLength()); + return QByteArray(str.AddressOf(), QByteArray::size_type(str.GetLength())); } QByteArray QAsParser::getFnRealName(asIScriptFunction *fn) { @@ -142,7 +144,7 @@ QByteArray QAsParser::getFnRealName(asIScriptFunction *fn) { str = name; } - return QByteArray(str.AddressOf(), str.GetLength()); + return QByteArray(str.AddressOf(), QByteArray::size_type(str.GetLength())); } QByteArray QAsParser::getFnRetTypeString(asIScriptFunction *fn, @@ -162,7 +164,8 @@ QByteArray QAsParser::getFnRetTypeString(asIScriptFunction *fn, (name.GetLength() > 0 && name[0] == '~') || name == "$beh0" || name == "$beh2"))) { auto str = returnType.Format(nameSpace, includeNamespace); - return QByteArray(str.AddressOf(), str.GetLength()); + return QByteArray(str.AddressOf(), + QByteArray::size_type(str.GetLength())); } return {}; @@ -338,9 +341,11 @@ void QAsParser::addClassCompletion(asIScriptEngine *engine) { auto p = obj->properties[i]; PropertyInfo pi; - pi.name = QByteArray(p->name.AddressOf(), p->name.GetLength()); + pi.name = QByteArray(p->name.AddressOf(), + QByteArray::size_type(p->name.GetLength())); auto tn = p->type.Format(obj->nameSpace); - pi.type = QByteArray(tn.AddressOf(), tn.GetLength()); + pi.type = QByteArray(tn.AddressOf(), + QByteArray::size_type(tn.GetLength())); pi.isPrivate = pi.isPrivate; pi.isProtected = pi.isProtected; pi.isRef = pi.isRef; diff --git a/src/control/editorview.cpp b/src/control/editorview.cpp index c030088..6eb934d 100644 --- a/src/control/editorview.cpp +++ b/src/control/editorview.cpp @@ -130,7 +130,12 @@ EditorView::EditorView(QWidget *parent) applySettings(); } -EditorView::~EditorView() {} +EditorView::~EditorView() { + for (auto &w : m_others) { + m_stack->removeWidget(w); + w->setParent(nullptr); + } +} void EditorView::registerView(WingEditorViewWidget *view) { Q_ASSERT(view); @@ -200,6 +205,7 @@ EditorView::FindError EditorView::find(const FindDialog::Result &result) { if (result.isStringFind) { data = Utilities::encodingString(result.str, result.encoding); + m_findResults->setEncoding(result.encoding); } else { data = result.buffer; } @@ -555,10 +561,17 @@ qsizetype EditorView::copyLimit() const { return m_hex->copyLimit(); } void EditorView::connectDocSavedFlag(EditorView *editor) { connect(editor->m_hex->document().get(), &QHexDocument::documentSaved, this, [=](bool b) { - if (b) { - editor->setWindowTitle(m_fileName); + QString fileName; + if (editor->isNewFile() || editor->isDriver()) { + fileName = m_fileName; } else { - editor->setWindowTitle(QStringLiteral("* ") + m_fileName); + fileName = QFileInfo(m_fileName).fileName(); + } + if (b) { + + editor->setWindowTitle(fileName); + } else { + editor->setWindowTitle(QStringLiteral("* ") + fileName); } }); } diff --git a/src/dialog/mainwindow.cpp b/src/dialog/mainwindow.cpp index 0a50b27..ab171f6 100644 --- a/src/dialog/mainwindow.cpp +++ b/src/dialog/mainwindow.cpp @@ -487,17 +487,56 @@ MainWindow::buildUpFindResultDock(ads::CDockManager *dock, auto header = m_findresult->horizontalHeader(); m_findresult->setContextMenuPolicy( - Qt::ContextMenuPolicy::ActionsContextMenu); - m_findresult->addAction(newAction(QStringLiteral("export"), - tr("ExportFindResult"), - &MainWindow::on_exportfindresult)); - m_findresult->addAction(newAction(QStringLiteral("del"), - tr("ClearFindResult"), - &MainWindow::on_clearfindresult)); + Qt::ContextMenuPolicy::CustomContextMenu); + + auto menu = new QMenu(tr("Encoding"), this); + menu->setIcon(ICONRES(QStringLiteral("encoding"))); + auto aGroup = new QActionGroup(this); + auto langs = Utilities::getEncodings(); + for (auto &l : langs) { + auto a = newCheckableAction(menu, l, [=]() { + auto model = qobject_cast(m_findresult->model()); + if (model) { + model->setEncoding(l); + } + }); + aGroup->addAction(a); + menu->addAction(a); + m_findEncoding.insert(l, a); + } + + m_menuFind = new QMenu(m_findresult); + m_menuFind->addMenu(menu); + m_menuFind->addAction( + newAction(QStringLiteral("copy"), tr("Copy"), [this]() { + auto idx = m_findresult->currentIndex(); + if (idx.isValid()) { + auto model = + qobject_cast(m_findresult->model()); + if (model) { + auto content = model->copyContent(idx); + qApp->clipboard()->setText(content); + Toast::toast(this, NAMEICONRES(QStringLiteral("copy")), + tr("CopyToClipBoard")); + } + } + })); + m_menuFind->addAction(newAction(QStringLiteral("export"), + tr("ExportFindResult"), + &MainWindow::on_exportfindresult)); + m_menuFind->addAction(newAction(QStringLiteral("del"), + tr("ClearFindResult"), + &MainWindow::on_clearfindresult)); + + connect(m_findresult, &QTableViewExt::customContextMenuRequested, this, + [=](const QPoint &pos) { + m_menuFind->popup(m_findresult->viewport()->mapToGlobal(pos)); + }); m_findresult->setItemDelegate(new RichTextItemDelegate(m_findresult)); m_findresult->setModel(_findEmptyResult); + m_findEncoding.value(_findEmptyResult->encoding())->setChecked(true); header->setSectionResizeMode(3, QHeaderView::Stretch); header->setSectionResizeMode(4, QHeaderView::Stretch); @@ -511,8 +550,15 @@ MainWindow::buildUpFindResultDock(ads::CDockManager *dock, editor->raise(); editor->setFocus(); } - editor->hexEditor()->cursor()->moveTo( - editor->findResultModel()->resultAt(index.row()).offset); + + auto e = editor->hexEditor(); + auto fm = editor->findResultModel(); + auto cursor = e->cursor(); + + cursor->moveTo(fm->resultAt(index.row()).offset); + if (cursor->selectionCount() <= 1 && index.column() >= 3) { + cursor->select(fm->lastFindData().length()); + } }); auto dw = buildDockWidget(dock, QStringLiteral("FindResult"), @@ -829,7 +875,53 @@ MainWindow::buildUpVisualDataDock(ads::CDockManager *dock, m_infolist->setProperty(dpname, tr("DVList")); m_infolist->setProperty(dockpname, quintptr(dw)); m_infolist->installEventFilter(efilter); + m_infolist->setContextMenuPolicy(Qt::ActionsContextMenu); + m_infolist->addAction( + newAction(QStringLiteral("copy"), tr("Copy"), [this]() { + auto idx = m_infolist->currentIndex(); + if (idx.isValid()) { + qApp->clipboard()->setText( + m_infolist->model()->data(idx).toString()); + Toast::toast(this, NAMEICONRES(QStringLiteral("copy")), + tr("CopyToClipBoard")); + } + })); + m_infolist->addAction( + newAction(QStringLiteral("export"), tr("ExportResult"), [this]() { + auto model = m_infotable->model(); + if (!model) { + Toast::toast(this, NAMEICONRES(QStringLiteral("save")), + tr("NothingToSave")); + return; + } + auto filename = WingFileDialog::getSaveFileName( + this, tr("ChooseSaveFile"), m_lastusedpath, + QStringLiteral("TXT (*.txt)")); + if (filename.isEmpty()) { + return; + } + QFile f(filename); + if (!f.open(QFile::WriteOnly | QFile::Text)) { + WingMessageBox::critical(this, tr("Error"), + tr("FilePermission")); + return; + } + + auto total = model->rowCount(); + for (int i = 0; i < total; ++i) { + f.write(model->data(model->index(i, 0)).toString().toUtf8()); + f.write("\n"); + } + f.close(); + Toast::toast(this, NAMEICONRES(QStringLiteral("save")), + tr("SaveSuccessfully")); + })); + m_infolist->addAction( + newAction(QStringLiteral("del"), tr("ClearResult"), [this]() { + auto model = m_infolist->model(); + model->removeRows(0, model->rowCount()); + })); auto ar = dock->addDockWidget(area, dw, areaw); m_infotree = new QTreeView(this); @@ -851,6 +943,57 @@ MainWindow::buildUpVisualDataDock(ads::CDockManager *dock, m_infotree->setProperty(dpname, tr("DVTree")); m_infotree->setProperty(dockpname, quintptr(dw)); m_infotree->installEventFilter(efilter); + m_infotree->setContextMenuPolicy(Qt::ActionsContextMenu); + m_infotree->addAction( + newAction(QStringLiteral("copy"), tr("Copy"), [this]() { + auto idx = m_infotree->currentIndex(); + if (idx.isValid()) { + qApp->clipboard()->setText( + m_infotree->model()->data(idx).toString()); + Toast::toast(this, NAMEICONRES(QStringLiteral("copy")), + tr("CopyToClipBoard")); + } + })); + m_infotree->addAction( + newAction(QStringLiteral("export"), tr("ExportResult"), [this]() { + auto model = m_infotable->model(); + if (!model) { + Toast::toast(this, NAMEICONRES(QStringLiteral("save")), + tr("NothingToSave")); + return; + } + + auto filename = WingFileDialog::getSaveFileName( + this, tr("ChooseSaveFile"), m_lastusedpath, + QStringLiteral("Json (*.json)")); + if (filename.isEmpty()) { + return; + } + + QJsonArray rootArray; + for (int row = 0; row < model->rowCount(); ++row) { + QModelIndex index = model->index(row, 0); + rootArray.append(extractModelData(model, index)); + } + + QJsonDocument jsonDocument(rootArray); + QFile file(filename); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + WingMessageBox::critical(this, tr("Error"), + tr("FilePermission")); + return; + } + + file.write(jsonDocument.toJson(QJsonDocument::Indented)); + file.close(); + Toast::toast(this, NAMEICONRES(QStringLiteral("save")), + tr("SaveSuccessfully")); + })); + m_infotree->addAction( + newAction(QStringLiteral("del"), tr("ClearResult"), [this]() { + auto model = m_infotree->model(); + model->removeRows(0, model->rowCount()); + })); dock->addDockWidget(CenterDockWidgetArea, dw, ar); m_infotable = new QTableView(this); @@ -872,6 +1015,111 @@ MainWindow::buildUpVisualDataDock(ads::CDockManager *dock, m_infotable->setProperty(dpname, tr("DVTable")); m_infotable->setProperty(dockpname, quintptr(dw)); m_infotable->installEventFilter(efilter); + m_infotable->setContextMenuPolicy(Qt::ActionsContextMenu); + m_infotable->addAction( + newAction(QStringLiteral("copy"), tr("Copy"), [this]() { + auto idx = m_infotable->currentIndex(); + if (idx.isValid()) { + qApp->clipboard()->setText( + m_infotable->model()->data(idx).toString()); + Toast::toast(this, NAMEICONRES(QStringLiteral("copy")), + tr("CopyToClipBoard")); + } + })); + m_infotable->addAction( + newAction(QStringLiteral("export"), tr("ExportResult"), [this]() { + auto model = m_infotable->model(); + if (!model) { + Toast::toast(this, NAMEICONRES(QStringLiteral("save")), + tr("NothingToSave")); + return; + } + + QString selFilter; + auto filename = WingFileDialog::getSaveFileName( + this, tr("ChooseSaveFile"), m_lastusedpath, + QStringLiteral("Json (*.json);;CSV (*.csv)"), &selFilter); + if (filename.isEmpty()) { + return; + } + + if (selFilter.startsWith(QStringLiteral("Json"))) { + QJsonArray tableData; + + // Add header row + QJsonArray headers; + for (int col = 0; col < model->columnCount(); ++col) { + headers.append( + model->headerData(col, Qt::Horizontal).toString()); + } + tableData.append(headers); + + // Add data rows + for (int row = 0; row < model->rowCount(); ++row) { + QJsonArray rowData; + for (int col = 0; col < model->columnCount(); ++col) { + QModelIndex index = model->index(row, col); + rowData.append(model->data(index).toString()); + } + tableData.append(rowData); + } + + // Create JSON document + QJsonDocument jsonDocument(tableData); + + // Write to file + QFile file(filename); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + WingMessageBox::critical(this, tr("Error"), + tr("FilePermission")); + return; + } + + file.write(jsonDocument.toJson(QJsonDocument::Indented)); + file.close(); + } else { + QFile file(filename); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + WingMessageBox::critical(this, tr("Error"), + tr("FilePermission")); + return; + } + + QTextStream stream(&file); + + // Write headers + QStringList headers; + for (int col = 0; col < model->columnCount(); ++col) { + auto content = + model->headerData(col, Qt::Horizontal).toString(); + content.prepend('"').append('"'); + headers << content; + } + stream << headers.join(',') << Qt::endl; + + // Write data rows + for (int row = 0; row < model->rowCount(); ++row) { + QStringList rowData; + for (int col = 0; col < model->columnCount(); ++col) { + QModelIndex index = model->index(row, col); + auto content = model->data(index).toString(); + content.prepend('"').append('"'); + rowData << content; + } + stream << rowData.join(',') << Qt::endl; + } + + file.close(); + } + + Toast::toast(this, NAMEICONRES(QStringLiteral("save")), + tr("SaveSuccessfully")); + })); + m_infotable->addAction( + newAction(QStringLiteral("del"), tr("ClearResult"), [this]() { + auto model = m_infotable->model(); + model->removeRows(0, model->rowCount()); + })); dock->addDockWidget(CenterDockWidgetArea, dw, ar); m_infotxt = new QTextBrowser(this); @@ -880,6 +1128,37 @@ MainWindow::buildUpVisualDataDock(ads::CDockManager *dock, m_infotxt->setProperty(dpname, tr("DVText")); m_infotxt->setProperty(dockpname, quintptr(dw)); m_infotxt->installEventFilter(efilter); + m_infotxt->setContextMenuPolicy(Qt::CustomContextMenu); + auto menu = m_infotxt->createStandardContextMenu(); + menu->addSeparator(); + menu->addAction( + newAction(QStringLiteral("export"), tr("ExportResult"), [this]() { + auto filename = WingFileDialog::getSaveFileName( + this, tr("ChooseSaveFile"), m_lastusedpath, + QStringLiteral("TXT (*.txt)")); + if (filename.isEmpty()) { + return; + } + + QFile file(filename); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + WingMessageBox::critical(this, tr("Error"), + tr("FilePermission")); + return; + } + + file.write(m_infotxt->toPlainText().toUtf8()); + + Toast::toast(this, NAMEICONRES(QStringLiteral("save")), + tr("SaveSuccessfully")); + })); + menu->addAction(newAction(QStringLiteral("del"), tr("ClearResult"), + [this]() { m_infotxt->clear(); })); + connect(m_infotxt, &QTextBrowser::customContextMenuRequested, this, + [=](const QPoint &pos) { + menu->popup(m_infotxt->viewport()->mapToGlobal(pos)); + }); + dock->addDockWidget(CenterDockWidgetArea, dw, ar); return ar; @@ -1912,6 +2191,12 @@ void MainWindow::on_findfile() { tr("MayTooMuchFindResult")); break; } + + auto result = + qobject_cast(m_findresult->model()); + if (result) { + m_findEncoding.value(result->encoding())->setChecked(true); + } m_find->raise(); }); } @@ -2168,10 +2453,10 @@ void MainWindow::on_metadataedit() { MetaDialog m(this); auto cur = hexeditor->cursor(); if (cur->currentSelectionLength() > 0) { - auto mc = doc->metadata()->gets(cur->position().offset()); + auto mc = doc->metadata()->get(cur->position().offset()); - if (mc.length() > 0) { - auto meta = mc.last(); + if (mc.has_value()) { + auto meta = mc.value(); auto begin = meta.begin; auto end = meta.end; m.setForeGroundColor(meta.foreground); @@ -2303,6 +2588,8 @@ void MainWindow::on_clearfindresult() { void MainWindow::on_exportfindresult() { auto editor = currentEditor(); if (editor == nullptr) { + Toast::toast(this, NAMEICONRES(QStringLiteral("export")), + tr("EmptyFindResult")); return; } @@ -2729,10 +3016,23 @@ void MainWindow::connectEditorView(EditorView *editor) { MetaDialog m(this); auto cur = hexeditor->cursor(); if (cur->hasSelection()) { - auto mc = doc->metadata()->gets(cur->position().offset()); - - if (mc.length() > 0) { - auto meta = mc.last(); + auto total = hexeditor->selectionCount(); + if (m.exec()) { + auto meta = doc->metadata(); + meta->beginMarco(QStringLiteral("OnMetaData")); + for (int i = 0; i < total; ++i) { + auto begin = cur->selectionStart(i).offset(); + auto end = cur->selectionEnd(i).offset() + 1; + meta->Metadata(begin, end, m.foreGroundColor(), + m.backGroundColor(), m.comment()); + } + meta->endMarco(); + cur->clearSelection(); + } + } else { + auto md = doc->metadata()->get(cur->position().offset()); + if (md.has_value()) { + auto meta = md.value(); auto begin = meta.begin; auto end = meta.end; m.setForeGroundColor(meta.foreground); @@ -2749,23 +3049,9 @@ void MainWindow::connectEditorView(EditorView *editor) { mi->ModifyMetadata(meta, o); } } else { - auto total = hexeditor->selectionCount(); - if (m.exec()) { - auto meta = doc->metadata(); - meta->beginMarco(QStringLiteral("OnMetaData")); - for (int i = 0; i < total; ++i) { - auto begin = cur->selectionStart(i).offset(); - auto end = cur->selectionEnd(i).offset() + 1; - meta->Metadata(begin, end, m.foreGroundColor(), - m.backGroundColor(), m.comment()); - } - meta->endMarco(); - cur->clearSelection(); - } + Toast::toast(this, NAMEICONRES(QStringLiteral("metadata")), + tr("NoSelection")); } - } else { - Toast::toast(this, NAMEICONRES(QStringLiteral("metadata")), - tr("NoSelection")); } } }); @@ -2889,6 +3175,7 @@ void MainWindow::swapEditor(EditorView *old, EditorView *cur) { void MainWindow::loadFindResult(EditorView *view) { auto result = view->findResultModel(); m_findresult->setModel(result); + m_findEncoding.value(result->encoding())->setChecked(true); m_findresult->setProperty("EditorView", QVariant::fromValue(view)); } @@ -3232,6 +3519,32 @@ ads::CDockAreaWidget *MainWindow::editorViewArea() const { return m_dock->centralWidget()->dockAreaWidget(); } +QJsonObject MainWindow::extractModelData(const QAbstractItemModel *model, + const QModelIndex &parent) { + QJsonObject jsonObject; + + // Add data for the current row + for (int col = 0; col < model->columnCount(parent); ++col) { + QVariant data = + model->data(model->index(parent.row(), col, parent.parent())); + QString header = model->headerData(col, Qt::Horizontal).toString(); + jsonObject[header.isEmpty() ? tr("Column %1").arg(col) : header] = + data.toString(); + } + + // Recursively add child rows + QJsonArray children; + for (int row = 0; row < model->rowCount(parent); ++row) { + QModelIndex childIndex = model->index(row, 0, parent); + children.append(extractModelData(model, childIndex)); + } + + if (!children.isEmpty()) + jsonObject["children"] = children; + + return jsonObject; +} + void MainWindow::closeEvent(QCloseEvent *event) { m_isOnClosing = true; // first checking the scripting dialog diff --git a/src/dialog/mainwindow.h b/src/dialog/mainwindow.h index bc4ddac..8498d77 100644 --- a/src/dialog/mainwindow.h +++ b/src/dialog/mainwindow.h @@ -18,6 +18,7 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H +#include "class/eventfilter.h" #include "dialog/splashdialog.h" #include "framelessmainwindow.h" @@ -255,6 +256,10 @@ private: inline ads::CDockAreaWidget *editorViewArea() const; +private: + QJsonObject extractModelData(const QAbstractItemModel *model, + const QModelIndex &parent = QModelIndex()); + protected: virtual void closeEvent(QCloseEvent *event) override; @@ -449,6 +454,8 @@ private: QTableViewExt *m_varshowtable = nullptr; ads::CDockWidget *m_find = nullptr; + QMenu *m_menuFind = nullptr; + QHash m_findEncoding; QTableViewExt *m_findresult = nullptr; FindResultModel *_findEmptyResult = nullptr; diff --git a/src/model/findresultmodel.cpp b/src/model/findresultmodel.cpp index 47d594e..d52758d 100644 --- a/src/model/findresultmodel.cpp +++ b/src/model/findresultmodel.cpp @@ -121,7 +121,10 @@ QVariant FindResultModel::headerData(int section, Qt::Orientation orientation, QString FindResultModel::encoding() const { return m_encoding; } void FindResultModel::setEncoding(const QString &newEncoding) { - m_encoding = newEncoding; + if (m_encoding != newEncoding) { + m_encoding = newEncoding; + emit dataChanged(index(0, 4), index(rowCount(QModelIndex()), 4)); + } } QList &FindResultModel::results() { return m_results; } @@ -148,3 +151,52 @@ void FindResultModel::clear() { QList::size_type FindResultModel::size() const { return m_results.size(); } + +QString FindResultModel::copyContent(const QModelIndex &index) const { + if (index.isValid()) { + auto row = index.row(); + auto r = m_results.at(row); + switch (index.column()) { + case 0: // line + return QString::number(r.line); + case 1: // col + return QString::number(r.col); + case 2: // offset + return QStringLiteral("0x") + + QString::number(r.offset, 16).toUpper(); + case 3: { + // range + auto data = m_findData.at(row); + QString buffer = data.cheader.toHex(' ').toUpper(); + if (!data.hbuffer.isEmpty()) { + buffer += data.hbuffer.toHex(' ').toUpper(); + if (!data.tbuffer.isEmpty()) { + buffer += QStringLiteral(" .. "); + } + } + + buffer += data.tbuffer.toHex(' ').toUpper() % + data.ctailer.toHex(' ').toUpper(); + + return buffer; + } + case 4: { // decoding + auto data = m_findData.at(row); + QString buffer = + Utilities::decodingString(data.cheader, m_encoding); + if (!data.hbuffer.isEmpty()) { + buffer += Utilities::decodingString(data.hbuffer); + if (!data.tbuffer.isEmpty()) { + buffer += QStringLiteral(" ... "); + } + } + + buffer += Utilities::decodingString(data.tbuffer) % + Utilities::decodingString(data.ctailer); + + return buffer; + } + } + } + return {}; +} diff --git a/src/model/findresultmodel.h b/src/model/findresultmodel.h index a549ca9..57f7ebb 100644 --- a/src/model/findresultmodel.h +++ b/src/model/findresultmodel.h @@ -47,6 +47,8 @@ public: void clear(); QList::size_type size() const; + QString copyContent(const QModelIndex &index) const; + // QAbstractItemModel interface public: virtual int rowCount(const QModelIndex &parent) const override; diff --git a/src/plugin/iwingplugin.h b/src/plugin/iwingplugin.h index 1351554..d7fc677 100644 --- a/src/plugin/iwingplugin.h +++ b/src/plugin/iwingplugin.h @@ -562,7 +562,8 @@ public: FileOpened = 1u << 3, FileSaved = 1u << 4, FileSwitched = 1u << 5, - FileClosed = 1u << 6 + FileClosed = 1u << 6, + ScriptPragma = 1u << 7 // TODO }; Q_DECLARE_FLAGS(RegisteredEvents, RegisteredEvent) @@ -636,6 +637,17 @@ public: virtual void eventReady() {} +public: + virtual bool eventOnScriptPragma(const QStringList &comments) { + Q_UNUSED(comments); + return false; + } + + virtual bool eventScriptPragmaLineStep(const QString &codes) { + Q_UNUSED(codes); + return false; + } + signals: // extension and exposed to WingHexAngelScript void toast(const QPixmap &icon, const QString &message); @@ -653,6 +665,8 @@ signals: QDialog *createDialog(QWidget *content); + bool raiseDockWidget(QWidget *w); + bool invokeService(const QString &puid, const char *method, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(nullptr), diff --git a/src/plugin/pluginsystem.cpp b/src/plugin/pluginsystem.cpp index ae61b2d..462acbf 100644 --- a/src/plugin/pluginsystem.cpp +++ b/src/plugin/pluginsystem.cpp @@ -645,6 +645,8 @@ void PluginSystem::loadPlugin(IWingPlugin *p, const QString &fileName, auto dw = _win->buildDockWidget(_win->m_dock, widgetName, displayName, info.widget, MainWindow::PLUGIN_VIEWS); + _raisedw.insert(info.widget, dw); + switch (info.area) { case Qt::LeftDockWidgetArea: { if (_win->m_leftViewArea == nullptr) { @@ -796,6 +798,13 @@ void PluginSystem::connectBaseInterface(IWingPlugin *plg) { return nullptr; } }); + connect(plg, &IWingPlugin::raiseDockWidget, this, [=](QWidget *w) -> bool { + if (_raisedw.contains(w)) { + _raisedw.value(w)->raise(); + return true; + } + return false; + }); connect( plg, QOverload _loadedplginfo; QList _loadedplgs; + QHash _raisedw; QList> _lazyplgs; QMap> _evplgs; diff --git a/src/scriptaddon/scriptjson.cpp b/src/scriptaddon/scriptjson.cpp index 23e8926..b86aa9d 100644 --- a/src/scriptaddon/scriptjson.cpp +++ b/src/scriptaddon/scriptjson.cpp @@ -168,7 +168,7 @@ bool CScriptJson::Get(const jsonKey_t &key, CScriptArray &value) const { return false; json js_temp = (*js_info)[key]; - value.Resize(js_temp.size()); + value.Resize(asUINT(js_temp.size())); for (asUINT i = 0; i < js_temp.size(); ++i) { CScriptJson *childNode = Create(engine); @@ -227,7 +227,7 @@ bool CScriptJson::Exists(const jsonKey_t &key) const { bool CScriptJson::IsEmpty() const { return js_info->empty(); } -asUINT CScriptJson::GetSize() const { return js_info->size(); } +asUINT CScriptJson::GetSize() const { return asUINT(js_info->size()); } void CScriptJson::Clear() { js_info->clear(); }