]> git.cworth.org Git - apitrace/blob - gui/mainwindow.cpp
Allow to specify an initial call no in the qapitrace command line.
[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 "argumentseditor.h"
9 #include "imageviewer.h"
10 #include "jumpwidget.h"
11 #include "retracer.h"
12 #include "searchwidget.h"
13 #include "settingsdialog.h"
14 #include "shaderssourcewidget.h"
15 #include "tracedialog.h"
16 #include "traceprocess.h"
17 #include "ui_retracerdialog.h"
18 #include "vertexdatainterpreter.h"
19
20 #include <QAction>
21 #include <QApplication>
22 #include <QDebug>
23 #include <QDesktopServices>
24 #include <QDesktopWidget>
25 #include <QDir>
26 #include <QFileDialog>
27 #include <QLineEdit>
28 #include <QMessageBox>
29 #include <QProgressBar>
30 #include <QToolBar>
31 #include <QUrl>
32 #include <QVBoxLayout>
33 #include <QWebPage>
34 #include <QWebView>
35
36
37 MainWindow::MainWindow()
38     : QMainWindow(),
39       m_initalCallNum(-1),
40       m_selectedEvent(0),
41       m_stateEvent(0),
42       m_nonDefaultsLookupEvent(0)
43 {
44     m_ui.setupUi(this);
45     initObjects();
46     initConnections();
47 }
48
49 void MainWindow::createTrace()
50 {
51     if (!m_traceProcess->canTrace()) {
52         QMessageBox::warning(
53             this,
54             tr("Unsupported"),
55             tr("Current configuration doesn't support tracing."));
56         return;
57     }
58
59     TraceDialog dialog;
60     if (dialog.exec() == QDialog::Accepted) {
61         qDebug()<< "App : " <<dialog.applicationPath();
62         qDebug()<< "  Arguments: "<<dialog.arguments();
63         m_traceProcess->setExecutablePath(dialog.applicationPath());
64         m_traceProcess->setArguments(dialog.arguments());
65         m_traceProcess->start();
66     }
67 }
68
69 void MainWindow::openTrace()
70 {
71     QString fileName =
72             QFileDialog::getOpenFileName(
73                 this,
74                 tr("Open Trace"),
75                 QDir::homePath(),
76                 tr("Trace Files (*.trace)"));
77
78     if (!fileName.isEmpty() && QFile::exists(fileName)) {
79         newTraceFile(fileName);
80     }
81 }
82
83 void MainWindow::loadTrace(const QString &fileName, int callNum)
84 {
85     if (!QFile::exists(fileName)) {
86         QMessageBox::warning(this, tr("File Missing"),
87                              tr("File '%1' doesn't exist.").arg(fileName));
88         return;
89     }
90
91     m_initalCallNum = callNum;
92     newTraceFile(fileName);
93 }
94
95 void MainWindow::callItemSelected(const QModelIndex &index)
96 {
97     ApiTraceEvent *event =
98         index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
99
100     if (event && event->type() == ApiTraceEvent::Call) {
101         ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
102         m_ui.detailsDock->setWindowTitle(
103             tr("Details View. Frame %1, Call %2")
104             .arg(call->parentFrame() ? call->parentFrame()->number : 0)
105             .arg(call->index()));
106         m_ui.detailsWebView->setHtml(call->toHtml());
107         m_ui.detailsDock->show();
108         if (call->hasBinaryData()) {
109             QByteArray data =
110                 call->arguments()[call->binaryDataIndex()].toByteArray();
111             m_vdataInterpreter->setData(data);
112
113             QVector<QVariant> args = call->arguments();
114             for (int i = 0; i < call->argNames().count(); ++i) {
115                 QString name = call->argNames()[i];
116                 if (name == QLatin1String("stride")) {
117                     int stride = args[i].toInt();
118                     m_ui.vertexStrideSB->setValue(stride);
119                 } else if (name == QLatin1String("size")) {
120                     int components = args[i].toInt();
121                     m_ui.vertexComponentsSB->setValue(components);
122                 } else if (name == QLatin1String("type")) {
123                     QString val = args[i].toString();
124                     int textIndex = m_ui.vertexTypeCB->findText(val);
125                     if (textIndex >= 0) {
126                         m_ui.vertexTypeCB->setCurrentIndex(textIndex);
127                     }
128                 }
129             }
130         }
131         m_ui.vertexDataDock->setVisible(call->hasBinaryData());
132         m_selectedEvent = call;
133     } else {
134         if (event && event->type() == ApiTraceEvent::Frame) {
135             m_selectedEvent = static_cast<ApiTraceFrame*>(event);
136         } else {
137             m_selectedEvent = 0;
138         }
139         m_ui.detailsDock->hide();
140         m_ui.vertexDataDock->hide();
141     }
142     if (m_selectedEvent && m_selectedEvent->hasState()) {
143         fillStateForFrame();
144     } else {
145         m_ui.stateDock->hide();
146     }
147 }
148
149 void MainWindow::replayStart()
150 {
151     if (m_trace->isSaving()) {
152         QMessageBox::warning(
153             this,
154             tr("Trace Saving"),
155             tr("QApiTrace is currently saving the edited trace file. "
156                "Please wait until it finishes and try again."));
157         return;
158     }
159     QDialog dlg;
160     Ui_RetracerDialog dlgUi;
161     dlgUi.setupUi(&dlg);
162
163     dlgUi.doubleBufferingCB->setChecked(
164         m_retracer->isDoubleBuffered());
165     dlgUi.errorCheckCB->setChecked(
166         !m_retracer->isBenchmarking());
167
168     if (dlg.exec() == QDialog::Accepted) {
169         m_retracer->setDoubleBuffered(
170             dlgUi.doubleBufferingCB->isChecked());
171         m_retracer->setBenchmarking(
172             !dlgUi.errorCheckCB->isChecked());
173         replayTrace(false);
174     }
175 }
176
177 void MainWindow::replayStop()
178 {
179     m_retracer->quit();
180     m_ui.actionStop->setEnabled(false);
181     m_ui.actionReplay->setEnabled(true);
182     m_ui.actionLookupState->setEnabled(true);
183 }
184
185 void MainWindow::newTraceFile(const QString &fileName)
186 {
187     qDebug()<< "Loading  : " <<fileName;
188
189     m_progressBar->setValue(0);
190     m_trace->setFileName(fileName);
191
192     if (fileName.isEmpty()) {
193         m_ui.actionReplay->setEnabled(false);
194         m_ui.actionLookupState->setEnabled(false);
195         setWindowTitle(tr("QApiTrace"));
196     } else {
197         QFileInfo info(fileName);
198         m_ui.actionReplay->setEnabled(true);
199         m_ui.actionLookupState->setEnabled(true);
200         setWindowTitle(
201             tr("QApiTrace - %1").arg(info.fileName()));
202     }
203 }
204
205 void MainWindow::replayFinished(const QString &output)
206 {
207     m_ui.actionStop->setEnabled(false);
208     m_ui.actionReplay->setEnabled(true);
209     m_ui.actionLookupState->setEnabled(true);
210
211     m_progressBar->hide();
212     if (output.length() < 80) {
213         statusBar()->showMessage(output);
214     }
215     m_stateEvent = 0;
216     m_ui.actionShowErrorsDock->setEnabled(m_trace->hasErrors());
217     m_ui.errorsDock->setVisible(m_trace->hasErrors());
218     if (!m_trace->hasErrors()) {
219         m_ui.errorsTreeWidget->clear();
220     }
221
222     statusBar()->showMessage(
223         tr("Replaying finished!"), 2000);
224 }
225
226 void MainWindow::replayError(const QString &message)
227 {
228     m_ui.actionStop->setEnabled(false);
229     m_ui.actionReplay->setEnabled(true);
230     m_ui.actionLookupState->setEnabled(true);
231     m_stateEvent = 0;
232     m_nonDefaultsLookupEvent = 0;
233
234     m_progressBar->hide();
235     statusBar()->showMessage(
236         tr("Replaying unsuccessful."), 2000);
237     QMessageBox::warning(
238         this, tr("Replay Failed"), message);
239 }
240
241 void MainWindow::startedLoadingTrace()
242 {
243     Q_ASSERT(m_trace);
244     m_progressBar->show();
245     QFileInfo info(m_trace->fileName());
246     statusBar()->showMessage(
247         tr("Loading %1...").arg(info.fileName()));
248 }
249
250 void MainWindow::finishedLoadingTrace()
251 {
252     m_progressBar->hide();
253     if (!m_trace) {
254         return;
255     }
256     QFileInfo info(m_trace->fileName());
257     statusBar()->showMessage(
258         tr("Loaded %1").arg(info.fileName()), 3000);
259     if (m_initalCallNum >= 0) {
260         m_trace->findCallIndex(m_initalCallNum);
261         m_initalCallNum = -1;
262     }
263 }
264
265 void MainWindow::replayTrace(bool dumpState)
266 {
267     if (m_trace->fileName().isEmpty()) {
268         return;
269     }
270
271     m_retracer->setFileName(m_trace->fileName());
272     m_retracer->setCaptureState(dumpState);
273     if (m_retracer->captureState() && m_selectedEvent) {
274         int index = 0;
275         if (m_selectedEvent->type() == ApiTraceEvent::Call) {
276             index = static_cast<ApiTraceCall*>(m_selectedEvent)->index();
277         } else if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
278             ApiTraceFrame *frame =
279                 static_cast<ApiTraceFrame*>(m_selectedEvent);
280             if (frame->isEmpty()) {
281                 //XXX i guess we could still get the current state
282                 qDebug()<<"tried to get a state for an empty frame";
283                 return;
284             }
285             index = frame->lastCallIndex();
286         } else {
287             qDebug()<<"Unknown event type";
288             return;
289         }
290         m_retracer->setCaptureAtCallNumber(index);
291     }
292     m_retracer->start();
293
294     m_ui.actionStop->setEnabled(true);
295     m_progressBar->show();
296     if (dumpState) {
297         statusBar()->showMessage(
298             tr("Looking up the state..."));
299     } else {
300         statusBar()->showMessage(
301             tr("Replaying the trace file..."));
302     }
303 }
304
305 void MainWindow::lookupState()
306 {
307     if (!m_selectedEvent) {
308         QMessageBox::warning(
309             this, tr("Unknown Event"),
310             tr("To inspect the state select an event in the event list."));
311         return;
312     }
313     if (m_trace->isSaving()) {
314         QMessageBox::warning(
315             this,
316             tr("Trace Saving"),
317             tr("QApiTrace is currently saving the edited trace file. "
318                "Please wait until it finishes and try again."));
319         return;
320     }
321     m_stateEvent = m_selectedEvent;
322     replayTrace(true);
323 }
324
325 MainWindow::~MainWindow()
326 {
327     delete m_trace;
328     m_trace = 0;
329
330     delete m_proxyModel;
331     delete m_model;
332 }
333
334 static void
335 variantToString(const QVariant &var, QString &str)
336 {
337     if (var.type() == QVariant::List) {
338         QVector<QVariant> lst = var.toList().toVector();
339         str += QLatin1String("[");
340         for (int i = 0; i < lst.count(); ++i) {
341             QVariant val = lst[i];
342             variantToString(val, str);
343             if (i < lst.count() - 1)
344                 str += QLatin1String(", ");
345         }
346         str += QLatin1String("]");
347     } else if (var.type() == QVariant::Map) {
348         Q_ASSERT(!"unsupported state type");
349     } else if (var.type() == QVariant::Hash) {
350         Q_ASSERT(!"unsupported state type");
351     } else {
352         str += var.toString();
353     }
354 }
355
356 static QTreeWidgetItem *
357 variantToItem(const QString &key, const QVariant &var,
358               const QVariant &defaultVar);
359
360 static void
361 variantMapToItems(const QVariantMap &map, const QVariantMap &defaultMap,
362                   QList<QTreeWidgetItem *> &items)
363 {
364     QVariantMap::const_iterator itr;
365     for (itr = map.constBegin(); itr != map.constEnd(); ++itr) {
366         QString key = itr.key();
367         QVariant var = itr.value();
368         QVariant defaultVar = defaultMap[key];
369
370         QTreeWidgetItem *item = variantToItem(key, var, defaultVar);
371         if (item) {
372             items.append(item);
373         }
374     }
375 }
376
377 static void
378 variantListToItems(const QVector<QVariant> &lst,
379                    const QVector<QVariant> &defaultLst,
380                    QList<QTreeWidgetItem *> &items)
381 {
382     for (int i = 0; i < lst.count(); ++i) {
383         QString key = QString::number(i);
384         QVariant var = lst[i];
385         QVariant defaultVar;
386         
387         if (i < defaultLst.count()) {
388             defaultVar = defaultLst[i];
389         }
390
391         QTreeWidgetItem *item = variantToItem(key, var, defaultVar);
392         if (item) {
393             items.append(item);
394         }
395     }
396 }
397
398 static bool
399 isVariantDeep(const QVariant &var)
400 {
401     if (var.type() == QVariant::List) {
402         QVector<QVariant> lst = var.toList().toVector();
403         for (int i = 0; i < lst.count(); ++i) {
404             if (isVariantDeep(lst[i])) {
405                 return true;
406             }
407         }
408         return false;
409     } else if (var.type() == QVariant::Map) {
410         return true;
411     } else if (var.type() == QVariant::Hash) {
412         return true;
413     } else {
414         return false;
415     }
416 }
417
418 static QTreeWidgetItem *
419 variantToItem(const QString &key, const QVariant &var,
420               const QVariant &defaultVar)
421 {
422     if (var == defaultVar) {
423         return NULL;
424     }
425
426     QString val;
427
428     bool deep = isVariantDeep(var);
429     if (!deep) {
430         variantToString(var, val);
431     }
432
433     //qDebug()<<"key = "<<key;
434     //qDebug()<<"val = "<<val;
435     QStringList lst;
436     lst += key;
437     lst += val;
438
439     QTreeWidgetItem *item = new QTreeWidgetItem((QTreeWidgetItem *)0, lst);
440
441     if (deep) {
442         QList<QTreeWidgetItem *> children;
443         if (var.type() == QVariant::Map) {
444             QVariantMap map = var.toMap();
445             QVariantMap defaultMap = defaultVar.toMap();
446             variantMapToItems(map, defaultMap, children);
447         }
448         if (var.type() == QVariant::List) {
449             QVector<QVariant> lst = var.toList().toVector();
450             QVector<QVariant> defaultLst = defaultVar.toList().toVector();
451             variantListToItems(lst, defaultLst, children);
452         }
453         item->addChildren(children);
454     }
455
456     return item;
457 }
458
459 static void addSurfaceItem(const ApiSurface &surface,
460                            const QString &label,
461                            QTreeWidgetItem *parent,
462                            QTreeWidget *tree)
463 {
464     QIcon icon(QPixmap::fromImage(surface.thumb()));
465     QTreeWidgetItem *item = new QTreeWidgetItem(parent);
466     item->setIcon(0, icon);
467
468     int width = surface.size().width();
469     int height = surface.size().height();
470     QString descr =
471         QString::fromLatin1("%1, %2, %3 x %4")
472         .arg(label)
473         .arg(surface.formatName())
474         .arg(width)
475         .arg(height);
476
477     //item->setText(1, descr);
478     QLabel *l = new QLabel(descr, tree);
479     l->setWordWrap(true);
480     tree->setItemWidget(item, 1, l);
481
482     item->setData(0, Qt::UserRole, surface.image());
483 }
484
485 void MainWindow::fillStateForFrame()
486 {
487     if (!m_selectedEvent || !m_selectedEvent->hasState()) {
488         return;
489     }
490
491     if (m_nonDefaultsLookupEvent) {
492         m_ui.nonDefaultsCB->blockSignals(true);
493         m_ui.nonDefaultsCB->setChecked(true);
494         m_ui.nonDefaultsCB->blockSignals(false);
495     }
496
497     bool nonDefaults = m_ui.nonDefaultsCB->isChecked();
498     QVariantMap defaultParams;
499     if (nonDefaults) {
500         ApiTraceState defaultState = m_trace->defaultState();
501         defaultParams = defaultState.parameters();
502     }
503
504     const ApiTraceState &state = *m_selectedEvent->state();
505     m_ui.stateTreeWidget->clear();
506     QList<QTreeWidgetItem *> items;
507     variantMapToItems(state.parameters(), defaultParams, items);
508     m_ui.stateTreeWidget->insertTopLevelItems(0, items);
509
510     QMap<QString, QString> shaderSources = state.shaderSources();
511     if (shaderSources.isEmpty()) {
512         m_sourcesWidget->setShaders(shaderSources);
513     } else {
514         m_sourcesWidget->setShaders(shaderSources);
515     }
516
517     m_ui.uniformsTreeWidget->clear();
518     QList<QTreeWidgetItem *> uniformsItems;
519     variantMapToItems(state.uniforms(), QVariantMap(), uniformsItems);
520     m_ui.uniformsTreeWidget->insertTopLevelItems(0, uniformsItems);
521
522     const QList<ApiTexture> &textures =
523         state.textures();
524     const QList<ApiFramebuffer> &fbos =
525         state.framebuffers();
526
527     m_ui.surfacesTreeWidget->clear();
528     if (textures.isEmpty() && fbos.isEmpty()) {
529         m_ui.surfacesTab->setDisabled(false);
530     } else {
531         m_ui.surfacesTreeWidget->setIconSize(QSize(64, 64));
532         if (!textures.isEmpty()) {
533             QTreeWidgetItem *textureItem =
534                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
535             textureItem->setText(0, tr("Textures"));
536             if (textures.count() <= 6) {
537                 textureItem->setExpanded(true);
538             }
539
540             for (int i = 0; i < textures.count(); ++i) {
541                 const ApiTexture &texture =
542                     textures[i];
543                 addSurfaceItem(texture, texture.label(),
544                                textureItem,
545                                m_ui.surfacesTreeWidget);
546             }
547         }
548         if (!fbos.isEmpty()) {
549             QTreeWidgetItem *fboItem =
550                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
551             fboItem->setText(0, tr("Framebuffers"));
552             if (fbos.count() <= 6) {
553                 fboItem->setExpanded(true);
554             }
555
556             for (int i = 0; i < fbos.count(); ++i) {
557                 const ApiFramebuffer &fbo =
558                     fbos[i];
559                 addSurfaceItem(fbo, fbo.type(),
560                                fboItem,
561                                m_ui.surfacesTreeWidget);
562             }
563         }
564         m_ui.surfacesTab->setEnabled(true);
565     }
566     m_ui.stateDock->show();
567 }
568
569 void MainWindow::showSettings()
570 {
571     SettingsDialog dialog;
572     dialog.setFilterModel(m_proxyModel);
573
574     dialog.exec();
575 }
576
577 void MainWindow::openHelp(const QUrl &url)
578 {
579     QDesktopServices::openUrl(url);
580 }
581
582 void MainWindow::showSurfacesMenu(const QPoint &pos)
583 {
584     QTreeWidget *tree = m_ui.surfacesTreeWidget;
585     QTreeWidgetItem *item = tree->itemAt(pos);
586     if (!item) {
587         return;
588     }
589
590     QMenu menu(tr("Surfaces"), this);
591
592     QAction *act = menu.addAction(tr("View Image"));
593     act->setStatusTip(tr("View the currently selected surface"));
594     connect(act, SIGNAL(triggered()),
595             SLOT(showSelectedSurface()));
596
597     act = menu.addAction(tr("Save Image"));
598     act->setStatusTip(tr("Save the currently selected surface"));
599     connect(act, SIGNAL(triggered()),
600             SLOT(saveSelectedSurface()));
601
602     menu.exec(tree->viewport()->mapToGlobal(pos));
603 }
604
605 void MainWindow::showSelectedSurface()
606 {
607     QTreeWidgetItem *item =
608         m_ui.surfacesTreeWidget->currentItem();
609
610     if (!item) {
611         return;
612     }
613
614     ImageViewer *viewer = new ImageViewer(this);
615
616     QString title;
617     if (selectedCall()) {
618         title = tr("QApiTrace - Surface at %1 (%2)")
619                 .arg(selectedCall()->name())
620                 .arg(selectedCall()->index());
621     } else {
622         title = tr("QApiTrace - Surface Viewer");
623     }
624     viewer->setWindowTitle(title);
625
626     viewer->setAttribute(Qt::WA_DeleteOnClose, true);
627
628     QVariant var = item->data(0, Qt::UserRole);
629     QImage img = var.value<QImage>();
630     viewer->setImage(img);
631
632     viewer->show();
633     viewer->raise();
634     viewer->activateWindow();
635 }
636
637 void MainWindow::initObjects()
638 {
639     m_ui.stateTreeWidget->sortByColumn(0, Qt::AscendingOrder);
640     m_ui.uniformsTreeWidget->sortByColumn(0, Qt::AscendingOrder);
641
642     m_sourcesWidget = new ShadersSourceWidget(m_ui.shadersTab);
643     QVBoxLayout *layout = new QVBoxLayout;
644     layout->addWidget(m_sourcesWidget);
645     m_ui.shadersTab->setLayout(layout);
646
647     m_trace = new ApiTrace();
648     m_retracer = new Retracer(this);
649
650     m_vdataInterpreter = new VertexDataInterpreter(this);
651     m_vdataInterpreter->setListWidget(m_ui.vertexDataListWidget);
652     m_vdataInterpreter->setStride(
653         m_ui.vertexStrideSB->value());
654     m_vdataInterpreter->setComponents(
655         m_ui.vertexComponentsSB->value());
656     m_vdataInterpreter->setStartingOffset(
657         m_ui.startingOffsetSB->value());
658     m_vdataInterpreter->setTypeFromString(
659         m_ui.vertexTypeCB->currentText());
660
661     m_model = new ApiTraceModel();
662     m_model->setApiTrace(m_trace);
663     m_proxyModel = new ApiTraceFilter();
664     m_proxyModel->setSourceModel(m_model);
665     m_ui.callView->setModel(m_proxyModel);
666     m_ui.callView->setItemDelegate(
667         new ApiCallDelegate(m_ui.callView));
668     m_ui.callView->resizeColumnToContents(0);
669     m_ui.callView->header()->swapSections(0, 1);
670     m_ui.callView->setColumnWidth(1, 42);
671     m_ui.callView->setContextMenuPolicy(Qt::CustomContextMenu);
672
673     m_progressBar = new QProgressBar();
674     m_progressBar->setRange(0, 100);
675     statusBar()->addPermanentWidget(m_progressBar);
676     m_progressBar->hide();
677
678     m_argsEditor = new ArgumentsEditor(this);
679
680     m_ui.detailsDock->hide();
681     m_ui.errorsDock->hide();
682     m_ui.vertexDataDock->hide();
683     m_ui.stateDock->hide();
684     setDockOptions(dockOptions() | QMainWindow::ForceTabbedDocks);
685
686     tabifyDockWidget(m_ui.stateDock, m_ui.vertexDataDock);
687     tabifyDockWidget(m_ui.detailsDock, m_ui.errorsDock);
688
689     m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
690
691     m_ui.detailsWebView->page()->setLinkDelegationPolicy(
692         QWebPage::DelegateExternalLinks);
693
694     m_jumpWidget = new JumpWidget(this);
695     m_ui.centralLayout->addWidget(m_jumpWidget);
696     m_jumpWidget->hide();
697
698     m_searchWidget = new SearchWidget(this);
699     m_ui.centralLayout->addWidget(m_searchWidget);
700     m_searchWidget->hide();
701
702     m_traceProcess = new TraceProcess(this);
703 }
704
705 void MainWindow::initConnections()
706 {
707     connect(m_trace, SIGNAL(startedLoadingTrace()),
708             this, SLOT(startedLoadingTrace()));
709     connect(m_trace, SIGNAL(loaded(int)),
710             this, SLOT(loadProgess(int)));
711     connect(m_trace, SIGNAL(finishedLoadingTrace()),
712             this, SLOT(finishedLoadingTrace()));
713     connect(m_trace, SIGNAL(startedSaving()),
714             this, SLOT(slotStartedSaving()));
715     connect(m_trace, SIGNAL(saved()),
716             this, SLOT(slotSaved()));
717     connect(m_trace, SIGNAL(changed(ApiTraceCall*)),
718             this, SLOT(slotTraceChanged(ApiTraceCall*)));
719     connect(m_trace, SIGNAL(findResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)),
720             this, SLOT(slotSearchResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)));
721     connect(m_trace, SIGNAL(foundFrameStart(ApiTraceFrame*)),
722             this, SLOT(slotFoundFrameStart(ApiTraceFrame*)));
723     connect(m_trace, SIGNAL(foundFrameEnd(ApiTraceFrame*)),
724             this, SLOT(slotFoundFrameEnd(ApiTraceFrame*)));
725     connect(m_trace, SIGNAL(foundCallIndex(ApiTraceCall*)),
726             this, SLOT(slotJumpToResult(ApiTraceCall*)));
727
728     connect(m_retracer, SIGNAL(finished(const QString&)),
729             this, SLOT(replayFinished(const QString&)));
730     connect(m_retracer, SIGNAL(error(const QString&)),
731             this, SLOT(replayError(const QString&)));
732     connect(m_retracer, SIGNAL(foundState(ApiTraceState*)),
733             this, SLOT(replayStateFound(ApiTraceState*)));
734     connect(m_retracer, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
735             this, SLOT(slotRetraceErrors(const QList<ApiTraceError>&)));
736
737     connect(m_ui.vertexInterpretButton, SIGNAL(clicked()),
738             m_vdataInterpreter, SLOT(interpretData()));
739     connect(m_ui.vertexTypeCB, SIGNAL(currentIndexChanged(const QString&)),
740             m_vdataInterpreter, SLOT(setTypeFromString(const QString&)));
741     connect(m_ui.vertexStrideSB, SIGNAL(valueChanged(int)),
742             m_vdataInterpreter, SLOT(setStride(int)));
743     connect(m_ui.vertexComponentsSB, SIGNAL(valueChanged(int)),
744             m_vdataInterpreter, SLOT(setComponents(int)));
745     connect(m_ui.startingOffsetSB, SIGNAL(valueChanged(int)),
746             m_vdataInterpreter, SLOT(setStartingOffset(int)));
747
748
749     connect(m_ui.actionNew, SIGNAL(triggered()),
750             this, SLOT(createTrace()));
751     connect(m_ui.actionOpen, SIGNAL(triggered()),
752             this, SLOT(openTrace()));
753     connect(m_ui.actionQuit, SIGNAL(triggered()),
754             this, SLOT(close()));
755
756     connect(m_ui.actionFind, SIGNAL(triggered()),
757             this, SLOT(slotSearch()));
758     connect(m_ui.actionGo, SIGNAL(triggered()),
759             this, SLOT(slotGoTo()));
760     connect(m_ui.actionGoFrameStart, SIGNAL(triggered()),
761             this, SLOT(slotGoFrameStart()));
762     connect(m_ui.actionGoFrameEnd, SIGNAL(triggered()),
763             this, SLOT(slotGoFrameEnd()));
764
765     connect(m_ui.actionReplay, SIGNAL(triggered()),
766             this, SLOT(replayStart()));
767     connect(m_ui.actionStop, SIGNAL(triggered()),
768             this, SLOT(replayStop()));
769     connect(m_ui.actionLookupState, SIGNAL(triggered()),
770             this, SLOT(lookupState()));
771     connect(m_ui.actionOptions, SIGNAL(triggered()),
772             this, SLOT(showSettings()));
773
774     connect(m_ui.callView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
775             this, SLOT(callItemSelected(const QModelIndex &)));
776     connect(m_ui.callView, SIGNAL(customContextMenuRequested(QPoint)),
777             this, SLOT(customContextMenuRequested(QPoint)));
778
779     connect(m_ui.surfacesTreeWidget,
780             SIGNAL(customContextMenuRequested(const QPoint &)),
781             SLOT(showSurfacesMenu(const QPoint &)));
782     connect(m_ui.surfacesTreeWidget,
783             SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)),
784             SLOT(showSelectedSurface()));
785
786     connect(m_ui.detailsWebView, SIGNAL(linkClicked(const QUrl&)),
787             this, SLOT(openHelp(const QUrl&)));
788
789     connect(m_ui.nonDefaultsCB, SIGNAL(toggled(bool)),
790             this, SLOT(fillState(bool)));
791
792     connect(m_jumpWidget, SIGNAL(jumpTo(int)),
793             SLOT(slotJumpTo(int)));
794
795     connect(m_searchWidget,
796             SIGNAL(searchNext(const QString&, Qt::CaseSensitivity)),
797             SLOT(slotSearchNext(const QString&, Qt::CaseSensitivity)));
798     connect(m_searchWidget,
799             SIGNAL(searchPrev(const QString&, Qt::CaseSensitivity)),
800             SLOT(slotSearchPrev(const QString&, Qt::CaseSensitivity)));
801
802     connect(m_traceProcess, SIGNAL(tracedFile(const QString&)),
803             SLOT(createdTrace(const QString&)));
804     connect(m_traceProcess, SIGNAL(error(const QString&)),
805             SLOT(traceError(const QString&)));
806
807     connect(m_ui.errorsDock, SIGNAL(visibilityChanged(bool)),
808             m_ui.actionShowErrorsDock, SLOT(setChecked(bool)));
809     connect(m_ui.actionShowErrorsDock, SIGNAL(triggered(bool)),
810             m_ui.errorsDock, SLOT(setVisible(bool)));
811     connect(m_ui.errorsTreeWidget,
812             SIGNAL(itemActivated(QTreeWidgetItem*, int)),
813             this, SLOT(slotErrorSelected(QTreeWidgetItem*)));
814 }
815
816 void MainWindow::replayStateFound(ApiTraceState *state)
817 {
818     m_stateEvent->setState(state);
819     m_model->stateSetOnEvent(m_stateEvent);
820     if (m_selectedEvent == m_stateEvent ||
821         m_nonDefaultsLookupEvent == m_selectedEvent) {
822         fillStateForFrame();
823     } else {
824         m_ui.stateDock->hide();
825     }
826     m_nonDefaultsLookupEvent = 0;
827 }
828
829 void MainWindow::slotGoTo()
830 {
831     m_searchWidget->hide();
832     m_jumpWidget->show();
833 }
834
835 void MainWindow::slotJumpTo(int callNum)
836 {
837     m_trace->findCallIndex(callNum);
838 }
839
840 void MainWindow::createdTrace(const QString &path)
841 {
842     qDebug()<<"Done tracing "<<path;
843     newTraceFile(path);
844 }
845
846 void MainWindow::traceError(const QString &msg)
847 {
848     QMessageBox::warning(
849             this,
850             tr("Tracing Error"),
851             msg);
852 }
853
854 void MainWindow::slotSearch()
855 {
856     m_jumpWidget->hide();
857     m_searchWidget->show();
858 }
859
860 void MainWindow::slotSearchNext(const QString &str,
861                                 Qt::CaseSensitivity sensitivity)
862 {
863     ApiTraceCall *call = currentCall();
864     ApiTraceFrame *frame = currentFrame();
865
866     Q_ASSERT(call || frame);
867     if (!frame) {
868         frame = call->parentFrame();
869     }
870     Q_ASSERT(frame);
871
872     m_trace->findNext(frame, call, str, sensitivity);
873 }
874
875 void MainWindow::slotSearchPrev(const QString &str,
876                                 Qt::CaseSensitivity sensitivity)
877 {
878     ApiTraceCall *call = currentCall();
879     ApiTraceFrame *frame = currentFrame();
880
881     Q_ASSERT(call || frame);
882     if (!frame) {
883         frame = call->parentFrame();
884     }
885     Q_ASSERT(frame);
886
887     m_trace->findPrev(frame, call, str, sensitivity);
888 }
889
890 void MainWindow::fillState(bool nonDefaults)
891 {
892     if (nonDefaults) {
893         ApiTraceState defaultState = m_trace->defaultState();
894         if (defaultState.isEmpty()) {
895             m_ui.nonDefaultsCB->blockSignals(true);
896             m_ui.nonDefaultsCB->setChecked(false);
897             m_ui.nonDefaultsCB->blockSignals(false);
898             ApiTraceFrame *firstFrame =
899                 m_trace->frameAt(0);
900             if (!firstFrame) {
901                 return;
902             }
903             if (!firstFrame->isLoaded()) {
904                 m_trace->loadFrame(firstFrame);
905                 return;
906             }
907             ApiTraceCall *firstCall = firstFrame->calls().first();
908             ApiTraceEvent *oldSelected = m_selectedEvent;
909             m_nonDefaultsLookupEvent = m_selectedEvent;
910             m_selectedEvent = firstCall;
911             lookupState();
912             m_selectedEvent = oldSelected;
913         }
914     }
915     fillStateForFrame();
916 }
917
918 void MainWindow::customContextMenuRequested(QPoint pos)
919 {
920     QModelIndex index = m_ui.callView->indexAt(pos);
921
922     callItemSelected(index);
923     if (!index.isValid()) {
924         return;
925     }
926
927     ApiTraceEvent *event =
928         index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
929     if (!event) {
930         return;
931     }
932
933     QMenu menu;
934     menu.addAction(QIcon(":/resources/media-record.png"),
935                    tr("Lookup state"), this, SLOT(lookupState()));
936     if (event->type() == ApiTraceEvent::Call) {
937         menu.addAction(tr("Edit"), this, SLOT(editCall()));
938     }
939
940     menu.exec(QCursor::pos());
941 }
942
943 void MainWindow::editCall()
944 {
945     if (m_selectedEvent && m_selectedEvent->type() == ApiTraceEvent::Call) {
946         ApiTraceCall *call = static_cast<ApiTraceCall*>(m_selectedEvent);
947         m_argsEditor->setCall(call);
948         m_argsEditor->show();
949     }
950 }
951
952 void MainWindow::slotStartedSaving()
953 {
954     m_progressBar->show();
955     statusBar()->showMessage(
956         tr("Saving to %1").arg(m_trace->fileName()));
957 }
958
959 void MainWindow::slotSaved()
960 {
961     statusBar()->showMessage(
962         tr("Saved to %1").arg(m_trace->fileName()), 2000);
963     m_progressBar->hide();
964 }
965
966 void MainWindow::slotGoFrameStart()
967 {
968     ApiTraceFrame *frame = currentFrame();
969     ApiTraceCall *call = currentCall();
970
971     if (!frame && call) {
972         frame = call->parentFrame();
973     }
974
975     m_trace->findFrameStart(frame);
976 }
977
978 void MainWindow::slotGoFrameEnd()
979 {
980     ApiTraceFrame *frame = currentFrame();
981     ApiTraceCall *call = currentCall();
982
983     if (!frame && call) {
984         frame = call->parentFrame();
985     }
986
987     m_trace->findFrameEnd(frame);
988 }
989
990 ApiTraceFrame * MainWindow::selectedFrame() const
991 {
992     if (m_selectedEvent) {
993         if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
994             return static_cast<ApiTraceFrame*>(m_selectedEvent);
995         } else {
996             Q_ASSERT(m_selectedEvent->type() == ApiTraceEvent::Call);
997             ApiTraceCall *call = static_cast<ApiTraceCall*>(m_selectedEvent);
998             return call->parentFrame();
999         }
1000     }
1001     return NULL;
1002 }
1003
1004 void MainWindow::slotTraceChanged(ApiTraceCall *call)
1005 {
1006     Q_ASSERT(call);
1007     if (call == m_selectedEvent) {
1008         m_ui.detailsWebView->setHtml(call->toHtml());
1009     }
1010 }
1011
1012 void MainWindow::slotRetraceErrors(const QList<ApiTraceError> &errors)
1013 {
1014     m_ui.errorsTreeWidget->clear();
1015
1016     foreach(ApiTraceError error, errors) {
1017         m_trace->setCallError(error);
1018
1019         QTreeWidgetItem *item =
1020             new QTreeWidgetItem(m_ui.errorsTreeWidget);
1021         item->setData(0, Qt::DisplayRole, error.callIndex);
1022         item->setData(0, Qt::UserRole, error.callIndex);
1023         QString type = error.type;
1024         type[0] = type[0].toUpper();
1025         item->setData(1, Qt::DisplayRole, type);
1026         item->setData(2, Qt::DisplayRole, error.message);
1027     }
1028 }
1029
1030 void MainWindow::slotErrorSelected(QTreeWidgetItem *current)
1031 {
1032     if (current) {
1033         int callIndex =
1034             current->data(0, Qt::UserRole).toInt();
1035         m_trace->findCallIndex(callIndex);
1036     }
1037 }
1038
1039 ApiTraceCall * MainWindow::selectedCall() const
1040 {
1041     if (m_selectedEvent &&
1042         m_selectedEvent->type() == ApiTraceEvent::Call) {
1043         return static_cast<ApiTraceCall*>(m_selectedEvent);
1044     }
1045     return NULL;
1046 }
1047
1048 void MainWindow::saveSelectedSurface()
1049 {
1050     QTreeWidgetItem *item =
1051         m_ui.surfacesTreeWidget->currentItem();
1052
1053     if (!item || !m_trace) {
1054         return;
1055     }
1056
1057     QVariant var = item->data(0, Qt::UserRole);
1058     QImage img = var.value<QImage>();
1059
1060     QString imageIndex;
1061     if (selectedCall()) {
1062         imageIndex = tr("_call_%1")
1063                      .arg(selectedCall()->index());
1064     } else if (selectedFrame()) {
1065         ApiTraceCall *firstCall = selectedFrame()->call(0);
1066         if (firstCall) {
1067             imageIndex = tr("_frame_%1")
1068                          .arg(firstCall->index());
1069         } else {
1070             qDebug()<<"unknown frame number";
1071             imageIndex = tr("_frame_%1")
1072                          .arg(firstCall->index());
1073         }
1074     }
1075
1076     //which of the surfaces are we saving
1077     QTreeWidgetItem *parent = item->parent();
1078     int parentIndex =
1079         m_ui.surfacesTreeWidget->indexOfTopLevelItem(parent);
1080     if (parentIndex < 0) {
1081         parentIndex = 0;
1082     }
1083     int childIndex = 0;
1084     if (parent) {
1085         childIndex = parent->indexOfChild(item);
1086     } else {
1087         childIndex = m_ui.surfacesTreeWidget->indexOfTopLevelItem(item);
1088     }
1089
1090
1091     QString fileName =
1092         tr("%1%2-%3_%4.png")
1093         .arg(m_trace->fileName())
1094         .arg(imageIndex)
1095         .arg(parentIndex)
1096         .arg(childIndex);
1097     //qDebug()<<"save "<<fileName;
1098     img.save(fileName, "PNG");
1099     statusBar()->showMessage( tr("Saved '%1'").arg(fileName), 5000);
1100 }
1101
1102 void MainWindow::loadProgess(int percent)
1103 {
1104     m_progressBar->setValue(percent);
1105 }
1106
1107 void MainWindow::slotSearchResult(const ApiTrace::SearchRequest &request,
1108                                   ApiTrace::SearchResult result,
1109                                   ApiTraceCall *call)
1110 {
1111     switch (result) {
1112     case ApiTrace::SearchResult_NotFound:
1113         m_searchWidget->setFound(false);
1114         break;
1115     case ApiTrace::SearchResult_Found: {
1116         QModelIndex index = m_proxyModel->indexForCall(call);
1117
1118         if (index.isValid()) {
1119             m_ui.callView->setCurrentIndex(index);
1120             m_searchWidget->setFound(true);
1121         } else {
1122             //call is filtered out, so continue searching but from the
1123             // filtered call
1124             if (!call) {
1125                 qDebug()<<"Error: search success with no call";
1126                 return;
1127             }
1128 //            qDebug()<<"filtered! search from "<<call->searchText()
1129 //                   <<", call idx = "<<call->index();
1130
1131             if (request.direction == ApiTrace::SearchRequest::Next) {
1132                 m_trace->findNext(call->parentFrame(), call,
1133                                   request.text, request.cs);
1134             } else {
1135                 m_trace->findNext(call->parentFrame(), call,
1136                                   request.text, request.cs);
1137             }
1138         }
1139     }
1140         break;
1141     case ApiTrace::SearchResult_Wrapped:
1142         m_searchWidget->setFound(false);
1143         break;
1144     }
1145 }
1146
1147 ApiTraceFrame * MainWindow::currentFrame() const
1148 {
1149     QModelIndex index = m_ui.callView->currentIndex();
1150     ApiTraceEvent *event = 0;
1151
1152     if (!index.isValid()) {
1153         index = m_proxyModel->index(0, 0, QModelIndex());
1154         if (!index.isValid()) {
1155             qDebug()<<"no currently valid index";
1156             return 0;
1157         }
1158     }
1159
1160     event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
1161     Q_ASSERT(event);
1162     if (!event) {
1163         return 0;
1164     }
1165
1166     ApiTraceFrame *frame = 0;
1167     if (event->type() == ApiTraceCall::Frame) {
1168         frame = static_cast<ApiTraceFrame*>(event);
1169     }
1170     return frame;
1171 }
1172
1173 ApiTraceCall * MainWindow::currentCall() const
1174 {
1175     QModelIndex index = m_ui.callView->currentIndex();
1176     ApiTraceEvent *event = 0;
1177
1178     if (!index.isValid()) {
1179         index = m_proxyModel->index(0, 0, QModelIndex());
1180         if (!index.isValid()) {
1181             qDebug()<<"no currently valid index";
1182             return 0;
1183         }
1184     }
1185
1186     event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
1187     Q_ASSERT(event);
1188     if (!event) {
1189         return 0;
1190     }
1191
1192     ApiTraceCall *call = 0;
1193     if (event->type() == ApiTraceCall::Call) {
1194         call = static_cast<ApiTraceCall*>(event);
1195     }
1196
1197     return call;
1198
1199 }
1200
1201 void MainWindow::slotFoundFrameStart(ApiTraceFrame *frame)
1202 {
1203     Q_ASSERT(frame->isLoaded());
1204     if (!frame || frame->isEmpty()) {
1205         return;
1206     }
1207
1208     QVector<ApiTraceCall*>::const_iterator itr;
1209     QVector<ApiTraceCall*> calls = frame->calls();
1210
1211     itr = calls.constBegin();
1212     while (itr != calls.constEnd()) {
1213         ApiTraceCall *call = *itr;
1214         QModelIndex idx = m_proxyModel->indexForCall(call);
1215         if (idx.isValid()) {
1216             m_ui.callView->setCurrentIndex(idx);
1217             break;
1218         }
1219         ++itr;
1220     }
1221 }
1222
1223 void MainWindow::slotFoundFrameEnd(ApiTraceFrame *frame)
1224 {
1225     Q_ASSERT(frame->isLoaded());
1226     if (!frame || frame->isEmpty()) {
1227         return;
1228     }
1229     QVector<ApiTraceCall*>::const_iterator itr;
1230     QVector<ApiTraceCall*> calls = frame->calls();
1231
1232     itr = calls.constEnd();
1233     do {
1234         --itr;
1235         ApiTraceCall *call = *itr;
1236         QModelIndex idx = m_proxyModel->indexForCall(call);
1237         if (idx.isValid()) {
1238             m_ui.callView->setCurrentIndex(idx);
1239             break;
1240         }
1241     } while (itr != calls.constBegin());
1242 }
1243
1244 void MainWindow::slotJumpToResult(ApiTraceCall *call)
1245 {
1246     QModelIndex index = m_proxyModel->indexForCall(call);
1247     if (index.isValid()) {
1248         m_ui.callView->setCurrentIndex(index);
1249     } else {
1250         statusBar()->showMessage(tr("Call has been filtered out."));
1251     }
1252 }
1253
1254 #include "mainwindow.moc"