9efb56e2d43161d952efb444d1f13d87bfdd45b5 introduced rather complex logic for picture drag'n'drop events onto the profile. Among other things, the code had to check whether the picture actually belongs to the displayed dive. This can be simplified by transporting the dive-id in the drag'n'drop event structure. The flow goes like this: DivePictureModel--(1)-->DivePictureWidget--(2)-->ProfileWidget For (1), we can use the Qt::UserRole role. This was used to transport the picture-offset, but this is not needed anymore since ProfileWidget was decoupled from DivePictureModel. For (2), we simply replace the "position" value, which was never used. Why would the receiver care which pixel was pressed in the media-tab? This commit also contains a minor cleanup in DivePictureWidget: QListView::mousePressEvent(event) was called in both branches of an if and can therefore be removed from the if. This is so trivial, that it doesn't warrant its own commit. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
213 lines
5.6 KiB
C++
213 lines
5.6 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() : zoomLevel(0.0)
|
|
{
|
|
connect(Thumbnailer::instance(), &Thumbnailer::thumbnailChanged,
|
|
this, &DivePictureModel::updateThumbnail, Qt::QueuedConnection);
|
|
}
|
|
|
|
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.filename);
|
|
}
|
|
|
|
void DivePictureModel::updateDivePictures()
|
|
{
|
|
beginResetModel();
|
|
if (!pictures.isEmpty()) {
|
|
pictures.clear();
|
|
Thumbnailer::instance()->clearWorkQueue();
|
|
}
|
|
|
|
int i;
|
|
struct dive *dive;
|
|
for_each_dive (i, dive) {
|
|
if (dive->selected) {
|
|
int first = pictures.count();
|
|
FOR_EACH_PICTURE(dive)
|
|
pictures.push_back({ dive->id, picture, picture->filename, {}, picture->offset.seconds });
|
|
|
|
// Sort pictures of this dive by offset.
|
|
// Thus, the list will be sorted by (diveId, offset).
|
|
std::sort(pictures.begin() + first, pictures.end(),
|
|
[](const PictureEntry &a, const PictureEntry &b) { return a.offsetSeconds < b.offsetSeconds; });
|
|
}
|
|
}
|
|
|
|
updateThumbnails();
|
|
endResetModel();
|
|
}
|
|
|
|
int DivePictureModel::columnCount(const QModelIndex&) const
|
|
{
|
|
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::DisplayRole:
|
|
ret = QFileInfo(entry.filename).fileName();
|
|
break;
|
|
case Qt::DisplayPropertyRole:
|
|
ret = QFileInfo(entry.filename).filePath();
|
|
break;
|
|
case Qt::UserRole:
|
|
ret = entry.diveId;
|
|
break;
|
|
}
|
|
} else if (index.column() == 1) {
|
|
switch (role) {
|
|
case Qt::DisplayRole:
|
|
ret = entry.filename;
|
|
break;
|
|
}
|
|
}
|
|
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();
|
|
}
|
|
emit picturesRemoved(fileUrls);
|
|
}
|
|
|
|
int DivePictureModel::rowCount(const QModelIndex&) const
|
|
{
|
|
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(int diveId, const QString &filename, int offsetSeconds)
|
|
{
|
|
// Find the pictures of the given dive.
|
|
auto from = std::find_if(pictures.begin(), pictures.end(), [diveId](const PictureEntry &e) { return e.diveId == diveId; });
|
|
auto to = std::find_if(from, pictures.end(), [diveId](const PictureEntry &e) { return e.diveId != diveId; });
|
|
|
|
// Find picture with the given filename
|
|
auto oldPos = std::find_if(from, to, [filename](const PictureEntry &e) { return e.filename == filename; });
|
|
if (oldPos == to)
|
|
return;
|
|
|
|
// Find new position
|
|
auto newPos = std::find_if(from, to, [offsetSeconds](const PictureEntry &e) { return e.offsetSeconds > offsetSeconds; });
|
|
|
|
// Update the offset here and in the backend
|
|
oldPos->offsetSeconds = offsetSeconds;
|
|
if (struct dive *dive = get_dive_by_uniq_id(diveId)) {
|
|
FOR_EACH_PICTURE(dive) {
|
|
if (picture->filename == filename) {
|
|
picture->offset.seconds = offsetSeconds;
|
|
mark_divelist_changed(true);
|
|
break;
|
|
}
|
|
}
|
|
copy_dive(current_dive, &displayed_dive);
|
|
}
|
|
|
|
// Henceforth we will work with indices instead of iterators
|
|
int oldIndex = oldPos - pictures.begin();
|
|
int newIndex = newPos - pictures.begin();
|
|
if (oldIndex == newIndex || oldIndex + 1 == newIndex)
|
|
return;
|
|
beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
|
|
moveInVector(pictures, oldIndex, oldIndex + 1, newIndex);
|
|
endMoveRows();
|
|
}
|