summaryrefslogtreecommitdiff
path: root/event_list.cpp
diff options
context:
space:
mode:
authorTimotej Lazar <timotej.lazar@araneo.si>2021-09-01 17:13:51 +0200
committerTimotej Lazar <timotej.lazar@araneo.si>2021-09-16 20:33:05 +0200
commitcb76fedcbc8e419e2b945baa56ac3f986a9e79a3 (patch)
treee29be52e4372d3d6e5bfe7056d7af1e1cc0ae2bc /event_list.cpp
parente9b70c585c6bf1fa68a594a8755d90c017e6260c (diff)
Implement event model in C++
Filtering events in JS is too slow with >20,000 events. This moves the event data model into C++.
Diffstat (limited to 'event_list.cpp')
-rw-r--r--event_list.cpp141
1 files changed, 141 insertions, 0 deletions
diff --git a/event_list.cpp b/event_list.cpp
new file mode 100644
index 0000000..2654bef
--- /dev/null
+++ b/event_list.cpp
@@ -0,0 +1,141 @@
+#include "event_list.h"
+
+#include <QJsonArray>
+#include <QJSValue>
+
+Qt::ItemFlags EventList::flags(const QModelIndex&) const
+{
+ return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;
+}
+
+QHash<int, QByteArray> EventList::roleNames() const
+{
+ static const QHash<int, QByteArray> roles{
+ {Role::Time, "time"},
+ {Role::Tag, "tag"},
+ {Role::Values, "values"},
+ };
+ return roles;
+}
+
+int EventList::rowCount(const QModelIndex&) const
+{
+ return events.size();
+}
+
+QVariant EventList::data(const QModelIndex& index, int role) const
+{
+ const auto& event = events[index.row()];
+ switch (role) {
+ case Role::Time:
+ return event.time;
+ case Role::Tag:
+ return event.tag;
+ case Role::Values:
+ return event.values;
+ default:
+ return {};
+ }
+}
+
+bool EventList::setData(const QModelIndex& index, const QVariant& value, int role)
+{
+ auto& event = events[index.row()];
+ switch (role) {
+ case Role::Time:
+ event.time = value.toLongLong();
+ break;
+ case Role::Tag:
+ event.tag = value.toString();
+ break;
+ case Role::Values:
+ event.values = value.value<QJSValue>().toVariant().toMap();
+ break;
+ default:
+ return false;
+ }
+ emit dataChanged(index, index, {role});
+ return true;
+}
+
+int EventList::insert(const int time)
+{
+ int row = time == -1 ? rowCount() : find(time);
+ beginInsertRows(QModelIndex{}, row, row);
+ events.insert(row, {time});
+ endInsertRows();
+ return row;
+}
+
+bool EventList::removeRows(int row, int count, const QModelIndex&)
+{
+ beginRemoveRows({}, row, row + count - 1);
+ while (row < events.size() && count-- > 0)
+ events.removeAt(row);
+ endRemoveRows();
+ return count == -1;
+}
+
+void EventList::load(const QJsonObject& json)
+{
+ const auto& jsonTags = json["tags"].toArray();
+ if (!jsonTags.isEmpty()) {
+ tags = {};
+ tagsOrder.clear();
+ for (int i = 0; i < jsonTags.size(); i++) {
+ const auto name = jsonTags[i]["tag"].toString();
+ tags[name] = jsonTags[i].toObject();
+ tagsOrder.append(name);
+ }
+ emit tagsChanged();
+ }
+
+ const auto& jsonEvents = json["events"].toArray();
+ if (!jsonEvents.isEmpty()) {
+ beginResetModel();
+ events.clear();
+ for (int i = 0; i < jsonEvents.size(); i++) {
+ auto event = jsonEvents[i].toObject().toVariantMap();
+ events.append({
+ event["time"].toLongLong(),
+ event["tag"].toString(),
+ event[event.contains("values") ? "values" : "fields"].toMap(),
+ });
+ }
+ endResetModel();
+ }
+}
+
+QJsonObject EventList::save() const
+{
+ QJsonArray jsonEvents;
+ for (const auto& event : events) {
+ jsonEvents.append(QJsonObject{
+ {"time", event.time},
+ {"tag", event.tag},
+ {"values", QJsonObject::fromVariantMap(event.values)}
+ });
+ }
+
+ QJsonArray jsonTags;
+ for (int i = 0; i < tagsOrder.size(); i++)
+ jsonTags.append(tags[tagsOrder[i]].toObject());
+
+ return {{"tags", jsonTags}, {"events", jsonEvents}};
+}
+
+// Return the index of the last event not later than given time.
+// Assumes events are sorted by time.
+int EventList::find(long long time) const
+{
+ int low = 0;
+ int high = events.size() - 1;
+ while (low <= high) {
+ int mid = (low + high) / 2;
+ if (events[mid].time <= time)
+ low = mid + 1;
+ else
+ high = mid - 1;
+ }
+ return low;
+}