forked from qt-creator/qt-creator
QmlProfiler: Merge SortedTimelineModel into AbstractTimelineModel
In order to provide a stable interface we need most of SortedTimelineModel to be private. There is no real benefit in keeping a distinction between AbstractTimelineModel and SortedTimelineModel as SortedTimelineModel isn't very useful on its own. Change-Id: Ibc6945e2740320f430f2634f95c7807d6b460123 Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
This commit is contained in:
@@ -31,28 +31,124 @@
|
|||||||
#include "abstracttimelinemodel.h"
|
#include "abstracttimelinemodel.h"
|
||||||
#include "abstracttimelinemodel_p.h"
|
#include "abstracttimelinemodel_p.h"
|
||||||
|
|
||||||
|
#include <QLinkedList>
|
||||||
|
|
||||||
namespace QmlProfiler {
|
namespace QmlProfiler {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class QmlProfiler::AbstractTimelineModel
|
||||||
|
\brief The AbstractTimelineModel class provides a sorted model for timeline data.
|
||||||
|
|
||||||
|
The AbstractTimelineModel lets you keep range data sorted by both start and end times, so that
|
||||||
|
visible ranges can easily be computed. The only precondition for that to work is that the ranges
|
||||||
|
must be perfectly nested. A "parent" range of a range R is defined as a range for which the
|
||||||
|
start time is smaller than R's start time and the end time is greater than R's end time. A set
|
||||||
|
of ranges is perfectly nested if all parent ranges of any given range have a common parent
|
||||||
|
range. Mind that you can always make that happen by defining a range that spans the whole
|
||||||
|
available time span. That, however, will make any code that uses firstStartTime() and
|
||||||
|
lastEndTime() for selecting subsets of the model always select all of it.
|
||||||
|
|
||||||
|
\note Indices returned from the various methods are only valid until a new range is inserted
|
||||||
|
before them. Inserting a new range before a given index moves the range pointed to by the
|
||||||
|
index by one. Incrementing the index by one will make it point to the item again.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn qint64 AbstractTimelineModelPrivate::firstStartTime() const
|
||||||
|
Returns the begin of the first range in the model.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn qint64 AbstractTimelineModelPrivate::lastEndTime() const
|
||||||
|
Returns the end of the last range in the model.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn const AbstractTimelineModelPrivate::Range &AbstractTimelineModelPrivate::range(int index) const
|
||||||
|
Returns the range data at the specified index.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void AbstractTimelineModel::computeNesting()
|
||||||
|
Compute all ranges' parents.
|
||||||
|
\sa findFirstIndex
|
||||||
|
*/
|
||||||
|
void AbstractTimelineModel::computeNesting()
|
||||||
|
{
|
||||||
|
Q_D(AbstractTimelineModel);
|
||||||
|
QLinkedList<int> parents;
|
||||||
|
for (int range = 0; range != count(); ++range) {
|
||||||
|
AbstractTimelineModelPrivate::Range ¤t = d->ranges[range];
|
||||||
|
for (QLinkedList<int>::iterator parentIt = parents.begin();;) {
|
||||||
|
if (parentIt == parents.end()) {
|
||||||
|
parents.append(range);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractTimelineModelPrivate::Range &parent = d->ranges[*parentIt];
|
||||||
|
qint64 parentEnd = parent.start + parent.duration;
|
||||||
|
if (parentEnd < current.start) {
|
||||||
|
if (parent.start == current.start) {
|
||||||
|
if (parent.parent == -1) {
|
||||||
|
parent.parent = range;
|
||||||
|
} else {
|
||||||
|
AbstractTimelineModelPrivate::Range &ancestor = d->ranges[parent.parent];
|
||||||
|
if (ancestor.start == current.start &&
|
||||||
|
ancestor.duration < current.duration)
|
||||||
|
parent.parent = range;
|
||||||
|
}
|
||||||
|
// Just switch the old parent range for the new, larger one
|
||||||
|
*parentIt = range;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
parentIt = parents.erase(parentIt);
|
||||||
|
}
|
||||||
|
} else if (parentEnd >= current.start + current.duration) {
|
||||||
|
// no need to insert
|
||||||
|
current.parent = *parentIt;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
++parentIt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractTimelineModel::AbstractTimelineModelPrivate::init(AbstractTimelineModel *q,
|
||||||
|
const QString &newDisplayName,
|
||||||
|
QmlDebug::Message newMessage,
|
||||||
|
QmlDebug::RangeType newRangeType)
|
||||||
|
{
|
||||||
|
q_ptr = q;
|
||||||
|
modelId = 0;
|
||||||
|
modelManager = 0;
|
||||||
|
expanded = false;
|
||||||
|
hidden = false;
|
||||||
|
displayName = newDisplayName;
|
||||||
|
message = newMessage;
|
||||||
|
rangeType = newRangeType;
|
||||||
|
expandedRowCount = 1;
|
||||||
|
collapsedRowCount = 1;
|
||||||
|
connect(q,SIGNAL(rowHeightChanged()),q,SIGNAL(heightChanged()));
|
||||||
|
connect(q,SIGNAL(expandedChanged()),q,SIGNAL(heightChanged()));
|
||||||
|
connect(q,SIGNAL(hiddenChanged()),q,SIGNAL(heightChanged()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
AbstractTimelineModel::AbstractTimelineModel(AbstractTimelineModelPrivate *dd,
|
AbstractTimelineModel::AbstractTimelineModel(AbstractTimelineModelPrivate *dd,
|
||||||
const QString &displayName, QmlDebug::Message message, QmlDebug::RangeType rangeType,
|
const QString &displayName, QmlDebug::Message message, QmlDebug::RangeType rangeType,
|
||||||
QObject *parent) :
|
QObject *parent) :
|
||||||
SortedTimelineModel(parent), d_ptr(dd)
|
QObject(parent), d_ptr(dd)
|
||||||
{
|
{
|
||||||
Q_D(AbstractTimelineModel);
|
dd->init(this, displayName, message, rangeType);
|
||||||
connect(this,SIGNAL(rowHeightChanged()),this,SIGNAL(heightChanged()));
|
}
|
||||||
connect(this,SIGNAL(expandedChanged()),this,SIGNAL(heightChanged()));
|
|
||||||
connect(this,SIGNAL(hiddenChanged()),this,SIGNAL(heightChanged()));
|
|
||||||
|
|
||||||
d->q_ptr = this;
|
AbstractTimelineModel::AbstractTimelineModel(const QString &displayName, QmlDebug::Message message,
|
||||||
d->modelId = 0;
|
QmlDebug::RangeType rangeType, QObject *parent) :
|
||||||
d->modelManager = 0;
|
QObject(parent), d_ptr(new AbstractTimelineModelPrivate)
|
||||||
d->expanded = false;
|
{
|
||||||
d->hidden = false;
|
d_ptr->init(this, displayName, message, rangeType);
|
||||||
d->displayName = displayName;
|
|
||||||
d->message = message;
|
|
||||||
d->rangeType = rangeType;
|
|
||||||
d->expandedRowCount = 1;
|
|
||||||
d->collapsedRowCount = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractTimelineModel::~AbstractTimelineModel()
|
AbstractTimelineModel::~AbstractTimelineModel()
|
||||||
@@ -138,6 +234,97 @@ int AbstractTimelineModel::height() const
|
|||||||
return d->rowOffsets.last() + (depth - d->rowOffsets.size()) * DefaultRowHeight;
|
return d->rowOffsets.last() + (depth - d->rowOffsets.size()) * DefaultRowHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn int AbstractTimelineModel::count() const
|
||||||
|
Returns the number of ranges in the model.
|
||||||
|
*/
|
||||||
|
int AbstractTimelineModel::count() const
|
||||||
|
{
|
||||||
|
Q_D(const AbstractTimelineModel);
|
||||||
|
return d->ranges.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 AbstractTimelineModel::duration(int index) const
|
||||||
|
{
|
||||||
|
Q_D(const AbstractTimelineModel);
|
||||||
|
return d->ranges[index].duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 AbstractTimelineModel::startTime(int index) const
|
||||||
|
{
|
||||||
|
Q_D(const AbstractTimelineModel);
|
||||||
|
return d->ranges[index].start;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 AbstractTimelineModel::endTime(int index) const
|
||||||
|
{
|
||||||
|
Q_D(const AbstractTimelineModel);
|
||||||
|
return d->ranges[index].start + d->ranges[index].duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AbstractTimelineModel::typeId(int index) const
|
||||||
|
{
|
||||||
|
Q_D(const AbstractTimelineModel);
|
||||||
|
return d->ranges[index].typeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn int AbstractTimelineModel::firstIndex(qint64 startTime) const
|
||||||
|
Looks up the first range with an end time greater than the given time and
|
||||||
|
returns its parent's index. If no such range is found, it returns -1. If there
|
||||||
|
is no parent, it returns the found range's index. The parent of a range is the
|
||||||
|
range with the lowest start time that completely covers the child range.
|
||||||
|
"Completely covers" means:
|
||||||
|
parent.startTime <= child.startTime && parent.endTime >= child.endTime
|
||||||
|
*/
|
||||||
|
int AbstractTimelineModel::firstIndex(qint64 startTime) const
|
||||||
|
{
|
||||||
|
Q_D(const AbstractTimelineModel);
|
||||||
|
int index = firstIndexNoParents(startTime);
|
||||||
|
if (index == -1)
|
||||||
|
return -1;
|
||||||
|
int parent = d->ranges[index].parent;
|
||||||
|
return parent == -1 ? index : parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn int AbstractTimelineModel::firstIndexNoParents(qint64 startTime) const
|
||||||
|
Looks up the first range with an end time greater than the given time and
|
||||||
|
returns its index. If no such range is found, it returns -1.
|
||||||
|
*/
|
||||||
|
int AbstractTimelineModel::firstIndexNoParents(qint64 startTime) const
|
||||||
|
{
|
||||||
|
Q_D(const AbstractTimelineModel);
|
||||||
|
// in the "endtime" list, find the first event that ends after startTime
|
||||||
|
if (d->endTimes.isEmpty())
|
||||||
|
return -1;
|
||||||
|
if (d->endTimes.count() == 1 || d->endTimes.first().end > startTime)
|
||||||
|
return d->endTimes.first().startIndex;
|
||||||
|
if (d->endTimes.last().end <= startTime)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return d->endTimes[d->lowerBound(d->endTimes, startTime) + 1].startIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn int AbstractTimelineModel::lastIndex(qint64 endTime) const
|
||||||
|
Looks up the last range with a start time smaller than the given time and
|
||||||
|
returns its index. If no such range is found, it returns -1.
|
||||||
|
*/
|
||||||
|
int AbstractTimelineModel::lastIndex(qint64 endTime) const
|
||||||
|
{
|
||||||
|
Q_D(const AbstractTimelineModel);
|
||||||
|
// in the "starttime" list, find the last event that starts before endtime
|
||||||
|
if (d->ranges.isEmpty() || d->ranges.first().start >= endTime)
|
||||||
|
return -1;
|
||||||
|
if (d->ranges.count() == 1)
|
||||||
|
return 0;
|
||||||
|
if (d->ranges.last().start < endTime)
|
||||||
|
return d->ranges.count() - 1;
|
||||||
|
|
||||||
|
return d->lowerBound(d->ranges, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
QVariantMap AbstractTimelineModel::location(int index) const
|
QVariantMap AbstractTimelineModel::location(int index) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(index);
|
Q_UNUSED(index);
|
||||||
@@ -183,6 +370,51 @@ int AbstractTimelineModel::rowMaxValue(int rowNumber) const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn int AbstractTimelineModel::insert(qint64 startTime, qint64 duration)
|
||||||
|
Inserts a range at the given time position and returns its index.
|
||||||
|
*/
|
||||||
|
int AbstractTimelineModel::insert(qint64 startTime, qint64 duration, int typeId)
|
||||||
|
{
|
||||||
|
Q_D(AbstractTimelineModel);
|
||||||
|
/* Doing insert-sort here is preferable as most of the time the times will actually be
|
||||||
|
* presorted in the right way. So usually this will just result in appending. */
|
||||||
|
int index = d->insertSorted(d->ranges,
|
||||||
|
AbstractTimelineModelPrivate::Range(startTime, duration, typeId));
|
||||||
|
if (index < d->ranges.size() - 1)
|
||||||
|
d->incrementStartIndices(index);
|
||||||
|
d->insertSorted(d->endTimes,
|
||||||
|
AbstractTimelineModelPrivate::RangeEnd(index, startTime + duration));
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn int AbstractTimelineModel::insertStart(qint64 startTime, int typeId)
|
||||||
|
Inserts the given data as range start at the given time position and
|
||||||
|
returns its index. The range end is not set.
|
||||||
|
*/
|
||||||
|
int AbstractTimelineModel::insertStart(qint64 startTime, int typeId)
|
||||||
|
{
|
||||||
|
Q_D(AbstractTimelineModel);
|
||||||
|
int index = d->insertSorted(d->ranges,
|
||||||
|
AbstractTimelineModelPrivate::Range(startTime, 0, typeId));
|
||||||
|
if (index < d->ranges.size() - 1)
|
||||||
|
d->incrementStartIndices(index);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn int AbstractTimelineModel::insertEnd(int index, qint64 duration)
|
||||||
|
Adds a range end for the given start index.
|
||||||
|
*/
|
||||||
|
void AbstractTimelineModel::insertEnd(int index, qint64 duration)
|
||||||
|
{
|
||||||
|
Q_D(AbstractTimelineModel);
|
||||||
|
d->ranges[index].duration = duration;
|
||||||
|
d->insertSorted(d->endTimes, AbstractTimelineModelPrivate::RangeEnd(index,
|
||||||
|
d->ranges[index].start + duration));
|
||||||
|
}
|
||||||
|
|
||||||
void AbstractTimelineModel::dataChanged()
|
void AbstractTimelineModel::dataChanged()
|
||||||
{
|
{
|
||||||
Q_D(AbstractTimelineModel);
|
Q_D(AbstractTimelineModel);
|
||||||
@@ -255,7 +487,8 @@ int AbstractTimelineModel::rowCount() const
|
|||||||
|
|
||||||
int AbstractTimelineModel::selectionId(int index) const
|
int AbstractTimelineModel::selectionId(int index) const
|
||||||
{
|
{
|
||||||
return range(index).typeId;
|
Q_D(const AbstractTimelineModel);
|
||||||
|
return d->ranges[index].typeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractTimelineModel::clear()
|
void AbstractTimelineModel::clear()
|
||||||
@@ -268,7 +501,8 @@ void AbstractTimelineModel::clear()
|
|||||||
d->rowOffsets.clear();
|
d->rowOffsets.clear();
|
||||||
d->expanded = false;
|
d->expanded = false;
|
||||||
d->hidden = false;
|
d->hidden = false;
|
||||||
SortedTimelineModel::clear();
|
d->ranges.clear();
|
||||||
|
d->endTimes.clear();
|
||||||
if (hadRowHeights)
|
if (hadRowHeights)
|
||||||
emit rowHeightChanged();
|
emit rowHeightChanged();
|
||||||
if (wasExpanded)
|
if (wasExpanded)
|
||||||
|
|||||||
@@ -35,13 +35,12 @@
|
|||||||
#include "qmlprofiler_global.h"
|
#include "qmlprofiler_global.h"
|
||||||
#include "qmlprofilermodelmanager.h"
|
#include "qmlprofilermodelmanager.h"
|
||||||
#include "qmlprofilerdatamodel.h"
|
#include "qmlprofilerdatamodel.h"
|
||||||
#include "sortedtimelinemodel.h"
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
|
||||||
namespace QmlProfiler {
|
namespace QmlProfiler {
|
||||||
|
|
||||||
class QMLPROFILER_EXPORT AbstractTimelineModel : public SortedTimelineModel
|
class QMLPROFILER_EXPORT AbstractTimelineModel : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QString displayName READ displayName CONSTANT)
|
Q_PROPERTY(QString displayName READ displayName CONSTANT)
|
||||||
@@ -51,6 +50,9 @@ class QMLPROFILER_EXPORT AbstractTimelineModel : public SortedTimelineModel
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
class AbstractTimelineModelPrivate;
|
class AbstractTimelineModelPrivate;
|
||||||
|
|
||||||
|
AbstractTimelineModel(const QString &displayName, QmlDebug::Message message,
|
||||||
|
QmlDebug::RangeType rangeType, QObject *parent);
|
||||||
~AbstractTimelineModel();
|
~AbstractTimelineModel();
|
||||||
|
|
||||||
// Trivial methods implemented by the abstract model itself
|
// Trivial methods implemented by the abstract model itself
|
||||||
@@ -63,6 +65,15 @@ public:
|
|||||||
int rowOffset(int rowNumber) const;
|
int rowOffset(int rowNumber) const;
|
||||||
void setRowHeight(int rowNumber, int height);
|
void setRowHeight(int rowNumber, int height);
|
||||||
int height() const;
|
int height() const;
|
||||||
|
int count() const;
|
||||||
|
qint64 duration(int index) const;
|
||||||
|
qint64 startTime(int index) const;
|
||||||
|
qint64 endTime(int index) const;
|
||||||
|
int typeId(int index) const;
|
||||||
|
|
||||||
|
int firstIndex(qint64 startTime) const;
|
||||||
|
int firstIndexNoParents(qint64 startTime) const;
|
||||||
|
int lastIndex(qint64 endTime) const;
|
||||||
|
|
||||||
bool accepted(const QmlProfilerDataModel::QmlEventTypeData &event) const;
|
bool accepted(const QmlProfilerDataModel::QmlEventTypeData &event) const;
|
||||||
bool expanded() const;
|
bool expanded() const;
|
||||||
@@ -123,6 +134,11 @@ protected:
|
|||||||
return QColor::fromHsl(hue % 360, Saturation, Lightness);
|
return QColor::fromHsl(hue % 360, Saturation, Lightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int insert(qint64 startTime, qint64 duration, int typeId);
|
||||||
|
int insertStart(qint64 startTime, int typeId);
|
||||||
|
void insertEnd(int index, qint64 duration);
|
||||||
|
void computeNesting();
|
||||||
|
|
||||||
explicit AbstractTimelineModel(AbstractTimelineModelPrivate *dd, const QString &displayName,
|
explicit AbstractTimelineModel(AbstractTimelineModelPrivate *dd, const QString &displayName,
|
||||||
QmlDebug::Message message, QmlDebug::RangeType rangeType,
|
QmlDebug::Message message, QmlDebug::RangeType rangeType,
|
||||||
QObject *parent);
|
QObject *parent);
|
||||||
|
|||||||
@@ -37,6 +37,74 @@ namespace QmlProfiler {
|
|||||||
|
|
||||||
class QMLPROFILER_EXPORT AbstractTimelineModel::AbstractTimelineModelPrivate {
|
class QMLPROFILER_EXPORT AbstractTimelineModel::AbstractTimelineModelPrivate {
|
||||||
public:
|
public:
|
||||||
|
struct Range {
|
||||||
|
Range() : start(-1), duration(-1), typeId(-1), parent(-1) {}
|
||||||
|
Range(qint64 start, qint64 duration, int typeId) :
|
||||||
|
start(start), duration(duration), typeId(typeId), parent(-1) {}
|
||||||
|
qint64 start;
|
||||||
|
qint64 duration;
|
||||||
|
int typeId;
|
||||||
|
int parent;
|
||||||
|
inline qint64 timestamp() const {return start;}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RangeEnd {
|
||||||
|
RangeEnd() : startIndex(-1), end(-1) {}
|
||||||
|
RangeEnd(int startIndex, qint64 end) :
|
||||||
|
startIndex(startIndex), end(end) {}
|
||||||
|
int startIndex;
|
||||||
|
qint64 end;
|
||||||
|
inline qint64 timestamp() const {return end;}
|
||||||
|
};
|
||||||
|
|
||||||
|
void init(AbstractTimelineModel *q, const QString &displayName, QmlDebug::Message message,
|
||||||
|
QmlDebug::RangeType rangeType);
|
||||||
|
|
||||||
|
inline qint64 lastEndTime() const { return endTimes.last().end; }
|
||||||
|
inline qint64 firstStartTime() const { return ranges.first().start; }
|
||||||
|
|
||||||
|
void incrementStartIndices(int index)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < endTimes.size(); ++i) {
|
||||||
|
if (endTimes[i].startIndex >= index)
|
||||||
|
endTimes[i].startIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename RangeDelimiter>
|
||||||
|
static inline int insertSorted(QVector<RangeDelimiter> &container, const RangeDelimiter &item)
|
||||||
|
{
|
||||||
|
for (int i = container.count();;) {
|
||||||
|
if (i == 0) {
|
||||||
|
container.prepend(item);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (container[--i].timestamp() <= item.timestamp()) {
|
||||||
|
container.insert(++i, item);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename RangeDelimiter>
|
||||||
|
static inline int lowerBound(const QVector<RangeDelimiter> &container, qint64 time)
|
||||||
|
{
|
||||||
|
int fromIndex = 0;
|
||||||
|
int toIndex = container.count() - 1;
|
||||||
|
while (toIndex - fromIndex > 1) {
|
||||||
|
int midIndex = (fromIndex + toIndex)/2;
|
||||||
|
if (container[midIndex].timestamp() < time)
|
||||||
|
fromIndex = midIndex;
|
||||||
|
else
|
||||||
|
toIndex = midIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fromIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<Range> ranges;
|
||||||
|
QVector<RangeEnd> endTimes;
|
||||||
|
|
||||||
QVector<int> rowOffsets;
|
QVector<int> rowOffsets;
|
||||||
QmlProfilerModelManager *modelManager;
|
QmlProfilerModelManager *modelManager;
|
||||||
int modelId;
|
int modelId;
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ SOURCES += \
|
|||||||
qmlprofilerviewmanager.cpp \
|
qmlprofilerviewmanager.cpp \
|
||||||
qv8profilerdatamodel.cpp \
|
qv8profilerdatamodel.cpp \
|
||||||
qv8profilereventview.cpp \
|
qv8profilereventview.cpp \
|
||||||
sortedtimelinemodel.cpp \
|
|
||||||
timelinemodelaggregator.cpp \
|
timelinemodelaggregator.cpp \
|
||||||
timelinerenderer.cpp \
|
timelinerenderer.cpp \
|
||||||
timelinezoomcontrol.cpp
|
timelinezoomcontrol.cpp
|
||||||
@@ -66,7 +65,6 @@ HEADERS += \
|
|||||||
qmlprofilerviewmanager.h \
|
qmlprofilerviewmanager.h \
|
||||||
qv8profilerdatamodel.h \
|
qv8profilerdatamodel.h \
|
||||||
qv8profilereventview.h \
|
qv8profilereventview.h \
|
||||||
sortedtimelinemodel.h \
|
|
||||||
timelinemodelaggregator.h \
|
timelinemodelaggregator.h \
|
||||||
timelinerenderer.h \
|
timelinerenderer.h \
|
||||||
timelinezoomcontrol.h
|
timelinezoomcontrol.h
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ QtcPlugin {
|
|||||||
"qmlprofilerviewmanager.cpp", "qmlprofilerviewmanager.h",
|
"qmlprofilerviewmanager.cpp", "qmlprofilerviewmanager.h",
|
||||||
"qv8profilerdatamodel.cpp", "qv8profilerdatamodel.h",
|
"qv8profilerdatamodel.cpp", "qv8profilerdatamodel.h",
|
||||||
"qv8profilereventview.h", "qv8profilereventview.cpp",
|
"qv8profilereventview.h", "qv8profilereventview.cpp",
|
||||||
"sortedtimelinemodel.h", "sortedtimelinemodel.cpp",
|
|
||||||
"timelinemodelaggregator.cpp", "timelinemodelaggregator.h",
|
"timelinemodelaggregator.cpp", "timelinemodelaggregator.h",
|
||||||
"timelinerenderer.cpp", "timelinerenderer.h",
|
"timelinerenderer.cpp", "timelinerenderer.h",
|
||||||
"timelinezoomcontrol.cpp", "timelinezoomcontrol.h"
|
"timelinezoomcontrol.cpp", "timelinezoomcontrol.h"
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ QVariantMap QmlProfilerAnimationsModel::details(int index) const
|
|||||||
QVariantMap result;
|
QVariantMap result;
|
||||||
|
|
||||||
result.insert(QStringLiteral("displayName"), displayName());
|
result.insert(QStringLiteral("displayName"), displayName());
|
||||||
result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(range(index).duration));
|
result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(duration(index)));
|
||||||
result.insert(tr("Framerate"), QString::fromLatin1("%1 FPS").arg(d->data[index].framerate));
|
result.insert(tr("Framerate"), QString::fromLatin1("%1 FPS").arg(d->data[index].framerate));
|
||||||
result.insert(tr("Animations"), QString::fromLatin1("%1").arg(d->data[index].animationcount));
|
result.insert(tr("Animations"), QString::fromLatin1("%1").arg(d->data[index].animationcount));
|
||||||
result.insert(tr("Context"), tr(d->data[index].threadId == QmlDebug::GuiThread ?
|
result.insert(tr("Context"), tr(d->data[index].threadId == QmlDebug::GuiThread ?
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ int QmlProfilerNotesModel::get(int timelineModel, int timelineIndex) const
|
|||||||
int QmlProfilerNotesModel::add(int timelineModel, int timelineIndex, const QString &text)
|
int QmlProfilerNotesModel::add(int timelineModel, int timelineIndex, const QString &text)
|
||||||
{
|
{
|
||||||
const AbstractTimelineModel *model = m_timelineModels[timelineModel];
|
const AbstractTimelineModel *model = m_timelineModels[timelineModel];
|
||||||
int typeId = model->range(timelineIndex).typeId;
|
int typeId = model->typeId(timelineIndex);
|
||||||
Note note = { text, timelineModel, timelineIndex };
|
Note note = { text, timelineModel, timelineIndex };
|
||||||
m_data << note;
|
m_data << note;
|
||||||
m_modified = true;
|
m_modified = true;
|
||||||
@@ -167,9 +167,8 @@ int QmlProfilerNotesModel::add(int typeId, qint64 start, qint64 duration, const
|
|||||||
for (int i = model->firstIndex(start); i <= model->lastIndex(start + duration); ++i) {
|
for (int i = model->firstIndex(start); i <= model->lastIndex(start + duration); ++i) {
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
continue;
|
continue;
|
||||||
const SortedTimelineModel::Range &timelineRange = model->range(i);
|
if (model->typeId(i) == typeId && model->startTime(i) == start &&
|
||||||
if (timelineRange.typeId == typeId && timelineRange.start == start &&
|
model->duration(i) == duration) {
|
||||||
timelineRange.duration == duration) {
|
|
||||||
timelineModel = model->modelId();
|
timelineModel = model->modelId();
|
||||||
timelineIndex = i;
|
timelineIndex = i;
|
||||||
break;
|
break;
|
||||||
@@ -218,9 +217,10 @@ void QmlProfilerNotesModel::saveData()
|
|||||||
if (it == m_timelineModels.end())
|
if (it == m_timelineModels.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const SortedTimelineModel::Range ¬eRange = it.value()->range(note.timelineIndex);
|
const AbstractTimelineModel *model = it.value();
|
||||||
QmlProfilerDataModel::QmlEventNoteData save = {
|
QmlProfilerDataModel::QmlEventNoteData save = {
|
||||||
noteRange.typeId, noteRange.start, noteRange.duration, note.text
|
model->typeId(note.timelineIndex), model->startTime(note.timelineIndex),
|
||||||
|
model->duration(note.timelineIndex), note.text
|
||||||
};
|
};
|
||||||
notes.append(save);
|
notes.append(save);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ void QmlProfilerRangeModel::QmlProfilerRangeModelPrivate::computeNestingContract
|
|||||||
nestingEndTimes.fill(0, nestingLevels + 1);
|
nestingEndTimes.fill(0, nestingLevels + 1);
|
||||||
|
|
||||||
for (i = 0; i < eventCount; i++) {
|
for (i = 0; i < eventCount; i++) {
|
||||||
qint64 st = q->ranges[i].start;
|
qint64 st = ranges[i].start;
|
||||||
|
|
||||||
// per type
|
// per type
|
||||||
if (nestingEndTimes[nestingLevels] > st) {
|
if (nestingEndTimes[nestingLevels] > st) {
|
||||||
@@ -151,7 +151,7 @@ void QmlProfilerRangeModel::QmlProfilerRangeModelPrivate::computeNestingContract
|
|||||||
nestingEndTimes[nestingLevels-1] <= st)
|
nestingEndTimes[nestingLevels-1] <= st)
|
||||||
nestingLevels--;
|
nestingLevels--;
|
||||||
}
|
}
|
||||||
nestingEndTimes[nestingLevels] = st + q->ranges[i].duration;
|
nestingEndTimes[nestingLevels] = st + ranges[i].duration;
|
||||||
|
|
||||||
data[i].displayRowCollapsed = nestingLevels;
|
data[i].displayRowCollapsed = nestingLevels;
|
||||||
}
|
}
|
||||||
@@ -163,7 +163,7 @@ void QmlProfilerRangeModel::QmlProfilerRangeModelPrivate::computeExpandedLevels(
|
|||||||
QHash<int, int> eventRow;
|
QHash<int, int> eventRow;
|
||||||
int eventCount = q->count();
|
int eventCount = q->count();
|
||||||
for (int i = 0; i < eventCount; i++) {
|
for (int i = 0; i < eventCount; i++) {
|
||||||
int typeId = q->range(i).typeId;
|
int typeId = ranges[i].typeId;
|
||||||
if (!eventRow.contains(typeId)) {
|
if (!eventRow.contains(typeId)) {
|
||||||
eventRow[typeId] = expandedRowTypes.size();
|
eventRow[typeId] = expandedRowTypes.size();
|
||||||
expandedRowTypes << typeId;
|
expandedRowTypes << typeId;
|
||||||
@@ -184,25 +184,23 @@ void QmlProfilerRangeModel::QmlProfilerRangeModelPrivate::findBindingLoops()
|
|||||||
|
|
||||||
for (int i = 0; i < q->count(); ++i) {
|
for (int i = 0; i < q->count(); ++i) {
|
||||||
const Range *potentialParent = callStack.isEmpty()
|
const Range *potentialParent = callStack.isEmpty()
|
||||||
? 0 : &q->ranges[callStack.top().second];
|
? 0 : &ranges[callStack.top().second];
|
||||||
|
|
||||||
while (potentialParent
|
while (potentialParent
|
||||||
&& !(potentialParent->start + potentialParent->duration > q->ranges[i].start)) {
|
&& !(potentialParent->start + potentialParent->duration > ranges[i].start)) {
|
||||||
callStack.pop();
|
callStack.pop();
|
||||||
potentialParent = callStack.isEmpty() ? 0
|
potentialParent = callStack.isEmpty() ? 0 : &ranges[callStack.top().second];
|
||||||
: &q->ranges[callStack.top().second];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether event is already in stack
|
// check whether event is already in stack
|
||||||
for (int ii = 0; ii < callStack.size(); ++ii) {
|
for (int ii = 0; ii < callStack.size(); ++ii) {
|
||||||
if (callStack.at(ii).first == q->range(i).typeId) {
|
if (callStack.at(ii).first == ranges[i].typeId) {
|
||||||
data[i].bindingLoopHead = callStack.at(ii).second;
|
data[i].bindingLoopHead = callStack.at(ii).second;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CallStackEntry newEntry(ranges[i].typeId, i);
|
||||||
CallStackEntry newEntry(q->range(i).typeId, i);
|
|
||||||
callStack.push(newEntry);
|
callStack.push(newEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,7 +264,7 @@ QVariantMap QmlProfilerRangeModel::details(int index) const
|
|||||||
d->modelManager->qmlModel()->getEventTypes();
|
d->modelManager->qmlModel()->getEventTypes();
|
||||||
|
|
||||||
result.insert(QStringLiteral("displayName"), categoryLabel(d->rangeType));
|
result.insert(QStringLiteral("displayName"), categoryLabel(d->rangeType));
|
||||||
result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(range(index).duration));
|
result.insert(tr("Duration"), QmlProfilerBaseModel::formatTime(duration(index)));
|
||||||
|
|
||||||
result.insert(tr("Details"), types[id].data);
|
result.insert(tr("Details"), types[id].data);
|
||||||
result.insert(tr("Location"), types[id].displayName);
|
result.insert(tr("Location"), types[id].displayName);
|
||||||
|
|||||||
@@ -1,165 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
|
||||||
** Contact: http://www.qt-project.org/legal
|
|
||||||
**
|
|
||||||
** This file is part of Qt Creator.
|
|
||||||
**
|
|
||||||
** Commercial License Usage
|
|
||||||
** Licensees holding valid commercial Qt licenses may use this file in
|
|
||||||
** accordance with the commercial license agreement provided with the
|
|
||||||
** Software or, alternatively, in accordance with the terms contained in
|
|
||||||
** a written agreement between you and Digia. For licensing terms and
|
|
||||||
** conditions see http://www.qt.io/licensing. For further information
|
|
||||||
** use the contact form at http://www.qt.io/contact-us.
|
|
||||||
**
|
|
||||||
** GNU Lesser General Public License Usage
|
|
||||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
||||||
** General Public License version 2.1 or version 3 as published by the Free
|
|
||||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
||||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
||||||
** following information to ensure the GNU Lesser General Public License
|
|
||||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
||||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
||||||
**
|
|
||||||
** In addition, as a special exception, Digia gives you certain additional
|
|
||||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
|
||||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
||||||
**
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#include "sortedtimelinemodel.h"
|
|
||||||
namespace QmlProfiler {
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\class QmlProfiler::SortedTimelineModel
|
|
||||||
\brief The SortedTimelineModel class provides a sorted model for timeline data.
|
|
||||||
|
|
||||||
The SortedTimelineModel lets you keep range data sorted by both start and end times, so that
|
|
||||||
visible ranges can easily be computed. The only precondition for that to work is that the ranges
|
|
||||||
must be perfectly nested. A "parent" range of a range R is defined as a range for which the
|
|
||||||
start time is smaller than R's start time and the end time is greater than R's end time. A set
|
|
||||||
of ranges is perfectly nested if all parent ranges of any given range have a common parent
|
|
||||||
range. Mind that you can always make that happen by defining a range that spans the whole
|
|
||||||
available time span. That, however, will make any code that uses firstStartTime() and
|
|
||||||
lastEndTime() for selecting subsets of the model always select all of it.
|
|
||||||
|
|
||||||
\note Indices returned from the various methods are only valid until a new range is inserted
|
|
||||||
before them. Inserting a new range before a given index moves the range pointed to by the
|
|
||||||
index by one. Incrementing the index by one will make it point to the item again.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn SortedTimelineModel::clear()
|
|
||||||
Clears the ranges and their end times.
|
|
||||||
*/
|
|
||||||
void SortedTimelineModel::clear()
|
|
||||||
{
|
|
||||||
ranges.clear();
|
|
||||||
endTimes.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn int SortedTimelineModel::count() const
|
|
||||||
Returns the number of ranges in the model.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn qint64 SortedTimelineModel::firstStartTime() const
|
|
||||||
Returns the begin of the first range in the model.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn qint64 SortedTimelineModel::lastEndTime() const
|
|
||||||
Returns the end of the last range in the model.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn const SortedTimelineModel::Range &SortedTimelineModel::range(int index) const
|
|
||||||
Returns the range data at the specified index.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn int SortedTimelineModel::insert(qint64 startTime, qint64 duration)
|
|
||||||
Inserts a range at the given time position and returns its index.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn int SortedTimelineModel::insertStart(qint64 startTime, const Data &item)
|
|
||||||
Inserts the given data as range start at the given time position and
|
|
||||||
returns its index. The range end is not set.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn int SortedTimelineModel::insertEnd(int index, qint64 duration)
|
|
||||||
Adds a range end for the given start index.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn int SortedTimelineModel::firstIndexNoParents(qint64 startTime) const
|
|
||||||
Looks up the first range with an end time greater than the given time and
|
|
||||||
returns its index. If no such range is found, it returns -1.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn int SortedTimelineModel::firstIndex(qint64 startTime) const
|
|
||||||
Looks up the first range with an end time greater than the given time and
|
|
||||||
returns its parent's index. If no such range is found, it returns -1. If there
|
|
||||||
is no parent, it returns the found range's index. The parent of a range is the
|
|
||||||
range with the lowest start time that completely covers the child range.
|
|
||||||
"Completely covers" means:
|
|
||||||
parent.startTime <= child.startTime && parent.endTime >= child.endTime
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn int SortedTimelineModel::lastIndex(qint64 endTime) const
|
|
||||||
Looks up the last range with a start time smaller than the given time and
|
|
||||||
returns its index. If no such range is found, it returns -1.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\fn void SortedTimelineModel::computeNesting()
|
|
||||||
Compute all ranges' parents.
|
|
||||||
\sa findFirstIndex
|
|
||||||
*/
|
|
||||||
void SortedTimelineModel::computeNesting()
|
|
||||||
{
|
|
||||||
QLinkedList<int> parents;
|
|
||||||
for (int range = 0; range != count(); ++range) {
|
|
||||||
Range ¤t = ranges[range];
|
|
||||||
for (QLinkedList<int>::iterator parentIt = parents.begin();;) {
|
|
||||||
if (parentIt == parents.end()) {
|
|
||||||
parents.append(range);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Range &parent = ranges[*parentIt];
|
|
||||||
qint64 parentEnd = parent.start + parent.duration;
|
|
||||||
if (parentEnd < current.start) {
|
|
||||||
if (parent.start == current.start) {
|
|
||||||
if (parent.parent == -1) {
|
|
||||||
parent.parent = range;
|
|
||||||
} else {
|
|
||||||
Range &ancestor = ranges[parent.parent];
|
|
||||||
if (ancestor.start == current.start &&
|
|
||||||
ancestor.duration < current.duration)
|
|
||||||
parent.parent = range;
|
|
||||||
}
|
|
||||||
// Just switch the old parent range for the new, larger one
|
|
||||||
*parentIt = range;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
parentIt = parents.erase(parentIt);
|
|
||||||
}
|
|
||||||
} else if (parentEnd >= current.start + current.duration) {
|
|
||||||
// no need to insert
|
|
||||||
current.parent = *parentIt;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
++parentIt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
**
|
|
||||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
|
||||||
** Contact: http://www.qt-project.org/legal
|
|
||||||
**
|
|
||||||
** This file is part of Qt Creator.
|
|
||||||
**
|
|
||||||
** Commercial License Usage
|
|
||||||
** Licensees holding valid commercial Qt licenses may use this file in
|
|
||||||
** accordance with the commercial license agreement provided with the
|
|
||||||
** Software or, alternatively, in accordance with the terms contained in
|
|
||||||
** a written agreement between you and Digia. For licensing terms and
|
|
||||||
** conditions see http://www.qt.io/licensing. For further information
|
|
||||||
** use the contact form at http://www.qt.io/contact-us.
|
|
||||||
**
|
|
||||||
** GNU Lesser General Public License Usage
|
|
||||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
||||||
** General Public License version 2.1 or version 3 as published by the Free
|
|
||||||
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
||||||
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
||||||
** following information to ensure the GNU Lesser General Public License
|
|
||||||
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
||||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
||||||
**
|
|
||||||
** In addition, as a special exception, Digia gives you certain additional
|
|
||||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
|
||||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
||||||
**
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef SORTEDTIMELINEMODEL_H
|
|
||||||
#define SORTEDTIMELINEMODEL_H
|
|
||||||
|
|
||||||
#include "qmlprofiler_global.h"
|
|
||||||
#include <QObject>
|
|
||||||
#include <QVector>
|
|
||||||
#include <QLinkedList>
|
|
||||||
|
|
||||||
namespace QmlProfiler {
|
|
||||||
|
|
||||||
class QMLPROFILER_EXPORT SortedTimelineModel : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
struct Range {
|
|
||||||
Range() : start(-1), duration(-1), typeId(-1), parent(-1) {}
|
|
||||||
Range(qint64 start, qint64 duration, int typeId) :
|
|
||||||
start(start), duration(duration), typeId(typeId), parent(-1) {}
|
|
||||||
qint64 start;
|
|
||||||
qint64 duration;
|
|
||||||
int typeId;
|
|
||||||
int parent;
|
|
||||||
inline qint64 timestamp() const {return start;}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RangeEnd {
|
|
||||||
RangeEnd() : startIndex(-1), end(-1) {}
|
|
||||||
RangeEnd(int startIndex, qint64 end) :
|
|
||||||
startIndex(startIndex), end(end) {}
|
|
||||||
int startIndex;
|
|
||||||
qint64 end;
|
|
||||||
inline qint64 timestamp() const {return end;}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline int count() const { return ranges.count(); }
|
|
||||||
|
|
||||||
qint64 duration(int index) const { return ranges[index].duration; }
|
|
||||||
qint64 startTime(int index) const { return ranges[index].start; }
|
|
||||||
qint64 endTime(int index) const { return ranges[index].start + ranges[index].duration; }
|
|
||||||
int typeId(int index) const { return ranges[index].typeId; }
|
|
||||||
|
|
||||||
inline qint64 lastEndTime() const { return endTimes.last().end; }
|
|
||||||
inline qint64 firstStartTime() const { return ranges.first().start; }
|
|
||||||
|
|
||||||
inline const Range &range(int index) const { return ranges[index]; }
|
|
||||||
|
|
||||||
inline int firstIndex(qint64 startTime) const
|
|
||||||
{
|
|
||||||
int index = firstIndexNoParents(startTime);
|
|
||||||
if (index == -1)
|
|
||||||
return -1;
|
|
||||||
int parent = ranges[index].parent;
|
|
||||||
return parent == -1 ? index : parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int firstIndexNoParents(qint64 startTime) const
|
|
||||||
{
|
|
||||||
// in the "endtime" list, find the first event that ends after startTime
|
|
||||||
if (endTimes.isEmpty())
|
|
||||||
return -1;
|
|
||||||
if (endTimes.count() == 1 || endTimes.first().end > startTime)
|
|
||||||
return endTimes.first().startIndex;
|
|
||||||
if (endTimes.last().end <= startTime)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return endTimes[lowerBound(endTimes, startTime) + 1].startIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int lastIndex(qint64 endTime) const
|
|
||||||
{
|
|
||||||
// in the "starttime" list, find the last event that starts before endtime
|
|
||||||
if (ranges.isEmpty() || ranges.first().start >= endTime)
|
|
||||||
return -1;
|
|
||||||
if (ranges.count() == 1)
|
|
||||||
return 0;
|
|
||||||
if (ranges.last().start < endTime)
|
|
||||||
return ranges.count() - 1;
|
|
||||||
|
|
||||||
return lowerBound(ranges, endTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
SortedTimelineModel(QObject *parent = 0) : QObject(parent) {}
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
inline int insert(qint64 startTime, qint64 duration, int typeId)
|
|
||||||
{
|
|
||||||
/* Doing insert-sort here is preferable as most of the time the times will actually be
|
|
||||||
* presorted in the right way. So usually this will just result in appending. */
|
|
||||||
int index = insertSorted(ranges, Range(startTime, duration, typeId));
|
|
||||||
if (index < ranges.size() - 1)
|
|
||||||
incrementStartIndices(index);
|
|
||||||
insertSorted(endTimes, RangeEnd(index, startTime + duration));
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int insertStart(qint64 startTime, int typeId)
|
|
||||||
{
|
|
||||||
int index = insertSorted(ranges, Range(startTime, 0, typeId));
|
|
||||||
if (index < ranges.size() - 1)
|
|
||||||
incrementStartIndices(index);
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void insertEnd(int index, qint64 duration)
|
|
||||||
{
|
|
||||||
ranges[index].duration = duration;
|
|
||||||
insertSorted(endTimes, RangeEnd(index, ranges[index].start + duration));
|
|
||||||
}
|
|
||||||
|
|
||||||
void computeNesting();
|
|
||||||
|
|
||||||
void incrementStartIndices(int index)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < endTimes.size(); ++i) {
|
|
||||||
if (endTimes[i].startIndex >= index)
|
|
||||||
endTimes[i].startIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename RangeDelimiter>
|
|
||||||
static inline int insertSorted(QVector<RangeDelimiter> &container, const RangeDelimiter &item)
|
|
||||||
{
|
|
||||||
for (int i = container.count();;) {
|
|
||||||
if (i == 0) {
|
|
||||||
container.prepend(item);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (container[--i].timestamp() <= item.timestamp()) {
|
|
||||||
container.insert(++i, item);
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename RangeDelimiter>
|
|
||||||
static inline int lowerBound(const QVector<RangeDelimiter> &container, qint64 time)
|
|
||||||
{
|
|
||||||
int fromIndex = 0;
|
|
||||||
int toIndex = container.count() - 1;
|
|
||||||
while (toIndex - fromIndex > 1) {
|
|
||||||
int midIndex = (fromIndex + toIndex)/2;
|
|
||||||
if (container[midIndex].timestamp() < time)
|
|
||||||
fromIndex = midIndex;
|
|
||||||
else
|
|
||||||
toIndex = midIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fromIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<Range> ranges;
|
|
||||||
QVector<RangeEnd> endTimes;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
Reference in New Issue
Block a user