Mercurial > hg > easaier-soundaccess
view layer/IntervalLayer.cpp @ 18:d8e6709e9075
add
- EasaierSessionManager
- Easaier menus
- Interval model
author | lbajardsilogic |
---|---|
date | Mon, 14 May 2007 13:13:14 +0000 |
parents | |
children | 66af7c1b10d9 |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Sound Access EASAIER client application. Silogic 2007. Luc Barthélémy. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #include <QObject> #include <QColor> #include <QPainter> #include <QMouseEvent> #include <QInputDialog> #include "layer/IntervalLayer.h" #include "view/View.h" #include "system/System.h" #include "widgets/ItemEditDialog.h" const int gIntervalHeight = 30; //**************************************************************** //* Function: Constructor //* Description: declaration and initialisation of all private parameters //**************************************************************** IntervalLayer::IntervalLayer(): Layer(), m_model(0), m_editing(false), m_colour(Qt::darkRed), m_editingCommand(0) { } IntervalLayer::~IntervalLayer() {} void IntervalLayer::setModel(IntervalModel* model) { if (m_model == model) return; m_model = model; connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); connect(m_model, SIGNAL(modelChanged(size_t, size_t)), this, SIGNAL(modelChanged(size_t, size_t))); connect(m_model, SIGNAL(completionChanged()), this, SIGNAL(modelCompletionChanged())); emit modelReplaced(); } Layer::PropertyList IntervalLayer::getProperties() const { PropertyList list; list.push_back("Colour"); return list; } Layer::PropertyType IntervalLayer::getPropertyType(const PropertyName &name) const { return ValueProperty; } int IntervalLayer::getPropertyRangeAndValue(const PropertyName &name, int *min, int *max, int *deflt) const { //!!! factor this colour handling stuff out into a colour manager class int val = 0; if (name == "Colour") { if (min) *min = 0; if (max) *max = 5; if (deflt) *deflt = 0; if (m_colour == Qt::black) val = 0; else if (m_colour == Qt::darkRed) val = 1; else if (m_colour == Qt::darkBlue) val = 2; else if (m_colour == Qt::darkGreen) val = 3; else if (m_colour == QColor(200, 50, 255)) val = 4; else if (m_colour == QColor(255, 150, 50)) val = 5; } return val; } QString IntervalLayer::getPropertyValueLabel(const PropertyName &name, int value) const { if (name == "Colour") { switch (value) { default: case 0: return tr("Black"); case 1: return tr("Red"); case 2: return tr("Blue"); case 3: return tr("Green"); case 4: return tr("Purple"); case 5: return tr("Orange"); } } return tr("<unknown>"); } void IntervalLayer::setProperties(const QXmlAttributes &attributes) { QString colourSpec = attributes.value("colour"); if (colourSpec != "") { QColor colour(colourSpec); if (colour.isValid()) { setBaseColour(QColor(colourSpec)); } } } void IntervalLayer::setProperty(const PropertyName &name, int value) { if (name == "Colour") { switch (value) { default: case 0: setBaseColour(Qt::black); break; case 1: setBaseColour(Qt::darkRed); break; case 2: setBaseColour(Qt::darkBlue); break; case 3: setBaseColour(Qt::darkGreen); break; case 4: setBaseColour(QColor(200, 50, 255)); break; case 5: setBaseColour(QColor(255, 150, 50)); break; } } } void IntervalLayer::setBaseColour(QColor colour) { if (m_colour == colour) return; m_colour = colour; emit layerParametersChanged(); } QString IntervalLayer::toXmlString(QString indent, QString extraAttributes) const { return Layer::toXmlString(indent, extraAttributes + QString(" colour=\"%1\"") .arg(encodeColour(m_colour))); } QString IntervalLayer::toEasaierXmlString(QString indent, QString extraAttributes) const { return Layer::toEasaierXmlString(indent, extraAttributes + QString(" colour=\"%1\"") .arg(encodeColour(m_colour))); } QString IntervalLayer::getPropertyLabel(const PropertyName &name) const { if (name == "Colour") return tr("Colour"); return ""; } int IntervalLayer::getYForHeight(View *v, float height) const { int h = v->height(); return h - int(height * h); } float IntervalLayer::getHeightForY(View *v, int y) const { int h = v->height(); return float(h - y) / h; } void IntervalLayer::paint(View *v, QPainter &paint, QRect rect) const { if (!m_model || !m_model->isOK()) return; int sampleRate = m_model->getSampleRate(); if (!sampleRate) return; int x0 = rect.left(), x1 = rect.right(); long frame0 = v->getFrameForX(x0); long frame1 = v->getFrameForX(x1); paint.save(); QColor brushColour(m_colour); brushColour.setAlpha(100); QColor penColour = Qt::black; paint.setPen(m_colour); IntervalList& intervals = m_model->intervals(); int xS,xE,y; bool draw, drawText, drawStart, drawEnd; QPoint localPos; long illuminateX = -1; if (v->shouldIlluminateLocalFeatures(this, localPos)) { // should highlight one endpoint ? illuminateX = localPos.x(); } for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i) { draw = drawText = drawStart = drawEnd = false; TimeIntervalPtr ti = (*i); if (ti->start() >= frame0 && ti->start() < frame1) { xS = v->getXForFrame(ti->start()); xE = v->getXForFrame(ti->end()); if (xE <= x1) drawEnd = true; else xE = x1; draw = drawText = drawStart = true; } else if (ti->end() > frame0 && ti->end <= frame1) { xS = v->getXForFrame(ti->start()); if (xS < x0) xS = x0; xE = v->getXForFrame(ti->end()); draw = drawEnd = true; } else if (ti->start <= frame0 && ti->end >= frame1) { xS = x0; xE = x1; draw = true; } if (draw) { y = getYForHeight(v, ti->value()); paint.setBrush(brushColour); paint.fillRect(xS, y-gIntervalHeight/2, (xE-xS+1), gIntervalHeight, brushColour); } if (drawStart) { if (abs(illuminateX - xS) < 3) paint.setPen(Qt::black); else paint.setPen(brushColour); paint.drawLine(xS, 0, xS, v->height() - 1); } if (drawEnd) { if (abs (illuminateX - xE) < 3) paint.setPen(Qt::black); else paint.setPen(brushColour); paint.drawLine(xE, 0, xE, v->height() - 1); } if (drawText) { paint.setPen(penColour); paint.drawText(xS+10, y, ti->label()); } } paint.restore(); } bool IntervalLayer::getValueExtents(float &min, float &max, bool &logarithmic, QString &unit) const { return false; } IntervalList IntervalLayer::getIntervalAt(View *v, int x, int y) // y < 0 means you don't make any test on it { IntervalList result; IntervalList& intervals = m_model->intervals(); for (IntervalListIterator i = intervals.begin(); i != intervals.end(); ++i) { TimeIntervalPtr ti = (*i); int yT = getYForHeight(v, ti->value()); int xS = v->getXForFrame(ti->start()); int xE = v->getXForFrame(ti->end()); if ((x >= xS) && (x <= xE)) { if ((y < 0) || (abs(y - yT) <= gIntervalHeight/2)) result.push_back(ti); } } return result; } IntervalList IntervalLayer::getInterval(long start, long end) { IntervalList result; IntervalList& intervals = m_model->intervals(); for (IntervalListIterator i = intervals.begin(); i != intervals.end(); ++i) { TimeIntervalPtr ti = (*i); if ( (start <= ti->start()) && (ti->start() <= end) && (start <= ti->end()) && (ti->end() <= end) ) { result.push_back(ti); } } return result; } QString IntervalLayer::getFeatureDescription(View *v, QPoint& pos) const { if (!m_model || !m_model->getSampleRate()) return ""; QString description; int x = pos.x(); int y = pos.y(); long frame = v->getFrameForX(x); IntervalList& intervals = m_model->intervals(); for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i) { TimeIntervalPtr ti = (*i); if (ti->start() <= frame && ti->end() >= frame) { int y = getYForHeight(v, ti->value()); if (abs(y - pos.y()) <= gIntervalHeight/2) { RealTime rtStart = RealTime::frame2RealTime(ti->start(), m_model->getSampleRate()); RealTime rtEnd = RealTime::frame2RealTime(ti->end(), m_model->getSampleRate()); RealTime rtDuration = rtEnd - rtStart; description = QString(tr("Interval:\t%1\nStart:\t%2 End:\t%3\tDuration:\t%4")) .arg(ti->label()) .arg(rtStart.toText(true).c_str()) .arg(rtEnd.toText(true).c_str()) .arg(rtDuration.toText(true).c_str()); break; } } } return description; } void IntervalLayer::drawStart(View *v, QMouseEvent *e) { if (!m_model) { std::cerr << "IntervalLayer::drawStart: no model" << std::endl; return; } long frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); int height = (int) (getHeightForY(v, e->y())*100+0.5); float value = ((float) height)/100; m_editingInterval = new TimeInterval(frame, frame, "", value); m_model->addInterval(m_editingInterval); if (m_editingCommand) { CommandHistory::getInstance()->addCommand(m_editingCommand, false); m_editingCommand = 0; } m_editingCommand = new IntervalModel::IntervalCommand(m_model, m_editingInterval, IntervalModel::IntervalCommand::Creation , frame, frame, value, ""); m_editing = 1; } void IntervalLayer::drawDrag(View *v, QMouseEvent *e) { if (!m_model || !m_editing) return; long frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); long start = m_editingInterval->start(); long end = frame; if (start < end) { m_editing = 2; m_editingInterval->end(frame); m_editingCommand->newEnd(frame); } else { m_editing = 1; m_editingInterval->start(frame); m_editingCommand->newStart(frame); } } void IntervalLayer::drawEnd(View *v, QMouseEvent *e) { if (!m_model || !m_editing) return; bool ok = false; QString label = QInputDialog::getText(v, tr("Enter label"), tr("Please enter a new label:"), QLineEdit::Normal, "", &ok); if (ok) { m_editingInterval->label(label); m_editingCommand->newLabel(label); } if (m_editingCommand) { CommandHistory::getInstance()->addCommand(m_editingCommand, false); m_editingCommand = 0; } m_editing = false; } void IntervalLayer::editStart(View *view, QMouseEvent *evt) { if (!m_model) return; IntervalList intervals = getIntervalAt(view, evt->x(), evt->y()); for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i) { TimeIntervalPtr ti = (*i); int xS = view->getXForFrame(ti->start()); int xE = view->getXForFrame(ti->end()); if (abs(xS - evt->x()) < 5) { m_editing = 1; // editing start m_editingInterval = ti; break; } else if (abs(xE - evt->x()) < 5) { m_editing = 2; // editing end m_editingInterval = ti; break; } else { m_editing = 0; } } if (m_editing) view->setCursor(Qt::SizeHorCursor); if (m_editingCommand) { CommandHistory::getInstance()->addCommand(m_editingCommand, false); m_editingCommand = 0; } } void IntervalLayer::editDrag(View *view, QMouseEvent *evt) { if (!m_model || !m_editing) return; long frame = view->getFrameForX(evt->x()); if (!m_editingCommand) { m_editingCommand = new IntervalModel::IntervalCommand(m_model, m_editingInterval, IntervalModel::IntervalCommand::Edition, m_editingInterval->start(), m_editingInterval->end(), m_editingInterval->value(), m_editingInterval->label()); } if (m_editing == 1) // start { if (frame < m_editingInterval->end()) m_editingCommand->newStart(frame); } else if (m_editing == 2) // end { if (frame > m_editingInterval->start()) m_editingCommand->newEnd(frame); } } void IntervalLayer::editEnd(View *view, QMouseEvent *evt) { if (!m_model || !m_editing) return; view->setCursor(Qt::UpArrowCursor); if (m_editingCommand) { CommandHistory::getInstance()->addCommand(m_editingCommand, false); m_editingCommand = 0; } m_editing = false; } void IntervalLayer::editOpen(View *view, QMouseEvent *evt) // on double-click { if (!m_model) return; IntervalList intervals = getIntervalAt(view, evt->x(), evt->y()); if (! intervals.empty()) { TimeIntervalPtr ti = intervals.front(); bool ok = false; ItemEditDialog *dialog = new ItemEditDialog (m_model->getSampleRate(), ItemEditDialog::ShowTime | ItemEditDialog::ShowDuration | ItemEditDialog::ShowText | ItemEditDialog::ShowValue); dialog->setFrameTime(ti->start()); dialog->setFrameDuration(ti->end() - ti->start()); dialog->setText(ti->label()); dialog->setValue((int)(ti->value()*100+0.5)); if (dialog->exec() == QDialog::Accepted) { IntervalModel::IntervalCommand *command = new IntervalModel::IntervalCommand(m_model, ti, IntervalModel::IntervalCommand::Edition, dialog->getFrameTime(), dialog->getFrameTime() + dialog->getFrameDuration(), dialog->getValue()/100, dialog->getText()); CommandHistory::getInstance()->addCommand(command); } } } void IntervalLayer::moveSelection(Selection s, size_t newStartFrame) { if (!m_model) return; IntervalList intervals = getInterval(s.getStartFrame(), s.getEndFrame()); MacroCommand * command = new MacroCommand("Drag Selection"); long newStart = 0; long newEnd = 0; for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i) { TimeIntervalPtr ti = (*i); newStart = ti->start() + ((long) (newStartFrame - s.getStartFrame())); newEnd = ti->end() + ((long) (newStartFrame - s.getStartFrame())); IntervalModel::IntervalCommand *intervalCommand = new IntervalModel::IntervalCommand(m_model, ti, IntervalModel::IntervalCommand::Edition, newStart, newEnd, ti->value(), ti->label()); m_model->changeInterval(ti, newStart, newEnd, ti->value(), ti->label() ); command->addCommand(intervalCommand); } CommandHistory::getInstance()->addCommand(command, false); } void IntervalLayer::resizeSelection(Selection s, Selection newSize) { if (!m_model) return; IntervalList intervals = getInterval(s.getStartFrame(), s.getEndFrame()); MacroCommand * command = new MacroCommand("Resize Selection"); long newStart = 0; long newEnd = 0; double ratio = ((double) (newSize.getEndFrame() - newSize.getStartFrame())) / ((double) (s.getEndFrame() - s.getStartFrame())); for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i) { TimeIntervalPtr ti = (*i); newStart = (long) newSize.getStartFrame() + (long) ( ((double) ti->start() - (double) s.getStartFrame()) * ratio ); newEnd = (long) newSize.getStartFrame() + (long) ( ((double) ti->end() - (double) s.getStartFrame()) * ratio ); IntervalModel::IntervalCommand *intervalCommand = new IntervalModel::IntervalCommand(m_model, ti, IntervalModel::IntervalCommand::Edition, newStart, newEnd, ti->value(), ti->label()); m_model->changeInterval(ti, newStart, newEnd, ti->value(), ti->label() ); command->addCommand(intervalCommand); } CommandHistory::getInstance()->addCommand(command, false); } void IntervalLayer::deleteSelection(Selection s) { if (!m_model) return; IntervalList intervals = getInterval(s.getStartFrame(), s.getEndFrame()); MacroCommand * command = new MacroCommand("Delete Selected Intervals"); for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i) { TimeIntervalPtr ti = (*i); m_model->removeInterval(ti); IntervalModel::IntervalCommand *intervalCommand = new IntervalModel::IntervalCommand(m_model, ti, IntervalModel::IntervalCommand::Deletion, ti->start(), ti->end(), ti->value(), ti->label()); command->addCommand(intervalCommand); } CommandHistory::getInstance()->addCommand(command, false); } void IntervalLayer::paste(const Clipboard &from, int frameOffset) { if (!m_model) return; const Clipboard::PointList &points = from.getPoints(); MacroCommand * command = new MacroCommand("Paste"); for (Clipboard::PointList::const_iterator i = points.begin(); i != points.end(); ++i) { if (!i->haveFrame()) continue; size_t frame = 0; if (frameOffset > 0 || -frameOffset < i->getFrame()) { frame = i->getFrame() + frameOffset; } TimeIntervalPtr ti = new TimeInterval((long) frame, (long) (frame+i->getDuration()), i->getLabel(), i->getValue()); m_model->addInterval(ti); IntervalModel::IntervalCommand *intervalCommand = new IntervalModel::IntervalCommand(m_model, ti, IntervalModel::IntervalCommand::Creation, ti->start(), ti->end(), ti->value(), ti->label()); command->addCommand(intervalCommand); } CommandHistory::getInstance()->addCommand(command, false); } void IntervalLayer::copy(Selection s, Clipboard &to) { if (!m_model) return; IntervalList intervals = getInterval(s.getStartFrame(), s.getEndFrame()); for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i) { TimeIntervalPtr ti = (*i); Clipboard::Point point(ti->start(), ti->value(), ti->end()-ti->start(), ti->label()); to.addPoint(point); } } bool IntervalLayer::snapToFeatureFrame(View *v, int &frame, size_t &resolution, SnapType snap) const { if (!m_model) return Layer::snapToFeatureFrame(v, frame, resolution, snap); resolution = m_model->getResolution(); IntervalList& intervals = m_model->intervals(); int snapped, best = frame; bool found = false; unsigned int dist, distMin; dist = distMin = m_model->getEndFrame(); for (IntervalListConstIterator i = intervals.begin(); i != intervals.end(); ++i) { TimeIntervalPtr ti = (*i); switch (snap) { case SnapRight: if (ti->start() >= frame) { dist = ti->start() - frame; snapped = ti->start(); } else if (ti->end() >= frame) { dist = ti->end() - frame; snapped = ti->end(); } break; case SnapLeft: if (ti->end() <= frame) { dist = frame - ti->end(); snapped = ti->end(); } else if (ti->start() <= frame) { dist = frame - ti->start(); snapped = ti->start(); } break; case SnapNearest: { int distS = abs(ti->start() - frame); int distE = abs(ti->end() - frame); if (distS < distE) { dist = distS; snapped = ti->start(); } else { dist = distE; snapped = ti->end(); } } break; case SnapNeighbouring: { int distS = abs(ti->start() - frame); int distE = abs(ti->end() - frame); if (distS < 5) { dist = distS; snapped = ti->start(); } else if (distE < 5) { dist = distE; snapped = ti->end(); } } break; } if (dist < distMin) { found = true; distMin = dist; best = snapped; } } if (found) { frame = best; } return found; }