Properly reset the test status bar when selection changes.
[kdevelopdvcssupport.git] / veritas / internal / runnerwindow.cpp
bloba2a76cc2c0bc3679f5d8e09b620b1aa1b6ed1ff9
1 /* KDevelop xUnit plugin
3 * Copyright 2006 Ernst Huber <qxrunner@systest.ch>
4 * Copyright 2008 Manuel Breugelmans <mbr.nxi@gmail.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
22 #include "runnerwindow.h"
24 #include "ui_runnerwindow.h"
26 #include "interfaces/iproject.h"
28 #include "resultsmodel.h"
29 #include "resultsproxymodel.h"
30 #include "runnermodel.h"
31 #include "runnerproxymodel.h"
32 #include "selectionmanager.h"
33 #include "verbosemanager.h"
34 #include "verbosetoggle.h"
35 #include "selectiontoggle.h"
36 #include "testexecutor.h"
38 #include <ktexteditor/cursor.h>
39 #include "interfaces/icore.h"
40 #include "interfaces/idocumentcontroller.h"
42 #include "utils.h"
43 #include "test_p.h"
44 #include "resultswidget.h"
46 #include <QMessageBox>
47 #include <QCloseEvent>
48 #include <KLocale>
49 #include <KAction>
50 #include <KActionMenu>
51 #include <KSelectAction>
52 #include <KIcon>
53 #include <KDebug>
54 #include <KColorScheme>
55 #include <QBrush>
56 #include <QColor>
57 #include <QHeaderView>
59 using KDevelop::IProject;
60 Q_DECLARE_METATYPE(KDevelop::IProject*)
62 static void initVeritasResource()
64 Q_INIT_RESOURCE(qxrunner);
67 using KDevelop::ICore;
68 using KDevelop::IDocumentController;
70 using Veritas::RunnerWindow;
71 using Veritas::RunnerModel;
72 using Veritas::RunnerProxyModel;
73 using Veritas::ResultsModel;
74 using Veritas::ResultsProxyModel;
75 using Veritas::VerboseManager;
76 using Veritas::TestExecutor;
77 using Veritas::Test;
79 const Ui::RunnerWindow* RunnerWindow::ui() const
81 return m_ui;
84 RunnerWindow::RunnerWindow(ResultsModel* rmodel, QWidget* parent, Qt::WFlags flags)
85 : QWidget(parent, flags), m_executor(0), m_isRunning(false)
87 initVeritasResource();
88 m_ui = new Ui::RunnerWindow;
89 m_ui->setupUi(this);
90 m_results = new ResultsWidget();
91 runnerView()->setRootIsDecorated(false);
92 runnerView()->setUniformRowHeights(true);
94 connectFocusStuff();
95 setGreenBar();
96 ui()->progressRun->setTextVisible(false);
97 ui()->progressRun->show();
98 addProjectMenu();
100 // Disable user interaction while there is no data.
101 enableItemActions(false);
103 initItemStatistics();
104 connectActions();
105 runnerView()->setMouseTracking(true);
107 ResultsProxyModel* rproxy = new ResultsProxyModel(this);
108 rproxy->setSourceModel(rmodel);
109 int filter = Veritas::RunError | Veritas::RunFatal | Veritas::RunInfo;
110 rproxy->setFilter(filter); // also updates the view
111 resultsView()->setModel(rproxy);
112 m_results->setResizeMode();
114 m_selection = new SelectionManager(runnerView());
115 SelectionToggle* selectionToggle = new SelectionToggle(runnerView()->viewport());
116 m_selection->setButton(selectionToggle);
117 m_verbose = new VerboseManager(runnerView());
118 VerboseToggle* verboseToggle = new VerboseToggle(runnerView()->viewport());
119 m_verbose->setButton(verboseToggle);
121 QPixmap refresh = KIconLoader::global()->loadIcon("view-refresh", KIconLoader::Small);
122 m_ui->actionReload->setIcon(refresh);
123 QPixmap run = KIconLoader::global()->loadIcon("arrow-right", KIconLoader::Small);
124 m_ui->actionStart->setIcon(run);
125 QPixmap stop = KIconLoader::global()->loadIcon("window-close", KIconLoader::Small);
126 m_ui->actionStop->setIcon(stop);
127 QPixmap select = KIconLoader::global()->loadIcon("list-add", KIconLoader::Small);
128 m_ui->actionSelectAll->setIcon(select);
129 QPixmap deselect = KIconLoader::global()->loadIcon("list-remove", KIconLoader::Small);
130 m_ui->actionUnselectAll->setIcon(deselect);
132 runnerView()->setStyleSheet(
133 "QTreeView::branch{"
134 "image: none;"
135 "border-image: none"
136 "}");
138 connect(runnerView(), SIGNAL(clicked(QModelIndex)),
139 SLOT(expandOrCollapse(QModelIndex)));
141 const char* whatsthis = "xTest runner. First select a project from the rightmost dropdown box. Next, load the test tree by clicking on the green circular arrow icon. Run your tests with a click on the leftmost green arrow icon.";
142 setWhatsThis( i18n(whatsthis) );
143 resultsView()->setWhatsThis( i18n(whatsthis) );
145 runnerView()->setSelectionMode(QAbstractItemView::SingleSelection);
146 runnerView()->setSelectionBehavior(QAbstractItemView::SelectRows);
149 // helper for RunnerWindow(...)
150 void RunnerWindow::addProjectMenu()
152 KSelectAction *m = new KSelectAction(i18n("Project"), this);
153 m->setToolTip(i18n("Select project"));
154 m->setToolBarMode(KSelectAction::MenuMode);
155 m->setEditable(true);
156 m_ui->runnerToolBar->addSeparator();
157 m_ui->runnerToolBar->addAction(m);
158 m_projectPopup = m;
161 void RunnerWindow::addProjectToPopup(IProject* proj)
163 kDebug() << "Adding project to popup " << proj->name();
164 QAction* p = new QAction(proj->name(), this);
165 QVariant v;
166 v.setValue(proj);
167 p->setData(v);
168 m_projectPopup->addAction(p);
169 m_project2action[proj] = p;
172 void RunnerWindow::rmProjectFromPopup(IProject* proj)
174 if (m_project2action.contains(proj)) {
175 QAction* p = m_project2action[proj];
176 m_projectPopup->removeAction(p);
177 m_project2action.remove(proj);
181 namespace
183 /*! functor that counts the selected leaf tests */
184 class SelectedLeafCount
186 public:
187 SelectedLeafCount() : result(0) {}
188 void operator()(Test* t) {
189 if ((t->childCount() == 0) && t->internal()->isChecked()) {
190 result++;
193 int result;
198 void RunnerWindow::resetProgressBar() const
200 ui()->progressRun->setValue(0);
201 ui()->progressRun->update();
202 if (runnerModel()) {
203 SelectedLeafCount slf;
204 traverseTree(runnerModel()->rootItem(), slf);
205 if (slf.result == 0) slf.result++; // 0 results in an indeterminate progressbar, not good
206 ui()->progressRun->setMaximum(slf.result);
207 } else {
208 ui()->progressRun->setMaximum(1);
212 // helper for RunnerWindow(...)
213 void RunnerWindow::initItemStatistics()
215 displayNumErrors(0);
216 displayNumFatals(0);
217 displayNumExceptions(0);
220 // helper for RunnerWindow(...)
221 void RunnerWindow::connectFocusStuff()
223 // To keep the views synchronized when there are highlighted rows
224 // which get clicked again..
225 connect(runnerView(), SIGNAL(pressed(const QModelIndex&)),
226 SLOT(scrollToHighlightedRows()));
227 connect(resultsView(), SIGNAL(pressed(const QModelIndex&)),
228 SLOT(scrollToHighlightedRows()));
231 KSelectAction* RunnerWindow::projectPopup() const
233 return m_projectPopup;
236 // helper for RunnerWindow(...) ctor
237 void RunnerWindow::connectActions()
239 // File commands.
240 connect(m_ui->actionExit, SIGNAL(triggered(bool)), SLOT(close()));
241 // Item commands.
242 m_ui->actionStart->setShortcut(QKeySequence(tr("Ctrl+R")));
243 connect(m_ui->actionStart, SIGNAL(triggered(bool)), SLOT(runItems()));
244 m_ui->actionStop->setShortcut(QKeySequence(tr("Ctrl+K")));
245 connect(m_ui->actionStop, SIGNAL(triggered(bool)), SLOT(stopItems()));
246 // View commands
247 m_ui->actionSelectAll->setShortcut(QKeySequence(tr("Ctrl+A")));
248 connect(m_ui->actionSelectAll, SIGNAL(triggered(bool)),
249 this, SLOT(selectAll()));
250 m_ui->actionUnselectAll->setShortcut(QKeySequence(tr("Ctrl+U")));
251 connect(m_ui->actionUnselectAll, SIGNAL(triggered(bool)),
252 this, SLOT(unselectAll()));
253 m_ui->actionExpandAll->setShortcut(QKeySequence(tr("Ctrl++")));
254 connect(m_ui->actionExpandAll, SIGNAL(triggered(bool)),
255 runnerView(), SLOT(expandAll()));
256 m_ui->actionCollapseAll->setShortcut(QKeySequence(tr("Ctrl+-")));
257 connect(m_ui->actionCollapseAll, SIGNAL(triggered(bool)),
258 runnerView(), SLOT(collapseAll()));
261 void RunnerWindow::unselectAll()
263 runnerModel()->uncheckAll();
264 resetProgressBar();
265 runnerView()->viewport()->update();
268 void RunnerWindow::selectAll()
270 runnerModel()->checkAll();
271 resetProgressBar();
272 runnerView()->viewport()->update();
275 RunnerWindow::~RunnerWindow()
277 // Deleting the model is left to the owner of the model instance.
278 if (m_selection) delete m_selection;
279 if (m_verbose) delete m_verbose;
280 if (m_executor) {
281 m_executor->stop();
282 delete m_executor;
284 if (runnerModel()) delete runnerModel();
285 delete m_ui;
288 // helper for setModel(RunnerModel*)
289 void RunnerWindow::stopPreviousModel()
291 RunnerModel* prevModel = runnerModel();
292 if (prevModel) {
293 if (m_executor) m_executor->stop();
295 RunnerProxyModel* m1 = runnerProxyModel();
296 runnerView()->setModel(0);
297 runnerView()->reset();
298 delete m1;
300 resultsModel()->clear();
301 m_results->setResizeMode();
302 prevModel->disconnect();
303 delete prevModel;
307 // helper for setModel(RunnerModel*)
308 void RunnerWindow::initProxyModels(RunnerModel* model)
310 // Proxy models for the views with the source model as their parent
311 // to have the proxies deleted when the model gets deleted.
312 RunnerProxyModel* proxy = new RunnerProxyModel(model);
313 proxy->setSourceModel(model);
314 runnerView()->setModel(proxy);
315 m_stopWatch = QTime();
318 // helper for setModel(RunnerModel*)
319 void RunnerWindow::connectItemStatistics(RunnerModel* model)
321 // Item statistics.
322 connect(model, SIGNAL(numTotalChanged(int)),
323 SLOT(displayNumTotal(int)));
324 connect(model, SIGNAL(numSelectedChanged(int)),
325 SLOT(displayNumSelected(int)));
326 connect(model, SIGNAL(numErrorsChanged(int)),
327 SLOT(displayNumErrors(int)));
328 connect(model, SIGNAL(numFatalsChanged(int)),
329 SLOT(displayNumFatals(int)));
330 connect(model, SIGNAL(numExceptionsChanged(int)),
331 SLOT(displayNumExceptions(int)));
333 model->countItems(); // this fires the signals connected above.
336 // helper for setModel(RunnerModel*)
337 void RunnerWindow::connectProgressIndicators(RunnerModel* model)
339 // Get notified of items run.
340 connect(model, SIGNAL(numStartedChanged(int)),
341 SLOT(displayProgress(int)));
342 connect(model, SIGNAL(numCompletedChanged(int)),
343 SLOT(displayNumCompleted(int)));
344 connect(model, SIGNAL(allItemsCompleted()),
345 SLOT(displayCompleted()));
346 connect(model, SIGNAL(itemCompleted(QModelIndex)),
347 resultsModel(), SLOT(addResult(QModelIndex)));
350 void RunnerWindow::setModel(RunnerModel* model)
352 m_verbose->reset();
353 m_selection->reset();
354 stopPreviousModel();
355 if (!model) {
356 // No user interaction without a model or an empty one
357 enableItemActions(false);
358 return;
360 initProxyModels(model);
361 if (model->rowCount() == 0) {
362 enableItemActions(false);
363 return;
365 connectItemStatistics(model);
367 connectProgressIndicators(model);
368 enableItemActions(true);
369 m_ui->actionStop->setDisabled(true);
371 connect(m_selection, SIGNAL(selectionChanged()),
372 runnerModel(), SLOT(countItems()));
374 // set top row higlighted
375 runnerView()->setCurrentIndex(runnerProxyModel()->index(0, 0));
376 enableToSource();
377 enableTestSync(true);
378 m_verbose->makeConnections();
379 m_selection->makeConnections();
380 runnerView()->resizeColumnToContents(0);
384 void RunnerWindow::displayProgress(int numItems) const
386 // Display only when there are selected items
387 if (ui()->progressRun->maximum() > 0) {
388 ui()->progressRun->setValue(numItems);
392 void RunnerWindow::displayCompleted() const
394 if (!m_isRunning) return;
395 ui()->progressRun->setValue(ui()->progressRun->maximum());
396 enableControlsAfterRunning();
397 displayElapsed();
398 m_isRunning = false;
399 emit runCompleted();
402 void RunnerWindow::displayNumTotal(int numItems) const
404 Q_UNUSED(numItems);
407 void RunnerWindow::displayNumSelected(int numItems) const
409 if (numItems == 0) numItems++;
410 resetProgressBar();
413 void RunnerWindow::displayNumCompleted(int numItems) const
415 ui()->labelNumRun->setText(QString().setNum(numItems));
416 displayElapsed();
419 void RunnerWindow::setGreenBar() const
421 QProgressBar* bar = ui()->progressRun;
422 bar->setStyleSheet(
423 QString("QProgressBar {"
424 "border: 1px solid grey;"
425 "border-radius: 2px;"
426 "text-align: center;"
428 "QProgressBar::chunk {"
429 "background-color: #009700;"
430 "width: 5px;"
431 "}"));
434 void RunnerWindow::setRedBar() const
436 QProgressBar* bar = ui()->progressRun;
437 bar->setStyleSheet(
438 QString("QProgressBar {"
439 "border: 1px solid grey;"
440 "border-radius: 2px;"
441 "text-align: center;"
443 "QProgressBar::chunk {"
444 "background-color: #DF1313;"
445 "width: 5px;"
446 "}"));
449 void RunnerWindow::displayNumErrors(int numItems) const
451 if (numItems > 0) setRedBar();
454 void RunnerWindow::displayNumFatals(int numItems) const
456 if (numItems > 0) setRedBar();
459 void RunnerWindow::displayNumExceptions(int numItems) const
461 if (numItems > 0) setRedBar();
464 namespace
466 inline Test* testFromIndex(const QModelIndex& index)
468 return static_cast<Test*>(index.internalPointer());
472 void RunnerWindow::syncResultWithTest(const QItemSelection& selected,
473 const QItemSelection& deselected) const
475 Q_UNUSED(deselected);
476 QModelIndexList indexes = selected.indexes();
477 if (indexes.count() < 1 || !runnerProxyModel()->index(0, 0).isValid()) {
478 return; // Do nothing when there are no results or no runner item is selected.
481 enableResultSync(false); // Prevent circular reaction
482 QModelIndex testIndex = runnerProxyModel()->mapToSource(indexes.first());
483 if (testIndex.isValid()) {
484 Test* t = testFromIndex(testIndex);
485 resultsProxyModel()->setTestFilter(t);
487 enableResultSync(true);
490 void RunnerWindow::syncTestWithResult(const QItemSelection& selected,
491 const QItemSelection& deselected) const
493 Q_UNUSED(deselected);
494 QModelIndexList indexes = selected.indexes();
496 if (indexes.count() < 1) {
497 return; // Do nothing when no result is selected.
500 // Determine the results model index.
501 QModelIndex resultIndex;
502 resultIndex = Utils::modelIndexFromProxy(resultsProxyModel(), indexes.first());
504 if (resultIndex.parent().isValid()) {
505 resultIndex = resultIndex.parent();
508 // Get the corresponding runner item index contained in the results model.
509 QModelIndex testItemIndex;
510 testItemIndex = resultsModel()->mapToTestIndex(resultIndex);
512 enableTestSync(false); // prevent circular dependencies.
514 // Determine the proxy model index and highlight it.
515 QModelIndex currentIndex;
516 currentIndex = Utils::proxyIndexFromModel(runnerProxyModel(), testItemIndex);
517 runnerView()->setCurrentIndex(currentIndex);
519 scrollToHighlightedRows(); // Make the row in every tree view visible
520 // and expand corresponding parents.
521 enableTestSync(true); // Enable selection handler again.
524 void RunnerWindow::displayElapsed() const
526 if (m_stopWatch.isValid()) {
527 int mili = m_stopWatch.elapsed();
528 QString elapsed = QString("%1.%2").arg(int(mili/1000)).arg(mili%1000);
529 ui()->labelElapsed->setText(elapsed);
530 } else {
531 ui()->labelElapsed->setText("0.000");
535 void RunnerWindow::scrollToHighlightedRows() const
538 if (runnerView()->selectionMode() == QAbstractItemView::NoSelection ||
539 resultsView()->selectionMode() == QAbstractItemView::NoSelection) {
540 return; // No relevance when selections not allowed.
543 // Note: It's important not to use the current index but work with the
544 // selection instead due to the fact that these indexes might not be the same.
546 QModelIndex index;
547 QModelIndexList indexes;
548 indexes = runnerView()->selectionModel()->selectedIndexes();
550 if (indexes.count() > 0) {
551 index = indexes.first();
553 if (index.isValid()) {
554 runnerView()->scrollTo(index);
557 index = QModelIndex();
558 indexes = resultsView()->selectionModel()->selectedIndexes();
560 if (indexes.count() > 0) {
561 index = indexes.first();
563 if (index.isValid()) {
564 resultsView()->scrollTo(index);
565 } else {
566 // Try to highlight a result.
567 syncResultWithTest(runnerView()->selectionModel()->selection(),
568 runnerView()->selectionModel()->selection());
572 void RunnerWindow::runItems()
574 if (m_isRunning || !runnerModel()->rootItem()) {
575 return;
577 m_isRunning = true;
579 m_stopWatch.start();
580 setGreenBar();
581 displayNumCompleted(0);
582 ui()->labelElapsed->setText("0.000");
584 disableControlsBeforeRunning();
585 resultsModel()->clear();
586 runnerModel()->clearTree();
587 runnerView()->viewport()->update(); // the icons have changed
588 runnerModel()->initCounters();
590 if (m_executor) delete m_executor;
591 m_executor = new TestExecutor;
592 m_executor->setRoot(runnerModel()->rootItem());
593 connect(m_executor, SIGNAL(allDone()), SLOT(displayCompleted()));
595 m_executor->go();
598 void RunnerWindow::stopItems()
600 if (!runnerModel() || !m_isRunning) return;
601 m_ui->actionStop->setDisabled(true);
602 if (m_executor) m_executor->stop();
603 displayCompleted();
604 enableControlsAfterRunning();
605 m_isRunning = false;
608 void RunnerWindow::disableControlsBeforeRunning()
610 enableItemActions(false);
612 m_ui->actionStop->setEnabled(true);
613 m_projectPopup->setEnabled(false);
614 m_ui->actionReload->setEnabled(false);
615 runnerView()->setCursor(QCursor(Qt::BusyCursor));
616 runnerView()->setFocus();
617 runnerView()->setSelectionMode(QAbstractItemView::NoSelection);
618 resultsView()->setSelectionMode(QAbstractItemView::NoSelection);
619 enableTestSync(false);
620 enableResultSync(false);
623 void RunnerWindow::enableControlsAfterRunning() const
625 enableItemActions(true);
627 m_ui->actionStop->setDisabled(true);
628 m_ui->actionReload->setEnabled(true);
629 m_projectPopup->setEnabled(true);
630 runnerView()->setCursor(QCursor());
631 runnerView()->setFocus();
632 runnerView()->setSelectionMode(QAbstractItemView::SingleSelection);
633 resultsView()->setSelectionMode(QAbstractItemView::SingleSelection);
634 enableTestSync(true);
635 enableResultSync(true);
639 void RunnerWindow::enableItemActions(bool enable) const
641 m_ui->actionStart->setEnabled(enable);
642 m_ui->actionStop->setEnabled(enable);
643 m_ui->actionSelectAll->setEnabled(enable);
644 m_ui->actionUnselectAll->setEnabled(enable);
645 m_ui->actionExpandAll->setEnabled(enable);
646 m_ui->actionCollapseAll->setEnabled(enable);
649 void RunnerWindow::enableTestSync(bool enable) const
651 if (enable) {
652 connect(runnerView()->selectionModel(),
653 SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
654 SLOT(syncResultWithTest(QItemSelection,QItemSelection)));
655 } else {
656 disconnect(runnerView()->selectionModel(),
657 SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
658 this, SLOT(syncResultWithTest(QItemSelection,QItemSelection)));
662 void RunnerWindow::enableToSource() const
664 connect(resultsView()->selectionModel(),
665 SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
666 this, SLOT(jumpToSource(QItemSelection,QItemSelection)));
669 void RunnerWindow::jumpToSource(const QItemSelection& selected, const QItemSelection& deselected)
671 Q_UNUSED(deselected);
672 QModelIndexList indexes = selected.indexes();
674 if (indexes.count() < 1) {
675 return; // Do nothing when no result is selected.
678 // Determine the results model index.
679 QModelIndex resultIndex;
680 resultIndex = Utils::modelIndexFromProxy(resultsProxyModel(), indexes.first());
681 Test* t = resultsModel()->testFromIndex(resultIndex);
682 TestResult* r = t->result();
684 KTextEditor::Cursor range(r->line() - 1, 0);
685 IDocumentController* dc = ICore::self()->documentController();
686 dc->openDocument(KUrl(r->file().pathOrUrl()), range);
689 void RunnerWindow::enableResultSync(bool enable) const
691 if (enable) {
692 connect(resultsView()->selectionModel(),
693 SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
694 SLOT(syncTestWithResult(QItemSelection,QItemSelection)));
695 } else {
696 disconnect(resultsView()->selectionModel(),
697 SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
698 this, SLOT(syncTestWithResult(QItemSelection,QItemSelection)));
702 void RunnerWindow::displayStatusNum(QLabel* labelForText,
703 QLabel* labelForPic, int numItems) const
705 labelForText->setText(": " + QString().setNum(numItems));
706 bool visible = (numItems > 0);
707 labelForText->setVisible(visible);
708 labelForPic->setVisible(visible);
711 void RunnerWindow::expandOrCollapse(const QModelIndex& index) const
713 runnerView()->isExpanded(index) ?
714 runnerView()->collapse(index) :
715 runnerView()->expand(index);
718 ////////////////// GETTERS /////////////////////////////////////////////////////////////
720 QTreeView* RunnerWindow::runnerView() const
722 return m_ui->treeRunner;
725 QTreeView* RunnerWindow::resultsView() const
727 return m_results->tree();
730 QWidget* RunnerWindow::resultsWidget() const
732 return m_results;
735 RunnerModel* RunnerWindow::runnerModel() const
737 RunnerProxyModel* proxy = runnerProxyModel();
738 return proxy ?
739 qobject_cast<RunnerModel*>(proxy->sourceModel()) :
743 RunnerProxyModel* RunnerWindow::runnerProxyModel() const
745 return qobject_cast<RunnerProxyModel*>(runnerView()->model());
748 ResultsModel* RunnerWindow::resultsModel() const
750 ResultsProxyModel* proxy = resultsProxyModel();
751 return proxy ?
752 qobject_cast<ResultsModel*>(proxy->sourceModel()) :
756 ResultsProxyModel* RunnerWindow::resultsProxyModel() const
758 return qobject_cast<ResultsProxyModel*>(resultsView()->model());
761 VerboseManager* RunnerWindow::verboseManager() const
763 return m_verbose;