svcore  1.9
SparseModel.h
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 Chris Cannam.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #ifndef _SPARSE_MODEL_H_
00017 #define _SPARSE_MODEL_H_
00018 
00019 #include "Model.h"
00020 #include "TabularModel.h"
00021 #include "base/Command.h"
00022 #include "base/RealTime.h"
00023 
00024 #include <iostream>
00025 
00026 #include <set>
00027 #include <vector>
00028 #include <algorithm>
00029 #include <iterator>
00030 
00031 #include <cmath>
00032 
00033 #include <QMutex>
00034 #include <QTextStream>
00035 
00041 template <typename PointType>
00042 class SparseModel : public Model,
00043                     public TabularModel
00044 {
00045 public:
00046     SparseModel(int sampleRate, int resolution,
00047                 bool notifyOnAdd = true);
00048     virtual ~SparseModel() { }
00049     
00050     virtual bool isOK() const { return true; }
00051     virtual int getStartFrame() const;
00052     virtual int getEndFrame() const;
00053     virtual int getSampleRate() const { return m_sampleRate; }
00054 
00055     virtual Model *clone() const;
00056 
00057     // Number of frames of the underlying sample rate that this model
00058     // is capable of resolving to.  For example, if m_resolution == 10
00059     // then every point in this model will be at a multiple of 10
00060     // sample frames and should be considered to cover a window ending
00061     // 10 sample frames later.
00062     virtual int getResolution() const {
00063         return m_resolution ? m_resolution : 1;
00064     }
00065     virtual void setResolution(int resolution);
00066 
00067     typedef PointType Point;
00068     typedef std::multiset<PointType,
00069                           typename PointType::OrderComparator> PointList;
00070     typedef typename PointList::iterator PointListIterator;
00071     typedef typename PointList::const_iterator PointListConstIterator;
00072 
00076     virtual bool isEmpty() const;
00077 
00081     virtual int getPointCount() const;
00082 
00086     virtual const PointList &getPoints() const;
00087 
00094     virtual PointList getPoints(long start, long end) const;
00095 
00100     virtual PointList getPoints(long frame) const;
00101 
00106     virtual PointList getPreviousPoints(long frame) const;
00107 
00112     virtual PointList getNextPoints(long frame) const;
00113 
00117     virtual void clear();
00118 
00122     virtual void addPoint(const PointType &point);
00123 
00130     virtual void deletePoint(const PointType &point);
00131 
00132     virtual bool isReady(int *completion = 0) const {
00133         bool ready = isOK() && (m_completion == 100);
00134         if (completion) *completion = m_completion;
00135         return ready;
00136     }
00137 
00138     virtual void setCompletion(int completion, bool update = true);
00139     virtual int getCompletion() const { return m_completion; }
00140 
00141     virtual bool hasTextLabels() const { return m_hasTextLabels; }
00142 
00143     QString getTypeName() const { return tr("Sparse"); }
00144 
00145     virtual QString getXmlOutputType() const { return "sparse"; }
00146 
00147     virtual void toXml(QTextStream &out,
00148                        QString indent = "",
00149                        QString extraAttributes = "") const;
00150 
00151     virtual QString toDelimitedDataString(QString delimiter) const
00152     { 
00153         QString s;
00154         for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) {
00155             s += i->toDelimitedDataString(delimiter, m_sampleRate) + "\n";
00156         }
00157         return s;
00158     }
00159 
00160     virtual QString toDelimitedDataStringSubset(QString delimiter, int f0, int f1) const
00161     { 
00162         QString s;
00163         for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) {
00164             if (i->frame >= (long)f0 && i->frame < (long)f1) {
00165                 s += i->toDelimitedDataString(delimiter, m_sampleRate) + "\n";
00166             }
00167         }
00168         return s;
00169     }
00170 
00174     class AddPointCommand : public Command
00175     {
00176     public:
00177         AddPointCommand(SparseModel<PointType> *model,
00178                         const PointType &point,
00179                         QString name = "") :
00180             m_model(model), m_point(point), m_name(name) { }
00181 
00182         virtual QString getName() const {
00183             return (m_name == "" ? tr("Add Point") : m_name);
00184         }
00185 
00186         virtual void execute() { m_model->addPoint(m_point); }
00187         virtual void unexecute() { m_model->deletePoint(m_point); }
00188 
00189         const PointType &getPoint() const { return m_point; }
00190 
00191     private:
00192         SparseModel<PointType> *m_model;
00193         PointType m_point;
00194         QString m_name;
00195     };
00196 
00197 
00201     class DeletePointCommand : public Command
00202     {
00203     public:
00204         DeletePointCommand(SparseModel<PointType> *model,
00205                            const PointType &point) :
00206             m_model(model), m_point(point) { }
00207 
00208         virtual QString getName() const { return tr("Delete Point"); }
00209 
00210         virtual void execute() { m_model->deletePoint(m_point); }
00211         virtual void unexecute() { m_model->addPoint(m_point); }
00212 
00213         const PointType &getPoint() const { return m_point; }
00214 
00215     private:
00216         SparseModel<PointType> *m_model;
00217         PointType m_point;
00218     };
00219 
00220     
00225     class EditCommand : public MacroCommand
00226     {
00227     public:
00228         EditCommand(SparseModel<PointType> *model, QString commandName);
00229 
00230         virtual void addPoint(const PointType &point);
00231         virtual void deletePoint(const PointType &point);
00232 
00236         virtual void addCommand(Command *command) { addCommand(command, true); }
00237 
00243         virtual EditCommand *finish();
00244 
00245     protected:
00246         virtual void addCommand(Command *command, bool executeFirst);
00247 
00248         SparseModel<PointType> *m_model;
00249     };
00250 
00251 
00255     class RelabelCommand : public Command
00256     {
00257     public:
00258         RelabelCommand(SparseModel<PointType> *model,
00259                        const PointType &point,
00260                        QString newLabel) :
00261             m_model(model), m_oldPoint(point), m_newPoint(point) {
00262             m_newPoint.label = newLabel;
00263         }
00264 
00265         virtual QString getName() const { return tr("Re-Label Point"); }
00266 
00267         virtual void execute() { 
00268             m_model->deletePoint(m_oldPoint);
00269             m_model->addPoint(m_newPoint);
00270             std::swap(m_oldPoint, m_newPoint);
00271         }
00272 
00273         virtual void unexecute() { execute(); }
00274 
00275     private:
00276         SparseModel<PointType> *m_model;
00277         PointType m_oldPoint;
00278         PointType m_newPoint;
00279     };
00280 
00285     virtual int getRowCount() const
00286     {
00287         return m_points.size();
00288     }
00289 
00290     virtual long getFrameForRow(int row) const
00291     {
00292         PointListConstIterator i = getPointListIteratorForRow(row);
00293         if (i == m_points.end()) return 0;
00294         return i->frame;
00295     }
00296 
00297     virtual int getRowForFrame(long frame) const
00298     {
00299         if (m_rows.empty()) rebuildRowVector();
00300         std::vector<long>::iterator i =
00301             std::lower_bound(m_rows.begin(), m_rows.end(), frame);
00302 #if defined(__SUNPRO_CC) && defined(__STD_RW_ITERATOR__)
00303         int row = 0;
00304         std::distance(m_rows.begin(), i, row);
00305 #else
00306         int row = std::distance(m_rows.begin(), i);
00307 #endif
00308         if (i != m_rows.begin() && (i == m_rows.end() || *i != frame)) {
00309             --row;
00310         }
00311         return row;
00312     }
00313 
00314     virtual int getColumnCount() const { return 1; }
00315     virtual QVariant getData(int row, int column, int role) const
00316     {
00317         PointListConstIterator i = getPointListIteratorForRow(row);
00318         if (i == m_points.end()) return QVariant();
00319 
00320         switch (column) {
00321         case 0: {
00322             if (role == SortRole) return int(i->frame);
00323             RealTime rt = RealTime::frame2RealTime(i->frame, getSampleRate());
00324             if (role == Qt::EditRole) return rt.toString().c_str();
00325             else return rt.toText().c_str();
00326         }
00327         case 1: return int(i->frame);
00328         }
00329 
00330         return QVariant();
00331     }
00332 
00333     virtual Command *getSetDataCommand(int row, int column,
00334                                        const QVariant &value, int role)
00335     {
00336         if (role != Qt::EditRole) return 0;
00337         PointListIterator i = getPointListIteratorForRow(row);
00338         if (i == m_points.end()) return 0;
00339         EditCommand *command = new EditCommand(this, tr("Edit Data"));
00340 
00341         Point point(*i);
00342         command->deletePoint(point);
00343 
00344         switch (column) {
00345         case 0: point.frame = lrint(value.toDouble() * getSampleRate()); break;
00346         case 1: point.frame = value.toInt(); break; 
00347         }
00348 
00349         command->addPoint(point);
00350         return command->finish();
00351     }
00352 
00353     virtual Command *getInsertRowCommand(int row)
00354     {
00355         EditCommand *command = new EditCommand(this, tr("Insert Data Point"));
00356         Point point(0);
00357         PointListIterator i = getPointListIteratorForRow(row);
00358         if (i == m_points.end() && i != m_points.begin()) --i;
00359         if (i != m_points.end()) point = *i;
00360         command->addPoint(point);
00361         return command->finish();
00362     }
00363             
00364     virtual Command *getRemoveRowCommand(int row)
00365     {
00366         PointListIterator i = getPointListIteratorForRow(row);
00367         if (i == m_points.end()) return 0;
00368         EditCommand *command = new EditCommand(this, tr("Delete Data Point"));
00369         command->deletePoint(*i);
00370         return command->finish();
00371     }
00372             
00373 protected:
00374     int m_sampleRate;
00375     int m_resolution;
00376     bool m_notifyOnAdd;
00377     long m_sinceLastNotifyMin;
00378     long m_sinceLastNotifyMax;
00379     bool m_hasTextLabels;
00380 
00381     PointList m_points;
00382     int m_pointCount;
00383     mutable QMutex m_mutex;
00384     int m_completion;
00385 
00386     void getPointIterators(long frame,
00387                            PointListIterator &startItr,
00388                            PointListIterator &endItr);
00389     void getPointIterators(long frame,
00390                            PointListConstIterator &startItr,
00391                            PointListConstIterator &endItr) const;
00392 
00393     // This is only used if the model is called on to act in
00394     // TabularModel mode
00395     mutable std::vector<long> m_rows; // map from row number to frame
00396     void rebuildRowVector() const
00397     {
00398         m_rows.clear();
00399         for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) {
00400 //            std::cerr << "rebuildRowVector: row " << m_rows.size() << " -> " << i->frame << std::endl;
00401             m_rows.push_back(i->frame);
00402         }
00403     }
00404 
00405     PointListIterator getPointListIteratorForRow(int row)
00406     {
00407         if (m_rows.empty()) rebuildRowVector();
00408         if (row < 0 || row + 1 > int(m_rows.size())) return m_points.end();
00409 
00410         int frame = m_rows[row];
00411         int indexAtFrame = 0;
00412         int ri = row;
00413         while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; }
00414         int initialIndexAtFrame = indexAtFrame;
00415 
00416         PointListIterator i0, i1;
00417         getPointIterators(frame, i0, i1);
00418         PointListIterator i = i0;
00419 
00420         for (i = i0; i != i1; ++i) {
00421             if (i->frame < (int)frame) { continue; }
00422             if (indexAtFrame > 0) { --indexAtFrame; continue; }
00423             return i;
00424         }
00425 
00426         if (indexAtFrame > 0) {
00427             std::cerr << "WARNING: SparseModel::getPointListIteratorForRow: No iterator available for row " << row << " (frame = " << frame << ", index at frame = " << initialIndexAtFrame << ", leftover index " << indexAtFrame << ")" << std::endl;
00428         }
00429         return i;
00430     }
00431 
00432     PointListConstIterator getPointListIteratorForRow(int row) const
00433     {
00434         if (m_rows.empty()) rebuildRowVector();
00435         if (row < 0 || row + 1 > int(m_rows.size())) return m_points.end();
00436 
00437         int frame = m_rows[row];
00438         int indexAtFrame = 0;
00439         int ri = row;
00440         while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; }
00441         int initialIndexAtFrame = indexAtFrame;
00442 
00443 //        std::cerr << "getPointListIteratorForRow " << row << ": initialIndexAtFrame = " << initialIndexAtFrame << std::endl;
00444 
00445         PointListConstIterator i0, i1;
00446         getPointIterators(frame, i0, i1);
00447         PointListConstIterator i = i0;
00448 
00449         for (i = i0; i != i1; ++i) {
00450 //            std::cerr << "i->frame is " << i->frame << ", wanting " << frame << std::endl;
00451 
00452             if (i->frame < (int)frame) { continue; }
00453             if (indexAtFrame > 0) { --indexAtFrame; continue; }
00454             return i;
00455         }
00456 
00457 //        std::cerr << "returning i with i->frame = " << i->frame << std::endl;
00458 
00459         if (indexAtFrame > 0) {
00460             std::cerr << "WARNING: SparseModel::getPointListIteratorForRow: No iterator available for row " << row << " (frame = " << frame << ", index at frame = " << initialIndexAtFrame << ", leftover index " << indexAtFrame << ")" << std::endl;
00461         }
00462         return i;
00463     }
00464 };
00465 
00466 
00467 template <typename PointType>
00468 SparseModel<PointType>::SparseModel(int sampleRate,
00469                                     int resolution,
00470                                     bool notifyOnAdd) :
00471     m_sampleRate(sampleRate),
00472     m_resolution(resolution),
00473     m_notifyOnAdd(notifyOnAdd),
00474     m_sinceLastNotifyMin(-1),
00475     m_sinceLastNotifyMax(-1),
00476     m_hasTextLabels(false),
00477     m_pointCount(0),
00478     m_completion(100)
00479 {
00480 }
00481 
00482 template <typename PointType>
00483 int
00484 SparseModel<PointType>::getStartFrame() const
00485 {
00486     QMutexLocker locker(&m_mutex);
00487     int f = 0;
00488     if (!m_points.empty()) {
00489         f = m_points.begin()->frame;
00490     }
00491     return f;
00492 }
00493 
00494 template <typename PointType>
00495 int
00496 SparseModel<PointType>::getEndFrame() const
00497 {
00498     QMutexLocker locker(&m_mutex);
00499     int f = 0;
00500     if (!m_points.empty()) {
00501         PointListConstIterator i(m_points.end());
00502         f = (--i)->frame;
00503     }
00504     return f;
00505 }
00506 
00507 template <typename PointType>
00508 Model *
00509 SparseModel<PointType>::clone() const
00510 {
00511     return 0; 
00512 /*
00513     SparseModel<PointType> *model =
00514         new SparseModel<PointType>(m_sampleRate, m_resolution, m_notifyOnAdd);
00515     model->m_points = m_points;
00516     model->m_pointCount = m_pointCount;
00517     return model;
00518 */
00519 }
00520 
00521 template <typename PointType>
00522 bool
00523 SparseModel<PointType>::isEmpty() const
00524 {
00525     return m_pointCount == 0;
00526 }
00527 
00528 template <typename PointType>
00529 int
00530 SparseModel<PointType>::getPointCount() const
00531 {
00532     return m_pointCount;
00533 }
00534 
00535 template <typename PointType>
00536 const typename SparseModel<PointType>::PointList &
00537 SparseModel<PointType>::getPoints() const
00538 {
00539     return m_points;
00540 }
00541 
00542 template <typename PointType>
00543 typename SparseModel<PointType>::PointList
00544 SparseModel<PointType>::getPoints(long start, long end) const
00545 {
00546     if (start > end) return PointList();
00547     QMutexLocker locker(&m_mutex);
00548 
00549     PointType startPoint(start), endPoint(end);
00550     
00551     PointListConstIterator startItr = m_points.lower_bound(startPoint);
00552     PointListConstIterator   endItr = m_points.upper_bound(endPoint);
00553 
00554     if (startItr != m_points.begin()) --startItr;
00555     if (startItr != m_points.begin()) --startItr;
00556     if (endItr != m_points.end()) ++endItr;
00557     if (endItr != m_points.end()) ++endItr;
00558 
00559     PointList rv;
00560 
00561     for (PointListConstIterator i = startItr; i != endItr; ++i) {
00562         rv.insert(*i);
00563     }
00564 
00565     return rv;
00566 }
00567 
00568 template <typename PointType>
00569 typename SparseModel<PointType>::PointList
00570 SparseModel<PointType>::getPoints(long frame) const
00571 {
00572     PointListConstIterator startItr, endItr;
00573     getPointIterators(frame, startItr, endItr);
00574 
00575     PointList rv;
00576 
00577     for (PointListConstIterator i = startItr; i != endItr; ++i) {
00578         rv.insert(*i);
00579     }
00580 
00581     return rv;
00582 }
00583 
00584 template <typename PointType>
00585 void
00586 SparseModel<PointType>::getPointIterators(long frame,
00587                                           PointListIterator &startItr,
00588                                           PointListIterator &endItr)
00589 {
00590     QMutexLocker locker(&m_mutex);
00591 
00592     if (m_resolution == 0) {
00593         startItr = m_points.end();
00594         endItr = m_points.end();
00595         return;
00596     }
00597 
00598     long start = (frame / m_resolution) * m_resolution;
00599     long end = start + m_resolution;
00600 
00601     PointType startPoint(start), endPoint(end);
00602 
00603     startItr = m_points.lower_bound(startPoint);
00604       endItr = m_points.upper_bound(endPoint);
00605 }
00606 
00607 template <typename PointType>
00608 void
00609 SparseModel<PointType>::getPointIterators(long frame,
00610                                           PointListConstIterator &startItr,
00611                                           PointListConstIterator &endItr) const
00612 {
00613     QMutexLocker locker(&m_mutex);
00614 
00615     if (m_resolution == 0) {
00616 //        std::cerr << "getPointIterators: resolution == 0, returning end()" << std::endl;
00617         startItr = m_points.end();
00618         endItr = m_points.end();
00619         return;
00620     }
00621 
00622     long start = (frame / m_resolution) * m_resolution;
00623     long end = start + m_resolution;
00624 
00625     PointType startPoint(start), endPoint(end);
00626     
00627 //    std::cerr << "getPointIterators: start frame " << start << ", end frame " << end << ", m_resolution " << m_resolution << std::endl;
00628 
00629     startItr = m_points.lower_bound(startPoint);
00630       endItr = m_points.upper_bound(endPoint);
00631 }
00632 
00633 template <typename PointType>
00634 typename SparseModel<PointType>::PointList
00635 SparseModel<PointType>::getPreviousPoints(long originFrame) const
00636 {
00637     QMutexLocker locker(&m_mutex);
00638 
00639     PointType lookupPoint(originFrame);
00640     PointList rv;
00641 
00642     PointListConstIterator i = m_points.lower_bound(lookupPoint);
00643     if (i == m_points.begin()) return rv;
00644 
00645     --i;
00646     long frame = i->frame;
00647     while (i->frame == frame) {
00648         rv.insert(*i);
00649         if (i == m_points.begin()) break;
00650         --i;
00651     }
00652 
00653     return rv;
00654 }
00655  
00656 template <typename PointType>
00657 typename SparseModel<PointType>::PointList
00658 SparseModel<PointType>::getNextPoints(long originFrame) const
00659 {
00660     QMutexLocker locker(&m_mutex);
00661 
00662     PointType lookupPoint(originFrame);
00663     PointList rv;
00664 
00665     PointListConstIterator i = m_points.upper_bound(lookupPoint);
00666     if (i == m_points.end()) return rv;
00667 
00668     long frame = i->frame;
00669     while (i != m_points.end() && i->frame == frame) {
00670         rv.insert(*i);
00671         ++i;
00672     }
00673 
00674     return rv;
00675 }
00676 
00677 template <typename PointType>
00678 void
00679 SparseModel<PointType>::setResolution(int resolution)
00680 {
00681     {
00682         QMutexLocker locker(&m_mutex);
00683         m_resolution = resolution;
00684     }
00685     m_rows.clear();
00686     emit modelChanged();
00687 }
00688 
00689 template <typename PointType>
00690 void
00691 SparseModel<PointType>::clear()
00692 {
00693     {
00694         QMutexLocker locker(&m_mutex);
00695         m_points.clear();
00696         m_pointCount = 0;
00697     }
00698     m_rows.clear();
00699     emit modelChanged();
00700 }
00701 
00702 template <typename PointType>
00703 void
00704 SparseModel<PointType>::addPoint(const PointType &point)
00705 {
00706     {
00707         QMutexLocker locker(&m_mutex);
00708         m_points.insert(point);
00709         m_pointCount++;
00710         if (point.getLabel() != "") m_hasTextLabels = true;
00711     }
00712 
00713     // Even though this model is nominally sparse, there may still be
00714     // too many signals going on here (especially as they'll probably
00715     // be queued from one thread to another), which is why we need the
00716     // notifyOnAdd as an option rather than a necessity (the
00717     // alternative is to notify on setCompletion).
00718 
00719     if (m_notifyOnAdd) {
00720         m_rows.clear(); 
00721         emit modelChangedWithin(point.frame, point.frame + m_resolution);
00722     } else {
00723         if (m_sinceLastNotifyMin == -1 ||
00724             point.frame < m_sinceLastNotifyMin) {
00725             m_sinceLastNotifyMin = point.frame;
00726         }
00727         if (m_sinceLastNotifyMax == -1 ||
00728             point.frame > m_sinceLastNotifyMax) {
00729             m_sinceLastNotifyMax = point.frame;
00730         }
00731     }
00732 }
00733 
00734 template <typename PointType>
00735 void
00736 SparseModel<PointType>::deletePoint(const PointType &point)
00737 {
00738     {
00739         QMutexLocker locker(&m_mutex);
00740 
00741         PointListIterator i = m_points.lower_bound(point);
00742         typename PointType::Comparator comparator;
00743         while (i != m_points.end()) {
00744             if (i->frame > point.frame) break;
00745             if (!comparator(*i, point) && !comparator(point, *i)) {
00746                 m_points.erase(i);
00747                 m_pointCount--;
00748                 break;
00749             }
00750             ++i;
00751         }
00752     }
00753 //    std::cout << "SparseOneDimensionalModel: emit modelChanged("
00754 //            << point.frame << ")" << std::endl;
00755     m_rows.clear(); 
00756     emit modelChangedWithin(point.frame, point.frame + m_resolution);
00757 }
00758 
00759 template <typename PointType>
00760 void
00761 SparseModel<PointType>::setCompletion(int completion, bool update)
00762 {
00763 //    std::cerr << "SparseModel::setCompletion(" << completion << ")" << std::endl;
00764 
00765     if (m_completion != completion) {
00766         m_completion = completion;
00767 
00768         if (completion == 100) {
00769 
00770             if (!m_notifyOnAdd) {
00771                 emit completionChanged();
00772             }
00773 
00774             m_notifyOnAdd = true; // henceforth
00775             m_rows.clear(); 
00776             emit modelChanged();
00777 
00778         } else if (!m_notifyOnAdd) {
00779 
00780             if (update &&
00781                 m_sinceLastNotifyMin >= 0 &&
00782                 m_sinceLastNotifyMax >= 0) {
00783                 m_rows.clear(); 
00784                 emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax);
00785                 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
00786             } else {
00787                 emit completionChanged();
00788             }
00789         } else {
00790             emit completionChanged();
00791         }           
00792     }
00793 }
00794 
00795 template <typename PointType>
00796 void
00797 SparseModel<PointType>::toXml(QTextStream &out,
00798                               QString indent,
00799                               QString extraAttributes) const
00800 {
00801 //    std::cerr << "SparseModel::toXml: extraAttributes = \"" 
00802 //              << extraAttributes.toStdString() << std::endl;
00803 
00804     QString type = getXmlOutputType();
00805 
00806     Model::toXml
00807         (out,
00808          indent,
00809          QString("type=\"%1\" dimensions=\"%2\" resolution=\"%3\" notifyOnAdd=\"%4\" dataset=\"%5\" %6")
00810          .arg(type)
00811          .arg(PointType(0).getDimensions())
00812          .arg(m_resolution)
00813          .arg(m_notifyOnAdd ? "true" : "false")
00814          .arg(getObjectExportId(&m_points))
00815          .arg(extraAttributes));
00816 
00817     out << indent;
00818     out << QString("<dataset id=\"%1\" dimensions=\"%2\">\n")
00819         .arg(getObjectExportId(&m_points))
00820         .arg(PointType(0).getDimensions());
00821 
00822     for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) {
00823         i->toXml(out, indent + "  ");
00824     }
00825 
00826     out << indent;
00827     out << "</dataset>\n";
00828 }
00829 
00830 template <typename PointType>
00831 SparseModel<PointType>::EditCommand::EditCommand(SparseModel *model,
00832                                                  QString commandName) :
00833     MacroCommand(commandName),
00834     m_model(model)
00835 {
00836 }
00837 
00838 template <typename PointType>
00839 void
00840 SparseModel<PointType>::EditCommand::addPoint(const PointType &point)
00841 {
00842     addCommand(new AddPointCommand(m_model, point), true);
00843 }
00844 
00845 template <typename PointType>
00846 void
00847 SparseModel<PointType>::EditCommand::deletePoint(const PointType &point)
00848 {
00849     addCommand(new DeletePointCommand(m_model, point), true);
00850 }
00851 
00852 template <typename PointType>
00853 typename SparseModel<PointType>::EditCommand *
00854 SparseModel<PointType>::EditCommand::finish()
00855 {
00856     if (!m_commands.empty()) {
00857         return this;
00858     } else {
00859         delete this;
00860         return 0;
00861     }
00862 }
00863 
00864 template <typename PointType>
00865 void
00866 SparseModel<PointType>::EditCommand::addCommand(Command *command,
00867                                                 bool executeFirst)
00868 {
00869     if (executeFirst) command->execute();
00870 
00871     if (!m_commands.empty()) {
00872         DeletePointCommand *dpc = dynamic_cast<DeletePointCommand *>(command);
00873         if (dpc) {
00874             AddPointCommand *apc = dynamic_cast<AddPointCommand *>
00875                 (m_commands[m_commands.size() - 1]);
00876             typename PointType::Comparator comparator;
00877             if (apc) {
00878                 if (!comparator(apc->getPoint(), dpc->getPoint()) &&
00879                     !comparator(dpc->getPoint(), apc->getPoint())) {
00880                     deleteCommand(apc);
00881                     return;
00882                 }
00883             }
00884         }
00885     }
00886 
00887     MacroCommand::addCommand(command);
00888 }
00889 
00890 
00891 #endif
00892 
00893 
00894