annotate layer/TextLayer.cpp @ 1457:160e6d010141 single-point

Avoid showing progress bars for dormant layers. They'll typically be duplicates, e.g. in the vect app you can get many dormant layers all reporting the alignment completion at the same time. (Maybe it would be better just to reject progress bars for alignment in dormant layers? Hm)
author Chris Cannam
date Thu, 16 May 2019 15:50:02 +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