annotate data/model/SparseModel.h @ 360:ac300d385ab2

* Various fixes to object lifetime management, particularly in the spectrum layer and for notification of main model deletion. The main purpose of this is to improve the behaviour of the spectrum, but I think it may also help with #1840922 Various crashes in Layer Summary window.
author Chris Cannam
date Wed, 23 Jan 2008 15:43:27 +0000
parents 700cd3350391
children 7aa1de571880
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 This file copyright 2006 Chris Cannam.
Chris@147 8
Chris@147 9 This program is free software; you can redistribute it and/or
Chris@147 10 modify it under the terms of the GNU General Public License as
Chris@147 11 published by the Free Software Foundation; either version 2 of the
Chris@147 12 License, or (at your option) any later version. See the file
Chris@147 13 COPYING included with this distribution for more information.
Chris@147 14 */
Chris@147 15
Chris@147 16 #ifndef _SPARSE_MODEL_H_
Chris@147 17 #define _SPARSE_MODEL_H_
Chris@147 18
Chris@150 19 #include "Model.h"
Chris@147 20 #include "base/Command.h"
Chris@147 21 #include "base/CommandHistory.h"
Chris@147 22
Chris@147 23 #include <iostream>
Chris@147 24
Chris@147 25 #include <set>
Chris@147 26 #include <QMutex>
Chris@147 27 #include <QTextStream>
Chris@147 28
Chris@147 29
Chris@147 30 /**
Chris@147 31 * Model containing sparse data (points with some properties). The
Chris@147 32 * properties depend on the point type.
Chris@147 33 */
Chris@147 34
Chris@147 35 template <typename PointType>
Chris@147 36 class SparseModel : public Model
Chris@147 37 {
Chris@147 38 public:
Chris@147 39 SparseModel(size_t sampleRate, size_t resolution,
Chris@147 40 bool notifyOnAdd = true);
Chris@147 41 virtual ~SparseModel() { }
Chris@147 42
Chris@147 43 virtual bool isOK() const { return true; }
Chris@147 44 virtual size_t getStartFrame() const;
Chris@147 45 virtual size_t getEndFrame() const;
Chris@147 46 virtual size_t getSampleRate() const { return m_sampleRate; }
Chris@147 47
Chris@147 48 virtual Model *clone() const;
Chris@147 49
Chris@147 50 // Number of frames of the underlying sample rate that this model
Chris@147 51 // is capable of resolving to. For example, if m_resolution == 10
Chris@147 52 // then every point in this model will be at a multiple of 10
Chris@147 53 // sample frames and should be considered to cover a window ending
Chris@147 54 // 10 sample frames later.
Chris@147 55 virtual size_t getResolution() const {
Chris@147 56 return m_resolution ? m_resolution : 1;
Chris@147 57 }
Chris@147 58 virtual void setResolution(size_t resolution);
Chris@147 59
Chris@147 60 typedef PointType Point;
Chris@147 61 typedef std::multiset<PointType,
Chris@147 62 typename PointType::OrderComparator> PointList;
Chris@147 63 typedef typename PointList::iterator PointListIterator;
Chris@147 64
Chris@147 65 /**
Chris@147 66 * Return whether the model is empty or not.
Chris@147 67 */
Chris@147 68 virtual bool isEmpty() const;
Chris@147 69
Chris@147 70 /**
Chris@147 71 * Get the total number of points in the model.
Chris@147 72 */
Chris@147 73 virtual size_t getPointCount() const;
Chris@147 74
Chris@147 75 /**
Chris@147 76 * Get all of the points in this model between the given
Chris@147 77 * boundaries (in frames), as well as up to two points before and
Chris@147 78 * after the boundaries. If you need exact boundaries, check the
Chris@147 79 * point coordinates in the returned list.
Chris@147 80 */
Chris@147 81 virtual PointList getPoints(long start, long end) const;
Chris@147 82
Chris@147 83 /**
Chris@147 84 * Get all points that cover the given frame number, taking the
Chris@147 85 * resolution of the model into account.
Chris@147 86 */
Chris@147 87 virtual PointList getPoints(long frame) const;
Chris@147 88
Chris@147 89 /**
Chris@297 90 * Get all points.
Chris@297 91 */
Chris@297 92 virtual const PointList &getPoints() const { return m_points; }
Chris@297 93
Chris@297 94 /**
Chris@147 95 * Return all points that share the nearest frame number prior to
Chris@147 96 * the given one at which there are any points.
Chris@147 97 */
Chris@147 98 virtual PointList getPreviousPoints(long frame) const;
Chris@147 99
Chris@147 100 /**
Chris@147 101 * Return all points that share the nearest frame number
Chris@147 102 * subsequent to the given one at which there are any points.
Chris@147 103 */
Chris@147 104 virtual PointList getNextPoints(long frame) const;
Chris@147 105
Chris@147 106 /**
Chris@147 107 * Remove all points.
Chris@147 108 */
Chris@147 109 virtual void clear();
Chris@147 110
Chris@147 111 /**
Chris@147 112 * Add a point.
Chris@147 113 */
Chris@147 114 virtual void addPoint(const PointType &point);
Chris@147 115
Chris@147 116 /**
Chris@147 117 * Remove a point. Points are not necessarily unique, so this
Chris@147 118 * function will remove the first point that compares equal to the
Chris@147 119 * supplied one using Point::Comparator. Other identical points
Chris@147 120 * may remain in the model.
Chris@147 121 */
Chris@147 122 virtual void deletePoint(const PointType &point);
Chris@147 123
Chris@297 124 virtual bool isReady(int *completion = 0) const {
Chris@297 125 bool ready = isOK() && (m_completion == 100);
Chris@297 126 if (completion) *completion = m_completion;
Chris@297 127 return ready;
Chris@297 128 }
Chris@297 129
Chris@333 130 virtual void setCompletion(int completion, bool update = true);
Chris@147 131 virtual int getCompletion() const { return m_completion; }
Chris@147 132
Chris@147 133 virtual bool hasTextLabels() const { return m_hasTextLabels; }
Chris@147 134
Chris@345 135 QString getTypeName() const { return tr("Sparse"); }
Chris@345 136
Chris@147 137 virtual void toXml(QTextStream &out,
Chris@147 138 QString indent = "",
Chris@147 139 QString extraAttributes = "") const;
Chris@147 140
Chris@147 141 virtual QString toDelimitedDataString(QString delimiter) const
Chris@147 142 {
Chris@147 143 QString s;
Chris@147 144 for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@147 145 s += i->toDelimitedDataString(delimiter, m_sampleRate) + "\n";
Chris@147 146 }
Chris@147 147 return s;
Chris@147 148 }
Chris@147 149
Chris@147 150 /**
Chris@147 151 * Command to add a point, with undo.
Chris@147 152 */
Chris@147 153 class AddPointCommand : public Command
Chris@147 154 {
Chris@147 155 public:
Chris@147 156 AddPointCommand(SparseModel<PointType> *model,
Chris@147 157 const PointType &point,
Chris@147 158 QString name = "") :
Chris@147 159 m_model(model), m_point(point), m_name(name) { }
Chris@147 160
Chris@147 161 virtual QString getName() const {
Chris@147 162 return (m_name == "" ? tr("Add Point") : m_name);
Chris@147 163 }
Chris@147 164
Chris@147 165 virtual void execute() { m_model->addPoint(m_point); }
Chris@147 166 virtual void unexecute() { m_model->deletePoint(m_point); }
Chris@147 167
Chris@147 168 const PointType &getPoint() const { return m_point; }
Chris@147 169
Chris@147 170 private:
Chris@147 171 SparseModel<PointType> *m_model;
Chris@147 172 PointType m_point;
Chris@147 173 QString m_name;
Chris@147 174 };
Chris@147 175
Chris@147 176
Chris@147 177 /**
Chris@147 178 * Command to remove a point, with undo.
Chris@147 179 */
Chris@147 180 class DeletePointCommand : public Command
Chris@147 181 {
Chris@147 182 public:
Chris@147 183 DeletePointCommand(SparseModel<PointType> *model,
Chris@147 184 const PointType &point) :
Chris@147 185 m_model(model), m_point(point) { }
Chris@147 186
Chris@147 187 virtual QString getName() const { return tr("Delete Point"); }
Chris@147 188
Chris@147 189 virtual void execute() { m_model->deletePoint(m_point); }
Chris@147 190 virtual void unexecute() { m_model->addPoint(m_point); }
Chris@147 191
Chris@147 192 const PointType &getPoint() const { return m_point; }
Chris@147 193
Chris@147 194 private:
Chris@147 195 SparseModel<PointType> *m_model;
Chris@147 196 PointType m_point;
Chris@147 197 };
Chris@147 198
Chris@147 199
Chris@147 200 /**
Chris@147 201 * Command to add or remove a series of points, with undo.
Chris@147 202 * Consecutive add/remove pairs for the same point are collapsed.
Chris@147 203 */
Chris@147 204 class EditCommand : public MacroCommand
Chris@147 205 {
Chris@147 206 public:
Chris@147 207 EditCommand(SparseModel<PointType> *model, QString commandName);
Chris@147 208
Chris@147 209 virtual void addPoint(const PointType &point);
Chris@147 210 virtual void deletePoint(const PointType &point);
Chris@147 211
Chris@147 212 /**
Chris@147 213 * Stack an arbitrary other command in the same sequence.
Chris@147 214 */
Chris@147 215 virtual void addCommand(Command *command) { addCommand(command, true); }
Chris@147 216
Chris@147 217 /**
Chris@147 218 * If any points have been added or deleted, add this command
Chris@147 219 * to the command history. Otherwise delete the command.
Chris@147 220 */
Chris@147 221 virtual void finish();
Chris@147 222
Chris@147 223 protected:
Chris@147 224 virtual void addCommand(Command *command, bool executeFirst);
Chris@147 225
Chris@147 226 SparseModel<PointType> *m_model;
Chris@147 227 };
Chris@147 228
Chris@147 229
Chris@147 230 /**
Chris@147 231 * Command to relabel a point.
Chris@147 232 */
Chris@147 233 class RelabelCommand : public Command
Chris@147 234 {
Chris@147 235 public:
Chris@147 236 RelabelCommand(SparseModel<PointType> *model,
Chris@147 237 const PointType &point,
Chris@147 238 QString newLabel) :
Chris@147 239 m_model(model), m_oldPoint(point), m_newPoint(point) {
Chris@147 240 m_newPoint.label = newLabel;
Chris@147 241 }
Chris@147 242
Chris@147 243 virtual QString getName() const { return tr("Re-Label Point"); }
Chris@147 244
Chris@147 245 virtual void execute() {
Chris@147 246 m_model->deletePoint(m_oldPoint);
Chris@147 247 m_model->addPoint(m_newPoint);
Chris@147 248 std::swap(m_oldPoint, m_newPoint);
Chris@147 249 }
Chris@147 250
Chris@147 251 virtual void unexecute() { execute(); }
Chris@147 252
Chris@147 253 private:
Chris@147 254 SparseModel<PointType> *m_model;
Chris@147 255 PointType m_oldPoint;
Chris@147 256 PointType m_newPoint;
Chris@147 257 };
Chris@147 258
Chris@147 259
Chris@147 260
Chris@147 261 protected:
Chris@147 262 size_t m_sampleRate;
Chris@147 263 size_t m_resolution;
Chris@147 264 bool m_notifyOnAdd;
Chris@147 265 long m_sinceLastNotifyMin;
Chris@147 266 long m_sinceLastNotifyMax;
Chris@147 267 bool m_hasTextLabels;
Chris@147 268
Chris@147 269 PointList m_points;
Chris@147 270 size_t m_pointCount;
Chris@147 271 mutable QMutex m_mutex;
Chris@147 272 int m_completion;
Chris@147 273 };
Chris@147 274
Chris@147 275
Chris@147 276 template <typename PointType>
Chris@147 277 SparseModel<PointType>::SparseModel(size_t sampleRate,
Chris@147 278 size_t resolution,
Chris@147 279 bool notifyOnAdd) :
Chris@147 280 m_sampleRate(sampleRate),
Chris@147 281 m_resolution(resolution),
Chris@147 282 m_notifyOnAdd(notifyOnAdd),
Chris@147 283 m_sinceLastNotifyMin(-1),
Chris@147 284 m_sinceLastNotifyMax(-1),
Chris@147 285 m_hasTextLabels(false),
Chris@147 286 m_pointCount(0),
Chris@147 287 m_completion(100)
Chris@147 288 {
Chris@147 289 }
Chris@147 290
Chris@147 291 template <typename PointType>
Chris@147 292 size_t
Chris@147 293 SparseModel<PointType>::getStartFrame() const
Chris@147 294 {
Chris@147 295 QMutexLocker locker(&m_mutex);
Chris@147 296 size_t f = 0;
Chris@147 297 if (!m_points.empty()) {
Chris@147 298 f = m_points.begin()->frame;
Chris@147 299 }
Chris@147 300 return f;
Chris@147 301 }
Chris@147 302
Chris@147 303 template <typename PointType>
Chris@147 304 size_t
Chris@147 305 SparseModel<PointType>::getEndFrame() const
Chris@147 306 {
Chris@147 307 QMutexLocker locker(&m_mutex);
Chris@147 308 size_t f = 0;
Chris@147 309 if (!m_points.empty()) {
Chris@147 310 PointListIterator i(m_points.end());
Chris@147 311 f = (--i)->frame;
Chris@147 312 }
Chris@147 313 return f;
Chris@147 314 }
Chris@147 315
Chris@147 316 template <typename PointType>
Chris@147 317 Model *
Chris@147 318 SparseModel<PointType>::clone() const
Chris@147 319 {
Chris@147 320 SparseModel<PointType> *model =
Chris@147 321 new SparseModel<PointType>(m_sampleRate, m_resolution, m_notifyOnAdd);
Chris@147 322 model->m_points = m_points;
Chris@147 323 model->m_pointCount = m_pointCount;
Chris@147 324 return model;
Chris@147 325 }
Chris@147 326
Chris@147 327 template <typename PointType>
Chris@147 328 bool
Chris@147 329 SparseModel<PointType>::isEmpty() const
Chris@147 330 {
Chris@147 331 return m_pointCount == 0;
Chris@147 332 }
Chris@147 333
Chris@147 334 template <typename PointType>
Chris@147 335 size_t
Chris@147 336 SparseModel<PointType>::getPointCount() const
Chris@147 337 {
Chris@147 338 return m_pointCount;
Chris@147 339 }
Chris@147 340
Chris@147 341 template <typename PointType>
Chris@147 342 typename SparseModel<PointType>::PointList
Chris@147 343 SparseModel<PointType>::getPoints(long start, long end) const
Chris@147 344 {
Chris@147 345 if (start > end) return PointList();
Chris@147 346 QMutexLocker locker(&m_mutex);
Chris@147 347
Chris@147 348 PointType startPoint(start), endPoint(end);
Chris@147 349
Chris@147 350 PointListIterator startItr = m_points.lower_bound(startPoint);
Chris@147 351 PointListIterator endItr = m_points.upper_bound(endPoint);
Chris@147 352
Chris@147 353 if (startItr != m_points.begin()) --startItr;
Chris@147 354 if (startItr != m_points.begin()) --startItr;
Chris@147 355 if (endItr != m_points.end()) ++endItr;
Chris@147 356 if (endItr != m_points.end()) ++endItr;
Chris@147 357
Chris@147 358 PointList rv;
Chris@147 359
Chris@147 360 for (PointListIterator i = startItr; i != endItr; ++i) {
Chris@147 361 rv.insert(*i);
Chris@147 362 }
Chris@147 363
Chris@147 364 return rv;
Chris@147 365 }
Chris@147 366
Chris@147 367 template <typename PointType>
Chris@147 368 typename SparseModel<PointType>::PointList
Chris@147 369 SparseModel<PointType>::getPoints(long frame) const
Chris@147 370 {
Chris@147 371 QMutexLocker locker(&m_mutex);
Chris@147 372
Chris@147 373 if (m_resolution == 0) return PointList();
Chris@147 374
Chris@147 375 long start = (frame / m_resolution) * m_resolution;
Chris@147 376 long end = start + m_resolution;
Chris@147 377
Chris@147 378 PointType startPoint(start), endPoint(end);
Chris@147 379
Chris@147 380 PointListIterator startItr = m_points.lower_bound(startPoint);
Chris@147 381 PointListIterator endItr = m_points.upper_bound(endPoint);
Chris@147 382
Chris@147 383 PointList rv;
Chris@147 384
Chris@147 385 for (PointListIterator i = startItr; i != endItr; ++i) {
Chris@147 386 rv.insert(*i);
Chris@147 387 }
Chris@147 388
Chris@147 389 return rv;
Chris@147 390 }
Chris@147 391
Chris@147 392 template <typename PointType>
Chris@147 393 typename SparseModel<PointType>::PointList
Chris@147 394 SparseModel<PointType>::getPreviousPoints(long originFrame) const
Chris@147 395 {
Chris@147 396 QMutexLocker locker(&m_mutex);
Chris@147 397
Chris@147 398 PointType lookupPoint(originFrame);
Chris@147 399 PointList rv;
Chris@147 400
Chris@147 401 PointListIterator i = m_points.lower_bound(lookupPoint);
Chris@147 402 if (i == m_points.begin()) return rv;
Chris@147 403
Chris@147 404 --i;
Chris@147 405 long frame = i->frame;
Chris@147 406 while (i->frame == frame) {
Chris@147 407 rv.insert(*i);
Chris@147 408 if (i == m_points.begin()) break;
Chris@147 409 --i;
Chris@147 410 }
Chris@147 411
Chris@147 412 return rv;
Chris@147 413 }
Chris@147 414
Chris@147 415 template <typename PointType>
Chris@147 416 typename SparseModel<PointType>::PointList
Chris@147 417 SparseModel<PointType>::getNextPoints(long originFrame) const
Chris@147 418 {
Chris@147 419 QMutexLocker locker(&m_mutex);
Chris@147 420
Chris@147 421 PointType lookupPoint(originFrame);
Chris@147 422 PointList rv;
Chris@147 423
Chris@147 424 PointListIterator i = m_points.upper_bound(lookupPoint);
Chris@147 425 if (i == m_points.end()) return rv;
Chris@147 426
Chris@147 427 long frame = i->frame;
Chris@147 428 while (i != m_points.end() && i->frame == frame) {
Chris@147 429 rv.insert(*i);
Chris@147 430 ++i;
Chris@147 431 }
Chris@147 432
Chris@147 433 return rv;
Chris@147 434 }
Chris@147 435
Chris@147 436 template <typename PointType>
Chris@147 437 void
Chris@147 438 SparseModel<PointType>::setResolution(size_t resolution)
Chris@147 439 {
Chris@147 440 {
Chris@147 441 QMutexLocker locker(&m_mutex);
Chris@147 442 m_resolution = resolution;
Chris@147 443 }
Chris@147 444 emit modelChanged();
Chris@147 445 }
Chris@147 446
Chris@147 447 template <typename PointType>
Chris@147 448 void
Chris@147 449 SparseModel<PointType>::clear()
Chris@147 450 {
Chris@147 451 {
Chris@147 452 QMutexLocker locker(&m_mutex);
Chris@147 453 m_points.clear();
Chris@147 454 m_pointCount = 0;
Chris@147 455 }
Chris@147 456 emit modelChanged();
Chris@147 457 }
Chris@147 458
Chris@147 459 template <typename PointType>
Chris@147 460 void
Chris@147 461 SparseModel<PointType>::addPoint(const PointType &point)
Chris@147 462 {
Chris@147 463 {
Chris@147 464 QMutexLocker locker(&m_mutex);
Chris@147 465 m_points.insert(point);
Chris@147 466 m_pointCount++;
Chris@338 467 if (point.getLabel() != "") m_hasTextLabels = true;
Chris@147 468 }
Chris@147 469
Chris@147 470 // Even though this model is nominally sparse, there may still be
Chris@147 471 // too many signals going on here (especially as they'll probably
Chris@147 472 // be queued from one thread to another), which is why we need the
Chris@147 473 // notifyOnAdd as an option rather than a necessity (the
Chris@147 474 // alternative is to notify on setCompletion).
Chris@147 475
Chris@147 476 if (m_notifyOnAdd) {
Chris@147 477 emit modelChanged(point.frame, point.frame + m_resolution);
Chris@147 478 } else {
Chris@147 479 if (m_sinceLastNotifyMin == -1 ||
Chris@147 480 point.frame < m_sinceLastNotifyMin) {
Chris@147 481 m_sinceLastNotifyMin = point.frame;
Chris@147 482 }
Chris@147 483 if (m_sinceLastNotifyMax == -1 ||
Chris@147 484 point.frame > m_sinceLastNotifyMax) {
Chris@147 485 m_sinceLastNotifyMax = point.frame;
Chris@147 486 }
Chris@147 487 }
Chris@147 488 }
Chris@147 489
Chris@147 490 template <typename PointType>
Chris@147 491 void
Chris@147 492 SparseModel<PointType>::deletePoint(const PointType &point)
Chris@147 493 {
Chris@147 494 {
Chris@147 495 QMutexLocker locker(&m_mutex);
Chris@147 496
Chris@147 497 PointListIterator i = m_points.lower_bound(point);
Chris@147 498 typename PointType::Comparator comparator;
Chris@147 499 while (i != m_points.end()) {
Chris@147 500 if (i->frame > point.frame) break;
Chris@147 501 if (!comparator(*i, point) && !comparator(point, *i)) {
Chris@147 502 m_points.erase(i);
Chris@147 503 m_pointCount--;
Chris@147 504 break;
Chris@147 505 }
Chris@147 506 ++i;
Chris@147 507 }
Chris@147 508 }
Chris@147 509 // std::cout << "SparseOneDimensionalModel: emit modelChanged("
Chris@147 510 // << point.frame << ")" << std::endl;
Chris@147 511 emit modelChanged(point.frame, point.frame + m_resolution);
Chris@147 512 }
Chris@147 513
Chris@147 514 template <typename PointType>
Chris@147 515 void
Chris@333 516 SparseModel<PointType>::setCompletion(int completion, bool update)
Chris@147 517 {
Chris@301 518 // std::cerr << "SparseModel::setCompletion(" << completion << ")" << std::endl;
Chris@191 519
Chris@147 520 if (m_completion != completion) {
Chris@147 521 m_completion = completion;
Chris@147 522
Chris@147 523 if (completion == 100) {
Chris@147 524
Chris@297 525 if (!m_notifyOnAdd) {
Chris@297 526 emit completionChanged();
Chris@297 527 }
Chris@297 528
Chris@147 529 m_notifyOnAdd = true; // henceforth
Chris@147 530 emit modelChanged();
Chris@147 531
Chris@147 532 } else if (!m_notifyOnAdd) {
Chris@147 533
Chris@333 534 if (update &&
Chris@333 535 m_sinceLastNotifyMin >= 0 &&
Chris@147 536 m_sinceLastNotifyMax >= 0) {
Chris@147 537 emit modelChanged(m_sinceLastNotifyMin, m_sinceLastNotifyMax);
Chris@147 538 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
Chris@147 539 } else {
Chris@147 540 emit completionChanged();
Chris@147 541 }
Chris@147 542 } else {
Chris@147 543 emit completionChanged();
Chris@147 544 }
Chris@147 545 }
Chris@147 546 }
Chris@147 547
Chris@147 548 template <typename PointType>
Chris@147 549 void
Chris@147 550 SparseModel<PointType>::toXml(QTextStream &out,
Chris@147 551 QString indent,
Chris@147 552 QString extraAttributes) const
Chris@147 553 {
Chris@318 554 std::cerr << "SparseModel::toXml: extraAttributes = \""
Chris@318 555 << extraAttributes.toStdString() << std::endl;
Chris@318 556
Chris@147 557 Model::toXml
Chris@147 558 (out,
Chris@147 559 indent,
Chris@147 560 QString("type=\"sparse\" dimensions=\"%1\" resolution=\"%2\" notifyOnAdd=\"%3\" dataset=\"%4\" %5")
Chris@147 561 .arg(PointType(0).getDimensions())
Chris@147 562 .arg(m_resolution)
Chris@147 563 .arg(m_notifyOnAdd ? "true" : "false")
Chris@147 564 .arg(getObjectExportId(&m_points))
Chris@147 565 .arg(extraAttributes));
Chris@147 566
Chris@147 567 out << indent;
Chris@147 568 out << QString("<dataset id=\"%1\" dimensions=\"%2\">\n")
Chris@147 569 .arg(getObjectExportId(&m_points))
Chris@147 570 .arg(PointType(0).getDimensions());
Chris@147 571
Chris@147 572 for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@314 573 i->toXml(out, indent + " ");
Chris@147 574 }
Chris@147 575
Chris@147 576 out << indent;
Chris@147 577 out << "</dataset>\n";
Chris@147 578 }
Chris@147 579
Chris@147 580 template <typename PointType>
Chris@147 581 SparseModel<PointType>::EditCommand::EditCommand(SparseModel *model,
Chris@147 582 QString commandName) :
Chris@147 583 MacroCommand(commandName),
Chris@147 584 m_model(model)
Chris@147 585 {
Chris@147 586 }
Chris@147 587
Chris@147 588 template <typename PointType>
Chris@147 589 void
Chris@147 590 SparseModel<PointType>::EditCommand::addPoint(const PointType &point)
Chris@147 591 {
Chris@147 592 addCommand(new AddPointCommand(m_model, point), true);
Chris@147 593 }
Chris@147 594
Chris@147 595 template <typename PointType>
Chris@147 596 void
Chris@147 597 SparseModel<PointType>::EditCommand::deletePoint(const PointType &point)
Chris@147 598 {
Chris@147 599 addCommand(new DeletePointCommand(m_model, point), true);
Chris@147 600 }
Chris@147 601
Chris@147 602 template <typename PointType>
Chris@147 603 void
Chris@147 604 SparseModel<PointType>::EditCommand::finish()
Chris@147 605 {
Chris@147 606 if (!m_commands.empty()) {
Chris@147 607 CommandHistory::getInstance()->addCommand(this, false);
Chris@147 608 } else {
Chris@147 609 delete this;
Chris@147 610 }
Chris@147 611 }
Chris@147 612
Chris@147 613 template <typename PointType>
Chris@147 614 void
Chris@147 615 SparseModel<PointType>::EditCommand::addCommand(Command *command,
Chris@147 616 bool executeFirst)
Chris@147 617 {
Chris@147 618 if (executeFirst) command->execute();
Chris@147 619
Chris@147 620 if (!m_commands.empty()) {
Chris@147 621 DeletePointCommand *dpc = dynamic_cast<DeletePointCommand *>(command);
Chris@147 622 if (dpc) {
Chris@147 623 AddPointCommand *apc = dynamic_cast<AddPointCommand *>
Chris@147 624 (m_commands[m_commands.size() - 1]);
Chris@147 625 typename PointType::Comparator comparator;
Chris@147 626 if (apc) {
Chris@147 627 if (!comparator(apc->getPoint(), dpc->getPoint()) &&
Chris@147 628 !comparator(dpc->getPoint(), apc->getPoint())) {
Chris@147 629 deleteCommand(apc);
Chris@147 630 return;
Chris@147 631 }
Chris@147 632 }
Chris@147 633 }
Chris@147 634 }
Chris@147 635
Chris@147 636 MacroCommand::addCommand(command);
Chris@147 637 }
Chris@147 638
Chris@147 639
Chris@147 640 #endif
Chris@147 641
Chris@147 642
Chris@147 643