2 * SPDX-FileCopyrightText: 2006-2010 Peter Penz <peter.penz19@gmail.com>
3 * SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org>
5 * SPDX-License-Identifier: GPL-2.0-or-later
8 #include "viewproperties.h"
10 #include "dolphin_directoryviewpropertysettings.h"
11 #include "dolphin_generalsettings.h"
12 #include "dolphindebug.h"
14 #include <QCryptographicHash>
15 #include <QTemporaryFile>
18 #include <KFileMetaData/UserMetaData>
22 const int AdditionalInfoViewPropertiesVersion
= 1;
23 const int NameRolePropertiesVersion
= 2;
24 const int DateRolePropertiesVersion
= 4;
25 const int CurrentViewPropertiesVersion
= 4;
27 // String representation to mark the additional properties of
28 // the details view as customized by the user. See
29 // ViewProperties::visibleRoles() for more information.
30 const char CustomizedDetailsString
[] = "CustomizedDetails";
32 // Filename that is used for storing the properties
33 const char ViewPropertiesFileName
[] = ".directory";
36 ViewPropertySettings
*ViewProperties::loadProperties(const QString
&folderPath
) const
38 const QString settingsFile
= folderPath
+ QDir::separator() + ViewPropertiesFileName
;
40 KFileMetaData::UserMetaData
metadata(folderPath
);
41 if (!metadata
.isSupported()) {
42 return new ViewPropertySettings(KSharedConfig::openConfig(settingsFile
, KConfig::SimpleConfig
));
45 std::unique_ptr
<QTemporaryFile
> tempFile(new QTemporaryFile());
46 tempFile
->setAutoRemove(false);
47 if (!tempFile
->open()) {
48 qCWarning(DolphinDebug
) << "Could not open temp file";
51 if (QFile::exists(settingsFile
)) {
52 // copy settings to tempfile to load them separately
53 QFile::remove(tempFile
->fileName());
54 QFile::copy(settingsFile
, tempFile
->fileName());
56 auto config
= KConfig(tempFile
->fileName(), KConfig::SimpleConfig
);
57 // ignore settings that are outside of dolphin scope
58 if (config
.hasGroup("Dolphin") || config
.hasGroup("Settings")) {
59 const auto groupList
= config
.groupList();
60 for (const auto &group
: groupList
) {
61 if (group
!= QStringLiteral("Dolphin") && group
!= QStringLiteral("Settings")) {
62 config
.deleteGroup(group
);
65 return new ViewPropertySettings(KSharedConfig::openConfig(tempFile
->fileName(), KConfig::SimpleConfig
));
67 } else if (!config
.groupList().isEmpty()) {
68 // clear temp file content
69 QFile::remove(tempFile
->fileName());
74 const QString viewPropertiesString
= metadata
.attribute(QStringLiteral("kde.fm.viewproperties#1"));
75 if (viewPropertiesString
.isEmpty()) {
78 // load view properties from xattr to temp file then loads into ViewPropertySettings
79 QFile
outputFile(tempFile
->fileName());
80 outputFile
.open(QIODevice::WriteOnly
);
81 outputFile
.write(viewPropertiesString
.toUtf8());
83 return new ViewPropertySettings(KSharedConfig::openConfig(tempFile
->fileName(), KConfig::SimpleConfig
));
86 ViewPropertySettings
*ViewProperties::defaultProperties() const
88 auto props
= loadProperties(destinationDir(QStringLiteral("global")));
89 if (props
== nullptr) {
90 qCWarning(DolphinDebug
) << "Could not load default global viewproperties";
91 QTemporaryFile tempFile
;
92 tempFile
.setAutoRemove(false);
93 if (!tempFile
.open()) {
94 qCWarning(DolphinDebug
) << "Could not open temp file";
95 props
= new ViewPropertySettings
;
97 props
= new ViewPropertySettings(KSharedConfig::openConfig(tempFile
.fileName(), KConfig::SimpleConfig
));
104 ViewProperties::ViewProperties(const QUrl
&url
)
105 : m_changedProps(false)
109 GeneralSettings
*settings
= GeneralSettings::self();
110 const bool useGlobalViewProps
= settings
->globalViewProps() || url
.isEmpty();
111 bool useSearchView
= false;
112 bool useTrashView
= false;
113 bool useRecentDocumentsView
= false;
114 bool useDownloadsView
= false;
116 // We try and save it to the file .directory in the directory being viewed.
117 // If the directory is not writable by the user or the directory is not local,
118 // we store the properties information in a local file.
119 if (url
.scheme().contains(QLatin1String("search"))) {
120 m_filePath
= destinationDir(QStringLiteral("search/")) + directoryHashForUrl(url
);
121 useSearchView
= true;
122 } else if (url
.scheme() == QLatin1String("trash")) {
123 m_filePath
= destinationDir(QStringLiteral("trash"));
125 } else if (url
.scheme() == QLatin1String("recentlyused")) {
126 m_filePath
= destinationDir(QStringLiteral("recentlyused"));
127 useRecentDocumentsView
= true;
128 } else if (url
.scheme() == QLatin1String("timeline")) {
129 m_filePath
= destinationDir(QStringLiteral("timeline"));
130 useRecentDocumentsView
= true;
131 } else if (useGlobalViewProps
) {
132 m_filePath
= destinationDir(QStringLiteral("global"));
133 } else if (url
.isLocalFile()) {
134 m_filePath
= url
.toLocalFile();
136 bool useDestinationDir
= !isPartOfHome(m_filePath
);
137 if (!useDestinationDir
) {
138 const KFileItem
fileItem(url
);
139 useDestinationDir
= fileItem
.isSlow();
142 if (!useDestinationDir
) {
143 const QFileInfo
dirInfo(m_filePath
);
144 const QFileInfo
fileInfo(m_filePath
+ QDir::separator() + ViewPropertiesFileName
);
145 useDestinationDir
= !dirInfo
.isWritable() || (dirInfo
.size() > 0 && fileInfo
.exists() && !(fileInfo
.isReadable() && fileInfo
.isWritable()));
148 if (useDestinationDir
) {
150 // m_filePath probably begins with C:/ - the colon is not a valid character for paths though
151 m_filePath
= QDir::separator() + m_filePath
.remove(QLatin1Char(':'));
153 m_filePath
= destinationDir(QStringLiteral("local")) + m_filePath
;
156 if (m_filePath
== QStandardPaths::writableLocation(QStandardPaths::DownloadLocation
)) {
157 useDownloadsView
= true;
160 m_filePath
= destinationDir(QStringLiteral("remote")) + m_filePath
;
163 m_node
= loadProperties(m_filePath
);
165 bool useDefaultSettings
= useGlobalViewProps
||
166 // If the props timestamp is too old,
167 // use default values instead.
168 (m_node
!= nullptr && (!useGlobalViewProps
|| useSearchView
|| useTrashView
|| useRecentDocumentsView
|| useDownloadsView
)
169 && m_node
->timestamp() < settings
->viewPropsTimestamp());
171 if (m_node
== nullptr) {
172 // no settings found for m_filepath, load defaults
173 m_node
= defaultProperties();
174 useDefaultSettings
= true;
177 // default values for special directories
178 if (useDefaultSettings
) {
180 const QString path
= url
.path();
182 if (path
== QLatin1String("/images")) {
183 setViewMode(DolphinView::IconsView
);
184 setPreviewsShown(true);
185 setVisibleRoles({"text", "dimensions", "imageDateTime"});
186 } else if (path
== QLatin1String("/audio")) {
187 setViewMode(DolphinView::DetailsView
);
188 setVisibleRoles({"text", "artist", "album", "duration"});
189 } else if (path
== QLatin1String("/videos")) {
190 setViewMode(DolphinView::IconsView
);
191 setPreviewsShown(true);
192 setVisibleRoles({"text"});
194 setViewMode(DolphinView::DetailsView
);
195 setVisibleRoles({"text", "path", "modificationtime"});
197 } else if (useTrashView
) {
198 setViewMode(DolphinView::DetailsView
);
199 setVisibleRoles({"text", "path", "deletiontime"});
200 } else if (useRecentDocumentsView
|| useDownloadsView
) {
201 setSortOrder(Qt::DescendingOrder
);
202 setSortFoldersFirst(false);
203 setGroupedSorting(true);
205 if (useRecentDocumentsView
) {
206 setSortRole(QByteArrayLiteral("accesstime"));
207 setViewMode(DolphinView::DetailsView
);
208 setVisibleRoles({"text", "path", "accesstime"});
210 setSortRole(QByteArrayLiteral("modificationtime"));
213 m_changedProps
= false;
217 if (m_node
->version() < CurrentViewPropertiesVersion
) {
218 // The view-properties have an outdated version. Convert the properties
219 // to the changes of the current version.
220 if (m_node
->version() < AdditionalInfoViewPropertiesVersion
) {
221 convertAdditionalInfo();
222 Q_ASSERT(m_node
->version() == AdditionalInfoViewPropertiesVersion
);
225 if (m_node
->version() < NameRolePropertiesVersion
) {
226 convertNameRoleToTextRole();
227 Q_ASSERT(m_node
->version() == NameRolePropertiesVersion
);
230 if (m_node
->version() < DateRolePropertiesVersion
) {
231 convertDateRoleToModificationTimeRole();
232 Q_ASSERT(m_node
->version() == DateRolePropertiesVersion
);
235 m_node
->setVersion(CurrentViewPropertiesVersion
);
239 ViewProperties::~ViewProperties()
241 if (m_changedProps
&& m_autoSave
) {
245 if (!m_node
->config()->name().endsWith(ViewPropertiesFileName
)) {
247 QFile::remove(m_node
->config()->name());
254 void ViewProperties::setViewMode(DolphinView::Mode mode
)
256 if (m_node
->viewMode() != mode
) {
257 m_node
->setViewMode(mode
);
262 DolphinView::Mode
ViewProperties::viewMode() const
264 const int mode
= qBound(0, m_node
->viewMode(), 2);
265 return static_cast<DolphinView::Mode
>(mode
);
268 void ViewProperties::setPreviewsShown(bool show
)
270 if (m_node
->previewsShown() != show
) {
271 m_node
->setPreviewsShown(show
);
276 bool ViewProperties::previewsShown() const
278 return m_node
->previewsShown();
281 void ViewProperties::setHiddenFilesShown(bool show
)
283 if (m_node
->hiddenFilesShown() != show
) {
284 m_node
->setHiddenFilesShown(show
);
289 void ViewProperties::setGroupedSorting(bool grouped
)
291 if (m_node
->groupedSorting() != grouped
) {
292 m_node
->setGroupedSorting(grouped
);
297 bool ViewProperties::groupedSorting() const
299 return m_node
->groupedSorting();
302 bool ViewProperties::hiddenFilesShown() const
304 return m_node
->hiddenFilesShown();
307 void ViewProperties::setSortRole(const QByteArray
&role
)
309 if (m_node
->sortRole() != role
) {
310 m_node
->setSortRole(role
);
315 QByteArray
ViewProperties::sortRole() const
317 return m_node
->sortRole().toLatin1();
320 void ViewProperties::setSortOrder(Qt::SortOrder sortOrder
)
322 if (m_node
->sortOrder() != sortOrder
) {
323 m_node
->setSortOrder(sortOrder
);
328 Qt::SortOrder
ViewProperties::sortOrder() const
330 return static_cast<Qt::SortOrder
>(m_node
->sortOrder());
333 void ViewProperties::setGroupRole(const QByteArray
&role
)
335 if (m_node
->groupRole() != role
) {
336 m_node
->setGroupRole(role
);
341 QByteArray
ViewProperties::groupRole() const
343 return m_node
->groupRole().toLatin1();
346 void ViewProperties::setGroupOrder(Qt::SortOrder groupOrder
)
348 if (m_node
->groupOrder() != groupOrder
) {
349 m_node
->setGroupOrder(groupOrder
);
354 Qt::SortOrder
ViewProperties::groupOrder() const
356 return static_cast<Qt::SortOrder
>(m_node
->groupOrder());
359 void ViewProperties::setSortFoldersFirst(bool foldersFirst
)
361 if (m_node
->sortFoldersFirst() != foldersFirst
) {
362 m_node
->setSortFoldersFirst(foldersFirst
);
367 bool ViewProperties::sortFoldersFirst() const
369 return m_node
->sortFoldersFirst();
372 void ViewProperties::setSortHiddenLast(bool hiddenLast
)
374 if (m_node
->sortHiddenLast() != hiddenLast
) {
375 m_node
->setSortHiddenLast(hiddenLast
);
380 bool ViewProperties::sortHiddenLast() const
382 return m_node
->sortHiddenLast();
385 void ViewProperties::setDynamicViewPassed(bool dynamicViewPassed
)
387 if (m_node
->dynamicViewPassed() != dynamicViewPassed
) {
388 m_node
->setDynamicViewPassed(dynamicViewPassed
);
393 bool ViewProperties::dynamicViewPassed() const
395 return m_node
->dynamicViewPassed();
398 void ViewProperties::setVisibleRoles(const QList
<QByteArray
> &roles
)
400 if (roles
== visibleRoles()) {
404 // See ViewProperties::visibleRoles() for the storage format
405 // of the additional information.
407 // Remove the old values stored for the current view-mode
408 const QStringList oldVisibleRoles
= m_node
->visibleRoles();
409 const QString prefix
= viewModePrefix();
410 QStringList newVisibleRoles
= oldVisibleRoles
;
411 for (int i
= newVisibleRoles
.count() - 1; i
>= 0; --i
) {
412 if (newVisibleRoles
[i
].startsWith(prefix
)) {
413 newVisibleRoles
.removeAt(i
);
417 // Add the updated values for the current view-mode
418 newVisibleRoles
.reserve(roles
.count());
419 for (const QByteArray
&role
: roles
) {
420 newVisibleRoles
.append(prefix
+ role
);
423 if (oldVisibleRoles
!= newVisibleRoles
) {
424 const bool markCustomizedDetails
= (m_node
->viewMode() == DolphinView::DetailsView
) && !newVisibleRoles
.contains(CustomizedDetailsString
);
425 if (markCustomizedDetails
) {
426 // The additional information of the details-view has been modified. Set a marker,
427 // so that it is allowed to also show no additional information without doing the
428 // fallback to show the size and date per default.
429 newVisibleRoles
.append(CustomizedDetailsString
);
432 m_node
->setVisibleRoles(newVisibleRoles
);
437 QList
<QByteArray
> ViewProperties::visibleRoles() const
439 // The shown additional information is stored for each view-mode separately as
440 // string with the view-mode as prefix. Example:
442 // AdditionalInfo=Details_size,Details_date,Details_owner,Icons_size
444 // To get the representation as QList<QByteArray>, the current
445 // view-mode must be checked and the values of this mode added to the list.
447 // For the details-view a special case must be respected: Per default the size
448 // and date should be shown without creating a .directory file. Only if
449 // the user explicitly has modified the properties of the details view (marked
450 // by "CustomizedDetails"), also a details-view with no additional information
453 QList
<QByteArray
> roles
{"text"};
455 // Iterate through all stored keys and append all roles that match to
456 // the current view mode.
457 const QString prefix
= viewModePrefix();
458 const int prefixLength
= prefix
.length();
460 const QStringList visibleRoles
= m_node
->visibleRoles();
461 for (const QString
&visibleRole
: visibleRoles
) {
462 if (visibleRole
.startsWith(prefix
)) {
463 const QByteArray role
= visibleRole
.right(visibleRole
.length() - prefixLength
).toLatin1();
464 if (role
!= "text") {
470 // For the details view the size and date should be shown per default
471 // until the additional information has been explicitly changed by the user
472 const bool useDefaultValues
= roles
.count() == 1 // "text"
473 && (m_node
->viewMode() == DolphinView::DetailsView
) && !visibleRoles
.contains(CustomizedDetailsString
);
474 if (useDefaultValues
) {
475 roles
.append("size");
476 roles
.append("modificationtime");
482 void ViewProperties::setHeaderColumnWidths(const QList
<int> &widths
)
484 if (m_node
->headerColumnWidths() != widths
) {
485 m_node
->setHeaderColumnWidths(widths
);
490 QList
<int> ViewProperties::headerColumnWidths() const
492 return m_node
->headerColumnWidths();
495 void ViewProperties::setDirProperties(const ViewProperties
&props
)
497 setViewMode(props
.viewMode());
498 setPreviewsShown(props
.previewsShown());
499 setHiddenFilesShown(props
.hiddenFilesShown());
500 setGroupedSorting(props
.groupedSorting());
501 setSortRole(props
.sortRole());
502 setSortOrder(props
.sortOrder());
503 setGroupRole(props
.groupRole());
504 setGroupOrder(props
.groupOrder());
505 setSortFoldersFirst(props
.sortFoldersFirst());
506 setSortHiddenLast(props
.sortHiddenLast());
507 setVisibleRoles(props
.visibleRoles());
508 setHeaderColumnWidths(props
.headerColumnWidths());
509 m_node
->setVersion(props
.m_node
->version());
512 void ViewProperties::setAutoSaveEnabled(bool autoSave
)
514 m_autoSave
= autoSave
;
517 bool ViewProperties::isAutoSaveEnabled() const
522 void ViewProperties::update()
524 m_changedProps
= true;
525 m_node
->setTimestamp(QDateTime::currentDateTime());
528 void ViewProperties::save()
530 qCDebug(DolphinDebug
) << "Saving view-properties to" << m_filePath
;
532 auto cleanDotDirectoryFile
= [this]() {
533 const QString settingsFile
= m_filePath
+ QDir::separator() + ViewPropertiesFileName
;
534 if (QFile::exists(settingsFile
)) {
535 qCDebug(DolphinDebug
) << "cleaning .directory" << settingsFile
;
536 KConfig
cfg(settingsFile
, KConfig::OpenFlag::SimpleConfig
);
537 const auto groupList
= cfg
.groupList();
538 for (const auto &group
: groupList
) {
539 if (group
== QStringLiteral("Dolphin") || group
== QStringLiteral("Settings")) {
540 cfg
.deleteGroup(group
);
543 if (cfg
.groupList().isEmpty()) {
544 QFile::remove(settingsFile
);
545 } else if (cfg
.isDirty()) {
551 // ensures the destination dir exists, in case we don't write metadata directly on the folder
552 QDir
destinationDir(m_filePath
);
553 if (!destinationDir
.exists() && !destinationDir
.mkpath(m_filePath
)) {
554 qCWarning(DolphinDebug
) << "Could not create fake directory to store metadata";
557 KFileMetaData::UserMetaData
metaData(m_filePath
);
558 if (metaData
.isSupported()) {
559 const auto metaDataKey
= QStringLiteral("kde.fm.viewproperties#1");
561 const auto items
= m_node
->items();
562 const auto defaultConfig
= defaultProperties();
563 bool allDefault
= true;
564 for (const auto item
: items
) {
565 if (item
->name() == "Timestamp") {
568 if (item
->name() == "Version") {
569 if (m_node
->version() != CurrentViewPropertiesVersion
) {
576 auto defaultItem
= defaultConfig
->findItem(item
->name());
577 if (!defaultItem
|| defaultItem
->property() != item
->property()) {
584 if (metaData
.hasAttribute(metaDataKey
)) {
585 qCDebug(DolphinDebug
) << "clearing extended attributes for " << m_filePath
;
586 const auto result
= metaData
.setAttribute(metaDataKey
, QString());
587 if (result
!= KFileMetaData::UserMetaData::NoError
) {
588 qCWarning(DolphinDebug
) << "could not clear extended attributes for " << m_filePath
<< "error:" << result
;
591 cleanDotDirectoryFile();
595 // save config to disk
596 if (!m_node
->save()) {
597 qCWarning(DolphinDebug
) << "could not save viewproperties" << m_node
->config()->name();
601 QFile
configFile(m_node
->config()->name());
602 if (!configFile
.open(QIODevice::ReadOnly
)) {
603 qCWarning(DolphinDebug
) << "Could not open readonly config file" << m_node
->config()->name();
605 // load config from disk
606 const QString viewPropertiesString
= configFile
.readAll();
609 const auto result
= metaData
.setAttribute(metaDataKey
, viewPropertiesString
);
610 if (result
!= KFileMetaData::UserMetaData::NoError
) {
611 if (result
== KFileMetaData::UserMetaData::NoSpace
) {
612 // copy settings to dotDirectory file as fallback
613 if (!configFile
.copy(m_filePath
+ QDir::separator() + ViewPropertiesFileName
)) {
614 qCWarning(DolphinDebug
) << "could not write viewproperties to .directory for dir " << m_filePath
;
616 // free the space used by viewproperties from the file metadata
617 metaData
.setAttribute(metaDataKey
, "");
619 qCWarning(DolphinDebug
) << "could not save viewproperties to extended attributes for dir " << m_filePath
<< "error:" << result
;
621 // keep .directory file
624 cleanDotDirectoryFile();
627 m_changedProps
= false;
632 dir
.mkpath(m_filePath
);
633 m_node
->setVersion(CurrentViewPropertiesVersion
);
636 m_changedProps
= false;
639 QString
ViewProperties::destinationDir(const QString
&subDir
) const
641 QString path
= QStandardPaths::writableLocation(QStandardPaths::AppDataLocation
);
642 path
.append("/view_properties/").append(subDir
);
646 QString
ViewProperties::viewModePrefix() const
650 switch (m_node
->viewMode()) {
651 case DolphinView::IconsView
:
652 prefix
= QStringLiteral("Icons_");
654 case DolphinView::CompactView
:
655 prefix
= QStringLiteral("Compact_");
657 case DolphinView::DetailsView
:
658 prefix
= QStringLiteral("Details_");
661 qCWarning(DolphinDebug
) << "Unknown view-mode of the view properties";
667 void ViewProperties::convertAdditionalInfo()
669 QStringList visibleRoles
= m_node
->visibleRoles();
671 const QStringList additionalInfo
= m_node
->additionalInfo();
672 if (!additionalInfo
.isEmpty()) {
673 // Convert the obsolete values like Icons_Size, Details_Date, ...
674 // to Icons_size, Details_date, ... where the suffix just represents
675 // the internal role. One special-case must be handled: "LinkDestination"
676 // has been used for "destination".
677 visibleRoles
.reserve(visibleRoles
.count() + additionalInfo
.count());
678 for (const QString
&info
: additionalInfo
) {
679 QString visibleRole
= info
;
680 int index
= visibleRole
.indexOf('_');
681 if (index
>= 0 && index
+ 1 < visibleRole
.length()) {
683 if (visibleRole
[index
] == QLatin1Char('L')) {
684 visibleRole
.replace(QLatin1String("LinkDestination"), QLatin1String("destination"));
686 visibleRole
[index
] = visibleRole
[index
].toLower();
689 if (!visibleRoles
.contains(visibleRole
)) {
690 visibleRoles
.append(visibleRole
);
695 m_node
->setAdditionalInfo(QStringList());
696 m_node
->setVisibleRoles(visibleRoles
);
697 m_node
->setVersion(AdditionalInfoViewPropertiesVersion
);
701 void ViewProperties::convertNameRoleToTextRole()
703 QStringList visibleRoles
= m_node
->visibleRoles();
704 for (int i
= 0; i
< visibleRoles
.count(); ++i
) {
705 if (visibleRoles
[i
].endsWith(QLatin1String("_name"))) {
706 const int leftLength
= visibleRoles
[i
].length() - 5;
707 visibleRoles
[i
] = visibleRoles
[i
].left(leftLength
) + "_text";
711 QString sortRole
= m_node
->sortRole();
712 if (sortRole
== QLatin1String("name")) {
713 sortRole
= QStringLiteral("text");
716 m_node
->setVisibleRoles(visibleRoles
);
717 m_node
->setSortRole(sortRole
);
718 m_node
->setVersion(NameRolePropertiesVersion
);
722 void ViewProperties::convertDateRoleToModificationTimeRole()
724 QStringList visibleRoles
= m_node
->visibleRoles();
725 for (int i
= 0; i
< visibleRoles
.count(); ++i
) {
726 if (visibleRoles
[i
].endsWith(QLatin1String("_date"))) {
727 const int leftLength
= visibleRoles
[i
].length() - 5;
728 visibleRoles
[i
] = visibleRoles
[i
].left(leftLength
) + "_modificationtime";
732 QString sortRole
= m_node
->sortRole();
733 if (sortRole
== QLatin1String("date")) {
734 sortRole
= QStringLiteral("modificationtime");
737 m_node
->setVisibleRoles(visibleRoles
);
738 m_node
->setSortRole(sortRole
);
739 m_node
->setVersion(DateRolePropertiesVersion
);
743 bool ViewProperties::isPartOfHome(const QString
&filePath
)
745 // For performance reasons cache the path in a static QString
746 // (see QDir::homePath() for more details)
747 static QString homePath
;
748 if (homePath
.isEmpty()) {
749 homePath
= QDir::homePath();
750 Q_ASSERT(!homePath
.isEmpty());
753 return filePath
.startsWith(homePath
);
756 QString
ViewProperties::directoryHashForUrl(const QUrl
&url
)
758 const QByteArray hashValue
= QCryptographicHash::hash(url
.toEncoded(), QCryptographicHash::Sha1
);
759 QString hashString
= hashValue
.toBase64();
760 hashString
.replace('/', '-');