]> git.cworth.org Git - apitrace/blob - gui/mainwindow.cpp
Clean the constructor a bit.
[apitrace] / gui / mainwindow.cpp
1 #include "mainwindow.h"
2
3 #include "apitrace.h"
4 #include "apitracecall.h"
5 #include "apicalldelegate.h"
6 #include "apitracemodel.h"
7 #include "apitracefilter.h"
8 #include "imageviewer.h"
9 #include "retracer.h"
10 #include "settingsdialog.h"
11 #include "shaderssourcewidget.h"
12 #include "ui_retracerdialog.h"
13 #include "vertexdatainterpreter.h"
14
15 #include <qjson/parser.h>
16
17 #include <QAction>
18 #include <QDebug>
19 #include <QDesktopServices>
20 #include <QDir>
21 #include <QFileDialog>
22 #include <QLineEdit>
23 #include <QMessageBox>
24 #include <QProgressBar>
25 #include <QToolBar>
26 #include <QUrl>
27 #include <QVBoxLayout>
28 #include <QWebPage>
29 #include <QWebView>
30
31
32 MainWindow::MainWindow()
33     : QMainWindow(),
34       m_selectedEvent(0),
35       m_stateEvent(0),
36       m_jsonParser(new QJson::Parser())
37 {
38     m_ui.setupUi(this);
39     initObjects();
40     initConnections();
41 }
42
43 void MainWindow::openTrace()
44 {
45     QString fileName =
46         QFileDialog::getOpenFileName(
47             this,
48             tr("Open Trace"),
49             QDir::homePath(),
50             tr("Trace Files (*.trace)"));
51
52     qDebug()<< "File name : " <<fileName;
53
54     newTraceFile(fileName);
55 }
56
57 void MainWindow::loadTrace(const QString &fileName)
58 {
59     if (!QFile::exists(fileName)) {
60         QMessageBox::warning(this, tr("File Missing"),
61                              tr("File '%1' doesn't exist.").arg(fileName));
62         return;
63     }
64     qDebug()<< "Loading  : " <<fileName;
65
66     m_progressBar->setValue(0);
67     newTraceFile(fileName);
68 }
69
70 void MainWindow::callItemSelected(const QModelIndex &index)
71 {
72     ApiTraceEvent *event =
73         index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
74
75     if (event && event->type() == ApiTraceEvent::Call) {
76         ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
77         m_ui.detailsWebView->setHtml(call->toHtml());
78         m_ui.detailsDock->show();
79         if (call->hasBinaryData()) {
80             QByteArray data =
81                 call->argValues[call->binaryDataIndex()].toByteArray();
82             m_vdataInterpreter->setData(data);
83
84             for (int i = 0; i < call->argNames.count(); ++i) {
85                 QString name = call->argNames[i];
86                 if (name == QLatin1String("stride")) {
87                     int stride = call->argValues[i].toInt();
88                     m_ui.vertexStrideSB->setValue(stride);
89                 } else if (name == QLatin1String("size")) {
90                     int components = call->argValues[i].toInt();
91                     m_ui.vertexComponentsSB->setValue(components);
92                 } else if (name == QLatin1String("type")) {
93                     QString val = call->argValues[i].toString();
94                     int textIndex = m_ui.vertexTypeCB->findText(val);
95                     if (textIndex >= 0)
96                         m_ui.vertexTypeCB->setCurrentIndex(textIndex);
97                 }
98             }
99         }
100         m_ui.vertexDataDock->setVisible(call->hasBinaryData());
101         m_selectedEvent = call;
102     } else {
103         if (event && event->type() == ApiTraceEvent::Frame) {
104             m_selectedEvent = static_cast<ApiTraceFrame*>(event);
105         } else
106             m_selectedEvent = 0;
107         m_ui.detailsDock->hide();
108         m_ui.vertexDataDock->hide();
109     }
110     if (m_selectedEvent && !m_selectedEvent->state().isEmpty()) {
111         fillStateForFrame();
112     } else
113         m_ui.stateDock->hide();
114 }
115
116 void MainWindow::filterTrace()
117 {
118     m_proxyModel->setFilterString(m_filterEdit->text());
119 }
120
121 void MainWindow::replayStart()
122 {
123     QDialog dlg;
124     Ui_RetracerDialog dlgUi;
125     dlgUi.setupUi(&dlg);
126
127     dlgUi.doubleBufferingCB->setChecked(
128         m_retracer->isDoubleBuffered());
129     dlgUi.benchmarkCB->setChecked(
130         m_retracer->isBenchmarking());
131
132     if (dlg.exec() == QDialog::Accepted) {
133         m_retracer->setDoubleBuffered(
134             dlgUi.doubleBufferingCB->isChecked());
135         m_retracer->setBenchmarking(
136             dlgUi.benchmarkCB->isChecked());
137         replayTrace(false);
138     }
139 }
140
141 void MainWindow::replayStop()
142 {
143     m_retracer->terminate();
144     m_ui.actionStop->setEnabled(false);
145     m_ui.actionReplay->setEnabled(true);
146     m_ui.actionLookupState->setEnabled(true);
147 }
148
149 void MainWindow::newTraceFile(const QString &fileName)
150 {
151     m_traceFileName = fileName;
152     m_trace->setFileName(fileName);
153
154     if (m_traceFileName.isEmpty()) {
155         m_ui.actionReplay->setEnabled(false);
156         m_ui.actionLookupState->setEnabled(false);
157         setWindowTitle(tr("QApiTrace"));
158     } else {
159         QFileInfo info(fileName);
160         m_ui.actionReplay->setEnabled(true);
161         m_ui.actionLookupState->setEnabled(true);
162         setWindowTitle(
163             tr("QApiTrace - %1").arg(info.fileName()));
164     }
165 }
166
167 void MainWindow::replayFinished(const QByteArray &output)
168 {
169     m_ui.actionStop->setEnabled(false);
170     m_ui.actionReplay->setEnabled(true);
171     m_ui.actionLookupState->setEnabled(true);
172
173     if (m_retracer->captureState()) {
174         bool ok = false;
175         QVariantMap parsedJson = m_jsonParser->parse(output, &ok).toMap();
176         parseState(parsedJson);
177     } else if (output.length() < 80) {
178         statusBar()->showMessage(output);
179     }
180     m_stateEvent = 0;
181 }
182
183 void MainWindow::replayError(const QString &message)
184 {
185     m_ui.actionStop->setEnabled(false);
186     m_ui.actionReplay->setEnabled(true);
187     m_ui.actionLookupState->setEnabled(true);
188     m_stateEvent = 0;
189
190     QMessageBox::warning(
191         this, tr("Replay Failed"), message);
192 }
193
194 void MainWindow::startedLoadingTrace()
195 {
196     Q_ASSERT(m_trace);
197     m_progressBar->show();
198     QFileInfo info(m_trace->fileName());
199     statusBar()->showMessage(
200         tr("Loading %1...").arg(info.fileName()));
201 }
202
203 void MainWindow::finishedLoadingTrace()
204 {
205     m_progressBar->hide();
206     if (!m_trace) {
207         return;
208     }
209     QFileInfo info(m_trace->fileName());
210     statusBar()->showMessage(
211         tr("Loaded %1").arg(info.fileName()), 3000);
212 }
213
214 void MainWindow::replayTrace(bool dumpState)
215 {
216     if (m_traceFileName.isEmpty())
217         return;
218
219     m_retracer->setFileName(m_traceFileName);
220     m_retracer->setCaptureState(dumpState);
221     if (m_retracer->captureState() && m_selectedEvent) {
222         int index = 0;
223         if (m_selectedEvent->type() == ApiTraceEvent::Call) {
224             index = static_cast<ApiTraceCall*>(m_selectedEvent)->index;
225         } else if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
226             ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(m_selectedEvent);
227             if (frame->calls.isEmpty()) {
228                 //XXX i guess we could still get the current state
229                 qDebug()<<"tried to get a state for an empty frame";
230                 return;
231             }
232             index = frame->calls.first()->index;
233         } else {
234             qDebug()<<"Unknown event type";
235             return;
236         }
237         m_retracer->setCaptureAtCallNumber(index);
238     }
239     m_retracer->start();
240
241     m_ui.actionStop->setEnabled(true);
242 }
243
244 void MainWindow::lookupState()
245 {
246     if (!m_selectedEvent) {
247         QMessageBox::warning(
248             this, tr("Unknown Event"),
249             tr("To inspect the state select an event in the event list."));
250         return;
251     }
252     m_stateEvent = m_selectedEvent;
253     replayTrace(true);
254 }
255
256 MainWindow::~MainWindow()
257 {
258     delete m_jsonParser;
259 }
260
261 void MainWindow::parseState(const QVariantMap &parsedJson)
262 {
263     m_stateEvent->setState(ApiTraceState(parsedJson));
264     m_model->stateSetOnEvent(m_stateEvent);
265     if (m_selectedEvent == m_stateEvent) {
266         fillStateForFrame();
267     } else {
268         m_ui.stateDock->hide();
269     }
270 }
271
272 static void
273 variantToString(const QVariant &var, QString &str)
274 {
275     if (var.type() == QVariant::List) {
276         QVariantList lst = var.toList();
277         str += QLatin1String("[");
278         for (int i = 0; i < lst.count(); ++i) {
279             QVariant val = lst[i];
280             variantToString(val, str);
281             if (i < lst.count() - 1)
282                 str += QLatin1String(", ");
283         }
284         str += QLatin1String("]");
285     } else if (var.type() == QVariant::Map) {
286         Q_ASSERT(!"unsupported state type");
287     } else if (var.type() == QVariant::Hash) {
288         Q_ASSERT(!"unsupported state type");
289     } else {
290         str += var.toString();
291     }
292 }
293
294 void MainWindow::fillStateForFrame()
295 {
296     QVariantMap::const_iterator itr;
297     QVariantMap params;
298
299     if (!m_selectedEvent || m_selectedEvent->state().isEmpty())
300         return;
301
302     const ApiTraceState &state = m_selectedEvent->state();
303     m_ui.stateTreeWidget->clear();
304     params = state.parameters();
305     QList<QTreeWidgetItem *> items;
306     for (itr = params.constBegin(); itr != params.constEnd(); ++itr) {
307         QString key = itr.key();
308         QString val;
309
310         variantToString(itr.value(), val);
311         //qDebug()<<"key = "<<key;
312         //qDebug()<<"val = "<<val;
313         QStringList lst;
314         lst += key;
315         lst += val;
316         items.append(new QTreeWidgetItem((QTreeWidget*)0, lst));
317     }
318     m_ui.stateTreeWidget->insertTopLevelItems(0, items);
319
320     QStringList shaderSources = state.shaderSources();
321     if (shaderSources.isEmpty()) {
322         m_sourcesWidget->setShaders(shaderSources);
323     } else {
324         m_sourcesWidget->setShaders(shaderSources);
325     }
326
327     const QList<ApiTexture> &textures =
328         state.textures();
329
330     m_ui.surfacesTreeWidget->clear();
331     if (textures.isEmpty()) {
332         m_ui.surfacesTab->setDisabled(false);
333     } else {
334         QTreeWidgetItem *textureItem =
335             new QTreeWidgetItem(m_ui.surfacesTreeWidget);
336         m_ui.surfacesTreeWidget->setIconSize(QSize(64, 64));
337         textureItem->setText(0, tr("Textures"));
338         for (int i = 0; i < textures.count(); ++i) {
339             const ApiTexture &texture =
340                 textures[i];
341             QIcon icon(QPixmap::fromImage(texture.thumb()));
342             QTreeWidgetItem *item = new QTreeWidgetItem(textureItem);
343             item->setIcon(0, icon);
344             int width = texture.size().width();
345             int height = texture.size().height();
346             QString descr =
347                 QString::fromLatin1("%1, %2 x %3")
348                 .arg(texture.target())
349                 .arg(width)
350                 .arg(height);
351             item->setText(1, descr);
352
353             item->setData(0, Qt::UserRole,
354                           texture.image());
355         }
356         m_ui.surfacesTab->setEnabled(true);
357     }
358     m_ui.stateDock->show();
359 }
360
361 void MainWindow::showSettings()
362 {
363     SettingsDialog dialog;
364     dialog.setFilterOptions(m_proxyModel->filterOptions());
365
366     if (dialog.exec() == QDialog::Accepted) {
367         m_proxyModel->setFilterOptions(dialog.filterOptions());
368     }
369 }
370
371 void MainWindow::openHelp(const QUrl &url)
372 {
373     QDesktopServices::openUrl(url);
374 }
375
376 void MainWindow::showSurfacesMenu(const QPoint &pos)
377 {
378     QTreeWidget *tree = m_ui.surfacesTreeWidget;
379     QTreeWidgetItem *item = tree->itemAt(pos);
380     if (!item)
381         return;
382
383     QMenu menu(tr("Surfaces"), this);
384     //add needed actions
385     QAction *act = menu.addAction(tr("View Image"));
386     act->setStatusTip(tr("View the currently selected surface"));
387     connect(act, SIGNAL(triggered()),
388             SLOT(showSelectedSurface()));
389
390     menu.exec(tree->viewport()->mapToGlobal(pos));
391 }
392
393 void MainWindow::showSelectedSurface()
394 {
395     QTreeWidgetItem *item =
396         m_ui.surfacesTreeWidget->currentItem();
397
398     if (!item)
399         return;
400
401     QVariant var = item->data(0, Qt::UserRole);
402     m_imageViewer->setImage(var.value<QImage>());
403     m_imageViewer->show();
404     m_imageViewer->raise();
405     m_imageViewer->activateWindow();
406 }
407
408 void MainWindow::initObjects()
409 {
410     m_ui.stateTreeWidget->sortByColumn(0, Qt::AscendingOrder);
411
412     m_sourcesWidget = new ShadersSourceWidget(m_ui.shadersTab);
413     QVBoxLayout *layout = new QVBoxLayout;
414     layout->addWidget(m_sourcesWidget);
415     m_ui.shadersTab->setLayout(layout);
416
417     m_trace = new ApiTrace();
418     m_retracer = new Retracer(this);
419
420     m_vdataInterpreter = new VertexDataInterpreter(this);
421     m_vdataInterpreter->setListWidget(m_ui.vertexDataListWidget);
422     m_vdataInterpreter->setStride(
423         m_ui.vertexStrideSB->value());
424     m_vdataInterpreter->setComponents(
425         m_ui.vertexComponentsSB->value());
426     m_vdataInterpreter->setStartingOffset(
427         m_ui.startingOffsetSB->value());
428     m_vdataInterpreter->setTypeFromString(
429         m_ui.vertexTypeCB->currentText());
430
431     m_imageViewer = new ImageViewer(this);
432
433     m_model = new ApiTraceModel();
434     m_model->setApiTrace(m_trace);
435     m_proxyModel = new ApiTraceFilter();
436     m_proxyModel->setSourceModel(m_model);
437     m_ui.callView->setModel(m_proxyModel);
438     m_ui.callView->setItemDelegate(new ApiCallDelegate);
439     m_ui.callView->resizeColumnToContents(0);
440     m_ui.callView->header()->swapSections(0, 1);
441     m_ui.callView->setColumnWidth(1, 42);
442
443     QToolBar *toolBar = addToolBar(tr("Navigation"));
444     m_filterEdit = new QLineEdit(toolBar);
445     toolBar->addWidget(m_filterEdit);
446
447     m_progressBar = new QProgressBar();
448     m_progressBar->setRange(0, 0);
449     statusBar()->addPermanentWidget(m_progressBar);
450     m_progressBar->hide();
451
452     m_ui.detailsDock->hide();
453     m_ui.vertexDataDock->hide();
454     m_ui.stateDock->hide();
455     setDockOptions(dockOptions() | QMainWindow::ForceTabbedDocks);
456
457     tabifyDockWidget(m_ui.stateDock, m_ui.vertexDataDock);
458
459     m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
460
461     m_ui.detailsWebView->page()->setLinkDelegationPolicy(
462         QWebPage::DelegateExternalLinks);
463 }
464
465 void MainWindow::initConnections()
466 {
467     connect(m_trace, SIGNAL(startedLoadingTrace()),
468             this, SLOT(startedLoadingTrace()));
469     connect(m_trace, SIGNAL(finishedLoadingTrace()),
470             this, SLOT(finishedLoadingTrace()));
471
472     connect(m_retracer, SIGNAL(finished(const QByteArray&)),
473             this, SLOT(replayFinished(const QByteArray&)));
474     connect(m_retracer, SIGNAL(error(const QString&)),
475             this, SLOT(replayError(const QString&)));
476
477     connect(m_ui.vertexInterpretButton, SIGNAL(clicked()),
478             m_vdataInterpreter, SLOT(interpretData()));
479     connect(m_ui.vertexTypeCB, SIGNAL(currentIndexChanged(const QString&)),
480             m_vdataInterpreter, SLOT(setTypeFromString(const QString&)));
481     connect(m_ui.vertexStrideSB, SIGNAL(valueChanged(int)),
482             m_vdataInterpreter, SLOT(setStride(int)));
483     connect(m_ui.vertexComponentsSB, SIGNAL(valueChanged(int)),
484             m_vdataInterpreter, SLOT(setComponents(int)));
485     connect(m_ui.startingOffsetSB, SIGNAL(valueChanged(int)),
486             m_vdataInterpreter, SLOT(setStartingOffset(int)));
487
488
489     connect(m_ui.actionOpen, SIGNAL(triggered()),
490             this, SLOT(openTrace()));
491     connect(m_ui.actionQuit, SIGNAL(triggered()),
492             this, SLOT(close()));
493
494     connect(m_ui.actionReplay, SIGNAL(triggered()),
495             this, SLOT(replayStart()));
496     connect(m_ui.actionStop, SIGNAL(triggered()),
497             this, SLOT(replayStop()));
498     connect(m_ui.actionLookupState, SIGNAL(triggered()),
499             this, SLOT(lookupState()));
500     connect(m_ui.actionOptions, SIGNAL(triggered()),
501             this, SLOT(showSettings()));
502
503     connect(m_ui.callView, SIGNAL(activated(const QModelIndex &)),
504             this, SLOT(callItemSelected(const QModelIndex &)));
505     connect(m_filterEdit, SIGNAL(returnPressed()),
506             this, SLOT(filterTrace()));
507
508     connect(m_ui.surfacesTreeWidget,
509             SIGNAL(customContextMenuRequested(const QPoint &)),
510             SLOT(showSurfacesMenu(const QPoint &)));
511     connect(m_ui.surfacesTreeWidget,
512             SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)),
513             SLOT(showSelectedSurface()));
514
515     connect(m_ui.detailsWebView, SIGNAL(linkClicked(const QUrl&)),
516             this, SLOT(openHelp(const QUrl&)));
517 }
518
519 #include "mainwindow.moc"