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.
 
 
 
 
 
 

704 lines
20 KiB

/***************************************************************************
* Copyright 2007 Paolo Capriotti <p.capriotti@gmail.com> *
* Copyright 2007 Aaron Seigo <aseigo@kde.org> *
* Copyright 2008 Petri Damsten <damu@iki.fi> *
* Copyright 2008 Alexis Ménard <darktears31@gmail.com> *
* Copyright 2014 Sebastian Kügler <sebas@kde.org> *
* *
* 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 "wallpaperpackage.h"
#include <QAction>
#include <QApplication>
#include <QPainter>
#include <QFile>
#include <QEasingCurve>
#include <QPropertyAnimation>
#include <QTimer>
#include <QDebug>
#include <KDirSelectDialog>
#include <KDirWatch>
#include <QFileDialog>
#include <KRandom>
#include <KStandardDirs>
#include <KIO/Job>
#include <krun.h>
#include <KNewStuff3/KNS3/DownloadDialog>
#include <klocalizedstring.h>
#include <KGlobal>
#include <Plasma/Theme>
#include <qstandardpaths.h>
#include "backgroundlistmodel.h"
Image::Image(QObject *parent)
: QObject(parent),
m_delay(10),
m_dirWatch(new KDirWatch(this)),
m_mode(SingleImage),
m_currentSlide(-1),
m_model(0),
m_dialog(0),
m_width(0),
m_height(0)
{
m_wallpaperPackage = Plasma::Package(new WallpaperPackage(this, this));
connect(&m_timer, SIGNAL(timeout()), this, SLOT(nextSlide()));
connect(m_dirWatch, SIGNAL(created(QString)), SLOT(pathCreated(QString)));
connect(m_dirWatch, SIGNAL(dirty(QString)), SLOT(pathDirty(QString)));
connect(m_dirWatch, SIGNAL(deleted(QString)), SLOT(pathDeleted(QString)));
m_dirWatch->startScan();
connect(this, &Image::sizeChanged, this, &Image::setTargetSize);
useSingleImageDefaults();
setSingleImage();
}
Image::~Image()
{
delete m_dialog;
}
QString Image::wallpaperPath() const
{
return m_wallpaperPath;
}
void Image::addUrl(const QString &url)
{
addUrl(QUrl(url), true);
}
void Image::addUrls(const QStringList &urls)
{
addUrls(urls);
}
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 << KStandardDirs::installPath("wallpaper");
}
QTimer::singleShot(200, this, SLOT(startSlideshow()));
updateDirWatch(m_slidePaths);
updateDirWatch(m_slidePaths);
}
emit renderingModeChanged();
}
QSize Image::targetSize() const
{
return m_targetSize;
}
void Image::setTargetSize(const QSize &size)
{
m_targetSize = size;
}
int Image::height() const
{
return m_height;
}
void Image::setHeight(int h)
{
if (m_height != h) {
m_height = h;
emit sizeChanged(QSize(m_width, m_height));
}
}
int Image::width() const
{
return m_width;
}
void Image::setWidth(int w)
{
if (m_width != w) {
m_width = w;
emit sizeChanged(QSize(m_width, m_height));
}
}
Plasma::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;
if (m_slidePaths.isEmpty()) {
m_slidePaths << KStandardDirs::installPath("wallpaper");
}
if (m_mode == SlideShow) {
updateDirWatch(m_slidePaths);
startSlideshow();
}
emit slidePathsChanged();
}
void Image::showAddSlidePathsDialog()
{
QUrl empty;
KDirSelectDialog *dialog = new KDirSelectDialog(empty, true);
connect(dialog, SIGNAL(accepted()), this, SLOT(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 (!path.isEmpty() && 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()
{
KDirSelectDialog *dialog = qobject_cast<KDirSelectDialog *>(sender());
if (dialog) {
addSlidePath(dialog->url().path());
}
}
void Image::setSingleImage()
{
if (m_wallpaper.isEmpty()) {
useSingleImageDefaults();
}
QString img;
if (QDir::isAbsolutePath(m_wallpaper)) {
m_wallpaperPackage.setPath(m_wallpaper);
img = m_wallpaperPackage.filePath("preferred");
m_wallpaperPath = m_wallpaperPackage.filePath("preferred");
if (img.isEmpty() && QFile::exists(m_wallpaper)) {
img = m_wallpaper;
m_wallpaperPath = m_wallpaper;
}
Q_EMIT wallpaperPathChanged();
} else {
//if it's not an absolute path, check if it's just a wallpaper name
const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("wallpapers/") + QString(m_wallpaper + QString::fromLatin1("/metadata.desktop")));
if (!path.isEmpty()) {
QDir dir(path);
dir.cdUp();
m_wallpaperPackage.setPath(m_wallpaper);
img = m_wallpaperPackage.filePath("preferred");
m_wallpaperPath = m_wallpaperPackage.filePath("preferred");
Q_EMIT wallpaperPathChanged();
}
}
if (img.isEmpty()) {
// ok, so the package we have failed to work out; let's try the default
// if we have already
const QString wallpaper = m_wallpaper;
useSingleImageDefaults();
if (wallpaper != m_wallpaper) {
setSingleImage();
}
}
}
void Image::addUrls(const QList<QUrl> &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)
{
///qDebug() << "droppage!" << url << url.isLocalFile();
if (url.isLocalFile()) {
const QString path = url.toLocalFile();
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);
}
} 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, SIGNAL(result(KJob*)), this, SLOT(setWallpaperRetrieved(KJob*)));
} else {
connect(job, SIGNAL(result(KJob*)), this, SLOT(addWallpaperRetrieved(KJob*)));
}
}
}
}
void Image::setWallpaperRetrieved(KJob *job)
{
KIO::FileCopyJob *copyJob = qobject_cast<KIO::FileCopyJob *>(job);
if (copyJob && !copyJob->error()) {
setWallpaper(copyJob->destUrl().toLocalFile());
}
}
void Image::addWallpaperRetrieved(KJob *job)
{
KIO::FileCopyJob *copyJob = qobject_cast<KIO::FileCopyJob *>(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, SIGNAL(backgroundsFound(QStringList,QString)), this, SLOT(backgroundsFound(QStringList,QString)));
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, SLOT(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") );
connect(m_newStuffDialog.data(), SIGNAL(accepted()), SLOT(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);
}
m_dialog = new QFileDialog(0, i18n("Open Image"),
path,
i18n("Image Files (*.png *.jpg *.jpeg *.bmp *.svg *.svgz *.xcf)"));
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) {
foreach (const QString &file, m_dialog->selectedFiles()) {
addUsersWallpaper(m_dialog->selectedFiles()[0]); // FIXME
}
emit customWallpaperPicked();
}
}
void Image::addUsersWallpaper(const QString &file)
{
QString f = file;
f.replace("file:/", "");
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);
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();
new KRun(filepath, NULL);
}
void Image::pathCreated(const QString &path)
{
if(!m_slideshowBackgrounds.contains(path)) {
QFileInfo fileInfo(path);
if(fileInfo.isFile() && BackgroundFinder::suffixes().contains(fileInfo.suffix().toLower())) {
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('/')) {
Plasma::Package p = Plasma::Package(new WallpaperPackage(this, this));
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);
}
}
}
#include "image.moc"