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