Mercurial > hg > svgui
comparison layer/NoteLayer.cpp @ 30:ea6fe8cfcdd5
* Add the Note layer for pianoroll-type display of note-type data
* Complete the MIDI file importer (well, nearly -- it would be nice to
be able to import the non-note data as other sorts of models, and that's
not done yet).
* Minor refactoring in RealTime etc
| author | Chris Cannam |
|---|---|
| date | Fri, 10 Feb 2006 17:51:36 +0000 |
| parents | |
| children | 1bdf285c4eac |
comparison
equal
deleted
inserted
replaced
| 29:9f55af9676b4 | 30:ea6fe8cfcdd5 |
|---|---|
| 1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */ | |
| 2 | |
| 3 /* | |
| 4 A waveform viewer and audio annotation editor. | |
| 5 Chris Cannam, Queen Mary University of London, 2005-2006 | |
| 6 | |
| 7 This is experimental software. Not for distribution. | |
| 8 */ | |
| 9 | |
| 10 #include "NoteLayer.h" | |
| 11 | |
| 12 #include "base/Model.h" | |
| 13 #include "base/RealTime.h" | |
| 14 #include "base/Profiler.h" | |
| 15 #include "base/Pitch.h" | |
| 16 #include "base/View.h" | |
| 17 | |
| 18 #include "model/NoteModel.h" | |
| 19 | |
| 20 #include <QPainter> | |
| 21 #include <QPainterPath> | |
| 22 #include <QMouseEvent> | |
| 23 | |
| 24 #include <iostream> | |
| 25 #include <cmath> | |
| 26 | |
| 27 NoteLayer::NoteLayer(View *w) : | |
| 28 Layer(w), | |
| 29 m_model(0), | |
| 30 m_editing(false), | |
| 31 m_originalPoint(0, 0.0, 0, tr("New Point")), | |
| 32 m_editingPoint(0, 0.0, 0, tr("New Point")), | |
| 33 m_editingCommand(0), | |
| 34 m_colour(Qt::black), | |
| 35 m_verticalScale(MinMaxRangeScale) | |
| 36 { | |
| 37 m_view->addLayer(this); | |
| 38 } | |
| 39 | |
| 40 void | |
| 41 NoteLayer::setModel(NoteModel *model) | |
| 42 { | |
| 43 if (m_model == model) return; | |
| 44 m_model = model; | |
| 45 | |
| 46 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); | |
| 47 connect(m_model, SIGNAL(modelChanged(size_t, size_t)), | |
| 48 this, SIGNAL(modelChanged(size_t, size_t))); | |
| 49 | |
| 50 connect(m_model, SIGNAL(completionChanged()), | |
| 51 this, SIGNAL(modelCompletionChanged())); | |
| 52 | |
| 53 std::cerr << "NoteLayer::setModel(" << model << ")" << std::endl; | |
| 54 | |
| 55 emit modelReplaced(); | |
| 56 } | |
| 57 | |
| 58 Layer::PropertyList | |
| 59 NoteLayer::getProperties() const | |
| 60 { | |
| 61 PropertyList list; | |
| 62 list.push_back(tr("Colour")); | |
| 63 list.push_back(tr("Vertical Scale")); | |
| 64 return list; | |
| 65 } | |
| 66 | |
| 67 Layer::PropertyType | |
| 68 NoteLayer::getPropertyType(const PropertyName &) const | |
| 69 { | |
| 70 return ValueProperty; | |
| 71 } | |
| 72 | |
| 73 int | |
| 74 NoteLayer::getPropertyRangeAndValue(const PropertyName &name, | |
| 75 int *min, int *max) const | |
| 76 { | |
| 77 //!!! factor this colour handling stuff out into a colour manager class | |
| 78 | |
| 79 int deft = 0; | |
| 80 | |
| 81 if (name == tr("Colour")) { | |
| 82 | |
| 83 if (min) *min = 0; | |
| 84 if (max) *max = 5; | |
| 85 | |
| 86 if (m_colour == Qt::black) deft = 0; | |
| 87 else if (m_colour == Qt::darkRed) deft = 1; | |
| 88 else if (m_colour == Qt::darkBlue) deft = 2; | |
| 89 else if (m_colour == Qt::darkGreen) deft = 3; | |
| 90 else if (m_colour == QColor(200, 50, 255)) deft = 4; | |
| 91 else if (m_colour == QColor(255, 150, 50)) deft = 5; | |
| 92 | |
| 93 } else if (name == tr("Vertical Scale")) { | |
| 94 | |
| 95 if (min) *min = 0; | |
| 96 if (max) *max = 2; | |
| 97 | |
| 98 deft = int(m_verticalScale); | |
| 99 | |
| 100 } else { | |
| 101 | |
| 102 deft = Layer::getPropertyRangeAndValue(name, min, max); | |
| 103 } | |
| 104 | |
| 105 return deft; | |
| 106 } | |
| 107 | |
| 108 QString | |
| 109 NoteLayer::getPropertyValueLabel(const PropertyName &name, | |
| 110 int value) const | |
| 111 { | |
| 112 if (name == tr("Colour")) { | |
| 113 switch (value) { | |
| 114 default: | |
| 115 case 0: return tr("Black"); | |
| 116 case 1: return tr("Red"); | |
| 117 case 2: return tr("Blue"); | |
| 118 case 3: return tr("Green"); | |
| 119 case 4: return tr("Purple"); | |
| 120 case 5: return tr("Orange"); | |
| 121 } | |
| 122 } else if (name == tr("Vertical Scale")) { | |
| 123 switch (value) { | |
| 124 default: | |
| 125 case 0: return tr("Note Range In Use"); | |
| 126 case 1: return tr("MIDI Note Range"); | |
| 127 case 2: return tr("Frequency"); | |
| 128 } | |
| 129 } | |
| 130 return tr("<unknown>"); | |
| 131 } | |
| 132 | |
| 133 void | |
| 134 NoteLayer::setProperty(const PropertyName &name, int value) | |
| 135 { | |
| 136 if (name == tr("Colour")) { | |
| 137 switch (value) { | |
| 138 default: | |
| 139 case 0: setBaseColour(Qt::black); break; | |
| 140 case 1: setBaseColour(Qt::darkRed); break; | |
| 141 case 2: setBaseColour(Qt::darkBlue); break; | |
| 142 case 3: setBaseColour(Qt::darkGreen); break; | |
| 143 case 4: setBaseColour(QColor(200, 50, 255)); break; | |
| 144 case 5: setBaseColour(QColor(255, 150, 50)); break; | |
| 145 } | |
| 146 } else if (name == tr("Vertical Scale")) { | |
| 147 setVerticalScale(VerticalScale(value)); | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 void | |
| 152 NoteLayer::setBaseColour(QColor colour) | |
| 153 { | |
| 154 if (m_colour == colour) return; | |
| 155 m_colour = colour; | |
| 156 emit layerParametersChanged(); | |
| 157 } | |
| 158 | |
| 159 void | |
| 160 NoteLayer::setVerticalScale(VerticalScale scale) | |
| 161 { | |
| 162 if (m_verticalScale == scale) return; | |
| 163 m_verticalScale = scale; | |
| 164 emit layerParametersChanged(); | |
| 165 } | |
| 166 | |
| 167 bool | |
| 168 NoteLayer::isLayerScrollable() const | |
| 169 { | |
| 170 QPoint discard; | |
| 171 return !m_view->shouldIlluminateLocalFeatures(this, discard); | |
| 172 } | |
| 173 | |
| 174 NoteModel::PointList | |
| 175 NoteLayer::getLocalPoints(int x) const | |
| 176 { | |
| 177 if (!m_model) return NoteModel::PointList(); | |
| 178 | |
| 179 long frame = getFrameForX(x); | |
| 180 | |
| 181 NoteModel::PointList onPoints = | |
| 182 m_model->getPoints(frame); | |
| 183 | |
| 184 if (!onPoints.empty()) { | |
| 185 return onPoints; | |
| 186 } | |
| 187 | |
| 188 NoteModel::PointList prevPoints = | |
| 189 m_model->getPreviousPoints(frame); | |
| 190 NoteModel::PointList nextPoints = | |
| 191 m_model->getNextPoints(frame); | |
| 192 | |
| 193 NoteModel::PointList usePoints = prevPoints; | |
| 194 | |
| 195 if (prevPoints.empty()) { | |
| 196 usePoints = nextPoints; | |
| 197 } else if (prevPoints.begin()->frame < m_view->getStartFrame() && | |
| 198 !(nextPoints.begin()->frame > m_view->getEndFrame())) { | |
| 199 usePoints = nextPoints; | |
| 200 } else if (nextPoints.begin()->frame - frame < | |
| 201 frame - prevPoints.begin()->frame) { | |
| 202 usePoints = nextPoints; | |
| 203 } | |
| 204 | |
| 205 if (!usePoints.empty()) { | |
| 206 int fuzz = 2; | |
| 207 int px = getXForFrame(usePoints.begin()->frame); | |
| 208 if ((px > x && px - x > fuzz) || | |
| 209 (px < x && x - px > fuzz + 1)) { | |
| 210 usePoints.clear(); | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 return usePoints; | |
| 215 } | |
| 216 | |
| 217 QString | |
| 218 NoteLayer::getFeatureDescription(QPoint &pos) const | |
| 219 { | |
| 220 int x = pos.x(); | |
| 221 | |
| 222 if (!m_model || !m_model->getSampleRate()) return ""; | |
| 223 | |
| 224 NoteModel::PointList points = getLocalPoints(x); | |
| 225 | |
| 226 if (points.empty()) { | |
| 227 if (!m_model->isReady()) { | |
| 228 return tr("In progress"); | |
| 229 } else { | |
| 230 return tr("No local points"); | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 Note note(0); | |
| 235 NoteModel::PointList::iterator i; | |
| 236 | |
| 237 for (i = points.begin(); i != points.end(); ++i) { | |
| 238 | |
| 239 int y = getYForValue(i->value); | |
| 240 int h = 3; | |
| 241 | |
| 242 if (m_model->getValueQuantization() != 0.0) { | |
| 243 h = y - getYForValue(i->value + m_model->getValueQuantization()); | |
| 244 if (h < 3) h = 3; | |
| 245 } | |
| 246 | |
| 247 if (pos.y() >= y - h && pos.y() <= y) { | |
| 248 note = *i; | |
| 249 break; | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 if (i == points.end()) return tr("No local points"); | |
| 254 | |
| 255 RealTime rt = RealTime::frame2RealTime(note.frame, | |
| 256 m_model->getSampleRate()); | |
| 257 RealTime rd = RealTime::frame2RealTime(note.duration, | |
| 258 m_model->getSampleRate()); | |
| 259 | |
| 260 QString text; | |
| 261 | |
| 262 if (note.label == "") { | |
| 263 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label")) | |
| 264 .arg(rt.toText(true).c_str()) | |
| 265 .arg(note.value) | |
| 266 .arg(rd.toText(true).c_str()); | |
| 267 } else { | |
| 268 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4")) | |
| 269 .arg(rt.toText(true).c_str()) | |
| 270 .arg(note.value) | |
| 271 .arg(rd.toText(true).c_str()) | |
| 272 .arg(note.label); | |
| 273 } | |
| 274 | |
| 275 pos = QPoint(getXForFrame(note.frame), | |
| 276 getYForValue(note.value)); | |
| 277 return text; | |
| 278 } | |
| 279 | |
| 280 bool | |
| 281 NoteLayer::snapToFeatureFrame(int &frame, | |
| 282 size_t &resolution, | |
| 283 SnapType snap) const | |
| 284 { | |
| 285 if (!m_model) { | |
| 286 return Layer::snapToFeatureFrame(frame, resolution, snap); | |
| 287 } | |
| 288 | |
| 289 resolution = m_model->getResolution(); | |
| 290 NoteModel::PointList points; | |
| 291 | |
| 292 if (snap == SnapNeighbouring) { | |
| 293 | |
| 294 points = getLocalPoints(getXForFrame(frame)); | |
| 295 if (points.empty()) return false; | |
| 296 frame = points.begin()->frame; | |
| 297 return true; | |
| 298 } | |
| 299 | |
| 300 points = m_model->getPoints(frame, frame); | |
| 301 int snapped = frame; | |
| 302 bool found = false; | |
| 303 | |
| 304 for (NoteModel::PointList::const_iterator i = points.begin(); | |
| 305 i != points.end(); ++i) { | |
| 306 | |
| 307 if (snap == SnapRight) { | |
| 308 | |
| 309 if (i->frame > frame) { | |
| 310 snapped = i->frame; | |
| 311 found = true; | |
| 312 break; | |
| 313 } | |
| 314 | |
| 315 } else if (snap == SnapLeft) { | |
| 316 | |
| 317 if (i->frame <= frame) { | |
| 318 snapped = i->frame; | |
| 319 found = true; // don't break, as the next may be better | |
| 320 } else { | |
| 321 break; | |
| 322 } | |
| 323 | |
| 324 } else { // nearest | |
| 325 | |
| 326 NoteModel::PointList::const_iterator j = i; | |
| 327 ++j; | |
| 328 | |
| 329 if (j == points.end()) { | |
| 330 | |
| 331 snapped = i->frame; | |
| 332 found = true; | |
| 333 break; | |
| 334 | |
| 335 } else if (j->frame >= frame) { | |
| 336 | |
| 337 if (j->frame - frame < frame - i->frame) { | |
| 338 snapped = j->frame; | |
| 339 } else { | |
| 340 snapped = i->frame; | |
| 341 } | |
| 342 found = true; | |
| 343 break; | |
| 344 } | |
| 345 } | |
| 346 } | |
| 347 | |
| 348 frame = snapped; | |
| 349 return found; | |
| 350 } | |
| 351 | |
| 352 int | |
| 353 NoteLayer::getYForValue(float value) const | |
| 354 { | |
| 355 float min, max, h = m_view->height(); | |
| 356 | |
| 357 switch (m_verticalScale) { | |
| 358 | |
| 359 case MIDIRangeScale: | |
| 360 min = 0.0; | |
| 361 max = 127.0; | |
| 362 break; | |
| 363 | |
| 364 case MinMaxRangeScale: | |
| 365 min = m_model->getValueMinimum(); | |
| 366 max = m_model->getValueMaximum(); | |
| 367 break; | |
| 368 | |
| 369 case FrequencyScale: | |
| 370 std::cerr << "FrequencyScale: value in = " << value << std::endl; | |
| 371 min = m_model->getValueMinimum(); | |
| 372 min = Pitch::getFrequencyForPitch(lrintf(min), min - lrintf(min)); | |
| 373 max = m_model->getValueMaximum(); | |
| 374 max = Pitch::getFrequencyForPitch(lrintf(max), max - lrintf(max)); | |
| 375 value = Pitch::getFrequencyForPitch(lrintf(value), value - lrintf(value)); | |
| 376 std::cerr << "FrequencyScale: min = " << min << ", max = " << max << ", value = " << value << std::endl; | |
| 377 break; | |
| 378 } | |
| 379 | |
| 380 if (max < min) max = min; | |
| 381 max = max + 1.0; | |
| 382 | |
| 383 return int(h - ((value - min) * h) / (max - min)) - 1; | |
| 384 } | |
| 385 | |
| 386 float | |
| 387 NoteLayer::getValueForY(int y) const | |
| 388 { | |
| 389 float min = m_model->getValueMinimum(); | |
| 390 float max = m_model->getValueMaximum(); | |
| 391 if (max == min) max = min + 1.0; | |
| 392 | |
| 393 int h = m_view->height(); | |
| 394 | |
| 395 return min + (float(h - y) * float(max - min)) / h; | |
| 396 } | |
| 397 | |
| 398 void | |
| 399 NoteLayer::paint(QPainter &paint, QRect rect) const | |
| 400 { | |
| 401 if (!m_model || !m_model->isOK()) return; | |
| 402 | |
| 403 int sampleRate = m_model->getSampleRate(); | |
| 404 if (!sampleRate) return; | |
| 405 | |
| 406 // Profiler profiler("NoteLayer::paint", true); | |
| 407 | |
| 408 int x0 = rect.left(), x1 = rect.right(); | |
| 409 long frame0 = getFrameForX(x0); | |
| 410 long frame1 = getFrameForX(x1); | |
| 411 | |
| 412 NoteModel::PointList points(m_model->getPoints(frame0, frame1)); | |
| 413 if (points.empty()) return; | |
| 414 | |
| 415 paint.setPen(m_colour); | |
| 416 | |
| 417 QColor brushColour(m_colour); | |
| 418 brushColour.setAlpha(80); | |
| 419 | |
| 420 // std::cerr << "NoteLayer::paint: resolution is " | |
| 421 // << m_model->getResolution() << " frames" << std::endl; | |
| 422 | |
| 423 float min = m_model->getValueMinimum(); | |
| 424 float max = m_model->getValueMaximum(); | |
| 425 if (max == min) max = min + 1.0; | |
| 426 | |
| 427 int origin = int(nearbyint(m_view->height() - | |
| 428 (-min * m_view->height()) / (max - min))); | |
| 429 | |
| 430 QPoint localPos; | |
| 431 long illuminateFrame = -1; | |
| 432 | |
| 433 if (m_view->shouldIlluminateLocalFeatures(this, localPos)) { | |
| 434 NoteModel::PointList localPoints = | |
| 435 getLocalPoints(localPos.x()); | |
| 436 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame; | |
| 437 } | |
| 438 | |
| 439 paint.save(); | |
| 440 paint.setRenderHint(QPainter::Antialiasing, false); | |
| 441 | |
| 442 for (NoteModel::PointList::const_iterator i = points.begin(); | |
| 443 i != points.end(); ++i) { | |
| 444 | |
| 445 const NoteModel::Point &p(*i); | |
| 446 | |
| 447 int x = getXForFrame(p.frame); | |
| 448 int y = getYForValue(p.value); | |
| 449 int w = getXForFrame(p.frame + p.duration) - x; | |
| 450 int h = 3; | |
| 451 | |
| 452 if (m_model->getValueQuantization() != 0.0) { | |
| 453 h = y - getYForValue(p.value + m_model->getValueQuantization()); | |
| 454 if (h < 3) h = 3; | |
| 455 } | |
| 456 | |
| 457 if (w < 1) w = 1; | |
| 458 paint.setPen(m_colour); | |
| 459 paint.setBrush(brushColour); | |
| 460 | |
| 461 if (illuminateFrame == p.frame) { | |
| 462 if (localPos.y() >= y - h && localPos.y() < y) { | |
| 463 paint.setPen(Qt::black);//!!! | |
| 464 paint.setBrush(Qt::black);//!!! | |
| 465 } | |
| 466 } | |
| 467 | |
| 468 paint.drawRect(x, y - h, w, h); | |
| 469 | |
| 470 /// if (p.label != "") { | |
| 471 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label); | |
| 472 /// } | |
| 473 } | |
| 474 | |
| 475 paint.restore(); | |
| 476 } | |
| 477 | |
| 478 void | |
| 479 NoteLayer::drawStart(QMouseEvent *e) | |
| 480 { | |
| 481 std::cerr << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl; | |
| 482 | |
| 483 if (!m_model) return; | |
| 484 | |
| 485 long frame = getFrameForX(e->x()); | |
| 486 if (frame < 0) frame = 0; | |
| 487 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
| 488 | |
| 489 float value = getValueForY(e->y()); | |
| 490 | |
| 491 m_editingPoint = NoteModel::Point(frame, value, 0, tr("New Point")); | |
| 492 m_originalPoint = m_editingPoint; | |
| 493 | |
| 494 if (m_editingCommand) m_editingCommand->finish(); | |
| 495 m_editingCommand = new NoteModel::EditCommand(m_model, | |
| 496 tr("Draw Point")); | |
| 497 m_editingCommand->addPoint(m_editingPoint); | |
| 498 | |
| 499 m_editing = true; | |
| 500 } | |
| 501 | |
| 502 void | |
| 503 NoteLayer::drawDrag(QMouseEvent *e) | |
| 504 { | |
| 505 std::cerr << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl; | |
| 506 | |
| 507 if (!m_model || !m_editing) return; | |
| 508 | |
| 509 long frame = getFrameForX(e->x()); | |
| 510 if (frame < 0) frame = 0; | |
| 511 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
| 512 | |
| 513 float value = getValueForY(e->y()); | |
| 514 | |
| 515 m_editingCommand->deletePoint(m_editingPoint); | |
| 516 m_editingPoint.frame = frame; | |
| 517 m_editingPoint.value = value; | |
| 518 m_editingCommand->addPoint(m_editingPoint); | |
| 519 } | |
| 520 | |
| 521 void | |
| 522 NoteLayer::drawEnd(QMouseEvent *e) | |
| 523 { | |
| 524 std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl; | |
| 525 if (!m_model || !m_editing) return; | |
| 526 m_editingCommand->finish(); | |
| 527 m_editingCommand = 0; | |
| 528 m_editing = false; | |
| 529 } | |
| 530 | |
| 531 void | |
| 532 NoteLayer::editStart(QMouseEvent *e) | |
| 533 { | |
| 534 std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; | |
| 535 | |
| 536 if (!m_model) return; | |
| 537 | |
| 538 NoteModel::PointList points = getLocalPoints(e->x()); | |
| 539 if (points.empty()) return; | |
| 540 | |
| 541 m_editingPoint = *points.begin(); | |
| 542 m_originalPoint = m_editingPoint; | |
| 543 | |
| 544 if (m_editingCommand) { | |
| 545 m_editingCommand->finish(); | |
| 546 m_editingCommand = 0; | |
| 547 } | |
| 548 | |
| 549 m_editing = true; | |
| 550 } | |
| 551 | |
| 552 void | |
| 553 NoteLayer::editDrag(QMouseEvent *e) | |
| 554 { | |
| 555 std::cerr << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl; | |
| 556 | |
| 557 if (!m_model || !m_editing) return; | |
| 558 | |
| 559 long frame = getFrameForX(e->x()); | |
| 560 if (frame < 0) frame = 0; | |
| 561 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
| 562 | |
| 563 float value = getValueForY(e->y()); | |
| 564 | |
| 565 if (!m_editingCommand) { | |
| 566 m_editingCommand = new NoteModel::EditCommand(m_model, | |
| 567 tr("Drag Point")); | |
| 568 } | |
| 569 | |
| 570 m_editingCommand->deletePoint(m_editingPoint); | |
| 571 m_editingPoint.frame = frame; | |
| 572 m_editingPoint.value = value; | |
| 573 m_editingCommand->addPoint(m_editingPoint); | |
| 574 } | |
| 575 | |
| 576 void | |
| 577 NoteLayer::editEnd(QMouseEvent *e) | |
| 578 { | |
| 579 std::cerr << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl; | |
| 580 if (!m_model || !m_editing) return; | |
| 581 | |
| 582 if (m_editingCommand) { | |
| 583 | |
| 584 QString newName = m_editingCommand->getName(); | |
| 585 | |
| 586 if (m_editingPoint.frame != m_originalPoint.frame) { | |
| 587 if (m_editingPoint.value != m_originalPoint.value) { | |
| 588 newName = tr("Edit Point"); | |
| 589 } else { | |
| 590 newName = tr("Relocate Point"); | |
| 591 } | |
| 592 } else { | |
| 593 newName = tr("Change Point Value"); | |
| 594 } | |
| 595 | |
| 596 m_editingCommand->setName(newName); | |
| 597 m_editingCommand->finish(); | |
| 598 } | |
| 599 | |
| 600 m_editingCommand = 0; | |
| 601 m_editing = false; | |
| 602 } | |
| 603 | |
| 604 QString | |
| 605 NoteLayer::toXmlString(QString indent, QString extraAttributes) const | |
| 606 { | |
| 607 return Layer::toXmlString(indent, extraAttributes + | |
| 608 QString(" colour=\"%1\" verticalScale=\"%2\"") | |
| 609 .arg(encodeColour(m_colour)).arg(m_verticalScale)); | |
| 610 } | |
| 611 | |
| 612 void | |
| 613 NoteLayer::setProperties(const QXmlAttributes &attributes) | |
| 614 { | |
| 615 QString colourSpec = attributes.value("colour"); | |
| 616 if (colourSpec != "") { | |
| 617 QColor colour(colourSpec); | |
| 618 if (colour.isValid()) { | |
| 619 setBaseColour(QColor(colourSpec)); | |
| 620 } | |
| 621 } | |
| 622 | |
| 623 bool ok; | |
| 624 VerticalScale scale = (VerticalScale) | |
| 625 attributes.value("verticalScale").toInt(&ok); | |
| 626 if (ok) setVerticalScale(scale); | |
| 627 } | |
| 628 | |
| 629 |
