pictures: turn QString into std::string for filenames

For undo of picture manipulation, it will be crucial that the
model and the core have the same order of pictures. The first
sort criterion will be time, the second filename in the case
that two pictures have, for whatever reason, the same timestamp.

However in the core we us C-strings and thus sort byte-wise
using strcmp. In the Qt-part we use QStrings, which sort according
to unicode encoding. To enable consistent sorting, change the
Qt-part to std::string, which uses a C-style 0-terminated string
as its backing store.

One might argue that in general filenames should use system-encoding
and therefore use std::string instead of QString. However, a
broader conversion to std::string turned out to be very painful,
since Qt is (deliberately?) difficult to use with std::string.
Notable all the file-manipulation functions don't take std::string
by default. Thus, this commit only converts the internal data
of DivePictureModel, but continues to use QString for the Qt-facing
interface.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2020-05-04 09:47:55 +02:00 committed by Dirk Hohndel
parent c50e58d761
commit 4d407dc666
2 changed files with 24 additions and 14 deletions

View file

@ -43,7 +43,7 @@ void DivePictureModel::updateThumbnails()
{ {
updateZoom(); updateZoom();
for (PictureEntry &entry: pictures) for (PictureEntry &entry: pictures)
entry.image = Thumbnailer::instance()->fetchThumbnail(entry.filename, false); entry.image = Thumbnailer::instance()->fetchThumbnail(QString::fromStdString(entry.filename), false);
} }
void DivePictureModel::updateDivePictures() void DivePictureModel::updateDivePictures()
@ -87,13 +87,13 @@ QVariant DivePictureModel::data(const QModelIndex &index, int role) const
if (index.column() == 0) { if (index.column() == 0) {
switch (role) { switch (role) {
case Qt::ToolTipRole: case Qt::ToolTipRole:
return entry.filename; return QString::fromStdString(entry.filename);
case Qt::DecorationRole: case Qt::DecorationRole:
return entry.image.scaled(size, size, Qt::KeepAspectRatio); return entry.image.scaled(size, size, Qt::KeepAspectRatio);
case Qt::DisplayRole: case Qt::DisplayRole:
return QFileInfo(entry.filename).fileName(); return QFileInfo(QString::fromStdString(entry.filename)).fileName();
case Qt::DisplayPropertyRole: case Qt::DisplayPropertyRole:
return QFileInfo(entry.filename).filePath(); return QFileInfo(QString::fromStdString(entry.filename)).filePath();
case Qt::UserRole: case Qt::UserRole:
return entry.diveId; return entry.diveId;
case Qt::UserRole + 1: case Qt::UserRole + 1:
@ -104,7 +104,7 @@ QVariant DivePictureModel::data(const QModelIndex &index, int role) const
} else if (index.column() == 1) { } else if (index.column() == 1) {
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole:
return entry.filename; return QString::fromStdString(entry.filename);
} }
} }
return QVariant(); return QVariant();
@ -122,11 +122,17 @@ static bool removePictureFromSelectedDive(const char *fileUrl)
return false; return false;
} }
void DivePictureModel::removePictures(const QVector<QString> &fileUrls) void DivePictureModel::removePictures(const QVector<QString> &fileUrlsIn)
{ {
// 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),
[] (const QString &s) { return s.toStdString(); });
bool removed = false; bool removed = false;
for (const QString &fileUrl: fileUrls) for (const std::string &fileUrl: fileUrls)
removed |= removePictureFromSelectedDive(qPrintable(fileUrl)); removed |= removePictureFromSelectedDive(fileUrl.c_str());
if (!removed) if (!removed)
return; return;
copy_dive(current_dive, &displayed_dive); copy_dive(current_dive, &displayed_dive);
@ -148,7 +154,7 @@ void DivePictureModel::removePictures(const QVector<QString> &fileUrls)
pictures.erase(pictures.begin() + i, pictures.begin() + j); pictures.erase(pictures.begin() + i, pictures.begin() + j);
endRemoveRows(); endRemoveRows();
} }
emit picturesRemoved(fileUrls); emit picturesRemoved(fileUrlsIn);
} }
int DivePictureModel::rowCount(const QModelIndex&) const int DivePictureModel::rowCount(const QModelIndex&) const
@ -156,7 +162,7 @@ int DivePictureModel::rowCount(const QModelIndex&) const
return pictures.count(); return pictures.count();
} }
int DivePictureModel::findPictureId(const QString &filename) int DivePictureModel::findPictureId(const std::string &filename)
{ {
for (int i = 0; i < pictures.size(); ++i) for (int i = 0; i < pictures.size(); ++i)
if (pictures[i].filename == filename) if (pictures[i].filename == filename)
@ -193,7 +199,7 @@ static void addDurationToThumbnail(QImage &img, duration_t duration)
void DivePictureModel::updateThumbnail(QString filename, QImage thumbnail, duration_t duration) void DivePictureModel::updateThumbnail(QString filename, QImage thumbnail, duration_t duration)
{ {
int i = findPictureId(filename); int i = findPictureId(filename.toStdString());
if (i >= 0) { if (i >= 0) {
if (duration.seconds > 0) { if (duration.seconds > 0) {
addDurationToThumbnail(thumbnail, duration); // If we know the duration paint it on top of the thumbnail addDurationToThumbnail(thumbnail, duration); // If we know the duration paint it on top of the thumbnail
@ -204,8 +210,10 @@ void DivePictureModel::updateThumbnail(QString filename, QImage thumbnail, durat
} }
} }
void DivePictureModel::updateDivePictureOffset(int diveId, const QString &filename, int offsetSeconds) void DivePictureModel::updateDivePictureOffset(int diveId, const QString &filenameIn, int offsetSeconds)
{ {
std::string filename = filenameIn.toStdString();
// Find the pictures of the given dive. // 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 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; }); auto to = std::find_if(from, pictures.end(), [diveId](const PictureEntry &e) { return e.diveId != diveId; });

View file

@ -8,9 +8,11 @@
#include <QImage> #include <QImage>
#include <QFuture> #include <QFuture>
// We use std::string instead of QString to use the same character-encoding
// as in the C core (UTF-8). This is crucial to guarantee the same sort-order.
struct PictureEntry { struct PictureEntry {
int diveId; int diveId;
QString filename; std::string filename;
QImage image; QImage image;
int offsetSeconds; int offsetSeconds;
duration_t length; duration_t length;
@ -34,7 +36,7 @@ public slots:
private: private:
DivePictureModel(); DivePictureModel();
QVector<PictureEntry> pictures; QVector<PictureEntry> pictures;
int findPictureId(const QString &filename); // Return -1 if not found int findPictureId(const std::string &filename); // Return -1 if not found
double zoomLevel; // -1.0: minimum, 0.0: standard, 1.0: maximum double zoomLevel; // -1.0: minimum, 0.0: standard, 1.0: maximum
int size; int size;
void updateThumbnails(); void updateThumbnails();