summaryrefslogtreecommitdiffstats
path: root/profile-widget/profilewidget2.cpp
diff options
context:
space:
mode:
authorGravatar Berthold Stoeger <bstoeger@mail.tuwien.ac.at>2018-06-30 21:32:14 +0200
committerGravatar Dirk Hohndel <dirk@hohndel.org>2018-07-13 17:07:42 -0700
commit9efb56e2d43161d952efb444d1f13d87bfdd45b5 (patch)
tree6acecf55e52cea217b79d8632ab4c760ddc50070 /profile-widget/profilewidget2.cpp
parent0aaa1bf3862901933ed68c15a38fed9dc4c621a4 (diff)
downloadsubsurface-9efb56e2d43161d952efb444d1f13d87bfdd45b5.tar.gz
Dive pictures: don't update all images on drag&drop to profile
Gracefully handle drag & drop to the profile, which changes the offset of the pictures. To do this, keep the pictures in the DivePictureModel and the ProfileWidget2 sorted by offset and re-arrange if needed to keep the list sorted. This needs some code reshuffling. Introduce a helper-function that moves ranges in arrays. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'profile-widget/profilewidget2.cpp')
-rw-r--r--profile-widget/profilewidget2.cpp132
1 files changed, 103 insertions, 29 deletions
diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp
index 4e0e0527e..6c4f0f2cc 100644
--- a/profile-widget/profilewidget2.cpp
+++ b/profile-widget/profilewidget2.cpp
@@ -2081,10 +2081,17 @@ void ProfileWidget2::updateThumbnail(QString filename, QImage thumbnail)
}
}
-ProfileWidget2::PictureEntry::PictureEntry (offset_t offsetIn, const QString &filenameIn) : offset(offsetIn),
+// Create a PictureEntry object and add its thumbnail to the scene if profile pictures are shown.
+ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const QString &filenameIn, QGraphicsScene *scene) : offset(offsetIn),
filename(filenameIn),
thumbnail(new DivePictureItem)
{
+ int size = Thumbnailer::defaultThumbnailSize();
+ scene->addItem(thumbnail.get());
+ thumbnail->setVisible(prefs.show_pictures_in_profile);
+ QImage img = Thumbnailer::instance()->fetchThumbnail(filename).scaled(size, size, Qt::KeepAspectRatio);
+ thumbnail->setPixmap(QPixmap::fromImage(img));
+ thumbnail->setFileUrl(filename);
}
// Define a default sort order for picture-entries: sort lexicographically by timestamp and filename.
@@ -2094,9 +2101,13 @@ bool ProfileWidget2::PictureEntry::operator< (const PictureEntry &e) const
return std::tie(offset.seconds, filename) < std::tie(e.offset.seconds, e.filename);
}
+// Calculate the y-coordinates of the thumbnails, which are supposed to be sorted by x-coordinate.
+// This will also change the order in which the thumbnails are painted, to avoid weird effects,
+// when items are added later to the scene. This is simply done by increasing the Z-value.
void ProfileWidget2::calculatePictureYPositions()
{
double lastX = -1.0, lastY;
+ double z = 0.0;
for (PictureEntry &e: pictures) {
if (!e.thumbnail)
continue;
@@ -2111,9 +2122,19 @@ void ProfileWidget2::calculatePictureYPositions()
lastX = x;
lastY = y;
e.thumbnail->setY(y);
+ e.thumbnail->setZValue(z);
+ z += 1.0;
}
}
+void ProfileWidget2::updateThumbnailXPos(PictureEntry &e)
+{
+ // Here, we only set the x-coordinate of the picture. The y-coordinate
+ // will be set later in calculatePictureYPositions().
+ double x = timeAxis->posAtValue(e.offset.seconds);
+ e.thumbnail->setX(x);
+}
+
// This function resets the picture thumbnails of the current dive.
void ProfileWidget2::plotPictures()
{
@@ -2124,9 +2145,10 @@ void ProfileWidget2::plotPictures()
// Fetch all pictures of the current dive, but consider only those that are within the dive time.
// For each picture, create a PictureEntry object in the pictures-vector.
// emplace_back() constructs an object at the end of the vector. The parameters are passed directly to the constructor.
+ // Note that FOR_EACH_PICTURE handles current_dive being null gracefully.
FOR_EACH_PICTURE(current_dive) {
if (picture->offset.seconds > 0 && picture->offset.seconds <= current_dive->duration.seconds)
- pictures.emplace_back(picture->offset, QString(picture->filename));
+ pictures.emplace_back(picture->offset, QString(picture->filename), scene());
}
if (pictures.empty())
return;
@@ -2134,21 +2156,9 @@ void ProfileWidget2::plotPictures()
// This will allow for proper location of the pictures on the profile plot.
std::sort(pictures.begin(), pictures.end());
- // Add the DivePictureItems to the scene, set their pixmaps and filenames
- // and finally calculate their positions.
- int size = Thumbnailer::defaultThumbnailSize();
- for (PictureEntry &e: pictures) {
- scene()->addItem(e.thumbnail.get());
- e.thumbnail->setVisible(prefs.show_pictures_in_profile);
- QImage thumbnail = Thumbnailer::instance()->fetchThumbnail(e.filename).scaled(size, size, Qt::KeepAspectRatio);
- e.thumbnail->setPixmap(QPixmap::fromImage(thumbnail));
- e.thumbnail->setFileUrl(e.filename);
-
- // Here, we only set the x-coordinate of the picture. The y-coordinate
- // will be set later in calculatePictureYPositions().
- double x = timeAxis->posAtValue(e.offset.seconds);
- e.thumbnail->setX(x);
- }
+ // Calculate thumbnail positions. First the x-coordinates and and then the y-coordinates.
+ for (PictureEntry &e: pictures)
+ updateThumbnailXPos(e);
calculatePictureYPositions();
}
@@ -2180,23 +2190,87 @@ void ProfileWidget2::dropEvent(QDropEvent *event)
QDataStream dataStream(&itemData, QIODevice::ReadOnly);
QString filename;
- QPoint offset;
- dataStream >> filename >> offset;
+ QPoint pos;
+ dataStream >> filename >> pos;
+#ifndef SUBSURFACE_MOBILE
+ // Calculate time in dive where picture was dropped and whether the new position is during the dive.
QPointF mappedPos = mapToScene(event->pos());
+ offset_t offset { (int32_t)lrint(timeAxis->valueAt(mappedPos)) };
+ bool duringDive = current_dive && offset.seconds > 0 && offset.seconds < current_dive->duration.seconds;
+
+ // Flag which states whether the drag&dropped picture actually belongs to this dive.
+ // If this is not the case, the calculated offset makes no sense whatsoever and we must ignore the event.
+ bool belongsToDive = true;
+
+ // A picture was drag&dropped onto the profile: We have four cases to consider:
+ // 1a) The image was already shown on the profile and is moved to a different position on the profile.
+ // Calculate the new position and move the picture.
+ // 1b) The image was on the profile and is moved outside of the dive time.
+ // Remove the picture.
+ // 2a) The image was not on the profile, but belongs to the current dive.
+ // Add the picture to the profile if it is during the dive.
+ // 2b) The picture does not belong to the current dive.
+ // For now, do nothing. We may think about adding the picture to the dive.
+ auto oldPos = std::find_if(pictures.begin(), pictures.end(), [filename](const PictureEntry &e)
+ { return e.filename == filename; });
+ if (oldPos != pictures.end()) {
+ // Cases 1a) and 1b): picture is on profile
+ if (duringDive) {
+ // Case 1a): move to new position
+ // First, find new position. Note that we also have to compare filenames,
+ // because it is quite easy to generate equal offsets.
+ auto newPos = std::find_if(pictures.begin(), pictures.end(), [offset, &filename](const PictureEntry &e)
+ { return std::tie(e.offset.seconds, e.filename) > std::tie(offset.seconds, filename); });
+ // Set new offset
+ oldPos->offset.seconds = offset.seconds;
+ updateThumbnailXPos(*oldPos);
+
+ // Move image from old to new position
+ int oldIndex = oldPos - pictures.begin();
+ int newIndex = newPos - pictures.begin();
+ moveInVector(pictures, oldIndex, oldIndex + 1, newIndex);
+ } else {
+ // Case 1b): remove picture
+ pictures.erase(oldPos);
+ }
- FOR_EACH_PICTURE(current_dive) {
- if (QString(picture->filename) == filename) {
- picture->offset.seconds = lrint(timeAxis->valueAt(mappedPos));
- mark_divelist_changed(true);
-#ifndef SUBSURFACE_MOBILE
- DivePictureModel::instance()->updateDivePictureOffset(filename, picture->offset.seconds);
- plotPictures();
-#endif
- break;
+ // In both cases the picture list changed, therefore we must recalculate the y-coordinatesA.
+ calculatePictureYPositions();
+ } else {
+ // Cases 2a) and 2b): picture not on profile. Check if it belongs to current dive.
+ // Note that FOR_EACH_PICTURE handles current_dive being null gracefully.
+ bool found = false;
+ FOR_EACH_PICTURE(current_dive) {
+ if (picture->filename == filename) {
+ found = true;
+ break;
+ }
+ }
+ if (found && duringDive) {
+ // Case 2a): add the picture at the appropriate position.
+ // The case move from outside-to-outside of the profile plot was handled by
+ // the "&& duringDive" condition in the if above.
+ // As for case 1a), we have to also consider filenames in the case of equal offsets.
+ auto newPos = std::find_if(pictures.begin(), pictures.end(), [offset, &filename](const PictureEntry &e)
+ { return std::tie(e.offset.seconds, e.filename) > std::tie(offset.seconds, filename); });
+ // emplace() constructs the element at the given position in the vector.
+ // The parameters are passed directly to the contructor.
+ // The call returns an iterator to the new element (which might differ from
+ // the old iterator, since the buffer might have been reallocated).
+ newPos = pictures.emplace(newPos, offset, filename, scene());
+ updateThumbnailXPos(*newPos);
+ calculatePictureYPositions();
+ } else if (!found) {
+ // Case 2b): Unknown picture. Ignore.
+ belongsToDive = false;
}
}
- copy_dive(current_dive, &displayed_dive);
+
+ // Only signal the drag&drop action if the picture actually belongs to the dive.
+ if (belongsToDive)
+ DivePictureModel::instance()->updateDivePictureOffset(displayed_dive.id, filename, offset.seconds);
+#endif
if (event->source() == this) {
event->setDropAction(Qt::MoveAction);