]> git.notmuchmail.org Git - apitrace/blobdiff - gui/mainwindow.cpp
Cleanup the ApiTraceFrame a bit.
[apitrace] / gui / mainwindow.cpp
index 4edf1faf1b8f3425221bf345b5c29e881d4744bf..441697518e247cbf04a55ab1f76917b847964b4d 100644 (file)
 #include "vertexdatainterpreter.h"
 
 #include <QAction>
+#include <QApplication>
 #include <QDebug>
 #include <QDesktopServices>
+#include <QDesktopWidget>
 #include <QDir>
 #include <QFileDialog>
 #include <QLineEdit>
 #include <QMessageBox>
 #include <QProgressBar>
-#include <QShortcut>
 #include <QToolBar>
 #include <QUrl>
 #include <QVBoxLayout>
@@ -140,20 +141,28 @@ void MainWindow::callItemSelected(const QModelIndex &index)
 
 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);
     }
 }
@@ -194,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);
 }
@@ -246,12 +260,12 @@ void MainWindow::replayTrace(bool dumpState)
         } else if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
             ApiTraceFrame *frame =
                 static_cast<ApiTraceFrame*>(m_selectedEvent);
-            if (frame->calls.isEmpty()) {
+            if (frame->isEmpty()) {
                 //XXX i guess we could still get the current state
                 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;
@@ -278,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
@@ -540,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()
@@ -569,14 +612,13 @@ 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();
     m_proxyModel->setSourceModel(m_model);
     m_ui.callView->setModel(m_proxyModel);
-    m_ui.callView->setItemDelegate(new ApiCallDelegate);
+    m_ui.callView->setItemDelegate(
+        new ApiCallDelegate(m_ui.callView));
     m_ui.callView->resizeColumnToContents(0);
     m_ui.callView->header()->swapSections(0, 1);
     m_ui.callView->setColumnWidth(1, 42);
@@ -590,11 +632,13 @@ void MainWindow::initObjects()
     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);
 
@@ -610,11 +654,6 @@ void MainWindow::initObjects()
     m_searchWidget->hide();
 
     m_traceProcess = new TraceProcess(this);
-
-    new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_G),
-                  this, SLOT(slotGoTo()));
-    new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F),
-                  this, SLOT(slotSearch()));
 }
 
 void MainWindow::initConnections()
@@ -623,6 +662,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&)));
@@ -630,6 +675,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()));
@@ -650,6 +697,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()),
@@ -691,6 +747,13 @@ void MainWindow::initConnections()
             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)
@@ -738,7 +801,8 @@ void MainWindow::slotSearch()
     m_searchWidget->show();
 }
 
-void MainWindow::slotSearchNext(const QString &str, Qt::CaseSensitivity sensitivity)
+void MainWindow::slotSearchNext(const QString &str,
+                                Qt::CaseSensitivity sensitivity)
 {
     QModelIndex index = m_ui.callView->currentIndex();
     ApiTraceEvent *event = 0;
@@ -761,7 +825,7 @@ void MainWindow::slotSearchNext(const QString &str, Qt::CaseSensitivity sensitiv
     else {
         Q_ASSERT(event->type() == ApiTraceCall::Frame);
         ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
-        call = frame->calls.value(0);
+        call = frame->call(0);
     }
 
     if (!call) {
@@ -773,12 +837,12 @@ void MainWindow::slotSearchNext(const QString &str, Qt::CaseSensitivity sensitiv
 
     for (int i = callNum + 1; i < calls.count(); ++i) {
         ApiTraceCall *testCall = calls[i];
-        QString txt = testCall->filterText();
-        if (txt.contains(str, sensitivity)) {
-            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()) {
+        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;
@@ -788,7 +852,8 @@ void MainWindow::slotSearchNext(const QString &str, Qt::CaseSensitivity sensitiv
     m_searchWidget->setFound(false);
 }
 
-void MainWindow::slotSearchPrev(const QString &str, Qt::CaseSensitivity sensitivity)
+void MainWindow::slotSearchPrev(const QString &str,
+                                Qt::CaseSensitivity sensitivity)
 {
     QModelIndex index = m_ui.callView->currentIndex();
     ApiTraceEvent *event = 0;
@@ -811,7 +876,7 @@ void MainWindow::slotSearchPrev(const QString &str, Qt::CaseSensitivity sensitiv
     else {
         Q_ASSERT(event->type() == ApiTraceCall::Frame);
         ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
-        call = frame->calls.value(0);
+        call = frame->call(0);
     }
 
     if (!call) {
@@ -823,12 +888,12 @@ void MainWindow::slotSearchPrev(const QString &str, Qt::CaseSensitivity sensitiv
 
     for (int i = callNum - 1; i >= 0; --i) {
         ApiTraceCall *testCall = calls[i];
-        QString txt = testCall->filterText();
-        if (txt.contains(str, sensitivity)) {
-            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()) {
+        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;
@@ -900,4 +965,128 @@ void MainWindow::editCall()
     }
 }
 
+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->isEmpty()) {
+        return;
+    }
+
+    QList<ApiTraceCall*>::const_iterator itr;
+    QList<ApiTraceCall*> calls = frame->calls();
+
+    itr = calls.constBegin();
+    while (itr != 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->isEmpty()) {
+        return;
+    }
+    QList<ApiTraceCall*>::const_iterator itr;
+    QList<ApiTraceCall*> calls = frame->calls();
+
+    itr = calls.constEnd();
+    do {
+        --itr;
+        ApiTraceCall *call = *itr;
+        QModelIndex idx = m_proxyModel->indexForCall(call);
+        if (idx.isValid()) {
+            m_ui.callView->setCurrentIndex(idx);
+            break;
+        }
+    } while (itr != 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"