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