From 292b4847dc99f0fda01395004ed317775d89ee33 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 19 Aug 2015 13:19:28 +0200 Subject: Add capability to store/copy output from results pane Change-Id: Ie32a374cac851009df3d2289f471bbb697788198 Reviewed-by: Riitta-Leena Miettinen Reviewed-by: David Schulz Reviewed-by: Niels Weber --- plugins/autotest/testresultspane.cpp | 94 ++++++++++++++++++++++++++++++++++-- plugins/autotest/testresultspane.h | 28 +++++++++-- 2 files changed, 114 insertions(+), 8 deletions(-) diff --git a/plugins/autotest/testresultspane.cpp b/plugins/autotest/testresultspane.cpp index db78e24cd6..8f3d4f5b82 100644 --- a/plugins/autotest/testresultspane.cpp +++ b/plugins/autotest/testresultspane.cpp @@ -28,15 +28,19 @@ #include #include #include +#include #include -#include #include +#include +#include #include +#include #include #include +#include #include #include #include @@ -44,11 +48,24 @@ namespace Autotest { namespace Internal { +ResultsTreeView::ResultsTreeView(QWidget *parent) + : Utils::TreeView(parent) +{} + +void ResultsTreeView::keyPressEvent(QKeyEvent *event) +{ + if (event->matches(QKeySequence::Copy)) { + emit copyShortcutTriggered(); + event->accept(); + } +} + TestResultsPane::TestResultsPane(QObject *parent) : Core::IOutputPane(parent), m_context(new Core::IContext(this)), m_wasVisibleBefore(false), - m_autoScroll(false) + m_autoScroll(false), + m_testRunning(false) { m_outputWidget = new QWidget; QVBoxLayout *outputLayout = new QVBoxLayout; @@ -74,9 +91,10 @@ TestResultsPane::TestResultsPane(QObject *parent) : outputLayout->addWidget(m_summaryWidget); - m_treeView = new Utils::TreeView(m_outputWidget); + m_treeView = new ResultsTreeView(m_outputWidget); m_treeView->setHeaderHidden(true); m_treeView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + m_treeView->setContextMenuPolicy(Qt::CustomContextMenu); m_model = new TestResultModel(this); m_filterModel = new TestResultFilterModel(m_model, this); m_filterModel->setDynamicSortFilter(true); @@ -91,6 +109,11 @@ TestResultsPane::TestResultsPane(QObject *parent) : connect(m_treeView, &Utils::TreeView::activated, this, &TestResultsPane::onItemActivated); connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, trd, &TestResultDelegate::currentChanged); + connect(m_treeView, &Utils::TreeView::customContextMenuRequested, + this, &TestResultsPane::onCustomContextMenuRequested); + connect(m_treeView, &ResultsTreeView::copyShortcutTriggered, [this] () { + onCopyItemTriggered(m_treeView->currentIndex()); + }); connect(TestRunner::instance(), &TestRunner::testRunStarted, this, &TestResultsPane::onTestRunStarted); connect(TestRunner::instance(), &TestRunner::testRunFinished, @@ -375,6 +398,7 @@ void TestResultsPane::filterMenuTriggered(QAction *action) void TestResultsPane::onTestRunStarted() { + m_testRunning = true; m_stopTestRun->setEnabled(true); m_runAll->setEnabled(false); m_runSelected->setEnabled(false); @@ -383,6 +407,7 @@ void TestResultsPane::onTestRunStarted() void TestResultsPane::onTestRunFinished() { + m_testRunning = false; m_stopTestRun->setEnabled(false); m_runAll->setEnabled(true); m_runSelected->setEnabled(true); @@ -406,5 +431,68 @@ void TestResultsPane::onTestTreeModelChanged() m_runSelected->setEnabled(enable); } +void TestResultsPane::onCustomContextMenuRequested(const QPoint &pos) +{ + const bool resultsAvailable = m_filterModel->hasResults(); + const bool enabled = !m_testRunning && resultsAvailable; + const QModelIndex clicked = m_treeView->indexAt(pos); + QMenu menu; + QAction *action = new QAction(tr("Copy"), &menu); + action->setShortcut(QKeySequence(QKeySequence::Copy)); + action->setEnabled(resultsAvailable); + connect(action, &QAction::triggered, [this, clicked] () { + onCopyItemTriggered(clicked); + }); + menu.addAction(action); + + action = new QAction(tr("Copy All"), &menu); + action->setEnabled(enabled); + connect(action, &QAction::triggered, this, &TestResultsPane::onCopyWholeTriggered); + menu.addAction(action); + + action = new QAction(tr("Save Output to File..."), &menu); + action->setEnabled(enabled); + connect(action, &QAction::triggered, this, &TestResultsPane::onSaveWholeTriggered); + menu.addAction(action); + + menu.exec(m_treeView->mapToGlobal(pos)); +} + +void TestResultsPane::onCopyItemTriggered(const QModelIndex &idx) +{ + const TestResult result = m_filterModel->testResult(idx); + QApplication::clipboard()->setText(TestResultDelegate::outputString(result, true)); +} + +void TestResultsPane::onCopyWholeTriggered() +{ + QApplication::clipboard()->setText(getWholeOutput()); +} + +void TestResultsPane::onSaveWholeTriggered() +{ + const QString fileName = QFileDialog::getSaveFileName(Core::ICore::dialogParent(), + tr("Save Output To...")); + Utils::FileSaver saver(fileName, QIODevice::Text); + if (!saver.write(getWholeOutput().toUtf8()) || !saver.finalize()) { + QMessageBox::critical(Core::ICore::dialogParent(), tr("Error"), + tr("Failed to write \"%1\".\n\n%2").arg(fileName) + .arg(saver.errorString())); + } +} + +// helper for onCopyWholeTriggered() and onSaveWholeTriggered() +QString TestResultsPane::getWholeOutput() +{ + QString output; + const QModelIndex invalid; // practically the invisible root item + for (int row = 0, count = m_model->rowCount(invalid); row < count; ++row) { + const TestResult result = m_model->testResult(m_model->index(row, 0, invalid)); + output.append(TestResult::resultToString(result.result())).append(QLatin1Char('\t')); + output.append(TestResultDelegate::outputString(result, true)).append(QLatin1Char('\n')); + } + return output; +} + } // namespace Internal } // namespace Autotest diff --git a/plugins/autotest/testresultspane.h b/plugins/autotest/testresultspane.h index 37a35eece8..1bb3cddedd 100644 --- a/plugins/autotest/testresultspane.h +++ b/plugins/autotest/testresultspane.h @@ -22,9 +22,12 @@ #include +#include + QT_BEGIN_NAMESPACE class QAction; class QFrame; +class QKeyEvent; class QLabel; class QModelIndex; class QMenu; @@ -35,10 +38,6 @@ namespace Core { class IContext; } -namespace Utils { -class TreeView; -} - namespace Autotest { namespace Internal { @@ -46,6 +45,19 @@ class TestResult; class TestResultModel; class TestResultFilterModel; +class ResultsTreeView : public Utils::TreeView +{ + Q_OBJECT +public: + ResultsTreeView(QWidget *parent = 0); + +signals: + void copyShortcutTriggered(); + +protected: + void keyPressEvent(QKeyEvent *event); +}; + class TestResultsPane : public Core::IOutputPane { Q_OBJECT @@ -90,11 +102,16 @@ private: void onTestRunFinished(); void onScrollBarRangeChanged(int, int max); void onTestTreeModelChanged(); + void onCustomContextMenuRequested(const QPoint &pos); + void onCopyItemTriggered(const QModelIndex &idx); + void onCopyWholeTriggered(); + void onSaveWholeTriggered(); + QString getWholeOutput(); QWidget *m_outputWidget; QFrame *m_summaryWidget; QLabel *m_summaryLabel; - Utils::TreeView *m_treeView; + ResultsTreeView *m_treeView; TestResultModel *m_model; TestResultFilterModel *m_filterModel; Core::IContext *m_context; @@ -106,6 +123,7 @@ private: bool m_wasVisibleBefore; bool m_autoScroll; bool m_atEnd; + bool m_testRunning; }; } // namespace Internal -- cgit v1.2.1