Chris@58: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@35: Chris@35: /* 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@35: 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@35: */ Chris@35: Chris@35: #include "TextLayer.h" Chris@35: Chris@128: #include "data/model/Model.h" Chris@35: #include "base/RealTime.h" Chris@35: #include "base/Profiler.h" Chris@128: #include "view/View.h" Chris@35: Chris@128: #include "data/model/TextModel.h" Chris@35: Chris@35: #include Chris@35: #include Chris@36: #include Chris@35: Chris@35: #include Chris@35: #include Chris@35: Chris@44: TextLayer::TextLayer() : Chris@44: Layer(), Chris@35: m_model(0), Chris@35: m_editing(false), Chris@35: m_originalPoint(0, 0.0, tr("Empty Label")), Chris@35: m_editingPoint(0, 0.0, tr("Empty Label")), Chris@35: m_editingCommand(0), Chris@35: m_colour(255, 150, 50) // orange Chris@35: { Chris@44: Chris@35: } Chris@35: Chris@35: void Chris@35: TextLayer::setModel(TextModel *model) Chris@35: { Chris@35: if (m_model == model) return; Chris@35: m_model = model; Chris@35: Chris@35: connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); Chris@35: connect(m_model, SIGNAL(modelChanged(size_t, size_t)), Chris@35: this, SIGNAL(modelChanged(size_t, size_t))); Chris@35: Chris@35: connect(m_model, SIGNAL(completionChanged()), Chris@35: this, SIGNAL(modelCompletionChanged())); Chris@35: Chris@36: // std::cerr << "TextLayer::setModel(" << model << ")" << std::endl; Chris@35: Chris@35: emit modelReplaced(); Chris@35: } Chris@35: Chris@35: Layer::PropertyList Chris@35: TextLayer::getProperties() const Chris@35: { Chris@35: PropertyList list; Chris@87: list.push_back("Colour"); Chris@35: return list; Chris@35: } Chris@35: Chris@87: QString Chris@87: TextLayer::getPropertyLabel(const PropertyName &name) const Chris@87: { Chris@87: if (name == "Colour") return tr("Colour"); Chris@93: return ""; Chris@87: } Chris@87: Chris@35: Layer::PropertyType Chris@248: TextLayer::getPropertyType(const PropertyName &) const Chris@35: { Chris@35: return ValueProperty; Chris@35: } Chris@35: Chris@35: int Chris@35: TextLayer::getPropertyRangeAndValue(const PropertyName &name, Chris@216: int *min, int *max, int *deflt) const Chris@35: { Chris@35: //!!! factor this colour handling stuff out into a colour manager class Chris@35: Chris@216: int val = 0; Chris@35: Chris@87: if (name == "Colour") { Chris@35: Chris@35: if (min) *min = 0; Chris@35: if (max) *max = 5; Chris@216: if (deflt) *deflt = 0; Chris@35: Chris@216: if (m_colour == Qt::black) val = 0; Chris@216: else if (m_colour == Qt::darkRed) val = 1; Chris@216: else if (m_colour == Qt::darkBlue) val = 2; Chris@216: else if (m_colour == Qt::darkGreen) val = 3; Chris@216: else if (m_colour == QColor(200, 50, 255)) val = 4; Chris@216: else if (m_colour == QColor(255, 150, 50)) val = 5; Chris@35: Chris@35: } else { Chris@35: Chris@216: val = Layer::getPropertyRangeAndValue(name, min, max, deflt); Chris@35: } Chris@35: Chris@216: return val; Chris@35: } Chris@35: Chris@35: QString Chris@35: TextLayer::getPropertyValueLabel(const PropertyName &name, Chris@35: int value) const Chris@35: { Chris@87: if (name == "Colour") { Chris@35: switch (value) { Chris@35: default: Chris@35: case 0: return tr("Black"); Chris@35: case 1: return tr("Red"); Chris@35: case 2: return tr("Blue"); Chris@35: case 3: return tr("Green"); Chris@35: case 4: return tr("Purple"); Chris@35: case 5: return tr("Orange"); Chris@35: } Chris@35: } Chris@35: return tr(""); Chris@35: } Chris@35: Chris@35: void Chris@35: TextLayer::setProperty(const PropertyName &name, int value) Chris@35: { Chris@87: if (name == "Colour") { Chris@35: switch (value) { Chris@35: default: Chris@35: case 0: setBaseColour(Qt::black); break; Chris@35: case 1: setBaseColour(Qt::darkRed); break; Chris@35: case 2: setBaseColour(Qt::darkBlue); break; Chris@35: case 3: setBaseColour(Qt::darkGreen); break; Chris@35: case 4: setBaseColour(QColor(200, 50, 255)); break; Chris@35: case 5: setBaseColour(QColor(255, 150, 50)); break; Chris@35: } Chris@35: } Chris@35: } Chris@35: Chris@79: bool Chris@248: TextLayer::getValueExtents(float &, float &, bool &, QString &) const Chris@79: { Chris@79: return false; Chris@79: } Chris@79: Chris@35: void Chris@35: TextLayer::setBaseColour(QColor colour) Chris@35: { Chris@35: if (m_colour == colour) return; Chris@35: m_colour = colour; Chris@35: emit layerParametersChanged(); Chris@35: } Chris@35: Chris@35: bool Chris@44: TextLayer::isLayerScrollable(const View *v) const Chris@35: { Chris@35: QPoint discard; Chris@44: return !v->shouldIlluminateLocalFeatures(this, discard); Chris@35: } Chris@35: Chris@35: Chris@35: TextModel::PointList Chris@44: TextLayer::getLocalPoints(View *v, int x, int y) const Chris@35: { Chris@35: if (!m_model) return TextModel::PointList(); Chris@35: Chris@44: long frame0 = v->getFrameForX(-150); Chris@44: long frame1 = v->getFrameForX(v->width() + 150); Chris@35: Chris@35: TextModel::PointList points(m_model->getPoints(frame0, frame1)); Chris@35: Chris@35: TextModel::PointList rv; Chris@35: QFontMetrics metrics = QPainter().fontMetrics(); Chris@35: Chris@35: for (TextModel::PointList::iterator i = points.begin(); Chris@35: i != points.end(); ++i) { Chris@35: Chris@35: const TextModel::Point &p(*i); Chris@35: Chris@44: int px = v->getXForFrame(p.frame); Chris@44: int py = getYForHeight(v, p.height); Chris@35: Chris@35: QString label = p.label; Chris@35: if (label == "") { Chris@35: label = tr(""); Chris@35: } Chris@35: Chris@35: QRect rect = metrics.boundingRect Chris@35: (QRect(0, 0, 150, 200), Chris@35: Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); Chris@35: Chris@44: if (py + rect.height() > v->height()) { Chris@44: if (rect.height() > v->height()) py = 0; Chris@44: else py = v->height() - rect.height() - 1; Chris@35: } Chris@35: Chris@35: if (x >= px && x < px + rect.width() && Chris@35: y >= py && y < py + rect.height()) { Chris@35: rv.insert(p); Chris@35: } Chris@35: } Chris@35: Chris@35: return rv; Chris@35: } Chris@35: Chris@35: QString Chris@44: TextLayer::getFeatureDescription(View *v, QPoint &pos) const Chris@35: { Chris@35: int x = pos.x(); Chris@35: Chris@35: if (!m_model || !m_model->getSampleRate()) return ""; Chris@35: Chris@44: TextModel::PointList points = getLocalPoints(v, x, pos.y()); Chris@35: Chris@35: if (points.empty()) { Chris@35: if (!m_model->isReady()) { Chris@35: return tr("In progress"); Chris@35: } else { Chris@35: return ""; Chris@35: } Chris@35: } Chris@35: Chris@35: long useFrame = points.begin()->frame; Chris@35: Chris@35: RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate()); Chris@35: Chris@35: QString text; Chris@35: Chris@35: if (points.begin()->label == "") { Chris@35: text = QString(tr("Time:\t%1\nHeight:\t%2\nLabel:\t%3")) Chris@35: .arg(rt.toText(true).c_str()) Chris@35: .arg(points.begin()->height) Chris@35: .arg(points.begin()->label); Chris@35: } Chris@35: Chris@44: pos = QPoint(v->getXForFrame(useFrame), Chris@44: getYForHeight(v, points.begin()->height)); Chris@35: return text; Chris@35: } Chris@35: Chris@35: Chris@35: //!!! too much overlap with TimeValueLayer/TimeInstantLayer Chris@35: Chris@35: bool Chris@44: TextLayer::snapToFeatureFrame(View *v, int &frame, Chris@35: size_t &resolution, Chris@35: SnapType snap) const Chris@35: { Chris@35: if (!m_model) { Chris@44: return Layer::snapToFeatureFrame(v, frame, resolution, snap); Chris@35: } Chris@35: Chris@35: resolution = m_model->getResolution(); Chris@35: TextModel::PointList points; Chris@35: Chris@35: if (snap == SnapNeighbouring) { Chris@35: Chris@44: points = getLocalPoints(v, v->getXForFrame(frame), -1); Chris@35: if (points.empty()) return false; Chris@35: frame = points.begin()->frame; Chris@35: return true; Chris@35: } Chris@35: Chris@35: points = m_model->getPoints(frame, frame); Chris@35: int snapped = frame; Chris@35: bool found = false; Chris@35: Chris@35: for (TextModel::PointList::const_iterator i = points.begin(); Chris@35: i != points.end(); ++i) { Chris@35: Chris@35: if (snap == SnapRight) { Chris@35: Chris@35: if (i->frame > frame) { Chris@35: snapped = i->frame; Chris@35: found = true; Chris@35: break; Chris@35: } Chris@35: Chris@35: } else if (snap == SnapLeft) { Chris@35: Chris@35: if (i->frame <= frame) { Chris@35: snapped = i->frame; Chris@35: found = true; // don't break, as the next may be better Chris@35: } else { Chris@35: break; Chris@35: } Chris@35: Chris@35: } else { // nearest Chris@35: Chris@35: TextModel::PointList::const_iterator j = i; Chris@35: ++j; Chris@35: Chris@35: if (j == points.end()) { Chris@35: Chris@35: snapped = i->frame; Chris@35: found = true; Chris@35: break; Chris@35: Chris@35: } else if (j->frame >= frame) { Chris@35: Chris@35: if (j->frame - frame < frame - i->frame) { Chris@35: snapped = j->frame; Chris@35: } else { Chris@35: snapped = i->frame; Chris@35: } Chris@35: found = true; Chris@35: break; Chris@35: } Chris@35: } Chris@35: } Chris@35: Chris@35: frame = snapped; Chris@35: return found; Chris@35: } Chris@35: Chris@35: int Chris@44: TextLayer::getYForHeight(View *v, float height) const Chris@35: { Chris@44: int h = v->height(); Chris@35: return h - int(height * h); Chris@35: } Chris@35: Chris@35: float Chris@44: TextLayer::getHeightForY(View *v, int y) const Chris@35: { Chris@44: int h = v->height(); Chris@35: return float(h - y) / h; Chris@35: } Chris@35: Chris@35: void Chris@44: TextLayer::paint(View *v, QPainter &paint, QRect rect) const Chris@35: { Chris@35: if (!m_model || !m_model->isOK()) return; Chris@35: Chris@35: int sampleRate = m_model->getSampleRate(); Chris@35: if (!sampleRate) return; Chris@35: Chris@35: // Profiler profiler("TextLayer::paint", true); Chris@35: Chris@35: int x0 = rect.left(), x1 = rect.right(); Chris@44: long frame0 = v->getFrameForX(x0); Chris@44: long frame1 = v->getFrameForX(x1); Chris@35: Chris@35: TextModel::PointList points(m_model->getPoints(frame0, frame1)); Chris@35: if (points.empty()) return; Chris@35: Chris@35: QColor brushColour(m_colour); Chris@35: Chris@44: int h, s, val; Chris@44: brushColour.getHsv(&h, &s, &val); Chris@36: brushColour.setHsv(h, s, 255, 100); Chris@36: Chris@36: QColor penColour; Chris@44: if (v->hasLightBackground()) { Chris@36: penColour = Qt::black; Chris@35: } else { Chris@36: penColour = Qt::white; Chris@35: } Chris@35: Chris@35: // std::cerr << "TextLayer::paint: resolution is " Chris@35: // << m_model->getResolution() << " frames" << std::endl; Chris@35: Chris@35: QPoint localPos; Chris@35: long illuminateFrame = -1; Chris@35: Chris@44: if (v->shouldIlluminateLocalFeatures(this, localPos)) { Chris@44: TextModel::PointList localPoints = getLocalPoints(v, localPos.x(), Chris@35: localPos.y()); Chris@35: if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame; Chris@35: } Chris@35: Chris@35: int boxMaxWidth = 150; Chris@35: int boxMaxHeight = 200; Chris@35: Chris@35: paint.save(); Chris@44: paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->height()); Chris@35: Chris@35: for (TextModel::PointList::const_iterator i = points.begin(); Chris@35: i != points.end(); ++i) { Chris@35: Chris@35: const TextModel::Point &p(*i); Chris@35: Chris@44: int x = v->getXForFrame(p.frame); Chris@44: int y = getYForHeight(v, p.height); Chris@35: Chris@35: if (illuminateFrame == p.frame) { Chris@36: paint.setBrush(penColour); Chris@44: if (v->hasLightBackground()) { Chris@36: paint.setPen(Qt::white); Chris@36: } else { Chris@36: paint.setPen(Qt::black); Chris@36: } Chris@36: } else { Chris@36: paint.setPen(penColour); Chris@36: paint.setBrush(brushColour); Chris@35: } Chris@35: Chris@35: QString label = p.label; Chris@35: if (label == "") { Chris@35: label = tr(""); Chris@35: } Chris@35: Chris@35: QRect boxRect = paint.fontMetrics().boundingRect Chris@35: (QRect(0, 0, boxMaxWidth, boxMaxHeight), Chris@35: Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); Chris@35: Chris@35: QRect textRect = QRect(3, 2, boxRect.width(), boxRect.height()); Chris@35: boxRect = QRect(0, 0, boxRect.width() + 6, boxRect.height() + 2); Chris@35: Chris@44: if (y + boxRect.height() > v->height()) { Chris@44: if (boxRect.height() > v->height()) y = 0; Chris@44: else y = v->height() - boxRect.height() - 1; Chris@35: } Chris@35: Chris@35: boxRect = QRect(x, y, boxRect.width(), boxRect.height()); Chris@35: textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height()); Chris@35: Chris@35: // boxRect = QRect(x, y, boxRect.width(), boxRect.height()); Chris@35: // textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height()); Chris@35: Chris@35: paint.setRenderHint(QPainter::Antialiasing, false); Chris@35: paint.drawRect(boxRect); Chris@35: Chris@35: paint.setRenderHint(QPainter::Antialiasing, true); Chris@35: paint.drawText(textRect, Chris@35: Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, Chris@35: label); Chris@35: Chris@35: /// if (p.label != "") { Chris@35: /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label); Chris@35: /// } Chris@35: } Chris@35: Chris@35: paint.restore(); Chris@35: Chris@35: // looks like save/restore doesn't deal with this: Chris@35: paint.setRenderHint(QPainter::Antialiasing, false); Chris@35: } Chris@35: Chris@35: void Chris@44: TextLayer::drawStart(View *v, QMouseEvent *e) Chris@35: { Chris@36: // std::cerr << "TextLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl; Chris@35: Chris@35: if (!m_model) { Chris@35: std::cerr << "TextLayer::drawStart: no model" << std::endl; Chris@35: return; Chris@35: } Chris@35: Chris@44: long frame = v->getFrameForX(e->x()); Chris@35: if (frame < 0) frame = 0; Chris@35: frame = frame / m_model->getResolution() * m_model->getResolution(); Chris@35: Chris@44: float height = getHeightForY(v, e->y()); Chris@35: Chris@35: m_editingPoint = TextModel::Point(frame, height, ""); Chris@35: m_originalPoint = m_editingPoint; Chris@35: Chris@35: if (m_editingCommand) m_editingCommand->finish(); Chris@35: m_editingCommand = new TextModel::EditCommand(m_model, "Add Label"); Chris@35: m_editingCommand->addPoint(m_editingPoint); Chris@35: Chris@35: m_editing = true; Chris@35: } Chris@35: Chris@35: void Chris@44: TextLayer::drawDrag(View *v, QMouseEvent *e) Chris@35: { Chris@36: // std::cerr << "TextLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl; Chris@35: Chris@35: if (!m_model || !m_editing) return; Chris@35: Chris@44: long frame = v->getFrameForX(e->x()); Chris@35: if (frame < 0) frame = 0; Chris@35: frame = frame / m_model->getResolution() * m_model->getResolution(); Chris@35: Chris@44: float height = getHeightForY(v, e->y()); Chris@35: Chris@35: m_editingCommand->deletePoint(m_editingPoint); Chris@35: m_editingPoint.frame = frame; Chris@35: m_editingPoint.height = height; Chris@35: m_editingCommand->addPoint(m_editingPoint); Chris@35: } Chris@35: Chris@35: void Chris@248: TextLayer::drawEnd(View *v, QMouseEvent *) Chris@35: { Chris@36: // std::cerr << "TextLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl; Chris@35: if (!m_model || !m_editing) return; Chris@36: Chris@36: bool ok = false; Chris@44: QString label = QInputDialog::getText(v, tr("Enter label"), Chris@36: tr("Please enter a new label:"), Chris@36: QLineEdit::Normal, "", &ok); Chris@36: Chris@36: if (ok) { Chris@36: TextModel::RelabelCommand *command = Chris@36: new TextModel::RelabelCommand(m_model, m_editingPoint, label); Chris@36: m_editingCommand->addCommand(command); Chris@36: } Chris@36: Chris@35: m_editingCommand->finish(); Chris@35: m_editingCommand = 0; Chris@35: m_editing = false; Chris@35: } Chris@35: Chris@35: void Chris@44: TextLayer::editStart(View *v, QMouseEvent *e) Chris@35: { Chris@36: // std::cerr << "TextLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; Chris@35: Chris@35: if (!m_model) return; Chris@35: Chris@44: TextModel::PointList points = getLocalPoints(v, e->x(), e->y()); Chris@35: if (points.empty()) return; Chris@35: Chris@36: m_editOrigin = e->pos(); Chris@35: m_editingPoint = *points.begin(); Chris@35: m_originalPoint = m_editingPoint; Chris@35: Chris@35: if (m_editingCommand) { Chris@35: m_editingCommand->finish(); Chris@35: m_editingCommand = 0; Chris@35: } Chris@35: Chris@35: m_editing = true; Chris@35: } Chris@35: Chris@35: void Chris@44: TextLayer::editDrag(View *v, QMouseEvent *e) Chris@35: { Chris@35: if (!m_model || !m_editing) return; Chris@35: Chris@44: long frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x()); Chris@44: float heightDiff = getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y()); Chris@36: Chris@36: long frame = m_originalPoint.frame + frameDiff; Chris@36: float height = m_originalPoint.height + heightDiff; Chris@36: Chris@44: // long frame = v->getFrameForX(e->x()); Chris@35: if (frame < 0) frame = 0; Chris@36: frame = (frame / m_model->getResolution()) * m_model->getResolution(); Chris@35: Chris@44: // float height = getHeightForY(v, e->y()); Chris@35: Chris@35: if (!m_editingCommand) { Chris@35: m_editingCommand = new TextModel::EditCommand(m_model, tr("Drag Label")); Chris@35: } Chris@35: Chris@35: m_editingCommand->deletePoint(m_editingPoint); Chris@35: m_editingPoint.frame = frame; Chris@35: m_editingPoint.height = height; Chris@35: m_editingCommand->addPoint(m_editingPoint); Chris@35: } Chris@35: Chris@35: void Chris@248: TextLayer::editEnd(View *, QMouseEvent *) Chris@35: { Chris@36: // std::cerr << "TextLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl; Chris@35: if (!m_model || !m_editing) return; Chris@35: Chris@35: if (m_editingCommand) { Chris@35: Chris@35: QString newName = m_editingCommand->getName(); Chris@35: Chris@35: if (m_editingPoint.frame != m_originalPoint.frame) { Chris@35: if (m_editingPoint.height != m_originalPoint.height) { Chris@35: newName = tr("Move Label"); Chris@35: } else { Chris@36: newName = tr("Move Label Horizontally"); Chris@35: } Chris@35: } else { Chris@36: newName = tr("Move Label Vertically"); Chris@35: } Chris@35: Chris@35: m_editingCommand->setName(newName); Chris@35: m_editingCommand->finish(); Chris@35: } Chris@35: Chris@35: m_editingCommand = 0; Chris@35: m_editing = false; Chris@35: } Chris@35: Chris@255: bool Chris@44: TextLayer::editOpen(View *v, QMouseEvent *e) Chris@36: { Chris@255: if (!m_model) return false; Chris@36: Chris@44: TextModel::PointList points = getLocalPoints(v, e->x(), e->y()); Chris@255: if (points.empty()) return false; Chris@36: Chris@36: QString label = points.begin()->label; Chris@36: Chris@36: bool ok = false; Chris@44: label = QInputDialog::getText(v, tr("Enter label"), Chris@36: tr("Please enter a new label:"), Chris@36: QLineEdit::Normal, label, &ok); Chris@36: if (ok && label != points.begin()->label) { Chris@36: TextModel::RelabelCommand *command = Chris@36: new TextModel::RelabelCommand(m_model, *points.begin(), label); Chris@99: CommandHistory::getInstance()->addCommand(command); Chris@36: } Chris@255: Chris@255: return true; Chris@36: } Chris@36: Chris@43: void Chris@43: TextLayer::moveSelection(Selection s, size_t newStartFrame) Chris@43: { Chris@99: if (!m_model) return; Chris@99: Chris@43: TextModel::EditCommand *command = Chris@43: new TextModel::EditCommand(m_model, tr("Drag Selection")); Chris@43: Chris@43: TextModel::PointList points = Chris@43: m_model->getPoints(s.getStartFrame(), s.getEndFrame()); Chris@43: Chris@43: for (TextModel::PointList::iterator i = points.begin(); Chris@43: i != points.end(); ++i) { Chris@43: Chris@43: if (s.contains(i->frame)) { Chris@43: TextModel::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: TextLayer::resizeSelection(Selection s, Selection newSize) Chris@43: { Chris@99: if (!m_model) return; Chris@99: Chris@43: TextModel::EditCommand *command = Chris@43: new TextModel::EditCommand(m_model, tr("Resize Selection")); Chris@43: Chris@43: TextModel::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 (TextModel::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: TextModel::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@76: void Chris@76: TextLayer::deleteSelection(Selection s) Chris@76: { Chris@99: if (!m_model) return; Chris@99: Chris@76: TextModel::EditCommand *command = Chris@76: new TextModel::EditCommand(m_model, tr("Delete Selection")); Chris@76: Chris@76: TextModel::PointList points = Chris@76: m_model->getPoints(s.getStartFrame(), s.getEndFrame()); Chris@76: Chris@76: for (TextModel::PointList::iterator i = points.begin(); Chris@76: i != points.end(); ++i) { Chris@76: if (s.contains(i->frame)) command->deletePoint(*i); Chris@76: } Chris@76: Chris@76: command->finish(); Chris@76: } Chris@76: Chris@76: void Chris@76: TextLayer::copy(Selection s, Clipboard &to) Chris@76: { Chris@99: if (!m_model) return; Chris@99: Chris@76: TextModel::PointList points = Chris@76: m_model->getPoints(s.getStartFrame(), s.getEndFrame()); Chris@76: Chris@76: for (TextModel::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->height, i->label); Chris@76: to.addPoint(point); Chris@76: } Chris@76: } Chris@76: } Chris@76: Chris@125: bool Chris@248: TextLayer::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: TextModel::EditCommand *command = Chris@76: new TextModel::EditCommand(m_model, tr("Paste")); Chris@76: Chris@125: float valueMin = 0.0, valueMax = 1.0; Chris@125: for (Clipboard::PointList::const_iterator i = points.begin(); Chris@125: i != points.end(); ++i) { Chris@125: if (i->haveValue()) { Chris@125: if (i->getValue() < valueMin) valueMin = i->getValue(); Chris@125: if (i->getValue() > valueMax) valueMax = i->getValue(); Chris@125: } Chris@125: } Chris@125: if (valueMax < valueMin + 1.0) valueMax = valueMin + 1.0; Chris@125: 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: TextModel::Point newPoint(frame); Chris@125: Chris@125: if (i->haveValue()) { Chris@125: newPoint.height = (i->getValue() - valueMin) / (valueMax - valueMin); Chris@125: } else { Chris@125: newPoint.height = 0.5; Chris@125: } Chris@125: 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: } else { Chris@125: newPoint.label = tr("New Point"); Chris@125: } Chris@76: Chris@76: command->addPoint(newPoint); Chris@76: } Chris@76: Chris@76: command->finish(); Chris@125: return true; Chris@76: } Chris@76: Chris@35: QString Chris@35: TextLayer::toXmlString(QString indent, QString extraAttributes) const Chris@35: { Chris@35: return Layer::toXmlString(indent, extraAttributes + Chris@35: QString(" colour=\"%1\"") Chris@35: .arg(encodeColour(m_colour))); Chris@35: } Chris@35: Chris@35: void Chris@35: TextLayer::setProperties(const QXmlAttributes &attributes) Chris@35: { Chris@35: QString colourSpec = attributes.value("colour"); Chris@35: if (colourSpec != "") { Chris@35: QColor colour(colourSpec); Chris@35: if (colour.isValid()) { Chris@35: setBaseColour(QColor(colourSpec)); Chris@35: } Chris@35: } Chris@35: } Chris@35: