File indexing completed on 2024-11-10 05:02:44
0001 /* 0002 SPDX-FileCopyrightText: 2007 Paolo Capriotti <p.capriotti@gmail.com> 0003 SPDX-FileCopyrightText: 2022 Fushan Wen <qydwhotmail@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "packagefinder.h" 0009 0010 #include <limits> 0011 0012 #include <QDir> 0013 0014 #include <KPackage/PackageLoader> 0015 0016 #include "findsymlinktarget.h" 0017 #include "suffixcheck.h" 0018 0019 namespace 0020 { 0021 /** 0022 * Computes difference of two areas 0023 */ 0024 double distance(const QSize &size, const QSize &desired) 0025 { 0026 const double desiredAspectRatio = (desired.height() > 0) ? desired.width() / static_cast<double>(desired.height()) : 0; 0027 const double candidateAspectRatio = (size.height() > 0) ? size.width() / static_cast<double>(size.height()) : std::numeric_limits<double>::max(); 0028 0029 double delta = size.width() - desired.width(); 0030 delta = delta >= 0.0 ? delta : -delta * 2; // Penalize for scaling up 0031 0032 return std::abs(candidateAspectRatio - desiredAspectRatio) * 25000 + delta; 0033 } 0034 0035 /** 0036 * @return size from the filename 0037 */ 0038 QSize resSize(QStringView str) 0039 { 0040 const int index = str.indexOf(QLatin1Char('x')); 0041 0042 if (index != -1) { 0043 return QSize(str.left(index).toInt(), str.mid(index + 1).toInt()); 0044 } 0045 0046 return QSize(); 0047 } 0048 } 0049 0050 PackageFinder::PackageFinder(const QStringList &paths, const QSize &targetSize, QObject *parent) 0051 : QObject(parent) 0052 , m_paths(paths) 0053 , m_targetSize(targetSize) 0054 { 0055 } 0056 0057 void PackageFinder::run() 0058 { 0059 QList<KPackage::Package> packages; 0060 QStringList folders; 0061 0062 QDir dir; 0063 dir.setFilter(QDir::Dirs | QDir::Readable | QDir::NoDotAndDotDot); 0064 0065 KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Images")); 0066 0067 const auto addPackage = [this, &package, &packages, &folders](const QString &_folderPath) { 0068 const QString folderPath = findSymlinkTarget(QFileInfo(_folderPath)).absoluteFilePath(); 0069 0070 if (folders.contains(folderPath)) { 0071 // The folder has been added, return true to skip it. 0072 return true; 0073 } 0074 0075 if (!QFile::exists(folderPath + QLatin1String("/metadata.desktop")) && !QFile::exists(folderPath + QLatin1String("/metadata.json"))) { 0076 folders << folderPath; 0077 return false; 0078 } 0079 0080 package.setPath(folderPath); 0081 0082 if (package.isValid() && package.metadata().isValid()) { 0083 // Check if there are any available images. 0084 QDir imageDir(package.filePath("images")); 0085 imageDir.setFilter(QDir::Files | QDir::Readable); 0086 imageDir.setNameFilters(suffixes()); 0087 0088 if (imageDir.entryInfoList().empty()) { 0089 // This is an empty package. Skip it. 0090 folders << folderPath; 0091 return true; 0092 } 0093 0094 findPreferredImageInPackage(package, m_targetSize); 0095 packages << package; 0096 folders << folderPath; 0097 0098 return true; 0099 } 0100 0101 folders << folderPath; 0102 return false; // Not found 0103 }; 0104 0105 int i; 0106 0107 for (i = 0; i < m_paths.size(); ++i) { 0108 const QString &path = m_paths.at(i); 0109 const QFileInfo info(path); 0110 0111 if (!info.isDir()) { 0112 continue; 0113 } 0114 0115 // Check the path itself is a package 0116 if (addPackage(path)) { 0117 continue; 0118 } 0119 0120 dir.setPath(path); 0121 const QFileInfoList files = dir.entryInfoList(); 0122 0123 for (const QFileInfo &wp : files) { 0124 if (!addPackage(wp.filePath())) { 0125 // Add this to the directories we should be looking at 0126 m_paths.append(wp.filePath()); 0127 } 0128 } 0129 } 0130 0131 Q_EMIT packageFound(packages); 0132 } 0133 0134 void PackageFinder::findPreferredImageInPackage(KPackage::Package &package, const QSize &targetSize) 0135 { 0136 if (!package.isValid()) { 0137 return; 0138 } 0139 0140 QSize tSize = targetSize; 0141 0142 if (tSize.isEmpty()) { 0143 tSize = QSize(1920, 1080); 0144 } 0145 0146 // find preferred size 0147 auto findBestMatch = [&package, &tSize](const QByteArray &folder) { 0148 QString preferred; 0149 const QStringList images = package.entryList(folder); 0150 0151 if (images.empty()) { 0152 return preferred; 0153 } 0154 0155 double best = std::numeric_limits<double>::max(); 0156 0157 for (const QString &entry : images) { 0158 QSize candidate = resSize(QFileInfo(entry).baseName()); 0159 0160 if (candidate.isEmpty()) { 0161 continue; 0162 } 0163 0164 const double dist = distance(candidate, tSize); 0165 0166 if (preferred.isEmpty() || dist < best) { 0167 preferred = entry; 0168 best = dist; 0169 } 0170 } 0171 0172 return preferred; 0173 }; 0174 0175 const QString preferred = findBestMatch(QByteArrayLiteral("images")); 0176 const QString preferredDark = findBestMatch(QByteArrayLiteral("images_dark")); 0177 0178 package.removeDefinition("preferred"); 0179 package.addFileDefinition("preferred", QStringLiteral("images/%1").arg(preferred)); 0180 0181 if (!preferredDark.isEmpty()) { 0182 package.removeDefinition("preferredDark"); 0183 package.addFileDefinition("preferredDark", QStringLiteral("images_dark/%1").arg(preferredDark)); 0184 } 0185 } 0186 0187 QString PackageFinder::packageDisplayName(const KPackage::Package &b) 0188 { 0189 const QString title = b.metadata().name(); 0190 0191 if (title.isEmpty()) { 0192 return QFileInfo(b.filePath("preferred")).completeBaseName(); 0193 } 0194 0195 return title; 0196 }