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@376: #include "ColourDatabase.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@316: #include Chris@360: #include Chris@35: Chris@35: #include Chris@35: #include Chris@35: Chris@44: TextLayer::TextLayer() : Chris@287: SingleColourLayer(), 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@1408: m_editingCommand(nullptr) Chris@35: { Chris@44: Chris@35: } Chris@35: Chris@1470: int Chris@1470: TextLayer::getCompletion(LayerGeometryProvider *) const Chris@1470: { Chris@1470: auto model = ModelById::get(m_model); Chris@1470: if (model) return model->getCompletion(); Chris@1470: else return 0; Chris@1470: } Chris@1470: Chris@35: void Chris@1471: TextLayer::setModel(ModelId modelId) Chris@35: { Chris@1471: auto newModel = ModelById::getAs(modelId); Chris@1471: Chris@1471: if (!modelId.isNone() && !newModel) { Chris@1471: throw std::logic_error("Not a TextModel"); Chris@1471: } Chris@1471: Chris@1471: if (m_model == modelId) return; Chris@1471: m_model = modelId; Chris@35: Chris@1471: if (newModel) { Chris@1471: connectSignals(m_model); Chris@1471: } Chris@35: Chris@35: emit modelReplaced(); Chris@35: } Chris@35: Chris@35: Layer::PropertyList Chris@35: TextLayer::getProperties() const Chris@35: { Chris@287: PropertyList list = SingleColourLayer::getProperties(); Chris@35: return list; Chris@35: } Chris@35: Chris@87: QString Chris@87: TextLayer::getPropertyLabel(const PropertyName &name) const Chris@87: { Chris@287: return SingleColourLayer::getPropertyLabel(name); Chris@87: } Chris@87: Chris@35: Layer::PropertyType Chris@287: TextLayer::getPropertyType(const PropertyName &name) const Chris@35: { Chris@287: return SingleColourLayer::getPropertyType(name); Chris@35: } Chris@35: Chris@35: int Chris@35: TextLayer::getPropertyRangeAndValue(const PropertyName &name, Chris@1266: int *min, int *max, int *deflt) const Chris@35: { Chris@287: return SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); Chris@35: } Chris@35: Chris@35: QString Chris@35: TextLayer::getPropertyValueLabel(const PropertyName &name, Chris@1266: int value) const Chris@35: { Chris@287: return SingleColourLayer::getPropertyValueLabel(name, value); Chris@35: } Chris@35: Chris@35: void Chris@35: TextLayer::setProperty(const PropertyName &name, int value) Chris@35: { Chris@287: SingleColourLayer::setProperty(name, value); Chris@35: } Chris@35: Chris@79: bool Chris@908: TextLayer::getValueExtents(double &, double &, bool &, QString &) const Chris@79: { Chris@79: return false; Chris@79: } Chris@79: Chris@35: bool Chris@918: TextLayer::isLayerScrollable(const LayerGeometryProvider *v) const Chris@35: { Chris@35: QPoint discard; Chris@44: return !v->shouldIlluminateLocalFeatures(this, discard); Chris@35: } Chris@35: Chris@1437: EventVector Chris@918: TextLayer::getLocalPoints(LayerGeometryProvider *v, int x, int y) const Chris@35: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model) return {}; Chris@35: Chris@1437: int overlap = ViewManager::scalePixelSize(150); Chris@35: Chris@1437: sv_frame_t frame0 = v->getFrameForX(-overlap); Chris@1437: sv_frame_t frame1 = v->getFrameForX(v->getPaintWidth() + overlap); Chris@1437: Chris@1474: EventVector points(model->getEventsSpanning(frame0, frame1 - frame0)); Chris@35: Chris@1437: EventVector rv; Chris@552: QFontMetrics metrics = QFontMetrics(QFont()); Chris@35: Chris@1437: for (EventVector::iterator i = points.begin(); i != points.end(); ++i) { Chris@35: Chris@1437: Event p(*i); Chris@35: Chris@1437: int px = v->getXForFrame(p.getFrame()); Chris@1437: int py = getYForHeight(v, p.getValue()); Chris@35: Chris@1437: QString label = p.getLabel(); Chris@1266: if (label == "") { Chris@1266: label = tr(""); Chris@1266: } Chris@35: Chris@1266: QRect rect = metrics.boundingRect Chris@1266: (QRect(0, 0, 150, 200), Chris@1266: Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); Chris@35: Chris@1266: if (py + rect.height() > v->getPaintHeight()) { Chris@1266: if (rect.height() > v->getPaintHeight()) py = 0; Chris@1266: else py = v->getPaintHeight() - rect.height() - 1; Chris@1266: } Chris@35: Chris@1266: if (x >= px && x < px + rect.width() && Chris@1266: y >= py && y < py + rect.height()) { Chris@1437: rv.push_back(p); Chris@1266: } Chris@35: } Chris@35: Chris@35: return rv; Chris@35: } Chris@35: Chris@552: bool Chris@1437: TextLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &p) const Chris@552: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model) return false; Chris@552: Chris@1437: sv_frame_t a = v->getFrameForX(x - ViewManager::scalePixelSize(120)); Chris@1437: sv_frame_t b = v->getFrameForX(x + ViewManager::scalePixelSize(10)); Chris@1474: EventVector onPoints = model->getEventsWithin(a, b); Chris@552: if (onPoints.empty()) return false; Chris@552: Chris@908: double nearestDistance = -1; Chris@552: Chris@1437: for (EventVector::const_iterator i = onPoints.begin(); Chris@552: i != onPoints.end(); ++i) { Chris@552: Chris@1437: double yd = getYForHeight(v, i->getValue()) - y; Chris@1437: double xd = v->getXForFrame(i->getFrame()) - x; Chris@908: double distance = sqrt(yd*yd + xd*xd); Chris@552: Chris@552: if (nearestDistance == -1 || distance < nearestDistance) { Chris@552: nearestDistance = distance; Chris@552: p = *i; Chris@552: } Chris@552: } Chris@552: Chris@552: return true; Chris@552: } Chris@552: Chris@35: QString Chris@918: TextLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const Chris@35: { Chris@35: int x = pos.x(); Chris@35: Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model || !model->getSampleRate()) return ""; Chris@35: Chris@1437: EventVector points = getLocalPoints(v, x, pos.y()); Chris@35: Chris@35: if (points.empty()) { Chris@1474: if (!model->isReady()) { Chris@1266: return tr("In progress"); Chris@1266: } else { Chris@1266: return ""; Chris@1266: } Chris@35: } Chris@35: Chris@1437: sv_frame_t useFrame = points.begin()->getFrame(); Chris@35: Chris@1474: RealTime rt = RealTime::frame2RealTime(useFrame, model->getSampleRate()); Chris@35: Chris@35: QString text; Chris@35: Chris@1437: if (points.begin()->getLabel() == "") { Chris@1266: text = QString(tr("Time:\t%1\nHeight:\t%2\nLabel:\t%3")) Chris@1266: .arg(rt.toText(true).c_str()) Chris@1437: .arg(points.begin()->getValue()) Chris@1437: .arg(points.begin()->getLabel()); Chris@35: } Chris@35: Chris@44: pos = QPoint(v->getXForFrame(useFrame), Chris@1437: getYForHeight(v, points.begin()->getValue())); Chris@35: return text; Chris@35: } Chris@35: Chris@35: Chris@35: //!!! too much overlap with TimeValueLayer/TimeInstantLayer Chris@35: Chris@35: bool Chris@918: TextLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, Chris@1266: int &resolution, Chris@1266: SnapType snap) const Chris@35: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model) { Chris@1266: return Layer::snapToFeatureFrame(v, frame, resolution, snap); Chris@35: } Chris@35: Chris@1437: // SnapLeft / SnapRight: return frame of nearest feature in that Chris@1437: // direction no matter how far away Chris@1437: // Chris@1437: // SnapNeighbouring: return frame of feature that would be used in Chris@1437: // an editing operation, i.e. closest feature in either direction Chris@1437: // but only if it is "close enough" Chris@1437: Chris@1474: resolution = model->getResolution(); Chris@35: Chris@35: if (snap == SnapNeighbouring) { Chris@1437: EventVector points = getLocalPoints(v, v->getXForFrame(frame), -1); Chris@1266: if (points.empty()) return false; Chris@1437: frame = points.begin()->getFrame(); Chris@1266: return true; Chris@35: } Chris@35: Chris@1437: Event e; Chris@1474: if (model->getNearestEventMatching Chris@1437: (frame, Chris@1437: [](Event) { return true; }, Chris@1437: snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward, Chris@1437: e)) { Chris@1437: frame = e.getFrame(); Chris@1437: return true; Chris@35: } Chris@35: Chris@1437: return false; Chris@35: } Chris@35: Chris@35: int Chris@918: TextLayer::getYForHeight(LayerGeometryProvider *v, double height) const Chris@35: { Chris@918: int h = v->getPaintHeight(); Chris@35: return h - int(height * h); Chris@35: } Chris@35: Chris@908: double Chris@918: TextLayer::getHeightForY(LayerGeometryProvider *v, int y) const Chris@35: { Chris@918: int h = v->getPaintHeight(); Chris@908: return double(h - y) / h; Chris@35: } Chris@35: Chris@35: void Chris@916: TextLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const Chris@35: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model || !model->isOK()) return; Chris@35: Chris@1474: sv_samplerate_t sampleRate = 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@1437: int overlap = ViewManager::scalePixelSize(150); Chris@1437: sv_frame_t frame0 = v->getFrameForX(x0 - overlap); Chris@1437: sv_frame_t frame1 = v->getFrameForX(x1 + overlap); Chris@35: Chris@1474: EventVector points(model->getEventsWithin(frame0, frame1 - frame0, 2)); Chris@35: if (points.empty()) return; Chris@35: Chris@287: QColor brushColour(getBaseQColor()); 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@287: penColour = v->getForeground(); Chris@35: Chris@587: // SVDEBUG << "TextLayer::paint: resolution is " Chris@1474: // << model->getResolution() << " frames" << endl; Chris@35: Chris@35: QPoint localPos; Chris@1437: Event illuminatePoint(0); Chris@850: bool shouldIlluminate = false; Chris@35: Chris@44: if (v->shouldIlluminateLocalFeatures(this, localPos)) { Chris@552: shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(), Chris@552: illuminatePoint); Chris@35: } Chris@35: Chris@35: int boxMaxWidth = 150; Chris@35: int boxMaxHeight = 200; Chris@35: Chris@35: paint.save(); Chris@918: paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->getPaintHeight()); Chris@35: Chris@1437: for (EventVector::const_iterator i = points.begin(); Chris@1266: i != points.end(); ++i) { Chris@35: Chris@1437: Event p(*i); Chris@35: Chris@1437: int x = v->getXForFrame(p.getFrame()); Chris@1437: int y = getYForHeight(v, p.getValue()); Chris@35: Chris@1437: if (!shouldIlluminate || illuminatePoint != p) { Chris@1266: paint.setPen(penColour); Chris@1266: paint.setBrush(brushColour); Chris@552: } else { Chris@1266: paint.setBrush(penColour); Chris@287: paint.setPen(v->getBackground()); Chris@1266: } Chris@35: Chris@1437: QString label = p.getLabel(); Chris@1266: if (label == "") { Chris@1266: label = tr(""); Chris@1266: } Chris@35: Chris@1266: QRect boxRect = paint.fontMetrics().boundingRect Chris@1266: (QRect(0, 0, boxMaxWidth, boxMaxHeight), Chris@1266: Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); Chris@35: Chris@1266: QRect textRect = QRect(3, 2, boxRect.width(), boxRect.height()); Chris@1266: boxRect = QRect(0, 0, boxRect.width() + 6, boxRect.height() + 2); Chris@35: Chris@1266: if (y + boxRect.height() > v->getPaintHeight()) { Chris@1266: if (boxRect.height() > v->getPaintHeight()) y = 0; Chris@1266: else y = v->getPaintHeight() - boxRect.height() - 1; Chris@1266: } Chris@35: Chris@1266: boxRect = QRect(x, y, boxRect.width(), boxRect.height()); Chris@1266: textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height()); Chris@35: Chris@1266: // boxRect = QRect(x, y, boxRect.width(), boxRect.height()); Chris@1266: // textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height()); Chris@35: Chris@1266: paint.setRenderHint(QPainter::Antialiasing, false); Chris@1266: paint.drawRect(boxRect); Chris@35: Chris@1266: paint.setRenderHint(QPainter::Antialiasing, true); Chris@1266: paint.drawText(textRect, Chris@1266: Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, Chris@1266: label); Chris@35: Chris@1437: /// if (p.getLabel() != "") { Chris@1437: /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.getLabel()); Chris@1266: /// } 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@918: TextLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e) Chris@35: { Chris@587: // SVDEBUG << "TextLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl; Chris@35: Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model) { Chris@1266: SVDEBUG << "TextLayer::drawStart: no model" << endl; Chris@1266: return; Chris@35: } Chris@35: Chris@908: sv_frame_t frame = v->getFrameForX(e->x()); Chris@35: if (frame < 0) frame = 0; Chris@1474: frame = frame / model->getResolution() * model->getResolution(); Chris@35: Chris@908: double height = getHeightForY(v, e->y()); Chris@35: Chris@1437: m_editingPoint = Event(frame, float(height), ""); Chris@35: m_originalPoint = m_editingPoint; Chris@35: Chris@376: if (m_editingCommand) finish(m_editingCommand); Chris@1474: m_editingCommand = new ChangeEventsCommand(m_model.untyped, "Add Label"); Chris@1437: m_editingCommand->add(m_editingPoint); Chris@35: Chris@35: m_editing = true; Chris@35: } Chris@35: Chris@35: void Chris@918: TextLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e) Chris@35: { Chris@587: // SVDEBUG << "TextLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl; Chris@35: Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model || !m_editing) return; Chris@35: Chris@908: sv_frame_t frame = v->getFrameForX(e->x()); Chris@35: if (frame < 0) frame = 0; Chris@1474: frame = frame / model->getResolution() * model->getResolution(); Chris@35: Chris@908: double height = getHeightForY(v, e->y()); Chris@35: Chris@1437: m_editingCommand->remove(m_editingPoint); Chris@1437: m_editingPoint = m_editingPoint Chris@1437: .withFrame(frame) Chris@1437: .withValue(float(height)); Chris@1437: m_editingCommand->add(m_editingPoint); Chris@35: } Chris@35: Chris@35: void Chris@918: TextLayer::drawEnd(LayerGeometryProvider *v, QMouseEvent *) Chris@35: { Chris@587: // SVDEBUG << "TextLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl; Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model || !m_editing) return; Chris@36: Chris@36: bool ok = false; Chris@918: QString label = QInputDialog::getText(v->getView(), tr("Enter label"), Chris@1266: tr("Please enter a new label:"), Chris@1266: QLineEdit::Normal, "", &ok); Chris@36: Chris@1437: m_editingCommand->remove(m_editingPoint); Chris@1437: Chris@36: if (ok) { Chris@1437: m_editingPoint = m_editingPoint Chris@1437: .withLabel(label); Chris@1437: m_editingCommand->add(m_editingPoint); Chris@36: } Chris@36: Chris@376: finish(m_editingCommand); Chris@1408: m_editingCommand = nullptr; Chris@35: m_editing = false; Chris@35: } Chris@35: Chris@35: void Chris@918: TextLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e) Chris@335: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model) return; Chris@335: Chris@552: if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; Chris@335: Chris@335: if (m_editingCommand) { Chris@1266: finish(m_editingCommand); Chris@1408: m_editingCommand = nullptr; Chris@335: } Chris@335: Chris@335: m_editing = true; Chris@335: } Chris@335: Chris@335: void Chris@918: TextLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *) Chris@335: { Chris@335: } Chris@335: Chris@335: void Chris@918: TextLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e) Chris@335: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model || !m_editing) return; Chris@335: Chris@335: m_editing = false; Chris@335: Chris@1437: Event p; Chris@552: if (!getPointToDrag(v, e->x(), e->y(), p)) return; Chris@1437: if (p.getFrame() != m_editingPoint.getFrame() || Chris@1437: p.getValue() != m_editingPoint.getValue()) return; Chris@335: Chris@1474: m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Erase Point")); Chris@1437: m_editingCommand->remove(m_editingPoint); Chris@376: finish(m_editingCommand); Chris@1408: m_editingCommand = nullptr; Chris@335: m_editing = false; Chris@335: } Chris@335: Chris@335: void Chris@918: TextLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e) Chris@35: { Chris@587: // SVDEBUG << "TextLayer::editStart(" << e->x() << "," << e->y() << ")" << endl; Chris@35: Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model) return; Chris@35: Chris@552: if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) { Chris@552: return; Chris@552: } Chris@35: Chris@36: m_editOrigin = e->pos(); Chris@35: m_originalPoint = m_editingPoint; Chris@35: Chris@35: if (m_editingCommand) { Chris@1266: finish(m_editingCommand); Chris@1408: m_editingCommand = nullptr; Chris@35: } Chris@35: Chris@35: m_editing = true; Chris@35: } Chris@35: Chris@35: void Chris@918: TextLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e) Chris@35: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model || !m_editing) return; Chris@35: Chris@1437: sv_frame_t frameDiff = Chris@1437: v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x()); Chris@1437: double heightDiff = Chris@1437: getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y()); Chris@36: Chris@1437: sv_frame_t frame = m_originalPoint.getFrame() + frameDiff; Chris@1437: double height = m_originalPoint.getValue() + heightDiff; Chris@36: Chris@35: if (frame < 0) frame = 0; Chris@1474: frame = (frame / model->getResolution()) * model->getResolution(); Chris@35: Chris@35: if (!m_editingCommand) { Chris@1474: m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Drag Label")); Chris@35: } Chris@35: Chris@1437: m_editingCommand->remove(m_editingPoint); Chris@1437: m_editingPoint = m_editingPoint Chris@1437: .withFrame(frame) Chris@1437: .withValue(float(height)); Chris@1437: m_editingCommand->add(m_editingPoint); Chris@35: } Chris@35: Chris@35: void Chris@918: TextLayer::editEnd(LayerGeometryProvider *, QMouseEvent *) Chris@35: { Chris@587: // SVDEBUG << "TextLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl; Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model || !m_editing) return; Chris@35: Chris@35: if (m_editingCommand) { Chris@35: Chris@1266: QString newName = m_editingCommand->getName(); Chris@35: Chris@1437: if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) { Chris@1437: if (m_editingPoint.getValue() != m_originalPoint.getValue()) { Chris@1266: newName = tr("Move Label"); Chris@1266: } else { Chris@1266: newName = tr("Move Label Horizontally"); Chris@1266: } Chris@1266: } else { Chris@1266: newName = tr("Move Label Vertically"); Chris@1266: } Chris@35: Chris@1266: m_editingCommand->setName(newName); Chris@1266: finish(m_editingCommand); Chris@35: } Chris@35: Chris@1408: m_editingCommand = nullptr; Chris@35: m_editing = false; Chris@35: } Chris@35: Chris@255: bool Chris@918: TextLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e) Chris@36: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model) return false; Chris@36: Chris@1437: Event text; Chris@552: if (!getPointToDrag(v, e->x(), e->y(), text)) return false; Chris@36: Chris@1437: QString label = text.getLabel(); Chris@36: Chris@36: bool ok = false; Chris@918: label = QInputDialog::getText(v->getView(), tr("Enter label"), Chris@1266: tr("Please enter a new label:"), Chris@1266: QLineEdit::Normal, label, &ok); Chris@1437: if (ok && label != text.getLabel()) { Chris@1437: ChangeEventsCommand *command = Chris@1474: new ChangeEventsCommand(m_model.untyped, tr("Re-Label Point")); Chris@1437: command->remove(text); Chris@1437: command->add(text.withLabel(label)); Chris@1437: finish(command); Chris@36: } Chris@255: Chris@255: return true; Chris@36: } Chris@36: Chris@43: void Chris@908: TextLayer::moveSelection(Selection s, sv_frame_t newStartFrame) Chris@43: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model) return; Chris@99: Chris@1437: ChangeEventsCommand *command = Chris@1474: new ChangeEventsCommand(m_model.untyped, tr("Drag Selection")); Chris@43: Chris@1437: EventVector points = Chris@1474: model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); Chris@43: Chris@1437: for (Event p: points) { Chris@1437: command->remove(p); Chris@1437: Event moved = p.withFrame(p.getFrame() + Chris@1437: newStartFrame - s.getStartFrame()); Chris@1437: command->add(moved); Chris@43: } Chris@43: Chris@376: finish(command); Chris@43: } Chris@43: Chris@43: void Chris@43: TextLayer::resizeSelection(Selection s, Selection newSize) Chris@43: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model) return; Chris@99: Chris@1437: ChangeEventsCommand *command = Chris@1474: new ChangeEventsCommand(m_model.untyped, tr("Resize Selection")); Chris@43: Chris@1437: EventVector points = Chris@1474: model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); Chris@43: Chris@1437: double ratio = double(newSize.getDuration()) / double(s.getDuration()); Chris@1437: double oldStart = double(s.getStartFrame()); Chris@1437: double newStart = double(newSize.getStartFrame()); Chris@1437: Chris@1437: for (Event p: points) { Chris@43: Chris@1437: double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart; Chris@43: Chris@1437: Event newPoint = p Chris@1437: .withFrame(lrint(newFrame)); Chris@1437: command->remove(p); Chris@1437: command->add(newPoint); Chris@43: } Chris@43: Chris@376: finish(command); Chris@43: } Chris@43: Chris@76: void Chris@76: TextLayer::deleteSelection(Selection s) Chris@76: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model) return; Chris@99: Chris@1437: ChangeEventsCommand *command = Chris@1474: new ChangeEventsCommand(m_model.untyped, tr("Delete Selection")); Chris@76: Chris@1437: EventVector points = Chris@1474: model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); Chris@76: Chris@1437: for (Event p: points) { Chris@1437: command->remove(p); Chris@76: } Chris@76: Chris@376: finish(command); Chris@76: } Chris@76: Chris@76: void Chris@918: TextLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to) Chris@76: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model) return; Chris@99: Chris@1437: EventVector points = Chris@1474: model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); Chris@76: Chris@1437: for (Event p: points) { Chris@1437: to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame()))); Chris@76: } Chris@76: } Chris@76: Chris@125: bool Chris@1533: TextLayer::paste(LayerGeometryProvider *v, const Clipboard &from, Chris@1533: sv_frame_t /* frameOffset */, bool /* interactive */) Chris@76: { Chris@1474: auto model = ModelById::getAs(m_model); Chris@1474: if (!model) return false; Chris@99: Chris@1423: const EventVector &points = from.getPoints(); Chris@76: Chris@360: bool realign = false; Chris@360: Chris@360: if (clipboardHasDifferentAlignment(v, from)) { Chris@360: Chris@360: QMessageBox::StandardButton button = Chris@918: QMessageBox::question(v->getView(), tr("Re-align pasted items?"), Chris@360: 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@360: QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, Chris@360: QMessageBox::Yes); Chris@360: Chris@360: if (button == QMessageBox::Cancel) { Chris@360: return false; Chris@360: } Chris@360: Chris@360: if (button == QMessageBox::Yes) { Chris@360: realign = true; Chris@360: } Chris@360: } Chris@360: Chris@1437: ChangeEventsCommand *command = Chris@1474: new ChangeEventsCommand(m_model.untyped, tr("Paste")); Chris@76: Chris@908: double valueMin = 0.0, valueMax = 1.0; Chris@1423: for (EventVector::const_iterator i = points.begin(); Chris@125: i != points.end(); ++i) { Chris@1423: if (i->hasValue()) { 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@1423: for (EventVector::const_iterator i = points.begin(); Chris@76: i != points.end(); ++i) { Chris@76: Chris@908: sv_frame_t frame = 0; Chris@360: Chris@360: if (!realign) { Chris@360: Chris@360: frame = i->getFrame(); Chris@360: Chris@360: } else { Chris@360: Chris@1423: if (i->hasReferenceFrame()) { Chris@360: frame = i->getReferenceFrame(); Chris@360: frame = alignFromReference(v, frame); Chris@360: } else { Chris@360: frame = i->getFrame(); Chris@360: } Chris@76: } Chris@360: Chris@1533: Event p = i->withFrame(frame); Chris@1533: Chris@1437: Event newPoint = p; Chris@1437: if (p.hasValue()) { Chris@1437: newPoint = newPoint.withValue(float((i->getValue() - valueMin) / Chris@1437: (valueMax - valueMin))); Chris@125: } else { Chris@1437: newPoint = newPoint.withValue(0.5f); Chris@125: } Chris@125: Chris@1437: if (!p.hasLabel()) { Chris@1437: if (p.hasValue()) { Chris@1437: newPoint = newPoint.withLabel(QString("%1").arg(p.getValue())); Chris@1437: } else { Chris@1437: newPoint = newPoint.withLabel(tr("New Point")); Chris@1437: } Chris@125: } Chris@76: Chris@1437: command->add(newPoint); Chris@76: } Chris@76: Chris@376: finish(command); Chris@125: return true; Chris@76: } Chris@76: Chris@287: int Chris@287: TextLayer::getDefaultColourHint(bool darkbg, bool &impose) Chris@287: { Chris@287: impose = false; Chris@287: return ColourDatabase::getInstance()->getColourIndex Chris@287: (QString(darkbg ? "Bright Orange" : "Orange")); Chris@287: } Chris@287: Chris@316: void Chris@316: TextLayer::toXml(QTextStream &stream, Chris@316: QString indent, QString extraAttributes) const Chris@35: { Chris@316: SingleColourLayer::toXml(stream, indent, extraAttributes); Chris@35: } Chris@35: Chris@35: void Chris@35: TextLayer::setProperties(const QXmlAttributes &attributes) Chris@35: { Chris@287: SingleColourLayer::setProperties(attributes); Chris@35: } Chris@35: