X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/38c34eeca315c7be58e65d4d3fb72aaf7b866719..04e493d78cdf46e64562fe8a302426b1fd8c47df:/src/views/viewproperties.cpp diff --git a/src/views/viewproperties.cpp b/src/views/viewproperties.cpp index f42adbce7..8bf3b2531 100644 --- a/src/views/viewproperties.cpp +++ b/src/views/viewproperties.cpp @@ -12,8 +12,10 @@ #include "dolphindebug.h" #include +#include #include +#include namespace { @@ -31,6 +33,74 @@ const char CustomizedDetailsString[] = "CustomizedDetails"; const char ViewPropertiesFileName[] = ".directory"; } +ViewPropertySettings *ViewProperties::loadProperties(const QString &folderPath) const +{ + const QString settingsFile = folderPath + QDir::separator() + ViewPropertiesFileName; + + KFileMetaData::UserMetaData metadata(folderPath); + if (!metadata.isSupported()) { + return new ViewPropertySettings(KSharedConfig::openConfig(settingsFile, KConfig::SimpleConfig)); + } + + std::unique_ptr tempFile(new QTemporaryFile()); + tempFile->setAutoRemove(false); + if (!tempFile->open()) { + qCWarning(DolphinDebug) << "Could not open temp file"; + return nullptr; + } + if (QFile::exists(settingsFile)) { + // copy settings to tempfile to load them separately + QFile::remove(tempFile->fileName()); + QFile::copy(settingsFile, tempFile->fileName()); + + auto config = KConfig(tempFile->fileName(), KConfig::SimpleConfig); + // ignore settings that are outside of dolphin scope + if (config.hasGroup("Dolphin") || config.hasGroup("Settings")) { + const auto groupList = config.groupList(); + for (const auto &group : groupList) { + if (group != QStringLiteral("Dolphin") && group != QStringLiteral("Settings")) { + config.deleteGroup(group); + } + } + return new ViewPropertySettings(KSharedConfig::openConfig(tempFile->fileName(), KConfig::SimpleConfig)); + + } else if (!config.groupList().isEmpty()) { + // clear temp file content + QFile::remove(tempFile->fileName()); + } + } + + // load from metadata + const QString viewPropertiesString = metadata.attribute(QStringLiteral("kde.fm.viewproperties#1")); + if (viewPropertiesString.isEmpty()) { + return nullptr; + } + // load view properties from xattr to temp file then loads into ViewPropertySettings + QFile outputFile(tempFile->fileName()); + outputFile.open(QIODevice::WriteOnly); + outputFile.write(viewPropertiesString.toUtf8()); + outputFile.close(); + return new ViewPropertySettings(KSharedConfig::openConfig(tempFile->fileName(), KConfig::SimpleConfig)); +} + +ViewPropertySettings *ViewProperties::defaultProperties() const +{ + auto props = loadProperties(destinationDir(QStringLiteral("global"))); + if (props == nullptr) { + qCWarning(DolphinDebug) << "Could not load default global viewproperties"; + QTemporaryFile tempFile; + tempFile.setAutoRemove(false); + if (!tempFile.open()) { + qCWarning(DolphinDebug) << "Could not open temp file"; + props = new ViewPropertySettings; + } else { + props = new ViewPropertySettings(KSharedConfig::openConfig(tempFile.fileName(), KConfig::SimpleConfig)); + } + } + + return props; +} + ViewProperties::ViewProperties(const QUrl &url) : m_changedProps(false) , m_autoSave(true) @@ -46,23 +116,20 @@ ViewProperties::ViewProperties(const QUrl &url) // We try and save it to the file .directory in the directory being viewed. // If the directory is not writable by the user or the directory is not local, // we store the properties information in a local file. - if (useGlobalViewProps) { - m_filePath = destinationDir(QStringLiteral("global")); - } else if (url.scheme().contains(QLatin1String("search"))) { + if (url.scheme().contains(QLatin1String("search"))) { m_filePath = destinationDir(QStringLiteral("search/")) + directoryHashForUrl(url); useSearchView = true; } else if (url.scheme() == QLatin1String("trash")) { m_filePath = destinationDir(QStringLiteral("trash")); useTrashView = true; - } else if (url.scheme() == QLatin1String("recentdocuments")) { - m_filePath = destinationDir(QStringLiteral("recentdocuments")); - useRecentDocumentsView = true; } else if (url.scheme() == QLatin1String("recentlyused")) { m_filePath = destinationDir(QStringLiteral("recentlyused")); useRecentDocumentsView = true; } else if (url.scheme() == QLatin1String("timeline")) { m_filePath = destinationDir(QStringLiteral("timeline")); useRecentDocumentsView = true; + } else if (useGlobalViewProps) { + m_filePath = destinationDir(QStringLiteral("global")); } else if (url.isLocalFile()) { m_filePath = url.toLocalFile(); @@ -93,14 +160,22 @@ ViewProperties::ViewProperties(const QUrl &url) m_filePath = destinationDir(QStringLiteral("remote")) + m_filePath; } - const QString file = m_filePath + QDir::separator() + ViewPropertiesFileName; - m_node = new ViewPropertySettings(KSharedConfig::openConfig(file)); + m_node = loadProperties(m_filePath); - // If the .directory file does not exist or the timestamp is too old, - // use default values instead. - const bool useDefaultProps = (!useGlobalViewProps || useSearchView || useTrashView || useRecentDocumentsView || useDownloadsView) - && (!QFile::exists(file) || (m_node->timestamp() < settings->viewPropsTimestamp())); - if (useDefaultProps) { + bool useDefaultSettings = useGlobalViewProps || + // If the props timestamp is too old, + // use default values instead. + (m_node != nullptr && (!useGlobalViewProps || useSearchView || useTrashView || useRecentDocumentsView || useDownloadsView) + && m_node->timestamp() < settings->viewPropsTimestamp()); + + if (m_node == nullptr) { + // no settings found for m_filepath, load defaults + m_node = defaultProperties(); + useDefaultSettings = true; + } + + // default values for special directories + if (useDefaultSettings) { if (useSearchView) { const QString path = url.path(); @@ -123,24 +198,18 @@ ViewProperties::ViewProperties(const QUrl &url) setViewMode(DolphinView::DetailsView); setVisibleRoles({"text", "path", "deletiontime"}); } else if (useRecentDocumentsView || useDownloadsView) { - setSortRole(QByteArrayLiteral("modificationtime")); setSortOrder(Qt::DescendingOrder); setSortFoldersFirst(false); setGroupedSorting(true); if (useRecentDocumentsView) { + setSortRole(QByteArrayLiteral("accesstime")); setViewMode(DolphinView::DetailsView); - setVisibleRoles({"text", "path", "modificationtime"}); + setVisibleRoles({"text", "path", "accesstime"}); + } else { + setSortRole(QByteArrayLiteral("modificationtime")); } } else { - // The global view-properties act as default for directories without - // any view-property configuration. Constructing a ViewProperties - // instance for an empty QUrl ensures that the global view-properties - // are loaded. - QUrl emptyUrl; - ViewProperties defaultProps(emptyUrl); - setDirProperties(defaultProps); - m_changedProps = false; } } @@ -173,6 +242,11 @@ ViewProperties::~ViewProperties() save(); } + if (!m_node->config()->name().endsWith(ViewPropertiesFileName)) { + // remove temp file + QFile::remove(m_node->config()->name()); + } + delete m_node; m_node = nullptr; } @@ -282,6 +356,19 @@ bool ViewProperties::sortHiddenLast() const return m_node->sortHiddenLast(); } +void ViewProperties::setDynamicViewPassed(bool dynamicViewPassed) +{ + if (m_node->dynamicViewPassed() != dynamicViewPassed) { + m_node->setDynamicViewPassed(dynamicViewPassed); + update(); + } +} + +bool ViewProperties::dynamicViewPassed() const +{ + return m_node->dynamicViewPassed(); +} + void ViewProperties::setVisibleRoles(const QList &roles) { if (roles == visibleRoles()) { @@ -413,17 +500,112 @@ void ViewProperties::update() void ViewProperties::save() { qCDebug(DolphinDebug) << "Saving view-properties to" << m_filePath; + + auto cleanDotDirectoryFile = [this]() { + const QString settingsFile = m_filePath + QDir::separator() + ViewPropertiesFileName; + if (QFile::exists(settingsFile)) { + qCDebug(DolphinDebug) << "cleaning .directory" << settingsFile; + KConfig cfg(settingsFile, KConfig::OpenFlag::SimpleConfig); + const auto groupList = cfg.groupList(); + for (const auto &group : groupList) { + if (group == QStringLiteral("Dolphin") || group == QStringLiteral("Settings")) { + cfg.deleteGroup(group); + } + } + if (cfg.groupList().isEmpty()) { + QFile::remove(settingsFile); + } else if (cfg.isDirty()) { + cfg.sync(); + } + } + }; + + // ensures the destination dir exists, in case we don't write metadata directly on the folder + QDir destinationDir(m_filePath); + if (!destinationDir.exists() && !destinationDir.mkpath(m_filePath)) { + qCWarning(DolphinDebug) << "Could not create fake directory to store metadata"; + } + + KFileMetaData::UserMetaData metaData(m_filePath); + if (metaData.isSupported()) { + const auto metaDataKey = QStringLiteral("kde.fm.viewproperties#1"); + + const auto items = m_node->items(); + const auto defaultConfig = defaultProperties(); + bool allDefault = true; + for (const auto item : items) { + if (item->name() == "Timestamp") { + continue; + } + if (item->name() == "Version") { + if (m_node->version() != CurrentViewPropertiesVersion) { + allDefault = false; + break; + } else { + continue; + } + } + auto defaultItem = defaultConfig->findItem(item->name()); + if (!defaultItem || defaultItem->property() != item->property()) { + allDefault = false; + break; + } + } + + if (allDefault) { + if (metaData.hasAttribute(metaDataKey)) { + qCDebug(DolphinDebug) << "clearing extended attributes for " << m_filePath; + const auto result = metaData.setAttribute(metaDataKey, QString()); + if (result != KFileMetaData::UserMetaData::NoError) { + qCWarning(DolphinDebug) << "could not clear extended attributes for " << m_filePath << "error:" << result; + } + } + cleanDotDirectoryFile(); + return; + } + + // save config to disk + if (!m_node->save()) { + qCWarning(DolphinDebug) << "could not save viewproperties" << m_node->config()->name(); + return; + } + + QFile configFile(m_node->config()->name()); + if (!configFile.open(QIODevice::ReadOnly)) { + qCWarning(DolphinDebug) << "Could not open readonly config file" << m_node->config()->name(); + } else { + // load config from disk + const QString viewPropertiesString = configFile.readAll(); + + // save to xattr + const auto result = metaData.setAttribute(metaDataKey, viewPropertiesString); + if (result != KFileMetaData::UserMetaData::NoError) { + if (result == KFileMetaData::UserMetaData::NoSpace) { + // copy settings to dotDirectory file as fallback + if (!configFile.copy(m_filePath + QDir::separator() + ViewPropertiesFileName)) { + qCWarning(DolphinDebug) << "could not write viewproperties to .directory for dir " << m_filePath; + } + // free the space used by viewproperties from the file metadata + metaData.setAttribute(metaDataKey, ""); + } else { + qCWarning(DolphinDebug) << "could not save viewproperties to extended attributes for dir " << m_filePath << "error:" << result; + } + // keep .directory file + return; + } + cleanDotDirectoryFile(); + } + + m_changedProps = false; + return; + } + QDir dir; dir.mkpath(m_filePath); m_node->setVersion(CurrentViewPropertiesVersion); m_node->save(); - m_changedProps = false; -} -bool ViewProperties::exist() const -{ - const QString file = m_filePath + QDir::separator() + ViewPropertiesFileName; - return QFile::exists(file); + m_changedProps = false; } QString ViewProperties::destinationDir(const QString &subDir) const