annotate layer/TextLayer.cpp @ 454:e2a40fdadd8c

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