svgui  1.9
FlexiNoteLayer.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 "FlexiNoteLayer.h"
00017 
00018 #include "data/model/Model.h"
00019 #include "data/model/SparseTimeValueModel.h"
00020 #include "base/RealTime.h"
00021 #include "base/Profiler.h"
00022 #include "base/Pitch.h"
00023 #include "base/LogRange.h"
00024 #include "base/RangeMapper.h"
00025 #include "ColourDatabase.h"
00026 #include "view/View.h"
00027 
00028 #include "PianoScale.h"
00029 #include "LinearNumericalScale.h"
00030 #include "LogNumericalScale.h"
00031 
00032 #include "data/model/FlexiNoteModel.h"
00033 
00034 #include "widgets/ItemEditDialog.h"
00035 #include "widgets/TextAbbrev.h"
00036 
00037 #include <QPainter>
00038 #include <QPainterPath>
00039 #include <QMouseEvent>
00040 #include <QTextStream>
00041 #include <QMessageBox>
00042 
00043 #include <iostream>
00044 #include <cmath>
00045 #include <utility>
00046 #include <limits> // GF: included to compile std::numerical_limits on linux
00047 #include <vector>
00048 
00049 
00050 FlexiNoteLayer::FlexiNoteLayer() :
00051     SingleColourLayer(),
00052 
00053     // m_model(0),
00054     // m_editing(false),
00055     // m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")),
00056     // m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")),
00057     // m_editingCommand(0),
00058     // m_verticalScale(AutoAlignScale),
00059     // m_scaleMinimum(0),
00060     // m_scaleMaximum(0)
00061 
00062     m_model(0),
00063     m_editing(false),
00064     m_intelligentActions(true),
00065     m_dragPointX(0),
00066     m_dragPointY(0),
00067     m_dragStartX(0),
00068     m_dragStartY(0),
00069     m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")),
00070     m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")),
00071     m_greatestLeftNeighbourFrame(0),
00072     m_smallestRightNeighbourFrame(0),
00073     m_editingCommand(0),
00074     m_verticalScale(AutoAlignScale),
00075     m_editMode(DragNote),
00076     m_scaleMinimum(34), 
00077     m_scaleMaximum(77)
00078 {
00079 }
00080 
00081 void
00082 FlexiNoteLayer::setModel(FlexiNoteModel *model) 
00083 {
00084     if (m_model == model) return;
00085     m_model = model;
00086 
00087     connectSignals(m_model);
00088 
00089     // m_scaleMinimum = 0;
00090     // m_scaleMaximum = 0;
00091 
00092     emit modelReplaced();
00093 }
00094 
00095 Layer::PropertyList
00096 FlexiNoteLayer::getProperties() const
00097 {
00098     PropertyList list = SingleColourLayer::getProperties();
00099     list.push_back("Vertical Scale");
00100     list.push_back("Scale Units");
00101     return list;
00102 }
00103 
00104 QString
00105 FlexiNoteLayer::getPropertyLabel(const PropertyName &name) const
00106 {
00107     if (name == "Vertical Scale") return tr("Vertical Scale");
00108     if (name == "Scale Units") return tr("Scale Units");
00109     return SingleColourLayer::getPropertyLabel(name);
00110 }
00111 
00112 Layer::PropertyType
00113 FlexiNoteLayer::getPropertyType(const PropertyName &name) const
00114 {
00115     if (name == "Scale Units") return UnitsProperty;
00116     if (name == "Vertical Scale") return ValueProperty;
00117     return SingleColourLayer::getPropertyType(name);
00118 }
00119 
00120 QString
00121 FlexiNoteLayer::getPropertyGroupName(const PropertyName &name) const
00122 {
00123     if (name == "Vertical Scale" || name == "Scale Units") {
00124         return tr("Scale");
00125     }
00126     return SingleColourLayer::getPropertyGroupName(name);
00127 }
00128 
00129 QString
00130 FlexiNoteLayer::getScaleUnits() const
00131 {
00132     if (m_model) return m_model->getScaleUnits();
00133     else return "";
00134 }
00135 
00136 int
00137 FlexiNoteLayer::getPropertyRangeAndValue(const PropertyName &name,
00138                                          int *min, int *max, int *deflt) const
00139 {
00140     int val = 0;
00141 
00142     if (name == "Vertical Scale") {
00143     
00144         if (min) *min = 0;
00145         if (max) *max = 3;
00146         if (deflt) *deflt = int(AutoAlignScale);
00147     
00148         val = int(m_verticalScale);
00149 
00150     } else if (name == "Scale Units") {
00151 
00152         if (deflt) *deflt = 0;
00153         if (m_model) {
00154             val = UnitDatabase::getInstance()->getUnitId
00155                 (getScaleUnits());
00156         }
00157 
00158     } else {
00159 
00160         val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
00161     }
00162 
00163     return val;
00164 }
00165 
00166 QString
00167 FlexiNoteLayer::getPropertyValueLabel(const PropertyName &name,
00168                                       int value) const
00169 {
00170     if (name == "Vertical Scale") {
00171         switch (value) {
00172         default:
00173         case 0: return tr("Auto-Align");
00174         case 1: return tr("Linear");
00175         case 2: return tr("Log");
00176         case 3: return tr("MIDI Notes");
00177         }
00178     }
00179     return SingleColourLayer::getPropertyValueLabel(name, value);
00180 }
00181 
00182 void
00183 FlexiNoteLayer::setProperty(const PropertyName &name, int value)
00184 {
00185     if (name == "Vertical Scale") {
00186         setVerticalScale(VerticalScale(value));
00187     } else if (name == "Scale Units") {
00188         if (m_model) {
00189             m_model->setScaleUnits
00190                 (UnitDatabase::getInstance()->getUnitById(value));
00191             emit modelChanged();
00192         }
00193     } else {
00194         return SingleColourLayer::setProperty(name, value);
00195     }
00196 }
00197 
00198 void
00199 FlexiNoteLayer::setVerticalScale(VerticalScale scale)
00200 {
00201     if (m_verticalScale == scale) return;
00202     m_verticalScale = scale;
00203     emit layerParametersChanged();
00204 }
00205 
00206 bool
00207 FlexiNoteLayer::isLayerScrollable(const View *v) const
00208 {
00209     QPoint discard;
00210     return !v->shouldIlluminateLocalFeatures(this, discard);
00211 }
00212 
00213 bool
00214 FlexiNoteLayer::shouldConvertMIDIToHz() const
00215 {
00216     QString unit = getScaleUnits();
00217     return (unit != "Hz");
00218 //    if (unit == "" ||
00219 //        unit.startsWith("MIDI") ||
00220 //        unit.startsWith("midi")) return true;
00221 //    return false;
00222 }
00223 
00224 bool
00225 FlexiNoteLayer::getValueExtents(float &min, float &max,
00226                                 bool &logarithmic, QString &unit) const
00227 {
00228     if (!m_model) return false;
00229     min = m_model->getValueMinimum();
00230     max = m_model->getValueMaximum();
00231 
00232     if (shouldConvertMIDIToHz()) {
00233         unit = "Hz";
00234         min = Pitch::getFrequencyForPitch(lrintf(min));
00235         max = Pitch::getFrequencyForPitch(lrintf(max + 1));
00236     } else unit = getScaleUnits();
00237 
00238     if (m_verticalScale == MIDIRangeScale ||
00239         m_verticalScale == LogScale) logarithmic = true;
00240 
00241     return true;
00242 }
00243 
00244 bool
00245 FlexiNoteLayer::getDisplayExtents(float &min, float &max) const
00246 {
00247     if (!m_model || shouldAutoAlign()) {
00248 //        std::cerr << "No model or shouldAutoAlign()" << std::endl;
00249         return false;
00250     }
00251 
00252     if (m_verticalScale == MIDIRangeScale) {
00253         min = Pitch::getFrequencyForPitch(0);
00254         max = Pitch::getFrequencyForPitch(127);
00255         return true;
00256     }
00257 
00258     if (m_scaleMinimum == m_scaleMaximum) {
00259         min = m_model->getValueMinimum();
00260         max = m_model->getValueMaximum();
00261     } else {
00262         min = m_scaleMinimum;
00263         max = m_scaleMaximum;
00264     }
00265 
00266     if (shouldConvertMIDIToHz()) {
00267         min = Pitch::getFrequencyForPitch(lrintf(min));
00268         max = Pitch::getFrequencyForPitch(lrintf(max + 1));
00269     }
00270 
00271 #ifdef DEBUG_NOTE_LAYER
00272     cerr << "NoteLayer::getDisplayExtents: min = " << min << ", max = " << max << " (m_scaleMinimum = " << m_scaleMinimum << ", m_scaleMaximum = " << m_scaleMaximum << ")" << endl;
00273 #endif
00274 
00275     return true;
00276 }
00277 
00278 bool
00279 FlexiNoteLayer::setDisplayExtents(float min, float max)
00280 {
00281     if (!m_model) return false;
00282 
00283     if (min == max) {
00284         if (min == 0.f) {
00285             max = 1.f;
00286         } else {
00287             max = min * 1.0001;
00288         }
00289     }
00290 
00291     m_scaleMinimum = min;
00292     m_scaleMaximum = max;
00293 
00294 #ifdef DEBUG_NOTE_LAYER
00295     cerr << "FlexiNoteLayer::setDisplayExtents: min = " << min << ", max = " << max << endl;
00296 #endif
00297     
00298     emit layerParametersChanged();
00299     return true;
00300 }
00301 
00302 int
00303 FlexiNoteLayer::getVerticalZoomSteps(int &defaultStep) const
00304 {
00305     if (shouldAutoAlign()) return 0;
00306     if (!m_model) return 0;
00307 
00308     defaultStep = 0;
00309     return 100;
00310 }
00311 
00312 int
00313 FlexiNoteLayer::getCurrentVerticalZoomStep() const
00314 {
00315     if (shouldAutoAlign()) return 0;
00316     if (!m_model) return 0;
00317 
00318     RangeMapper *mapper = getNewVerticalZoomRangeMapper();
00319     if (!mapper) return 0;
00320 
00321     float dmin, dmax;
00322     getDisplayExtents(dmin, dmax);
00323 
00324     int nr = mapper->getPositionForValue(dmax - dmin);
00325 
00326     delete mapper;
00327 
00328     return 100 - nr;
00329 }
00330 
00332 
00333 void
00334 FlexiNoteLayer::setVerticalZoomStep(int step)
00335 {
00336     if (shouldAutoAlign()) return;
00337     if (!m_model) return;
00338 
00339     RangeMapper *mapper = getNewVerticalZoomRangeMapper();
00340     if (!mapper) return;
00341     
00342     float min, max;
00343     bool logarithmic;
00344     QString unit;
00345     getValueExtents(min, max, logarithmic, unit);
00346     
00347     float dmin, dmax;
00348     getDisplayExtents(dmin, dmax);
00349 
00350     float newdist = mapper->getValueForPosition(100 - step);
00351 
00352     float newmin, newmax;
00353 
00354     if (logarithmic) {
00355 
00356         // see SpectrogramLayer::setVerticalZoomStep
00357 
00358         newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2;
00359         newmin = newmax - newdist;
00360 
00361 //        cerr << "newmin = " << newmin << ", newmax = " << newmax << endl;
00362 
00363     } else {
00364         float dmid = (dmax + dmin) / 2;
00365         newmin = dmid - newdist / 2;
00366         newmax = dmid + newdist / 2;
00367     }
00368 
00369     if (newmin < min) {
00370         newmax += (min - newmin);
00371         newmin = min;
00372     }
00373     if (newmax > max) {
00374         newmax = max;
00375     }
00376     
00377 #ifdef DEBUG_NOTE_LAYER
00378     cerr << "FlexiNoteLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl;
00379 #endif
00380 
00381     setDisplayExtents(newmin, newmax);
00382 }
00383 
00384 RangeMapper *
00385 FlexiNoteLayer::getNewVerticalZoomRangeMapper() const
00386 {
00387     if (!m_model) return 0;
00388     
00389     RangeMapper *mapper;
00390 
00391     float min, max;
00392     bool logarithmic;
00393     QString unit;
00394     getValueExtents(min, max, logarithmic, unit);
00395 
00396     if (min == max) return 0;
00397     
00398     if (logarithmic) {
00399         mapper = new LogRangeMapper(0, 100, min, max, unit);
00400     } else {
00401         mapper = new LinearRangeMapper(0, 100, min, max, unit);
00402     }
00403 
00404     return mapper;
00405 }
00406 
00407 FlexiNoteModel::PointList
00408 FlexiNoteLayer::getLocalPoints(View *v, int x) const
00409 {
00410     if (!m_model) return FlexiNoteModel::PointList();
00411 
00412     int frame = v->getFrameForX(x);
00413 
00414     FlexiNoteModel::PointList onPoints =
00415         m_model->getPoints(frame);
00416 
00417     if (!onPoints.empty()) {
00418         return onPoints;
00419     }
00420 
00421     FlexiNoteModel::PointList prevPoints =
00422         m_model->getPreviousPoints(frame);
00423     FlexiNoteModel::PointList nextPoints =
00424         m_model->getNextPoints(frame);
00425 
00426     FlexiNoteModel::PointList usePoints = prevPoints;
00427 
00428     if (prevPoints.empty()) {
00429         usePoints = nextPoints;
00430     } else if (prevPoints.begin()->frame < v->getStartFrame() &&
00431                !(nextPoints.begin()->frame > v->getEndFrame())) {
00432         usePoints = nextPoints;
00433     } else if (nextPoints.begin()->frame - frame <
00434                frame - prevPoints.begin()->frame) {
00435         usePoints = nextPoints;
00436     }
00437 
00438     if (!usePoints.empty()) {
00439         int fuzz = 2;
00440         int px = v->getXForFrame(usePoints.begin()->frame);
00441         if ((px > x && px - x > fuzz) ||
00442             (px < x && x - px > fuzz + 1)) {
00443             usePoints.clear();
00444         }
00445     }
00446 
00447     return usePoints;
00448 }
00449 
00450 bool
00451 FlexiNoteLayer::getPointToDrag(View *v, int x, int y, FlexiNoteModel::Point &p) const
00452 {
00453     if (!m_model) return false;
00454 
00455     int frame = v->getFrameForX(x);
00456 
00457     FlexiNoteModel::PointList onPoints = m_model->getPoints(frame);
00458     if (onPoints.empty()) return false;
00459 
00460 //    cerr << "frame " << frame << ": " << onPoints.size() << " candidate points" << endl;
00461 
00462     int nearestDistance = -1;
00463 
00464     for (FlexiNoteModel::PointList::const_iterator i = onPoints.begin();
00465          i != onPoints.end(); ++i) {
00466         
00467         int distance = getYForValue(v, (*i).value) - y;
00468         if (distance < 0) distance = -distance;
00469         if (nearestDistance == -1 || distance < nearestDistance) {
00470             nearestDistance = distance;
00471             p = *i;
00472         }
00473     }
00474 
00475     return true;
00476 }
00477 
00478 bool
00479 FlexiNoteLayer::getNoteToEdit(View *v, int x, int y, FlexiNoteModel::Point &p) const
00480 {
00481     // GF: find the note that is closest to the cursor
00482     if (!m_model) return false;
00483 
00484     int frame = v->getFrameForX(x);
00485 
00486     FlexiNoteModel::PointList onPoints = m_model->getPoints(frame);
00487     if (onPoints.empty()) return false;
00488 
00489 //    std::cerr << "frame " << frame << ": " << onPoints.size() << " candidate points" << std::endl;
00490 
00491     int nearestDistance = -1;
00492 
00493     for (FlexiNoteModel::PointList::const_iterator i = onPoints.begin();
00494          i != onPoints.end(); ++i) {
00495         
00496         int distance = getYForValue(v, (*i).value) - y;
00497         if (distance < 0) distance = -distance;
00498         if (nearestDistance == -1 || distance < nearestDistance) {
00499             nearestDistance = distance;
00500             p = *i;
00501         }
00502     }
00503 
00504     return true;
00505 }
00506 
00507 QString
00508 FlexiNoteLayer::getFeatureDescription(View *v, QPoint &pos) const
00509 {
00510     int x = pos.x();
00511 
00512     if (!m_model || !m_model->getSampleRate()) return "";
00513 
00514     FlexiNoteModel::PointList points = getLocalPoints(v, x);
00515 
00516     if (points.empty()) {
00517         if (!m_model->isReady()) {
00518             return tr("In progress");
00519         } else {
00520             return tr("No local points");
00521         }
00522     }
00523 
00524     FlexiNote note(0);
00525     FlexiNoteModel::PointList::iterator i;
00526 
00527     for (i = points.begin(); i != points.end(); ++i) {
00528 
00529         int y = getYForValue(v, i->value);
00530         int h = NOTE_HEIGHT; // GF: larger notes
00531 
00532         if (m_model->getValueQuantization() != 0.0) {
00533             h = y - getYForValue(v, i->value + m_model->getValueQuantization());
00534             if (h < NOTE_HEIGHT) h = NOTE_HEIGHT;
00535         }
00536 
00537         // GF: this is not quite correct
00538         if (pos.y() >= y - 4 && pos.y() <= y + h) {
00539             note = *i;
00540             break;
00541         }
00542     }
00543 
00544     if (i == points.end()) return tr("No local points");
00545 
00546     RealTime rt = RealTime::frame2RealTime(note.frame,
00547                                            m_model->getSampleRate());
00548     RealTime rd = RealTime::frame2RealTime(note.duration,
00549                                            m_model->getSampleRate());
00550     
00551     QString pitchText;
00552 
00553     if (shouldConvertMIDIToHz()) {
00554 
00555         int mnote = lrintf(note.value);
00556         int cents = lrintf((note.value - mnote) * 100);
00557         float freq = Pitch::getFrequencyForPitch(mnote, cents);
00558         pitchText = tr("%1 (%2, %3 Hz)")
00559             .arg(Pitch::getPitchLabel(mnote, cents))
00560             .arg(mnote)
00561             .arg(freq);
00562 
00563     } else if (getScaleUnits() == "Hz") {
00564 
00565         pitchText = tr("%1 Hz (%2, %3)")
00566             .arg(note.value)
00567             .arg(Pitch::getPitchLabelForFrequency(note.value))
00568             .arg(Pitch::getPitchForFrequency(note.value));
00569 
00570     } else {
00571         pitchText = tr("%1 %2")
00572             .arg(note.value).arg(getScaleUnits());
00573     }
00574 
00575     QString text;
00576 
00577     if (note.label == "") {
00578         text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label"))
00579             .arg(rt.toText(true).c_str())
00580             .arg(pitchText)
00581             .arg(rd.toText(true).c_str());
00582     } else {
00583         text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4"))
00584             .arg(rt.toText(true).c_str())
00585             .arg(pitchText)
00586             .arg(rd.toText(true).c_str())
00587             .arg(note.label);
00588     }
00589 
00590     pos = QPoint(v->getXForFrame(note.frame),
00591                  getYForValue(v, note.value));
00592     return text;
00593 }
00594 
00595 bool
00596 FlexiNoteLayer::snapToFeatureFrame(View *v, int &frame,
00597                                    int &resolution,
00598                                    SnapType snap) const
00599 {
00600     if (!m_model) {
00601         return Layer::snapToFeatureFrame(v, frame, resolution, snap);
00602     }
00603 
00604     resolution = m_model->getResolution();
00605     FlexiNoteModel::PointList points;
00606 
00607     if (snap == SnapNeighbouring) {
00608     
00609         points = getLocalPoints(v, v->getXForFrame(frame));
00610         if (points.empty()) return false;
00611         frame = points.begin()->frame;
00612         return true;
00613     }    
00614 
00615     points = m_model->getPoints(frame, frame);
00616     int snapped = frame;
00617     bool found = false;
00618 
00619     for (FlexiNoteModel::PointList::const_iterator i = points.begin();
00620          i != points.end(); ++i) {
00621 
00622         cerr << "FlexiNoteModel: point at " << i->frame << endl;
00623 
00624         if (snap == SnapRight) {
00625 
00626             if (i->frame > frame) {
00627                 snapped = i->frame;
00628                 found = true;
00629                 break;
00630             } else if (i->frame + i->duration >= frame) {
00631                 snapped = i->frame + i->duration;
00632                 found = true;
00633                 break;
00634             }
00635 
00636         } else if (snap == SnapLeft) {
00637 
00638             if (i->frame <= frame) {
00639                 snapped = i->frame;
00640                 found = true; // don't break, as the next may be better
00641             } else {
00642                 break;
00643             }
00644 
00645         } else { // nearest
00646 
00647             FlexiNoteModel::PointList::const_iterator j = i;
00648             ++j;
00649 
00650             if (j == points.end()) {
00651 
00652                 snapped = i->frame;
00653                 found = true;
00654                 break;
00655 
00656             } else if (j->frame >= frame) {
00657 
00658                 if (j->frame - frame < frame - i->frame) {
00659                     snapped = j->frame;
00660                 } else {
00661                     snapped = i->frame;
00662                 }
00663                 found = true;
00664                 break;
00665             }
00666         }
00667     }
00668 
00669     cerr << "snapToFeatureFrame: frame " << frame << " -> snapped " << snapped << ", found = " << found << endl;
00670 
00671     frame = snapped;
00672     return found;
00673 }
00674 
00675 void
00676 FlexiNoteLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
00677 {
00678     min = 0.0;
00679     max = 0.0;
00680     log = false;
00681 
00682     QString queryUnits;
00683     if (shouldConvertMIDIToHz()) queryUnits = "Hz";
00684     else queryUnits = getScaleUnits();
00685 
00686     if (shouldAutoAlign()) {
00687 
00688         if (!v->getValueExtents(queryUnits, min, max, log)) {
00689 
00690             min = m_model->getValueMinimum();
00691             max = m_model->getValueMaximum();
00692 
00693             if (shouldConvertMIDIToHz()) {
00694                 min = Pitch::getFrequencyForPitch(lrintf(min));
00695                 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
00696             }
00697 
00698 #ifdef DEBUG_NOTE_LAYER
00699             cerr << "FlexiNoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
00700 #endif
00701 
00702         } else if (log) {
00703 
00704             LogRange::mapRange(min, max);
00705 
00706 #ifdef DEBUG_NOTE_LAYER
00707             cerr << "FlexiNoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
00708 #endif
00709         }
00710 
00711     } else {
00712 
00713         getDisplayExtents(min, max);
00714 
00715         if (m_verticalScale == MIDIRangeScale) {
00716             min = Pitch::getFrequencyForPitch(0);
00717             max = Pitch::getFrequencyForPitch(70);
00718         } else if (shouldConvertMIDIToHz()) {
00719             min = Pitch::getFrequencyForPitch(lrintf(min));
00720             max = Pitch::getFrequencyForPitch(lrintf(max + 1));
00721         }
00722 
00723         if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
00724             LogRange::mapRange(min, max);
00725             log = true;
00726         }
00727     }
00728 
00729     if (max == min) max = min + 1.0;
00730 }
00731 
00732 int
00733 FlexiNoteLayer::getYForValue(View *v, float val) const
00734 {
00735     float min = 0.0, max = 0.0;
00736     bool logarithmic = false;
00737     int h = v->height();
00738 
00739     getScaleExtents(v, min, max, logarithmic);
00740 
00741 #ifdef DEBUG_NOTE_LAYER
00742     cerr << "FlexiNoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl;
00743 #endif
00744 
00745     if (shouldConvertMIDIToHz()) {
00746         val = Pitch::getFrequencyForPitch(lrintf(val),
00747                                           lrintf((val - lrintf(val)) * 100));
00748 #ifdef DEBUG_NOTE_LAYER
00749         cerr << "shouldConvertMIDIToHz true, val now = " << val << endl;
00750 #endif
00751     }
00752 
00753     if (logarithmic) {
00754         val = LogRange::map(val);
00755 #ifdef DEBUG_NOTE_LAYER
00756         cerr << "logarithmic true, val now = " << val << endl;
00757 #endif
00758     }
00759 
00760     int y = int(h - ((val - min) * h) / (max - min)) - 1;
00761 #ifdef DEBUG_NOTE_LAYER
00762     cerr << "y = " << y << endl;
00763 #endif
00764     return y;
00765 }
00766 
00767 float
00768 FlexiNoteLayer::getValueForY(View *v, int y) const
00769 {
00770     float min = 0.0, max = 0.0;
00771     bool logarithmic = false;
00772     int h = v->height();
00773 
00774     getScaleExtents(v, min, max, logarithmic);
00775 
00776     float val = min + (float(h - y) * float(max - min)) / h;
00777 
00778     if (logarithmic) {
00779         val = powf(10.f, val);
00780     }
00781 
00782     if (shouldConvertMIDIToHz()) {
00783         val = Pitch::getPitchForFrequency(val);
00784     }
00785 
00786     return val;
00787 }
00788 
00789 bool
00790 FlexiNoteLayer::shouldAutoAlign() const
00791 {
00792     if (!m_model) return false;
00793     return (m_verticalScale == AutoAlignScale);
00794 }
00795 
00796 void
00797 FlexiNoteLayer::paint(View *v, QPainter &paint, QRect rect) const
00798 {
00799     if (!m_model || !m_model->isOK()) return;
00800 
00801     int sampleRate = m_model->getSampleRate();
00802     if (!sampleRate) return;
00803 
00804 //    Profiler profiler("FlexiNoteLayer::paint", true);
00805 
00806     int x1 = rect.right();
00807     int frame1 = v->getFrameForX(x1);
00808 
00809     FlexiNoteModel::PointList points(m_model->getPoints(0, frame1));
00810     if (points.empty()) return;
00811 
00812     paint.setPen(getBaseQColor());
00813 
00814     QColor brushColour(getBaseQColor());
00815     brushColour.setAlpha(80);
00816 
00817 //    SVDEBUG << "FlexiNoteLayer::paint: resolution is "
00818 //        << m_model->getResolution() << " frames" << endl;
00819 
00820     float min = m_model->getValueMinimum();
00821     float max = m_model->getValueMaximum();
00822     if (max == min) max = min + 1.0;
00823 
00824     QPoint localPos;
00825     FlexiNoteModel::Point illuminatePoint(0);
00826     bool shouldIlluminate = false;
00827 
00828     if (v->shouldIlluminateLocalFeatures(this, localPos)) {
00829         shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
00830                                           illuminatePoint);
00831     }
00832 
00833     paint.save();
00834     paint.setRenderHint(QPainter::Antialiasing, false);
00835     
00836     int noteNumber = 0;
00837 
00838     for (FlexiNoteModel::PointList::const_iterator i = points.begin();
00839          i != points.end(); ++i) {
00840 
00841         ++noteNumber;
00842         const FlexiNoteModel::Point &p(*i);
00843 
00844         int x = v->getXForFrame(p.frame);
00845         int y = getYForValue(v, p.value);
00846         int w = v->getXForFrame(p.frame + p.duration) - x;
00847         int h = NOTE_HEIGHT; //GF: larger notes
00848     
00849         if (m_model->getValueQuantization() != 0.0) {
00850             h = y - getYForValue(v, p.value + m_model->getValueQuantization());
00851             if (h < NOTE_HEIGHT) h = NOTE_HEIGHT; //GF: larger notes
00852         }
00853 
00854         if (w < 1) w = 1;
00855         paint.setPen(getBaseQColor());
00856         paint.setBrush(brushColour);
00857 
00858         if (shouldIlluminate &&
00859                 // "illuminatePoint == p"
00860                 !FlexiNoteModel::Point::Comparator()(illuminatePoint, p) &&
00861                 !FlexiNoteModel::Point::Comparator()(p, illuminatePoint)) {
00862 
00863                 paint.drawLine(x, -1, x, v->height() + 1);
00864                 paint.drawLine(x+w, -1, x+w, v->height() + 1);
00865         
00866                 paint.setPen(v->getForeground());
00867                 // paint.setBrush(v->getForeground());
00868         
00869                 QString vlabel = QString("freq: %1%2").arg(p.value).arg(m_model->getScaleUnits());
00870                 // v->drawVisibleText(paint, 
00871                 //                    x - paint.fontMetrics().width(vlabel) - 2,
00872                 //                    y + paint.fontMetrics().height()/2
00873                 //                      - paint.fontMetrics().descent(), 
00874                 //                    vlabel, View::OutlinedText);
00875                 v->drawVisibleText(paint, 
00876                                    x,
00877                                    y - h/2 - 2 - paint.fontMetrics().height()
00878                                      - paint.fontMetrics().descent(), 
00879                                    vlabel, View::OutlinedText);
00880 
00881                 QString hlabel = "dur: " + QString(RealTime::frame2RealTime
00882                     (p.duration, m_model->getSampleRate()).toText(true).c_str());
00883                 v->drawVisibleText(paint, 
00884                                    x,
00885                                    y - h/2 - paint.fontMetrics().descent() - 2,
00886                                    hlabel, View::OutlinedText);
00887 
00888                 QString llabel = QString("%1").arg(p.label);
00889                 v->drawVisibleText(paint, 
00890                                    x,
00891                                    y + h + 2 + paint.fontMetrics().descent(),
00892                                    llabel, View::OutlinedText);
00893                 QString nlabel = QString("%1").arg(noteNumber);
00894                 v->drawVisibleText(paint, 
00895                                    x + paint.fontMetrics().averageCharWidth() / 2,
00896                                    y + h/2 - paint.fontMetrics().descent(),
00897                                    nlabel, View::OutlinedText);
00898         }
00899     
00900         paint.drawRect(x, y - h/2, w, h);
00901     }
00902 
00903     paint.restore();
00904 }
00905 
00906 int
00907 FlexiNoteLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const
00908 {
00909     if (!m_model || shouldAutoAlign()) {
00910         return 0;
00911     } else  {
00912         if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
00913             return LogNumericalScale().getWidth(v, paint) + 10; // for piano
00914         } else {
00915             return LinearNumericalScale().getWidth(v, paint);
00916         }
00917     }
00918 }
00919 
00920 void
00921 FlexiNoteLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const
00922 {
00923     if (!m_model || m_model->getPoints().empty()) return;
00924 
00925     QString unit;
00926     float min, max;
00927     bool logarithmic;
00928 
00929     int w = getVerticalScaleWidth(v, false, paint);
00930     int h = v->height();
00931 
00932     getScaleExtents(v, min, max, logarithmic);
00933 
00934     if (logarithmic) {
00935         LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
00936     } else {
00937         LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
00938     }
00939     
00940     if (logarithmic && (getScaleUnits() == "Hz")) {
00941         PianoScale().paintPianoVertical
00942             (v, paint, QRect(w - 10, 0, 10, h), 
00943              LogRange::unmap(min), 
00944              LogRange::unmap(max));
00945         paint.drawLine(w, 0, w, h);
00946     }
00947         
00948     if (getScaleUnits() != "") {
00949         int mw = w - 5;
00950         paint.drawText(5,
00951                        5 + paint.fontMetrics().ascent(),
00952                        TextAbbrev::abbreviate(getScaleUnits(),
00953                                               paint.fontMetrics(),
00954                                               mw));
00955     }
00956 }
00957 
00958 void
00959 FlexiNoteLayer::drawStart(View *v, QMouseEvent *e)
00960 {
00961 //    SVDEBUG << "FlexiNoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
00962 
00963     if (!m_model) return;
00964 
00965     int frame = v->getFrameForX(e->x());
00966     if (frame < 0) frame = 0;
00967     frame = frame / m_model->getResolution() * m_model->getResolution();
00968 
00969     float value = getValueForY(v, e->y());
00970 
00971     m_editingPoint = FlexiNoteModel::Point(frame, value, 0, 0.8, tr("New Point"));
00972     m_originalPoint = m_editingPoint;
00973 
00974     if (m_editingCommand) finish(m_editingCommand);
00975     m_editingCommand = new FlexiNoteModel::EditCommand(m_model,
00976                                                        tr("Draw Point"));
00977     m_editingCommand->addPoint(m_editingPoint);
00978 
00979     m_editing = true;
00980 }
00981 
00982 void
00983 FlexiNoteLayer::drawDrag(View *v, QMouseEvent *e)
00984 {
00985 //    SVDEBUG << "FlexiNoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
00986 
00987     if (!m_model || !m_editing) return;
00988 
00989     int frame = v->getFrameForX(e->x());
00990     if (frame < 0) frame = 0;
00991     frame = frame / m_model->getResolution() * m_model->getResolution();
00992 
00993     float newValue = getValueForY(v, e->y());
00994 
00995     int newFrame = m_editingPoint.frame;
00996     int newDuration = frame - newFrame;
00997     if (newDuration < 0) {
00998         newFrame = frame;
00999         newDuration = -newDuration;
01000     } else if (newDuration == 0) {
01001         newDuration = 1;
01002     }
01003 
01004     m_editingCommand->deletePoint(m_editingPoint);
01005     m_editingPoint.frame = newFrame;
01006     m_editingPoint.value = newValue;
01007     m_editingPoint.duration = newDuration;
01008     m_editingCommand->addPoint(m_editingPoint);
01009 }
01010 
01011 void
01012 FlexiNoteLayer::drawEnd(View *, QMouseEvent *)
01013 {
01014 //    SVDEBUG << "FlexiNoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl;
01015     if (!m_model || !m_editing) return;
01016     finish(m_editingCommand);
01017     m_editingCommand = 0;
01018     m_editing = false;
01019 }
01020 
01021 void
01022 FlexiNoteLayer::eraseStart(View *v, QMouseEvent *e)
01023 {
01024     if (!m_model) return;
01025 
01026     if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
01027 
01028     if (m_editingCommand) {
01029         finish(m_editingCommand);
01030         m_editingCommand = 0;
01031     }
01032 
01033     m_editing = true;
01034 }
01035 
01036 void
01037 FlexiNoteLayer::eraseDrag(View *, QMouseEvent *)
01038 {
01039 }
01040 
01041 void
01042 FlexiNoteLayer::eraseEnd(View *v, QMouseEvent *e)
01043 {
01044     if (!m_model || !m_editing) return;
01045 
01046     m_editing = false;
01047 
01048     FlexiNoteModel::Point p(0);
01049     if (!getPointToDrag(v, e->x(), e->y(), p)) return;
01050     if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return;
01051 
01052     m_editingCommand = new FlexiNoteModel::EditCommand(m_model, tr("Erase Point"));
01053 
01054     m_editingCommand->deletePoint(m_editingPoint);
01055 
01056     finish(m_editingCommand);
01057     m_editingCommand = 0;
01058     m_editing = false;
01059 }
01060 
01061 void
01062 FlexiNoteLayer::editStart(View *v, QMouseEvent *e)
01063 {
01064 //    SVDEBUG << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
01065     std::cerr << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
01066 
01067     if (!m_model) return;
01068 
01069     if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
01070     m_originalPoint = FlexiNote(m_editingPoint);
01071     
01072     if (m_editMode == RightBoundary) {
01073         m_dragPointX = v->getXForFrame(m_editingPoint.frame + m_editingPoint.duration);
01074     } else {
01075         m_dragPointX = v->getXForFrame(m_editingPoint.frame);
01076     }
01077     m_dragPointY = getYForValue(v, m_editingPoint.value);
01078 
01079     if (m_editingCommand) {
01080         finish(m_editingCommand);
01081         m_editingCommand = 0;
01082     }
01083 
01084     m_editing = true;
01085     m_dragStartX = e->x();
01086     m_dragStartY = e->y();
01087     
01088     int onset = m_originalPoint.frame;
01089     int offset = m_originalPoint.frame + m_originalPoint.duration - 1;
01090     
01091     m_greatestLeftNeighbourFrame = -1;
01092     m_smallestRightNeighbourFrame = std::numeric_limits<int>::max();
01093     
01094     for (FlexiNoteModel::PointList::const_iterator i = m_model->getPoints().begin();
01095          i != m_model->getPoints().end(); ++i) {
01096         FlexiNote currentNote = *i;
01097         
01098         // left boundary
01099         if (currentNote.frame + currentNote.duration - 1 < onset) {
01100             m_greatestLeftNeighbourFrame = currentNote.frame + currentNote.duration - 1;
01101         }
01102         
01103         // right boundary
01104         if (currentNote.frame > offset) {
01105             m_smallestRightNeighbourFrame = currentNote.frame;
01106             break;
01107         }
01108     }
01109     std::cerr << "editStart: mode is " << m_editMode << ", note frame: " << onset << ", left boundary: " << m_greatestLeftNeighbourFrame << ", right boundary: " << m_smallestRightNeighbourFrame << std::endl;
01110 }
01111 
01112 void
01113 FlexiNoteLayer::editDrag(View *v, QMouseEvent *e)
01114 {
01115 //    SVDEBUG << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
01116     std::cerr << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
01117 
01118     if (!m_model || !m_editing) return;
01119 
01120     int xdist = e->x() - m_dragStartX;
01121     int ydist = e->y() - m_dragStartY;
01122     int newx = m_dragPointX + xdist;
01123     int newy = m_dragPointY + ydist;
01124 
01125     int dragFrame = v->getFrameForX(newx);
01126     if (dragFrame < 0) dragFrame = 0;
01127     dragFrame = dragFrame / m_model->getResolution() * m_model->getResolution();
01128     
01129     float value = getValueForY(v, newy);
01130 
01131     if (!m_editingCommand) {
01132         m_editingCommand = new FlexiNoteModel::EditCommand(m_model,
01133                                                            tr("Drag Point"));
01134     }
01135 
01136     m_editingCommand->deletePoint(m_editingPoint);
01137 
01138     std::cerr << "edit mode: " << m_editMode << std::endl;
01139     
01140     switch (m_editMode) {
01141     case LeftBoundary : {
01142         // left 
01143         if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1;
01144         // right
01145         if (m_intelligentActions && dragFrame >= m_originalPoint.frame + m_originalPoint.duration) {
01146             dragFrame = m_originalPoint.frame + m_originalPoint.duration - 1;
01147         }
01148         m_editingPoint.frame = dragFrame;
01149         m_editingPoint.duration = m_originalPoint.frame - dragFrame + m_originalPoint.duration;
01150         break;
01151     }
01152     case RightBoundary : {
01153         // left
01154         if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1;
01155         if (m_intelligentActions && dragFrame >= m_smallestRightNeighbourFrame) dragFrame = m_smallestRightNeighbourFrame - 1;
01156         m_editingPoint.duration = dragFrame - m_originalPoint.frame + 1;
01157         break;
01158     }
01159     case DragNote : {
01160         // left
01161         if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1;
01162         // right
01163         if (m_intelligentActions && dragFrame + m_originalPoint.duration >= m_smallestRightNeighbourFrame) {
01164             dragFrame = m_smallestRightNeighbourFrame - m_originalPoint.duration;
01165         }
01166         m_editingPoint.frame = dragFrame;
01167         m_editingPoint.value = value;
01168         break;
01169     }
01170     case SplitNote: // nothing
01171         break;
01172     }
01173     updateNoteValue(v, m_editingPoint);
01174     m_editingCommand->addPoint(m_editingPoint);
01175     std::cerr << "added new point(" << m_editingPoint.frame << "," << m_editingPoint.duration << ")" << std::endl;
01176     
01177 }
01178 
01179 void
01180 FlexiNoteLayer::editEnd(View *, QMouseEvent *e)
01181 {
01182 //    SVDEBUG << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl;
01183     std::cerr << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
01184     
01185     if (!m_model || !m_editing) return;
01186 
01187     if (m_editingCommand) {
01188 
01189         QString newName = m_editingCommand->getName();
01190 
01191         if (m_editingPoint.frame != m_originalPoint.frame) {
01192             if (m_editingPoint.value != m_originalPoint.value) {
01193                 newName = tr("Edit Point");
01194             } else {
01195                 newName = tr("Relocate Point");
01196             }
01197         } else {
01198             newName = tr("Change Point Value");
01199         }
01200 
01201         m_editingCommand->setName(newName);
01202         finish(m_editingCommand);
01203     }
01204 
01205     m_editingCommand = 0;
01206     m_editing = false;
01207 }
01208 
01209 void
01210 FlexiNoteLayer::splitStart(View *v, QMouseEvent *e)
01211 {
01212     // GF: note splitting starts (!! remove printing soon)
01213     std::cerr << "splitStart" << std::endl;
01214     if (!m_model) return;
01215 
01216     if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
01217     // m_originalPoint = m_editingPoint;
01218     // 
01219     // m_dragPointX = v->getXForFrame(m_editingPoint.frame);
01220     // m_dragPointY = getYForValue(v, m_editingPoint.value);
01221 
01222     if (m_editingCommand) {
01223         finish(m_editingCommand);
01224         m_editingCommand = 0;
01225     }
01226 
01227     m_editing = true;
01228     m_dragStartX = e->x();
01229     m_dragStartY = e->y();
01230     
01231 }
01232 
01233 void
01234 FlexiNoteLayer::splitEnd(View *v, QMouseEvent *e)
01235 {
01236     // GF: note splitting ends. (!! remove printing soon)
01237     std::cerr << "splitEnd" << std::endl;
01238     if (!m_model || !m_editing || m_editMode != SplitNote) return;
01239 
01240     int xdist = e->x() - m_dragStartX;
01241     int ydist = e->y() - m_dragStartY;
01242     if (xdist != 0 || ydist != 0) { 
01243         std::cerr << "mouse moved" << std::endl;    
01244         return; 
01245     }
01246 
01247     int frame = v->getFrameForX(e->x());
01248 
01249     splitNotesAt(v, frame, e);
01250 }
01251 
01252 void
01253 FlexiNoteLayer::splitNotesAt(View *v, int frame)
01254 {
01255     splitNotesAt(v, frame, 0);
01256 }
01257 
01258 void
01259 FlexiNoteLayer::splitNotesAt(View *v, int frame, QMouseEvent *e)
01260 {
01261     FlexiNoteModel::PointList onPoints = m_model->getPoints(frame);
01262     if (onPoints.empty()) return;
01263     
01264     FlexiNote note(*onPoints.begin());
01265 
01266     FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand
01267         (m_model, tr("Edit Point"));
01268     command->deletePoint(note);
01269 
01270     if (!e || !(e->modifiers() & Qt::ShiftModifier)) {
01271 
01272         int gap = 0; // MM: I prefer a gap of 0, but we can decide later
01273     
01274         FlexiNote newNote1(note.frame, note.value, 
01275                            frame - note.frame - gap, 
01276                            note.level, note.label);
01277     
01278         FlexiNote newNote2(frame, note.value, 
01279                            note.duration - newNote1.duration, 
01280                            note.level, note.label);
01281                        
01282         if (m_intelligentActions) {
01283             if (updateNoteValue(v, newNote1)) {
01284                 command->addPoint(newNote1);
01285             }
01286             if (updateNoteValue(v, newNote2)) {
01287                 command->addPoint(newNote2);
01288             }
01289         } else {
01290             command->addPoint(newNote1);
01291             command->addPoint(newNote2);
01292         }
01293     }
01294 
01295     finish(command);
01296 }
01297 
01298 void
01299 FlexiNoteLayer::addNote(View *v, QMouseEvent *e)
01300 {
01301     std::cerr << "addNote" << std::endl;
01302     if (!m_model) return;
01303 
01304     int duration = 10000;
01305     
01306     int frame = v->getFrameForX(e->x());
01307     float value = getValueForY(v, e->y());
01308     
01309     FlexiNoteModel::PointList noteList = m_model->getPoints();
01310 
01311     if (m_intelligentActions) {
01312         int smallestRightNeighbourFrame = 0;
01313         for (FlexiNoteModel::PointList::const_iterator i = noteList.begin();
01314              i != noteList.end(); ++i) {
01315             FlexiNote currentNote = *i;
01316             if (currentNote.frame > frame) {
01317                 smallestRightNeighbourFrame = currentNote.frame;
01318                 break;
01319             }
01320         }
01321         if (smallestRightNeighbourFrame > 0) {
01322             duration = std::min(smallestRightNeighbourFrame - frame + 1, duration);
01323             duration = (duration > 0) ? duration : 0;
01324         }
01325     }
01326 
01327     if (!m_intelligentActions || 
01328         (m_model->getPoints(frame).empty() && duration > 0)) {
01329         FlexiNote newNote(frame, value, duration, 100, "new note");
01330         FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand
01331             (m_model, tr("Add Point"));
01332         command->addPoint(newNote);
01333         finish(command);
01334     }
01335 }
01336 
01337 SparseTimeValueModel *
01338 FlexiNoteLayer::getAssociatedPitchModel(View *v) const
01339 {
01340     // Better than we used to do, but still not very satisfactory
01341 
01342     cerr << "FlexiNoteLayer::getAssociatedPitchModel()" << endl;
01343 
01344     for (int i = 0; i < v->getLayerCount(); ++i) {
01345         Layer *layer = v->getLayer(i);
01346         if (layer &&
01347             layer->getLayerPresentationName() != "candidate") {
01348             cerr << "FlexiNoteLayer::getAssociatedPitchModel: looks like our layer is " << layer << endl;
01349             SparseTimeValueModel *model = qobject_cast<SparseTimeValueModel *>
01350                 (layer->getModel());
01351             cerr << "FlexiNoteLayer::getAssociatedPitchModel: and its model is " << model << endl;
01352             if (model && model->getScaleUnits() == "Hz") {
01353                 cerr << "FlexiNoteLayer::getAssociatedPitchModel: it's good, returning " << model << endl;
01354                 return model;
01355             }
01356         }
01357     }
01358     return 0;
01359 }
01360 
01361 void
01362 FlexiNoteLayer::snapSelectedNotesToPitchTrack(View *v, Selection s)
01363 {
01364     if (!m_model) return;
01365 
01366     FlexiNoteModel::PointList points =
01367         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
01368 
01369     FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand
01370         (m_model, tr("Snap Notes"));
01371 
01372     cerr << "snapSelectedNotesToPitchTrack: selection is from " << s.getStartFrame() << " to " << s.getEndFrame() << endl;
01373 
01374     for (FlexiNoteModel::PointList::iterator i = points.begin();
01375          i != points.end(); ++i) {
01376 
01377         FlexiNote note(*i);
01378 
01379         cerr << "snapSelectedNotesToPitchTrack: looking at note from " << note.frame << " to " << note.frame + note.duration << endl;
01380 
01381         if (!s.contains(note.frame) &&
01382             !s.contains(note.frame + note.duration - 1)) {
01383             continue;
01384         }
01385 
01386         cerr << "snapSelectedNotesToPitchTrack: making new note" << endl;
01387         FlexiNote newNote(note);
01388 
01389         command->deletePoint(note);
01390 
01391         if (updateNoteValue(v, newNote)) {
01392             command->addPoint(newNote);
01393         }
01394     }
01395     
01396     finish(command);
01397 }
01398 
01399 void
01400 FlexiNoteLayer::mergeNotes(View *v, Selection s, bool inclusive)
01401 {
01402     FlexiNoteModel::PointList points =
01403         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
01404 
01405     FlexiNoteModel::PointList::iterator i = points.begin();
01406     if (inclusive) {
01407         while (i != points.end() && i->frame + i->duration < s.getStartFrame()) {
01408             ++i;
01409         }
01410     } else {
01411         while (i != points.end() && i->frame < s.getStartFrame()) {
01412             ++i;
01413         }
01414     }
01415         
01416     if (i == points.end()) return;
01417 
01418     FlexiNoteModel::EditCommand *command = 
01419         new FlexiNoteModel::EditCommand(m_model, tr("Merge Notes"));
01420 
01421     FlexiNote newNote(*i);
01422 
01423     while (i != points.end()) {
01424 
01425         if (inclusive) {
01426             if (i->frame >= s.getEndFrame()) break;
01427         } else {
01428             if (i->frame + i->duration > s.getEndFrame()) break;
01429         }
01430 
01431         newNote.duration = i->frame + i->duration - newNote.frame;
01432         command->deletePoint(*i);
01433 
01434         ++i;
01435     }
01436 
01437     updateNoteValue(v, newNote);
01438     command->addPoint(newNote);
01439     finish(command);
01440 }
01441 
01442 bool
01443 FlexiNoteLayer::updateNoteValue(View *v, FlexiNoteModel::Point &note) const
01444 {
01445     SparseTimeValueModel *model = getAssociatedPitchModel(v);
01446     if (!model) return false;
01447         
01448     std::cerr << model->getTypeName() << std::endl;
01449 
01450     SparseModel<TimeValuePoint>::PointList dataPoints =
01451         model->getPoints(note.frame, note.frame + note.duration);
01452    
01453     std::cerr << "frame " << note.frame << ": " << dataPoints.size() << " candidate points" << std::endl;
01454    
01455     if (dataPoints.empty()) return false;
01456 
01457     std::vector<float> pitchValues;
01458    
01459     for (SparseModel<TimeValuePoint>::PointList::const_iterator i =
01460              dataPoints.begin(); i != dataPoints.end(); ++i) {
01461         if (i->frame >= note.frame &&
01462             i->frame < note.frame + note.duration) {
01463             pitchValues.push_back(i->value);
01464         }
01465     }
01466         
01467     if (pitchValues.empty()) return false;
01468 
01469     sort(pitchValues.begin(), pitchValues.end());
01470     int size = pitchValues.size();
01471     double median;
01472 
01473     if (size % 2 == 0) {
01474         median = (pitchValues[size/2 - 1] + pitchValues[size/2]) / 2;
01475     } else {
01476         median = pitchValues[size/2];
01477     }
01478     
01479     note.value = median;
01480 
01481     return true;
01482 }
01483 
01484 void 
01485 FlexiNoteLayer::mouseMoveEvent(View *v, QMouseEvent *e)
01486 {
01487     // GF: context sensitive cursors
01488     // v->setCursor(Qt::ArrowCursor);
01489     FlexiNoteModel::Point note(0);
01490     if (!getNoteToEdit(v, e->x(), e->y(), note)) { 
01491         // v->setCursor(Qt::UpArrowCursor);
01492         return; 
01493     }
01494 
01495     bool closeToLeft = false, closeToRight = false, closeToTop = false, closeToBottom = false;
01496     getRelativeMousePosition(v, note, e->x(), e->y(), closeToLeft, closeToRight, closeToTop, closeToBottom);
01497     // if (!closeToLeft) return;
01498     // if (closeToTop) v->setCursor(Qt::SizeVerCursor);
01499     
01500     if (closeToLeft) { v->setCursor(Qt::SizeHorCursor); m_editMode = LeftBoundary; return; }
01501     if (closeToRight) { v->setCursor(Qt::SizeHorCursor); m_editMode = RightBoundary; return; }
01502     if (closeToTop) { v->setCursor(Qt::CrossCursor);  m_editMode = DragNote; return; }
01503     if (closeToBottom) { v->setCursor(Qt::UpArrowCursor); m_editMode = SplitNote; return; }
01504 
01505     v->setCursor(Qt::ArrowCursor);
01506 
01507     std::cerr << "Mouse moved in edit mode over FlexiNoteLayer" << std::endl;
01508     // v->setCursor(Qt::SizeHorCursor);
01509 
01510 }
01511 
01512 void
01513 FlexiNoteLayer::getRelativeMousePosition(View *v, FlexiNoteModel::Point &note, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const
01514 {
01515     // GF: TODO: consoloidate the tolerance values
01516     if (!m_model) return;
01517 
01518     int ctol = 0;
01519     int noteStartX = v->getXForFrame(note.frame);
01520     int noteEndX = v->getXForFrame(note.frame + note.duration);
01521     int noteValueY = getYForValue(v,note.value);
01522     int noteStartY = noteValueY - (NOTE_HEIGHT / 2);
01523     int noteEndY = noteValueY + (NOTE_HEIGHT / 2);
01524     
01525     bool closeToNote = false;
01526     
01527     if (y >= noteStartY-ctol && y <= noteEndY+ctol && x >= noteStartX-ctol && x <= noteEndX+ctol) closeToNote = true;
01528     if (!closeToNote) return;
01529     
01530     int tol = NOTE_HEIGHT / 2;
01531     
01532     if (x >= noteStartX - tol && x <= noteStartX + tol) closeToLeft = true;
01533     if (x >= noteEndX - tol && x <= noteEndX + tol) closeToRight = true;
01534     if (y >= noteStartY - tol && y <= noteStartY + tol) closeToTop = true;
01535     if (y >= noteEndY - tol && y <= noteEndY + tol) closeToBottom = true;
01536 
01537 //    cerr << "FlexiNoteLayer::getRelativeMousePosition: close to: left " << closeToLeft << " right " << closeToRight << " top " << closeToTop << " bottom " << closeToBottom << endl;
01538 }
01539 
01540 
01541 bool
01542 FlexiNoteLayer::editOpen(View *v, QMouseEvent *e)
01543 {
01544     std::cerr << "Opening note editor dialog" << std::endl;
01545     if (!m_model) return false;
01546 
01547     FlexiNoteModel::Point note(0);
01548     if (!getPointToDrag(v, e->x(), e->y(), note)) return false;
01549 
01550 //    FlexiNoteModel::Point note = *points.begin();
01551 
01552     ItemEditDialog *dialog = new ItemEditDialog
01553         (m_model->getSampleRate(),
01554          ItemEditDialog::ShowTime |
01555          ItemEditDialog::ShowDuration |
01556          ItemEditDialog::ShowValue |
01557          ItemEditDialog::ShowText,
01558          getScaleUnits());
01559 
01560     dialog->setFrameTime(note.frame);
01561     dialog->setValue(note.value);
01562     dialog->setFrameDuration(note.duration);
01563     dialog->setText(note.label);
01564 
01565     if (dialog->exec() == QDialog::Accepted) {
01566 
01567         FlexiNoteModel::Point newNote = note;
01568         newNote.frame = dialog->getFrameTime();
01569         newNote.value = dialog->getValue();
01570         newNote.duration = dialog->getFrameDuration();
01571         newNote.label = dialog->getText();
01572         
01573         FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand
01574             (m_model, tr("Edit Point"));
01575         command->deletePoint(note);
01576         command->addPoint(newNote);
01577         finish(command);
01578     }
01579 
01580     delete dialog;
01581     return true;
01582 }
01583 
01584 void
01585 FlexiNoteLayer::moveSelection(Selection s, int newStartFrame)
01586 {
01587     if (!m_model) return;
01588 
01589     FlexiNoteModel::EditCommand *command =
01590         new FlexiNoteModel::EditCommand(m_model, tr("Drag Selection"));
01591 
01592     FlexiNoteModel::PointList points =
01593         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
01594 
01595     for (FlexiNoteModel::PointList::iterator i = points.begin();
01596          i != points.end(); ++i) {
01597 
01598         if (s.contains(i->frame)) {
01599             FlexiNoteModel::Point newPoint(*i);
01600             newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
01601             command->deletePoint(*i);
01602             command->addPoint(newPoint);
01603         }
01604     }
01605 
01606     finish(command);
01607 }
01608 
01609 void
01610 FlexiNoteLayer::resizeSelection(Selection s, Selection newSize)
01611 {
01612     if (!m_model) return;
01613 
01614     FlexiNoteModel::EditCommand *command =
01615         new FlexiNoteModel::EditCommand(m_model, tr("Resize Selection"));
01616 
01617     FlexiNoteModel::PointList points =
01618         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
01619 
01620     double ratio =
01621         double(newSize.getEndFrame() - newSize.getStartFrame()) /
01622         double(s.getEndFrame() - s.getStartFrame());
01623 
01624     for (FlexiNoteModel::PointList::iterator i = points.begin();
01625          i != points.end(); ++i) {
01626 
01627         if (s.contains(i->frame)) {
01628 
01629             double targetStart = i->frame;
01630             targetStart = newSize.getStartFrame() + 
01631                 double(targetStart - s.getStartFrame()) * ratio;
01632 
01633             double targetEnd = i->frame + i->duration;
01634             targetEnd = newSize.getStartFrame() +
01635                 double(targetEnd - s.getStartFrame()) * ratio;
01636 
01637             FlexiNoteModel::Point newPoint(*i);
01638             newPoint.frame = lrint(targetStart);
01639             newPoint.duration = lrint(targetEnd - targetStart);
01640             command->deletePoint(*i);
01641             command->addPoint(newPoint);
01642         }
01643     }
01644 
01645     finish(command);
01646 }
01647 
01648 void
01649 FlexiNoteLayer::deleteSelection(Selection s)
01650 {
01651     if (!m_model) return;
01652 
01653     FlexiNoteModel::EditCommand *command =
01654         new FlexiNoteModel::EditCommand(m_model, tr("Delete Selected Points"));
01655 
01656     FlexiNoteModel::PointList points =
01657         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
01658 
01659     for (FlexiNoteModel::PointList::iterator i = points.begin();
01660          i != points.end(); ++i) {
01661 
01662         if (s.contains(i->frame)) {
01663             command->deletePoint(*i);
01664         }
01665     }
01666 
01667     finish(command);
01668 }    
01669 
01670 void
01671 FlexiNoteLayer::deleteSelectionInclusive(Selection s)
01672 {
01673     if (!m_model) return;
01674 
01675     FlexiNoteModel::EditCommand *command =
01676         new FlexiNoteModel::EditCommand(m_model, tr("Delete Selected Points"));
01677 
01678     FlexiNoteModel::PointList points =
01679         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
01680 
01681     for (FlexiNoteModel::PointList::iterator i = points.begin();
01682          i != points.end(); ++i) {
01683         bool overlap = !(
01684             ((s.getStartFrame() <= i->frame) && (s.getEndFrame() <= i->frame)) || // selection is left of note
01685             ((s.getStartFrame() >= (i->frame+i->duration)) && (s.getEndFrame() >= (i->frame+i->duration))) // selection is right of note
01686             );
01687         if (overlap) {
01688             command->deletePoint(*i);
01689         }
01690     }
01691 
01692     finish(command);
01693 }
01694 
01695 void
01696 FlexiNoteLayer::copy(View *v, Selection s, Clipboard &to)
01697 {
01698     if (!m_model) return;
01699 
01700     FlexiNoteModel::PointList points =
01701         m_model->getPoints(s.getStartFrame(), s.getEndFrame());
01702 
01703     for (FlexiNoteModel::PointList::iterator i = points.begin();
01704          i != points.end(); ++i) {
01705         if (s.contains(i->frame)) {
01706             Clipboard::Point point(i->frame, i->value, i->duration, i->level, i->label);
01707             point.setReferenceFrame(alignToReference(v, i->frame));
01708             to.addPoint(point);
01709         }
01710     }
01711 }
01712 
01713 bool
01714 FlexiNoteLayer::paste(View *v, const Clipboard &from, int /*frameOffset */, bool /* interactive */)
01715 {
01716     if (!m_model) return false;
01717 
01718     const Clipboard::PointList &points = from.getPoints();
01719 
01720     bool realign = false;
01721 
01722     if (clipboardHasDifferentAlignment(v, from)) {
01723 
01724         QMessageBox::StandardButton button =
01725             QMessageBox::question(v, tr("Re-align pasted items?"),
01726                                   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?"),
01727                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
01728                                   QMessageBox::Yes);
01729 
01730         if (button == QMessageBox::Cancel) {
01731             return false;
01732         }
01733 
01734         if (button == QMessageBox::Yes) {
01735             realign = true;
01736         }
01737     }
01738 
01739     FlexiNoteModel::EditCommand *command =
01740         new FlexiNoteModel::EditCommand(m_model, tr("Paste"));
01741 
01742     for (Clipboard::PointList::const_iterator i = points.begin();
01743          i != points.end(); ++i) {
01744         
01745         if (!i->haveFrame()) continue;
01746         int frame = 0;
01747 
01748         if (!realign) {
01749             
01750             frame = i->getFrame();
01751 
01752         } else {
01753 
01754             if (i->haveReferenceFrame()) {
01755                 frame = i->getReferenceFrame();
01756                 frame = alignFromReference(v, frame);
01757             } else {
01758                 frame = i->getFrame();
01759             }
01760         }
01761 
01762         FlexiNoteModel::Point newPoint(frame);
01763   
01764         if (i->haveLabel()) newPoint.label = i->getLabel();
01765         if (i->haveValue()) newPoint.value = i->getValue();
01766         else newPoint.value = (m_model->getValueMinimum() +
01767                                m_model->getValueMaximum()) / 2;
01768         if (i->haveLevel()) newPoint.level = i->getLevel();
01769         if (i->haveDuration()) newPoint.duration = i->getDuration();
01770         else {
01771             int nextFrame = frame;
01772             Clipboard::PointList::const_iterator j = i;
01773             for (; j != points.end(); ++j) {
01774                 if (!j->haveFrame()) continue;
01775                 if (j != i) break;
01776             }
01777             if (j != points.end()) {
01778                 nextFrame = j->getFrame();
01779             }
01780             if (nextFrame == frame) {
01781                 newPoint.duration = m_model->getResolution();
01782             } else {
01783                 newPoint.duration = nextFrame - frame;
01784             }
01785         }
01786         
01787         command->addPoint(newPoint);
01788     }
01789 
01790     finish(command);
01791     return true;
01792 }
01793 
01794 void
01795 FlexiNoteLayer::addNoteOn(int frame, int pitch, int velocity)
01796 {
01797     m_pendingNoteOns.insert(FlexiNote(frame, pitch, 0, float(velocity) / 127.0, ""));
01798 }
01799 
01800 void
01801 FlexiNoteLayer::addNoteOff(int frame, int pitch)
01802 {
01803     for (FlexiNoteSet::iterator i = m_pendingNoteOns.begin();
01804          i != m_pendingNoteOns.end(); ++i) {
01805         if (lrintf((*i).value) == pitch) {
01806             FlexiNote note(*i);
01807             m_pendingNoteOns.erase(i);
01808             note.duration = frame - note.frame;
01809             if (m_model) {
01810                 FlexiNoteModel::AddPointCommand *c = new FlexiNoteModel::AddPointCommand
01811                     (m_model, note, tr("Record FlexiNote"));
01812                 // execute and bundle:
01813                 CommandHistory::getInstance()->addCommand(c, true, true);
01814             }
01815             break;
01816         }
01817     }
01818 }
01819 
01820 void
01821 FlexiNoteLayer::abandonNoteOns()
01822 {
01823     m_pendingNoteOns.clear();
01824 }
01825 
01826 int
01827 FlexiNoteLayer::getDefaultColourHint(bool darkbg, bool &impose)
01828 {
01829     impose = false;
01830     return ColourDatabase::getInstance()->getColourIndex
01831         (QString(darkbg ? "White" : "Black"));
01832 }
01833 
01834 void
01835 FlexiNoteLayer::toXml(QTextStream &stream,
01836                       QString indent, QString extraAttributes) const
01837 {
01838     SingleColourLayer::toXml(stream, indent, extraAttributes +
01839                              QString(" verticalScale=\"%1\" scaleMinimum=\"%2\" scaleMaximum=\"%3\" ")
01840                              .arg(m_verticalScale)
01841                              .arg(m_scaleMinimum)
01842                              .arg(m_scaleMaximum));
01843 }
01844 
01845 void
01846 FlexiNoteLayer::setProperties(const QXmlAttributes &attributes)
01847 {
01848     SingleColourLayer::setProperties(attributes);
01849 
01850     bool ok;
01851     VerticalScale scale = (VerticalScale)
01852         attributes.value("verticalScale").toInt(&ok);
01853     if (ok) setVerticalScale(scale);
01854 
01855 //    bool alsoOk;
01856 //    float min = attributes.value("scaleMinimum").toFloat(&ok);
01857 //    float max = attributes.value("scaleMaximum").toFloat(&alsoOk);
01858 //    if (ok && alsoOk && min != max) setDisplayExtents(min, max);
01859 }
01860 
01861 void
01862 FlexiNoteLayer::setVerticalRangeToNoteRange(View *v)
01863 {
01864     float minf = std::numeric_limits<float>::max();
01865     float maxf = 0;
01866     bool hasNotes = 0;
01867     for (FlexiNoteModel::PointList::const_iterator i = m_model->getPoints().begin();
01868          i != m_model->getPoints().end(); ++i) {
01869         hasNotes = 1;
01870         FlexiNote note = *i;
01871         if (note.value < minf) minf = note.value;
01872         if (note.value > maxf) maxf = note.value;
01873     }
01874     
01875     std::cerr << "min frequency:" << minf << ", max frequency: " << maxf << std::endl;
01876     
01877     if (hasNotes) {
01878         v->getLayer(1)->setDisplayExtents(minf*0.66,maxf*1.5); 
01879         // MM: this is a hack because we rely on 
01880         // * this layer being automatically aligned to layer 1
01881         // * layer one is a log frequency layer.
01882     }
01883 }
01884 
01885