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