18 #include "data/model/Model.h" 19 #include "base/RealTime.h" 20 #include "base/Profiler.h" 23 #include "data/model/ImageModel.h" 24 #include "data/fileio/FileSource.h" 30 #include <QMouseEvent> 31 #include <QInputDialog> 32 #include <QMutexLocker> 33 #include <QTextStream> 34 #include <QMessageBox> 49 m_originalPoint(0,
"",
""),
50 m_editingPoint(0,
"",
""),
77 return Layer::getProperties();
89 return Layer::getPropertyType(name);
94 int *min,
int *max,
int *deflt)
const 96 return Layer::getPropertyRangeAndValue(name, min, max, deflt);
103 return Layer::getPropertyValueLabel(name, value);
109 Layer::setProperty(name, value);
125 ImageModel::PointList
128 if (!
m_model)
return ImageModel::PointList();
131 const ImageModel::PointList &points(
m_model->getPoints());
133 ImageModel::PointList rv;
135 for (ImageModel::PointList::const_iterator i = points.begin();
136 i != points.end(); ) {
138 const ImageModel::Point &p(*i);
143 if (i != points.end()) {
157 width =
m_scaled[v][p.image].width();
161 if (x >= px && x < px + width) {
180 if (points.empty()) {
182 return tr(
"In progress");
219 resolution =
m_model->getResolution();
220 ImageModel::PointList points;
225 if (points.empty())
return false;
226 frame = points.begin()->frame;
230 points =
m_model->getPoints(frame, frame);
234 for (ImageModel::PointList::const_iterator i = points.begin();
235 i != points.end(); ++i) {
239 if (i->frame > frame) {
247 if (i->frame <= frame) {
256 ImageModel::PointList::const_iterator j = i;
259 if (j == points.end()) {
265 }
else if (j->frame >= frame) {
267 if (j->frame - frame < frame - i->frame) {
287 int sampleRate =
m_model->getSampleRate();
288 if (!sampleRate)
return;
293 int x0 = 0, x1 = v->width();
298 ImageModel::PointList points(
m_model->getPoints(frame0, frame1));
299 if (points.empty())
return;
302 paint.setClipRect(rect.x(), 0, rect.width(), v->height());
311 brushColour.getHsv(&h, &s, &val);
312 brushColour.setHsv(h, s, 255, 240);
314 paint.setPen(penColour);
315 paint.setBrush(brushColour);
316 paint.setRenderHint(QPainter::Antialiasing,
true);
318 for (ImageModel::PointList::const_iterator i = points.begin();
319 i != points.end(); ++i) {
321 const ImageModel::Point &p(*i);
326 ImageModel::PointList::const_iterator j = i;
328 if (j != points.end()) {
330 if (jx < nx) nx = jx;
336 paint.setRenderHint(QPainter::Antialiasing,
false);
344 QString label = p.label;
345 QString imageName = p.image;
348 QString additionalText;
352 image = QImage(
":icons/emptypage.png");
353 imageSize = image.size();
354 additionalText = imageName;
358 int bottomMargin = 10;
361 if (v->height() < 100) {
366 int maxBoxHeight = v->height() - topMargin - bottomMargin;
368 int availableWidth = nx - x - 3;
369 if (availableWidth < 20) availableWidth = 20;
375 int likelyHeight = v->height() / 4;
378 ((maxBoxHeight - likelyHeight) * imageSize.width())
379 / imageSize.height();
381 if (likelyWidth > imageSize.width()) {
382 likelyWidth = imageSize.width();
385 if (likelyWidth > availableWidth) {
386 likelyWidth = availableWidth;
389 int singleWidth =
paint.fontMetrics().width(label);
390 if (singleWidth < availableWidth && singleWidth < likelyWidth * 2) {
391 likelyWidth = singleWidth + 4;
394 labelRect =
paint.fontMetrics().boundingRect
395 (QRect(0, 0, likelyWidth, likelyHeight),
396 Qt::AlignCenter | Qt::TextWordWrap, label);
398 labelRect.setWidth(labelRect.width() + 6);
401 if (image.isNull()) {
403 QSize(availableWidth,
404 maxBoxHeight - labelRect.height()));
407 int boxWidth = image.width();
408 if (boxWidth < labelRect.width()) {
409 boxWidth = labelRect.width();
412 int boxHeight = image.height();
414 boxHeight += labelRect.height() + spacing;
417 int division = image.height();
419 if (additionalText !=
"") {
423 QFont font(
paint.font());
424 font.setItalic(
true);
427 int tw =
paint.fontMetrics().width(additionalText);
428 if (tw > availableWidth) {
434 boxHeight +=
paint.fontMetrics().height();
435 division +=
paint.fontMetrics().height();
438 bottomMargin = v->height() - topMargin - boxHeight;
439 if (bottomMargin > topMargin + v->height()/7) {
440 topMargin += v->height()/8;
441 bottomMargin -= v->height()/8;
444 paint.drawRect(x - 1,
451 imageY = topMargin + labelRect.height() + spacing;
456 paint.drawImage(x + (boxWidth - image.width())/2,
460 if (additionalText !=
"") {
462 imageY + image.height() +
paint.fontMetrics().ascent(),
469 topMargin + labelRect.height() + spacing,
471 topMargin + labelRect.height() + spacing);
473 paint.drawText(QRect(x,
477 Qt::AlignCenter | Qt::TextWordWrap,
490 for (ImageMap::iterator i =
m_scaled[v].begin();
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()))) {
543 }
else if (
m_images[name].width() <= maxSize.width() &&
544 m_images[name].height() <= maxSize.height()) {
550 Qt::SmoothTransformation);
562 SVDEBUG <<
"ImageLayer::drawStart: no model" << endl;
567 if (frame < 0) frame = 0;
568 frame = frame /
m_model->getResolution() *
m_model->getResolution();
588 if (frame < 0) frame = 0;
589 frame = frame /
m_model->getResolution() *
m_model->getResolution();
604 if (dialog.exec() == QDialog::Accepted) {
608 ImageModel::ChangeImageCommand *command =
609 new ImageModel::ChangeImageCommand
625 if (image.isNull()) {
626 cerr <<
"Failed to open image from url \"" << url <<
"\" (local filename \"" <<
getLocalFilename(url) <<
"\"" << endl;
632 ImageModel::Point point(frame, url,
"");
633 ImageModel::EditCommand *command =
634 new ImageModel::EditCommand(
m_model,
"Add Image");
635 command->addPoint(point);
648 if (points.empty())
return;
670 if (frame < 0) frame = 0;
671 frame = (frame /
m_model->getResolution()) *
m_model->getResolution();
702 if (points.empty())
return false;
704 QString image = points.begin()->image;
705 QString label = points.begin()->label;
711 if (dialog.exec() == QDialog::Accepted) {
715 ImageModel::ChangeImageCommand *command =
716 new ImageModel::ChangeImageCommand
730 ImageModel::EditCommand *command =
731 new ImageModel::EditCommand(
m_model, tr(
"Drag Selection"));
733 ImageModel::PointList points =
734 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
736 for (ImageModel::PointList::iterator i = points.begin();
737 i != points.end(); ++i) {
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);
755 ImageModel::EditCommand *command =
756 new ImageModel::EditCommand(
m_model, tr(
"Resize Selection"));
758 ImageModel::PointList points =
759 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
762 double(newSize.getEndFrame() - newSize.getStartFrame()) /
763 double(s.getEndFrame() - s.getStartFrame());
765 for (ImageModel::PointList::iterator i = points.begin();
766 i != points.end(); ++i) {
768 if (s.contains(i->frame)) {
770 double target = i->frame;
771 target = newSize.getStartFrame() +
772 double(target - s.getStartFrame()) * ratio;
774 ImageModel::Point newPoint(*i);
775 newPoint.frame = lrint(target);
776 command->deletePoint(*i);
777 command->addPoint(newPoint);
789 ImageModel::EditCommand *command =
790 new ImageModel::EditCommand(
m_model, tr(
"Delete Selection"));
792 ImageModel::PointList points =
793 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
795 for (ImageModel::PointList::iterator i = points.begin();
796 i != points.end(); ++i) {
797 if (s.contains(i->frame)) command->deletePoint(*i);
808 ImageModel::PointList points =
809 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
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);
826 const Clipboard::PointList &points = from.getPoints();
828 bool realign =
false;
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,
838 if (button == QMessageBox::Cancel) {
842 if (button == QMessageBox::Yes) {
847 ImageModel::EditCommand *command =
848 new ImageModel::EditCommand(
m_model, tr(
"Paste"));
850 for (Clipboard::PointList::const_iterator i = points.begin();
851 i != points.end(); ++i) {
853 if (!i->haveFrame())
continue;
859 frame = i->getFrame();
863 if (i->haveReferenceFrame()) {
864 frame = i->getReferenceFrame();
867 frame = i->getFrame();
871 ImageModel::Point newPoint(frame);
875 if (i->haveLabel()) {
876 newPoint.label = i->getLabel();
877 }
else if (i->haveValue()) {
878 newPoint.label = QString(
"%1").arg(i->getValue());
880 newPoint.label = tr(
"New Point");
883 command->addPoint(newPoint);
905 SVDEBUG <<
"ImageLayer::checkAddSource(" << img <<
"): yes, trying..." << endl;
912 FileSource *rf =
new FileSource(img, &dialog);
914 cerr <<
"ok, adding it (local filename = " << rf->getLocalFilename() <<
")" << endl;
925 const ImageModel::PointList &points(
m_model->getPoints());
927 for (ImageModel::PointList::const_iterator i = points.begin();
928 i != points.end(); ++i) {
939 FileSource *rf = dynamic_cast<FileSource *>(sender());
943 for (FileSourceMap::const_iterator i =
m_fileSources.begin();
945 if (i->second == rf) {
951 if (img ==
"")
return;
955 for (ViewImageMap::iterator i =
m_scaled.begin(); i !=
m_scaled.end(); ++i) {
956 i->second.erase(img);
963 QString indent, QString extraAttributes)
const int getFrameForX(int x) const
Return the closest frame to the given pixel x-coordinate.
virtual void editDrag(View *v, QMouseEvent *)
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...
The base class for visual representations of the data found in a Model.
virtual bool snapToFeatureFrame(View *, int &, int &resolution, SnapType) const
Adjust the given frame to snap to the nearest feature, if possible.
void connectSignals(const Model *)
QImage getImage(View *v, QString name, QSize maxSize) const
void drawImage(View *v, QPainter &paint, const ImageModel::Point &p, int x, int nx) const
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...
virtual QColor getForeground() const
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.
virtual void drawDrag(View *v, QMouseEvent *)
virtual void deleteSelection(Selection s)
virtual void resizeSelection(Selection s, Selection newSize)
FileSourceMap m_fileSources
virtual void setProperty(const PropertyName &, int value)
virtual void copy(View *v, Selection s, Clipboard &to)
void addCommand(Command *command)
Add a command to the command history.
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,...
ImageModel::Point m_editingPoint
virtual void editEnd(View *v, QMouseEvent *)
std::map< QString, QImage > ImageMap
!! how to reap no-longer-used images?
virtual void drawEnd(View *v, QMouseEvent *)
virtual QString getPropertyLabel(const PropertyName &) const
virtual bool snapToFeatureFrame(View *v, int &frame, int &resolution, SnapType snap) const
!! too much overlap with TimeValueLayer/TimeInstantLayer/TextLayer
bool getImageOriginalSize(QString name, QSize &size) const
!! how to reap no-longer-used images?
virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const
virtual void drawStart(View *v, QMouseEvent *)
ImageModel::Point m_originalPoint
virtual bool addImage(int frame, QString url)
ImageModel::EditCommand * m_editingCommand
void checkAddSource(QString img) const
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.
static CommandHistory * getInstance()
bool clipboardHasDifferentAlignment(View *v, const Clipboard &clip) const
QString getLocalFilename(QString img) const
void setProperties(const QXmlAttributes &attributes)
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
virtual QString getFeatureDescription(View *v, QPoint &) const
void setModel(ImageModel *model)
virtual PropertyList getProperties() const
virtual int alignFromReference(View *v, int frame) const
virtual int alignToReference(View *v, int frame) const
View is the base class of widgets that display one or more overlaid views of data against a horizonta...
void finish(ImageModel::EditCommand *command)
virtual bool editOpen(View *, QMouseEvent *)
Open an editor on the item under the mouse (e.g.
virtual void moveSelection(Selection s, int newStartFrame)
virtual void editStart(View *v, QMouseEvent *)
static QMutex m_imageMapMutex
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...
virtual QString getPropertyValueLabel(const PropertyName &, int value) const
ImageModel::PointList getLocalPoints(View *v, int x, int y) const
virtual QColor getBackground() const
int getXForFrame(int frame) const
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative).
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.
virtual PropertyType getPropertyType(const PropertyName &) const