annotate layer/NoteLayer.cpp @ 77:fd348f36c0d3

* Implement harmonic cursor in spectrogram * Implement layer export. This doesn't quite do the right thing for the SV XML layer export yet -- it doesn't include layer display information, so when imported, it only creates an invisible model. Could also do with fixing CSV file import so as to work correctly for note and text layers.
author Chris Cannam
date Mon, 10 Apr 2006 17:22:59 +0000
parents 45ba0b381c5d
children 19bf27e4fb29
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@30 2
Chris@30 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@59 7 This file copyright 2006 Chris Cannam.
Chris@30 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@30 14 */
Chris@30 15
Chris@30 16 #include "NoteLayer.h"
Chris@30 17
Chris@30 18 #include "base/Model.h"
Chris@30 19 #include "base/RealTime.h"
Chris@30 20 #include "base/Profiler.h"
Chris@30 21 #include "base/Pitch.h"
Chris@30 22 #include "base/View.h"
Chris@30 23
Chris@30 24 #include "model/NoteModel.h"
Chris@30 25
Chris@70 26 #include "widgets/ItemEditDialog.h"
Chris@70 27
Chris@42 28 #include "SpectrogramLayer.h" // for optional frequency alignment
Chris@42 29
Chris@30 30 #include <QPainter>
Chris@30 31 #include <QPainterPath>
Chris@30 32 #include <QMouseEvent>
Chris@30 33
Chris@30 34 #include <iostream>
Chris@30 35 #include <cmath>
Chris@30 36
Chris@44 37 NoteLayer::NoteLayer() :
Chris@44 38 Layer(),
Chris@30 39 m_model(0),
Chris@30 40 m_editing(false),
Chris@30 41 m_originalPoint(0, 0.0, 0, tr("New Point")),
Chris@30 42 m_editingPoint(0, 0.0, 0, tr("New Point")),
Chris@30 43 m_editingCommand(0),
Chris@30 44 m_colour(Qt::black),
Chris@30 45 m_verticalScale(MinMaxRangeScale)
Chris@30 46 {
Chris@44 47
Chris@30 48 }
Chris@30 49
Chris@30 50 void
Chris@30 51 NoteLayer::setModel(NoteModel *model)
Chris@30 52 {
Chris@30 53 if (m_model == model) return;
Chris@30 54 m_model = model;
Chris@30 55
Chris@30 56 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@30 57 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@30 58 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@30 59
Chris@30 60 connect(m_model, SIGNAL(completionChanged()),
Chris@30 61 this, SIGNAL(modelCompletionChanged()));
Chris@30 62
Chris@30 63 std::cerr << "NoteLayer::setModel(" << model << ")" << std::endl;
Chris@30 64
Chris@30 65 emit modelReplaced();
Chris@30 66 }
Chris@30 67
Chris@30 68 Layer::PropertyList
Chris@30 69 NoteLayer::getProperties() const
Chris@30 70 {
Chris@30 71 PropertyList list;
Chris@30 72 list.push_back(tr("Colour"));
Chris@30 73 list.push_back(tr("Vertical Scale"));
Chris@30 74 return list;
Chris@30 75 }
Chris@30 76
Chris@30 77 Layer::PropertyType
Chris@30 78 NoteLayer::getPropertyType(const PropertyName &) const
Chris@30 79 {
Chris@30 80 return ValueProperty;
Chris@30 81 }
Chris@30 82
Chris@30 83 int
Chris@30 84 NoteLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@30 85 int *min, int *max) const
Chris@30 86 {
Chris@30 87 //!!! factor this colour handling stuff out into a colour manager class
Chris@30 88
Chris@30 89 int deft = 0;
Chris@30 90
Chris@30 91 if (name == tr("Colour")) {
Chris@30 92
Chris@30 93 if (min) *min = 0;
Chris@30 94 if (max) *max = 5;
Chris@30 95
Chris@30 96 if (m_colour == Qt::black) deft = 0;
Chris@30 97 else if (m_colour == Qt::darkRed) deft = 1;
Chris@30 98 else if (m_colour == Qt::darkBlue) deft = 2;
Chris@30 99 else if (m_colour == Qt::darkGreen) deft = 3;
Chris@30 100 else if (m_colour == QColor(200, 50, 255)) deft = 4;
Chris@30 101 else if (m_colour == QColor(255, 150, 50)) deft = 5;
Chris@30 102
Chris@30 103 } else if (name == tr("Vertical Scale")) {
Chris@30 104
Chris@30 105 if (min) *min = 0;
Chris@30 106 if (max) *max = 2;
Chris@30 107
Chris@30 108 deft = int(m_verticalScale);
Chris@30 109
Chris@30 110 } else {
Chris@30 111
Chris@30 112 deft = Layer::getPropertyRangeAndValue(name, min, max);
Chris@30 113 }
Chris@30 114
Chris@30 115 return deft;
Chris@30 116 }
Chris@30 117
Chris@30 118 QString
Chris@30 119 NoteLayer::getPropertyValueLabel(const PropertyName &name,
Chris@30 120 int value) const
Chris@30 121 {
Chris@30 122 if (name == tr("Colour")) {
Chris@30 123 switch (value) {
Chris@30 124 default:
Chris@30 125 case 0: return tr("Black");
Chris@30 126 case 1: return tr("Red");
Chris@30 127 case 2: return tr("Blue");
Chris@30 128 case 3: return tr("Green");
Chris@30 129 case 4: return tr("Purple");
Chris@30 130 case 5: return tr("Orange");
Chris@30 131 }
Chris@30 132 } else if (name == tr("Vertical Scale")) {
Chris@30 133 switch (value) {
Chris@30 134 default:
Chris@30 135 case 0: return tr("Note Range In Use");
Chris@30 136 case 1: return tr("MIDI Note Range");
Chris@30 137 case 2: return tr("Frequency");
Chris@30 138 }
Chris@30 139 }
Chris@30 140 return tr("<unknown>");
Chris@30 141 }
Chris@30 142
Chris@30 143 void
Chris@30 144 NoteLayer::setProperty(const PropertyName &name, int value)
Chris@30 145 {
Chris@30 146 if (name == tr("Colour")) {
Chris@30 147 switch (value) {
Chris@30 148 default:
Chris@30 149 case 0: setBaseColour(Qt::black); break;
Chris@30 150 case 1: setBaseColour(Qt::darkRed); break;
Chris@30 151 case 2: setBaseColour(Qt::darkBlue); break;
Chris@30 152 case 3: setBaseColour(Qt::darkGreen); break;
Chris@30 153 case 4: setBaseColour(QColor(200, 50, 255)); break;
Chris@30 154 case 5: setBaseColour(QColor(255, 150, 50)); break;
Chris@30 155 }
Chris@30 156 } else if (name == tr("Vertical Scale")) {
Chris@30 157 setVerticalScale(VerticalScale(value));
Chris@30 158 }
Chris@30 159 }
Chris@30 160
Chris@30 161 void
Chris@30 162 NoteLayer::setBaseColour(QColor colour)
Chris@30 163 {
Chris@30 164 if (m_colour == colour) return;
Chris@30 165 m_colour = colour;
Chris@30 166 emit layerParametersChanged();
Chris@30 167 }
Chris@30 168
Chris@30 169 void
Chris@30 170 NoteLayer::setVerticalScale(VerticalScale scale)
Chris@30 171 {
Chris@30 172 if (m_verticalScale == scale) return;
Chris@30 173 m_verticalScale = scale;
Chris@30 174 emit layerParametersChanged();
Chris@30 175 }
Chris@30 176
Chris@30 177 bool
Chris@44 178 NoteLayer::isLayerScrollable(const View *v) const
Chris@30 179 {
Chris@30 180 QPoint discard;
Chris@44 181 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@30 182 }
Chris@30 183
Chris@30 184 NoteModel::PointList
Chris@44 185 NoteLayer::getLocalPoints(View *v, int x) const
Chris@30 186 {
Chris@30 187 if (!m_model) return NoteModel::PointList();
Chris@30 188
Chris@44 189 long frame = v->getFrameForX(x);
Chris@30 190
Chris@30 191 NoteModel::PointList onPoints =
Chris@30 192 m_model->getPoints(frame);
Chris@30 193
Chris@30 194 if (!onPoints.empty()) {
Chris@30 195 return onPoints;
Chris@30 196 }
Chris@30 197
Chris@30 198 NoteModel::PointList prevPoints =
Chris@30 199 m_model->getPreviousPoints(frame);
Chris@30 200 NoteModel::PointList nextPoints =
Chris@30 201 m_model->getNextPoints(frame);
Chris@30 202
Chris@30 203 NoteModel::PointList usePoints = prevPoints;
Chris@30 204
Chris@30 205 if (prevPoints.empty()) {
Chris@30 206 usePoints = nextPoints;
Chris@44 207 } else if (prevPoints.begin()->frame < v->getStartFrame() &&
Chris@44 208 !(nextPoints.begin()->frame > v->getEndFrame())) {
Chris@30 209 usePoints = nextPoints;
Chris@30 210 } else if (nextPoints.begin()->frame - frame <
Chris@30 211 frame - prevPoints.begin()->frame) {
Chris@30 212 usePoints = nextPoints;
Chris@30 213 }
Chris@30 214
Chris@30 215 if (!usePoints.empty()) {
Chris@30 216 int fuzz = 2;
Chris@44 217 int px = v->getXForFrame(usePoints.begin()->frame);
Chris@30 218 if ((px > x && px - x > fuzz) ||
Chris@30 219 (px < x && x - px > fuzz + 1)) {
Chris@30 220 usePoints.clear();
Chris@30 221 }
Chris@30 222 }
Chris@30 223
Chris@30 224 return usePoints;
Chris@30 225 }
Chris@30 226
Chris@30 227 QString
Chris@44 228 NoteLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@30 229 {
Chris@30 230 int x = pos.x();
Chris@30 231
Chris@30 232 if (!m_model || !m_model->getSampleRate()) return "";
Chris@30 233
Chris@44 234 NoteModel::PointList points = getLocalPoints(v, x);
Chris@30 235
Chris@30 236 if (points.empty()) {
Chris@30 237 if (!m_model->isReady()) {
Chris@30 238 return tr("In progress");
Chris@30 239 } else {
Chris@30 240 return tr("No local points");
Chris@30 241 }
Chris@30 242 }
Chris@30 243
Chris@30 244 Note note(0);
Chris@30 245 NoteModel::PointList::iterator i;
Chris@30 246
Chris@30 247 for (i = points.begin(); i != points.end(); ++i) {
Chris@30 248
Chris@44 249 int y = getYForValue(v, i->value);
Chris@30 250 int h = 3;
Chris@30 251
Chris@30 252 if (m_model->getValueQuantization() != 0.0) {
Chris@44 253 h = y - getYForValue(v, i->value + m_model->getValueQuantization());
Chris@30 254 if (h < 3) h = 3;
Chris@30 255 }
Chris@30 256
Chris@30 257 if (pos.y() >= y - h && pos.y() <= y) {
Chris@30 258 note = *i;
Chris@30 259 break;
Chris@30 260 }
Chris@30 261 }
Chris@30 262
Chris@30 263 if (i == points.end()) return tr("No local points");
Chris@30 264
Chris@30 265 RealTime rt = RealTime::frame2RealTime(note.frame,
Chris@30 266 m_model->getSampleRate());
Chris@30 267 RealTime rd = RealTime::frame2RealTime(note.duration,
Chris@30 268 m_model->getSampleRate());
Chris@30 269
Chris@30 270 QString text;
Chris@30 271
Chris@30 272 if (note.label == "") {
Chris@30 273 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label"))
Chris@30 274 .arg(rt.toText(true).c_str())
Chris@30 275 .arg(note.value)
Chris@30 276 .arg(rd.toText(true).c_str());
Chris@30 277 } else {
Chris@30 278 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4"))
Chris@30 279 .arg(rt.toText(true).c_str())
Chris@30 280 .arg(note.value)
Chris@30 281 .arg(rd.toText(true).c_str())
Chris@30 282 .arg(note.label);
Chris@30 283 }
Chris@30 284
Chris@44 285 pos = QPoint(v->getXForFrame(note.frame),
Chris@44 286 getYForValue(v, note.value));
Chris@30 287 return text;
Chris@30 288 }
Chris@30 289
Chris@30 290 bool
Chris@44 291 NoteLayer::snapToFeatureFrame(View *v, int &frame,
Chris@44 292 size_t &resolution,
Chris@44 293 SnapType snap) const
Chris@30 294 {
Chris@30 295 if (!m_model) {
Chris@44 296 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@30 297 }
Chris@30 298
Chris@30 299 resolution = m_model->getResolution();
Chris@30 300 NoteModel::PointList points;
Chris@30 301
Chris@30 302 if (snap == SnapNeighbouring) {
Chris@30 303
Chris@44 304 points = getLocalPoints(v, v->getXForFrame(frame));
Chris@30 305 if (points.empty()) return false;
Chris@30 306 frame = points.begin()->frame;
Chris@30 307 return true;
Chris@30 308 }
Chris@30 309
Chris@30 310 points = m_model->getPoints(frame, frame);
Chris@30 311 int snapped = frame;
Chris@30 312 bool found = false;
Chris@30 313
Chris@30 314 for (NoteModel::PointList::const_iterator i = points.begin();
Chris@30 315 i != points.end(); ++i) {
Chris@30 316
Chris@30 317 if (snap == SnapRight) {
Chris@30 318
Chris@30 319 if (i->frame > frame) {
Chris@30 320 snapped = i->frame;
Chris@30 321 found = true;
Chris@30 322 break;
Chris@30 323 }
Chris@30 324
Chris@30 325 } else if (snap == SnapLeft) {
Chris@30 326
Chris@30 327 if (i->frame <= frame) {
Chris@30 328 snapped = i->frame;
Chris@30 329 found = true; // don't break, as the next may be better
Chris@30 330 } else {
Chris@30 331 break;
Chris@30 332 }
Chris@30 333
Chris@30 334 } else { // nearest
Chris@30 335
Chris@30 336 NoteModel::PointList::const_iterator j = i;
Chris@30 337 ++j;
Chris@30 338
Chris@30 339 if (j == points.end()) {
Chris@30 340
Chris@30 341 snapped = i->frame;
Chris@30 342 found = true;
Chris@30 343 break;
Chris@30 344
Chris@30 345 } else if (j->frame >= frame) {
Chris@30 346
Chris@30 347 if (j->frame - frame < frame - i->frame) {
Chris@30 348 snapped = j->frame;
Chris@30 349 } else {
Chris@30 350 snapped = i->frame;
Chris@30 351 }
Chris@30 352 found = true;
Chris@30 353 break;
Chris@30 354 }
Chris@30 355 }
Chris@30 356 }
Chris@30 357
Chris@30 358 frame = snapped;
Chris@30 359 return found;
Chris@30 360 }
Chris@30 361
Chris@30 362 int
Chris@44 363 NoteLayer::getYForValue(View *v, float value) const
Chris@30 364 {
Chris@44 365 float min, max, h = v->height();
Chris@42 366
Chris@30 367 switch (m_verticalScale) {
Chris@30 368
Chris@30 369 case MIDIRangeScale:
Chris@30 370 min = 0.0;
Chris@30 371 max = 127.0;
Chris@30 372 break;
Chris@30 373
Chris@30 374 case MinMaxRangeScale:
Chris@30 375 min = m_model->getValueMinimum();
Chris@30 376 max = m_model->getValueMaximum();
Chris@30 377 break;
Chris@30 378
Chris@30 379 case FrequencyScale:
Chris@42 380
Chris@42 381 value = Pitch::getFrequencyForPitch(lrintf(value),
Chris@42 382 value - lrintf(value));
Chris@42 383
Chris@42 384 // If we have a spectrogram layer on the same view as us, align
Chris@42 385 // ourselves with it...
Chris@44 386 for (int i = 0; i < v->getLayerCount(); ++i) {
Chris@42 387 SpectrogramLayer *spectrogram = dynamic_cast<SpectrogramLayer *>
Chris@44 388 (v->getLayer(i));
Chris@42 389 if (spectrogram) {
Chris@44 390 return spectrogram->getYForFrequency(v, value);
Chris@42 391 }
Chris@42 392 }
Chris@42 393
Chris@42 394 // ...otherwise just interpolate
Chris@30 395 std::cerr << "FrequencyScale: value in = " << value << std::endl;
Chris@30 396 min = m_model->getValueMinimum();
Chris@30 397 min = Pitch::getFrequencyForPitch(lrintf(min), min - lrintf(min));
Chris@30 398 max = m_model->getValueMaximum();
Chris@30 399 max = Pitch::getFrequencyForPitch(lrintf(max), max - lrintf(max));
Chris@30 400 std::cerr << "FrequencyScale: min = " << min << ", max = " << max << ", value = " << value << std::endl;
Chris@30 401 break;
Chris@30 402 }
Chris@30 403
Chris@30 404 if (max < min) max = min;
Chris@30 405 max = max + 1.0;
Chris@30 406
Chris@30 407 return int(h - ((value - min) * h) / (max - min)) - 1;
Chris@30 408 }
Chris@30 409
Chris@30 410 float
Chris@44 411 NoteLayer::getValueForY(View *v, int y) const
Chris@30 412 {
Chris@66 413 //!!!
Chris@66 414
Chris@30 415 float min = m_model->getValueMinimum();
Chris@30 416 float max = m_model->getValueMaximum();
Chris@30 417 if (max == min) max = min + 1.0;
Chris@30 418
Chris@44 419 int h = v->height();
Chris@30 420
Chris@30 421 return min + (float(h - y) * float(max - min)) / h;
Chris@30 422 }
Chris@30 423
Chris@30 424 void
Chris@44 425 NoteLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@30 426 {
Chris@30 427 if (!m_model || !m_model->isOK()) return;
Chris@30 428
Chris@30 429 int sampleRate = m_model->getSampleRate();
Chris@30 430 if (!sampleRate) return;
Chris@30 431
Chris@30 432 // Profiler profiler("NoteLayer::paint", true);
Chris@30 433
Chris@30 434 int x0 = rect.left(), x1 = rect.right();
Chris@44 435 long frame0 = v->getFrameForX(x0);
Chris@44 436 long frame1 = v->getFrameForX(x1);
Chris@30 437
Chris@30 438 NoteModel::PointList points(m_model->getPoints(frame0, frame1));
Chris@30 439 if (points.empty()) return;
Chris@30 440
Chris@30 441 paint.setPen(m_colour);
Chris@30 442
Chris@30 443 QColor brushColour(m_colour);
Chris@30 444 brushColour.setAlpha(80);
Chris@30 445
Chris@30 446 // std::cerr << "NoteLayer::paint: resolution is "
Chris@30 447 // << m_model->getResolution() << " frames" << std::endl;
Chris@30 448
Chris@30 449 float min = m_model->getValueMinimum();
Chris@30 450 float max = m_model->getValueMaximum();
Chris@30 451 if (max == min) max = min + 1.0;
Chris@30 452
Chris@44 453 int origin = int(nearbyint(v->height() -
Chris@44 454 (-min * v->height()) / (max - min)));
Chris@30 455
Chris@30 456 QPoint localPos;
Chris@30 457 long illuminateFrame = -1;
Chris@30 458
Chris@44 459 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@30 460 NoteModel::PointList localPoints =
Chris@44 461 getLocalPoints(v, localPos.x());
Chris@30 462 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@30 463 }
Chris@30 464
Chris@30 465 paint.save();
Chris@30 466 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@30 467
Chris@30 468 for (NoteModel::PointList::const_iterator i = points.begin();
Chris@30 469 i != points.end(); ++i) {
Chris@30 470
Chris@30 471 const NoteModel::Point &p(*i);
Chris@30 472
Chris@44 473 int x = v->getXForFrame(p.frame);
Chris@44 474 int y = getYForValue(v, p.value);
Chris@44 475 int w = v->getXForFrame(p.frame + p.duration) - x;
Chris@30 476 int h = 3;
Chris@30 477
Chris@30 478 if (m_model->getValueQuantization() != 0.0) {
Chris@44 479 h = y - getYForValue(v, p.value + m_model->getValueQuantization());
Chris@30 480 if (h < 3) h = 3;
Chris@30 481 }
Chris@30 482
Chris@30 483 if (w < 1) w = 1;
Chris@30 484 paint.setPen(m_colour);
Chris@30 485 paint.setBrush(brushColour);
Chris@30 486
Chris@30 487 if (illuminateFrame == p.frame) {
Chris@30 488 if (localPos.y() >= y - h && localPos.y() < y) {
Chris@30 489 paint.setPen(Qt::black);//!!!
Chris@30 490 paint.setBrush(Qt::black);//!!!
Chris@30 491 }
Chris@30 492 }
Chris@30 493
Chris@30 494 paint.drawRect(x, y - h, w, h);
Chris@30 495
Chris@30 496 /// if (p.label != "") {
Chris@30 497 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label);
Chris@30 498 /// }
Chris@30 499 }
Chris@30 500
Chris@30 501 paint.restore();
Chris@30 502 }
Chris@30 503
Chris@30 504 void
Chris@44 505 NoteLayer::drawStart(View *v, QMouseEvent *e)
Chris@30 506 {
Chris@30 507 std::cerr << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 508
Chris@30 509 if (!m_model) return;
Chris@30 510
Chris@44 511 long frame = v->getFrameForX(e->x());
Chris@30 512 if (frame < 0) frame = 0;
Chris@30 513 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@30 514
Chris@44 515 float value = getValueForY(v, e->y());
Chris@30 516
Chris@30 517 m_editingPoint = NoteModel::Point(frame, value, 0, tr("New Point"));
Chris@30 518 m_originalPoint = m_editingPoint;
Chris@30 519
Chris@30 520 if (m_editingCommand) m_editingCommand->finish();
Chris@30 521 m_editingCommand = new NoteModel::EditCommand(m_model,
Chris@30 522 tr("Draw Point"));
Chris@30 523 m_editingCommand->addPoint(m_editingPoint);
Chris@30 524
Chris@30 525 m_editing = true;
Chris@30 526 }
Chris@30 527
Chris@30 528 void
Chris@44 529 NoteLayer::drawDrag(View *v, QMouseEvent *e)
Chris@30 530 {
Chris@30 531 std::cerr << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 532
Chris@30 533 if (!m_model || !m_editing) return;
Chris@30 534
Chris@44 535 long frame = v->getFrameForX(e->x());
Chris@30 536 if (frame < 0) frame = 0;
Chris@30 537 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@30 538
Chris@44 539 float value = getValueForY(v, e->y());
Chris@30 540
Chris@30 541 m_editingCommand->deletePoint(m_editingPoint);
Chris@30 542 m_editingPoint.frame = frame;
Chris@30 543 m_editingPoint.value = value;
Chris@30 544 m_editingCommand->addPoint(m_editingPoint);
Chris@30 545 }
Chris@30 546
Chris@30 547 void
Chris@44 548 NoteLayer::drawEnd(View *v, QMouseEvent *e)
Chris@30 549 {
Chris@30 550 std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 551 if (!m_model || !m_editing) return;
Chris@30 552 m_editingCommand->finish();
Chris@30 553 m_editingCommand = 0;
Chris@30 554 m_editing = false;
Chris@30 555 }
Chris@30 556
Chris@30 557 void
Chris@44 558 NoteLayer::editStart(View *v, QMouseEvent *e)
Chris@30 559 {
Chris@30 560 std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 561
Chris@30 562 if (!m_model) return;
Chris@30 563
Chris@44 564 NoteModel::PointList points = getLocalPoints(v, e->x());
Chris@30 565 if (points.empty()) return;
Chris@30 566
Chris@30 567 m_editingPoint = *points.begin();
Chris@30 568 m_originalPoint = m_editingPoint;
Chris@30 569
Chris@30 570 if (m_editingCommand) {
Chris@30 571 m_editingCommand->finish();
Chris@30 572 m_editingCommand = 0;
Chris@30 573 }
Chris@30 574
Chris@30 575 m_editing = true;
Chris@30 576 }
Chris@30 577
Chris@30 578 void
Chris@44 579 NoteLayer::editDrag(View *v, QMouseEvent *e)
Chris@30 580 {
Chris@30 581 std::cerr << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 582
Chris@30 583 if (!m_model || !m_editing) return;
Chris@30 584
Chris@44 585 long frame = v->getFrameForX(e->x());
Chris@30 586 if (frame < 0) frame = 0;
Chris@30 587 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@30 588
Chris@44 589 float value = getValueForY(v, e->y());
Chris@30 590
Chris@30 591 if (!m_editingCommand) {
Chris@30 592 m_editingCommand = new NoteModel::EditCommand(m_model,
Chris@30 593 tr("Drag Point"));
Chris@30 594 }
Chris@30 595
Chris@30 596 m_editingCommand->deletePoint(m_editingPoint);
Chris@30 597 m_editingPoint.frame = frame;
Chris@30 598 m_editingPoint.value = value;
Chris@30 599 m_editingCommand->addPoint(m_editingPoint);
Chris@30 600 }
Chris@30 601
Chris@30 602 void
Chris@44 603 NoteLayer::editEnd(View *v, QMouseEvent *e)
Chris@30 604 {
Chris@30 605 std::cerr << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 606 if (!m_model || !m_editing) return;
Chris@30 607
Chris@30 608 if (m_editingCommand) {
Chris@30 609
Chris@30 610 QString newName = m_editingCommand->getName();
Chris@30 611
Chris@30 612 if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@30 613 if (m_editingPoint.value != m_originalPoint.value) {
Chris@30 614 newName = tr("Edit Point");
Chris@30 615 } else {
Chris@30 616 newName = tr("Relocate Point");
Chris@30 617 }
Chris@30 618 } else {
Chris@30 619 newName = tr("Change Point Value");
Chris@30 620 }
Chris@30 621
Chris@30 622 m_editingCommand->setName(newName);
Chris@30 623 m_editingCommand->finish();
Chris@30 624 }
Chris@30 625
Chris@30 626 m_editingCommand = 0;
Chris@30 627 m_editing = false;
Chris@30 628 }
Chris@30 629
Chris@43 630 void
Chris@70 631 NoteLayer::editOpen(View *v, QMouseEvent *e)
Chris@70 632 {
Chris@70 633 if (!m_model) return;
Chris@70 634
Chris@70 635 NoteModel::PointList points = getLocalPoints(v, e->x());
Chris@70 636 if (points.empty()) return;
Chris@70 637
Chris@70 638 NoteModel::Point note = *points.begin();
Chris@70 639
Chris@70 640 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 641 (m_model->getSampleRate(),
Chris@70 642 ItemEditDialog::ShowTime |
Chris@70 643 ItemEditDialog::ShowDuration |
Chris@70 644 ItemEditDialog::ShowValue |
Chris@70 645 ItemEditDialog::ShowText);
Chris@70 646
Chris@70 647 dialog->setFrameTime(note.frame);
Chris@70 648 dialog->setValue(note.value);
Chris@70 649 dialog->setFrameDuration(note.duration);
Chris@70 650 dialog->setText(note.label);
Chris@70 651
Chris@70 652 if (dialog->exec() == QDialog::Accepted) {
Chris@70 653
Chris@70 654 NoteModel::Point newNote = note;
Chris@70 655 newNote.frame = dialog->getFrameTime();
Chris@70 656 newNote.value = dialog->getValue();
Chris@70 657 newNote.duration = dialog->getFrameDuration();
Chris@70 658 newNote.label = dialog->getText();
Chris@70 659
Chris@70 660 NoteModel::EditCommand *command = new NoteModel::EditCommand
Chris@70 661 (m_model, tr("Edit Point"));
Chris@70 662 command->deletePoint(note);
Chris@70 663 command->addPoint(newNote);
Chris@70 664 command->finish();
Chris@70 665 }
Chris@70 666
Chris@70 667 delete dialog;
Chris@70 668 }
Chris@70 669
Chris@70 670 void
Chris@43 671 NoteLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@43 672 {
Chris@43 673 NoteModel::EditCommand *command =
Chris@43 674 new NoteModel::EditCommand(m_model, tr("Drag Selection"));
Chris@43 675
Chris@43 676 NoteModel::PointList points =
Chris@43 677 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 678
Chris@43 679 for (NoteModel::PointList::iterator i = points.begin();
Chris@43 680 i != points.end(); ++i) {
Chris@43 681
Chris@43 682 if (s.contains(i->frame)) {
Chris@43 683 NoteModel::Point newPoint(*i);
Chris@43 684 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@43 685 command->deletePoint(*i);
Chris@43 686 command->addPoint(newPoint);
Chris@43 687 }
Chris@43 688 }
Chris@43 689
Chris@43 690 command->finish();
Chris@43 691 }
Chris@43 692
Chris@43 693 void
Chris@43 694 NoteLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 695 {
Chris@43 696 NoteModel::EditCommand *command =
Chris@43 697 new NoteModel::EditCommand(m_model, tr("Resize Selection"));
Chris@43 698
Chris@43 699 NoteModel::PointList points =
Chris@43 700 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 701
Chris@43 702 double ratio =
Chris@43 703 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@43 704 double(s.getEndFrame() - s.getStartFrame());
Chris@43 705
Chris@43 706 for (NoteModel::PointList::iterator i = points.begin();
Chris@43 707 i != points.end(); ++i) {
Chris@43 708
Chris@43 709 if (s.contains(i->frame)) {
Chris@43 710
Chris@43 711 double targetStart = i->frame;
Chris@43 712 targetStart = newSize.getStartFrame() +
Chris@43 713 double(targetStart - s.getStartFrame()) * ratio;
Chris@43 714
Chris@43 715 double targetEnd = i->frame + i->duration;
Chris@43 716 targetEnd = newSize.getStartFrame() +
Chris@43 717 double(targetEnd - s.getStartFrame()) * ratio;
Chris@43 718
Chris@43 719 NoteModel::Point newPoint(*i);
Chris@43 720 newPoint.frame = lrint(targetStart);
Chris@43 721 newPoint.duration = lrint(targetEnd - targetStart);
Chris@43 722 command->deletePoint(*i);
Chris@43 723 command->addPoint(newPoint);
Chris@43 724 }
Chris@43 725 }
Chris@43 726
Chris@43 727 command->finish();
Chris@43 728 }
Chris@43 729
Chris@76 730 void
Chris@76 731 NoteLayer::deleteSelection(Selection s)
Chris@76 732 {
Chris@76 733 NoteModel::EditCommand *command =
Chris@76 734 new NoteModel::EditCommand(m_model, tr("Delete Selected Points"));
Chris@76 735
Chris@76 736 NoteModel::PointList points =
Chris@76 737 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 738
Chris@76 739 for (NoteModel::PointList::iterator i = points.begin();
Chris@76 740 i != points.end(); ++i) {
Chris@76 741
Chris@76 742 if (s.contains(i->frame)) {
Chris@76 743 command->deletePoint(*i);
Chris@76 744 }
Chris@76 745 }
Chris@76 746
Chris@76 747 command->finish();
Chris@76 748 }
Chris@76 749
Chris@76 750 void
Chris@76 751 NoteLayer::copy(Selection s, Clipboard &to)
Chris@76 752 {
Chris@76 753 NoteModel::PointList points =
Chris@76 754 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 755
Chris@76 756 for (NoteModel::PointList::iterator i = points.begin();
Chris@76 757 i != points.end(); ++i) {
Chris@76 758 if (s.contains(i->frame)) {
Chris@76 759 Clipboard::Point point(i->frame, i->label);
Chris@76 760 to.addPoint(point);
Chris@76 761 }
Chris@76 762 }
Chris@76 763 }
Chris@76 764
Chris@76 765 void
Chris@76 766 NoteLayer::paste(const Clipboard &from, int frameOffset)
Chris@76 767 {
Chris@76 768 const Clipboard::PointList &points = from.getPoints();
Chris@76 769
Chris@76 770 NoteModel::EditCommand *command =
Chris@76 771 new NoteModel::EditCommand(m_model, tr("Paste"));
Chris@76 772
Chris@76 773 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 774 i != points.end(); ++i) {
Chris@76 775
Chris@76 776 if (!i->haveFrame()) continue;
Chris@76 777 size_t frame = 0;
Chris@76 778 if (frameOffset > 0 || -frameOffset < i->getFrame()) {
Chris@76 779 frame = i->getFrame() + frameOffset;
Chris@76 780 }
Chris@76 781 NoteModel::Point newPoint(frame);
Chris@76 782
Chris@76 783 if (i->haveLabel()) newPoint.label = i->getLabel();
Chris@76 784 if (i->haveValue()) newPoint.value = i->getValue();
Chris@76 785 else newPoint.value = (m_model->getValueMinimum() +
Chris@76 786 m_model->getValueMaximum()) / 2;
Chris@76 787 if (i->haveDuration()) newPoint.duration = i->getDuration();
Chris@76 788 else newPoint.duration = m_model->getResolution(); //!!!
Chris@76 789
Chris@76 790 command->addPoint(newPoint);
Chris@76 791 }
Chris@76 792
Chris@76 793 command->finish();
Chris@76 794 }
Chris@76 795
Chris@30 796 QString
Chris@30 797 NoteLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@30 798 {
Chris@30 799 return Layer::toXmlString(indent, extraAttributes +
Chris@30 800 QString(" colour=\"%1\" verticalScale=\"%2\"")
Chris@30 801 .arg(encodeColour(m_colour)).arg(m_verticalScale));
Chris@30 802 }
Chris@30 803
Chris@30 804 void
Chris@30 805 NoteLayer::setProperties(const QXmlAttributes &attributes)
Chris@30 806 {
Chris@30 807 QString colourSpec = attributes.value("colour");
Chris@30 808 if (colourSpec != "") {
Chris@30 809 QColor colour(colourSpec);
Chris@30 810 if (colour.isValid()) {
Chris@30 811 setBaseColour(QColor(colourSpec));
Chris@30 812 }
Chris@30 813 }
Chris@30 814
Chris@30 815 bool ok;
Chris@30 816 VerticalScale scale = (VerticalScale)
Chris@30 817 attributes.value("verticalScale").toInt(&ok);
Chris@30 818 if (ok) setVerticalScale(scale);
Chris@30 819 }
Chris@30 820
Chris@30 821