annotate layer/ImageLayer.cpp @ 303:46faec7aae12

* Phase 1 of an image layer.
author Chris Cannam
date Thu, 04 Oct 2007 16:34:11 +0000
parents
children 4b7e8da8f069
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@303 113 QPoint discard;
Chris@303 114 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@303 115 }
Chris@303 116
Chris@303 117
Chris@303 118 ImageModel::PointList
Chris@303 119 ImageLayer::getLocalPoints(View *v, int x, int y) const
Chris@303 120 {
Chris@303 121 if (!m_model) return ImageModel::PointList();
Chris@303 122
Chris@303 123 std::cerr << "ImageLayer::getLocalPoints(" << x << "," << y << "):";
Chris@303 124
Chris@303 125 long frame0 = v->getFrameForX(-150);
Chris@303 126 long frame1 = v->getFrameForX(v->width() + 150);
Chris@303 127
Chris@303 128 ImageModel::PointList points(m_model->getPoints(frame0, frame1));
Chris@303 129
Chris@303 130 ImageModel::PointList rv;
Chris@303 131
Chris@303 132 //!!! need to store drawn size as well as original size for each
Chris@303 133 //image, but for now:
Chris@303 134
Chris@303 135 for (ImageModel::PointList::iterator i = points.begin();
Chris@303 136 i != points.end(); ++i) {
Chris@303 137
Chris@303 138 const ImageModel::Point &p(*i);
Chris@303 139
Chris@303 140 int px = v->getXForFrame(p.frame);
Chris@303 141
Chris@303 142 if (x >= px && x < px + 100) {
Chris@303 143 rv.insert(p);
Chris@303 144 }
Chris@303 145 }
Chris@303 146
Chris@303 147 std::cerr << rv.size() << " point(s)" << std::endl;
Chris@303 148
Chris@303 149 return rv;
Chris@303 150 }
Chris@303 151
Chris@303 152 QString
Chris@303 153 ImageLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@303 154 {
Chris@303 155 int x = pos.x();
Chris@303 156
Chris@303 157 if (!m_model || !m_model->getSampleRate()) return "";
Chris@303 158
Chris@303 159 ImageModel::PointList points = getLocalPoints(v, x, pos.y());
Chris@303 160
Chris@303 161 if (points.empty()) {
Chris@303 162 if (!m_model->isReady()) {
Chris@303 163 return tr("In progress");
Chris@303 164 } else {
Chris@303 165 return "";
Chris@303 166 }
Chris@303 167 }
Chris@303 168
Chris@303 169 long useFrame = points.begin()->frame;
Chris@303 170
Chris@303 171 RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
Chris@303 172
Chris@303 173 QString text;
Chris@303 174 /*
Chris@303 175 if (points.begin()->label == "") {
Chris@303 176 text = QString(tr("Time:\t%1\nHeight:\t%2\nLabel:\t%3"))
Chris@303 177 .arg(rt.toText(true).c_str())
Chris@303 178 .arg(points.begin()->height)
Chris@303 179 .arg(points.begin()->label);
Chris@303 180 }
Chris@303 181
Chris@303 182 pos = QPoint(v->getXForFrame(useFrame),
Chris@303 183 getYForHeight(v, points.begin()->height));
Chris@303 184 */
Chris@303 185 return text;
Chris@303 186 }
Chris@303 187
Chris@303 188
Chris@303 189 //!!! too much overlap with TimeValueLayer/TimeInstantLayer/TextLayer
Chris@303 190
Chris@303 191 bool
Chris@303 192 ImageLayer::snapToFeatureFrame(View *v, int &frame,
Chris@303 193 size_t &resolution,
Chris@303 194 SnapType snap) const
Chris@303 195 {
Chris@303 196 if (!m_model) {
Chris@303 197 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@303 198 }
Chris@303 199
Chris@303 200 resolution = m_model->getResolution();
Chris@303 201 ImageModel::PointList points;
Chris@303 202
Chris@303 203 if (snap == SnapNeighbouring) {
Chris@303 204
Chris@303 205 points = getLocalPoints(v, v->getXForFrame(frame), -1);
Chris@303 206 if (points.empty()) return false;
Chris@303 207 frame = points.begin()->frame;
Chris@303 208 return true;
Chris@303 209 }
Chris@303 210
Chris@303 211 points = m_model->getPoints(frame, frame);
Chris@303 212 int snapped = frame;
Chris@303 213 bool found = false;
Chris@303 214
Chris@303 215 for (ImageModel::PointList::const_iterator i = points.begin();
Chris@303 216 i != points.end(); ++i) {
Chris@303 217
Chris@303 218 if (snap == SnapRight) {
Chris@303 219
Chris@303 220 if (i->frame > frame) {
Chris@303 221 snapped = i->frame;
Chris@303 222 found = true;
Chris@303 223 break;
Chris@303 224 }
Chris@303 225
Chris@303 226 } else if (snap == SnapLeft) {
Chris@303 227
Chris@303 228 if (i->frame <= frame) {
Chris@303 229 snapped = i->frame;
Chris@303 230 found = true; // don't break, as the next may be better
Chris@303 231 } else {
Chris@303 232 break;
Chris@303 233 }
Chris@303 234
Chris@303 235 } else { // nearest
Chris@303 236
Chris@303 237 ImageModel::PointList::const_iterator j = i;
Chris@303 238 ++j;
Chris@303 239
Chris@303 240 if (j == points.end()) {
Chris@303 241
Chris@303 242 snapped = i->frame;
Chris@303 243 found = true;
Chris@303 244 break;
Chris@303 245
Chris@303 246 } else if (j->frame >= frame) {
Chris@303 247
Chris@303 248 if (j->frame - frame < frame - i->frame) {
Chris@303 249 snapped = j->frame;
Chris@303 250 } else {
Chris@303 251 snapped = i->frame;
Chris@303 252 }
Chris@303 253 found = true;
Chris@303 254 break;
Chris@303 255 }
Chris@303 256 }
Chris@303 257 }
Chris@303 258
Chris@303 259 frame = snapped;
Chris@303 260 return found;
Chris@303 261 }
Chris@303 262
Chris@303 263 void
Chris@303 264 ImageLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@303 265 {
Chris@303 266 if (!m_model || !m_model->isOK()) return;
Chris@303 267
Chris@303 268 int sampleRate = m_model->getSampleRate();
Chris@303 269 if (!sampleRate) return;
Chris@303 270
Chris@303 271 // Profiler profiler("ImageLayer::paint", true);
Chris@303 272
Chris@303 273 int x0 = rect.left(), x1 = rect.right();
Chris@303 274 long frame0 = v->getFrameForX(x0);
Chris@303 275 long frame1 = v->getFrameForX(x1);
Chris@303 276
Chris@303 277 ImageModel::PointList points(m_model->getPoints(frame0, frame1));
Chris@303 278 if (points.empty()) return;
Chris@303 279
Chris@303 280 QColor penColour;
Chris@303 281 penColour = v->getForeground();
Chris@303 282
Chris@303 283 // std::cerr << "ImageLayer::paint: resolution is "
Chris@303 284 // << m_model->getResolution() << " frames" << std::endl;
Chris@303 285
Chris@303 286 QPoint localPos;
Chris@303 287 long illuminateFrame = -1;
Chris@303 288
Chris@303 289 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@303 290 ImageModel::PointList localPoints = getLocalPoints(v, localPos.x(),
Chris@303 291 localPos.y());
Chris@303 292 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
Chris@303 293 }
Chris@303 294
Chris@303 295 paint.save();
Chris@303 296 paint.setClipRect(rect.x(), 0, rect.width(), v->height());
Chris@303 297
Chris@303 298 for (ImageModel::PointList::const_iterator i = points.begin();
Chris@303 299 i != points.end(); ++i) {
Chris@303 300
Chris@303 301 const ImageModel::Point &p(*i);
Chris@303 302
Chris@303 303 int x = v->getXForFrame(p.frame);
Chris@303 304
Chris@303 305 int nx = x + 2000;
Chris@303 306 ImageModel::PointList::const_iterator j = i;
Chris@303 307 ++j;
Chris@303 308 if (j != points.end()) {
Chris@303 309 int jx = v->getXForFrame(j->frame);
Chris@303 310 if (jx < nx) nx = jx;
Chris@303 311 }
Chris@303 312 /*
Chris@303 313 if (illuminateFrame == p.frame) {
Chris@303 314 paint.setBrush(penColour);
Chris@303 315 paint.setPen(v->getBackground());
Chris@303 316 } else {
Chris@303 317 paint.setPen(penColour);
Chris@303 318 paint.setBrush(brushColour);
Chris@303 319 }
Chris@303 320 */
Chris@303 321 QString label = p.label;
Chris@303 322 QString imageName = p.image;
Chris@303 323
Chris@303 324 int nw = nx - x;
Chris@303 325 if (nw < 10) nw = 20;
Chris@303 326
Chris@303 327 int top = 10;
Chris@303 328 if (v->height() < 50) top = 5;
Chris@303 329
Chris@303 330 int bottom = top;
Chris@303 331
Chris@303 332 QRect labelRect;
Chris@303 333 if (label != "") {
Chris@303 334 float aspect = getImageAspect(imageName);
Chris@303 335 int iw = lrintf((v->height() - v->height()/4 - top - bottom)
Chris@303 336 * aspect);
Chris@303 337 labelRect = paint.fontMetrics().boundingRect
Chris@303 338 (QRect(0, 0, iw, v->height()/4),
Chris@303 339 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label);
Chris@303 340 bottom += labelRect.height() + 5;
Chris@303 341 }
Chris@303 342
Chris@303 343 QImage image = getImage(v,
Chris@303 344 imageName,
Chris@303 345 QSize(nw, v->height() - top - bottom));
Chris@303 346
Chris@303 347 if (image.isNull()) {
Chris@303 348 image = QImage(":icons/emptypage.png");
Chris@303 349 }
Chris@303 350
Chris@303 351 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@303 352
Chris@303 353 int boxWidth = image.width();
Chris@303 354
Chris@303 355 if (label != "") {
Chris@303 356 boxWidth = std::max(boxWidth, labelRect.width());
Chris@303 357 }
Chris@303 358
Chris@303 359 paint.drawRect(x-1, top-1, boxWidth+1, v->height() - 2 * top +1);
Chris@303 360
Chris@303 361 paint.setRenderHint(QPainter::Antialiasing, true);
Chris@303 362
Chris@303 363 paint.drawImage(x + (boxWidth - image.width())/2, top, image);
Chris@303 364
Chris@303 365 paint.drawText(QRect(x, v->height() - bottom + 5,
Chris@303 366 boxWidth, labelRect.height()),
Chris@303 367 Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap,
Chris@303 368 label);
Chris@303 369 }
Chris@303 370
Chris@303 371 paint.restore();
Chris@303 372 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@303 373 }
Chris@303 374
Chris@303 375 void
Chris@303 376 ImageLayer::setLayerDormant(const View *v, bool dormant)
Chris@303 377 {
Chris@303 378 if (dormant) {
Chris@303 379 // Delete the images named in the view's scaled map from the
Chris@303 380 // general image map as well. They can always be re-loaded
Chris@303 381 // if it turns out another view still needs them.
Chris@303 382 for (ImageMap::iterator i = m_scaled[v].begin();
Chris@303 383 i != m_scaled[v].end(); ++i) {
Chris@303 384 m_images.erase(i->first);
Chris@303 385 }
Chris@303 386 m_scaled.erase(v);
Chris@303 387 }
Chris@303 388 }
Chris@303 389
Chris@303 390 //!!! how to reap no-longer-used images?
Chris@303 391
Chris@303 392 float
Chris@303 393 ImageLayer::getImageAspect(QString name) const
Chris@303 394 {
Chris@303 395 if (m_images.find(name) == m_images.end()) {
Chris@303 396 m_images[name] = QImage(name);
Chris@303 397 }
Chris@303 398 if (m_images[name].isNull()) return 1.f;
Chris@303 399 return float(m_images[name].width()) / float(m_images[name].height());
Chris@303 400 }
Chris@303 401
Chris@303 402 QImage
Chris@303 403 ImageLayer::getImage(View *v, QString name, QSize maxSize) const
Chris@303 404 {
Chris@303 405 bool need = false;
Chris@303 406
Chris@303 407 // std::cerr << "ImageLayer::getImage(" << v << ", " << name.toStdString() << ", ("
Chris@303 408 // << maxSize.width() << "x" << maxSize.height() << "))" << std::endl;
Chris@303 409
Chris@303 410 if (!m_scaled[v][name].isNull() &&
Chris@303 411 (m_scaled[v][name].width() == maxSize.width() ||
Chris@303 412 m_scaled[v][name].height() == maxSize.height())) {
Chris@303 413 // std::cerr << "cache hit" << std::endl;
Chris@303 414 return m_scaled[v][name];
Chris@303 415 }
Chris@303 416
Chris@303 417 if (m_images.find(name) == m_images.end()) {
Chris@303 418 m_images[name] = QImage(name);
Chris@303 419 }
Chris@303 420
Chris@303 421 if (m_images[name].isNull()) {
Chris@303 422 // std::cerr << "null image" << std::endl;
Chris@303 423 m_scaled[v][name] = QImage();
Chris@303 424 } else {
Chris@303 425 m_scaled[v][name] =
Chris@303 426 m_images[name].scaled(maxSize,
Chris@303 427 Qt::KeepAspectRatio,
Chris@303 428 Qt::SmoothTransformation);
Chris@303 429 }
Chris@303 430
Chris@303 431 return m_scaled[v][name];
Chris@303 432 }
Chris@303 433
Chris@303 434 void
Chris@303 435 ImageLayer::drawStart(View *v, QMouseEvent *e)
Chris@303 436 {
Chris@303 437 // std::cerr << "ImageLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@303 438
Chris@303 439 if (!m_model) {
Chris@303 440 std::cerr << "ImageLayer::drawStart: no model" << std::endl;
Chris@303 441 return;
Chris@303 442 }
Chris@303 443
Chris@303 444 long frame = v->getFrameForX(e->x());
Chris@303 445 if (frame < 0) frame = 0;
Chris@303 446 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@303 447
Chris@303 448 m_editingPoint = ImageModel::Point(frame, "", "");
Chris@303 449 m_originalPoint = m_editingPoint;
Chris@303 450
Chris@303 451 if (m_editingCommand) m_editingCommand->finish();
Chris@303 452 m_editingCommand = new ImageModel::EditCommand(m_model, "Add Image");
Chris@303 453 m_editingCommand->addPoint(m_editingPoint);
Chris@303 454
Chris@303 455 m_editing = true;
Chris@303 456 }
Chris@303 457
Chris@303 458 void
Chris@303 459 ImageLayer::drawDrag(View *v, QMouseEvent *e)
Chris@303 460 {
Chris@303 461 // std::cerr << "ImageLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@303 462
Chris@303 463 if (!m_model || !m_editing) return;
Chris@303 464
Chris@303 465 long frame = v->getFrameForX(e->x());
Chris@303 466 if (frame < 0) frame = 0;
Chris@303 467 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@303 468
Chris@303 469 m_editingCommand->deletePoint(m_editingPoint);
Chris@303 470 m_editingPoint.frame = frame;
Chris@303 471 m_editingCommand->addPoint(m_editingPoint);
Chris@303 472 }
Chris@303 473
Chris@303 474 void
Chris@303 475 ImageLayer::drawEnd(View *v, QMouseEvent *)
Chris@303 476 {
Chris@303 477 // std::cerr << "ImageLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@303 478 if (!m_model || !m_editing) return;
Chris@303 479
Chris@303 480 bool ok = false;
Chris@303 481
Chris@303 482 ImageDialog dialog(tr("Select image"), "", tr("<no label>"));
Chris@303 483 if (dialog.exec() == QDialog::Accepted) {
Chris@303 484 ImageModel::ChangeImageCommand *command =
Chris@303 485 new ImageModel::ChangeImageCommand
Chris@303 486 (m_model, m_editingPoint, dialog.getImage(), dialog.getLabel());
Chris@303 487 m_editingCommand->addCommand(command);
Chris@303 488 }
Chris@303 489
Chris@303 490 m_editingCommand->finish();
Chris@303 491 m_editingCommand = 0;
Chris@303 492 m_editing = false;
Chris@303 493 }
Chris@303 494
Chris@303 495 void
Chris@303 496 ImageLayer::editStart(View *v, QMouseEvent *e)
Chris@303 497 {
Chris@303 498 // std::cerr << "ImageLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@303 499
Chris@303 500 if (!m_model) return;
Chris@303 501
Chris@303 502 ImageModel::PointList points = getLocalPoints(v, e->x(), e->y());
Chris@303 503 if (points.empty()) return;
Chris@303 504
Chris@303 505 m_editOrigin = e->pos();
Chris@303 506 m_editingPoint = *points.begin();
Chris@303 507 m_originalPoint = m_editingPoint;
Chris@303 508
Chris@303 509 if (m_editingCommand) {
Chris@303 510 m_editingCommand->finish();
Chris@303 511 m_editingCommand = 0;
Chris@303 512 }
Chris@303 513
Chris@303 514 m_editing = true;
Chris@303 515 }
Chris@303 516
Chris@303 517 void
Chris@303 518 ImageLayer::editDrag(View *v, QMouseEvent *e)
Chris@303 519 {
Chris@303 520 if (!m_model || !m_editing) return;
Chris@303 521
Chris@303 522 long frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x());
Chris@303 523 long frame = m_originalPoint.frame + frameDiff;
Chris@303 524
Chris@303 525 if (frame < 0) frame = 0;
Chris@303 526 frame = (frame / m_model->getResolution()) * m_model->getResolution();
Chris@303 527
Chris@303 528 if (!m_editingCommand) {
Chris@303 529 m_editingCommand = new ImageModel::EditCommand(m_model, tr("Move Image"));
Chris@303 530 }
Chris@303 531
Chris@303 532 m_editingCommand->deletePoint(m_editingPoint);
Chris@303 533 m_editingPoint.frame = frame;
Chris@303 534 m_editingCommand->addPoint(m_editingPoint);
Chris@303 535 }
Chris@303 536
Chris@303 537 void
Chris@303 538 ImageLayer::editEnd(View *, QMouseEvent *)
Chris@303 539 {
Chris@303 540 // std::cerr << "ImageLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@303 541 if (!m_model || !m_editing) return;
Chris@303 542
Chris@303 543 if (m_editingCommand) {
Chris@303 544 m_editingCommand->finish();
Chris@303 545 }
Chris@303 546
Chris@303 547 m_editingCommand = 0;
Chris@303 548 m_editing = false;
Chris@303 549 }
Chris@303 550
Chris@303 551 bool
Chris@303 552 ImageLayer::editOpen(View *v, QMouseEvent *e)
Chris@303 553 {
Chris@303 554 if (!m_model) return false;
Chris@303 555
Chris@303 556 ImageModel::PointList points = getLocalPoints(v, e->x(), e->y());
Chris@303 557 if (points.empty()) return false;
Chris@303 558
Chris@303 559 QString image = points.begin()->image;
Chris@303 560 QString label = points.begin()->label;
Chris@303 561
Chris@303 562 ImageDialog dialog(tr("Select image"),
Chris@303 563 image,
Chris@303 564 label);
Chris@303 565
Chris@303 566 if (dialog.exec() == QDialog::Accepted) {
Chris@303 567 ImageModel::ChangeImageCommand *command =
Chris@303 568 new ImageModel::ChangeImageCommand
Chris@303 569 (m_model, *points.begin(), dialog.getImage(), dialog.getLabel());
Chris@303 570 CommandHistory::getInstance()->addCommand(command);
Chris@303 571 }
Chris@303 572
Chris@303 573 return true;
Chris@303 574 }
Chris@303 575
Chris@303 576 void
Chris@303 577 ImageLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@303 578 {
Chris@303 579 if (!m_model) return;
Chris@303 580
Chris@303 581 ImageModel::EditCommand *command =
Chris@303 582 new ImageModel::EditCommand(m_model, tr("Drag Selection"));
Chris@303 583
Chris@303 584 ImageModel::PointList points =
Chris@303 585 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@303 586
Chris@303 587 for (ImageModel::PointList::iterator i = points.begin();
Chris@303 588 i != points.end(); ++i) {
Chris@303 589
Chris@303 590 if (s.contains(i->frame)) {
Chris@303 591 ImageModel::Point newPoint(*i);
Chris@303 592 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@303 593 command->deletePoint(*i);
Chris@303 594 command->addPoint(newPoint);
Chris@303 595 }
Chris@303 596 }
Chris@303 597
Chris@303 598 command->finish();
Chris@303 599 }
Chris@303 600
Chris@303 601 void
Chris@303 602 ImageLayer::resizeSelection(Selection s, Selection newSize)
Chris@303 603 {
Chris@303 604 if (!m_model) return;
Chris@303 605
Chris@303 606 ImageModel::EditCommand *command =
Chris@303 607 new ImageModel::EditCommand(m_model, tr("Resize Selection"));
Chris@303 608
Chris@303 609 ImageModel::PointList points =
Chris@303 610 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@303 611
Chris@303 612 double ratio =
Chris@303 613 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@303 614 double(s.getEndFrame() - s.getStartFrame());
Chris@303 615
Chris@303 616 for (ImageModel::PointList::iterator i = points.begin();
Chris@303 617 i != points.end(); ++i) {
Chris@303 618
Chris@303 619 if (s.contains(i->frame)) {
Chris@303 620
Chris@303 621 double target = i->frame;
Chris@303 622 target = newSize.getStartFrame() +
Chris@303 623 double(target - s.getStartFrame()) * ratio;
Chris@303 624
Chris@303 625 ImageModel::Point newPoint(*i);
Chris@303 626 newPoint.frame = lrint(target);
Chris@303 627 command->deletePoint(*i);
Chris@303 628 command->addPoint(newPoint);
Chris@303 629 }
Chris@303 630 }
Chris@303 631
Chris@303 632 command->finish();
Chris@303 633 }
Chris@303 634
Chris@303 635 void
Chris@303 636 ImageLayer::deleteSelection(Selection s)
Chris@303 637 {
Chris@303 638 if (!m_model) return;
Chris@303 639
Chris@303 640 ImageModel::EditCommand *command =
Chris@303 641 new ImageModel::EditCommand(m_model, tr("Delete Selection"));
Chris@303 642
Chris@303 643 ImageModel::PointList points =
Chris@303 644 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@303 645
Chris@303 646 for (ImageModel::PointList::iterator i = points.begin();
Chris@303 647 i != points.end(); ++i) {
Chris@303 648 if (s.contains(i->frame)) command->deletePoint(*i);
Chris@303 649 }
Chris@303 650
Chris@303 651 command->finish();
Chris@303 652 }
Chris@303 653
Chris@303 654 void
Chris@303 655 ImageLayer::copy(Selection s, Clipboard &to)
Chris@303 656 {
Chris@303 657 if (!m_model) return;
Chris@303 658
Chris@303 659 ImageModel::PointList points =
Chris@303 660 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@303 661
Chris@303 662 for (ImageModel::PointList::iterator i = points.begin();
Chris@303 663 i != points.end(); ++i) {
Chris@303 664 if (s.contains(i->frame)) {
Chris@303 665 //!!! inadequate
Chris@303 666 Clipboard::Point point(i->frame, i->label);
Chris@303 667 to.addPoint(point);
Chris@303 668 }
Chris@303 669 }
Chris@303 670 }
Chris@303 671
Chris@303 672 bool
Chris@303 673 ImageLayer::paste(const Clipboard &from, int frameOffset, bool /* interactive */)
Chris@303 674 {
Chris@303 675 if (!m_model) return false;
Chris@303 676
Chris@303 677 const Clipboard::PointList &points = from.getPoints();
Chris@303 678
Chris@303 679 ImageModel::EditCommand *command =
Chris@303 680 new ImageModel::EditCommand(m_model, tr("Paste"));
Chris@303 681
Chris@303 682 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@303 683 i != points.end(); ++i) {
Chris@303 684
Chris@303 685 if (!i->haveFrame()) continue;
Chris@303 686 size_t frame = 0;
Chris@303 687 if (frameOffset > 0 || -frameOffset < i->getFrame()) {
Chris@303 688 frame = i->getFrame() + frameOffset;
Chris@303 689 }
Chris@303 690 ImageModel::Point newPoint(frame);
Chris@303 691
Chris@303 692 //!!! inadequate
Chris@303 693
Chris@303 694 if (i->haveLabel()) {
Chris@303 695 newPoint.label = i->getLabel();
Chris@303 696 } else if (i->haveValue()) {
Chris@303 697 newPoint.label = QString("%1").arg(i->getValue());
Chris@303 698 } else {
Chris@303 699 newPoint.label = tr("New Point");
Chris@303 700 }
Chris@303 701
Chris@303 702 command->addPoint(newPoint);
Chris@303 703 }
Chris@303 704
Chris@303 705 command->finish();
Chris@303 706 return true;
Chris@303 707 }
Chris@303 708
Chris@303 709 QString
Chris@303 710 ImageLayer::toXmlString(QString indent, QString extraAttributes) const
Chris@303 711 {
Chris@303 712 return Layer::toXmlString(indent, extraAttributes);
Chris@303 713 }
Chris@303 714
Chris@303 715 void
Chris@303 716 ImageLayer::setProperties(const QXmlAttributes &attributes)
Chris@303 717 {
Chris@303 718 }
Chris@303 719