Chris@58: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@0: 
Chris@0: /*
Chris@59:     Sonic Visualiser
Chris@59:     An audio file viewer and annotation editor.
Chris@59:     Centre for Digital Music, Queen Mary, University of London.
Chris@59:     This file copyright 2006 Chris Cannam.
Chris@0:     
Chris@59:     This program is free software; you can redistribute it and/or
Chris@59:     modify it under the terms of the GNU General Public License as
Chris@59:     published by the Free Software Foundation; either version 2 of the
Chris@59:     License, or (at your option) any later version.  See the file
Chris@59:     COPYING included with this distribution for more information.
Chris@0: */
Chris@0: 
Chris@0: #include "TimeInstantLayer.h"
Chris@0: 
Chris@128: #include "data/model/Model.h"
Chris@0: #include "base/RealTime.h"
Chris@128: #include "view/View.h"
Chris@0: #include "base/Profiler.h"
Chris@76: #include "base/Clipboard.h"
Chris@0: 
Chris@128: #include "data/model/SparseOneDimensionalModel.h"
Chris@0: 
Chris@70: #include "widgets/ItemEditDialog.h"
Chris@70: 
Chris@0: #include <QPainter>
Chris@17: #include <QMouseEvent>
Chris@0: 
Chris@0: #include <iostream>
Martin@46: #include <cmath>
Chris@0: 
Chris@44: TimeInstantLayer::TimeInstantLayer() :
Chris@44:     Layer(),
Chris@0:     m_model(0),
Chris@18:     m_editing(false),
Chris@17:     m_editingPoint(0, tr("New Point")),
Chris@22:     m_editingCommand(0),
Chris@28:     m_colour(QColor(200, 50, 255)),
Chris@28:     m_plotStyle(PlotInstants)
Chris@0: {
Chris@44:     
Chris@0: }
Chris@0: 
Chris@0: void
Chris@0: TimeInstantLayer::setModel(SparseOneDimensionalModel *model)
Chris@0: {
Chris@0:     if (m_model == model) return;
Chris@0:     m_model = model;
Chris@0: 
Chris@0:     connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0:     connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0: 	    this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0: 
Chris@0:     connect(m_model, SIGNAL(completionChanged()),
Chris@0: 	    this, SIGNAL(modelCompletionChanged()));
Chris@0: 
Chris@0:     std::cerr << "TimeInstantLayer::setModel(" << model << ")" << std::endl;
Chris@0: 
Chris@0:     emit modelReplaced();
Chris@0: }
Chris@0: 
Chris@0: Layer::PropertyList
Chris@0: TimeInstantLayer::getProperties() const
Chris@0: {
Chris@0:     PropertyList list;
Chris@87:     list.push_back("Colour");
Chris@87:     list.push_back("Plot Type");
Chris@0:     return list;
Chris@0: }
Chris@0: 
Chris@87: QString
Chris@87: TimeInstantLayer::getPropertyLabel(const PropertyName &name) const
Chris@87: {
Chris@87:     if (name == "Colour") return tr("Colour");
Chris@87:     if (name == "Plot Type") return tr("Plot Type");
Chris@87:     return "";
Chris@87: }
Chris@87: 
Chris@0: Layer::PropertyType
Chris@20: TimeInstantLayer::getPropertyType(const PropertyName &) const
Chris@0: {
Chris@0:     return ValueProperty;
Chris@0: }
Chris@0: 
Chris@0: int
Chris@0: TimeInstantLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@0: 					 int *min, int *max) const
Chris@0: {
Chris@0:     int deft = 0;
Chris@0: 
Chris@87:     if (name == "Colour") {
Chris@0: 
Chris@10: 	if (min) *min = 0;
Chris@10: 	if (max) *max = 5;
Chris@0: 
Chris@0: 	if (m_colour == Qt::black) deft = 0;
Chris@0: 	else if (m_colour == Qt::darkRed) deft = 1;
Chris@0: 	else if (m_colour == Qt::darkBlue) deft = 2;
Chris@0: 	else if (m_colour == Qt::darkGreen) deft = 3;
Chris@0: 	else if (m_colour == QColor(200, 50, 255)) deft = 4;
Chris@0: 	else if (m_colour == QColor(255, 150, 50)) deft = 5;
Chris@0: 
Chris@87:     } else if (name == "Plot Type") {
Chris@28: 	
Chris@28: 	if (min) *min = 0;
Chris@28: 	if (max) *max = 1;
Chris@28: 	
Chris@28: 	deft = int(m_plotStyle);
Chris@28: 
Chris@0:     } else {
Chris@0: 	
Chris@0: 	deft = Layer::getPropertyRangeAndValue(name, min, max);
Chris@0:     }
Chris@0: 
Chris@0:     return deft;
Chris@0: }
Chris@0: 
Chris@0: QString
Chris@0: TimeInstantLayer::getPropertyValueLabel(const PropertyName &name,
Chris@0: 				    int value) const
Chris@0: {
Chris@87:     if (name == "Colour") {
Chris@0: 	switch (value) {
Chris@0: 	default:
Chris@0: 	case 0: return tr("Black");
Chris@0: 	case 1: return tr("Red");
Chris@0: 	case 2: return tr("Blue");
Chris@0: 	case 3: return tr("Green");
Chris@0: 	case 4: return tr("Purple");
Chris@0: 	case 5: return tr("Orange");
Chris@0: 	}
Chris@87:     } else if (name == "Plot Type") {
Chris@28: 	switch (value) {
Chris@28: 	default:
Chris@28: 	case 0: return tr("Instants");
Chris@28: 	case 1: return tr("Segmentation");
Chris@28: 	}
Chris@0:     }
Chris@0:     return tr("<unknown>");
Chris@0: }
Chris@0: 
Chris@0: void
Chris@0: TimeInstantLayer::setProperty(const PropertyName &name, int value)
Chris@0: {
Chris@87:     if (name == "Colour") {
Chris@0: 	switch (value) {
Chris@0: 	default:
Chris@0: 	case 0:	setBaseColour(Qt::black); break;
Chris@0: 	case 1: setBaseColour(Qt::darkRed); break;
Chris@0: 	case 2: setBaseColour(Qt::darkBlue); break;
Chris@0: 	case 3: setBaseColour(Qt::darkGreen); break;
Chris@0: 	case 4: setBaseColour(QColor(200, 50, 255)); break;
Chris@0: 	case 5: setBaseColour(QColor(255, 150, 50)); break;
Chris@0: 	}
Chris@87:     } else if (name == "Plot Type") {
Chris@28: 	setPlotStyle(PlotStyle(value));
Chris@0:     }
Chris@0: }
Chris@0: 
Chris@0: void
Chris@0: TimeInstantLayer::setBaseColour(QColor colour)
Chris@0: {
Chris@0:     if (m_colour == colour) return;
Chris@0:     m_colour = colour;
Chris@0:     emit layerParametersChanged();
Chris@0: }
Chris@0: 
Chris@28: void
Chris@28: TimeInstantLayer::setPlotStyle(PlotStyle style)
Chris@28: {
Chris@28:     if (m_plotStyle == style) return;
Chris@28:     m_plotStyle = style;
Chris@28:     emit layerParametersChanged();
Chris@28: }
Chris@28: 
Chris@0: bool
Chris@44: TimeInstantLayer::isLayerScrollable(const View *v) const
Chris@0: {
Chris@0:     QPoint discard;
Chris@44:     return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@0: }
Chris@0: 
Chris@0: SparseOneDimensionalModel::PointList
Chris@44: TimeInstantLayer::getLocalPoints(View *v, int x) const
Chris@0: {
Chris@28:     // Return a set of points that all have the same frame number, the
Chris@28:     // nearest to the given x coordinate, and that are within a
Chris@28:     // certain fuzz distance of that x coordinate.
Chris@28: 
Chris@0:     if (!m_model) return SparseOneDimensionalModel::PointList();
Chris@0: 
Chris@44:     long frame = v->getFrameForX(x);
Chris@0: 
Chris@0:     SparseOneDimensionalModel::PointList onPoints =
Chris@0: 	m_model->getPoints(frame);
Chris@0: 
Chris@0:     if (!onPoints.empty()) {
Chris@0: 	return onPoints;
Chris@0:     }
Chris@0: 
Chris@0:     SparseOneDimensionalModel::PointList prevPoints =
Chris@0: 	m_model->getPreviousPoints(frame);
Chris@0:     SparseOneDimensionalModel::PointList nextPoints =
Chris@0: 	m_model->getNextPoints(frame);
Chris@0: 
Chris@0:     SparseOneDimensionalModel::PointList usePoints = prevPoints;
Chris@0: 
Chris@0:     if (prevPoints.empty()) {
Chris@0: 	usePoints = nextPoints;
Chris@44:     } else if (prevPoints.begin()->frame < v->getStartFrame() &&
Chris@44: 	       !(nextPoints.begin()->frame > v->getEndFrame())) {
Chris@0: 	usePoints = nextPoints;
Chris@0:     } else if (nextPoints.begin()->frame - frame <
Chris@0: 	       frame - prevPoints.begin()->frame) {
Chris@0: 	usePoints = nextPoints;
Chris@0:     }
Chris@0: 
Chris@28:     if (!usePoints.empty()) {
Chris@28: 	int fuzz = 2;
Chris@44: 	int px = v->getXForFrame(usePoints.begin()->frame);
Chris@28: 	if ((px > x && px - x > fuzz) ||
Chris@28: 	    (px < x && x - px > fuzz + 1)) {
Chris@28: 	    usePoints.clear();
Chris@28: 	}
Chris@28:     }
Chris@28: 
Chris@0:     return usePoints;
Chris@0: }
Chris@0: 
Chris@25: QString
Chris@44: TimeInstantLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@0: {
Chris@25:     int x = pos.x();
Chris@0: 
Chris@25:     if (!m_model || !m_model->getSampleRate()) return "";
Chris@0: 
Chris@44:     SparseOneDimensionalModel::PointList points = getLocalPoints(v, x);
Chris@0: 
Chris@0:     if (points.empty()) {
Chris@0: 	if (!m_model->isReady()) {
Chris@25: 	    return tr("In progress");
Chris@25: 	} else {
Chris@25: 	    return tr("No local points");
Chris@0: 	}
Chris@0:     }
Chris@0: 
Chris@0:     long useFrame = points.begin()->frame;
Chris@0: 
Chris@0:     RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
Chris@25:     
Chris@25:     QString text;
Chris@0: 
Chris@25:     if (points.begin()->label == "") {
Chris@25: 	text = QString(tr("Time:\t%1\nNo label"))
Chris@25: 	    .arg(rt.toText(true).c_str());
Chris@25:     } else {
Chris@25: 	text = QString(tr("Time:\t%1\nLabel:\t%2"))
Chris@25: 	    .arg(rt.toText(true).c_str())
Chris@25: 	    .arg(points.begin()->label);
Chris@25:     }
Chris@0: 
Chris@44:     pos = QPoint(v->getXForFrame(useFrame), pos.y());
Chris@25:     return text;
Chris@0: }
Chris@0: 
Chris@28: bool
Chris@44: TimeInstantLayer::snapToFeatureFrame(View *v, int &frame,
Chris@28: 				     size_t &resolution,
Chris@28: 				     SnapType snap) const
Chris@13: {
Chris@13:     if (!m_model) {
Chris@44: 	return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@13:     }
Chris@13: 
Chris@13:     resolution = m_model->getResolution();
Chris@28:     SparseOneDimensionalModel::PointList points;
Chris@13: 
Chris@28:     if (snap == SnapNeighbouring) {
Chris@28: 	
Chris@44: 	points = getLocalPoints(v, v->getXForFrame(frame));
Chris@28: 	if (points.empty()) return false;
Chris@28: 	frame = points.begin()->frame;
Chris@28: 	return true;
Chris@28:     }    
Chris@28: 
Chris@28:     points = m_model->getPoints(frame, frame);
Chris@28:     int snapped = frame;
Chris@28:     bool found = false;
Chris@13: 
Chris@13:     for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin();
Chris@13: 	 i != points.end(); ++i) {
Chris@13: 
Chris@28: 	if (snap == SnapRight) {
Chris@28: 
Chris@28: 	    if (i->frame >= frame) {
Chris@28: 		snapped = i->frame;
Chris@28: 		found = true;
Chris@13: 		break;
Chris@13: 	    }
Chris@28: 
Chris@28: 	} else if (snap == SnapLeft) {
Chris@28: 
Chris@13: 	    if (i->frame <= frame) {
Chris@28: 		snapped = i->frame;
Chris@28: 		found = true; // don't break, as the next may be better
Chris@28: 	    } else {
Chris@28: 		break;
Chris@28: 	    }
Chris@28: 
Chris@28: 	} else { // nearest
Chris@28: 
Chris@28: 	    SparseOneDimensionalModel::PointList::const_iterator j = i;
Chris@28: 	    ++j;
Chris@28: 
Chris@28: 	    if (j == points.end()) {
Chris@28: 
Chris@28: 		snapped = i->frame;
Chris@28: 		found = true;
Chris@28: 		break;
Chris@28: 
Chris@28: 	    } else if (j->frame >= frame) {
Chris@28: 
Chris@28: 		if (j->frame - frame < frame - i->frame) {
Chris@28: 		    snapped = j->frame;
Chris@28: 		} else {
Chris@28: 		    snapped = i->frame;
Chris@28: 		}
Chris@28: 		found = true;
Chris@28: 		break;
Chris@13: 	    }
Chris@13: 	}
Chris@13:     }
Chris@13: 
Chris@28:     frame = snapped;
Chris@28:     return found;
Chris@13: }
Chris@13: 
Chris@0: void
Chris@44: TimeInstantLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0: {
Chris@0:     if (!m_model || !m_model->isOK()) return;
Chris@0: 
Chris@0: //    Profiler profiler("TimeInstantLayer::paint", true);
Chris@0: 
Chris@20:     int x0 = rect.left(), x1 = rect.right();
Chris@0: 
Chris@44:     long frame0 = v->getFrameForX(x0);
Chris@44:     long frame1 = v->getFrameForX(x1);
Chris@0: 
Chris@0:     SparseOneDimensionalModel::PointList points(m_model->getPoints
Chris@0: 						(frame0, frame1));
Chris@0: 
Chris@28:     bool odd = false;
Chris@28:     if (m_plotStyle == PlotSegmentation && !points.empty()) {
Chris@28: 	int index = m_model->getIndexOf(*points.begin());
Chris@28: 	odd = ((index % 2) == 1);
Chris@28:     }
Chris@28: 
Chris@0:     paint.setPen(m_colour);
Chris@0: 
Chris@0:     QColor brushColour(m_colour);
Chris@0:     brushColour.setAlpha(100);
Chris@0:     paint.setBrush(brushColour);
Chris@0: 
Chris@28:     QColor oddBrushColour(brushColour);
Chris@28:     if (m_plotStyle == PlotSegmentation) {
Chris@28: 	if (m_colour == Qt::black) {
Chris@28: 	    oddBrushColour = Qt::gray;
Chris@28: 	} else if (m_colour == Qt::darkRed) {
Chris@28: 	    oddBrushColour = Qt::red;
Chris@28: 	} else if (m_colour == Qt::darkBlue) {
Chris@28: 	    oddBrushColour = Qt::blue;
Chris@28: 	} else if (m_colour == Qt::darkGreen) {
Chris@28: 	    oddBrushColour = Qt::green;
Chris@28: 	} else {
Chris@28: 	    oddBrushColour = oddBrushColour.light(150);
Chris@28: 	}
Chris@28: 	oddBrushColour.setAlpha(100);
Chris@28:     }
Chris@28: 
Chris@0: //    std::cerr << "TimeInstantLayer::paint: resolution is "
Chris@0: //	      << m_model->getResolution() << " frames" << std::endl;
Chris@0: 
Chris@0:     QPoint localPos;
Chris@0:     long illuminateFrame = -1;
Chris@0: 
Chris@44:     if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@0: 	SparseOneDimensionalModel::PointList localPoints =
Chris@44: 	    getLocalPoints(v, localPos.x());
Chris@0: 	if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@0:     }
Chris@0: 	
Chris@23:     int prevX = -1;
Chris@79:     int textY = v->getTextLabelHeight(this, paint);
Chris@79:     
Chris@0:     for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin();
Chris@0: 	 i != points.end(); ++i) {
Chris@0: 
Chris@0: 	const SparseOneDimensionalModel::Point &p(*i);
Chris@17: 	SparseOneDimensionalModel::PointList::const_iterator j = i;
Chris@17: 	++j;
Chris@0: 
Chris@44: 	int x = v->getXForFrame(p.frame);
Chris@23: 	if (x == prevX && p.frame != illuminateFrame) continue;
Chris@23: 
Chris@44: 	int iw = v->getXForFrame(p.frame + m_model->getResolution()) - x;
Chris@16: 	if (iw < 2) {
Chris@17: 	    if (iw < 1) {
Chris@17: 		iw = 2;
Chris@17: 		if (j != points.end()) {
Chris@44: 		    int nx = v->getXForFrame(j->frame);
Chris@17: 		    if (nx < x + 3) iw = 1;
Chris@17: 		}
Chris@17: 	    } else {
Chris@17: 		iw = 2;
Chris@17: 	    }
Chris@16: 	}
Chris@20: 		
Chris@0: 	if (p.frame == illuminateFrame) {
Chris@0: 	    paint.setPen(Qt::black); //!!!
Chris@0: 	} else {
Chris@0: 	    paint.setPen(brushColour);
Chris@0: 	}
Chris@23: 
Chris@28: 	if (m_plotStyle == PlotInstants) {
Chris@28: 	    if (iw > 1) {
Chris@44: 		paint.drawRect(x, 0, iw - 1, v->height() - 1);
Chris@28: 	    } else {
Chris@44: 		paint.drawLine(x, 0, x, v->height() - 1);
Chris@28: 	    }
Chris@23: 	} else {
Chris@28: 
Chris@28: 	    if (odd) paint.setBrush(oddBrushColour);
Chris@28: 	    else paint.setBrush(brushColour);
Chris@28: 	    
Chris@28: 	    int nx;
Chris@28: 	    
Chris@28: 	    if (j != points.end()) {
Chris@28: 		const SparseOneDimensionalModel::Point &q(*j);
Chris@44: 		nx = v->getXForFrame(q.frame);
Chris@28: 	    } else {
Chris@44: 		nx = v->getXForFrame(m_model->getEndFrame());
Chris@28: 	    }
Chris@28: 
Chris@28: 	    if (nx >= x) {
Chris@28: 		
Chris@28: 		if (illuminateFrame != p.frame &&
Chris@44: 		    (nx < x + 5 || x >= v->width() - 1)) {
Chris@28: 		    paint.setPen(Qt::NoPen);
Chris@28: 		}
Chris@28: 
Chris@44: 		paint.drawRect(x, -1, nx - x, v->height() + 1);
Chris@28: 	    }
Chris@28: 
Chris@28: 	    odd = !odd;
Chris@23: 	}
Chris@28: 
Chris@0: 	paint.setPen(m_colour);
Chris@0: 	
Chris@0: 	if (p.label != "") {
Chris@0: 
Chris@0: 	    // only draw if there's enough room from here to the next point
Chris@0: 
Chris@0: 	    int lw = paint.fontMetrics().width(p.label);
Chris@0: 	    bool good = true;
Chris@0: 
Chris@17: 	    if (j != points.end()) {
Chris@44: 		int nx = v->getXForFrame(j->frame);
Chris@20: 		if (nx >= x && nx - x - iw - 3 <= lw) good = false;
Chris@0: 	    }
Chris@0: 
Chris@0: 	    if (good) {
Chris@79: 		paint.drawText(x + iw + 2, textY, p.label);
Chris@0: 	    }
Chris@0: 	}
Chris@23: 
Chris@23: 	prevX = x;
Chris@0:     }
Chris@0: }
Chris@0: 
Chris@17: void
Chris@44: TimeInstantLayer::drawStart(View *v, QMouseEvent *e)
Chris@17: {
Chris@17:     std::cerr << "TimeInstantLayer::drawStart(" << e->x() << ")" << std::endl;
Chris@17: 
Chris@17:     if (!m_model) return;
Chris@17: 
Chris@44:     long frame = v->getFrameForX(e->x());
Chris@17:     if (frame < 0) frame = 0;
Chris@21:     frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22: 
Chris@17:     m_editingPoint = SparseOneDimensionalModel::Point(frame, tr("New Point"));
Chris@22: 
Chris@22:     if (m_editingCommand) m_editingCommand->finish();
Chris@22:     m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model,
Chris@22: 								  tr("Draw Point"));
Chris@22:     m_editingCommand->addPoint(m_editingPoint);
Chris@22: 
Chris@18:     m_editing = true;
Chris@17: }
Chris@17: 
Chris@17: void
Chris@44: TimeInstantLayer::drawDrag(View *v, QMouseEvent *e)
Chris@17: {
Chris@17:     std::cerr << "TimeInstantLayer::drawDrag(" << e->x() << ")" << std::endl;
Chris@17: 
Chris@18:     if (!m_model || !m_editing) return;
Chris@17: 
Chris@44:     long frame = v->getFrameForX(e->x());
Chris@17:     if (frame < 0) frame = 0;
Chris@21:     frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22:     m_editingCommand->deletePoint(m_editingPoint);
Chris@17:     m_editingPoint.frame = frame;
Chris@22:     m_editingCommand->addPoint(m_editingPoint);
Chris@17: }
Chris@17: 
Chris@17: void
Chris@44: TimeInstantLayer::drawEnd(View *v, QMouseEvent *e)
Chris@17: {
Chris@17:     std::cerr << "TimeInstantLayer::drawEnd(" << e->x() << ")" << std::endl;
Chris@18:     if (!m_model || !m_editing) return;
Chris@23:     QString newName = tr("Add Point at %1 s")
Chris@23: 	.arg(RealTime::frame2RealTime(m_editingPoint.frame,
Chris@23: 				      m_model->getSampleRate())
Chris@23: 	     .toText(false).c_str());
Chris@23:     m_editingCommand->setName(newName);
Chris@22:     m_editingCommand->finish();
Chris@22:     m_editingCommand = 0;
Chris@18:     m_editing = false;
Chris@18: }
Chris@18: 
Chris@18: void
Chris@44: TimeInstantLayer::editStart(View *v, QMouseEvent *e)
Chris@18: {
Chris@18:     std::cerr << "TimeInstantLayer::editStart(" << e->x() << ")" << std::endl;
Chris@18: 
Chris@17:     if (!m_model) return;
Chris@18: 
Chris@44:     SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@18:     if (points.empty()) return;
Chris@18: 
Chris@18:     m_editingPoint = *points.begin();
Chris@22: 
Chris@22:     if (m_editingCommand) {
Chris@22: 	m_editingCommand->finish();
Chris@22: 	m_editingCommand = 0;
Chris@22:     }
Chris@22: 
Chris@18:     m_editing = true;
Chris@18: }
Chris@18: 
Chris@18: void
Chris@44: TimeInstantLayer::editDrag(View *v, QMouseEvent *e)
Chris@18: {
Chris@18:     std::cerr << "TimeInstantLayer::editDrag(" << e->x() << ")" << std::endl;
Chris@18: 
Chris@18:     if (!m_model || !m_editing) return;
Chris@18: 
Chris@44:     long frame = v->getFrameForX(e->x());
Chris@18:     if (frame < 0) frame = 0;
Chris@21:     frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@22: 
Chris@22:     if (!m_editingCommand) {
Chris@22: 	m_editingCommand = new SparseOneDimensionalModel::EditCommand(m_model,
Chris@22: 								      tr("Drag Point"));
Chris@22:     }
Chris@22: 
Chris@22:     m_editingCommand->deletePoint(m_editingPoint);
Chris@18:     m_editingPoint.frame = frame;
Chris@22:     m_editingCommand->addPoint(m_editingPoint);
Chris@18: }
Chris@18: 
Chris@18: void
Chris@44: TimeInstantLayer::editEnd(View *v, QMouseEvent *e)
Chris@18: {
Chris@18:     std::cerr << "TimeInstantLayer::editEnd(" << e->x() << ")" << std::endl;
Chris@18:     if (!m_model || !m_editing) return;
Chris@23:     if (m_editingCommand) {
Chris@23: 	QString newName = tr("Move Point to %1 s")
Chris@23: 	    .arg(RealTime::frame2RealTime(m_editingPoint.frame,
Chris@23: 					  m_model->getSampleRate())
Chris@23: 		 .toText(false).c_str());
Chris@23: 	m_editingCommand->setName(newName);
Chris@23: 	m_editingCommand->finish();
Chris@23:     }
Chris@22:     m_editingCommand = 0;
Chris@18:     m_editing = false;
Chris@17: }
Chris@17: 
Chris@43: void
Chris@70: TimeInstantLayer::editOpen(View *v, QMouseEvent *e)
Chris@70: {
Chris@70:     if (!m_model) return;
Chris@70: 
Chris@70:     SparseOneDimensionalModel::PointList points = getLocalPoints(v, e->x());
Chris@70:     if (points.empty()) return;
Chris@70: 
Chris@70:     SparseOneDimensionalModel::Point point = *points.begin();
Chris@70: 
Chris@70:     ItemEditDialog *dialog = new ItemEditDialog
Chris@70:         (m_model->getSampleRate(),
Chris@70:          ItemEditDialog::ShowTime |
Chris@70:          ItemEditDialog::ShowText);
Chris@70: 
Chris@70:     dialog->setFrameTime(point.frame);
Chris@70:     dialog->setText(point.label);
Chris@70: 
Chris@70:     if (dialog->exec() == QDialog::Accepted) {
Chris@70: 
Chris@70:         SparseOneDimensionalModel::Point newPoint = point;
Chris@70:         newPoint.frame = dialog->getFrameTime();
Chris@70:         newPoint.label = dialog->getText();
Chris@70:         
Chris@70:         SparseOneDimensionalModel::EditCommand *command =
Chris@70:             new SparseOneDimensionalModel::EditCommand(m_model, tr("Edit Point"));
Chris@70:         command->deletePoint(point);
Chris@70:         command->addPoint(newPoint);
Chris@70:         command->finish();
Chris@70:     }
Chris@70: 
Chris@70:     delete dialog;
Chris@70: }
Chris@70: 
Chris@70: void
Chris@43: TimeInstantLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@43: {
Chris@99:     if (!m_model) return;
Chris@99: 
Chris@43:     SparseOneDimensionalModel::EditCommand *command =
Chris@43: 	new SparseOneDimensionalModel::EditCommand(m_model,
Chris@43: 						   tr("Drag Selection"));
Chris@43: 
Chris@43:     SparseOneDimensionalModel::PointList points =
Chris@43: 	m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43: 
Chris@43:     for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@43: 	 i != points.end(); ++i) {
Chris@43: 
Chris@43: 	if (s.contains(i->frame)) {
Chris@43: 	    SparseOneDimensionalModel::Point newPoint(*i);
Chris@43: 	    newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@43: 	    command->deletePoint(*i);
Chris@43: 	    command->addPoint(newPoint);
Chris@43: 	}
Chris@43:     }
Chris@43: 
Chris@43:     command->finish();
Chris@43: }
Chris@43: 
Chris@43: void
Chris@43: TimeInstantLayer::resizeSelection(Selection s, Selection newSize)
Chris@43: {
Chris@99:     if (!m_model) return;
Chris@99: 
Chris@43:     SparseOneDimensionalModel::EditCommand *command =
Chris@43: 	new SparseOneDimensionalModel::EditCommand(m_model,
Chris@43: 						   tr("Resize Selection"));
Chris@43: 
Chris@43:     SparseOneDimensionalModel::PointList points =
Chris@43: 	m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43: 
Chris@43:     double ratio =
Chris@43: 	double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@43: 	double(s.getEndFrame() - s.getStartFrame());
Chris@43: 
Chris@43:     for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@43: 	 i != points.end(); ++i) {
Chris@43: 
Chris@43: 	if (s.contains(i->frame)) {
Chris@43: 
Chris@43: 	    double target = i->frame;
Chris@43: 	    target = newSize.getStartFrame() + 
Chris@43: 		double(target - s.getStartFrame()) * ratio;
Chris@43: 
Chris@43: 	    SparseOneDimensionalModel::Point newPoint(*i);
Chris@43: 	    newPoint.frame = lrint(target);
Chris@43: 	    command->deletePoint(*i);
Chris@43: 	    command->addPoint(newPoint);
Chris@43: 	}
Chris@43:     }
Chris@43: 
Chris@43:     command->finish();
Chris@43: }
Chris@43: 
Chris@43: void
Chris@43: TimeInstantLayer::deleteSelection(Selection s)
Chris@43: {
Chris@99:     if (!m_model) return;
Chris@99: 
Chris@43:     SparseOneDimensionalModel::EditCommand *command =
Chris@43: 	new SparseOneDimensionalModel::EditCommand(m_model,
Chris@43: 						   tr("Delete Selection"));
Chris@43: 
Chris@43:     SparseOneDimensionalModel::PointList points =
Chris@43: 	m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43: 
Chris@43:     for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@43: 	 i != points.end(); ++i) {
Chris@43: 	if (s.contains(i->frame)) command->deletePoint(*i);
Chris@43:     }
Chris@43: 
Chris@43:     command->finish();
Chris@43: }
Chris@76: 
Chris@76: void
Chris@76: TimeInstantLayer::copy(Selection s, Clipboard &to)
Chris@76: {
Chris@99:     if (!m_model) return;
Chris@99: 
Chris@76:     SparseOneDimensionalModel::PointList points =
Chris@76: 	m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76: 
Chris@76:     for (SparseOneDimensionalModel::PointList::iterator i = points.begin();
Chris@76: 	 i != points.end(); ++i) {
Chris@76: 	if (s.contains(i->frame)) {
Chris@76:             Clipboard::Point point(i->frame, i->label);
Chris@76:             to.addPoint(point);
Chris@76:         }
Chris@76:     }
Chris@76: }
Chris@76: 
Chris@125: bool
Chris@125: TimeInstantLayer::paste(const Clipboard &from, int frameOffset, bool interactive)
Chris@76: {
Chris@125:     if (!m_model) return false;
Chris@99: 
Chris@76:     const Clipboard::PointList &points = from.getPoints();
Chris@76: 
Chris@76:     SparseOneDimensionalModel::EditCommand *command =
Chris@76: 	new SparseOneDimensionalModel::EditCommand(m_model, tr("Paste"));
Chris@76: 
Chris@76:     for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76:          i != points.end(); ++i) {
Chris@76:         
Chris@76:         if (!i->haveFrame()) continue;
Chris@76:         size_t frame = 0;
Chris@76:         if (frameOffset > 0 || -frameOffset < i->getFrame()) {
Chris@76:             frame = i->getFrame() + frameOffset;
Chris@76:         }
Chris@76:         SparseOneDimensionalModel::Point newPoint(frame);
Chris@125:         if (i->haveLabel()) {
Chris@125:             newPoint.label = i->getLabel();
Chris@125:         } else if (i->haveValue()) {
Chris@125:             newPoint.label = QString("%1").arg(i->getValue());
Chris@125:         }
Chris@76:         
Chris@76:         command->addPoint(newPoint);
Chris@76:     }
Chris@76: 
Chris@76:     command->finish();
Chris@125:     return true;
Chris@76: }
Chris@43: 
Chris@6: QString
Chris@6: TimeInstantLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@6: {
Chris@6:     return Layer::toXmlString(indent, extraAttributes +
Chris@28: 			      QString(" colour=\"%1\" plotStyle=\"%2\"")
Chris@28: 			      .arg(encodeColour(m_colour)).arg(m_plotStyle));
Chris@6: }
Chris@0: 
Chris@11: void
Chris@11: TimeInstantLayer::setProperties(const QXmlAttributes &attributes)
Chris@11: {
Chris@11:     QString colourSpec = attributes.value("colour");
Chris@11:     if (colourSpec != "") {
Chris@11: 	QColor colour(colourSpec);
Chris@11: 	if (colour.isValid()) {
Chris@11: 	    setBaseColour(QColor(colourSpec));
Chris@11: 	}
Chris@11:     }
Chris@28: 
Chris@28:     bool ok;
Chris@28:     PlotStyle style = (PlotStyle)
Chris@28: 	attributes.value("plotStyle").toInt(&ok);
Chris@28:     if (ok) setPlotStyle(style);
Chris@11: }
Chris@11: 
Chris@0: #ifdef INCLUDE_MOCFILES
Chris@0: #include "TimeInstantLayer.moc.cpp"
Chris@0: #endif
Chris@0: