Chris@411: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@411: 
Chris@411: /*
Chris@411:     Sonic Visualiser
Chris@411:     An audio file viewer and annotation editor.
Chris@411:     Centre for Digital Music, Queen Mary, University of London.
Chris@411:     This file copyright 2006-2008 Chris Cannam and QMUL.
Chris@411:     
Chris@411:     This program is free software; you can redistribute it and/or
Chris@411:     modify it under the terms of the GNU General Public License as
Chris@411:     published by the Free Software Foundation; either version 2 of the
Chris@411:     License, or (at your option) any later version.  See the file
Chris@411:     COPYING included with this distribution for more information.
Chris@411: */
Chris@411: 
Chris@411: #include "RegionLayer.h"
Chris@411: 
Chris@411: #include "data/model/Model.h"
Chris@411: #include "base/RealTime.h"
Chris@411: #include "base/Profiler.h"
Chris@411: #include "base/LogRange.h"
Chris@411: #include "ColourDatabase.h"
Chris@427: #include "ColourMapper.h"
Chris@411: #include "view/View.h"
Chris@411: 
Chris@411: #include "data/model/RegionModel.h"
Chris@411: 
Chris@411: #include "widgets/ItemEditDialog.h"
Chris@411: 
Chris@411: #include <QPainter>
Chris@411: #include <QPainterPath>
Chris@411: #include <QMouseEvent>
Chris@411: #include <QTextStream>
Chris@411: #include <QMessageBox>
Chris@411: 
Chris@411: #include <iostream>
Chris@411: #include <cmath>
Chris@411: 
Chris@411: RegionLayer::RegionLayer() :
Chris@411:     SingleColourLayer(),
Chris@411:     m_model(0),
Chris@411:     m_editing(false),
Chris@411:     m_originalPoint(0, 0.0, 0, tr("New Point")),
Chris@411:     m_editingPoint(0, 0.0, 0, tr("New Point")),
Chris@411:     m_editingCommand(0),
Chris@433:     m_verticalScale(EqualSpaced),
Chris@427:     m_colourMap(0),
Chris@412:     m_plotStyle(PlotLines)
Chris@411: {
Chris@411:     
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::setModel(RegionModel *model)
Chris@411: {
Chris@411:     if (m_model == model) return;
Chris@411:     m_model = model;
Chris@411: 
Chris@411:     connectSignals(m_model);
Chris@411: 
Chris@433:     connect(m_model, SIGNAL(modelChanged()), this, SLOT(recalcSpacing()));
Chris@433:     recalcSpacing();
Chris@433: 
Chris@411: //    std::cerr << "RegionLayer::setModel(" << model << ")" << std::endl;
Chris@411: 
Chris@411:     emit modelReplaced();
Chris@411: }
Chris@411: 
Chris@411: Layer::PropertyList
Chris@411: RegionLayer::getProperties() const
Chris@411: {
Chris@411:     PropertyList list = SingleColourLayer::getProperties();
Chris@411:     list.push_back("Vertical Scale");
Chris@411:     list.push_back("Scale Units");
Chris@412:     list.push_back("Plot Type");
Chris@411:     return list;
Chris@411: }
Chris@411: 
Chris@411: QString
Chris@411: RegionLayer::getPropertyLabel(const PropertyName &name) const
Chris@411: {
Chris@411:     if (name == "Vertical Scale") return tr("Vertical Scale");
Chris@411:     if (name == "Scale Units") return tr("Scale Units");
Chris@412:     if (name == "Plot Type") return tr("Plot Type");
Chris@411:     return SingleColourLayer::getPropertyLabel(name);
Chris@411: }
Chris@411: 
Chris@411: Layer::PropertyType
Chris@411: RegionLayer::getPropertyType(const PropertyName &name) const
Chris@411: {
Chris@411:     if (name == "Scale Units") return UnitsProperty;
Chris@411:     if (name == "Vertical Scale") return ValueProperty;
Chris@412:     if (name == "Plot Type") return ValueProperty;
Chris@427:     if (name == "Colour" && m_plotStyle == PlotSegmentation) return ValueProperty;
Chris@411:     return SingleColourLayer::getPropertyType(name);
Chris@411: }
Chris@411: 
Chris@411: QString
Chris@411: RegionLayer::getPropertyGroupName(const PropertyName &name) const
Chris@411: {
Chris@411:     if (name == "Vertical Scale" || name == "Scale Units") {
Chris@411:         return tr("Scale");
Chris@411:     }
Chris@411:     return SingleColourLayer::getPropertyGroupName(name);
Chris@411: }
Chris@411: 
Chris@411: int
Chris@411: RegionLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@411:                                     int *min, int *max, int *deflt) const
Chris@411: {
Chris@411:     int val = 0;
Chris@411: 
Chris@427:     if (name == "Colour" && m_plotStyle == PlotSegmentation) {
Chris@427:             
Chris@427:         if (min) *min = 0;
Chris@427:         if (max) *max = ColourMapper::getColourMapCount() - 1;
Chris@427:         if (deflt) *deflt = 0;
Chris@427:         
Chris@427:         val = m_colourMap;
Chris@427: 
Chris@427:     } else if (name == "Plot Type") {
Chris@412: 	
Chris@412: 	if (min) *min = 0;
Chris@412: 	if (max) *max = 1;
Chris@412:         if (deflt) *deflt = 0;
Chris@412: 	
Chris@412: 	val = int(m_plotStyle);
Chris@412: 
Chris@412:     } else if (name == "Vertical Scale") {
Chris@411: 	
Chris@411: 	if (min) *min = 0;
Chris@411: 	if (max) *max = 3;
Chris@433:         if (deflt) *deflt = int(EqualSpaced);
Chris@411: 	
Chris@411: 	val = int(m_verticalScale);
Chris@411: 
Chris@411:     } else if (name == "Scale Units") {
Chris@411: 
Chris@411:         if (deflt) *deflt = 0;
Chris@411:         if (m_model) {
Chris@411:             val = UnitDatabase::getInstance()->getUnitId
Chris@411:                 (m_model->getScaleUnits());
Chris@411:         }
Chris@411: 
Chris@411:     } else {
Chris@411: 
Chris@411: 	val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@411:     }
Chris@411: 
Chris@411:     return val;
Chris@411: }
Chris@411: 
Chris@411: QString
Chris@411: RegionLayer::getPropertyValueLabel(const PropertyName &name,
Chris@427:                                    int value) const
Chris@411: {
Chris@427:     if (name == "Colour" && m_plotStyle == PlotSegmentation) {
Chris@427:         return ColourMapper::getColourMapName(value);
Chris@427:     } else if (name == "Plot Type") {
Chris@412: 
Chris@412: 	switch (value) {
Chris@412: 	default:
Chris@427: 	case 0: return tr("Bars");
Chris@412: 	case 1: return tr("Segmentation");
Chris@412: 	}
Chris@412: 
Chris@412:     } else if (name == "Vertical Scale") {
Chris@411: 	switch (value) {
Chris@411: 	default:
Chris@411: 	case 0: return tr("Auto-Align");
Chris@433: 	case 1: return tr("Equal Spaced");
Chris@433: 	case 2: return tr("Linear");
Chris@433: 	case 3: return tr("Log");
Chris@411: 	}
Chris@411:     }
Chris@411:     return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::setProperty(const PropertyName &name, int value)
Chris@411: {
Chris@427:     if (name == "Colour" && m_plotStyle == PlotSegmentation) {
Chris@427:         setFillColourMap(value);
Chris@427:     } else if (name == "Plot Type") {
Chris@412: 	setPlotStyle(PlotStyle(value));
Chris@412:     } else if (name == "Vertical Scale") {
Chris@411: 	setVerticalScale(VerticalScale(value));
Chris@411:     } else if (name == "Scale Units") {
Chris@411:         if (m_model) {
Chris@411:             m_model->setScaleUnits
Chris@411:                 (UnitDatabase::getInstance()->getUnitById(value));
Chris@411:             emit modelChanged();
Chris@411:         }
Chris@411:     } else {
Chris@411:         return SingleColourLayer::setProperty(name, value);
Chris@411:     }
Chris@411: }
Chris@411: 
Chris@411: void
Chris@427: RegionLayer::setFillColourMap(int map)
Chris@427: {
Chris@427:     if (m_colourMap == map) return;
Chris@427:     m_colourMap = map;
Chris@427:     emit layerParametersChanged();
Chris@427: }
Chris@427: 
Chris@427: void
Chris@412: RegionLayer::setPlotStyle(PlotStyle style)
Chris@412: {
Chris@412:     if (m_plotStyle == style) return;
Chris@427:     bool colourTypeChanged = (style == PlotSegmentation ||
Chris@427:                               m_plotStyle == PlotSegmentation);
Chris@412:     m_plotStyle = style;
Chris@427:     if (colourTypeChanged) {
Chris@427:         emit layerParameterRangesChanged();
Chris@427:     }
Chris@412:     emit layerParametersChanged();
Chris@412: }
Chris@412: 
Chris@412: void
Chris@411: RegionLayer::setVerticalScale(VerticalScale scale)
Chris@411: {
Chris@411:     if (m_verticalScale == scale) return;
Chris@411:     m_verticalScale = scale;
Chris@411:     emit layerParametersChanged();
Chris@411: }
Chris@411: 
Chris@411: bool
Chris@411: RegionLayer::isLayerScrollable(const View *v) const
Chris@411: {
Chris@411:     QPoint discard;
Chris@411:     return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@411: }
Chris@411: 
Chris@433: void
Chris@433: RegionLayer::recalcSpacing()
Chris@433: {
Chris@433:     m_spacingMap.clear();
Chris@433:     if (!m_model) return;
Chris@433: 
Chris@433:     std::set<float> values;
Chris@433: 
Chris@433:     for (RegionModel::PointList::const_iterator i = m_model->getPoints().begin();
Chris@433:          i != m_model->getPoints().end(); ++i) {
Chris@433:         values.insert(i->value);
Chris@433:     }
Chris@433: 
Chris@433:     int n = 0;
Chris@433: 
Chris@433:     for (std::set<float>::const_iterator i = values.begin();
Chris@433:          i != values.end(); ++i) {
Chris@433:         m_spacingMap[*i] = n++;
Chris@433:     }
Chris@433: }
Chris@433: 
Chris@411: bool
Chris@411: RegionLayer::getValueExtents(float &min, float &max,
Chris@411:                            bool &logarithmic, QString &unit) const
Chris@411: {
Chris@411:     if (!m_model) return false;
Chris@411:     min = m_model->getValueMinimum();
Chris@411:     max = m_model->getValueMaximum();
Chris@411:     unit = m_model->getScaleUnits();
Chris@411: 
Chris@411:     if (m_verticalScale == LogScale) logarithmic = true;
Chris@411: 
Chris@411:     return true;
Chris@411: }
Chris@411: 
Chris@411: bool
Chris@411: RegionLayer::getDisplayExtents(float &min, float &max) const
Chris@411: {
Chris@433:     if (!m_model ||
Chris@433:         m_verticalScale == AutoAlignScale ||
Chris@433:         m_verticalScale == EqualSpaced) return false;
Chris@411: 
Chris@411:     min = m_model->getValueMinimum();
Chris@411:     max = m_model->getValueMaximum();
Chris@411: 
Chris@411:     return true;
Chris@411: }
Chris@411: 
Chris@411: RegionModel::PointList
Chris@411: RegionLayer::getLocalPoints(View *v, int x) const
Chris@411: {
Chris@411:     if (!m_model) return RegionModel::PointList();
Chris@411: 
Chris@411:     long frame = v->getFrameForX(x);
Chris@411: 
Chris@411:     RegionModel::PointList onPoints =
Chris@411: 	m_model->getPoints(frame);
Chris@411: 
Chris@411:     if (!onPoints.empty()) {
Chris@411: 	return onPoints;
Chris@411:     }
Chris@411: 
Chris@411:     RegionModel::PointList prevPoints =
Chris@411: 	m_model->getPreviousPoints(frame);
Chris@411:     RegionModel::PointList nextPoints =
Chris@411: 	m_model->getNextPoints(frame);
Chris@411: 
Chris@411:     RegionModel::PointList usePoints = prevPoints;
Chris@411: 
Chris@411:     if (prevPoints.empty()) {
Chris@411: 	usePoints = nextPoints;
Chris@411:     } else if (long(prevPoints.begin()->frame) < v->getStartFrame() &&
Chris@411: 	       !(nextPoints.begin()->frame > v->getEndFrame())) {
Chris@411: 	usePoints = nextPoints;
Chris@411:     } else if (long(nextPoints.begin()->frame) - frame <
Chris@411: 	       frame - long(prevPoints.begin()->frame)) {
Chris@411: 	usePoints = nextPoints;
Chris@411:     }
Chris@411: 
Chris@411:     if (!usePoints.empty()) {
Chris@411: 	int fuzz = 2;
Chris@411: 	int px = v->getXForFrame(usePoints.begin()->frame);
Chris@411: 	if ((px > x && px - x > fuzz) ||
Chris@411: 	    (px < x && x - px > fuzz + 1)) {
Chris@411: 	    usePoints.clear();
Chris@411: 	}
Chris@411:     }
Chris@411: 
Chris@411:     return usePoints;
Chris@411: }
Chris@411: 
Chris@411: QString
Chris@411: RegionLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@411: {
Chris@411:     int x = pos.x();
Chris@411: 
Chris@411:     if (!m_model || !m_model->getSampleRate()) return "";
Chris@411: 
Chris@411:     RegionModel::PointList points = getLocalPoints(v, x);
Chris@411: 
Chris@411:     if (points.empty()) {
Chris@411: 	if (!m_model->isReady()) {
Chris@411: 	    return tr("In progress");
Chris@411: 	} else {
Chris@411: 	    return tr("No local points");
Chris@411: 	}
Chris@411:     }
Chris@411: 
Chris@411:     RegionRec region(0);
Chris@411:     RegionModel::PointList::iterator i;
Chris@411: 
Chris@413:     //!!! harmonise with whatever decision is made about point y
Chris@413:     //!!! coords in paint method
Chris@413: 
Chris@411:     for (i = points.begin(); i != points.end(); ++i) {
Chris@411: 
Chris@411: 	int y = getYForValue(v, i->value);
Chris@411: 	int h = 3;
Chris@411: 
Chris@411: 	if (m_model->getValueQuantization() != 0.0) {
Chris@411: 	    h = y - getYForValue(v, i->value + m_model->getValueQuantization());
Chris@411: 	    if (h < 3) h = 3;
Chris@411: 	}
Chris@411: 
Chris@411: 	if (pos.y() >= y - h && pos.y() <= y) {
Chris@411: 	    region = *i;
Chris@411: 	    break;
Chris@411: 	}
Chris@411:     }
Chris@411: 
Chris@411:     if (i == points.end()) return tr("No local points");
Chris@411: 
Chris@411:     RealTime rt = RealTime::frame2RealTime(region.frame,
Chris@411: 					   m_model->getSampleRate());
Chris@411:     RealTime rd = RealTime::frame2RealTime(region.duration,
Chris@411: 					   m_model->getSampleRate());
Chris@411:     
Chris@411:     QString valueText;
Chris@411: 
Chris@411:     valueText = tr("%1 %2").arg(region.value).arg(m_model->getScaleUnits());
Chris@411: 
Chris@411:     QString text;
Chris@411: 
Chris@411:     if (region.label == "") {
Chris@411: 	text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label"))
Chris@411: 	    .arg(rt.toText(true).c_str())
Chris@411: 	    .arg(valueText)
Chris@411: 	    .arg(rd.toText(true).c_str());
Chris@411:     } else {
Chris@411: 	text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nLabel:\t%4"))
Chris@411: 	    .arg(rt.toText(true).c_str())
Chris@411: 	    .arg(valueText)
Chris@411: 	    .arg(rd.toText(true).c_str())
Chris@411: 	    .arg(region.label);
Chris@411:     }
Chris@411: 
Chris@411:     pos = QPoint(v->getXForFrame(region.frame),
Chris@411: 		 getYForValue(v, region.value));
Chris@411:     return text;
Chris@411: }
Chris@411: 
Chris@411: bool
Chris@411: RegionLayer::snapToFeatureFrame(View *v, int &frame,
Chris@414:                                 size_t &resolution,
Chris@414:                                 SnapType snap) const
Chris@411: {
Chris@411:     if (!m_model) {
Chris@411: 	return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@411:     }
Chris@411: 
Chris@411:     resolution = m_model->getResolution();
Chris@411:     RegionModel::PointList points;
Chris@411: 
Chris@411:     if (snap == SnapNeighbouring) {
Chris@411: 	
Chris@411: 	points = getLocalPoints(v, v->getXForFrame(frame));
Chris@411: 	if (points.empty()) return false;
Chris@411: 	frame = points.begin()->frame;
Chris@411: 	return true;
Chris@411:     }    
Chris@411: 
Chris@411:     points = m_model->getPoints(frame, frame);
Chris@411:     int snapped = frame;
Chris@411:     bool found = false;
Chris@411: 
Chris@411:     for (RegionModel::PointList::const_iterator i = points.begin();
Chris@411: 	 i != points.end(); ++i) {
Chris@411: 
Chris@411: 	if (snap == SnapRight) {
Chris@411: 
Chris@414:             // The best frame to snap to is the end frame of whichever
Chris@414:             // feature we would have snapped to the start frame of if
Chris@414:             // we had been snapping left.
Chris@414: 
Chris@414: 	    if (i->frame <= frame) {
Chris@414:                 if (i->frame + i->duration > frame) {
Chris@414:                     snapped = i->frame + i->duration;
Chris@414:                     found = true; // don't break, as the next may be better
Chris@414:                 }
Chris@414:             } else {
Chris@414:                 if (!found) {
Chris@414:                     snapped = i->frame;
Chris@414:                     found = true;
Chris@414:                 }
Chris@414:                 break;
Chris@414:             }
Chris@411: 
Chris@411: 	} else if (snap == SnapLeft) {
Chris@411: 
Chris@411: 	    if (i->frame <= frame) {
Chris@411: 		snapped = i->frame;
Chris@411: 		found = true; // don't break, as the next may be better
Chris@411: 	    } else {
Chris@411: 		break;
Chris@411: 	    }
Chris@411: 
Chris@411: 	} else { // nearest
Chris@411: 
Chris@411: 	    RegionModel::PointList::const_iterator j = i;
Chris@411: 	    ++j;
Chris@411: 
Chris@411: 	    if (j == points.end()) {
Chris@411: 
Chris@411: 		snapped = i->frame;
Chris@411: 		found = true;
Chris@411: 		break;
Chris@411: 
Chris@411: 	    } else if (j->frame >= frame) {
Chris@411: 
Chris@411: 		if (j->frame - frame < frame - i->frame) {
Chris@411: 		    snapped = j->frame;
Chris@411: 		} else {
Chris@411: 		    snapped = i->frame;
Chris@411: 		}
Chris@411: 		found = true;
Chris@411: 		break;
Chris@411: 	    }
Chris@411: 	}
Chris@411:     }
Chris@411: 
Chris@411:     frame = snapped;
Chris@411:     return found;
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
Chris@411: {
Chris@411:     min = 0.0;
Chris@411:     max = 0.0;
Chris@411:     log = false;
Chris@411: 
Chris@411:     QString queryUnits;
Chris@411:     queryUnits = m_model->getScaleUnits();
Chris@411: 
Chris@411:     if (m_verticalScale == AutoAlignScale) {
Chris@411: 
Chris@411:         if (!v->getValueExtents(queryUnits, min, max, log)) {
Chris@411: 
Chris@411:             min = m_model->getValueMinimum();
Chris@411:             max = m_model->getValueMaximum();
Chris@411: 
Chris@411:             std::cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@411: 
Chris@411:         } else if (log) {
Chris@411: 
Chris@411:             LogRange::mapRange(min, max);
Chris@411: 
Chris@411:             std::cerr << "RegionLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@411: 
Chris@411:         }
Chris@411: 
Chris@433:     } else if (m_verticalScale == EqualSpaced) {
Chris@433: 
Chris@433:         if (!m_spacingMap.empty()) {
Chris@433:             SpacingMap::const_iterator i = m_spacingMap.begin();
Chris@433:             min = i->second;
Chris@433:             i = m_spacingMap.end();
Chris@433:             --i;
Chris@433:             max = i->second;
Chris@433:             std::cerr << "RegionLayer[" << this << "]::getScaleExtents: equal spaced; min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@433:         }
Chris@433: 
Chris@411:     } else {
Chris@411: 
Chris@411:         min = m_model->getValueMinimum();
Chris@411:         max = m_model->getValueMaximum();
Chris@411: 
Chris@411:         if (m_verticalScale == LogScale) {
Chris@411:             LogRange::mapRange(min, max);
Chris@411:             log = true;
Chris@411:         }
Chris@411:     }
Chris@411: 
Chris@411:     if (max == min) max = min + 1.0;
Chris@411: }
Chris@411: 
Chris@411: int
Chris@411: RegionLayer::getYForValue(View *v, float val) const
Chris@411: {
Chris@411:     float min = 0.0, max = 0.0;
Chris@411:     bool logarithmic = false;
Chris@411:     int h = v->height();
Chris@411: 
Chris@433:     if (m_verticalScale == EqualSpaced) {
Chris@433: 
Chris@433:         if (m_spacingMap.empty()) return h/2;
Chris@433:         
Chris@433:         SpacingMap::const_iterator i = m_spacingMap.lower_bound(val);
Chris@433:         //!!! what now, if i->first != v?
Chris@433: 
Chris@433:         int vh = i->second;
Chris@433: 
Chris@433:         SpacingMap::const_iterator j = m_spacingMap.end();
Chris@433:         --j;
Chris@433: 
Chris@433:         return h - (((h * vh) / (j->second + 1)) + (h / (2 * (j->second + 1))));
Chris@433: 
Chris@433:     } else {
Chris@433: 
Chris@433:         getScaleExtents(v, min, max, logarithmic);
Chris@411: 
Chris@432: //    std::cerr << "RegionLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << std::endl;
Chris@432: //    std::cerr << "h = " << h << ", margin = " << margin << std::endl;
Chris@411: 
Chris@433:         if (logarithmic) {
Chris@433:             val = LogRange::map(val);
Chris@433:         }
Chris@433: 
Chris@433:         return int(h - ((val - min) * h) / (max - min));
Chris@411:     }
Chris@411: }
Chris@411: 
Chris@427: QColor
Chris@427: RegionLayer::getColourForValue(View *v, float val) const
Chris@427: {
Chris@427:     float min, max;
Chris@427:     bool log;
Chris@427:     getScaleExtents(v, min, max, log);
Chris@427: 
Chris@427:     if (min > max) std::swap(min, max);
Chris@427:     if (max == min) max = min + 1;
Chris@427: 
Chris@427:     if (log) {
Chris@427:         LogRange::mapRange(min, max);
Chris@427:         val = LogRange::map(val);
Chris@427:     }
Chris@427: 
Chris@427: //    std::cerr << "RegionLayer::getColourForValue: min " << min << ", max "
Chris@427: //              << max << ", log " << log << ", value " << val << std::endl;
Chris@427: 
Chris@427:     QColor solid = ColourMapper(m_colourMap, min, max).map(val);
Chris@427:     return QColor(solid.red(), solid.green(), solid.blue(), 120);
Chris@427: }
Chris@427: 
Chris@427: int
Chris@427: RegionLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@427: {
Chris@427:     impose = false;
Chris@427:     return ColourDatabase::getInstance()->getColourIndex
Chris@427:         (QString(darkbg ? "Bright Blue" : "Blue"));
Chris@427: }
Chris@427: 
Chris@411: float
Chris@411: RegionLayer::getValueForY(View *v, int y) const
Chris@411: {
Chris@411:     float min = 0.0, max = 0.0;
Chris@411:     bool logarithmic = false;
Chris@411:     int h = v->height();
Chris@411: 
Chris@411:     getScaleExtents(v, min, max, logarithmic);
Chris@411: 
Chris@411:     float val = min + (float(h - y) * float(max - min)) / h;
Chris@411: 
Chris@411:     if (logarithmic) {
Chris@411:         val = powf(10.f, val);
Chris@411:     }
Chris@411: 
Chris@411:     return val;
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@411: {
Chris@411:     if (!m_model || !m_model->isOK()) return;
Chris@411: 
Chris@411:     int sampleRate = m_model->getSampleRate();
Chris@411:     if (!sampleRate) return;
Chris@411: 
Chris@411: //    Profiler profiler("RegionLayer::paint", true);
Chris@411: 
Chris@411:     int x0 = rect.left(), x1 = rect.right();
Chris@411:     long frame0 = v->getFrameForX(x0);
Chris@411:     long frame1 = v->getFrameForX(x1);
Chris@411: 
Chris@411:     RegionModel::PointList points(m_model->getPoints(frame0, frame1));
Chris@411:     if (points.empty()) return;
Chris@411: 
Chris@411:     paint.setPen(getBaseQColor());
Chris@411: 
Chris@411:     QColor brushColour(getBaseQColor());
Chris@411:     brushColour.setAlpha(80);
Chris@411: 
Chris@411: //    std::cerr << "RegionLayer::paint: resolution is "
Chris@411: //	      << m_model->getResolution() << " frames" << std::endl;
Chris@411: 
Chris@411:     float min = m_model->getValueMinimum();
Chris@411:     float max = m_model->getValueMaximum();
Chris@411:     if (max == min) max = min + 1.0;
Chris@411: 
Chris@411:     QPoint localPos;
Chris@411:     long illuminateFrame = -1;
Chris@411: 
Chris@411:     if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@411: 	RegionModel::PointList localPoints =
Chris@411: 	    getLocalPoints(v, localPos.x());
Chris@411: 	if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@411:     }
Chris@411: 
Chris@411:     paint.save();
Chris@411:     paint.setRenderHint(QPainter::Antialiasing, false);
Chris@411:     
Chris@413:     //!!! point y coords if model does not haveDistinctValues() should
Chris@413:     //!!! be assigned to avoid overlaps
Chris@413: 
Chris@413:     //!!! if it does have distinct values, we should still ensure y
Chris@413:     //!!! coord is never completely flat on the top or bottom
Chris@413: 
Chris@427:     int textY = 0;
Chris@427:     if (m_plotStyle == PlotSegmentation) {
Chris@427:         textY = v->getTextLabelHeight(this, paint);
Chris@427:     }
Chris@427:     
Chris@411:     for (RegionModel::PointList::const_iterator i = points.begin();
Chris@411: 	 i != points.end(); ++i) {
Chris@411: 
Chris@411: 	const RegionModel::Point &p(*i);
Chris@411: 
Chris@411: 	int x = v->getXForFrame(p.frame);
Chris@411: 	int y = getYForValue(v, p.value);
Chris@411: 	int w = v->getXForFrame(p.frame + p.duration) - x;
Chris@413: 	int h = 9;
Chris@433: 	int ex = x + w;
Chris@427: 
Chris@427:         RegionModel::PointList::const_iterator j = i;
Chris@427: 	++j;
Chris@427: 
Chris@427: 	if (j != points.end()) {
Chris@427: 	    const RegionModel::Point &q(*j);
Chris@433: 	    int nx = v->getXForFrame(q.frame);
Chris@433:             if (nx < ex) ex = nx;
Chris@427:         }
Chris@427: 
Chris@427:         if (m_plotStyle != PlotSegmentation) {
Chris@427:             textY = y - paint.fontMetrics().height()
Chris@427:                       + paint.fontMetrics().ascent();
Chris@427:             if (textY < paint.fontMetrics().ascent() + 1) {
Chris@427:                 textY = paint.fontMetrics().ascent() + 1;
Chris@427:             }
Chris@427:         }
Chris@427: 
Chris@411: 	if (m_model->getValueQuantization() != 0.0) {
Chris@411: 	    h = y - getYForValue(v, p.value + m_model->getValueQuantization());
Chris@411: 	    if (h < 3) h = 3;
Chris@411: 	}
Chris@411: 
Chris@411: 	if (w < 1) w = 1;
Chris@411: 
Chris@427: 	if (m_plotStyle == PlotSegmentation) {
Chris@427:             paint.setPen(getForegroundQColor(v));
Chris@427:             paint.setBrush(getColourForValue(v, p.value));
Chris@427:         } else {
Chris@427:             paint.setPen(getBaseQColor());
Chris@427:             paint.setBrush(brushColour);
Chris@427:         }
Chris@427: 
Chris@427: 	if (m_plotStyle == PlotSegmentation) {
Chris@427: 
Chris@433: 	    if (ex <= x) continue;
Chris@427: 
Chris@427: 	    if (illuminateFrame != p.frame &&
Chris@433: 		(ex < x + 5 || x >= v->width() - 1)) {
Chris@427: 		paint.setPen(Qt::NoPen);
Chris@411: 	    }
Chris@427: 
Chris@433: 	    paint.drawRect(x, -1, ex - x, v->height() + 1);
Chris@427: 
Chris@427: 	} else {
Chris@427: 
Chris@427:             if (illuminateFrame == p.frame) {
Chris@427:                 if (localPos.y() >= y - h && localPos.y() < y) {
Chris@427:                     paint.setPen(v->getForeground());
Chris@427:                     paint.setBrush(v->getForeground());
Chris@427:                 }
Chris@427:             }
Chris@427: 	
Chris@427:             paint.drawLine(x, y-1, x + w, y-1);
Chris@427:             paint.drawLine(x, y+1, x + w, y+1);
Chris@427:             paint.drawLine(x, y - h/2, x, y + h/2);
Chris@427:             paint.drawLine(x+w, y - h/2, x + w, y + h/2);
Chris@427:         }
Chris@427: 
Chris@427: 	if (p.label != "") {
Chris@433: //            if (ex > x + 6 + paint.fontMetrics().width(p.label)) {
Chris@427:                 paint.drawText(x + 5, textY, p.label);
Chris@433: //            }
Chris@411: 	}
Chris@411:     }
Chris@411: 
Chris@411:     paint.restore();
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::drawStart(View *v, QMouseEvent *e)
Chris@411: {
Chris@411: //    std::cerr << "RegionLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411: 
Chris@411:     if (!m_model) return;
Chris@411: 
Chris@411:     long frame = v->getFrameForX(e->x());
Chris@411:     if (frame < 0) frame = 0;
Chris@411:     frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@411: 
Chris@411:     float value = getValueForY(v, e->y());
Chris@411: 
Chris@411:     m_editingPoint = RegionModel::Point(frame, value, 0, tr("New Point"));
Chris@411:     m_originalPoint = m_editingPoint;
Chris@411: 
Chris@411:     if (m_editingCommand) finish(m_editingCommand);
Chris@411:     m_editingCommand = new RegionModel::EditCommand(m_model,
Chris@411:                                                     tr("Draw Point"));
Chris@411:     m_editingCommand->addPoint(m_editingPoint);
Chris@411: 
Chris@411:     m_editing = true;
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::drawDrag(View *v, QMouseEvent *e)
Chris@411: {
Chris@411: //    std::cerr << "RegionLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411: 
Chris@411:     if (!m_model || !m_editing) return;
Chris@411: 
Chris@411:     long frame = v->getFrameForX(e->x());
Chris@411:     if (frame < 0) frame = 0;
Chris@411:     frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@411: 
Chris@411:     float newValue = getValueForY(v, e->y());
Chris@411: 
Chris@411:     long newFrame = m_editingPoint.frame;
Chris@411:     long newDuration = frame - newFrame;
Chris@411:     if (newDuration < 0) {
Chris@411:         newFrame = frame;
Chris@411:         newDuration = -newDuration;
Chris@411:     } else if (newDuration == 0) {
Chris@411:         newDuration = 1;
Chris@411:     }
Chris@411: 
Chris@411:     m_editingCommand->deletePoint(m_editingPoint);
Chris@411:     m_editingPoint.frame = newFrame;
Chris@411:     m_editingPoint.value = newValue;
Chris@411:     m_editingPoint.duration = newDuration;
Chris@411:     m_editingCommand->addPoint(m_editingPoint);
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::drawEnd(View *, QMouseEvent *)
Chris@411: {
Chris@411: //    std::cerr << "RegionLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411:     if (!m_model || !m_editing) return;
Chris@411:     finish(m_editingCommand);
Chris@411:     m_editingCommand = 0;
Chris@411:     m_editing = false;
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::eraseStart(View *v, QMouseEvent *e)
Chris@411: {
Chris@411:     if (!m_model) return;
Chris@411: 
Chris@411:     RegionModel::PointList points = getLocalPoints(v, e->x());
Chris@411:     if (points.empty()) return;
Chris@411: 
Chris@411:     m_editingPoint = *points.begin();
Chris@411: 
Chris@411:     if (m_editingCommand) {
Chris@411: 	finish(m_editingCommand);
Chris@411: 	m_editingCommand = 0;
Chris@411:     }
Chris@411: 
Chris@411:     m_editing = true;
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::eraseDrag(View *v, QMouseEvent *e)
Chris@411: {
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::eraseEnd(View *v, QMouseEvent *e)
Chris@411: {
Chris@411:     if (!m_model || !m_editing) return;
Chris@411: 
Chris@411:     m_editing = false;
Chris@411: 
Chris@411:     RegionModel::PointList points = getLocalPoints(v, e->x());
Chris@411:     if (points.empty()) return;
Chris@411:     if (points.begin()->frame != m_editingPoint.frame ||
Chris@411:         points.begin()->value != m_editingPoint.value) return;
Chris@411: 
Chris@411:     m_editingCommand = new RegionModel::EditCommand
Chris@411:         (m_model, tr("Erase Point"));
Chris@411: 
Chris@411:     m_editingCommand->deletePoint(m_editingPoint);
Chris@411: 
Chris@411:     finish(m_editingCommand);
Chris@411:     m_editingCommand = 0;
Chris@411:     m_editing = false;
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::editStart(View *v, QMouseEvent *e)
Chris@411: {
Chris@411: //    std::cerr << "RegionLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411: 
Chris@411:     if (!m_model) return;
Chris@411: 
Chris@411:     RegionModel::PointList points = getLocalPoints(v, e->x());
Chris@411:     if (points.empty()) return;
Chris@411: 
Chris@411:     m_editingPoint = *points.begin();
Chris@411:     m_originalPoint = m_editingPoint;
Chris@411: 
Chris@411:     if (m_editingCommand) {
Chris@411: 	finish(m_editingCommand);
Chris@411: 	m_editingCommand = 0;
Chris@411:     }
Chris@411: 
Chris@411:     m_editing = true;
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::editDrag(View *v, QMouseEvent *e)
Chris@411: {
Chris@411: //    std::cerr << "RegionLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411: 
Chris@411:     if (!m_model || !m_editing) return;
Chris@411: 
Chris@411:     long frame = v->getFrameForX(e->x());
Chris@411:     if (frame < 0) frame = 0;
Chris@411:     frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@411: 
Chris@411:     float value = getValueForY(v, e->y());
Chris@411: 
Chris@411:     if (!m_editingCommand) {
Chris@411: 	m_editingCommand = new RegionModel::EditCommand(m_model,
Chris@411: 						      tr("Drag Point"));
Chris@411:     }
Chris@411: 
Chris@411:     m_editingCommand->deletePoint(m_editingPoint);
Chris@411:     m_editingPoint.frame = frame;
Chris@411:     m_editingPoint.value = value;
Chris@411:     m_editingCommand->addPoint(m_editingPoint);
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::editEnd(View *, QMouseEvent *)
Chris@411: {
Chris@411: //    std::cerr << "RegionLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@411:     if (!m_model || !m_editing) return;
Chris@411: 
Chris@411:     if (m_editingCommand) {
Chris@411: 
Chris@411: 	QString newName = m_editingCommand->getName();
Chris@411: 
Chris@411: 	if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@411: 	    if (m_editingPoint.value != m_originalPoint.value) {
Chris@411: 		newName = tr("Edit Point");
Chris@411: 	    } else {
Chris@411: 		newName = tr("Relocate Point");
Chris@411: 	    }
Chris@411: 	} else {
Chris@411: 	    newName = tr("Change Point Value");
Chris@411: 	}
Chris@411: 
Chris@411: 	m_editingCommand->setName(newName);
Chris@411: 	finish(m_editingCommand);
Chris@411:     }
Chris@411: 
Chris@411:     m_editingCommand = 0;
Chris@411:     m_editing = false;
Chris@411: }
Chris@411: 
Chris@411: bool
Chris@411: RegionLayer::editOpen(View *v, QMouseEvent *e)
Chris@411: {
Chris@411:     if (!m_model) return false;
Chris@411: 
Chris@411:     RegionModel::PointList points = getLocalPoints(v, e->x());
Chris@411:     if (points.empty()) return false;
Chris@411: 
Chris@411:     RegionModel::Point region = *points.begin();
Chris@411: 
Chris@411:     ItemEditDialog *dialog = new ItemEditDialog
Chris@411:         (m_model->getSampleRate(),
Chris@411:          ItemEditDialog::ShowTime |
Chris@411:          ItemEditDialog::ShowDuration |
Chris@411:          ItemEditDialog::ShowValue |
Chris@411:          ItemEditDialog::ShowText,
Chris@411:          m_model->getScaleUnits());
Chris@411: 
Chris@411:     dialog->setFrameTime(region.frame);
Chris@411:     dialog->setValue(region.value);
Chris@411:     dialog->setFrameDuration(region.duration);
Chris@411:     dialog->setText(region.label);
Chris@411: 
Chris@411:     if (dialog->exec() == QDialog::Accepted) {
Chris@411: 
Chris@411:         RegionModel::Point newRegion = region;
Chris@411:         newRegion.frame = dialog->getFrameTime();
Chris@411:         newRegion.value = dialog->getValue();
Chris@411:         newRegion.duration = dialog->getFrameDuration();
Chris@411:         newRegion.label = dialog->getText();
Chris@411:         
Chris@411:         RegionModel::EditCommand *command = new RegionModel::EditCommand
Chris@411:             (m_model, tr("Edit Point"));
Chris@411:         command->deletePoint(region);
Chris@411:         command->addPoint(newRegion);
Chris@411:         finish(command);
Chris@411:     }
Chris@411: 
Chris@411:     delete dialog;
Chris@411:     return true;
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@411: {
Chris@411:     if (!m_model) return;
Chris@411: 
Chris@411:     RegionModel::EditCommand *command =
Chris@411: 	new RegionModel::EditCommand(m_model, tr("Drag Selection"));
Chris@411: 
Chris@411:     RegionModel::PointList points =
Chris@411: 	m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411: 
Chris@411:     for (RegionModel::PointList::iterator i = points.begin();
Chris@411: 	 i != points.end(); ++i) {
Chris@411: 
Chris@411: 	if (s.contains(i->frame)) {
Chris@411: 	    RegionModel::Point newPoint(*i);
Chris@411: 	    newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@411: 	    command->deletePoint(*i);
Chris@411: 	    command->addPoint(newPoint);
Chris@411: 	}
Chris@411:     }
Chris@411: 
Chris@411:     finish(command);
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::resizeSelection(Selection s, Selection newSize)
Chris@411: {
Chris@411:     if (!m_model) return;
Chris@411: 
Chris@411:     RegionModel::EditCommand *command =
Chris@411: 	new RegionModel::EditCommand(m_model, tr("Resize Selection"));
Chris@411: 
Chris@411:     RegionModel::PointList points =
Chris@411: 	m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411: 
Chris@411:     double ratio =
Chris@411: 	double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@411: 	double(s.getEndFrame() - s.getStartFrame());
Chris@411: 
Chris@411:     for (RegionModel::PointList::iterator i = points.begin();
Chris@411: 	 i != points.end(); ++i) {
Chris@411: 
Chris@411: 	if (s.contains(i->frame)) {
Chris@411: 
Chris@411: 	    double targetStart = i->frame;
Chris@411: 	    targetStart = newSize.getStartFrame() + 
Chris@411: 		double(targetStart - s.getStartFrame()) * ratio;
Chris@411: 
Chris@411: 	    double targetEnd = i->frame + i->duration;
Chris@411: 	    targetEnd = newSize.getStartFrame() +
Chris@411: 		double(targetEnd - s.getStartFrame()) * ratio;
Chris@411: 
Chris@411: 	    RegionModel::Point newPoint(*i);
Chris@411: 	    newPoint.frame = lrint(targetStart);
Chris@411: 	    newPoint.duration = lrint(targetEnd - targetStart);
Chris@411: 	    command->deletePoint(*i);
Chris@411: 	    command->addPoint(newPoint);
Chris@411: 	}
Chris@411:     }
Chris@411: 
Chris@411:     finish(command);
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::deleteSelection(Selection s)
Chris@411: {
Chris@411:     if (!m_model) return;
Chris@411: 
Chris@411:     RegionModel::EditCommand *command =
Chris@411: 	new RegionModel::EditCommand(m_model, tr("Delete Selected Points"));
Chris@411: 
Chris@411:     RegionModel::PointList points =
Chris@411: 	m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411: 
Chris@411:     for (RegionModel::PointList::iterator i = points.begin();
Chris@411: 	 i != points.end(); ++i) {
Chris@411: 
Chris@411:         if (s.contains(i->frame)) {
Chris@411:             command->deletePoint(*i);
Chris@411:         }
Chris@411:     }
Chris@411: 
Chris@411:     finish(command);
Chris@411: }    
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::copy(View *v, Selection s, Clipboard &to)
Chris@411: {
Chris@411:     if (!m_model) return;
Chris@411: 
Chris@411:     RegionModel::PointList points =
Chris@411: 	m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@411: 
Chris@411:     for (RegionModel::PointList::iterator i = points.begin();
Chris@411: 	 i != points.end(); ++i) {
Chris@411: 	if (s.contains(i->frame)) {
Chris@411:             Clipboard::Point point(i->frame, i->value, i->duration, i->label);
Chris@411:             point.setReferenceFrame(alignToReference(v, i->frame));
Chris@411:             to.addPoint(point);
Chris@411:         }
Chris@411:     }
Chris@411: }
Chris@411: 
Chris@411: bool
Chris@411: RegionLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */)
Chris@411: {
Chris@411:     if (!m_model) return false;
Chris@411: 
Chris@411:     const Clipboard::PointList &points = from.getPoints();
Chris@411: 
Chris@411:     bool realign = false;
Chris@411: 
Chris@411:     if (clipboardHasDifferentAlignment(v, from)) {
Chris@411: 
Chris@411:         QMessageBox::StandardButton button =
Chris@411:             QMessageBox::question(v, tr("Re-align pasted items?"),
Chris@411:                                   tr("The items you are pasting came from a layer with different source material from this one.  Do you want to re-align them in time, to match the source material for this layer?"),
Chris@411:                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@411:                                   QMessageBox::Yes);
Chris@411: 
Chris@411:         if (button == QMessageBox::Cancel) {
Chris@411:             return false;
Chris@411:         }
Chris@411: 
Chris@411:         if (button == QMessageBox::Yes) {
Chris@411:             realign = true;
Chris@411:         }
Chris@411:     }
Chris@411: 
Chris@411:     RegionModel::EditCommand *command =
Chris@411: 	new RegionModel::EditCommand(m_model, tr("Paste"));
Chris@411: 
Chris@411:     for (Clipboard::PointList::const_iterator i = points.begin();
Chris@411:          i != points.end(); ++i) {
Chris@411:         
Chris@411:         if (!i->haveFrame()) continue;
Chris@411:         size_t frame = 0;
Chris@411: 
Chris@411:         if (!realign) {
Chris@411:             
Chris@411:             frame = i->getFrame();
Chris@411: 
Chris@411:         } else {
Chris@411: 
Chris@411:             if (i->haveReferenceFrame()) {
Chris@411:                 frame = i->getReferenceFrame();
Chris@411:                 frame = alignFromReference(v, frame);
Chris@411:             } else {
Chris@411:                 frame = i->getFrame();
Chris@411:             }
Chris@411:         }
Chris@411: 
Chris@411:         RegionModel::Point newPoint(frame);
Chris@411:   
Chris@411:         if (i->haveLabel()) newPoint.label = i->getLabel();
Chris@411:         if (i->haveValue()) newPoint.value = i->getValue();
Chris@411:         else newPoint.value = (m_model->getValueMinimum() +
Chris@411:                                m_model->getValueMaximum()) / 2;
Chris@411:         if (i->haveDuration()) newPoint.duration = i->getDuration();
Chris@411:         else {
Chris@411:             size_t nextFrame = frame;
Chris@411:             Clipboard::PointList::const_iterator j = i;
Chris@411:             for (; j != points.end(); ++j) {
Chris@411:                 if (!j->haveFrame()) continue;
Chris@411:                 if (j != i) break;
Chris@411:             }
Chris@411:             if (j != points.end()) {
Chris@411:                 nextFrame = j->getFrame();
Chris@411:             }
Chris@411:             if (nextFrame == frame) {
Chris@411:                 newPoint.duration = m_model->getResolution();
Chris@411:             } else {
Chris@411:                 newPoint.duration = nextFrame - frame;
Chris@411:             }
Chris@411:         }
Chris@411:         
Chris@411:         command->addPoint(newPoint);
Chris@411:     }
Chris@411: 
Chris@411:     finish(command);
Chris@411:     return true;
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::toXml(QTextStream &stream,
Chris@411:                  QString indent, QString extraAttributes) const
Chris@411: {
Chris@411:     SingleColourLayer::toXml(stream, indent, extraAttributes +
Chris@412:                              QString(" verticalScale=\"%1\" plotStyle=\"%2\"")
Chris@412:                              .arg(m_verticalScale)
Chris@412:                              .arg(m_plotStyle));
Chris@411: }
Chris@411: 
Chris@411: void
Chris@411: RegionLayer::setProperties(const QXmlAttributes &attributes)
Chris@411: {
Chris@411:     SingleColourLayer::setProperties(attributes);
Chris@411: 
Chris@411:     bool ok;
Chris@411:     VerticalScale scale = (VerticalScale)
Chris@411: 	attributes.value("verticalScale").toInt(&ok);
Chris@411:     if (ok) setVerticalScale(scale);
Chris@412:     PlotStyle style = (PlotStyle)
Chris@412: 	attributes.value("plotStyle").toInt(&ok);
Chris@412:     if (ok) setPlotStyle(style);
Chris@411: }
Chris@411: 
Chris@411: