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-2008 Chris Cannam and QMUL. 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 "RegionLayer.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 "ColourDatabase.h" 00023 00024 #include "ColourMapper.h" 00025 #include "LinearNumericalScale.h" 00026 #include "LogNumericalScale.h" 00027 #include "LinearColourScale.h" 00028 #include "LogColourScale.h" 00029 00030 #include "view/View.h" 00031 00032 #include "data/model/RegionModel.h" 00033 00034 #include "widgets/ItemEditDialog.h" 00035 #include "widgets/TextAbbrev.h" 00036 00037 #include <QPainter> 00038 #include <QPainterPath> 00039 #include <QMouseEvent> 00040 #include <QTextStream> 00041 #include <QMessageBox> 00042 00043 #include <iostream> 00044 #include <cmath> 00045 00046 RegionLayer::RegionLayer() : 00047 SingleColourLayer(), 00048 m_model(0), 00049 m_editing(false), 00050 m_dragPointX(0), 00051 m_dragPointY(0), 00052 m_dragStartX(0), 00053 m_dragStartY(0), 00054 m_originalPoint(0, 0.0, 0, tr("New Region")), 00055 m_editingPoint(0, 0.0, 0, tr("New Region")), 00056 m_editingCommand(0), 00057 m_verticalScale(EqualSpaced), 00058 m_colourMap(0), 00059 m_plotStyle(PlotLines) 00060 { 00061 00062 } 00063 00064 void 00065 RegionLayer::setModel(RegionModel *model) 00066 { 00067 if (m_model == model) return; 00068 m_model = model; 00069 00070 connectSignals(m_model); 00071 00072 connect(m_model, SIGNAL(modelChanged()), this, SLOT(recalcSpacing())); 00073 recalcSpacing(); 00074 00075 // SVDEBUG << "RegionLayer::setModel(" << model << ")" << endl; 00076 00077 if (m_model && m_model->getRDFTypeURI().endsWith("Segment")) { 00078 setPlotStyle(PlotSegmentation); 00079 } 00080 if (m_model && m_model->getRDFTypeURI().endsWith("Change")) { 00081 setPlotStyle(PlotSegmentation); 00082 } 00083 00084 emit modelReplaced(); 00085 } 00086 00087 Layer::PropertyList 00088 RegionLayer::getProperties() const 00089 { 00090 PropertyList list = SingleColourLayer::getProperties(); 00091 list.push_back("Vertical Scale"); 00092 list.push_back("Scale Units"); 00093 list.push_back("Plot Type"); 00094 return list; 00095 } 00096 00097 QString 00098 RegionLayer::getPropertyLabel(const PropertyName &name) const 00099 { 00100 if (name == "Vertical Scale") return tr("Vertical Scale"); 00101 if (name == "Scale Units") return tr("Scale Units"); 00102 if (name == "Plot Type") return tr("Plot Type"); 00103 return SingleColourLayer::getPropertyLabel(name); 00104 } 00105 00106 Layer::PropertyType 00107 RegionLayer::getPropertyType(const PropertyName &name) const 00108 { 00109 if (name == "Scale Units") return UnitsProperty; 00110 if (name == "Vertical Scale") return ValueProperty; 00111 if (name == "Plot Type") return ValueProperty; 00112 if (name == "Colour" && m_plotStyle == PlotSegmentation) return ValueProperty; 00113 return SingleColourLayer::getPropertyType(name); 00114 } 00115 00116 QString 00117 RegionLayer::getPropertyGroupName(const PropertyName &name) const 00118 { 00119 if (name == "Vertical Scale" || name == "Scale Units") { 00120 return tr("Scale"); 00121 } 00122 return SingleColourLayer::getPropertyGroupName(name); 00123 } 00124 00125 int 00126 RegionLayer::getPropertyRangeAndValue(const PropertyName &name, 00127 int *min, int *max, int *deflt) const 00128 { 00129 int val = 0; 00130 00131 if (name == "Colour" && m_plotStyle == PlotSegmentation) { 00132 00133 if (min) *min = 0; 00134 if (max) *max = ColourMapper::getColourMapCount() - 1; 00135 if (deflt) *deflt = 0; 00136 00137 val = m_colourMap; 00138 00139 } else if (name == "Plot Type") { 00140 00141 if (min) *min = 0; 00142 if (max) *max = 1; 00143 if (deflt) *deflt = 0; 00144 00145 val = int(m_plotStyle); 00146 00147 } else if (name == "Vertical Scale") { 00148 00149 if (min) *min = 0; 00150 if (max) *max = 3; 00151 if (deflt) *deflt = int(EqualSpaced); 00152 00153 val = int(m_verticalScale); 00154 00155 } else if (name == "Scale Units") { 00156 00157 if (deflt) *deflt = 0; 00158 if (m_model) { 00159 val = UnitDatabase::getInstance()->getUnitId 00160 (getScaleUnits()); 00161 } 00162 00163 } else { 00164 00165 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); 00166 } 00167 00168 return val; 00169 } 00170 00171 QString 00172 RegionLayer::getPropertyValueLabel(const PropertyName &name, 00173 int value) const 00174 { 00175 if (name == "Colour" && m_plotStyle == PlotSegmentation) { 00176 return ColourMapper::getColourMapName(value); 00177 } else if (name == "Plot Type") { 00178 00179 switch (value) { 00180 default: 00181 case 0: return tr("Bars"); 00182 case 1: return tr("Segmentation"); 00183 } 00184 00185 } else if (name == "Vertical Scale") { 00186 switch (value) { 00187 default: 00188 case 0: return tr("Auto-Align"); 00189 case 1: return tr("Equal Spaced"); 00190 case 2: return tr("Linear"); 00191 case 3: return tr("Log"); 00192 } 00193 } 00194 return SingleColourLayer::getPropertyValueLabel(name, value); 00195 } 00196 00197 void 00198 RegionLayer::setProperty(const PropertyName &name, int value) 00199 { 00200 if (name == "Colour" && m_plotStyle == PlotSegmentation) { 00201 setFillColourMap(value); 00202 } else if (name == "Plot Type") { 00203 setPlotStyle(PlotStyle(value)); 00204 } else if (name == "Vertical Scale") { 00205 setVerticalScale(VerticalScale(value)); 00206 } else if (name == "Scale Units") { 00207 if (m_model) { 00208 m_model->setScaleUnits 00209 (UnitDatabase::getInstance()->getUnitById(value)); 00210 emit modelChanged(); 00211 } 00212 } else { 00213 return SingleColourLayer::setProperty(name, value); 00214 } 00215 } 00216 00217 void 00218 RegionLayer::setFillColourMap(int map) 00219 { 00220 if (m_colourMap == map) return; 00221 m_colourMap = map; 00222 emit layerParametersChanged(); 00223 } 00224 00225 void 00226 RegionLayer::setPlotStyle(PlotStyle style) 00227 { 00228 if (m_plotStyle == style) return; 00229 bool colourTypeChanged = (style == PlotSegmentation || 00230 m_plotStyle == PlotSegmentation); 00231 m_plotStyle = style; 00232 if (colourTypeChanged) { 00233 emit layerParameterRangesChanged(); 00234 } 00235 emit layerParametersChanged(); 00236 } 00237 00238 void 00239 RegionLayer::setVerticalScale(VerticalScale scale) 00240 { 00241 if (m_verticalScale == scale) return; 00242 m_verticalScale = scale; 00243 emit layerParametersChanged(); 00244 } 00245 00246 bool 00247 RegionLayer::isLayerScrollable(const View *v) const 00248 { 00249 QPoint discard; 00250 return !v->shouldIlluminateLocalFeatures(this, discard); 00251 } 00252 00253 void 00254 RegionLayer::recalcSpacing() 00255 { 00256 m_spacingMap.clear(); 00257 m_distributionMap.clear(); 00258 if (!m_model) return; 00259 00260 // SVDEBUG << "RegionLayer::recalcSpacing" << endl; 00261 00262 for (RegionModel::PointList::const_iterator i = m_model->getPoints().begin(); 00263 i != m_model->getPoints().end(); ++i) { 00264 m_distributionMap[i->value]++; 00265 // SVDEBUG << "RegionLayer::recalcSpacing: value found: " << i->value << " (now have " << m_distributionMap[i->value] << " of this value)" << endl; 00266 } 00267 00268 int n = 0; 00269 00270 for (SpacingMap::const_iterator i = m_distributionMap.begin(); 00271 i != m_distributionMap.end(); ++i) { 00272 m_spacingMap[i->first] = n++; 00273 // SVDEBUG << "RegionLayer::recalcSpacing: " << i->first << " -> " << m_spacingMap[i->first] << endl; 00274 } 00275 } 00276 00277 bool 00278 RegionLayer::getValueExtents(float &min, float &max, 00279 bool &logarithmic, QString &unit) const 00280 { 00281 if (!m_model) return false; 00282 min = m_model->getValueMinimum(); 00283 max = m_model->getValueMaximum(); 00284 unit = getScaleUnits(); 00285 00286 if (m_verticalScale == LogScale) logarithmic = true; 00287 00288 return true; 00289 } 00290 00291 bool 00292 RegionLayer::getDisplayExtents(float &min, float &max) const 00293 { 00294 if (!m_model || 00295 m_verticalScale == AutoAlignScale || 00296 m_verticalScale == EqualSpaced) return false; 00297 00298 min = m_model->getValueMinimum(); 00299 max = m_model->getValueMaximum(); 00300 00301 return true; 00302 } 00303 00304 RegionModel::PointList 00305 RegionLayer::getLocalPoints(View *v, int x) const 00306 { 00307 if (!m_model) return RegionModel::PointList(); 00308 00309 long frame = v->getFrameForX(x); 00310 00311 RegionModel::PointList onPoints = 00312 m_model->getPoints(frame); 00313 00314 if (!onPoints.empty()) { 00315 return onPoints; 00316 } 00317 00318 RegionModel::PointList prevPoints = 00319 m_model->getPreviousPoints(frame); 00320 RegionModel::PointList nextPoints = 00321 m_model->getNextPoints(frame); 00322 00323 RegionModel::PointList usePoints = prevPoints; 00324 00325 if (prevPoints.empty()) { 00326 usePoints = nextPoints; 00327 } else if (long(prevPoints.begin()->frame) < v->getStartFrame() && 00328 !(nextPoints.begin()->frame > v->getEndFrame())) { 00329 usePoints = nextPoints; 00330 } else if (long(nextPoints.begin()->frame) - frame < 00331 frame - long(prevPoints.begin()->frame)) { 00332 usePoints = nextPoints; 00333 } 00334 00335 if (!usePoints.empty()) { 00336 int fuzz = 2; 00337 int px = v->getXForFrame(usePoints.begin()->frame); 00338 if ((px > x && px - x > fuzz) || 00339 (px < x && x - px > fuzz + 1)) { 00340 usePoints.clear(); 00341 } 00342 } 00343 00344 return usePoints; 00345 } 00346 00347 bool 00348 RegionLayer::getPointToDrag(View *v, int x, int y, RegionModel::Point &p) const 00349 { 00350 if (!m_model) return false; 00351 00352 long frame = v->getFrameForX(x); 00353 00354 RegionModel::PointList onPoints = m_model->getPoints(frame); 00355 if (onPoints.empty()) return false; 00356 00357 int nearestDistance = -1; 00358 00359 for (RegionModel::PointList::const_iterator i = onPoints.begin(); 00360 i != onPoints.end(); ++i) { 00361 00362 int distance = getYForValue(v, (*i).value) - y; 00363 if (distance < 0) distance = -distance; 00364 if (nearestDistance == -1 || distance < nearestDistance) { 00365 nearestDistance = distance; 00366 p = *i; 00367 } 00368 } 00369 00370 return true; 00371 } 00372 00373 QString 00374 RegionLayer::getLabelPreceding(int frame) const 00375 { 00376 if (!m_model) return ""; 00377 RegionModel::PointList points = m_model->getPreviousPoints(frame); 00378 for (RegionModel::PointList::const_iterator i = points.begin(); 00379 i != points.end(); ++i) { 00380 if (i->label != "") return i->label; 00381 } 00382 return ""; 00383 } 00384 00385 QString 00386 RegionLayer::getFeatureDescription(View *v, QPoint &pos) const 00387 { 00388 int x = pos.x(); 00389 00390 if (!m_model || !m_model->getSampleRate()) return ""; 00391 00392 RegionModel::PointList points = getLocalPoints(v, x); 00393 00394 if (points.empty()) { 00395 if (!m_model->isReady()) { 00396 return tr("In progress"); 00397 } else { 00398 return tr("No local points"); 00399 } 00400 } 00401 00402 RegionRec region(0); 00403 RegionModel::PointList::iterator i; 00404 00407 00408 for (i = points.begin(); i != points.end(); ++i) { 00409 00410 int y = getYForValue(v, i->value); 00411 int h = 3; 00412 00413 if (m_model->getValueQuantization() != 0.0) { 00414 h = y - getYForValue(v, i->value + m_model->getValueQuantization()); 00415 if (h < 3) h = 3; 00416 } 00417 00418 if (pos.y() >= y - h && pos.y() <= y) { 00419 region = *i; 00420 break; 00421 } 00422 } 00423 00424 if (i == points.end()) return tr("No local points"); 00425 00426 RealTime rt = RealTime::frame2RealTime(region.frame, 00427 m_model->getSampleRate()); 00428 RealTime rd = RealTime::frame2RealTime(region.duration, 00429 m_model->getSampleRate()); 00430 00431 QString valueText; 00432 00433 valueText = tr("%1 %2").arg(region.value).arg(getScaleUnits()); 00434 00435 QString text; 00436 00437 if (region.label == "") { 00438 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label")) 00439 .arg(rt.toText(true).c_str()) 00440 .arg(valueText) 00441 .arg(rd.toText(true).c_str()); 00442 } else { 00443 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nLabel:\t%4")) 00444 .arg(rt.toText(true).c_str()) 00445 .arg(valueText) 00446 .arg(rd.toText(true).c_str()) 00447 .arg(region.label); 00448 } 00449 00450 pos = QPoint(v->getXForFrame(region.frame), 00451 getYForValue(v, region.value)); 00452 return text; 00453 } 00454 00455 bool 00456 RegionLayer::snapToFeatureFrame(View *v, int &frame, 00457 int &resolution, 00458 SnapType snap) const 00459 { 00460 if (!m_model) { 00461 return Layer::snapToFeatureFrame(v, frame, resolution, snap); 00462 } 00463 00464 resolution = m_model->getResolution(); 00465 RegionModel::PointList points; 00466 00467 if (snap == SnapNeighbouring) { 00468 00469 points = getLocalPoints(v, v->getXForFrame(frame)); 00470 if (points.empty()) return false; 00471 frame = points.begin()->frame; 00472 return true; 00473 } 00474 00475 points = m_model->getPoints(frame, frame); 00476 int snapped = frame; 00477 bool found = false; 00478 00479 for (RegionModel::PointList::const_iterator i = points.begin(); 00480 i != points.end(); ++i) { 00481 00482 if (snap == SnapRight) { 00483 00484 // The best frame to snap to is the end frame of whichever 00485 // feature we would have snapped to the start frame of if 00486 // we had been snapping left. 00487 00488 if (i->frame <= frame) { 00489 if (i->frame + i->duration > frame) { 00490 snapped = i->frame + i->duration; 00491 found = true; // don't break, as the next may be better 00492 } 00493 } else { 00494 if (!found) { 00495 snapped = i->frame; 00496 found = true; 00497 } 00498 break; 00499 } 00500 00501 } else if (snap == SnapLeft) { 00502 00503 if (i->frame <= frame) { 00504 snapped = i->frame; 00505 found = true; // don't break, as the next may be better 00506 } else { 00507 break; 00508 } 00509 00510 } else { // nearest 00511 00512 RegionModel::PointList::const_iterator j = i; 00513 ++j; 00514 00515 if (j == points.end()) { 00516 00517 snapped = i->frame; 00518 found = true; 00519 break; 00520 00521 } else if (j->frame >= frame) { 00522 00523 if (j->frame - frame < frame - i->frame) { 00524 snapped = j->frame; 00525 } else { 00526 snapped = i->frame; 00527 } 00528 found = true; 00529 break; 00530 } 00531 } 00532 } 00533 00534 frame = snapped; 00535 return found; 00536 } 00537 00538 bool 00539 RegionLayer::snapToSimilarFeature(View *v, int &frame, 00540 int &resolution, 00541 SnapType snap) const 00542 { 00543 if (!m_model) { 00544 return Layer::snapToSimilarFeature(v, frame, resolution, snap); 00545 } 00546 00547 resolution = m_model->getResolution(); 00548 00549 const RegionModel::PointList &points = m_model->getPoints(); 00550 RegionModel::PointList close = m_model->getPoints(frame, frame); 00551 00552 RegionModel::PointList::const_iterator i; 00553 00554 int matchframe = frame; 00555 float matchvalue = 0.f; 00556 00557 for (i = close.begin(); i != close.end(); ++i) { 00558 if (i->frame > frame) break; 00559 matchvalue = i->value; 00560 matchframe = i->frame; 00561 } 00562 00563 int snapped = frame; 00564 bool found = false; 00565 bool distant = false; 00566 float epsilon = 0.0001; 00567 00568 i = close.begin(); 00569 00570 // Scan through the close points first, then the more distant ones 00571 // if no suitable close one is found. So the while-termination 00572 // condition here can only happen once i has passed through the 00573 // whole of the close container and then the whole of the separate 00574 // points container. The two iterators are totally distinct, but 00575 // have the same type so we cheekily use the same variable and a 00576 // single loop for both. 00577 00578 while (i != points.end()) { 00579 00580 if (!distant) { 00581 if (i == close.end()) { 00582 // switch from the close container to the points container 00583 i = points.begin(); 00584 distant = true; 00585 } 00586 } 00587 00588 if (snap == SnapRight) { 00589 00590 if (i->frame > matchframe && 00591 fabsf(i->value - matchvalue) < epsilon) { 00592 snapped = i->frame; 00593 found = true; 00594 break; 00595 } 00596 00597 } else if (snap == SnapLeft) { 00598 00599 if (i->frame < matchframe) { 00600 if (fabsf(i->value - matchvalue) < epsilon) { 00601 snapped = i->frame; 00602 found = true; // don't break, as the next may be better 00603 } 00604 } else if (found || distant) { 00605 break; 00606 } 00607 00608 } else { 00609 // no other snap types supported 00610 } 00611 00612 ++i; 00613 } 00614 00615 frame = snapped; 00616 return found; 00617 } 00618 00619 QString 00620 RegionLayer::getScaleUnits() const 00621 { 00622 if (m_model) return m_model->getScaleUnits(); 00623 else return ""; 00624 } 00625 00626 void 00627 RegionLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const 00628 { 00629 min = 0.0; 00630 max = 0.0; 00631 log = false; 00632 00633 QString queryUnits; 00634 queryUnits = getScaleUnits(); 00635 00636 if (m_verticalScale == AutoAlignScale) { 00637 00638 if (!v->getValueExtents(queryUnits, min, max, log)) { 00639 00640 min = m_model->getValueMinimum(); 00641 max = m_model->getValueMaximum(); 00642 00643 // cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl; 00644 00645 } else if (log) { 00646 00647 LogRange::mapRange(min, max); 00648 00649 // cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl; 00650 00651 } 00652 00653 } else if (m_verticalScale == EqualSpaced) { 00654 00655 if (!m_spacingMap.empty()) { 00656 SpacingMap::const_iterator i = m_spacingMap.begin(); 00657 min = i->second; 00658 i = m_spacingMap.end(); 00659 --i; 00660 max = i->second; 00661 // cerr << "RegionLayer[" << this << "]::getScaleExtents: equal spaced; min = " << min << ", max = " << max << ", log = " << log << endl; 00662 } 00663 00664 } else { 00665 00666 min = m_model->getValueMinimum(); 00667 max = m_model->getValueMaximum(); 00668 00669 if (m_verticalScale == LogScale) { 00670 LogRange::mapRange(min, max); 00671 log = true; 00672 } 00673 } 00674 00675 if (max == min) max = min + 1.0; 00676 } 00677 00678 int 00679 RegionLayer::spacingIndexToY(View *v, int i) const 00680 { 00681 int h = v->height(); 00682 int n = m_spacingMap.size(); 00683 // this maps from i (spacing of the value from the spacing 00684 // map) and n (number of region types) to y 00685 int y = h - (((h * i) / n) + (h / (2 * n))); 00686 return y; 00687 } 00688 00689 float 00690 RegionLayer::yToSpacingIndex(View *v, int y) const 00691 { 00692 // we return an inexact result here (float rather than int) 00693 int h = v->height(); 00694 int n = m_spacingMap.size(); 00695 // from y = h - ((h * i) / n) + (h / (2 * n)) as above (vh taking place of i) 00696 float vh = float(2*h*n - h - 2*n*y) / float(2*h); 00697 return vh; 00698 } 00699 00700 int 00701 RegionLayer::getYForValue(View *v, float val) const 00702 { 00703 float min = 0.0, max = 0.0; 00704 bool logarithmic = false; 00705 int h = v->height(); 00706 00707 if (m_verticalScale == EqualSpaced) { 00708 00709 if (m_spacingMap.empty()) return h/2; 00710 00711 SpacingMap::const_iterator i = m_spacingMap.lower_bound(val); 00713 00714 int y = spacingIndexToY(v, i->second); 00715 00716 // SVDEBUG << "RegionLayer::getYForValue: value " << val << " -> i->second " << i->second << " -> y " << y << endl; 00717 return y; 00718 00719 00720 } else { 00721 00722 getScaleExtents(v, min, max, logarithmic); 00723 00724 // cerr << "RegionLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl; 00725 // cerr << "h = " << h << ", margin = " << margin << endl; 00726 00727 if (logarithmic) { 00728 val = LogRange::map(val); 00729 } 00730 00731 return int(h - ((val - min) * h) / (max - min)); 00732 } 00733 } 00734 00735 float 00736 RegionLayer::getValueForY(View *v, int y) const 00737 { 00738 return getValueForY(v, y, -1); 00739 } 00740 00741 float 00742 RegionLayer::getValueForY(View *v, int y, int avoid) const 00743 { 00744 float min = 0.0, max = 0.0; 00745 bool logarithmic = false; 00746 int h = v->height(); 00747 00748 if (m_verticalScale == EqualSpaced) { 00749 00750 // if we're equal spaced, we probably want to snap to the 00751 // nearest item when close to it, and give some notification 00752 // that we're doing so 00753 00754 if (m_spacingMap.empty()) return 1.f; 00755 00756 // n is the number of distinct regions. if we are close to 00757 // one of the m/n divisions in the y scale, we should snap to 00758 // the value of the mth region. 00759 00760 float vh = yToSpacingIndex(v, y); 00761 00762 // spacings in the map are integral, so find the closest one, 00763 // map it back to its y coordinate, and see how far we are 00764 // from it 00765 00766 int n = m_spacingMap.size(); 00767 int ivh = lrintf(vh); 00768 if (ivh < 0) ivh = 0; 00769 if (ivh > n-1) ivh = n-1; 00770 int iy = spacingIndexToY(v, ivh); 00771 00772 int dist = iy - y; 00773 int gap = h / n; // between region lines 00774 00775 // cerr << "getValueForY: y = " << y << ", vh = " << vh << ", ivh = " << ivh << " of " << n << ", iy = " << iy << ", dist = " << dist << ", gap = " << gap << endl; 00776 00777 SpacingMap::const_iterator i = m_spacingMap.begin(); 00778 while (i != m_spacingMap.end()) { 00779 if (i->second == ivh) break; 00780 ++i; 00781 } 00782 if (i == m_spacingMap.end()) i = m_spacingMap.begin(); 00783 00784 // cerr << "nearest existing value = " << i->first << " at " << iy << endl; 00785 00786 float val = 0; 00787 00788 // cerr << "note: avoid = " << avoid << ", i->second = " << i->second << endl; 00789 00790 if (dist < -gap/3 && 00791 ((avoid == -1) || 00792 (avoid != i->second && avoid != i->second - 1))) { 00793 // bisect gap to prior 00794 if (i == m_spacingMap.begin()) { 00795 val = i->first - 1.f; 00796 // cerr << "extended down to " << val << endl; 00797 } else { 00798 SpacingMap::const_iterator j = i; 00799 --j; 00800 val = (i->first + j->first) / 2; 00801 // cerr << "bisected down to " << val << endl; 00802 } 00803 } else if (dist > gap/3 && 00804 ((avoid == -1) || 00805 (avoid != i->second && avoid != i->second + 1))) { 00806 // bisect gap to following 00807 SpacingMap::const_iterator j = i; 00808 ++j; 00809 if (j == m_spacingMap.end()) { 00810 val = i->first + 1.f; 00811 // cerr << "extended up to " << val << endl; 00812 } else { 00813 val = (i->first + j->first) / 2; 00814 // cerr << "bisected up to " << val << endl; 00815 } 00816 } else { 00817 // snap 00818 val = i->first; 00819 // cerr << "snapped to " << val << endl; 00820 } 00821 00822 return val; 00823 00824 } else { 00825 00826 getScaleExtents(v, min, max, logarithmic); 00827 00828 float val = min + (float(h - y) * float(max - min)) / h; 00829 00830 if (logarithmic) { 00831 val = powf(10.f, val); 00832 } 00833 00834 return val; 00835 } 00836 } 00837 00838 QColor 00839 RegionLayer::getColourForValue(View *v, float val) const 00840 { 00841 float min, max; 00842 bool log; 00843 getScaleExtents(v, min, max, log); 00844 00845 if (min > max) std::swap(min, max); 00846 if (max == min) max = min + 1; 00847 00848 if (log) { 00849 LogRange::mapRange(min, max); 00850 val = LogRange::map(val); 00851 } 00852 00853 // SVDEBUG << "RegionLayer::getColourForValue: min " << min << ", max " 00854 // << max << ", log " << log << ", value " << val << endl; 00855 00856 QColor solid = ColourMapper(m_colourMap, min, max).map(val); 00857 return QColor(solid.red(), solid.green(), solid.blue(), 120); 00858 } 00859 00860 int 00861 RegionLayer::getDefaultColourHint(bool darkbg, bool &impose) 00862 { 00863 impose = false; 00864 return ColourDatabase::getInstance()->getColourIndex 00865 (QString(darkbg ? "Bright Blue" : "Blue")); 00866 } 00867 00868 void 00869 RegionLayer::paint(View *v, QPainter &paint, QRect rect) const 00870 { 00871 if (!m_model || !m_model->isOK()) return; 00872 00873 int sampleRate = m_model->getSampleRate(); 00874 if (!sampleRate) return; 00875 00876 // Profiler profiler("RegionLayer::paint", true); 00877 00878 int x0 = rect.left() - 40, x1 = rect.right(); 00879 long frame0 = v->getFrameForX(x0); 00880 long frame1 = v->getFrameForX(x1); 00881 00882 RegionModel::PointList points(m_model->getPoints(frame0, frame1)); 00883 if (points.empty()) return; 00884 00885 paint.setPen(getBaseQColor()); 00886 00887 QColor brushColour(getBaseQColor()); 00888 brushColour.setAlpha(80); 00889 00890 // SVDEBUG << "RegionLayer::paint: resolution is " 00891 // << m_model->getResolution() << " frames" << endl; 00892 00893 float min = m_model->getValueMinimum(); 00894 float max = m_model->getValueMaximum(); 00895 if (max == min) max = min + 1.0; 00896 00897 QPoint localPos; 00898 RegionModel::Point illuminatePoint(0); 00899 bool shouldIlluminate = false; 00900 00901 if (v->shouldIlluminateLocalFeatures(this, localPos)) { 00902 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(), 00903 illuminatePoint); 00904 } 00905 00906 paint.save(); 00907 paint.setRenderHint(QPainter::Antialiasing, false); 00908 00911 00914 00915 int fontHeight = paint.fontMetrics().height(); 00916 00917 for (RegionModel::PointList::const_iterator i = points.begin(); 00918 i != points.end(); ++i) { 00919 00920 const RegionModel::Point &p(*i); 00921 00922 int x = v->getXForFrame(p.frame); 00923 int y = getYForValue(v, p.value); 00924 int w = v->getXForFrame(p.frame + p.duration) - x; 00925 int h = 9; 00926 int ex = x + w; 00927 00928 RegionModel::PointList::const_iterator j = i; 00929 ++j; 00930 00931 if (j != points.end()) { 00932 const RegionModel::Point &q(*j); 00933 int nx = v->getXForFrame(q.frame); 00934 if (nx < ex) ex = nx; 00935 } 00936 00937 if (m_model->getValueQuantization() != 0.0) { 00938 h = y - getYForValue(v, p.value + m_model->getValueQuantization()); 00939 if (h < 3) h = 3; 00940 } 00941 00942 if (w < 1) w = 1; 00943 00944 if (m_plotStyle == PlotSegmentation) { 00945 paint.setPen(getForegroundQColor(v)); 00946 paint.setBrush(getColourForValue(v, p.value)); 00947 } else { 00948 paint.setPen(getBaseQColor()); 00949 paint.setBrush(brushColour); 00950 } 00951 00952 if (m_plotStyle == PlotSegmentation) { 00953 00954 if (ex <= x) continue; 00955 00956 if (!shouldIlluminate || 00957 // "illuminatePoint != p" 00958 RegionModel::Point::Comparator()(illuminatePoint, p) || 00959 RegionModel::Point::Comparator()(p, illuminatePoint)) { 00960 00961 paint.setPen(QPen(getForegroundQColor(v), 1)); 00962 paint.drawLine(x, 0, x, v->height()); 00963 paint.setPen(Qt::NoPen); 00964 00965 } else { 00966 paint.setPen(QPen(getForegroundQColor(v), 2)); 00967 } 00968 00969 paint.drawRect(x, -1, ex - x, v->height() + 2); 00970 00971 } else { 00972 00973 if (shouldIlluminate && 00974 // "illuminatePoint == p" 00975 !RegionModel::Point::Comparator()(illuminatePoint, p) && 00976 !RegionModel::Point::Comparator()(p, illuminatePoint)) { 00977 00978 paint.setPen(v->getForeground()); 00979 paint.setBrush(v->getForeground()); 00980 00981 QString vlabel = QString("%1%2").arg(p.value).arg(getScaleUnits()); 00982 v->drawVisibleText(paint, 00983 x - paint.fontMetrics().width(vlabel) - 2, 00984 y + paint.fontMetrics().height()/2 00985 - paint.fontMetrics().descent(), 00986 vlabel, View::OutlinedText); 00987 00988 QString hlabel = RealTime::frame2RealTime 00989 (p.frame, m_model->getSampleRate()).toText(true).c_str(); 00990 v->drawVisibleText(paint, 00991 x, 00992 y - h/2 - paint.fontMetrics().descent() - 2, 00993 hlabel, View::OutlinedText); 00994 } 00995 00996 paint.drawLine(x, y-1, x + w, y-1); 00997 paint.drawLine(x, y+1, x + w, y+1); 00998 paint.drawLine(x, y - h/2, x, y + h/2); 00999 paint.drawLine(x+w, y - h/2, x + w, y + h/2); 01000 } 01001 } 01002 01003 int nextLabelMinX = -100; 01004 int lastLabelY = 0; 01005 01006 for (RegionModel::PointList::const_iterator i = points.begin(); 01007 i != points.end(); ++i) { 01008 01009 const RegionModel::Point &p(*i); 01010 01011 int x = v->getXForFrame(p.frame); 01012 int y = getYForValue(v, p.value); 01013 01014 bool illuminated = false; 01015 01016 if (m_plotStyle != PlotSegmentation) { 01017 01018 if (shouldIlluminate && 01019 // "illuminatePoint == p" 01020 !RegionModel::Point::Comparator()(illuminatePoint, p) && 01021 !RegionModel::Point::Comparator()(p, illuminatePoint)) { 01022 01023 illuminated = true; 01024 } 01025 } 01026 01027 if (!illuminated) { 01028 QString label = p.label; 01029 if (label == "") { 01030 label = QString("%1%2").arg(p.value).arg(getScaleUnits()); 01031 } 01032 01033 int labelX, labelY; 01034 01035 if (m_plotStyle != PlotSegmentation) { 01036 labelX = x - paint.fontMetrics().width(label) - 2; 01037 labelY = y + paint.fontMetrics().height()/2 01038 - paint.fontMetrics().descent(); 01039 } else { 01040 labelX = x + 5; 01041 labelY = v->getTextLabelHeight(this, paint); 01042 if (labelX < nextLabelMinX) { 01043 if (lastLabelY < v->height()/2) { 01044 labelY = lastLabelY + fontHeight; 01045 } 01046 } 01047 lastLabelY = labelY; 01048 nextLabelMinX = labelX + paint.fontMetrics().width(label); 01049 } 01050 01051 v->drawVisibleText(paint, labelX, labelY, label, View::OutlinedText); 01052 } 01053 } 01054 01055 paint.restore(); 01056 } 01057 01058 int 01059 RegionLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const 01060 { 01061 if (!m_model || 01062 m_verticalScale == AutoAlignScale || 01063 m_verticalScale == EqualSpaced) { 01064 return 0; 01065 } else if (m_plotStyle == PlotSegmentation) { 01066 if (m_verticalScale == LogScale) { 01067 return LogColourScale().getWidth(v, paint); 01068 } else { 01069 return LinearColourScale().getWidth(v, paint); 01070 } 01071 } else { 01072 if (m_verticalScale == LogScale) { 01073 return LogNumericalScale().getWidth(v, paint); 01074 } else { 01075 return LinearNumericalScale().getWidth(v, paint); 01076 } 01077 } 01078 } 01079 01080 void 01081 RegionLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const 01082 { 01083 if (!m_model || m_model->getPoints().empty()) return; 01084 01085 QString unit; 01086 float min, max; 01087 bool logarithmic; 01088 01089 int w = getVerticalScaleWidth(v, false, paint); 01090 01091 if (m_plotStyle == PlotSegmentation) { 01092 01093 getValueExtents(min, max, logarithmic, unit); 01094 01095 if (logarithmic) { 01096 LogRange::mapRange(min, max); 01097 LogColourScale().paintVertical(v, this, paint, 0, min, max); 01098 } else { 01099 LinearColourScale().paintVertical(v, this, paint, 0, min, max); 01100 } 01101 01102 } else { 01103 01104 getScaleExtents(v, min, max, logarithmic); 01105 01106 if (logarithmic) { 01107 LogNumericalScale().paintVertical(v, this, paint, 0, min, max); 01108 } else { 01109 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max); 01110 } 01111 } 01112 01113 if (getScaleUnits() != "") { 01114 int mw = w - 5; 01115 paint.drawText(5, 01116 5 + paint.fontMetrics().ascent(), 01117 TextAbbrev::abbreviate(getScaleUnits(), 01118 paint.fontMetrics(), 01119 mw)); 01120 } 01121 } 01122 01123 void 01124 RegionLayer::drawStart(View *v, QMouseEvent *e) 01125 { 01126 if (!m_model) return; 01127 01128 long frame = v->getFrameForX(e->x()); 01129 if (frame < 0) frame = 0; 01130 frame = frame / m_model->getResolution() * m_model->getResolution(); 01131 01132 float value = getValueForY(v, e->y()); 01133 01134 m_editingPoint = RegionModel::Point(frame, value, 0, ""); 01135 m_originalPoint = m_editingPoint; 01136 01137 if (m_editingCommand) finish(m_editingCommand); 01138 m_editingCommand = new RegionModel::EditCommand(m_model, 01139 tr("Draw Region")); 01140 m_editingCommand->addPoint(m_editingPoint); 01141 01142 recalcSpacing(); 01143 01144 m_editing = true; 01145 } 01146 01147 void 01148 RegionLayer::drawDrag(View *v, QMouseEvent *e) 01149 { 01150 if (!m_model || !m_editing) return; 01151 01152 long frame = v->getFrameForX(e->x()); 01153 if (frame < 0) frame = 0; 01154 frame = frame / m_model->getResolution() * m_model->getResolution(); 01155 01156 float newValue = m_editingPoint.value; 01157 if (m_verticalScale != EqualSpaced) newValue = getValueForY(v, e->y()); 01158 01159 long newFrame = m_editingPoint.frame; 01160 long newDuration = frame - newFrame; 01161 if (newDuration < 0) { 01162 newFrame = frame; 01163 newDuration = -newDuration; 01164 } else if (newDuration == 0) { 01165 newDuration = 1; 01166 } 01167 01168 m_editingCommand->deletePoint(m_editingPoint); 01169 m_editingPoint.frame = newFrame; 01170 m_editingPoint.value = newValue; 01171 m_editingPoint.duration = newDuration; 01172 m_editingCommand->addPoint(m_editingPoint); 01173 01174 recalcSpacing(); 01175 } 01176 01177 void 01178 RegionLayer::drawEnd(View *, QMouseEvent *) 01179 { 01180 if (!m_model || !m_editing) return; 01181 finish(m_editingCommand); 01182 m_editingCommand = 0; 01183 m_editing = false; 01184 01185 recalcSpacing(); 01186 } 01187 01188 void 01189 RegionLayer::eraseStart(View *v, QMouseEvent *e) 01190 { 01191 if (!m_model) return; 01192 01193 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; 01194 01195 if (m_editingCommand) { 01196 finish(m_editingCommand); 01197 m_editingCommand = 0; 01198 } 01199 01200 m_editing = true; 01201 recalcSpacing(); 01202 } 01203 01204 void 01205 RegionLayer::eraseDrag(View *, QMouseEvent *) 01206 { 01207 } 01208 01209 void 01210 RegionLayer::eraseEnd(View *v, QMouseEvent *e) 01211 { 01212 if (!m_model || !m_editing) return; 01213 01214 m_editing = false; 01215 01216 RegionModel::Point p(0); 01217 if (!getPointToDrag(v, e->x(), e->y(), p)) return; 01218 if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return; 01219 01220 m_editingCommand = new RegionModel::EditCommand 01221 (m_model, tr("Erase Region")); 01222 01223 m_editingCommand->deletePoint(m_editingPoint); 01224 01225 finish(m_editingCommand); 01226 m_editingCommand = 0; 01227 m_editing = false; 01228 recalcSpacing(); 01229 } 01230 01231 void 01232 RegionLayer::editStart(View *v, QMouseEvent *e) 01233 { 01234 if (!m_model) return; 01235 01236 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) { 01237 return; 01238 } 01239 01240 m_dragPointX = v->getXForFrame(m_editingPoint.frame); 01241 m_dragPointY = getYForValue(v, m_editingPoint.value); 01242 01243 m_originalPoint = m_editingPoint; 01244 01245 if (m_editingCommand) { 01246 finish(m_editingCommand); 01247 m_editingCommand = 0; 01248 } 01249 01250 m_editing = true; 01251 m_dragStartX = e->x(); 01252 m_dragStartY = e->y(); 01253 recalcSpacing(); 01254 } 01255 01256 void 01257 RegionLayer::editDrag(View *v, QMouseEvent *e) 01258 { 01259 if (!m_model || !m_editing) return; 01260 01261 int xdist = e->x() - m_dragStartX; 01262 int ydist = e->y() - m_dragStartY; 01263 int newx = m_dragPointX + xdist; 01264 int newy = m_dragPointY + ydist; 01265 01266 long frame = v->getFrameForX(newx); 01267 if (frame < 0) frame = 0; 01268 frame = frame / m_model->getResolution() * m_model->getResolution(); 01269 01270 // Do not bisect between two values, if one of those values is 01271 // that of the point we're actually moving ... 01272 int avoid = m_spacingMap[m_editingPoint.value]; 01273 01274 // ... unless there are other points with the same value 01275 if (m_distributionMap[m_editingPoint.value] > 1) avoid = -1; 01276 01277 float value = getValueForY(v, newy, avoid); 01278 01279 if (!m_editingCommand) { 01280 m_editingCommand = new RegionModel::EditCommand(m_model, 01281 tr("Drag Region")); 01282 } 01283 01284 m_editingCommand->deletePoint(m_editingPoint); 01285 m_editingPoint.frame = frame; 01286 m_editingPoint.value = value; 01287 m_editingCommand->addPoint(m_editingPoint); 01288 recalcSpacing(); 01289 } 01290 01291 void 01292 RegionLayer::editEnd(View *, QMouseEvent *) 01293 { 01294 if (!m_model || !m_editing) return; 01295 01296 if (m_editingCommand) { 01297 01298 QString newName = m_editingCommand->getName(); 01299 01300 if (m_editingPoint.frame != m_originalPoint.frame) { 01301 if (m_editingPoint.value != m_originalPoint.value) { 01302 newName = tr("Edit Region"); 01303 } else { 01304 newName = tr("Relocate Region"); 01305 } 01306 } else { 01307 newName = tr("Change Point Value"); 01308 } 01309 01310 m_editingCommand->setName(newName); 01311 finish(m_editingCommand); 01312 } 01313 01314 m_editingCommand = 0; 01315 m_editing = false; 01316 recalcSpacing(); 01317 } 01318 01319 bool 01320 RegionLayer::editOpen(View *v, QMouseEvent *e) 01321 { 01322 if (!m_model) return false; 01323 01324 RegionModel::Point region(0); 01325 if (!getPointToDrag(v, e->x(), e->y(), region)) return false; 01326 01327 ItemEditDialog *dialog = new ItemEditDialog 01328 (m_model->getSampleRate(), 01329 ItemEditDialog::ShowTime | 01330 ItemEditDialog::ShowDuration | 01331 ItemEditDialog::ShowValue | 01332 ItemEditDialog::ShowText, 01333 getScaleUnits()); 01334 01335 dialog->setFrameTime(region.frame); 01336 dialog->setValue(region.value); 01337 dialog->setFrameDuration(region.duration); 01338 dialog->setText(region.label); 01339 01340 if (dialog->exec() == QDialog::Accepted) { 01341 01342 RegionModel::Point newRegion = region; 01343 newRegion.frame = dialog->getFrameTime(); 01344 newRegion.value = dialog->getValue(); 01345 newRegion.duration = dialog->getFrameDuration(); 01346 newRegion.label = dialog->getText(); 01347 01348 RegionModel::EditCommand *command = new RegionModel::EditCommand 01349 (m_model, tr("Edit Region")); 01350 command->deletePoint(region); 01351 command->addPoint(newRegion); 01352 finish(command); 01353 } 01354 01355 delete dialog; 01356 recalcSpacing(); 01357 return true; 01358 } 01359 01360 void 01361 RegionLayer::moveSelection(Selection s, int newStartFrame) 01362 { 01363 if (!m_model) return; 01364 01365 RegionModel::EditCommand *command = 01366 new RegionModel::EditCommand(m_model, tr("Drag Selection")); 01367 01368 RegionModel::PointList points = 01369 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 01370 01371 for (RegionModel::PointList::iterator i = points.begin(); 01372 i != points.end(); ++i) { 01373 01374 if (s.contains(i->frame)) { 01375 RegionModel::Point newPoint(*i); 01376 newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); 01377 command->deletePoint(*i); 01378 command->addPoint(newPoint); 01379 } 01380 } 01381 01382 finish(command); 01383 recalcSpacing(); 01384 } 01385 01386 void 01387 RegionLayer::resizeSelection(Selection s, Selection newSize) 01388 { 01389 if (!m_model) return; 01390 01391 RegionModel::EditCommand *command = 01392 new RegionModel::EditCommand(m_model, tr("Resize Selection")); 01393 01394 RegionModel::PointList points = 01395 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 01396 01397 double ratio = 01398 double(newSize.getEndFrame() - newSize.getStartFrame()) / 01399 double(s.getEndFrame() - s.getStartFrame()); 01400 01401 for (RegionModel::PointList::iterator i = points.begin(); 01402 i != points.end(); ++i) { 01403 01404 if (s.contains(i->frame)) { 01405 01406 double targetStart = i->frame; 01407 targetStart = newSize.getStartFrame() + 01408 double(targetStart - s.getStartFrame()) * ratio; 01409 01410 double targetEnd = i->frame + i->duration; 01411 targetEnd = newSize.getStartFrame() + 01412 double(targetEnd - s.getStartFrame()) * ratio; 01413 01414 RegionModel::Point newPoint(*i); 01415 newPoint.frame = lrint(targetStart); 01416 newPoint.duration = lrint(targetEnd - targetStart); 01417 command->deletePoint(*i); 01418 command->addPoint(newPoint); 01419 } 01420 } 01421 01422 finish(command); 01423 recalcSpacing(); 01424 } 01425 01426 void 01427 RegionLayer::deleteSelection(Selection s) 01428 { 01429 if (!m_model) return; 01430 01431 RegionModel::EditCommand *command = 01432 new RegionModel::EditCommand(m_model, tr("Delete Selected Points")); 01433 01434 RegionModel::PointList points = 01435 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 01436 01437 for (RegionModel::PointList::iterator i = points.begin(); 01438 i != points.end(); ++i) { 01439 01440 if (s.contains(i->frame)) { 01441 command->deletePoint(*i); 01442 } 01443 } 01444 01445 finish(command); 01446 recalcSpacing(); 01447 } 01448 01449 void 01450 RegionLayer::copy(View *v, Selection s, Clipboard &to) 01451 { 01452 if (!m_model) return; 01453 01454 RegionModel::PointList points = 01455 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 01456 01457 for (RegionModel::PointList::iterator i = points.begin(); 01458 i != points.end(); ++i) { 01459 if (s.contains(i->frame)) { 01460 Clipboard::Point point(i->frame, i->value, i->duration, i->label); 01461 point.setReferenceFrame(alignToReference(v, i->frame)); 01462 to.addPoint(point); 01463 } 01464 } 01465 } 01466 01467 bool 01468 RegionLayer::paste(View *v, const Clipboard &from, int /* frameOffset */, bool /* interactive */) 01469 { 01470 if (!m_model) return false; 01471 01472 const Clipboard::PointList &points = from.getPoints(); 01473 01474 bool realign = false; 01475 01476 if (clipboardHasDifferentAlignment(v, from)) { 01477 01478 QMessageBox::StandardButton button = 01479 QMessageBox::question(v, tr("Re-align pasted items?"), 01480 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?"), 01481 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, 01482 QMessageBox::Yes); 01483 01484 if (button == QMessageBox::Cancel) { 01485 return false; 01486 } 01487 01488 if (button == QMessageBox::Yes) { 01489 realign = true; 01490 } 01491 } 01492 01493 RegionModel::EditCommand *command = 01494 new RegionModel::EditCommand(m_model, tr("Paste")); 01495 01496 for (Clipboard::PointList::const_iterator i = points.begin(); 01497 i != points.end(); ++i) { 01498 01499 if (!i->haveFrame()) continue; 01500 int frame = 0; 01501 01502 if (!realign) { 01503 01504 frame = i->getFrame(); 01505 01506 } else { 01507 01508 if (i->haveReferenceFrame()) { 01509 frame = i->getReferenceFrame(); 01510 frame = alignFromReference(v, frame); 01511 } else { 01512 frame = i->getFrame(); 01513 } 01514 } 01515 01516 RegionModel::Point newPoint(frame); 01517 01518 if (i->haveLabel()) newPoint.label = i->getLabel(); 01519 if (i->haveValue()) newPoint.value = i->getValue(); 01520 else newPoint.value = (m_model->getValueMinimum() + 01521 m_model->getValueMaximum()) / 2; 01522 if (i->haveDuration()) newPoint.duration = i->getDuration(); 01523 else { 01524 int nextFrame = frame; 01525 Clipboard::PointList::const_iterator j = i; 01526 for (; j != points.end(); ++j) { 01527 if (!j->haveFrame()) continue; 01528 if (j != i) break; 01529 } 01530 if (j != points.end()) { 01531 nextFrame = j->getFrame(); 01532 } 01533 if (nextFrame == frame) { 01534 newPoint.duration = m_model->getResolution(); 01535 } else { 01536 newPoint.duration = nextFrame - frame; 01537 } 01538 } 01539 01540 command->addPoint(newPoint); 01541 } 01542 01543 finish(command); 01544 recalcSpacing(); 01545 return true; 01546 } 01547 01548 void 01549 RegionLayer::toXml(QTextStream &stream, 01550 QString indent, QString extraAttributes) const 01551 { 01552 SingleColourLayer::toXml(stream, indent, extraAttributes + 01553 QString(" verticalScale=\"%1\" plotStyle=\"%2\"") 01554 .arg(m_verticalScale) 01555 .arg(m_plotStyle)); 01556 } 01557 01558 void 01559 RegionLayer::setProperties(const QXmlAttributes &attributes) 01560 { 01561 SingleColourLayer::setProperties(attributes); 01562 01563 bool ok; 01564 VerticalScale scale = (VerticalScale) 01565 attributes.value("verticalScale").toInt(&ok); 01566 if (ok) setVerticalScale(scale); 01567 PlotStyle style = (PlotStyle) 01568 attributes.value("plotStyle").toInt(&ok); 01569 if (ok) setPlotStyle(style); 01570 } 01571 01572