annotate layer/ImageLayer.cpp @ 304:4b7e8da8f069

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