summaryrefslogtreecommitdiffstats
path: root/qt-models/divepicturemodel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qt-models/divepicturemodel.cpp')
-rw-r--r--qt-models/divepicturemodel.cpp134
1 files changed, 106 insertions, 28 deletions
diff --git a/qt-models/divepicturemodel.cpp b/qt-models/divepicturemodel.cpp
index 351396e5f..7c9fb18c2 100644
--- a/qt-models/divepicturemodel.cpp
+++ b/qt-models/divepicturemodel.cpp
@@ -1,16 +1,42 @@
// SPDX-License-Identifier: GPL-2.0
#include "qt-models/divepicturemodel.h"
+#include "core/divelist.h" // for comp_dives
#include "core/metrics.h"
-#include "core/divelist.h" // for mark_divelist_changed()
-#include "core/dive.h"
#include "core/imagedownloader.h"
#include "core/picture.h"
#include "core/qthelper.h"
#include "core/subsurface-qt/divelistnotifier.h"
+#include "commands/command.h"
#include <QFileInfo>
#include <QPainter>
+PictureEntry::PictureEntry(dive *dIn, const PictureObj &p) : d(dIn),
+ filename(p.filename),
+ offsetSeconds(p.offset.seconds),
+ length({ 0 })
+{
+}
+
+PictureEntry::PictureEntry(dive *dIn, const picture &p) : d(dIn),
+ filename(p.filename),
+ offsetSeconds(p.offset.seconds),
+ length({ 0 })
+{
+}
+
+// Note: it is crucial that this uses the same sorting as the core.
+// Therefore, we use the C strcmp functions [std::string::operator<()
+// should give the same result].
+bool PictureEntry::operator<(const PictureEntry &p2) const
+{
+ if (int cmp = comp_dives(d, p2.d))
+ return cmp < 0;
+ if (offsetSeconds != p2.offsetSeconds)
+ return offsetSeconds < p2.offsetSeconds;
+ return strcmp(filename.c_str(), p2.filename.c_str()) < 0;
+}
+
DivePictureModel *DivePictureModel::instance()
{
static DivePictureModel *self = new DivePictureModel();
@@ -23,6 +49,10 @@ DivePictureModel::DivePictureModel() : zoomLevel(0.0)
this, &DivePictureModel::updateThumbnail, Qt::QueuedConnection);
connect(&diveListNotifier, &DiveListNotifier::pictureOffsetChanged,
this, &DivePictureModel::pictureOffsetChanged);
+ connect(&diveListNotifier, &DiveListNotifier::picturesRemoved,
+ this, &DivePictureModel::picturesRemoved);
+ connect(&diveListNotifier, &DiveListNotifier::picturesAdded,
+ this, &DivePictureModel::picturesAdded);
}
void DivePictureModel::setZoomLevel(int level)
@@ -63,7 +93,7 @@ void DivePictureModel::updateDivePictures()
if (dive->selected) {
size_t first = pictures.size();
FOR_EACH_PICTURE(dive)
- pictures.push_back({ dive, picture->filename, {}, picture->offset.seconds, {.seconds = 0}});
+ pictures.push_back(PictureEntry(dive, *picture));
// Sort pictures of this dive by offset.
// Thus, the list will be sorted by (dive, offset).
@@ -111,43 +141,51 @@ QVariant DivePictureModel::data(const QModelIndex &index, int role) const
return QVariant();
}
-// Return true if we actually removed a picture
-static bool removePictureFromSelectedDive(const char *fileUrl)
+void DivePictureModel::removePictures(const QModelIndexList &indices)
{
- int i;
- struct dive *dive;
- for_each_dive (i, dive) {
- if (dive->selected && remove_picture(&dive->pictures, fileUrl)) {
- invalidate_dive_cache(dive);
- return true;
- }
+ // Collect pictures to remove by dive
+ std::vector<Command::PictureListForDeletion> pics;
+ for (const QModelIndex &idx: indices) {
+ if (!idx.isValid())
+ continue;
+ const PictureEntry &item = pictures[idx.row()];
+ // Check if we already have pictures for that dive.
+ auto it = find_if(pics.begin(), pics.end(),
+ [&item](const Command::PictureListForDeletion &list)
+ { return list.d == item.d; });
+ // If not found, add a new list
+ if (it == pics.end())
+ pics.push_back({ item.d, { item.filename }});
+ else
+ it->filenames.push_back(item.filename);
}
- return false;
+ Command::removePictures(pics);
}
-void DivePictureModel::removePictures(const QVector<QString> &fileUrlsIn)
+void DivePictureModel::picturesRemoved(dive *d, QVector<QString> filenamesIn)
{
// Transform vector of QStrings into vector of std::strings
- std::vector<std::string> fileUrls;
- fileUrls.reserve(fileUrlsIn.size());
- std::transform(fileUrlsIn.begin(), fileUrlsIn.end(), std::back_inserter(fileUrls),
+ std::vector<std::string> filenames;
+ filenames.reserve(filenamesIn.size());
+ std::transform(filenamesIn.begin(), filenamesIn.end(), std::back_inserter(filenames),
[] (const QString &s) { return s.toStdString(); });
- bool removed = false;
- for (const std::string &fileUrl: fileUrls)
- removed |= removePictureFromSelectedDive(fileUrl.c_str());
- if (!removed)
+ // Get range of pictures of the given dive.
+ // Note: we could be more efficient by either using a binary search or a two-level data structure.
+ auto from = std::find_if(pictures.begin(), pictures.end(), [d](const PictureEntry &e) { return e.d == d; });
+ auto to = std::find_if(from, pictures.end(), [d](const PictureEntry &e) { return e.d != d; });
+ if (from == pictures.end())
return;
- copy_dive(current_dive, &displayed_dive);
- mark_divelist_changed(true);
- for (size_t i = 0; i < pictures.size(); ++i) {
+ size_t fromIdx = from - pictures.begin();
+ size_t toIdx = to - pictures.begin();
+ for (size_t i = fromIdx; i < toIdx; ++i) {
// Find range [i j) of pictures to remove
- if (std::find(fileUrls.begin(), fileUrls.end(), pictures[i].filename) == fileUrls.end())
+ if (std::find(filenames.begin(), filenames.end(), pictures[i].filename) == filenames.end())
continue;
size_t j;
- for (j = i + 1; j < pictures.size(); ++j) {
- if (std::find(fileUrls.begin(), fileUrls.end(), pictures[j].filename) == fileUrls.end())
+ for (j = i + 1; j < toIdx; ++j) {
+ if (std::find(filenames.begin(), filenames.end(), pictures[j].filename) == filenames.end())
break;
}
@@ -156,8 +194,48 @@ void DivePictureModel::removePictures(const QVector<QString> &fileUrlsIn)
beginRemoveRows(QModelIndex(), i, j - 1);
pictures.erase(pictures.begin() + i, pictures.begin() + j);
endRemoveRows();
+ toIdx -= j - i;
+ }
+ copy_dive(current_dive, &displayed_dive); // TODO: Remove once displayed_dive is moved to the planner
+}
+
+// Assumes that pics is sorted!
+void DivePictureModel::picturesAdded(dive *d, QVector<PictureObj> picsIn)
+{
+ // We only display pictures of selected dives
+ if (!d->selected || picsIn.empty())
+ return;
+
+ // Convert the picture-data into our own format
+ std::vector<PictureEntry> pics;
+ pics.reserve(picsIn.size());
+ for (int i = 0; i < picsIn.size(); ++i)
+ pics.push_back(PictureEntry(d, picsIn[i]));
+
+ // Insert batch-wise to avoid too many reloads
+ pictures.reserve(pictures.size() + pics.size());
+ auto from = pics.begin();
+ int dest = 0;
+ while (from != pics.end()) {
+ // Search for the insertion index. This supposes a lexicographical sort for the [dive, offset, filename] triple.
+ // TODO: currently this works, because all undo commands that manipulate the dive list also reset the selection
+ // and thus the model is rebuilt. However, we might catch the respective signals here and not rely on being
+ // called by the tab-widgets.
+ auto dest_it = std::lower_bound(pictures.begin() + dest, pictures.end(), *from);
+ int dest = dest_it - pictures.begin();
+ auto to = dest_it == pictures.end() ? pics.end() : from + 1; // If at the end - just add the rest
+ while (to != pics.end() && *to < *dest_it)
+ ++to;
+ int batch_size = to - from;
+ beginInsertRows(QModelIndex(), dest, dest + batch_size - 1);
+ pictures.insert(pictures.begin() + dest, from, to);
+ // Get thumbnails of inserted pictures
+ for (auto it = pictures.begin() + dest; it < pictures.begin() + dest + batch_size; ++it)
+ it->image = Thumbnailer::instance()->fetchThumbnail(QString::fromStdString(it->filename), false);
+ endInsertRows();
+ from = to;
+ dest += batch_size;
}
- emit picturesRemoved(fileUrlsIn);
}
int DivePictureModel::rowCount(const QModelIndex&) const