]> git.cworth.org Git - apitrace/blob - gui/apitracecall.cpp
Fix HTML representation of multiline strings.
[apitrace] / gui / apitracecall.cpp
1 #include "apitracecall.h"
2
3 #include "apitrace.h"
4 #include "trace_model.hpp"
5
6 #include <QDebug>
7 #include <QObject>
8 #define QT_USE_FAST_OPERATOR_PLUS
9 #include <QStringBuilder>
10 #include <QTextDocument>
11
12 const char * const styleSheet =
13     ".call {\n"
14     "    font-weight:bold;\n"
15     // text shadow looks great but doesn't work well in qtwebkit 4.7
16     "    /*text-shadow: 0px 2px 3px #555;*/\n"
17     "    font-size: 1.2em;\n"
18     "}\n"
19     ".arg-name {\n"
20     "    border: 1px solid rgb(238,206,0);\n"
21     "    border-radius: 4px;\n"
22     "    background: yellow;\n"
23     "    padding: 2px;\n"
24     "    box-shadow: 0px 1px 3px dimgrey;\n"
25     "    -webkit-transition: background 1s linear;\n"
26     "}\n"
27     ".arg-name:hover {\n"
28     "    background: white;\n"
29     "}\n"
30     ".arg-value {\n"
31     "    color: #0000ff;\n"
32     "}\n"
33     ".error {\n"
34     "    border: 1px solid rgb(255,0,0);\n"
35     "    margin: 10px;\n"
36     "    padding: 1;\n"
37     "    border-radius: 4px;\n"
38     // also looks great but qtwebkit doesn't support it
39     //"    background: #6fb2e5;\n"
40     //"    box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
41     //"    -o-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
42     //"    -webkit-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
43     //"    -moz-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
44     "}\n";
45
46 ApiPointer::ApiPointer(unsigned long long val)
47     : m_value(val)
48 {
49 }
50
51 QString ApiPointer::toString() const
52 {
53     if (m_value)
54         return QString("0x%1").arg(m_value, 0, 16);
55     else
56         return QLatin1String("NULL");
57 }
58
59 // Qt::convertFromPlainText doesn't do precisely what we want
60 static QString
61 plainTextToHTML(const QString & plain, bool multiLine)
62 {
63     int col = 0;
64     bool quote = false;
65     QString rich;
66     for (int i = 0; i < plain.length(); ++i) {
67         if (plain[i] == QLatin1Char('\n')){
68             if (multiLine) {
69                 rich += QLatin1String("<br>\n");
70             } else {
71                 rich += QLatin1String("\\n");
72             }
73             col = 0;
74             quote = true;
75         } else {
76             if (plain[i] == QLatin1Char('\t')){
77                 if (multiLine) {
78                     rich += QChar(0x00a0U);
79                     ++col;
80                     while (col % 8) {
81                         rich += QChar(0x00a0U);
82                         ++col;
83                     }
84                 } else {
85                     rich += QLatin1String("\\t");
86                 }
87                 quote = true;
88             } else if (plain[i].isSpace()) {
89                 rich += QChar(0x00a0U);
90                 quote = true;
91             } else if (plain[i] == QLatin1Char('<')) {
92                 rich += QLatin1String("&lt;");
93             } else if (plain[i] == QLatin1Char('>')) {
94                 rich += QLatin1String("&gt;");
95             } else if (plain[i] == QLatin1Char('&')) {
96                 rich += QLatin1String("&amp;");
97             } else {
98                 rich += plain[i];
99             }
100             ++col;
101         }
102     }
103
104     if (quote) {
105         return QLatin1Literal("\"") + rich + QLatin1Literal("\"");
106     }
107
108     return rich;
109 }
110
111 QString apiVariantToString(const QVariant &variant, bool multiLine)
112 {
113     if (variant.userType() == QVariant::Double) {
114         return QString::number(variant.toFloat());
115     }
116     if (variant.userType() == QVariant::ByteArray) {
117         if (variant.toByteArray().size() < 1024) {
118             int bytes = variant.toByteArray().size();
119             return QObject::tr("[binary data, size = %1 bytes]").arg(bytes);
120         } else {
121             float kb = variant.toByteArray().size()/1024.;
122             return QObject::tr("[binary data, size = %1 kb]").arg(kb);
123         }
124     }
125
126     if (variant.userType() == QVariant::String) {
127         return plainTextToHTML(variant.toString(), multiLine);
128     }
129
130     if (variant.userType() < QVariant::UserType) {
131         return variant.toString();
132     }
133
134     if (variant.canConvert<ApiPointer>()) {
135         return variant.value<ApiPointer>().toString();
136     }
137     if (variant.canConvert<ApiBitmask>()) {
138         return variant.value<ApiBitmask>().toString();
139     }
140     if (variant.canConvert<ApiStruct>()) {
141         return variant.value<ApiStruct>().toString();
142     }
143     if (variant.canConvert<ApiArray>()) {
144         return variant.value<ApiArray>().toString();
145     }
146     if (variant.canConvert<ApiEnum>()) {
147         return variant.value<ApiEnum>().toString();
148     }
149
150     return QString();
151 }
152
153 ApiBitmask::ApiBitmask(const Trace::Bitmask *bitmask)
154     : m_value(0)
155 {
156     init(bitmask);
157 }
158
159
160 void ApiBitmask::init(const Trace::Bitmask *bitmask)
161 {
162     if (!bitmask)
163         return;
164
165     m_value = bitmask->value;
166     for (Trace::Bitmask::Signature::const_iterator it = bitmask->sig->begin();
167          it != bitmask->sig->end(); ++it) {
168         assert(it->second);
169         QPair<QString, unsigned long long> pair;
170
171         pair.first = QString::fromStdString(it->first);
172         pair.second = it->second;
173
174         m_sig.append(pair);
175     }
176 }
177
178 QString ApiBitmask::toString() const
179 {
180     QString str;
181     unsigned long long value = m_value;
182     bool first = true;
183     for (Signature::const_iterator it = m_sig.begin();
184          value != 0 && it != m_sig.end(); ++it) {
185         Q_ASSERT(it->second);
186         if ((value & it->second) == it->second) {
187             if (!first) {
188                 str += QLatin1String(" | ");
189             }
190             str += it->first;
191             value &= ~it->second;
192             first = false;
193         }
194     }
195     if (value || first) {
196         if (!first) {
197             str += QLatin1String(" | ");
198         }
199         str += QString::fromLatin1("0x%1").arg(value, 0, 16);
200     }
201     return str;
202 }
203
204 ApiStruct::ApiStruct(const Trace::Struct *s)
205 {
206     init(s);
207 }
208
209 QString ApiStruct::toString() const
210 {
211     QString str;
212
213     str += QLatin1String("{");
214     for (unsigned i = 0; i < m_members.count(); ++i) {
215         str += m_sig.memberNames[i] %
216                QLatin1Literal(" = ") %
217                apiVariantToString(m_members[i]);
218         if (i < m_members.count() - 1)
219             str += QLatin1String(", ");
220     }
221     str += QLatin1String("}");
222
223     return str;
224 }
225
226 void ApiStruct::init(const Trace::Struct *s)
227 {
228     if (!s)
229         return;
230
231     m_sig.name = QString::fromStdString(s->sig->name);
232     for (unsigned i = 0; i < s->members.size(); ++i) {
233         VariantVisitor vis;
234         m_sig.memberNames.append(
235             QString::fromStdString(s->sig->member_names[i]));
236         s->members[i]->visit(vis);
237         m_members.append(vis.variant());
238     }
239 }
240
241 void VariantVisitor::visit(Trace::Null *)
242 {
243     m_variant = QVariant::fromValue(ApiPointer(0));
244 }
245
246 void VariantVisitor::visit(Trace::Bool *node)
247 {
248     m_variant = QVariant(node->value);
249 }
250
251 void VariantVisitor::visit(Trace::SInt *node)
252 {
253     m_variant = QVariant(node->value);
254 }
255
256 void VariantVisitor::visit(Trace::UInt *node)
257 {
258     m_variant = QVariant(node->value);
259 }
260
261 void VariantVisitor::visit(Trace::Float *node)
262 {
263     m_variant = QVariant(node->value);
264 }
265
266 void VariantVisitor::visit(Trace::String *node)
267 {
268     m_variant = QVariant(QString::fromStdString(node->value));
269 }
270
271 void VariantVisitor::visit(Trace::Enum *e)
272 {
273     VariantVisitor vis;
274     e->sig->second->visit(vis);
275
276     QVariant val = vis.variant();
277
278     m_variant = QVariant::fromValue(
279         ApiEnum(QString::fromStdString(e->sig->first), val));
280 }
281
282 void VariantVisitor::visit(Trace::Bitmask *bitmask)
283 {
284     m_variant = QVariant::fromValue(ApiBitmask(bitmask));
285 }
286
287 void VariantVisitor::visit(Trace::Struct *str)
288 {
289     m_variant = QVariant::fromValue(ApiStruct(str));
290 }
291
292 void VariantVisitor::visit(Trace::Array *array)
293 {
294     m_variant = QVariant::fromValue(ApiArray(array));
295 }
296
297 void VariantVisitor::visit(Trace::Blob *blob)
298 {
299     //XXX
300     //FIXME: this is a nasty hack. Trace::Blob's can't
301     //   delete the contents in the destructor because
302     //   the data is being used by other calls. We piggy back
303     //   on that assumption and don't deep copy the data. If
304     //   Blob's will start deleting the data we will need to
305     //   start deep copying it or switch to using something like
306     //   Boost's shared_ptr or Qt's QSharedPointer to handle it
307     QByteArray barray = QByteArray::fromRawData(blob->buf, blob->size);
308     m_variant = QVariant(barray);
309 }
310
311 void VariantVisitor::visit(Trace::Pointer *ptr)
312 {
313     m_variant = QVariant::fromValue(ApiPointer(ptr->value));
314 }
315
316 ApiArray::ApiArray(const Trace::Array *arr)
317 {
318     init(arr);
319 }
320
321 ApiArray::ApiArray(const QList<QVariant> &vals)
322     : m_array(vals)
323 {
324 }
325
326 QString ApiArray::toString() const
327 {
328     QString str;
329     str += QLatin1String("[");
330     for(int i = 0; i < m_array.count(); ++i) {
331         const QVariant &var = m_array[i];
332         str += apiVariantToString(var);
333         if (i < m_array.count() - 1)
334             str += QLatin1String(", ");
335     }
336     str += QLatin1String("]");
337
338     return str;
339 }
340
341 void ApiArray::init(const Trace::Array *arr)
342 {
343     if (!arr)
344         return;
345
346     for (int i = 0; i < arr->values.size(); ++i) {
347         VariantVisitor vis;
348         arr->values[i]->visit(vis);
349
350         m_array.append(vis.variant());
351     }
352 }
353
354 QStaticText ApiTraceCall::staticText() const
355 {
356     if (m_staticText && !m_staticText->text().isEmpty())
357         return *m_staticText;
358
359     QVariantList argValues = arguments();
360
361     QString richText = QString::fromLatin1(
362         "<span style=\"font-weight:bold\">%1</span>(").arg(m_name);
363     for (int i = 0; i < m_argNames.count(); ++i) {
364         richText += QLatin1String("<span style=\"color:#0000ff\">");
365         QString argText = apiVariantToString(argValues[i]);
366
367         //if arguments are really long (e.g. shader text), cut them
368         // and elide it
369         if (argText.length() > 40) {
370             QString shortened = argText.mid(0, 40);
371             shortened[argText.length() - 5] = '.';
372             shortened[argText.length() - 4] = '.';
373             shortened[argText.length() - 3] = '.';
374             shortened[argText.length() - 2] = argText[argText.length() - 2];
375             shortened[argText.length() - 1] = argText[argText.length() - 1];
376             richText += shortened;
377         } else {
378             richText += argText;
379         }
380         richText += QLatin1String("</span>");
381         if (i < m_argNames.count() - 1)
382             richText += QLatin1String(", ");
383     }
384     richText += QLatin1String(")");
385     if (m_returnValue.isValid()) {
386         richText +=
387             QLatin1Literal(" = ") %
388             QLatin1Literal("<span style=\"color:#0000ff\">") %
389             apiVariantToString(m_returnValue) %
390             QLatin1Literal("</span>");
391     }
392
393     if (!m_staticText)
394         m_staticText = new QStaticText(richText);
395     else
396         m_staticText->setText(richText);
397     QTextOption opt;
398     opt.setWrapMode(QTextOption::NoWrap);
399     m_staticText->setTextOption(opt);
400     m_staticText->prepare();
401
402     return *m_staticText;
403 }
404
405 QString ApiTraceCall::toHtml() const
406 {
407     if (!m_richText.isEmpty())
408         return m_richText;
409
410     m_richText = QLatin1String("<div class=\"call\">");
411
412     if (m_helpUrl.isEmpty()) {
413         m_richText += QString::fromLatin1(
414             "%1) <span class=\"callName\">%2</span>(")
415                       .arg(m_index)
416                       .arg(m_name);
417     } else {
418         m_richText += QString::fromLatin1(
419             "%1) <span class=\"callName\"><a href=\"%2\">%3</a></span>(")
420                       .arg(m_index)
421                       .arg(m_helpUrl.toString())
422                       .arg(m_name);
423     }
424
425     QVariantList argValues = arguments();
426     for (int i = 0; i < m_argNames.count(); ++i) {
427         m_richText +=
428             QLatin1String("<span class=\"arg-name\">") +
429             m_argNames[i] +
430             QLatin1String("</span>") +
431             QLatin1Literal(" = ") +
432             QLatin1Literal("<span class=\"arg-value\">") +
433             apiVariantToString(argValues[i], true) +
434             QLatin1Literal("</span>");
435         if (i < m_argNames.count() - 1)
436             m_richText += QLatin1String(", ");
437     }
438     m_richText += QLatin1String(")");
439
440     if (m_returnValue.isValid()) {
441         m_richText +=
442             QLatin1String(" = ") +
443             QLatin1String("<span style=\"color:#0000ff\">") +
444             apiVariantToString(m_returnValue, true) +
445             QLatin1String("</span>");
446     }
447     m_richText += QLatin1String("</div>");
448
449     if (hasError()) {
450         QString errorStr =
451             QString::fromLatin1(
452                 "<div class=\"error\">%1</div>")
453             .arg(m_error);
454         m_richText += errorStr;
455     }
456
457     m_richText =
458         QString::fromLatin1(
459             "<html><head><style type=\"text/css\" media=\"all\">"
460             "%1</style></head><body>%2</body></html>")
461         .arg(styleSheet)
462         .arg(m_richText);
463     m_richText.squeeze();
464
465     //qDebug()<<m_richText;
466     return m_richText;
467 }
468
469 QString ApiTraceCall::filterText() const
470 {
471     if (!m_filterText.isEmpty())
472         return m_filterText;
473
474     QVariantList argValues = arguments();
475     m_filterText = m_name + QLatin1Literal("(");
476     for (int i = 0; i < m_argNames.count(); ++i) {
477         m_filterText += m_argNames[i] +
478                         QLatin1Literal(" = ") +
479                         apiVariantToString(argValues[i]);
480         if (argValues[i].type() == QVariant::ByteArray) {
481             m_hasBinaryData = true;
482             m_binaryDataIndex = i;
483         }
484         if (i < m_argNames.count() - 1)
485             m_filterText += QLatin1String(", ");
486     }
487     m_filterText += QLatin1String(")");
488
489     if (m_returnValue.isValid()) {
490         m_filterText += QLatin1Literal(" = ") +
491                         apiVariantToString(m_returnValue);
492     }
493     m_filterText.squeeze();
494     return m_filterText;
495 }
496
497 QStaticText ApiTraceFrame::staticText() const
498 {
499     if (m_staticText && !m_staticText->text().isEmpty())
500         return *m_staticText;
501
502     QString richText =
503         QString::fromLatin1("<span style=\"font-weight:bold\">Frame %1</span>").arg(number);
504
505     if (!m_staticText)
506         m_staticText = new QStaticText(richText);
507
508     QTextOption opt;
509     opt.setWrapMode(QTextOption::NoWrap);
510     m_staticText->setTextOption(opt);
511     m_staticText->prepare();
512
513     return *m_staticText;
514 }
515
516 int ApiTraceCall::numChildren() const
517 {
518     return 0;
519 }
520
521 int ApiTraceFrame::numChildren() const
522 {
523     return calls.count();
524 }
525
526 ApiTraceFrame::ApiTraceFrame()
527     : ApiTraceEvent(ApiTraceEvent::Frame),
528       m_parentTrace(0)
529 {
530 }
531
532 ApiTraceCall::ApiTraceCall()
533     : ApiTraceEvent(ApiTraceEvent::Call),
534       m_hasBinaryData(false),
535       m_binaryDataIndex(0)
536 {
537 }
538
539 ApiTraceEvent::ApiTraceEvent()
540     : m_type(ApiTraceEvent::None),
541       m_staticText(0)
542 {
543 }
544
545 ApiTraceEvent::ApiTraceEvent(Type t)
546     : m_type(t),
547       m_staticText(0)
548 {
549 }
550
551 ApiTraceCall::~ApiTraceCall()
552 {
553 }
554
555 QVariantMap ApiTraceEvent::stateParameters() const
556 {
557     return m_state.parameters();
558 }
559
560 ApiTraceState ApiTraceEvent::state() const
561 {
562     return m_state;
563 }
564
565 void ApiTraceEvent::setState(const ApiTraceState &state)
566 {
567     m_state = state;
568 }
569
570 bool ApiTraceCall::hasBinaryData() const
571 {
572     return m_hasBinaryData;
573 }
574
575 int ApiTraceCall::binaryDataIndex() const
576 {
577     return m_binaryDataIndex;
578 }
579
580 ApiTraceState::ApiTraceState()
581 {
582 }
583
584 ApiTraceState::ApiTraceState(const QVariantMap &parsedJson)
585 {
586     m_parameters = parsedJson[QLatin1String("parameters")].toMap();
587     QVariantMap attachedShaders =
588         parsedJson[QLatin1String("shaders")].toMap();
589     QVariantMap::const_iterator itr;
590
591
592     for (itr = attachedShaders.constBegin(); itr != attachedShaders.constEnd();
593          ++itr) {
594         QString type = itr.key();
595         QString source = itr.value().toString();
596         m_shaderSources[type] = source;
597     }
598
599     QVariantList textureUnits =
600         parsedJson[QLatin1String("textures")].toList();
601     for (int i = 0; i < textureUnits.count(); ++i) {
602         QVariantMap unit = textureUnits[i].toMap();
603         for (itr = unit.constBegin(); itr != unit.constEnd(); ++itr) {
604             QVariantMap target = itr.value().toMap();
605             if (target.count()) {
606                 QVariantList levels = target[QLatin1String("levels")].toList();
607                 for (int j = 0; j < levels.count(); ++j) {
608                     QVariantMap level = levels[j].toMap();
609                     QVariantMap image = level[QLatin1String("image")].toMap();
610                     QSize size(image[QLatin1String("__width__")].toInt(),
611                                image[QLatin1String("__height__")].toInt());
612                     QString cls = image[QLatin1String("__class__")].toString();
613                     QString type = image[QLatin1String("__type__")].toString();
614                     bool normalized =
615                         image[QLatin1String("__normalized__")].toBool();
616                     int numChannels =
617                         image[QLatin1String("__channels__")].toInt();
618
619                     Q_ASSERT(type == QLatin1String("uint8"));
620                     Q_ASSERT(normalized == true);
621
622                     QByteArray dataArray =
623                         image[QLatin1String("__data__")].toByteArray();
624
625                     ApiTexture tex;
626                     tex.setSize(size);
627                     tex.setNumChannels(numChannels);
628                     tex.setLevel(j);
629                     tex.setUnit(i);
630                     tex.setTarget(itr.key());
631                     tex.contentsFromBase64(dataArray);
632
633                     m_textures.append(tex);
634                 }
635             }
636         }
637     }
638
639     QVariantMap fbos =
640         parsedJson[QLatin1String("framebuffer")].toMap();
641     for (itr = fbos.constBegin(); itr != fbos.constEnd(); ++itr) {
642         QVariantMap buffer = itr.value().toMap();
643         QSize size(buffer[QLatin1String("__width__")].toInt(),
644                    buffer[QLatin1String("__height__")].toInt());
645         QString cls = buffer[QLatin1String("__class__")].toString();
646         QString type = buffer[QLatin1String("__type__")].toString();
647         bool normalized = buffer[QLatin1String("__normalized__")].toBool();
648         int numChannels = buffer[QLatin1String("__channels__")].toInt();
649
650         Q_ASSERT(type == QLatin1String("uint8"));
651         Q_ASSERT(normalized == true);
652
653         QByteArray dataArray =
654             buffer[QLatin1String("__data__")].toByteArray();
655
656         ApiFramebuffer fbo;
657         fbo.setSize(size);
658         fbo.setNumChannels(numChannels);
659         fbo.setType(itr.key());
660         fbo.contentsFromBase64(dataArray);
661         m_framebuffers.append(fbo);
662     }
663 }
664
665 QVariantMap ApiTraceState::parameters() const
666 {
667     return m_parameters;
668 }
669
670 QMap<QString, QString> ApiTraceState::shaderSources() const
671 {
672     return m_shaderSources;
673 }
674
675 bool ApiTraceState::isEmpty() const
676 {
677     return m_parameters.isEmpty();
678 }
679
680 QList<ApiTexture> ApiTraceState::textures() const
681 {
682     return m_textures;
683 }
684
685 QList<ApiFramebuffer> ApiTraceState::framebuffers() const
686 {
687     return m_framebuffers;
688 }
689
690 QList<QVariant> ApiArray::values() const
691 {
692     return m_array;
693 }
694
695 int ApiTraceCall::index() const
696 {
697     return m_index;
698 }
699
700 QString ApiTraceCall::name() const
701 {
702     return m_name;
703 }
704
705 QStringList ApiTraceCall::argNames() const
706 {
707     return m_argNames;
708 }
709
710 QVariantList ApiTraceCall::arguments() const
711 {
712     if (m_editedValues.isEmpty())
713         return m_argValues;
714     else
715         return m_editedValues;
716 }
717
718 QVariant ApiTraceCall::returnValue() const
719 {
720     return m_returnValue;
721 }
722
723 QUrl ApiTraceCall::helpUrl() const
724 {
725     return m_helpUrl;
726 }
727
728 ApiTraceCall::ApiTraceCall(const Trace::Call *call)
729     : ApiTraceEvent(ApiTraceEvent::Call),
730       m_hasBinaryData(false),
731       m_binaryDataIndex(0)
732 {
733     m_name = QString::fromStdString(call->sig->name);
734     m_index = call->no;
735
736     QString argumentsText;
737     for (int i = 0; i < call->sig->arg_names.size(); ++i) {
738         m_argNames +=
739             QString::fromStdString(call->sig->arg_names[i]);
740     }
741     if (call->ret) {
742         VariantVisitor retVisitor;
743         call->ret->visit(retVisitor);
744         m_returnValue = retVisitor.variant();
745     }
746     for (int i = 0; i < call->args.size(); ++i) {
747         VariantVisitor argVisitor;
748         call->args[i]->visit(argVisitor);
749         m_argValues += argVisitor.variant();
750     }
751 }
752
753 void ApiTraceCall::setHelpUrl(const QUrl &url)
754 {
755     m_helpUrl = url;
756 }
757
758 void ApiTraceCall::setParentFrame(ApiTraceFrame *frame)
759 {
760     m_parentFrame = frame;
761 }
762
763 ApiTraceFrame * ApiTraceCall::parentFrame()const
764 {
765     return m_parentFrame;
766 }
767
768 ApiTraceEvent::~ApiTraceEvent()
769 {
770     delete m_staticText;
771 }
772
773 void ApiTraceCall::revert()
774 {
775     setEditedValues(QVariantList());
776 }
777
778 ApiTrace * ApiTraceFrame::parentTrace() const
779 {
780     return m_parentTrace;
781 }
782
783 void ApiTraceFrame::setParentTrace(ApiTrace *trace)
784 {
785     m_parentTrace = trace;
786 }
787
788 QVariantList ApiTraceCall::originalValues() const
789 {
790     return m_argValues;
791 }
792
793 void ApiTraceCall::setEditedValues(const QVariantList &lst)
794 {
795     ApiTrace *trace = parentTrace();
796
797     m_editedValues = lst;
798     //lets regenerate data
799     m_richText = QString();
800     m_filterText = QString();
801     delete m_staticText;
802     m_staticText = 0;
803
804     if (trace) {
805         if (!lst.isEmpty()) {
806             trace->callEdited(this);
807         } else {
808             trace->callReverted(this);
809         }
810     }
811 }
812
813 QVariantList ApiTraceCall::editedValues() const
814 {
815     return m_editedValues;
816 }
817
818 bool ApiTraceCall::edited() const
819 {
820     return !m_editedValues.isEmpty();
821 }
822
823 ApiEnum::ApiEnum(const QString &name, const QVariant &val)
824     : m_name(name),
825       m_value(val)
826 {
827 }
828
829 QString ApiEnum::toString() const
830 {
831     return m_name;
832 }
833
834 QVariant ApiEnum::value() const
835 {
836     return m_value;
837 }
838
839 QString ApiEnum::name() const
840 {
841     return m_name;
842 }
843
844 unsigned long long ApiBitmask::value() const
845 {
846     return m_value;
847 }
848
849 ApiBitmask::Signature ApiBitmask::signature() const
850 {
851     return m_sig;
852 }
853
854 ApiStruct::Signature ApiStruct::signature() const
855 {
856     return m_sig;
857 }
858
859 QList<QVariant> ApiStruct::values() const
860 {
861     return m_members;
862 }
863
864 unsigned long long ApiPointer::value() const
865 {
866     return m_value;
867 }
868
869 bool ApiTraceCall::hasError() const
870 {
871     return !m_error.isEmpty();
872 }
873
874 QString ApiTraceCall::error() const
875 {
876     return m_error;
877 }
878
879 void ApiTraceCall::setError(const QString &msg)
880 {
881     if (m_error != msg) {
882         ApiTrace *trace = parentTrace();
883         m_error = msg;
884         m_richText = QString();
885         if (trace)
886             trace->callError(this);
887     }
888 }
889
890 ApiTrace * ApiTraceCall::parentTrace() const
891 {
892     if (m_parentFrame)
893         return m_parentFrame->parentTrace();
894     return NULL;
895 }
896