annotate data/model/SparseModel.h @ 392:183ee2a55fc7

* More work to abstract out interactive components used in the data library, so that it does not need to depend on QtGui.
author Chris Cannam
date Fri, 14 Mar 2008 17:14:21 +0000
parents a1b6d2e33cab
children 88ad01799040
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
Chris@147 22 #include <iostream>
Chris@147 23
Chris@147 24 #include <set>
Chris@147 25 #include <QMutex>
Chris@147 26 #include <QTextStream>
Chris@147 27
Chris@147 28
Chris@147 29 /**
Chris@147 30 * Model containing sparse data (points with some properties). The
Chris@147 31 * properties depend on the point type.
Chris@147 32 */
Chris@147 33
Chris@147 34 template <typename PointType>
Chris@147 35 class SparseModel : public Model
Chris@147 36 {
Chris@147 37 public:
Chris@147 38 SparseModel(size_t sampleRate, size_t resolution,
Chris@147 39 bool notifyOnAdd = true);
Chris@147 40 virtual ~SparseModel() { }
Chris@147 41
Chris@147 42 virtual bool isOK() const { return true; }
Chris@147 43 virtual size_t getStartFrame() const;
Chris@147 44 virtual size_t getEndFrame() const;
Chris@147 45 virtual size_t getSampleRate() const { return m_sampleRate; }
Chris@147 46
Chris@147 47 virtual Model *clone() const;
Chris@147 48
Chris@147 49 // Number of frames of the underlying sample rate that this model
Chris@147 50 // is capable of resolving to. For example, if m_resolution == 10
Chris@147 51 // then every point in this model will be at a multiple of 10
Chris@147 52 // sample frames and should be considered to cover a window ending
Chris@147 53 // 10 sample frames later.
Chris@147 54 virtual size_t getResolution() const {
Chris@147 55 return m_resolution ? m_resolution : 1;
Chris@147 56 }
Chris@147 57 virtual void setResolution(size_t resolution);
Chris@147 58
Chris@147 59 typedef PointType Point;
Chris@147 60 typedef std::multiset<PointType,
Chris@147 61 typename PointType::OrderComparator> PointList;
Chris@147 62 typedef typename PointList::iterator PointListIterator;
Chris@147 63
Chris@147 64 /**
Chris@147 65 * Return whether the model is empty or not.
Chris@147 66 */
Chris@147 67 virtual bool isEmpty() const;
Chris@147 68
Chris@147 69 /**
Chris@147 70 * Get the total number of points in the model.
Chris@147 71 */
Chris@147 72 virtual size_t getPointCount() const;
Chris@147 73
Chris@147 74 /**
Chris@147 75 * Get all of the points in this model between the given
Chris@147 76 * boundaries (in frames), as well as up to two points before and
Chris@147 77 * after the boundaries. If you need exact boundaries, check the
Chris@147 78 * point coordinates in the returned list.
Chris@147 79 */
Chris@147 80 virtual PointList getPoints(long start, long end) const;
Chris@147 81
Chris@147 82 /**
Chris@147 83 * Get all points that cover the given frame number, taking the
Chris@147 84 * resolution of the model into account.
Chris@147 85 */
Chris@147 86 virtual PointList getPoints(long frame) const;
Chris@147 87
Chris@147 88 /**
Chris@297 89 * Get all points.
Chris@297 90 */
Chris@297 91 virtual const PointList &getPoints() const { return m_points; }
Chris@297 92
Chris@297 93 /**
Chris@147 94 * Return all points that share the nearest frame number prior to
Chris@147 95 * the given one at which there are any points.
Chris@147 96 */
Chris@147 97 virtual PointList getPreviousPoints(long frame) const;
Chris@147 98
Chris@147 99 /**
Chris@147 100 * Return all points that share the nearest frame number
Chris@147 101 * subsequent to the given one at which there are any points.
Chris@147 102 */
Chris@147 103 virtual PointList getNextPoints(long frame) const;
Chris@147 104
Chris@147 105 /**
Chris@147 106 * Remove all points.
Chris@147 107 */
Chris@147 108 virtual void clear();
Chris@147 109
Chris@147 110 /**
Chris@147 111 * Add a point.
Chris@147 112 */
Chris@147 113 virtual void addPoint(const PointType &point);
Chris@147 114
Chris@147 115 /**
Chris@147 116 * Remove a point. Points are not necessarily unique, so this
Chris@147 117 * function will remove the first point that compares equal to the
Chris@147 118 * supplied one using Point::Comparator. Other identical points
Chris@147 119 * may remain in the model.
Chris@147 120 */
Chris@147 121 virtual void deletePoint(const PointType &point);
Chris@147 122
Chris@297 123 virtual bool isReady(int *completion = 0) const {
Chris@297 124 bool ready = isOK() && (m_completion == 100);
Chris@297 125 if (completion) *completion = m_completion;
Chris@297 126 return ready;
Chris@297 127 }
Chris@297 128
Chris@333 129 virtual void setCompletion(int completion, bool update = true);
Chris@147 130 virtual int getCompletion() const { return m_completion; }
Chris@147 131
Chris@147 132 virtual bool hasTextLabels() const { return m_hasTextLabels; }
Chris@147 133
Chris@345 134 QString getTypeName() const { return tr("Sparse"); }
Chris@345 135
Chris@147 136 virtual void toXml(QTextStream &out,
Chris@147 137 QString indent = "",
Chris@147 138 QString extraAttributes = "") const;
Chris@147 139
Chris@147 140 virtual QString toDelimitedDataString(QString delimiter) const
Chris@147 141 {
Chris@147 142 QString s;
Chris@147 143 for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@147 144 s += i->toDelimitedDataString(delimiter, m_sampleRate) + "\n";
Chris@147 145 }
Chris@147 146 return s;
Chris@147 147 }
Chris@147 148
Chris@147 149 /**
Chris@147 150 * Command to add a point, with undo.
Chris@147 151 */
Chris@147 152 class AddPointCommand : public Command
Chris@147 153 {
Chris@147 154 public:
Chris@147 155 AddPointCommand(SparseModel<PointType> *model,
Chris@147 156 const PointType &point,
Chris@147 157 QString name = "") :
Chris@147 158 m_model(model), m_point(point), m_name(name) { }
Chris@147 159
Chris@147 160 virtual QString getName() const {
Chris@147 161 return (m_name == "" ? tr("Add Point") : m_name);
Chris@147 162 }
Chris@147 163
Chris@147 164 virtual void execute() { m_model->addPoint(m_point); }
Chris@147 165 virtual void unexecute() { m_model->deletePoint(m_point); }
Chris@147 166
Chris@147 167 const PointType &getPoint() const { return m_point; }
Chris@147 168
Chris@147 169 private:
Chris@147 170 SparseModel<PointType> *m_model;
Chris@147 171 PointType m_point;
Chris@147 172 QString m_name;
Chris@147 173 };
Chris@147 174
Chris@147 175
Chris@147 176 /**
Chris@147 177 * Command to remove a point, with undo.
Chris@147 178 */
Chris@147 179 class DeletePointCommand : public Command
Chris@147 180 {
Chris@147 181 public:
Chris@147 182 DeletePointCommand(SparseModel<PointType> *model,
Chris@147 183 const PointType &point) :
Chris@147 184 m_model(model), m_point(point) { }
Chris@147 185
Chris@147 186 virtual QString getName() const { return tr("Delete Point"); }
Chris@147 187
Chris@147 188 virtual void execute() { m_model->deletePoint(m_point); }
Chris@147 189 virtual void unexecute() { m_model->addPoint(m_point); }
Chris@147 190
Chris@147 191 const PointType &getPoint() const { return m_point; }
Chris@147 192
Chris@147 193 private:
Chris@147 194 SparseModel<PointType> *m_model;
Chris@147 195 PointType m_point;
Chris@147 196 };
Chris@147 197
Chris@147 198
Chris@147 199 /**
Chris@147 200 * Command to add or remove a series of points, with undo.
Chris@147 201 * Consecutive add/remove pairs for the same point are collapsed.
Chris@147 202 */
Chris@147 203 class EditCommand : public MacroCommand
Chris@147 204 {
Chris@147 205 public:
Chris@147 206 EditCommand(SparseModel<PointType> *model, QString commandName);
Chris@147 207
Chris@147 208 virtual void addPoint(const PointType &point);
Chris@147 209 virtual void deletePoint(const PointType &point);
Chris@147 210
Chris@147 211 /**
Chris@147 212 * Stack an arbitrary other command in the same sequence.
Chris@147 213 */
Chris@147 214 virtual void addCommand(Command *command) { addCommand(command, true); }
Chris@147 215
Chris@147 216 /**
Chris@387 217 * If any points have been added or deleted, return this
Chris@387 218 * command (so the caller can add it to the command history).
Chris@387 219 * Otherwise delete the command.
Chris@147 220 */
Chris@387 221 virtual Command *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@387 603 Command *
Chris@147 604 SparseModel<PointType>::EditCommand::finish()
Chris@147 605 {
Chris@147 606 if (!m_commands.empty()) {
Chris@387 607 return this;
Chris@147 608 } else {
Chris@147 609 delete this;
Chris@389 610 return 0;
Chris@147 611 }
Chris@147 612 }
Chris@147 613
Chris@147 614 template <typename PointType>
Chris@147 615 void
Chris@147 616 SparseModel<PointType>::EditCommand::addCommand(Command *command,
Chris@147 617 bool executeFirst)
Chris@147 618 {
Chris@147 619 if (executeFirst) command->execute();
Chris@147 620
Chris@147 621 if (!m_commands.empty()) {
Chris@147 622 DeletePointCommand *dpc = dynamic_cast<DeletePointCommand *>(command);
Chris@147 623 if (dpc) {
Chris@147 624 AddPointCommand *apc = dynamic_cast<AddPointCommand *>
Chris@147 625 (m_commands[m_commands.size() - 1]);
Chris@147 626 typename PointType::Comparator comparator;
Chris@147 627 if (apc) {
Chris@147 628 if (!comparator(apc->getPoint(), dpc->getPoint()) &&
Chris@147 629 !comparator(dpc->getPoint(), apc->getPoint())) {
Chris@147 630 deleteCommand(apc);
Chris@147 631 return;
Chris@147 632 }
Chris@147 633 }
Chris@147 634 }
Chris@147 635 }
Chris@147 636
Chris@147 637 MacroCommand::addCommand(command);
Chris@147 638 }
Chris@147 639
Chris@147 640
Chris@147 641 #endif
Chris@147 642
Chris@147 643
Chris@147 644