annotate layer/TextLayer.cpp @ 1135:628cd329c241 spectrogram-minor-refactor

Use a count of bins rather than min and max bins (because the name maxbin tells us nothing about whether the range is inclusive or not)
author Chris Cannam
date Wed, 03 Aug 2016 14:20:27 +0100
parents 4fe7a09be0fe
children c02c51ae5238 a34a2a25907c
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@35 37 m_model(0),
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@287 41 m_editingCommand(0)
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@216 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@35 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@35 111
Chris@35 112 TextModel::PointList
Chris@918 113 TextLayer::getLocalPoints(LayerGeometryProvider *v, int x, int y) const
Chris@35 114 {
Chris@35 115 if (!m_model) return TextModel::PointList();
Chris@35 116
Chris@908 117 sv_frame_t frame0 = v->getFrameForX(-150);
Chris@918 118 sv_frame_t frame1 = v->getFrameForX(v->getPaintWidth() + 150);
Chris@35 119
Chris@35 120 TextModel::PointList points(m_model->getPoints(frame0, frame1));
Chris@35 121
Chris@35 122 TextModel::PointList rv;
Chris@552 123 QFontMetrics metrics = QFontMetrics(QFont());
Chris@35 124
Chris@35 125 for (TextModel::PointList::iterator i = points.begin();
Chris@35 126 i != points.end(); ++i) {
Chris@35 127
Chris@35 128 const TextModel::Point &p(*i);
Chris@35 129
Chris@44 130 int px = v->getXForFrame(p.frame);
Chris@44 131 int py = getYForHeight(v, p.height);
Chris@35 132
Chris@35 133 QString label = p.label;
Chris@35 134 if (label == "") {
Chris@35 135 label = tr("<no text>");
Chris@35 136 }
Chris@35 137
Chris@35 138 QRect rect = metrics.boundingRect
Chris@35 139 (QRect(0, 0, 150, 200),
Chris@35 140 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label);
Chris@35 141
Chris@918 142 if (py + rect.height() > v->getPaintHeight()) {
Chris@918 143 if (rect.height() > v->getPaintHeight()) py = 0;
Chris@918 144 else py = v->getPaintHeight() - rect.height() - 1;
Chris@35 145 }
Chris@35 146
Chris@35 147 if (x >= px && x < px + rect.width() &&
Chris@35 148 y >= py && y < py + rect.height()) {
Chris@35 149 rv.insert(p);
Chris@35 150 }
Chris@35 151 }
Chris@35 152
Chris@35 153 return rv;
Chris@35 154 }
Chris@35 155
Chris@552 156 bool
Chris@918 157 TextLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, TextModel::Point &p) const
Chris@552 158 {
Chris@552 159 if (!m_model) return false;
Chris@552 160
Chris@908 161 sv_frame_t a = v->getFrameForX(x - 120);
Chris@908 162 sv_frame_t b = v->getFrameForX(x + 10);
Chris@552 163 TextModel::PointList onPoints = m_model->getPoints(a, b);
Chris@552 164 if (onPoints.empty()) return false;
Chris@552 165
Chris@908 166 double nearestDistance = -1;
Chris@552 167
Chris@552 168 for (TextModel::PointList::const_iterator i = onPoints.begin();
Chris@552 169 i != onPoints.end(); ++i) {
Chris@552 170
Chris@908 171 double yd = getYForHeight(v, (*i).height) - y;
Chris@908 172 double xd = v->getXForFrame((*i).frame) - 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@44 191 TextModel::PointList points = getLocalPoints(v, x, pos.y());
Chris@35 192
Chris@35 193 if (points.empty()) {
Chris@35 194 if (!m_model->isReady()) {
Chris@35 195 return tr("In progress");
Chris@35 196 } else {
Chris@35 197 return "";
Chris@35 198 }
Chris@35 199 }
Chris@35 200
Chris@908 201 sv_frame_t useFrame = points.begin()->frame;
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@35 207 if (points.begin()->label == "") {
Chris@35 208 text = QString(tr("Time:\t%1\nHeight:\t%2\nLabel:\t%3"))
Chris@35 209 .arg(rt.toText(true).c_str())
Chris@35 210 .arg(points.begin()->height)
Chris@35 211 .arg(points.begin()->label);
Chris@35 212 }
Chris@35 213
Chris@44 214 pos = QPoint(v->getXForFrame(useFrame),
Chris@44 215 getYForHeight(v, points.begin()->height));
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@805 224 int &resolution,
Chris@35 225 SnapType snap) const
Chris@35 226 {
Chris@35 227 if (!m_model) {
Chris@44 228 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@35 229 }
Chris@35 230
Chris@35 231 resolution = m_model->getResolution();
Chris@35 232 TextModel::PointList points;
Chris@35 233
Chris@35 234 if (snap == SnapNeighbouring) {
Chris@35 235
Chris@44 236 points = getLocalPoints(v, v->getXForFrame(frame), -1);
Chris@35 237 if (points.empty()) return false;
Chris@35 238 frame = points.begin()->frame;
Chris@35 239 return true;
Chris@35 240 }
Chris@35 241
Chris@35 242 points = m_model->getPoints(frame, frame);
Chris@908 243 sv_frame_t snapped = frame;
Chris@35 244 bool found = false;
Chris@35 245
Chris@35 246 for (TextModel::PointList::const_iterator i = points.begin();
Chris@35 247 i != points.end(); ++i) {
Chris@35 248
Chris@35 249 if (snap == SnapRight) {
Chris@35 250
Chris@35 251 if (i->frame > frame) {
Chris@35 252 snapped = i->frame;
Chris@35 253 found = true;
Chris@35 254 break;
Chris@35 255 }
Chris@35 256
Chris@35 257 } else if (snap == SnapLeft) {
Chris@35 258
Chris@35 259 if (i->frame <= frame) {
Chris@35 260 snapped = i->frame;
Chris@35 261 found = true; // don't break, as the next may be better
Chris@35 262 } else {
Chris@35 263 break;
Chris@35 264 }
Chris@35 265
Chris@35 266 } else { // nearest
Chris@35 267
Chris@35 268 TextModel::PointList::const_iterator j = i;
Chris@35 269 ++j;
Chris@35 270
Chris@35 271 if (j == points.end()) {
Chris@35 272
Chris@35 273 snapped = i->frame;
Chris@35 274 found = true;
Chris@35 275 break;
Chris@35 276
Chris@35 277 } else if (j->frame >= frame) {
Chris@35 278
Chris@35 279 if (j->frame - frame < frame - i->frame) {
Chris@35 280 snapped = j->frame;
Chris@35 281 } else {
Chris@35 282 snapped = i->frame;
Chris@35 283 }
Chris@35 284 found = true;
Chris@35 285 break;
Chris@35 286 }
Chris@35 287 }
Chris@35 288 }
Chris@35 289
Chris@35 290 frame = snapped;
Chris@35 291 return found;
Chris@35 292 }
Chris@35 293
Chris@35 294 int
Chris@918 295 TextLayer::getYForHeight(LayerGeometryProvider *v, double height) const
Chris@35 296 {
Chris@918 297 int h = v->getPaintHeight();
Chris@35 298 return h - int(height * h);
Chris@35 299 }
Chris@35 300
Chris@908 301 double
Chris@918 302 TextLayer::getHeightForY(LayerGeometryProvider *v, int y) const
Chris@35 303 {
Chris@918 304 int h = v->getPaintHeight();
Chris@908 305 return double(h - y) / h;
Chris@35 306 }
Chris@35 307
Chris@35 308 void
Chris@916 309 TextLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@35 310 {
Chris@35 311 if (!m_model || !m_model->isOK()) return;
Chris@35 312
Chris@908 313 sv_samplerate_t sampleRate = m_model->getSampleRate();
Chris@35 314 if (!sampleRate) return;
Chris@35 315
Chris@35 316 // Profiler profiler("TextLayer::paint", true);
Chris@35 317
Chris@35 318 int x0 = rect.left(), x1 = rect.right();
Chris@908 319 sv_frame_t frame0 = v->getFrameForX(x0);
Chris@908 320 sv_frame_t frame1 = v->getFrameForX(x1);
Chris@35 321
Chris@35 322 TextModel::PointList points(m_model->getPoints(frame0, frame1));
Chris@35 323 if (points.empty()) return;
Chris@35 324
Chris@287 325 QColor brushColour(getBaseQColor());
Chris@35 326
Chris@44 327 int h, s, val;
Chris@44 328 brushColour.getHsv(&h, &s, &val);
Chris@36 329 brushColour.setHsv(h, s, 255, 100);
Chris@36 330
Chris@36 331 QColor penColour;
Chris@287 332 penColour = v->getForeground();
Chris@35 333
Chris@587 334 // SVDEBUG << "TextLayer::paint: resolution is "
Chris@585 335 // << m_model->getResolution() << " frames" << endl;
Chris@35 336
Chris@35 337 QPoint localPos;
Chris@552 338 TextModel::Point illuminatePoint(0);
Chris@850 339 bool shouldIlluminate = false;
Chris@35 340
Chris@44 341 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@552 342 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
Chris@552 343 illuminatePoint);
Chris@35 344 }
Chris@35 345
Chris@35 346 int boxMaxWidth = 150;
Chris@35 347 int boxMaxHeight = 200;
Chris@35 348
Chris@35 349 paint.save();
Chris@918 350 paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->getPaintHeight());
Chris@35 351
Chris@35 352 for (TextModel::PointList::const_iterator i = points.begin();
Chris@35 353 i != points.end(); ++i) {
Chris@35 354
Chris@35 355 const TextModel::Point &p(*i);
Chris@35 356
Chris@44 357 int x = v->getXForFrame(p.frame);
Chris@44 358 int y = getYForHeight(v, p.height);
Chris@35 359
Chris@552 360 if (!shouldIlluminate ||
Chris@552 361 // "illuminatePoint != p"
Chris@552 362 TextModel::Point::Comparator()(illuminatePoint, p) ||
Chris@552 363 TextModel::Point::Comparator()(p, illuminatePoint)) {
Chris@552 364 paint.setPen(penColour);
Chris@552 365 paint.setBrush(brushColour);
Chris@552 366 } else {
Chris@36 367 paint.setBrush(penColour);
Chris@287 368 paint.setPen(v->getBackground());
Chris@35 369 }
Chris@35 370
Chris@35 371 QString label = p.label;
Chris@35 372 if (label == "") {
Chris@35 373 label = tr("<no text>");
Chris@35 374 }
Chris@35 375
Chris@35 376 QRect boxRect = paint.fontMetrics().boundingRect
Chris@35 377 (QRect(0, 0, boxMaxWidth, boxMaxHeight),
Chris@35 378 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label);
Chris@35 379
Chris@35 380 QRect textRect = QRect(3, 2, boxRect.width(), boxRect.height());
Chris@35 381 boxRect = QRect(0, 0, boxRect.width() + 6, boxRect.height() + 2);
Chris@35 382
Chris@918 383 if (y + boxRect.height() > v->getPaintHeight()) {
Chris@918 384 if (boxRect.height() > v->getPaintHeight()) y = 0;
Chris@918 385 else y = v->getPaintHeight() - boxRect.height() - 1;
Chris@35 386 }
Chris@35 387
Chris@35 388 boxRect = QRect(x, y, boxRect.width(), boxRect.height());
Chris@35 389 textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height());
Chris@35 390
Chris@35 391 // boxRect = QRect(x, y, boxRect.width(), boxRect.height());
Chris@35 392 // textRect = QRect(x + 3, y + 2, textRect.width(), textRect.height());
Chris@35 393
Chris@35 394 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@35 395 paint.drawRect(boxRect);
Chris@35 396
Chris@35 397 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@35 398 paint.drawText(textRect,
Chris@35 399 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap,
Chris@35 400 label);
Chris@35 401
Chris@35 402 /// if (p.label != "") {
Chris@35 403 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label);
Chris@35 404 /// }
Chris@35 405 }
Chris@35 406
Chris@35 407 paint.restore();
Chris@35 408
Chris@35 409 // looks like save/restore doesn't deal with this:
Chris@35 410 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@35 411 }
Chris@35 412
Chris@35 413 void
Chris@918 414 TextLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@35 415 {
Chris@587 416 // SVDEBUG << "TextLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@35 417
Chris@35 418 if (!m_model) {
Chris@587 419 SVDEBUG << "TextLayer::drawStart: no model" << endl;
Chris@35 420 return;
Chris@35 421 }
Chris@35 422
Chris@908 423 sv_frame_t frame = v->getFrameForX(e->x());
Chris@35 424 if (frame < 0) frame = 0;
Chris@35 425 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@35 426
Chris@908 427 double height = getHeightForY(v, e->y());
Chris@35 428
Chris@908 429 m_editingPoint = TextModel::Point(frame, float(height), "");
Chris@35 430 m_originalPoint = m_editingPoint;
Chris@35 431
Chris@376 432 if (m_editingCommand) finish(m_editingCommand);
Chris@35 433 m_editingCommand = new TextModel::EditCommand(m_model, "Add Label");
Chris@35 434 m_editingCommand->addPoint(m_editingPoint);
Chris@35 435
Chris@35 436 m_editing = true;
Chris@35 437 }
Chris@35 438
Chris@35 439 void
Chris@918 440 TextLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@35 441 {
Chris@587 442 // SVDEBUG << "TextLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@35 443
Chris@35 444 if (!m_model || !m_editing) return;
Chris@35 445
Chris@908 446 sv_frame_t frame = v->getFrameForX(e->x());
Chris@35 447 if (frame < 0) frame = 0;
Chris@35 448 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@35 449
Chris@908 450 double height = getHeightForY(v, e->y());
Chris@35 451
Chris@35 452 m_editingCommand->deletePoint(m_editingPoint);
Chris@35 453 m_editingPoint.frame = frame;
Chris@908 454 m_editingPoint.height = float(height);
Chris@35 455 m_editingCommand->addPoint(m_editingPoint);
Chris@35 456 }
Chris@35 457
Chris@35 458 void
Chris@918 459 TextLayer::drawEnd(LayerGeometryProvider *v, QMouseEvent *)
Chris@35 460 {
Chris@587 461 // SVDEBUG << "TextLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl;
Chris@35 462 if (!m_model || !m_editing) return;
Chris@36 463
Chris@36 464 bool ok = false;
Chris@918 465 QString label = QInputDialog::getText(v->getView(), tr("Enter label"),
Chris@36 466 tr("Please enter a new label:"),
Chris@36 467 QLineEdit::Normal, "", &ok);
Chris@36 468
Chris@36 469 if (ok) {
Chris@36 470 TextModel::RelabelCommand *command =
Chris@36 471 new TextModel::RelabelCommand(m_model, m_editingPoint, label);
Chris@36 472 m_editingCommand->addCommand(command);
Chris@307 473 } else {
Chris@307 474 m_editingCommand->deletePoint(m_editingPoint);
Chris@36 475 }
Chris@36 476
Chris@376 477 finish(m_editingCommand);
Chris@35 478 m_editingCommand = 0;
Chris@35 479 m_editing = false;
Chris@35 480 }
Chris@35 481
Chris@35 482 void
Chris@918 483 TextLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 484 {
Chris@335 485 if (!m_model) return;
Chris@335 486
Chris@552 487 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
Chris@335 488
Chris@335 489 if (m_editingCommand) {
Chris@376 490 finish(m_editingCommand);
Chris@335 491 m_editingCommand = 0;
Chris@335 492 }
Chris@335 493
Chris@335 494 m_editing = true;
Chris@335 495 }
Chris@335 496
Chris@335 497 void
Chris@918 498 TextLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@335 499 {
Chris@335 500 }
Chris@335 501
Chris@335 502 void
Chris@918 503 TextLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 504 {
Chris@335 505 if (!m_model || !m_editing) return;
Chris@335 506
Chris@335 507 m_editing = false;
Chris@335 508
Chris@552 509 TextModel::Point p(0);
Chris@552 510 if (!getPointToDrag(v, e->x(), e->y(), p)) return;
Chris@552 511 if (p.frame != m_editingPoint.frame || p.height != m_editingPoint.height) return;
Chris@335 512
Chris@335 513 m_editingCommand = new TextModel::EditCommand
Chris@335 514 (m_model, tr("Erase Point"));
Chris@335 515
Chris@335 516 m_editingCommand->deletePoint(m_editingPoint);
Chris@335 517
Chris@376 518 finish(m_editingCommand);
Chris@335 519 m_editingCommand = 0;
Chris@335 520 m_editing = false;
Chris@335 521 }
Chris@335 522
Chris@335 523 void
Chris@918 524 TextLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@35 525 {
Chris@587 526 // SVDEBUG << "TextLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@35 527
Chris@35 528 if (!m_model) return;
Chris@35 529
Chris@552 530 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) {
Chris@552 531 return;
Chris@552 532 }
Chris@35 533
Chris@36 534 m_editOrigin = e->pos();
Chris@35 535 m_originalPoint = m_editingPoint;
Chris@35 536
Chris@35 537 if (m_editingCommand) {
Chris@376 538 finish(m_editingCommand);
Chris@35 539 m_editingCommand = 0;
Chris@35 540 }
Chris@35 541
Chris@35 542 m_editing = true;
Chris@35 543 }
Chris@35 544
Chris@35 545 void
Chris@918 546 TextLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@35 547 {
Chris@35 548 if (!m_model || !m_editing) return;
Chris@35 549
Chris@908 550 sv_frame_t frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x());
Chris@908 551 double heightDiff = getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y());
Chris@36 552
Chris@908 553 sv_frame_t frame = m_originalPoint.frame + frameDiff;
Chris@908 554 double height = m_originalPoint.height + heightDiff;
Chris@36 555
Chris@908 556 // sv_frame_t frame = v->getFrameForX(e->x());
Chris@35 557 if (frame < 0) frame = 0;
Chris@36 558 frame = (frame / m_model->getResolution()) * m_model->getResolution();
Chris@35 559
Chris@908 560 // double height = getHeightForY(v, e->y());
Chris@35 561
Chris@35 562 if (!m_editingCommand) {
Chris@35 563 m_editingCommand = new TextModel::EditCommand(m_model, tr("Drag Label"));
Chris@35 564 }
Chris@35 565
Chris@35 566 m_editingCommand->deletePoint(m_editingPoint);
Chris@35 567 m_editingPoint.frame = frame;
Chris@908 568 m_editingPoint.height = float(height);
Chris@35 569 m_editingCommand->addPoint(m_editingPoint);
Chris@35 570 }
Chris@35 571
Chris@35 572 void
Chris@918 573 TextLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@35 574 {
Chris@587 575 // SVDEBUG << "TextLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl;
Chris@35 576 if (!m_model || !m_editing) return;
Chris@35 577
Chris@35 578 if (m_editingCommand) {
Chris@35 579
Chris@35 580 QString newName = m_editingCommand->getName();
Chris@35 581
Chris@35 582 if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@35 583 if (m_editingPoint.height != m_originalPoint.height) {
Chris@35 584 newName = tr("Move Label");
Chris@35 585 } else {
Chris@36 586 newName = tr("Move Label Horizontally");
Chris@35 587 }
Chris@35 588 } else {
Chris@36 589 newName = tr("Move Label Vertically");
Chris@35 590 }
Chris@35 591
Chris@35 592 m_editingCommand->setName(newName);
Chris@376 593 finish(m_editingCommand);
Chris@35 594 }
Chris@35 595
Chris@35 596 m_editingCommand = 0;
Chris@35 597 m_editing = false;
Chris@35 598 }
Chris@35 599
Chris@255 600 bool
Chris@918 601 TextLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@36 602 {
Chris@255 603 if (!m_model) return false;
Chris@36 604
Chris@552 605 TextModel::Point text(0);
Chris@552 606 if (!getPointToDrag(v, e->x(), e->y(), text)) return false;
Chris@36 607
Chris@552 608 QString label = text.label;
Chris@36 609
Chris@36 610 bool ok = false;
Chris@918 611 label = QInputDialog::getText(v->getView(), tr("Enter label"),
Chris@36 612 tr("Please enter a new label:"),
Chris@36 613 QLineEdit::Normal, label, &ok);
Chris@552 614 if (ok && label != text.label) {
Chris@36 615 TextModel::RelabelCommand *command =
Chris@552 616 new TextModel::RelabelCommand(m_model, text, label);
Chris@99 617 CommandHistory::getInstance()->addCommand(command);
Chris@36 618 }
Chris@255 619
Chris@255 620 return true;
Chris@36 621 }
Chris@36 622
Chris@43 623 void
Chris@908 624 TextLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@43 625 {
Chris@99 626 if (!m_model) return;
Chris@99 627
Chris@43 628 TextModel::EditCommand *command =
Chris@43 629 new TextModel::EditCommand(m_model, tr("Drag Selection"));
Chris@43 630
Chris@43 631 TextModel::PointList points =
Chris@43 632 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 633
Chris@43 634 for (TextModel::PointList::iterator i = points.begin();
Chris@43 635 i != points.end(); ++i) {
Chris@43 636
Chris@43 637 if (s.contains(i->frame)) {
Chris@43 638 TextModel::Point newPoint(*i);
Chris@43 639 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@43 640 command->deletePoint(*i);
Chris@43 641 command->addPoint(newPoint);
Chris@43 642 }
Chris@43 643 }
Chris@43 644
Chris@376 645 finish(command);
Chris@43 646 }
Chris@43 647
Chris@43 648 void
Chris@43 649 TextLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 650 {
Chris@99 651 if (!m_model) return;
Chris@99 652
Chris@43 653 TextModel::EditCommand *command =
Chris@43 654 new TextModel::EditCommand(m_model, tr("Resize Selection"));
Chris@43 655
Chris@43 656 TextModel::PointList points =
Chris@43 657 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 658
Chris@43 659 double ratio =
Chris@43 660 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@43 661 double(s.getEndFrame() - s.getStartFrame());
Chris@43 662
Chris@43 663 for (TextModel::PointList::iterator i = points.begin();
Chris@43 664 i != points.end(); ++i) {
Chris@43 665
Chris@43 666 if (s.contains(i->frame)) {
Chris@43 667
Chris@908 668 double target = double(i->frame);
Chris@908 669 target = double(newSize.getStartFrame()) +
Chris@908 670 target - double(s.getStartFrame()) * ratio;
Chris@43 671
Chris@43 672 TextModel::Point newPoint(*i);
Chris@43 673 newPoint.frame = lrint(target);
Chris@43 674 command->deletePoint(*i);
Chris@43 675 command->addPoint(newPoint);
Chris@43 676 }
Chris@43 677 }
Chris@43 678
Chris@376 679 finish(command);
Chris@43 680 }
Chris@43 681
Chris@76 682 void
Chris@76 683 TextLayer::deleteSelection(Selection s)
Chris@76 684 {
Chris@99 685 if (!m_model) return;
Chris@99 686
Chris@76 687 TextModel::EditCommand *command =
Chris@76 688 new TextModel::EditCommand(m_model, tr("Delete Selection"));
Chris@76 689
Chris@76 690 TextModel::PointList points =
Chris@76 691 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 692
Chris@76 693 for (TextModel::PointList::iterator i = points.begin();
Chris@76 694 i != points.end(); ++i) {
Chris@76 695 if (s.contains(i->frame)) command->deletePoint(*i);
Chris@76 696 }
Chris@76 697
Chris@376 698 finish(command);
Chris@76 699 }
Chris@76 700
Chris@76 701 void
Chris@918 702 TextLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@76 703 {
Chris@99 704 if (!m_model) return;
Chris@99 705
Chris@76 706 TextModel::PointList points =
Chris@76 707 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 708
Chris@76 709 for (TextModel::PointList::iterator i = points.begin();
Chris@76 710 i != points.end(); ++i) {
Chris@76 711 if (s.contains(i->frame)) {
Chris@76 712 Clipboard::Point point(i->frame, i->height, i->label);
Chris@360 713 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@76 714 to.addPoint(point);
Chris@76 715 }
Chris@76 716 }
Chris@76 717 }
Chris@76 718
Chris@125 719 bool
Chris@918 720 TextLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
Chris@76 721 {
Chris@125 722 if (!m_model) return false;
Chris@99 723
Chris@76 724 const Clipboard::PointList &points = from.getPoints();
Chris@76 725
Chris@360 726 bool realign = false;
Chris@360 727
Chris@360 728 if (clipboardHasDifferentAlignment(v, from)) {
Chris@360 729
Chris@360 730 QMessageBox::StandardButton button =
Chris@918 731 QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
Chris@360 732 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 733 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 734 QMessageBox::Yes);
Chris@360 735
Chris@360 736 if (button == QMessageBox::Cancel) {
Chris@360 737 return false;
Chris@360 738 }
Chris@360 739
Chris@360 740 if (button == QMessageBox::Yes) {
Chris@360 741 realign = true;
Chris@360 742 }
Chris@360 743 }
Chris@360 744
Chris@76 745 TextModel::EditCommand *command =
Chris@76 746 new TextModel::EditCommand(m_model, tr("Paste"));
Chris@76 747
Chris@908 748 double valueMin = 0.0, valueMax = 1.0;
Chris@125 749 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@125 750 i != points.end(); ++i) {
Chris@125 751 if (i->haveValue()) {
Chris@125 752 if (i->getValue() < valueMin) valueMin = i->getValue();
Chris@125 753 if (i->getValue() > valueMax) valueMax = i->getValue();
Chris@125 754 }
Chris@125 755 }
Chris@125 756 if (valueMax < valueMin + 1.0) valueMax = valueMin + 1.0;
Chris@125 757
Chris@76 758 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 759 i != points.end(); ++i) {
Chris@76 760
Chris@76 761 if (!i->haveFrame()) continue;
Chris@908 762 sv_frame_t frame = 0;
Chris@360 763
Chris@360 764 if (!realign) {
Chris@360 765
Chris@360 766 frame = i->getFrame();
Chris@360 767
Chris@360 768 } else {
Chris@360 769
Chris@360 770 if (i->haveReferenceFrame()) {
Chris@360 771 frame = i->getReferenceFrame();
Chris@360 772 frame = alignFromReference(v, frame);
Chris@360 773 } else {
Chris@360 774 frame = i->getFrame();
Chris@360 775 }
Chris@76 776 }
Chris@360 777
Chris@76 778 TextModel::Point newPoint(frame);
Chris@125 779
Chris@125 780 if (i->haveValue()) {
Chris@908 781 newPoint.height = float((i->getValue() - valueMin) / (valueMax - valueMin));
Chris@125 782 } else {
Chris@908 783 newPoint.height = 0.5f;
Chris@125 784 }
Chris@125 785
Chris@125 786 if (i->haveLabel()) {
Chris@125 787 newPoint.label = i->getLabel();
Chris@125 788 } else if (i->haveValue()) {
Chris@125 789 newPoint.label = QString("%1").arg(i->getValue());
Chris@125 790 } else {
Chris@125 791 newPoint.label = tr("New Point");
Chris@125 792 }
Chris@76 793
Chris@76 794 command->addPoint(newPoint);
Chris@76 795 }
Chris@76 796
Chris@376 797 finish(command);
Chris@125 798 return true;
Chris@76 799 }
Chris@76 800
Chris@287 801 int
Chris@287 802 TextLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 803 {
Chris@287 804 impose = false;
Chris@287 805 return ColourDatabase::getInstance()->getColourIndex
Chris@287 806 (QString(darkbg ? "Bright Orange" : "Orange"));
Chris@287 807 }
Chris@287 808
Chris@316 809 void
Chris@316 810 TextLayer::toXml(QTextStream &stream,
Chris@316 811 QString indent, QString extraAttributes) const
Chris@35 812 {
Chris@316 813 SingleColourLayer::toXml(stream, indent, extraAttributes);
Chris@35 814 }
Chris@35 815
Chris@35 816 void
Chris@35 817 TextLayer::setProperties(const QXmlAttributes &attributes)
Chris@35 818 {
Chris@287 819 SingleColourLayer::setProperties(attributes);
Chris@35 820 }
Chris@35 821