svgui
1.9
|
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