annotate data/model/TextModel.h @ 1671:82d03c9661f9 single-point

Rework isReady()/getCompletion() on models. Previously the new overhauled models were implementing getCompletion() but inheriting a version of isReady() (from the Model base) that didn't call it, referring only to isOK(). So they were reporting completion as soon as they had begun. Instead hoist getCompletion() to abstract base and call it from Model::isReady().
author Chris Cannam
date Wed, 27 Mar 2019 13:15:16 +0000
parents 353a2d15f213
children 69ab62d378bf
rev   line source
Chris@147 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@147 2
Chris@147 3 /*
Chris@147 4 Sonic Visualiser
Chris@147 5 An audio file viewer and annotation editor.
Chris@147 6 Centre for Digital Music, Queen Mary, University of London.
Chris@147 7
Chris@147 8 This program is free software; you can redistribute it and/or
Chris@147 9 modify it under the terms of the GNU General Public License as
Chris@147 10 published by the Free Software Foundation; either version 2 of the
Chris@147 11 License, or (at your option) any later version. See the file
Chris@147 12 COPYING included with this distribution for more information.
Chris@147 13 */
Chris@147 14
Chris@1581 15 #ifndef SV_TEXT_MODEL_H
Chris@1581 16 #define SV_TEXT_MODEL_H
Chris@147 17
Chris@1661 18 #include "EventCommands.h"
Chris@1661 19 #include "TabularModel.h"
Chris@1661 20 #include "Model.h"
Chris@1661 21 #include "DeferredNotifier.h"
Chris@1661 22
Chris@1661 23 #include "base/EventSeries.h"
Chris@302 24 #include "base/XmlExportable.h"
Chris@147 25 #include "base/RealTime.h"
Chris@147 26
Chris@1661 27 #include "system/System.h"
Chris@1661 28
Chris@423 29 #include <QStringList>
Chris@423 30
Chris@147 31 /**
Chris@1661 32 * A model representing casual textual annotations. A piece of text
Chris@1661 33 * has a given time and y-value in the [0,1) range (indicative of
Chris@1661 34 * height on the window).
Chris@147 35 */
Chris@1661 36 class TextModel : public Model,
Chris@1661 37 public TabularModel,
Chris@1661 38 public EventEditable
Chris@147 39 {
Chris@423 40 Q_OBJECT
Chris@423 41
Chris@147 42 public:
Chris@1661 43 TextModel(sv_samplerate_t sampleRate,
Chris@1661 44 int resolution,
Chris@1661 45 bool notifyOnAdd = true) :
Chris@1661 46 m_sampleRate(sampleRate),
Chris@1661 47 m_resolution(resolution),
Chris@1661 48 m_notifier(this,
Chris@1661 49 notifyOnAdd ?
Chris@1661 50 DeferredNotifier::NOTIFY_ALWAYS :
Chris@1661 51 DeferredNotifier::NOTIFY_DEFERRED),
Chris@1661 52 m_completion(100) {
Chris@147 53 }
Chris@345 54
Chris@1580 55 QString getTypeName() const override { return tr("Text"); }
Chris@1661 56 bool isSparse() const { return true; }
Chris@1661 57 bool isOK() const override { return true; }
Chris@1661 58
Chris@1661 59 sv_frame_t getStartFrame() const override {
Chris@1661 60 return m_events.getStartFrame();
Chris@1661 61 }
Chris@1661 62 sv_frame_t getEndFrame() const override {
Chris@1661 63 if (m_events.isEmpty()) return 0;
Chris@1661 64 sv_frame_t e = m_events.getEndFrame() + 1;
Chris@1661 65 if (e % m_resolution == 0) return e;
Chris@1661 66 else return (e / m_resolution + 1) * m_resolution;
Chris@1661 67 }
Chris@1661 68
Chris@1661 69 sv_samplerate_t getSampleRate() const override { return m_sampleRate; }
Chris@1661 70 int getResolution() const { return m_resolution; }
Chris@1661 71
Chris@1671 72 int getCompletion() const override { return m_completion; }
Chris@1661 73
Chris@1661 74 void setCompletion(int completion, bool update = true) {
Chris@1661 75
Chris@1661 76 { QMutexLocker locker(&m_mutex);
Chris@1661 77 if (m_completion == completion) return;
Chris@1661 78 m_completion = completion;
Chris@1661 79 }
Chris@1661 80
Chris@1661 81 if (update) {
Chris@1661 82 m_notifier.makeDeferredNotifications();
Chris@1661 83 }
Chris@1661 84
Chris@1661 85 emit completionChanged();
Chris@1661 86
Chris@1661 87 if (completion == 100) {
Chris@1661 88 // henceforth:
Chris@1661 89 m_notifier.switchMode(DeferredNotifier::NOTIFY_ALWAYS);
Chris@1661 90 emit modelChanged();
Chris@1661 91 }
Chris@1661 92 }
Chris@1661 93
Chris@1661 94 /**
Chris@1661 95 * Query methods.
Chris@1661 96 */
Chris@1661 97
Chris@1661 98 int getEventCount() const {
Chris@1661 99 return m_events.count();
Chris@1661 100 }
Chris@1661 101 bool isEmpty() const {
Chris@1661 102 return m_events.isEmpty();
Chris@1661 103 }
Chris@1661 104 bool containsEvent(const Event &e) const {
Chris@1661 105 return m_events.contains(e);
Chris@1661 106 }
Chris@1661 107 EventVector getAllEvents() const {
Chris@1661 108 return m_events.getAllEvents();
Chris@1661 109 }
Chris@1661 110 EventVector getEventsSpanning(sv_frame_t f, sv_frame_t duration) const {
Chris@1661 111 return m_events.getEventsSpanning(f, duration);
Chris@1661 112 }
Chris@1661 113 EventVector getEventsCovering(sv_frame_t f) const {
Chris@1661 114 return m_events.getEventsCovering(f);
Chris@1661 115 }
Chris@1661 116 EventVector getEventsWithin(sv_frame_t f, sv_frame_t duration,
Chris@1661 117 int overspill = 0) const {
Chris@1661 118 return m_events.getEventsWithin(f, duration, overspill);
Chris@1661 119 }
Chris@1661 120 EventVector getEventsStartingWithin(sv_frame_t f, sv_frame_t duration) const {
Chris@1661 121 return m_events.getEventsStartingWithin(f, duration);
Chris@1661 122 }
Chris@1661 123 EventVector getEventsStartingAt(sv_frame_t f) const {
Chris@1661 124 return m_events.getEventsStartingAt(f);
Chris@1661 125 }
Chris@1661 126 bool getNearestEventMatching(sv_frame_t startSearchAt,
Chris@1661 127 std::function<bool(Event)> predicate,
Chris@1661 128 EventSeries::Direction direction,
Chris@1661 129 Event &found) const {
Chris@1661 130 return m_events.getNearestEventMatching
Chris@1661 131 (startSearchAt, predicate, direction, found);
Chris@1661 132 }
Chris@1661 133
Chris@1661 134 /**
Chris@1661 135 * Editing methods.
Chris@1661 136 */
Chris@1661 137 void add(Event e) override {
Chris@1661 138
Chris@1661 139 { QMutexLocker locker(&m_mutex);
Chris@1661 140 m_events.add(e.withoutDuration());
Chris@1661 141 }
Chris@1661 142
Chris@1661 143 m_notifier.update(e.getFrame(), m_resolution);
Chris@1661 144 }
Chris@1661 145
Chris@1661 146 void remove(Event e) override {
Chris@1661 147 { QMutexLocker locker(&m_mutex);
Chris@1661 148 m_events.remove(e);
Chris@1661 149 }
Chris@1661 150 emit modelChangedWithin(e.getFrame(), e.getFrame() + m_resolution);
Chris@1661 151 }
Chris@424 152
Chris@424 153 /**
Chris@424 154 * TabularModel methods.
Chris@424 155 */
Chris@424 156
Chris@1661 157 int getRowCount() const override {
Chris@1661 158 return m_events.count();
Chris@1661 159 }
Chris@1661 160
Chris@1661 161 int getColumnCount() const override {
Chris@424 162 return 4;
Chris@424 163 }
Chris@424 164
Chris@1661 165 bool isColumnTimeValue(int column) const override {
Chris@1661 166 return (column < 2);
Chris@1661 167 }
Chris@1661 168
Chris@1661 169 sv_frame_t getFrameForRow(int row) const override {
Chris@1661 170 if (row < 0 || row >= m_events.count()) {
Chris@1661 171 return 0;
Chris@1661 172 }
Chris@1661 173 Event e = m_events.getEventByIndex(row);
Chris@1661 174 return e.getFrame();
Chris@1661 175 }
Chris@1661 176
Chris@1661 177 int getRowForFrame(sv_frame_t frame) const override {
Chris@1661 178 return m_events.getIndexForEvent(Event(frame));
Chris@1661 179 }
Chris@1661 180
Chris@1661 181 QString getHeading(int column) const override {
Chris@424 182 switch (column) {
Chris@424 183 case 0: return tr("Time");
Chris@424 184 case 1: return tr("Frame");
Chris@424 185 case 2: return tr("Height");
Chris@424 186 case 3: return tr("Label");
Chris@424 187 default: return tr("Unknown");
Chris@424 188 }
Chris@424 189 }
Chris@424 190
Chris@1661 191 SortType getSortType(int column) const override {
Chris@1661 192 if (column == 3) return SortAlphabetical;
Chris@1661 193 return SortNumeric;
Chris@1661 194 }
Chris@1661 195
Chris@1661 196 QVariant getData(int row, int column, int role) const override {
Chris@1661 197
Chris@1661 198 if (row < 0 || row >= m_events.count()) {
Chris@1661 199 return QVariant();
Chris@425 200 }
Chris@425 201
Chris@1661 202 Event e = m_events.getEventByIndex(row);
Chris@424 203
Chris@424 204 switch (column) {
Chris@1661 205 case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role);
Chris@1661 206 case 1: return int(e.getFrame());
Chris@1661 207 case 2: return e.getValue();
Chris@1661 208 case 3: return e.getLabel();
Chris@424 209 default: return QVariant();
Chris@424 210 }
Chris@424 211 }
Chris@424 212
Chris@1661 213 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override {
Chris@1661 214
Chris@1661 215 if (row < 0 || row >= m_events.count()) return nullptr;
Chris@1661 216 if (role != Qt::EditRole) return nullptr;
Chris@1661 217
Chris@1661 218 Event e0 = m_events.getEventByIndex(row);
Chris@1661 219 Event e1;
Chris@1661 220
Chris@1661 221 switch (column) {
Chris@1661 222 case 0: e1 = e0.withFrame(sv_frame_t(round(value.toDouble() *
Chris@1661 223 getSampleRate()))); break;
Chris@1661 224 case 1: e1 = e0.withFrame(value.toInt()); break;
Chris@1661 225 case 2: e1 = e0.withValue(float(value.toDouble())); break;
Chris@1661 226 case 3: e1 = e0.withLabel(value.toString()); break;
Chris@425 227 }
Chris@425 228
Chris@1661 229 ChangeEventsCommand *command =
Chris@1661 230 new ChangeEventsCommand(this, tr("Edit Data"));
Chris@1661 231 command->remove(e0);
Chris@1661 232 command->add(e1);
Chris@424 233 return command->finish();
Chris@424 234 }
Chris@1661 235
Chris@1661 236 /**
Chris@1661 237 * XmlExportable methods.
Chris@1661 238 */
Chris@1661 239 void toXml(QTextStream &out,
Chris@1661 240 QString indent = "",
Chris@1661 241 QString extraAttributes = "") const override {
Chris@424 242
Chris@1661 243 Model::toXml
Chris@1661 244 (out,
Chris@1661 245 indent,
Chris@1661 246 QString("type=\"sparse\" dimensions=\"2\" resolution=\"%1\" "
Chris@1661 247 "notifyOnAdd=\"%2\" dataset=\"%3\" subtype=\"text\" %4")
Chris@1661 248 .arg(m_resolution)
Chris@1661 249 .arg("true") // always true after model reaches 100% -
Chris@1661 250 // subsequent events are always notified
Chris@1661 251 .arg(getObjectExportId(&m_events))
Chris@1661 252 .arg(extraAttributes));
Chris@1661 253
Chris@1661 254 m_events.toXml(out, indent, QString("dimensions=\"2\""));
Chris@424 255 }
Chris@1661 256
Chris@1661 257 protected:
Chris@1661 258 sv_samplerate_t m_sampleRate;
Chris@1661 259 int m_resolution;
Chris@424 260
Chris@1661 261 DeferredNotifier m_notifier;
Chris@1661 262 int m_completion;
Chris@1661 263
Chris@1661 264 EventSeries m_events;
Chris@1661 265
Chris@1661 266 mutable QMutex m_mutex;
Chris@424 267
Chris@147 268 };
Chris@147 269
Chris@147 270
Chris@147 271 #endif
Chris@147 272
Chris@147 273
Chris@147 274