summaryrefslogtreecommitdiff
path: root/examples/triage
diff options
context:
space:
mode:
authorAlexander Taylor <alex@vector35.com>2025-04-30 21:16:54 -0400
committerAlexander Taylor <alex@vector35.com>2025-10-17 21:17:53 -0400
commit715dd78287934c2dcb6de54b0a9be48b2237f9e6 (patch)
tree5c339ec4723db8617b2888096efb5dacaabb66bc /examples/triage
parent93438ca338adccada9fbb973f09ffd44275ca334 (diff)
Make Triage Summary tables multi-select + copyable.
Closes #6562.
Diffstat (limited to 'examples/triage')
-rw-r--r--examples/triage/entry.cpp50
-rw-r--r--examples/triage/entry.h2
-rw-r--r--examples/triage/exports.cpp50
-rw-r--r--examples/triage/exports.h2
-rw-r--r--examples/triage/imports.cpp50
-rw-r--r--examples/triage/imports.h2
-rw-r--r--examples/triage/strings.cpp52
-rw-r--r--examples/triage/strings.h4
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
+};