File indexing completed on 2024-11-10 05:02:45
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 "packagelistmodel.h" 0009 0010 #include <QDir> 0011 #include <QGuiApplication> 0012 #include <QPalette> 0013 #include <QPixmap> 0014 #include <QStandardPaths> 0015 #include <QThreadPool> 0016 0017 #include <KAboutData> 0018 #include <KPackage/PackageLoader> 0019 0020 #include "../finder/packagefinder.h" 0021 #include "../finder/suffixcheck.h" 0022 0023 PackageListModel::PackageListModel(const QBindable<QSize> &bindableTargetSize, const QBindable<bool> &bindableUsedInConfig, QObject *parent) 0024 : AbstractImageListModel(bindableTargetSize, bindableUsedInConfig, parent) 0025 { 0026 qRegisterMetaType<QList<KPackage::Package>>(); 0027 } 0028 0029 int PackageListModel::rowCount(const QModelIndex &parent) const 0030 { 0031 return parent.isValid() ? 0 : m_packages.size(); 0032 } 0033 0034 QVariant PackageListModel::data(const QModelIndex &index, int role) const 0035 { 0036 if (!index.isValid()) { 0037 return QVariant(); 0038 } 0039 0040 const KPackage::Package &b = m_packages.at(index.row()); 0041 0042 if (!b.isValid()) { 0043 Q_UNREACHABLE(); // Should be already filtered out by the finder 0044 } 0045 0046 switch (role) { 0047 case Qt::DisplayRole: 0048 return PackageFinder::packageDisplayName(b); 0049 0050 case ScreenshotRole: { 0051 QStringList paths{b.filePath(QByteArrayLiteral("preferred"))}; 0052 const QString darkPath = b.filePath(QByteArrayLiteral("preferredDark")); 0053 0054 if (!darkPath.isEmpty()) { 0055 paths.append(darkPath); 0056 } 0057 0058 QPixmap *cachedPreview = m_imageCache.object(paths); 0059 0060 if (cachedPreview) { 0061 return *cachedPreview; 0062 } 0063 0064 asyncGetPreview(paths, QPersistentModelIndex(index)); 0065 0066 return QVariant(); 0067 } 0068 0069 case AuthorRole: { 0070 if (!b.metadata().authors().empty()) { 0071 return b.metadata().authors().at(0).name(); 0072 } 0073 0074 return QString(); 0075 } 0076 0077 case ResolutionRole: { 0078 const QString path = b.filePath("preferred"); 0079 0080 QSize *size = m_imageSizeCache.object(path); 0081 0082 if (size && size->isValid()) { 0083 return QStringLiteral("%1x%2").arg(size->width()).arg(size->height()); 0084 } 0085 0086 asyncGetMediaMetadata(path, QPersistentModelIndex(index)); 0087 0088 return QString(); 0089 } 0090 0091 case PathRole: { 0092 if (qGray(qGuiApp->palette().window().color().rgb()) < 192) { 0093 const QString darkPath = b.filePath(QByteArrayLiteral("preferredDark")); 0094 if (!darkPath.isEmpty()) { 0095 return QUrl::fromLocalFile(darkPath); 0096 } 0097 } 0098 0099 return QUrl::fromLocalFile(b.filePath("preferred")); 0100 } 0101 0102 case PackageNameRole: 0103 return b.path(); 0104 0105 case RemovableRole: { 0106 const QString path = b.path(); 0107 0108 return path.startsWith(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/wallpapers/")) 0109 || m_removableWallpapers.contains(path); 0110 } 0111 0112 case PendingDeletionRole: 0113 return m_pendingDeletion.value(b.path(), false); 0114 } 0115 Q_UNREACHABLE(); 0116 } 0117 0118 bool PackageListModel::setData(const QModelIndex &index, const QVariant &value, int role) 0119 { 0120 if (!index.isValid()) { 0121 return false; 0122 } 0123 0124 if (role == PendingDeletionRole) { 0125 const KPackage::Package &b = m_packages.at(index.row()); 0126 m_pendingDeletion[b.path()] = value.toBool(); 0127 0128 Q_EMIT dataChanged(index, index, {PendingDeletionRole}); 0129 return true; 0130 } 0131 0132 return false; 0133 } 0134 0135 int PackageListModel::indexOf(const QString &_path) const 0136 { 0137 QString path = _path.endsWith(QDir::separator()) ? _path : _path + QDir::separator(); 0138 0139 if (path.startsWith(QLatin1String("file://"))) { 0140 path.remove(0, 7); 0141 } 0142 0143 const auto it = std::find_if(m_packages.cbegin(), m_packages.cend(), [&path](const KPackage::Package &p) { 0144 return path == p.path(); 0145 }); 0146 0147 if (it == m_packages.cend()) { 0148 return -1; 0149 } 0150 0151 return std::distance(m_packages.cbegin(), it); 0152 } 0153 0154 void PackageListModel::load(const QStringList &customPaths) 0155 { 0156 if (m_loading || customPaths.empty()) { 0157 return; 0158 } 0159 0160 AbstractImageListModel::load(customPaths); 0161 0162 PackageFinder *finder = new PackageFinder(m_customPaths, m_targetSize); 0163 connect(finder, &PackageFinder::packageFound, this, &PackageListModel::slotHandlePackageFound); 0164 QThreadPool::globalInstance()->start(finder); 0165 } 0166 0167 QStringList PackageListModel::addBackground(const QString &path) 0168 { 0169 if (path.isEmpty() || indexOf(path) >= 0 || !QFileInfo(path).isDir()) { 0170 return {}; 0171 } 0172 0173 KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Images")); 0174 package.setPath(path); 0175 0176 if (!package.isValid() || !package.metadata().isValid()) { 0177 return {}; 0178 } 0179 0180 // Check if there are any available images. 0181 QDir imageDir(package.filePath("images")); 0182 imageDir.setFilter(QDir::Files | QDir::Readable); 0183 imageDir.setNameFilters(suffixes()); 0184 0185 if (imageDir.entryInfoList().empty()) { 0186 // This is an empty package. Skip it. 0187 return {}; 0188 } 0189 0190 PackageFinder::findPreferredImageInPackage(package, m_targetSize); 0191 0192 if (m_usedInConfig) { 0193 beginInsertRows(QModelIndex(), 0, 0); 0194 0195 m_removableWallpapers.prepend(package.path()); 0196 m_packages.prepend(package); 0197 0198 endInsertRows(); 0199 } else { 0200 // In a slideshow, append to last so the random order can be kept 0201 const int count = rowCount(); 0202 beginInsertRows(QModelIndex(), count, count); 0203 0204 m_removableWallpapers.append(package.path()); 0205 m_packages.append(package); 0206 0207 endInsertRows(); 0208 } 0209 0210 return {package.path()}; 0211 } 0212 0213 QStringList PackageListModel::removeBackground(const QString &_path) 0214 { 0215 QStringList results; 0216 0217 if (_path.isEmpty()) { 0218 return results; 0219 } 0220 0221 const QString path = _path.endsWith(QDir::separator()) ? _path : _path + QDir::separator(); 0222 0223 const int idx = indexOf(path); 0224 0225 if (idx < 0) { 0226 return results; 0227 } 0228 0229 beginRemoveRows(QModelIndex(), idx, idx); 0230 0231 m_pendingDeletion.remove(m_packages.at(idx).path()); 0232 m_removableWallpapers.removeOne(m_packages.at(idx).path()); 0233 results.append(m_packages.takeAt(idx).path()); 0234 0235 // Uninstall local package 0236 if (path.startsWith(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/wallpapers/"))) { 0237 QDir f(path); 0238 0239 if (f.exists()) { 0240 f.removeRecursively(); 0241 } 0242 } 0243 0244 endRemoveRows(); 0245 0246 return results; 0247 } 0248 0249 void PackageListModel::slotHandlePackageFound(const QList<KPackage::Package> &packages) 0250 { 0251 beginResetModel(); 0252 0253 m_packages = packages; 0254 clearCache(); 0255 0256 endResetModel(); 0257 0258 m_loading = false; 0259 Q_EMIT loaded(this); 0260 }