| Chris@58 | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */ | 
| Chris@0 | 2 | 
| Chris@0 | 3 /* | 
| Chris@59 | 4     Sonic Visualiser | 
| Chris@59 | 5     An audio file viewer and annotation editor. | 
| Chris@59 | 6     Centre for Digital Music, Queen Mary, University of London. | 
| Chris@59 | 7     This file copyright 2006 Chris Cannam. | 
| Chris@0 | 8 | 
| Chris@59 | 9     This program is free software; you can redistribute it and/or | 
| Chris@59 | 10     modify it under the terms of the GNU General Public License as | 
| Chris@59 | 11     published by the Free Software Foundation; either version 2 of the | 
| Chris@59 | 12     License, or (at your option) any later version.  See the file | 
| Chris@59 | 13     COPYING included with this distribution for more information. | 
| Chris@0 | 14 */ | 
| Chris@0 | 15 | 
| Chris@0 | 16 #include "TimeValueLayer.h" | 
| Chris@0 | 17 | 
| Chris@128 | 18 #include "data/model/Model.h" | 
| Chris@0 | 19 #include "base/RealTime.h" | 
| Chris@0 | 20 #include "base/Profiler.h" | 
| Chris@197 | 21 #include "base/LogRange.h" | 
| Chris@437 | 22 #include "base/RangeMapper.h" | 
| Chris@376 | 23 #include "ColourDatabase.h" | 
| Chris@128 | 24 #include "view/View.h" | 
| Chris@0 | 25 | 
| Chris@128 | 26 #include "data/model/SparseTimeValueModel.h" | 
| Chris@340 | 27 #include "data/model/Labeller.h" | 
| Chris@0 | 28 | 
| Chris@70 | 29 #include "widgets/ItemEditDialog.h" | 
| Chris@125 | 30 #include "widgets/ListInputDialog.h" | 
| Chris@701 | 31 #include "widgets/TextAbbrev.h" | 
| Chris@70 | 32 | 
| Chris@376 | 33 #include "ColourMapper.h" | 
| Chris@691 | 34 #include "PianoScale.h" | 
| Chris@698 | 35 #include "LinearNumericalScale.h" | 
| Chris@698 | 36 #include "LogNumericalScale.h" | 
| Chris@699 | 37 #include "LinearColourScale.h" | 
| Chris@699 | 38 #include "LogColourScale.h" | 
| Chris@66 | 39 | 
| Chris@0 | 40 #include <QPainter> | 
| Chris@6 | 41 #include <QPainterPath> | 
| Chris@21 | 42 #include <QMouseEvent> | 
| Chris@125 | 43 #include <QRegExp> | 
| Chris@316 | 44 #include <QTextStream> | 
| Chris@360 | 45 #include <QMessageBox> | 
| Chris@340 | 46 #include <QInputDialog> | 
| Chris@0 | 47 | 
| Chris@0 | 48 #include <iostream> | 
| Chris@0 | 49 #include <cmath> | 
| Chris@0 | 50 | 
| Chris@526 | 51 //#define DEBUG_TIME_VALUE_LAYER 1 | 
| Chris@526 | 52 | 
| Chris@44 | 53 TimeValueLayer::TimeValueLayer() : | 
| Chris@287 | 54     SingleColourLayer(), | 
| Chris@0 | 55     m_model(0), | 
| Chris@21 | 56     m_editing(false), | 
| Chris@23 | 57     m_originalPoint(0, 0.0, tr("New Point")), | 
| Chris@21 | 58     m_editingPoint(0, 0.0, tr("New Point")), | 
| Chris@22 | 59     m_editingCommand(0), | 
| Chris@197 | 60     m_colourMap(0), | 
| Chris@66 | 61     m_plotStyle(PlotConnectedPoints), | 
| Chris@437 | 62     m_verticalScale(AutoAlignScale), | 
| Chris@513 | 63     m_drawSegmentDivisions(true), | 
| Chris@553 | 64     m_derivative(false), | 
| Chris@437 | 65     m_scaleMinimum(0), | 
| Chris@437 | 66     m_scaleMaximum(0) | 
| Chris@0 | 67 { | 
| Chris@44 | 68 | 
| Chris@0 | 69 } | 
| Chris@0 | 70 | 
| Chris@0 | 71 void | 
| Chris@0 | 72 TimeValueLayer::setModel(SparseTimeValueModel *model) | 
| Chris@0 | 73 { | 
| Chris@0 | 74     if (m_model == model) return; | 
| Chris@0 | 75     m_model = model; | 
| Chris@0 | 76 | 
| Chris@320 | 77     connectSignals(m_model); | 
| Chris@0 | 78 | 
| Chris@438 | 79     m_scaleMinimum = 0; | 
| Chris@438 | 80     m_scaleMaximum = 0; | 
| Chris@438 | 81 | 
| Chris@494 | 82     if (m_model && m_model->getRDFTypeURI().endsWith("Segment")) { | 
| Chris@494 | 83         setPlotStyle(PlotSegmentation); | 
| Chris@494 | 84     } | 
| Chris@494 | 85     if (m_model && m_model->getRDFTypeURI().endsWith("Change")) { | 
| Chris@494 | 86         setPlotStyle(PlotSegmentation); | 
| Chris@494 | 87     } | 
| Chris@494 | 88 | 
| Chris@526 | 89 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 90     cerr << "TimeValueLayer::setModel(" << model << ")" << endl; | 
| Chris@526 | 91 #endif | 
| Chris@0 | 92 | 
| Chris@0 | 93     emit modelReplaced(); | 
| Chris@0 | 94 } | 
| Chris@0 | 95 | 
| Chris@0 | 96 Layer::PropertyList | 
| Chris@0 | 97 TimeValueLayer::getProperties() const | 
| Chris@0 | 98 { | 
| Chris@287 | 99     PropertyList list = SingleColourLayer::getProperties(); | 
| Chris@87 | 100     list.push_back("Plot Type"); | 
| Chris@87 | 101     list.push_back("Vertical Scale"); | 
| Chris@100 | 102     list.push_back("Scale Units"); | 
| Chris@513 | 103     list.push_back("Draw Segment Division Lines"); | 
| Chris@553 | 104     list.push_back("Show Derivative"); | 
| Chris@0 | 105     return list; | 
| Chris@0 | 106 } | 
| Chris@0 | 107 | 
| Chris@87 | 108 QString | 
| Chris@87 | 109 TimeValueLayer::getPropertyLabel(const PropertyName &name) const | 
| Chris@87 | 110 { | 
| Chris@87 | 111     if (name == "Plot Type") return tr("Plot Type"); | 
| Chris@87 | 112     if (name == "Vertical Scale") return tr("Vertical Scale"); | 
| Chris@100 | 113     if (name == "Scale Units") return tr("Scale Units"); | 
| Chris@513 | 114     if (name == "Draw Segment Division Lines") return tr("Draw Segment Division Lines"); | 
| Chris@553 | 115     if (name == "Show Derivative") return tr("Show Derivative"); | 
| Chris@287 | 116     return SingleColourLayer::getPropertyLabel(name); | 
| Chris@87 | 117 } | 
| Chris@87 | 118 | 
| Chris@515 | 119 QString | 
| Chris@515 | 120 TimeValueLayer::getPropertyIconName(const PropertyName &name) const | 
| Chris@515 | 121 { | 
| Chris@515 | 122     if (name == "Draw Segment Division Lines") return "lines"; | 
| Chris@553 | 123     if (name == "Show Derivative") return "derivative"; | 
| Chris@515 | 124     return ""; | 
| Chris@515 | 125 } | 
| Chris@515 | 126 | 
| Chris@0 | 127 Layer::PropertyType | 
| Chris@0 | 128 TimeValueLayer::getPropertyType(const PropertyName &name) const | 
| Chris@0 | 129 { | 
| Chris@287 | 130     if (name == "Plot Type") return ValueProperty; | 
| Chris@287 | 131     if (name == "Vertical Scale") return ValueProperty; | 
| Chris@100 | 132     if (name == "Scale Units") return UnitsProperty; | 
| Chris@287 | 133     if (name == "Colour" && m_plotStyle == PlotSegmentation) return ValueProperty; | 
| Chris@513 | 134     if (name == "Draw Segment Division Lines") return ToggleProperty; | 
| Chris@553 | 135     if (name == "Show Derivative") return ToggleProperty; | 
| Chris@287 | 136     return SingleColourLayer::getPropertyType(name); | 
| Chris@0 | 137 } | 
| Chris@0 | 138 | 
| Chris@198 | 139 QString | 
| Chris@198 | 140 TimeValueLayer::getPropertyGroupName(const PropertyName &name) const | 
| Chris@198 | 141 { | 
| Chris@198 | 142     if (name == "Vertical Scale" || name == "Scale Units") { | 
| Chris@198 | 143         return tr("Scale"); | 
| Chris@198 | 144     } | 
| Chris@553 | 145     if (name == "Plot Type" || name == "Draw Segment Division Lines" || | 
| Chris@553 | 146         name == "Show Derivative") { | 
| Chris@513 | 147         return tr("Plot Type"); | 
| Chris@513 | 148     } | 
| Chris@287 | 149     return SingleColourLayer::getPropertyGroupName(name); | 
| Chris@198 | 150 } | 
| Chris@198 | 151 | 
| Chris@698 | 152 QString | 
| Chris@698 | 153 TimeValueLayer::getScaleUnits() const | 
| Chris@698 | 154 { | 
| Chris@698 | 155     if (m_model) return m_model->getScaleUnits(); | 
| Chris@698 | 156     else return ""; | 
| Chris@698 | 157 } | 
| Chris@698 | 158 | 
| Chris@0 | 159 int | 
| Chris@0 | 160 TimeValueLayer::getPropertyRangeAndValue(const PropertyName &name, | 
| Chris@216 | 161 					 int *min, int *max, int *deflt) const | 
| Chris@0 | 162 { | 
| Chris@216 | 163     int val = 0; | 
| Chris@0 | 164 | 
| Chris@287 | 165     if (name == "Colour" && m_plotStyle == PlotSegmentation) { | 
| Chris@197 | 166 | 
| Chris@287 | 167         if (min) *min = 0; | 
| Chris@287 | 168         if (max) *max = ColourMapper::getColourMapCount() - 1; | 
| Chris@287 | 169         if (deflt) *deflt = 0; | 
| Chris@197 | 170 | 
| Chris@287 | 171         val = m_colourMap; | 
| Chris@0 | 172 | 
| Chris@87 | 173     } else if (name == "Plot Type") { | 
| Chris@0 | 174 | 
| Chris@10 | 175 	if (min) *min = 0; | 
| Chris@615 | 176 	if (max) *max = 6; | 
| Chris@216 | 177         if (deflt) *deflt = int(PlotConnectedPoints); | 
| Chris@0 | 178 | 
| Chris@216 | 179 	val = int(m_plotStyle); | 
| Chris@0 | 180 | 
| Chris@87 | 181     } else if (name == "Vertical Scale") { | 
| Chris@66 | 182 | 
| Chris@66 | 183 	if (min) *min = 0; | 
| Chris@66 | 184 	if (max) *max = 3; | 
| Chris@216 | 185         if (deflt) *deflt = int(AutoAlignScale); | 
| Chris@66 | 186 | 
| Chris@216 | 187 	val = int(m_verticalScale); | 
| Chris@66 | 188 | 
| Chris@100 | 189     } else if (name == "Scale Units") { | 
| Chris@100 | 190 | 
| Chris@216 | 191         if (deflt) *deflt = 0; | 
| Chris@100 | 192         if (m_model) { | 
| Chris@216 | 193             val = UnitDatabase::getInstance()->getUnitId | 
| Chris@698 | 194                 (getScaleUnits()); | 
| Chris@100 | 195         } | 
| Chris@100 | 196 | 
| Chris@513 | 197     } else if (name == "Draw Segment Division Lines") { | 
| Chris@513 | 198 | 
| Chris@513 | 199         if (min) *min = 0; | 
| Chris@513 | 200         if (max) *max = 0; | 
| Chris@513 | 201         if (deflt) *deflt = 1; | 
| Chris@513 | 202         val = (m_drawSegmentDivisions ? 1.0 : 0.0); | 
| Chris@513 | 203 | 
| Chris@553 | 204     } else if (name == "Show Derivative") { | 
| Chris@553 | 205 | 
| Chris@553 | 206         if (min) *min = 0; | 
| Chris@553 | 207         if (max) *max = 0; | 
| Chris@553 | 208         if (deflt) *deflt = 0; | 
| Chris@553 | 209         val = (m_derivative ? 1.0 : 0.0); | 
| Chris@553 | 210 | 
| Chris@0 | 211     } else { | 
| Chris@0 | 212 | 
| Chris@287 | 213 	val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); | 
| Chris@0 | 214     } | 
| Chris@0 | 215 | 
| Chris@216 | 216     return val; | 
| Chris@0 | 217 } | 
| Chris@0 | 218 | 
| Chris@0 | 219 QString | 
| Chris@0 | 220 TimeValueLayer::getPropertyValueLabel(const PropertyName &name, | 
| Chris@0 | 221 				    int value) const | 
| Chris@0 | 222 { | 
| Chris@287 | 223     if (name == "Colour" && m_plotStyle == PlotSegmentation) { | 
| Chris@287 | 224         return ColourMapper::getColourMapName(value); | 
| Chris@87 | 225     } else if (name == "Plot Type") { | 
| Chris@0 | 226 	switch (value) { | 
| Chris@0 | 227 	default: | 
| Chris@0 | 228 	case 0: return tr("Points"); | 
| Chris@0 | 229 	case 1: return tr("Stems"); | 
| Chris@6 | 230 	case 2: return tr("Connected Points"); | 
| Chris@6 | 231 	case 3: return tr("Lines"); | 
| Chris@6 | 232 	case 4: return tr("Curve"); | 
| Chris@26 | 233 	case 5: return tr("Segmentation"); | 
| Chris@615 | 234 	case 6: return tr("Discrete Curves"); | 
| Chris@0 | 235 	} | 
| Chris@87 | 236     } else if (name == "Vertical Scale") { | 
| Chris@66 | 237 	switch (value) { | 
| Chris@66 | 238 	default: | 
| Chris@101 | 239 	case 0: return tr("Auto-Align"); | 
| Chris@198 | 240 	case 1: return tr("Linear"); | 
| Chris@198 | 241 	case 2: return tr("Log"); | 
| Chris@198 | 242 	case 3: return tr("+/-1"); | 
| Chris@66 | 243 	} | 
| Chris@0 | 244     } | 
| Chris@287 | 245     return SingleColourLayer::getPropertyValueLabel(name, value); | 
| Chris@0 | 246 } | 
| Chris@0 | 247 | 
| Chris@0 | 248 void | 
| Chris@0 | 249 TimeValueLayer::setProperty(const PropertyName &name, int value) | 
| Chris@0 | 250 { | 
| Chris@287 | 251     if (name == "Colour" && m_plotStyle == PlotSegmentation) { | 
| Chris@287 | 252         setFillColourMap(value); | 
| Chris@87 | 253     } else if (name == "Plot Type") { | 
| Chris@0 | 254 	setPlotStyle(PlotStyle(value)); | 
| Chris@87 | 255     } else if (name == "Vertical Scale") { | 
| Chris@66 | 256 	setVerticalScale(VerticalScale(value)); | 
| Chris@100 | 257     } else if (name == "Scale Units") { | 
| Chris@100 | 258         if (m_model) { | 
| Chris@100 | 259             m_model->setScaleUnits | 
| Chris@100 | 260                 (UnitDatabase::getInstance()->getUnitById(value)); | 
| Chris@100 | 261             emit modelChanged(); | 
| Chris@100 | 262         } | 
| Chris@513 | 263     } else if (name == "Draw Segment Division Lines") { | 
| Chris@513 | 264         setDrawSegmentDivisions(value > 0.5); | 
| Chris@553 | 265     } else if (name == "Show Derivative") { | 
| Chris@553 | 266         setShowDerivative(value > 0.5); | 
| Chris@287 | 267     } else { | 
| Chris@287 | 268         SingleColourLayer::setProperty(name, value); | 
| Chris@0 | 269     } | 
| Chris@0 | 270 } | 
| Chris@0 | 271 | 
| Chris@0 | 272 void | 
| Chris@197 | 273 TimeValueLayer::setFillColourMap(int map) | 
| Chris@197 | 274 { | 
| Chris@197 | 275     if (m_colourMap == map) return; | 
| Chris@197 | 276     m_colourMap = map; | 
| Chris@197 | 277     emit layerParametersChanged(); | 
| Chris@197 | 278 } | 
| Chris@197 | 279 | 
| Chris@197 | 280 void | 
| Chris@0 | 281 TimeValueLayer::setPlotStyle(PlotStyle style) | 
| Chris@0 | 282 { | 
| Chris@0 | 283     if (m_plotStyle == style) return; | 
| Chris@197 | 284     bool colourTypeChanged = (style == PlotSegmentation || | 
| Chris@197 | 285                               m_plotStyle == PlotSegmentation); | 
| Chris@0 | 286     m_plotStyle = style; | 
| Chris@197 | 287     if (colourTypeChanged) { | 
| Chris@197 | 288         emit layerParameterRangesChanged(); | 
| Chris@197 | 289     } | 
| Chris@0 | 290     emit layerParametersChanged(); | 
| Chris@0 | 291 } | 
| Chris@0 | 292 | 
| Chris@66 | 293 void | 
| Chris@66 | 294 TimeValueLayer::setVerticalScale(VerticalScale scale) | 
| Chris@66 | 295 { | 
| Chris@66 | 296     if (m_verticalScale == scale) return; | 
| Chris@66 | 297     m_verticalScale = scale; | 
| Chris@66 | 298     emit layerParametersChanged(); | 
| Chris@66 | 299 } | 
| Chris@66 | 300 | 
| Chris@513 | 301 void | 
| Chris@513 | 302 TimeValueLayer::setDrawSegmentDivisions(bool draw) | 
| Chris@513 | 303 { | 
| Chris@513 | 304     if (m_drawSegmentDivisions == draw) return; | 
| Chris@513 | 305     m_drawSegmentDivisions = draw; | 
| Chris@513 | 306     emit layerParametersChanged(); | 
| Chris@513 | 307 } | 
| Chris@513 | 308 | 
| Chris@553 | 309 void | 
| Chris@553 | 310 TimeValueLayer::setShowDerivative(bool show) | 
| Chris@553 | 311 { | 
| Chris@553 | 312     if (m_derivative == show) return; | 
| Chris@553 | 313     m_derivative = show; | 
| Chris@553 | 314     emit layerParametersChanged(); | 
| Chris@553 | 315 } | 
| Chris@553 | 316 | 
| Chris@0 | 317 bool | 
| Chris@44 | 318 TimeValueLayer::isLayerScrollable(const View *v) const | 
| Chris@0 | 319 { | 
| Chris@6 | 320     // We don't illuminate sections in the line or curve modes, so | 
| Chris@6 | 321     // they're always scrollable | 
| Chris@6 | 322 | 
| Chris@6 | 323     if (m_plotStyle == PlotLines || | 
| Chris@615 | 324 	m_plotStyle == PlotCurve || | 
| Chris@615 | 325         m_plotStyle == PlotDiscreteCurves) return true; | 
| Chris@6 | 326 | 
| Chris@0 | 327     QPoint discard; | 
| Chris@44 | 328     return !v->shouldIlluminateLocalFeatures(this, discard); | 
| Chris@0 | 329 } | 
| Chris@0 | 330 | 
| Chris@79 | 331 bool | 
| Chris@101 | 332 TimeValueLayer::getValueExtents(float &min, float &max, | 
| Chris@101 | 333                                 bool &logarithmic, QString &unit) const | 
| Chris@79 | 334 { | 
| Chris@101 | 335     if (!m_model) return false; | 
| Chris@668 | 336 | 
| Chris@79 | 337     min = m_model->getValueMinimum(); | 
| Chris@79 | 338     max = m_model->getValueMaximum(); | 
| Chris@668 | 339 | 
| Chris@101 | 340     logarithmic = (m_verticalScale == LogScale); | 
| Chris@668 | 341 | 
| Chris@698 | 342     unit = getScaleUnits(); | 
| Chris@668 | 343 | 
| Chris@553 | 344     if (m_derivative) { | 
| Chris@553 | 345         max = std::max(fabsf(min), fabsf(max)); | 
| Chris@553 | 346         min = -max; | 
| Chris@553 | 347     } | 
| Chris@629 | 348 | 
| Chris@629 | 349 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 350     cerr << "TimeValueLayer::getValueExtents: min = " << min << ", max = " << max << endl; | 
| Chris@629 | 351 #endif | 
| Chris@629 | 352 | 
| Chris@668 | 353     if (!shouldAutoAlign() && !logarithmic && !m_derivative) { | 
| Chris@668 | 354 | 
| Chris@668 | 355         if (max == min) { | 
| Chris@668 | 356             max = max + 0.5; | 
| Chris@668 | 357             min = min - 0.5; | 
| Chris@668 | 358         } else { | 
| Chris@668 | 359             float margin = (max - min) / 10.0; | 
| Chris@668 | 360             max = max + margin; | 
| Chris@668 | 361             min = min - margin; | 
| Chris@668 | 362         } | 
| Chris@668 | 363 | 
| Chris@668 | 364 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 365         cerr << "TimeValueLayer::getValueExtents: min = " << min << ", max = " << max << " (after adjustment)" << endl; | 
| Chris@668 | 366 #endif | 
| Chris@668 | 367     } | 
| Chris@668 | 368 | 
| Chris@79 | 369     return true; | 
| Chris@79 | 370 } | 
| Chris@79 | 371 | 
| Chris@101 | 372 bool | 
| Chris@101 | 373 TimeValueLayer::getDisplayExtents(float &min, float &max) const | 
| Chris@101 | 374 { | 
| Chris@296 | 375     if (!m_model || shouldAutoAlign()) return false; | 
| Chris@101 | 376 | 
| Chris@437 | 377     if (m_scaleMinimum == m_scaleMaximum) { | 
| Chris@668 | 378         bool log; | 
| Chris@668 | 379         QString unit; | 
| Chris@668 | 380         getValueExtents(min, max, log, unit); | 
| Chris@553 | 381     } else { | 
| Chris@553 | 382         min = m_scaleMinimum; | 
| Chris@553 | 383         max = m_scaleMaximum; | 
| Chris@437 | 384     } | 
| Chris@437 | 385 | 
| Chris@553 | 386     if (m_derivative) { | 
| Chris@553 | 387         max = std::max(fabsf(min), fabsf(max)); | 
| Chris@553 | 388         min = -max; | 
| Chris@553 | 389     } | 
| Chris@437 | 390 | 
| Chris@526 | 391 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 392     cerr << "TimeValueLayer::getDisplayExtents: min = " << min << ", max = " << max << endl; | 
| Chris@526 | 393 #endif | 
| Chris@437 | 394 | 
| Chris@101 | 395     return true; | 
| Chris@101 | 396 } | 
| Chris@101 | 397 | 
| Chris@437 | 398 bool | 
| Chris@437 | 399 TimeValueLayer::setDisplayExtents(float min, float max) | 
| Chris@437 | 400 { | 
| Chris@437 | 401     if (!m_model) return false; | 
| Chris@437 | 402 | 
| Chris@437 | 403     if (min == max) { | 
| Chris@437 | 404         if (min == 0.f) { | 
| Chris@437 | 405             max = 1.f; | 
| Chris@437 | 406         } else { | 
| Chris@437 | 407             max = min * 1.0001; | 
| Chris@437 | 408         } | 
| Chris@437 | 409     } | 
| Chris@437 | 410 | 
| Chris@437 | 411     m_scaleMinimum = min; | 
| Chris@437 | 412     m_scaleMaximum = max; | 
| Chris@437 | 413 | 
| Chris@526 | 414 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 415     cerr << "TimeValueLayer::setDisplayExtents: min = " << min << ", max = " << max << endl; | 
| Chris@526 | 416 #endif | 
| Chris@437 | 417 | 
| Chris@437 | 418     emit layerParametersChanged(); | 
| Chris@437 | 419     return true; | 
| Chris@437 | 420 } | 
| Chris@437 | 421 | 
| Chris@437 | 422 int | 
| Chris@437 | 423 TimeValueLayer::getVerticalZoomSteps(int &defaultStep) const | 
| Chris@437 | 424 { | 
| Chris@437 | 425     if (shouldAutoAlign()) return 0; | 
| Chris@437 | 426     if (!m_model) return 0; | 
| Chris@437 | 427 | 
| Chris@439 | 428     defaultStep = 0; | 
| Chris@437 | 429     return 100; | 
| Chris@437 | 430 } | 
| Chris@437 | 431 | 
| Chris@437 | 432 int | 
| Chris@437 | 433 TimeValueLayer::getCurrentVerticalZoomStep() const | 
| Chris@437 | 434 { | 
| Chris@437 | 435     if (shouldAutoAlign()) return 0; | 
| Chris@437 | 436     if (!m_model) return 0; | 
| Chris@437 | 437 | 
| Chris@437 | 438     RangeMapper *mapper = getNewVerticalZoomRangeMapper(); | 
| Chris@437 | 439     if (!mapper) return 0; | 
| Chris@437 | 440 | 
| Chris@437 | 441     float dmin, dmax; | 
| Chris@437 | 442     getDisplayExtents(dmin, dmax); | 
| Chris@437 | 443 | 
| Chris@437 | 444     int nr = mapper->getPositionForValue(dmax - dmin); | 
| Chris@526 | 445 | 
| Chris@526 | 446 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 447     cerr << "TimeValueLayer::getCurrentVerticalZoomStep: dmin = " << dmin << ", dmax = " << dmax << ", nr = " << nr << endl; | 
| Chris@526 | 448 #endif | 
| Chris@526 | 449 | 
| Chris@437 | 450     delete mapper; | 
| Chris@437 | 451 | 
| Chris@437 | 452     return 100 - nr; | 
| Chris@437 | 453 } | 
| Chris@437 | 454 | 
| Chris@437 | 455 void | 
| Chris@437 | 456 TimeValueLayer::setVerticalZoomStep(int step) | 
| Chris@437 | 457 { | 
| Chris@437 | 458     if (shouldAutoAlign()) return; | 
| Chris@437 | 459     if (!m_model) return; | 
| Chris@437 | 460 | 
| Chris@437 | 461     RangeMapper *mapper = getNewVerticalZoomRangeMapper(); | 
| Chris@437 | 462     if (!mapper) return; | 
| Chris@437 | 463 | 
| Chris@437 | 464     float min, max; | 
| Chris@437 | 465     bool logarithmic; | 
| Chris@437 | 466     QString unit; | 
| Chris@437 | 467     getValueExtents(min, max, logarithmic, unit); | 
| Chris@437 | 468 | 
| Chris@437 | 469     float dmin, dmax; | 
| Chris@437 | 470     getDisplayExtents(dmin, dmax); | 
| Chris@437 | 471 | 
| Chris@437 | 472     float newdist = mapper->getValueForPosition(100 - step); | 
| Chris@437 | 473 | 
| Chris@437 | 474     float newmin, newmax; | 
| Chris@437 | 475 | 
| Chris@437 | 476     if (logarithmic) { | 
| Chris@437 | 477 | 
| Chris@437 | 478         // see SpectrogramLayer::setVerticalZoomStep | 
| Chris@437 | 479 | 
| Chris@437 | 480         newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2; | 
| Chris@437 | 481         newmin = newmax - newdist; | 
| Chris@437 | 482 | 
| Chris@526 | 483 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 484         cerr << "newmin = " << newmin << ", newmax = " << newmax << endl; | 
| Chris@526 | 485 #endif | 
| Chris@437 | 486 | 
| Chris@437 | 487     } else { | 
| Chris@437 | 488         float dmid = (dmax + dmin) / 2; | 
| Chris@437 | 489         newmin = dmid - newdist / 2; | 
| Chris@437 | 490         newmax = dmid + newdist / 2; | 
| Chris@437 | 491     } | 
| Chris@437 | 492 | 
| Chris@437 | 493     if (newmin < min) { | 
| Chris@437 | 494         newmax += (min - newmin); | 
| Chris@437 | 495         newmin = min; | 
| Chris@437 | 496     } | 
| Chris@437 | 497     if (newmax > max) { | 
| Chris@437 | 498         newmax = max; | 
| Chris@437 | 499     } | 
| Chris@437 | 500 | 
| Chris@526 | 501 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 502     cerr << "TimeValueLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl; | 
| Chris@526 | 503 #endif | 
| Chris@437 | 504 | 
| Chris@437 | 505     setDisplayExtents(newmin, newmax); | 
| Chris@437 | 506 } | 
| Chris@437 | 507 | 
| Chris@437 | 508 RangeMapper * | 
| Chris@437 | 509 TimeValueLayer::getNewVerticalZoomRangeMapper() const | 
| Chris@437 | 510 { | 
| Chris@437 | 511     if (!m_model) return 0; | 
| Chris@437 | 512 | 
| Chris@437 | 513     RangeMapper *mapper; | 
| Chris@437 | 514 | 
| Chris@437 | 515     float min, max; | 
| Chris@437 | 516     bool logarithmic; | 
| Chris@437 | 517     QString unit; | 
| Chris@437 | 518     getValueExtents(min, max, logarithmic, unit); | 
| Chris@437 | 519 | 
| Chris@437 | 520     if (min == max) return 0; | 
| Chris@437 | 521 | 
| Chris@437 | 522     if (logarithmic) { | 
| Chris@437 | 523         mapper = new LogRangeMapper(0, 100, min, max, unit); | 
| Chris@437 | 524     } else { | 
| Chris@437 | 525         mapper = new LinearRangeMapper(0, 100, min, max, unit); | 
| Chris@437 | 526     } | 
| Chris@437 | 527 | 
| Chris@437 | 528     return mapper; | 
| Chris@437 | 529 } | 
| Chris@437 | 530 | 
| Chris@0 | 531 SparseTimeValueModel::PointList | 
| Chris@44 | 532 TimeValueLayer::getLocalPoints(View *v, int x) const | 
| Chris@0 | 533 { | 
| Chris@0 | 534     if (!m_model) return SparseTimeValueModel::PointList(); | 
| Chris@0 | 535 | 
| Chris@44 | 536     long frame = v->getFrameForX(x); | 
| Chris@0 | 537 | 
| Chris@0 | 538     SparseTimeValueModel::PointList onPoints = | 
| Chris@0 | 539 	m_model->getPoints(frame); | 
| Chris@0 | 540 | 
| Chris@0 | 541     if (!onPoints.empty()) { | 
| Chris@0 | 542 	return onPoints; | 
| Chris@0 | 543     } | 
| Chris@0 | 544 | 
| Chris@0 | 545     SparseTimeValueModel::PointList prevPoints = | 
| Chris@0 | 546 	m_model->getPreviousPoints(frame); | 
| Chris@0 | 547     SparseTimeValueModel::PointList nextPoints = | 
| Chris@0 | 548 	m_model->getNextPoints(frame); | 
| Chris@0 | 549 | 
| Chris@0 | 550     SparseTimeValueModel::PointList usePoints = prevPoints; | 
| Chris@0 | 551 | 
| Chris@0 | 552     if (prevPoints.empty()) { | 
| Chris@0 | 553 	usePoints = nextPoints; | 
| Chris@631 | 554     } else if (nextPoints.empty()) { | 
| Chris@631 | 555         // stick with prevPoints | 
| Chris@248 | 556     } else if (long(prevPoints.begin()->frame) < v->getStartFrame() && | 
| Chris@44 | 557 	       !(nextPoints.begin()->frame > v->getEndFrame())) { | 
| Chris@0 | 558 	usePoints = nextPoints; | 
| Chris@0 | 559     } else if (nextPoints.begin()->frame - frame < | 
| Chris@0 | 560 	       frame - prevPoints.begin()->frame) { | 
| Chris@0 | 561 	usePoints = nextPoints; | 
| Chris@0 | 562     } | 
| Chris@0 | 563 | 
| Chris@28 | 564     if (!usePoints.empty()) { | 
| Chris@28 | 565 	int fuzz = 2; | 
| Chris@44 | 566 	int px = v->getXForFrame(usePoints.begin()->frame); | 
| Chris@28 | 567 	if ((px > x && px - x > fuzz) || | 
| Chris@631 | 568 	    (px < x && x - px > fuzz + 3)) { | 
| Chris@28 | 569 	    usePoints.clear(); | 
| Chris@28 | 570 	} | 
| Chris@28 | 571     } | 
| Chris@28 | 572 | 
| Chris@0 | 573     return usePoints; | 
| Chris@0 | 574 } | 
| Chris@0 | 575 | 
| Chris@25 | 576 QString | 
| Chris@552 | 577 TimeValueLayer::getLabelPreceding(size_t frame) const | 
| Chris@552 | 578 { | 
| Chris@552 | 579     if (!m_model) return ""; | 
| Chris@552 | 580     SparseTimeValueModel::PointList points = m_model->getPreviousPoints(frame); | 
| Chris@552 | 581     for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); | 
| Chris@552 | 582          i != points.end(); ++i) { | 
| Chris@552 | 583         if (i->label != "") return i->label; | 
| Chris@552 | 584     } | 
| Chris@552 | 585     return ""; | 
| Chris@552 | 586 } | 
| Chris@552 | 587 | 
| Chris@552 | 588 QString | 
| Chris@44 | 589 TimeValueLayer::getFeatureDescription(View *v, QPoint &pos) const | 
| Chris@0 | 590 { | 
| Chris@25 | 591     int x = pos.x(); | 
| Chris@0 | 592 | 
| Chris@25 | 593     if (!m_model || !m_model->getSampleRate()) return ""; | 
| Chris@0 | 594 | 
| Chris@44 | 595     SparseTimeValueModel::PointList points = getLocalPoints(v, x); | 
| Chris@0 | 596 | 
| Chris@0 | 597     if (points.empty()) { | 
| Chris@0 | 598 	if (!m_model->isReady()) { | 
| Chris@25 | 599 	    return tr("In progress"); | 
| Chris@25 | 600 	} else { | 
| Chris@25 | 601 	    return tr("No local points"); | 
| Chris@0 | 602 	} | 
| Chris@0 | 603     } | 
| Chris@0 | 604 | 
| Chris@0 | 605     long useFrame = points.begin()->frame; | 
| Chris@0 | 606 | 
| Chris@0 | 607     RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate()); | 
| Chris@25 | 608 | 
| Chris@25 | 609     QString text; | 
| Chris@698 | 610     QString unit = getScaleUnits(); | 
| Chris@101 | 611     if (unit != "") unit = " " + unit; | 
| Chris@0 | 612 | 
| Chris@25 | 613     if (points.begin()->label == "") { | 
| Chris@101 | 614 	text = QString(tr("Time:\t%1\nValue:\t%2%3\nNo label")) | 
| Chris@25 | 615 	    .arg(rt.toText(true).c_str()) | 
| Chris@25 | 616 	    .arg(points.begin()->value) | 
| Chris@101 | 617             .arg(unit); | 
| Chris@101 | 618     } else { | 
| Chris@101 | 619 	text = QString(tr("Time:\t%1\nValue:\t%2%3\nLabel:\t%4")) | 
| Chris@101 | 620 	    .arg(rt.toText(true).c_str()) | 
| Chris@101 | 621 	    .arg(points.begin()->value) | 
| Chris@101 | 622             .arg(unit) | 
| Chris@25 | 623 	    .arg(points.begin()->label); | 
| Chris@25 | 624     } | 
| Chris@0 | 625 | 
| Chris@44 | 626     pos = QPoint(v->getXForFrame(useFrame), | 
| Chris@44 | 627 		 getYForValue(v, points.begin()->value)); | 
| Chris@25 | 628     return text; | 
| Chris@0 | 629 } | 
| Chris@0 | 630 | 
| Chris@28 | 631 bool | 
| Chris@44 | 632 TimeValueLayer::snapToFeatureFrame(View *v, int &frame, | 
| Chris@28 | 633 				   size_t &resolution, | 
| Chris@28 | 634 				   SnapType snap) const | 
| Chris@13 | 635 { | 
| Chris@13 | 636     if (!m_model) { | 
| Chris@44 | 637 	return Layer::snapToFeatureFrame(v, frame, resolution, snap); | 
| Chris@13 | 638     } | 
| Chris@13 | 639 | 
| Chris@13 | 640     resolution = m_model->getResolution(); | 
| Chris@28 | 641     SparseTimeValueModel::PointList points; | 
| Chris@13 | 642 | 
| Chris@28 | 643     if (snap == SnapNeighbouring) { | 
| Chris@28 | 644 | 
| Chris@44 | 645 	points = getLocalPoints(v, v->getXForFrame(frame)); | 
| Chris@28 | 646 	if (points.empty()) return false; | 
| Chris@28 | 647 	frame = points.begin()->frame; | 
| Chris@28 | 648 	return true; | 
| Chris@28 | 649     } | 
| Chris@28 | 650 | 
| Chris@28 | 651     points = m_model->getPoints(frame, frame); | 
| Chris@28 | 652     int snapped = frame; | 
| Chris@28 | 653     bool found = false; | 
| Chris@13 | 654 | 
| Chris@13 | 655     for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); | 
| Chris@13 | 656 	 i != points.end(); ++i) { | 
| Chris@13 | 657 | 
| Chris@28 | 658 	if (snap == SnapRight) { | 
| Chris@28 | 659 | 
| Chris@13 | 660 	    if (i->frame > frame) { | 
| Chris@28 | 661 		snapped = i->frame; | 
| Chris@28 | 662 		found = true; | 
| Chris@13 | 663 		break; | 
| Chris@13 | 664 	    } | 
| Chris@28 | 665 | 
| Chris@28 | 666 	} else if (snap == SnapLeft) { | 
| Chris@28 | 667 | 
| Chris@13 | 668 	    if (i->frame <= frame) { | 
| Chris@28 | 669 		snapped = i->frame; | 
| Chris@28 | 670 		found = true; // don't break, as the next may be better | 
| Chris@28 | 671 	    } else { | 
| Chris@28 | 672 		break; | 
| Chris@28 | 673 	    } | 
| Chris@28 | 674 | 
| Chris@28 | 675 	} else { // nearest | 
| Chris@28 | 676 | 
| Chris@28 | 677 	    SparseTimeValueModel::PointList::const_iterator j = i; | 
| Chris@28 | 678 	    ++j; | 
| Chris@28 | 679 | 
| Chris@28 | 680 	    if (j == points.end()) { | 
| Chris@28 | 681 | 
| Chris@28 | 682 		snapped = i->frame; | 
| Chris@28 | 683 		found = true; | 
| Chris@28 | 684 		break; | 
| Chris@28 | 685 | 
| Chris@28 | 686 	    } else if (j->frame >= frame) { | 
| Chris@28 | 687 | 
| Chris@28 | 688 		if (j->frame - frame < frame - i->frame) { | 
| Chris@28 | 689 		    snapped = j->frame; | 
| Chris@28 | 690 		} else { | 
| Chris@28 | 691 		    snapped = i->frame; | 
| Chris@28 | 692 		} | 
| Chris@28 | 693 		found = true; | 
| Chris@28 | 694 		break; | 
| Chris@13 | 695 	    } | 
| Chris@13 | 696 	} | 
| Chris@13 | 697     } | 
| Chris@13 | 698 | 
| Chris@28 | 699     frame = snapped; | 
| Chris@28 | 700     return found; | 
| Chris@13 | 701 } | 
| Chris@13 | 702 | 
| Chris@517 | 703 bool | 
| Chris@517 | 704 TimeValueLayer::snapToSimilarFeature(View *v, int &frame, | 
| Chris@517 | 705                                      size_t &resolution, | 
| Chris@517 | 706                                      SnapType snap) const | 
| Chris@517 | 707 { | 
| Chris@517 | 708     if (!m_model) { | 
| Chris@517 | 709 	return Layer::snapToSimilarFeature(v, frame, resolution, snap); | 
| Chris@517 | 710     } | 
| Chris@517 | 711 | 
| Chris@517 | 712     resolution = m_model->getResolution(); | 
| Chris@517 | 713 | 
| Chris@517 | 714     const SparseTimeValueModel::PointList &points = m_model->getPoints(); | 
| Chris@517 | 715     SparseTimeValueModel::PointList close = m_model->getPoints(frame, frame); | 
| Chris@517 | 716 | 
| Chris@517 | 717     SparseTimeValueModel::PointList::const_iterator i; | 
| Chris@517 | 718 | 
| Chris@517 | 719     int matchframe = frame; | 
| Chris@517 | 720     float matchvalue = 0.f; | 
| Chris@517 | 721 | 
| Chris@517 | 722     for (i = close.begin(); i != close.end(); ++i) { | 
| Chris@517 | 723         if (i->frame > frame) break; | 
| Chris@517 | 724         matchvalue = i->value; | 
| Chris@517 | 725         matchframe = i->frame; | 
| Chris@517 | 726     } | 
| Chris@517 | 727 | 
| Chris@517 | 728     int snapped = frame; | 
| Chris@517 | 729     bool found = false; | 
| Chris@517 | 730     bool distant = false; | 
| Chris@517 | 731     float epsilon = 0.0001; | 
| Chris@517 | 732 | 
| Chris@517 | 733     i = close.begin(); | 
| Chris@517 | 734 | 
| Chris@517 | 735     // Scan through the close points first, then the more distant ones | 
| Chris@517 | 736     // if no suitable close one is found | 
| Chris@517 | 737 | 
| Chris@517 | 738     while (i != points.end()) { | 
| Chris@517 | 739 | 
| Chris@517 | 740         if (i == close.end()) { | 
| Chris@517 | 741             i = points.begin(); | 
| Chris@517 | 742             distant = true; | 
| Chris@517 | 743         } | 
| Chris@517 | 744 | 
| Chris@517 | 745 	if (snap == SnapRight) { | 
| Chris@517 | 746 | 
| Chris@517 | 747 	    if (i->frame > matchframe && | 
| Chris@517 | 748                 fabsf(i->value - matchvalue) < epsilon) { | 
| Chris@517 | 749 		snapped = i->frame; | 
| Chris@517 | 750 		found = true; | 
| Chris@517 | 751 		break; | 
| Chris@517 | 752 	    } | 
| Chris@517 | 753 | 
| Chris@517 | 754 	} else if (snap == SnapLeft) { | 
| Chris@517 | 755 | 
| Chris@517 | 756 	    if (i->frame < matchframe) { | 
| Chris@517 | 757                 if (fabsf(i->value - matchvalue) < epsilon) { | 
| Chris@517 | 758                     snapped = i->frame; | 
| Chris@517 | 759                     found = true; // don't break, as the next may be better | 
| Chris@517 | 760                 } | 
| Chris@517 | 761 	    } else if (found || distant) { | 
| Chris@517 | 762 		break; | 
| Chris@517 | 763 	    } | 
| Chris@517 | 764 | 
| Chris@517 | 765 	} else { | 
| Chris@517 | 766             // no other snap types supported | 
| Chris@517 | 767 	} | 
| Chris@517 | 768 | 
| Chris@517 | 769         ++i; | 
| Chris@517 | 770     } | 
| Chris@517 | 771 | 
| Chris@517 | 772     frame = snapped; | 
| Chris@517 | 773     return found; | 
| Chris@517 | 774 } | 
| Chris@517 | 775 | 
| Chris@101 | 776 void | 
| Chris@101 | 777 TimeValueLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const | 
| Chris@101 | 778 { | 
| Chris@101 | 779     min = 0.0; | 
| Chris@101 | 780     max = 0.0; | 
| Chris@101 | 781     log = false; | 
| Chris@101 | 782 | 
| Chris@296 | 783     if (shouldAutoAlign()) { | 
| Chris@101 | 784 | 
| Chris@698 | 785         if (!v->getValueExtents(getScaleUnits(), min, max, log)) { | 
| Chris@101 | 786             min = m_model->getValueMinimum(); | 
| Chris@101 | 787             max = m_model->getValueMaximum(); | 
| Chris@668 | 788         } else if (log) { | 
| Chris@668 | 789             LogRange::mapRange(min, max); | 
| Chris@101 | 790         } | 
| Chris@101 | 791 | 
| Chris@101 | 792     } else if (m_verticalScale == PlusMinusOneScale) { | 
| Chris@101 | 793 | 
| Chris@101 | 794         min = -1.0; | 
| Chris@101 | 795         max = 1.0; | 
| Chris@101 | 796 | 
| Chris@101 | 797     } else { | 
| Chris@101 | 798 | 
| Chris@437 | 799         getDisplayExtents(min, max); | 
| Chris@632 | 800 | 
| Chris@101 | 801         if (m_verticalScale == LogScale) { | 
| Chris@197 | 802             LogRange::mapRange(min, max); | 
| Chris@101 | 803             log = true; | 
| Chris@101 | 804         } | 
| Chris@101 | 805     } | 
| Chris@101 | 806 | 
| Chris@629 | 807 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 808     cerr << "TimeValueLayer::getScaleExtents: min = " << min << ", max = " << max << endl; | 
| Chris@629 | 809 #endif | 
| Chris@101 | 810 } | 
| Chris@101 | 811 | 
| Chris@21 | 812 int | 
| Chris@66 | 813 TimeValueLayer::getYForValue(View *v, float val) const | 
| Chris@21 | 814 { | 
| Chris@79 | 815     float min = 0.0, max = 0.0; | 
| Chris@101 | 816     bool logarithmic = false; | 
| Chris@79 | 817     int h = v->height(); | 
| Chris@79 | 818 | 
| Chris@101 | 819     getScaleExtents(v, min, max, logarithmic); | 
| Chris@101 | 820 | 
| Chris@526 | 821 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 822     cerr << "getYForValue(" << val << "): min " << min << ", max " | 
| Chris@682 | 823               << max << ", log " << logarithmic << endl; | 
| Chris@526 | 824 #endif | 
| Chris@101 | 825 | 
| Chris@101 | 826     if (logarithmic) { | 
| Chris@197 | 827         val = LogRange::map(val); | 
| Chris@79 | 828     } | 
| Chris@79 | 829 | 
| Chris@66 | 830     return int(h - ((val - min) * h) / (max - min)); | 
| Chris@21 | 831 } | 
| Chris@21 | 832 | 
| Chris@21 | 833 float | 
| Chris@44 | 834 TimeValueLayer::getValueForY(View *v, int y) const | 
| Chris@21 | 835 { | 
| Chris@101 | 836     float min = 0.0, max = 0.0; | 
| Chris@101 | 837     bool logarithmic = false; | 
| Chris@44 | 838     int h = v->height(); | 
| Chris@21 | 839 | 
| Chris@101 | 840     getScaleExtents(v, min, max, logarithmic); | 
| Chris@101 | 841 | 
| Chris@101 | 842     float val = min + (float(h - y) * float(max - min)) / h; | 
| Chris@101 | 843 | 
| Chris@101 | 844     if (logarithmic) { | 
| Chris@437 | 845         val = LogRange::map(val); | 
| Chris@101 | 846     } | 
| Chris@101 | 847 | 
| Chris@101 | 848     return val; | 
| Chris@21 | 849 } | 
| Chris@21 | 850 | 
| Chris@296 | 851 bool | 
| Chris@296 | 852 TimeValueLayer::shouldAutoAlign() const | 
| Chris@296 | 853 { | 
| Chris@296 | 854     if (!m_model) return false; | 
| Chris@698 | 855     QString unit = getScaleUnits(); | 
| Chris@296 | 856     return (m_verticalScale == AutoAlignScale && unit != ""); | 
| Chris@296 | 857 } | 
| Chris@296 | 858 | 
| Chris@68 | 859 QColor | 
| Chris@101 | 860 TimeValueLayer::getColourForValue(View *v, float val) const | 
| Chris@68 | 861 { | 
| Chris@101 | 862     float min, max; | 
| Chris@101 | 863     bool log; | 
| Chris@101 | 864     getScaleExtents(v, min, max, log); | 
| Chris@68 | 865 | 
| Chris@197 | 866     if (min > max) std::swap(min, max); | 
| Chris@197 | 867     if (max == min) max = min + 1; | 
| Chris@197 | 868 | 
| Chris@101 | 869     if (log) { | 
| Chris@197 | 870         val = LogRange::map(val); | 
| Chris@68 | 871     } | 
| Chris@68 | 872 | 
| Chris@526 | 873 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 874     cerr << "TimeValueLayer::getColourForValue: min " << min << ", max " | 
| Chris@682 | 875               << max << ", log " << log << ", value " << val << endl; | 
| Chris@526 | 876 #endif | 
| Chris@68 | 877 | 
| Chris@197 | 878     QColor solid = ColourMapper(m_colourMap, min, max).map(val); | 
| Chris@197 | 879     return QColor(solid.red(), solid.green(), solid.blue(), 120); | 
| Chris@68 | 880 } | 
| Chris@68 | 881 | 
| Chris@287 | 882 int | 
| Chris@287 | 883 TimeValueLayer::getDefaultColourHint(bool darkbg, bool &impose) | 
| Chris@287 | 884 { | 
| Chris@287 | 885     impose = false; | 
| Chris@287 | 886     return ColourDatabase::getInstance()->getColourIndex | 
| Chris@287 | 887         (QString(darkbg ? "Bright Green" : "Green")); | 
| Chris@287 | 888 } | 
| Chris@287 | 889 | 
| Chris@0 | 890 void | 
| Chris@44 | 891 TimeValueLayer::paint(View *v, QPainter &paint, QRect rect) const | 
| Chris@0 | 892 { | 
| Chris@0 | 893     if (!m_model || !m_model->isOK()) return; | 
| Chris@0 | 894 | 
| Chris@0 | 895     int sampleRate = m_model->getSampleRate(); | 
| Chris@0 | 896     if (!sampleRate) return; | 
| Chris@0 | 897 | 
| Chris@353 | 898     paint.setRenderHint(QPainter::Antialiasing, false); | 
| Chris@353 | 899 | 
| Chris@0 | 900 //    Profiler profiler("TimeValueLayer::paint", true); | 
| Chris@0 | 901 | 
| Chris@0 | 902     int x0 = rect.left(), x1 = rect.right(); | 
| Chris@44 | 903     long frame0 = v->getFrameForX(x0); | 
| Chris@44 | 904     long frame1 = v->getFrameForX(x1); | 
| Chris@553 | 905     if (m_derivative) --frame0; | 
| Chris@0 | 906 | 
| Chris@0 | 907     SparseTimeValueModel::PointList points(m_model->getPoints | 
| Chris@0 | 908 					   (frame0, frame1)); | 
| Chris@11 | 909     if (points.empty()) return; | 
| Chris@0 | 910 | 
| Chris@287 | 911     paint.setPen(getBaseQColor()); | 
| Chris@0 | 912 | 
| Chris@287 | 913     QColor brushColour(getBaseQColor()); | 
| Chris@0 | 914     brushColour.setAlpha(80); | 
| Chris@0 | 915     paint.setBrush(brushColour); | 
| Chris@0 | 916 | 
| Chris@526 | 917 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 918     cerr << "TimeValueLayer::paint: resolution is " | 
| Chris@682 | 919 	      << m_model->getResolution() << " frames" << endl; | 
| Chris@526 | 920 #endif | 
| Chris@0 | 921 | 
| Chris@0 | 922     float min = m_model->getValueMinimum(); | 
| Chris@0 | 923     float max = m_model->getValueMaximum(); | 
| Chris@0 | 924     if (max == min) max = min + 1.0; | 
| Chris@0 | 925 | 
| Chris@44 | 926     int origin = int(nearbyint(v->height() - | 
| Chris@44 | 927 			       (-min * v->height()) / (max - min))); | 
| Chris@0 | 928 | 
| Chris@0 | 929     QPoint localPos; | 
| Chris@0 | 930     long illuminateFrame = -1; | 
| Chris@0 | 931 | 
| Chris@44 | 932     if (v->shouldIlluminateLocalFeatures(this, localPos)) { | 
| Chris@0 | 933 	SparseTimeValueModel::PointList localPoints = | 
| Chris@44 | 934 	    getLocalPoints(v, localPos.x()); | 
| Chris@526 | 935 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 936         cerr << "TimeValueLayer: " << localPoints.size() << " local points" << endl; | 
| Chris@526 | 937 #endif | 
| Chris@0 | 938 	if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame; | 
| Chris@0 | 939     } | 
| Chris@6 | 940 | 
| Chris@20 | 941     int w = | 
| Chris@44 | 942 	v->getXForFrame(frame0 + m_model->getResolution()) - | 
| Chris@44 | 943 	v->getXForFrame(frame0); | 
| Chris@7 | 944 | 
| Chris@629 | 945     if (m_plotStyle == PlotStems) { | 
| Chris@629 | 946         if (w < 2) w = 2; | 
| Chris@629 | 947     } else { | 
| Chris@629 | 948         if (w < 1) w = 1; | 
| Chris@629 | 949     } | 
| Chris@629 | 950 | 
| Chris@6 | 951     paint.save(); | 
| Chris@6 | 952 | 
| Chris@6 | 953     QPainterPath path; | 
| Chris@55 | 954     int pointCount = 0; | 
| Chris@79 | 955 | 
| Chris@79 | 956     int textY = 0; | 
| Chris@79 | 957     if (m_plotStyle == PlotSegmentation) { | 
| Chris@79 | 958         textY = v->getTextLabelHeight(this, paint); | 
| Chris@554 | 959     } else { | 
| Chris@554 | 960         int originY = getYForValue(v, 0.f); | 
| Chris@554 | 961         if (originY > 0 && originY < v->height()) { | 
| Chris@554 | 962             paint.save(); | 
| Chris@554 | 963             paint.setPen(getPartialShades(v)[1]); | 
| Chris@554 | 964             paint.drawLine(x0, originY, x1, originY); | 
| Chris@554 | 965             paint.restore(); | 
| Chris@554 | 966         } | 
| Chris@79 | 967     } | 
| Chris@6 | 968 | 
| Chris@615 | 969     int prevFrame = 0; | 
| Chris@615 | 970 | 
| Chris@0 | 971     for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); | 
| Chris@0 | 972 	 i != points.end(); ++i) { | 
| Chris@0 | 973 | 
| Chris@553 | 974         if (m_derivative && i == points.begin()) continue; | 
| Chris@553 | 975 | 
| Chris@0 | 976 	const SparseTimeValueModel::Point &p(*i); | 
| Chris@0 | 977 | 
| Chris@553 | 978         float value = p.value; | 
| Chris@553 | 979         if (m_derivative) { | 
| Chris@553 | 980             SparseTimeValueModel::PointList::const_iterator j = i; | 
| Chris@553 | 981             --j; | 
| Chris@553 | 982             value -= j->value; | 
| Chris@553 | 983         } | 
| Chris@553 | 984 | 
| Chris@44 | 985 	int x = v->getXForFrame(p.frame); | 
| Chris@553 | 986 	int y = getYForValue(v, value); | 
| Chris@0 | 987 | 
| Chris@615 | 988         bool gap = false; | 
| Chris@615 | 989         if (m_plotStyle == PlotDiscreteCurves) { | 
| Chris@615 | 990             gap = (p.frame > prevFrame && | 
| Chris@615 | 991                    (p.frame - prevFrame >= m_model->getResolution() * 2)); | 
| Chris@615 | 992         } | 
| Chris@615 | 993 | 
| Chris@79 | 994         if (m_plotStyle != PlotSegmentation) { | 
| Chris@79 | 995             textY = y - paint.fontMetrics().height() | 
| Chris@631 | 996                       + paint.fontMetrics().ascent() - 1; | 
| Chris@372 | 997             if (textY < paint.fontMetrics().ascent() + 1) { | 
| Chris@372 | 998                 textY = paint.fontMetrics().ascent() + 1; | 
| Chris@372 | 999             } | 
| Chris@79 | 1000         } | 
| Chris@79 | 1001 | 
| Chris@34 | 1002 	bool haveNext = false; | 
| Chris@615 | 1003         int nf = v->getModelsEndFrame(); | 
| Chris@615 | 1004 	int nx = v->getXForFrame(nf); | 
| Chris@34 | 1005 	int ny = y; | 
| Chris@34 | 1006 | 
| Chris@34 | 1007 	SparseTimeValueModel::PointList::const_iterator j = i; | 
| Chris@34 | 1008 	++j; | 
| Chris@34 | 1009 | 
| Chris@34 | 1010 	if (j != points.end()) { | 
| Chris@34 | 1011 	    const SparseTimeValueModel::Point &q(*j); | 
| Chris@553 | 1012             float nvalue = q.value; | 
| Chris@553 | 1013             if (m_derivative) nvalue -= p.value; | 
| Chris@615 | 1014             nf = q.frame; | 
| Chris@615 | 1015 	    nx = v->getXForFrame(nf); | 
| Chris@553 | 1016 	    ny = getYForValue(v, nvalue); | 
| Chris@34 | 1017 	    haveNext = true; | 
| Chris@76 | 1018         } | 
| Chris@76 | 1019 | 
| Chris@682 | 1020 //        cout << "frame = " << p.frame << ", x = " << x << ", haveNext = " << haveNext | 
| Chris@682 | 1021 //                  << ", nx = " << nx << endl; | 
| Chris@34 | 1022 | 
| Chris@615 | 1023         if (m_plotStyle == PlotDiscreteCurves) { | 
| Chris@615 | 1024             paint.setPen(QPen(getBaseQColor(), 3)); | 
| Chris@615 | 1025             paint.setBrush(Qt::NoBrush); | 
| Chris@615 | 1026         } else if (m_plotStyle == PlotSegmentation) { | 
| Chris@287 | 1027             paint.setPen(getForegroundQColor(v)); | 
| Chris@553 | 1028             paint.setBrush(getColourForValue(v, value)); | 
| Chris@26 | 1029 	} else if (m_plotStyle == PlotLines || | 
| Chris@26 | 1030 		   m_plotStyle == PlotCurve) { | 
| Chris@615 | 1031             paint.setPen(getBaseQColor()); | 
| Chris@6 | 1032 	    paint.setBrush(Qt::NoBrush); | 
| Chris@3 | 1033 	} else { | 
| Chris@615 | 1034             paint.setPen(getBaseQColor()); | 
| Chris@6 | 1035 	    paint.setBrush(brushColour); | 
| Chris@3 | 1036 	} | 
| Chris@0 | 1037 | 
| Chris@0 | 1038 	if (m_plotStyle == PlotStems) { | 
| Chris@430 | 1039 /* | 
| Chris@0 | 1040 	    paint.setPen(brushColour); | 
| Chris@0 | 1041 	    if (y < origin - 1) { | 
| Chris@0 | 1042 		paint.drawRect(x + w/2, y + 1, 1, origin - y); | 
| Chris@0 | 1043 	    } else if (y > origin + 1) { | 
| Chris@0 | 1044 		paint.drawRect(x + w/2, origin, 1, y - origin - 1); | 
| Chris@0 | 1045 	    } | 
| Chris@430 | 1046 */ | 
| Chris@287 | 1047 	    paint.setPen(getBaseQColor()); | 
| Chris@430 | 1048 	    if (y < origin - 1) { | 
| Chris@430 | 1049 		paint.drawLine(x + w/2, y + 1, x + w/2, origin); | 
| Chris@430 | 1050 	    } else if (y > origin + 1) { | 
| Chris@430 | 1051 		paint.drawLine(x + w/2, origin, x + w/2, y - 1); | 
| Chris@430 | 1052 	    } | 
| Chris@0 | 1053 	} | 
| Chris@0 | 1054 | 
| Chris@631 | 1055         bool illuminate = false; | 
| Chris@631 | 1056 | 
| Chris@0 | 1057 	if (illuminateFrame == p.frame) { | 
| Chris@6 | 1058 | 
| Chris@631 | 1059 	    // not equipped to illuminate the right section in line | 
| Chris@631 | 1060 	    // or curve mode | 
| Chris@6 | 1061 | 
| Chris@6 | 1062 	    if (m_plotStyle != PlotCurve && | 
| Chris@615 | 1063                 m_plotStyle != PlotDiscreteCurves && | 
| Chris@6 | 1064 		m_plotStyle != PlotLines) { | 
| Chris@631 | 1065                 illuminate = true; | 
| Chris@631 | 1066             } | 
| Chris@631 | 1067         } | 
| Chris@0 | 1068 | 
| Chris@6 | 1069 	if (m_plotStyle != PlotLines && | 
| Chris@26 | 1070 	    m_plotStyle != PlotCurve && | 
| Chris@615 | 1071             m_plotStyle != PlotDiscreteCurves && | 
| Chris@26 | 1072 	    m_plotStyle != PlotSegmentation) { | 
| Chris@631 | 1073             if (illuminate) { | 
| Chris@631 | 1074                 paint.save(); | 
| Chris@631 | 1075 		paint.setPen(getForegroundQColor(v)); | 
| Chris@631 | 1076                 paint.setBrush(getForegroundQColor(v)); | 
| Chris@631 | 1077             } | 
| Chris@326 | 1078             if (m_plotStyle != PlotStems || | 
| Chris@326 | 1079                 w > 1) { | 
| Chris@326 | 1080                 paint.drawRect(x, y - 1, w, 2); | 
| Chris@326 | 1081             } | 
| Chris@631 | 1082             if (illuminate) { | 
| Chris@631 | 1083                 paint.restore(); | 
| Chris@631 | 1084             } | 
| Chris@3 | 1085 	} | 
| Chris@0 | 1086 | 
| Chris@6 | 1087 	if (m_plotStyle == PlotConnectedPoints || | 
| Chris@6 | 1088 	    m_plotStyle == PlotLines || | 
| Chris@615 | 1089             m_plotStyle == PlotDiscreteCurves || | 
| Chris@6 | 1090 	    m_plotStyle == PlotCurve) { | 
| Chris@0 | 1091 | 
| Chris@34 | 1092 	    if (haveNext) { | 
| Chris@3 | 1093 | 
| Chris@6 | 1094 		if (m_plotStyle == PlotConnectedPoints) { | 
| Chris@34 | 1095 | 
| Chris@79 | 1096                     paint.save(); | 
| Chris@3 | 1097 		    paint.setPen(brushColour); | 
| Chris@3 | 1098 		    paint.drawLine(x + w, y, nx, ny); | 
| Chris@79 | 1099                     paint.restore(); | 
| Chris@6 | 1100 | 
| Chris@6 | 1101 		} else if (m_plotStyle == PlotLines) { | 
| Chris@430 | 1102 | 
| Chris@430 | 1103                     if (pointCount == 0) { | 
| Chris@430 | 1104                         path.moveTo(x + w/2, y); | 
| Chris@430 | 1105                     } | 
| Chris@6 | 1106 | 
| Chris@430 | 1107 //		    paint.drawLine(x + w/2, y, nx + w/2, ny); | 
| Chris@430 | 1108                     path.lineTo(nx + w/2, ny); | 
| Chris@6 | 1109 | 
| Chris@3 | 1110 		} else { | 
| Chris@6 | 1111 | 
| Chris@55 | 1112 		    float x0 = x + float(w)/2; | 
| Chris@55 | 1113 		    float x1 = nx + float(w)/2; | 
| Chris@55 | 1114 | 
| Chris@55 | 1115 		    float y0 = y; | 
| Chris@55 | 1116 		    float y1 = ny; | 
| Chris@55 | 1117 | 
| Chris@615 | 1118                     if (m_plotStyle == PlotDiscreteCurves) { | 
| Chris@615 | 1119                         bool nextGap = nf - p.frame >= m_model->getResolution() * 2; | 
| Chris@615 | 1120                         if (nextGap) { | 
| Chris@615 | 1121                             x1 = x0; | 
| Chris@615 | 1122                             y1 = y0; | 
| Chris@615 | 1123                         } | 
| Chris@615 | 1124                     } | 
| Chris@615 | 1125 | 
| Chris@615 | 1126 		    if (pointCount == 0 || gap) { | 
| Chris@55 | 1127 			path.moveTo((x0 + x1) / 2, (y0 + y1) / 2); | 
| Chris@6 | 1128 		    } | 
| Chris@6 | 1129 | 
| Chris@6 | 1130 		    if (nx - x > 5) { | 
| Chris@55 | 1131 			path.cubicTo(x0, y0, | 
| Chris@55 | 1132 				     x0, y0, | 
| Chris@55 | 1133 				     (x0 + x1) / 2, (y0 + y1) / 2); | 
| Chris@55 | 1134 | 
| Chris@55 | 1135 			// // or | 
| Chris@55 | 1136 			// path.quadTo(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2); | 
| Chris@55 | 1137 | 
| Chris@6 | 1138 		    } else { | 
| Chris@55 | 1139 			path.lineTo((x0 + x1) / 2, (y0 + y1) / 2); | 
| Chris@6 | 1140 		    } | 
| Chris@3 | 1141 		} | 
| Chris@0 | 1142 	    } | 
| Chris@0 | 1143 	} | 
| Chris@0 | 1144 | 
| Chris@26 | 1145 	if (m_plotStyle == PlotSegmentation) { | 
| Chris@76 | 1146 | 
| Chris@526 | 1147 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1148             cerr << "drawing rect" << endl; | 
| Chris@526 | 1149 #endif | 
| Chris@26 | 1150 | 
| Chris@27 | 1151 	    if (nx <= x) continue; | 
| Chris@26 | 1152 | 
| Chris@559 | 1153             paint.setPen(QPen(getForegroundQColor(v), 2)); | 
| Chris@559 | 1154 | 
| Chris@631 | 1155             if (!illuminate) { | 
| Chris@513 | 1156                 if (!m_drawSegmentDivisions || | 
| Chris@513 | 1157                     nx < x + 5 || | 
| Chris@513 | 1158                     x >= v->width() - 1) { | 
| Chris@513 | 1159                     paint.setPen(Qt::NoPen); | 
| Chris@513 | 1160                 } | 
| Chris@27 | 1161 	    } | 
| Chris@26 | 1162 | 
| Chris@44 | 1163 	    paint.drawRect(x, -1, nx - x, v->height() + 1); | 
| Chris@26 | 1164 	} | 
| Chris@26 | 1165 | 
| Chris@629 | 1166         QString label = p.label; | 
| Chris@630 | 1167         bool italic = false; | 
| Chris@629 | 1168 | 
| Chris@629 | 1169         if (label == "" && | 
| Chris@629 | 1170             (m_plotStyle == PlotPoints || | 
| Chris@631 | 1171              m_plotStyle == PlotSegmentation || | 
| Chris@631 | 1172              m_plotStyle == PlotConnectedPoints)) { | 
| Chris@632 | 1173             char lc[20]; | 
| Chris@632 | 1174             snprintf(lc, 20, "%.3g", p.value); | 
| Chris@632 | 1175             label = lc; | 
| Chris@630 | 1176             italic = true; | 
| Chris@629 | 1177         } | 
| Chris@629 | 1178 | 
| Chris@629 | 1179 	if (label != "") { | 
| Chris@632 | 1180             bool haveRoom = nx > x + 6 + paint.fontMetrics().width(label); | 
| Chris@632 | 1181             if (haveRoom || | 
| Chris@632 | 1182                 (!haveNext && | 
| Chris@632 | 1183                  (pointCount == 0 || !italic))) { | 
| Chris@630 | 1184                 v->drawVisibleText(paint, x + 5, textY, label, | 
| Chris@630 | 1185                                    italic ? | 
| Chris@630 | 1186                                    View::OutlinedItalicText : | 
| Chris@630 | 1187                                    View::OutlinedText); | 
| Chris@79 | 1188             } | 
| Chris@55 | 1189 	} | 
| Chris@615 | 1190 | 
| Chris@615 | 1191         prevFrame = p.frame; | 
| Chris@632 | 1192         ++pointCount; | 
| Chris@0 | 1193     } | 
| Chris@6 | 1194 | 
| Chris@691 | 1195     if (m_plotStyle == PlotDiscreteCurves) { | 
| Chris@691 | 1196         paint.setRenderHint(QPainter::Antialiasing, true); | 
| Chris@691 | 1197 	paint.drawPath(path); | 
| Chris@691 | 1198     } else if ((m_plotStyle == PlotCurve || m_plotStyle == PlotLines) | 
| Chris@691 | 1199                && !path.isEmpty()) { | 
| Chris@55 | 1200 	paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->width()); | 
| Chris@6 | 1201 	paint.drawPath(path); | 
| Chris@6 | 1202     } | 
| Chris@6 | 1203 | 
| Chris@6 | 1204     paint.restore(); | 
| Chris@6 | 1205 | 
| Chris@6 | 1206     // looks like save/restore doesn't deal with this: | 
| Chris@6 | 1207     paint.setRenderHint(QPainter::Antialiasing, false); | 
| Chris@6 | 1208 } | 
| Chris@6 | 1209 | 
| Chris@42 | 1210 int | 
| Chris@698 | 1211 TimeValueLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const | 
| Chris@42 | 1212 { | 
| Chris@701 | 1213     if (!m_model || shouldAutoAlign()) { | 
| Chris@701 | 1214         return 0; | 
| Chris@701 | 1215     } else if (m_plotStyle == PlotSegmentation) { | 
| Chris@699 | 1216         if (m_verticalScale == LogScale) { | 
| Chris@699 | 1217             return LogColourScale().getWidth(v, paint); | 
| Chris@699 | 1218         } else { | 
| Chris@699 | 1219             return LinearColourScale().getWidth(v, paint); | 
| Chris@699 | 1220         } | 
| Chris@698 | 1221     } else { | 
| Chris@699 | 1222         if (m_verticalScale == LogScale) { | 
| Chris@699 | 1223             return LogNumericalScale().getWidth(v, paint) + 10; // for piano | 
| Chris@699 | 1224         } else { | 
| Chris@699 | 1225             return LinearNumericalScale().getWidth(v, paint); | 
| Chris@699 | 1226         } | 
| Chris@698 | 1227     } | 
| Chris@42 | 1228 } | 
| Chris@42 | 1229 | 
| Chris@42 | 1230 void | 
| Chris@607 | 1231 TimeValueLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const | 
| Chris@42 | 1232 { | 
| Chris@42 | 1233     if (!m_model) return; | 
| Chris@42 | 1234 | 
| Chris@699 | 1235     QString unit; | 
| Chris@698 | 1236     float min, max; | 
| Chris@698 | 1237     bool logarithmic; | 
| Chris@698 | 1238 | 
| Chris@698 | 1239     int w = getVerticalScaleWidth(v, false, paint); | 
| Chris@698 | 1240     int h = v->height(); | 
| Chris@698 | 1241 | 
| Chris@698 | 1242     if (m_plotStyle == PlotSegmentation) { | 
| Chris@698 | 1243 | 
| Chris@699 | 1244         getValueExtents(min, max, logarithmic, unit); | 
| Chris@699 | 1245 | 
| Chris@699 | 1246         if (logarithmic) { | 
| Chris@699 | 1247             LogRange::mapRange(min, max); | 
| Chris@699 | 1248             LogColourScale().paintVertical(v, this, paint, 0, min, max); | 
| Chris@699 | 1249         } else { | 
| Chris@699 | 1250             LinearColourScale().paintVertical(v, this, paint, 0, min, max); | 
| Chris@699 | 1251         } | 
| Chris@698 | 1252 | 
| Chris@698 | 1253     } else { | 
| Chris@698 | 1254 | 
| Chris@699 | 1255         getScaleExtents(v, min, max, logarithmic); | 
| Chris@699 | 1256 | 
| Chris@698 | 1257         if (logarithmic) { | 
| Chris@698 | 1258             LogNumericalScale().paintVertical(v, this, paint, 0, min, max); | 
| Chris@698 | 1259         } else { | 
| Chris@698 | 1260             LinearNumericalScale().paintVertical(v, this, paint, 0, min, max); | 
| Chris@698 | 1261         } | 
| Chris@698 | 1262 | 
| Chris@698 | 1263         if (logarithmic && (getScaleUnits() == "Hz")) { | 
| Chris@698 | 1264             PianoScale().paintPianoVertical | 
| Chris@698 | 1265                 (v, paint, QRect(w - 10, 0, 10, h), | 
| Chris@698 | 1266                  LogRange::unmap(min), | 
| Chris@698 | 1267                  LogRange::unmap(max)); | 
| Chris@698 | 1268             paint.drawLine(w, 0, w, h); | 
| Chris@698 | 1269         } | 
| Chris@698 | 1270     } | 
| Chris@698 | 1271 | 
| Chris@698 | 1272     if (getScaleUnits() != "") { | 
| Chris@701 | 1273         int mw = w - 5; | 
| Chris@701 | 1274         paint.drawText(5, | 
| Chris@701 | 1275                        5 + paint.fontMetrics().ascent(), | 
| Chris@701 | 1276                        TextAbbrev::abbreviate(getScaleUnits(), | 
| Chris@701 | 1277                                               paint.fontMetrics(), | 
| Chris@701 | 1278                                               mw)); | 
| Chris@691 | 1279     } | 
| Chris@42 | 1280 } | 
| Chris@42 | 1281 | 
| Chris@21 | 1282 void | 
| Chris@44 | 1283 TimeValueLayer::drawStart(View *v, QMouseEvent *e) | 
| Chris@21 | 1284 { | 
| Chris@526 | 1285 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1286     cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl; | 
| Chris@526 | 1287 #endif | 
| Chris@21 | 1288 | 
| Chris@21 | 1289     if (!m_model) return; | 
| Chris@21 | 1290 | 
| Chris@44 | 1291     long frame = v->getFrameForX(e->x()); | 
| Chris@76 | 1292     long resolution = m_model->getResolution(); | 
| Chris@21 | 1293     if (frame < 0) frame = 0; | 
| Chris@76 | 1294     frame = (frame / resolution) * resolution; | 
| Chris@21 | 1295 | 
| Chris@44 | 1296     float value = getValueForY(v, e->y()); | 
| Chris@21 | 1297 | 
| Chris@76 | 1298     bool havePoint = false; | 
| Chris@76 | 1299 | 
| Chris@76 | 1300     SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); | 
| Chris@76 | 1301     if (!points.empty()) { | 
| Chris@76 | 1302         for (SparseTimeValueModel::PointList::iterator i = points.begin(); | 
| Chris@76 | 1303              i != points.end(); ++i) { | 
| Chris@76 | 1304             if (((i->frame / resolution) * resolution) != frame) { | 
| Chris@526 | 1305 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1306                 cerr << "ignoring out-of-range frame at " << i->frame << endl; | 
| Chris@526 | 1307 #endif | 
| Chris@76 | 1308                 continue; | 
| Chris@76 | 1309             } | 
| Chris@76 | 1310             m_editingPoint = *i; | 
| Chris@76 | 1311             havePoint = true; | 
| Chris@76 | 1312         } | 
| Chris@76 | 1313     } | 
| Chris@76 | 1314 | 
| Chris@76 | 1315     if (!havePoint) { | 
| Chris@76 | 1316         m_editingPoint = SparseTimeValueModel::Point | 
| Chris@76 | 1317             (frame, value, tr("New Point")); | 
| Chris@76 | 1318     } | 
| Chris@76 | 1319 | 
| Chris@23 | 1320     m_originalPoint = m_editingPoint; | 
| Chris@22 | 1321 | 
| Chris@376 | 1322     if (m_editingCommand) finish(m_editingCommand); | 
| Chris@22 | 1323     m_editingCommand = new SparseTimeValueModel::EditCommand(m_model, | 
| Chris@22 | 1324 							     tr("Draw Point")); | 
| Chris@76 | 1325     if (!havePoint) { | 
| Chris@76 | 1326         m_editingCommand->addPoint(m_editingPoint); | 
| Chris@76 | 1327     } | 
| Chris@22 | 1328 | 
| Chris@21 | 1329     m_editing = true; | 
| Chris@21 | 1330 } | 
| Chris@21 | 1331 | 
| Chris@21 | 1332 void | 
| Chris@44 | 1333 TimeValueLayer::drawDrag(View *v, QMouseEvent *e) | 
| Chris@21 | 1334 { | 
| Chris@526 | 1335 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1336     cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl; | 
| Chris@526 | 1337 #endif | 
| Chris@21 | 1338 | 
| Chris@21 | 1339     if (!m_model || !m_editing) return; | 
| Chris@21 | 1340 | 
| Chris@44 | 1341     long frame = v->getFrameForX(e->x()); | 
| Chris@76 | 1342     long resolution = m_model->getResolution(); | 
| Chris@21 | 1343     if (frame < 0) frame = 0; | 
| Chris@76 | 1344     frame = (frame / resolution) * resolution; | 
| Chris@21 | 1345 | 
| Chris@44 | 1346     float value = getValueForY(v, e->y()); | 
| Chris@21 | 1347 | 
| Chris@76 | 1348     SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); | 
| Chris@76 | 1349 | 
| Chris@526 | 1350 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1351     cerr << points.size() << " points" << endl; | 
| Chris@526 | 1352 #endif | 
| Chris@76 | 1353 | 
| Chris@76 | 1354     bool havePoint = false; | 
| Chris@76 | 1355 | 
| Chris@76 | 1356     if (!points.empty()) { | 
| Chris@76 | 1357         for (SparseTimeValueModel::PointList::iterator i = points.begin(); | 
| Chris@76 | 1358              i != points.end(); ++i) { | 
| Chris@76 | 1359             if (i->frame == m_editingPoint.frame && | 
| Chris@76 | 1360                 i->value == m_editingPoint.value) { | 
| Chris@526 | 1361 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1362                 cerr << "ignoring current editing point at " << i->frame << ", " << i->value << endl; | 
| Chris@526 | 1363 #endif | 
| Chris@76 | 1364                 continue; | 
| Chris@76 | 1365             } | 
| Chris@76 | 1366             if (((i->frame / resolution) * resolution) != frame) { | 
| Chris@526 | 1367 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1368                 cerr << "ignoring out-of-range frame at " << i->frame << endl; | 
| Chris@526 | 1369 #endif | 
| Chris@76 | 1370                 continue; | 
| Chris@76 | 1371             } | 
| Chris@526 | 1372 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1373             cerr << "adjusting to new point at " << i->frame << ", " << i->value << endl; | 
| Chris@526 | 1374 #endif | 
| Chris@76 | 1375             m_editingPoint = *i; | 
| Chris@76 | 1376             m_originalPoint = m_editingPoint; | 
| Chris@76 | 1377             m_editingCommand->deletePoint(m_editingPoint); | 
| Chris@76 | 1378             havePoint = true; | 
| Chris@76 | 1379         } | 
| Chris@76 | 1380     } | 
| Chris@76 | 1381 | 
| Chris@76 | 1382     if (!havePoint) { | 
| Chris@76 | 1383         if (frame == m_editingPoint.frame) { | 
| Chris@76 | 1384             m_editingCommand->deletePoint(m_editingPoint); | 
| Chris@76 | 1385         } | 
| Chris@76 | 1386     } | 
| Chris@76 | 1387 | 
| Chris@76 | 1388 //    m_editingCommand->deletePoint(m_editingPoint); | 
| Chris@21 | 1389     m_editingPoint.frame = frame; | 
| Chris@21 | 1390     m_editingPoint.value = value; | 
| Chris@22 | 1391     m_editingCommand->addPoint(m_editingPoint); | 
| Chris@21 | 1392 } | 
| Chris@21 | 1393 | 
| Chris@21 | 1394 void | 
| Chris@248 | 1395 TimeValueLayer::drawEnd(View *, QMouseEvent *) | 
| Chris@21 | 1396 { | 
| Chris@526 | 1397 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1398     cerr << "TimeValueLayer::drawEnd" << endl; | 
| Chris@526 | 1399 #endif | 
| Chris@21 | 1400     if (!m_model || !m_editing) return; | 
| Chris@376 | 1401     finish(m_editingCommand); | 
| Chris@22 | 1402     m_editingCommand = 0; | 
| Chris@21 | 1403     m_editing = false; | 
| Chris@21 | 1404 } | 
| Chris@21 | 1405 | 
| Chris@21 | 1406 void | 
| Chris@335 | 1407 TimeValueLayer::eraseStart(View *v, QMouseEvent *e) | 
| Chris@335 | 1408 { | 
| Chris@335 | 1409     if (!m_model) return; | 
| Chris@335 | 1410 | 
| Chris@335 | 1411     SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); | 
| Chris@335 | 1412     if (points.empty()) return; | 
| Chris@335 | 1413 | 
| Chris@335 | 1414     m_editingPoint = *points.begin(); | 
| Chris@335 | 1415 | 
| Chris@335 | 1416     if (m_editingCommand) { | 
| Chris@376 | 1417 	finish(m_editingCommand); | 
| Chris@335 | 1418 	m_editingCommand = 0; | 
| Chris@335 | 1419     } | 
| Chris@335 | 1420 | 
| Chris@335 | 1421     m_editing = true; | 
| Chris@335 | 1422 } | 
| Chris@335 | 1423 | 
| Chris@335 | 1424 void | 
| Chris@335 | 1425 TimeValueLayer::eraseDrag(View *v, QMouseEvent *e) | 
| Chris@335 | 1426 { | 
| Chris@335 | 1427 } | 
| Chris@335 | 1428 | 
| Chris@335 | 1429 void | 
| Chris@335 | 1430 TimeValueLayer::eraseEnd(View *v, QMouseEvent *e) | 
| Chris@335 | 1431 { | 
| Chris@335 | 1432     if (!m_model || !m_editing) return; | 
| Chris@335 | 1433 | 
| Chris@335 | 1434     m_editing = false; | 
| Chris@335 | 1435 | 
| Chris@335 | 1436     SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); | 
| Chris@335 | 1437     if (points.empty()) return; | 
| Chris@335 | 1438     if (points.begin()->frame != m_editingPoint.frame || | 
| Chris@335 | 1439         points.begin()->value != m_editingPoint.value) return; | 
| Chris@335 | 1440 | 
| Chris@335 | 1441     m_editingCommand = new SparseTimeValueModel::EditCommand | 
| Chris@335 | 1442         (m_model, tr("Erase Point")); | 
| Chris@335 | 1443 | 
| Chris@335 | 1444     m_editingCommand->deletePoint(m_editingPoint); | 
| Chris@335 | 1445 | 
| Chris@376 | 1446     finish(m_editingCommand); | 
| Chris@335 | 1447     m_editingCommand = 0; | 
| Chris@335 | 1448     m_editing = false; | 
| Chris@335 | 1449 } | 
| Chris@335 | 1450 | 
| Chris@335 | 1451 void | 
| Chris@44 | 1452 TimeValueLayer::editStart(View *v, QMouseEvent *e) | 
| Chris@21 | 1453 { | 
| Chris@526 | 1454 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1455     cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << endl; | 
| Chris@526 | 1456 #endif | 
| Chris@21 | 1457 | 
| Chris@21 | 1458     if (!m_model) return; | 
| Chris@21 | 1459 | 
| Chris@44 | 1460     SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); | 
| Chris@21 | 1461     if (points.empty()) return; | 
| Chris@21 | 1462 | 
| Chris@21 | 1463     m_editingPoint = *points.begin(); | 
| Chris@23 | 1464     m_originalPoint = m_editingPoint; | 
| Chris@22 | 1465 | 
| Chris@22 | 1466     if (m_editingCommand) { | 
| Chris@376 | 1467 	finish(m_editingCommand); | 
| Chris@22 | 1468 	m_editingCommand = 0; | 
| Chris@22 | 1469     } | 
| Chris@22 | 1470 | 
| Chris@21 | 1471     m_editing = true; | 
| Chris@21 | 1472 } | 
| Chris@21 | 1473 | 
| Chris@21 | 1474 void | 
| Chris@44 | 1475 TimeValueLayer::editDrag(View *v, QMouseEvent *e) | 
| Chris@21 | 1476 { | 
| Chris@526 | 1477 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1478     cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl; | 
| Chris@526 | 1479 #endif | 
| Chris@21 | 1480 | 
| Chris@21 | 1481     if (!m_model || !m_editing) return; | 
| Chris@21 | 1482 | 
| Chris@44 | 1483     long frame = v->getFrameForX(e->x()); | 
| Chris@21 | 1484     if (frame < 0) frame = 0; | 
| Chris@21 | 1485     frame = frame / m_model->getResolution() * m_model->getResolution(); | 
| Chris@21 | 1486 | 
| Chris@44 | 1487     float value = getValueForY(v, e->y()); | 
| Chris@21 | 1488 | 
| Chris@22 | 1489     if (!m_editingCommand) { | 
| Chris@22 | 1490 	m_editingCommand = new SparseTimeValueModel::EditCommand(m_model, | 
| Chris@22 | 1491 								 tr("Drag Point")); | 
| Chris@22 | 1492     } | 
| Chris@22 | 1493 | 
| Chris@22 | 1494     m_editingCommand->deletePoint(m_editingPoint); | 
| Chris@21 | 1495     m_editingPoint.frame = frame; | 
| Chris@21 | 1496     m_editingPoint.value = value; | 
| Chris@22 | 1497     m_editingCommand->addPoint(m_editingPoint); | 
| Chris@21 | 1498 } | 
| Chris@21 | 1499 | 
| Chris@21 | 1500 void | 
| Chris@248 | 1501 TimeValueLayer::editEnd(View *, QMouseEvent *) | 
| Chris@21 | 1502 { | 
| Chris@526 | 1503 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1504     cerr << "TimeValueLayer::editEnd" << endl; | 
| Chris@526 | 1505 #endif | 
| Chris@21 | 1506     if (!m_model || !m_editing) return; | 
| Chris@23 | 1507 | 
| Chris@23 | 1508     if (m_editingCommand) { | 
| Chris@23 | 1509 | 
| Chris@23 | 1510 	QString newName = m_editingCommand->getName(); | 
| Chris@23 | 1511 | 
| Chris@23 | 1512 	if (m_editingPoint.frame != m_originalPoint.frame) { | 
| Chris@23 | 1513 	    if (m_editingPoint.value != m_originalPoint.value) { | 
| Chris@23 | 1514 		newName = tr("Edit Point"); | 
| Chris@23 | 1515 	    } else { | 
| Chris@23 | 1516 		newName = tr("Relocate Point"); | 
| Chris@23 | 1517 	    } | 
| Chris@23 | 1518 	} else { | 
| Chris@23 | 1519 	    newName = tr("Change Point Value"); | 
| Chris@23 | 1520 	} | 
| Chris@23 | 1521 | 
| Chris@23 | 1522 	m_editingCommand->setName(newName); | 
| Chris@376 | 1523 	finish(m_editingCommand); | 
| Chris@23 | 1524     } | 
| Chris@23 | 1525 | 
| Chris@22 | 1526     m_editingCommand = 0; | 
| Chris@21 | 1527     m_editing = false; | 
| Chris@21 | 1528 } | 
| Chris@21 | 1529 | 
| Chris@255 | 1530 bool | 
| Chris@70 | 1531 TimeValueLayer::editOpen(View *v, QMouseEvent *e) | 
| Chris@70 | 1532 { | 
| Chris@255 | 1533     if (!m_model) return false; | 
| Chris@70 | 1534 | 
| Chris@70 | 1535     SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); | 
| Chris@255 | 1536     if (points.empty()) return false; | 
| Chris@70 | 1537 | 
| Chris@70 | 1538     SparseTimeValueModel::Point point = *points.begin(); | 
| Chris@70 | 1539 | 
| Chris@70 | 1540     ItemEditDialog *dialog = new ItemEditDialog | 
| Chris@70 | 1541         (m_model->getSampleRate(), | 
| Chris@70 | 1542          ItemEditDialog::ShowTime | | 
| Chris@70 | 1543          ItemEditDialog::ShowValue | | 
| Chris@73 | 1544          ItemEditDialog::ShowText, | 
| Chris@698 | 1545          getScaleUnits()); | 
| Chris@70 | 1546 | 
| Chris@70 | 1547     dialog->setFrameTime(point.frame); | 
| Chris@70 | 1548     dialog->setValue(point.value); | 
| Chris@70 | 1549     dialog->setText(point.label); | 
| Chris@70 | 1550 | 
| Chris@70 | 1551     if (dialog->exec() == QDialog::Accepted) { | 
| Chris@70 | 1552 | 
| Chris@70 | 1553         SparseTimeValueModel::Point newPoint = point; | 
| Chris@70 | 1554         newPoint.frame = dialog->getFrameTime(); | 
| Chris@70 | 1555         newPoint.value = dialog->getValue(); | 
| Chris@70 | 1556         newPoint.label = dialog->getText(); | 
| Chris@70 | 1557 | 
| Chris@70 | 1558         SparseTimeValueModel::EditCommand *command = | 
| Chris@70 | 1559             new SparseTimeValueModel::EditCommand(m_model, tr("Edit Point")); | 
| Chris@70 | 1560         command->deletePoint(point); | 
| Chris@70 | 1561         command->addPoint(newPoint); | 
| Chris@376 | 1562         finish(command); | 
| Chris@70 | 1563     } | 
| Chris@70 | 1564 | 
| Chris@70 | 1565     delete dialog; | 
| Chris@255 | 1566     return true; | 
| Chris@70 | 1567 } | 
| Chris@70 | 1568 | 
| Chris@70 | 1569 void | 
| Chris@43 | 1570 TimeValueLayer::moveSelection(Selection s, size_t newStartFrame) | 
| Chris@43 | 1571 { | 
| Chris@99 | 1572     if (!m_model) return; | 
| Chris@99 | 1573 | 
| Chris@43 | 1574     SparseTimeValueModel::EditCommand *command = | 
| Chris@43 | 1575 	new SparseTimeValueModel::EditCommand(m_model, | 
| Chris@43 | 1576 					      tr("Drag Selection")); | 
| Chris@43 | 1577 | 
| Chris@43 | 1578     SparseTimeValueModel::PointList points = | 
| Chris@43 | 1579 	m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | 
| Chris@43 | 1580 | 
| Chris@43 | 1581     for (SparseTimeValueModel::PointList::iterator i = points.begin(); | 
| Chris@43 | 1582 	 i != points.end(); ++i) { | 
| Chris@43 | 1583 | 
| Chris@43 | 1584 	if (s.contains(i->frame)) { | 
| Chris@43 | 1585 	    SparseTimeValueModel::Point newPoint(*i); | 
| Chris@43 | 1586 	    newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); | 
| Chris@43 | 1587 	    command->deletePoint(*i); | 
| Chris@43 | 1588 	    command->addPoint(newPoint); | 
| Chris@43 | 1589 	} | 
| Chris@43 | 1590     } | 
| Chris@43 | 1591 | 
| Chris@376 | 1592     finish(command); | 
| Chris@43 | 1593 } | 
| Chris@43 | 1594 | 
| Chris@43 | 1595 void | 
| Chris@43 | 1596 TimeValueLayer::resizeSelection(Selection s, Selection newSize) | 
| Chris@43 | 1597 { | 
| Chris@99 | 1598     if (!m_model) return; | 
| Chris@99 | 1599 | 
| Chris@43 | 1600     SparseTimeValueModel::EditCommand *command = | 
| Chris@43 | 1601 	new SparseTimeValueModel::EditCommand(m_model, | 
| Chris@43 | 1602 					      tr("Resize Selection")); | 
| Chris@43 | 1603 | 
| Chris@43 | 1604     SparseTimeValueModel::PointList points = | 
| Chris@43 | 1605 	m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | 
| Chris@43 | 1606 | 
| Chris@43 | 1607     double ratio = | 
| Chris@43 | 1608 	double(newSize.getEndFrame() - newSize.getStartFrame()) / | 
| Chris@43 | 1609 	double(s.getEndFrame() - s.getStartFrame()); | 
| Chris@43 | 1610 | 
| Chris@43 | 1611     for (SparseTimeValueModel::PointList::iterator i = points.begin(); | 
| Chris@43 | 1612 	 i != points.end(); ++i) { | 
| Chris@43 | 1613 | 
| Chris@43 | 1614 	if (s.contains(i->frame)) { | 
| Chris@43 | 1615 | 
| Chris@43 | 1616 	    double target = i->frame; | 
| Chris@43 | 1617 	    target = newSize.getStartFrame() + | 
| Chris@43 | 1618 		double(target - s.getStartFrame()) * ratio; | 
| Chris@43 | 1619 | 
| Chris@43 | 1620 	    SparseTimeValueModel::Point newPoint(*i); | 
| Chris@43 | 1621 	    newPoint.frame = lrint(target); | 
| Chris@43 | 1622 	    command->deletePoint(*i); | 
| Chris@43 | 1623 	    command->addPoint(newPoint); | 
| Chris@43 | 1624 	} | 
| Chris@43 | 1625     } | 
| Chris@43 | 1626 | 
| Chris@376 | 1627     finish(command); | 
| Chris@43 | 1628 } | 
| Chris@43 | 1629 | 
| Chris@76 | 1630 void | 
| Chris@76 | 1631 TimeValueLayer::deleteSelection(Selection s) | 
| Chris@76 | 1632 { | 
| Chris@99 | 1633     if (!m_model) return; | 
| Chris@99 | 1634 | 
| Chris@76 | 1635     SparseTimeValueModel::EditCommand *command = | 
| Chris@76 | 1636 	new SparseTimeValueModel::EditCommand(m_model, | 
| Chris@76 | 1637 					      tr("Delete Selected Points")); | 
| Chris@76 | 1638 | 
| Chris@76 | 1639     SparseTimeValueModel::PointList points = | 
| Chris@76 | 1640 	m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | 
| Chris@76 | 1641 | 
| Chris@76 | 1642     for (SparseTimeValueModel::PointList::iterator i = points.begin(); | 
| Chris@76 | 1643 	 i != points.end(); ++i) { | 
| Chris@76 | 1644 | 
| Chris@76 | 1645         if (s.contains(i->frame)) { | 
| Chris@76 | 1646             command->deletePoint(*i); | 
| Chris@76 | 1647         } | 
| Chris@76 | 1648     } | 
| Chris@76 | 1649 | 
| Chris@376 | 1650     finish(command); | 
| Chris@76 | 1651 } | 
| Chris@76 | 1652 | 
| Chris@76 | 1653 void | 
| Chris@359 | 1654 TimeValueLayer::copy(View *v, Selection s, Clipboard &to) | 
| Chris@76 | 1655 { | 
| Chris@99 | 1656     if (!m_model) return; | 
| Chris@99 | 1657 | 
| Chris@76 | 1658     SparseTimeValueModel::PointList points = | 
| Chris@76 | 1659 	m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | 
| Chris@76 | 1660 | 
| Chris@76 | 1661     for (SparseTimeValueModel::PointList::iterator i = points.begin(); | 
| Chris@76 | 1662 	 i != points.end(); ++i) { | 
| Chris@76 | 1663 	if (s.contains(i->frame)) { | 
| Chris@83 | 1664             Clipboard::Point point(i->frame, i->value, i->label); | 
| Chris@360 | 1665             point.setReferenceFrame(alignToReference(v, i->frame)); | 
| Chris@76 | 1666             to.addPoint(point); | 
| Chris@76 | 1667         } | 
| Chris@76 | 1668     } | 
| Chris@76 | 1669 } | 
| Chris@76 | 1670 | 
| Chris@125 | 1671 bool | 
| Chris@359 | 1672 TimeValueLayer::paste(View *v, const Clipboard &from, int frameOffset, | 
| Chris@125 | 1673                       bool interactive) | 
| Chris@76 | 1674 { | 
| Chris@125 | 1675     if (!m_model) return false; | 
| Chris@99 | 1676 | 
| Chris@76 | 1677     const Clipboard::PointList &points = from.getPoints(); | 
| Chris@76 | 1678 | 
| Chris@360 | 1679     bool realign = false; | 
| Chris@360 | 1680 | 
| Chris@360 | 1681     if (clipboardHasDifferentAlignment(v, from)) { | 
| Chris@360 | 1682 | 
| Chris@360 | 1683         QMessageBox::StandardButton button = | 
| Chris@360 | 1684             QMessageBox::question(v, tr("Re-align pasted items?"), | 
| Chris@360 | 1685                                   tr("The items you are pasting came from a layer with different source material from this one.  Do you want to re-align them in time, to match the source material for this layer?"), | 
| Chris@360 | 1686                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, | 
| Chris@360 | 1687                                   QMessageBox::Yes); | 
| Chris@360 | 1688 | 
| Chris@360 | 1689         if (button == QMessageBox::Cancel) { | 
| Chris@360 | 1690             return false; | 
| Chris@360 | 1691         } | 
| Chris@360 | 1692 | 
| Chris@360 | 1693         if (button == QMessageBox::Yes) { | 
| Chris@360 | 1694             realign = true; | 
| Chris@360 | 1695         } | 
| Chris@360 | 1696     } | 
| Chris@360 | 1697 | 
| Chris@76 | 1698     SparseTimeValueModel::EditCommand *command = | 
| Chris@76 | 1699 	new SparseTimeValueModel::EditCommand(m_model, tr("Paste")); | 
| Chris@76 | 1700 | 
| Chris@125 | 1701     enum ValueAvailability { | 
| Chris@125 | 1702         UnknownAvailability, | 
| Chris@125 | 1703         NoValues, | 
| Chris@125 | 1704         SomeValues, | 
| Chris@125 | 1705         AllValues | 
| Chris@125 | 1706     }; | 
| Chris@125 | 1707 | 
| Chris@340 | 1708     Labeller::ValueType generation = Labeller::ValueNone; | 
| Chris@125 | 1709 | 
| Chris@125 | 1710     bool haveUsableLabels = false; | 
| Chris@125 | 1711     bool haveExistingItems = !(m_model->isEmpty()); | 
| Chris@340 | 1712     Labeller labeller; | 
| Chris@340 | 1713     labeller.setSampleRate(m_model->getSampleRate()); | 
| Chris@125 | 1714 | 
| Chris@125 | 1715     if (interactive) { | 
| Chris@125 | 1716 | 
| Chris@125 | 1717         ValueAvailability availability = UnknownAvailability; | 
| Chris@125 | 1718 | 
| Chris@125 | 1719         for (Clipboard::PointList::const_iterator i = points.begin(); | 
| Chris@125 | 1720              i != points.end(); ++i) { | 
| Chris@125 | 1721 | 
| Chris@125 | 1722             if (!i->haveFrame()) continue; | 
| Chris@125 | 1723 | 
| Chris@125 | 1724             if (availability == UnknownAvailability) { | 
| Chris@125 | 1725                 if (i->haveValue()) availability = AllValues; | 
| Chris@125 | 1726                 else availability = NoValues; | 
| Chris@125 | 1727                 continue; | 
| Chris@125 | 1728             } | 
| Chris@125 | 1729 | 
| Chris@125 | 1730             if (i->haveValue()) { | 
| Chris@125 | 1731                 if (availability == NoValues) { | 
| Chris@125 | 1732                     availability = SomeValues; | 
| Chris@125 | 1733                 } | 
| Chris@125 | 1734             } else { | 
| Chris@125 | 1735                 if (availability == AllValues) { | 
| Chris@125 | 1736                     availability = SomeValues; | 
| Chris@125 | 1737                 } | 
| Chris@125 | 1738             } | 
| Chris@125 | 1739 | 
| Chris@125 | 1740             if (!haveUsableLabels) { | 
| Chris@125 | 1741                 if (i->haveLabel()) { | 
| Chris@125 | 1742                     if (i->getLabel().contains(QRegExp("[0-9]"))) { | 
| Chris@125 | 1743                         haveUsableLabels = true; | 
| Chris@125 | 1744                     } | 
| Chris@125 | 1745                 } | 
| Chris@125 | 1746             } | 
| Chris@125 | 1747 | 
| Chris@125 | 1748             if (availability == SomeValues && haveUsableLabels) break; | 
| Chris@125 | 1749         } | 
| Chris@125 | 1750 | 
| Chris@125 | 1751         if (availability == NoValues || availability == SomeValues) { | 
| Chris@125 | 1752 | 
| Chris@125 | 1753             QString text; | 
| Chris@125 | 1754             if (availability == NoValues) { | 
| Chris@125 | 1755                 text = tr("The items you are pasting do not have values.\nWhat values do you want to use for these items?"); | 
| Chris@125 | 1756             } else { | 
| Chris@125 | 1757                 text = tr("Some of the items you are pasting do not have values.\nWhat values do you want to use for these items?"); | 
| Chris@125 | 1758             } | 
| Chris@125 | 1759 | 
| Chris@340 | 1760             Labeller::TypeNameMap names = labeller.getTypeNames(); | 
| Chris@340 | 1761 | 
| Chris@125 | 1762             QStringList options; | 
| Chris@340 | 1763             std::vector<Labeller::ValueType> genopts; | 
| Chris@125 | 1764 | 
| Chris@340 | 1765             for (Labeller::TypeNameMap::const_iterator i = names.begin(); | 
| Chris@340 | 1766                  i != names.end(); ++i) { | 
| Chris@340 | 1767                 if (i->first == Labeller::ValueNone) options << tr("Zero for all items"); | 
| Chris@340 | 1768                 else options << i->second; | 
| Chris@340 | 1769                 genopts.push_back(i->first); | 
| Chris@125 | 1770             } | 
| Chris@125 | 1771 | 
| Chris@125 | 1772             static int prevSelection = 0; | 
| Chris@125 | 1773 | 
| Chris@125 | 1774             bool ok = false; | 
| Chris@125 | 1775             QString selected = ListInputDialog::getItem | 
| Chris@125 | 1776                 (0, tr("Choose value calculation"), | 
| Chris@125 | 1777                  text, options, prevSelection, &ok); | 
| Chris@125 | 1778 | 
| Chris@125 | 1779             if (!ok) return false; | 
| Chris@125 | 1780             int selection = 0; | 
| Chris@340 | 1781             generation = Labeller::ValueNone; | 
| Chris@125 | 1782 | 
| Chris@125 | 1783             for (QStringList::const_iterator i = options.begin(); | 
| Chris@125 | 1784                  i != options.end(); ++i) { | 
| Chris@125 | 1785                 if (selected == *i) { | 
| Chris@340 | 1786                     generation = genopts[selection]; | 
| Chris@125 | 1787                     break; | 
| Chris@125 | 1788                 } | 
| Chris@125 | 1789                 ++selection; | 
| Chris@125 | 1790             } | 
| Chris@340 | 1791 | 
| Chris@340 | 1792             labeller.setType(generation); | 
| Chris@340 | 1793 | 
| Chris@340 | 1794             if (generation == Labeller::ValueFromCyclicalCounter || | 
| Chris@340 | 1795                 generation == Labeller::ValueFromTwoLevelCounter) { | 
| Chris@616 | 1796                 int cycleSize = QInputDialog::getInt | 
| Chris@340 | 1797                     (0, tr("Select cycle size"), | 
| Chris@340 | 1798                      tr("Cycle size:"), 4, 2, 16, 1); | 
| Chris@340 | 1799                 labeller.setCounterCycleSize(cycleSize); | 
| Chris@340 | 1800             } | 
| Chris@125 | 1801 | 
| Chris@125 | 1802             prevSelection = selection; | 
| Chris@125 | 1803         } | 
| Chris@125 | 1804     } | 
| Chris@125 | 1805 | 
| Chris@372 | 1806     SparseTimeValueModel::Point prevPoint(0); | 
| Chris@125 | 1807 | 
| Chris@76 | 1808     for (Clipboard::PointList::const_iterator i = points.begin(); | 
| Chris@76 | 1809          i != points.end(); ++i) { | 
| Chris@76 | 1810 | 
| Chris@76 | 1811         if (!i->haveFrame()) continue; | 
| Chris@360 | 1812 | 
| Chris@76 | 1813         size_t frame = 0; | 
| Chris@360 | 1814 | 
| Chris@360 | 1815         if (!realign) { | 
| Chris@360 | 1816 | 
| Chris@360 | 1817             frame = i->getFrame(); | 
| Chris@360 | 1818 | 
| Chris@360 | 1819         } else { | 
| Chris@360 | 1820 | 
| Chris@360 | 1821             if (i->haveReferenceFrame()) { | 
| Chris@360 | 1822                 frame = i->getReferenceFrame(); | 
| Chris@360 | 1823                 frame = alignFromReference(v, frame); | 
| Chris@360 | 1824             } else { | 
| Chris@360 | 1825                 frame = i->getFrame(); | 
| Chris@360 | 1826             } | 
| Chris@76 | 1827         } | 
| Chris@360 | 1828 | 
| Chris@76 | 1829         SparseTimeValueModel::Point newPoint(frame); | 
| Chris@76 | 1830 | 
| Chris@125 | 1831         if (i->haveLabel()) { | 
| Chris@125 | 1832             newPoint.label = i->getLabel(); | 
| Chris@125 | 1833         } else if (i->haveValue()) { | 
| Chris@125 | 1834             newPoint.label = QString("%1").arg(i->getValue()); | 
| Chris@125 | 1835         } | 
| Chris@125 | 1836 | 
| Chris@372 | 1837         bool usePrev = false; | 
| Chris@372 | 1838         SparseTimeValueModel::Point formerPrevPoint = prevPoint; | 
| Chris@372 | 1839 | 
| Chris@125 | 1840         if (i->haveValue()) { | 
| Chris@125 | 1841             newPoint.value = i->getValue(); | 
| Chris@125 | 1842         } else { | 
| Chris@526 | 1843 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1844             cerr << "Setting value on point at " << newPoint.frame << " from labeller"; | 
| Chris@526 | 1845             if (i == points.begin()) { | 
| Chris@682 | 1846                 cerr << ", no prev point" << endl; | 
| Chris@526 | 1847             } else { | 
| Chris@682 | 1848                 cerr << ", prev point is at " << prevPoint.frame << endl; | 
| Chris@526 | 1849             } | 
| Chris@526 | 1850 #endif | 
| Chris@340 | 1851             labeller.setValue<SparseTimeValueModel::Point> | 
| Chris@371 | 1852                 (newPoint, (i == points.begin()) ? 0 : &prevPoint); | 
| Chris@526 | 1853 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1854             cerr << "New point value = " << newPoint.value << endl; | 
| Chris@526 | 1855 #endif | 
| Chris@372 | 1856             if (labeller.actingOnPrevPoint() && i != points.begin()) { | 
| Chris@372 | 1857                 usePrev = true; | 
| Chris@372 | 1858             } | 
| Chris@372 | 1859         } | 
| Chris@372 | 1860 | 
| Chris@372 | 1861         if (usePrev) { | 
| Chris@372 | 1862             command->deletePoint(formerPrevPoint); | 
| Chris@372 | 1863             command->addPoint(prevPoint); | 
| Chris@340 | 1864         } | 
| Chris@125 | 1865 | 
| Chris@340 | 1866         prevPoint = newPoint; | 
| Chris@76 | 1867         command->addPoint(newPoint); | 
| Chris@76 | 1868     } | 
| Chris@76 | 1869 | 
| Chris@376 | 1870     finish(command); | 
| Chris@125 | 1871     return true; | 
| Chris@360 | 1872 } | 
| Chris@76 | 1873 | 
| Chris@316 | 1874 void | 
| Chris@316 | 1875 TimeValueLayer::toXml(QTextStream &stream, | 
| Chris@316 | 1876                       QString indent, QString extraAttributes) const | 
| Chris@6 | 1877 { | 
| Chris@316 | 1878     SingleColourLayer::toXml(stream, indent, | 
| Chris@316 | 1879                              extraAttributes + | 
| Chris@553 | 1880                              QString(" colourMap=\"%1\" plotStyle=\"%2\" verticalScale=\"%3\" scaleMinimum=\"%4\" scaleMaximum=\"%5\" drawDivisions=\"%6\" derivative=\"%7\" ") | 
| Chris@316 | 1881                              .arg(m_colourMap) | 
| Chris@316 | 1882                              .arg(m_plotStyle) | 
| Chris@445 | 1883                              .arg(m_verticalScale) | 
| Chris@445 | 1884                              .arg(m_scaleMinimum) | 
| Chris@513 | 1885                              .arg(m_scaleMaximum) | 
| Chris@553 | 1886                              .arg(m_drawSegmentDivisions ? "true" : "false") | 
| Chris@553 | 1887                              .arg(m_derivative ? "true" : "false")); | 
| Chris@0 | 1888 } | 
| Chris@0 | 1889 | 
| Chris@11 | 1890 void | 
| Chris@11 | 1891 TimeValueLayer::setProperties(const QXmlAttributes &attributes) | 
| Chris@11 | 1892 { | 
| Chris@287 | 1893     SingleColourLayer::setProperties(attributes); | 
| Chris@11 | 1894 | 
| Chris@445 | 1895     bool ok, alsoOk; | 
| Chris@287 | 1896 | 
| Chris@287 | 1897     int cmap = attributes.value("colourMap").toInt(&ok); | 
| Chris@287 | 1898     if (ok) setFillColourMap(cmap); | 
| Chris@287 | 1899 | 
| Chris@11 | 1900     PlotStyle style = (PlotStyle) | 
| Chris@11 | 1901 	attributes.value("plotStyle").toInt(&ok); | 
| Chris@11 | 1902     if (ok) setPlotStyle(style); | 
| Chris@286 | 1903 | 
| Chris@286 | 1904     VerticalScale scale = (VerticalScale) | 
| Chris@286 | 1905 	attributes.value("verticalScale").toInt(&ok); | 
| Chris@286 | 1906     if (ok) setVerticalScale(scale); | 
| Chris@445 | 1907 | 
| Chris@513 | 1908     bool draw = (attributes.value("drawDivisions").trimmed() == "true"); | 
| Chris@513 | 1909     setDrawSegmentDivisions(draw); | 
| Chris@513 | 1910 | 
| Chris@553 | 1911     bool derivative = (attributes.value("derivative").trimmed() == "true"); | 
| Chris@553 | 1912     setShowDerivative(derivative); | 
| Chris@553 | 1913 | 
| Chris@445 | 1914     float min = attributes.value("scaleMinimum").toFloat(&ok); | 
| Chris@445 | 1915     float max = attributes.value("scaleMaximum").toFloat(&alsoOk); | 
| Chris@526 | 1916 #ifdef DEBUG_TIME_VALUE_LAYER | 
| Chris@682 | 1917     cerr << "from properties: min = " << min << ", max = " << max << endl; | 
| Chris@526 | 1918 #endif | 
| Chris@526 | 1919     if (ok && alsoOk && min != max) setDisplayExtents(min, max); | 
| Chris@11 | 1920 } | 
| Chris@11 | 1921 |