comparison data/model/SparseModel.h @ 1527:710e6250a401 zoom

Merge from default branch
author Chris Cannam
date Mon, 17 Sep 2018 13:51:14 +0100
parents f68911282993
children c01cbe41aeb5
comparison
equal deleted inserted replaced
1324:d4a28d1479a8 1527:710e6250a401
11 published by the Free Software Foundation; either version 2 of the 11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file 12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information. 13 COPYING included with this distribution for more information.
14 */ 14 */
15 15
16 #ifndef _SPARSE_MODEL_H_ 16 #ifndef SV_SPARSE_MODEL_H
17 #define _SPARSE_MODEL_H_ 17 #define SV_SPARSE_MODEL_H
18 18
19 #include "Model.h" 19 #include "Model.h"
20 #include "TabularModel.h" 20 #include "TabularModel.h"
21 #include "base/Command.h" 21 #include "base/Command.h"
22 #include "base/RealTime.h" 22 #include "base/RealTime.h"
41 41
42 template <typename PointType> 42 template <typename PointType>
43 class SparseModel : public Model, 43 class SparseModel : public Model,
44 public TabularModel 44 public TabularModel
45 { 45 {
46 // If we omit the Q_OBJECT macro, lupdate complains.
47
48 // If we include it, moc fails (can't handle template classes).
49
50 // If we omit it, lupdate still seems to emit translatable
51 // messages for the tr() strings in here. So I guess we omit it.
52
46 public: 53 public:
47 SparseModel(sv_samplerate_t sampleRate, int resolution, 54 SparseModel(sv_samplerate_t sampleRate, int resolution,
48 bool notifyOnAdd = true); 55 bool notifyOnAdd = true);
49 virtual ~SparseModel() { } 56 virtual ~SparseModel() { }
50 57
51 virtual bool isOK() const { return true; } 58 virtual bool isOK() const { return true; }
52 virtual sv_frame_t getStartFrame() const; 59 virtual sv_frame_t getStartFrame() const;
53 virtual sv_frame_t getEndFrame() const; 60 virtual sv_frame_t getEndFrame() const;
64 virtual void setResolution(int resolution); 71 virtual void setResolution(int resolution);
65 72
66 // Extend the end of the model. If this is set to something beyond 73 // Extend the end of the model. If this is set to something beyond
67 // the end of the final point in the model, then getEndFrame() 74 // the end of the final point in the model, then getEndFrame()
68 // will return this value. Otherwise getEndFrame() will return the 75 // will return this value. Otherwise getEndFrame() will return the
69 // end of the final point. 76 // end of the final point. (This is used by the Tony application)
70 virtual void extendEndFrame(sv_frame_t to) { m_extendTo = to; } 77 virtual void extendEndFrame(sv_frame_t to) { m_extendTo = to; }
71 78
72 typedef PointType Point; 79 typedef PointType Point;
73 typedef std::multiset<PointType, 80 typedef std::multiset<PointType,
74 typename PointType::OrderComparator> PointList; 81 typename PointType::OrderComparator> PointList;
75 typedef typename PointList::iterator PointListIterator; 82 typedef typename PointList::iterator PointListIterator;
76 typedef typename PointList::const_iterator PointListConstIterator; 83 typedef typename PointList::const_iterator PointListConstIterator;
77 84
78 /** 85 /**
79 * Return whether the model is empty or not. 86 * Return whether the model is empty or not.
149 virtual void setCompletion(int completion, bool update = true); 156 virtual void setCompletion(int completion, bool update = true);
150 virtual int getCompletion() const { return m_completion; } 157 virtual int getCompletion() const { return m_completion; }
151 158
152 virtual bool hasTextLabels() const { return m_hasTextLabels; } 159 virtual bool hasTextLabels() const { return m_hasTextLabels; }
153 160
161 virtual bool isSparse() const { return true; }
162
154 QString getTypeName() const { return tr("Sparse"); } 163 QString getTypeName() const { return tr("Sparse"); }
155 164
156 virtual QString getXmlOutputType() const { return "sparse"; } 165 virtual QString getXmlOutputType() const { return "sparse"; }
157 166
158 virtual void toXml(QTextStream &out, 167 virtual void toXml(QTextStream &out,
166 175
167 virtual QString toDelimitedDataStringWithOptions(QString delimiter, 176 virtual QString toDelimitedDataStringWithOptions(QString delimiter,
168 DataExportOptions opts) const { 177 DataExportOptions opts) const {
169 return toDelimitedDataStringSubsetWithOptions 178 return toDelimitedDataStringSubsetWithOptions
170 (delimiter, opts, 179 (delimiter, opts,
171 std::min(getStartFrame(), sv_frame_t(0)), getEndFrame() + 1); 180 std::min(getStartFrame(), sv_frame_t(0)), getEndFrame());
172 } 181 }
173 182
174 virtual QString toDelimitedDataStringSubset(QString delimiter, sv_frame_t f0, sv_frame_t f1) const { 183 virtual QString toDelimitedDataStringSubset(QString delimiter, sv_frame_t f0, sv_frame_t f1) const {
175 return toDelimitedDataStringSubsetWithOptions 184 return toDelimitedDataStringSubsetWithOptions
176 (delimiter, DataExportDefaults, f0, f1); 185 (delimiter, DataExportDefaults, f0, f1);
194 * Command to add a point, with undo. 203 * Command to add a point, with undo.
195 */ 204 */
196 class AddPointCommand : public Command 205 class AddPointCommand : public Command
197 { 206 {
198 public: 207 public:
199 AddPointCommand(SparseModel<PointType> *model, 208 AddPointCommand(SparseModel<PointType> *model,
200 const PointType &point, 209 const PointType &point,
201 QString name = "") : 210 QString name = "") :
202 m_model(model), m_point(point), m_name(name) { } 211 m_model(model), m_point(point), m_name(name) { }
203 212
204 virtual QString getName() const { 213 virtual QString getName() const {
205 return (m_name == "" ? tr("Add Point") : m_name); 214 return (m_name == "" ? tr("Add Point") : m_name);
206 } 215 }
207 216
208 virtual void execute() { m_model->addPoint(m_point); } 217 virtual void execute() { m_model->addPoint(m_point); }
209 virtual void unexecute() { m_model->deletePoint(m_point); } 218 virtual void unexecute() { m_model->deletePoint(m_point); }
210 219
211 const PointType &getPoint() const { return m_point; } 220 const PointType &getPoint() const { return m_point; }
212 221
213 private: 222 private:
214 SparseModel<PointType> *m_model; 223 SparseModel<PointType> *m_model;
215 PointType m_point; 224 PointType m_point;
216 QString m_name; 225 QString m_name;
217 }; 226 };
218 227
219 228
220 /** 229 /**
221 * Command to remove a point, with undo. 230 * Command to remove a point, with undo.
222 */ 231 */
223 class DeletePointCommand : public Command 232 class DeletePointCommand : public Command
224 { 233 {
225 public: 234 public:
226 DeletePointCommand(SparseModel<PointType> *model, 235 DeletePointCommand(SparseModel<PointType> *model,
227 const PointType &point) : 236 const PointType &point) :
228 m_model(model), m_point(point) { } 237 m_model(model), m_point(point) { }
229 238
230 virtual QString getName() const { return tr("Delete Point"); } 239 virtual QString getName() const { return tr("Delete Point"); }
231 240
232 virtual void execute() { m_model->deletePoint(m_point); } 241 virtual void execute() { m_model->deletePoint(m_point); }
233 virtual void unexecute() { m_model->addPoint(m_point); } 242 virtual void unexecute() { m_model->addPoint(m_point); }
234 243
235 const PointType &getPoint() const { return m_point; } 244 const PointType &getPoint() const { return m_point; }
236 245
237 private: 246 private:
238 SparseModel<PointType> *m_model; 247 SparseModel<PointType> *m_model;
239 PointType m_point; 248 PointType m_point;
240 }; 249 };
241 250
242 251
243 /** 252 /**
244 * Command to add or remove a series of points, with undo. 253 * Command to add or remove a series of points, with undo.
245 * Consecutive add/remove pairs for the same point are collapsed. 254 * Consecutive add/remove pairs for the same point are collapsed.
246 */ 255 */
247 class EditCommand : public MacroCommand 256 class EditCommand : public MacroCommand
248 { 257 {
249 public: 258 public:
250 EditCommand(SparseModel<PointType> *model, QString commandName); 259 EditCommand(SparseModel<PointType> *model, QString commandName);
251 260
252 virtual void addPoint(const PointType &point); 261 virtual void addPoint(const PointType &point);
253 virtual void deletePoint(const PointType &point); 262 virtual void deletePoint(const PointType &point);
254 263
255 /** 264 /**
256 * Stack an arbitrary other command in the same sequence. 265 * Stack an arbitrary other command in the same sequence.
257 */ 266 */
258 virtual void addCommand(Command *command) { addCommand(command, true); } 267 virtual void addCommand(Command *command) { addCommand(command, true); }
259 268
260 /** 269 /**
261 * If any points have been added or deleted, return this 270 * If any points have been added or deleted, return this
262 * command (so the caller can add it to the command history). 271 * command (so the caller can add it to the command history).
263 * Otherwise delete the command and return NULL. 272 * Otherwise delete the command and return NULL.
264 */ 273 */
265 virtual EditCommand *finish(); 274 virtual EditCommand *finish();
266 275
267 protected: 276 protected:
268 virtual void addCommand(Command *command, bool executeFirst); 277 virtual void addCommand(Command *command, bool executeFirst);
269 278
270 SparseModel<PointType> *m_model; 279 SparseModel<PointType> *m_model;
271 }; 280 };
272 281
273 282
274 /** 283 /**
275 * Command to relabel a point. 284 * Command to relabel a point.
276 */ 285 */
277 class RelabelCommand : public Command 286 class RelabelCommand : public Command
278 { 287 {
279 public: 288 public:
280 RelabelCommand(SparseModel<PointType> *model, 289 RelabelCommand(SparseModel<PointType> *model,
281 const PointType &point, 290 const PointType &point,
282 QString newLabel) : 291 QString newLabel) :
283 m_model(model), m_oldPoint(point), m_newPoint(point) { 292 m_model(model), m_oldPoint(point), m_newPoint(point) {
284 m_newPoint.label = newLabel; 293 m_newPoint.label = newLabel;
285 } 294 }
286 295
287 virtual QString getName() const { return tr("Re-Label Point"); } 296 virtual QString getName() const { return tr("Re-Label Point"); }
288 297
289 virtual void execute() { 298 virtual void execute() {
290 m_model->deletePoint(m_oldPoint); 299 m_model->deletePoint(m_oldPoint);
291 m_model->addPoint(m_newPoint); 300 m_model->addPoint(m_newPoint);
292 std::swap(m_oldPoint, m_newPoint); 301 std::swap(m_oldPoint, m_newPoint);
293 } 302 }
294 303
295 virtual void unexecute() { execute(); } 304 virtual void unexecute() { execute(); }
296 305
297 private: 306 private:
298 SparseModel<PointType> *m_model; 307 SparseModel<PointType> *m_model;
299 PointType m_oldPoint; 308 PointType m_oldPoint;
300 PointType m_newPoint; 309 PointType m_newPoint;
301 }; 310 };
302 311
303 /** 312 /**
304 * TabularModel methods. 313 * TabularModel methods.
305 */ 314 */
556 SparseModel<PointType>::getStartFrame() const 565 SparseModel<PointType>::getStartFrame() const
557 { 566 {
558 QMutexLocker locker(&m_mutex); 567 QMutexLocker locker(&m_mutex);
559 sv_frame_t f = 0; 568 sv_frame_t f = 0;
560 if (!m_points.empty()) { 569 if (!m_points.empty()) {
561 f = m_points.begin()->frame; 570 f = m_points.begin()->frame;
562 } 571 }
563 return f; 572 return f;
564 } 573 }
565 574
566 template <typename PointType> 575 template <typename PointType>
568 SparseModel<PointType>::getEndFrame() const 577 SparseModel<PointType>::getEndFrame() const
569 { 578 {
570 QMutexLocker locker(&m_mutex); 579 QMutexLocker locker(&m_mutex);
571 sv_frame_t f = 0; 580 sv_frame_t f = 0;
572 if (!m_points.empty()) { 581 if (!m_points.empty()) {
573 PointListConstIterator i(m_points.end()); 582 PointListConstIterator i(m_points.end());
574 f = (--i)->frame; 583 f = (--i)->frame + 1;
575 } 584 }
576 if (m_extendTo > f) return m_extendTo; 585 if (m_extendTo > f) {
577 else return f; 586 return m_extendTo;
587 } else {
588 return f;
589 }
578 } 590 }
579 591
580 template <typename PointType> 592 template <typename PointType>
581 bool 593 bool
582 SparseModel<PointType>::isEmpty() const 594 SparseModel<PointType>::isEmpty() const
616 if (endItr != m_points.end()) ++endItr; 628 if (endItr != m_points.end()) ++endItr;
617 629
618 PointList rv; 630 PointList rv;
619 631
620 for (PointListConstIterator i = startItr; i != endItr; ++i) { 632 for (PointListConstIterator i = startItr; i != endItr; ++i) {
621 rv.insert(*i); 633 rv.insert(*i);
622 } 634 }
623 635
624 return rv; 636 return rv;
625 } 637 }
626 638
632 getPointIterators(frame, startItr, endItr); 644 getPointIterators(frame, startItr, endItr);
633 645
634 PointList rv; 646 PointList rv;
635 647
636 for (PointListConstIterator i = startItr; i != endItr; ++i) { 648 for (PointListConstIterator i = startItr; i != endItr; ++i) {
637 rv.insert(*i); 649 rv.insert(*i);
638 } 650 }
639 651
640 return rv; 652 return rv;
641 } 653 }
642 654
702 if (i == m_points.begin()) return rv; 714 if (i == m_points.begin()) return rv;
703 715
704 --i; 716 --i;
705 sv_frame_t frame = i->frame; 717 sv_frame_t frame = i->frame;
706 while (i->frame == frame) { 718 while (i->frame == frame) {
707 rv.insert(*i); 719 rv.insert(*i);
708 if (i == m_points.begin()) break; 720 if (i == m_points.begin()) break;
709 --i; 721 --i;
710 } 722 }
711 723
712 return rv; 724 return rv;
713 } 725 }
714 726
724 PointListConstIterator i = m_points.upper_bound(lookupPoint); 736 PointListConstIterator i = m_points.upper_bound(lookupPoint);
725 if (i == m_points.end()) return rv; 737 if (i == m_points.end()) return rv;
726 738
727 sv_frame_t frame = i->frame; 739 sv_frame_t frame = i->frame;
728 while (i != m_points.end() && i->frame == frame) { 740 while (i != m_points.end() && i->frame == frame) {
729 rv.insert(*i); 741 rv.insert(*i);
730 ++i; 742 ++i;
731 } 743 }
732 744
733 return rv; 745 return rv;
734 } 746 }
735 747
736 template <typename PointType> 748 template <typename PointType>
737 void 749 void
738 SparseModel<PointType>::setResolution(int resolution) 750 SparseModel<PointType>::setResolution(int resolution)
739 { 751 {
740 { 752 {
741 QMutexLocker locker(&m_mutex); 753 QMutexLocker locker(&m_mutex);
742 m_resolution = resolution; 754 m_resolution = resolution;
743 m_rows.clear(); 755 m_rows.clear();
744 } 756 }
745 emit modelChanged(); 757 emit modelChanged();
746 } 758 }
747 759
748 template <typename PointType> 760 template <typename PointType>
749 void 761 void
750 SparseModel<PointType>::clear() 762 SparseModel<PointType>::clear()
751 { 763 {
752 { 764 {
753 QMutexLocker locker(&m_mutex); 765 QMutexLocker locker(&m_mutex);
754 m_points.clear(); 766 m_points.clear();
755 m_pointCount = 0; 767 m_pointCount = 0;
756 m_rows.clear(); 768 m_rows.clear();
757 } 769 }
758 emit modelChanged(); 770 emit modelChanged();
759 } 771 }
760 772
761 template <typename PointType> 773 template <typename PointType>
762 void 774 void
763 SparseModel<PointType>::addPoint(const PointType &point) 775 SparseModel<PointType>::addPoint(const PointType &point)
764 { 776 {
765 QMutexLocker locker(&m_mutex); 777 {
766 778 QMutexLocker locker(&m_mutex);
767 m_points.insert(point); 779
768 m_pointCount++; 780 m_points.insert(point);
769 if (point.getLabel() != "") m_hasTextLabels = true; 781 m_pointCount++;
770 782 if (point.getLabel() != "") m_hasTextLabels = true;
771 // Even though this model is nominally sparse, there may still be 783
772 // too many signals going on here (especially as they'll probably 784 // Even though this model is nominally sparse, there may still
773 // be queued from one thread to another), which is why we need the 785 // be too many signals going on here (especially as they'll
774 // notifyOnAdd as an option rather than a necessity (the 786 // probably be queued from one thread to another), which is
775 // alternative is to notify on setCompletion). 787 // why we need the notifyOnAdd as an option rather than a
788 // necessity (the alternative is to notify on setCompletion).
789
790 if (m_notifyOnAdd) {
791 m_rows.clear(); //!!! inefficient
792 } else {
793 if (m_sinceLastNotifyMin == -1 ||
794 point.frame < m_sinceLastNotifyMin) {
795 m_sinceLastNotifyMin = point.frame;
796 }
797 if (m_sinceLastNotifyMax == -1 ||
798 point.frame > m_sinceLastNotifyMax) {
799 m_sinceLastNotifyMax = point.frame;
800 }
801 }
802 }
776 803
777 if (m_notifyOnAdd) { 804 if (m_notifyOnAdd) {
778 m_rows.clear(); //!!! inefficient 805 emit modelChangedWithin(point.frame, point.frame + m_resolution);
779 emit modelChangedWithin(point.frame, point.frame + m_resolution);
780 } else {
781 if (m_sinceLastNotifyMin == -1 ||
782 point.frame < m_sinceLastNotifyMin) {
783 m_sinceLastNotifyMin = point.frame;
784 }
785 if (m_sinceLastNotifyMax == -1 ||
786 point.frame > m_sinceLastNotifyMax) {
787 m_sinceLastNotifyMax = point.frame;
788 }
789 } 806 }
790 } 807 }
791 808
792 template <typename PointType> 809 template <typename PointType>
793 bool 810 bool
810 827
811 template <typename PointType> 828 template <typename PointType>
812 void 829 void
813 SparseModel<PointType>::deletePoint(const PointType &point) 830 SparseModel<PointType>::deletePoint(const PointType &point)
814 { 831 {
815 QMutexLocker locker(&m_mutex); 832 {
816 833 QMutexLocker locker(&m_mutex);
817 PointListIterator i = m_points.lower_bound(point); 834
818 typename PointType::Comparator comparator; 835 PointListIterator i = m_points.lower_bound(point);
819 while (i != m_points.end()) { 836 typename PointType::Comparator comparator;
820 if (i->frame > point.frame) break; 837 while (i != m_points.end()) {
821 if (!comparator(*i, point) && !comparator(point, *i)) { 838 if (i->frame > point.frame) break;
822 m_points.erase(i); 839 if (!comparator(*i, point) && !comparator(point, *i)) {
823 m_pointCount--; 840 m_points.erase(i);
824 break; 841 m_pointCount--;
825 } 842 break;
826 ++i; 843 }
827 } 844 ++i;
845 }
828 846
829 // std::cout << "SparseOneDimensionalModel: emit modelChanged(" 847 // std::cout << "SparseOneDimensionalModel: emit modelChanged("
830 // << point.frame << ")" << std::endl; 848 // << point.frame << ")" << std::endl;
831 m_rows.clear(); //!!! inefficient 849 m_rows.clear(); //!!! inefficient
850 }
851
832 emit modelChangedWithin(point.frame, point.frame + m_resolution); 852 emit modelChangedWithin(point.frame, point.frame + m_resolution);
833 } 853 }
834 854
835 template <typename PointType> 855 template <typename PointType>
836 void 856 void
837 SparseModel<PointType>::setCompletion(int completion, bool update) 857 SparseModel<PointType>::setCompletion(int completion, bool update)
838 { 858 {
839 // std::cerr << "SparseModel::setCompletion(" << completion << ")" << std::endl; 859 // std::cerr << "SparseModel::setCompletion(" << completion << ")" << std::endl;
840 860 bool emitCompletionChanged = true;
841 QMutexLocker locker(&m_mutex); 861 bool emitGeneralModelChanged = false;
842 862 bool emitRegionChanged = false;
843 if (m_completion != completion) { 863
844 m_completion = completion; 864 {
845 865 QMutexLocker locker(&m_mutex);
846 if (completion == 100) { 866
847 867 if (m_completion != completion) {
848 if (!m_notifyOnAdd) { 868 m_completion = completion;
849 emit completionChanged(); 869
870 if (completion == 100) {
871
872 if (m_notifyOnAdd) {
873 emitCompletionChanged = false;
874 }
875
876 m_notifyOnAdd = true; // henceforth
877 m_rows.clear(); //!!! inefficient
878 emitGeneralModelChanged = true;
879
880 } else if (!m_notifyOnAdd) {
881
882 if (update &&
883 m_sinceLastNotifyMin >= 0 &&
884 m_sinceLastNotifyMax >= 0) {
885 m_rows.clear(); //!!! inefficient
886 emitRegionChanged = true;
887 }
850 } 888 }
851 889 }
852 m_notifyOnAdd = true; // henceforth 890 }
853 m_rows.clear(); //!!! inefficient 891
854 emit modelChanged(); 892 if (emitCompletionChanged) {
855 893 emit completionChanged();
856 } else if (!m_notifyOnAdd) { 894 }
857 895 if (emitGeneralModelChanged) {
858 if (update && 896 emit modelChanged();
859 m_sinceLastNotifyMin >= 0 && 897 }
860 m_sinceLastNotifyMax >= 0) { 898 if (emitRegionChanged) {
861 m_rows.clear(); //!!! inefficient 899 emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax);
862 emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax); 900 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
863 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
864 } else {
865 emit completionChanged();
866 }
867 } else {
868 emit completionChanged();
869 }
870 } 901 }
871 } 902 }
872 903
873 template <typename PointType> 904 template <typename PointType>
874 void 905 void
880 // << extraAttributes.toStdString() << std::endl; 911 // << extraAttributes.toStdString() << std::endl;
881 912
882 QString type = getXmlOutputType(); 913 QString type = getXmlOutputType();
883 914
884 Model::toXml 915 Model::toXml
885 (out, 916 (out,
886 indent, 917 indent,
887 QString("type=\"%1\" dimensions=\"%2\" resolution=\"%3\" notifyOnAdd=\"%4\" dataset=\"%5\" %6") 918 QString("type=\"%1\" dimensions=\"%2\" resolution=\"%3\" notifyOnAdd=\"%4\" dataset=\"%5\" %6")
888 .arg(type) 919 .arg(type)
889 .arg(PointType(0).getDimensions()) 920 .arg(PointType(0).getDimensions())
890 .arg(m_resolution) 921 .arg(m_resolution)
891 .arg(m_notifyOnAdd ? "true" : "false") 922 .arg(m_notifyOnAdd ? "true" : "false")
892 .arg(getObjectExportId(&m_points)) 923 .arg(getObjectExportId(&m_points))
893 .arg(extraAttributes)); 924 .arg(extraAttributes));
894 925
895 out << indent; 926 out << indent;
896 out << QString("<dataset id=\"%1\" dimensions=\"%2\">\n") 927 out << QString("<dataset id=\"%1\" dimensions=\"%2\">\n")
897 .arg(getObjectExportId(&m_points)) 928 .arg(getObjectExportId(&m_points))
898 .arg(PointType(0).getDimensions()); 929 .arg(PointType(0).getDimensions());
899 930
900 for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) { 931 for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) {
901 i->toXml(out, indent + " "); 932 i->toXml(out, indent + " ");
902 } 933 }
903 934
940 } 971 }
941 972
942 template <typename PointType> 973 template <typename PointType>
943 void 974 void
944 SparseModel<PointType>::EditCommand::addCommand(Command *command, 975 SparseModel<PointType>::EditCommand::addCommand(Command *command,
945 bool executeFirst) 976 bool executeFirst)
946 { 977 {
947 if (executeFirst) command->execute(); 978 if (executeFirst) command->execute();
948 979
949 if (!m_commands.empty()) { 980 if (!m_commands.empty()) {
950 DeletePointCommand *dpc = dynamic_cast<DeletePointCommand *>(command); 981 DeletePointCommand *dpc = dynamic_cast<DeletePointCommand *>(command);
951 if (dpc) { 982 if (dpc) {
952 AddPointCommand *apc = dynamic_cast<AddPointCommand *> 983 AddPointCommand *apc = dynamic_cast<AddPointCommand *>
953 (m_commands[m_commands.size() - 1]); 984 (m_commands[m_commands.size() - 1]);
954 typename PointType::Comparator comparator; 985 typename PointType::Comparator comparator;
955 if (apc) { 986 if (apc) {
956 if (!comparator(apc->getPoint(), dpc->getPoint()) && 987 if (!comparator(apc->getPoint(), dpc->getPoint()) &&
957 !comparator(dpc->getPoint(), apc->getPoint())) { 988 !comparator(dpc->getPoint(), apc->getPoint())) {
958 deleteCommand(apc); 989 deleteCommand(apc);
959 return; 990 return;
960 } 991 }
961 } 992 }
962 } 993 }
963 } 994 }
964 995
965 MacroCommand::addCommand(command); 996 MacroCommand::addCommand(command);
966 } 997 }
967 998