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