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