mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-28 05:00:20 +00:00
f47f2773fd
On deletion of a single or multiple pictures, the whole DivePictureModel was repopulated, which was clearly visible in the UI, owing to the reconstructing of all images in the profile plot. To avoid this vexing behavior, implement proper deletion routines in DivePictureModel and ProfileWidget2. Since this needs sensible erase() semantics the QList<PictureEntry> member of DivePictureModel was replaced by a QVector. A QVector should be the default anyway, unless there are very specific reasons to use a QList (which actually is a deque, not a classical linked list). Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
200 lines
4.9 KiB
C++
200 lines
4.9 KiB
C++
// SPDX-License-Identifier: GPL-2.0
|
|
#include "qt-models/divepicturemodel.h"
|
|
#include "core/dive.h"
|
|
#include "core/metrics.h"
|
|
#include "core/divelist.h"
|
|
#include "core/imagedownloader.h"
|
|
#include "core/qthelper.h"
|
|
|
|
#include <QFileInfo>
|
|
|
|
DivePictureModel *DivePictureModel::instance()
|
|
{
|
|
static DivePictureModel *self = new DivePictureModel();
|
|
return self;
|
|
}
|
|
|
|
DivePictureModel::DivePictureModel() : rowDDStart(0),
|
|
rowDDEnd(0),
|
|
zoomLevel(0.0),
|
|
defaultSize(Thumbnailer::defaultThumbnailSize())
|
|
{
|
|
connect(Thumbnailer::instance(), &Thumbnailer::thumbnailChanged,
|
|
this, &DivePictureModel::updateThumbnail, Qt::QueuedConnection);
|
|
}
|
|
|
|
void DivePictureModel::updateDivePicturesWhenDone(QList<QFuture<void>> futures)
|
|
{
|
|
Q_FOREACH (QFuture<void> f, futures) {
|
|
f.waitForFinished();
|
|
}
|
|
updateDivePictures();
|
|
}
|
|
|
|
void DivePictureModel::setZoomLevel(int level)
|
|
{
|
|
zoomLevel = level / 10.0;
|
|
// zoomLevel is bound by [-1.0 1.0], see comment below.
|
|
if (zoomLevel < -1.0)
|
|
zoomLevel = -1.0;
|
|
if (zoomLevel > 1.0)
|
|
zoomLevel = 1.0;
|
|
updateZoom();
|
|
layoutChanged();
|
|
}
|
|
|
|
void DivePictureModel::updateZoom()
|
|
{
|
|
size = Thumbnailer::thumbnailSize(zoomLevel);
|
|
}
|
|
|
|
void DivePictureModel::updateThumbnails()
|
|
{
|
|
updateZoom();
|
|
for (PictureEntry &entry: pictures)
|
|
entry.image = Thumbnailer::instance()->fetchThumbnail(entry);
|
|
}
|
|
|
|
void DivePictureModel::updateDivePictures()
|
|
{
|
|
beginResetModel();
|
|
if (!pictures.isEmpty()) {
|
|
pictures.clear();
|
|
rowDDStart = rowDDEnd = 0;
|
|
Thumbnailer::instance()->clearWorkQueue();
|
|
}
|
|
|
|
// if the dive_table is empty, quit
|
|
if (dive_table.nr == 0)
|
|
return;
|
|
|
|
int i;
|
|
struct dive *dive;
|
|
for_each_dive (i, dive) {
|
|
if (dive->selected) {
|
|
if (dive->id == displayed_dive.id)
|
|
rowDDStart = pictures.count();
|
|
FOR_EACH_PICTURE(dive)
|
|
pictures.push_back({picture, picture->filename, {}, picture->offset.seconds});
|
|
if (dive->id == displayed_dive.id)
|
|
rowDDEnd = pictures.count();
|
|
}
|
|
}
|
|
|
|
updateThumbnails();
|
|
endResetModel();
|
|
}
|
|
|
|
int DivePictureModel::columnCount(const QModelIndex &parent) const
|
|
{
|
|
Q_UNUSED(parent);
|
|
return 2;
|
|
}
|
|
|
|
QVariant DivePictureModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
QVariant ret;
|
|
if (!index.isValid())
|
|
return ret;
|
|
|
|
const PictureEntry &entry = pictures.at(index.row());
|
|
if (index.column() == 0) {
|
|
switch (role) {
|
|
case Qt::ToolTipRole:
|
|
ret = entry.filename;
|
|
break;
|
|
case Qt::DecorationRole:
|
|
ret = entry.image.scaled(size, size, Qt::KeepAspectRatio);
|
|
break;
|
|
case Qt::UserRole: // Used by profile widget to access bigger thumbnails
|
|
ret = entry.image.scaled(defaultSize, defaultSize, Qt::KeepAspectRatio);
|
|
break;
|
|
case Qt::DisplayRole:
|
|
ret = QFileInfo(entry.filename).fileName();
|
|
break;
|
|
case Qt::DisplayPropertyRole:
|
|
ret = QFileInfo(entry.filename).filePath();
|
|
}
|
|
} else if (index.column() == 1) {
|
|
switch (role) {
|
|
case Qt::UserRole:
|
|
ret = QVariant::fromValue(entry.offsetSeconds);
|
|
break;
|
|
case Qt::DisplayRole:
|
|
ret = entry.filename;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Return true if we actually removed a picture
|
|
static bool removePictureFromSelectedDive(const char *fileUrl)
|
|
{
|
|
int i;
|
|
struct dive *dive;
|
|
for_each_dive (i, dive) {
|
|
if (dive->selected && dive_remove_picture(dive, fileUrl))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void DivePictureModel::removePictures(const QVector<QString> &fileUrls)
|
|
{
|
|
bool removed = false;
|
|
for (const QString &fileUrl: fileUrls)
|
|
removed |= removePictureFromSelectedDive(qPrintable(fileUrl));
|
|
if (!removed)
|
|
return;
|
|
copy_dive(current_dive, &displayed_dive);
|
|
mark_divelist_changed(true);
|
|
|
|
for (int i = 0; i < pictures.size(); ++i) {
|
|
// Find range [i j) of pictures to remove
|
|
if (std::find(fileUrls.begin(), fileUrls.end(), pictures[i].filename) == fileUrls.end())
|
|
continue;
|
|
int j;
|
|
for (j = i + 1; j < pictures.size(); ++j) {
|
|
if (std::find(fileUrls.begin(), fileUrls.end(), pictures[j].filename) == fileUrls.end())
|
|
break;
|
|
}
|
|
|
|
// Qt's model-interface is surprisingly idiosyncratic: you don't pass [first last), but [first last] ranges.
|
|
// For example, an empty list would be [0 -1].
|
|
beginRemoveRows(QModelIndex(), i, j - 1);
|
|
pictures.erase(pictures.begin() + i, pictures.begin() + j);
|
|
endRemoveRows();
|
|
}
|
|
}
|
|
|
|
int DivePictureModel::rowCount(const QModelIndex &parent) const
|
|
{
|
|
Q_UNUSED(parent);
|
|
return pictures.count();
|
|
}
|
|
|
|
int DivePictureModel::findPictureId(const QString &filename)
|
|
{
|
|
for (int i = 0; i < pictures.size(); ++i)
|
|
if (pictures[i].filename == filename)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
void DivePictureModel::updateThumbnail(QString filename, QImage thumbnail)
|
|
{
|
|
int i = findPictureId(filename);
|
|
if (i >= 0) {
|
|
pictures[i].image = thumbnail;
|
|
emit dataChanged(createIndex(i, 0), createIndex(i, 1));
|
|
}
|
|
}
|
|
|
|
void DivePictureModel::updateDivePictureOffset(const QString &filename, int offsetSeconds)
|
|
{
|
|
int i = findPictureId(filename);
|
|
if (i >= 0) {
|
|
pictures[i].offsetSeconds = offsetSeconds;
|
|
emit dataChanged(createIndex(i, 0), createIndex(i, 1));
|
|
}
|
|
}
|