#include "trace_model.hpp"
#include <QDebug>
+#include <QLocale>
#include <QObject>
#define QT_USE_FAST_OPERATOR_PLUS
#include <QStringBuilder>
+#include <QTextDocument>
+
+const char * const styleSheet =
+ ".call {\n"
+ " font-weight:bold;\n"
+ // text shadow looks great but doesn't work well in qtwebkit 4.7
+ " /*text-shadow: 0px 2px 3px #555;*/\n"
+ " font-size: 1.2em;\n"
+ "}\n"
+ ".arg-name {\n"
+ " border: 1px solid rgb(238,206,0);\n"
+ " border-radius: 4px;\n"
+ " background: yellow;\n"
+ " padding: 2px;\n"
+ " box-shadow: 0px 1px 3px dimgrey;\n"
+ " -webkit-transition: background 1s linear;\n"
+ "}\n"
+ ".arg-name:hover {\n"
+ " background: white;\n"
+ "}\n"
+ ".arg-value {\n"
+ " color: #0000ff;\n"
+ "}\n"
+ ".error {\n"
+ " border: 1px solid rgb(255,0,0);\n"
+ " margin: 10px;\n"
+ " padding: 1;\n"
+ " border-radius: 4px;\n"
+ // also looks great but qtwebkit doesn't support it
+ //" background: #6fb2e5;\n"
+ //" box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
+ //" -o-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
+ //" -webkit-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
+ //" -moz-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
+ "}\n";
ApiPointer::ApiPointer(unsigned long long val)
: m_value(val)
return QLatin1String("NULL");
}
-QString apiVariantToString(const QVariant &variant)
+// Qt::convertFromPlainText doesn't do precisely what we want
+static QString
+plainTextToHTML(const QString & plain, bool multiLine)
+{
+ int col = 0;
+ bool quote = false;
+ QString rich;
+ for (int i = 0; i < plain.length(); ++i) {
+ if (plain[i] == QLatin1Char('\n')){
+ if (multiLine) {
+ rich += QLatin1String("<br>\n");
+ } else {
+ rich += QLatin1String("\\n");
+ }
+ col = 0;
+ quote = true;
+ } else {
+ if (plain[i] == QLatin1Char('\t')){
+ if (multiLine) {
+ rich += QChar(0x00a0U);
+ ++col;
+ while (col % 8) {
+ rich += QChar(0x00a0U);
+ ++col;
+ }
+ } else {
+ rich += QLatin1String("\\t");
+ }
+ quote = true;
+ } else if (plain[i].isSpace()) {
+ rich += QChar(0x00a0U);
+ quote = true;
+ } else if (plain[i] == QLatin1Char('<')) {
+ rich += QLatin1String("<");
+ } else if (plain[i] == QLatin1Char('>')) {
+ rich += QLatin1String(">");
+ } else if (plain[i] == QLatin1Char('&')) {
+ rich += QLatin1String("&");
+ } else {
+ rich += plain[i];
+ }
+ ++col;
+ }
+ }
+
+ if (quote) {
+ return QLatin1Literal("\"") + rich + QLatin1Literal("\"");
+ }
+
+ return rich;
+}
+
+QString apiVariantToString(const QVariant &variant, bool multiLine)
{
if (variant.userType() == QVariant::Double) {
return QString::number(variant.toFloat());
}
}
+ if (variant.userType() == QVariant::String) {
+ return plainTextToHTML(variant.toString(), multiLine);
+ }
+
if (variant.userType() < QVariant::UserType) {
return variant.toString();
}
void VariantVisitor::visit(Trace::Null *)
{
- m_variant = QVariant(QLatin1String("NULL"));
+ m_variant = QVariant::fromValue(ApiPointer(0));
}
void VariantVisitor::visit(Trace::Bool *node)
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(m_name);
for (int i = 0; i < m_argNames.count(); ++i) {
richText += QLatin1String("<span style=\"color:#0000ff\">");
- QString argText = apiVariantToString(m_argValues[i]);
+ QString argText = apiVariantToString(argValues[i]);
//if arguments are really long (e.g. shader text), cut them
// and elide it
if (!m_staticText)
m_staticText = new QStaticText(richText);
+ else
+ m_staticText->setText(richText);
QTextOption opt;
opt.setWrapMode(QTextOption::NoWrap);
m_staticText->setTextOption(opt);
if (!m_richText.isEmpty())
return m_richText;
+ m_richText = QLatin1String("<div class=\"call\">");
+
if (m_helpUrl.isEmpty()) {
- m_richText = QString::fromLatin1(
- "%1) <span style=\"font-weight:bold\">%2</span>(")
- .arg(m_index)
- .arg(m_name);
+ m_richText += QString::fromLatin1(
+ "%1) <span class=\"callName\">%2</span>(")
+ .arg(m_index)
+ .arg(m_name);
} else {
- m_richText = QString::fromLatin1(
- "%1) <span style=\"font-weight:bold\"><a href=\"%2\">%3</a></span>(")
+ m_richText += QString::fromLatin1(
+ "%1) <span class=\"callName\"><a href=\"%2\">%3</a></span>(")
.arg(m_index)
.arg(m_helpUrl.toString())
.arg(m_name);
}
+ QVariantList argValues = arguments();
for (int i = 0; i < m_argNames.count(); ++i) {
- m_richText += m_argNames[i] +
- QLatin1Literal(" = ") +
- QLatin1Literal("<span style=\"color:#0000ff\">") +
- apiVariantToString(m_argValues[i]) +
- QLatin1Literal("</span>");
+ m_richText +=
+ QLatin1String("<span class=\"arg-name\">") +
+ m_argNames[i] +
+ QLatin1String("</span>") +
+ QLatin1Literal(" = ") +
+ QLatin1Literal("<span class=\"arg-value\">") +
+ apiVariantToString(argValues[i], true) +
+ QLatin1Literal("</span>");
if (i < m_argNames.count() - 1)
m_richText += QLatin1String(", ");
}
m_richText +=
QLatin1String(" = ") +
QLatin1String("<span style=\"color:#0000ff\">") +
- apiVariantToString(m_returnValue) +
+ apiVariantToString(m_returnValue, true) +
QLatin1String("</span>");
}
+ m_richText += QLatin1String("</div>");
+
+ if (hasError()) {
+ QString errorStr =
+ QString::fromLatin1(
+ "<div class=\"error\">%1</div>")
+ .arg(m_error);
+ m_richText += errorStr;
+ }
+
+ m_richText =
+ QString::fromLatin1(
+ "<html><head><style type=\"text/css\" media=\"all\">"
+ "%1</style></head><body>%2</body></html>")
+ .arg(styleSheet)
+ .arg(m_richText);
m_richText.squeeze();
+
+ //qDebug()<<m_richText;
return m_richText;
}
if (!m_filterText.isEmpty())
return m_filterText;
+ QVariantList argValues = arguments();
m_filterText = m_name + QLatin1Literal("(");
for (int i = 0; i < m_argNames.count(); ++i) {
m_filterText += m_argNames[i] +
QLatin1Literal(" = ") +
- apiVariantToString(m_argValues[i]);
- if (m_argValues[i].type() == QVariant::ByteArray) {
- m_hasBinaryData = true;
- m_binaryDataIndex = i;
- }
+ apiVariantToString(argValues[i]);
if (i < m_argNames.count() - 1)
m_filterText += QLatin1String(", ");
}
if (m_staticText && !m_staticText->text().isEmpty())
return *m_staticText;
- QString richText =
- QString::fromLatin1("<span style=\"font-weight:bold\">Frame %1</span>").arg(number);
+ QString richText;
+
+ //mark the frame if it uploads more than a meg a frame
+ if (m_binaryDataSize > (1024*1024)) {
+ richText =
+ QObject::tr(
+ "<span style=\"font-weight:bold;\">"
+ "Frame %1</span>"
+ "<span style=\"font-style:italic;\">"
+ " (%2MB)</span>")
+ .arg(number)
+ .arg(double(m_binaryDataSize / (1024.*1024.)), 0, 'g', 2);
+ } else {
+ richText =
+ QObject::tr(
+ "<span style=\"font-weight:bold\">Frame %1</span>")
+ .arg(number);
+ }
if (!m_staticText)
m_staticText = new QStaticText(richText);
int ApiTraceFrame::numChildren() const
{
- return calls.count();
+ return m_calls.count();
}
ApiTraceFrame::ApiTraceFrame()
: ApiTraceEvent(ApiTraceEvent::Frame),
- m_parentTrace(0)
+ m_parentTrace(0),
+ m_binaryDataSize(0)
{
}
m_shaderSources[type] = source;
}
- QVariantList textureUnits =
- parsedJson[QLatin1String("textures")].toList();
- for (int i = 0; i < textureUnits.count(); ++i) {
- QVariantMap unit = textureUnits[i].toMap();
- for (itr = unit.constBegin(); itr != unit.constEnd(); ++itr) {
- QVariantMap target = itr.value().toMap();
- if (target.count()) {
- QVariantList levels = target[QLatin1String("levels")].toList();
- for (int j = 0; j < levels.count(); ++j) {
- QVariantMap level = levels[j].toMap();
- QVariantMap image = level[QLatin1String("image")].toMap();
- QSize size(image[QLatin1String("__width__")].toInt(),
- image[QLatin1String("__height__")].toInt());
- QString cls = image[QLatin1String("__class__")].toString();
- QString type = image[QLatin1String("__type__")].toString();
- bool normalized =
- image[QLatin1String("__normalized__")].toBool();
- int numChannels =
- image[QLatin1String("__channels__")].toInt();
-
- Q_ASSERT(type == QLatin1String("uint8"));
- Q_ASSERT(normalized == true);
-
- QByteArray dataArray =
- image[QLatin1String("__data__")].toByteArray();
-
- ApiTexture tex;
- tex.setSize(size);
- tex.setNumChannels(numChannels);
- tex.setLevel(j);
- tex.setUnit(i);
- tex.setTarget(itr.key());
- tex.contentsFromBase64(dataArray);
-
- m_textures.append(tex);
- }
- }
- }
+ QVariantMap textures =
+ parsedJson[QLatin1String("textures")].toMap();
+ for (itr = textures.constBegin(); itr != textures.constEnd(); ++itr) {
+ QVariantMap image = itr.value().toMap();
+ QSize size(image[QLatin1String("__width__")].toInt(),
+ image[QLatin1String("__height__")].toInt());
+ QString cls = image[QLatin1String("__class__")].toString();
+ QString type = image[QLatin1String("__type__")].toString();
+ bool normalized =
+ image[QLatin1String("__normalized__")].toBool();
+ int numChannels =
+ image[QLatin1String("__channels__")].toInt();
+
+ Q_ASSERT(type == QLatin1String("uint8"));
+ Q_ASSERT(normalized == true);
+
+ QByteArray dataArray =
+ image[QLatin1String("__data__")].toByteArray();
+
+ ApiTexture tex;
+ tex.setSize(size);
+ tex.setNumChannels(numChannels);
+ tex.setLabel(itr.key());
+ tex.contentsFromBase64(dataArray);
+
+ m_textures.append(tex);
}
QVariantMap fbos =
VariantVisitor argVisitor;
call->args[i]->visit(argVisitor);
m_argValues += argVisitor.variant();
+ if (m_argValues[i].type() == QVariant::ByteArray) {
+ m_hasBinaryData = true;
+ m_binaryDataIndex = i;
+ }
}
}
void ApiTraceCall::setEditedValues(const QVariantList &lst)
{
- ApiTrace *trace = 0;
- if (m_parentFrame)
- trace = m_parentFrame->parentTrace();
+ ApiTrace *trace = parentTrace();
+
m_editedValues = lst;
+ //lets regenerate data
+ m_richText = QString();
+ m_filterText = QString();
+ delete m_staticText;
+ m_staticText = 0;
if (trace) {
if (!lst.isEmpty()) {
return m_value;
}
+bool ApiTraceCall::hasError() const
+{
+ return !m_error.isEmpty();
+}
+
+QString ApiTraceCall::error() const
+{
+ return m_error;
+}
+
+void ApiTraceCall::setError(const QString &msg)
+{
+ if (m_error != msg) {
+ ApiTrace *trace = parentTrace();
+ m_error = msg;
+ m_richText = QString();
+ if (trace)
+ trace->callError(this);
+ }
+}
+
+ApiTrace * ApiTraceCall::parentTrace() const
+{
+ if (m_parentFrame)
+ return m_parentFrame->parentTrace();
+ return NULL;
+}
+
+void ApiTraceFrame::addCall(ApiTraceCall *call)
+{
+ m_calls.append(call);
+ if (call->hasBinaryData()) {
+ QByteArray data =
+ call->arguments()[call->binaryDataIndex()].toByteArray();
+ m_binaryDataSize += data.size();
+ }
+}
+
+QList<ApiTraceCall*> ApiTraceFrame::calls() const
+{
+ return m_calls;
+}
+
+ApiTraceCall * ApiTraceFrame::call(int idx) const
+{
+ return m_calls.value(idx);
+}
+
+int ApiTraceFrame::callIndex(ApiTraceCall *call) const
+{
+ return m_calls.indexOf(call);
+}
+
+bool ApiTraceFrame::isEmpty() const
+{
+ return m_calls.isEmpty();
+}
+
+int ApiTraceFrame::binaryDataSize() const
+{
+ return m_binaryDataSize;
+}
+