summaryrefslogtreecommitdiffstats
path: root/profile-widget
diff options
context:
space:
mode:
Diffstat (limited to 'profile-widget')
-rw-r--r--profile-widget/profilewidget2.cpp118
-rw-r--r--profile-widget/profilewidget2.h18
2 files changed, 87 insertions, 49 deletions
diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp
index 1e1599937..9af364f20 100644
--- a/profile-widget/profilewidget2.cpp
+++ b/profile-widget/profilewidget2.cpp
@@ -24,6 +24,7 @@
#include "desktop-widgets/mainwindow.h"
#include "core/qthelper.h"
#include "core/gettextfromc.h"
+#include "core/imagedownloader.h"
#endif
#include <libdivecomputer/parser.h>
@@ -155,9 +156,9 @@ ProfileWidget2::ProfileWidget2(QWidget *parent) : QGraphicsView(parent),
addActionShortcut(Qt::Key_Left, &ProfileWidget2::keyLeftAction);
addActionShortcut(Qt::Key_Right, &ProfileWidget2::keyRightAction);
- connect(DivePictureModel::instance(), &DivePictureModel::dataChanged, this, &ProfileWidget2::updatePictures);
+ connect(Thumbnailer::instance(), &Thumbnailer::thumbnailChanged, this, &ProfileWidget2::updateThumbnail, Qt::QueuedConnection);
connect(DivePictureModel::instance(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(plotPictures()));
- connect(DivePictureModel::instance(), &DivePictureModel::rowsRemoved, this, &ProfileWidget2::removePictures);
+ connect(DivePictureModel::instance(), &DivePictureModel::picturesRemoved, this, &ProfileWidget2::removePictures);
connect(DivePictureModel::instance(), &DivePictureModel::modelReset, this, &ProfileWidget2::plotPictures);
#endif // SUBSURFACE_MOBILE
@@ -2063,49 +2064,70 @@ void ProfileWidget2::clearPictures()
pictures.clear();
}
-void ProfileWidget2::updatePictures(const QModelIndex &from, const QModelIndex &to)
+// This function is called asynchronously by the thumbnailer if a thumbnail
+// was fetched from disk or freshly calculated.
+void ProfileWidget2::updateThumbnail(QString filename, QImage thumbnail)
{
- DivePictureModel *m = DivePictureModel::instance();
- for (int picNr = from.row(); picNr <= to.row(); ++picNr) {
- int picItemNr = picNr - m->rowDDStart;
- if (picItemNr < 0 || (size_t)picItemNr >= pictures.size())
- return;
- if (!pictures[picItemNr])
- return;
+ // Find the picture with the given filename
+ auto it = std::find_if(pictures.begin(), pictures.end(), [&filename](const PictureEntry &e)
+ { return e.filename == filename; });
- pictures[picItemNr]->setPixmap(m->index(picNr, 0).data(Qt::UserRole).value<QPixmap>());
+ // If we didn't find a picture, it does either not belong to the current dive,
+ // or its timestamp is outside of the profile.
+ if (it != pictures.end()) {
+ // Replace the pixmap of the thumbnail with the newly calculated one.
+ int size = Thumbnailer::defaultThumbnailSize();
+ it->thumbnail->setPixmap(QPixmap::fromImage(thumbnail.scaled(size, size, Qt::KeepAspectRatio)));
}
}
+ProfileWidget2::PictureEntry::PictureEntry (offset_t offsetIn, const QString &filenameIn) : offset(offsetIn),
+ filename(filenameIn),
+ thumbnail(new DivePictureItem)
+{
+}
+
+// Define a default sort order for picture-entries: sort lexicographically by timestamp and filename.
+bool ProfileWidget2::PictureEntry::operator< (const PictureEntry &e) const
+{
+ // Use std::tie() for lexicographical sorting.
+ return std::tie(offset.seconds, filename) < std::tie(e.offset.seconds, e.filename);
+}
+
+// This function resets the picture thumbnails of the current dive.
void ProfileWidget2::plotPictures()
{
- DivePictureModel *m = DivePictureModel::instance();
- pictures.resize(m->rowDDEnd - m->rowDDStart);
+ pictures.clear();
+ if (currentState == ADD || currentState == PLAN)
+ return;
+ // 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.
+ 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));
+ }
+ if (pictures.empty())
+ return;
+ // Sort pictures by timestamp (and filename if equal timestamps).
+ // 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 finaly calculate their positions.
double x, y, lastX = -1.0, lastY = -1.0;
- for (int i = m->rowDDStart; i < m->rowDDEnd; i++) {
- int picItemNr = i - m->rowDDStart;
- int offsetSeconds = m->index(i, 1).data(Qt::UserRole).value<int>();
- // it's a correct picture, but doesn't have a timestamp: only show on the widget near the
- // information area. A null pointer in the pictures array indicates that this picture is not
- // shown.
- if (!offsetSeconds) {
- pictures[picItemNr].reset();
- continue;
- }
- DivePictureItem *item = pictures[picItemNr].get();
- if (!item) {
- item = new DivePictureItem;
- pictures[picItemNr].reset(item);
- scene()->addItem(item);
- }
- item->setVisible(prefs.show_pictures_in_profile);
- item->setPixmap(m->index(i, 0).data(Qt::UserRole).value<QPixmap>());
- item->setFileUrl(m->index(i, 1).data().toString());
+ 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);
// let's put the picture at the correct time, but at a fixed "depth" on the profile
// not sure this is ideal, but it seems to look right.
- x = timeAxis->posAtValue(offsetSeconds);
- if (i == 0)
+ x = timeAxis->posAtValue(e.offset.seconds);
+ if (lastX < 0.0)
y = 10;
else if (fabs(x - lastX) < 3 && lastY <= (10 + 14 * 3))
y = lastY + 3;
@@ -2113,20 +2135,26 @@ void ProfileWidget2::plotPictures()
y = 10;
lastX = x;
lastY = y;
- item->setPos(x, y);
+ e.thumbnail->setPos(x, y);
}
}
-void ProfileWidget2::removePictures(const QModelIndex &, int first, int last)
-{
- DivePictureModel *m = DivePictureModel::instance();
- first = std::max(0, first - m->rowDDStart);
- // Note that last points *to* the last item and not *past* the last item,
- // therefore we add 1 to achieve conventional C++ semantics.
- last = std::min((int)pictures.size(), last + 1 - m->rowDDStart);
- if (first >= (int)pictures.size() || last <= first)
- return;
- pictures.erase(pictures.begin() + first, pictures.begin() + last);
+// Remove the pictures with the given filenames from the profile plot.
+// TODO: This does not check for the fact that the same image may be attributed
+// to different dives! Deleting the picture from one dive may therefore remove
+// it from the profile of a different dive.
+void ProfileWidget2::removePictures(const QVector<QString> &fileUrls)
+{
+ // To remove the pictures, we use the std::remove_if() algorithm.
+ // std::remove_if() does not actually delete the elements, but moves
+ // them to the end of the given range. It returns an iterator to the
+ // end of the new range of non-deleted elements. A subsequent call to
+ // std::erase on the range of deleted elements then ultimately shrinks the vector.
+ // (c.f. erase-remove idiom: https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom)
+ auto it = std::remove_if(pictures.begin(), pictures.end(), [&fileUrls](const PictureEntry &e)
+ // Check whether filename of entry is in list of provided filenames
+ { return std::find(fileUrls.begin(), fileUrls.end(), e.filename) != fileUrls.end(); });
+ pictures.erase(it, pictures.end());
}
#endif
diff --git a/profile-widget/profilewidget2.h b/profile-widget/profilewidget2.h
index 7c01a8139..c7db09a92 100644
--- a/profile-widget/profilewidget2.h
+++ b/profile-widget/profilewidget2.h
@@ -20,6 +20,7 @@
#include "profile-widget/diveprofileitem.h"
#include "core/display.h"
#include "core/color.h"
+#include "core/units.h"
class RulerItem2;
struct dive;
@@ -110,7 +111,7 @@ slots: // Necessary to call from QAction's signals.
void replot(dive *d = 0);
#ifndef SUBSURFACE_MOBILE
void plotPictures();
- void removePictures(const QModelIndex &, int first, int last);
+ void removePictures(const QVector<QString> &fileUrls);
void setPlanState();
void setAddState();
void changeGas();
@@ -126,7 +127,7 @@ slots: // Necessary to call from QAction's signals.
void deleteCurrentDC();
void pointInserted(const QModelIndex &parent, int start, int end);
void pointsRemoved(const QModelIndex &, int start, int end);
- void updatePictures(const QModelIndex &from, const QModelIndex &to);
+ void updateThumbnail(QString filename, QImage thumbnail);
/* this is called for every move on the handlers. maybe we can speed up this a bit? */
void recreatePlannedDive();
@@ -228,8 +229,17 @@ private:
//specifics for ADD and PLAN
#ifndef SUBSURFACE_MOBILE
- // Use std::vector<> and std::unique_ptr<>, because QVector<QScopedPointer<...>> is unsupported.
- std::vector<std::unique_ptr<DivePictureItem>> pictures;
+ // The list of pictures in this plot. The pictures are sorted by offset in seconds.
+ // For the same offset, sort by filename.
+ // Pictures that are outside of the dive time are not shown.
+ struct PictureEntry {
+ offset_t offset;
+ QString filename;
+ std::unique_ptr<DivePictureItem> thumbnail;
+ PictureEntry (offset_t offsetIn, const QString &filenameIn);
+ bool operator< (const PictureEntry &e) const;
+ };
+ std::vector<PictureEntry> pictures;
QList<DiveHandler *> handles;
void repositionDiveHandlers();