]> git.cworth.org Git - apitrace/blob - gui/apitracecall.cpp
Detect the binary data at the construction.
[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 (i < m_argNames.count() - 1)
481             m_filterText += QLatin1String(", ");
482     }
483     m_filterText += QLatin1String(")");
484
485     if (m_returnValue.isValid()) {
486         m_filterText += QLatin1Literal(" = ") +
487                         apiVariantToString(m_returnValue);
488     }
489     m_filterText.squeeze();
490     return m_filterText;
491 }
492
493 QStaticText ApiTraceFrame::staticText() const
494 {
495     if (m_staticText && !m_staticText->text().isEmpty())
496         return *m_staticText;
497
498     QString richText =
499         QString::fromLatin1("<span style=\"font-weight:bold\">Frame %1</span>").arg(number);
500
501     if (!m_staticText)
502         m_staticText = new QStaticText(richText);
503
504     QTextOption opt;
505     opt.setWrapMode(QTextOption::NoWrap);
506     m_staticText->setTextOption(opt);
507     m_staticText->prepare();
508
509     return *m_staticText;
510 }
511
512 int ApiTraceCall::numChildren() const
513 {
514     return 0;
515 }
516
517 int ApiTraceFrame::numChildren() const
518 {
519     return calls.count();
520 }
521
522 ApiTraceFrame::ApiTraceFrame()
523     : ApiTraceEvent(ApiTraceEvent::Frame),
524       m_parentTrace(0)
525 {
526 }
527
528 ApiTraceCall::ApiTraceCall()
529     : ApiTraceEvent(ApiTraceEvent::Call),
530       m_hasBinaryData(false),
531       m_binaryDataIndex(0)
532 {
533 }
534
535 ApiTraceEvent::ApiTraceEvent()
536     : m_type(ApiTraceEvent::None),
537       m_staticText(0)
538 {
539 }
540
541 ApiTraceEvent::ApiTraceEvent(Type t)
542     : m_type(t),
543       m_staticText(0)
544 {
545 }
546
547 ApiTraceCall::~ApiTraceCall()
548 {
549 }
550
551 QVariantMap ApiTraceEvent::stateParameters() const
552 {
553     return m_state.parameters();
554 }
555
556 ApiTraceState ApiTraceEvent::state() const
557 {
558     return m_state;
559 }
560
561 void ApiTraceEvent::setState(const ApiTraceState &state)
562 {
563     m_state = state;
564 }
565
566 bool ApiTraceCall::hasBinaryData() const
567 {
568     return m_hasBinaryData;
569 }
570
571 int ApiTraceCall::binaryDataIndex() const
572 {
573     return m_binaryDataIndex;
574 }
575
576 ApiTraceState::ApiTraceState()
577 {
578 }
579
580 ApiTraceState::ApiTraceState(const QVariantMap &parsedJson)
581 {
582     m_parameters = parsedJson[QLatin1String("parameters")].toMap();
583     QVariantMap attachedShaders =
584         parsedJson[QLatin1String("shaders")].toMap();
585     QVariantMap::const_iterator itr;
586
587
588     for (itr = attachedShaders.constBegin(); itr != attachedShaders.constEnd();
589          ++itr) {
590         QString type = itr.key();
591         QString source = itr.value().toString();
592         m_shaderSources[type] = source;
593     }
594
595     QVariantList textureUnits =
596         parsedJson[QLatin1String("textures")].toList();
597     for (int i = 0; i < textureUnits.count(); ++i) {
598         QVariantMap unit = textureUnits[i].toMap();
599         for (itr = unit.constBegin(); itr != unit.constEnd(); ++itr) {
600             QVariantMap target = itr.value().toMap();
601             if (target.count()) {
602                 QVariantList levels = target[QLatin1String("levels")].toList();
603                 for (int j = 0; j < levels.count(); ++j) {
604                     QVariantMap level = levels[j].toMap();
605                     QVariantMap image = level[QLatin1String("image")].toMap();
606                     QSize size(image[QLatin1String("__width__")].toInt(),
607                                image[QLatin1String("__height__")].toInt());
608                     QString cls = image[QLatin1String("__class__")].toString();
609                     QString type = image[QLatin1String("__type__")].toString();
610                     bool normalized =
611                         image[QLatin1String("__normalized__")].toBool();
612                     int numChannels =
613                         image[QLatin1String("__channels__")].toInt();
614
615                     Q_ASSERT(type == QLatin1String("uint8"));
616                     Q_ASSERT(normalized == true);
617
618                     QByteArray dataArray =
619                         image[QLatin1String("__data__")].toByteArray();
620
621                     ApiTexture tex;
622                     tex.setSize(size);
623                     tex.setNumChannels(numChannels);
624                     tex.setLevel(j);
625                     tex.setUnit(i);
626                     tex.setTarget(itr.key());
627                     tex.contentsFromBase64(dataArray);
628
629                     m_textures.append(tex);
630                 }
631             }
632         }
633     }
634
635     QVariantMap fbos =
636         parsedJson[QLatin1String("framebuffer")].toMap();
637     for (itr = fbos.constBegin(); itr != fbos.constEnd(); ++itr) {
638         QVariantMap buffer = itr.value().toMap();
639         QSize size(buffer[QLatin1String("__width__")].toInt(),
640                    buffer[QLatin1String("__height__")].toInt());
641         QString cls = buffer[QLatin1String("__class__")].toString();
642         QString type = buffer[QLatin1String("__type__")].toString();
643         bool normalized = buffer[QLatin1String("__normalized__")].toBool();
644         int numChannels = buffer[QLatin1String("__channels__")].toInt();
645
646         Q_ASSERT(type == QLatin1String("uint8"));
647         Q_ASSERT(normalized == true);
648
649         QByteArray dataArray =
650             buffer[QLatin1String("__data__")].toByteArray();
651
652         ApiFramebuffer fbo;
653         fbo.setSize(size);
654         fbo.setNumChannels(numChannels);
655         fbo.setType(itr.key());
656         fbo.contentsFromBase64(dataArray);
657         m_framebuffers.append(fbo);
658     }
659 }
660
661 QVariantMap ApiTraceState::parameters() const
662 {
663     return m_parameters;
664 }
665
666 QMap<QString, QString> ApiTraceState::shaderSources() const
667 {
668     return m_shaderSources;
669 }
670
671 bool ApiTraceState::isEmpty() const
672 {
673     return m_parameters.isEmpty();
674 }
675
676 QList<ApiTexture> ApiTraceState::textures() const
677 {
678     return m_textures;
679 }
680
681 QList<ApiFramebuffer> ApiTraceState::framebuffers() const
682 {
683     return m_framebuffers;
684 }
685
686 QList<QVariant> ApiArray::values() const
687 {
688     return m_array;
689 }
690
691 int ApiTraceCall::index() const
692 {
693     return m_index;
694 }
695
696 QString ApiTraceCall::name() const
697 {
698     return m_name;
699 }
700
701 QStringList ApiTraceCall::argNames() const
702 {
703     return m_argNames;
704 }
705
706 QVariantList ApiTraceCall::arguments() const
707 {
708     if (m_editedValues.isEmpty())
709         return m_argValues;
710     else
711         return m_editedValues;
712 }
713
714 QVariant ApiTraceCall::returnValue() const
715 {
716     return m_returnValue;
717 }
718
719 QUrl ApiTraceCall::helpUrl() const
720 {
721     return m_helpUrl;
722 }
723
724 ApiTraceCall::ApiTraceCall(const Trace::Call *call)
725     : ApiTraceEvent(ApiTraceEvent::Call),
726       m_hasBinaryData(false),
727       m_binaryDataIndex(0)
728 {
729     m_name = QString::fromStdString(call->sig->name);
730     m_index = call->no;
731
732     QString argumentsText;
733     for (int i = 0; i < call->sig->arg_names.size(); ++i) {
734         m_argNames +=
735             QString::fromStdString(call->sig->arg_names[i]);
736     }
737     if (call->ret) {
738         VariantVisitor retVisitor;
739         call->ret->visit(retVisitor);
740         m_returnValue = retVisitor.variant();
741     }
742     for (int i = 0; i < call->args.size(); ++i) {
743         VariantVisitor argVisitor;
744         call->args[i]->visit(argVisitor);
745         m_argValues += argVisitor.variant();
746         if (m_argValues[i].type() == QVariant::ByteArray) {
747             m_hasBinaryData = true;
748             m_binaryDataIndex = i;
749         }
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