diff --git a/wallpapers/image/CMakeLists.txt b/wallpapers/image/CMakeLists.txt index e22652615..ba404f22c 100644 --- a/wallpapers/image/CMakeLists.txt +++ b/wallpapers/image/CMakeLists.txt @@ -1,36 +1,41 @@ add_definitions(-DTRANSLATION_DOMAIN=\"plasma_applet_org.kde.image\") set(image_SRCS image.cpp imageplugin.cpp backgroundlistmodel.cpp ) +ecm_qt_declare_logging_category(image_SRCS HEADER debug.h + IDENTIFIER IMAGEWALLPAPER + CATEGORY_NAME kde.wallpapers.image + DEFAULT_SEVERITY Info) + add_library(plasma_wallpaper_imageplugin SHARED ${image_SRCS}) target_link_libraries(plasma_wallpaper_imageplugin Qt5::Core Qt5::Quick Qt5::Qml KF5::Plasma KF5::KIOCore KF5::KIOWidgets KF5::I18n KF5::KIOCore KF5::KIOWidgets # KFileDialog KF5::NewStuff KF5::GuiAddons ) add_subdirectory(autotests) install(TARGETS plasma_wallpaper_imageplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/wallpapers/image) install(FILES qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/wallpapers/image) install(FILES wallpaper.knsrc DESTINATION ${KDE_INSTALL_CONFDIR}) plasma_install_package(imagepackage org.kde.image wallpapers wallpaper) plasma_install_package(slideshowpackage org.kde.slideshow wallpapers wallpaper) install(DIRECTORY imagepackage/contents/ui DESTINATION ${PLASMA_DATA_INSTALL_DIR}/wallpapers/org.kde.slideshow/contents PATTERN .svn EXCLUDE PATTERN CMakeLists.txt EXCLUDE PATTERN Messages.sh EXCLUDE) diff --git a/wallpapers/image/backgroundlistmodel.cpp b/wallpapers/image/backgroundlistmodel.cpp index e24db26b5..b48ec00b2 100644 --- a/wallpapers/image/backgroundlistmodel.cpp +++ b/wallpapers/image/backgroundlistmodel.cpp @@ -1,590 +1,590 @@ /*************************************************************************** * Copyright 2007 Paolo Capriotti * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef BACKGROUNDLISTMODEL_CPP #define BACKGROUNDLISTMODEL_CPP +#include "debug.h" #include "backgroundlistmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "image.h" QStringList BackgroundFinder::m_suffixes; ImageSizeFinder::ImageSizeFinder(const QString &path, QObject *parent) : QObject(parent), m_path(path) { } void ImageSizeFinder::run() { QImageReader reader(m_path); Q_EMIT sizeFound(m_path, reader.size()); } BackgroundListModel::BackgroundListModel(Image *wallpaper, QObject *parent) : QAbstractListModel(parent), m_wallpaper(wallpaper) { connect(&m_dirwatch, &KDirWatch::deleted, this, &BackgroundListModel::removeBackground); //TODO: on Qt 4.4 use the ui scale factor QFontMetrics fm(QGuiApplication::font()); m_screenshotSize = fm.width('M') * 15; m_imageCache = new KImageCache(QStringLiteral("plasma_wallpaper_preview"), 10485760); } BackgroundListModel::~BackgroundListModel() { delete m_imageCache; } QHash BackgroundListModel::BackgroundListModel::roleNames() const { return { { Qt::DisplayRole, "display" }, { Qt::DecorationRole, "decoration" }, { AuthorRole, "author" }, { ScreenshotRole, "screenshot" }, { ResolutionRole, "resolution" }, { PathRole, "path" }, { PackageNameRole, "packageName" }, { RemovableRole, "removable" }, { PendingDeletionRole, "pendingDeletion" }, }; } void BackgroundListModel::removeBackground(const QString &path) { int index = -1; while ((index = indexOf(path)) >= 0) { beginRemoveRows(QModelIndex(), index, index); m_packages.removeAt(index); endRemoveRows(); emit countChanged(); } } void BackgroundListModel::reload() { reload(QStringList()); } void BackgroundListModel::reload(const QStringList &selected) { if (!m_packages.isEmpty()) { beginRemoveRows(QModelIndex(), 0, m_packages.count() - 1); m_packages.clear(); endRemoveRows(); emit countChanged(); } if (!m_wallpaper) { return; } if (!selected.isEmpty()) { - qDebug() << "selected" << selected; + qCDebug(IMAGEWALLPAPER) << "selected" << selected; processPaths(selected); } const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("wallpapers/"), QStandardPaths::LocateDirectory); - qDebug() << " WP : -------" << dirs; + qCDebug(IMAGEWALLPAPER) << "Looking into" << dirs << "for wallpapers"; BackgroundFinder *finder = new BackgroundFinder(m_wallpaper.data(), dirs); connect(finder, &BackgroundFinder::backgroundsFound, this, &BackgroundListModel::backgroundsFound); m_findToken = finder->token(); finder->start(); m_removableWallpapers = QSet::fromList(selected); } void BackgroundListModel::backgroundsFound(const QStringList &paths, const QString &token) { if (token == m_findToken) { processPaths(paths); } } void BackgroundListModel::processPaths(const QStringList &paths) { if (!m_wallpaper) { return; } QList newPackages; Q_FOREACH (QString file, paths) { // check if the path is a symlink and if it is, // work with the target rather than the symlink QFileInfo info(file); if (info.isSymLink()) { file = info.symLinkTarget(); } // now check if the path contains "contents" part // which could indicate that the file is part of some other // package (could have been symlinked) and we should work // with the package (which can already be present) rather // than just one file from it int contentsIndex = file.indexOf(QStringLiteral("contents")); // FIXME: additionally check for metadata.desktop being present // which would confirm a package but might be slowing things if (contentsIndex != -1) { file.truncate(contentsIndex); } // so now we have a path to a package, check if we're not // processing the same path twice (this is different from // the "!contains(file)" call lower down, that one checks paths // already in the model and does not include the paths // that are being checked in here); we want to check for duplicates // if and only if we actually changed the path (so the conditions from above // are reused here as that means we did change the path) if ((info.isSymLink() || contentsIndex != -1) && paths.contains(file)) { continue; } if (!contains(file) && QFile::exists(file)) { KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Images")); package.setPath(file); if (package.isValid()) { m_wallpaper->findPreferedImageInPackage(package); newPackages << package; } } } // add new files to dirwatch Q_FOREACH (const KPackage::Package &b, newPackages) { if (!m_dirwatch.contains(b.path())) { m_dirwatch.addFile(b.path()); } } if (!newPackages.isEmpty()) { const int start = rowCount(); beginInsertRows(QModelIndex(), start, start + newPackages.size() - 1); m_packages.append(newPackages); endInsertRows(); emit countChanged(); } - //qDebug() << t.elapsed(); + //qCDebug(IMAGEWALLPAPER) << t.elapsed(); } void BackgroundListModel::addBackground(const QString& path) { if (!m_wallpaper || !contains(path)) { if (!m_dirwatch.contains(path)) { m_dirwatch.addFile(path); } beginInsertRows(QModelIndex(), 0, 0); KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Images")); m_removableWallpapers.insert(path); package.setPath(path); m_wallpaper->findPreferedImageInPackage(package); - qDebug() << "WP Bckground added " << path << package.isValid(); + qCDebug(IMAGEWALLPAPER) << "Background added " << path << package.isValid(); m_packages.prepend(package); endInsertRows(); emit countChanged(); } } int BackgroundListModel::indexOf(const QString &path) const { for (int i = 0; i < m_packages.size(); i++) { // packages will end with a '/', but the path passed in may not QString package = m_packages[i].path(); if (package.at(package.length() - 1) == QChar::fromLatin1('/')) { package.truncate(package.length() - 1); } //remove eventual file:/// const QString filteredPath = QUrl(path).path(); if (filteredPath.startsWith(package)) { // FIXME: ugly hack to make a difference between local files in the same dir // package->path does not contain the actual file name - qDebug() << "WP prefix" << m_packages[i].contentsPrefixPaths() << m_packages[i].filePath("preferred") << package << filteredPath; + qCDebug(IMAGEWALLPAPER) << "prefix" << m_packages[i].contentsPrefixPaths() << m_packages[i].filePath("preferred") << package << filteredPath; QStringList ps = m_packages[i].contentsPrefixPaths(); bool prefixempty = ps.count() == 0; if (!prefixempty) { prefixempty = ps[0].isEmpty(); } //For local files (user wallpapers) filteredPath == m_packages[i].filePath("preferred") //E.X. filteredPath = "/home/kde/next.png" //m_packages[i].filePath("preferred") = "/home/kde/next.png" // //But for the system wallpapers this is not the case. filteredPath != m_packages[i].filePath("preferred") //E.X. filteredPath = /usr/share/wallpapers/Next/" //m_packages[i].filePath("preferred") = "/usr/share/wallpapers/Next/contents/images/1920x1080.png" if ((filteredPath == m_packages[i].filePath("preferred")) || m_packages[i].filePath("preferred").contains(filteredPath)) { - qDebug() << "WP TRUE" << (!m_packages[i].contentsPrefixPaths().isEmpty()) << (filteredPath == m_packages[i].filePath("preferred")); return i; } } } return -1; } bool BackgroundListModel::contains(const QString &path) const { - //qDebug() << "WP contains: " << path << indexOf(path).isValid(); + //qCDebug(IMAGEWALLPAPER) << "WP contains: " << path << indexOf(path).isValid(); return indexOf(path) >= 0; } int BackgroundListModel::rowCount(const QModelIndex &) const { return m_packages.size(); } QSize BackgroundListModel::bestSize(const KPackage::Package &package) const { if (m_sizeCache.contains(package.path())) { return m_sizeCache.value(package.path()); } const QString image = package.filePath("preferred"); if (image.isEmpty()) { return QSize(); } ImageSizeFinder *finder = new ImageSizeFinder(image); connect(finder, &ImageSizeFinder::sizeFound, this, &BackgroundListModel::sizeFound); QThreadPool::globalInstance()->start(finder); QSize size(-1, -1); const_cast(this)->m_sizeCache.insert(package.path(), size); return size; } void BackgroundListModel::sizeFound(const QString &path, const QSize &s) { if (!m_wallpaper) { return; } int idx = indexOf(path); if (idx >= 0) { KPackage::Package package = m_packages.at(idx); m_sizeCache.insert(package.path(), s); emit dataChanged(index(idx, 0), index(idx, 0)); } } QVariant BackgroundListModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } if (index.row() >= m_packages.size()) { return QVariant(); } KPackage::Package b = package(index.row()); if (!b.isValid()) { return QVariant(); } switch (role) { case Qt::DisplayRole: { QString title = b.metadata().isValid() ? b.metadata().name() : QString(); if (title.isEmpty()) { return QFileInfo(b.filePath("preferred")).completeBaseName(); } return title; } break; case ScreenshotRole: { QPixmap preview = QPixmap(QSize(m_screenshotSize*1.6, m_screenshotSize)); if (m_imageCache->findPixmap(b.filePath("preferred"), &preview)) { return preview; } -// qDebug() << "WP preferred: " << b.filePath("preferred"); -// qDebug() << "WP screenshot: " << b.filePath("screenshot"); +// qCDebug(IMAGEWALLPAPER) << "WP preferred: " << b.filePath("preferred"); +// qCDebug(IMAGEWALLPAPER) << "WP screenshot: " << b.filePath("screenshot"); QUrl file = QUrl::fromLocalFile(b.filePath("preferred")); if (!m_previewJobs.contains(file) && file.isValid()) { KFileItemList list; list.append(KFileItem(file, QString(), 0)); QStringList availablePlugins = KIO::PreviewJob::availablePlugins(); KIO::PreviewJob* job = KIO::filePreview(list, QSize(m_screenshotSize*1.6, m_screenshotSize), &availablePlugins); job->setIgnoreMaximumSize(true); connect(job, &KIO::PreviewJob::gotPreview, this, &BackgroundListModel::showPreview); connect(job, &KIO::PreviewJob::failed, this, &BackgroundListModel::previewFailed); const_cast(this)->m_previewJobs.insert(file, QPersistentModelIndex(index)); } return QVariant(); } break; case AuthorRole: if (b.metadata().isValid() && !b.metadata().authors().isEmpty()) { return b.metadata().authors().first().name(); } else { return QString(); } break; case ResolutionRole:{ QSize size = bestSize(b); if (size.isValid()) { return QString::fromLatin1("%1x%2").arg(size.width()).arg(size.height()); } return QString(); } break; case PathRole: return QUrl::fromLocalFile(b.filePath("preferred")); break; case PackageNameRole: return !b.metadata().isValid() || b.metadata().pluginId().isEmpty() ? b.filePath("preferred") : b.metadata().pluginId(); break; case RemovableRole: { QString localWallpapers = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/wallpapers/"; QString path = b.filePath("preferred"); return path.startsWith(localWallpapers) || m_removableWallpapers.contains(path); } break; case PendingDeletionRole: { QUrl wallpaperUrl = QUrl::fromLocalFile(b.filePath("preferred")); return m_pendingDeletion.contains(wallpaperUrl.toLocalFile()) ? m_pendingDeletion[wallpaperUrl.toLocalFile()] : false; } break; default: return QVariant(); break; } Q_UNREACHABLE(); } bool BackgroundListModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) { return false; } if (role == PendingDeletionRole) { KPackage::Package b = package(index.row()); if (!b.isValid()) { return false; } const QUrl wallpaperUrl = QUrl::fromLocalFile(b.filePath("preferred")); m_pendingDeletion[wallpaperUrl.toLocalFile()] = value.toBool(); emit dataChanged(index, index); return true; } return false; } void BackgroundListModel::showPreview(const KFileItem &item, const QPixmap &preview) { if (!m_wallpaper) { return; } QPersistentModelIndex index = m_previewJobs.value(item.url()); m_previewJobs.remove(item.url()); if (!index.isValid()) { return; } KPackage::Package b = package(index.row()); if (!b.isValid()) { return; } m_imageCache->insertPixmap(b.filePath("preferred"), preview); - //qDebug() << "WP preview size:" << preview.size(); + //qCDebug(IMAGEWALLPAPER) << "WP preview size:" << preview.size(); emit dataChanged(index, index); } void BackgroundListModel::previewFailed(const KFileItem &item) { m_previewJobs.remove(item.url()); } KPackage::Package BackgroundListModel::package(int index) const { return m_packages.at(index); } void BackgroundListModel::setPendingDeletion(int rowIndex, bool pendingDeletion) { setData(index(rowIndex, 0), pendingDeletion, PendingDeletionRole); } const QStringList BackgroundListModel::wallpapersAwaitingDeletion() { QStringList candidates; for (const KPackage::Package &b : m_packages) { const QUrl wallpaperUrl = QUrl::fromLocalFile(b.filePath("preferred")); if (m_pendingDeletion.contains(wallpaperUrl.toLocalFile()) && m_pendingDeletion[wallpaperUrl.toLocalFile()]) { candidates << wallpaperUrl.toLocalFile(); } } return candidates; } BackgroundFinder::BackgroundFinder(Image *wallpaper, const QStringList &paths) : QThread(wallpaper), m_paths(paths), m_token(QUuid().toString()) { } BackgroundFinder::~BackgroundFinder() { wait(); } QString BackgroundFinder::token() const { return m_token; } const QStringList &BackgroundFinder::suffixes() { if (m_suffixes.isEmpty()) { QSet suffixes; QMimeDatabase db; Q_FOREACH (const QByteArray &mimeType, QImageReader::supportedMimeTypes()) { QMimeType mime(db.mimeTypeForName(mimeType)); Q_FOREACH (const QString &pattern, mime.globPatterns()) { suffixes.insert(pattern); } } m_suffixes = suffixes.toList(); } return m_suffixes; } bool BackgroundFinder::isAcceptableSuffix(const QString &suffix) { // Despite its name, suffixes() returns a list of glob patterns. // Therefore the file suffix check needs to include the "*." prefix. const QStringList &globPatterns = suffixes(); return globPatterns.contains("*."+suffix.toLower()); } void BackgroundFinder::run() { QTime t; t.start(); QStringList papersFound; QDir dir; dir.setFilter(QDir::AllDirs | QDir::Files | QDir::Hidden | QDir::Readable); dir.setNameFilters(suffixes()); KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Images")); int i; for (i = 0; i < m_paths.count(); ++i) { const QString path = m_paths.at(i); dir.setPath(path); const QFileInfoList files = dir.entryInfoList(); Q_FOREACH (const QFileInfo &wp, files) { if (wp.isDir()) { - //qDebug() << "scanning directory" << wp.fileName(); + //qCDebug(IMAGEWALLPAPER) << "scanning directory" << wp.fileName(); const QString name = wp.fileName(); if (name == QString::fromLatin1(".") || name == QString::fromLatin1("..")) { // do nothing continue; } const QString filePath = wp.filePath(); if (QFile::exists(filePath + QString::fromLatin1("/metadata.desktop")) || QFile::exists(filePath + QString::fromLatin1("/metadata.json"))) { package.setPath(filePath); if (package.isValid()) { if (!package.filePath("images").isEmpty()) { papersFound << package.path(); } - //qDebug() << "adding package" << wp.filePath(); + //qCDebug(IMAGEWALLPAPER) << "adding package" << wp.filePath(); continue; } } // add this to the directories we should be looking at m_paths.append(filePath); } else { - //qDebug() << "adding image file" << wp.filePath(); + //qCDebug(IMAGEWALLPAPER) << "adding image file" << wp.filePath(); papersFound << wp.filePath(); } } } - //qDebug() << "WP background found!" << papersFound.size() << "in" << i << "dirs, taking" << t.elapsed() << "ms"; + //qCDebug(IMAGEWALLPAPER) << "WP background found!" << papersFound.size() << "in" << i << "dirs, taking" << t.elapsed() << "ms"; Q_EMIT backgroundsFound(papersFound, m_token); deleteLater(); } #endif // BACKGROUNDLISTMODEL_CPP diff --git a/wallpapers/image/image.cpp b/wallpapers/image/image.cpp index 34b9e1979..beb02c61c 100644 --- a/wallpapers/image/image.cpp +++ b/wallpapers/image/image.cpp @@ -1,840 +1,841 @@ /*************************************************************************** * Copyright 2007 Paolo Capriotti * * Copyright 2007 Aaron Seigo * * Copyright 2008 Petri Damsten * * Copyright 2008 Alexis Ménard * * Copyright 2014 Sebastian Kügler * * Copyright 2015 Kai Uwe Broulik * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "image.h" +#include "debug.h" #include #include // FLT_MAX #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backgroundlistmodel.h" #include Image::Image(QObject *parent) : QObject(parent), m_ready(false), m_delay(10), m_dirWatch(new KDirWatch(this)), m_mode(SingleImage), m_currentSlide(-1), m_model(0), m_dialog(0) { m_wallpaperPackage = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Images")); connect(&m_timer, &QTimer::timeout, this, &Image::nextSlide); connect(m_dirWatch, &KDirWatch::created, this, &Image::pathCreated); connect(m_dirWatch, &KDirWatch::dirty, this, &Image::pathDirty); connect(m_dirWatch, &KDirWatch::deleted, this, &Image::pathDeleted); m_dirWatch->startScan(); useSingleImageDefaults(); setSingleImage(); } Image::~Image() { delete m_dialog; } void Image::classBegin() { } void Image::componentComplete() { // don't bother loading single image until all properties have settled // otherwise we would load a too small image (initial view size) just // to load the proper one afterwards etc etc m_ready = true; if (m_mode == SingleImage) { setSingleImage(); } } QString Image::photosPath() const { return QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); } QUrl Image::wallpaperPath() const { return QUrl::fromLocalFile(m_wallpaperPath); } void Image::addUrl(const QString &url) { addUrl(QUrl(url), true); } void Image::addUrls(const QStringList &urls) { bool first = true; for (const QString &url: urls) { // set the first drop as the current paper, just add the rest to the roll addUrl(QUrl(url), first); first = false; } } Image::RenderingMode Image::renderingMode() const { return m_mode; } void Image::setRenderingMode(RenderingMode mode) { if (mode == m_mode) { return; } m_mode = mode; if (m_mode == SlideShow) { if (m_slidePaths.isEmpty()) { m_slidePaths << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("share/wallpapers"), QStandardPaths::LocateDirectory); } QTimer::singleShot(200, this, &Image::startSlideshow); updateDirWatch(m_slidePaths); updateDirWatch(m_slidePaths); } else { // we need to reset the prefered image setSingleImage(); } } float distance(const QSize& size, const QSize& desired) { // compute difference of areas float desiredAspectRatio = ( desired.height() > 0 ) ? desired.width() / (float)desired.height() : 0; float candidateAspectRatio = ( size.height() > 0 ) ? size.width() / (float)size.height() : FLT_MAX; float delta = size.width() - desired.width(); delta = (delta >= 0.0 ? delta : -delta*2 ); // Penalize for scaling up return qAbs(candidateAspectRatio - desiredAspectRatio)*25000 + delta; } QSize resSize(const QString &str) { int index = str.indexOf('x'); if (index != -1) { return QSize(str.leftRef(index).toInt(), str.midRef(index + 1).toInt()); } return QSize(); } QString Image::findPreferedImage(const QStringList &images) { if (images.empty()) { return QString(); } //float targetAspectRatio = (m_targetSize.height() > 0 ) ? m_targetSize.width() / (float)m_targetSize.height() : 0; - //qDebug() << "wanted" << m_targetSize << "options" << images << "aspect ratio" << targetAspectRatio; + //qCDebug(IMAGEWALLPAPER) << "wanted" << m_targetSize << "options" << images << "aspect ratio" << targetAspectRatio; float best = FLT_MAX; QString bestImage; foreach (const QString &entry, images) { QSize candidate = resSize(QFileInfo(entry).baseName()); if (candidate == QSize()) { continue; } //float candidateAspectRatio = (candidate.height() > 0 ) ? candidate.width() / (float)candidate.height() : FLT_MAX; float dist = distance(candidate, m_targetSize); - //qDebug() << "candidate" << candidate << "distance" << dist << "aspect ratio" << candidateAspectRatio; + //qCDebug(IMAGEWALLPAPER) << "candidate" << candidate << "distance" << dist << "aspect ratio" << candidateAspectRatio; if (bestImage.isEmpty() || dist < best) { bestImage = entry; best = dist; - //qDebug() << "best" << bestImage; + //qCDebug(IMAGEWALLPAPER) << "best" << bestImage; } } - //qDebug() << "best image" << bestImage; + //qCDebug(IMAGEWALLPAPER) << "best image" << bestImage; return bestImage; } void Image::findPreferedImageInPackage(KPackage::Package &package) { if (!package.isValid() || !package.filePath("preferred").isEmpty()) { return; } QString preferred = findPreferedImage( package.entryList("images") ); package.removeDefinition("preferred"); package.addFileDefinition("preferred", "images/" + preferred, i18n("Recommended wallpaper file")); } QSize Image::targetSize() const { return m_targetSize; } void Image::setTargetSize(const QSize &size) { bool sizeChanged = m_targetSize != size; m_targetSize = size; if (m_mode == SingleImage) { if (sizeChanged) { // If screen size was changed, we may want to select a new preferred image // which has correct aspect ratio for the new screen size. m_wallpaperPackage.removeDefinition("preferred"); } setSingleImage(); } if (sizeChanged) { emit targetSizeChanged(); } } KPackage::Package *Image::package() { return &m_wallpaperPackage; } void Image::useSingleImageDefaults() { Plasma::Theme theme; m_wallpaper = theme.wallpaperPath(); int index = m_wallpaper.indexOf(QString::fromLatin1("/contents/images/")); if (index > -1) { // We have file from package -> get path to package m_wallpaper = m_wallpaper.left(index); } } QAbstractItemModel* Image::wallpaperModel() { if (!m_model) { KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("plasmarc")), QStringLiteral("Wallpapers")); m_usersWallpapers = cfg.readEntry("usersWallpapers", m_usersWallpapers); m_model = new BackgroundListModel(this, this); m_model->reload(m_usersWallpapers); } return m_model; } int Image::slideTimer() const { return m_delay; } void Image::setSlideTimer(int time) { if (time == m_delay) { return; } m_delay = time; if (m_mode == SlideShow) { updateDirWatch(m_slidePaths); startSlideshow(); } emit slideTimerChanged(); } QStringList Image::usersWallpapers() const { return m_usersWallpapers; } void Image::setUsersWallpapers(const QStringList &usersWallpapers) { if (usersWallpapers == m_usersWallpapers) { return; } m_usersWallpapers = usersWallpapers; emit usersWallpapersChanged(); } QStringList Image::slidePaths() const { return m_slidePaths; } void Image::setSlidePaths(const QStringList &slidePaths) { if (slidePaths == m_slidePaths) { return; } m_slidePaths = slidePaths; m_slidePaths.removeAll(QString()); if (m_slidePaths.isEmpty()) { m_slidePaths << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("share/wallpapers"), QStandardPaths::LocateDirectory); } if (m_mode == SlideShow) { updateDirWatch(m_slidePaths); startSlideshow(); } emit slidePathsChanged(); } void Image::showAddSlidePathsDialog() { QFileDialog *dialog = new QFileDialog(0, i18n("Directory with the wallpaper to show slides from"), QLatin1String("")); dialog->setAttribute(Qt::WA_DeleteOnClose, true ); dialog->setOptions(QFileDialog::ShowDirsOnly); dialog->setAcceptMode(QFileDialog::AcceptOpen); connect(dialog, &QDialog::accepted, this, &Image::addDirFromSelectionDialog); dialog->show(); } void Image::addSlidePath(const QString &path) { if (!path.isEmpty() && !m_slidePaths.contains(path)) { m_slidePaths.append(path); if (m_mode == SlideShow) { updateDirWatch(m_slidePaths); } emit slidePathsChanged(); startSlideshow(); } } void Image::removeSlidePath(const QString &path) { if (m_slidePaths.contains(path)) { m_slidePaths.removeAll(path); if (m_mode == SlideShow) { updateDirWatch(m_slidePaths); } emit slidePathsChanged(); startSlideshow(); } } void Image::pathDirty(const QString& path) { updateDirWatch(QStringList(path)); } void Image::updateDirWatch(const QStringList &newDirs) { Q_FOREACH(const QString &oldDir, m_dirs) { if(!newDirs.contains(oldDir)) { m_dirWatch->removeDir(oldDir); } } Q_FOREACH(const QString &newDir, newDirs) { if(!m_dirWatch->contains(newDir)) { m_dirWatch->addDir(newDir, KDirWatch::WatchSubDirs | KDirWatch::WatchFiles); } } m_dirs = newDirs; } void Image::addDirFromSelectionDialog() { QFileDialog *dialog = qobject_cast(sender()); if (dialog) { addSlidePath(dialog->directoryUrl().toLocalFile()); } } void Image::syncWallpaperPackage() { m_wallpaperPackage.setPath(m_wallpaper); findPreferedImageInPackage(m_wallpaperPackage); m_wallpaperPath = m_wallpaperPackage.filePath("preferred"); } void Image::setSingleImage() { if (!m_ready) { return; } // supposedly QSize::isEmpty() is true if "either width or height are >= 0" if (!m_targetSize.width() || !m_targetSize.height()) { return; } const QString oldPath = m_wallpaperPath; if (m_wallpaper.isEmpty()) { useSingleImageDefaults(); } QString img; if (QDir::isAbsolutePath(m_wallpaper)) { syncWallpaperPackage(); if (QFile::exists(m_wallpaperPath)) { img = m_wallpaperPath; } } else { //if it's not an absolute path, check if it's just a wallpaper name QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("wallpapers/") + m_wallpaper + QString::fromLatin1("/metadata.json")); if (path.isEmpty()) path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("wallpapers/") + m_wallpaper + QString::fromLatin1("/metadata.desktop")); if (!path.isEmpty()) { QDir dir(path); dir.cdUp(); syncWallpaperPackage(); img = m_wallpaperPath; } } if (img.isEmpty()) { // ok, so the package we have failed to work out; let's try the default useSingleImageDefaults(); syncWallpaperPackage(); } if (m_wallpaperPath != oldPath) { Q_EMIT wallpaperPathChanged(); } } void Image::addUrls(const QList &urls) { bool first = true; Q_FOREACH (const QUrl &url, urls) { // set the first drop as the current paper, just add the rest to the roll addUrl(url, first); first = false; } } void Image::addUrl(const QUrl &url, bool setAsCurrent) { QString path; if (url.isLocalFile()) { path = url.toLocalFile(); } else if (url.scheme().isEmpty()) { if (QDir::isAbsolutePath(url.path())) { path = url.path(); } else { path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("wallpapers/") + url.path(), QStandardPaths::LocateDirectory); } if (path.isEmpty()) { return; } } else { QString wallpaperPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("wallpapers/") + url.path(); if (!wallpaperPath.isEmpty()) { KIO::FileCopyJob *job = KIO::file_copy(url, QUrl(wallpaperPath), -1, KIO::HideProgressInfo); if (setAsCurrent) { connect(job, &KJob::result, this, &Image::setWallpaperRetrieved); } else { connect(job, &KJob::result, this, &Image::addWallpaperRetrieved); } } return; } if (setAsCurrent) { setWallpaper(path); } else { if (m_mode != SingleImage) { // it's a slide show, add it to the slide show m_slideshowBackgrounds.append(path); m_unseenSlideshowBackgrounds.append(path); } // always add it to the user papers, though addUsersWallpaper(path); } } void Image::setWallpaperRetrieved(KJob *job) { KIO::FileCopyJob *copyJob = qobject_cast(job); if (copyJob && !copyJob->error()) { setWallpaper(copyJob->destUrl().toLocalFile()); } } void Image::addWallpaperRetrieved(KJob *job) { KIO::FileCopyJob *copyJob = qobject_cast(job); if (copyJob && !copyJob->error()) { addUrl(copyJob->destUrl(), false); } } void Image::setWallpaper(const QString &path) { if (m_mode == SingleImage) { m_wallpaper = path; setSingleImage(); } else { m_slideshowBackgrounds.append(path); m_unseenSlideshowBackgrounds.clear(); m_currentSlide = m_slideshowBackgrounds.size() - 2; nextSlide(); } //addUsersWallpaper(path); } void Image::startSlideshow() { if(m_findToken.isEmpty()) { // populate background list m_timer.stop(); m_slideshowBackgrounds.clear(); m_unseenSlideshowBackgrounds.clear(); BackgroundFinder *finder = new BackgroundFinder(this, m_dirs); m_findToken = finder->token(); connect(finder, &BackgroundFinder::backgroundsFound, this, &Image::backgroundsFound); finder->start(); //TODO: what would be cool: paint on the wallpaper itself a busy widget and perhaps some text //about loading wallpaper slideshow while the thread runs } else { m_scanDirty = true; } } void Image::backgroundsFound(const QStringList &paths, const QString &token) { if (token != m_findToken) { return; } m_findToken.clear(); if(m_scanDirty) { m_scanDirty = false; startSlideshow(); return; } m_slideshowBackgrounds = paths; m_unseenSlideshowBackgrounds.clear(); // start slideshow if (m_slideshowBackgrounds.isEmpty()) { // no image has been found, which is quite weird... try again later (this is useful for events which // are not detected by KDirWatch, like a NFS directory being mounted) QTimer::singleShot(1000, this, &Image::startSlideshow); } else { m_currentSlide = -1; nextSlide(); m_timer.start(m_delay * 1000); } } void Image::getNewWallpaper() { if (!m_newStuffDialog) { m_newStuffDialog = new KNS3::DownloadDialog( QString::fromLatin1("wallpaper.knsrc") ); KNS3::DownloadDialog *strong = m_newStuffDialog.data(); strong->setTitle(i18n("Download Wallpapers")); connect(m_newStuffDialog.data(), &QDialog::accepted, this, &Image::newStuffFinished); } m_newStuffDialog.data()->show(); } void Image::newStuffFinished() { if (m_model && (!m_newStuffDialog || m_newStuffDialog.data()->changedEntries().size() > 0)) { m_model->reload(m_usersWallpapers); } } void Image::showFileDialog() { if (!m_dialog) { QUrl baseUrl; if(m_wallpaper.indexOf(QDir::homePath()) > -1){ baseUrl = QUrl(m_wallpaper); } /* m_dialog = new KFileDialog(baseUrl, QString::fromLatin1("*.png *.jpeg *.jpg *.xcf *.svg *.svgz *.bmp"), 0); m_dialog->setOperationMode(KFileDialog::Opening); m_dialog->setInlinePreviewShown(true); m_dialog->setModal(false); connect(m_dialog, SIGNAL(okClicked()), this, SLOT(wallpaperBrowseCompleted())); connect(m_dialog, SIGNAL(destroyed(QObject*)), this, SLOT(fileDialogFinished())); */ QString path; const QStringList &locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); if (!locations.isEmpty()) { path = locations.at(0); } else { // HomeLocation is guaranteed not to be empty. path = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).at(0); } QMimeDatabase db; QStringList imageGlobPatterns; foreach(const QByteArray &mimeType, QImageReader::supportedMimeTypes()) { QMimeType mime(db.mimeTypeForName(mimeType)); imageGlobPatterns << mime.globPatterns(); } m_dialog = new QFileDialog(0, i18n("Open Image"), path, i18n("Image Files") + " ("+imageGlobPatterns.join(' ') + ')'); //i18n people, this isn't a "word puzzle". there is a specific string format for QFileDialog::setNameFilters m_dialog->setFileMode(QFileDialog::ExistingFile); connect(m_dialog, &QDialog::accepted, this, &Image::wallpaperBrowseCompleted); } m_dialog->show(); m_dialog->raise(); m_dialog->activateWindow(); } void Image::fileDialogFinished() { m_dialog = 0; } void Image::wallpaperBrowseCompleted() { Q_ASSERT(m_model); if (m_dialog && m_dialog->selectedFiles().count() > 0) { addUsersWallpaper(m_dialog->selectedFiles().first()); emit customWallpaperPicked(); } } void Image::addUsersWallpaper(const QString &file) { QString f = file; f.replace(QLatin1String("file:/"), QLatin1String("")); const QFileInfo info(f); // FIXME //the full file path, so it isn't broken when dealing with symlinks const QString wallpaper = info.canonicalFilePath(); if (wallpaper.isEmpty()) { return; } if (m_model) { if (m_model->contains(wallpaper)) { return; } // add background to the model m_model->addBackground(wallpaper); } // save it KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("plasmarc")), QStringLiteral("Wallpapers")); m_usersWallpapers = cfg.readEntry("usersWallpapers", m_usersWallpapers); if (!m_usersWallpapers.contains(wallpaper)) { m_usersWallpapers.prepend(wallpaper); cfg.writeEntry("usersWallpapers", m_usersWallpapers); cfg.sync(); emit usersWallpapersChanged(); } } void Image::nextSlide() { if (m_slideshowBackgrounds.isEmpty()) { return; } QString previousPath; if (m_currentSlide > -1 && m_currentSlide < m_unseenSlideshowBackgrounds.size()) { previousPath = m_unseenSlideshowBackgrounds.takeAt(m_currentSlide); } if (m_unseenSlideshowBackgrounds.isEmpty()) { m_unseenSlideshowBackgrounds = m_slideshowBackgrounds; // We're filling the queue again, make sure we can't pick up again // the last one picked from the previous set if (!previousPath.isEmpty()) { m_unseenSlideshowBackgrounds.removeAll(previousPath); // prevent empty list if (m_unseenSlideshowBackgrounds.isEmpty()) { m_unseenSlideshowBackgrounds = m_slideshowBackgrounds; } } } m_currentSlide = KRandom::random() % m_unseenSlideshowBackgrounds.size(); const QString currentPath = m_unseenSlideshowBackgrounds.at(m_currentSlide); m_wallpaperPackage.setPath(currentPath); findPreferedImageInPackage(m_wallpaperPackage); m_timer.stop(); m_timer.start(m_delay * 1000); QString current = m_wallpaperPackage.filePath("preferred"); if (current.isEmpty()) { m_wallpaperPath = currentPath; } else { m_wallpaperPath = current; } Q_EMIT wallpaperPathChanged(); } void Image::openSlide() { if (!m_wallpaperPackage.isValid()) { return; } // open in image viewer QUrl filepath(m_wallpaperPackage.filePath("preferred")); - qDebug() << "opening file " << filepath.path(); + qCDebug(IMAGEWALLPAPER) << "opening file " << filepath.path(); new KRun(filepath, NULL); } void Image::pathCreated(const QString &path) { if(!m_slideshowBackgrounds.contains(path)) { QFileInfo fileInfo(path); if(fileInfo.isFile() && BackgroundFinder::isAcceptableSuffix(fileInfo.suffix())) { m_slideshowBackgrounds.append(path); m_unseenSlideshowBackgrounds.append(path); if(m_slideshowBackgrounds.count() == 1) { nextSlide(); } } } } void Image::pathDeleted(const QString &path) { if(m_slideshowBackgrounds.removeAll(path)) { m_unseenSlideshowBackgrounds.removeAll(path); if(path == m_img) { nextSlide(); } } } //FIXME: we have to save the configuration also when the dialog cancel button is clicked. void Image::removeWallpaper(QString name) { QString localWallpapers = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/wallpapers/"; QUrl nameUrl(name); //Package plugin name if (!name.contains('/')) { KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Wallpaper/Images")); KJob *j = p.uninstall(name, localWallpapers); connect(j, &KJob::finished, [=] () { m_model->reload(m_usersWallpapers); }); //absolute path in the home } else if (nameUrl.path().startsWith(localWallpapers)) { QFile f(nameUrl.path()); if (f.exists()) { f.remove(); } m_model->reload(m_usersWallpapers); } else { // save it KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("plasmarc")), QStringLiteral("Wallpapers")); m_usersWallpapers = cfg.readEntry("usersWallpapers", m_usersWallpapers); int wallpaperIndex = -1; //passed as a path or as a file:// url? if (nameUrl.isValid()) { wallpaperIndex = m_usersWallpapers.indexOf(nameUrl.path()); } else { wallpaperIndex = m_usersWallpapers.indexOf(name); } if (wallpaperIndex >= 0){ m_usersWallpapers.removeAt(wallpaperIndex); m_model->reload(m_usersWallpapers); cfg.writeEntry("usersWallpapers", m_usersWallpapers); cfg.sync(); emit usersWallpapersChanged(); Q_EMIT settingsChanged(true); } } } void Image::commitDeletion() { //This is invokable from qml, so at any moment //we can't be sure the model exists if (!m_model) { return; } for (const QString &wallpaperCandidate : m_model->wallpapersAwaitingDeletion()) { removeWallpaper(wallpaperCandidate); } }