diff options
| author | Alexander Taylor <alex@vector35.com> | 2025-04-30 21:16:54 -0400 |
|---|---|---|
| committer | Alexander Taylor <alex@vector35.com> | 2025-10-17 21:17:53 -0400 |
| commit | 715dd78287934c2dcb6de54b0a9be48b2237f9e6 (patch) | |
| tree | 5c339ec4723db8617b2888096efb5dacaabb66bc /examples/triage | |
| parent | 93438ca338adccada9fbb973f09ffd44275ca334 (diff) | |
Make Triage Summary tables multi-select + copyable.
Closes #6562.
Diffstat (limited to 'examples/triage')
| -rw-r--r-- | examples/triage/entry.cpp | 50 | ||||
| -rw-r--r-- | examples/triage/entry.h | 2 | ||||
| -rw-r--r-- | examples/triage/exports.cpp | 50 | ||||
| -rw-r--r-- | examples/triage/exports.h | 2 | ||||
| -rw-r--r-- | examples/triage/imports.cpp | 50 | ||||
| -rw-r--r-- | examples/triage/imports.h | 2 | ||||
| -rw-r--r-- | examples/triage/strings.cpp | 52 | ||||
| -rw-r--r-- | examples/triage/strings.h | 4 |
8 files changed, 210 insertions, 2 deletions
diff --git a/examples/triage/entry.cpp b/examples/triage/entry.cpp index f66634f1..c62b9c36 100644 --- a/examples/triage/entry.cpp +++ b/examples/triage/entry.cpp @@ -1,5 +1,8 @@ #include <cstring> #include <algorithm> +#include <QtGui/QClipboard> +#include <QtGui/QGuiApplication> +#include <QtCore/QStringList> #include "entry.h" #include "view.h" #include "fontsettings.h" @@ -167,6 +170,9 @@ EntryTreeView::EntryTreeView(EntryWidget* parent, TriageView* view, BinaryViewRe setRootIsDecorated(false); setUniformRowHeights(true); setSortingEnabled(true); + setSelectionMode(QAbstractItemView::ExtendedSelection); + setSelectionBehavior(QAbstractItemView::SelectRows); + setAllColumnsShowFocus(true); sortByColumn(AddressColumn, Qt::AscendingOrder); setColumnWidth(AddressColumn, 90); @@ -175,6 +181,44 @@ EntryTreeView::EntryTreeView(EntryWidget* parent, TriageView* view, BinaryViewRe connect(selectionModel(), &QItemSelectionModel::currentChanged, this, &EntryTreeView::entrySelected); connect(this, &QTreeView::doubleClicked, this, &EntryTreeView::entryDoubleClicked); + + m_actionHandler.bindAction("Copy", UIAction([this]() { copySelection(); }, [this]() { return canCopySelection(); })); +} + +void EntryTreeView::copySelection() +{ + if (!model() || !selectionModel()) + return; + + QModelIndexList rows = selectionModel()->selectedRows(); + if (rows.isEmpty()) + return; + + std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) { return a.row() < b.row(); }); + + QStringList lines; + for (const QModelIndex& rowIndex : rows) + { + QStringList cells; + for (int column = 0; column < m_model->columnCount(QModelIndex()); column++) + { + if (isColumnHidden(column)) + continue; + + QModelIndex idx = m_model->index(rowIndex.row(), column, QModelIndex()); + cells << m_model->data(idx, Qt::DisplayRole).toString(); + } + lines << cells.join("\t"); + } + + if (QClipboard* clipboard = QGuiApplication::clipboard()) + clipboard->setText(lines.join("\n")); +} + + +bool EntryTreeView::canCopySelection() const +{ + return !selectionModel()->selectedRows().isEmpty(); } @@ -258,6 +302,12 @@ void EntryTreeView::keyPressEvent(QKeyEvent* event) if (sel.size() != 0) entryDoubleClicked(sel[0]); } + else if (event->matches(QKeySequence::Copy)) + { + copySelection(); + event->accept(); + return; + } QTreeView::keyPressEvent(event); } diff --git a/examples/triage/entry.h b/examples/triage/entry.h index 3ed82db5..66943d1d 100644 --- a/examples/triage/entry.h +++ b/examples/triage/entry.h @@ -43,6 +43,8 @@ class EntryTreeView : public QTreeView, public FilterTarget public: EntryTreeView(EntryWidget* parent, TriageView* view, BinaryViewRef data); + void copySelection(); + bool canCopySelection() const; virtual void setFilter(const std::string& filterText) override; virtual void scrollToFirstItem() override; diff --git a/examples/triage/exports.cpp b/examples/triage/exports.cpp index 04547144..ad315384 100644 --- a/examples/triage/exports.cpp +++ b/examples/triage/exports.cpp @@ -1,4 +1,7 @@ #include <QtWidgets/QScrollBar> +#include <QtGui/QClipboard> +#include <QtGui/QGuiApplication> +#include <QtCore/QStringList> #include <algorithm> #include "exports.h" #include "view.h" @@ -319,6 +322,9 @@ ExportsTreeView::ExportsTreeView(ExportsWidget* parent, TriageView* view, Binary setRootIsDecorated(false); setUniformRowHeights(true); setSortingEnabled(true); + setSelectionMode(QAbstractItemView::ExtendedSelection); + setSelectionBehavior(QAbstractItemView::SelectRows); + setAllColumnsShowFocus(true); sortByColumn(AddressColumn, Qt::AscendingOrder); setColumnWidth(OrdinalColumn, 55); @@ -342,6 +348,44 @@ ExportsTreeView::ExportsTreeView(ExportsWidget* parent, TriageView* view, Binary } verticalScrollBar()->setValue(m_scroll); }); + + m_actionHandler.bindAction("Copy", UIAction([this]() { copySelection(); }, [this]() { return canCopySelection(); })); +} + +void ExportsTreeView::copySelection() +{ + if (!model() || !selectionModel()) + return; + + QModelIndexList rows = selectionModel()->selectedRows(); + if (rows.isEmpty()) + return; + + std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) { return a.row() < b.row(); }); + + QStringList lines; + for (const QModelIndex& rowIndex : rows) + { + QStringList cells; + for (int column = 0; column < m_model->columnCount(QModelIndex()); column++) + { + if (isColumnHidden(column)) + continue; + + QModelIndex idx = m_model->index(rowIndex.row(), column, QModelIndex()); + cells << m_model->data(idx, Qt::DisplayRole).toString(); + } + lines << cells.join("\t"); + } + + if (QClipboard* clipboard = QGuiApplication::clipboard()) + clipboard->setText(lines.join("\n")); +} + + +bool ExportsTreeView::canCopySelection() const +{ + return !selectionModel()->selectedRows().isEmpty(); } @@ -425,6 +469,12 @@ void ExportsTreeView::keyPressEvent(QKeyEvent* event) if (sel.size() != 0) exportDoubleClicked(sel[0]); } + else if (event->matches(QKeySequence::Copy)) + { + copySelection(); + event->accept(); + return; + } QTreeView::keyPressEvent(event); } diff --git a/examples/triage/exports.h b/examples/triage/exports.h index 96c2b779..e47b775e 100644 --- a/examples/triage/exports.h +++ b/examples/triage/exports.h @@ -74,6 +74,8 @@ class ExportsTreeView : public QTreeView, public FilterTarget public: ExportsTreeView(ExportsWidget* parent, TriageView* view, BinaryViewRef data); + void copySelection(); + bool canCopySelection() const; virtual void setFilter(const std::string& filterText) override; virtual void scrollToFirstItem() override; diff --git a/examples/triage/imports.cpp b/examples/triage/imports.cpp index 62c8a370..ea4fe5d3 100644 --- a/examples/triage/imports.cpp +++ b/examples/triage/imports.cpp @@ -1,5 +1,8 @@ #include <cstring> #include <algorithm> +#include <QtGui/QClipboard> +#include <QtGui/QGuiApplication> +#include <QtCore/QStringList> #include "imports.h" #include "view.h" #include "fontsettings.h" @@ -239,6 +242,9 @@ ImportsTreeView::ImportsTreeView(ImportsWidget* parent, TriageView* view, Binary setRootIsDecorated(false); setUniformRowHeights(true); setSortingEnabled(true); + setSelectionMode(QAbstractItemView::ExtendedSelection); + setSelectionBehavior(QAbstractItemView::SelectRows); + setAllColumnsShowFocus(true); sortByColumn(0, Qt::AscendingOrder); if (m_model->HasOrdinalCol()) setColumnWidth(m_model->GetOrdinalCol(), 55); @@ -247,6 +253,44 @@ ImportsTreeView::ImportsTreeView(ImportsWidget* parent, TriageView* view, Binary connect(selectionModel(), &QItemSelectionModel::currentChanged, this, &ImportsTreeView::importSelected); connect(this, &QTreeView::doubleClicked, this, &ImportsTreeView::importDoubleClicked); + + m_actionHandler.bindAction("Copy", UIAction([this]() { copySelection(); }, [this]() { return canCopySelection(); })); +} + +void ImportsTreeView::copySelection() +{ + if (!model() || !selectionModel()) + return; + + QModelIndexList rows = selectionModel()->selectedRows(); + if (rows.isEmpty()) + return; + + std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) { return a.row() < b.row(); }); + + QStringList lines; + for (const QModelIndex& rowIndex : rows) + { + QStringList cells; + for (int column = 0; column < m_model->columnCount(QModelIndex()); column++) + { + if (isColumnHidden(column)) + continue; + + QModelIndex idx = m_model->index(rowIndex.row(), column, QModelIndex()); + cells << m_model->data(idx, Qt::DisplayRole).toString(); + } + lines << cells.join("\t"); + } + + if (QClipboard* clipboard = QGuiApplication::clipboard()) + clipboard->setText(lines.join("\n")); +} + + +bool ImportsTreeView::canCopySelection() const +{ + return !selectionModel()->selectedRows().isEmpty(); } @@ -330,6 +374,12 @@ void ImportsTreeView::keyPressEvent(QKeyEvent* event) if (sel.size() != 0) importDoubleClicked(sel[0]); } + else if (event->matches(QKeySequence::Copy)) + { + copySelection(); + event->accept(); + return; + } QTreeView::keyPressEvent(event); } diff --git a/examples/triage/imports.h b/examples/triage/imports.h index 77d39858..9d490c01 100644 --- a/examples/triage/imports.h +++ b/examples/triage/imports.h @@ -52,6 +52,8 @@ class ImportsTreeView : public QTreeView, public FilterTarget public: ImportsTreeView(ImportsWidget* parent, TriageView* view, BinaryViewRef data); + void copySelection(); + bool canCopySelection() const; virtual void setFilter(const std::string& filterText) override; virtual void scrollToFirstItem() override; diff --git a/examples/triage/strings.cpp b/examples/triage/strings.cpp index f7fe5989..b84f6b62 100644 --- a/examples/triage/strings.cpp +++ b/examples/triage/strings.cpp @@ -1,5 +1,8 @@ #include <string.h> #include <algorithm> +#include <QtGui/QClipboard> +#include <QtGui/QGuiApplication> +#include <QtCore/QStringList> #include "strings.h" #include "view.h" #include "fontsettings.h" @@ -193,12 +196,53 @@ StringsTreeView::StringsTreeView(StringsWidget* parent, TriageView* view, Binary setRootIsDecorated(false); setUniformRowHeights(true); setSortingEnabled(true); + setSelectionMode(QAbstractItemView::ExtendedSelection); + setSelectionBehavior(QAbstractItemView::SelectRows); + setAllColumnsShowFocus(true); sortByColumn(0, Qt::AscendingOrder); setFont(getMonospaceFont(this)); connect(selectionModel(), &QItemSelectionModel::currentChanged, this, &StringsTreeView::stringSelected); connect(this, &QTreeView::doubleClicked, this, &StringsTreeView::stringDoubleClicked); + + m_actionHandler.bindAction("Copy", UIAction([this]() { copySelection(); }, [this]() { return canCopySelection(); })); +} + +void StringsTreeView::copySelection() +{ + if (!model() || !selectionModel()) + return; + + QModelIndexList rows = selectionModel()->selectedRows(); + if (rows.isEmpty()) + return; + + std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) { return a.row() < b.row(); }); + + QStringList lines; + for (const QModelIndex& rowIndex : rows) + { + QStringList cells; + for (int column = 0; column < m_model->columnCount(QModelIndex()); column++) + { + if (isColumnHidden(column)) + continue; + + QModelIndex idx = m_model->index(rowIndex.row(), column, QModelIndex()); + cells << m_model->data(idx, Qt::DisplayRole).toString(); + } + lines << cells.join("\t"); + } + + if (QClipboard* clipboard = QGuiApplication::clipboard()) + clipboard->setText(lines.join("\n")); +} + + +bool StringsTreeView::canCopySelection() const +{ + return !selectionModel()->selectedRows().isEmpty(); } @@ -275,6 +319,12 @@ void StringsTreeView::keyPressEvent(QKeyEvent* event) if (sel.size() != 0) stringDoubleClicked(sel[0]); } + else if (event->matches(QKeySequence::Copy)) + { + copySelection(); + event->accept(); + return; + } QTreeView::keyPressEvent(event); } @@ -295,4 +345,4 @@ StringsWidget::StringsWidget(QWidget* parent, TriageView* view, BinaryViewRef da void StringsWidget::showFilter(const QString& filter) { m_filter->showFilter(filter); -}
\ No newline at end of file +} diff --git a/examples/triage/strings.h b/examples/triage/strings.h index 4401cd1b..a7de2912 100644 --- a/examples/triage/strings.h +++ b/examples/triage/strings.h @@ -44,6 +44,8 @@ class StringsTreeView : public QTreeView, public FilterTarget public: StringsTreeView(StringsWidget* parent, TriageView* view, BinaryViewRef data); + void copySelection(); + bool canCopySelection() const; virtual void setFilter(const std::string& filterText) override; virtual void scrollToFirstItem() override; @@ -68,4 +70,4 @@ class StringsWidget : public QWidget public: StringsWidget(QWidget* parent, TriageView* view, BinaryViewRef data); void showFilter(const QString& filter); -};
\ No newline at end of file +}; |
