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@1408: m_model(nullptr), 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@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@320: connectSignals(m_model); Chris@35: Chris@587: // SVDEBUG << "TextLayer::setModel(" << model << ")" << endl; 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@35: Chris@35: TextModel::PointList Chris@918: TextLayer::getLocalPoints(LayerGeometryProvider *v, int x, int y) const Chris@35: { Chris@35: if (!m_model) return TextModel::PointList(); Chris@35: Chris@908: sv_frame_t frame0 = v->getFrameForX(-150); Chris@918: sv_frame_t frame1 = v->getFrameForX(v->getPaintWidth() + 150); Chris@35: Chris@35: TextModel::PointList points(m_model->getPoints(frame0, frame1)); Chris@35: Chris@35: TextModel::PointList rv; Chris@552: QFontMetrics metrics = QFontMetrics(QFont()); Chris@35: Chris@35: for (TextModel::PointList::iterator i = points.begin(); Chris@1266: i != points.end(); ++i) { Chris@35: Chris@1266: const TextModel::Point &p(*i); Chris@35: Chris@1266: int px = v->getXForFrame(p.frame); Chris@1266: int py = getYForHeight(v, p.height); Chris@35: Chris@1266: QString label = p.label; 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@1266: rv.insert(p); Chris@1266: } Chris@35: } Chris@35: Chris@35: return rv; Chris@35: } Chris@35: Chris@552: bool Chris@918: TextLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, TextModel::Point &p) const Chris@552: { Chris@552: if (!m_model) return false; Chris@552: Chris@908: sv_frame_t a = v->getFrameForX(x - 120); Chris@908: sv_frame_t b = v->getFrameForX(x + 10); Chris@552: TextModel::PointList onPoints = m_model->getPoints(a, b); Chris@552: if (onPoints.empty()) return false; Chris@552: Chris@908: double nearestDistance = -1; Chris@552: Chris@552: for (TextModel::PointList::const_iterator i = onPoints.begin(); Chris@552: i != onPoints.end(); ++i) { Chris@552: Chris@908: double yd = getYForHeight(v, (*i).height) - y; Chris@908: double xd = v->getXForFrame((*i).frame) - 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@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@1266: if (!m_model->isReady()) { Chris@1266: return tr("In progress"); Chris@1266: } else { Chris@1266: return ""; Chris@1266: } Chris@35: } Chris@35: Chris@908: sv_frame_t 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@1266: text = QString(tr("Time:\t%1\nHeight:\t%2\nLabel:\t%3")) Chris@1266: .arg(rt.toText(true).c_str()) Chris@1266: .arg(points.begin()->height) Chris@1266: .arg(points.begin()->label); Chris@35: } Chris@35: Chris@44: pos = QPoint(v->getXForFrame(useFrame), Chris@1266: 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@918: TextLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, Chris@1266: int &resolution, Chris@1266: SnapType snap) const Chris@35: { Chris@35: if (!m_model) { Chris@1266: 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@1266: Chris@1266: points = getLocalPoints(v, v->getXForFrame(frame), -1); Chris@1266: if (points.empty()) return false; Chris@1266: frame = points.begin()->frame; Chris@1266: return true; Chris@35: } Chris@35: Chris@35: points = m_model->getPoints(frame, frame); Chris@908: sv_frame_t snapped = frame; Chris@35: bool found = false; Chris@35: Chris@35: for (TextModel::PointList::const_iterator i = points.begin(); Chris@1266: i != points.end(); ++i) { Chris@35: Chris@1266: if (snap == SnapRight) { Chris@35: Chris@1266: if (i->frame > frame) { Chris@1266: snapped = i->frame; Chris@1266: found = true; Chris@1266: break; Chris@1266: } Chris@35: Chris@1266: } else if (snap == SnapLeft) { Chris@35: Chris@1266: if (i->frame <= frame) { Chris@1266: snapped = i->frame; Chris@1266: found = true; // don't break, as the next may be better Chris@1266: } else { Chris@1266: break; Chris@1266: } Chris@35: Chris@1266: } else { // nearest Chris@35: Chris@1266: TextModel::PointList::const_iterator j = i; Chris@1266: ++j; Chris@35: Chris@1266: if (j == points.end()) { Chris@35: Chris@1266: snapped = i->frame; Chris@1266: found = true; Chris@1266: break; Chris@35: Chris@1266: } else if (j->frame >= frame) { Chris@35: Chris@1266: if (j->frame - frame < frame - i->frame) { Chris@1266: snapped = j->frame; Chris@1266: } else { Chris@1266: snapped = i->frame; Chris@1266: } Chris@1266: found = true; Chris@1266: break; Chris@1266: } Chris@1266: } Chris@35: } Chris@35: Chris@35: frame = snapped; Chris@35: return found; 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@35: if (!m_model || !m_model->isOK()) return; Chris@35: Chris@908: sv_samplerate_t 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@908: sv_frame_t frame0 = v->getFrameForX(x0); Chris@908: sv_frame_t 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@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@1266: // << m_model->getResolution() << " frames" << endl; Chris@35: Chris@35: QPoint localPos; Chris@552: TextModel::Point 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@35: for (TextModel::PointList::const_iterator i = points.begin(); Chris@1266: i != points.end(); ++i) { Chris@35: Chris@1266: const TextModel::Point &p(*i); Chris@35: Chris@1266: int x = v->getXForFrame(p.frame); Chris@1266: int y = getYForHeight(v, p.height); Chris@35: Chris@552: if (!shouldIlluminate || Chris@552: // "illuminatePoint != p" Chris@552: TextModel::Point::Comparator()(illuminatePoint, p) || Chris@552: TextModel::Point::Comparator()(p, illuminatePoint)) { 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@1266: QString label = p.label; 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@1266: /// if (p.label != "") { Chris@1266: /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label); 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@35: if (!m_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@35: frame = frame / m_model->getResolution() * m_model->getResolution(); Chris@35: Chris@908: double height = getHeightForY(v, e->y()); Chris@35: Chris@908: m_editingPoint = TextModel::Point(frame, float(height), ""); Chris@35: m_originalPoint = m_editingPoint; Chris@35: Chris@376: if (m_editingCommand) finish(m_editingCommand); 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@918: TextLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e) Chris@35: { Chris@587: // SVDEBUG << "TextLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl; Chris@35: Chris@35: if (!m_model || !m_editing) return; Chris@35: Chris@908: sv_frame_t 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@908: double height = getHeightForY(v, e->y()); Chris@35: Chris@35: m_editingCommand->deletePoint(m_editingPoint); Chris@35: m_editingPoint.frame = frame; Chris@908: m_editingPoint.height = float(height); Chris@35: m_editingCommand->addPoint(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@35: if (!m_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@36: if (ok) { Chris@1266: TextModel::RelabelCommand *command = Chris@1266: new TextModel::RelabelCommand(m_model, m_editingPoint, label); Chris@1266: m_editingCommand->addCommand(command); Chris@307: } else { Chris@307: m_editingCommand->deletePoint(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@335: if (!m_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@335: if (!m_model || !m_editing) return; Chris@335: Chris@335: m_editing = false; Chris@335: Chris@552: TextModel::Point p(0); Chris@552: if (!getPointToDrag(v, e->x(), e->y(), p)) return; Chris@552: if (p.frame != m_editingPoint.frame || p.height != m_editingPoint.height) return; Chris@335: Chris@335: m_editingCommand = new TextModel::EditCommand Chris@335: (m_model, tr("Erase Point")); Chris@335: Chris@335: m_editingCommand->deletePoint(m_editingPoint); Chris@335: 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@35: if (!m_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@35: if (!m_model || !m_editing) return; Chris@35: Chris@908: sv_frame_t frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x()); Chris@908: double heightDiff = getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y()); Chris@36: Chris@908: sv_frame_t frame = m_originalPoint.frame + frameDiff; Chris@908: double height = m_originalPoint.height + heightDiff; Chris@36: Chris@908: // sv_frame_t 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@908: // double height = getHeightForY(v, e->y()); Chris@35: Chris@35: if (!m_editingCommand) { Chris@1266: 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@908: m_editingPoint.height = float(height); Chris@35: m_editingCommand->addPoint(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@35: if (!m_model || !m_editing) return; Chris@35: Chris@35: if (m_editingCommand) { Chris@35: Chris@1266: QString newName = m_editingCommand->getName(); Chris@35: Chris@1266: if (m_editingPoint.frame != m_originalPoint.frame) { Chris@1266: if (m_editingPoint.height != m_originalPoint.height) { 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@255: if (!m_model) return false; Chris@36: Chris@552: TextModel::Point text(0); Chris@552: if (!getPointToDrag(v, e->x(), e->y(), text)) return false; Chris@36: Chris@552: QString label = text.label; 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@552: if (ok && label != text.label) { Chris@1266: TextModel::RelabelCommand *command = Chris@1266: new TextModel::RelabelCommand(m_model, text, label); Chris@1266: CommandHistory::getInstance()->addCommand(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@99: if (!m_model) return; Chris@99: Chris@43: TextModel::EditCommand *command = Chris@1266: new TextModel::EditCommand(m_model, tr("Drag Selection")); Chris@43: Chris@43: TextModel::PointList points = Chris@1266: m_model->getPoints(s.getStartFrame(), s.getEndFrame()); Chris@43: Chris@43: for (TextModel::PointList::iterator i = points.begin(); Chris@1266: i != points.end(); ++i) { Chris@43: Chris@1266: if (s.contains(i->frame)) { Chris@1266: TextModel::Point newPoint(*i); Chris@1266: newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); Chris@1266: command->deletePoint(*i); Chris@1266: command->addPoint(newPoint); Chris@1266: } 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@99: if (!m_model) return; Chris@99: Chris@43: TextModel::EditCommand *command = Chris@1266: new TextModel::EditCommand(m_model, tr("Resize Selection")); Chris@43: Chris@43: TextModel::PointList points = Chris@1266: m_model->getPoints(s.getStartFrame(), s.getEndFrame()); Chris@43: Chris@43: double ratio = Chris@1266: double(newSize.getEndFrame() - newSize.getStartFrame()) / Chris@1266: double(s.getEndFrame() - s.getStartFrame()); Chris@43: Chris@43: for (TextModel::PointList::iterator i = points.begin(); Chris@1266: i != points.end(); ++i) { Chris@43: Chris@1266: if (s.contains(i->frame)) { Chris@43: Chris@1266: double target = double(i->frame); Chris@1266: target = double(newSize.getStartFrame()) + Chris@1266: target - double(s.getStartFrame()) * ratio; Chris@43: Chris@1266: TextModel::Point newPoint(*i); Chris@1266: newPoint.frame = lrint(target); Chris@1266: command->deletePoint(*i); Chris@1266: command->addPoint(newPoint); Chris@1266: } Chris@43: } Chris@43: Chris@376: finish(command); 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@1266: new TextModel::EditCommand(m_model, tr("Delete Selection")); Chris@76: Chris@76: TextModel::PointList points = Chris@1266: m_model->getPoints(s.getStartFrame(), s.getEndFrame()); Chris@76: Chris@76: for (TextModel::PointList::iterator i = points.begin(); Chris@1266: i != points.end(); ++i) { Chris@1266: if (s.contains(i->frame)) command->deletePoint(*i); 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@99: if (!m_model) return; Chris@99: Chris@76: TextModel::PointList points = Chris@1266: m_model->getPoints(s.getStartFrame(), s.getEndFrame()); Chris@76: Chris@76: for (TextModel::PointList::iterator i = points.begin(); Chris@1266: i != points.end(); ++i) { Chris@1266: if (s.contains(i->frame)) { Chris@76: Clipboard::Point point(i->frame, i->height, i->label); Chris@360: point.setReferenceFrame(alignToReference(v, i->frame)); Chris@76: to.addPoint(point); Chris@76: } Chris@76: } Chris@76: } Chris@76: Chris@125: bool Chris@918: TextLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* 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@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@76: TextModel::EditCommand *command = Chris@1266: new TextModel::EditCommand(m_model, tr("Paste")); Chris@76: Chris@908: double 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@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@360: if (i->haveReferenceFrame()) { 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@76: TextModel::Point newPoint(frame); Chris@125: Chris@125: if (i->haveValue()) { Chris@908: newPoint.height = float((i->getValue() - valueMin) / (valueMax - valueMin)); Chris@125: } else { Chris@908: newPoint.height = 0.5f; 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@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: