Skip to content

Commit 715dd78

Browse files
committed
Make Triage Summary tables multi-select + copyable.
Closes #6562.
1 parent 93438ca commit 715dd78

File tree

8 files changed

+210
-2
lines changed

8 files changed

+210
-2
lines changed

examples/triage/entry.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#include <cstring>
22
#include <algorithm>
3+
#include <QtGui/QClipboard>
4+
#include <QtGui/QGuiApplication>
5+
#include <QtCore/QStringList>
36
#include "entry.h"
47
#include "view.h"
58
#include "fontsettings.h"
@@ -167,6 +170,9 @@ EntryTreeView::EntryTreeView(EntryWidget* parent, TriageView* view, BinaryViewRe
167170
setRootIsDecorated(false);
168171
setUniformRowHeights(true);
169172
setSortingEnabled(true);
173+
setSelectionMode(QAbstractItemView::ExtendedSelection);
174+
setSelectionBehavior(QAbstractItemView::SelectRows);
175+
setAllColumnsShowFocus(true);
170176
sortByColumn(AddressColumn, Qt::AscendingOrder);
171177

172178
setColumnWidth(AddressColumn, 90);
@@ -175,6 +181,44 @@ EntryTreeView::EntryTreeView(EntryWidget* parent, TriageView* view, BinaryViewRe
175181

176182
connect(selectionModel(), &QItemSelectionModel::currentChanged, this, &EntryTreeView::entrySelected);
177183
connect(this, &QTreeView::doubleClicked, this, &EntryTreeView::entryDoubleClicked);
184+
185+
m_actionHandler.bindAction("Copy", UIAction([this]() { copySelection(); }, [this]() { return canCopySelection(); }));
186+
}
187+
188+
void EntryTreeView::copySelection()
189+
{
190+
if (!model() || !selectionModel())
191+
return;
192+
193+
QModelIndexList rows = selectionModel()->selectedRows();
194+
if (rows.isEmpty())
195+
return;
196+
197+
std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) { return a.row() < b.row(); });
198+
199+
QStringList lines;
200+
for (const QModelIndex& rowIndex : rows)
201+
{
202+
QStringList cells;
203+
for (int column = 0; column < m_model->columnCount(QModelIndex()); column++)
204+
{
205+
if (isColumnHidden(column))
206+
continue;
207+
208+
QModelIndex idx = m_model->index(rowIndex.row(), column, QModelIndex());
209+
cells << m_model->data(idx, Qt::DisplayRole).toString();
210+
}
211+
lines << cells.join("\t");
212+
}
213+
214+
if (QClipboard* clipboard = QGuiApplication::clipboard())
215+
clipboard->setText(lines.join("\n"));
216+
}
217+
218+
219+
bool EntryTreeView::canCopySelection() const
220+
{
221+
return !selectionModel()->selectedRows().isEmpty();
178222
}
179223

180224

@@ -258,6 +302,12 @@ void EntryTreeView::keyPressEvent(QKeyEvent* event)
258302
if (sel.size() != 0)
259303
entryDoubleClicked(sel[0]);
260304
}
305+
else if (event->matches(QKeySequence::Copy))
306+
{
307+
copySelection();
308+
event->accept();
309+
return;
310+
}
261311
QTreeView::keyPressEvent(event);
262312
}
263313

examples/triage/entry.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class EntryTreeView : public QTreeView, public FilterTarget
4343

4444
public:
4545
EntryTreeView(EntryWidget* parent, TriageView* view, BinaryViewRef data);
46+
void copySelection();
47+
bool canCopySelection() const;
4648

4749
virtual void setFilter(const std::string& filterText) override;
4850
virtual void scrollToFirstItem() override;

examples/triage/exports.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#include <QtWidgets/QScrollBar>
2+
#include <QtGui/QClipboard>
3+
#include <QtGui/QGuiApplication>
4+
#include <QtCore/QStringList>
25
#include <algorithm>
36
#include "exports.h"
47
#include "view.h"
@@ -319,6 +322,9 @@ ExportsTreeView::ExportsTreeView(ExportsWidget* parent, TriageView* view, Binary
319322
setRootIsDecorated(false);
320323
setUniformRowHeights(true);
321324
setSortingEnabled(true);
325+
setSelectionMode(QAbstractItemView::ExtendedSelection);
326+
setSelectionBehavior(QAbstractItemView::SelectRows);
327+
setAllColumnsShowFocus(true);
322328
sortByColumn(AddressColumn, Qt::AscendingOrder);
323329

324330
setColumnWidth(OrdinalColumn, 55);
@@ -342,6 +348,44 @@ ExportsTreeView::ExportsTreeView(ExportsWidget* parent, TriageView* view, Binary
342348
}
343349
verticalScrollBar()->setValue(m_scroll);
344350
});
351+
352+
m_actionHandler.bindAction("Copy", UIAction([this]() { copySelection(); }, [this]() { return canCopySelection(); }));
353+
}
354+
355+
void ExportsTreeView::copySelection()
356+
{
357+
if (!model() || !selectionModel())
358+
return;
359+
360+
QModelIndexList rows = selectionModel()->selectedRows();
361+
if (rows.isEmpty())
362+
return;
363+
364+
std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) { return a.row() < b.row(); });
365+
366+
QStringList lines;
367+
for (const QModelIndex& rowIndex : rows)
368+
{
369+
QStringList cells;
370+
for (int column = 0; column < m_model->columnCount(QModelIndex()); column++)
371+
{
372+
if (isColumnHidden(column))
373+
continue;
374+
375+
QModelIndex idx = m_model->index(rowIndex.row(), column, QModelIndex());
376+
cells << m_model->data(idx, Qt::DisplayRole).toString();
377+
}
378+
lines << cells.join("\t");
379+
}
380+
381+
if (QClipboard* clipboard = QGuiApplication::clipboard())
382+
clipboard->setText(lines.join("\n"));
383+
}
384+
385+
386+
bool ExportsTreeView::canCopySelection() const
387+
{
388+
return !selectionModel()->selectedRows().isEmpty();
345389
}
346390

347391

@@ -425,6 +469,12 @@ void ExportsTreeView::keyPressEvent(QKeyEvent* event)
425469
if (sel.size() != 0)
426470
exportDoubleClicked(sel[0]);
427471
}
472+
else if (event->matches(QKeySequence::Copy))
473+
{
474+
copySelection();
475+
event->accept();
476+
return;
477+
}
428478
QTreeView::keyPressEvent(event);
429479
}
430480

examples/triage/exports.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ class ExportsTreeView : public QTreeView, public FilterTarget
7474

7575
public:
7676
ExportsTreeView(ExportsWidget* parent, TriageView* view, BinaryViewRef data);
77+
void copySelection();
78+
bool canCopySelection() const;
7779

7880
virtual void setFilter(const std::string& filterText) override;
7981
virtual void scrollToFirstItem() override;

examples/triage/imports.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#include <cstring>
22
#include <algorithm>
3+
#include <QtGui/QClipboard>
4+
#include <QtGui/QGuiApplication>
5+
#include <QtCore/QStringList>
36
#include "imports.h"
47
#include "view.h"
58
#include "fontsettings.h"
@@ -239,6 +242,9 @@ ImportsTreeView::ImportsTreeView(ImportsWidget* parent, TriageView* view, Binary
239242
setRootIsDecorated(false);
240243
setUniformRowHeights(true);
241244
setSortingEnabled(true);
245+
setSelectionMode(QAbstractItemView::ExtendedSelection);
246+
setSelectionBehavior(QAbstractItemView::SelectRows);
247+
setAllColumnsShowFocus(true);
242248
sortByColumn(0, Qt::AscendingOrder);
243249
if (m_model->HasOrdinalCol())
244250
setColumnWidth(m_model->GetOrdinalCol(), 55);
@@ -247,6 +253,44 @@ ImportsTreeView::ImportsTreeView(ImportsWidget* parent, TriageView* view, Binary
247253

248254
connect(selectionModel(), &QItemSelectionModel::currentChanged, this, &ImportsTreeView::importSelected);
249255
connect(this, &QTreeView::doubleClicked, this, &ImportsTreeView::importDoubleClicked);
256+
257+
m_actionHandler.bindAction("Copy", UIAction([this]() { copySelection(); }, [this]() { return canCopySelection(); }));
258+
}
259+
260+
void ImportsTreeView::copySelection()
261+
{
262+
if (!model() || !selectionModel())
263+
return;
264+
265+
QModelIndexList rows = selectionModel()->selectedRows();
266+
if (rows.isEmpty())
267+
return;
268+
269+
std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) { return a.row() < b.row(); });
270+
271+
QStringList lines;
272+
for (const QModelIndex& rowIndex : rows)
273+
{
274+
QStringList cells;
275+
for (int column = 0; column < m_model->columnCount(QModelIndex()); column++)
276+
{
277+
if (isColumnHidden(column))
278+
continue;
279+
280+
QModelIndex idx = m_model->index(rowIndex.row(), column, QModelIndex());
281+
cells << m_model->data(idx, Qt::DisplayRole).toString();
282+
}
283+
lines << cells.join("\t");
284+
}
285+
286+
if (QClipboard* clipboard = QGuiApplication::clipboard())
287+
clipboard->setText(lines.join("\n"));
288+
}
289+
290+
291+
bool ImportsTreeView::canCopySelection() const
292+
{
293+
return !selectionModel()->selectedRows().isEmpty();
250294
}
251295

252296

@@ -330,6 +374,12 @@ void ImportsTreeView::keyPressEvent(QKeyEvent* event)
330374
if (sel.size() != 0)
331375
importDoubleClicked(sel[0]);
332376
}
377+
else if (event->matches(QKeySequence::Copy))
378+
{
379+
copySelection();
380+
event->accept();
381+
return;
382+
}
333383
QTreeView::keyPressEvent(event);
334384
}
335385

examples/triage/imports.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class ImportsTreeView : public QTreeView, public FilterTarget
5252

5353
public:
5454
ImportsTreeView(ImportsWidget* parent, TriageView* view, BinaryViewRef data);
55+
void copySelection();
56+
bool canCopySelection() const;
5557

5658
virtual void setFilter(const std::string& filterText) override;
5759
virtual void scrollToFirstItem() override;

examples/triage/strings.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#include <string.h>
22
#include <algorithm>
3+
#include <QtGui/QClipboard>
4+
#include <QtGui/QGuiApplication>
5+
#include <QtCore/QStringList>
36
#include "strings.h"
47
#include "view.h"
58
#include "fontsettings.h"
@@ -193,12 +196,53 @@ StringsTreeView::StringsTreeView(StringsWidget* parent, TriageView* view, Binary
193196
setRootIsDecorated(false);
194197
setUniformRowHeights(true);
195198
setSortingEnabled(true);
199+
setSelectionMode(QAbstractItemView::ExtendedSelection);
200+
setSelectionBehavior(QAbstractItemView::SelectRows);
201+
setAllColumnsShowFocus(true);
196202
sortByColumn(0, Qt::AscendingOrder);
197203

198204
setFont(getMonospaceFont(this));
199205

200206
connect(selectionModel(), &QItemSelectionModel::currentChanged, this, &StringsTreeView::stringSelected);
201207
connect(this, &QTreeView::doubleClicked, this, &StringsTreeView::stringDoubleClicked);
208+
209+
m_actionHandler.bindAction("Copy", UIAction([this]() { copySelection(); }, [this]() { return canCopySelection(); }));
210+
}
211+
212+
void StringsTreeView::copySelection()
213+
{
214+
if (!model() || !selectionModel())
215+
return;
216+
217+
QModelIndexList rows = selectionModel()->selectedRows();
218+
if (rows.isEmpty())
219+
return;
220+
221+
std::sort(rows.begin(), rows.end(), [](const QModelIndex& a, const QModelIndex& b) { return a.row() < b.row(); });
222+
223+
QStringList lines;
224+
for (const QModelIndex& rowIndex : rows)
225+
{
226+
QStringList cells;
227+
for (int column = 0; column < m_model->columnCount(QModelIndex()); column++)
228+
{
229+
if (isColumnHidden(column))
230+
continue;
231+
232+
QModelIndex idx = m_model->index(rowIndex.row(), column, QModelIndex());
233+
cells << m_model->data(idx, Qt::DisplayRole).toString();
234+
}
235+
lines << cells.join("\t");
236+
}
237+
238+
if (QClipboard* clipboard = QGuiApplication::clipboard())
239+
clipboard->setText(lines.join("\n"));
240+
}
241+
242+
243+
bool StringsTreeView::canCopySelection() const
244+
{
245+
return !selectionModel()->selectedRows().isEmpty();
202246
}
203247

204248

@@ -275,6 +319,12 @@ void StringsTreeView::keyPressEvent(QKeyEvent* event)
275319
if (sel.size() != 0)
276320
stringDoubleClicked(sel[0]);
277321
}
322+
else if (event->matches(QKeySequence::Copy))
323+
{
324+
copySelection();
325+
event->accept();
326+
return;
327+
}
278328
QTreeView::keyPressEvent(event);
279329
}
280330

@@ -295,4 +345,4 @@ StringsWidget::StringsWidget(QWidget* parent, TriageView* view, BinaryViewRef da
295345
void StringsWidget::showFilter(const QString& filter)
296346
{
297347
m_filter->showFilter(filter);
298-
}
348+
}

examples/triage/strings.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class StringsTreeView : public QTreeView, public FilterTarget
4444

4545
public:
4646
StringsTreeView(StringsWidget* parent, TriageView* view, BinaryViewRef data);
47+
void copySelection();
48+
bool canCopySelection() const;
4749

4850
virtual void setFilter(const std::string& filterText) override;
4951
virtual void scrollToFirstItem() override;
@@ -68,4 +70,4 @@ class StringsWidget : public QWidget
6870
public:
6971
StringsWidget(QWidget* parent, TriageView* view, BinaryViewRef data);
7072
void showFilter(const QString& filter);
71-
};
73+
};

0 commit comments

Comments
 (0)