You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
322 lines
9.2 KiB
322 lines
9.2 KiB
/* |
|
SPDX-FileCopyrightText: 2022 Fushan Wen <qydwhotmail@gmail.com> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "imageproxymodel.h" |
|
|
|
#include <QDir> |
|
|
|
#include <KConfigGroup> |
|
#include <KIO/OpenFileManagerWindowJob> |
|
#include <KSharedConfig> |
|
|
|
#include "../finder/suffixcheck.h" |
|
#include "imagelistmodel.h" |
|
#include "packagelistmodel.h" |
|
|
|
ImageProxyModel::ImageProxyModel(const QStringList &_customPaths, const QSize &targetSize, QObject *parent) |
|
: QConcatenateTablesProxyModel(parent) |
|
, m_imageModel(new ImageListModel(targetSize, this)) |
|
, m_packageModel(new PackageListModel(targetSize, this)) |
|
{ |
|
connect(this, &ImageProxyModel::rowsInserted, this, &ImageProxyModel::countChanged); |
|
connect(this, &ImageProxyModel::rowsRemoved, this, &ImageProxyModel::countChanged); |
|
connect(this, &ImageProxyModel::modelReset, this, &ImageProxyModel::countChanged); |
|
|
|
/** |
|
* Add files to KDirWatch. |
|
* Files or dirs should be already added to KDirWatch when |
|
* rowsInserted or rowsRemoved is emitted. |
|
*/ |
|
connect(m_imageModel, &QAbstractItemModel::modelAboutToBeReset, this, &ImageProxyModel::slotSourceModelAboutToBeReset); |
|
connect(m_packageModel, &QAbstractItemModel::modelAboutToBeReset, this, &ImageProxyModel::slotSourceModelAboutToBeReset); |
|
connect(m_imageModel, &QAbstractItemModel::modelReset, this, &ImageProxyModel::slotSourceModelReset); |
|
connect(m_packageModel, &QAbstractItemModel::modelReset, this, &ImageProxyModel::slotSourceModelReset); |
|
|
|
// Monitor file changes in the custom directories for the slideshow backend. |
|
QStringList customPaths = _customPaths; |
|
|
|
if (customPaths.empty()) { |
|
KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("plasmarc")), QStringLiteral("Wallpapers")); |
|
customPaths = cfg.readEntry("usersWallpapers", QStringList{}); |
|
m_imageModel->m_removableWallpapers = customPaths; |
|
m_packageModel->m_removableWallpapers = customPaths; |
|
|
|
customPaths += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("wallpapers/"), QStandardPaths::LocateDirectory); |
|
} |
|
|
|
for (const QString &path : std::as_const(customPaths)) { |
|
if (QFileInfo(path).isDir()) { |
|
m_dirWatch.addDir(path, KDirWatch::WatchFiles | KDirWatch::WatchSubDirs); |
|
} |
|
} |
|
|
|
connect(&m_dirWatch, &KDirWatch::created, this, &ImageProxyModel::slotDirWatchCreated); |
|
connect(&m_dirWatch, &KDirWatch::deleted, this, &ImageProxyModel::slotDirWatchDeleted); |
|
|
|
connect(m_imageModel, &AbstractImageListModel::loaded, this, &ImageProxyModel::slotHandleLoaded); |
|
connect(m_packageModel, &AbstractImageListModel::loaded, this, &ImageProxyModel::slotHandleLoaded); |
|
|
|
m_loaded = 0; |
|
Q_EMIT loadingChanged(); |
|
|
|
m_imageModel->load(customPaths); |
|
m_packageModel->load(customPaths); |
|
} |
|
|
|
QHash<int, QByteArray> ImageProxyModel::roleNames() const |
|
{ |
|
const auto models = sourceModels(); |
|
|
|
if (!models.empty()) { |
|
return models.at(0)->roleNames(); |
|
} |
|
|
|
return QConcatenateTablesProxyModel::roleNames(); |
|
} |
|
|
|
int ImageProxyModel::count() const |
|
{ |
|
return rowCount(); |
|
} |
|
|
|
int ImageProxyModel::indexOf(const QString &packagePath) const |
|
{ |
|
int idx = -1; |
|
|
|
const auto models = sourceModels(); |
|
|
|
for (const auto &m : models) { |
|
idx = static_cast<const AbstractImageListModel *>(m)->indexOf(packagePath); |
|
|
|
if (idx >= 0) { |
|
return mapFromSource(m->index(idx, 0)).row(); |
|
} |
|
} |
|
|
|
return idx; |
|
} |
|
|
|
bool ImageProxyModel::loading() const |
|
{ |
|
return m_loaded != 2; |
|
} |
|
|
|
void ImageProxyModel::reload() |
|
{ |
|
const auto models = sourceModels(); |
|
|
|
for (const auto &m : models) { |
|
static_cast<AbstractImageListModel *>(m)->reload(); |
|
} |
|
|
|
m_loaded = 0; |
|
Q_EMIT loadingChanged(); |
|
} |
|
|
|
QStringList ImageProxyModel::addBackground(const QString &_path) |
|
{ |
|
QString path = _path; |
|
|
|
if (path.startsWith(QLatin1String("file://"))) { |
|
path.remove(0, 7); |
|
} |
|
|
|
const QFileInfo info(path); |
|
|
|
QStringList results; |
|
|
|
if (info.isDir()) { |
|
if (!path.endsWith(QDir::separator())) { |
|
path += QDir::separator(); |
|
} |
|
|
|
results = m_packageModel->addBackground(path); |
|
} else if (info.isFile()) { |
|
results = m_imageModel->addBackground(path); |
|
} |
|
|
|
if (!results.empty()) { |
|
m_pendingAddition.append(results); |
|
|
|
std::for_each(results.cbegin(), results.cend(), [this](const QString &path) { |
|
if (m_dirWatch.contains(path)) { |
|
return; |
|
} |
|
|
|
const QFileInfo info(path); |
|
|
|
if (info.isFile()) { |
|
m_dirWatch.addFile(path); |
|
} else if (info.isDir()) { |
|
m_dirWatch.addDir(path); |
|
} |
|
}); |
|
} |
|
|
|
return results; |
|
} |
|
|
|
void ImageProxyModel::removeBackground(const QString &_packagePath) |
|
{ |
|
QString packagePath = _packagePath; |
|
|
|
if (packagePath.startsWith(QLatin1String("file://"))) { |
|
packagePath.remove(0, 7); |
|
} |
|
|
|
QStringList results; |
|
|
|
// The file may be already deleted, so isFile/isDir won't work. |
|
if (isAcceptableSuffix(QFileInfo(packagePath).suffix())) { |
|
results = m_imageModel->removeBackground(packagePath); |
|
|
|
if (!results.empty()){ |
|
m_dirWatch.removeFile(results.at(0)); |
|
} |
|
} else { |
|
results = m_packageModel->removeBackground(packagePath); |
|
|
|
if (!results.empty()) { |
|
m_dirWatch.removeFile(results.at(0)); |
|
} |
|
} |
|
|
|
// The user may add a wallpaper and delete it later. |
|
for (const QString &path : std::as_const(results)) { |
|
m_pendingAddition.removeOne(path); |
|
} |
|
} |
|
|
|
void ImageProxyModel::commitAddition() |
|
{ |
|
if (m_pendingAddition.empty()) { |
|
return; |
|
} |
|
|
|
KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("plasmarc")), QStringLiteral("Wallpapers")); |
|
QStringList list = cfg.readEntry("usersWallpapers", QStringList{}); |
|
|
|
list += m_pendingAddition; |
|
list.removeDuplicates(); |
|
|
|
cfg.writeEntry("usersWallpapers", list); |
|
|
|
m_pendingAddition.clear(); |
|
} |
|
|
|
void ImageProxyModel::commitDeletion() |
|
{ |
|
QStringList pendingList; |
|
|
|
for (int row = 0; row < rowCount(); row++) { |
|
QModelIndex idx = index(row, 0); |
|
|
|
if (idx.data(PendingDeletionRole).toBool()) { |
|
pendingList.append(idx.data(PackageNameRole).toString()); |
|
} |
|
} |
|
|
|
for (const QString &p : std::as_const(pendingList)) { |
|
removeBackground(p); |
|
} |
|
|
|
// Update the config |
|
KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("plasmarc")), QStringLiteral("Wallpapers")); |
|
const QStringList list = cfg.readEntry("usersWallpapers", QStringList{}); |
|
QStringList updatedList; |
|
|
|
// Check if the file still exists |
|
std::copy_if(list.cbegin(), list.cend(), std::back_inserter(updatedList), [&pendingList](const QString &_p) { |
|
QString p = _p; |
|
if (p.startsWith(QLatin1String("file://"))) { |
|
p.remove(0, 7); |
|
} |
|
|
|
return !pendingList.contains(p) && QFileInfo(p).exists(); |
|
}); |
|
|
|
cfg.writeEntry("usersWallpapers", updatedList); |
|
cfg.sync(); |
|
} |
|
|
|
void ImageProxyModel::openContainingFolder(int row) const |
|
{ |
|
KIO::highlightInFileManager({index(row, 0).data(PathRole).toUrl()}); |
|
} |
|
|
|
void ImageProxyModel::slotHandleLoaded(AbstractImageListModel *model) |
|
{ |
|
disconnect(model, &AbstractImageListModel::loaded, this, 0); |
|
|
|
if (++m_loaded == 2) { |
|
// All models are loaded, now add them. |
|
addSourceModel(m_imageModel); |
|
addSourceModel(m_packageModel); |
|
|
|
connect(this, &ImageProxyModel::targetSizeChanged, m_imageModel, &AbstractImageListModel::slotTargetSizeChanged); |
|
connect(this, &ImageProxyModel::targetSizeChanged, m_packageModel, &AbstractImageListModel::slotTargetSizeChanged); |
|
|
|
Q_EMIT loadingChanged(); |
|
} |
|
} |
|
|
|
void ImageProxyModel::slotSourceModelAboutToBeReset() |
|
{ |
|
AbstractImageListModel *model = qobject_cast<AbstractImageListModel *>(this->sender()); |
|
|
|
if (!model) { |
|
return; |
|
} |
|
|
|
// Delete all items in KDirWatch |
|
for (int i = 0; i < model->rowCount(); i++) { |
|
const QString packageName = model->index(i, 0).data(ImageRoles::PackageNameRole).toString(); |
|
const QFileInfo info(packageName); |
|
|
|
if (info.isFile()) { |
|
m_dirWatch.removeFile(packageName); |
|
} else if (info.isDir()) { |
|
m_dirWatch.removeDir(packageName); |
|
} |
|
} |
|
} |
|
|
|
void ImageProxyModel::slotSourceModelReset() |
|
{ |
|
AbstractImageListModel *model = qobject_cast<AbstractImageListModel *>(this->sender()); |
|
|
|
if (!model) { |
|
return; |
|
} |
|
|
|
// Add all items to KDirWatch |
|
for (int i = 0; i < model->rowCount(); i++) { |
|
const QString packageName = model->index(i, 0).data(ImageRoles::PackageNameRole).toString(); |
|
const QFileInfo info(packageName); |
|
|
|
if (info.isFile()) { |
|
m_dirWatch.addFile(packageName); |
|
} else if (info.isDir()) { |
|
m_dirWatch.addDir(packageName); |
|
} |
|
} |
|
} |
|
|
|
void ImageProxyModel::slotDirWatchCreated(const QString &_path) |
|
{ |
|
QString path = _path; |
|
|
|
if (int idx = path.indexOf(QLatin1String("contents/images/")); idx > 0) { |
|
path = path.mid(0, idx); |
|
} |
|
|
|
addBackground(path); |
|
} |
|
|
|
void ImageProxyModel::slotDirWatchDeleted(const QString &path) |
|
{ |
|
removeBackground(path); |
|
}
|
|
|