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);
}
}