annotate layer/TextLayer.cpp @ 1459:42c87368287c

Merge from branch single-point
author Chris Cannam
date Fri, 17 May 2019 10:02:52 +0100
parents e2b6a13a1f69
children 696e569ff21b
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@35 2
Chris@35 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@59 7 This file copyright 2006 Chris Cannam.
Chris@35 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@35 14 */
Chris@35 15
Chris@35 16 #include "TextLayer.h"
Chris@35 17
Chris@128 18 #include "data/model/Model.h"
Chris@35 19 #include "base/RealTime.h"
Chris@35 20 #include "base/Profiler.h"
Chris@376 21 #include "ColourDatabase.h"
Chris@128 22 #include "view/View.h"
Chris@35 23
Chris@128 24 #include "data/model/TextModel.h"
Chris@35 25
Chris@35 26 #include <QPainter>
Chris@35 27 #include <QMouseEvent>
Chris@36 28 #include <QInputDialog>
Chris@316 29 #include <QTextStream>
Chris@360 30 #include <QMessageBox>
Chris@35 31
Chris@35 32 #include <iostream>
Chris@35 33 #include <cmath>
Chris@35 34
Chris@44 35 TextLayer::TextLayer() :
Chris@287 36 SingleColourLayer(),
Chris@1408 37 m_model(nullptr),
Chris@35 38 m_editing(false),
Chris@35 39 m_originalPoint(0, 0.0, tr("Empty Label")),
Chris@35 40 m_editingPoint(0, 0.0, tr("Empty Label")),
Chris@1408 41 m_editingCommand(nullptr)
Chris@35 42 {
Chris@44 43
Chris@35 44 }
Chris@35 45
Chris@35 46 void
Chris@35 47 TextLayer::setModel(TextModel *model)
Chris@35 48 {
Chris@35 49 if (m_model == model) return;
Chris@35 50 m_model = model;
Chris@35 51
Chris@320 52 connectSignals(m_model);
Chris@35 53
Chris@587 54 // SVDEBUG << "TextLayer::setModel(" << model << ")" << endl;
Chris@35 55
Chris@35 56 emit modelReplaced();
Chris@35 57 }
Chris@35 58
Chris@35 59 Layer::PropertyList
Chris@35 60 TextLayer::getProperties() const
Chris@35 61 {
Chris@287 62 PropertyList list = SingleColourLayer::getProperties();
Chris@35 63 return list;
Chris@35 64 }
Chris@35 65
Chris@87 66 QString
Chris@87 67 TextLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 68 {
Chris@287 69 return SingleColourLayer::getPropertyLabel(name);
Chris@87 70 }
Chris@87 71
Chris@35 72 Layer::PropertyType
Chris@287 73 TextLayer::getPropertyType(const PropertyName &name) const
Chris@35 74 {
Chris@287 75 return SingleColourLayer::getPropertyType(name);
Chris@35 76 }
Chris@35 77
Chris@35 78 int
Chris@35 79 TextLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@1266 80 int *min, int *max, int *deflt) const
Chris@35 81 {
Chris@287 82 return SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@35 83 }
Chris@35 84
Chris@35 85 QString
Chris@35 86 TextLayer::getPropertyValueLabel(const PropertyName &name,
Chris@1266 87 int value) const
Chris@35 88 {
Chris@287 89 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@35 90 }
Chris@35 91
Chris@35 92 void
Chris@35 93 TextLayer::setProperty(const PropertyName &name, int value)
Chris@35 94 {
Chris@287 95 SingleColourLayer::setProperty(name, value);
Chris@35 96 }
Chris@35 97
Chris@79 98 bool
Chris@908 99 TextLayer::getValueExtents(double &, double &, bool &, QString &) const
Chris@79 100 {
Chris@79 101 return false;
Chris@79 102 }
Chris@79 103
Chris@35 104 bool
Chris@918 105 TextLayer::isLayerScrollable(const LayerGeometryProvider *v) const
Chris@35 106 {
Chris@35 107 QPoint discard;
Chris@44 108 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@35 109 }
Chris@35 110
Chris@1437 111 EventVector
Chris@918 112 TextLayer::getLocalPoints(LayerGeometryProvider *v, int x, int y) const
Chris@35 113 {
Chris@1437 114 if (!m_model) return {};
Chris@35 115
Chris@1437 116 int overlap = ViewManager::scalePixelSize(150);
Chris@35 117
Chris@1437 118 sv_frame_t frame0 = v->getFrameForX(-overlap);
Chris@1437 119 sv_frame_t frame1 = v->getFrameForX(v->getPaintWidth() + overlap);
Chris@1437 120
Chris@1437 121 EventVector points(m_model->getEventsSpanning(frame0, frame1 - frame0));
Chris@35 122
Chris@1437 123 EventVector rv;
Chris@552 124 QFontMetrics metrics = QFontMetrics(QFont());
Chris@35 125
Chris@1437 126 for (EventVector::iterator i = points.begin(); i != points.end(); ++i) {
Chris@35 127
Chris@1437 128 Event p(*i);
Chris@35 129
Chris@1437 130 int px = v->getXForFrame(p.getFrame());
Chris@1437 131 int py = getYForHeight(v, p.getValue());
Chris@35 132
Chris@1437 133 QString label = p.getLabel();
Chris@1266 134 if (label == "") {
Chris@1266 135 label = tr("<no text>");
Chris@1266 136 }
Chris@35 137
Chris@1266 138 QRect rect = metrics.boundingRect
Chris@1266 139 (QRect(0, 0, 150, 200),
Chris@1266 140 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label);
Chris@35 141
Chris@1266 142 if (py + rect.height() > v->getPaintHeight()) {
Chris@1266 143 if (rect.height() > v->getPaintHeight()) py = 0;
Chris@1266 144 else py = v->getPaintHeight() - rect.height() - 1;
Chris@1266 145 }
Chris@35 146
Chris@1266 147 if (x >= px && x < px + rect.width() &&
Chris@1266 148 y >= py && y < py + rect.height()) {
Chris@1437 149 rv.push_back(p);
Chris@1266 150 }
Chris@35 151 }
Chris@35 152
Chris@35 153 return rv;
Chris@35 154 }
Chris@35 155
Chris@552 156 bool
Chris@1437 157 TextLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &p) const
Chris@552 158 {
Chris@552 159 if (!m_model) return false;
Chris@552 160
Chris@1437 161 sv_frame_t a = v->getFrameForX(x - ViewManager::scalePixelSize(120));
Chris@1437 162 sv_frame_t b = v->getFrameForX(x + ViewManager::scalePixelSize(10));
Chris@1437 163 EventVector onPoints = m_model->getEventsWithin(a, b);
Chris@552 164 if (onPoints.empty()) return false;
Chris@552 165
Chris@908 166 double nearestDistance = -1;
Chris@552 167
Chris@1437 168 for (EventVector::const_iterator i = onPoints.begin();
Chris@552 169 i != onPoints.end(); ++i) {
Chris@552 170
Chris@1437 171 double yd = getYForHeight(v, i->getValue()) - y;
Chris@1437 172 double xd = v->getXForFrame(i->getFrame()) - x;
Chris@908 173 double distance = sqrt(yd*yd + xd*xd);
Chris@552 174
Chris@552 175 if (nearestDistance == -1 || distance < nearestDistance) {
Chris@552 176 nearestDistance = distance;
Chris@552 177 p = *i;
Chris@552 178 }
Chris@552 179 }
Chris@552 180
Chris@552 181 return true;
Chris@552 182 }
Chris@552 183
Chris@35 184 QString
Chris@918 185 TextLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
Chris@35 186 {
Chris@35 187 int x = pos.x();
Chris@35 188
Chris@35 189 if (!m_model || !m_model->getSampleRate()) return "";
Chris@35 190
Chris@1437 191 EventVector points = getLocalPoints(v, x, pos.y());
Chris@35 192
Chris@35 193 if (points.empty()) {
Chris@1266 194 if (!m_model->isReady()) {
Chris@1266 195 return tr("In progress");
Chris@1266 196 } else {
Chris@1266 197 return "";
Chris@1266 198 }
Chris@35 199 }
Chris@35 200
Chris@1437 201 sv_frame_t useFrame = points.begin()->getFrame();
Chris@35 202
Chris@35 203 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
Chris@35 204
Chris@35 205 QString text;
Chris@35 206
Chris@1437 207 if (points.begin()->getLabel() == "") {
Chris@1266 208 text = QString(tr("Time:\t%1\nHeight:\t%2\nLabel:\t%3"))
Chris@1266 209 .arg(rt.toText(true).c_str())
Chris@1437 210 .arg(points.begin()->getValue())
Chris@1437 211 .arg(points.begin()->getLabel());
Chris@35 212 }
Chris@35 213
Chris@44 214 pos = QPoint(v->getXForFrame(useFrame),
Chris@1437 215 getYForHeight(v, points.begin()->getValue()));
Chris@35 216 return text;
Chris@35 217 }
Chris@35 218
Chris@35 219
Chris@35 220 //!!! too much overlap with TimeValueLayer/TimeInstantLayer
Chris@35 221
Chris@35 222 bool
Chris@918 223 TextLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
Chris@1266 224 int &resolution,
Chris@1266 225 SnapType snap) const
Chris@35 226 {
Chris@35 227 if (!m_model) {
Chris@1266 228 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@35 229 }
Chris@35 230
Chris@1437 231 // SnapLeft / SnapRight: return frame of nearest feature in that
Chris@1437 232 // direction no matter how far away
Chris@1437 233 //
Chris@1437 234 // SnapNeighbouring: return frame of feature that would be used in
Chris@1437 235 // an editing operation, i.e. closest feature in either direction
Chris@1437 236 // but only if it is "close enough"
Chris@1437 237
Chris@35 238 resolution = m_model->getResolution();
Chris@35 239
Chris@35 240 if (snap == SnapNeighbouring) {
Chris@1437 241 EventVector points = getLocalPoints(v, v->getXForFrame(frame), -1);
Chris@1266 242 if (points.empty()) return false;
Chris@1437 243 frame = points.begin()->getFrame();
Chris@1266 244 return true;
Chris@35 245 }
Chris@35 246
Chris@1437 247 Event e;
Chris@1437 248 if (m_model->getNearestEventMatching
Chris@1437 249 (frame,
Chris@1437 250 [](Event) { return true; },
Chris@1437 251 snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
Chris@1437 252 e)) {
Chris@1437 253 frame = e.getFrame();
Chris@1437 254 return true;
Chris@35 255 }
Chris@35 256
Chris@1437 257 return false;
Chris@35 258 }
Chris@35 259
Chris@35 260 int
Chris@918 261 TextLayer::getYForHeight(LayerGeometryProvider *v, double height) const
Chris@35 262 {
Chris@918 263 int h = v->getPaintHeight();
Chris@35 264 return h - int(height * h);
Chris@35 265 }
Chris@35 266
Chris@908 267 double
Chris@918 268 TextLayer::getHeightForY(LayerGeometryProvider *v, int y) const
Chris@35 269 {
Chris@918 270 int h = v->getPaintHeight();
Chris@908 271 return double(h - y) / h;
Chris@35 272 }
Chris@35 273
Chris@35 274 void
Chris@916 275 TextLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@35 276 {
Chris@35 277 if (!m_model || !m_model->isOK()) return;
Chris@35 278
Chris@908 279 sv_samplerate_t sampleRate = m_model->getSampleRate();
Chris@35 280 if (!sampleRate) return;
Chris@35 281
Chris@35 282 // Profiler profiler("TextLayer::paint", true);
Chris@35 283
Chris@35 284 int x0 = rect.left(), x1 = rect.right();
Chris@1437 285 int overlap = ViewManager::scalePixelSize(150);
Chris@1437 286 sv_frame_t frame0 = v->getFrameForX(x0 - overlap);
Chris@1437 287 sv_frame_t frame1 = v->getFrameForX(x1 + overlap);
Chris@35 288
Chris@1437 289 EventVector points(m_model->getEventsWithin(frame0, frame1 - frame0, 2));
Chris@35 290 if (points.empty()) return;
Chris@35 291
Chris@287 292 QColor brushColour(getBaseQColor());
Chris@35 293
Chris@44 294 int h, s, val;
Chris@44 295 brushColour.getHsv(&h, &s, &val);
Chris@36 296 brushColour.setHsv(h, s, 255, 100);
Chris@36 297
Chris@36 298 QColor penColour;
Chris@287 299 penColour = v->getForeground();
Chris@35 300
Chris@587 301 // SVDEBUG << "TextLayer::paint: resolution is "
Chris@1266 302 // << m_model->getResolution() << " frames" << endl;
Chris@35 303
Chris@35 304 QPoint localPos;
Chris@1437 305 Event illuminatePoint(0);
Chris@850 306 bool shouldIlluminate = false;
Chris@35 307
Chris@44 308 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@552 309 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
Chris@552 310 illuminatePoint);
Chris@35 311 }
Chris@35 312
Chris@35 313 int boxMaxWidth = 150;
Chris@35 314 int boxMaxHeight = 200;
Chris@35 315
Chris@35 316 paint.save();
Chris@918 317 paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->getPaintHeight());
Chris@35 318
Chris@1437 319 for (EventVector::const_iterator i = points.begin();
Chris@1266 320 i != points.end(); ++i) {
Chris@35 321
Chris@1437 322 Event p(*i);
Chris@35 323
Chris@1437 324 int x = v->getXForFrame(p.getFrame());
Chris@1437 325 int y = getYForHeight(v, p.getValue());
Chris@35 326
Chris@1437 327 if (!shouldIlluminate || illuminatePoint != p) {
Chris@1266 328 paint.setPen(penColour);
Chris@1266 329 paint.setBrush(brushColour);
Chris@552 330 } else {
Chris@1266 331 paint.setBrush(penColour);
Chris@287 332 paint.setPen(v->getBackground());
Chris@1266 333 }
Chris@35 334
Chris@1437 335 QString label = p.getLabel();
Chris@1266 336 if (label == "") {
Chris@1266 337 label = tr("<no text>");
Chris@1266 338 }
Chris@35 339
Chris@1266 340 QRect boxRect = paint.fontMetrics().boundingRect
Chris@1266 341 (QRect(0, 0, boxMaxWidth, boxMaxHeight),
Chris@1266 342 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label);
Chris@35 343
Chris@1266 344 QRect textRect = QRect(3, 2, boxRect.width(), boxRect.height());
Chris@1266 345 boxRect = QRect(0, 0, boxRect.width() + 6, boxRect.height() + 2);
Chris@35 346
Chris@1266 347 if (y + boxRect.height() > v->getPaintHeight()) {
Chris@1266 348 if (boxRect.height() > v->getPaintHeight()) y = 0;
Chris@1266 349 else y = v->getPaintHeight() - boxRect.height() - 1;
Chris@1266 350 }
Chris@35 351
Chris@1266 352 boxRect = QRect(x, y, boxRect.width(), boxRect.height());
Chris@1266 353 textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height());
Chris@35 354
Chris@1266 355 // boxRect = QRect(x, y, boxRect.width(), boxRect.height());
Chris@1266 356 // textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height());
Chris@35 357
Chris@1266 358 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@1266 359 paint.drawRect(boxRect);
Chris@35 360
Chris@1266 361 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@1266 362 paint.drawText(textRect,
Chris@1266 363 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap,
Chris@1266 364 label);
Chris@35 365
Chris@1437 366 /// if (p.getLabel() != "") {
Chris@1437 367 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.getLabel());
Chris@1266 368 /// }
Chris@35 369 }
Chris@35 370
Chris@35 371 paint.restore();
Chris@35 372
Chris@35 373 // looks like save/restore doesn't deal with this:
Chris@35 374 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@35 375 }
Chris@35 376
Chris@35 377 void
Chris@918 378 TextLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@35 379 {
Chris@587 380 // SVDEBUG << "TextLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@35 381
Chris@35 382 if (!m_model) {
Chris@1266 383 SVDEBUG << "TextLayer::drawStart: no model" << endl;
Chris@1266 384 return;
Chris@35 385 }
Chris@35 386
Chris@908 387 sv_frame_t frame = v->getFrameForX(e->x());
Chris@35 388 if (frame < 0) frame = 0;
Chris@35 389 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@35 390
Chris@908 391 double height = getHeightForY(v, e->y());
Chris@35 392
Chris@1437 393 m_editingPoint = Event(frame, float(height), "");
Chris@35 394 m_originalPoint = m_editingPoint;
Chris@35 395
Chris@376 396 if (m_editingCommand) finish(m_editingCommand);
Chris@1437 397 m_editingCommand = new ChangeEventsCommand(m_model, "Add Label");
Chris@1437 398 m_editingCommand->add(m_editingPoint);
Chris@35 399
Chris@35 400 m_editing = true;
Chris@35 401 }
Chris@35 402
Chris@35 403 void
Chris@918 404 TextLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@35 405 {
Chris@587 406 // SVDEBUG << "TextLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@35 407
Chris@35 408 if (!m_model || !m_editing) return;
Chris@35 409
Chris@908 410 sv_frame_t frame = v->getFrameForX(e->x());
Chris@35 411 if (frame < 0) frame = 0;
Chris@35 412 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@35 413
Chris@908 414 double height = getHeightForY(v, e->y());
Chris@35 415
Chris@1437 416 m_editingCommand->remove(m_editingPoint);
Chris@1437 417 m_editingPoint = m_editingPoint
Chris@1437 418 .withFrame(frame)
Chris@1437 419 .withValue(float(height));
Chris@1437 420 m_editingCommand->add(m_editingPoint);
Chris@35 421 }
Chris@35 422
Chris@35 423 void
Chris@918 424 TextLayer::drawEnd(LayerGeometryProvider *v, QMouseEvent *)
Chris@35 425 {
Chris@587 426 // SVDEBUG << "TextLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl;
Chris@35 427 if (!m_model || !m_editing) return;
Chris@36 428
Chris@36 429 bool ok = false;
Chris@918 430 QString label = QInputDialog::getText(v->getView(), tr("Enter label"),
Chris@1266 431 tr("Please enter a new label:"),
Chris@1266 432 QLineEdit::Normal, "", &ok);
Chris@36 433
Chris@1437 434 m_editingCommand->remove(m_editingPoint);
Chris@1437 435
Chris@36 436 if (ok) {
Chris@1437 437 m_editingPoint = m_editingPoint
Chris@1437 438 .withLabel(label);
Chris@1437 439 m_editingCommand->add(m_editingPoint);
Chris@36 440 }
Chris@36 441
Chris@376 442 finish(m_editingCommand);
Chris@1408 443 m_editingCommand = nullptr;
Chris@35 444 m_editing = false;
Chris@35 445 }
Chris@35 446
Chris@35 447 void
Chris@918 448 TextLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 449 {
Chris@335 450 if (!m_model) return;
Chris@335 451
Chris@552 452 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
Chris@335 453
Chris@335 454 if (m_editingCommand) {
Chris@1266 455 finish(m_editingCommand);
Chris@1408 456 m_editingCommand = nullptr;
Chris@335 457 }
Chris@335 458
Chris@335 459 m_editing = true;
Chris@335 460 }
Chris@335 461
Chris@335 462 void
Chris@918 463 TextLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@335 464 {
Chris@335 465 }
Chris@335 466
Chris@335 467 void
Chris@918 468 TextLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 469 {
Chris@335 470 if (!m_model || !m_editing) return;
Chris@335 471
Chris@335 472 m_editing = false;
Chris@335 473
Chris@1437 474 Event p;
Chris@552 475 if (!getPointToDrag(v, e->x(), e->y(), p)) return;
Chris@1437 476 if (p.getFrame() != m_editingPoint.getFrame() ||
Chris@1437 477 p.getValue() != m_editingPoint.getValue()) return;
Chris@335 478
Chris@1437 479 m_editingCommand = new ChangeEventsCommand(m_model, tr("Erase Point"));
Chris@1437 480 m_editingCommand->remove(m_editingPoint);
Chris@376 481 finish(m_editingCommand);
Chris@1408 482 m_editingCommand = nullptr;
Chris@335 483 m_editing = false;
Chris@335 484 }
Chris@335 485
Chris@335 486 void
Chris@918 487 TextLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@35 488 {
Chris@587 489 // SVDEBUG << "TextLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@35 490
Chris@35 491 if (!m_model) return;
Chris@35 492
Chris@552 493 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) {
Chris@552 494 return;
Chris@552 495 }
Chris@35 496
Chris@36 497 m_editOrigin = e->pos();
Chris@35 498 m_originalPoint = m_editingPoint;
Chris@35 499
Chris@35 500 if (m_editingCommand) {
Chris@1266 501 finish(m_editingCommand);
Chris@1408 502 m_editingCommand = nullptr;
Chris@35 503 }
Chris@35 504
Chris@35 505 m_editing = true;
Chris@35 506 }
Chris@35 507
Chris@35 508 void
Chris@918 509 TextLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@35 510 {
Chris@35 511 if (!m_model || !m_editing) return;
Chris@35 512
Chris@1437 513 sv_frame_t frameDiff =
Chris@1437 514 v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x());
Chris@1437 515 double heightDiff =
Chris@1437 516 getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y());
Chris@36 517
Chris@1437 518 sv_frame_t frame = m_originalPoint.getFrame() + frameDiff;
Chris@1437 519 double height = m_originalPoint.getValue() + heightDiff;
Chris@36 520
Chris@35 521 if (frame < 0) frame = 0;
Chris@36 522 frame = (frame / m_model->getResolution()) * m_model->getResolution();
Chris@35 523
Chris@35 524 if (!m_editingCommand) {
Chris@1437 525 m_editingCommand = new ChangeEventsCommand(m_model, tr("Drag Label"));
Chris@35 526 }
Chris@35 527
Chris@1437 528 m_editingCommand->remove(m_editingPoint);
Chris@1437 529 m_editingPoint = m_editingPoint
Chris@1437 530 .withFrame(frame)
Chris@1437 531 .withValue(float(height));
Chris@1437 532 m_editingCommand->add(m_editingPoint);
Chris@35 533 }
Chris@35 534
Chris@35 535 void
Chris@918 536 TextLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@35 537 {
Chris@587 538 // SVDEBUG << "TextLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl;
Chris@35 539 if (!m_model || !m_editing) return;
Chris@35 540
Chris@35 541 if (m_editingCommand) {
Chris@35 542
Chris@1266 543 QString newName = m_editingCommand->getName();
Chris@35 544
Chris@1437 545 if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
Chris@1437 546 if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
Chris@1266 547 newName = tr("Move Label");
Chris@1266 548 } else {
Chris@1266 549 newName = tr("Move Label Horizontally");
Chris@1266 550 }
Chris@1266 551 } else {
Chris@1266 552 newName = tr("Move Label Vertically");
Chris@1266 553 }
Chris@35 554
Chris@1266 555 m_editingCommand->setName(newName);
Chris@1266 556 finish(m_editingCommand);
Chris@35 557 }
Chris@35 558
Chris@1408 559 m_editingCommand = nullptr;
Chris@35 560 m_editing = false;
Chris@35 561 }
Chris@35 562
Chris@255 563 bool
Chris@918 564 TextLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@36 565 {
Chris@255 566 if (!m_model) return false;
Chris@36 567
Chris@1437 568 Event text;
Chris@552 569 if (!getPointToDrag(v, e->x(), e->y(), text)) return false;
Chris@36 570
Chris@1437 571 QString label = text.getLabel();
Chris@36 572
Chris@36 573 bool ok = false;
Chris@918 574 label = QInputDialog::getText(v->getView(), tr("Enter label"),
Chris@1266 575 tr("Please enter a new label:"),
Chris@1266 576 QLineEdit::Normal, label, &ok);
Chris@1437 577 if (ok && label != text.getLabel()) {
Chris@1437 578 ChangeEventsCommand *command =
Chris@1437 579 new ChangeEventsCommand(m_model, tr("Re-Label Point"));
Chris@1437 580 command->remove(text);
Chris@1437 581 command->add(text.withLabel(label));
Chris@1437 582 finish(command);
Chris@36 583 }
Chris@255 584
Chris@255 585 return true;
Chris@36 586 }
Chris@36 587
Chris@43 588 void
Chris@908 589 TextLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@43 590 {
Chris@99 591 if (!m_model) return;
Chris@99 592
Chris@1437 593 ChangeEventsCommand *command =
Chris@1437 594 new ChangeEventsCommand(m_model, tr("Drag Selection"));
Chris@43 595
Chris@1437 596 EventVector points =
Chris@1437 597 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@43 598
Chris@1437 599 for (Event p: points) {
Chris@1437 600 command->remove(p);
Chris@1437 601 Event moved = p.withFrame(p.getFrame() +
Chris@1437 602 newStartFrame - s.getStartFrame());
Chris@1437 603 command->add(moved);
Chris@43 604 }
Chris@43 605
Chris@376 606 finish(command);
Chris@43 607 }
Chris@43 608
Chris@43 609 void
Chris@43 610 TextLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 611 {
Chris@99 612 if (!m_model) return;
Chris@99 613
Chris@1437 614 ChangeEventsCommand *command =
Chris@1437 615 new ChangeEventsCommand(m_model, tr("Resize Selection"));
Chris@43 616
Chris@1437 617 EventVector points =
Chris@1437 618 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@43 619
Chris@1437 620 double ratio = double(newSize.getDuration()) / double(s.getDuration());
Chris@1437 621 double oldStart = double(s.getStartFrame());
Chris@1437 622 double newStart = double(newSize.getStartFrame());
Chris@1437 623
Chris@1437 624 for (Event p: points) {
Chris@43 625
Chris@1437 626 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
Chris@43 627
Chris@1437 628 Event newPoint = p
Chris@1437 629 .withFrame(lrint(newFrame));
Chris@1437 630 command->remove(p);
Chris@1437 631 command->add(newPoint);
Chris@43 632 }
Chris@43 633
Chris@376 634 finish(command);
Chris@43 635 }
Chris@43 636
Chris@76 637 void
Chris@76 638 TextLayer::deleteSelection(Selection s)
Chris@76 639 {
Chris@99 640 if (!m_model) return;
Chris@99 641
Chris@1437 642 ChangeEventsCommand *command =
Chris@1437 643 new ChangeEventsCommand(m_model, tr("Delete Selection"));
Chris@76 644
Chris@1437 645 EventVector points =
Chris@1437 646 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@76 647
Chris@1437 648 for (Event p: points) {
Chris@1437 649 command->remove(p);
Chris@76 650 }
Chris@76 651
Chris@376 652 finish(command);
Chris@76 653 }
Chris@76 654
Chris@76 655 void
Chris@918 656 TextLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@76 657 {
Chris@99 658 if (!m_model) return;
Chris@99 659
Chris@1437 660 EventVector points =
Chris@1437 661 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@76 662
Chris@1437 663 for (Event p: points) {
Chris@1437 664 to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
Chris@76 665 }
Chris@76 666 }
Chris@76 667
Chris@125 668 bool
Chris@918 669 TextLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
Chris@76 670 {
Chris@125 671 if (!m_model) return false;
Chris@99 672
Chris@1423 673 const EventVector &points = from.getPoints();
Chris@76 674
Chris@360 675 bool realign = false;
Chris@360 676
Chris@360 677 if (clipboardHasDifferentAlignment(v, from)) {
Chris@360 678
Chris@360 679 QMessageBox::StandardButton button =
Chris@918 680 QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
Chris@360 681 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 682 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 683 QMessageBox::Yes);
Chris@360 684
Chris@360 685 if (button == QMessageBox::Cancel) {
Chris@360 686 return false;
Chris@360 687 }
Chris@360 688
Chris@360 689 if (button == QMessageBox::Yes) {
Chris@360 690 realign = true;
Chris@360 691 }
Chris@360 692 }
Chris@360 693
Chris@1437 694 ChangeEventsCommand *command =
Chris@1437 695 new ChangeEventsCommand(m_model, tr("Paste"));
Chris@76 696
Chris@908 697 double valueMin = 0.0, valueMax = 1.0;
Chris@1423 698 for (EventVector::const_iterator i = points.begin();
Chris@125 699 i != points.end(); ++i) {
Chris@1423 700 if (i->hasValue()) {
Chris@125 701 if (i->getValue() < valueMin) valueMin = i->getValue();
Chris@125 702 if (i->getValue() > valueMax) valueMax = i->getValue();
Chris@125 703 }
Chris@125 704 }
Chris@125 705 if (valueMax < valueMin + 1.0) valueMax = valueMin + 1.0;
Chris@125 706
Chris@1423 707 for (EventVector::const_iterator i = points.begin();
Chris@76 708 i != points.end(); ++i) {
Chris@76 709
Chris@908 710 sv_frame_t frame = 0;
Chris@360 711
Chris@360 712 if (!realign) {
Chris@360 713
Chris@360 714 frame = i->getFrame();
Chris@360 715
Chris@360 716 } else {
Chris@360 717
Chris@1423 718 if (i->hasReferenceFrame()) {
Chris@360 719 frame = i->getReferenceFrame();
Chris@360 720 frame = alignFromReference(v, frame);
Chris@360 721 } else {
Chris@360 722 frame = i->getFrame();
Chris@360 723 }
Chris@76 724 }
Chris@360 725
Chris@1437 726 Event p = *i;
Chris@1437 727 Event newPoint = p;
Chris@1437 728 if (p.hasValue()) {
Chris@1437 729 newPoint = newPoint.withValue(float((i->getValue() - valueMin) /
Chris@1437 730 (valueMax - valueMin)));
Chris@125 731 } else {
Chris@1437 732 newPoint = newPoint.withValue(0.5f);
Chris@125 733 }
Chris@125 734
Chris@1437 735 if (!p.hasLabel()) {
Chris@1437 736 if (p.hasValue()) {
Chris@1437 737 newPoint = newPoint.withLabel(QString("%1").arg(p.getValue()));
Chris@1437 738 } else {
Chris@1437 739 newPoint = newPoint.withLabel(tr("New Point"));
Chris@1437 740 }
Chris@125 741 }
Chris@76 742
Chris@1437 743 command->add(newPoint);
Chris@76 744 }
Chris@76 745
Chris@376 746 finish(command);
Chris@125 747 return true;
Chris@76 748 }
Chris@76 749
Chris@287 750 int
Chris@287 751 TextLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 752 {
Chris@287 753 impose = false;
Chris@287 754 return ColourDatabase::getInstance()->getColourIndex
Chris@287 755 (QString(darkbg ? "Bright Orange" : "Orange"));
Chris@287 756 }
Chris@287 757
Chris@316 758 void
Chris@316 759 TextLayer::toXml(QTextStream &stream,
Chris@316 760 QString indent, QString extraAttributes) const
Chris@35 761 {
Chris@316 762 SingleColourLayer::toXml(stream, indent, extraAttributes);
Chris@35 763 }
Chris@35 764
Chris@35 765 void
Chris@35 766 TextLayer::setProperties(const QXmlAttributes &attributes)
Chris@35 767 {
Chris@287 768 SingleColourLayer::setProperties(attributes);
Chris@35 769 }
Chris@35 770