/*
SPDX-FileCopyrightText: 2007 Paolo Capriotti
SPDX-FileCopyrightText: 2022 Fushan Wen
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "packagefinder.h"
#include
#include
#include
#include "findsymlinktarget.h"
#include "suffixcheck.h"
namespace
{
/**
* Computes difference of two areas
*/
double distance(const QSize &size, const QSize &desired)
{
const double desiredAspectRatio = (desired.height() > 0) ? desired.width() / static_cast(desired.height()) : 0;
const double candidateAspectRatio = (size.height() > 0) ? size.width() / static_cast(size.height()) : std::numeric_limits::max();
double delta = size.width() - desired.width();
delta = delta >= 0.0 ? delta : -delta * 2; // Penalize for scaling up
return std::abs(candidateAspectRatio - desiredAspectRatio) * 25000 + delta;
}
/**
* @return size from the filename
*/
QSize resSize(QStringView str)
{
const int index = str.indexOf(QLatin1Char('x'));
if (index != -1) {
return QSize(str.left(index).toInt(), str.mid(index + 1).toInt());
}
return QSize();
}
}
PackageFinder::PackageFinder(const QStringList &paths, const QSize &targetSize, QObject *parent)
: QObject(parent)
, m_paths(paths)
, m_targetSize(targetSize)
{
}
void PackageFinder::run()
{
QList packages;
QStringList folders;
QDir dir;
dir.setFilter(QDir::Dirs | QDir::Readable | QDir::NoDotAndDotDot);
KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Images"));
const auto addPackage = [this, &package, &packages, &folders](const QString &_folderPath) {
const QString folderPath = findSymlinkTarget(QFileInfo(_folderPath)).absoluteFilePath();
if (folders.contains(folderPath)) {
// The folder has been added, return true to skip it.
return true;
}
if (!QFile::exists(folderPath + QLatin1String("/metadata.desktop")) && !QFile::exists(folderPath + QLatin1String("/metadata.json"))) {
folders << folderPath;
return false;
}
package.setPath(folderPath);
if (package.isValid() && package.metadata().isValid()) {
// Check if there are any available images.
QDir imageDir(package.filePath("images"));
imageDir.setFilter(QDir::Files | QDir::Readable);
imageDir.setNameFilters(suffixes());
if (imageDir.entryInfoList().empty()) {
// This is an empty package. Skip it.
folders << folderPath;
return true;
}
findPreferredImageInPackage(package, m_targetSize);
packages << package;
folders << folderPath;
return true;
}
folders << folderPath;
return false; // Not found
};
int i;
for (i = 0; i < m_paths.size(); ++i) {
const QString &path = m_paths.at(i);
const QFileInfo info(path);
if (!info.isDir()) {
continue;
}
// Check the path itself is a package
if (addPackage(path)) {
continue;
}
dir.setPath(path);
const QFileInfoList files = dir.entryInfoList();
for (const QFileInfo &wp : files) {
if (!addPackage(wp.filePath())) {
// Add this to the directories we should be looking at
m_paths.append(wp.filePath());
}
}
}
Q_EMIT packageFound(packages);
}
void PackageFinder::findPreferredImageInPackage(KPackage::Package &package, const QSize &targetSize)
{
if (!package.isValid()) {
return;
}
QSize tSize = targetSize;
if (tSize.isEmpty()) {
tSize = QSize(1920, 1080);
}
// find preferred size
auto findBestMatch = [&package, &tSize](const QByteArray &folder) {
QString preferred;
const QStringList images = package.entryList(folder);
if (images.empty()) {
return preferred;
}
double best = std::numeric_limits::max();
for (const QString &entry : images) {
QSize candidate = resSize(QFileInfo(entry).baseName());
if (candidate.isEmpty()) {
continue;
}
const double dist = distance(candidate, tSize);
if (preferred.isEmpty() || dist < best) {
preferred = entry;
best = dist;
}
}
return preferred;
};
const QString preferred = findBestMatch(QByteArrayLiteral("images"));
const QString preferredDark = findBestMatch(QByteArrayLiteral("images_dark"));
package.removeDefinition("preferred");
package.addFileDefinition("preferred", QStringLiteral("images/%1").arg(preferred));
if (!preferredDark.isEmpty()) {
package.removeDefinition("preferredDark");
package.addFileDefinition("preferredDark", QStringLiteral("images_dark/%1").arg(preferredDark));
}
}
QString PackageFinder::packageDisplayName(const KPackage::Package &b)
{
const QString title = b.metadata().name();
if (title.isEmpty()) {
return QFileInfo(b.filePath("preferred")).completeBaseName();
}
return title;
}