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