annotate data/model/SparseModel.h @ 184:5a916fee6d2d

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