]> git.cworth.org Git - apitrace/blob - gui/mainwindow.cpp
Merge branch 'master' into multi-context
[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             QVariantList 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->state().isEmpty()) {
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->calls().first()->index();
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         QVariantList lst = var.toList();
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 QVariantList &lst, const QVariantList &defaultLst, QList<QTreeWidgetItem *> &items)
362 {
363     for (int i = 0; i < lst.count(); ++i) {
364         QString key = QString::number(i);
365         QVariant var = lst[i];
366         QVariant defaultVar;
367         
368         if (i < defaultLst.count()) {
369             defaultVar = defaultLst[i];
370         }
371
372         QTreeWidgetItem *item = variantToItem(key, var, defaultVar);
373         if (item) {
374             items.append(item);
375         }
376     }
377 }
378
379 static bool
380 isVariantDeep(const QVariant &var)
381 {
382     if (var.type() == QVariant::List) {
383         QVariantList lst = var.toList();
384         for (int i = 0; i < lst.count(); ++i) {
385             if (isVariantDeep(lst[i])) {
386                 return true;
387             }
388         }
389         return false;
390     } else if (var.type() == QVariant::Map) {
391         return true;
392     } else if (var.type() == QVariant::Hash) {
393         return true;
394     } else {
395         return false;
396     }
397 }
398
399 static QTreeWidgetItem *
400 variantToItem(const QString &key, const QVariant &var, const QVariant &defaultVar)
401 {
402     if (var == defaultVar) {
403         return NULL;
404     }
405
406     QString val;
407
408     bool deep = isVariantDeep(var);
409     if (!deep) {
410         variantToString(var, val);
411     }
412
413     //qDebug()<<"key = "<<key;
414     //qDebug()<<"val = "<<val;
415     QStringList lst;
416     lst += key;
417     lst += val;
418
419     QTreeWidgetItem *item = new QTreeWidgetItem((QTreeWidgetItem *)0, lst);
420
421     if (deep) {
422         QList<QTreeWidgetItem *> children;
423         if (var.type() == QVariant::Map) {
424             QVariantMap map = var.toMap();
425             QVariantMap defaultMap = defaultVar.toMap();
426             variantMapToItems(map, defaultMap, children);
427         }
428         if (var.type() == QVariant::List) {
429             QVariantList lst = var.toList();
430             QVariantList defaultLst = defaultVar.toList();
431             variantListToItems(lst, defaultLst, children);
432         }
433         item->addChildren(children);
434     }
435
436     return item;
437 }
438
439 void MainWindow::fillStateForFrame()
440 {
441     QVariantMap params;
442
443     if (!m_selectedEvent || m_selectedEvent->state().isEmpty())
444         return;
445
446     if (m_nonDefaultsLookupEvent) {
447         m_ui.nonDefaultsCB->blockSignals(true);
448         m_ui.nonDefaultsCB->setChecked(true);
449         m_ui.nonDefaultsCB->blockSignals(false);
450     }
451
452     bool nonDefaults = m_ui.nonDefaultsCB->isChecked();
453     QVariantMap defaultParams;
454     if (nonDefaults) {
455         ApiTraceState defaultState = m_trace->defaultState();
456         defaultParams = defaultState.parameters();
457     }
458
459     const ApiTraceState &state = m_selectedEvent->state();
460     m_ui.stateTreeWidget->clear();
461     params = state.parameters();
462     QList<QTreeWidgetItem *> items;
463     variantMapToItems(params, defaultParams, items);
464     m_ui.stateTreeWidget->insertTopLevelItems(0, items);
465
466     QMap<QString, QString> shaderSources = state.shaderSources();
467     if (shaderSources.isEmpty()) {
468         m_sourcesWidget->setShaders(shaderSources);
469     } else {
470         m_sourcesWidget->setShaders(shaderSources);
471     }
472
473     const QList<ApiTexture> &textures =
474         state.textures();
475     const QList<ApiFramebuffer> &fbos =
476         state.framebuffers();
477
478     m_ui.surfacesTreeWidget->clear();
479     if (textures.isEmpty() && fbos.isEmpty()) {
480         m_ui.surfacesTab->setDisabled(false);
481     } else {
482         m_ui.surfacesTreeWidget->setIconSize(QSize(64, 64));
483         if (!textures.isEmpty()) {
484             QTreeWidgetItem *textureItem =
485                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
486             textureItem->setText(0, tr("Textures"));
487             if (textures.count() <= 6)
488                 textureItem->setExpanded(true);
489
490             for (int i = 0; i < textures.count(); ++i) {
491                 const ApiTexture &texture =
492                     textures[i];
493                 QIcon icon(QPixmap::fromImage(texture.thumb()));
494                 QTreeWidgetItem *item = new QTreeWidgetItem(textureItem);
495                 item->setIcon(0, icon);
496                 int width = texture.size().width();
497                 int height = texture.size().height();
498                 QString descr =
499                     QString::fromLatin1("%1, %2 x %3")
500                     .arg(texture.label())
501                     .arg(width)
502                     .arg(height);
503                 item->setText(1, descr);
504
505                 item->setData(0, Qt::UserRole,
506                               texture.image());
507             }
508         }
509         if (!fbos.isEmpty()) {
510             QTreeWidgetItem *fboItem =
511                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
512             fboItem->setText(0, tr("Framebuffers"));
513             if (fbos.count() <= 6)
514                 fboItem->setExpanded(true);
515
516             for (int i = 0; i < fbos.count(); ++i) {
517                 const ApiFramebuffer &fbo =
518                     fbos[i];
519                 QIcon icon(QPixmap::fromImage(fbo.thumb()));
520                 QTreeWidgetItem *item = new QTreeWidgetItem(fboItem);
521                 item->setIcon(0, icon);
522                 int width = fbo.size().width();
523                 int height = fbo.size().height();
524                 QString descr =
525                     QString::fromLatin1("%1, %2 x %3")
526                     .arg(fbo.type())
527                     .arg(width)
528                     .arg(height);
529                 item->setText(1, descr);
530
531                 item->setData(0, Qt::UserRole,
532                               fbo.image());
533             }
534         }
535         m_ui.surfacesTab->setEnabled(true);
536     }
537     m_ui.stateDock->show();
538 }
539
540 void MainWindow::showSettings()
541 {
542     SettingsDialog dialog;
543     dialog.setFilterModel(m_proxyModel);
544
545     dialog.exec();
546 }
547
548 void MainWindow::openHelp(const QUrl &url)
549 {
550     QDesktopServices::openUrl(url);
551 }
552
553 void MainWindow::showSurfacesMenu(const QPoint &pos)
554 {
555     QTreeWidget *tree = m_ui.surfacesTreeWidget;
556     QTreeWidgetItem *item = tree->itemAt(pos);
557     if (!item)
558         return;
559
560     QMenu menu(tr("Surfaces"), this);
561     //add needed actions
562     QAction *act = menu.addAction(tr("View Image"));
563     act->setStatusTip(tr("View the currently selected surface"));
564     connect(act, SIGNAL(triggered()),
565             SLOT(showSelectedSurface()));
566
567     menu.exec(tree->viewport()->mapToGlobal(pos));
568 }
569
570 void MainWindow::showSelectedSurface()
571 {
572     QTreeWidgetItem *item =
573         m_ui.surfacesTreeWidget->currentItem();
574
575     if (!item)
576         return;
577
578     QVariant var = item->data(0, Qt::UserRole);
579     QImage img = var.value<QImage>();
580     ImageViewer *viewer = new ImageViewer(this);
581
582     QString title;
583     if (currentCall()) {
584         title = tr("QApiTrace - Surface at %1 (%2)")
585                 .arg(currentCall()->name())
586                 .arg(currentCall()->index());
587     } else {
588         title = tr("QApiTrace - Surface Viewer");
589     }
590     viewer->setWindowTitle(title);
591     viewer->setAttribute(Qt::WA_DeleteOnClose, true);
592     viewer->setImage(img);
593     QRect screenRect = QApplication::desktop()->availableGeometry();
594     viewer->resize(qMin(int(0.75 * screenRect.width()), img.width()) + 40,
595                    qMin(int(0.75 * screenRect.height()), img.height()) + 40);
596     viewer->show();
597     viewer->raise();
598     viewer->activateWindow();
599 }
600
601 void MainWindow::initObjects()
602 {
603     m_ui.stateTreeWidget->sortByColumn(0, Qt::AscendingOrder);
604
605     m_sourcesWidget = new ShadersSourceWidget(m_ui.shadersTab);
606     QVBoxLayout *layout = new QVBoxLayout;
607     layout->addWidget(m_sourcesWidget);
608     m_ui.shadersTab->setLayout(layout);
609
610     m_trace = new ApiTrace();
611     m_retracer = new Retracer(this);
612
613     m_vdataInterpreter = new VertexDataInterpreter(this);
614     m_vdataInterpreter->setListWidget(m_ui.vertexDataListWidget);
615     m_vdataInterpreter->setStride(
616         m_ui.vertexStrideSB->value());
617     m_vdataInterpreter->setComponents(
618         m_ui.vertexComponentsSB->value());
619     m_vdataInterpreter->setStartingOffset(
620         m_ui.startingOffsetSB->value());
621     m_vdataInterpreter->setTypeFromString(
622         m_ui.vertexTypeCB->currentText());
623
624     m_model = new ApiTraceModel();
625     m_model->setApiTrace(m_trace);
626     m_proxyModel = new ApiTraceFilter();
627     m_proxyModel->setSourceModel(m_model);
628     m_ui.callView->setModel(m_proxyModel);
629     m_ui.callView->setItemDelegate(
630         new ApiCallDelegate(m_ui.callView));
631     m_ui.callView->resizeColumnToContents(0);
632     m_ui.callView->header()->swapSections(0, 1);
633     m_ui.callView->setColumnWidth(1, 42);
634     m_ui.callView->setContextMenuPolicy(Qt::CustomContextMenu);
635
636     m_progressBar = new QProgressBar();
637     m_progressBar->setRange(0, 0);
638     statusBar()->addPermanentWidget(m_progressBar);
639     m_progressBar->hide();
640
641     m_argsEditor = new ArgumentsEditor(this);
642
643     m_ui.detailsDock->hide();
644     m_ui.errorsDock->hide();
645     m_ui.vertexDataDock->hide();
646     m_ui.stateDock->hide();
647     setDockOptions(dockOptions() | QMainWindow::ForceTabbedDocks);
648
649     tabifyDockWidget(m_ui.stateDock, m_ui.vertexDataDock);
650     tabifyDockWidget(m_ui.detailsDock, m_ui.errorsDock);
651
652     m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
653
654     m_ui.detailsWebView->page()->setLinkDelegationPolicy(
655         QWebPage::DelegateExternalLinks);
656
657     m_jumpWidget = new JumpWidget(this);
658     m_ui.centralLayout->addWidget(m_jumpWidget);
659     m_jumpWidget->hide();
660
661     m_searchWidget = new SearchWidget(this);
662     m_ui.centralLayout->addWidget(m_searchWidget);
663     m_searchWidget->hide();
664
665     m_traceProcess = new TraceProcess(this);
666 }
667
668 void MainWindow::initConnections()
669 {
670     connect(m_trace, SIGNAL(startedLoadingTrace()),
671             this, SLOT(startedLoadingTrace()));
672     connect(m_trace, SIGNAL(finishedLoadingTrace()),
673             this, SLOT(finishedLoadingTrace()));
674     connect(m_trace, SIGNAL(startedSaving()),
675             this, SLOT(slotStartedSaving()));
676     connect(m_trace, SIGNAL(saved()),
677             this, SLOT(slotSaved()));
678     connect(m_trace, SIGNAL(changed(ApiTraceCall*)),
679             this, SLOT(slotTraceChanged(ApiTraceCall*)));
680
681     connect(m_retracer, SIGNAL(finished(const QString&)),
682             this, SLOT(replayFinished(const QString&)));
683     connect(m_retracer, SIGNAL(error(const QString&)),
684             this, SLOT(replayError(const QString&)));
685     connect(m_retracer, SIGNAL(foundState(const ApiTraceState&)),
686             this, SLOT(replayStateFound(const ApiTraceState&)));
687     connect(m_retracer, SIGNAL(retraceErrors(const QList<RetraceError>&)),
688             this, SLOT(slotRetraceErrors(const QList<RetraceError>&)));
689
690     connect(m_ui.vertexInterpretButton, SIGNAL(clicked()),
691             m_vdataInterpreter, SLOT(interpretData()));
692     connect(m_ui.vertexTypeCB, SIGNAL(currentIndexChanged(const QString&)),
693             m_vdataInterpreter, SLOT(setTypeFromString(const QString&)));
694     connect(m_ui.vertexStrideSB, SIGNAL(valueChanged(int)),
695             m_vdataInterpreter, SLOT(setStride(int)));
696     connect(m_ui.vertexComponentsSB, SIGNAL(valueChanged(int)),
697             m_vdataInterpreter, SLOT(setComponents(int)));
698     connect(m_ui.startingOffsetSB, SIGNAL(valueChanged(int)),
699             m_vdataInterpreter, SLOT(setStartingOffset(int)));
700
701
702     connect(m_ui.actionNew, SIGNAL(triggered()),
703             this, SLOT(createTrace()));
704     connect(m_ui.actionOpen, SIGNAL(triggered()),
705             this, SLOT(openTrace()));
706     connect(m_ui.actionQuit, SIGNAL(triggered()),
707             this, SLOT(close()));
708
709     connect(m_ui.actionFind, SIGNAL(triggered()),
710             this, SLOT(slotSearch()));
711     connect(m_ui.actionGo, SIGNAL(triggered()),
712             this, SLOT(slotGoTo()));
713     connect(m_ui.actionGoFrameStart, SIGNAL(triggered()),
714             this, SLOT(slotGoFrameStart()));
715     connect(m_ui.actionGoFrameEnd, SIGNAL(triggered()),
716             this, SLOT(slotGoFrameEnd()));
717
718     connect(m_ui.actionReplay, SIGNAL(triggered()),
719             this, SLOT(replayStart()));
720     connect(m_ui.actionStop, SIGNAL(triggered()),
721             this, SLOT(replayStop()));
722     connect(m_ui.actionLookupState, SIGNAL(triggered()),
723             this, SLOT(lookupState()));
724     connect(m_ui.actionOptions, SIGNAL(triggered()),
725             this, SLOT(showSettings()));
726
727     connect(m_ui.callView, SIGNAL(activated(const QModelIndex &)),
728             this, SLOT(callItemSelected(const QModelIndex &)));
729     connect(m_ui.callView, SIGNAL(customContextMenuRequested(QPoint)),
730             this, SLOT(customContextMenuRequested(QPoint)));
731
732     connect(m_ui.surfacesTreeWidget,
733             SIGNAL(customContextMenuRequested(const QPoint &)),
734             SLOT(showSurfacesMenu(const QPoint &)));
735     connect(m_ui.surfacesTreeWidget,
736             SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)),
737             SLOT(showSelectedSurface()));
738
739     connect(m_ui.detailsWebView, SIGNAL(linkClicked(const QUrl&)),
740             this, SLOT(openHelp(const QUrl&)));
741
742     connect(m_ui.nonDefaultsCB, SIGNAL(toggled(bool)),
743             this, SLOT(fillState(bool)));
744
745     connect(m_jumpWidget, SIGNAL(jumpTo(int)),
746             SLOT(slotJumpTo(int)));
747
748     connect(m_searchWidget,
749             SIGNAL(searchNext(const QString&, Qt::CaseSensitivity)),
750             SLOT(slotSearchNext(const QString&, Qt::CaseSensitivity)));
751     connect(m_searchWidget,
752             SIGNAL(searchPrev(const QString&, Qt::CaseSensitivity)),
753             SLOT(slotSearchPrev(const QString&, Qt::CaseSensitivity)));
754
755     connect(m_traceProcess, SIGNAL(tracedFile(const QString&)),
756             SLOT(createdTrace(const QString&)));
757     connect(m_traceProcess, SIGNAL(error(const QString&)),
758             SLOT(traceError(const QString&)));
759
760     connect(m_ui.errorsDock, SIGNAL(visibilityChanged(bool)),
761             m_ui.actionShowErrorsDock, SLOT(setChecked(bool)));
762     connect(m_ui.actionShowErrorsDock, SIGNAL(triggered(bool)),
763             m_ui.errorsDock, SLOT(setVisible(bool)));
764     connect(m_ui.errorsTreeWidget,
765             SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
766             this, SLOT(slotErrorSelected(QTreeWidgetItem*)));
767 }
768
769 void MainWindow::replayStateFound(const ApiTraceState &state)
770 {
771     m_stateEvent->setState(state);
772     m_model->stateSetOnEvent(m_stateEvent);
773     if (m_selectedEvent == m_stateEvent ||
774         m_nonDefaultsLookupEvent == m_selectedEvent) {
775         fillStateForFrame();
776     } else {
777         m_ui.stateDock->hide();
778     }
779     m_nonDefaultsLookupEvent = 0;
780 }
781
782 void MainWindow::slotGoTo()
783 {
784     m_searchWidget->hide();
785     m_jumpWidget->show();
786 }
787
788 void MainWindow::slotJumpTo(int callNum)
789 {
790     QModelIndex index = m_proxyModel->callIndex(callNum);
791     if (index.isValid()) {
792         m_ui.callView->setCurrentIndex(index);
793     }
794 }
795
796 void MainWindow::createdTrace(const QString &path)
797 {
798     qDebug()<<"Done tracing "<<path;
799     newTraceFile(path);
800 }
801
802 void MainWindow::traceError(const QString &msg)
803 {
804     QMessageBox::warning(
805             this,
806             tr("Tracing Error"),
807             msg);
808 }
809
810 void MainWindow::slotSearch()
811 {
812     m_jumpWidget->hide();
813     m_searchWidget->show();
814 }
815
816 void MainWindow::slotSearchNext(const QString &str,
817                                 Qt::CaseSensitivity sensitivity)
818 {
819     QModelIndex index = m_ui.callView->currentIndex();
820     ApiTraceEvent *event = 0;
821
822
823     if (!index.isValid()) {
824         index = m_proxyModel->index(0, 0, QModelIndex());
825         if (!index.isValid()) {
826             qDebug()<<"no currently valid index";
827             m_searchWidget->setFound(false);
828             return;
829         }
830     }
831
832     event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
833     ApiTraceCall *call = 0;
834
835     if (event->type() == ApiTraceCall::Call)
836         call = static_cast<ApiTraceCall*>(event);
837     else {
838         Q_ASSERT(event->type() == ApiTraceCall::Frame);
839         ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
840         call = frame->call(0);
841     }
842
843     if (!call) {
844         m_searchWidget->setFound(false);
845         return;
846     }
847     const QList<ApiTraceCall*> &calls = m_trace->calls();
848     int callNum = calls.indexOf(call);
849
850     for (int i = callNum + 1; i < calls.count(); ++i) {
851         ApiTraceCall *testCall = calls[i];
852         QModelIndex index = m_proxyModel->indexForCall(testCall);
853         /* if it's not valid it means that the proxy model has already
854          * filtered it out */
855         if (index.isValid()) {
856             QString txt = testCall->filterText();
857             if (txt.contains(str, sensitivity)) {
858                 m_ui.callView->setCurrentIndex(index);
859                 m_searchWidget->setFound(true);
860                 return;
861             }
862         }
863     }
864     m_searchWidget->setFound(false);
865 }
866
867 void MainWindow::slotSearchPrev(const QString &str,
868                                 Qt::CaseSensitivity sensitivity)
869 {
870     QModelIndex index = m_ui.callView->currentIndex();
871     ApiTraceEvent *event = 0;
872
873
874     if (!index.isValid()) {
875         index = m_proxyModel->index(0, 0, QModelIndex());
876         if (!index.isValid()) {
877             qDebug()<<"no currently valid index";
878             m_searchWidget->setFound(false);
879             return;
880         }
881     }
882
883     event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
884     ApiTraceCall *call = 0;
885
886     if (event->type() == ApiTraceCall::Call)
887         call = static_cast<ApiTraceCall*>(event);
888     else {
889         Q_ASSERT(event->type() == ApiTraceCall::Frame);
890         ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
891         call = frame->call(0);
892     }
893
894     if (!call) {
895         m_searchWidget->setFound(false);
896         return;
897     }
898     const QList<ApiTraceCall*> &calls = m_trace->calls();
899     int callNum = calls.indexOf(call);
900
901     for (int i = callNum - 1; i >= 0; --i) {
902         ApiTraceCall *testCall = calls[i];
903         QModelIndex index = m_proxyModel->indexForCall(testCall);
904         /* if it's not valid it means that the proxy model has already
905          * filtered it out */
906         if (index.isValid()) {
907             QString txt = testCall->filterText();
908             if (txt.contains(str, sensitivity)) {
909                 m_ui.callView->setCurrentIndex(index);
910                 m_searchWidget->setFound(true);
911                 return;
912             }
913         }
914     }
915     m_searchWidget->setFound(false);
916 }
917
918 void MainWindow::fillState(bool nonDefaults)
919 {
920     if (nonDefaults) {
921         ApiTraceState defaultState = m_trace->defaultState();
922         if (defaultState.isEmpty()) {
923             m_ui.nonDefaultsCB->blockSignals(true);
924             m_ui.nonDefaultsCB->setChecked(false);
925             m_ui.nonDefaultsCB->blockSignals(false);
926             int ret = QMessageBox::question(
927                 this, tr("Empty Default State"),
928                 tr("The applcation needs to figure out the "
929                    "default state for the current trace. "
930                    "This only has to be done once and "
931                    "afterwards you will be able to enable "
932                    "displaying of non default state for all calls."
933                    "\nDo you want to lookup the default state now?"),
934                 QMessageBox::Yes | QMessageBox::No);
935             if (ret != QMessageBox::Yes)
936                 return;
937             ApiTraceFrame *firstFrame =
938                 m_trace->frameAt(0);
939             ApiTraceEvent *oldSelected = m_selectedEvent;
940             if (!firstFrame)
941                 return;
942             m_nonDefaultsLookupEvent = m_selectedEvent;
943             m_selectedEvent = firstFrame;
944             lookupState();
945             m_selectedEvent = oldSelected;
946         }
947     }
948     fillStateForFrame();
949 }
950
951 void MainWindow::customContextMenuRequested(QPoint pos)
952 {
953     QMenu menu;
954     QModelIndex index = m_ui.callView->indexAt(pos);
955
956     callItemSelected(index);
957     if (!index.isValid())
958         return;
959
960     ApiTraceEvent *event =
961         index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
962     if (!event || event->type() != ApiTraceEvent::Call)
963         return;
964
965     menu.addAction(QIcon(":/resources/media-record.png"),
966                    tr("Lookup state"), this, SLOT(lookupState()));
967     menu.addAction(tr("Edit"), this, SLOT(editCall()));
968
969     menu.exec(QCursor::pos());
970 }
971
972 void MainWindow::editCall()
973 {
974     if (m_selectedEvent && m_selectedEvent->type() == ApiTraceEvent::Call) {
975         ApiTraceCall *call = static_cast<ApiTraceCall*>(m_selectedEvent);
976         m_argsEditor->setCall(call);
977         m_argsEditor->show();
978     }
979 }
980
981 void MainWindow::slotStartedSaving()
982 {
983     m_progressBar->show();
984     statusBar()->showMessage(
985         tr("Saving to %1").arg(m_trace->fileName()));
986 }
987
988 void MainWindow::slotSaved()
989 {
990     statusBar()->showMessage(
991         tr("Saved to %1").arg(m_trace->fileName()), 2000);
992     m_progressBar->hide();
993 }
994
995 void MainWindow::slotGoFrameStart()
996 {
997     ApiTraceFrame *frame = currentFrame();
998     if (!frame || frame->isEmpty()) {
999         return;
1000     }
1001
1002     QList<ApiTraceCall*>::const_iterator itr;
1003     QList<ApiTraceCall*> calls = frame->calls();
1004
1005     itr = calls.constBegin();
1006     while (itr != calls.constEnd()) {
1007         ApiTraceCall *call = *itr;
1008         QModelIndex idx = m_proxyModel->indexForCall(call);
1009         if (idx.isValid()) {
1010             m_ui.callView->setCurrentIndex(idx);
1011             break;
1012         }
1013         ++itr;
1014     }
1015 }
1016
1017 void MainWindow::slotGoFrameEnd()
1018 {
1019     ApiTraceFrame *frame = currentFrame();
1020     if (!frame || frame->isEmpty()) {
1021         return;
1022     }
1023     QList<ApiTraceCall*>::const_iterator itr;
1024     QList<ApiTraceCall*> calls = frame->calls();
1025
1026     itr = calls.constEnd();
1027     do {
1028         --itr;
1029         ApiTraceCall *call = *itr;
1030         QModelIndex idx = m_proxyModel->indexForCall(call);
1031         if (idx.isValid()) {
1032             m_ui.callView->setCurrentIndex(idx);
1033             break;
1034         }
1035     } while (itr != calls.constBegin());
1036 }
1037
1038 ApiTraceFrame * MainWindow::currentFrame() const
1039 {
1040     if (m_selectedEvent) {
1041         if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
1042             return static_cast<ApiTraceFrame*>(m_selectedEvent);
1043         } else {
1044             Q_ASSERT(m_selectedEvent->type() == ApiTraceEvent::Call);
1045             ApiTraceCall *call = static_cast<ApiTraceCall*>(m_selectedEvent);
1046             return call->parentFrame();
1047         }
1048     }
1049     return NULL;
1050 }
1051
1052 void MainWindow::slotTraceChanged(ApiTraceCall *call)
1053 {
1054     Q_ASSERT(call);
1055     if (call == m_selectedEvent) {
1056         m_ui.detailsWebView->setHtml(call->toHtml());
1057     }
1058 }
1059
1060 void MainWindow::slotRetraceErrors(const QList<RetraceError> &errors)
1061 {
1062     m_ui.errorsTreeWidget->clear();
1063
1064     foreach(RetraceError error, errors) {
1065         ApiTraceCall *call = m_trace->callWithIndex(error.callIndex);
1066         if (!call)
1067             continue;
1068         call->setError(error.message);
1069
1070         QTreeWidgetItem *item =
1071             new QTreeWidgetItem(m_ui.errorsTreeWidget);
1072         item->setData(0, Qt::DisplayRole, error.callIndex);
1073         item->setData(0, Qt::UserRole, QVariant::fromValue(call));
1074         QString type = error.type;
1075         type[0] = type[0].toUpper();
1076         item->setData(1, Qt::DisplayRole, type);
1077         item->setData(2, Qt::DisplayRole, error.message);
1078     }
1079 }
1080
1081 void MainWindow::slotErrorSelected(QTreeWidgetItem *current)
1082 {
1083     if (current) {
1084         ApiTraceCall *call =
1085             current->data(0, Qt::UserRole).value<ApiTraceCall*>();
1086         Q_ASSERT(call);
1087         QModelIndex index = m_proxyModel->indexForCall(call);
1088         if (index.isValid()) {
1089             m_ui.callView->setCurrentIndex(index);
1090         } else {
1091             statusBar()->showMessage(tr("Call has been filtered out."));
1092         }
1093     }
1094 }
1095
1096 ApiTraceCall * MainWindow::currentCall() const
1097 {
1098     if (m_selectedEvent &&
1099         m_selectedEvent->type() == ApiTraceEvent::Call) {
1100         return static_cast<ApiTraceCall*>(m_selectedEvent);
1101     }
1102     return NULL;
1103 }
1104
1105 #include "mainwindow.moc"