diff --git a/kcms/desktoppaths/CMakeLists.txt b/kcms/desktoppaths/CMakeLists.txt --- a/kcms/desktoppaths/CMakeLists.txt +++ b/kcms/desktoppaths/CMakeLists.txt @@ -3,6 +3,7 @@ set(kcm_desktoppaths_PART_SRCS globalpaths.cpp + desktoppathssettings.cpp ) add_library(kcm_desktoppaths MODULE ${kcm_desktoppaths_PART_SRCS}) diff --git a/kcms/desktoppaths/desktoppathssettings.h b/kcms/desktoppaths/desktoppathssettings.h new file mode 100644 --- /dev/null +++ b/kcms/desktoppaths/desktoppathssettings.h @@ -0,0 +1,63 @@ +/** + * Copyright 2020 Kevin Ottens + * + * 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 DESKTOPPATHSSETTINGS_H +#define DESKTOPPATHSSETTINGS_H + +#include + +class PathsSettingsStore; +class XdgPathsSettingsStore; + +class DesktopPathsSettings : public KCoreConfigSkeleton +{ + Q_OBJECT +public: + DesktopPathsSettings(QObject *parent = nullptr); + + QUrl autostartLocation() const; + void setAutostartLocation(const QUrl &url); + + QUrl desktopLocation() const; + void setDesktopLocation(const QUrl &url); + + QUrl documentsLocation() const; + void setDocumentsLocation(const QUrl &url); + + QUrl downloadsLocation() const; + void setDownloadsLocation(const QUrl &url); + + QUrl musicLocation() const; + void setMusicLocation(const QUrl &url); + + QUrl picturesLocation() const; + void setPicturesLocation(const QUrl &url); + + QUrl videosLocation() const; + void setVideosLocation(const QUrl &url); + +private: + bool usrSave() override; + +private: + PathsSettingsStore *m_pathsStore; + XdgPathsSettingsStore *m_xdgPathsStore; +}; + +#endif + diff --git a/kcms/desktoppaths/desktoppathssettings.cpp b/kcms/desktoppaths/desktoppathssettings.cpp new file mode 100644 --- /dev/null +++ b/kcms/desktoppaths/desktoppathssettings.cpp @@ -0,0 +1,316 @@ +/** + * Copyright 2020 Kevin Ottens + * + * 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 "desktoppathssettings.h" + +#include + +namespace { + //save in XDG user-dirs.dirs config file, this is where KGlobalSettings/QDesktopServices reads from. + KSharedConfig::Ptr userDirsConfig() + { + const QString userDirsFilePath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/user-dirs.dirs"); + return KSharedConfig::openConfig(userDirsFilePath, KConfig::SimpleConfig); + } + + QUrl defaultAutostartLocation() + { + return QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/.config/autostart")); + } + + QUrl defaultDesktopLocation() + { + return QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Desktop")); + } + + QUrl defaultDocumentsLocation() + { + return QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Documents")); + } + + QUrl defaultDownloadsLocation() + { + return QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Downloads")); + } + + QUrl defaultMusicLocation() + { + return QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Music")); + } + + QUrl defaultPicturesLocation() + { + return QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Pictures")); + } + + QUrl defaultVideosLocation() + { + return QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Videos")); + } +} + +class PathsSettingsStore : public QObject +{ + Q_OBJECT + Q_PROPERTY(QUrl autostartLocation READ autostartLocation WRITE setAutostartLocation) +public: + PathsSettingsStore(QObject *parent = nullptr) + : QObject(parent) + , m_config(KSharedConfig::openConfig()) + { + } + + QUrl autostartLocation() const + { + return readUrl(QStringLiteral("Autostart"), defaultAutostartLocation()); + } + + void setAutostartLocation(const QUrl &url) + { + if (url.matches(defaultAutostartLocation(), QUrl::StripTrailingSlash)) { + resetUrl(QStringLiteral("Autostart")); + } else { + writeUrl(QStringLiteral("Autostart"), url); + } + } + + void save() + { + if (m_config->isDirty()) { + m_config->sync(); + } + } + +private: + QUrl readUrl(const QString &key, const QUrl &defaultValue) const + { + KConfigGroup group(m_config, QStringLiteral("Paths")); + const auto path = group.readPathEntry(key, QString()); + if (path.isEmpty()) { + return defaultValue; + } else { + return QUrl::fromLocalFile(path); + } + } + + void writeUrl(const QString &key, const QUrl &url) + { + KConfigGroup group(m_config, QStringLiteral("Paths")); + group.writePathEntry(key, url.toLocalFile(), KConfig::Normal | KConfig::Global); + } + + void resetUrl(const QString &key) + { + KConfigGroup group(m_config, QStringLiteral("Paths")); + group.revertToDefault(key, KConfig::Normal | KConfig::Global); + } + + KSharedConfig::Ptr m_config; +}; + +class XdgPathsSettingsStore : public QObject +{ + Q_OBJECT + Q_PROPERTY(QUrl desktopLocation READ desktopLocation WRITE setDesktopLocation) + Q_PROPERTY(QUrl documentsLocation READ documentsLocation WRITE setDocumentsLocation) + Q_PROPERTY(QUrl downloadsLocation READ downloadsLocation WRITE setDownloadsLocation) + Q_PROPERTY(QUrl musicLocation READ musicLocation WRITE setMusicLocation) + Q_PROPERTY(QUrl picturesLocation READ picturesLocation WRITE setPicturesLocation) + Q_PROPERTY(QUrl videosLocation READ videosLocation WRITE setVideosLocation) +public: + XdgPathsSettingsStore(DesktopPathsSettings *parent = nullptr) + : QObject(parent) + , m_settings(parent) + { + } + + QUrl desktopLocation() const + { + return readUrl(QStringLiteral("XDG_DESKTOP_DIR"), defaultDesktopLocation()); + } + + void setDesktopLocation(const QUrl &url) + { + writeUrl(QStringLiteral("XDG_DESKTOP_DIR"), url); + } + + QUrl documentsLocation() const + { + return readUrl(QStringLiteral("XDG_DOCUMENTS_DIR"), defaultDocumentsLocation()); + } + + void setDocumentsLocation(const QUrl &url) + { + writeUrl(QStringLiteral("XDG_DOCUMENTS_DIR"), url); + } + + QUrl downloadsLocation() const + { + return readUrl(QStringLiteral("XDG_DOWNLOAD_DIR"), defaultDownloadsLocation()); + } + + void setDownloadsLocation(const QUrl &url) + { + writeUrl(QStringLiteral("XDG_DOWNLOAD_DIR"), url); + } + + QUrl musicLocation() const + { + return readUrl(QStringLiteral("XDG_MUSIC_DIR"), defaultMusicLocation()); + } + + void setMusicLocation(const QUrl &url) + { + writeUrl(QStringLiteral("XDG_MUSIC_DIR"), url); + } + + QUrl picturesLocation() const + { + return readUrl(QStringLiteral("XDG_PICTURES_DIR"), defaultPicturesLocation()); + } + + void setPicturesLocation(const QUrl &url) + { + writeUrl(QStringLiteral("XDG_PICTURES_DIR"), url); + } + + QUrl videosLocation() const + { + return readUrl(QStringLiteral("XDG_VIDEOS_DIR"), defaultVideosLocation()); + } + + void setVideosLocation(const QUrl &url) + { + writeUrl(QStringLiteral("XDG_VIDEOS_DIR"), url); + } + +private: + QUrl readUrl(const QString &key, const QUrl &defaultValue) const + { + KConfigGroup group(m_settings->config(), QString()); + const auto path = group.readPathEntry(key, QString()); + if (path.isEmpty()) { + return defaultValue; + } else { + return QUrl::fromLocalFile(path.mid(1, path.length() - 2)); + } + } + + void writeUrl(const QString &key, const QUrl &url) + { + KConfigGroup group(m_settings->config(), QString()); + // HACK to benefit from path translation (thus unexpanding $HOME) + group.writePathEntry(key, url.toLocalFile()); + const auto path = group.readEntryUntranslated(key, QString()); + group.writeEntry(key, QString(QStringLiteral("\"") + path + QStringLiteral("\""))); + } + + DesktopPathsSettings *m_settings; +}; + +DesktopPathsSettings::DesktopPathsSettings(QObject *parent) + : KCoreConfigSkeleton(userDirsConfig(), parent) + , m_pathsStore(new PathsSettingsStore(this)) + , m_xdgPathsStore(new XdgPathsSettingsStore(this)) +{ + addItem(new KPropertySkeletonItem(m_xdgPathsStore, "desktopLocation", defaultDesktopLocation()), "desktopLocation"); + addItem(new KPropertySkeletonItem(m_pathsStore, "autostartLocation", defaultAutostartLocation()), "autostartLocation"); + addItem(new KPropertySkeletonItem(m_xdgPathsStore, "documentsLocation", defaultDocumentsLocation()), "documentsLocation"); + addItem(new KPropertySkeletonItem(m_xdgPathsStore, "downloadsLocation", defaultDownloadsLocation()), "downloadsLocation"); + addItem(new KPropertySkeletonItem(m_xdgPathsStore, "musicLocation", defaultMusicLocation()), "musicLocation"); + addItem(new KPropertySkeletonItem(m_xdgPathsStore, "picturesLocation", defaultPicturesLocation()), "picturesLocation"); + addItem(new KPropertySkeletonItem(m_xdgPathsStore, "videosLocation", defaultVideosLocation()), "videosLocation"); +} + +QUrl DesktopPathsSettings::autostartLocation() const +{ + return findItem("autostartLocation")->property().toUrl(); +} + +void DesktopPathsSettings::setAutostartLocation(const QUrl &url) +{ + findItem("autostartLocation")->setProperty(url); +} + +QUrl DesktopPathsSettings::desktopLocation() const +{ + return findItem("desktopLocation")->property().toUrl(); +} + +void DesktopPathsSettings::setDesktopLocation(const QUrl &url) +{ + findItem("desktopLocation")->setProperty(url); +} + +QUrl DesktopPathsSettings::documentsLocation() const +{ + return findItem("documentsLocation")->property().toUrl(); +} + +void DesktopPathsSettings::setDocumentsLocation(const QUrl &url) +{ + findItem("documentsLocation")->setProperty(url); +} + +QUrl DesktopPathsSettings::downloadsLocation() const +{ + return findItem("downloadsLocation")->property().toUrl(); +} + +void DesktopPathsSettings::setDownloadsLocation(const QUrl &url) +{ + findItem("downloadsLocation")->setProperty(url); +} + +QUrl DesktopPathsSettings::musicLocation() const +{ + return findItem("musicLocation")->property().toUrl(); +} + +void DesktopPathsSettings::setMusicLocation(const QUrl &url) +{ + findItem("musicLocation")->setProperty(url); +} + +QUrl DesktopPathsSettings::picturesLocation() const +{ + return findItem("picturesLocation")->property().toUrl(); +} + +void DesktopPathsSettings::setPicturesLocation(const QUrl &url) +{ + findItem("picturesLocation")->setProperty(url); +} + +QUrl DesktopPathsSettings::videosLocation() const +{ + return findItem("videosLocation")->property().toUrl(); +} + +void DesktopPathsSettings::setVideosLocation(const QUrl &url) +{ + findItem("videosLocation")->setProperty(url); +} + +bool DesktopPathsSettings::usrSave() +{ + m_pathsStore->save(); + return KCoreConfigSkeleton::usrSave(); +} + +#include "desktoppathssettings.moc" diff --git a/kcms/desktoppaths/globalpaths.h b/kcms/desktoppaths/globalpaths.h --- a/kcms/desktoppaths/globalpaths.h +++ b/kcms/desktoppaths/globalpaths.h @@ -41,6 +41,8 @@ namespace KIO { class Job; } +class DesktopPathsSettings; + //----------------------------------------------------------------------------- // The "Path" Tab contains : // The paths for Desktop, Autostart and Documents @@ -61,6 +63,8 @@ KUrlRequester* addRow(QFormLayout *lay, const QString& label, const QString& whatsThis); bool xdgSavePath(KUrlRequester* ur, const QUrl& currentUrl, const char* xdgKey, const QString& type); + DesktopPathsSettings *m_pathsSettings; + // Desktop Paths KUrlRequester *urDesktop; KUrlRequester *urAutostart; diff --git a/kcms/desktoppaths/globalpaths.cpp b/kcms/desktoppaths/globalpaths.cpp --- a/kcms/desktoppaths/globalpaths.cpp +++ b/kcms/desktoppaths/globalpaths.cpp @@ -37,6 +37,7 @@ // Own #include "globalpaths.h" +#include "desktoppathssettings.h" // Qt #include @@ -69,43 +70,9 @@ //----------------------------------------------------------------------------- -static QUrl desktopLocation() -{ - return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); -} - -static QUrl autostartLocation() -{ - return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/autostart")); -} - -static QUrl documentsLocation() -{ - return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); -} - -static QUrl downloadLocation() -{ - return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); -} - -static QUrl moviesLocation() -{ - return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation)); -} - -static QUrl picturesLocation() -{ - return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); -} - -static QUrl musicLocation() -{ - return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::MusicLocation)); -} - DesktopPathConfig::DesktopPathConfig(QWidget *parent, const QVariantList &) : KCModule( parent ) + , m_pathsSettings(new DesktopPathsSettings(this)) { QFormLayout *lay = new QFormLayout(this); lay->setVerticalSpacing(0); @@ -163,84 +130,44 @@ void DesktopPathConfig::load() { + m_pathsSettings->load(); + // Desktop Paths - urDesktop->setUrl(desktopLocation()); - urAutostart->setUrl(autostartLocation()); - urDocument->setUrl(documentsLocation()); - urDownload->setUrl(downloadLocation()); - urMovie->setUrl(moviesLocation()); - urPicture->setUrl(picturesLocation()); - urMusic->setUrl(musicLocation()); + urAutostart->setUrl(m_pathsSettings->autostartLocation()); + + urDesktop->setUrl(m_pathsSettings->desktopLocation()); + urDocument->setUrl(m_pathsSettings->documentsLocation()); + urDownload->setUrl(m_pathsSettings->downloadsLocation()); + urMovie->setUrl(m_pathsSettings->videosLocation()); + urPicture->setUrl(m_pathsSettings->picturesLocation()); + urMusic->setUrl(m_pathsSettings->musicLocation()); + emit changed(false); } void DesktopPathConfig::defaults() { // Desktop Paths - keep defaults in sync with kglobalsettings.cpp - urDesktop->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Desktop"))); - urAutostart->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/.config/autostart"))); - urDocument->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Documents"))); - urDownload->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Downloads"))); - urMovie->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Movies"))); - urPicture->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Pictures"))); - urMusic->setUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/Music"))); -} + m_pathsSettings->setDefaults(); + m_pathsSettings->setDefaults(); -// the following method is copied from kdelibs/kdecore/config/kconfiggroup.cpp -static bool cleanHomeDirPath( QString &path, const QString &homeDir ) -{ -#ifdef Q_WS_WIN //safer - if (!QDir::convertSeparators(path).startsWith(QDir::convertSeparators(homeDir))) - return false; -#else - if (!path.startsWith(homeDir)) - return false; -#endif - - int len = homeDir.length(); - // replace by "$HOME" if possible - if (len && (path.length() == len || path[len] == '/')) { - path.replace(0, len, QStringLiteral("$HOME")); - return true; - } else - return false; -} - -// TODO this functionality is duplicated in libkonq - keep it only there and export + urAutostart->setUrl(m_pathsSettings->autostartLocation()); -static QString translatePath( QString path ) // krazy:exclude=passbyvalue -{ - // keep only one single '/' at the beginning - needed for cleanHomeDirPath() - while (path.length() >= 2 && path[0] == '/' && path[1] == '/') - path.remove(0,1); - - // we probably should escape any $ ` and \ characters that may occur in the path, but the Qt code that reads back - // the file doesn't unescape them so not much point in doing so - - // All of the 3 following functions to return the user's home directory - // can return different paths. We have to test all them. - const QString homeDir0 = QFile::decodeName(qgetenv("HOME")); - const QString homeDir1 = QDir::homePath(); - const QString homeDir2 = QDir(homeDir1).canonicalPath(); - if (cleanHomeDirPath(path, homeDir0) || - cleanHomeDirPath(path, homeDir1) || - cleanHomeDirPath(path, homeDir2) ) { - // qCDebug(KCM_DESKTOPPATH) << "Path was replaced\n"; - } - - return path; + urDesktop->setUrl(m_pathsSettings->desktopLocation()); + urDocument->setUrl(m_pathsSettings->documentsLocation()); + urDownload->setUrl(m_pathsSettings->downloadsLocation()); + urMovie->setUrl(m_pathsSettings->videosLocation()); + urPicture->setUrl(m_pathsSettings->picturesLocation()); + urMusic->setUrl(m_pathsSettings->musicLocation()); } void DesktopPathConfig::save() { - KSharedConfig::Ptr config = KSharedConfig::openConfig(); - KConfigGroup configGroup( config, "Paths" ); - bool autostartMoved = false; - QUrl desktopURL(desktopLocation()); + QUrl desktopURL = m_pathsSettings->desktopLocation(); - QUrl autostartURL(autostartLocation()); + QUrl autostartURL = m_pathsSettings->autostartLocation(); QUrl newAutostartURL = urAutostart->url(); if ( !urDesktop->url().matches( desktopURL, QUrl::StripTrailingSlash ) ) @@ -274,37 +201,34 @@ if ( newAutostartURL.matches( futureAutostartURL, QUrl::StripTrailingSlash ) ) autostartMoved = true; else - autostartMoved = moveDir( autostartLocation(), urAutostart->url(), i18n("Autostart") ); + autostartMoved = moveDir( m_pathsSettings->autostartLocation(), urAutostart->url(), i18n("Autostart") ); } } - if ( moveDir( desktopLocation(), QUrl::fromLocalFile( urlDesktop ), i18n("Desktop") ) ) + if ( moveDir( m_pathsSettings->desktopLocation(), QUrl::fromLocalFile( urlDesktop ), i18n("Desktop") ) ) { //save in XDG path - const QString userDirsFile(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/user-dirs.dirs")); - KConfig xdgUserConf( userDirsFile, KConfig::SimpleConfig ); - KConfigGroup g( &xdgUserConf, "" ); - g.writeEntry( "XDG_DESKTOP_DIR", QString("\"" + translatePath( urlDesktop ) + "\"") ); + m_pathsSettings->setDesktopLocation(QUrl::fromLocalFile(urlDesktop)); } } if ( !newAutostartURL.matches( autostartURL, QUrl::StripTrailingSlash ) ) { if (!autostartMoved) - autostartMoved = moveDir( autostartLocation(), urAutostart->url(), i18n("Autostart") ); + autostartMoved = moveDir( m_pathsSettings->autostartLocation(), urAutostart->url(), i18n("Autostart") ); if (autostartMoved) { - configGroup.writePathEntry( "Autostart", urAutostart->url().toLocalFile(), KConfigBase::Normal | KConfigBase::Global ); + m_pathsSettings->setAutostartLocation(urAutostart->url()); } } - config->sync(); + xdgSavePath(urDocument, m_pathsSettings->documentsLocation(), "documentsLocation", i18n("Documents")); + xdgSavePath(urDownload, m_pathsSettings->downloadsLocation(), "downloadsLocation", i18n("Downloads")); + xdgSavePath(urMovie, m_pathsSettings->videosLocation(), "videosLocation", i18n("Movies")); + xdgSavePath(urPicture, m_pathsSettings->picturesLocation(), "picturesLocation", i18n("Pictures")); + xdgSavePath(urMusic, m_pathsSettings->musicLocation(), "musicLocation", i18n("Music")); - xdgSavePath(urDocument, documentsLocation(), "XDG_DOCUMENTS_DIR", i18n("Documents")); - xdgSavePath(urDownload, downloadLocation(), "XDG_DOWNLOAD_DIR", i18n("Downloads")); - xdgSavePath(urMovie, moviesLocation(), "XDG_VIDEOS_DIR", i18n("Movies")); - xdgSavePath(urPicture, picturesLocation(), "XDG_PICTURES_DIR", i18n("Pictures")); - xdgSavePath(urMusic, musicLocation(), "XDG_MUSIC_DIR", i18n("Music")); + m_pathsSettings->save(); } bool DesktopPathConfig::xdgSavePath(KUrlRequester* ur, const QUrl& currentUrl, const char* xdgKey, const QString& type) @@ -327,11 +251,9 @@ } } if (moveDir(currentUrl, newUrl, type)) { - //save in XDG user-dirs.dirs config file, this is where KGlobalSettings/QDesktopServices reads from. - const QString userDirsFile(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/user-dirs.dirs")); - KConfig xdgUserConf(userDirsFile, KConfig::SimpleConfig); - KConfigGroup g(&xdgUserConf, ""); - g.writeEntry(xdgKey, QString("\"" + translatePath(path) + "\"")); + auto item = m_pathsSettings->findItem(xdgKey); + Q_ASSERT(item); + item->setProperty(newUrl); return true; } } @@ -345,8 +267,8 @@ if (!QFile::exists(src.toLocalFile())) return true; // Do not move $HOME! #193057 - const QString translatedPath = translatePath(src.toLocalFile()); - if (translatedPath == QLatin1String("$HOME") || translatedPath == QLatin1String("$HOME/")) { + const QString translatedPath = src.toLocalFile(); + if (translatedPath == QDir::homePath() || translatedPath == QDir::homePath() + QLatin1Char('/')) { return true; }