feat: 调整插件模板;移除脚本编译元数据支持;完善插件接口;

This commit is contained in:
寂静的羽夏 2025-01-06 09:36:02 +08:00
parent 0713b4d8dc
commit 1bd978e2b2
23 changed files with 1508 additions and 519 deletions

View File

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

View File

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

View File

@ -10,6 +10,9 @@ public:
QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
QVector<QHexMetadataItem> _brokenMetas;
};
#endif // METAADDCOMMAND_H

View File

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

View File

@ -18,7 +18,7 @@ public:
protected:
QHexMetadata *m_hexmeta;
qsizetype m_pos;
QVector<QHexMetadataItem> olditems;
QHexMetadataItem oldmeta;
};
#endif // METAREMOVEPOSCOMMAND_H

View File

@ -1,8 +1,6 @@
#include "qhexcursor.h"
#include <QWidget>
#include <QtConcurrent/QtConcurrentMap>
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<QHexSelection> 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();

View File

@ -4,7 +4,7 @@
#include <QMutex>
#include <QObject>
#include <optional>
#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<QHexPosition, QHexSelection> {
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<QHexSelection>
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<QHexSelection> m_sels;
QHexRegionObjectList<QHexPosition, QHexSelection> m_sels;
};
#endif // QHEXCURSOR_H

View File

@ -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<QHexMetadataItem> QHexMetadata::getAllMetadata() const {
return m_metadata;
}
QVector<QHexMetadataItem> QHexMetadata::gets(qsizetype offset) {
QVector<QHexMetadataItem> ret;
std::optional<QHexMetadataItem> 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<qsizetype, qsizetype> QHexMetadata::getRealMetaRange(qsizetype begin,
qsizetype end) {
Q_ASSERT(begin <= end);
using QHexRegionGadget = QHexRegionGadget<qsizetype>;
QList<QHexRegionGadget> 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<QHexMetadataItem> &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<QHexMetadataItem> QHexMetadata::mayBrokenMetaData(qsizetype begin,
qsizetype end) {
QVector<QHexMetadataItem> 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) {

View File

@ -1,6 +1,8 @@
#ifndef QHEXMETADATA_H
#define QHEXMETADATA_H
#include "qhexregionobject.h"
#include <QObject>
#include <QColor>
@ -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<qsizetype, QHexMetadataItem> {
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<bool, std::optional<QHexMetadataItem>>
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<bool>(ret)) {
return std::nullopt;
}
return false;
} else {
auto ret = removeRegion(sel, locker);
if (std::holds_alternative<QHexMetadataItem>(ret)) {
return std::get<QHexMetadataItem>(ret);
} else {
auto r = std::get<bool>(ret);
if (r) {
return std::nullopt;
}
return false;
}
}
}
};
inline qhash_result_t qHash(const QHexMetadataItem &c,
@ -75,6 +114,9 @@ typedef QList<QHexLineMetadataItem> 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<QHexMetadataItem> getAllMetadata() const;
QVector<QHexMetadataItem> gets(qsizetype offset);
std::optional<QHexMetadataItem> get(qsizetype offset);
QHexLineMetadata gets(qsizetype line);
QPair<qsizetype, qsizetype> getRealMetaRange(qsizetype begin,
qsizetype end);
void applyMetas(const QVector<QHexMetadataItem> &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<QHexMetadataItem> mayBrokenMetaData(qsizetype begin, qsizetype end);
private:
void addMetadata(const QHexMetadataItem &mi);
@ -141,7 +192,8 @@ private:
quint8 m_lineWidth;
QMap<qsizetype, QHash<QHexMetadataItem, QHexLineMetadata>> m_linemeta;
QVector<QHexMetadataItem> m_metadata;
QHexRegionObjectList<qsizetype, QHexMetadataItem> m_metadata;
QUndoStack *m_undo = nullptr; // added by wingsummer
};

View File

@ -0,0 +1,468 @@
#ifndef QHEXREGIONOBJECT_H
#define QHEXREGIONOBJECT_H
namespace REQUIRE_CHECK {
struct NO_OP {};
template <typename T, typename Arg>
NO_OP &operator>(const T &, const Arg &);
template <typename T, typename Arg>
NO_OP &operator>=(const T &, const Arg &);
template <typename T, typename Arg>
NO_OP &operator<(const T &, const Arg &);
template <typename T, typename Arg>
NO_OP &operator<=(const T &, const Arg &);
template <typename T, typename Arg>
NO_OP &operator==(const T &, const Arg &);
template <typename T, typename Arg>
NO_OP &operator!=(const T &, const Arg &);
int optest(NO_OP const &);
template <typename T>
char optest(T const &);
template <typename T, typename Arg = T>
struct GreatThanExists {
enum { value = sizeof(optest(*(T *)(0) > *(Arg *)(0))) == sizeof(char) };
};
template <typename T, typename Arg = T>
struct LessThanExists {
enum { value = sizeof(optest(*(T *)(0) < *(Arg *)(0))) == sizeof(char) };
};
template <typename T, typename Arg = T>
struct GreatEqualThanExists {
enum { value = sizeof(optest(*(T *)(0) >= *(Arg *)(0))) == sizeof(char) };
};
template <typename T, typename Arg = T>
struct LessEqualThanExists {
enum { value = sizeof(optest(*(T *)(0) <= *(Arg *)(0))) == sizeof(char) };
};
template <typename T, typename Arg = T>
struct EqualExists {
enum { value = sizeof(optest(*(T *)(0) == *(Arg *)(0))) == sizeof(char) };
};
template <typename T, typename Arg = T>
struct NotEqualExists {
enum { value = sizeof(optest(*(T *)(0) != *(Arg *)(0))) == sizeof(char) };
};
} // namespace REQUIRE_CHECK
#include <optional>
#include <variant>
#include <QMutex>
template <typename T>
struct QHexRegionGadget final {
static_assert(REQUIRE_CHECK::GreatThanExists<T>::value,
"Operator > is required");
static_assert(REQUIRE_CHECK::LessThanExists<T>::value,
"Operator < is required");
static_assert(REQUIRE_CHECK::GreatEqualThanExists<T>::value,
"Operator >= is required");
static_assert(REQUIRE_CHECK::LessEqualThanExists<T>::value,
"Operator <= is required");
static_assert(REQUIRE_CHECK::EqualExists<T>::value,
"Operator == is required");
static_assert(REQUIRE_CHECK::NotEqualExists<T>::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<bool, QHexRegionGadget>
removeRegion(const QHexRegionGadget &sel) {
Q_ASSERT(isValid());
Q_ASSERT(isNormalized());
Q_ASSERT(sel.isNormalized());
std::variant<bool, QHexRegionGadget> 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<bool, std::optional<QHexRegionGadget>>
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 <typename T, typename P>
struct QHexRegionObject {
static_assert(REQUIRE_CHECK::GreatThanExists<T>::value,
"Operator > is required");
static_assert(REQUIRE_CHECK::LessThanExists<T>::value,
"Operator < is required");
static_assert(REQUIRE_CHECK::GreatEqualThanExists<T>::value,
"Operator >= is required");
static_assert(REQUIRE_CHECK::LessEqualThanExists<T>::value,
"Operator <= is required");
static_assert(REQUIRE_CHECK::EqualExists<T>::value,
"Operator == is required");
static_assert(REQUIRE_CHECK::NotEqualExists<T>::value,
"Operator != is required");
using Super = QHexRegionObject<T, P>;
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<QHexRegionObject<T, P>, 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<QHexRegionObject<T, P>, P>);
Q_ASSERT(isValid());
P sel = *reinterpret_cast<const P *>(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<QHexRegionObject<T, P>, 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<QHexRegionObject<T, P>, 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<bool, P>
removeRegion(const P &sel, QMutex *locker = nullptr) {
Q_ASSERT(isValid());
Q_ASSERT(isNormalized());
Q_ASSERT(sel.isNormalized());
std::variant<bool, P> 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<bool, std::optional<P>>
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 <QtConcurrent/QtConcurrentMap>
template <typename T, typename P>
class QHexRegionObjectList : public QVector<P> {
static_assert(std::is_base_of_v<QHexRegionObject<T, P>, P>);
public:
QHexRegionObjectList() = default;
void mergeRemove(const P &sel) {
Q_ASSERT(sel.isNormalized());
QList<P> buffer;
QMutex locker;
QtConcurrent::blockingMap(*this, [&buffer, &locker, &sel](P &s) {
auto r = s.removeRegion(sel);
if (std::holds_alternative<P>(r)) {
auto region = std::get<P>(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<bool, std::optional<P>> 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<bool>(res)) {
auto merged = std::get<bool>(res);
if (merged) {
break;
}
} else {
auto region = std::get<std::optional<P>>(res);
idx = std::distance(this->begin(), p);
if (region.has_value()) {
this->append(region.value());
}
break;
}
}
if (std::holds_alternative<bool>(res)) {
auto merged = std::get<bool>(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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -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<FindResultModel *>(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<FindResultModel *>(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<FindResultModel *>(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

View File

@ -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<QString, QAction *> m_findEncoding;
QTableViewExt *m_findresult = nullptr;
FindResultModel *_findEmptyResult = nullptr;

View File

@ -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<WingHex::FindResult> &FindResultModel::results() { return m_results; }
@ -148,3 +151,52 @@ void FindResultModel::clear() {
QList<WingHex::FindResult>::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 {};
}

View File

@ -47,6 +47,8 @@ public:
void clear();
QList<WingHex::FindResult>::size_type size() const;
QString copyContent(const QModelIndex &index) const;
// QAbstractItemModel interface
public:
virtual int rowCount(const QModelIndex &parent) const override;

View File

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

View File

@ -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<const QString &, const char *, Qt::ConnectionType,

View File

@ -236,6 +236,7 @@ private:
MainWindow *_win = nullptr;
QList<WingDependency> _loadedplginfo;
QList<IWingPlugin *> _loadedplgs;
QHash<QWidget *, ads::CDockWidget *> _raisedw;
QList<QPair<IWingPlugin *, QString>> _lazyplgs;
QMap<IWingPlugin::RegisteredEvent, QList<IWingPlugin *>> _evplgs;

View File

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