annotate layer/NoteLayer.cpp @ 454:e2a40fdadd8c

Various fixes: * Fix handling of HTTP redirects (avoiding crashes... I hope) * Fix failure to delete FFT models when a feature extraction model transformer was abandoned (also a cause of crashes in the past) * Fix deadlock when said transform was abandoned before its source model was ready because the session was being cleared (and so the source model would never be ready)
author Chris Cannam
date Fri, 28 Nov 2008 13:36:13 +0000
parents 4a14499fb184
children 747a58d45d56
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@128 18 #include "data/model/Model.h"
Chris@30 19 #include "base/RealTime.h"
Chris@30 20 #include "base/Profiler.h"
Chris@30 21 #include "base/Pitch.h"
Chris@197 22 #include "base/LogRange.h"
Chris@439 23 #include "base/RangeMapper.h"
Chris@376 24 #include "ColourDatabase.h"
Chris@128 25 #include "view/View.h"
Chris@30 26
Chris@128 27 #include "data/model/NoteModel.h"
Chris@30 28
Chris@70 29 #include "widgets/ItemEditDialog.h"
Chris@70 30
Chris@42 31 #include "SpectrogramLayer.h" // for optional frequency alignment
Chris@42 32
Chris@30 33 #include <QPainter>
Chris@30 34 #include <QPainterPath>
Chris@30 35 #include <QMouseEvent>
Chris@316 36 #include <QTextStream>
Chris@360 37 #include <QMessageBox>
Chris@30 38
Chris@30 39 #include <iostream>
Chris@30 40 #include <cmath>
Chris@30 41
Chris@44 42 NoteLayer::NoteLayer() :
Chris@287 43 SingleColourLayer(),
Chris@30 44 m_model(0),
Chris@30 45 m_editing(false),
Chris@335 46 m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")),
Chris@335 47 m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")),
Chris@30 48 m_editingCommand(0),
Chris@439 49 m_verticalScale(AutoAlignScale),
Chris@439 50 m_scaleMinimum(0),
Chris@439 51 m_scaleMaximum(0)
Chris@30 52 {
Chris@44 53
Chris@30 54 }
Chris@30 55
Chris@30 56 void
Chris@30 57 NoteLayer::setModel(NoteModel *model)
Chris@30 58 {
Chris@30 59 if (m_model == model) return;
Chris@30 60 m_model = model;
Chris@30 61
Chris@320 62 connectSignals(m_model);
Chris@30 63
Chris@101 64 // std::cerr << "NoteLayer::setModel(" << model << ")" << std::endl;
Chris@30 65
Chris@439 66 m_scaleMinimum = 0;
Chris@439 67 m_scaleMaximum = 0;
Chris@439 68
Chris@30 69 emit modelReplaced();
Chris@30 70 }
Chris@30 71
Chris@30 72 Layer::PropertyList
Chris@30 73 NoteLayer::getProperties() const
Chris@30 74 {
Chris@287 75 PropertyList list = SingleColourLayer::getProperties();
Chris@87 76 list.push_back("Vertical Scale");
Chris@100 77 list.push_back("Scale Units");
Chris@30 78 return list;
Chris@30 79 }
Chris@30 80
Chris@87 81 QString
Chris@87 82 NoteLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 83 {
Chris@87 84 if (name == "Vertical Scale") return tr("Vertical Scale");
Chris@116 85 if (name == "Scale Units") return tr("Scale Units");
Chris@287 86 return SingleColourLayer::getPropertyLabel(name);
Chris@87 87 }
Chris@87 88
Chris@30 89 Layer::PropertyType
Chris@100 90 NoteLayer::getPropertyType(const PropertyName &name) const
Chris@30 91 {
Chris@100 92 if (name == "Scale Units") return UnitsProperty;
Chris@287 93 if (name == "Vertical Scale") return ValueProperty;
Chris@287 94 return SingleColourLayer::getPropertyType(name);
Chris@30 95 }
Chris@30 96
Chris@198 97 QString
Chris@198 98 NoteLayer::getPropertyGroupName(const PropertyName &name) const
Chris@198 99 {
Chris@198 100 if (name == "Vertical Scale" || name == "Scale Units") {
Chris@198 101 return tr("Scale");
Chris@198 102 }
Chris@287 103 return SingleColourLayer::getPropertyGroupName(name);
Chris@198 104 }
Chris@198 105
Chris@30 106 int
Chris@30 107 NoteLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 108 int *min, int *max, int *deflt) const
Chris@30 109 {
Chris@216 110 int val = 0;
Chris@30 111
Chris@287 112 if (name == "Vertical Scale") {
Chris@30 113
Chris@30 114 if (min) *min = 0;
Chris@101 115 if (max) *max = 3;
Chris@216 116 if (deflt) *deflt = int(AutoAlignScale);
Chris@30 117
Chris@216 118 val = int(m_verticalScale);
Chris@30 119
Chris@100 120 } else if (name == "Scale Units") {
Chris@100 121
Chris@216 122 if (deflt) *deflt = 0;
Chris@100 123 if (m_model) {
Chris@216 124 val = UnitDatabase::getInstance()->getUnitId
Chris@100 125 (m_model->getScaleUnits());
Chris@100 126 }
Chris@100 127
Chris@30 128 } else {
Chris@216 129
Chris@287 130 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@30 131 }
Chris@30 132
Chris@216 133 return val;
Chris@30 134 }
Chris@30 135
Chris@30 136 QString
Chris@30 137 NoteLayer::getPropertyValueLabel(const PropertyName &name,
Chris@287 138 int value) const
Chris@30 139 {
Chris@287 140 if (name == "Vertical Scale") {
Chris@30 141 switch (value) {
Chris@30 142 default:
Chris@101 143 case 0: return tr("Auto-Align");
Chris@198 144 case 1: return tr("Linear");
Chris@198 145 case 2: return tr("Log");
Chris@198 146 case 3: return tr("MIDI Notes");
Chris@30 147 }
Chris@30 148 }
Chris@287 149 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@30 150 }
Chris@30 151
Chris@30 152 void
Chris@30 153 NoteLayer::setProperty(const PropertyName &name, int value)
Chris@30 154 {
Chris@287 155 if (name == "Vertical Scale") {
Chris@30 156 setVerticalScale(VerticalScale(value));
Chris@100 157 } else if (name == "Scale Units") {
Chris@100 158 if (m_model) {
Chris@100 159 m_model->setScaleUnits
Chris@100 160 (UnitDatabase::getInstance()->getUnitById(value));
Chris@100 161 emit modelChanged();
Chris@100 162 }
Chris@287 163 } else {
Chris@287 164 return SingleColourLayer::setProperty(name, value);
Chris@30 165 }
Chris@30 166 }
Chris@30 167
Chris@30 168 void
Chris@30 169 NoteLayer::setVerticalScale(VerticalScale scale)
Chris@30 170 {
Chris@30 171 if (m_verticalScale == scale) return;
Chris@30 172 m_verticalScale = scale;
Chris@30 173 emit layerParametersChanged();
Chris@30 174 }
Chris@30 175
Chris@30 176 bool
Chris@44 177 NoteLayer::isLayerScrollable(const View *v) const
Chris@30 178 {
Chris@30 179 QPoint discard;
Chris@44 180 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@30 181 }
Chris@30 182
Chris@79 183 bool
Chris@101 184 NoteLayer::shouldConvertMIDIToHz() const
Chris@101 185 {
Chris@101 186 QString unit = m_model->getScaleUnits();
Chris@101 187 return (unit != "Hz");
Chris@101 188 // if (unit == "" ||
Chris@101 189 // unit.startsWith("MIDI") ||
Chris@101 190 // unit.startsWith("midi")) return true;
Chris@101 191 // return false;
Chris@101 192 }
Chris@101 193
Chris@101 194 bool
Chris@101 195 NoteLayer::getValueExtents(float &min, float &max,
Chris@101 196 bool &logarithmic, QString &unit) const
Chris@79 197 {
Chris@79 198 if (!m_model) return false;
Chris@79 199 min = m_model->getValueMinimum();
Chris@79 200 max = m_model->getValueMaximum();
Chris@101 201
Chris@105 202 if (shouldConvertMIDIToHz()) {
Chris@105 203 unit = "Hz";
Chris@105 204 min = Pitch::getFrequencyForPitch(lrintf(min));
Chris@105 205 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
Chris@105 206 } else unit = m_model->getScaleUnits();
Chris@101 207
Chris@101 208 if (m_verticalScale == MIDIRangeScale ||
Chris@101 209 m_verticalScale == LogScale) logarithmic = true;
Chris@101 210
Chris@101 211 return true;
Chris@101 212 }
Chris@101 213
Chris@101 214 bool
Chris@101 215 NoteLayer::getDisplayExtents(float &min, float &max) const
Chris@101 216 {
Chris@439 217 if (!m_model || shouldAutoAlign()) return false;
Chris@101 218
Chris@101 219 if (m_verticalScale == MIDIRangeScale) {
Chris@101 220 min = Pitch::getFrequencyForPitch(0);
Chris@101 221 max = Pitch::getFrequencyForPitch(127);
Chris@101 222 return true;
Chris@101 223 }
Chris@101 224
Chris@439 225 if (m_scaleMinimum == m_scaleMaximum) {
Chris@439 226 m_scaleMinimum = m_model->getValueMinimum();
Chris@439 227 m_scaleMaximum = m_model->getValueMaximum();
Chris@439 228 }
Chris@439 229
Chris@439 230 min = m_scaleMinimum;
Chris@439 231 max = m_scaleMaximum;
Chris@101 232
Chris@101 233 if (shouldConvertMIDIToHz()) {
Chris@101 234 min = Pitch::getFrequencyForPitch(lrintf(min));
Chris@101 235 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
Chris@101 236 }
Chris@101 237
Chris@79 238 return true;
Chris@79 239 }
Chris@79 240
Chris@439 241 bool
Chris@439 242 NoteLayer::setDisplayExtents(float min, float max)
Chris@439 243 {
Chris@439 244 if (!m_model) return false;
Chris@439 245
Chris@439 246 if (min == max) {
Chris@439 247 if (min == 0.f) {
Chris@439 248 max = 1.f;
Chris@439 249 } else {
Chris@439 250 max = min * 1.0001;
Chris@439 251 }
Chris@439 252 }
Chris@439 253
Chris@439 254 m_scaleMinimum = min;
Chris@439 255 m_scaleMaximum = max;
Chris@439 256
Chris@439 257 // std::cerr << "NoteLayer::setDisplayExtents: min = " << min << ", max = " << max << std::endl;
Chris@439 258
Chris@439 259 emit layerParametersChanged();
Chris@439 260 return true;
Chris@439 261 }
Chris@439 262
Chris@439 263 int
Chris@439 264 NoteLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@439 265 {
Chris@439 266 if (shouldAutoAlign()) return 0;
Chris@439 267 if (!m_model) return 0;
Chris@439 268
Chris@439 269 defaultStep = 0;
Chris@439 270 return 100;
Chris@439 271 }
Chris@439 272
Chris@439 273 int
Chris@439 274 NoteLayer::getCurrentVerticalZoomStep() const
Chris@439 275 {
Chris@439 276 if (shouldAutoAlign()) return 0;
Chris@439 277 if (!m_model) return 0;
Chris@439 278
Chris@439 279 RangeMapper *mapper = getNewVerticalZoomRangeMapper();
Chris@439 280 if (!mapper) return 0;
Chris@439 281
Chris@439 282 float dmin, dmax;
Chris@439 283 getDisplayExtents(dmin, dmax);
Chris@439 284
Chris@439 285 int nr = mapper->getPositionForValue(dmax - dmin);
Chris@439 286
Chris@439 287 delete mapper;
Chris@439 288
Chris@439 289 return 100 - nr;
Chris@439 290 }
Chris@439 291
Chris@439 292 //!!! lots of duplication with TimeValueLayer
Chris@439 293
Chris@439 294 void
Chris@439 295 NoteLayer::setVerticalZoomStep(int step)
Chris@439 296 {
Chris@439 297 if (shouldAutoAlign()) return;
Chris@439 298 if (!m_model) return;
Chris@439 299
Chris@439 300 RangeMapper *mapper = getNewVerticalZoomRangeMapper();
Chris@439 301 if (!mapper) return;
Chris@439 302
Chris@439 303 float min, max;
Chris@439 304 bool logarithmic;
Chris@439 305 QString unit;
Chris@439 306 getValueExtents(min, max, logarithmic, unit);
Chris@439 307
Chris@439 308 float dmin, dmax;
Chris@439 309 getDisplayExtents(dmin, dmax);
Chris@439 310
Chris@439 311 float newdist = mapper->getValueForPosition(100 - step);
Chris@439 312
Chris@439 313 float newmin, newmax;
Chris@439 314
Chris@439 315 if (logarithmic) {
Chris@439 316
Chris@439 317 // see SpectrogramLayer::setVerticalZoomStep
Chris@439 318
Chris@439 319 newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2;
Chris@439 320 newmin = newmax - newdist;
Chris@439 321
Chris@439 322 // std::cerr << "newmin = " << newmin << ", newmax = " << newmax << std::endl;
Chris@439 323
Chris@439 324 } else {
Chris@439 325 float dmid = (dmax + dmin) / 2;
Chris@439 326 newmin = dmid - newdist / 2;
Chris@439 327 newmax = dmid + newdist / 2;
Chris@439 328 }
Chris@439 329
Chris@439 330 if (newmin < min) {
Chris@439 331 newmax += (min - newmin);
Chris@439 332 newmin = min;
Chris@439 333 }
Chris@439 334 if (newmax > max) {
Chris@439 335 newmax = max;
Chris@439 336 }
Chris@439 337
Chris@439 338 std::cerr << "NoteLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << std::endl;
Chris@439 339
Chris@439 340 setDisplayExtents(newmin, newmax);
Chris@439 341 }
Chris@439 342
Chris@439 343 RangeMapper *
Chris@439 344 NoteLayer::getNewVerticalZoomRangeMapper() const
Chris@439 345 {
Chris@439 346 if (!m_model) return 0;
Chris@439 347
Chris@439 348 RangeMapper *mapper;
Chris@439 349
Chris@439 350 float min, max;
Chris@439 351 bool logarithmic;
Chris@439 352 QString unit;
Chris@439 353 getValueExtents(min, max, logarithmic, unit);
Chris@439 354
Chris@439 355 if (min == max) return 0;
Chris@439 356
Chris@439 357 if (logarithmic) {
Chris@439 358 mapper = new LogRangeMapper(0, 100, min, max, unit);
Chris@439 359 } else {
Chris@439 360 mapper = new LinearRangeMapper(0, 100, min, max, unit);
Chris@439 361 }
Chris@439 362
Chris@439 363 return mapper;
Chris@439 364 }
Chris@439 365
Chris@30 366 NoteModel::PointList
Chris@44 367 NoteLayer::getLocalPoints(View *v, int x) const
Chris@30 368 {
Chris@30 369 if (!m_model) return NoteModel::PointList();
Chris@30 370
Chris@44 371 long frame = v->getFrameForX(x);
Chris@30 372
Chris@30 373 NoteModel::PointList onPoints =
Chris@30 374 m_model->getPoints(frame);
Chris@30 375
Chris@30 376 if (!onPoints.empty()) {
Chris@30 377 return onPoints;
Chris@30 378 }
Chris@30 379
Chris@30 380 NoteModel::PointList prevPoints =
Chris@30 381 m_model->getPreviousPoints(frame);
Chris@30 382 NoteModel::PointList nextPoints =
Chris@30 383 m_model->getNextPoints(frame);
Chris@30 384
Chris@30 385 NoteModel::PointList usePoints = prevPoints;
Chris@30 386
Chris@30 387 if (prevPoints.empty()) {
Chris@30 388 usePoints = nextPoints;
Chris@248 389 } else if (long(prevPoints.begin()->frame) < v->getStartFrame() &&
Chris@44 390 !(nextPoints.begin()->frame > v->getEndFrame())) {
Chris@30 391 usePoints = nextPoints;
Chris@248 392 } else if (long(nextPoints.begin()->frame) - frame <
Chris@248 393 frame - long(prevPoints.begin()->frame)) {
Chris@30 394 usePoints = nextPoints;
Chris@30 395 }
Chris@30 396
Chris@30 397 if (!usePoints.empty()) {
Chris@30 398 int fuzz = 2;
Chris@44 399 int px = v->getXForFrame(usePoints.begin()->frame);
Chris@30 400 if ((px > x && px - x > fuzz) ||
Chris@30 401 (px < x && x - px > fuzz + 1)) {
Chris@30 402 usePoints.clear();
Chris@30 403 }
Chris@30 404 }
Chris@30 405
Chris@30 406 return usePoints;
Chris@30 407 }
Chris@30 408
Chris@30 409 QString
Chris@44 410 NoteLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@30 411 {
Chris@30 412 int x = pos.x();
Chris@30 413
Chris@30 414 if (!m_model || !m_model->getSampleRate()) return "";
Chris@30 415
Chris@44 416 NoteModel::PointList points = getLocalPoints(v, x);
Chris@30 417
Chris@30 418 if (points.empty()) {
Chris@30 419 if (!m_model->isReady()) {
Chris@30 420 return tr("In progress");
Chris@30 421 } else {
Chris@30 422 return tr("No local points");
Chris@30 423 }
Chris@30 424 }
Chris@30 425
Chris@30 426 Note note(0);
Chris@30 427 NoteModel::PointList::iterator i;
Chris@30 428
Chris@30 429 for (i = points.begin(); i != points.end(); ++i) {
Chris@30 430
Chris@44 431 int y = getYForValue(v, i->value);
Chris@30 432 int h = 3;
Chris@30 433
Chris@30 434 if (m_model->getValueQuantization() != 0.0) {
Chris@44 435 h = y - getYForValue(v, i->value + m_model->getValueQuantization());
Chris@30 436 if (h < 3) h = 3;
Chris@30 437 }
Chris@30 438
Chris@30 439 if (pos.y() >= y - h && pos.y() <= y) {
Chris@30 440 note = *i;
Chris@30 441 break;
Chris@30 442 }
Chris@30 443 }
Chris@30 444
Chris@30 445 if (i == points.end()) return tr("No local points");
Chris@30 446
Chris@30 447 RealTime rt = RealTime::frame2RealTime(note.frame,
Chris@30 448 m_model->getSampleRate());
Chris@30 449 RealTime rd = RealTime::frame2RealTime(note.duration,
Chris@30 450 m_model->getSampleRate());
Chris@30 451
Chris@101 452 QString pitchText;
Chris@101 453
Chris@101 454 if (shouldConvertMIDIToHz()) {
Chris@101 455
Chris@101 456 int mnote = lrintf(note.value);
Chris@101 457 int cents = lrintf((note.value - mnote) * 100);
Chris@101 458 float freq = Pitch::getFrequencyForPitch(mnote, cents);
Chris@234 459 pitchText = tr("%1 (%2 Hz)")
Chris@101 460 .arg(Pitch::getPitchLabel(mnote, cents)).arg(freq);
Chris@101 461
Chris@101 462 } else if (m_model->getScaleUnits() == "Hz") {
Chris@101 463
Chris@234 464 pitchText = tr("%1 Hz (%2)")
Chris@101 465 .arg(note.value)
Chris@101 466 .arg(Pitch::getPitchLabelForFrequency(note.value));
Chris@101 467
Chris@101 468 } else {
Chris@234 469 pitchText = tr("%1 %2")
Chris@101 470 .arg(note.value).arg(m_model->getScaleUnits());
Chris@101 471 }
Chris@101 472
Chris@30 473 QString text;
Chris@30 474
Chris@30 475 if (note.label == "") {
Chris@30 476 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label"))
Chris@30 477 .arg(rt.toText(true).c_str())
Chris@101 478 .arg(pitchText)
Chris@30 479 .arg(rd.toText(true).c_str());
Chris@30 480 } else {
Chris@30 481 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4"))
Chris@30 482 .arg(rt.toText(true).c_str())
Chris@101 483 .arg(pitchText)
Chris@30 484 .arg(rd.toText(true).c_str())
Chris@30 485 .arg(note.label);
Chris@30 486 }
Chris@30 487
Chris@44 488 pos = QPoint(v->getXForFrame(note.frame),
Chris@44 489 getYForValue(v, note.value));
Chris@30 490 return text;
Chris@30 491 }
Chris@30 492
Chris@30 493 bool
Chris@44 494 NoteLayer::snapToFeatureFrame(View *v, int &frame,
Chris@44 495 size_t &resolution,
Chris@44 496 SnapType snap) const
Chris@30 497 {
Chris@30 498 if (!m_model) {
Chris@44 499 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@30 500 }
Chris@30 501
Chris@30 502 resolution = m_model->getResolution();
Chris@30 503 NoteModel::PointList points;
Chris@30 504
Chris@30 505 if (snap == SnapNeighbouring) {
Chris@30 506
Chris@44 507 points = getLocalPoints(v, v->getXForFrame(frame));
Chris@30 508 if (points.empty()) return false;
Chris@30 509 frame = points.begin()->frame;
Chris@30 510 return true;
Chris@30 511 }
Chris@30 512
Chris@30 513 points = m_model->getPoints(frame, frame);
Chris@30 514 int snapped = frame;
Chris@30 515 bool found = false;
Chris@30 516
Chris@30 517 for (NoteModel::PointList::const_iterator i = points.begin();
Chris@30 518 i != points.end(); ++i) {
Chris@30 519
Chris@30 520 if (snap == SnapRight) {
Chris@30 521
Chris@30 522 if (i->frame > frame) {
Chris@30 523 snapped = i->frame;
Chris@30 524 found = true;
Chris@30 525 break;
Chris@30 526 }
Chris@30 527
Chris@30 528 } else if (snap == SnapLeft) {
Chris@30 529
Chris@30 530 if (i->frame <= frame) {
Chris@30 531 snapped = i->frame;
Chris@30 532 found = true; // don't break, as the next may be better
Chris@30 533 } else {
Chris@30 534 break;
Chris@30 535 }
Chris@30 536
Chris@30 537 } else { // nearest
Chris@30 538
Chris@30 539 NoteModel::PointList::const_iterator j = i;
Chris@30 540 ++j;
Chris@30 541
Chris@30 542 if (j == points.end()) {
Chris@30 543
Chris@30 544 snapped = i->frame;
Chris@30 545 found = true;
Chris@30 546 break;
Chris@30 547
Chris@30 548 } else if (j->frame >= frame) {
Chris@30 549
Chris@30 550 if (j->frame - frame < frame - i->frame) {
Chris@30 551 snapped = j->frame;
Chris@30 552 } else {
Chris@30 553 snapped = i->frame;
Chris@30 554 }
Chris@30 555 found = true;
Chris@30 556 break;
Chris@30 557 }
Chris@30 558 }
Chris@30 559 }
Chris@30 560
Chris@30 561 frame = snapped;
Chris@30 562 return found;
Chris@30 563 }
Chris@30 564
Chris@101 565 void
Chris@101 566 NoteLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
Chris@30 567 {
Chris@101 568 min = 0.0;
Chris@101 569 max = 0.0;
Chris@101 570 log = false;
Chris@42 571
Chris@101 572 QString queryUnits;
Chris@101 573 if (shouldConvertMIDIToHz()) queryUnits = "Hz";
Chris@101 574 else queryUnits = m_model->getScaleUnits();
Chris@30 575
Chris@439 576 if (shouldAutoAlign()) {
Chris@30 577
Chris@101 578 if (!v->getValueExtents(queryUnits, min, max, log)) {
Chris@30 579
Chris@101 580 min = m_model->getValueMinimum();
Chris@101 581 max = m_model->getValueMaximum();
Chris@42 582
Chris@101 583 if (shouldConvertMIDIToHz()) {
Chris@101 584 min = Pitch::getFrequencyForPitch(lrintf(min));
Chris@101 585 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
Chris@101 586 }
Chris@42 587
Chris@105 588 std::cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@105 589
Chris@101 590 } else if (log) {
Chris@101 591
Chris@197 592 LogRange::mapRange(min, max);
Chris@105 593
Chris@105 594 std::cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@105 595
Chris@101 596 }
Chris@101 597
Chris@101 598 } else {
Chris@101 599
Chris@439 600 getDisplayExtents(min, max);
Chris@101 601
Chris@101 602 if (m_verticalScale == MIDIRangeScale) {
Chris@101 603 min = Pitch::getFrequencyForPitch(0);
Chris@101 604 max = Pitch::getFrequencyForPitch(127);
Chris@101 605 } else if (shouldConvertMIDIToHz()) {
Chris@101 606 min = Pitch::getFrequencyForPitch(lrintf(min));
Chris@101 607 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
Chris@101 608 }
Chris@101 609
Chris@101 610 if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
Chris@197 611 LogRange::mapRange(min, max);
Chris@101 612 log = true;
Chris@101 613 }
Chris@30 614 }
Chris@30 615
Chris@101 616 if (max == min) max = min + 1.0;
Chris@101 617 }
Chris@30 618
Chris@101 619 int
Chris@101 620 NoteLayer::getYForValue(View *v, float val) const
Chris@101 621 {
Chris@101 622 float min = 0.0, max = 0.0;
Chris@101 623 bool logarithmic = false;
Chris@101 624 int h = v->height();
Chris@101 625
Chris@101 626 getScaleExtents(v, min, max, logarithmic);
Chris@101 627
Chris@101 628 // std::cerr << "NoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << std::endl;
Chris@101 629
Chris@101 630 if (shouldConvertMIDIToHz()) {
Chris@101 631 val = Pitch::getFrequencyForPitch(lrintf(val),
Chris@101 632 lrintf((val - lrintf(val)) * 100));
Chris@101 633 // std::cerr << "shouldConvertMIDIToHz true, val now = " << val << std::endl;
Chris@101 634 }
Chris@101 635
Chris@101 636 if (logarithmic) {
Chris@197 637 val = LogRange::map(val);
Chris@101 638 // std::cerr << "logarithmic true, val now = " << val << std::endl;
Chris@101 639 }
Chris@101 640
Chris@101 641 int y = int(h - ((val - min) * h) / (max - min)) - 1;
Chris@101 642 // std::cerr << "y = " << y << std::endl;
Chris@101 643 return y;
Chris@30 644 }
Chris@30 645
Chris@30 646 float
Chris@44 647 NoteLayer::getValueForY(View *v, int y) const
Chris@30 648 {
Chris@101 649 float min = 0.0, max = 0.0;
Chris@101 650 bool logarithmic = false;
Chris@44 651 int h = v->height();
Chris@30 652
Chris@101 653 getScaleExtents(v, min, max, logarithmic);
Chris@101 654
Chris@101 655 float val = min + (float(h - y) * float(max - min)) / h;
Chris@101 656
Chris@101 657 if (logarithmic) {
Chris@197 658 val = powf(10.f, val);
Chris@101 659 }
Chris@101 660
Chris@101 661 if (shouldConvertMIDIToHz()) {
Chris@101 662 val = Pitch::getPitchForFrequency(val);
Chris@101 663 }
Chris@101 664
Chris@101 665 return val;
Chris@30 666 }
Chris@30 667
Chris@439 668 bool
Chris@439 669 NoteLayer::shouldAutoAlign() const
Chris@439 670 {
Chris@439 671 if (!m_model) return false;
Chris@439 672 return (m_verticalScale == AutoAlignScale);
Chris@439 673 }
Chris@439 674
Chris@30 675 void
Chris@44 676 NoteLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@30 677 {
Chris@30 678 if (!m_model || !m_model->isOK()) return;
Chris@30 679
Chris@30 680 int sampleRate = m_model->getSampleRate();
Chris@30 681 if (!sampleRate) return;
Chris@30 682
Chris@30 683 // Profiler profiler("NoteLayer::paint", true);
Chris@30 684
Chris@30 685 int x0 = rect.left(), x1 = rect.right();
Chris@44 686 long frame0 = v->getFrameForX(x0);
Chris@44 687 long frame1 = v->getFrameForX(x1);
Chris@30 688
Chris@30 689 NoteModel::PointList points(m_model->getPoints(frame0, frame1));
Chris@30 690 if (points.empty()) return;
Chris@30 691
Chris@287 692 paint.setPen(getBaseQColor());
Chris@30 693
Chris@287 694 QColor brushColour(getBaseQColor());
Chris@30 695 brushColour.setAlpha(80);
Chris@30 696
Chris@30 697 // std::cerr << "NoteLayer::paint: resolution is "
Chris@30 698 // << m_model->getResolution() << " frames" << std::endl;
Chris@30 699
Chris@30 700 float min = m_model->getValueMinimum();
Chris@30 701 float max = m_model->getValueMaximum();
Chris@30 702 if (max == min) max = min + 1.0;
Chris@30 703
Chris@30 704 QPoint localPos;
Chris@30 705 long illuminateFrame = -1;
Chris@30 706
Chris@44 707 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@30 708 NoteModel::PointList localPoints =
Chris@44 709 getLocalPoints(v, localPos.x());
Chris@30 710 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@30 711 }
Chris@30 712
Chris@30 713 paint.save();
Chris@30 714 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@30 715
Chris@30 716 for (NoteModel::PointList::const_iterator i = points.begin();
Chris@30 717 i != points.end(); ++i) {
Chris@30 718
Chris@30 719 const NoteModel::Point &p(*i);
Chris@30 720
Chris@44 721 int x = v->getXForFrame(p.frame);
Chris@44 722 int y = getYForValue(v, p.value);
Chris@44 723 int w = v->getXForFrame(p.frame + p.duration) - x;
Chris@30 724 int h = 3;
Chris@30 725
Chris@30 726 if (m_model->getValueQuantization() != 0.0) {
Chris@44 727 h = y - getYForValue(v, p.value + m_model->getValueQuantization());
Chris@30 728 if (h < 3) h = 3;
Chris@30 729 }
Chris@30 730
Chris@30 731 if (w < 1) w = 1;
Chris@287 732 paint.setPen(getBaseQColor());
Chris@30 733 paint.setBrush(brushColour);
Chris@30 734
Chris@30 735 if (illuminateFrame == p.frame) {
Chris@30 736 if (localPos.y() >= y - h && localPos.y() < y) {
Chris@287 737 paint.setPen(v->getForeground());
Chris@287 738 paint.setBrush(v->getForeground());
Chris@30 739 }
Chris@30 740 }
Chris@30 741
Chris@124 742 paint.drawRect(x, y - h/2, w, h);
Chris@30 743
Chris@30 744 /// if (p.label != "") {
Chris@30 745 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label);
Chris@30 746 /// }
Chris@30 747 }
Chris@30 748
Chris@30 749 paint.restore();
Chris@30 750 }
Chris@30 751
Chris@30 752 void
Chris@44 753 NoteLayer::drawStart(View *v, QMouseEvent *e)
Chris@30 754 {
Chris@101 755 // std::cerr << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 756
Chris@30 757 if (!m_model) return;
Chris@30 758
Chris@44 759 long frame = v->getFrameForX(e->x());
Chris@30 760 if (frame < 0) frame = 0;
Chris@30 761 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@30 762
Chris@44 763 float value = getValueForY(v, e->y());
Chris@30 764
Chris@335 765 m_editingPoint = NoteModel::Point(frame, value, 0, 0.8, tr("New Point"));
Chris@30 766 m_originalPoint = m_editingPoint;
Chris@30 767
Chris@376 768 if (m_editingCommand) finish(m_editingCommand);
Chris@30 769 m_editingCommand = new NoteModel::EditCommand(m_model,
Chris@30 770 tr("Draw Point"));
Chris@30 771 m_editingCommand->addPoint(m_editingPoint);
Chris@30 772
Chris@30 773 m_editing = true;
Chris@30 774 }
Chris@30 775
Chris@30 776 void
Chris@44 777 NoteLayer::drawDrag(View *v, QMouseEvent *e)
Chris@30 778 {
Chris@101 779 // std::cerr << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 780
Chris@30 781 if (!m_model || !m_editing) return;
Chris@30 782
Chris@44 783 long frame = v->getFrameForX(e->x());
Chris@30 784 if (frame < 0) frame = 0;
Chris@30 785 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@30 786
Chris@101 787 float newValue = getValueForY(v, e->y());
Chris@101 788
Chris@101 789 long newFrame = m_editingPoint.frame;
Chris@101 790 long newDuration = frame - newFrame;
Chris@101 791 if (newDuration < 0) {
Chris@101 792 newFrame = frame;
Chris@101 793 newDuration = -newDuration;
Chris@101 794 } else if (newDuration == 0) {
Chris@101 795 newDuration = 1;
Chris@101 796 }
Chris@30 797
Chris@30 798 m_editingCommand->deletePoint(m_editingPoint);
Chris@101 799 m_editingPoint.frame = newFrame;
Chris@101 800 m_editingPoint.value = newValue;
Chris@101 801 m_editingPoint.duration = newDuration;
Chris@30 802 m_editingCommand->addPoint(m_editingPoint);
Chris@30 803 }
Chris@30 804
Chris@30 805 void
Chris@248 806 NoteLayer::drawEnd(View *, QMouseEvent *)
Chris@30 807 {
Chris@101 808 // std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 809 if (!m_model || !m_editing) return;
Chris@376 810 finish(m_editingCommand);
Chris@30 811 m_editingCommand = 0;
Chris@30 812 m_editing = false;
Chris@30 813 }
Chris@30 814
Chris@30 815 void
Chris@335 816 NoteLayer::eraseStart(View *v, QMouseEvent *e)
Chris@335 817 {
Chris@335 818 if (!m_model) return;
Chris@335 819
Chris@335 820 NoteModel::PointList points = getLocalPoints(v, e->x());
Chris@335 821 if (points.empty()) return;
Chris@335 822
Chris@335 823 m_editingPoint = *points.begin();
Chris@335 824
Chris@335 825 if (m_editingCommand) {
Chris@376 826 finish(m_editingCommand);
Chris@335 827 m_editingCommand = 0;
Chris@335 828 }
Chris@335 829
Chris@335 830 m_editing = true;
Chris@335 831 }
Chris@335 832
Chris@335 833 void
Chris@335 834 NoteLayer::eraseDrag(View *v, QMouseEvent *e)
Chris@335 835 {
Chris@335 836 }
Chris@335 837
Chris@335 838 void
Chris@335 839 NoteLayer::eraseEnd(View *v, QMouseEvent *e)
Chris@335 840 {
Chris@335 841 if (!m_model || !m_editing) return;
Chris@335 842
Chris@335 843 m_editing = false;
Chris@335 844
Chris@335 845 NoteModel::PointList points = getLocalPoints(v, e->x());
Chris@335 846 if (points.empty()) return;
Chris@335 847 if (points.begin()->frame != m_editingPoint.frame ||
Chris@335 848 points.begin()->value != m_editingPoint.value) return;
Chris@335 849
Chris@335 850 m_editingCommand = new NoteModel::EditCommand
Chris@335 851 (m_model, tr("Erase Point"));
Chris@335 852
Chris@335 853 m_editingCommand->deletePoint(m_editingPoint);
Chris@335 854
Chris@376 855 finish(m_editingCommand);
Chris@335 856 m_editingCommand = 0;
Chris@335 857 m_editing = false;
Chris@335 858 }
Chris@335 859
Chris@335 860 void
Chris@44 861 NoteLayer::editStart(View *v, QMouseEvent *e)
Chris@30 862 {
Chris@101 863 // std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 864
Chris@30 865 if (!m_model) return;
Chris@30 866
Chris@44 867 NoteModel::PointList points = getLocalPoints(v, e->x());
Chris@30 868 if (points.empty()) return;
Chris@30 869
Chris@30 870 m_editingPoint = *points.begin();
Chris@30 871 m_originalPoint = m_editingPoint;
Chris@30 872
Chris@30 873 if (m_editingCommand) {
Chris@376 874 finish(m_editingCommand);
Chris@30 875 m_editingCommand = 0;
Chris@30 876 }
Chris@30 877
Chris@30 878 m_editing = true;
Chris@30 879 }
Chris@30 880
Chris@30 881 void
Chris@44 882 NoteLayer::editDrag(View *v, QMouseEvent *e)
Chris@30 883 {
Chris@101 884 // std::cerr << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 885
Chris@30 886 if (!m_model || !m_editing) return;
Chris@30 887
Chris@44 888 long frame = v->getFrameForX(e->x());
Chris@30 889 if (frame < 0) frame = 0;
Chris@30 890 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@30 891
Chris@44 892 float value = getValueForY(v, e->y());
Chris@30 893
Chris@30 894 if (!m_editingCommand) {
Chris@30 895 m_editingCommand = new NoteModel::EditCommand(m_model,
Chris@30 896 tr("Drag Point"));
Chris@30 897 }
Chris@30 898
Chris@30 899 m_editingCommand->deletePoint(m_editingPoint);
Chris@30 900 m_editingPoint.frame = frame;
Chris@30 901 m_editingPoint.value = value;
Chris@30 902 m_editingCommand->addPoint(m_editingPoint);
Chris@30 903 }
Chris@30 904
Chris@30 905 void
Chris@248 906 NoteLayer::editEnd(View *, QMouseEvent *)
Chris@30 907 {
Chris@101 908 // std::cerr << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 909 if (!m_model || !m_editing) return;
Chris@30 910
Chris@30 911 if (m_editingCommand) {
Chris@30 912
Chris@30 913 QString newName = m_editingCommand->getName();
Chris@30 914
Chris@30 915 if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@30 916 if (m_editingPoint.value != m_originalPoint.value) {
Chris@30 917 newName = tr("Edit Point");
Chris@30 918 } else {
Chris@30 919 newName = tr("Relocate Point");
Chris@30 920 }
Chris@30 921 } else {
Chris@30 922 newName = tr("Change Point Value");
Chris@30 923 }
Chris@30 924
Chris@30 925 m_editingCommand->setName(newName);
Chris@376 926 finish(m_editingCommand);
Chris@30 927 }
Chris@30 928
Chris@30 929 m_editingCommand = 0;
Chris@30 930 m_editing = false;
Chris@30 931 }
Chris@30 932
Chris@255 933 bool
Chris@70 934 NoteLayer::editOpen(View *v, QMouseEvent *e)
Chris@70 935 {
Chris@255 936 if (!m_model) return false;
Chris@70 937
Chris@70 938 NoteModel::PointList points = getLocalPoints(v, e->x());
Chris@255 939 if (points.empty()) return false;
Chris@70 940
Chris@70 941 NoteModel::Point note = *points.begin();
Chris@70 942
Chris@70 943 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 944 (m_model->getSampleRate(),
Chris@70 945 ItemEditDialog::ShowTime |
Chris@70 946 ItemEditDialog::ShowDuration |
Chris@70 947 ItemEditDialog::ShowValue |
Chris@100 948 ItemEditDialog::ShowText,
Chris@100 949 m_model->getScaleUnits());
Chris@70 950
Chris@70 951 dialog->setFrameTime(note.frame);
Chris@70 952 dialog->setValue(note.value);
Chris@70 953 dialog->setFrameDuration(note.duration);
Chris@70 954 dialog->setText(note.label);
Chris@70 955
Chris@70 956 if (dialog->exec() == QDialog::Accepted) {
Chris@70 957
Chris@70 958 NoteModel::Point newNote = note;
Chris@70 959 newNote.frame = dialog->getFrameTime();
Chris@70 960 newNote.value = dialog->getValue();
Chris@70 961 newNote.duration = dialog->getFrameDuration();
Chris@70 962 newNote.label = dialog->getText();
Chris@70 963
Chris@70 964 NoteModel::EditCommand *command = new NoteModel::EditCommand
Chris@70 965 (m_model, tr("Edit Point"));
Chris@70 966 command->deletePoint(note);
Chris@70 967 command->addPoint(newNote);
Chris@376 968 finish(command);
Chris@70 969 }
Chris@70 970
Chris@70 971 delete dialog;
Chris@255 972 return true;
Chris@70 973 }
Chris@70 974
Chris@70 975 void
Chris@43 976 NoteLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@43 977 {
Chris@99 978 if (!m_model) return;
Chris@99 979
Chris@43 980 NoteModel::EditCommand *command =
Chris@43 981 new NoteModel::EditCommand(m_model, tr("Drag Selection"));
Chris@43 982
Chris@43 983 NoteModel::PointList points =
Chris@43 984 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 985
Chris@43 986 for (NoteModel::PointList::iterator i = points.begin();
Chris@43 987 i != points.end(); ++i) {
Chris@43 988
Chris@43 989 if (s.contains(i->frame)) {
Chris@43 990 NoteModel::Point newPoint(*i);
Chris@43 991 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@43 992 command->deletePoint(*i);
Chris@43 993 command->addPoint(newPoint);
Chris@43 994 }
Chris@43 995 }
Chris@43 996
Chris@376 997 finish(command);
Chris@43 998 }
Chris@43 999
Chris@43 1000 void
Chris@43 1001 NoteLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 1002 {
Chris@99 1003 if (!m_model) return;
Chris@99 1004
Chris@43 1005 NoteModel::EditCommand *command =
Chris@43 1006 new NoteModel::EditCommand(m_model, tr("Resize Selection"));
Chris@43 1007
Chris@43 1008 NoteModel::PointList points =
Chris@43 1009 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1010
Chris@43 1011 double ratio =
Chris@43 1012 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@43 1013 double(s.getEndFrame() - s.getStartFrame());
Chris@43 1014
Chris@43 1015 for (NoteModel::PointList::iterator i = points.begin();
Chris@43 1016 i != points.end(); ++i) {
Chris@43 1017
Chris@43 1018 if (s.contains(i->frame)) {
Chris@43 1019
Chris@43 1020 double targetStart = i->frame;
Chris@43 1021 targetStart = newSize.getStartFrame() +
Chris@43 1022 double(targetStart - s.getStartFrame()) * ratio;
Chris@43 1023
Chris@43 1024 double targetEnd = i->frame + i->duration;
Chris@43 1025 targetEnd = newSize.getStartFrame() +
Chris@43 1026 double(targetEnd - s.getStartFrame()) * ratio;
Chris@43 1027
Chris@43 1028 NoteModel::Point newPoint(*i);
Chris@43 1029 newPoint.frame = lrint(targetStart);
Chris@43 1030 newPoint.duration = lrint(targetEnd - targetStart);
Chris@43 1031 command->deletePoint(*i);
Chris@43 1032 command->addPoint(newPoint);
Chris@43 1033 }
Chris@43 1034 }
Chris@43 1035
Chris@376 1036 finish(command);
Chris@43 1037 }
Chris@43 1038
Chris@76 1039 void
Chris@76 1040 NoteLayer::deleteSelection(Selection s)
Chris@76 1041 {
Chris@99 1042 if (!m_model) return;
Chris@99 1043
Chris@76 1044 NoteModel::EditCommand *command =
Chris@76 1045 new NoteModel::EditCommand(m_model, tr("Delete Selected Points"));
Chris@76 1046
Chris@76 1047 NoteModel::PointList points =
Chris@76 1048 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1049
Chris@76 1050 for (NoteModel::PointList::iterator i = points.begin();
Chris@76 1051 i != points.end(); ++i) {
Chris@76 1052
Chris@76 1053 if (s.contains(i->frame)) {
Chris@76 1054 command->deletePoint(*i);
Chris@76 1055 }
Chris@76 1056 }
Chris@76 1057
Chris@376 1058 finish(command);
Chris@76 1059 }
Chris@76 1060
Chris@76 1061 void
Chris@359 1062 NoteLayer::copy(View *v, Selection s, Clipboard &to)
Chris@76 1063 {
Chris@99 1064 if (!m_model) return;
Chris@99 1065
Chris@76 1066 NoteModel::PointList points =
Chris@76 1067 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1068
Chris@76 1069 for (NoteModel::PointList::iterator i = points.begin();
Chris@76 1070 i != points.end(); ++i) {
Chris@76 1071 if (s.contains(i->frame)) {
Chris@335 1072 Clipboard::Point point(i->frame, i->value, i->duration, i->level, i->label);
Chris@360 1073 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@76 1074 to.addPoint(point);
Chris@76 1075 }
Chris@76 1076 }
Chris@76 1077 }
Chris@76 1078
Chris@125 1079 bool
Chris@359 1080 NoteLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */)
Chris@76 1081 {
Chris@125 1082 if (!m_model) return false;
Chris@99 1083
Chris@76 1084 const Clipboard::PointList &points = from.getPoints();
Chris@76 1085
Chris@360 1086 bool realign = false;
Chris@360 1087
Chris@360 1088 if (clipboardHasDifferentAlignment(v, from)) {
Chris@360 1089
Chris@360 1090 QMessageBox::StandardButton button =
Chris@360 1091 QMessageBox::question(v, tr("Re-align pasted items?"),
Chris@360 1092 tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"),
Chris@360 1093 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 1094 QMessageBox::Yes);
Chris@360 1095
Chris@360 1096 if (button == QMessageBox::Cancel) {
Chris@360 1097 return false;
Chris@360 1098 }
Chris@360 1099
Chris@360 1100 if (button == QMessageBox::Yes) {
Chris@360 1101 realign = true;
Chris@360 1102 }
Chris@360 1103 }
Chris@360 1104
Chris@76 1105 NoteModel::EditCommand *command =
Chris@76 1106 new NoteModel::EditCommand(m_model, tr("Paste"));
Chris@76 1107
Chris@76 1108 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 1109 i != points.end(); ++i) {
Chris@76 1110
Chris@76 1111 if (!i->haveFrame()) continue;
Chris@76 1112 size_t frame = 0;
Chris@360 1113
Chris@360 1114 if (!realign) {
Chris@360 1115
Chris@360 1116 frame = i->getFrame();
Chris@360 1117
Chris@360 1118 } else {
Chris@360 1119
Chris@360 1120 if (i->haveReferenceFrame()) {
Chris@360 1121 frame = i->getReferenceFrame();
Chris@360 1122 frame = alignFromReference(v, frame);
Chris@360 1123 } else {
Chris@360 1124 frame = i->getFrame();
Chris@360 1125 }
Chris@76 1126 }
Chris@360 1127
Chris@76 1128 NoteModel::Point newPoint(frame);
Chris@76 1129
Chris@76 1130 if (i->haveLabel()) newPoint.label = i->getLabel();
Chris@76 1131 if (i->haveValue()) newPoint.value = i->getValue();
Chris@76 1132 else newPoint.value = (m_model->getValueMinimum() +
Chris@76 1133 m_model->getValueMaximum()) / 2;
Chris@335 1134 if (i->haveLevel()) newPoint.level = i->getLevel();
Chris@76 1135 if (i->haveDuration()) newPoint.duration = i->getDuration();
Chris@125 1136 else {
Chris@125 1137 size_t nextFrame = frame;
Chris@125 1138 Clipboard::PointList::const_iterator j = i;
Chris@125 1139 for (; j != points.end(); ++j) {
Chris@125 1140 if (!j->haveFrame()) continue;
Chris@125 1141 if (j != i) break;
Chris@125 1142 }
Chris@125 1143 if (j != points.end()) {
Chris@125 1144 nextFrame = j->getFrame();
Chris@125 1145 }
Chris@125 1146 if (nextFrame == frame) {
Chris@125 1147 newPoint.duration = m_model->getResolution();
Chris@125 1148 } else {
Chris@125 1149 newPoint.duration = nextFrame - frame;
Chris@125 1150 }
Chris@125 1151 }
Chris@76 1152
Chris@76 1153 command->addPoint(newPoint);
Chris@76 1154 }
Chris@76 1155
Chris@376 1156 finish(command);
Chris@125 1157 return true;
Chris@76 1158 }
Chris@76 1159
Chris@287 1160 int
Chris@287 1161 NoteLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 1162 {
Chris@287 1163 impose = false;
Chris@287 1164 return ColourDatabase::getInstance()->getColourIndex
Chris@287 1165 (QString(darkbg ? "White" : "Black"));
Chris@287 1166 }
Chris@287 1167
Chris@316 1168 void
Chris@316 1169 NoteLayer::toXml(QTextStream &stream,
Chris@316 1170 QString indent, QString extraAttributes) const
Chris@30 1171 {
Chris@316 1172 SingleColourLayer::toXml(stream, indent, extraAttributes +
Chris@445 1173 QString(" verticalScale=\"%1\" scaleMinimum=\"%2\" scaleMaximum=\"%3\" ")
Chris@445 1174 .arg(m_verticalScale)
Chris@445 1175 .arg(m_scaleMinimum)
Chris@445 1176 .arg(m_scaleMaximum));
Chris@30 1177 }
Chris@30 1178
Chris@30 1179 void
Chris@30 1180 NoteLayer::setProperties(const QXmlAttributes &attributes)
Chris@30 1181 {
Chris@287 1182 SingleColourLayer::setProperties(attributes);
Chris@30 1183
Chris@445 1184 bool ok, alsoOk;
Chris@30 1185 VerticalScale scale = (VerticalScale)
Chris@30 1186 attributes.value("verticalScale").toInt(&ok);
Chris@30 1187 if (ok) setVerticalScale(scale);
Chris@445 1188
Chris@445 1189 float min = attributes.value("scaleMinimum").toFloat(&ok);
Chris@445 1190 float max = attributes.value("scaleMaximum").toFloat(&alsoOk);
Chris@445 1191 if (ok && alsoOk) setDisplayExtents(min, max);
Chris@30 1192 }
Chris@30 1193
Chris@30 1194