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