NoteLayer.cpp

Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 Chris Cannam.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "NoteLayer.h"
00017 
00018 #include "data/model/Model.h"
00019 #include "base/RealTime.h"
00020 #include "base/Profiler.h"
00021 #include "base/Pitch.h"
00022 #include "base/LogRange.h"
00023 #include "base/ColourDatabase.h"
00024 #include "view/View.h"
00025 
00026 #include "data/model/NoteModel.h"
00027 
00028 #include "widgets/ItemEditDialog.h"
00029 
00030 #include "SpectrogramLayer.h" // for optional frequency alignment
00031 
00032 #include <QPainter>
00033 #include <QPainterPath>
00034 #include <QMouseEvent>
00035 #include <QTextStream>
00036 #include <QMessageBox>
00037 
00038 #include <iostream>
00039 #include <cmath>
00040 
00041 NoteLayer::NoteLayer() :
00042     SingleColourLayer(),
00043     m_model(0),
00044     m_editing(false),
00045     m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")),
00046     m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")),
00047     m_editingCommand(0),
00048     m_verticalScale(AutoAlignScale)
00049 {
00050     
00051 }
00052 
00053 void
00054 NoteLayer::setModel(NoteModel *model)
00055 {
00056     if (m_model == model) return;
00057     m_model = model;
00058 
00059     connectSignals(m_model);
00060 
00061 //    std::cerr << "NoteLayer::setModel(" << model << ")" << std::endl;
00062 
00063     emit modelReplaced();
00064 }
00065 
00066 Layer::PropertyList
00067 NoteLayer::getProperties() const
00068 {
00069     PropertyList list = SingleColourLayer::getProperties();
00070     list.push_back("Vertical Scale");
00071     list.push_back("Scale Units");
00072     return list;
00073 }
00074 
00075 QString
00076 NoteLayer::getPropertyLabel(const PropertyName &name) const
00077 {
00078     if (name == "Vertical Scale") return tr("Vertical Scale");
00079     if (name == "Scale Units") return tr("Scale Units");
00080     return SingleColourLayer::getPropertyLabel(name);
00081 }
00082 
00083 Layer::PropertyType
00084 NoteLayer::getPropertyType(const PropertyName &name) const
00085 {
00086     if (name == "Scale Units") return UnitsProperty;
00087     if (name == "Vertical Scale") return ValueProperty;
00088     return SingleColourLayer::getPropertyType(name);
00089 }
00090 
00091 QString
00092 NoteLayer::getPropertyGroupName(const PropertyName &name) const
00093 {
00094     if (name == "Vertical Scale" || name == "Scale Units") {
00095         return tr("Scale");
00096     }
00097     return SingleColourLayer::getPropertyGroupName(name);
00098 }
00099 
00100 int
00101 NoteLayer::getPropertyRangeAndValue(const PropertyName &name,
00102                                     int *min, int *max, int *deflt) const
00103 {
00104     int val = 0;
00105 
00106     if (name == "Vertical Scale") {
00107         
00108         if (min) *min = 0;
00109         if (max) *max = 3;
00110         if (deflt) *deflt = int(AutoAlignScale);
00111         
00112         val = int(m_verticalScale);
00113 
00114     } else if (name == "Scale Units") {
00115 
00116         if (deflt) *deflt = 0;
00117         if (m_model) {
00118             val = UnitDatabase::getInstance()->getUnitId
00119                 (m_model->getScaleUnits());
00120         }
00121 
00122     } else {
00123 
00124         val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
00125     }
00126 
00127     return val;
00128 }
00129 
00130 QString
00131 NoteLayer::getPropertyValueLabel(const PropertyName &name,
00132                                  int value) const
00133 {
00134     if (name == "Vertical Scale") {
00135         switch (value) {
00136         default:
00137         case 0: return tr("Auto-Align");
00138         case 1: return tr("Linear");
00139         case 2: return tr("Log");
00140         case 3: return tr("MIDI Notes");
00141         }
00142     }
00143     return SingleColourLayer::getPropertyValueLabel(name, value);
00144 }
00145 
00146 void
00147 NoteLayer::setProperty(const PropertyName &name, int value)
00148 {
00149     if (name == "Vertical Scale") {
00150         setVerticalScale(VerticalScale(value));
00151     } else if (name == "Scale Units") {
00152         if (m_model) {
00153             m_model->setScaleUnits
00154                 (UnitDatabase::getInstance()->getUnitById(value));
00155             emit modelChanged();
00156         }
00157     } else {
00158         return SingleColourLayer::setProperty(name, value);
00159     }
00160 }
00161 
00162 void
00163 NoteLayer::setVerticalScale(VerticalScale scale)
00164 {
00165     if (m_verticalScale == scale) return;
00166     m_verticalScale = scale;
00167     emit layerParametersChanged();
00168 }
00169 
00170 bool
00171 NoteLayer::isLayerScrollable(const View *v) const
00172 {
00173     QPoint discard;
00174     return !v->shouldIlluminateLocalFeatures(this, discard);
00175 }
00176 
00177 bool
00178 NoteLayer::shouldConvertMIDIToHz() const
00179 {
00180     QString unit = m_model->getScaleUnits();
00181     return (unit != "Hz");
00182 //    if (unit == "" ||
00183 //        unit.startsWith("MIDI") ||
00184 //        unit.startsWith("midi")) return true;
00185 //    return false;
00186 }
00187 
00188 bool
00189 NoteLayer::getValueExtents(float &min, float &max,
00190                            bool &logarithmic, QString &unit) const
00191 {
00192     if (!m_model) return false;
00193     min = m_model->getValueMinimum();
00194     max = m_model->getValueMaximum();
00195 
00196     if (shouldConvertMIDIToHz()) {
00197         unit = "Hz";
00198         min = Pitch::getFrequencyForPitch(lrintf(min));
00199         max = Pitch::getFrequencyForPitch(lrintf(max + 1));
00200     } else unit = m_model->getScaleUnits();
00201 
00202     if (m_verticalScale == MIDIRangeScale ||
00203         m_verticalScale == LogScale) logarithmic = true;
00204 
00205     return true;
00206 }
00207 
00208 bool
00209 NoteLayer::getDisplayExtents(float &min, float &max) const
00210 {
00211     if (!m_model || m_verticalScale == AutoAlignScale) return false;
00212 
00213     if (m_verticalScale == MIDIRangeScale) {
00214         min = Pitch::getFrequencyForPitch(0);
00215         max = Pitch::getFrequencyForPitch(127);
00216         return true;
00217     }
00218 
00219     min = m_model->getValueMinimum();
00220     max = m_model->getValueMaximum();
00221 
00222     if (shouldConvertMIDIToHz()) {
00223         min = Pitch::getFrequencyForPitch(lrintf(min));
00224         max = Pitch::getFrequencyForPitch(lrintf(max + 1));
00225     }
00226 
00227     return true;
00228 }
00229 
00230 NoteModel::PointList
00231 NoteLayer::getLocalPoints(View *v, int x) const
00232 {
00233     if (!m_model) return NoteModel::PointList();
00234 
00235     long frame = v->getFrameForX(x);
00236 
00237     NoteModel::PointList onPoints =
00238         m_model->getPoints(frame);
00239 
00240     if (!onPoints.empty()) {
00241         return onPoints;
00242     }
00243 
00244     NoteModel::PointList prevPoints =
00245         m_model->getPreviousPoints(frame);
00246     NoteModel::PointList nextPoints =
00247         m_model->getNextPoints(frame);
00248 
00249     NoteModel::PointList usePoints = prevPoints;
00250 
00251     if (prevPoints.empty()) {
00252         usePoints = nextPoints;
00253     } else if (long(prevPoints.begin()->frame) < v->getStartFrame() &&
00254                !(nextPoints.begin()->frame > v->getEndFrame())) {
00255         usePoints = nextPoints;
00256     } else if (long(nextPoints.begin()->frame) - frame <
00257                frame - long(prevPoints.begin()->frame)) {
00258         usePoints = nextPoints;
00259     }
00260 
00261     if (!usePoints.empty()) {
00262         int fuzz = 2;
00263         int px = v->getXForFrame(usePoints.begin()->frame);
00264         if ((px > x && px - x > fuzz) ||
00265             (px < x && x - px > fuzz + 1)) {
00266             usePoints.clear();
00267         }
00268     }
00269 
00270     return usePoints;
00271 }
00272 
00273 QString
00274 NoteLayer::getFeatureDescription(View *v, QPoint &pos) const
00275 {
00276     int x = pos.x();
00277 
00278     if (!m_model || !m_model->getSampleRate()) return "";
00279 
00280     NoteModel::PointList points = getLocalPoints(v, x);
00281 
00282     if (points.empty()) {
00283         if (!m_model->isReady()) {
00284             return tr("In progress");
00285         } else {
00286             return tr("No local points");
00287         }
00288     }
00289 
00290     Note note(0);
00291     NoteModel::PointList::iterator i;
00292 
00293     for (i = points.begin(); i != points.end(); ++i) {
00294 
00295         int y = getYForValue(v, i->value);
00296         int h = 3;
00297 
00298         if (m_model->getValueQuantization() != 0.0) {
00299             h = y - getYForValue(v, i->value + m_model->getValueQuantization());
00300             if (h < 3) h = 3;
00301         }
00302 
00303         if (pos.y() >= y - h && pos.y() <= y) {
00304             note = *i;
00305             break;
00306         }
00307     }
00308 
00309     if (i == points.end()) return tr("No local points");
00310 
00311     RealTime rt = RealTime::frame2RealTime(note.frame,
00312                                            m_model->getSampleRate());
00313     RealTime rd = RealTime::frame2RealTime(note.duration,
00314                                            m_model->getSampleRate());
00315     
00316     QString pitchText;
00317 
00318     if (shouldConvertMIDIToHz()) {
00319 
00320         int mnote = lrintf(note.value);
00321         int cents = lrintf((note.value - mnote) * 100);
00322         float freq = Pitch::getFrequencyForPitch(mnote, cents);
00323         pitchText = tr("%1 (%2 Hz)")
00324             .arg(Pitch::getPitchLabel(mnote, cents)).arg(freq);
00325 
00326     } else if (m_model->getScaleUnits() == "Hz") {
00327 
00328         pitchText = tr("%1 Hz (%2)")
00329             .arg(note.value)
00330             .arg(Pitch::getPitchLabelForFrequency(note.value));
00331 
00332     } else {
00333         pitchText = tr("%1 %2")
00334             .arg(note.value).arg(m_model->getScaleUnits());
00335     }
00336 
00337     QString text;
00338 
00339     if (note.label == "") {
00340         text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label"))
00341             .arg(rt.toText(true).c_str())
00342             .arg(pitchText)
00343             .arg(rd.toText(true).c_str());
00344     } else {
00345         text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4"))
00346             .arg(rt.toText(true).c_str())
00347             .arg(pitchText)
00348             .arg(rd.toText(true).c_str())
00349             .arg(note.label);
00350     }
00351 
00352     pos = QPoint(v->getXForFrame(note.frame),
00353                  getYForValue(v, note.value));
00354     return text;
00355 }
00356 
00357 bool
00358 NoteLayer::snapToFeatureFrame(View *v, int &frame,
00359                               size_t &resolution,
00360                               SnapType snap) const
00361 {
00362     if (!m_model) {
00363         return Layer::snapToFeatureFrame(v, frame, resolution, snap);
00364     }
00365 
00366     resolution = m_model->getResolution();
00367     NoteModel::PointList points;
00368 
00369     if (snap == SnapNeighbouring) {
00370         
00371         points = getLocalPoints(v, v->getXForFrame(frame));
00372         if (points.empty()) return false;
00373         frame = points.begin()->frame;
00374         return true;
00375     }    
00376 
00377     points = m_model->getPoints(frame, frame);
00378     int snapped = frame;
00379     bool found = false;
00380 
00381     for (NoteModel::PointList::const_iterator i = points.begin();
00382          i != points.end(); ++i) {
00383 
00384         if (snap == SnapRight) {
00385 
00386             if (i->frame > frame) {
00387                 snapped = i->frame;
00388                 found = true;
00389                 break;
00390             }
00391 
00392         } else if (snap == SnapLeft) {
00393 
00394             if (i->frame <= frame) {
00395                 snapped = i->frame;
00396                 found = true; // don't break, as the next may be better
00397             } else {
00398                 break;
00399             }
00400 
00401         } else { // nearest
00402 
00403             NoteModel::PointList::const_iterator j = i;
00404             ++j;
00405 
00406             if (j == points.end()) {
00407 
00408                 snapped = i->frame;
00409                 found = true;
00410                 break;
00411 
00412             } else if (j->frame >= frame) {
00413 
00414                 if (j->frame - frame < frame - i->frame) {
00415                     snapped = j->frame;
00416                 } else {
00417                     snapped = i->frame;
00418                 }
00419                 found = true;
00420                 break;
00421             }
00422         }
00423     }
00424 
00425     frame = snapped;
00426     return found;
00427 }
00428 
00429 void
00430 NoteLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
00431 {
00432     min = 0.0;
00433     max = 0.0;
00434     log = false;
00435 
00436     QString queryUnits;
00437     if (shouldConvertMIDIToHz()) queryUnits = "Hz";
00438     else queryUnits = m_model->getScaleUnits();
00439 
00440     if (m_verticalScale == AutoAlignScale) {
00441 
00442         if (!v->getValueExtents(queryUnits, min, max, log)) {
00443 
00444             min = m_model->getValueMinimum();
00445             max = m_model->getValueMaximum();
00446 
00447             if (shouldConvertMIDIToHz()) {
00448                 min = Pitch::getFrequencyForPitch(lrintf(min));
00449                 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
00450             }
00451 
00452             std::cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
00453 
00454         } else if (log) {
00455 
00456             LogRange::mapRange(min, max);
00457 
00458             std::cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
00459 
00460         }
00461 
00462     } else {
00463 
00464         min = m_model->getValueMinimum();
00465         max = m_model->getValueMaximum();
00466 
00467         if (m_verticalScale == MIDIRangeScale) {
00468             min = Pitch::getFrequencyForPitch(0);
00469             max = Pitch::getFrequencyForPitch(127);
00470         } else if (shouldConvertMIDIToHz()) {
00471             min = Pitch::getFrequencyForPitch(lrintf(min));
00472             max = Pitch::getFrequencyForPitch(lrintf(max + 1));
00473         }
00474 
00475         if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
00476             LogRange::mapRange(min, max);
00477             log = true;
00478         }
00479     }
00480 
00481     if (max == min) max = min + 1.0;
00482 }
00483 
00484 int
00485 NoteLayer::getYForValue(View *v, float val) const
00486 {
00487     float min = 0.0, max = 0.0;
00488     bool logarithmic = false;
00489     int h = v->height();
00490 
00491     getScaleExtents(v, min, max, logarithmic);
00492 
00493 //    std::cerr << "NoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << std::endl;
00494 
00495     if (shouldConvertMIDIToHz()) {
00496         val = Pitch::getFrequencyForPitch(lrintf(val),
00497                                           lrintf((val - lrintf(val)) * 100));
00498 //        std::cerr << "shouldConvertMIDIToHz true, val now = " << val << std::endl;
00499     }
00500 
00501     if (logarithmic) {
00502         val = LogRange::map(val);
00503 //        std::cerr << "logarithmic true, val now = " << val << std::endl;
00504     }
00505 
00506     int y = int(h - ((val - min) * h) / (max - min)) - 1;
00507 //    std::cerr << "y = " << y << std::endl;
00508     return y;
00509 }
00510 
00511 float
00512 NoteLayer::getValueForY(View *v, int y) const
00513 {
00514     float min = 0.0, max = 0.0;
00515     bool logarithmic = false;
00516     int h = v->height();
00517 
00518     getScaleExtents(v, min, max, logarithmic);
00519 
00520     float val = min + (float(h - y) * float(max - min)) / h;
00521 
00522     if (logarithmic) {
00523         val = powf(10.f, val);
00524     }
00525 
00526     if (shouldConvertMIDIToHz()) {
00527         val = Pitch::getPitchForFrequency(val);
00528     }
00529 
00530     return val;
00531 }
00532 
00533 void
00534 NoteLayer::paint(View *v, QPainter &paint, QRect rect) const
00535 {
00536     if (!m_model || !m_model->isOK()) return;
00537 
00538     int sampleRate = m_model->getSampleRate();
00539     if (!sampleRate) return;
00540 
00541 //    Profiler profiler("NoteLayer::paint", true);
00542 
00543     int x0 = rect.left(), x1 = rect.right();
00544     long frame0 = v->getFrameForX(x0);
00545     long frame1 = v->getFrameForX(x1);
00546 
00547     NoteModel::PointList points(m_model->getPoints(frame0, frame1));
00548     if (points.empty()) return;
00549 
00550     paint.setPen(getBaseQColor());
00551 
00552     QColor brushColour(getBaseQColor());
00553     brushColour.setAlpha(80);
00554 
00555 //    std::cerr << "NoteLayer::paint: resolution is "
00556 //            << m_model->getResolution() << " frames" << std::endl;
00557 
00558     float min = m_model->getValueMinimum();
00559     float max = m_model->getValueMaximum();
00560     if (max == min) max = min + 1.0;
00561 
00562     QPoint localPos;
00563     long illuminateFrame = -1;
00564 
00565     if (v->shouldIlluminateLocalFeatures(this, localPos)) {
00566         NoteModel::PointList localPoints =
00567             getLocalPoints(v, localPos.x());
00568         if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
00569     }
00570 
00571     paint.save();
00572     paint.setRenderHint(QPainter::Antialiasing, false);
00573     
00574     for (NoteModel::PointList::const_iterator i = points.begin();
00575          i != points.end(); ++i) {
00576 
00577         const NoteModel::Point &p(*i);
00578 
00579         int x = v->getXForFrame(p.frame);
00580         int y = getYForValue(v, p.value);
00581         int w = v->getXForFrame(p.frame + p.duration) - x;
00582         int h = 3;
00583         
00584         if (m_model->getValueQuantization() != 0.0) {
00585             h = y - getYForValue(v, p.value + m_model->getValueQuantization());
00586             if (h < 3) h = 3;
00587         }
00588 
00589         if (w < 1) w = 1;
00590         paint.setPen(getBaseQColor());
00591         paint.setBrush(brushColour);
00592 
00593         if (illuminateFrame == p.frame) {
00594             if (localPos.y() >= y - h && localPos.y() < y) {
00595                 paint.setPen(v->getForeground());
00596                 paint.setBrush(v->getForeground());
00597             }
00598         }
00599         
00600         paint.drawRect(x, y - h/2, w, h);
00601 
00605     }
00606 
00607     paint.restore();
00608 }
00609 
00610 void
00611 NoteLayer::drawStart(View *v, QMouseEvent *e)
00612 {
00613 //    std::cerr << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
00614 
00615     if (!m_model) return;
00616 
00617     long frame = v->getFrameForX(e->x());
00618     if (frame < 0) frame = 0;
00619     frame = frame / m_model->getResolution() * m_model->getResolution();
00620 
00621     float value = getValueForY(v, e->y());
00622 
00623     m_editingPoint = NoteModel::Point(frame, value, 0, 0.8, tr("New Point"));
00624     m_originalPoint = m_editingPoint;
00625 
00626     if (m_editingCommand) m_editingCommand->finish();
00627     m_editingCommand = new NoteModel::EditCommand(m_model,
00628                                                   tr("Draw Point"));
00629     m_editingCommand->addPoint(m_editingPoint);
00630 
00631     m_editing = true;
00632 }
00633 
00634 void
00635 NoteLayer::drawDrag(View *v, QMouseEvent *e)
00636 {
00637 //    std::cerr << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
00638 
00639     if (!m_model || !m_editing) return;
00640 
00641     long frame = v->getFrameForX(e->x());
00642     if (frame < 0) frame = 0;
00643     frame = frame / m_model->getResolution() * m_model->getResolution();
00644 
00645     float newValue = getValueForY(v, e->y());
00646 
00647     long newFrame = m_editingPoint.frame;
00648     long newDuration = frame - newFrame;
00649     if (newDuration < 0) {
00650         newFrame = frame;
00651         newDuration = -newDuration;
00652     } else if (newDuration == 0) {
00653         newDuration = 1;
00654     }
00655 
00656     m_editingCommand->deletePoint(m_editingPoint);
00657     m_editingPoint.frame = newFrame;
00658     m_editingPoint.value = newValue;
00659     m_editingPoint.duration = newDuration;
00660     m_editingCommand->addPoint(m_editingPoint);
00661 }
00662 
00663 void
00664 NoteLayer::drawEnd(View *, QMouseEvent *)
00665 {
00666 //    std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
00667     if (!m_model || !m_editing) return;
00668     m_editingCommand->finish();
00669     m_editingCommand = 0;
00670     m_editing = false;
00671 }
00672 
00673 void
00674 NoteLayer::eraseStart(View *v, QMouseEvent *e)
00675 {
00676     if (!m_model) return;
00677 
00678     NoteModel::PointList points = getLocalPoints(v, e->x());
00679     if (points.empty()) return;
00680 
00681     m_editingPoint = *points.begin();
00682 
00683     if (m_editingCommand) {
00684         m_editingCommand->finish();
00685         m_editingCommand = 0;
00686     }
00687 
00688     m_editing = true;
00689 }
00690 
00691 void
00692 NoteLayer::eraseDrag(View *v, QMouseEvent *e)
00693 {
00694 }
00695 
00696 void
00697 NoteLayer::eraseEnd(View *v, QMouseEvent *e)
00698 {
00699     if (!m_model || !m_editing) return;
00700 
00701     m_editing = false;
00702 
00703     NoteModel::PointList points = getLocalPoints(v, e->x());
00704     if (points.empty()) return;
00705     if (points.begin()->frame != m_editingPoint.frame ||
00706         points.begin()->value != m_editingPoint.value) return;
00707 
00708     m_editingCommand = new NoteModel::EditCommand
00709         (m_model, tr("Erase Point"));
00710 
00711     m_editingCommand->deletePoint(m_editingPoint);
00712 
00713     m_editingCommand->finish();
00714     m_editingCommand = 0;
00715     m_editing = false;
00716 }
00717 
00718 void
00719 NoteLayer::editStart(View *v, QMouseEvent *e)
00720 {
00721 //    std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
00722 
00723     if (!m_model) return;
00724 
00725     NoteModel::PointList points = getLocalPoints(v, e->x());
00726     if (points.empty()) return;
00727 
00728     m_editingPoint = *points.begin();
00729     m_originalPoint = m_editingPoint;
00730 
00731     if (m_editingCommand) {
00732         m_editingCommand->finish();
00733         m_editingCommand = 0;
00734     }
00735 
00736     m_editing = true;
00737 }
00738 
00739 void
00740 NoteLayer::editDrag(View *v, QMouseEvent *e)
00741 {
00742 //    std::cerr << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
00743 
00744     if (!m_model || !m_editing) return;
00745 
00746     long frame = v->getFrameForX(e->x());
00747     if (frame < 0) frame = 0;
00748     frame = frame / m_model->getResolution() * m_model->getResolution();
00749 
00750     float value = getValueForY(v, e->y());
00751 
00752     if (!m_editingCommand) {
00753         m_editingCommand = new NoteModel::EditCommand(m_model,
00754                                                       tr("Drag Point"));
00755     }
00756 
00757     m_editingCommand->deletePoint(m_editingPoint);
00758     m_editingPoint.frame = frame;
00759     m_editingPoint.value = value;
00760     m_editingCommand->addPoint(m_editingPoint);
00761 }
00762 
00763 void
00764 NoteLayer::editEnd(View *, QMouseEvent *)
00765 {
00766 //    std::cerr << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
00767     if (!m_model || !m_editing) return;
00768 
00769     if (m_editingCommand) {
00770 
00771         QString newName = m_editingCommand->getName();
00772 
00773         if (m_editingPoint.frame != m_originalPoint.frame) {
00774             if (m_editingPoint.value != m_originalPoint.value) {
00775                 newName = tr("Edit Point");
00776             } else {
00777                 newName = tr("Relocate Point");
00778             }
00779         } else {
00780             newName = tr("Change Point Value");
00781         }
00782 
00783         m_editingCommand->setName(newName);
00784         m_editingCommand->finish();
00785     }
00786 
00787     m_editingCommand = 0;
00788     m_editing = false;
00789 }
00790 
00791 bool
00792 NoteLayer::editOpen(View *v, QMouseEvent *e)
00793 {
00794     if (!m_model) return false;
00795 
00796     NoteModel::PointList points = getLocalPoints(v, e->x());
00797     if (points.empty()) return false;
00798 
00799     NoteModel::Point note = *points.begin();
00800 
00801     ItemEditDialog *dialog = new ItemEditDialog
00802         (m_model->getSampleRate(),
00803          ItemEditDialog::ShowTime |
00804          ItemEditDialog::ShowDuration |
00805          ItemEditDialog::ShowValue |
00806          ItemEditDialog::ShowText,
00807          m_model->getScaleUnits());
00808 
00809     dialog->setFrameTime(note.frame);
00810     dialog->setValue(note.value);
00811     dialog->setFrameDuration(note.duration);
00812     dialog->setText(note.label);
00813 
00814     if (dialog->exec() == QDialog::Accepted) {
00815 
00816         NoteModel::Point newNote = note;
00817         newNote.frame = dialog->getFrameTime();
00818         newNote.value = dialog->getValue();
00819         newNote.duration = dialog->getFrameDuration();
00820         newNote.label = dialog->getText();
00821         
00822         NoteModel::EditCommand *command = new NoteModel::EditCommand
00823             (m_model, tr("Edit Point"));
00824         command->deletePoint(note);
00825         command->addPoint(newNote);
00826         command->finish();
00827     }
00828 
00829     delete dialog;
00830     return true;
00831 }
00832 
00833 void
00834 NoteLayer::moveSelection(Selection s, size_t newStartFrame)
00835 {
00836     if (!m_model) return;
00837 
00838     NoteModel::EditCommand *command =
00839         new NoteModel::EditCommand(m_model, tr("Drag Selection"));
00840 
00841     NoteModel::PointList points =
00842         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
00843 
00844     for (NoteModel::PointList::iterator i = points.begin();
00845          i != points.end(); ++i) {
00846 
00847         if (s.contains(i->frame)) {
00848             NoteModel::Point newPoint(*i);
00849             newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
00850             command->deletePoint(*i);
00851             command->addPoint(newPoint);
00852         }
00853     }
00854 
00855     command->finish();
00856 }
00857 
00858 void
00859 NoteLayer::resizeSelection(Selection s, Selection newSize)
00860 {
00861     if (!m_model) return;
00862 
00863     NoteModel::EditCommand *command =
00864         new NoteModel::EditCommand(m_model, tr("Resize Selection"));
00865 
00866     NoteModel::PointList points =
00867         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
00868 
00869     double ratio =
00870         double(newSize.getEndFrame() - newSize.getStartFrame()) /
00871         double(s.getEndFrame() - s.getStartFrame());
00872 
00873     for (NoteModel::PointList::iterator i = points.begin();
00874          i != points.end(); ++i) {
00875 
00876         if (s.contains(i->frame)) {
00877 
00878             double targetStart = i->frame;
00879             targetStart = newSize.getStartFrame() + 
00880                 double(targetStart - s.getStartFrame()) * ratio;
00881 
00882             double targetEnd = i->frame + i->duration;
00883             targetEnd = newSize.getStartFrame() +
00884                 double(targetEnd - s.getStartFrame()) * ratio;
00885 
00886             NoteModel::Point newPoint(*i);
00887             newPoint.frame = lrint(targetStart);
00888             newPoint.duration = lrint(targetEnd - targetStart);
00889             command->deletePoint(*i);
00890             command->addPoint(newPoint);
00891         }
00892     }
00893 
00894     command->finish();
00895 }
00896 
00897 void
00898 NoteLayer::deleteSelection(Selection s)
00899 {
00900     if (!m_model) return;
00901 
00902     NoteModel::EditCommand *command =
00903         new NoteModel::EditCommand(m_model, tr("Delete Selected Points"));
00904 
00905     NoteModel::PointList points =
00906         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
00907 
00908     for (NoteModel::PointList::iterator i = points.begin();
00909          i != points.end(); ++i) {
00910 
00911         if (s.contains(i->frame)) {
00912             command->deletePoint(*i);
00913         }
00914     }
00915 
00916     command->finish();
00917 }    
00918 
00919 void
00920 NoteLayer::copy(View *v, Selection s, Clipboard &to)
00921 {
00922     if (!m_model) return;
00923 
00924     NoteModel::PointList points =
00925         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
00926 
00927     for (NoteModel::PointList::iterator i = points.begin();
00928          i != points.end(); ++i) {
00929         if (s.contains(i->frame)) {
00930             Clipboard::Point point(i->frame, i->value, i->duration, i->level, i->label);
00931             point.setReferenceFrame(alignToReference(v, i->frame));
00932             to.addPoint(point);
00933         }
00934     }
00935 }
00936 
00937 bool
00938 NoteLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */)
00939 {
00940     if (!m_model) return false;
00941 
00942     const Clipboard::PointList &points = from.getPoints();
00943 
00944     bool realign = false;
00945 
00946     if (clipboardHasDifferentAlignment(v, from)) {
00947 
00948         QMessageBox::StandardButton button =
00949             QMessageBox::question(v, tr("Re-align pasted items?"),
00950                                   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?"),
00951                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
00952                                   QMessageBox::Yes);
00953 
00954         if (button == QMessageBox::Cancel) {
00955             return false;
00956         }
00957 
00958         if (button == QMessageBox::Yes) {
00959             realign = true;
00960         }
00961     }
00962 
00963     NoteModel::EditCommand *command =
00964         new NoteModel::EditCommand(m_model, tr("Paste"));
00965 
00966     for (Clipboard::PointList::const_iterator i = points.begin();
00967          i != points.end(); ++i) {
00968         
00969         if (!i->haveFrame()) continue;
00970         size_t frame = 0;
00971 
00972         if (!realign) {
00973             
00974             frame = i->getFrame();
00975 
00976         } else {
00977 
00978             if (i->haveReferenceFrame()) {
00979                 frame = i->getReferenceFrame();
00980                 frame = alignFromReference(v, frame);
00981             } else {
00982                 frame = i->getFrame();
00983             }
00984         }
00985 
00986         NoteModel::Point newPoint(frame);
00987   
00988         if (i->haveLabel()) newPoint.label = i->getLabel();
00989         if (i->haveValue()) newPoint.value = i->getValue();
00990         else newPoint.value = (m_model->getValueMinimum() +
00991                                m_model->getValueMaximum()) / 2;
00992         if (i->haveLevel()) newPoint.level = i->getLevel();
00993         if (i->haveDuration()) newPoint.duration = i->getDuration();
00994         else {
00995             size_t nextFrame = frame;
00996             Clipboard::PointList::const_iterator j = i;
00997             for (; j != points.end(); ++j) {
00998                 if (!j->haveFrame()) continue;
00999                 if (j != i) break;
01000             }
01001             if (j != points.end()) {
01002                 nextFrame = j->getFrame();
01003             }
01004             if (nextFrame == frame) {
01005                 newPoint.duration = m_model->getResolution();
01006             } else {
01007                 newPoint.duration = nextFrame - frame;
01008             }
01009         }
01010         
01011         command->addPoint(newPoint);
01012     }
01013 
01014     command->finish();
01015     return true;
01016 }
01017 
01018 int
01019 NoteLayer::getDefaultColourHint(bool darkbg, bool &impose)
01020 {
01021     impose = false;
01022     return ColourDatabase::getInstance()->getColourIndex
01023         (QString(darkbg ? "White" : "Black"));
01024 }
01025 
01026 void
01027 NoteLayer::toXml(QTextStream &stream,
01028                  QString indent, QString extraAttributes) const
01029 {
01030     SingleColourLayer::toXml(stream, indent, extraAttributes +
01031                              QString(" verticalScale=\"%1\"")
01032                              .arg(m_verticalScale));
01033 }
01034 
01035 void
01036 NoteLayer::setProperties(const QXmlAttributes &attributes)
01037 {
01038     SingleColourLayer::setProperties(attributes);
01039 
01040     bool ok;
01041     VerticalScale scale = (VerticalScale)
01042         attributes.value("verticalScale").toInt(&ok);
01043     if (ok) setVerticalScale(scale);
01044 }
01045 
01046 

Generated on Wed Feb 20 15:45:27 2008 for SonicVisualiser by  doxygen 1.5.1