]> git.cworth.org Git - apitrace/blob - gui/apitracemodel.cpp
Implement grouping of calls.
[apitrace] / gui / apitracemodel.cpp
1 #include "apitracemodel.h"
2
3 #include "apitracecall.h"
4 #include "traceloader.h"
5 #include "trace_parser.hpp"
6
7 #include <QBuffer>
8 #include <QDebug>
9 #include <QImage>
10 #include <QVariant>
11
12
13 ApiTraceModel::ApiTraceModel(QObject *parent)
14     : QAbstractItemModel(parent),
15       m_trace(0)
16 {
17 }
18
19 ApiTraceModel::~ApiTraceModel()
20 {
21     m_trace = 0;
22 }
23
24 QVariant ApiTraceModel::data(const QModelIndex &index, int role) const
25 {
26     if (!index.isValid())
27         return QVariant();
28
29     if (index.column() != 0)
30         return QVariant();
31
32     ApiTraceEvent *itm = item(index);
33     if (!itm) {
34         return QVariant();
35     }
36
37     switch (role) {
38     case Qt::DisplayRole:
39         return itm->staticText().text();
40     case Qt::DecorationRole:
41         return QImage();
42     case Qt::ToolTipRole: {
43         const QString stateText = tr("State info available.");
44         if (itm->type() == ApiTraceEvent::Call) {
45             ApiTraceCall *call = static_cast<ApiTraceCall*>(itm);
46             if (!call->hasState())
47                 return QString::fromLatin1("%1)&nbsp;<b>%2</b>")
48                     .arg(call->index())
49                     .arg(call->name());
50             else
51                 return QString::fromLatin1("%1)&nbsp;<b>%2</b><br/>%3")
52                     .arg(call->index())
53                     .arg(call->name())
54                     .arg(stateText);
55         } else {
56             const char *htmlTempl =
57                     "<div>\n"
58                     "<div>\n"
59                     "%1"
60                     "<span style=\"font-weight:bold; font-size:large; vertical-align:center; padding-bottom: 30px \">\n"
61                     "Frame %2</span>\n"
62                     "</div>\n"
63                     "<div >%3 calls%4</div>\n"
64                     "</div>\n";
65
66
67             ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(itm);
68             QString thumbStr, sizeStr;
69
70             if (frame->hasState()) {
71                 static const char *imgTempl =
72                         "<img style=\"float:left;\" "
73                         "src=\"data:image/png;base64,%1\"/>\n";
74                 static const char *sizeTempl =
75                         ", %1kb";
76
77                 ApiFramebuffer fbo = frame->state()->colorBuffer();
78                 QImage thumb = fbo.thumb();
79                 if (!thumb.isNull()) {
80                     QByteArray ba;
81                     QBuffer buffer(&ba);
82                     buffer.open(QIODevice::WriteOnly);
83                     thumb.save(&buffer, "PNG");
84                     thumbStr = tr(imgTempl).arg(
85                                 QString(buffer.data().toBase64()));
86                 }
87
88                 int binaryDataSize = frame->binaryDataSize() / 1024;
89                 if (binaryDataSize > 0) {
90                     sizeStr = tr(sizeTempl).arg(binaryDataSize);
91                 }
92             }
93
94             int numCalls = frame->isLoaded()
95                     ? frame->numTotalCalls()
96                     : frame->numChildrenToLoad();
97
98             return tr(htmlTempl)
99                     .arg(thumbStr)
100                     .arg(frame->number)
101                     .arg(numCalls)
102                     .arg(sizeStr);
103         }
104     }
105     case ApiTraceModel::EventRole:
106         return QVariant::fromValue(itm);
107     }
108
109     return QVariant();
110 }
111
112 Qt::ItemFlags ApiTraceModel::flags(const QModelIndex &index) const
113 {
114     if (!index.isValid())
115         return 0;
116
117     return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
118 }
119
120 QVariant ApiTraceModel::headerData(int section, Qt::Orientation orientation,
121                                    int role ) const
122 {
123     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
124         switch (section) {
125         case 0:
126             return tr("Events");
127         case 1:
128             return tr("Flags");
129         default:
130             //fall through
131             break;
132         }
133     }
134
135     return QVariant();
136 }
137
138 QModelIndex ApiTraceModel::index(int row, int column,
139                                  const QModelIndex &parent) const
140 {
141     if ((parent.isValid() && parent.column() != 0) || column != 0)
142         return QModelIndex();
143
144     //qDebug()<<"At row = "<<row<<", column = "<<column<<", parent "<<parent;
145     ApiTraceEvent *parentEvent = item(parent);
146     if (parentEvent) {
147         ApiTraceEvent *event = parentEvent->eventAtRow(row);
148         if (event) {
149             Q_ASSERT(event->type() == ApiTraceEvent::Call);
150             return createIndex(row, column, event);
151         } else {
152             return QModelIndex();
153         }
154     } else {
155         ApiTraceFrame *frame = m_trace->frameAt(row);
156         if (frame)
157             return createIndex(row, column, frame);
158         else
159             return QModelIndex();
160     }
161     return QModelIndex();
162 }
163
164 bool ApiTraceModel::hasChildren(const QModelIndex &parent) const
165 {
166     if (parent.isValid()) {
167         ApiTraceEvent *event = item(parent);
168         if (!event)
169             return false;
170         if (event->type() == ApiTraceEvent::Frame) {
171             ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
172             return !frame->isEmpty();
173         } else {
174             Q_ASSERT(event->type() == ApiTraceEvent::Call);
175             ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
176             return call->numChildren() != 0;
177         }
178     } else {
179         return (rowCount() > 0);
180     }
181 }
182
183 QModelIndex ApiTraceModel::parent(const QModelIndex &index) const
184 {
185     if (!index.isValid())
186         return QModelIndex();
187
188     ApiTraceEvent *event = item(index);
189
190     if (event->type() == ApiTraceEvent::Call) {
191         ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
192
193         if (call->parentCall()) {
194             ApiTraceCall *parentCall = call->parentCall();
195             ApiTraceEvent *topEvent = parentCall->parentEvent();
196             return createIndex(topEvent->callIndex(parentCall), 0, parentCall);
197         } else {
198             Q_ASSERT(call->parentFrame());
199             return createIndex(call->parentFrame()->number,
200                                0, call->parentFrame());
201         }
202     }
203     return QModelIndex();
204 }
205
206 int ApiTraceModel::rowCount(const QModelIndex &parent) const
207 {
208     if (!parent.isValid())
209         return m_trace->numFrames();
210
211     ApiTraceEvent *event = item(parent);
212     if (!event)
213         return 0;
214
215     return event->numChildren();
216 }
217
218 int ApiTraceModel::columnCount(const QModelIndex &parent) const
219 {
220     return 1;
221 }
222
223 bool ApiTraceModel::insertRows(int position, int rows,
224                                const QModelIndex &parent)
225 {
226     return false;
227 }
228
229 bool ApiTraceModel::removeRows(int position, int rows,
230                                const QModelIndex &parent)
231 {
232     bool success = true;
233
234     Q_UNUSED(parent);
235
236     beginRemoveRows(parent, position, position + rows - 1);
237     //XXX remove it from ApiTrace
238     endRemoveRows();
239
240     return success;
241 }
242
243 void ApiTraceModel::setApiTrace(ApiTrace *trace)
244 {
245     if (m_trace == trace)
246         return;
247     if (m_trace)
248         disconnect(m_trace);
249     m_trace = trace;
250     connect(m_trace, SIGNAL(invalidated()),
251             this, SLOT(invalidateFrames()));
252     connect(m_trace, SIGNAL(framesInvalidated()),
253             this, SLOT(invalidateFrames()));
254     connect(m_trace, SIGNAL(beginAddingFrames(int, int)),
255             this, SLOT(beginAddingFrames(int, int)));
256     connect(m_trace, SIGNAL(endAddingFrames()),
257             this, SLOT(endAddingFrames()));
258     connect(m_trace, SIGNAL(changed(ApiTraceEvent*)),
259             this, SLOT(changed(ApiTraceEvent*)));
260     connect(m_trace, SIGNAL(beginLoadingFrame(ApiTraceFrame*,int)),
261             this, SLOT(beginLoadingFrame(ApiTraceFrame*,int)));
262     connect(m_trace, SIGNAL(endLoadingFrame(ApiTraceFrame*)),
263             this, SLOT(endLoadingFrame(ApiTraceFrame*)));
264
265 }
266
267 const ApiTrace * ApiTraceModel::apiTrace() const
268 {
269     return m_trace;
270 }
271
272 void ApiTraceModel::invalidateFrames()
273 {
274     beginResetModel();
275     endResetModel();
276 }
277
278 void ApiTraceModel::beginAddingFrames(int oldCount, int numAdded)
279 {
280     beginInsertRows(QModelIndex(), oldCount,
281                     oldCount + numAdded - 1);
282 }
283
284 ApiTraceEvent * ApiTraceModel::item(const QModelIndex &index) const
285 {
286     if (!index.isValid())
287         return 0;
288     return static_cast<ApiTraceEvent*>(index.internalPointer());
289 }
290
291 void ApiTraceModel::stateSetOnEvent(ApiTraceEvent *event)
292 {
293     if (!event)
294         return;
295
296     if (event->type() == ApiTraceEvent::Call) {
297         ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
298         QModelIndex index = indexForCall(call);
299         emit dataChanged(index, index);
300     } else if (event->type() == ApiTraceEvent::Frame) {
301         ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
302         const QList<ApiTraceFrame*> & frames = m_trace->frames();
303         int row = frames.indexOf(frame);
304         QModelIndex index = createIndex(row, 0, frame);
305         emit dataChanged(index, index);
306     }
307 }
308
309 QModelIndex ApiTraceModel::indexForCall(ApiTraceCall *call) const
310 {
311     if (!call) {
312         return QModelIndex();
313     }
314
315     ApiTraceEvent *parentEvent = call->parentEvent();
316     Q_ASSERT(parentEvent);
317
318     int row = parentEvent->callIndex(call);
319     if (row < 0) {
320         qDebug() << "Couldn't find call num "<<call->index()<<" inside parent!";
321         return QModelIndex();
322     }
323     return createIndex(row, 0, call);
324 }
325
326 void ApiTraceModel::changed(ApiTraceEvent *event)
327 {
328     if (event->type() == ApiTraceEvent::Call) {
329         callChanged(static_cast<ApiTraceCall*>(event));
330     } else if (event->type() == ApiTraceEvent::Frame) {
331         frameChanged(static_cast<ApiTraceFrame*>(event));
332     }
333 }
334
335 void ApiTraceModel::callChanged(ApiTraceCall *call)
336 {
337     ApiTrace *trace = call->parentFrame()->parentTrace();
338
339 #if 0
340     qDebug()<<"Call changed = "<<call->edited();
341     qDebug()<<"\ttrace edited = "<<trace->edited();
342     qDebug()<<"\ttrace file = "<<trace->fileName();
343     qDebug()<<"\ttrace needs saving = "<<trace->needsSaving();
344 #endif
345
346     Q_ASSERT(trace);
347     if (trace->needsSaving())
348         trace->save();
349
350     QModelIndex index = indexForCall(call);
351     emit dataChanged(index, index);
352 }
353
354 void ApiTraceModel::frameChanged(ApiTraceFrame *frame)
355 {
356     const QList<ApiTraceFrame*> & frames = m_trace->frames();
357     int row = frames.indexOf(frame);
358     QModelIndex index = createIndex(row, 0, frame);
359     emit dataChanged(index, index);
360 }
361
362 void ApiTraceModel::endAddingFrames()
363 {
364     endInsertRows();
365 }
366
367 bool ApiTraceModel::canFetchMore(const QModelIndex &parent) const
368 {
369     if (parent.isValid()) {
370         ApiTraceEvent *event = item(parent);
371         if (event && event->type() == ApiTraceEvent::Frame) {
372             ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
373             return !frame->isLoaded() && !m_loadingFrames.contains(frame);
374         } else
375             return false;
376     } else {
377         return false;
378     }
379 }
380
381 void ApiTraceModel::fetchMore(const QModelIndex &parent)
382 {
383     if (parent.isValid()) {
384         ApiTraceEvent *event = item(parent);
385         if (event && event->type() == ApiTraceEvent::Frame) {
386             ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
387             QModelIndex index = createIndex(frame->number, 0, frame);
388
389             Q_ASSERT(!frame->isLoaded());
390             m_loadingFrames.insert(frame);
391
392             m_trace->loadFrame(frame);
393         }
394     }
395 }
396
397 void ApiTraceModel::beginLoadingFrame(ApiTraceFrame *frame, int numAdded)
398 {
399     QModelIndex index = createIndex(frame->number, 0, frame);
400     beginInsertRows(index, 0, numAdded - 1);
401 }
402
403 void ApiTraceModel::endLoadingFrame(ApiTraceFrame *frame)
404 {
405     QModelIndex index = createIndex(frame->number, 0, frame);
406 #if 0
407     qDebug()<<"Frame loaded = "<<frame->loaded();
408     qDebug()<<"\tframe idx = "<<frame->number;
409     qDebug()<<"\tis empty = "<<frame->isEmpty();
410     qDebug()<<"\tnum children = "<<frame->numChildren();
411     qDebug()<<"\tindex is "<<index;
412 #endif
413
414     endInsertRows();
415
416     emit dataChanged(index, index);
417
418     m_loadingFrames.remove(frame);
419 }
420
421 #include "apitracemodel.moc"