From 350615cc7111b8b5427d623ecfb6ea91f80ff96c Mon Sep 17 00:00:00 2001
From: Ulf Hermann <ulf.hermann@digia.com>
Date: Fri, 26 Sep 2014 15:49:49 +0200
Subject: [PATCH] QmlProfiler: Add special model for notes

Change-Id: Ia0acbc5e0a02563cf497594b67a5f7a67488fb79
Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
---
 src/plugins/qmlprofiler/notesmodel.cpp        | 216 ++++++++++++++++++
 src/plugins/qmlprofiler/notesmodel.h          |  89 ++++++++
 src/plugins/qmlprofiler/qmlprofiler.pro       |   6 +-
 .../qmlprofiler/qmlprofilerdatamodel.cpp      |  15 ++
 .../qmlprofiler/qmlprofilerdatamodel.h        |   9 +
 .../qmlprofiler/qmlprofilermodelmanager.cpp   |  13 ++
 .../qmlprofiler/qmlprofilermodelmanager.h     |   2 +
 .../qmlprofiler/timelinemodelaggregator.cpp   |  86 ++++++-
 .../qmlprofiler/timelinemodelaggregator.h     |  12 +
 9 files changed, 445 insertions(+), 3 deletions(-)
 create mode 100644 src/plugins/qmlprofiler/notesmodel.cpp
 create mode 100644 src/plugins/qmlprofiler/notesmodel.h

diff --git a/src/plugins/qmlprofiler/notesmodel.cpp b/src/plugins/qmlprofiler/notesmodel.cpp
new file mode 100644
index 00000000000..70d0fdd4961
--- /dev/null
+++ b/src/plugins/qmlprofiler/notesmodel.cpp
@@ -0,0 +1,216 @@
+/****************************************************************************
+**
+** 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://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: 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 "notesmodel.h"
+
+namespace QmlProfiler {
+
+NotesModel::NotesModel(QObject *parent) : QObject(parent), m_modelManager(0)
+{
+}
+
+int NotesModel::count() const
+{
+    return m_data.count();
+}
+
+void NotesModel::setModelManager(QmlProfilerModelManager *modelManager)
+{
+    m_modelManager = modelManager;
+}
+
+void NotesModel::addTimelineModel(const AbstractTimelineModel *timelineModel)
+{
+    connect(timelineModel, &AbstractTimelineModel::destroyed,
+            this, &NotesModel::removeTimelineModel);
+    m_timelineModels.insert(timelineModel->modelId(), timelineModel);
+}
+
+int NotesModel::typeId(int index) const
+{
+    const Note &note = m_data[index];
+    auto it = m_timelineModels.find(note.timelineModel);
+    if (it == m_timelineModels.end())
+        return -1; // This can happen if one of the timeline models has been removed
+    return it.value()->typeId(note.timelineIndex);
+}
+
+QString NotesModel::text(int index) const
+{
+    return m_data[index].text;
+}
+
+int NotesModel::timelineModel(int index) const
+{
+    return m_data[index].timelineModel;
+}
+
+int NotesModel::timelineIndex(int index) const
+{
+    return m_data[index].timelineIndex;
+}
+
+QVariantList NotesModel::byTypeId(int selectedType) const
+{
+    QVariantList ret;
+    for (int noteId = 0; noteId < count(); ++noteId) {
+        if (selectedType == typeId(noteId))
+            ret << noteId;
+    }
+    return ret;
+}
+
+QVariantList NotesModel::byTimelineModel(int timelineModel) const
+{
+    QVariantList ret;
+    for (int noteId = 0; noteId < count(); ++noteId) {
+        if (m_data[noteId].timelineModel == timelineModel)
+            ret << noteId;
+    }
+    return ret;
+}
+
+int NotesModel::get(int timelineModel, int timelineIndex) const
+{
+    for (int noteId = 0; noteId < count(); ++noteId) {
+        const Note &note = m_data[noteId];
+        if (note.timelineModel == timelineModel && note.timelineIndex == timelineIndex)
+            return noteId;
+    }
+
+    return -1;
+}
+
+int NotesModel::add(int timelineModel, int timelineIndex, const QString &text)
+{
+    const AbstractTimelineModel *model = m_timelineModels[timelineModel];
+    int typeId = model->range(timelineIndex).typeId;
+    Note note = { text, timelineModel, timelineIndex };
+    m_data << note;
+    emit changed(typeId, timelineModel, timelineIndex);
+    return m_data.count() - 1;
+}
+
+void NotesModel::update(int index, const QString &text)
+{
+    Note &note = m_data[index];
+    if (text != note.text) {
+        note.text = text;
+        emit changed(typeId(index), note.timelineModel, note.timelineIndex);
+    }
+}
+
+void NotesModel::remove(int index)
+{
+    Note &note = m_data[index];
+    int noteType = typeId(index);
+    int timelineModel = note.timelineModel;
+    int timelineIndex = note.timelineIndex;
+    m_data.removeAt(index);
+    emit changed(noteType, timelineModel, timelineIndex);
+}
+
+void NotesModel::removeTimelineModel(QObject *timelineModel)
+{
+    for (auto i = m_timelineModels.begin(); i != m_timelineModels.end();) {
+        if (i.value() == timelineModel)
+            i = m_timelineModels.erase(i);
+        else
+            ++i;
+    }
+}
+
+int NotesModel::add(int typeId, qint64 start, qint64 duration, const QString &text)
+{
+    int timelineModel = -1;
+    int timelineIndex = -1;
+    const QVector<QmlProfilerDataModel::QmlEventTypeData> &types =
+            m_modelManager->qmlModel()->getEventTypes();
+    foreach (const AbstractTimelineModel *model, m_timelineModels) {
+        if (model->accepted(types[typeId])) {
+            for (int i = model->firstIndex(start); i <= model->lastIndex(start + duration); ++i) {
+                if (i < 0)
+                    continue;
+                const SortedTimelineModel::Range &timelineRange = model->range(i);
+                if (timelineRange.typeId == typeId && timelineRange.start == start &&
+                        timelineRange.duration == duration) {
+                    timelineModel = model->modelId();
+                    timelineIndex = i;
+                    break;
+                }
+            }
+            if (timelineIndex != -1)
+                break;
+        }
+    }
+
+    if (timelineModel == -1 || timelineIndex == -1)
+        return -1;
+
+    Note note = { text, timelineModel, timelineIndex };
+    m_data << note;
+    return m_data.count() - 1;
+}
+
+void NotesModel::clear()
+{
+    m_data.clear();
+    emit changed(-1, -1, -1);
+}
+
+void NotesModel::loadData()
+{
+    m_data.clear();
+    const QVector<QmlProfilerDataModel::QmlEventNoteData> &notes =
+            m_modelManager->qmlModel()->getEventNotes();
+    for (int i = 0; i != notes.size(); ++i) {
+        const QmlProfilerDataModel::QmlEventNoteData &note = notes[i];
+        add(note.typeIndex, note.startTime, note.duration, note.text);
+    }
+    emit changed(-1, -1, -1);
+}
+
+void NotesModel::saveData()
+{
+    QVector<QmlProfilerDataModel::QmlEventNoteData> notes;
+    for (int i = 0; i < count(); ++i) {
+        const Note &note = m_data[i];
+        auto it = m_timelineModels.find(note.timelineModel);
+        if (it == m_timelineModels.end())
+            continue;
+
+        const SortedTimelineModel::Range &noteRange = it.value()->range(note.timelineIndex);
+        QmlProfilerDataModel::QmlEventNoteData save = {
+            noteRange.typeId, noteRange.start, noteRange.duration, note.text
+        };
+        notes.append(save);
+    }
+    m_modelManager->qmlModel()->setNoteData(notes);
+}
+}
diff --git a/src/plugins/qmlprofiler/notesmodel.h b/src/plugins/qmlprofiler/notesmodel.h
new file mode 100644
index 00000000000..9d96f79129c
--- /dev/null
+++ b/src/plugins/qmlprofiler/notesmodel.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** 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://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: 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 NOTESMODEL_H
+#define NOTESMODEL_H
+
+#include "abstracttimelinemodel.h"
+#include "qmlprofilermodelmanager.h"
+#include <QList>
+#include <QHash>
+
+namespace QmlProfiler {
+class QMLPROFILER_EXPORT NotesModel : public QObject {
+    Q_OBJECT
+public:
+    struct Note {
+        // Saved properties
+        QString text;
+
+        // Cache, created on loading
+        int timelineModel;
+        int timelineIndex;
+    };
+
+    NotesModel(QObject *parent);
+    int count() const;
+
+    void setModelManager(QmlProfilerModelManager *modelManager);
+    void addTimelineModel(const AbstractTimelineModel *timelineModel);
+
+    int typeId(int index) const;
+    QString text(int index) const;
+    int timelineModel(int index) const;
+    int timelineIndex(int index) const;
+
+    QVariantList byTypeId(int typeId) const;
+
+    QVariantList byTimelineModel(int timelineModel) const;
+
+    int get(int timelineModel, int timelineIndex) const;
+    int add(int timelineModel, int timelineIndex, const QString &text);
+    void update(int index, const QString &text);
+    void remove(int index);
+
+    void loadData();
+    void saveData();
+
+signals:
+    void changed(int typeId, int timelineModel, int timelineIndex);
+
+private slots:
+    void removeTimelineModel(QObject *timelineModel);
+
+protected:
+    QmlProfilerModelManager *m_modelManager;
+    QList<Note> m_data;
+    QHash<int, const AbstractTimelineModel *> m_timelineModels;
+
+    int add(int typeId, qint64 startTime, qint64 duration, const QString &text);
+    void clear();
+};
+}
+#endif // NOTESMODEL_H
diff --git a/src/plugins/qmlprofiler/qmlprofiler.pro b/src/plugins/qmlprofiler/qmlprofiler.pro
index 5eb0b593e1c..f71830c7df1 100644
--- a/src/plugins/qmlprofiler/qmlprofiler.pro
+++ b/src/plugins/qmlprofiler/qmlprofiler.pro
@@ -31,7 +31,8 @@ SOURCES += \
     qmlprofilerpainteventsmodelproxy.cpp \
     sortedtimelinemodel.cpp \
     qmlprofilerbasemodel.cpp \
-    qmlprofilerdatamodel.cpp
+    qmlprofilerdatamodel.cpp \
+    notesmodel.cpp
 
 HEADERS += \
     qmlprofilerconstants.h \
@@ -65,7 +66,8 @@ HEADERS += \
     qmlprofilerbasemodel.h \
     abstracttimelinemodel_p.h \
     qmlprofilerdatamodel.h \
-    qmlprofilerbasemodel_p.h
+    qmlprofilerbasemodel_p.h \
+    notesmodel.h
 
 RESOURCES += \
     qml/qmlprofiler.qrc
diff --git a/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp b/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp
index 9e18cb8dce3..b0ff9638596 100644
--- a/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp
@@ -31,6 +31,7 @@
 #include "qmlprofilerdatamodel.h"
 #include "qmlprofilerbasemodel_p.h"
 #include "qmlprofilermodelmanager.h"
+#include "notesmodel.h"
 #include <qmldebug/qmlprofilereventtypes.h>
 #include <utils/qtcassert.h>
 #include <QUrl>
@@ -46,6 +47,7 @@ public:
     QmlProfilerDataModelPrivate(QmlProfilerDataModel *qq) : QmlProfilerBaseModelPrivate(qq) {}
     QVector<QmlEventTypeData> eventTypes;
     QVector<QmlEventData> eventList;
+    QVector<QmlEventNoteData> eventNotes;
     QHash<QmlEventTypeData, int> eventTypeIds;
 private:
     Q_DECLARE_PUBLIC(QmlProfilerDataModel)
@@ -127,6 +129,12 @@ const QVector<QmlProfilerDataModel::QmlEventTypeData> &QmlProfilerDataModel::get
     return d->eventTypes;
 }
 
+const QVector<QmlProfilerDataModel::QmlEventNoteData> &QmlProfilerDataModel::getEventNotes() const
+{
+    Q_D(const QmlProfilerDataModel);
+    return d->eventNotes;
+}
+
 void QmlProfilerDataModel::setData(const QVector<QmlProfilerDataModel::QmlEventTypeData> &types,
                                    const QVector<QmlProfilerDataModel::QmlEventData> &events)
 {
@@ -139,6 +147,12 @@ void QmlProfilerDataModel::setData(const QVector<QmlProfilerDataModel::QmlEventT
     d->modelManager->modelProxyCountUpdated(d->modelId, 1, 2);
 }
 
+void QmlProfilerDataModel::setNoteData(const QVector<QmlProfilerDataModel::QmlEventNoteData> &notes)
+{
+    Q_D(QmlProfilerDataModel);
+    d->eventNotes = notes;
+}
+
 int QmlProfilerDataModel::count() const
 {
     Q_D(const QmlProfilerDataModel);
@@ -151,6 +165,7 @@ void QmlProfilerDataModel::clear()
     d->eventList.clear();
     d->eventTypes.clear();
     d->eventTypeIds.clear();
+    d->eventNotes.clear();
     // This call emits changed(). Don't emit it again here.
     QmlProfilerBaseModel::clear();
 }
diff --git a/src/plugins/qmlprofiler/qmlprofilerdatamodel.h b/src/plugins/qmlprofiler/qmlprofilerdatamodel.h
index 2e4509d2a2a..8b6a3f24794 100644
--- a/src/plugins/qmlprofiler/qmlprofilerdatamodel.h
+++ b/src/plugins/qmlprofiler/qmlprofilerdatamodel.h
@@ -60,11 +60,20 @@ public:
         qint64 numericData5;
     };
 
+    struct QmlEventNoteData {
+        int typeIndex;
+        qint64 startTime;
+        qint64 duration;
+        QString text;
+    };
+
     explicit QmlProfilerDataModel(Utils::FileInProjectFinder *fileFinder, QmlProfilerModelManager *parent = 0);
 
     const QVector<QmlEventData> &getEvents() const;
     const QVector<QmlEventTypeData> &getEventTypes() const;
+    const QVector<QmlEventNoteData> &getEventNotes() const;
     void setData(const QVector<QmlEventTypeData> &types, const QVector<QmlEventData> &events);
+    void setNoteData(const QVector<QmlEventNoteData> &notes);
 
     int count() const;
     virtual void clear();
diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
index f704077b981..da753259561 100644
--- a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
@@ -32,6 +32,7 @@
 #include "qmlprofilerdatamodel.h"
 #include "qv8profilerdatamodel.h"
 #include "qmlprofilertracefile.h"
+#include "notesmodel.h"
 
 #include <utils/qtcassert.h>
 
@@ -172,6 +173,8 @@ public:
 
     QmlProfilerDataModel *model;
     QV8ProfilerDataModel *v8Model;
+    NotesModel *notesModel;
+
     QmlProfilerDataState *dataState;
     QmlProfilerTraceTime *traceTime;
 
@@ -197,6 +200,8 @@ QmlProfilerModelManager::QmlProfilerModelManager(Utils::FileInProjectFinder *fin
     d->v8Model = new QV8ProfilerDataModel(finder, this);
     d->dataState = new QmlProfilerDataState(this, this);
     d->traceTime = new QmlProfilerTraceTime(this);
+    d->notesModel = new NotesModel(this);
+    d->notesModel->setModelManager(this);
 }
 
 QmlProfilerModelManager::~QmlProfilerModelManager()
@@ -219,6 +224,11 @@ QV8ProfilerDataModel *QmlProfilerModelManager::v8Model() const
     return d->v8Model;
 }
 
+NotesModel *QmlProfilerModelManager::notesModel() const
+{
+    return d->notesModel;
+}
+
 bool QmlProfilerModelManager::isEmpty() const
 {
     return d->model->isEmpty() && d->v8Model->isEmpty();
@@ -328,6 +338,8 @@ void QmlProfilerModelManager::complete()
 {
     switch (state()) {
     case QmlProfilerDataState::ProcessingData:
+        // Load notes after the timeline models have been initialized.
+        d->notesModel->loadData();
         setState(QmlProfilerDataState::Done);
         emit dataAvailable();
         break;
@@ -367,6 +379,7 @@ void QmlProfilerModelManager::save(const QString &filename)
 
     QmlProfilerFileWriter writer;
 
+    d->notesModel->saveData();
     writer.setTraceTime(traceTime()->startTime(), traceTime()->endTime(), traceTime()->duration());
     writer.setV8DataModel(d->v8Model);
     writer.setQmlEvents(d->model->getEventTypes(), d->model->getEvents());
diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.h b/src/plugins/qmlprofiler/qmlprofilermodelmanager.h
index 7dcbb1b55f4..e3c883eed1f 100644
--- a/src/plugins/qmlprofiler/qmlprofilermodelmanager.h
+++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.h
@@ -43,6 +43,7 @@ namespace QmlProfiler {
 class QmlProfilerModelManager;
 class QmlProfilerDataModel;
 class QV8ProfilerDataModel;
+class NotesModel;
 
 namespace Internal {
 
@@ -120,6 +121,7 @@ public:
     QmlProfilerTraceTime *traceTime() const;
     QmlProfilerDataModel *qmlModel() const;
     QV8ProfilerDataModel *v8Model() const;
+    NotesModel *notesModel() const;
 
     bool isEmpty() const;
     int count() const;
diff --git a/src/plugins/qmlprofiler/timelinemodelaggregator.cpp b/src/plugins/qmlprofiler/timelinemodelaggregator.cpp
index d0ac18ea197..6b839bd3c40 100644
--- a/src/plugins/qmlprofiler/timelinemodelaggregator.cpp
+++ b/src/plugins/qmlprofiler/timelinemodelaggregator.cpp
@@ -33,6 +33,7 @@
 #include "qmlprofilertimelinemodelproxy.h"
 #include "qmlprofilerpainteventsmodelproxy.h"
 #include "qmlprofilerplugin.h"
+#include "notesmodel.h"
 
 #include <QStringList>
 #include <QVariant>
@@ -48,6 +49,9 @@ public:
 
     TimelineModelAggregator *q;
 
+    // mapping of modelId assigned by manager to index in our list
+    QList <int> modelManagerIndexMapping;
+
     QList <AbstractTimelineModel *> modelList;
     QmlProfilerModelManager *modelManager;
 };
@@ -93,15 +97,21 @@ void TimelineModelAggregator::setModelManager(QmlProfilerModelManager *modelMana
 
     // Connect this last so that it's executed after the models have updated their data.
     connect(modelManager->qmlModel(),SIGNAL(changed()),this,SIGNAL(stateChanged()));
+    connect(modelManager->notesModel(), SIGNAL(changed(int,int,int)),
+            this, SIGNAL(notesChanged(int,int,int)));
 }
 
 void TimelineModelAggregator::addModel(AbstractTimelineModel *m)
 {
+    while (d->modelManagerIndexMapping.size() <= m->modelId())
+        d->modelManagerIndexMapping.append(-1);
+    d->modelManagerIndexMapping[m->modelId()] = d->modelList.size();
     d->modelList << m;
     connect(m,SIGNAL(expandedChanged()),this,SIGNAL(expandedChanged()));
     connect(m,SIGNAL(hiddenChanged()),this,SIGNAL(hiddenChanged()));
     connect(m,SIGNAL(rowHeightChanged()),this,SIGNAL(rowHeightChanged()));
     connect(m,SIGNAL(heightChanged()),this,SIGNAL(heightChanged()));
+    d->modelManager->notesModel()->addTimelineModel(m);
     emit modelsChanged(d->modelList.length(), d->modelList.length());
 }
 
@@ -118,6 +128,16 @@ QVariantList TimelineModelAggregator::models() const
     return ret;
 }
 
+int TimelineModelAggregator::modelIndexFromManagerIndex(int modelManagerIndex) const
+{
+    return d->modelManagerIndexMapping[modelManagerIndex];
+}
+
+NotesModel *TimelineModelAggregator::notes() const
+{
+    return d->modelManager->notesModel();
+}
+
 int TimelineModelAggregator::count(int modelIndex) const
 {
     return d->modelList[modelIndex]->count();
@@ -282,10 +302,74 @@ int TimelineModelAggregator::selectionIdForLocation(int modelIndex, const QStrin
 
 void TimelineModelAggregator::swapModels(int modelIndex1, int modelIndex2)
 {
-    qSwap(d->modelList[modelIndex1], d->modelList[modelIndex2]);
+    AbstractTimelineModel *&model1 = d->modelList[modelIndex1];
+    AbstractTimelineModel *&model2 = d->modelList[modelIndex2];
+    std::swap(d->modelManagerIndexMapping[model1->modelId()],
+              d->modelManagerIndexMapping[model2->modelId()]);
+    std::swap(model1, model2);
     emit modelsChanged(modelIndex1, modelIndex2);
 }
 
+QString TimelineModelAggregator::noteText(int noteId) const
+{
+    return d->modelManager->notesModel()->text(noteId);
+}
+
+QString TimelineModelAggregator::noteText(int modelIndex, int index) const
+{
+    int managerId = d->modelList[modelIndex]->modelId();
+    int noteId = d->modelManager->notesModel()->get(managerId, index);
+    return noteId != -1 ? noteText(noteId) : QString();
+}
+
+void TimelineModelAggregator::setNoteText(int noteId, const QString &text)
+{
+    if (text.length() > 0) {
+        notes()->update(noteId, text);
+    } else {
+        notes()->remove(noteId);
+    }
+}
+
+void TimelineModelAggregator::setNoteText(int modelIndex, int index, const QString &text)
+{
+    int managerId = d->modelList[modelIndex]->modelId();
+    NotesModel *notesModel = notes();
+    int noteId = notesModel->get(managerId, index);
+    if (noteId == -1) {
+        if (text.length() > 0)
+            notesModel->add(managerId, index, text);
+    } else {
+        setNoteText(noteId, text);
+    }
+}
+
+int TimelineModelAggregator::noteTimelineModel(int noteIndex) const
+{
+    return d->modelManagerIndexMapping[notes()->timelineModel(noteIndex)];
+}
+
+int TimelineModelAggregator::noteTimelineIndex(int noteIndex) const
+{
+    return notes()->timelineIndex(noteIndex);
+}
+
+QVariantList TimelineModelAggregator::notesByTimelineModel(int modelIndex) const
+{
+    int managerId = d->modelList[modelIndex]->modelId();
+    return notes()->byTimelineModel(managerId);
+}
+
+QVariantList TimelineModelAggregator::notesByTypeId(int typeId) const
+{
+    return notes()->byTypeId(typeId);
+}
+
+int TimelineModelAggregator::noteCount() const
+{
+    return notes()->count();
+}
+
 void TimelineModelAggregator::dataChanged()
 {
     // this is a slot connected for every modelproxy
diff --git a/src/plugins/qmlprofiler/timelinemodelaggregator.h b/src/plugins/qmlprofiler/timelinemodelaggregator.h
index 056a15e8ee0..ebc9f28ca0d 100644
--- a/src/plugins/qmlprofiler/timelinemodelaggregator.h
+++ b/src/plugins/qmlprofiler/timelinemodelaggregator.h
@@ -51,7 +51,9 @@ public:
     void addModel(AbstractTimelineModel *m);
     const AbstractTimelineModel *model(int modelIndex) const;
     QVariantList models() const;
+    int modelIndexFromManagerIndex(int modelManagerIndex) const;
 
+    Q_INVOKABLE NotesModel *notes() const;
     Q_INVOKABLE int count(int modelIndex) const;
     void clear();
     Q_INVOKABLE int modelCount() const;
@@ -102,6 +104,15 @@ public:
                                        int column) const;
 
     Q_INVOKABLE void swapModels(int modelIndex1, int modelIndex2);
+    Q_INVOKABLE QString noteText(int noteId) const;
+    Q_INVOKABLE QString noteText(int modelIndex, int index) const;
+    Q_INVOKABLE void setNoteText(int noteId, const QString &text);
+    Q_INVOKABLE void setNoteText(int modelIndex, int index, const QString &text);
+    Q_INVOKABLE int noteTimelineModel(int noteIndex) const;
+    Q_INVOKABLE int noteTimelineIndex(int noteIndex) const;
+    Q_INVOKABLE QVariantList notesByTimelineModel(int modelIndex) const;
+    Q_INVOKABLE QVariantList notesByTypeId(int typeId) const;
+    Q_INVOKABLE int noteCount() const;
 
 signals:
     void dataAvailable();
@@ -109,6 +120,7 @@ signals:
     void expandedChanged();
     void hiddenChanged();
     void rowHeightChanged();
+    void notesChanged(int typeId, int modelIndex, int eventIndex);
     void modelsChanged(int modelIndex1, int modelIndex2);
     void heightChanged();
 
-- 
GitLab