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