]> git.cworth.org Git - apitrace/blob - gui/apitracecall.cpp
Cleanup the ApiTraceFrame a bit.
[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 m_calls.count();
520 }
521
522 ApiTraceFrame::ApiTraceFrame()
523     : ApiTraceEvent(ApiTraceEvent::Frame),
524       m_parentTrace(0),
525       m_binaryDataSize(0)
526 {
527 }
528
529 ApiTraceCall::ApiTraceCall()
530     : ApiTraceEvent(ApiTraceEvent::Call),
531       m_hasBinaryData(false),
532       m_binaryDataIndex(0)
533 {
534 }
535
536 ApiTraceEvent::ApiTraceEvent()
537     : m_type(ApiTraceEvent::None),
538       m_staticText(0)
539 {
540 }
541
542 ApiTraceEvent::ApiTraceEvent(Type t)
543     : m_type(t),
544       m_staticText(0)
545 {
546 }
547
548 ApiTraceCall::~ApiTraceCall()
549 {
550 }
551
552 QVariantMap ApiTraceEvent::stateParameters() const
553 {
554     return m_state.parameters();
555 }
556
557 ApiTraceState ApiTraceEvent::state() const
558 {
559     return m_state;
560 }
561
562 void ApiTraceEvent::setState(const ApiTraceState &state)
563 {
564     m_state = state;
565 }
566
567 bool ApiTraceCall::hasBinaryData() const
568 {
569     return m_hasBinaryData;
570 }
571
572 int ApiTraceCall::binaryDataIndex() const
573 {
574     return m_binaryDataIndex;
575 }
576
577 ApiTraceState::ApiTraceState()
578 {
579 }
580
581 ApiTraceState::ApiTraceState(const QVariantMap &parsedJson)
582 {
583     m_parameters = parsedJson[QLatin1String("parameters")].toMap();
584     QVariantMap attachedShaders =
585         parsedJson[QLatin1String("shaders")].toMap();
586     QVariantMap::const_iterator itr;
587
588
589     for (itr = attachedShaders.constBegin(); itr != attachedShaders.constEnd();
590          ++itr) {
591         QString type = itr.key();
592         QString source = itr.value().toString();
593         m_shaderSources[type] = source;
594     }
595
596     QVariantList textureUnits =
597         parsedJson[QLatin1String("textures")].toList();
598     for (int i = 0; i < textureUnits.count(); ++i) {
599         QVariantMap unit = textureUnits[i].toMap();
600         for (itr = unit.constBegin(); itr != unit.constEnd(); ++itr) {
601             QVariantMap target = itr.value().toMap();
602             if (target.count()) {
603                 QVariantList levels = target[QLatin1String("levels")].toList();
604                 for (int j = 0; j < levels.count(); ++j) {
605                     QVariantMap level = levels[j].toMap();
606                     QVariantMap image = level[QLatin1String("image")].toMap();
607                     QSize size(image[QLatin1String("__width__")].toInt(),
608                                image[QLatin1String("__height__")].toInt());
609                     QString cls = image[QLatin1String("__class__")].toString();
610                     QString type = image[QLatin1String("__type__")].toString();
611                     bool normalized =
612                         image[QLatin1String("__normalized__")].toBool();
613                     int numChannels =
614                         image[QLatin1String("__channels__")].toInt();
615
616                     Q_ASSERT(type == QLatin1String("uint8"));
617                     Q_ASSERT(normalized == true);
618
619                     QByteArray dataArray =
620                         image[QLatin1String("__data__")].toByteArray();
621
622                     ApiTexture tex;
623                     tex.setSize(size);
624                     tex.setNumChannels(numChannels);
625                     tex.setLevel(j);
626                     tex.setUnit(i);
627                     tex.setTarget(itr.key());
628                     tex.contentsFromBase64(dataArray);
629
630                     m_textures.append(tex);
631                 }
632             }
633         }
634     }
635
636     QVariantMap fbos =
637         parsedJson[QLatin1String("framebuffer")].toMap();
638     for (itr = fbos.constBegin(); itr != fbos.constEnd(); ++itr) {
639         QVariantMap buffer = itr.value().toMap();
640         QSize size(buffer[QLatin1String("__width__")].toInt(),
641                    buffer[QLatin1String("__height__")].toInt());
642         QString cls = buffer[QLatin1String("__class__")].toString();
643         QString type = buffer[QLatin1String("__type__")].toString();
644         bool normalized = buffer[QLatin1String("__normalized__")].toBool();
645         int numChannels = buffer[QLatin1String("__channels__")].toInt();
646
647         Q_ASSERT(type == QLatin1String("uint8"));
648         Q_ASSERT(normalized == true);
649
650         QByteArray dataArray =
651             buffer[QLatin1String("__data__")].toByteArray();
652
653         ApiFramebuffer fbo;
654         fbo.setSize(size);
655         fbo.setNumChannels(numChannels);
656         fbo.setType(itr.key());
657         fbo.contentsFromBase64(dataArray);
658         m_framebuffers.append(fbo);
659     }
660 }
661
662 QVariantMap ApiTraceState::parameters() const
663 {
664     return m_parameters;
665 }
666
667 QMap<QString, QString> ApiTraceState::shaderSources() const
668 {
669     return m_shaderSources;
670 }
671
672 bool ApiTraceState::isEmpty() const
673 {
674     return m_parameters.isEmpty();
675 }
676
677 QList<ApiTexture> ApiTraceState::textures() const
678 {
679     return m_textures;
680 }
681
682 QList<ApiFramebuffer> ApiTraceState::framebuffers() const
683 {
684     return m_framebuffers;
685 }
686
687 QList<QVariant> ApiArray::values() const
688 {
689     return m_array;
690 }
691
692 int ApiTraceCall::index() const
693 {
694     return m_index;
695 }
696
697 QString ApiTraceCall::name() const
698 {
699     return m_name;
700 }
701
702 QStringList ApiTraceCall::argNames() const
703 {
704     return m_argNames;
705 }
706
707 QVariantList ApiTraceCall::arguments() const
708 {
709     if (m_editedValues.isEmpty())
710         return m_argValues;
711     else
712         return m_editedValues;
713 }
714
715 QVariant ApiTraceCall::returnValue() const
716 {
717     return m_returnValue;
718 }
719
720 QUrl ApiTraceCall::helpUrl() const
721 {
722     return m_helpUrl;
723 }
724
725 ApiTraceCall::ApiTraceCall(const Trace::Call *call)
726     : ApiTraceEvent(ApiTraceEvent::Call),
727       m_hasBinaryData(false),
728       m_binaryDataIndex(0)
729 {
730     m_name = QString::fromStdString(call->sig->name);
731     m_index = call->no;
732
733     QString argumentsText;
734     for (int i = 0; i < call->sig->arg_names.size(); ++i) {
735         m_argNames +=
736             QString::fromStdString(call->sig->arg_names[i]);
737     }
738     if (call->ret) {
739         VariantVisitor retVisitor;
740         call->ret->visit(retVisitor);
741         m_returnValue = retVisitor.variant();
742     }
743     for (int i = 0; i < call->args.size(); ++i) {
744         VariantVisitor argVisitor;
745         call->args[i]->visit(argVisitor);
746         m_argValues += argVisitor.variant();
747         if (m_argValues[i].type() == QVariant::ByteArray) {
748             m_hasBinaryData = true;
749             m_binaryDataIndex = i;
750         }
751     }
752 }
753
754 void ApiTraceCall::setHelpUrl(const QUrl &url)
755 {
756     m_helpUrl = url;
757 }
758
759 void ApiTraceCall::setParentFrame(ApiTraceFrame *frame)
760 {
761     m_parentFrame = frame;
762 }
763
764 ApiTraceFrame * ApiTraceCall::parentFrame()const
765 {
766     return m_parentFrame;
767 }
768
769 ApiTraceEvent::~ApiTraceEvent()
770 {
771     delete m_staticText;
772 }
773
774 void ApiTraceCall::revert()
775 {
776     setEditedValues(QVariantList());
777 }
778
779 ApiTrace * ApiTraceFrame::parentTrace() const
780 {
781     return m_parentTrace;
782 }
783
784 void ApiTraceFrame::setParentTrace(ApiTrace *trace)
785 {
786     m_parentTrace = trace;
787 }
788
789 QVariantList ApiTraceCall::originalValues() const
790 {
791     return m_argValues;
792 }
793
794 void ApiTraceCall::setEditedValues(const QVariantList &lst)
795 {
796     ApiTrace *trace = parentTrace();
797
798     m_editedValues = lst;
799     //lets regenerate data
800     m_richText = QString();
801     m_filterText = QString();
802     delete m_staticText;
803     m_staticText = 0;
804
805     if (trace) {
806         if (!lst.isEmpty()) {
807             trace->callEdited(this);
808         } else {
809             trace->callReverted(this);
810         }
811     }
812 }
813
814 QVariantList ApiTraceCall::editedValues() const
815 {
816     return m_editedValues;
817 }
818
819 bool ApiTraceCall::edited() const
820 {
821     return !m_editedValues.isEmpty();
822 }
823
824 ApiEnum::ApiEnum(const QString &name, const QVariant &val)
825     : m_name(name),
826       m_value(val)
827 {
828 }
829
830 QString ApiEnum::toString() const
831 {
832     return m_name;
833 }
834
835 QVariant ApiEnum::value() const
836 {
837     return m_value;
838 }
839
840 QString ApiEnum::name() const
841 {
842     return m_name;
843 }
844
845 unsigned long long ApiBitmask::value() const
846 {
847     return m_value;
848 }
849
850 ApiBitmask::Signature ApiBitmask::signature() const
851 {
852     return m_sig;
853 }
854
855 ApiStruct::Signature ApiStruct::signature() const
856 {
857     return m_sig;
858 }
859
860 QList<QVariant> ApiStruct::values() const
861 {
862     return m_members;
863 }
864
865 unsigned long long ApiPointer::value() const
866 {
867     return m_value;
868 }
869
870 bool ApiTraceCall::hasError() const
871 {
872     return !m_error.isEmpty();
873 }
874
875 QString ApiTraceCall::error() const
876 {
877     return m_error;
878 }
879
880 void ApiTraceCall::setError(const QString &msg)
881 {
882     if (m_error != msg) {
883         ApiTrace *trace = parentTrace();
884         m_error = msg;
885         m_richText = QString();
886         if (trace)
887             trace->callError(this);
888     }
889 }
890
891 ApiTrace * ApiTraceCall::parentTrace() const
892 {
893     if (m_parentFrame)
894         return m_parentFrame->parentTrace();
895     return NULL;
896 }
897
898 void ApiTraceFrame::addCall(ApiTraceCall *call)
899 {
900     m_calls.append(call);
901 }
902
903 QList<ApiTraceCall*> ApiTraceFrame::calls() const
904 {
905     return m_calls;
906 }
907
908 ApiTraceCall * ApiTraceFrame::call(int idx) const
909 {
910     return m_calls.value(idx);
911 }
912
913 int ApiTraceFrame::callIndex(ApiTraceCall *call) const
914 {
915     return m_calls.indexOf(call);
916 }
917
918 bool ApiTraceFrame::isEmpty() const
919 {
920     return m_calls.isEmpty();
921 }
922