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 }