Chris@147: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@147: Chris@147: /* Chris@147: Sonic Visualiser Chris@147: An audio file viewer and annotation editor. Chris@147: Centre for Digital Music, Queen Mary, University of London. Chris@147: This file copyright 2006 Chris Cannam. Chris@147: Chris@147: This program is free software; you can redistribute it and/or Chris@147: modify it under the terms of the GNU General Public License as Chris@147: published by the Free Software Foundation; either version 2 of the Chris@147: License, or (at your option) any later version. See the file Chris@147: COPYING included with this distribution for more information. Chris@147: */ Chris@147: cannam@1452: #ifndef SV_SPARSE_MODEL_H cannam@1452: #define SV_SPARSE_MODEL_H Chris@147: Chris@150: #include "Model.h" Chris@420: #include "TabularModel.h" Chris@147: #include "base/Command.h" Chris@425: #include "base/RealTime.h" Chris@1218: #include "system/System.h" Chris@147: Chris@147: #include Chris@147: Chris@147: #include Chris@420: #include Chris@420: #include Chris@608: #include Chris@420: Chris@425: #include Chris@425: Chris@147: #include Chris@147: #include Chris@147: Chris@147: /** Chris@147: * Model containing sparse data (points with some properties). The Chris@147: * properties depend on the point type. Chris@147: */ Chris@147: template Chris@420: class SparseModel : public Model, Chris@420: public TabularModel Chris@147: { Chris@1458: // If we omit the Q_OBJECT macro, lupdate complains. Chris@1458: Chris@1458: // If we include it, moc fails (can't handle template classes). Chris@1458: Chris@1458: // If we omit it, lupdate still seems to emit translatable Chris@1458: // messages for the tr() strings in here. So I guess we omit it. Chris@1458: Chris@147: public: Chris@1040: SparseModel(sv_samplerate_t sampleRate, int resolution, Chris@1429: bool notifyOnAdd = true); Chris@147: virtual ~SparseModel() { } Chris@147: Chris@1580: bool isOK() const override { return true; } Chris@1608: sv_frame_t getStartFrame() const override; Chris@1608: sv_frame_t getEndFrame() const override; Chris@1580: sv_samplerate_t getSampleRate() const override { return m_sampleRate; } Chris@147: Chris@147: // Number of frames of the underlying sample rate that this model Chris@147: // is capable of resolving to. For example, if m_resolution == 10 Chris@147: // then every point in this model will be at a multiple of 10 Chris@147: // sample frames and should be considered to cover a window ending Chris@147: // 10 sample frames later. Chris@929: virtual int getResolution() const { Chris@147: return m_resolution ? m_resolution : 1; Chris@147: } Chris@929: virtual void setResolution(int resolution); Chris@1466: Chris@1466: // Extend the end of the model. If this is set to something beyond Chris@1466: // the end of the final point in the model, then getEndFrame() Chris@1466: // will return this value. Otherwise getEndFrame() will return the Chris@1466: // end of the final point. (This is used by the Tony application) Chris@1466: virtual void extendEndFrame(sv_frame_t to) { m_extendTo = to; } Chris@1466: Chris@147: typedef PointType Point; Chris@147: typedef std::multiset PointList; Chris@147: typedef typename PointList::iterator PointListIterator; Chris@606: typedef typename PointList::const_iterator PointListConstIterator; Chris@147: Chris@147: /** Chris@147: * Return whether the model is empty or not. Chris@147: */ Chris@147: virtual bool isEmpty() const; Chris@147: Chris@147: /** Chris@147: * Get the total number of points in the model. Chris@147: */ Chris@929: virtual int getPointCount() const; Chris@147: Chris@147: /** Chris@459: * Get all points. Chris@459: */ Chris@459: virtual const PointList &getPoints() const; Chris@459: Chris@459: /** Chris@147: * Get all of the points in this model between the given Chris@147: * boundaries (in frames), as well as up to two points before and Chris@147: * after the boundaries. If you need exact boundaries, check the Chris@147: * point coordinates in the returned list. Chris@147: */ Chris@1038: virtual PointList getPoints(sv_frame_t start, sv_frame_t end) const; Chris@147: Chris@147: /** Chris@147: * Get all points that cover the given frame number, taking the Chris@147: * resolution of the model into account. Chris@147: */ Chris@1038: virtual PointList getPoints(sv_frame_t frame) const; Chris@147: Chris@147: /** Chris@147: * Return all points that share the nearest frame number prior to Chris@147: * the given one at which there are any points. Chris@147: */ Chris@1038: virtual PointList getPreviousPoints(sv_frame_t frame) const; Chris@147: Chris@147: /** Chris@147: * Return all points that share the nearest frame number Chris@147: * subsequent to the given one at which there are any points. Chris@147: */ Chris@1038: virtual PointList getNextPoints(sv_frame_t frame) const; Chris@147: Chris@147: /** Chris@147: * Remove all points. Chris@147: */ Chris@147: virtual void clear(); Chris@147: Chris@147: /** Chris@147: * Add a point. Chris@147: */ Chris@147: virtual void addPoint(const PointType &point); Chris@147: Chris@147: /** Chris@147: * Remove a point. Points are not necessarily unique, so this Chris@147: * function will remove the first point that compares equal to the Chris@147: * supplied one using Point::Comparator. Other identical points Chris@147: * may remain in the model. Chris@147: */ Chris@147: virtual void deletePoint(const PointType &point); Chris@147: Chris@1113: /** Chris@1113: * Return true if the given point is found in this model, false Chris@1113: * otherwise. Chris@1113: */ Chris@1113: virtual bool containsPoint(const PointType &point); Chris@1113: Chris@1580: bool isReady(int *completion = 0) const override { Chris@297: bool ready = isOK() && (m_completion == 100); Chris@297: if (completion) *completion = m_completion; Chris@297: return ready; Chris@297: } Chris@297: Chris@333: virtual void setCompletion(int completion, bool update = true); Chris@147: virtual int getCompletion() const { return m_completion; } Chris@147: Chris@147: virtual bool hasTextLabels() const { return m_hasTextLabels; } Chris@147: Chris@1580: bool isSparse() const override { return true; } cannam@1452: Chris@1580: QString getTypeName() const override { return tr("Sparse"); } Chris@345: Chris@407: virtual QString getXmlOutputType() const { return "sparse"; } Chris@407: Chris@1608: void toXml(QTextStream &out, Chris@1608: QString indent = "", Chris@1608: QString extraAttributes = "") const override; Chris@147: Chris@1580: QString toDelimitedDataString(QString delimiter) const override { Chris@1072: return toDelimitedDataStringWithOptions Chris@1072: (delimiter, DataExportDefaults); Chris@1060: } Chris@1060: Chris@1580: QString toDelimitedDataStringWithOptions(QString delimiter, Chris@1580: DataExportOptions opts) const override { Chris@1064: return toDelimitedDataStringSubsetWithOptions Chris@1064: (delimiter, opts, Chris@1451: std::min(getStartFrame(), sv_frame_t(0)), getEndFrame()); Chris@147: } Chris@147: Chris@1580: QString toDelimitedDataStringSubset(QString delimiter, sv_frame_t f0, sv_frame_t f1) const override { Chris@1072: return toDelimitedDataStringSubsetWithOptions Chris@1072: (delimiter, DataExportDefaults, f0, f1); Chris@1060: } Chris@1060: Chris@1580: QString toDelimitedDataStringSubsetWithOptions(QString delimiter, DataExportOptions opts, sv_frame_t f0, sv_frame_t f1) const override { Chris@1064: if (opts & DataExportFillGaps) { Chris@1064: return toDelimitedDataStringSubsetFilled(delimiter, opts, f0, f1); Chris@1064: } else { Chris@1064: QString s; Chris@1064: for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) { Chris@1127: if (i->frame >= f0 && i->frame < f1) { Chris@1064: s += i->toDelimitedDataString(delimiter, opts, m_sampleRate) + "\n"; Chris@1064: } Chris@838: } Chris@1064: return s; Chris@838: } Chris@838: } Chris@838: Chris@147: /** Chris@147: * Command to add a point, with undo. Chris@147: */ Chris@147: class AddPointCommand : public Command Chris@147: { Chris@147: public: Chris@1429: AddPointCommand(SparseModel *model, Chris@1429: const PointType &point, Chris@147: QString name = "") : Chris@1429: m_model(model), m_point(point), m_name(name) { } Chris@147: Chris@1580: QString getName() const override { Chris@147: return (m_name == "" ? tr("Add Point") : m_name); Chris@147: } Chris@147: Chris@1580: void execute() override { m_model->addPoint(m_point); } Chris@1580: void unexecute() override { m_model->deletePoint(m_point); } Chris@147: Chris@1429: const PointType &getPoint() const { return m_point; } Chris@147: Chris@147: private: Chris@1429: SparseModel *m_model; Chris@1429: PointType m_point; Chris@147: QString m_name; Chris@147: }; Chris@147: Chris@147: Chris@147: /** Chris@147: * Command to remove a point, with undo. Chris@147: */ Chris@147: class DeletePointCommand : public Command Chris@147: { Chris@147: public: Chris@1429: DeletePointCommand(SparseModel *model, Chris@1429: const PointType &point) : Chris@1429: m_model(model), m_point(point) { } Chris@147: Chris@1580: QString getName() const override { return tr("Delete Point"); } Chris@147: Chris@1580: void execute() override { m_model->deletePoint(m_point); } Chris@1580: void unexecute() override { m_model->addPoint(m_point); } Chris@147: Chris@1429: const PointType &getPoint() const { return m_point; } Chris@147: Chris@147: private: Chris@1429: SparseModel *m_model; Chris@1429: PointType m_point; Chris@147: }; Chris@147: Chris@147: Chris@147: /** Chris@147: * Command to add or remove a series of points, with undo. Chris@147: * Consecutive add/remove pairs for the same point are collapsed. Chris@147: */ Chris@147: class EditCommand : public MacroCommand Chris@147: { Chris@147: public: Chris@1429: EditCommand(SparseModel *model, QString commandName); Chris@147: Chris@1429: virtual void addPoint(const PointType &point); Chris@1429: virtual void deletePoint(const PointType &point); Chris@147: Chris@1429: /** Chris@1429: * Stack an arbitrary other command in the same sequence. Chris@1429: */ Chris@1580: void addCommand(Command *command) override { addCommand(command, true); } Chris@147: Chris@1429: /** Chris@1429: * If any points have been added or deleted, return this Chris@1429: * command (so the caller can add it to the command history). Chris@1429: * Otherwise delete the command and return NULL. Chris@1429: */ Chris@1429: virtual EditCommand *finish(); Chris@147: Chris@147: protected: Chris@1429: virtual void addCommand(Command *command, bool executeFirst); Chris@147: Chris@1429: SparseModel *m_model; Chris@147: }; Chris@147: Chris@147: Chris@147: /** Chris@147: * Command to relabel a point. Chris@147: */ Chris@147: class RelabelCommand : public Command Chris@147: { Chris@147: public: Chris@1429: RelabelCommand(SparseModel *model, Chris@1429: const PointType &point, Chris@1429: QString newLabel) : Chris@1429: m_model(model), m_oldPoint(point), m_newPoint(point) { Chris@1429: m_newPoint.label = newLabel; Chris@1429: } Chris@147: Chris@1580: QString getName() const override { return tr("Re-Label Point"); } Chris@147: Chris@1580: void execute() override { Chris@1429: m_model->deletePoint(m_oldPoint); Chris@1429: m_model->addPoint(m_newPoint); Chris@1429: std::swap(m_oldPoint, m_newPoint); Chris@1429: } Chris@147: Chris@1580: void unexecute() override { execute(); } Chris@147: Chris@147: private: Chris@1429: SparseModel *m_model; Chris@1429: PointType m_oldPoint; Chris@1429: PointType m_newPoint; Chris@147: }; Chris@147: Chris@420: /** Chris@420: * TabularModel methods. Chris@420: */ Chris@420: Chris@1580: int getRowCount() const override Chris@420: { Chris@1038: return int(m_points.size()); Chris@420: } Chris@420: Chris@1580: sv_frame_t getFrameForRow(int row) const override Chris@420: { Chris@606: PointListConstIterator i = getPointListIteratorForRow(row); Chris@420: if (i == m_points.end()) return 0; Chris@420: return i->frame; Chris@420: } Chris@420: Chris@1580: int getRowForFrame(sv_frame_t frame) const override Chris@420: { Chris@420: if (m_rows.empty()) rebuildRowVector(); Chris@1038: std::vector::iterator i = Chris@420: std::lower_bound(m_rows.begin(), m_rows.end(), frame); Chris@1038: ssize_t row = std::distance(m_rows.begin(), i); Chris@432: if (i != m_rows.begin() && (i == m_rows.end() || *i != frame)) { Chris@432: --row; Chris@432: } Chris@1038: return int(row); Chris@420: } Chris@420: Chris@1580: int getColumnCount() const override { return 1; } Chris@1580: QVariant getData(int row, int column, int role) const override Chris@425: { Chris@606: PointListConstIterator i = getPointListIteratorForRow(row); Chris@1254: if (i == m_points.end()) { Chris@1254: // cerr << "no iterator for row " << row << " (have " << getRowCount() << " rows)" << endl; Chris@1254: return QVariant(); Chris@1254: } Chris@425: Chris@1254: // cerr << "returning data for row " << row << " col " << column << endl; Chris@1254: Chris@425: switch (column) { Chris@425: case 0: { Chris@425: if (role == SortRole) return int(i->frame); Chris@425: RealTime rt = RealTime::frame2RealTime(i->frame, getSampleRate()); Chris@425: if (role == Qt::EditRole) return rt.toString().c_str(); Chris@425: else return rt.toText().c_str(); Chris@425: } Chris@425: case 1: return int(i->frame); Chris@425: } Chris@425: Chris@420: return QVariant(); Chris@420: } Chris@427: Chris@1580: Command *getSetDataCommand(int row, int column, Chris@1580: const QVariant &value, int role) override Chris@425: { Chris@740: if (role != Qt::EditRole) return 0; Chris@425: PointListIterator i = getPointListIteratorForRow(row); Chris@740: if (i == m_points.end()) return 0; Chris@425: EditCommand *command = new EditCommand(this, tr("Edit Data")); Chris@425: Chris@425: Point point(*i); Chris@425: command->deletePoint(point); Chris@425: Chris@425: switch (column) { Chris@425: case 0: point.frame = lrint(value.toDouble() * getSampleRate()); break; Chris@425: case 1: point.frame = value.toInt(); break; Chris@425: } Chris@425: Chris@425: command->addPoint(point); Chris@425: return command->finish(); Chris@425: } Chris@425: Chris@1580: Command *getInsertRowCommand(int row) override Chris@427: { Chris@427: EditCommand *command = new EditCommand(this, tr("Insert Data Point")); Chris@427: Point point(0); Chris@427: PointListIterator i = getPointListIteratorForRow(row); Chris@427: if (i == m_points.end() && i != m_points.begin()) --i; Chris@427: if (i != m_points.end()) point = *i; Chris@427: command->addPoint(point); Chris@427: return command->finish(); Chris@427: } Chris@427: Chris@1580: Command *getRemoveRowCommand(int row) override Chris@427: { Chris@427: PointListIterator i = getPointListIteratorForRow(row); Chris@427: if (i == m_points.end()) return 0; Chris@978: EditCommand *command = new EditCommand(this, tr("Delete Data Point")); Chris@427: command->deletePoint(*i); Chris@427: return command->finish(); Chris@427: } Chris@427: Chris@147: protected: Chris@1040: sv_samplerate_t m_sampleRate; Chris@929: int m_resolution; Chris@1466: sv_frame_t m_extendTo; Chris@147: bool m_notifyOnAdd; Chris@1038: sv_frame_t m_sinceLastNotifyMin; Chris@1038: sv_frame_t m_sinceLastNotifyMax; Chris@147: bool m_hasTextLabels; Chris@147: Chris@147: PointList m_points; Chris@929: int m_pointCount; Chris@147: mutable QMutex m_mutex; Chris@147: int m_completion; Chris@420: Chris@1038: void getPointIterators(sv_frame_t frame, Chris@420: PointListIterator &startItr, Chris@608: PointListIterator &endItr); Chris@1038: void getPointIterators(sv_frame_t frame, Chris@608: PointListConstIterator &startItr, Chris@608: PointListConstIterator &endItr) const; Chris@420: Chris@420: // This is only used if the model is called on to act in Chris@420: // TabularModel mode Chris@1038: mutable std::vector m_rows; // map from row number to frame Chris@1254: Chris@420: void rebuildRowVector() const Chris@420: { Chris@420: m_rows.clear(); Chris@608: for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) { Chris@777: // std::cerr << "rebuildRowVector: row " << m_rows.size() << " -> " << i->frame << std::endl; Chris@420: m_rows.push_back(i->frame); Chris@420: } Chris@420: } Chris@420: Chris@608: PointListIterator getPointListIteratorForRow(int row) Chris@420: { Chris@420: if (m_rows.empty()) rebuildRowVector(); Chris@425: if (row < 0 || row + 1 > int(m_rows.size())) return m_points.end(); Chris@420: Chris@1038: sv_frame_t frame = m_rows[row]; Chris@420: int indexAtFrame = 0; Chris@420: int ri = row; Chris@420: while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; } Chris@420: int initialIndexAtFrame = indexAtFrame; Chris@420: Chris@420: PointListIterator i0, i1; Chris@420: getPointIterators(frame, i0, i1); Chris@420: PointListIterator i = i0; Chris@420: Chris@420: for (i = i0; i != i1; ++i) { Chris@778: if (i->frame < (int)frame) { continue; } Chris@420: if (indexAtFrame > 0) { --indexAtFrame; continue; } Chris@420: return i; Chris@420: } Chris@420: Chris@420: if (indexAtFrame > 0) { Chris@420: std::cerr << "WARNING: SparseModel::getPointListIteratorForRow: No iterator available for row " << row << " (frame = " << frame << ", index at frame = " << initialIndexAtFrame << ", leftover index " << indexAtFrame << ")" << std::endl; Chris@420: } Chris@420: return i; Chris@420: } Chris@608: Chris@608: PointListConstIterator getPointListIteratorForRow(int row) const Chris@608: { Chris@608: if (m_rows.empty()) rebuildRowVector(); Chris@608: if (row < 0 || row + 1 > int(m_rows.size())) return m_points.end(); Chris@608: Chris@1038: sv_frame_t frame = m_rows[row]; Chris@608: int indexAtFrame = 0; Chris@608: int ri = row; Chris@608: while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; } Chris@608: int initialIndexAtFrame = indexAtFrame; Chris@608: Chris@1254: // std::cerr << "getPointListIteratorForRow " << row << ": initialIndexAtFrame = " << initialIndexAtFrame << " for frame " << frame << std::endl; Chris@777: Chris@608: PointListConstIterator i0, i1; Chris@608: getPointIterators(frame, i0, i1); Chris@608: PointListConstIterator i = i0; Chris@608: Chris@608: for (i = i0; i != i1; ++i) { Chris@785: // std::cerr << "i->frame is " << i->frame << ", wanting " << frame << std::endl; Chris@785: Chris@778: if (i->frame < (int)frame) { continue; } Chris@608: if (indexAtFrame > 0) { --indexAtFrame; continue; } Chris@608: return i; Chris@608: } Chris@1254: /* Chris@1254: if (i == m_points.end()) { Chris@1254: std::cerr << "returning i at end" << std::endl; Chris@1254: } else { Chris@1254: std::cerr << "returning i with i->frame = " << i->frame << std::endl; Chris@1254: } Chris@1254: */ Chris@608: if (indexAtFrame > 0) { Chris@608: std::cerr << "WARNING: SparseModel::getPointListIteratorForRow: No iterator available for row " << row << " (frame = " << frame << ", index at frame = " << initialIndexAtFrame << ", leftover index " << indexAtFrame << ")" << std::endl; Chris@608: } Chris@608: return i; Chris@608: } Chris@1064: Chris@1064: QString toDelimitedDataStringSubsetFilled(QString delimiter, Chris@1064: DataExportOptions opts, Chris@1064: sv_frame_t f0, Chris@1064: sv_frame_t f1) const { Chris@1064: Chris@1064: QString s; Chris@1064: opts &= ~DataExportFillGaps; Chris@1064: Chris@1064: // find frame time of first point in range (if any) Chris@1064: sv_frame_t first = f0; Chris@1064: for (auto &p: m_points) { Chris@1064: if (p.frame >= f0) { Chris@1064: first = p.frame; Chris@1064: break; Chris@1064: } Chris@1064: } Chris@1064: Chris@1064: // project back to first frame time in range according to Chris@1064: // resolution. e.g. if f0 = 2, first = 9, resolution = 4 then Chris@1064: // we start at 5 (because 1 is too early and we need to arrive Chris@1064: // at 9 to match the first actual point). This method is Chris@1064: // stupid but easy to understand: Chris@1064: sv_frame_t f = first; Chris@1064: while (f >= f0 + m_resolution) f -= m_resolution; Chris@1064: Chris@1064: // now progress, either writing the next point (if within Chris@1064: // distance) or a default point Chris@1064: PointListConstIterator itr = m_points.begin(); Chris@1064: Chris@1064: while (f < f1) { Chris@1064: if (itr != m_points.end() && itr->frame <= f) { Chris@1064: s += itr->toDelimitedDataString(delimiter, opts, m_sampleRate); Chris@1064: ++itr; Chris@1064: } else { Chris@1064: s += Point(f).toDelimitedDataString(delimiter, opts, m_sampleRate); Chris@1064: } Chris@1064: s += "\n"; Chris@1064: f += m_resolution; Chris@1064: } Chris@1064: Chris@1064: return s; Chris@1064: } Chris@147: }; Chris@147: Chris@147: Chris@147: template Chris@1040: SparseModel::SparseModel(sv_samplerate_t sampleRate, Chris@929: int resolution, Chris@147: bool notifyOnAdd) : Chris@147: m_sampleRate(sampleRate), Chris@147: m_resolution(resolution), Chris@1466: m_extendTo(0), Chris@147: m_notifyOnAdd(notifyOnAdd), Chris@147: m_sinceLastNotifyMin(-1), Chris@147: m_sinceLastNotifyMax(-1), Chris@147: m_hasTextLabels(false), Chris@147: m_pointCount(0), Chris@147: m_completion(100) Chris@147: { Chris@147: } Chris@147: Chris@147: template Chris@1038: sv_frame_t Chris@147: SparseModel::getStartFrame() const Chris@147: { Chris@147: QMutexLocker locker(&m_mutex); Chris@1038: sv_frame_t f = 0; Chris@147: if (!m_points.empty()) { Chris@1429: f = m_points.begin()->frame; Chris@147: } Chris@147: return f; Chris@147: } Chris@147: Chris@147: template Chris@1038: sv_frame_t Chris@147: SparseModel::getEndFrame() const Chris@147: { Chris@147: QMutexLocker locker(&m_mutex); Chris@1038: sv_frame_t f = 0; Chris@147: if (!m_points.empty()) { Chris@1429: PointListConstIterator i(m_points.end()); Chris@1609: f = (--i)->frame + getResolution(); Chris@147: } Chris@1466: if (m_extendTo > f) { Chris@1466: return m_extendTo; Chris@1466: } else { Chris@1466: return f; Chris@1466: } Chris@147: } Chris@147: Chris@147: template Chris@147: bool Chris@147: SparseModel::isEmpty() const Chris@147: { Chris@147: return m_pointCount == 0; Chris@147: } Chris@147: Chris@147: template Chris@929: int Chris@147: SparseModel::getPointCount() const Chris@147: { Chris@147: return m_pointCount; Chris@147: } Chris@147: Chris@147: template Chris@459: const typename SparseModel::PointList & Chris@459: SparseModel::getPoints() const Chris@459: { Chris@459: return m_points; Chris@459: } Chris@459: Chris@459: template Chris@147: typename SparseModel::PointList Chris@1038: SparseModel::getPoints(sv_frame_t start, sv_frame_t end) const Chris@147: { Chris@147: if (start > end) return PointList(); Chris@147: QMutexLocker locker(&m_mutex); Chris@147: Chris@147: PointType startPoint(start), endPoint(end); Chris@147: Chris@608: PointListConstIterator startItr = m_points.lower_bound(startPoint); Chris@608: PointListConstIterator endItr = m_points.upper_bound(endPoint); Chris@147: Chris@147: if (startItr != m_points.begin()) --startItr; Chris@147: if (startItr != m_points.begin()) --startItr; Chris@147: if (endItr != m_points.end()) ++endItr; Chris@147: if (endItr != m_points.end()) ++endItr; Chris@147: Chris@147: PointList rv; Chris@147: Chris@608: for (PointListConstIterator i = startItr; i != endItr; ++i) { Chris@1429: rv.insert(*i); Chris@147: } Chris@147: Chris@147: return rv; Chris@147: } Chris@147: Chris@147: template Chris@147: typename SparseModel::PointList Chris@1038: SparseModel::getPoints(sv_frame_t frame) const Chris@147: { Chris@608: PointListConstIterator startItr, endItr; Chris@420: getPointIterators(frame, startItr, endItr); Chris@147: Chris@147: PointList rv; Chris@147: Chris@608: for (PointListConstIterator i = startItr; i != endItr; ++i) { Chris@1429: rv.insert(*i); Chris@147: } Chris@147: Chris@147: return rv; Chris@147: } Chris@147: Chris@147: template Chris@420: void Chris@1038: SparseModel::getPointIterators(sv_frame_t frame, Chris@420: PointListIterator &startItr, Chris@608: PointListIterator &endItr) Chris@608: { Chris@608: QMutexLocker locker(&m_mutex); Chris@608: Chris@608: if (m_resolution == 0) { Chris@608: startItr = m_points.end(); Chris@608: endItr = m_points.end(); Chris@608: return; Chris@608: } Chris@608: Chris@1038: sv_frame_t start = (frame / m_resolution) * m_resolution; Chris@1038: sv_frame_t end = start + m_resolution; Chris@608: Chris@608: PointType startPoint(start), endPoint(end); Chris@777: Chris@608: startItr = m_points.lower_bound(startPoint); Chris@608: endItr = m_points.upper_bound(endPoint); Chris@608: } Chris@608: Chris@608: template Chris@608: void Chris@1038: SparseModel::getPointIterators(sv_frame_t frame, Chris@608: PointListConstIterator &startItr, Chris@608: PointListConstIterator &endItr) const Chris@420: { Chris@420: QMutexLocker locker(&m_mutex); Chris@420: Chris@420: if (m_resolution == 0) { Chris@785: // std::cerr << "getPointIterators: resolution == 0, returning end()" << std::endl; Chris@420: startItr = m_points.end(); Chris@420: endItr = m_points.end(); Chris@420: return; Chris@420: } Chris@420: Chris@1038: sv_frame_t start = (frame / m_resolution) * m_resolution; Chris@1038: sv_frame_t end = start + m_resolution; Chris@420: Chris@420: PointType startPoint(start), endPoint(end); Chris@420: Chris@777: // std::cerr << "getPointIterators: start frame " << start << ", end frame " << end << ", m_resolution " << m_resolution << std::endl; Chris@785: Chris@420: startItr = m_points.lower_bound(startPoint); Chris@420: endItr = m_points.upper_bound(endPoint); Chris@420: } Chris@420: Chris@420: template Chris@147: typename SparseModel::PointList Chris@1038: SparseModel::getPreviousPoints(sv_frame_t originFrame) const Chris@147: { Chris@147: QMutexLocker locker(&m_mutex); Chris@147: Chris@147: PointType lookupPoint(originFrame); Chris@147: PointList rv; Chris@147: Chris@608: PointListConstIterator i = m_points.lower_bound(lookupPoint); Chris@147: if (i == m_points.begin()) return rv; Chris@147: Chris@147: --i; Chris@1038: sv_frame_t frame = i->frame; Chris@147: while (i->frame == frame) { Chris@1429: rv.insert(*i); Chris@1429: if (i == m_points.begin()) break; Chris@1429: --i; Chris@147: } Chris@147: Chris@147: return rv; Chris@147: } Chris@147: Chris@147: template Chris@147: typename SparseModel::PointList Chris@1038: SparseModel::getNextPoints(sv_frame_t originFrame) const Chris@147: { Chris@147: QMutexLocker locker(&m_mutex); Chris@147: Chris@147: PointType lookupPoint(originFrame); Chris@147: PointList rv; Chris@147: Chris@608: PointListConstIterator i = m_points.upper_bound(lookupPoint); Chris@147: if (i == m_points.end()) return rv; Chris@147: Chris@1038: sv_frame_t frame = i->frame; Chris@147: while (i != m_points.end() && i->frame == frame) { Chris@1429: rv.insert(*i); Chris@1429: ++i; Chris@147: } Chris@147: Chris@147: return rv; Chris@147: } Chris@147: Chris@147: template Chris@147: void Chris@929: SparseModel::setResolution(int resolution) Chris@147: { Chris@147: { Chris@1429: QMutexLocker locker(&m_mutex); Chris@1429: m_resolution = resolution; Chris@1212: m_rows.clear(); Chris@147: } Chris@147: emit modelChanged(); Chris@147: } Chris@147: Chris@147: template Chris@147: void Chris@147: SparseModel::clear() Chris@147: { Chris@147: { Chris@1429: QMutexLocker locker(&m_mutex); Chris@1429: m_points.clear(); Chris@147: m_pointCount = 0; Chris@1212: m_rows.clear(); Chris@147: } Chris@147: emit modelChanged(); Chris@147: } Chris@147: Chris@147: template Chris@147: void Chris@147: SparseModel::addPoint(const PointType &point) Chris@147: { Chris@1456: { Chris@1456: QMutexLocker locker(&m_mutex); Chris@1212: Chris@1456: m_points.insert(point); Chris@1456: m_pointCount++; Chris@1456: if (point.getLabel() != "") m_hasTextLabels = true; Chris@147: Chris@1456: // Even though this model is nominally sparse, there may still Chris@1456: // be too many signals going on here (especially as they'll Chris@1456: // probably be queued from one thread to another), which is Chris@1456: // why we need the notifyOnAdd as an option rather than a Chris@1456: // necessity (the alternative is to notify on setCompletion). Chris@1456: Chris@1456: if (m_notifyOnAdd) { Chris@1456: m_rows.clear(); //!!! inefficient Chris@1456: } else { Chris@1456: if (m_sinceLastNotifyMin == -1 || Chris@1456: point.frame < m_sinceLastNotifyMin) { Chris@1456: m_sinceLastNotifyMin = point.frame; Chris@1456: } Chris@1456: if (m_sinceLastNotifyMax == -1 || Chris@1456: point.frame > m_sinceLastNotifyMax) { Chris@1456: m_sinceLastNotifyMax = point.frame; Chris@1456: } Chris@1456: } Chris@1456: } Chris@147: Chris@147: if (m_notifyOnAdd) { Chris@1429: emit modelChangedWithin(point.frame, point.frame + m_resolution); Chris@147: } Chris@147: } Chris@147: Chris@147: template Chris@1113: bool Chris@1113: SparseModel::containsPoint(const PointType &point) Chris@1113: { Chris@1212: QMutexLocker locker(&m_mutex); Chris@1113: Chris@1212: PointListIterator i = m_points.lower_bound(point); Chris@1212: typename PointType::Comparator comparator; Chris@1212: while (i != m_points.end()) { Chris@1212: if (i->frame > point.frame) break; Chris@1212: if (!comparator(*i, point) && !comparator(point, *i)) { Chris@1212: return true; Chris@1212: } Chris@1212: ++i; Chris@1113: } Chris@1113: Chris@1113: return false; Chris@1113: } Chris@1113: Chris@1113: template Chris@147: void Chris@147: SparseModel::deletePoint(const PointType &point) Chris@147: { Chris@1456: { Chris@1456: QMutexLocker locker(&m_mutex); Chris@147: Chris@1456: PointListIterator i = m_points.lower_bound(point); Chris@1456: typename PointType::Comparator comparator; Chris@1456: while (i != m_points.end()) { Chris@1456: if (i->frame > point.frame) break; Chris@1456: if (!comparator(*i, point) && !comparator(point, *i)) { Chris@1456: m_points.erase(i); Chris@1456: m_pointCount--; Chris@1456: break; Chris@1429: } Chris@1456: ++i; Chris@1456: } Chris@1212: Chris@147: // std::cout << "SparseOneDimensionalModel: emit modelChanged(" Chris@1429: // << point.frame << ")" << std::endl; Chris@1456: m_rows.clear(); //!!! inefficient Chris@1456: } Chris@1456: Chris@931: emit modelChangedWithin(point.frame, point.frame + m_resolution); Chris@147: } Chris@147: Chris@147: template Chris@147: void Chris@333: SparseModel::setCompletion(int completion, bool update) Chris@147: { Chris@301: // std::cerr << "SparseModel::setCompletion(" << completion << ")" << std::endl; Chris@1456: bool emitCompletionChanged = true; Chris@1456: bool emitGeneralModelChanged = false; Chris@1456: bool emitRegionChanged = false; Chris@191: Chris@1456: { Chris@1456: QMutexLocker locker(&m_mutex); Chris@1212: Chris@1456: if (m_completion != completion) { Chris@1456: m_completion = completion; Chris@147: Chris@1456: if (completion == 100) { Chris@147: Chris@1456: if (m_notifyOnAdd) { Chris@1456: emitCompletionChanged = false; Chris@1456: } Chris@1456: Chris@1456: m_notifyOnAdd = true; // henceforth Chris@1456: m_rows.clear(); //!!! inefficient Chris@1456: emitGeneralModelChanged = true; Chris@1456: Chris@1456: } else if (!m_notifyOnAdd) { Chris@1456: Chris@1456: if (update && Chris@1456: m_sinceLastNotifyMin >= 0 && Chris@1456: m_sinceLastNotifyMax >= 0) { Chris@1456: m_rows.clear(); //!!! inefficient Chris@1456: emitRegionChanged = true; Chris@1456: } Chris@297: } Chris@1456: } Chris@1456: } Chris@297: Chris@1456: if (emitCompletionChanged) { Chris@1456: emit completionChanged(); Chris@1456: } Chris@1456: if (emitGeneralModelChanged) { Chris@1456: emit modelChanged(); Chris@1456: } Chris@1456: if (emitRegionChanged) { Chris@1456: emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax); Chris@1456: m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; Chris@147: } Chris@147: } Chris@147: Chris@147: template Chris@147: void Chris@147: SparseModel::toXml(QTextStream &out, Chris@147: QString indent, Chris@147: QString extraAttributes) const Chris@147: { Chris@777: // std::cerr << "SparseModel::toXml: extraAttributes = \"" Chris@777: // << extraAttributes.toStdString() << std::endl; Chris@318: Chris@407: QString type = getXmlOutputType(); Chris@407: Chris@147: Model::toXml Chris@1429: (out, Chris@147: indent, Chris@1429: QString("type=\"%1\" dimensions=\"%2\" resolution=\"%3\" notifyOnAdd=\"%4\" dataset=\"%5\" %6") Chris@407: .arg(type) Chris@1429: .arg(PointType(0).getDimensions()) Chris@1429: .arg(m_resolution) Chris@1429: .arg(m_notifyOnAdd ? "true" : "false") Chris@1429: .arg(getObjectExportId(&m_points)) Chris@1429: .arg(extraAttributes)); Chris@147: Chris@147: out << indent; Chris@147: out << QString("\n") Chris@1429: .arg(getObjectExportId(&m_points)) Chris@1429: .arg(PointType(0).getDimensions()); Chris@147: Chris@608: for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) { Chris@314: i->toXml(out, indent + " "); Chris@147: } Chris@147: Chris@147: out << indent; Chris@147: out << "\n"; Chris@147: } Chris@147: Chris@147: template Chris@147: SparseModel::EditCommand::EditCommand(SparseModel *model, Chris@147: QString commandName) : Chris@147: MacroCommand(commandName), Chris@147: m_model(model) Chris@147: { Chris@147: } Chris@147: Chris@147: template Chris@147: void Chris@147: SparseModel::EditCommand::addPoint(const PointType &point) Chris@147: { Chris@147: addCommand(new AddPointCommand(m_model, point), true); Chris@147: } Chris@147: Chris@147: template Chris@147: void Chris@147: SparseModel::EditCommand::deletePoint(const PointType &point) Chris@147: { Chris@147: addCommand(new DeletePointCommand(m_model, point), true); Chris@147: } Chris@147: Chris@147: template Chris@416: typename SparseModel::EditCommand * Chris@147: SparseModel::EditCommand::finish() Chris@147: { Chris@147: if (!m_commands.empty()) { Chris@387: return this; Chris@147: } else { Chris@147: delete this; Chris@389: return 0; Chris@147: } Chris@147: } Chris@147: Chris@147: template Chris@147: void Chris@147: SparseModel::EditCommand::addCommand(Command *command, Chris@1429: bool executeFirst) Chris@147: { Chris@147: if (executeFirst) command->execute(); Chris@147: Chris@147: if (!m_commands.empty()) { Chris@1429: DeletePointCommand *dpc = dynamic_cast(command); Chris@1429: if (dpc) { Chris@1429: AddPointCommand *apc = dynamic_cast Chris@1429: (m_commands[m_commands.size() - 1]); Chris@1429: typename PointType::Comparator comparator; Chris@1429: if (apc) { Chris@1429: if (!comparator(apc->getPoint(), dpc->getPoint()) && Chris@1429: !comparator(dpc->getPoint(), apc->getPoint())) { Chris@1429: deleteCommand(apc); Chris@1429: return; Chris@1429: } Chris@1429: } Chris@1429: } Chris@147: } Chris@147: Chris@147: MacroCommand::addCommand(command); Chris@147: } Chris@147: Chris@147: #endif Chris@147: Chris@147: Chris@147: