annotate data/model/SparseModel.h @ 316:3a6725f285d6

* Make RemoteFile far more pervasive, and use it for local files as well so that we can handle both transparently. Make it shallow copy with reference counting, so it can be used by value without having to worry about the cache file lifetime. Use RemoteFile for MainWindow file-open functions, etc
author Chris Cannam
date Thu, 18 Oct 2007 15:31:20 +0000
parents 70a232b1f12a
children 7a4bd2c8585c
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@147 130 virtual void setCompletion(int completion);
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@147 135 virtual void toXml(QTextStream &out,
Chris@147 136 QString indent = "",
Chris@147 137 QString extraAttributes = "") const;
Chris@147 138
Chris@147 139 virtual QString toDelimitedDataString(QString delimiter) const
Chris@147 140 {
Chris@147 141 QString s;
Chris@147 142 for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@147 143 s += i->toDelimitedDataString(delimiter, m_sampleRate) + "\n";
Chris@147 144 }
Chris@147 145 return s;
Chris@147 146 }
Chris@147 147
Chris@147 148 /**
Chris@147 149 * Command to add a point, with undo.
Chris@147 150 */
Chris@147 151 class AddPointCommand : public Command
Chris@147 152 {
Chris@147 153 public:
Chris@147 154 AddPointCommand(SparseModel<PointType> *model,
Chris@147 155 const PointType &point,
Chris@147 156 QString name = "") :
Chris@147 157 m_model(model), m_point(point), m_name(name) { }
Chris@147 158
Chris@147 159 virtual QString getName() const {
Chris@147 160 return (m_name == "" ? tr("Add Point") : m_name);
Chris@147 161 }
Chris@147 162
Chris@147 163 virtual void execute() { m_model->addPoint(m_point); }
Chris@147 164 virtual void unexecute() { m_model->deletePoint(m_point); }
Chris@147 165
Chris@147 166 const PointType &getPoint() const { return m_point; }
Chris@147 167
Chris@147 168 private:
Chris@147 169 SparseModel<PointType> *m_model;
Chris@147 170 PointType m_point;
Chris@147 171 QString m_name;
Chris@147 172 };
Chris@147 173
Chris@147 174
Chris@147 175 /**
Chris@147 176 * Command to remove a point, with undo.
Chris@147 177 */
Chris@147 178 class DeletePointCommand : public Command
Chris@147 179 {
Chris@147 180 public:
Chris@147 181 DeletePointCommand(SparseModel<PointType> *model,
Chris@147 182 const PointType &point) :
Chris@147 183 m_model(model), m_point(point) { }
Chris@147 184
Chris@147 185 virtual QString getName() const { return tr("Delete Point"); }
Chris@147 186
Chris@147 187 virtual void execute() { m_model->deletePoint(m_point); }
Chris@147 188 virtual void unexecute() { m_model->addPoint(m_point); }
Chris@147 189
Chris@147 190 const PointType &getPoint() const { return m_point; }
Chris@147 191
Chris@147 192 private:
Chris@147 193 SparseModel<PointType> *m_model;
Chris@147 194 PointType m_point;
Chris@147 195 };
Chris@147 196
Chris@147 197
Chris@147 198 /**
Chris@147 199 * Command to add or remove a series of points, with undo.
Chris@147 200 * Consecutive add/remove pairs for the same point are collapsed.
Chris@147 201 */
Chris@147 202 class EditCommand : public MacroCommand
Chris@147 203 {
Chris@147 204 public:
Chris@147 205 EditCommand(SparseModel<PointType> *model, QString commandName);
Chris@147 206
Chris@147 207 virtual void addPoint(const PointType &point);
Chris@147 208 virtual void deletePoint(const PointType &point);
Chris@147 209
Chris@147 210 /**
Chris@147 211 * Stack an arbitrary other command in the same sequence.
Chris@147 212 */
Chris@147 213 virtual void addCommand(Command *command) { addCommand(command, true); }
Chris@147 214
Chris@147 215 /**
Chris@147 216 * If any points have been added or deleted, add this command
Chris@147 217 * to the command history. Otherwise delete the command.
Chris@147 218 */
Chris@147 219 virtual void finish();
Chris@147 220
Chris@147 221 protected:
Chris@147 222 virtual void addCommand(Command *command, bool executeFirst);
Chris@147 223
Chris@147 224 SparseModel<PointType> *m_model;
Chris@147 225 };
Chris@147 226
Chris@147 227
Chris@147 228 /**
Chris@147 229 * Command to relabel a point.
Chris@147 230 */
Chris@147 231 class RelabelCommand : public Command
Chris@147 232 {
Chris@147 233 public:
Chris@147 234 RelabelCommand(SparseModel<PointType> *model,
Chris@147 235 const PointType &point,
Chris@147 236 QString newLabel) :
Chris@147 237 m_model(model), m_oldPoint(point), m_newPoint(point) {
Chris@147 238 m_newPoint.label = newLabel;
Chris@147 239 }
Chris@147 240
Chris@147 241 virtual QString getName() const { return tr("Re-Label Point"); }
Chris@147 242
Chris@147 243 virtual void execute() {
Chris@147 244 m_model->deletePoint(m_oldPoint);
Chris@147 245 m_model->addPoint(m_newPoint);
Chris@147 246 std::swap(m_oldPoint, m_newPoint);
Chris@147 247 }
Chris@147 248
Chris@147 249 virtual void unexecute() { execute(); }
Chris@147 250
Chris@147 251 private:
Chris@147 252 SparseModel<PointType> *m_model;
Chris@147 253 PointType m_oldPoint;
Chris@147 254 PointType m_newPoint;
Chris@147 255 };
Chris@147 256
Chris@147 257
Chris@147 258
Chris@147 259 protected:
Chris@147 260 size_t m_sampleRate;
Chris@147 261 size_t m_resolution;
Chris@147 262 bool m_notifyOnAdd;
Chris@147 263 long m_sinceLastNotifyMin;
Chris@147 264 long m_sinceLastNotifyMax;
Chris@147 265 bool m_hasTextLabels;
Chris@147 266
Chris@147 267 PointList m_points;
Chris@147 268 size_t m_pointCount;
Chris@147 269 mutable QMutex m_mutex;
Chris@147 270 int m_completion;
Chris@147 271 };
Chris@147 272
Chris@147 273
Chris@147 274 template <typename PointType>
Chris@147 275 SparseModel<PointType>::SparseModel(size_t sampleRate,
Chris@147 276 size_t resolution,
Chris@147 277 bool notifyOnAdd) :
Chris@147 278 m_sampleRate(sampleRate),
Chris@147 279 m_resolution(resolution),
Chris@147 280 m_notifyOnAdd(notifyOnAdd),
Chris@147 281 m_sinceLastNotifyMin(-1),
Chris@147 282 m_sinceLastNotifyMax(-1),
Chris@147 283 m_hasTextLabels(false),
Chris@147 284 m_pointCount(0),
Chris@147 285 m_completion(100)
Chris@147 286 {
Chris@147 287 }
Chris@147 288
Chris@147 289 template <typename PointType>
Chris@147 290 size_t
Chris@147 291 SparseModel<PointType>::getStartFrame() const
Chris@147 292 {
Chris@147 293 QMutexLocker locker(&m_mutex);
Chris@147 294 size_t f = 0;
Chris@147 295 if (!m_points.empty()) {
Chris@147 296 f = m_points.begin()->frame;
Chris@147 297 }
Chris@147 298 return f;
Chris@147 299 }
Chris@147 300
Chris@147 301 template <typename PointType>
Chris@147 302 size_t
Chris@147 303 SparseModel<PointType>::getEndFrame() const
Chris@147 304 {
Chris@147 305 QMutexLocker locker(&m_mutex);
Chris@147 306 size_t f = 0;
Chris@147 307 if (!m_points.empty()) {
Chris@147 308 PointListIterator i(m_points.end());
Chris@147 309 f = (--i)->frame;
Chris@147 310 }
Chris@147 311 return f;
Chris@147 312 }
Chris@147 313
Chris@147 314 template <typename PointType>
Chris@147 315 Model *
Chris@147 316 SparseModel<PointType>::clone() const
Chris@147 317 {
Chris@147 318 SparseModel<PointType> *model =
Chris@147 319 new SparseModel<PointType>(m_sampleRate, m_resolution, m_notifyOnAdd);
Chris@147 320 model->m_points = m_points;
Chris@147 321 model->m_pointCount = m_pointCount;
Chris@147 322 return model;
Chris@147 323 }
Chris@147 324
Chris@147 325 template <typename PointType>
Chris@147 326 bool
Chris@147 327 SparseModel<PointType>::isEmpty() const
Chris@147 328 {
Chris@147 329 return m_pointCount == 0;
Chris@147 330 }
Chris@147 331
Chris@147 332 template <typename PointType>
Chris@147 333 size_t
Chris@147 334 SparseModel<PointType>::getPointCount() const
Chris@147 335 {
Chris@147 336 return m_pointCount;
Chris@147 337 }
Chris@147 338
Chris@147 339 template <typename PointType>
Chris@147 340 typename SparseModel<PointType>::PointList
Chris@147 341 SparseModel<PointType>::getPoints(long start, long end) const
Chris@147 342 {
Chris@147 343 if (start > end) return PointList();
Chris@147 344 QMutexLocker locker(&m_mutex);
Chris@147 345
Chris@147 346 PointType startPoint(start), endPoint(end);
Chris@147 347
Chris@147 348 PointListIterator startItr = m_points.lower_bound(startPoint);
Chris@147 349 PointListIterator endItr = m_points.upper_bound(endPoint);
Chris@147 350
Chris@147 351 if (startItr != m_points.begin()) --startItr;
Chris@147 352 if (startItr != m_points.begin()) --startItr;
Chris@147 353 if (endItr != m_points.end()) ++endItr;
Chris@147 354 if (endItr != m_points.end()) ++endItr;
Chris@147 355
Chris@147 356 PointList rv;
Chris@147 357
Chris@147 358 for (PointListIterator i = startItr; i != endItr; ++i) {
Chris@147 359 rv.insert(*i);
Chris@147 360 }
Chris@147 361
Chris@147 362 return rv;
Chris@147 363 }
Chris@147 364
Chris@147 365 template <typename PointType>
Chris@147 366 typename SparseModel<PointType>::PointList
Chris@147 367 SparseModel<PointType>::getPoints(long frame) const
Chris@147 368 {
Chris@147 369 QMutexLocker locker(&m_mutex);
Chris@147 370
Chris@147 371 if (m_resolution == 0) return PointList();
Chris@147 372
Chris@147 373 long start = (frame / m_resolution) * m_resolution;
Chris@147 374 long end = start + m_resolution;
Chris@147 375
Chris@147 376 PointType startPoint(start), endPoint(end);
Chris@147 377
Chris@147 378 PointListIterator startItr = m_points.lower_bound(startPoint);
Chris@147 379 PointListIterator endItr = m_points.upper_bound(endPoint);
Chris@147 380
Chris@147 381 PointList rv;
Chris@147 382
Chris@147 383 for (PointListIterator i = startItr; i != endItr; ++i) {
Chris@147 384 rv.insert(*i);
Chris@147 385 }
Chris@147 386
Chris@147 387 return rv;
Chris@147 388 }
Chris@147 389
Chris@147 390 template <typename PointType>
Chris@147 391 typename SparseModel<PointType>::PointList
Chris@147 392 SparseModel<PointType>::getPreviousPoints(long originFrame) const
Chris@147 393 {
Chris@147 394 QMutexLocker locker(&m_mutex);
Chris@147 395
Chris@147 396 PointType lookupPoint(originFrame);
Chris@147 397 PointList rv;
Chris@147 398
Chris@147 399 PointListIterator i = m_points.lower_bound(lookupPoint);
Chris@147 400 if (i == m_points.begin()) return rv;
Chris@147 401
Chris@147 402 --i;
Chris@147 403 long frame = i->frame;
Chris@147 404 while (i->frame == frame) {
Chris@147 405 rv.insert(*i);
Chris@147 406 if (i == m_points.begin()) break;
Chris@147 407 --i;
Chris@147 408 }
Chris@147 409
Chris@147 410 return rv;
Chris@147 411 }
Chris@147 412
Chris@147 413 template <typename PointType>
Chris@147 414 typename SparseModel<PointType>::PointList
Chris@147 415 SparseModel<PointType>::getNextPoints(long originFrame) const
Chris@147 416 {
Chris@147 417 QMutexLocker locker(&m_mutex);
Chris@147 418
Chris@147 419 PointType lookupPoint(originFrame);
Chris@147 420 PointList rv;
Chris@147 421
Chris@147 422 PointListIterator i = m_points.upper_bound(lookupPoint);
Chris@147 423 if (i == m_points.end()) return rv;
Chris@147 424
Chris@147 425 long frame = i->frame;
Chris@147 426 while (i != m_points.end() && i->frame == frame) {
Chris@147 427 rv.insert(*i);
Chris@147 428 ++i;
Chris@147 429 }
Chris@147 430
Chris@147 431 return rv;
Chris@147 432 }
Chris@147 433
Chris@147 434 template <typename PointType>
Chris@147 435 void
Chris@147 436 SparseModel<PointType>::setResolution(size_t resolution)
Chris@147 437 {
Chris@147 438 {
Chris@147 439 QMutexLocker locker(&m_mutex);
Chris@147 440 m_resolution = resolution;
Chris@147 441 }
Chris@147 442 emit modelChanged();
Chris@147 443 }
Chris@147 444
Chris@147 445 template <typename PointType>
Chris@147 446 void
Chris@147 447 SparseModel<PointType>::clear()
Chris@147 448 {
Chris@147 449 {
Chris@147 450 QMutexLocker locker(&m_mutex);
Chris@147 451 m_points.clear();
Chris@147 452 m_pointCount = 0;
Chris@147 453 }
Chris@147 454 emit modelChanged();
Chris@147 455 }
Chris@147 456
Chris@147 457 template <typename PointType>
Chris@147 458 void
Chris@147 459 SparseModel<PointType>::addPoint(const PointType &point)
Chris@147 460 {
Chris@147 461 {
Chris@147 462 QMutexLocker locker(&m_mutex);
Chris@147 463 m_points.insert(point);
Chris@147 464 m_pointCount++;
Chris@147 465 if (point.label != "") m_hasTextLabels = true;
Chris@147 466 }
Chris@147 467
Chris@147 468 // Even though this model is nominally sparse, there may still be
Chris@147 469 // too many signals going on here (especially as they'll probably
Chris@147 470 // be queued from one thread to another), which is why we need the
Chris@147 471 // notifyOnAdd as an option rather than a necessity (the
Chris@147 472 // alternative is to notify on setCompletion).
Chris@147 473
Chris@147 474 if (m_notifyOnAdd) {
Chris@147 475 emit modelChanged(point.frame, point.frame + m_resolution);
Chris@147 476 } else {
Chris@147 477 if (m_sinceLastNotifyMin == -1 ||
Chris@147 478 point.frame < m_sinceLastNotifyMin) {
Chris@147 479 m_sinceLastNotifyMin = point.frame;
Chris@147 480 }
Chris@147 481 if (m_sinceLastNotifyMax == -1 ||
Chris@147 482 point.frame > m_sinceLastNotifyMax) {
Chris@147 483 m_sinceLastNotifyMax = point.frame;
Chris@147 484 }
Chris@147 485 }
Chris@147 486 }
Chris@147 487
Chris@147 488 template <typename PointType>
Chris@147 489 void
Chris@147 490 SparseModel<PointType>::deletePoint(const PointType &point)
Chris@147 491 {
Chris@147 492 {
Chris@147 493 QMutexLocker locker(&m_mutex);
Chris@147 494
Chris@147 495 PointListIterator i = m_points.lower_bound(point);
Chris@147 496 typename PointType::Comparator comparator;
Chris@147 497 while (i != m_points.end()) {
Chris@147 498 if (i->frame > point.frame) break;
Chris@147 499 if (!comparator(*i, point) && !comparator(point, *i)) {
Chris@147 500 m_points.erase(i);
Chris@147 501 m_pointCount--;
Chris@147 502 break;
Chris@147 503 }
Chris@147 504 ++i;
Chris@147 505 }
Chris@147 506 }
Chris@147 507 // std::cout << "SparseOneDimensionalModel: emit modelChanged("
Chris@147 508 // << point.frame << ")" << std::endl;
Chris@147 509 emit modelChanged(point.frame, point.frame + m_resolution);
Chris@147 510 }
Chris@147 511
Chris@147 512 template <typename PointType>
Chris@147 513 void
Chris@147 514 SparseModel<PointType>::setCompletion(int completion)
Chris@147 515 {
Chris@301 516 // std::cerr << "SparseModel::setCompletion(" << completion << ")" << std::endl;
Chris@191 517
Chris@147 518 if (m_completion != completion) {
Chris@147 519 m_completion = completion;
Chris@147 520
Chris@147 521 if (completion == 100) {
Chris@147 522
Chris@297 523 if (!m_notifyOnAdd) {
Chris@297 524 emit completionChanged();
Chris@297 525 }
Chris@297 526
Chris@147 527 m_notifyOnAdd = true; // henceforth
Chris@147 528 emit modelChanged();
Chris@147 529
Chris@147 530 } else if (!m_notifyOnAdd) {
Chris@147 531
Chris@147 532 if (m_sinceLastNotifyMin >= 0 &&
Chris@147 533 m_sinceLastNotifyMax >= 0) {
Chris@147 534 emit modelChanged(m_sinceLastNotifyMin, m_sinceLastNotifyMax);
Chris@147 535 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
Chris@147 536 } else {
Chris@147 537 emit completionChanged();
Chris@147 538 }
Chris@147 539 } else {
Chris@147 540 emit completionChanged();
Chris@147 541 }
Chris@147 542 }
Chris@147 543 }
Chris@147 544
Chris@147 545 template <typename PointType>
Chris@147 546 void
Chris@147 547 SparseModel<PointType>::toXml(QTextStream &out,
Chris@147 548 QString indent,
Chris@147 549 QString extraAttributes) const
Chris@147 550 {
Chris@147 551 Model::toXml
Chris@147 552 (out,
Chris@147 553 indent,
Chris@147 554 QString("type=\"sparse\" dimensions=\"%1\" resolution=\"%2\" notifyOnAdd=\"%3\" dataset=\"%4\" %5")
Chris@147 555 .arg(PointType(0).getDimensions())
Chris@147 556 .arg(m_resolution)
Chris@147 557 .arg(m_notifyOnAdd ? "true" : "false")
Chris@147 558 .arg(getObjectExportId(&m_points))
Chris@147 559 .arg(extraAttributes));
Chris@147 560
Chris@147 561 out << indent;
Chris@147 562 out << QString("<dataset id=\"%1\" dimensions=\"%2\">\n")
Chris@147 563 .arg(getObjectExportId(&m_points))
Chris@147 564 .arg(PointType(0).getDimensions());
Chris@147 565
Chris@147 566 for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@314 567 i->toXml(out, indent + " ");
Chris@147 568 }
Chris@147 569
Chris@147 570 out << indent;
Chris@147 571 out << "</dataset>\n";
Chris@147 572 }
Chris@147 573
Chris@147 574 template <typename PointType>
Chris@147 575 SparseModel<PointType>::EditCommand::EditCommand(SparseModel *model,
Chris@147 576 QString commandName) :
Chris@147 577 MacroCommand(commandName),
Chris@147 578 m_model(model)
Chris@147 579 {
Chris@147 580 }
Chris@147 581
Chris@147 582 template <typename PointType>
Chris@147 583 void
Chris@147 584 SparseModel<PointType>::EditCommand::addPoint(const PointType &point)
Chris@147 585 {
Chris@147 586 addCommand(new AddPointCommand(m_model, point), true);
Chris@147 587 }
Chris@147 588
Chris@147 589 template <typename PointType>
Chris@147 590 void
Chris@147 591 SparseModel<PointType>::EditCommand::deletePoint(const PointType &point)
Chris@147 592 {
Chris@147 593 addCommand(new DeletePointCommand(m_model, point), true);
Chris@147 594 }
Chris@147 595
Chris@147 596 template <typename PointType>
Chris@147 597 void
Chris@147 598 SparseModel<PointType>::EditCommand::finish()
Chris@147 599 {
Chris@147 600 if (!m_commands.empty()) {
Chris@147 601 CommandHistory::getInstance()->addCommand(this, false);
Chris@147 602 } else {
Chris@147 603 delete this;
Chris@147 604 }
Chris@147 605 }
Chris@147 606
Chris@147 607 template <typename PointType>
Chris@147 608 void
Chris@147 609 SparseModel<PointType>::EditCommand::addCommand(Command *command,
Chris@147 610 bool executeFirst)
Chris@147 611 {
Chris@147 612 if (executeFirst) command->execute();
Chris@147 613
Chris@147 614 if (!m_commands.empty()) {
Chris@147 615 DeletePointCommand *dpc = dynamic_cast<DeletePointCommand *>(command);
Chris@147 616 if (dpc) {
Chris@147 617 AddPointCommand *apc = dynamic_cast<AddPointCommand *>
Chris@147 618 (m_commands[m_commands.size() - 1]);
Chris@147 619 typename PointType::Comparator comparator;
Chris@147 620 if (apc) {
Chris@147 621 if (!comparator(apc->getPoint(), dpc->getPoint()) &&
Chris@147 622 !comparator(dpc->getPoint(), apc->getPoint())) {
Chris@147 623 deleteCommand(apc);
Chris@147 624 return;
Chris@147 625 }
Chris@147 626 }
Chris@147 627 }
Chris@147 628 }
Chris@147 629
Chris@147 630 MacroCommand::addCommand(command);
Chris@147 631 }
Chris@147 632
Chris@147 633
Chris@147 634 #endif
Chris@147 635
Chris@147 636
Chris@147 637