]> git.cworth.org Git - apitrace/commitdiff
Merge branch 'trace-editing'
authorZack Rusin <zack@kde.org>
Mon, 18 Apr 2011 21:04:01 +0000 (17:04 -0400)
committerZack Rusin <zack@kde.org>
Mon, 18 Apr 2011 21:04:01 +0000 (17:04 -0400)
fixes #1

24 files changed:
CMakeLists.txt
gui/CMakeLists.txt
gui/apicalldelegate.cpp
gui/apicalldelegate.h
gui/apitrace.cpp
gui/apitrace.h
gui/apitracecall.cpp
gui/apitracecall.h
gui/apitracefilter.cpp
gui/apitracemodel.cpp
gui/apitracemodel.h
gui/argumentseditor.cpp [new file with mode: 0644]
gui/argumentseditor.h [new file with mode: 0644]
gui/loaderthread.cpp
gui/mainwindow.cpp
gui/mainwindow.h
gui/qapitrace.qrc
gui/resources/document-edit.png [new file with mode: 0644]
gui/resources/edit-undo.png [new file with mode: 0644]
gui/resources/emblem-locked.png [new file with mode: 0644]
gui/saverthread.cpp [new file with mode: 0644]
gui/saverthread.h [new file with mode: 0644]
gui/ui/argumentseditor.ui [new file with mode: 0644]
trace_write.cpp

index 1131ae4c61d62601976e10468fd9ffdd531583cb..a157889e3092e81457fb6aabc030c192b8c4a04b 100755 (executable)
@@ -210,7 +210,7 @@ else (WIN32)
     set (glws glws_glx.cpp)
 endif (WIN32)
 
-add_library (trace trace_model.cpp trace_parser.cpp ${os})
+add_library (trace trace_model.cpp trace_parser.cpp trace_write.cpp ${os})
 
 add_executable (tracedump tracedump.cpp)
 target_link_libraries (tracedump trace)
index 6787043cd5c32ea046dad76d06181c212e5b594b..b8d5a6908b39f0a8d65f4d6097746988881d6302 100644 (file)
@@ -8,6 +8,7 @@ set(qapitrace_SRCS
    apitracecall.cpp
    apitracefilter.cpp
    apitracemodel.cpp
+   argumentseditor.cpp
    glsledit.cpp
    imageviewer.cpp
    jumpwidget.cpp
@@ -15,6 +16,7 @@ set(qapitrace_SRCS
    mainwindow.cpp
    main.cpp
    retracer.cpp
+   saverthread.cpp
    searchwidget.cpp
    settingsdialog.cpp
    shaderssourcewidget.cpp
@@ -28,6 +30,7 @@ qt4_automoc(${qapitrace_SRCS})
 qt4_add_resources(qapitrace_SRCS qapitrace.qrc)
 
 set(qapitrace_UIS
+   ui/argumentseditor.ui
    ui/imageviewer.ui
    ui/jumpwidget.ui
    ui/mainwindow.ui
@@ -42,7 +45,7 @@ QT4_WRAP_UI(qapitrace_UIS_H ${qapitrace_UIS})
 
 #add_app_icon(qapitrace_SRCS ../icons/hi*-qapitrace.png)
 link_directories(${LINK_DIRECTORIES} ${QJSON_LIBRARY_DIRS})
-include_directories(${QT_INCLUDES} ${QJSON_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/..)
+include_directories(${QT_INCLUDES} ${QJSON_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/..)
 
 add_definitions(-DBUILD_DIR="${CMAKE_CURRENT_BINARY_DIR}/..")
 
index 4eb542be2171bce1c410da3397fb8eb7592356c1..c2a1b23f54f47172fcc59885989011b2c68c7d58 100644 (file)
@@ -11,7 +11,8 @@
 
 ApiCallDelegate::ApiCallDelegate(QWidget *parent)
     : QStyledItemDelegate(parent),
-      m_stateEmblem(":/resources/dialog-information.png")
+      m_stateEmblem(":/resources/dialog-information.png"),
+      m_editEmblem(":/resources/document-edit.png")
 {
 }
 
@@ -32,10 +33,21 @@ void ApiCallDelegate::paint(QPainter *painter,
         QStyle *style = QApplication::style();
         style->drawControl(QStyle::CE_ItemViewItem, &option, painter, 0);
         if (!event->state().isEmpty()) {
-            QPixmap px = m_stateEmblem.pixmap(option.rect.height(), option.rect.height());
+            QPixmap px = m_stateEmblem.pixmap(option.rect.height(),
+                                              option.rect.height());
             painter->drawPixmap(option.rect.topLeft(), px);
             offset = QPoint(option.rect.height() + 5, 0);
         }
+        if (event->type() == ApiTraceEvent::Call) {
+            ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
+            if (call->edited()) {
+                QPixmap px = m_editEmblem.pixmap(option.rect.height(),
+                                                 option.rect.height());
+                painter->drawPixmap(option.rect.topLeft() + offset, px);
+                offset += QPoint(option.rect.height() + 5, 0);
+            }
+        }
+
         painter->drawStaticText(option.rect.topLeft() + offset, text);
     } else {
         QStyledItemDelegate::paint(painter, option, index);
index 525ab8320ad6be475f1d1967b2197a43e6926dcb..55cd13ef2b13b7fd2617f55899d27956b2541391 100644 (file)
@@ -17,6 +17,7 @@ public:
 
 private:
     QIcon m_stateEmblem;
+    QIcon m_editEmblem;
 };
 
 #endif
index 592202fee3f0d1d9529d3dc5bcd700db25d4dcd0..4ce4cd423f64873c87abb4477855ec86419c0dcd 100644 (file)
@@ -1,9 +1,13 @@
 #include "apitrace.h"
 
 #include "loaderthread.h"
+#include "saverthread.h"
+
+#include <QDir>
 
 ApiTrace::ApiTrace()
-    : m_frameMarker(ApiTrace::FrameMarker_SwapBuffers)
+    : m_frameMarker(ApiTrace::FrameMarker_SwapBuffers),
+      m_needsSaving(false)
 {
     m_loader = new LoaderThread(this);
     connect(m_loader, SIGNAL(parsedFrames(const QList<ApiTraceFrame*>)),
@@ -12,6 +16,12 @@ ApiTrace::ApiTrace()
             this, SIGNAL(startedLoadingTrace()));
     connect(m_loader, SIGNAL(finished()),
             this, SIGNAL(finishedLoadingTrace()));
+
+    m_saver = new SaverThread(this);
+    connect(m_saver, SIGNAL(traceSaved()),
+            this, SLOT(slotSaved()));
+    connect(m_saver, SIGNAL(traceSaved()),
+            this, SIGNAL(saved()));
 }
 
 ApiTrace::~ApiTrace()
@@ -19,6 +29,7 @@ ApiTrace::~ApiTrace()
     qDeleteAll(m_calls);
     qDeleteAll(m_frames);
     delete m_loader;
+    delete m_saver;
 }
 
 bool ApiTrace::isCallAFrameMarker(const ApiTraceCall *call,
@@ -29,13 +40,13 @@ bool ApiTrace::isCallAFrameMarker(const ApiTraceCall *call,
 
     switch (marker) {
     case FrameMarker_SwapBuffers:
-        return call->name.contains(QLatin1String("SwapBuffers"));
+        return call->name().contains(QLatin1String("SwapBuffers"));
     case FrameMarker_Flush:
-        return call->name == QLatin1String("glFlush");
+        return call->name() == QLatin1String("glFlush");
     case FrameMarker_Finish:
-        return call->name == QLatin1String("glFinish");
+        return call->name() == QLatin1String("glFinish");
     case FrameMarker_Clear:
-        return call->name == QLatin1String("glClear");
+        return call->name() == QLatin1String("glClear");
     }
 
     Q_ASSERT(!"unknown frame marker");
@@ -50,6 +61,9 @@ bool ApiTrace::isEmpty() const
 
 QString ApiTrace::fileName() const
 {
+    if (edited())
+        return m_tempFileName;
+
     return m_fileName;
 }
 
@@ -134,6 +148,7 @@ void ApiTrace::addFrames(const QList<ApiTraceFrame*> &frames)
     int currentCalls = m_calls.count();
     int numNewCalls = 0;
     foreach(ApiTraceFrame *frame, frames) {
+        frame->setParentTrace(this);
         numNewCalls += frame->calls.count();
         m_calls += frame->calls;
     }
@@ -151,9 +166,10 @@ void ApiTrace::detectFrames()
     foreach(ApiTraceCall *apiCall, m_calls) {
         if (!currentFrame) {
             currentFrame = new ApiTraceFrame();
+            currentFrame->setParentTrace(this);
             currentFrame->number = m_frames.count();
         }
-        apiCall->parentFrame = currentFrame;
+        apiCall->setParentFrame(currentFrame);
         currentFrame->calls.append(apiCall);
         if (ApiTrace::isCallAFrameMarker(apiCall,
                                          m_frameMarker)) {
@@ -175,7 +191,7 @@ ApiTraceCall * ApiTrace::callWithIndex(int idx) const
 {
     for (int i = 0; i < m_calls.count(); ++i) {
         ApiTraceCall *call = m_calls[i];
-        if (call->index == idx)
+        if (call->index() == idx)
             return call;
     }
     return NULL;
@@ -190,4 +206,58 @@ ApiTraceState ApiTrace::defaultState() const
     return frame->state();
 }
 
+void ApiTrace::callEdited(ApiTraceCall *call)
+{
+    if (!m_editedCalls.contains(call)) {
+        //lets generate a temp filename
+        QString tempPath = QDir::tempPath();
+        m_tempFileName = QString::fromLatin1("%1/%2.edited")
+                         .arg(tempPath)
+                         .arg(m_fileName);
+    }
+    m_editedCalls.insert(call);
+    m_needsSaving = true;
+
+    emit changed(call);
+}
+
+void ApiTrace::callReverted(ApiTraceCall *call)
+{
+    m_editedCalls.remove(call);
+
+    if (m_editedCalls.isEmpty()) {
+        m_needsSaving = false;
+    }
+    emit changed(call);
+}
+
+bool ApiTrace::edited() const
+{
+    return !m_editedCalls.isEmpty();
+}
+
+bool ApiTrace::needsSaving() const
+{
+    return m_needsSaving;
+}
+
+void ApiTrace::save()
+{
+    QFileInfo fi(m_tempFileName);
+    QDir dir;
+    emit startedSaving();
+    dir.mkpath(fi.absolutePath());
+    m_saver->saveFile(m_tempFileName, m_calls);
+}
+
+void ApiTrace::slotSaved()
+{
+    m_needsSaving = false;
+}
+
+bool ApiTrace::isSaving() const
+{
+    return m_saver->isRunning();
+}
+
 #include "apitrace.moc"
index 2bf916af8f4aab21bb0e3fae8ac9f9c46c949cb1..32e24084b26802545fb1dbe800d788239df5231f 100644 (file)
@@ -4,8 +4,10 @@
 #include "apitracecall.h"
 
 #include <QObject>
+#include <QSet>
 
 class LoaderThread;
+class SaverThread;
 
 class ApiTrace : public QObject
 {
@@ -41,25 +43,39 @@ public:
     int numFrames() const;
     int numCallsInFrame(int idx) const;
 
+    void callEdited(ApiTraceCall *call);
+    void callReverted(ApiTraceCall *call);
+
+    bool edited() const;
+    bool needsSaving() const;
+
+    bool isSaving() const;
+
 public slots:
     void setFileName(const QString &name);
     void setFrameMarker(FrameMarker marker);
+    void save();
 
 signals:
     void startedLoadingTrace();
     void finishedLoadingTrace();
     void invalidated();
     void framesInvalidated();
+    void changed(ApiTraceCall *call);
+    void startedSaving();
+    void saved();
 
     void framesAdded(int oldCount, int numAdded);
     void callsAdded(int oldCount, int numAdded);
 
 private slots:
     void addFrames(const QList<ApiTraceFrame*> &frames);
+    void slotSaved();
 private:
     void detectFrames();
 private:
     QString m_fileName;
+    QString m_tempFileName;
 
     QList<ApiTraceFrame*> m_frames;
     QList<ApiTraceCall*> m_calls;
@@ -67,6 +83,11 @@ private:
     FrameMarker m_frameMarker;
 
     LoaderThread *m_loader;
+    SaverThread  *m_saver;
+
+    QSet<ApiTraceCall*> m_editedCalls;
+
+    bool m_needsSaving;
 };
 
 #endif
index 2d28e164cc550d667fed83f645a1473932beb84b..b2ded2ff053fdfc2b110f9d3ecd7cab70102fc4a 100644 (file)
@@ -1,11 +1,14 @@
 #include "apitracecall.h"
 
+#include "apitrace.h"
 #include "trace_model.hpp"
 
 #include <QDebug>
 #include <QObject>
+#define QT_USE_FAST_OPERATOR_PLUS
+#include <QStringBuilder>
 
-ApiPointer::ApiPointer(int val)
+ApiPointer::ApiPointer(unsigned long long val)
     : m_value(val)
 {
 }
@@ -49,6 +52,9 @@ QString apiVariantToString(const QVariant &variant)
     if (variant.canConvert<ApiArray>()) {
         return variant.value<ApiArray>().toString();
     }
+    if (variant.canConvert<ApiEnum>()) {
+        return variant.value<ApiEnum>().toString();
+    }
 
     return QString();
 }
@@ -115,9 +121,9 @@ QString ApiStruct::toString() const
 
     str += QLatin1String("{");
     for (unsigned i = 0; i < m_members.count(); ++i) {
-        str += m_sig.memberNames[i];
-        str += QLatin1String(" = ");
-        str += apiVariantToString(m_members[i]);
+        str += m_sig.memberNames[i] %
+               QLatin1Literal(" = ") %
+               apiVariantToString(m_members[i]);
         if (i < m_members.count() - 1)
             str += QLatin1String(", ");
     }
@@ -143,7 +149,7 @@ void ApiStruct::init(const Trace::Struct *s)
 
 void VariantVisitor::visit(Trace::Null *)
 {
-    m_variant = QVariant(QLatin1String("NULL"));
+    m_variant = QVariant::fromValue(ApiPointer(0));
 }
 
 void VariantVisitor::visit(Trace::Bool *node)
@@ -173,7 +179,13 @@ void VariantVisitor::visit(Trace::String *node)
 
 void VariantVisitor::visit(Trace::Enum *e)
 {
-    m_variant = QVariant(QString::fromStdString(e->sig->first));
+    VariantVisitor vis;
+    e->sig->second->visit(vis);
+
+    QVariant val = vis.variant();
+
+    m_variant = QVariant::fromValue(
+        ApiEnum(QString::fromStdString(e->sig->first), val));
 }
 
 void VariantVisitor::visit(Trace::Bitmask *bitmask)
@@ -193,6 +205,14 @@ void VariantVisitor::visit(Trace::Array *array)
 
 void VariantVisitor::visit(Trace::Blob *blob)
 {
+    //XXX
+    //FIXME: this is a nasty hack. Trace::Blob's can't
+    //   delete the contents in the destructor because
+    //   the data is being used by other calls. We piggy back
+    //   on that assumption and don't deep copy the data. If
+    //   Blob's will start deleting the data we will need to
+    //   start deep copying it or switch to using something like
+    //   Boost's shared_ptr or Qt's QSharedPointer to handle it
     QByteArray barray = QByteArray::fromRawData(blob->buf, blob->size);
     m_variant = QVariant(barray);
 }
@@ -207,6 +227,11 @@ ApiArray::ApiArray(const Trace::Array *arr)
     init(arr);
 }
 
+ApiArray::ApiArray(const QList<QVariant> &vals)
+    : m_array(vals)
+{
+}
+
 QString ApiArray::toString() const
 {
     QString str;
@@ -237,11 +262,14 @@ void ApiArray::init(const Trace::Array *arr)
 
 QStaticText ApiTraceCall::staticText() const
 {
-    if (!m_staticText.text().isEmpty())
-        return m_staticText;
+    if (m_staticText && !m_staticText->text().isEmpty())
+        return *m_staticText;
+
+    QVariantList argValues = arguments();
 
-    QString richText = QString::fromLatin1("<span style=\"font-weight:bold\">%1</span>(").arg(name);
-    for (int i = 0; i < argNames.count(); ++i) {
+    QString richText = QString::fromLatin1(
+        "<span style=\"font-weight:bold\">%1</span>(").arg(m_name);
+    for (int i = 0; i < m_argNames.count(); ++i) {
         richText += QLatin1String("<span style=\"color:#0000ff\">");
         QString argText = apiVariantToString(argValues[i]);
 
@@ -259,24 +287,28 @@ QStaticText ApiTraceCall::staticText() const
             richText += argText;
         }
         richText += QLatin1String("</span>");
-        if (i < argNames.count() - 1)
-            richText += QString::fromLatin1(", ");
+        if (i < m_argNames.count() - 1)
+            richText += QLatin1String(", ");
     }
     richText += QLatin1String(")");
-    if (returnValue.isValid()) {
-        richText += QLatin1String(" = ");
-        richText += QLatin1String("<span style=\"color:#0000ff\">");
-        richText += apiVariantToString(returnValue);
-        richText += QLatin1String("</span>");
+    if (m_returnValue.isValid()) {
+        richText +=
+            QLatin1Literal(" = ") %
+            QLatin1Literal("<span style=\"color:#0000ff\">") %
+            apiVariantToString(m_returnValue) %
+            QLatin1Literal("</span>");
     }
 
-    m_staticText.setText(richText);
+    if (!m_staticText)
+        m_staticText = new QStaticText(richText);
+    else
+        m_staticText->setText(richText);
     QTextOption opt;
     opt.setWrapMode(QTextOption::NoWrap);
-    m_staticText.setTextOption(opt);
-    m_staticText.prepare();
+    m_staticText->setTextOption(opt);
+    m_staticText->prepare();
 
-    return m_staticText;
+    return *m_staticText;
 }
 
 QString ApiTraceCall::toHtml() const
@@ -284,36 +316,39 @@ QString ApiTraceCall::toHtml() const
     if (!m_richText.isEmpty())
         return m_richText;
 
-    if (helpUrl.isEmpty()) {
+    if (m_helpUrl.isEmpty()) {
         m_richText = QString::fromLatin1(
             "%1) <span style=\"font-weight:bold\">%2</span>(")
-                     .arg(index)
-                     .arg(name);
+                     .arg(m_index)
+                     .arg(m_name);
     } else {
         m_richText = QString::fromLatin1(
             "%1) <span style=\"font-weight:bold\"><a href=\"%2\">%3</a></span>(")
-                      .arg(index)
-                      .arg(helpUrl.toString())
-                      .arg(name);
+                      .arg(m_index)
+                      .arg(m_helpUrl.toString())
+                      .arg(m_name);
     }
 
-    for (int i = 0; i < argNames.count(); ++i) {
-        m_richText += argNames[i];
-        m_richText += QString::fromLatin1(" = ");
-        m_richText += QLatin1String("<span style=\"color:#0000ff\">");
-        m_richText += apiVariantToString(argValues[i]);
-        m_richText += QLatin1String("</span>");
-        if (i < argNames.count() - 1)
-            m_richText += QString::fromLatin1(", ");
+    QVariantList argValues = arguments();
+    for (int i = 0; i < m_argNames.count(); ++i) {
+        m_richText += m_argNames[i] +
+                      QLatin1Literal(" = ") +
+                      QLatin1Literal("<span style=\"color:#0000ff\">") +
+                      apiVariantToString(argValues[i]) +
+                      QLatin1Literal("</span>");
+        if (i < m_argNames.count() - 1)
+            m_richText += QLatin1String(", ");
     }
     m_richText += QLatin1String(")");
 
-    if (returnValue.isValid()) {
-        m_richText += QLatin1String(" = ");
-        m_richText += QLatin1String("<span style=\"color:#0000ff\">");
-        m_richText += apiVariantToString(returnValue);
-        m_richText += QLatin1String("</span>");
+    if (m_returnValue.isValid()) {
+        m_richText +=
+            QLatin1String(" = ") +
+            QLatin1String("<span style=\"color:#0000ff\">") +
+            apiVariantToString(m_returnValue) +
+            QLatin1String("</span>");
     }
+    m_richText.squeeze();
     return m_richText;
 }
 
@@ -322,44 +357,46 @@ QString ApiTraceCall::filterText() const
     if (!m_filterText.isEmpty())
         return m_filterText;
 
-    m_filterText = name;
-    m_filterText += QLatin1String("(");
-    for (int i = 0; i < argNames.count(); ++i) {
-        m_filterText += argNames[i];
-        m_filterText += QString::fromLatin1(" = ");
-
+    QVariantList argValues = arguments();
+    m_filterText = m_name + QLatin1Literal("(");
+    for (int i = 0; i < m_argNames.count(); ++i) {
+        m_filterText += m_argNames[i] +
+                        QLatin1Literal(" = ") +
+                        apiVariantToString(argValues[i]);
         if (argValues[i].type() == QVariant::ByteArray) {
             m_hasBinaryData = true;
             m_binaryDataIndex = i;
         }
-        m_filterText += apiVariantToString(argValues[i]);
-        if (i < argNames.count() - 1)
-            m_filterText += QString::fromLatin1(", ");
+        if (i < m_argNames.count() - 1)
+            m_filterText += QLatin1String(", ");
     }
     m_filterText += QLatin1String(")");
 
-    if (returnValue.isValid()) {
-        m_filterText += QLatin1String(" = ");
-        m_filterText += apiVariantToString(returnValue);
+    if (m_returnValue.isValid()) {
+        m_filterText += QLatin1Literal(" = ") +
+                        apiVariantToString(m_returnValue);
     }
+    m_filterText.squeeze();
     return m_filterText;
 }
 
 QStaticText ApiTraceFrame::staticText() const
 {
-    if (!m_staticText.text().isEmpty())
-        return m_staticText;
+    if (m_staticText && !m_staticText->text().isEmpty())
+        return *m_staticText;
 
     QString richText =
         QString::fromLatin1("<span style=\"font-weight:bold\">Frame %1</span>").arg(number);
 
-    m_staticText.setText(richText);
+    if (!m_staticText)
+        m_staticText = new QStaticText(richText);
+
     QTextOption opt;
     opt.setWrapMode(QTextOption::NoWrap);
-    m_staticText.setTextOption(opt);
-    m_staticText.prepare();
+    m_staticText->setTextOption(opt);
+    m_staticText->prepare();
 
-    return m_staticText;
+    return *m_staticText;
 }
 
 int ApiTraceCall::numChildren() const
@@ -373,7 +410,8 @@ int ApiTraceFrame::numChildren() const
 }
 
 ApiTraceFrame::ApiTraceFrame()
-    : ApiTraceEvent(ApiTraceEvent::Frame)
+    : ApiTraceEvent(ApiTraceEvent::Frame),
+      m_parentTrace(0)
 {
 }
 
@@ -385,12 +423,14 @@ ApiTraceCall::ApiTraceCall()
 }
 
 ApiTraceEvent::ApiTraceEvent()
-    : m_type(ApiTraceEvent::None)
+    : m_type(ApiTraceEvent::None),
+      m_staticText(0)
 {
 }
 
 ApiTraceEvent::ApiTraceEvent(Type t)
-    : m_type(t)
+    : m_type(t),
+      m_staticText(0)
 {
 }
 
@@ -435,7 +475,8 @@ ApiTraceState::ApiTraceState(const QVariantMap &parsedJson)
     QVariantMap::const_iterator itr;
 
 
-    for (itr = attachedShaders.constBegin(); itr != attachedShaders.constEnd(); ++itr) {
+    for (itr = attachedShaders.constBegin(); itr != attachedShaders.constEnd();
+         ++itr) {
         QString type = itr.key();
         QString source = itr.value().toString();
         m_shaderSources[type] = source;
@@ -532,4 +573,184 @@ QList<ApiFramebuffer> ApiTraceState::framebuffers() const
     return m_framebuffers;
 }
 
+QList<QVariant> ApiArray::values() const
+{
+    return m_array;
+}
+
+int ApiTraceCall::index() const
+{
+    return m_index;
+}
+
+QString ApiTraceCall::name() const
+{
+    return m_name;
+}
+
+QStringList ApiTraceCall::argNames() const
+{
+    return m_argNames;
+}
+
+QVariantList ApiTraceCall::arguments() const
+{
+    if (m_editedValues.isEmpty())
+        return m_argValues;
+    else
+        return m_editedValues;
+}
+
+QVariant ApiTraceCall::returnValue() const
+{
+    return m_returnValue;
+}
+
+QUrl ApiTraceCall::helpUrl() const
+{
+    return m_helpUrl;
+}
+
+ApiTraceCall::ApiTraceCall(const Trace::Call *call)
+    : ApiTraceEvent(ApiTraceEvent::Call),
+      m_hasBinaryData(false),
+      m_binaryDataIndex(0)
+{
+    m_name = QString::fromStdString(call->sig->name);
+    m_index = call->no;
+
+    QString argumentsText;
+    for (int i = 0; i < call->sig->arg_names.size(); ++i) {
+        m_argNames +=
+            QString::fromStdString(call->sig->arg_names[i]);
+    }
+    if (call->ret) {
+        VariantVisitor retVisitor;
+        call->ret->visit(retVisitor);
+        m_returnValue = retVisitor.variant();
+    }
+    for (int i = 0; i < call->args.size(); ++i) {
+        VariantVisitor argVisitor;
+        call->args[i]->visit(argVisitor);
+        m_argValues += argVisitor.variant();
+    }
+}
+
+void ApiTraceCall::setHelpUrl(const QUrl &url)
+{
+    m_helpUrl = url;
+}
+
+void ApiTraceCall::setParentFrame(ApiTraceFrame *frame)
+{
+    m_parentFrame = frame;
+}
+
+ApiTraceFrame * ApiTraceCall::parentFrame()const
+{
+    return m_parentFrame;
+}
+
+ApiTraceEvent::~ApiTraceEvent()
+{
+    delete m_staticText;
+}
+
+void ApiTraceCall::revert()
+{
+    setEditedValues(QVariantList());
+}
+
+ApiTrace * ApiTraceFrame::parentTrace() const
+{
+    return m_parentTrace;
+}
+
+void ApiTraceFrame::setParentTrace(ApiTrace *trace)
+{
+    m_parentTrace = trace;
+}
+
+QVariantList ApiTraceCall::originalValues() const
+{
+    return m_argValues;
+}
+
+void ApiTraceCall::setEditedValues(const QVariantList &lst)
+{
+    ApiTrace *trace = 0;
+    if (m_parentFrame)
+        trace = m_parentFrame->parentTrace();
+
+    m_editedValues = lst;
+    //lets regenerate data
+    m_richText = QString();
+    m_filterText = QString();
+    delete m_staticText;
+    m_staticText = 0;
+
+    if (trace) {
+        if (!lst.isEmpty()) {
+            trace->callEdited(this);
+        } else {
+            trace->callReverted(this);
+        }
+    }
+}
+
+QVariantList ApiTraceCall::editedValues() const
+{
+    return m_editedValues;
+}
+
+bool ApiTraceCall::edited() const
+{
+    return !m_editedValues.isEmpty();
+}
+
+ApiEnum::ApiEnum(const QString &name, const QVariant &val)
+    : m_name(name),
+      m_value(val)
+{
+}
+
+QString ApiEnum::toString() const
+{
+    return m_name;
+}
+
+QVariant ApiEnum::value() const
+{
+    return m_value;
+}
+
+QString ApiEnum::name() const
+{
+    return m_name;
+}
+
+unsigned long long ApiBitmask::value() const
+{
+    return m_value;
+}
+
+ApiBitmask::Signature ApiBitmask::signature() const
+{
+    return m_sig;
+}
+
+ApiStruct::Signature ApiStruct::signature() const
+{
+    return m_sig;
+}
+
+QList<QVariant> ApiStruct::values() const
+{
+    return m_members;
+}
+
+unsigned long long ApiPointer::value() const
+{
+    return m_value;
+}
 
index db5d38d3539bfecc1d0f0570d8bae8ac107e13ff..59c6fe75eec5613025e895033097defd93f423b9 100644 (file)
@@ -11,6 +11,8 @@
 #include "trace_model.hpp"
 
 
+class ApiTrace;
+
 class VariantVisitor : public Trace::Visitor
 {
 public:
@@ -35,15 +37,32 @@ private:
     QVariant m_variant;
 };
 
+class ApiEnum
+{
+public:
+    ApiEnum(const QString &name = QString(), const QVariant &val=QVariant());
+
+    QString toString() const;
+
+    QVariant value() const;
+    QString name() const;
+private:
+    QString m_name;
+    QVariant m_value;
+};
+Q_DECLARE_METATYPE(ApiEnum);
 
 class ApiPointer
 {
 public:
-    ApiPointer(int val=0);
+    ApiPointer(unsigned long long val=0);
 
     QString toString() const;
+
+    unsigned long long value() const;
+
 private:
-    int m_value;
+    unsigned long long m_value;
 };
 Q_DECLARE_METATYPE(ApiPointer);
 
@@ -56,6 +75,9 @@ public:
 
     QString toString() const;
 
+    unsigned long long value() const;
+    Signature signature() const;
+
 private:
     void init(const Trace::Bitmask *bitmask);
 private:
@@ -75,6 +97,8 @@ public:
     ApiStruct(const Trace::Struct *s = 0);
 
     QString toString() const;
+    Signature signature() const;
+    QList<QVariant> values() const;
 
 private:
     void init(const Trace::Struct *bitmask);
@@ -88,9 +112,11 @@ class ApiArray
 {
 public:
     ApiArray(const Trace::Array *arr = 0);
+    ApiArray(const QList<QVariant> &vals);
 
     QString toString() const;
 
+    QList<QVariant> values() const;
 private:
     void init(const Trace::Array *arr);
 private:
@@ -133,6 +159,7 @@ public:
 public:
     ApiTraceEvent();
     ApiTraceEvent(Type t);
+    virtual ~ApiTraceEvent();
 
     Type type() const { return m_type; }
 
@@ -146,6 +173,8 @@ public:
 protected:
     Type m_type;
     ApiTraceState m_state;
+
+    mutable QStaticText *m_staticText;
 };
 Q_DECLARE_METATYPE(ApiTraceEvent*);
 
@@ -153,15 +182,25 @@ class ApiTraceCall : public ApiTraceEvent
 {
 public:
     ApiTraceCall();
+    ApiTraceCall(const Trace::Call *tcall);
     ~ApiTraceCall();
 
-    int index;
-    QString name;
-    QStringList argNames;
-    QVariantList argValues;
-    QVariant returnValue;
-    ApiTraceFrame *parentFrame;
-    QUrl helpUrl;
+    int index() const;
+    QString name() const;
+    QStringList argNames() const;
+    QVariantList arguments() const;
+    QVariant returnValue() const;
+    QUrl helpUrl() const;
+    void setHelpUrl(const QUrl &url);
+    ApiTraceFrame *parentFrame()const;
+    void setParentFrame(ApiTraceFrame *frame);
+
+    QVariantList originalValues() const;
+
+    bool edited() const;
+    void setEditedValues(const QVariantList &lst);
+    QVariantList editedValues() const;
+    void revert();
 
     QString toHtml() const;
     QString filterText() const;
@@ -170,9 +209,18 @@ public:
     bool hasBinaryData() const;
     int binaryDataIndex() const;
 private:
+    int m_index;
+    QString m_name;
+    QStringList m_argNames;
+    QVariantList m_argValues;
+    QVariant m_returnValue;
+    ApiTraceFrame *m_parentFrame;
+    QUrl m_helpUrl;
+
+    QVariantList m_editedValues;
+
     mutable QString m_richText;
     mutable QString m_filterText;
-    mutable QStaticText m_staticText;
     mutable bool m_hasBinaryData;
     mutable int m_binaryDataIndex;
 };
@@ -185,10 +233,13 @@ public:
     int number;
     QList<ApiTraceCall*> calls;
 
+    ApiTrace *parentTrace() const;
+    void setParentTrace(ApiTrace *trace);
+
     int numChildren() const;
     QStaticText staticText() const;
 private:
-    mutable QStaticText m_staticText;
+    ApiTrace *m_parentTrace;
 };
 Q_DECLARE_METATYPE(ApiTraceFrame*);
 
index 8a40faf5d563cb8bc7475adc259cbde686ac597b..f541f736055bbbd5aa31aaec4d361e92ecca2a62 100644 (file)
@@ -29,7 +29,7 @@ bool ApiTraceFilter::filterAcceptsRow(int sourceRow,
     }
 
     ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
-    QString function = call->name;
+    QString function = call->name();
 
     if (!m_regexp.isEmpty() && m_regexp.isValid()) {
         return function.contains(m_regexp);
index d3f3e6a7545aedbcef8d6269de04fe05b358ac14..a39a92935dfba6812ab5f13512dce25535b53f1e 100644 (file)
@@ -44,12 +44,12 @@ QVariant ApiTraceModel::data(const QModelIndex &index, int role) const
             ApiTraceCall *call = static_cast<ApiTraceCall*>(itm);
             if (call->state().isEmpty())
                 return QString::fromLatin1("%1)&nbsp;<b>%2</b>")
-                    .arg(call->index)
-                    .arg(call->name);
+                    .arg(call->index())
+                    .arg(call->name());
             else
                 return QString::fromLatin1("%1)&nbsp;<b>%2</b><br/>%3")
-                    .arg(call->index)
-                    .arg(call->name)
+                    .arg(call->index())
+                    .arg(call->name())
                     .arg(stateText);
         } else {
             ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(itm);
@@ -145,9 +145,9 @@ QModelIndex ApiTraceModel::parent(const QModelIndex &index) const
     ApiTraceEvent *event = item(index);
     if (event && event->type() == ApiTraceEvent::Call) {
         ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
-        Q_ASSERT(call->parentFrame);
-        return createIndex(call->parentFrame->number,
-                           0, call->parentFrame);
+        Q_ASSERT(call->parentFrame());
+        return createIndex(call->parentFrame()->number,
+                           0, call->parentFrame());
     }
     return QModelIndex();
 }
@@ -206,6 +206,8 @@ void ApiTraceModel::setApiTrace(ApiTrace *trace)
             this, SLOT(invalidateFrames()));
     connect(m_trace, SIGNAL(framesAdded(int, int)),
             this, SLOT(appendFrames(int, int)));
+    connect(m_trace, SIGNAL(changed(ApiTraceCall*)),
+            this, SLOT(callChanged(ApiTraceCall*)));
 }
 
 const ApiTrace * ApiTraceModel::apiTrace() const
@@ -240,7 +242,7 @@ void ApiTraceModel::stateSetOnEvent(ApiTraceEvent *event)
 
     if (event->type() == ApiTraceEvent::Call) {
         ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
-        ApiTraceFrame *frame = call->parentFrame;
+        ApiTraceFrame *frame = call->parentFrame();
         int row = frame->calls.indexOf(call);
         QModelIndex index = createIndex(row, 0, call);
         emit dataChanged(index, index);
@@ -265,15 +267,35 @@ QModelIndex ApiTraceModel::indexForCall(ApiTraceCall *call) const
         return QModelIndex();
     }
 
-    ApiTraceFrame *frame = call->parentFrame;
+    ApiTraceFrame *frame = call->parentFrame();
     Q_ASSERT(frame);
 
     int row = frame->calls.indexOf(call);
     if (row < 0) {
-        qDebug() << "Couldn't find call num "<<call->index<<" inside parent!";
+        qDebug() << "Couldn't find call num "<<call->index()<<" inside parent!";
         return QModelIndex();
     }
     return createIndex(row, 0, call);
 }
 
+void ApiTraceModel::callChanged(ApiTraceCall *call)
+{
+    ApiTrace *trace = call->parentFrame()->parentTrace();
+
+    Q_ASSERT(trace);
+    trace->save();
+
+#if 0
+    qDebug()<<"Call changed = "<<call->edited();
+    qDebug()<<"\ttrace edited = "<<trace->edited();
+    qDebug()<<"\ttrace file = "<<trace->fileName();
+    qDebug()<<"\ttrace needs saving = "<<trace->needsSaving();
+#endif
+
+    ApiTraceFrame *frame = call->parentFrame();
+    int row = frame->calls.indexOf(call);
+    QModelIndex index = createIndex(row, 0, call);
+    emit dataChanged(index, index);
+}
+
 #include "apitracemodel.moc"
index 7a0394b2bc7facae2c5aa84766d04c0afdb3a5d3..752d7b654436a92c2e920a60de51a7e4438472b0 100644 (file)
@@ -50,6 +50,7 @@ public:
 private slots:
     void invalidateFrames();
     void appendFrames(int oldCount, int numAdded);
+    void callChanged(ApiTraceCall *call);
 
 private:
     ApiTraceEvent *item(const QModelIndex &index) const;
diff --git a/gui/argumentseditor.cpp b/gui/argumentseditor.cpp
new file mode 100644 (file)
index 0000000..04a1952
--- /dev/null
@@ -0,0 +1,439 @@
+#include "argumentseditor.h"
+
+#include "apitracecall.h"
+
+#include <QDebug>
+#include <QDoubleSpinBox>
+#include <QItemEditorFactory>
+#include <QSpinBox>
+
+#include <limits.h>
+#include <float.h>
+
+
+static bool
+isVariantEditable(const QVariant &var)
+{
+    switch (var.userType()) {
+    case QVariant::Bool:
+    case QVariant::Int:
+    case QVariant::UInt:
+    case QVariant::LongLong:
+    case QVariant::ULongLong:
+    case QVariant::Double:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static bool
+isVariantStringArray(const QVariant &var)
+{
+    if (var.isNull() || var.userType() != QMetaType::type("ApiArray"))
+        return false;
+
+    ApiArray array = var.value<ApiArray>();
+    QList<QVariant> origValues = array.values();
+    if (origValues.isEmpty() ||
+        origValues.first().userType() != QVariant::String)
+        return false;
+
+    return true;
+}
+
+ArgumentsItemEditorFactory::ArgumentsItemEditorFactory()
+    : QItemEditorFactory()
+{
+}
+
+QWidget * ArgumentsItemEditorFactory::createEditor(QVariant::Type type,
+                                                   QWidget *parent) const
+{
+    switch (type) {
+    case QVariant::Bool: {
+        BooleanComboBox *cb = new BooleanComboBox(parent);
+        cb->setFrame(false);
+        return cb;
+    }
+    case QVariant::UInt: {
+        QSpinBox *sb = new QSpinBox(parent);
+        sb->setFrame(false);
+        sb->setMaximum(INT_MAX);
+        return sb; }
+    case QVariant::Int: {
+        QSpinBox *sb = new QSpinBox(parent);
+        sb->setFrame(false);
+        sb->setMinimum(INT_MIN);
+        sb->setMaximum(INT_MAX);
+        return sb;
+    }
+    case QVariant::ULongLong: {
+        QSpinBox *sb = new QSpinBox(parent);
+        sb->setFrame(false);
+        sb->setMaximum(INT_MAX);
+        return sb; }
+    case QVariant::LongLong: {
+        QSpinBox *sb = new QSpinBox(parent);
+        sb->setFrame(false);
+        sb->setMinimum(INT_MIN);
+        sb->setMaximum(INT_MAX);
+        return sb;
+    }
+    case QVariant::Pixmap:
+        return new QLabel(parent);
+    case QVariant::Double: {
+        QDoubleSpinBox *sb = new QDoubleSpinBox(parent);
+        sb->setFrame(false);
+        sb->setMinimum(-DBL_MAX);
+        sb->setMaximum(DBL_MAX);
+        sb->setDecimals(8);
+        return sb;
+    }
+    default:
+        break;
+    }
+    return 0;
+}
+
+QByteArray
+ArgumentsItemEditorFactory::valuePropertyName(QVariant::Type type) const
+{
+    switch (type) {
+    case QVariant::Bool:
+        return "currentIndex";
+    case QVariant::UInt:
+    case QVariant::Int:
+    case QVariant::LongLong:
+    case QVariant::ULongLong:
+    case QVariant::Double:
+        return "value";
+#if 0
+    case QVariant::String:
+#endif
+    default:
+        return "text";
+    }
+}
+
+BooleanComboBox::BooleanComboBox(QWidget *parent)
+    : QComboBox(parent)
+{
+    addItem(tr("False"));
+    addItem(tr("True"));
+}
+
+void BooleanComboBox::setValue(bool value)
+{
+    setCurrentIndex(value ? 1 : 0);
+}
+
+bool BooleanComboBox::value() const
+{
+    return (currentIndex() == 1);
+}
+
+ArgumentsEditor::ArgumentsEditor(QWidget *parent)
+    : QDialog(parent),
+      m_model(new QStandardItemModel()),
+      m_call(0)
+{
+    init();
+}
+
+ArgumentsEditor::~ArgumentsEditor()
+{
+}
+
+void ArgumentsEditor::setCall(ApiTraceCall *call)
+{
+    m_call = call;
+    setupCall();
+}
+
+ApiTraceCall * ArgumentsEditor::call() const
+{
+    return m_call;
+}
+
+void ArgumentsEditor::init()
+{
+    m_ui.setupUi(this);
+
+    connect(m_ui.selectStringCB, SIGNAL(currentIndexChanged(int)),
+            SLOT(currentSourceChanged(int)));
+    connect(m_ui.glslEdit, SIGNAL(textChanged()),
+            SLOT(sourceChanged()));
+    connect(m_ui.revertButton, SIGNAL(clicked()),
+            SLOT(revert()));
+
+    m_ui.argsTree->setModel(m_model);
+    QItemEditorFactory *factory =
+        new ArgumentsItemEditorFactory();
+
+    QItemEditorFactory::setDefaultFactory(factory);
+}
+
+void ArgumentsEditor::setupCall()
+{
+    m_model->clear();
+
+    QStringList headers;
+    headers.append(tr("Argument"));
+    headers.append(tr("Value"));
+    m_model->setColumnCount(2);
+    m_model->setHorizontalHeaderLabels(headers);
+    m_ui.argsTabWidget->removeTab(
+        m_ui.argsTabWidget->indexOf(m_ui.shaderTab));
+
+    if (!m_call)
+        return;
+
+    m_ui.callLabel->setText(m_call->name());
+    QStandardItem *rootItem = m_model->invisibleRootItem();
+    for (int i = 0; i < m_call->argNames().count(); ++i) {
+        QString argName = m_call->argNames()[i];
+        QVariant val = m_call->arguments()[i];
+        QStandardItem *nameItem = new QStandardItem(argName);
+        nameItem->setFlags(nameItem->flags() ^ Qt::ItemIsEditable);
+        QList<QStandardItem*> topRow;
+        topRow.append(nameItem);
+
+        if (val.canConvert<ApiArray>()) {
+            ApiArray array = val.value<ApiArray>();
+            QList<QVariant> vals = array.values();
+
+            QVariant firstVal = vals.value(0);
+            if (firstVal.userType() == QVariant::String) {
+                m_ui.argsTabWidget->addTab(
+                    m_ui.shaderTab, argName);
+                setupShaderEditor(vals);
+                delete nameItem;
+                continue;
+            } else if (isVariantEditable(firstVal)) {
+                for (int i = 0; i < vals.count(); ++i) {
+                    QList<QStandardItem*> row;
+
+                    QStandardItem *idx = new QStandardItem();
+                    idx->setFlags(idx->flags() ^ Qt::ItemIsEditable);
+                    idx->setText(tr("%1)").arg(i));
+
+                    QStandardItem *col = new QStandardItem();
+                    col->setFlags(col->flags() | Qt::ItemIsEditable);
+                    col->setData(vals[i], Qt::DisplayRole);
+                    row.append(idx);
+                    row.append(col);
+                    nameItem->appendRow(row);
+                }
+            } else {
+                qDebug()<<"\tUnsupported array = "<<firstVal;
+                delete nameItem;
+                continue;
+            }
+        } else  if (val.canConvert<ApiPointer>()) {
+            ApiPointer ptr = val.value<ApiPointer>();
+            QStandardItem *item = new QStandardItem();
+            item->setFlags(item->flags() ^ Qt::ItemIsEditable);
+            item->setText(ptr.toString());
+            QIcon icon(":/resources/emblem-locked.png");
+            item->setIcon(icon);
+            item->setToolTip(tr("Argument is read-only"));
+            topRow.append(item);
+        } else if (val.canConvert<ApiEnum>()) {
+            ApiEnum en = val.value<ApiEnum>();
+            QStandardItem *item = new QStandardItem();
+            item->setFlags(item->flags() ^ Qt::ItemIsEditable);
+            item->setText(en.toString());
+            QIcon icon(":/resources/emblem-locked.png");
+            item->setIcon(icon);
+            item->setToolTip(tr("Argument is read-only"));
+            topRow.append(item);
+        } else if (val.canConvert<ApiBitmask>()) {
+            ApiBitmask mask = val.value<ApiBitmask>();
+            QStandardItem *item = new QStandardItem();
+            item->setFlags(item->flags() ^ Qt::ItemIsEditable);
+            item->setText(mask.toString());
+            QIcon icon(":/resources/emblem-locked.png");
+            item->setIcon(icon);
+            item->setToolTip(tr("Argument is read-only"));
+            topRow.append(item);
+        } else if (val.canConvert<ApiStruct>()) {
+            ApiStruct str = val.value<ApiStruct>();
+            QStandardItem *item = new QStandardItem();
+            item->setFlags(item->flags() ^ Qt::ItemIsEditable);
+            item->setText(str.toString());
+            QIcon icon(":/resources/emblem-locked.png");
+            item->setIcon(icon);
+            item->setToolTip(tr("Argument is read-only"));
+            topRow.append(item);
+        } else if (val.userType() == QVariant::ByteArray) {
+            QByteArray ba = val.value<QByteArray>();
+            QStandardItem *item = new QStandardItem();
+            item->setFlags(item->flags() ^ Qt::ItemIsEditable);
+            item->setText(
+                tr("<binary data, size = %1 bytes>").arg(ba.size()));
+            QIcon icon(":/resources/emblem-locked.png");
+            item->setIcon(icon);
+            item->setToolTip(tr("Argument is read-only"));
+            topRow.append(item);
+        } else {
+            QStandardItem *item
+                = new QStandardItem();
+
+            if (isVariantEditable(val)) {
+                item->setFlags(item->flags() | Qt::ItemIsEditable);
+            } else {
+                QIcon icon(":/resources/emblem-locked.png");
+                item->setIcon(icon);
+                item->setFlags(item->flags() ^ Qt::ItemIsEditable);
+                item->setToolTip(tr("Argument is read-only"));
+            }
+            item->setData(val, Qt::DisplayRole);
+            topRow.append(item);
+        }
+        rootItem->appendRow(topRow);
+    }
+}
+
+void ArgumentsEditor::setupShaderEditor(const QList<QVariant> &sources)
+{
+    m_ui.selectStringCB->clear();
+    m_ui.glslEdit->clear();
+    for (int i = 0; i < sources.count(); ++i) {
+        m_ui.selectStringCB->addItem(
+            tr("Shader string: %1").arg(i),
+            sources[i]);
+    }
+    m_ui.selectStringCB->setCurrentIndex(0);
+}
+
+void ArgumentsEditor::currentSourceChanged(int idx)
+{
+    QVariant source = m_ui.selectStringCB->itemData(idx);
+    QString str = source.toString();
+    m_ui.glslEdit->setPlainText(source.toString());
+    m_ui.lengthLabel->setText(
+        tr("%1").arg(str.length()));
+}
+
+void ArgumentsEditor::sourceChanged()
+{
+    QString str = m_ui.glslEdit->toPlainText();
+    m_ui.lengthLabel->setText(
+        tr("%1").arg(str.length()));
+
+    m_ui.selectStringCB->setItemData(
+        m_ui.selectStringCB->currentIndex(),
+        str);
+}
+
+void ArgumentsEditor::accept()
+{
+    QStringList argNames = m_call->argNames();
+    QList<QVariant> originalValues = m_call->arguments();
+    QList<QVariant> newValues;
+    bool changed = false;
+    for (int i = 0; i < argNames.count(); ++i) {
+        bool valChanged = false;
+        QString argName = argNames[i];
+        QVariant argValue = originalValues[i];
+        QVariant editorValue = valueForName(argName, argValue, &valChanged);
+        newValues.append(editorValue);
+#if 0
+        qDebug()<<"Arg = "<<argName;
+        qDebug()<<"\toriginal = "<<argValue;
+        qDebug()<<"\teditor   = "<<editorValue;
+        qDebug()<<"\tchanged  = "<<valChanged;
+#endif
+        if (valChanged)
+            changed = true;
+    }
+    if (changed)
+        m_call->setEditedValues(newValues);
+    QDialog::accept();
+}
+
+QVariant ArgumentsEditor::valueForName(const QString &name,
+                                       const QVariant &originalValue,
+                                       bool *changed) const
+{
+    QVariant val;
+
+    *changed = false;
+
+    //Handle string arrays specially
+    if (isVariantStringArray(originalValue)) {
+        ApiArray array = originalValue.value<ApiArray>();
+        return arrayFromEditor(array, changed);
+    }
+
+    for (int topRow = 0; topRow < m_model->rowCount(); ++topRow) {
+        QModelIndex nameIdx = m_model->index(topRow, 0, QModelIndex());
+        QString argName = nameIdx.data().toString();
+        /* we display shaders in a separate widget so
+         * the ordering might be different */
+        if (argName == name) {
+            if (originalValue.userType() == QMetaType::type("ApiArray")) {
+                ApiArray array = originalValue.value<ApiArray>();
+                val = arrayFromIndex(nameIdx, array, changed);
+            } else {
+                QModelIndex valIdx = m_model->index(topRow, 1, QModelIndex());
+                val = valIdx.data();
+                if (val != originalValue)
+                    *changed = true;
+            }
+        }
+    }
+    return val;
+}
+
+QVariant ArgumentsEditor::arrayFromIndex(const QModelIndex &parentIndex,
+                                         const ApiArray &origArray,
+                                         bool *changed) const
+{
+    QList<QVariant> origValues = origArray.values();
+
+    *changed = false;
+
+    if (origValues.isEmpty())
+        return QVariant::fromValue(ApiArray());
+
+    QList<QVariant> lst;
+    for (int i = 0; i < origValues.count(); ++i) {
+        QModelIndex valIdx = m_model->index(i, 1, parentIndex);
+        QVariant var = valIdx.data();
+        QVariant origValue = origValues[i];
+        if (var != origValue)
+            *changed = true;
+        //qDebug()<<"\t\tarray "<<i<<") "<<var;
+        lst.append(var);
+    }
+    return QVariant::fromValue(ApiArray(lst));
+}
+
+QVariant ArgumentsEditor::arrayFromEditor(const ApiArray &origArray,
+                                          bool *changed) const
+{
+    QList<QVariant> vals;
+    QList<QVariant> origValues = origArray.values();
+
+    Q_ASSERT(isVariantStringArray(QVariant::fromValue(origArray)));
+    *changed = false;
+    //shaders
+    for (int i = 0; i < m_ui.selectStringCB->count(); ++i) {
+        QVariant val = m_ui.selectStringCB->itemData(i);
+        QVariant origValue = origValues[i];
+        if (origValue != val)
+            *changed = true;
+        vals.append(val);
+    }
+    return QVariant::fromValue(ApiArray(vals));
+}
+
+void ArgumentsEditor::revert()
+{
+    m_call->revert();
+    setupCall();
+}
+
+#include "argumentseditor.moc"
diff --git a/gui/argumentseditor.h b/gui/argumentseditor.h
new file mode 100644 (file)
index 0000000..e6b3d8f
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef ARGUMENTSEDITOR_H
+#define ARGUMENTSEDITOR_H
+
+#include "apitracecall.h"
+#include "ui_argumentseditor.h"
+
+#include <QComboBox>
+#include <QDialog>
+#include <QItemEditorFactory>
+#include <QStandardItemModel>
+
+class ApiTraceCall;
+
+class BooleanComboBox : public QComboBox
+{
+    Q_OBJECT
+    Q_PROPERTY(bool value READ value WRITE setValue USER true)
+public:
+    BooleanComboBox(QWidget *parent);
+    void setValue(bool);
+    bool value() const;
+};
+
+class ArgumentsItemEditorFactory : public QItemEditorFactory
+{
+public:
+    ArgumentsItemEditorFactory();
+    QWidget *createEditor(QVariant::Type type, QWidget *parent) const;
+    QByteArray valuePropertyName(QVariant::Type) const;
+};
+
+class ArgumentsEditor : public QDialog
+{
+    Q_OBJECT
+public:
+    ArgumentsEditor(QWidget *parent=0);
+    ~ArgumentsEditor();
+
+
+    virtual void accept();
+
+    void setCall(ApiTraceCall *call);
+    ApiTraceCall *call() const;
+
+private slots:
+    void currentSourceChanged(int idx);
+    void sourceChanged();
+    void revert();
+private:
+    void init();
+    void setupCall();
+    void setupShaderEditor(const QList<QVariant> &sources);
+    QVariant valueForName(const QString &name,
+                          const QVariant &orignalValue,
+                          bool *changed) const;
+    QVariant arrayFromIndex(const QModelIndex &index,
+                            const ApiArray &array,
+                            bool *changed) const;
+    QVariant arrayFromEditor(const ApiArray &origArray,
+                             bool *changed) const;
+private:
+    Ui_ArgumentsEditor m_ui;
+    QStandardItemModel *m_model;
+
+    ApiTraceCall *m_call;
+};
+
+#endif
index 37898a490ceb49a53dab23edee628087df38ca51..1f5474425f214d79ca1e162d34b33ccb7f653e09 100644 (file)
@@ -14,27 +14,9 @@ static ApiTraceCall *
 apiCallFromTraceCall(const Trace::Call *call,
                      const QHash<QString, QUrl> &helpHash)
 {
-    ApiTraceCall *apiCall = new ApiTraceCall();
-    apiCall->name = QString::fromStdString(call->sig->name);
-    apiCall->index = call->no;
-
-    QString argumentsText;
-    for (int i = 0; i < call->sig->arg_names.size(); ++i) {
-        apiCall->argNames +=
-            QString::fromStdString(call->sig->arg_names[i]);
-    }
-    if (call->ret) {
-        VariantVisitor retVisitor;
-        call->ret->visit(retVisitor);
-        apiCall->returnValue = retVisitor.variant();
-    }
-    for (int i = 0; i < call->args.size(); ++i) {
-        VariantVisitor argVisitor;
-        call->args[i]->visit(argVisitor);
-        apiCall->argValues += argVisitor.variant();
-    }
+    ApiTraceCall *apiCall = new ApiTraceCall(call);
 
-    apiCall->helpUrl = helpHash.value(apiCall->name);
+    apiCall->setHelpUrl(helpHash.value(apiCall->name()));
 
     //force generation of the internal state
     apiCall->filterText();
@@ -74,8 +56,7 @@ void LoaderThread::run()
 
     Trace::Parser p;
     if (p.open(m_fileName.toLatin1().constData())) {
-        Trace::Call *call;
-        call = p.parse_call();
+        Trace::Call *call = p.parse_call();
         while (call) {
             //std::cout << *call;
             if (!currentFrame) {
@@ -85,7 +66,7 @@ void LoaderThread::run()
             }
             ApiTraceCall *apiCall =
                 apiCallFromTraceCall(call, helpHash);
-            apiCall->parentFrame = currentFrame;
+            apiCall->setParentFrame(currentFrame);
             currentFrame->calls.append(apiCall);
             if (ApiTrace::isCallAFrameMarker(apiCall,
                                              m_frameMarker)) {
@@ -96,6 +77,7 @@ void LoaderThread::run()
                     frames.clear();
                 }
             }
+            delete call;
             call = p.parse_call();
         }
     }
index f63b87e0600323632a3612cbc85da8ee628cf881..cd63962d068f704e57984efa77002ccfc0024348 100644 (file)
@@ -5,6 +5,7 @@
 #include "apicalldelegate.h"
 #include "apitracemodel.h"
 #include "apitracefilter.h"
+#include "argumentseditor.h"
 #include "imageviewer.h"
 #include "jumpwidget.h"
 #include "retracer.h"
@@ -101,19 +102,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);
@@ -138,6 +140,14 @@ 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);
@@ -166,10 +176,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"));
@@ -233,15 +242,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);
@@ -250,7 +259,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;
@@ -277,6 +286,14 @@ 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);
 }
@@ -579,12 +596,15 @@ void MainWindow::initObjects()
     m_ui.callView->resizeColumnToContents(0);
     m_ui.callView->header()->swapSections(0, 1);
     m_ui.callView->setColumnWidth(1, 42);
+    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.vertexDataDock->hide();
     m_ui.stateDock->hide();
@@ -619,6 +639,10 @@ 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_retracer, SIGNAL(finished(const QString&)),
             this, SLOT(replayFinished(const QString&)));
@@ -657,6 +681,8 @@ void MainWindow::initConnections()
 
     connect(m_ui.callView, SIGNAL(activated(const QModelIndex &)),
             this, SLOT(callItemSelected(const QModelIndex &)));
+    connect(m_ui.callView, SIGNAL(customContextMenuRequested(QPoint)),
+            this, SLOT(customContextMenuRequested(QPoint)));
 
     connect(m_ui.surfacesTreeWidget,
             SIGNAL(customContextMenuRequested(const QPoint &)),
@@ -864,4 +890,48 @@ void MainWindow::fillState(bool nonDefaults)
     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();
+}
+
 #include "mainwindow.moc"
index de413c0b689dfb352721e34529c8e0bf401e5bad..07a1e912798767bcef9bb73519a7eb475d33a412 100644 (file)
@@ -12,6 +12,7 @@ class ApiTraceFilter;
 class ApiTraceFrame;
 class ApiTraceModel;
 class ApiTraceState;
+class ArgumentsEditor;
 class ImageViewer;
 class JumpWidget;
 class QModelIndex;
@@ -57,6 +58,10 @@ private slots:
     void slotSearchNext(const QString &str, Qt::CaseSensitivity sensitivity);
     void slotSearchPrev(const QString &str, Qt::CaseSensitivity sensitivity);
     void fillState(bool nonDefaults);
+    void customContextMenuRequested(QPoint pos);
+    void editCall();
+    void slotStartedSaving();
+    void slotSaved();
 
 private:
     void initObjects();
@@ -75,8 +80,6 @@ private:
 
     QProgressBar *m_progressBar;
 
-    QString m_traceFileName;
-
     ApiTraceEvent *m_selectedEvent;
 
     ApiTraceEvent *m_stateEvent;
@@ -91,6 +94,8 @@ private:
     SearchWidget *m_searchWidget;
 
     TraceProcess *m_traceProcess;
+
+    ArgumentsEditor *m_argsEditor;
 };
 
 
index a989c425506c6ae89b66320a763d59d17e5d9015..6f6597fef7b4809c9396d17214c8ddfb30b3a13b 100644 (file)
@@ -3,8 +3,11 @@
     <file>resources/application-exit.png</file>
     <file>resources/dialog-close.png</file>
     <file>resources/dialog-information.png</file>
+    <file>resources/document-edit.png</file>
     <file>resources/document-new.png</file>
     <file>resources/document-open.png</file>
+    <file>resources/edit-undo.png</file>
+    <file>resources/emblem-locked.png</file>
     <file>resources/go-down-search.png</file>
     <file>resources/go-up-search.png</file>
     <file>resources/media-playback-start.png</file>
diff --git a/gui/resources/document-edit.png b/gui/resources/document-edit.png
new file mode 100644 (file)
index 0000000..c5883ac
Binary files /dev/null and b/gui/resources/document-edit.png differ
diff --git a/gui/resources/edit-undo.png b/gui/resources/edit-undo.png
new file mode 100644 (file)
index 0000000..c893a1a
Binary files /dev/null and b/gui/resources/edit-undo.png differ
diff --git a/gui/resources/emblem-locked.png b/gui/resources/emblem-locked.png
new file mode 100644 (file)
index 0000000..60024fd
Binary files /dev/null and b/gui/resources/emblem-locked.png differ
diff --git a/gui/saverthread.cpp b/gui/saverthread.cpp
new file mode 100644 (file)
index 0000000..ac726a1
--- /dev/null
@@ -0,0 +1,259 @@
+#include "saverthread.h"
+
+#include "trace_write.hpp"
+
+#include <QFile>
+#include <QHash>
+#include <QUrl>
+
+#include <QDebug>
+
+
+static Trace::FunctionSig *
+createFunctionSig(ApiTraceCall *call, unsigned id)
+{
+    Trace::FunctionSig *sig = new Trace::FunctionSig();
+
+    sig->id = id;
+    sig->name = qstrdup(call->name().toLocal8Bit());
+
+    QStringList args = call->argNames();
+    sig->num_args = args.count();
+    sig->args = new const char*[args.count()];
+    for (int i = 0; i < args.count(); ++i) {
+        sig->args[i] = qstrdup(args[i].toLocal8Bit());
+    }
+
+    return sig;
+}
+
+static void
+deleteFunctionSig(Trace::FunctionSig *sig)
+{
+    for (int i = 0; i < sig->num_args; ++i) {
+        delete [] sig->args[i];
+    }
+    delete [] sig->args;
+    delete [] sig->name;
+    delete sig;
+}
+
+static Trace::StructSig *
+createStructSig(const ApiStruct &str, unsigned id)
+{
+    ApiStruct::Signature aSig = str.signature();
+
+    Trace::StructSig *sig = new Trace::StructSig();
+    sig->id = id;
+    sig->name = qstrdup(aSig.name.toLocal8Bit());
+    sig->num_members = aSig.memberNames.count();
+    char **members = new char*[aSig.memberNames.count()];
+    sig->members = (const char **)members;
+    for (int i = 0; i < aSig.memberNames.count(); ++i) {
+        members[i] = qstrdup(aSig.memberNames[i].toLocal8Bit());
+    }
+    return sig;
+}
+
+static void
+deleteStructSig(Trace::StructSig *sig)
+{
+    for (int i = 0; i < sig->num_members; ++i) {
+        delete [] sig->members[i];
+    }
+    delete [] sig->members;
+    delete [] sig->name;
+    delete sig;
+}
+
+static Trace::EnumSig *
+createEnumSig(const ApiEnum &en, unsigned id)
+{
+    Trace::EnumSig *sig = new Trace::EnumSig();
+
+    sig->id = id;
+    sig->name = qstrdup(en.name().toLocal8Bit());
+    sig->value = en.value().toLongLong();
+
+    return sig;
+}
+
+static void
+deleteEnumSig(Trace::EnumSig *sig)
+{
+    delete [] sig->name;
+    delete sig;
+}
+
+static Trace::BitmaskSig *
+createBitmaskSig(const ApiBitmask &bt, unsigned id)
+{
+    ApiBitmask::Signature bsig = bt.signature();
+    ApiBitmask::Signature::const_iterator itr;
+
+    Trace::BitmaskSig *sig = new Trace::BitmaskSig();
+    Trace::BitmaskVal *values = new Trace::BitmaskVal[bsig.count()];
+
+    sig->id = id;
+    sig->count = bsig.count();
+    sig->values = values;
+
+    int i = 0;
+    for (itr = bsig.constBegin(); itr != bsig.constEnd(); ++itr, ++i) {
+        values[i].name = qstrdup(itr->first.toLocal8Bit());
+        values[i].value = itr->second;
+    }
+
+    return sig;
+}
+
+static void
+deleteBitmaskSig(Trace::BitmaskSig *sig)
+{
+    for (int i = 0; i < sig->count; ++i) {
+        delete [] sig->values[i].name;
+    }
+    delete [] sig->values;
+    delete sig;
+}
+
+static void
+writeValue(const QVariant &var, unsigned &id)
+{
+    int arrayType   = QMetaType::type("ApiArray");
+    int bitmaskType = QMetaType::type("ApiBitmask");
+    int structType  = QMetaType::type("ApiStruct");
+    int pointerType = QMetaType::type("ApiPointer");
+    int enumType    = QMetaType::type("ApiEnum");
+    int type = var.userType();
+
+    switch(type) {
+    case QVariant::Bool:
+        Trace::LiteralBool(var.toBool());
+        break;
+    case QVariant::ByteArray: {
+        QByteArray ba = var.toByteArray();
+        Trace::LiteralBlob((const void*)ba.constData(), ba.size());
+    }
+        break;
+    case QVariant::Double:
+        Trace::LiteralFloat(var.toDouble());
+        break;
+    case QMetaType::Float:
+        Trace::LiteralFloat(var.toFloat());
+        break;
+    case QVariant::Int:
+        Trace::LiteralSInt(var.toInt());
+        break;
+    case QVariant::LongLong:
+        Trace::LiteralSInt(var.toLongLong());
+        break;
+    case QVariant::String: {
+        QString str = var.toString();
+        Trace::LiteralString(str.toLocal8Bit().constData(), str.length());
+    }
+        break;
+    case QVariant::UInt:
+        Trace::LiteralUInt(var.toInt());
+        break;
+    case QVariant::ULongLong:
+        Trace::LiteralUInt(var.toLongLong());
+        break;
+    default:
+        if (type == arrayType) {
+            ApiArray array = var.value<ApiArray>();
+            QList<QVariant> vals = array.values();
+            Trace::BeginArray(vals.count());
+            foreach(QVariant el, vals) {
+                Trace::BeginElement();
+                writeValue(el, ++id);
+                Trace::EndElement();
+            }
+            Trace::EndArray();
+        } else if (type == bitmaskType) {
+            ApiBitmask bm = var.value<ApiBitmask>();
+            Trace::BitmaskSig *sig = createBitmaskSig(bm, ++id);
+            LiteralBitmask(*sig, bm.value());
+            deleteBitmaskSig(sig);
+        } else if (type == structType) {
+            ApiStruct apiStr = var.value<ApiStruct>();
+            QList<QVariant> vals = apiStr.values();
+            Trace::StructSig *str = createStructSig(apiStr, ++id);
+            Trace::BeginStruct(str);
+            foreach(QVariant val, vals) {
+                writeValue(val, ++id);
+            }
+            Trace::EndStruct();
+            deleteStructSig(str);
+        } else if (type == pointerType) {
+            ApiPointer apiPtr = var.value<ApiPointer>();
+            //Trace::BeginArray(1);
+            //Trace::BeginElement();
+            Trace::LiteralOpaque((const void*)apiPtr.value());
+            //Trace::EndElement();
+            //Trace::EndArray();
+        } else if (type == enumType) {
+            ApiEnum apiEnum = var.value<ApiEnum>();
+            Trace::EnumSig *sig = createEnumSig(apiEnum, ++id);
+            Trace::LiteralEnum(sig);
+            deleteEnumSig(sig);
+        } else {
+            qWarning()<<"Unsupported write variant : "
+                      << QMetaType::typeName(type);
+        }
+    }
+}
+
+SaverThread::SaverThread(QObject *parent)
+    : QThread(parent)
+{
+}
+
+void SaverThread::saveFile(const QString &fileName,
+                           const QList<ApiTraceCall*> &calls)
+{
+    m_fileName = fileName;
+    m_calls = calls;
+    start();
+}
+
+void SaverThread::run()
+{
+    qputenv("TRACE_PATH", m_fileName.toLocal8Bit());
+    unsigned id = 0;
+
+    Trace::Open();
+    for (int i = 0; i < m_calls.count(); ++i) {
+        ApiTraceCall *call = m_calls[i];
+        Trace::FunctionSig *funcSig = createFunctionSig(call, ++id);
+        unsigned callNo = Trace::BeginEnter(*funcSig);
+        {
+            //args
+            QVariantList vars = call->arguments();
+            int index = 0;
+            foreach(QVariant var, vars) {
+                Trace::BeginArg(index++);
+                writeValue(var, ++id);
+                Trace::EndArg();
+            }
+        }
+        Trace::EndEnter();
+        Trace::BeginLeave(callNo);
+        {
+            QVariant ret = call->returnValue();
+            if (!ret.isNull()) {
+                Trace::BeginReturn();
+                writeValue(ret, ++id);
+                Trace::EndReturn();
+            }
+        }
+        Trace::EndLeave();
+
+        deleteFunctionSig(funcSig);
+    }
+    Trace::Close();
+
+    emit traceSaved();
+}
+
+#include "saverthread.moc"
diff --git a/gui/saverthread.h b/gui/saverthread.h
new file mode 100644 (file)
index 0000000..5bea5b7
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef SAVERTHREAD_H
+#define SAVERTHREAD_H
+
+
+#include "apitrace.h"
+#include <QThread>
+#include <QList>
+
+class ApiTraceCall;
+class ApiTraceFrame;
+
+class SaverThread : public QThread
+{
+    Q_OBJECT
+public:
+    SaverThread(QObject *parent=0);
+
+public slots:
+    void saveFile(const QString &fileName,
+                  const QList<ApiTraceCall*> &calls);
+
+signals:
+    void traceSaved();
+
+protected:
+    virtual void run();
+
+private:
+    QString m_fileName;
+    QList<ApiTraceCall*> m_calls;
+};
+
+
+#endif
diff --git a/gui/ui/argumentseditor.ui b/gui/ui/argumentseditor.ui
new file mode 100644 (file)
index 0000000..abfb918
--- /dev/null
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ArgumentsEditor</class>
+ <widget class="QDialog" name="ArgumentsEditor">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>481</width>
+    <height>615</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Trace Arguments Editor</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>false</bool>
+  </property>
+  <property name="modal">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="callLabel">
+       <property name="font">
+        <font>
+         <pointsize>16</pointsize>
+         <weight>50</weight>
+         <bold>false</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>TextLabel</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <spacer name="horizontalSpacer_3">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>128</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="revertButton">
+       <property name="enabled">
+        <bool>true</bool>
+       </property>
+       <property name="text">
+        <string>Revert</string>
+       </property>
+       <property name="icon">
+        <iconset resource="../qapitrace.qrc">
+         <normaloff>:/resources/edit-undo.png</normaloff>:/resources/edit-undo.png</iconset>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QTabWidget" name="argsTabWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="tab">
+      <attribute name="title">
+       <string>Arguments</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <item>
+        <widget class="QTreeView" name="argsTree">
+         <attribute name="headerVisible">
+          <bool>false</bool>
+         </attribute>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="shaderTab">
+      <attribute name="title">
+       <string>Shader</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_3">
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_4">
+         <item>
+          <widget class="QComboBox" name="selectStringCB"/>
+         </item>
+         <item>
+          <spacer name="horizontalSpacer_4">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout_3">
+           <item>
+            <widget class="QLabel" name="label">
+             <property name="text">
+              <string>Length:</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLabel" name="lengthLabel">
+             <property name="text">
+              <string>100</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <widget class="GLSLEdit" name="glslEdit"/>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>GLSLEdit</class>
+   <extends>QPlainTextEdit</extends>
+   <header>glsledit.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources>
+  <include location="../qapitrace.qrc"/>
+ </resources>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>ArgumentsEditor</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>ArgumentsEditor</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
index f77a275524a611e76ceea8586806f3819bd45740..3187b9972ab1ab125fad58f87199de81a8bc9547 100644 (file)
@@ -148,10 +148,6 @@ void Open(void) {
     }
 }
 
-void Close(void) {
-    _Close();
-}
-
 static unsigned call_no = 0;
 
 inline bool lookup(std::vector<bool> &map, size_t index) {
@@ -169,6 +165,15 @@ static std::vector<bool> enums;
 static std::vector<bool> bitmasks;
 
 
+void Close(void) {
+    _Close();
+    call_no = 0;
+    functions = std::vector<bool>();
+    structs = std::vector<bool>();
+    enums = std::vector<bool>();
+    bitmasks = std::vector<bool>();
+}
+
 unsigned BeginEnter(const FunctionSig &function) {
     OS::AcquireMutex();
     Open();