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 "imagelistmodel.h"
0009 
0010 #include <QFileInfo>
0011 #include <QPixmap>
0012 #include <QStandardPaths>
0013 #include <QThreadPool>
0014 #include <QUrl>
0015 
0016 #include <KIO/PreviewJob>
0017 
0018 #include "../finder/imagefinder.h"
0019 #include "../finder/suffixcheck.h"
0020 
0021 ImageListModel::ImageListModel(const QBindable<QSize> &bindableTargetSize, const QBindable<bool> &bindableUsedInConfig, QObject *parent)
0022     : AbstractImageListModel(bindableTargetSize, bindableUsedInConfig, parent)
0023 {
0024 }
0025 
0026 int ImageListModel::rowCount(const QModelIndex &parent) const
0027 {
0028     return parent.isValid() ? 0 : m_data.size();
0029 }
0030 
0031 QVariant ImageListModel::data(const QModelIndex &index, int role) const
0032 {
0033     if (!index.isValid()) {
0034         return QVariant();
0035     }
0036 
0037     const int row = index.row();
0038 
0039     switch (role) {
0040     case Qt::DisplayRole: {
0041         const QString *const title = m_backgroundTitleCache.object(m_data.at(row));
0042 
0043         if (title) {
0044             return title->isEmpty() ? QFileInfo(m_data.at(row)).completeBaseName() : *title;
0045         }
0046 
0047         asyncGetMediaMetadata(m_data.at(row), QPersistentModelIndex(index));
0048 
0049         return QFileInfo(m_data.at(row)).completeBaseName();
0050     }
0051 
0052     case ScreenshotRole: {
0053         QPixmap *cachedPreview = m_imageCache.object({m_data.at(row)});
0054 
0055         if (cachedPreview) {
0056             return *cachedPreview;
0057         }
0058 
0059         asyncGetPreview({m_data.at(row)}, QPersistentModelIndex(index));
0060 
0061         return QVariant();
0062     }
0063 
0064     case AuthorRole: {
0065         const QString *const author = m_backgroundAuthorCache.object(m_data.at(row));
0066 
0067         if (author) {
0068             return *author;
0069         }
0070 
0071         asyncGetMediaMetadata(m_data.at(row), QPersistentModelIndex(index));
0072 
0073         return QString();
0074     }
0075 
0076     case ResolutionRole: {
0077         QSize *size = m_imageSizeCache.object(m_data.at(row));
0078 
0079         if (size && size->isValid()) {
0080             return QStringLiteral("%1x%2").arg(size->width()).arg(size->height());
0081         }
0082 
0083         asyncGetMediaMetadata(m_data.at(row), QPersistentModelIndex(index));
0084 
0085         return QString();
0086     }
0087 
0088     case PathRole:
0089         return QUrl::fromLocalFile(m_data.at(row));
0090 
0091     case PackageNameRole:
0092         return m_data.at(row);
0093 
0094     case RemovableRole: {
0095         const QString &path = m_data.at(row);
0096 
0097         return path.startsWith(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/wallpapers/"))
0098             || m_removableWallpapers.contains(path);
0099     }
0100 
0101     case PendingDeletionRole:
0102         return m_pendingDeletion.value(m_data.at(row), false);
0103     }
0104     Q_UNREACHABLE();
0105 }
0106 
0107 bool ImageListModel::setData(const QModelIndex &index, const QVariant &value, int role)
0108 {
0109     if (!index.isValid()) {
0110         return false;
0111     }
0112 
0113     if (role == PendingDeletionRole) {
0114         m_pendingDeletion[m_data.at(index.row())] = value.toBool();
0115 
0116         Q_EMIT dataChanged(index, index, {PendingDeletionRole});
0117         return true;
0118     }
0119 
0120     return false;
0121 }
0122 
0123 int ImageListModel::indexOf(const QString &_path) const
0124 {
0125     QString path = _path;
0126 
0127     if (path.startsWith(QLatin1String("file://"))) {
0128         path.remove(0, 7);
0129     }
0130 
0131     const auto it = std::find(m_data.cbegin(), m_data.cend(), path);
0132 
0133     if (it == m_data.cend()) {
0134         return -1;
0135     }
0136 
0137     return std::distance(m_data.cbegin(), it);
0138 }
0139 
0140 void ImageListModel::load(const QStringList &customPaths)
0141 {
0142     if (m_loading || customPaths.empty()) {
0143         return;
0144     }
0145 
0146     AbstractImageListModel::load(customPaths);
0147 
0148     ImageFinder *finder = new ImageFinder(m_customPaths);
0149     connect(finder, &ImageFinder::imageFound, this, &ImageListModel::slotHandleImageFound);
0150     QThreadPool::globalInstance()->start(finder);
0151 }
0152 
0153 void ImageListModel::slotHandleImageFound(const QStringList &paths)
0154 {
0155     beginResetModel();
0156 
0157     m_data = paths;
0158     clearCache();
0159 
0160     endResetModel();
0161 
0162     m_loading = false;
0163     Q_EMIT loaded(this);
0164 }
0165 
0166 QStringList ImageListModel::addBackground(const QString &path)
0167 {
0168     if (path.isEmpty() || !QFile::exists(path) || m_data.contains(path)) {
0169         return {};
0170     }
0171 
0172     if (QFileInfo info(path); info.isHidden() || !isAcceptableSuffix(info.suffix())) {
0173         // Skip hidden files or Format not supported
0174         return {};
0175     }
0176 
0177     if (m_usedInConfig) {
0178         beginInsertRows(QModelIndex(), 0, 0);
0179 
0180         m_data.prepend(path);
0181         m_removableWallpapers.prepend(path);
0182 
0183         endInsertRows();
0184     } else {
0185         // In a slideshow, append to the last so the random order can be kept
0186         const int count = rowCount();
0187         beginInsertRows(QModelIndex(), count, count);
0188 
0189         m_data.append(path);
0190         m_removableWallpapers.append(path);
0191 
0192         endInsertRows();
0193     }
0194 
0195     return {path};
0196 }
0197 
0198 QStringList ImageListModel::removeBackground(const QString &path)
0199 {
0200     QStringList results;
0201 
0202     if (path.isEmpty()) {
0203         return results;
0204     }
0205 
0206     const int idx = indexOf(path);
0207 
0208     if (idx < 0) {
0209         return results;
0210     }
0211 
0212     beginRemoveRows(QModelIndex(), idx, idx);
0213 
0214     m_pendingDeletion.remove(m_data.at(idx));
0215     m_removableWallpapers.removeOne(m_data.at(idx));
0216     results.append(m_data.takeAt(idx));
0217 
0218     // Remove local wallpaper
0219     if (path.startsWith(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/wallpapers/"))) {
0220         QFile f(path);
0221 
0222         if (f.exists()) {
0223             f.remove();
0224         }
0225     }
0226 
0227     endRemoveRows();
0228 
0229     return results;
0230 }