svgui  1.9
RegionLayer.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006-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