annotate layer/ImageLayer.cpp @ 349:369a197737c7

* Various fixes to object lifetime management, particularly in the spectrum layer and for notification of main model deletion. The main purpose of this is to improve the behaviour of the spectrum, but I think it may also help with #1840922 Various crashes in Layer Summary window.
author Chris Cannam
date Wed, 23 Jan 2008 15:43:27 +0000
parents 832d246b327b
children 020c485aa7e0
rev   line source
Chris@303 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@303 2
Chris@303 3 /*
Chris@303 4 Sonic Visualiser
Chris@303 5 An audio file viewer and annotation editor.
Chris@303 6 Centre for Digital Music, Queen Mary, University of London.
Chris@303 7 This file copyright 2006 Chris Cannam.
Chris@303 8
Chris@303 9 This program is free software; you can redistribute it and/or
Chris@303 10 modify it under the terms of the GNU General Public License as
Chris@303 11 published by the Free Software Foundation; either version 2 of the
Chris@303 12 License, or (at your option) any later version. See the file
Chris@303 13 COPYING included with this distribution for more information.
Chris@303 14 */
Chris@303 15
Chris@303 16 #include "ImageLayer.h"
Chris@303 17
Chris@303 18 #include "data/model/Model.h"
Chris@303 19 #include "base/RealTime.h"
Chris@303 20 #include "base/Profiler.h"
Chris@303 21 #include "view/View.h"
Chris@303 22
Chris@303 23 #include "data/model/ImageModel.h"
Chris@318 24 #include "data/fileio/FileSource.h"
Chris@303 25
Chris@303 26 #include "widgets/ImageDialog.h"
Chris@303 27
Chris@303 28 #include <QPainter>
Chris@303 29 #include <QMouseEvent>
Chris@303 30 #include <QInputDialog>
Chris@305 31 #include <QMutexLocker>
Chris@316 32 #include <QTextStream>
Chris@303 33
Chris@303 34 #include <iostream>
Chris@303 35 #include <cmath>
Chris@303 36
Chris@303 37 ImageLayer::ImageMap
Chris@303 38 ImageLayer::m_images;
Chris@303 39
Chris@305 40 QMutex
Chris@305 41 ImageLayer::m_imageMapMutex;
Chris@305 42
Chris@303 43 ImageLayer::ImageLayer() :
Chris@303 44 Layer(),
Chris@303 45 m_model(0),
Chris@303 46 m_editing(false),
Chris@303 47 m_originalPoint(0, "", ""),
Chris@303 48 m_editingPoint(0, "", ""),
Chris@303 49 m_editingCommand(0)
Chris@303 50 {
Chris@305 51 }
Chris@305 52
Chris@305 53 ImageLayer::~ImageLayer()
Chris@305 54 {
Chris@318 55 for (FileSourceMap::iterator i = m_remoteFiles.begin();
Chris@305 56 i != m_remoteFiles.end(); ++i) {
Chris@305 57 delete i->second;
Chris@305 58 }
Chris@303 59 }
Chris@303 60
Chris@303 61 void
Chris@303 62 ImageLayer::setModel(ImageModel *model)
Chris@303 63 {
Chris@303 64 if (m_model == model) return;
Chris@303 65 m_model = model;
Chris@303 66
Chris@320 67 connectSignals(m_model);
Chris@305 68
Chris@303 69 emit modelReplaced();
Chris@303 70 }
Chris@303 71
Chris@303 72 Layer::PropertyList
Chris@303 73 ImageLayer::getProperties() const
Chris@303 74 {
Chris@303 75 return Layer::getProperties();
Chris@303 76 }
Chris@303 77
Chris@303 78 QString
Chris@303 79 ImageLayer::getPropertyLabel(const PropertyName &name) const
Chris@303 80 {
Chris@303 81 return "";
Chris@303 82 }
Chris@303 83
Chris@303 84 Layer::PropertyType
Chris@303 85 ImageLayer::getPropertyType(const PropertyName &name) const
Chris@303 86 {
Chris@303 87 return Layer::getPropertyType(name);
Chris@303 88 }
Chris@303 89
Chris@303 90 int
Chris@303 91 ImageLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@303 92 int *min, int *max, int *deflt) const
Chris@303 93 {
Chris@303 94 return Layer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@303 95 }
Chris@303 96
Chris@303 97 QString
Chris@303 98 ImageLayer::getPropertyValueLabel(const PropertyName &name,
Chris@303 99 int value) const
Chris@303 100 {
Chris@303 101 return Layer::getPropertyValueLabel(name, value);
Chris@303 102 }
Chris@303 103
Chris@303 104 void
Chris@303 105 ImageLayer::setProperty(const PropertyName &name, int value)
Chris@303 106 {
Chris@303 107 Layer::setProperty(name, value);
Chris@303 108 }
Chris@303 109
Chris@303 110 bool
Chris@303 111 ImageLayer::getValueExtents(float &, float &, bool &, QString &) const
Chris@303 112 {
Chris@303 113 return false;
Chris@303 114 }
Chris@303 115
Chris@303 116 bool
Chris@303 117 ImageLayer::isLayerScrollable(const View *v) const
Chris@303 118 {
Chris@304 119 return true;
Chris@303 120 }
Chris@303 121
Chris@303 122
Chris@303 123 ImageModel::PointList
Chris@303 124 ImageLayer::getLocalPoints(View *v, int x, int y) const
Chris@303 125 {
Chris@303 126 if (!m_model) return ImageModel::PointList();
Chris@303 127
Chris@304 128 // std::cerr << "ImageLayer::getLocalPoints(" << x << "," << y << "):";
Chris@304 129 const ImageModel::PointList &points(m_model->getPoints());
Chris@303 130
Chris@303 131 ImageModel::PointList rv;
Chris@303 132
Chris@304 133 for (ImageModel::PointList::const_iterator i = points.begin();
Chris@304 134 i != points.end(); ) {
Chris@303 135
Chris@303 136 const ImageModel::Point &p(*i);
Chris@304 137 int px = v->getXForFrame(p.frame);
Chris@304 138 if (px > x) break;
Chris@303 139
Chris@304 140 ++i;
Chris@304 141 if (i != points.end()) {
Chris@304 142 int nx = v->getXForFrame((*i).frame);
Chris@304 143 if (nx < x) {
Chris@304 144 // as we aim not to overlap the images, if the following
Chris@304 145 // image begins to the left of a point then the current
Chris@304 146 // one may be assumed to end to the left of it as well.
Chris@304 147 continue;
Chris@304 148 }
Chris@304 149 }
Chris@303 150
Chris@304 151 // this image is a candidate, test it properly
Chris@304 152
Chris@304 153 int width = 32;
Chris@304 154 if (m_scaled[v].find(p.image) != m_scaled[v].end()) {
Chris@304 155 width = m_scaled[v][p.image].width();
Chris@305 156 // std::cerr << "scaled width = " << width << std::endl;
Chris@304 157 }
Chris@304 158
Chris@304 159 if (x >= px && x < px + width) {
Chris@303 160 rv.insert(p);
Chris@303 161 }
Chris@303 162 }
Chris@303 163
Chris@304 164 // std::cerr << rv.size() << " point(s)" << std::endl;
Chris@303 165
Chris@303 166 return rv;
Chris@303 167 }
Chris@303 168
Chris@303 169 QString
Chris@303 170 ImageLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@303 171 {
Chris@303 172 int x = pos.x();
Chris@303 173
Chris@303 174 if (!m_model || !m_model->getSampleRate()) return "";
Chris@303 175
Chris@303 176 ImageModel::PointList points = getLocalPoints(v, x, pos.y());
Chris@303 177
Chris@303 178 if (points.empty()) {
Chris@303 179 if (!m_model->isReady()) {
Chris@303 180 return tr("In progress");
Chris@303 181 } else {
Chris@303 182 return "";
Chris@303 183 }
Chris@303 184 }
Chris@303 185
Chris@303 186 long useFrame = points.begin()->frame;
Chris@303 187
Chris@303 188 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
Chris@303 189
Chris@303 190 QString text;
Chris@303 191 /*
Chris@303 192 if (points.begin()->label == "") {
Chris@303 193 text = QString(tr("Time:\t%1\nHeight:\t%2\nLabel:\t%3"))
Chris@303 194 .arg(rt.toText(true).c_str())
Chris@303 195 .arg(points.begin()->height)
Chris@303 196 .arg(points.begin()->label);
Chris@303 197 }
Chris@303 198
Chris@303 199 pos = QPoint(v->getXForFrame(useFrame),
Chris@303 200 getYForHeight(v, points.begin()->height));
Chris@303 201 */
Chris@303 202 return text;
Chris@303 203 }
Chris@303 204
Chris@303 205
Chris@303 206 //!!! too much overlap with TimeValueLayer/TimeInstantLayer/TextLayer
Chris@303 207
Chris@303 208 bool
Chris@303 209 ImageLayer::snapToFeatureFrame(View *v, int &frame,
Chris@303 210 size_t &resolution,
Chris@303 211 SnapType snap) const
Chris@303 212 {
Chris@303 213 if (!m_model) {
Chris@303 214 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@303 215 }
Chris@303 216
Chris@303 217 resolution = m_model->getResolution();
Chris@303 218 ImageModel::PointList points;
Chris@303 219
Chris@303 220 if (snap == SnapNeighbouring) {
Chris@303 221
Chris@303 222 points = getLocalPoints(v, v->getXForFrame(frame), -1);
Chris@303 223 if (points.empty()) return false;
Chris@303 224 frame = points.begin()->frame;
Chris@303 225 return true;
Chris@303 226 }
Chris@303 227
Chris@303 228 points = m_model->getPoints(frame, frame);
Chris@303 229 int snapped = frame;
Chris@303 230 bool found = false;
Chris@303 231
Chris@303 232 for (ImageModel::PointList::const_iterator i = points.begin();
Chris@303 233 i != points.end(); ++i) {
Chris@303 234
Chris@303 235 if (snap == SnapRight) {
Chris@303 236
Chris@303 237 if (i->frame > frame) {
Chris@303 238 snapped = i->frame;
Chris@303 239 found = true;
Chris@303 240 break;
Chris@303 241 }
Chris@303 242
Chris@303 243 } else if (snap == SnapLeft) {
Chris@303 244
Chris@303 245 if (i->frame <= frame) {
Chris@303 246 snapped = i->frame;
Chris@303 247 found = true; // don't break, as the next may be better
Chris@303 248 } else {
Chris@303 249 break;
Chris@303 250 }
Chris@303 251
Chris@303 252 } else { // nearest
Chris@303 253
Chris@303 254 ImageModel::PointList::const_iterator j = i;
Chris@303 255 ++j;
Chris@303 256
Chris@303 257 if (j == points.end()) {
Chris@303 258
Chris@303 259 snapped = i->frame;
Chris@303 260 found = true;
Chris@303 261 break;
Chris@303 262
Chris@303 263 } else if (j->frame >= frame) {
Chris@303 264
Chris@303 265 if (j->frame - frame < frame - i->frame) {
Chris@303 266 snapped = j->frame;
Chris@303 267 } else {
Chris@303 268 snapped = i->frame;
Chris@303 269 }
Chris@303 270 found = true;
Chris@303 271 break;
Chris@303 272 }
Chris@303 273 }
Chris@303 274 }
Chris@303 275
Chris@303 276 frame = snapped;
Chris@303 277 return found;
Chris@303 278 }
Chris@303 279
Chris@303 280 void
Chris@303 281 ImageLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@303 282 {
Chris@303 283 if (!m_model || !m_model->isOK()) return;
Chris@303 284
Chris@303 285 int sampleRate = m_model->getSampleRate();
Chris@303 286 if (!sampleRate) return;
Chris@303 287
Chris@303 288 // Profiler profiler("ImageLayer::paint", true);
Chris@303 289
Chris@304 290 // int x0 = rect.left(), x1 = rect.right();
Chris@304 291 int x0 = 0, x1 = v->width();
Chris@304 292
Chris@303 293 long frame0 = v->getFrameForX(x0);
Chris@303 294 long frame1 = v->getFrameForX(x1);
Chris@303 295
Chris@303 296 ImageModel::PointList points(m_model->getPoints(frame0, frame1));
Chris@303 297 if (points.empty()) return;
Chris@303 298
Chris@304 299 paint.save();
Chris@304 300 paint.setClipRect(rect.x(), 0, rect.width(), v->height());
Chris@304 301
Chris@303 302 QColor penColour;
Chris@303 303 penColour = v->getForeground();
Chris@303 304
Chris@304 305 QColor brushColour;
Chris@304 306 brushColour = v->getBackground();
Chris@303 307
Chris@304 308 int h, s, val;
Chris@304 309 brushColour.getHsv(&h, &s, &val);
Chris@304 310 brushColour.setHsv(h, s, 255, 240);
Chris@303 311
Chris@304 312 paint.setPen(penColour);
Chris@304 313 paint.setBrush(brushColour);
Chris@304 314 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@303 315
Chris@303 316 for (ImageModel::PointList::const_iterator i = points.begin();
Chris@303 317 i != points.end(); ++i) {
Chris@303 318
Chris@303 319 const ImageModel::Point &p(*i);
Chris@303 320
Chris@303 321 int x = v->getXForFrame(p.frame);
Chris@303 322
Chris@303 323 int nx = x + 2000;
Chris@303 324 ImageModel::PointList::const_iterator j = i;
Chris@303 325 ++j;
Chris@303 326 if (j != points.end()) {
Chris@303 327 int jx = v->getXForFrame(j->frame);
Chris@303 328 if (jx < nx) nx = jx;
Chris@303 329 }
Chris@303 330
Chris@304 331 drawImage(v, paint, p, x, nx);
Chris@304 332 }
Chris@303 333
Chris@304 334 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@304 335 paint.restore();
Chris@304 336 }
Chris@303 337
Chris@304 338 void
Chris@304 339 ImageLayer::drawImage(View *v, QPainter &paint, const ImageModel::Point &p,
Chris@304 340 int x, int nx) const
Chris@304 341 {
Chris@304 342 QString label = p.label;
Chris@304 343 QString imageName = p.image;
Chris@304 344
Chris@304 345 QImage image;
Chris@304 346 QString additionalText;
Chris@304 347
Chris@304 348 QSize imageSize;
Chris@304 349 if (!getImageOriginalSize(imageName, imageSize)) {
Chris@304 350 image = QImage(":icons/emptypage.png");
Chris@304 351 imageSize = image.size();
Chris@304 352 additionalText = imageName;
Chris@304 353 }
Chris@304 354
Chris@304 355 int topMargin = 10;
Chris@304 356 int bottomMargin = 10;
Chris@304 357 int spacing = 5;
Chris@304 358
Chris@304 359 if (v->height() < 100) {
Chris@304 360 topMargin = 5;
Chris@304 361 bottomMargin = 5;
Chris@304 362 }
Chris@304 363
Chris@304 364 int maxBoxHeight = v->height() - topMargin - bottomMargin;
Chris@304 365
Chris@304 366 int availableWidth = nx - x - 3;
Chris@304 367 if (availableWidth < 20) availableWidth = 20;
Chris@304 368
Chris@304 369 QRect labelRect;
Chris@304 370
Chris@304 371 if (label != "") {
Chris@304 372
Chris@304 373 int likelyHeight = v->height() / 4;
Chris@304 374
Chris@304 375 int likelyWidth = // available height times image aspect
Chris@304 376 ((maxBoxHeight - likelyHeight) * imageSize.width())
Chris@304 377 / imageSize.height();
Chris@304 378
Chris@304 379 if (likelyWidth > imageSize.width()) {
Chris@304 380 likelyWidth = imageSize.width();
Chris@303 381 }
Chris@303 382
Chris@304 383 if (likelyWidth > availableWidth) {
Chris@304 384 likelyWidth = availableWidth;
Chris@303 385 }
Chris@303 386
Chris@304 387 int singleWidth = paint.fontMetrics().width(label);
Chris@304 388 if (singleWidth < availableWidth && singleWidth < likelyWidth * 2) {
Chris@304 389 likelyWidth = singleWidth + 4;
Chris@303 390 }
Chris@303 391
Chris@304 392 labelRect = paint.fontMetrics().boundingRect
Chris@304 393 (QRect(0, 0, likelyWidth, likelyHeight),
Chris@304 394 Qt::AlignCenter | Qt::TextWordWrap, label);
Chris@303 395
Chris@304 396 labelRect.setWidth(labelRect.width() + 6);
Chris@303 397 }
Chris@303 398
Chris@304 399 if (image.isNull()) {
Chris@304 400 image = getImage(v, imageName,
Chris@304 401 QSize(availableWidth,
Chris@304 402 maxBoxHeight - labelRect.height()));
Chris@304 403 }
Chris@304 404
Chris@304 405 int boxWidth = image.width();
Chris@304 406 if (boxWidth < labelRect.width()) {
Chris@304 407 boxWidth = labelRect.width();
Chris@304 408 }
Chris@304 409
Chris@304 410 int boxHeight = image.height();
Chris@304 411 if (label != "") {
Chris@304 412 boxHeight += labelRect.height() + spacing;
Chris@304 413 }
Chris@304 414
Chris@304 415 int division = image.height();
Chris@304 416
Chris@304 417 if (additionalText != "") {
Chris@304 418
Chris@304 419 paint.save();
Chris@304 420
Chris@304 421 QFont font(paint.font());
Chris@304 422 font.setItalic(true);
Chris@304 423 paint.setFont(font);
Chris@304 424
Chris@304 425 int tw = paint.fontMetrics().width(additionalText);
Chris@304 426 if (tw > availableWidth) {
Chris@304 427 tw = availableWidth;
Chris@304 428 }
Chris@304 429 if (boxWidth < tw) {
Chris@304 430 boxWidth = tw;
Chris@304 431 }
Chris@304 432 boxHeight += paint.fontMetrics().height();
Chris@304 433 division += paint.fontMetrics().height();
Chris@304 434 }
Chris@304 435
Chris@304 436 bottomMargin = v->height() - topMargin - boxHeight;
Chris@304 437 if (bottomMargin > topMargin + v->height()/7) {
Chris@304 438 topMargin += v->height()/8;
Chris@304 439 bottomMargin -= v->height()/8;
Chris@304 440 }
Chris@304 441
Chris@304 442 paint.drawRect(x - 1,
Chris@304 443 topMargin - 1,
Chris@304 444 boxWidth + 2,
Chris@304 445 boxHeight + 2);
Chris@304 446
Chris@304 447 int imageY;
Chris@304 448 if (label != "") {
Chris@304 449 imageY = topMargin + labelRect.height() + spacing;
Chris@304 450 } else {
Chris@304 451 imageY = topMargin;
Chris@304 452 }
Chris@304 453
Chris@304 454 paint.drawImage(x + (boxWidth - image.width())/2,
Chris@304 455 imageY,
Chris@304 456 image);
Chris@304 457
Chris@304 458 if (additionalText != "") {
Chris@304 459 paint.drawText(x,
Chris@304 460 imageY + image.height() + paint.fontMetrics().ascent(),
Chris@304 461 additionalText);
Chris@304 462 paint.restore();
Chris@304 463 }
Chris@304 464
Chris@304 465 if (label != "") {
Chris@304 466 paint.drawLine(x,
Chris@304 467 topMargin + labelRect.height() + spacing,
Chris@304 468 x + boxWidth,
Chris@304 469 topMargin + labelRect.height() + spacing);
Chris@304 470
Chris@304 471 paint.drawText(QRect(x,
Chris@304 472 topMargin,
Chris@304 473 boxWidth,
Chris@304 474 labelRect.height()),
Chris@304 475 Qt::AlignCenter | Qt::TextWordWrap,
Chris@304 476 label);
Chris@304 477 }
Chris@303 478 }
Chris@303 479
Chris@303 480 void
Chris@303 481 ImageLayer::setLayerDormant(const View *v, bool dormant)
Chris@303 482 {
Chris@303 483 if (dormant) {
Chris@303 484 // Delete the images named in the view's scaled map from the
Chris@303 485 // general image map as well. They can always be re-loaded
Chris@303 486 // if it turns out another view still needs them.
Chris@305 487 QMutexLocker locker(&m_imageMapMutex);
Chris@303 488 for (ImageMap::iterator i = m_scaled[v].begin();
Chris@303 489 i != m_scaled[v].end(); ++i) {
Chris@303 490 m_images.erase(i->first);
Chris@303 491 }
Chris@303 492 m_scaled.erase(v);
Chris@303 493 }
Chris@303 494 }
Chris@303 495
Chris@303 496 //!!! how to reap no-longer-used images?
Chris@303 497
Chris@304 498 bool
Chris@304 499 ImageLayer::getImageOriginalSize(QString name, QSize &size) const
Chris@303 500 {
Chris@305 501 // std::cerr << "getImageOriginalSize: \"" << name.toStdString() << "\"" << std::endl;
Chris@305 502
Chris@305 503 QMutexLocker locker(&m_imageMapMutex);
Chris@303 504 if (m_images.find(name) == m_images.end()) {
Chris@305 505 // std::cerr << "don't have, trying to open local" << std::endl;
Chris@305 506 m_images[name] = QImage(getLocalFilename(name));
Chris@303 507 }
Chris@304 508 if (m_images[name].isNull()) {
Chris@305 509 // std::cerr << "null image" << std::endl;
Chris@304 510 return false;
Chris@304 511 } else {
Chris@304 512 size = m_images[name].size();
Chris@304 513 return true;
Chris@304 514 }
Chris@303 515 }
Chris@303 516
Chris@303 517 QImage
Chris@303 518 ImageLayer::getImage(View *v, QString name, QSize maxSize) const
Chris@303 519 {
Chris@303 520 bool need = false;
Chris@303 521
Chris@305 522 // std::cerr << "ImageLayer::getImage(" << v << ", " << name.toStdString() << ", ("
Chris@305 523 // << maxSize.width() << "x" << maxSize.height() << "))" << std::endl;
Chris@303 524
Chris@304 525 if (!m_scaled[v][name].isNull() &&
Chris@304 526 ((m_scaled[v][name].width() == maxSize.width() &&
Chris@304 527 m_scaled[v][name].height() <= maxSize.height()) ||
Chris@304 528 (m_scaled[v][name].width() <= maxSize.width() &&
Chris@304 529 m_scaled[v][name].height() == maxSize.height()))) {
Chris@305 530 // std::cerr << "cache hit" << std::endl;
Chris@303 531 return m_scaled[v][name];
Chris@303 532 }
Chris@303 533
Chris@305 534 QMutexLocker locker(&m_imageMapMutex);
Chris@305 535
Chris@303 536 if (m_images.find(name) == m_images.end()) {
Chris@305 537 m_images[name] = QImage(getLocalFilename(name));
Chris@303 538 }
Chris@303 539
Chris@303 540 if (m_images[name].isNull()) {
Chris@305 541 // std::cerr << "null image" << std::endl;
Chris@303 542 m_scaled[v][name] = QImage();
Chris@304 543 } else if (m_images[name].width() <= maxSize.width() &&
Chris@304 544 m_images[name].height() <= maxSize.height()) {
Chris@304 545 m_scaled[v][name] = m_images[name];
Chris@303 546 } else {
Chris@303 547 m_scaled[v][name] =
Chris@303 548 m_images[name].scaled(maxSize,
Chris@303 549 Qt::KeepAspectRatio,
Chris@303 550 Qt::SmoothTransformation);
Chris@303 551 }
Chris@303 552
Chris@303 553 return m_scaled[v][name];
Chris@303 554 }
Chris@303 555
Chris@303 556 void
Chris@303 557 ImageLayer::drawStart(View *v, QMouseEvent *e)
Chris@303 558 {
Chris@303 559 // std::cerr << "ImageLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@303 560
Chris@303 561 if (!m_model) {
Chris@303 562 std::cerr << "ImageLayer::drawStart: no model" << std::endl;
Chris@303 563 return;
Chris@303 564 }
Chris@303 565
Chris@303 566 long frame = v->getFrameForX(e->x());
Chris@303 567 if (frame < 0) frame = 0;
Chris@303 568 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@303 569
Chris@303 570 m_editingPoint = ImageModel::Point(frame, "", "");
Chris@303 571 m_originalPoint = m_editingPoint;
Chris@303 572
Chris@303 573 if (m_editingCommand) m_editingCommand->finish();
Chris@303 574 m_editingCommand = new ImageModel::EditCommand(m_model, "Add Image");
Chris@303 575 m_editingCommand->addPoint(m_editingPoint);
Chris@303 576
Chris@303 577 m_editing = true;
Chris@303 578 }
Chris@303 579
Chris@303 580 void
Chris@303 581 ImageLayer::drawDrag(View *v, QMouseEvent *e)
Chris@303 582 {
Chris@303 583 // std::cerr << "ImageLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@303 584
Chris@303 585 if (!m_model || !m_editing) return;
Chris@303 586
Chris@303 587 long frame = v->getFrameForX(e->x());
Chris@303 588 if (frame < 0) frame = 0;
Chris@303 589 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@303 590
Chris@303 591 m_editingCommand->deletePoint(m_editingPoint);
Chris@303 592 m_editingPoint.frame = frame;
Chris@303 593 m_editingCommand->addPoint(m_editingPoint);
Chris@303 594 }
Chris@303 595
Chris@303 596 void
Chris@303 597 ImageLayer::drawEnd(View *v, QMouseEvent *)
Chris@303 598 {
Chris@303 599 // std::cerr << "ImageLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@303 600 if (!m_model || !m_editing) return;
Chris@303 601
Chris@303 602 bool ok = false;
Chris@303 603
Chris@307 604 ImageDialog dialog(tr("Select image"), "", "");
Chris@305 605
Chris@303 606 if (dialog.exec() == QDialog::Accepted) {
Chris@305 607
Chris@305 608 checkAddRemote(dialog.getImage());
Chris@305 609
Chris@303 610 ImageModel::ChangeImageCommand *command =
Chris@303 611 new ImageModel::ChangeImageCommand
Chris@303 612 (m_model, m_editingPoint, dialog.getImage(), dialog.getLabel());
Chris@303 613 m_editingCommand->addCommand(command);
Chris@307 614 } else {
Chris@307 615 m_editingCommand->deletePoint(m_editingPoint);
Chris@303 616 }
Chris@303 617
Chris@303 618 m_editingCommand->finish();
Chris@303 619 m_editingCommand = 0;
Chris@303 620 m_editing = false;
Chris@303 621 }
Chris@303 622
Chris@312 623 bool
Chris@312 624 ImageLayer::addImage(long frame, QString url)
Chris@312 625 {
Chris@312 626 QImage image(getLocalFilename(url));
Chris@312 627 if (image.isNull()) {
Chris@312 628 delete m_remoteFiles[url];
Chris@312 629 m_remoteFiles.erase(url);
Chris@312 630 return false;
Chris@312 631 }
Chris@312 632
Chris@312 633 ImageModel::Point point(frame, url, "");
Chris@312 634 ImageModel::EditCommand *command =
Chris@312 635 new ImageModel::EditCommand(m_model, "Add Image");
Chris@312 636 command->addPoint(point);
Chris@312 637 command->finish();
Chris@312 638 return true;
Chris@312 639 }
Chris@312 640
Chris@303 641 void
Chris@303 642 ImageLayer::editStart(View *v, QMouseEvent *e)
Chris@303 643 {
Chris@303 644 // std::cerr << "ImageLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@303 645
Chris@303 646 if (!m_model) return;
Chris@303 647
Chris@303 648 ImageModel::PointList points = getLocalPoints(v, e->x(), e->y());
Chris@303 649 if (points.empty()) return;
Chris@303 650
Chris@303 651 m_editOrigin = e->pos();
Chris@303 652 m_editingPoint = *points.begin();
Chris@303 653 m_originalPoint = m_editingPoint;
Chris@303 654
Chris@303 655 if (m_editingCommand) {
Chris@303 656 m_editingCommand->finish();
Chris@303 657 m_editingCommand = 0;
Chris@303 658 }
Chris@303 659
Chris@303 660 m_editing = true;
Chris@303 661 }
Chris@303 662
Chris@303 663 void
Chris@303 664 ImageLayer::editDrag(View *v, QMouseEvent *e)
Chris@303 665 {
Chris@303 666 if (!m_model || !m_editing) return;
Chris@303 667
Chris@303 668 long frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x());
Chris@303 669 long frame = m_originalPoint.frame + frameDiff;
Chris@303 670
Chris@303 671 if (frame < 0) frame = 0;
Chris@303 672 frame = (frame / m_model->getResolution()) * m_model->getResolution();
Chris@303 673
Chris@303 674 if (!m_editingCommand) {
Chris@303 675 m_editingCommand = new ImageModel::EditCommand(m_model, tr("Move Image"));
Chris@303 676 }
Chris@303 677
Chris@303 678 m_editingCommand->deletePoint(m_editingPoint);
Chris@303 679 m_editingPoint.frame = frame;
Chris@303 680 m_editingCommand->addPoint(m_editingPoint);
Chris@303 681 }
Chris@303 682
Chris@303 683 void
Chris@303 684 ImageLayer::editEnd(View *, QMouseEvent *)
Chris@303 685 {
Chris@303 686 // std::cerr << "ImageLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@303 687 if (!m_model || !m_editing) return;
Chris@303 688
Chris@303 689 if (m_editingCommand) {
Chris@303 690 m_editingCommand->finish();
Chris@303 691 }
Chris@303 692
Chris@303 693 m_editingCommand = 0;
Chris@303 694 m_editing = false;
Chris@303 695 }
Chris@303 696
Chris@303 697 bool
Chris@303 698 ImageLayer::editOpen(View *v, QMouseEvent *e)
Chris@303 699 {
Chris@303 700 if (!m_model) return false;
Chris@303 701
Chris@303 702 ImageModel::PointList points = getLocalPoints(v, e->x(), e->y());
Chris@303 703 if (points.empty()) return false;
Chris@303 704
Chris@303 705 QString image = points.begin()->image;
Chris@303 706 QString label = points.begin()->label;
Chris@303 707
Chris@303 708 ImageDialog dialog(tr("Select image"),
Chris@303 709 image,
Chris@303 710 label);
Chris@303 711
Chris@303 712 if (dialog.exec() == QDialog::Accepted) {
Chris@305 713
Chris@305 714 checkAddRemote(dialog.getImage());
Chris@305 715
Chris@303 716 ImageModel::ChangeImageCommand *command =
Chris@303 717 new ImageModel::ChangeImageCommand
Chris@303 718 (m_model, *points.begin(), dialog.getImage(), dialog.getLabel());
Chris@305 719
Chris@303 720 CommandHistory::getInstance()->addCommand(command);
Chris@303 721 }
Chris@303 722
Chris@303 723 return true;
Chris@303 724 }
Chris@303 725
Chris@303 726 void
Chris@303 727 ImageLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@303 728 {
Chris@303 729 if (!m_model) return;
Chris@303 730
Chris@303 731 ImageModel::EditCommand *command =
Chris@303 732 new ImageModel::EditCommand(m_model, tr("Drag Selection"));
Chris@303 733
Chris@303 734 ImageModel::PointList points =
Chris@303 735 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@303 736
Chris@303 737 for (ImageModel::PointList::iterator i = points.begin();
Chris@303 738 i != points.end(); ++i) {
Chris@303 739
Chris@303 740 if (s.contains(i->frame)) {
Chris@303 741 ImageModel::Point newPoint(*i);
Chris@303 742 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@303 743 command->deletePoint(*i);
Chris@303 744 command->addPoint(newPoint);
Chris@303 745 }
Chris@303 746 }
Chris@303 747
Chris@303 748 command->finish();
Chris@303 749 }
Chris@303 750
Chris@303 751 void
Chris@303 752 ImageLayer::resizeSelection(Selection s, Selection newSize)
Chris@303 753 {
Chris@303 754 if (!m_model) return;
Chris@303 755
Chris@303 756 ImageModel::EditCommand *command =
Chris@303 757 new ImageModel::EditCommand(m_model, tr("Resize Selection"));
Chris@303 758
Chris@303 759 ImageModel::PointList points =
Chris@303 760 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@303 761
Chris@303 762 double ratio =
Chris@303 763 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@303 764 double(s.getEndFrame() - s.getStartFrame());
Chris@303 765
Chris@303 766 for (ImageModel::PointList::iterator i = points.begin();
Chris@303 767 i != points.end(); ++i) {
Chris@303 768
Chris@303 769 if (s.contains(i->frame)) {
Chris@303 770
Chris@303 771 double target = i->frame;
Chris@303 772 target = newSize.getStartFrame() +
Chris@303 773 double(target - s.getStartFrame()) * ratio;
Chris@303 774
Chris@303 775 ImageModel::Point newPoint(*i);
Chris@303 776 newPoint.frame = lrint(target);
Chris@303 777 command->deletePoint(*i);
Chris@303 778 command->addPoint(newPoint);
Chris@303 779 }
Chris@303 780 }
Chris@303 781
Chris@303 782 command->finish();
Chris@303 783 }
Chris@303 784
Chris@303 785 void
Chris@303 786 ImageLayer::deleteSelection(Selection s)
Chris@303 787 {
Chris@303 788 if (!m_model) return;
Chris@303 789
Chris@303 790 ImageModel::EditCommand *command =
Chris@303 791 new ImageModel::EditCommand(m_model, tr("Delete Selection"));
Chris@303 792
Chris@303 793 ImageModel::PointList points =
Chris@303 794 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@303 795
Chris@303 796 for (ImageModel::PointList::iterator i = points.begin();
Chris@303 797 i != points.end(); ++i) {
Chris@303 798 if (s.contains(i->frame)) command->deletePoint(*i);
Chris@303 799 }
Chris@303 800
Chris@303 801 command->finish();
Chris@303 802 }
Chris@303 803
Chris@303 804 void
Chris@303 805 ImageLayer::copy(Selection s, Clipboard &to)
Chris@303 806 {
Chris@303 807 if (!m_model) return;
Chris@303 808
Chris@303 809 ImageModel::PointList points =
Chris@303 810 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@303 811
Chris@303 812 for (ImageModel::PointList::iterator i = points.begin();
Chris@303 813 i != points.end(); ++i) {
Chris@303 814 if (s.contains(i->frame)) {
Chris@303 815 //!!! inadequate
Chris@303 816 Clipboard::Point point(i->frame, i->label);
Chris@335 817 point.setReferenceFrame(m_model->alignToReference(i->frame));
Chris@303 818 to.addPoint(point);
Chris@303 819 }
Chris@303 820 }
Chris@303 821 }
Chris@303 822
Chris@303 823 bool
Chris@303 824 ImageLayer::paste(const Clipboard &from, int frameOffset, bool /* interactive */)
Chris@303 825 {
Chris@303 826 if (!m_model) return false;
Chris@303 827
Chris@303 828 const Clipboard::PointList &points = from.getPoints();
Chris@303 829
Chris@303 830 ImageModel::EditCommand *command =
Chris@303 831 new ImageModel::EditCommand(m_model, tr("Paste"));
Chris@303 832
Chris@303 833 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@303 834 i != points.end(); ++i) {
Chris@303 835
Chris@303 836 if (!i->haveFrame()) continue;
Chris@303 837 size_t frame = 0;
Chris@303 838 if (frameOffset > 0 || -frameOffset < i->getFrame()) {
Chris@303 839 frame = i->getFrame() + frameOffset;
Chris@303 840 }
Chris@303 841 ImageModel::Point newPoint(frame);
Chris@303 842
Chris@303 843 //!!! inadequate
Chris@303 844
Chris@303 845 if (i->haveLabel()) {
Chris@303 846 newPoint.label = i->getLabel();
Chris@303 847 } else if (i->haveValue()) {
Chris@303 848 newPoint.label = QString("%1").arg(i->getValue());
Chris@303 849 } else {
Chris@303 850 newPoint.label = tr("New Point");
Chris@303 851 }
Chris@303 852
Chris@303 853 command->addPoint(newPoint);
Chris@303 854 }
Chris@303 855
Chris@303 856 command->finish();
Chris@303 857 return true;
Chris@303 858 }
Chris@303 859
Chris@303 860 QString
Chris@305 861 ImageLayer::getLocalFilename(QString img) const
Chris@305 862 {
Chris@305 863 if (m_remoteFiles.find(img) == m_remoteFiles.end()) {
Chris@305 864 checkAddRemote(img);
Chris@312 865 if (m_remoteFiles.find(img) == m_remoteFiles.end()) {
Chris@312 866 return img;
Chris@312 867 }
Chris@305 868 }
Chris@305 869 return m_remoteFiles[img]->getLocalFilename();
Chris@305 870 }
Chris@305 871
Chris@305 872 void
Chris@305 873 ImageLayer::checkAddRemote(QString img) const
Chris@305 874 {
Chris@318 875 if (FileSource::isRemote(img)) {
Chris@305 876
Chris@312 877 std::cerr << "ImageLayer::checkAddRemote(" << img.toStdString() << "): yes, trying..." << std::endl;
Chris@312 878
Chris@305 879 if (m_remoteFiles.find(img) != m_remoteFiles.end()) {
Chris@305 880 return;
Chris@305 881 }
Chris@305 882
Chris@344 883 FileSource *rf = new FileSource(img, FileSource::ProgressDialog);
Chris@317 884 if (rf->isOK()) {
Chris@317 885 std::cerr << "ok, adding it (local filename = " << rf->getLocalFilename().toStdString() << ")" << std::endl;
Chris@317 886 m_remoteFiles[img] = rf;
Chris@317 887 connect(rf, SIGNAL(ready()), this, SLOT(remoteFileReady()));
Chris@317 888 } else {
Chris@317 889 delete rf;
Chris@305 890 }
Chris@305 891 }
Chris@305 892 }
Chris@305 893
Chris@305 894 void
Chris@305 895 ImageLayer::checkAddRemotes()
Chris@305 896 {
Chris@305 897 const ImageModel::PointList &points(m_model->getPoints());
Chris@305 898
Chris@305 899 for (ImageModel::PointList::const_iterator i = points.begin();
Chris@305 900 i != points.end(); ++i) {
Chris@305 901
Chris@305 902 checkAddRemote((*i).image);
Chris@305 903 }
Chris@305 904 }
Chris@305 905
Chris@305 906 void
Chris@305 907 ImageLayer::remoteFileReady()
Chris@305 908 {
Chris@305 909 // std::cerr << "ImageLayer::remoteFileReady" << std::endl;
Chris@305 910
Chris@318 911 FileSource *rf = dynamic_cast<FileSource *>(sender());
Chris@305 912 if (!rf) return;
Chris@305 913
Chris@305 914 QString img;
Chris@318 915 for (FileSourceMap::const_iterator i = m_remoteFiles.begin();
Chris@305 916 i != m_remoteFiles.end(); ++i) {
Chris@305 917 if (i->second == rf) {
Chris@305 918 img = i->first;
Chris@305 919 // std::cerr << "it's image \"" << img.toStdString() << "\"" << std::endl;
Chris@305 920 break;
Chris@305 921 }
Chris@305 922 }
Chris@305 923 if (img == "") return;
Chris@305 924
Chris@305 925 QMutexLocker locker(&m_imageMapMutex);
Chris@305 926 m_images.erase(img);
Chris@305 927 for (ViewImageMap::iterator i = m_scaled.begin(); i != m_scaled.end(); ++i) {
Chris@305 928 i->second.erase(img);
Chris@306 929 emit modelChanged();
Chris@305 930 }
Chris@305 931 }
Chris@305 932
Chris@316 933 void
Chris@316 934 ImageLayer::toXml(QTextStream &stream,
Chris@316 935 QString indent, QString extraAttributes) const
Chris@303 936 {
Chris@316 937 Layer::toXml(stream, indent, extraAttributes);
Chris@303 938 }
Chris@303 939
Chris@303 940 void
Chris@303 941 ImageLayer::setProperties(const QXmlAttributes &attributes)
Chris@303 942 {
Chris@303 943 }
Chris@303 944