blob: ee43dd7fe9a76725280f3193a3443f72a9d22335 [file] [log] [blame]
#include "apitracemodel.h"
#include "apitracecall.h"
#include "traceloader.h"
#include "trace_parser.hpp"
#include <QBuffer>
#include <QDebug>
#include <QImage>
#include <QVariant>
ApiTraceModel::ApiTraceModel(QObject *parent)
: QAbstractItemModel(parent),
m_trace(0)
{
}
ApiTraceModel::~ApiTraceModel()
{
m_trace = 0;
}
QVariant ApiTraceModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.column() != 0)
return QVariant();
ApiTraceEvent *itm = item(index);
if (!itm) {
return QVariant();
}
switch (role) {
case Qt::DisplayRole:
return itm->staticText().text();
case Qt::DecorationRole:
return QImage();
case Qt::ToolTipRole: {
const QString stateText = tr("State info available.");
if (itm->type() == ApiTraceEvent::Call) {
ApiTraceCall *call = static_cast<ApiTraceCall*>(itm);
if (!call->hasState())
return QString::fromLatin1("%1)&nbsp;<b>%2</b>")
.arg(call->index())
.arg(call->name());
else
return QString::fromLatin1("%1)&nbsp;<b>%2</b><br/>%3")
.arg(call->index())
.arg(call->name())
.arg(stateText);
} else {
const char *htmlTempl =
"<div>\n"
"<div>\n"
"%1"
"<span style=\"font-weight:bold; font-size:large; vertical-align:center; padding-bottom: 30px \">\n"
"Frame %2</span>\n"
"</div>\n"
"<div >%3 calls%4</div>\n"
"</div>\n";
ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(itm);
QString thumbStr, sizeStr;
if (frame->hasState()) {
static const char *imgTempl =
"<img style=\"float:left;\" "
"src=\"data:image/png;base64,%1\"/>\n";
static const char *sizeTempl =
", %1kb";
ApiFramebuffer fbo = frame->state()->colorBuffer();
QImage thumb = fbo.calculateThumbnail(false, false);
if (!thumb.isNull()) {
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
thumb.save(&buffer, "PNG");
thumbStr = tr(imgTempl).arg(
QString(buffer.data().toBase64()));
}
int binaryDataSize = frame->binaryDataSize() / 1024;
if (binaryDataSize > 0) {
sizeStr = tr(sizeTempl).arg(binaryDataSize);
}
}
int numCalls = frame->isLoaded()
? frame->numTotalCalls()
: frame->numChildrenToLoad();
return tr(htmlTempl)
.arg(thumbStr)
.arg(frame->number)
.arg(numCalls)
.arg(sizeStr);
}
}
case ApiTraceModel::EventRole:
return QVariant::fromValue(itm);
}
return QVariant();
}
Qt::ItemFlags ApiTraceModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QVariant ApiTraceModel::headerData(int section, Qt::Orientation orientation,
int role ) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case 0:
return tr("Events");
case 1:
return tr("Flags");
default:
//fall through
break;
}
}
return QVariant();
}
QModelIndex ApiTraceModel::index(int row, int column,
const QModelIndex &parent) const
{
if ((parent.isValid() && parent.column() != 0) || column != 0)
return QModelIndex();
//qDebug()<<"At row = "<<row<<", column = "<<column<<", parent "<<parent;
ApiTraceEvent *parentEvent = item(parent);
if (parentEvent) {
ApiTraceEvent *event = parentEvent->eventAtRow(row);
if (event) {
Q_ASSERT(event->type() == ApiTraceEvent::Call);
return createIndex(row, column, event);
} else {
return QModelIndex();
}
} else {
ApiTraceFrame *frame = m_trace->frameAt(row);
if (frame)
return createIndex(row, column, frame);
else
return QModelIndex();
}
return QModelIndex();
}
bool ApiTraceModel::hasChildren(const QModelIndex &parent) const
{
if (parent.isValid()) {
ApiTraceEvent *event = item(parent);
if (!event)
return false;
if (event->type() == ApiTraceEvent::Frame) {
ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
return !frame->isEmpty();
} else {
Q_ASSERT(event->type() == ApiTraceEvent::Call);
ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
return call->numChildren() != 0;
}
} else {
return (rowCount() > 0);
}
}
QModelIndex ApiTraceModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
ApiTraceEvent *event = item(index);
if (event->type() == ApiTraceEvent::Call) {
ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
if (call->parentCall()) {
ApiTraceCall *parentCall = call->parentCall();
ApiTraceEvent *topEvent = parentCall->parentEvent();
return createIndex(topEvent->callIndex(parentCall), 0, parentCall);
} else {
Q_ASSERT(call->parentFrame());
return createIndex(call->parentFrame()->number,
0, call->parentFrame());
}
}
return QModelIndex();
}
int ApiTraceModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid())
return m_trace->numFrames();
ApiTraceEvent *event = item(parent);
if (!event)
return 0;
return event->numChildren();
}
int ApiTraceModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
bool ApiTraceModel::insertRows(int position, int rows,
const QModelIndex &parent)
{
return false;
}
bool ApiTraceModel::removeRows(int position, int rows,
const QModelIndex &parent)
{
bool success = true;
Q_UNUSED(parent);
beginRemoveRows(parent, position, position + rows - 1);
//XXX remove it from ApiTrace
endRemoveRows();
return success;
}
void ApiTraceModel::setApiTrace(ApiTrace *trace)
{
if (m_trace == trace)
return;
if (m_trace)
disconnect(m_trace);
m_trace = trace;
connect(m_trace, SIGNAL(invalidated()),
this, SLOT(invalidateFrames()));
connect(m_trace, SIGNAL(framesInvalidated()),
this, SLOT(invalidateFrames()));
connect(m_trace, SIGNAL(beginAddingFrames(int, int)),
this, SLOT(beginAddingFrames(int, int)));
connect(m_trace, SIGNAL(endAddingFrames()),
this, SLOT(endAddingFrames()));
connect(m_trace, SIGNAL(changed(ApiTraceEvent*)),
this, SLOT(changed(ApiTraceEvent*)));
connect(m_trace, SIGNAL(beginLoadingFrame(ApiTraceFrame*,int)),
this, SLOT(beginLoadingFrame(ApiTraceFrame*,int)));
connect(m_trace, SIGNAL(endLoadingFrame(ApiTraceFrame*)),
this, SLOT(endLoadingFrame(ApiTraceFrame*)));
}
const ApiTrace * ApiTraceModel::apiTrace() const
{
return m_trace;
}
void ApiTraceModel::invalidateFrames()
{
beginResetModel();
endResetModel();
}
void ApiTraceModel::beginAddingFrames(int oldCount, int numAdded)
{
beginInsertRows(QModelIndex(), oldCount,
oldCount + numAdded - 1);
}
ApiTraceEvent * ApiTraceModel::item(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return static_cast<ApiTraceEvent*>(index.internalPointer());
}
void ApiTraceModel::stateSetOnEvent(ApiTraceEvent *event)
{
if (!event)
return;
if (event->type() == ApiTraceEvent::Call) {
ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
QModelIndex index = indexForCall(call);
emit dataChanged(index, index);
} else if (event->type() == ApiTraceEvent::Frame) {
ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
const QList<ApiTraceFrame*> & frames = m_trace->frames();
int row = frames.indexOf(frame);
QModelIndex index = createIndex(row, 0, frame);
emit dataChanged(index, index);
}
}
QModelIndex ApiTraceModel::indexForCall(ApiTraceCall *call) const
{
if (!call) {
return QModelIndex();
}
ApiTraceEvent *parentEvent = call->parentEvent();
Q_ASSERT(parentEvent);
int row = parentEvent->callIndex(call);
if (row < 0) {
qDebug() << "Couldn't find call num "<<call->index()<<" inside parent!";
return QModelIndex();
}
return createIndex(row, 0, call);
}
void ApiTraceModel::changed(ApiTraceEvent *event)
{
if (event->type() == ApiTraceEvent::Call) {
callChanged(static_cast<ApiTraceCall*>(event));
} else if (event->type() == ApiTraceEvent::Frame) {
frameChanged(static_cast<ApiTraceFrame*>(event));
}
}
void ApiTraceModel::callChanged(ApiTraceCall *call)
{
ApiTrace *trace = call->parentFrame()->parentTrace();
#if 0
qDebug()<<"Call changed = "<<call->edited();
qDebug()<<"\ttrace edited = "<<trace->edited();
qDebug()<<"\ttrace file = "<<trace->fileName();
qDebug()<<"\ttrace needs saving = "<<trace->needsSaving();
#endif
Q_ASSERT(trace);
if (trace->needsSaving())
trace->save();
QModelIndex index = indexForCall(call);
emit dataChanged(index, index);
}
void ApiTraceModel::frameChanged(ApiTraceFrame *frame)
{
const QList<ApiTraceFrame*> & frames = m_trace->frames();
int row = frames.indexOf(frame);
QModelIndex index = createIndex(row, 0, frame);
emit dataChanged(index, index);
}
void ApiTraceModel::endAddingFrames()
{
endInsertRows();
}
bool ApiTraceModel::canFetchMore(const QModelIndex &parent) const
{
if (parent.isValid()) {
ApiTraceEvent *event = item(parent);
if (event && event->type() == ApiTraceEvent::Frame) {
ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
return !frame->isLoaded() && !m_loadingFrames.contains(frame);
} else
return false;
} else {
return false;
}
}
void ApiTraceModel::fetchMore(const QModelIndex &parent)
{
if (parent.isValid()) {
ApiTraceEvent *event = item(parent);
if (event && event->type() == ApiTraceEvent::Frame) {
ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
Q_ASSERT(!frame->isLoaded());
m_loadingFrames.insert(frame);
m_trace->loadFrame(frame);
}
}
}
void ApiTraceModel::beginLoadingFrame(ApiTraceFrame *frame, int numAdded)
{
QModelIndex index = createIndex(frame->number, 0, frame);
beginInsertRows(index, 0, numAdded - 1);
}
void ApiTraceModel::endLoadingFrame(ApiTraceFrame *frame)
{
QModelIndex index = createIndex(frame->number, 0, frame);
#if 0
qDebug()<<"Frame loaded = "<<frame->loaded();
qDebug()<<"\tframe idx = "<<frame->number;
qDebug()<<"\tis empty = "<<frame->isEmpty();
qDebug()<<"\tnum children = "<<frame->numChildren();
qDebug()<<"\tindex is "<<index;
#endif
endInsertRows();
emit dataChanged(index, index);
m_loadingFrames.remove(frame);
}
#include "apitracemodel.moc"