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 ¬e = 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 ¬e = 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 ¬e = m_data[index]; + if (text != note.text) { + note.text = text; + emit changed(typeId(index), note.timelineModel, note.timelineIndex); + } +} + +void NotesModel::remove(int index) +{ + Note ¬e = 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> ¬es = + m_modelManager->qmlModel()->getEventNotes(); + for (int i = 0; i != notes.size(); ++i) { + const QmlProfilerDataModel::QmlEventNoteData ¬e = 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 ¬e = m_data[i]; + auto it = m_timelineModels.find(note.timelineModel); + if (it == m_timelineModels.end()) + continue; + + const SortedTimelineModel::Range ¬eRange = 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> ¬es) +{ + 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> ¬es); 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