feat: 调整插件模板;移除脚本编译元数据支持;完善插件接口;
This commit is contained in:
parent
0713b4d8dc
commit
1bd978e2b2
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@ public:
|
|||
QUndoCommand *parent = nullptr);
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
|
||||
private:
|
||||
QVector<QHexMetadataItem> _brokenMetas;
|
||||
};
|
||||
|
||||
#endif // METAADDCOMMAND_H
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ public:
|
|||
protected:
|
||||
QHexMetadata *m_hexmeta;
|
||||
qsizetype m_pos;
|
||||
QVector<QHexMetadataItem> olditems;
|
||||
QHexMetadataItem oldmeta;
|
||||
};
|
||||
|
||||
#endif // METAREMOVEPOSCOMMAND_H
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
|
|
Loading…
Reference in New Issue