]> git.cworth.org Git - apitrace/blobdiff - gui/mainwindow.cpp
Plugging some memory leaks
[apitrace] / gui / mainwindow.cpp
index e2d58b6cb55bf0f6d967dbadc2e79f6b83b134e9..1b4de12b6f35c2c7094334b266e73af1d8e22ea4 100644 (file)
@@ -5,17 +5,23 @@
 #include "apicalldelegate.h"
 #include "apitracemodel.h"
 #include "apitracefilter.h"
+#include "argumentseditor.h"
 #include "imageviewer.h"
+#include "jumpwidget.h"
 #include "retracer.h"
+#include "searchwidget.h"
 #include "settingsdialog.h"
 #include "shaderssourcewidget.h"
 #include "tracedialog.h"
+#include "traceprocess.h"
 #include "ui_retracerdialog.h"
 #include "vertexdatainterpreter.h"
 
 #include <QAction>
+#include <QApplication>
 #include <QDebug>
 #include <QDesktopServices>
+#include <QDesktopWidget>
 #include <QDir>
 #include <QFileDialog>
 #include <QLineEdit>
@@ -42,9 +48,20 @@ void MainWindow::createTrace()
 {
     TraceDialog dialog;
 
+    if (!m_traceProcess->canTrace()) {
+        QMessageBox::warning(
+            this,
+            tr("Unsupported"),
+            tr("Current configuration doesn't support tracing."));
+        return;
+    }
+
     if (dialog.exec() == QDialog::Accepted) {
         qDebug()<< "App : " <<dialog.applicationPath();
         qDebug()<< "  Arguments: "<<dialog.arguments();
+        m_traceProcess->setExecutablePath(dialog.applicationPath());
+        m_traceProcess->setArguments(dialog.arguments());
+        m_traceProcess->start();
     }
 }
 
@@ -86,19 +103,20 @@ void MainWindow::callItemSelected(const QModelIndex &index)
         m_ui.detailsDock->show();
         if (call->hasBinaryData()) {
             QByteArray data =
-                call->argValues[call->binaryDataIndex()].toByteArray();
+                call->arguments()[call->binaryDataIndex()].toByteArray();
             m_vdataInterpreter->setData(data);
+            QVariantList args = call->arguments();
 
-            for (int i = 0; i < call->argNames.count(); ++i) {
-                QString name = call->argNames[i];
+            for (int i = 0; i < call->argNames().count(); ++i) {
+                QString name = call->argNames()[i];
                 if (name == QLatin1String("stride")) {
-                    int stride = call->argValues[i].toInt();
+                    int stride = args[i].toInt();
                     m_ui.vertexStrideSB->setValue(stride);
                 } else if (name == QLatin1String("size")) {
-                    int components = call->argValues[i].toInt();
+                    int components = args[i].toInt();
                     m_ui.vertexComponentsSB->setValue(components);
                 } else if (name == QLatin1String("type")) {
-                    QString val = call->argValues[i].toString();
+                    QString val = args[i].toString();
                     int textIndex = m_ui.vertexTypeCB->findText(val);
                     if (textIndex >= 0)
                         m_ui.vertexTypeCB->setCurrentIndex(textIndex);
@@ -121,27 +139,30 @@ void MainWindow::callItemSelected(const QModelIndex &index)
         m_ui.stateDock->hide();
 }
 
-void MainWindow::filterTrace()
-{
-    m_proxyModel->setFilterString(m_filterEdit->text());
-}
-
 void MainWindow::replayStart()
 {
+    if (m_trace->isSaving()) {
+        QMessageBox::warning(
+            this,
+            tr("Trace Saving"),
+            tr("QApiTrace is currently saving the edited trace file. "
+               "Please wait until it finishes and try again."));
+        return;
+    }
     QDialog dlg;
     Ui_RetracerDialog dlgUi;
     dlgUi.setupUi(&dlg);
 
     dlgUi.doubleBufferingCB->setChecked(
         m_retracer->isDoubleBuffered());
-    dlgUi.benchmarkCB->setChecked(
-        m_retracer->isBenchmarking());
+    dlgUi.errorCheckCB->setChecked(
+        !m_retracer->isBenchmarking());
 
     if (dlg.exec() == QDialog::Accepted) {
         m_retracer->setDoubleBuffered(
             dlgUi.doubleBufferingCB->isChecked());
         m_retracer->setBenchmarking(
-            dlgUi.benchmarkCB->isChecked());
+            !dlgUi.errorCheckCB->isChecked());
         replayTrace(false);
     }
 }
@@ -156,10 +177,9 @@ void MainWindow::replayStop()
 
 void MainWindow::newTraceFile(const QString &fileName)
 {
-    m_traceFileName = fileName;
     m_trace->setFileName(fileName);
 
-    if (m_traceFileName.isEmpty()) {
+    if (fileName.isEmpty()) {
         m_ui.actionReplay->setEnabled(false);
         m_ui.actionLookupState->setEnabled(false);
         setWindowTitle(tr("QApiTrace"));
@@ -183,6 +203,11 @@ void MainWindow::replayFinished(const QString &output)
         statusBar()->showMessage(output);
     }
     m_stateEvent = 0;
+    m_ui.actionShowErrorsDock->setEnabled(m_trace->hasErrors());
+    m_ui.errorsDock->setVisible(m_trace->hasErrors());
+    if (!m_trace->hasErrors())
+        m_ui.errorsTreeWidget->clear();
+
     statusBar()->showMessage(
         tr("Replaying finished!"), 2000);
 }
@@ -223,15 +248,15 @@ void MainWindow::finishedLoadingTrace()
 
 void MainWindow::replayTrace(bool dumpState)
 {
-    if (m_traceFileName.isEmpty())
+    if (m_trace->fileName().isEmpty())
         return;
 
-    m_retracer->setFileName(m_traceFileName);
+    m_retracer->setFileName(m_trace->fileName());
     m_retracer->setCaptureState(dumpState);
     if (m_retracer->captureState() && m_selectedEvent) {
         int index = 0;
         if (m_selectedEvent->type() == ApiTraceEvent::Call) {
-            index = static_cast<ApiTraceCall*>(m_selectedEvent)->index;
+            index = static_cast<ApiTraceCall*>(m_selectedEvent)->index();
         } else if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
             ApiTraceFrame *frame =
                 static_cast<ApiTraceFrame*>(m_selectedEvent);
@@ -240,7 +265,7 @@ void MainWindow::replayTrace(bool dumpState)
                 qDebug()<<"tried to get a state for an empty frame";
                 return;
             }
-            index = frame->calls.first()->index;
+            index = frame->calls.first()->index();
         } else {
             qDebug()<<"Unknown event type";
             return;
@@ -267,12 +292,25 @@ void MainWindow::lookupState()
             tr("To inspect the state select an event in the event list."));
         return;
     }
+    if (m_trace->isSaving()) {
+        QMessageBox::warning(
+            this,
+            tr("Trace Saving"),
+            tr("QApiTrace is currently saving the edited trace file. "
+               "Please wait until it finishes and try again."));
+        return;
+    }
     m_stateEvent = m_selectedEvent;
     replayTrace(true);
 }
 
 MainWindow::~MainWindow()
 {
+    delete m_trace;
+    m_trace = 0;
+
+    delete m_proxyModel;
+    delete m_model;
 }
 
 static void
@@ -297,33 +335,126 @@ variantToString(const QVariant &var, QString &str)
     }
 }
 
-void MainWindow::fillStateForFrame()
+static QTreeWidgetItem *
+variantToItem(const QString &key, const QVariant &var, const QVariant &defaultVar);
+
+static void
+variantMapToItems(const QVariantMap &map, const QVariantMap &defaultMap, QList<QTreeWidgetItem *> &items)
 {
     QVariantMap::const_iterator itr;
+    for (itr = map.constBegin(); itr != map.constEnd(); ++itr) {
+        QString key = itr.key();
+        QVariant var = itr.value();
+        QVariant defaultVar = defaultMap[key];
+
+        QTreeWidgetItem *item = variantToItem(key, var, defaultVar);
+        if (item) {
+            items.append(item);
+        }
+    }
+}
+
+static void
+variantListToItems(const QVariantList &lst, const QVariantList &defaultLst, QList<QTreeWidgetItem *> &items)
+{
+    for (int i = 0; i < lst.count(); ++i) {
+        QString key = QString::number(i);
+        QVariant var = lst[i];
+        QVariant defaultVar;
+        
+        if (i < defaultLst.count()) {
+            defaultVar = defaultLst[i];
+        }
+
+        QTreeWidgetItem *item = variantToItem(key, var, defaultVar);
+        if (item) {
+            items.append(item);
+        }
+    }
+}
+
+static bool
+isVariantDeep(const QVariant &var)
+{
+    if (var.type() == QVariant::List) {
+        QVariantList lst = var.toList();
+        for (int i = 0; i < lst.count(); ++i) {
+            if (isVariantDeep(lst[i])) {
+                return true;
+            }
+        }
+        return false;
+    } else if (var.type() == QVariant::Map) {
+        return true;
+    } else if (var.type() == QVariant::Hash) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static QTreeWidgetItem *
+variantToItem(const QString &key, const QVariant &var, const QVariant &defaultVar)
+{
+    if (var == defaultVar) {
+        return NULL;
+    }
+
+    QString val;
+
+    bool deep = isVariantDeep(var);
+    if (!deep) {
+        variantToString(var, val);
+    }
+
+    //qDebug()<<"key = "<<key;
+    //qDebug()<<"val = "<<val;
+    QStringList lst;
+    lst += key;
+    lst += val;
+
+    QTreeWidgetItem *item = new QTreeWidgetItem((QTreeWidgetItem *)0, lst);
+
+    if (deep) {
+        QList<QTreeWidgetItem *> children;
+        if (var.type() == QVariant::Map) {
+            QVariantMap map = var.toMap();
+            QVariantMap defaultMap = defaultVar.toMap();
+            variantMapToItems(map, defaultMap, children);
+        }
+        if (var.type() == QVariant::List) {
+            QVariantList lst = var.toList();
+            QVariantList defaultLst = defaultVar.toList();
+            variantListToItems(lst, defaultLst, children);
+        }
+        item->addChildren(children);
+    }
+
+    return item;
+}
+
+void MainWindow::fillStateForFrame()
+{
     QVariantMap params;
 
     if (!m_selectedEvent || m_selectedEvent->state().isEmpty())
         return;
 
+    bool nonDefaults = m_ui.nonDefaultsCB->isChecked();
+    QVariantMap defaultParams;
+    if (nonDefaults) {
+        ApiTraceState defaultState = m_trace->defaultState();
+        defaultParams = defaultState.parameters();
+    }
+
     const ApiTraceState &state = m_selectedEvent->state();
     m_ui.stateTreeWidget->clear();
     params = state.parameters();
     QList<QTreeWidgetItem *> items;
-    for (itr = params.constBegin(); itr != params.constEnd(); ++itr) {
-        QString key = itr.key();
-        QString val;
-
-        variantToString(itr.value(), val);
-        //qDebug()<<"key = "<<key;
-        //qDebug()<<"val = "<<val;
-        QStringList lst;
-        lst += key;
-        lst += val;
-        items.append(new QTreeWidgetItem((QTreeWidget*)0, lst));
-    }
+    variantMapToItems(params, defaultParams, items);
     m_ui.stateTreeWidget->insertTopLevelItems(0, items);
 
-    QStringList shaderSources = state.shaderSources();
+    QMap<QString, QString> shaderSources = state.shaderSources();
     if (shaderSources.isEmpty()) {
         m_sourcesWidget->setShaders(shaderSources);
     } else {
@@ -344,6 +475,9 @@ void MainWindow::fillStateForFrame()
             QTreeWidgetItem *textureItem =
                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
             textureItem->setText(0, tr("Textures"));
+            if (textures.count() <= 6)
+                textureItem->setExpanded(true);
+
             for (int i = 0; i < textures.count(); ++i) {
                 const ApiTexture &texture =
                     textures[i];
@@ -367,6 +501,9 @@ void MainWindow::fillStateForFrame()
             QTreeWidgetItem *fboItem =
                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
             fboItem->setText(0, tr("Framebuffers"));
+            if (fbos.count() <= 6)
+                fboItem->setExpanded(true);
+
             for (int i = 0; i < fbos.count(); ++i) {
                 const ApiFramebuffer &fbo =
                     fbos[i];
@@ -394,11 +531,9 @@ void MainWindow::fillStateForFrame()
 void MainWindow::showSettings()
 {
     SettingsDialog dialog;
-    dialog.setFilterOptions(m_proxyModel->filterOptions());
+    dialog.setFilterModel(m_proxyModel);
 
-    if (dialog.exec() == QDialog::Accepted) {
-        m_proxyModel->setFilterOptions(dialog.filterOptions());
-    }
+    dialog.exec();
 }
 
 void MainWindow::openHelp(const QUrl &url)
@@ -432,10 +567,26 @@ void MainWindow::showSelectedSurface()
         return;
 
     QVariant var = item->data(0, Qt::UserRole);
-    m_imageViewer->setImage(var.value<QImage>());
-    m_imageViewer->show();
-    m_imageViewer->raise();
-    m_imageViewer->activateWindow();
+    QImage img = var.value<QImage>();
+    ImageViewer *viewer = new ImageViewer(this);
+
+    QString title;
+    if (currentCall()) {
+        title = tr("QApiTrace - Surface at %1 (%2)")
+                .arg(currentCall()->name())
+                .arg(currentCall()->index());
+    } else {
+        title = tr("QApiTrace - Surface Viewer");
+    }
+    viewer->setWindowTitle(title);
+    viewer->setAttribute(Qt::WA_DeleteOnClose, true);
+    viewer->setImage(img);
+    QRect screenRect = QApplication::desktop()->availableGeometry();
+    viewer->resize(qMin(int(0.75 * screenRect.width()), img.width()) + 40,
+                   qMin(int(0.75 * screenRect.height()), img.height()) + 40);
+    viewer->show();
+    viewer->raise();
+    viewer->activateWindow();
 }
 
 void MainWindow::initObjects()
@@ -461,8 +612,6 @@ void MainWindow::initObjects()
     m_vdataInterpreter->setTypeFromString(
         m_ui.vertexTypeCB->currentText());
 
-    m_imageViewer = new ImageViewer(this);
-
     m_model = new ApiTraceModel();
     m_model->setApiTrace(m_trace);
     m_proxyModel = new ApiTraceFilter();
@@ -472,27 +621,38 @@ void MainWindow::initObjects()
     m_ui.callView->resizeColumnToContents(0);
     m_ui.callView->header()->swapSections(0, 1);
     m_ui.callView->setColumnWidth(1, 42);
-
-    QToolBar *toolBar = addToolBar(tr("Navigation"));
-    m_filterEdit = new QLineEdit(toolBar);
-    toolBar->addWidget(m_filterEdit);
+    m_ui.callView->setContextMenuPolicy(Qt::CustomContextMenu);
 
     m_progressBar = new QProgressBar();
     m_progressBar->setRange(0, 0);
     statusBar()->addPermanentWidget(m_progressBar);
     m_progressBar->hide();
 
+    m_argsEditor = new ArgumentsEditor(this);
+
     m_ui.detailsDock->hide();
+    m_ui.errorsDock->hide();
     m_ui.vertexDataDock->hide();
     m_ui.stateDock->hide();
     setDockOptions(dockOptions() | QMainWindow::ForceTabbedDocks);
 
     tabifyDockWidget(m_ui.stateDock, m_ui.vertexDataDock);
+    tabifyDockWidget(m_ui.detailsDock, m_ui.errorsDock);
 
     m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
 
     m_ui.detailsWebView->page()->setLinkDelegationPolicy(
         QWebPage::DelegateExternalLinks);
+
+    m_jumpWidget = new JumpWidget(this);
+    m_ui.centralLayout->addWidget(m_jumpWidget);
+    m_jumpWidget->hide();
+
+    m_searchWidget = new SearchWidget(this);
+    m_ui.centralLayout->addWidget(m_searchWidget);
+    m_searchWidget->hide();
+
+    m_traceProcess = new TraceProcess(this);
 }
 
 void MainWindow::initConnections()
@@ -501,6 +661,12 @@ void MainWindow::initConnections()
             this, SLOT(startedLoadingTrace()));
     connect(m_trace, SIGNAL(finishedLoadingTrace()),
             this, SLOT(finishedLoadingTrace()));
+    connect(m_trace, SIGNAL(startedSaving()),
+            this, SLOT(slotStartedSaving()));
+    connect(m_trace, SIGNAL(saved()),
+            this, SLOT(slotSaved()));
+    connect(m_trace, SIGNAL(changed(ApiTraceCall*)),
+            this, SLOT(slotTraceChanged(ApiTraceCall*)));
 
     connect(m_retracer, SIGNAL(finished(const QString&)),
             this, SLOT(replayFinished(const QString&)));
@@ -508,6 +674,8 @@ void MainWindow::initConnections()
             this, SLOT(replayError(const QString&)));
     connect(m_retracer, SIGNAL(foundState(const ApiTraceState&)),
             this, SLOT(replayStateFound(const ApiTraceState&)));
+    connect(m_retracer, SIGNAL(retraceErrors(const QList<RetraceError>&)),
+            this, SLOT(slotRetraceErrors(const QList<RetraceError>&)));
 
     connect(m_ui.vertexInterpretButton, SIGNAL(clicked()),
             m_vdataInterpreter, SLOT(interpretData()));
@@ -528,6 +696,15 @@ void MainWindow::initConnections()
     connect(m_ui.actionQuit, SIGNAL(triggered()),
             this, SLOT(close()));
 
+    connect(m_ui.actionFind, SIGNAL(triggered()),
+            this, SLOT(slotSearch()));
+    connect(m_ui.actionGo, SIGNAL(triggered()),
+            this, SLOT(slotGoTo()));
+    connect(m_ui.actionGoFrameStart, SIGNAL(triggered()),
+            this, SLOT(slotGoFrameStart()));
+    connect(m_ui.actionGoFrameEnd, SIGNAL(triggered()),
+            this, SLOT(slotGoFrameEnd()));
+
     connect(m_ui.actionReplay, SIGNAL(triggered()),
             this, SLOT(replayStart()));
     connect(m_ui.actionStop, SIGNAL(triggered()),
@@ -539,8 +716,8 @@ void MainWindow::initConnections()
 
     connect(m_ui.callView, SIGNAL(activated(const QModelIndex &)),
             this, SLOT(callItemSelected(const QModelIndex &)));
-    connect(m_filterEdit, SIGNAL(returnPressed()),
-            this, SLOT(filterTrace()));
+    connect(m_ui.callView, SIGNAL(customContextMenuRequested(QPoint)),
+            this, SLOT(customContextMenuRequested(QPoint)));
 
     connect(m_ui.surfacesTreeWidget,
             SIGNAL(customContextMenuRequested(const QPoint &)),
@@ -551,6 +728,31 @@ void MainWindow::initConnections()
 
     connect(m_ui.detailsWebView, SIGNAL(linkClicked(const QUrl&)),
             this, SLOT(openHelp(const QUrl&)));
+
+    connect(m_ui.nonDefaultsCB, SIGNAL(toggled(bool)),
+            this, SLOT(fillState(bool)));
+
+    connect(m_jumpWidget, SIGNAL(jumpTo(int)),
+            SLOT(slotJumpTo(int)));
+
+    connect(m_searchWidget,
+            SIGNAL(searchNext(const QString&, Qt::CaseSensitivity)),
+            SLOT(slotSearchNext(const QString&, Qt::CaseSensitivity)));
+    connect(m_searchWidget,
+            SIGNAL(searchPrev(const QString&, Qt::CaseSensitivity)),
+            SLOT(slotSearchPrev(const QString&, Qt::CaseSensitivity)));
+
+    connect(m_traceProcess, SIGNAL(tracedFile(const QString&)),
+            SLOT(createdTrace(const QString&)));
+    connect(m_traceProcess, SIGNAL(error(const QString&)),
+            SLOT(traceError(const QString&)));
+
+    connect(m_ui.errorsDock, SIGNAL(visibilityChanged(bool)),
+            m_ui.actionShowErrorsDock, SLOT(setChecked(bool)));
+    connect(m_ui.actionShowErrorsDock, SIGNAL(triggered(bool)),
+            m_ui.errorsDock, SLOT(setVisible(bool)));
+    connect(m_ui.errorsTreeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
+            this, SLOT(slotErrorSelected(QTreeWidgetItem*)));
 }
 
 void MainWindow::replayStateFound(const ApiTraceState &state)
@@ -564,4 +766,324 @@ void MainWindow::replayStateFound(const ApiTraceState &state)
     }
 }
 
+void MainWindow::slotGoTo()
+{
+    m_searchWidget->hide();
+    m_jumpWidget->show();
+}
+
+void MainWindow::slotJumpTo(int callNum)
+{
+    QModelIndex index = m_proxyModel->callIndex(callNum);
+    if (index.isValid()) {
+        m_ui.callView->setCurrentIndex(index);
+    }
+}
+
+void MainWindow::createdTrace(const QString &path)
+{
+    qDebug()<<"Done tracing "<<path;
+    newTraceFile(path);
+}
+
+void MainWindow::traceError(const QString &msg)
+{
+    QMessageBox::warning(
+            this,
+            tr("Tracing Error"),
+            msg);
+}
+
+void MainWindow::slotSearch()
+{
+    m_jumpWidget->hide();
+    m_searchWidget->show();
+}
+
+void MainWindow::slotSearchNext(const QString &str,
+                                Qt::CaseSensitivity sensitivity)
+{
+    QModelIndex index = m_ui.callView->currentIndex();
+    ApiTraceEvent *event = 0;
+
+
+    if (!index.isValid()) {
+        index = m_proxyModel->index(0, 0, QModelIndex());
+        if (!index.isValid()) {
+            qDebug()<<"no currently valid index";
+            m_searchWidget->setFound(false);
+            return;
+        }
+    }
+
+    event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
+    ApiTraceCall *call = 0;
+
+    if (event->type() == ApiTraceCall::Call)
+        call = static_cast<ApiTraceCall*>(event);
+    else {
+        Q_ASSERT(event->type() == ApiTraceCall::Frame);
+        ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
+        call = frame->calls.value(0);
+    }
+
+    if (!call) {
+        m_searchWidget->setFound(false);
+        return;
+    }
+    const QList<ApiTraceCall*> &calls = m_trace->calls();
+    int callNum = calls.indexOf(call);
+
+    for (int i = callNum + 1; i < calls.count(); ++i) {
+        ApiTraceCall *testCall = calls[i];
+        QModelIndex index = m_proxyModel->indexForCall(testCall);
+        /* if it's not valid it means that the proxy model has already
+         * filtered it out */
+        if (index.isValid()) {
+            QString txt = testCall->filterText();
+            if (txt.contains(str, sensitivity)) {
+                m_ui.callView->setCurrentIndex(index);
+                m_searchWidget->setFound(true);
+                return;
+            }
+        }
+    }
+    m_searchWidget->setFound(false);
+}
+
+void MainWindow::slotSearchPrev(const QString &str,
+                                Qt::CaseSensitivity sensitivity)
+{
+    QModelIndex index = m_ui.callView->currentIndex();
+    ApiTraceEvent *event = 0;
+
+
+    if (!index.isValid()) {
+        index = m_proxyModel->index(0, 0, QModelIndex());
+        if (!index.isValid()) {
+            qDebug()<<"no currently valid index";
+            m_searchWidget->setFound(false);
+            return;
+        }
+    }
+
+    event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
+    ApiTraceCall *call = 0;
+
+    if (event->type() == ApiTraceCall::Call)
+        call = static_cast<ApiTraceCall*>(event);
+    else {
+        Q_ASSERT(event->type() == ApiTraceCall::Frame);
+        ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
+        call = frame->calls.value(0);
+    }
+
+    if (!call) {
+        m_searchWidget->setFound(false);
+        return;
+    }
+    const QList<ApiTraceCall*> &calls = m_trace->calls();
+    int callNum = calls.indexOf(call);
+
+    for (int i = callNum - 1; i >= 0; --i) {
+        ApiTraceCall *testCall = calls[i];
+        QModelIndex index = m_proxyModel->indexForCall(testCall);
+        /* if it's not valid it means that the proxy model has already
+         * filtered it out */
+        if (index.isValid()) {
+            QString txt = testCall->filterText();
+            if (txt.contains(str, sensitivity)) {
+                m_ui.callView->setCurrentIndex(index);
+                m_searchWidget->setFound(true);
+                return;
+            }
+        }
+    }
+    m_searchWidget->setFound(false);
+}
+
+void MainWindow::fillState(bool nonDefaults)
+{
+    if (nonDefaults) {
+        ApiTraceState defaultState = m_trace->defaultState();
+        if (defaultState.isEmpty()) {
+            m_ui.nonDefaultsCB->blockSignals(true);
+            m_ui.nonDefaultsCB->setChecked(false);
+            m_ui.nonDefaultsCB->blockSignals(false);
+            int ret = QMessageBox::question(
+                this, tr("Empty Default State"),
+                tr("The applcation needs to figure out the "
+                   "default state for the current trace. "
+                   "This only has to be done once and "
+                   "afterwards you will be able to enable "
+                   "displaying of non default state for all calls."
+                   "\nDo you want to lookup the default state now?"),
+                QMessageBox::Yes | QMessageBox::No);
+            if (ret != QMessageBox::Yes)
+                return;
+            ApiTraceFrame *firstFrame =
+                m_trace->frameAt(0);
+            ApiTraceEvent *oldSelected = m_selectedEvent;
+            if (!firstFrame)
+                return;
+            m_selectedEvent = firstFrame;
+            lookupState();
+            m_selectedEvent = oldSelected;
+        }
+    }
+    fillStateForFrame();
+}
+
+void MainWindow::customContextMenuRequested(QPoint pos)
+{
+    QMenu menu;
+    QModelIndex index = m_ui.callView->indexAt(pos);
+
+    callItemSelected(index);
+    if (!index.isValid())
+        return;
+
+    ApiTraceEvent *event =
+        index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
+    if (!event || event->type() != ApiTraceEvent::Call)
+        return;
+
+    menu.addAction(QIcon(":/resources/media-record.png"),
+                   tr("Lookup state"), this, SLOT(lookupState()));
+    menu.addAction(tr("Edit"), this, SLOT(editCall()));
+
+    menu.exec(QCursor::pos());
+}
+
+void MainWindow::editCall()
+{
+    if (m_selectedEvent && m_selectedEvent->type() == ApiTraceEvent::Call) {
+        ApiTraceCall *call = static_cast<ApiTraceCall*>(m_selectedEvent);
+        m_argsEditor->setCall(call);
+        m_argsEditor->show();
+    }
+}
+
+void MainWindow::slotStartedSaving()
+{
+    m_progressBar->show();
+    statusBar()->showMessage(
+        tr("Saving to %1").arg(m_trace->fileName()));
+}
+
+void MainWindow::slotSaved()
+{
+    statusBar()->showMessage(
+        tr("Saved to %1").arg(m_trace->fileName()), 2000);
+    m_progressBar->hide();
+}
+
+void MainWindow::slotGoFrameStart()
+{
+    ApiTraceFrame *frame = currentFrame();
+    if (!frame || frame->calls.isEmpty()) {
+        return;
+    }
+
+    QList<ApiTraceCall*>::const_iterator itr;
+
+    itr = frame->calls.constBegin();
+    while (itr != frame->calls.constEnd()) {
+        ApiTraceCall *call = *itr;
+        QModelIndex idx = m_proxyModel->indexForCall(call);
+        if (idx.isValid()) {
+            m_ui.callView->setCurrentIndex(idx);
+            break;
+        }
+        ++itr;
+    }
+}
+
+void MainWindow::slotGoFrameEnd()
+{
+    ApiTraceFrame *frame = currentFrame();
+    if (!frame || frame->calls.isEmpty()) {
+        return;
+    }
+    QList<ApiTraceCall*>::const_iterator itr;
+
+    itr = frame->calls.constEnd();
+    do {
+        --itr;
+        ApiTraceCall *call = *itr;
+        QModelIndex idx = m_proxyModel->indexForCall(call);
+        if (idx.isValid()) {
+            m_ui.callView->setCurrentIndex(idx);
+            break;
+        }
+    } while (itr != frame->calls.constBegin());
+}
+
+ApiTraceFrame * MainWindow::currentFrame() const
+{
+    if (m_selectedEvent) {
+        if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
+            return static_cast<ApiTraceFrame*>(m_selectedEvent);
+        } else {
+            Q_ASSERT(m_selectedEvent->type() == ApiTraceEvent::Call);
+            ApiTraceCall *call = static_cast<ApiTraceCall*>(m_selectedEvent);
+            return call->parentFrame();
+        }
+    }
+    return NULL;
+}
+
+void MainWindow::slotTraceChanged(ApiTraceCall *call)
+{
+    Q_ASSERT(call);
+    if (call == m_selectedEvent) {
+        m_ui.detailsWebView->setHtml(call->toHtml());
+    }
+}
+
+void MainWindow::slotRetraceErrors(const QList<RetraceError> &errors)
+{
+    m_ui.errorsTreeWidget->clear();
+
+    foreach(RetraceError error, errors) {
+        ApiTraceCall *call = m_trace->callWithIndex(error.callIndex);
+        if (!call)
+            continue;
+        call->setError(error.message);
+
+        QTreeWidgetItem *item =
+            new QTreeWidgetItem(m_ui.errorsTreeWidget);
+        item->setData(0, Qt::DisplayRole, error.callIndex);
+        item->setData(0, Qt::UserRole, QVariant::fromValue(call));
+        QString type = error.type;
+        type[0] = type[0].toUpper();
+        item->setData(1, Qt::DisplayRole, type);
+        item->setData(2, Qt::DisplayRole, error.message);
+    }
+}
+
+void MainWindow::slotErrorSelected(QTreeWidgetItem *current)
+{
+    if (current) {
+        ApiTraceCall *call =
+            current->data(0, Qt::UserRole).value<ApiTraceCall*>();
+        Q_ASSERT(call);
+        QModelIndex index = m_proxyModel->indexForCall(call);
+        if (index.isValid()) {
+            m_ui.callView->setCurrentIndex(index);
+        } else {
+            statusBar()->showMessage(tr("Call has been filtered out."));
+        }
+    }
+}
+
+ApiTraceCall * MainWindow::currentCall() const
+{
+    if (m_selectedEvent &&
+        m_selectedEvent->type() == ApiTraceEvent::Call) {
+        return static_cast<ApiTraceCall*>(m_selectedEvent);
+    }
+    return NULL;
+}
+
 #include "mainwindow.moc"