Dive pictures: implement FindMovedImagesDialog

Move the find-moved-images functions into a new translation unit
and present the user with the identified matches before applying
them.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2018-06-10 16:40:23 +02:00 committed by Dirk Hohndel
parent f3ef38ca0d
commit 09fd5c40d1
9 changed files with 535 additions and 125 deletions

View file

@ -1217,97 +1217,6 @@ QStringList imageExtensionFilters() {
return filters;
}
// Compare two full paths and return the number of matching levels, starting from the filename.
// String comparison is case-insensitive.
static int matchFilename(const QString &path1, const QString &path2)
{
QFileInfo f1(path1);
QFileInfo f2(path2);
int score = 0;
for (;;) {
QString fn1 = f1.fileName();
QString fn2 = f2.fileName();
if (fn1.isEmpty() || fn2.isEmpty())
break;
if (fn1 == ".") {
f1 = QFileInfo(f1.path());
continue;
}
if (fn2 == ".") {
f2 = QFileInfo(f2.path());
continue;
}
if (QString::compare(fn1, fn2, Qt::CaseInsensitive) != 0)
break;
f1 = QFileInfo(f1.path());
f2 = QFileInfo(f2.path());
++score;
}
return score;
}
struct ImageMatch {
QString localFilename;
int score;
};
static void learnImage(const QString &filename, QMap<QString, ImageMatch> &matches, const QVector<QString> &imageFilenames)
{
// Find the original filenames with the highest match-score
QStringList newMatches;
int bestScore = 1;
for (const QString &originalFilename: imageFilenames) {
int score = matchFilename(filename, originalFilename);
if (score < bestScore)
continue;
if (score > bestScore)
newMatches.clear();
newMatches.append(originalFilename);
bestScore = score;
}
// Add the new original filenames to the list of matches, if the score is higher than previously
for (const QString &originalFilename: newMatches) {
auto it = matches.find(originalFilename);
if (it == matches.end())
matches.insert(originalFilename, { filename, bestScore });
else if (it->score < bestScore)
*it = { filename, bestScore };
}
}
void learnImages(const QStringList &dirNames, int max_recursions, const QVector<QString> &imageFilenames)
{
QStringList filters = imageExtensionFilters();
QMap<QString, ImageMatch> matches;
QVector<QStringList> stack; // Use a stack to recurse into directories
stack.reserve(max_recursions + 1);
stack.append(dirNames);
while (!stack.isEmpty()) {
if (stack.last().isEmpty()) {
stack.removeLast();
continue;
}
QDir dir(stack.last().takeLast());
for (const QString &file: dir.entryList(filters, QDir::Files))
learnImage(dir.absoluteFilePath(file), matches, imageFilenames);
if (stack.size() <= max_recursions) {
stack.append(QStringList());
for (const QString &dirname: dir.entryList(QStringList(), QDir::NoDotAndDotDot | QDir::Dirs))
stack.last().append(dir.filePath(dirname));
}
}
for (auto it = matches.begin(); it != matches.end(); ++it)
learnPictureFilename(it.key(), it->localFilename);
write_hashes();
}
extern "C" const char *local_file_path(struct picture *picture)
{
return copy_qstring(localFilePath(picture->filename));