File indexing completed on 2024-05-12 17:07:08

0001 /*
0002     This file is part of the KDE Baloo Project
0003     SPDX-FileCopyrightText: 2014 Vishesh Handa <me@vhanda.in>
0004     SPDX-FileCopyrightText: 2019 Tomaz Canabrava <tcanabrava@kde.org>
0005     SPDX-FileCopyrightText: 2020 Benjamin Port <benjamin.port@enioka.com>
0006 
0007     SPDX-License-Identifier: LGPL-2.1-or-later
0008 
0009 */
0010 
0011 #include "filteredfoldermodel.h"
0012 
0013 #include <QIcon>
0014 
0015 #include <KLocalizedString>
0016 #include <QDir>
0017 #include <QStringList>
0018 #include <QTimer>
0019 #include <QUrl>
0020 
0021 #include "baloo/baloosettings.h"
0022 
0023 namespace
0024 {
0025 QString normalizeTrailingSlashes(QString &&input)
0026 {
0027     if (!input.endsWith('/'))
0028         return input + QLatin1Char('/');
0029     return input;
0030 }
0031 
0032 QStringList addTrailingSlashes(QStringList &&list)
0033 {
0034     for (QString &str : list) {
0035         str = normalizeTrailingSlashes(std::move(str));
0036     }
0037     return list;
0038 }
0039 
0040 QString makeHomePretty(const QString &url)
0041 {
0042     if (url.startsWith(QDir::homePath()))
0043         return QString(url).replace(0, QDir::homePath().length(), QStringLiteral("~"));
0044     return url;
0045 }
0046 }
0047 
0048 FilteredFolderModel::FilteredFolderModel(BalooSettings *settings, QObject *parent)
0049     : QAbstractListModel(parent)
0050     , m_settings(settings)
0051 {
0052 }
0053 
0054 void FilteredFolderModel::updateDirectoryList()
0055 {
0056     beginResetModel();
0057 
0058     const QStringList runtimeExcluded = m_runtimeConfig.excludeFolders();
0059 
0060     QStringList settingsIncluded = addTrailingSlashes(m_settings->folders());
0061     QStringList settingsExcluded = addTrailingSlashes(m_settings->excludedFolders());
0062 
0063     const QString homePath = normalizeTrailingSlashes(QDir::homePath());
0064 
0065     auto folderListEntry = [&homePath](const QString &url, bool include, bool fromConfig) {
0066         QString displayName = url;
0067         if (displayName.size() > 1) {
0068             displayName.chop(1);
0069         }
0070         if (displayName.startsWith(homePath)) {
0071             displayName.replace(0, homePath.length(), QStringLiteral("~/"));
0072         }
0073 
0074         QString icon = QStringLiteral("folder");
0075         if (url == homePath) {
0076             icon = QStringLiteral("user-home");
0077         } else if (!fromConfig) {
0078             icon = QStringLiteral("drive-harddisk");
0079         }
0080 
0081         return FolderInfo{url, displayName, icon, include, fromConfig};
0082     };
0083     m_folderList.clear();
0084 
0085     for (const QString &folder : settingsIncluded) {
0086         m_folderList.append(folderListEntry(folder, true, true));
0087     }
0088     for (const QString &folder : settingsExcluded) {
0089         m_folderList.append(folderListEntry(folder, false, true));
0090     }
0091 
0092     // Add any automatically excluded mounts to the list
0093     for (const QString &folder : runtimeExcluded) {
0094         if (settingsIncluded.contains(folder) || settingsExcluded.contains(folder)) {
0095             // Do not add any duplicates
0096             continue;
0097         }
0098         if (m_deletedSettings.contains(folder)) {
0099             // Skip entries deleted from the config
0100             continue;
0101         }
0102         m_folderList.append(folderListEntry(folder, false, false));
0103     }
0104 
0105     std::sort(m_folderList.begin(), m_folderList.end(), [](const FolderInfo &a, const FolderInfo &b) {
0106         return a.url < b.url;
0107     });
0108 
0109     endResetModel();
0110 }
0111 
0112 QVariant FilteredFolderModel::data(const QModelIndex &idx, int role) const
0113 {
0114     if (!idx.isValid() || idx.row() >= m_folderList.size()) {
0115         return {};
0116     }
0117 
0118     const auto entry = m_folderList.at(idx.row());
0119     switch (role) {
0120     case Qt::DisplayRole:
0121         return entry.displayName;
0122     case Qt::WhatsThisRole:
0123         return entry.url;
0124     case Qt::DecorationRole:
0125         return entry.icon;
0126     case Qt::ToolTipRole:
0127         return makeHomePretty(entry.url);
0128     case Url:
0129         return entry.url;
0130     case Folder:
0131         return entry.displayName;
0132     case EnableIndex:
0133         return entry.enableIndex;
0134     case Deletable:
0135         return entry.isFromConfig && entry.url != normalizeTrailingSlashes(QDir::homePath());
0136     default:
0137         return {};
0138     }
0139 }
0140 
0141 bool FilteredFolderModel::setData(const QModelIndex &idx, const QVariant &value, int role)
0142 {
0143     if (!idx.isValid() || idx.row() >= m_folderList.size()) {
0144         return false;
0145     }
0146     FolderInfo &entry = m_folderList[idx.row()];
0147     if (role == EnableIndex) {
0148         entry.enableIndex = value.toBool();
0149         syncFolderConfig(entry);
0150         Q_EMIT dataChanged(idx, idx);
0151         return true;
0152     }
0153     return false;
0154 }
0155 
0156 int FilteredFolderModel::rowCount(const QModelIndex &parent) const
0157 {
0158     Q_UNUSED(parent);
0159     return m_folderList.count();
0160 }
0161 
0162 void FilteredFolderModel::addFolder(const QString &url, const bool included = false)
0163 {
0164     QString nUrl = normalizeTrailingSlashes(QUrl(url).toLocalFile());
0165 
0166     auto it = std::find_if(m_folderList.begin(), m_folderList.end(), [nUrl](const FolderInfo &folder) {
0167         return folder.url == nUrl;
0168     });
0169     if (it != m_folderList.end() && (*it).isFromConfig) {
0170         return;
0171     }
0172     if (included) {
0173         auto included = addTrailingSlashes(m_settings->folders());
0174         included.append(nUrl);
0175         std::sort(std::begin(included), std::end(included));
0176         m_settings->setFolders(included);
0177     } else {
0178         auto excluded = addTrailingSlashes(m_settings->excludedFolders());
0179         excluded.append(nUrl);
0180         std::sort(std::begin(excluded), std::end(excluded));
0181         m_settings->setExcludedFolders(excluded);
0182     }
0183     m_deletedSettings.removeAll(nUrl);
0184 }
0185 
0186 void FilteredFolderModel::removeFolder(int row)
0187 {
0188     auto entry = m_folderList.at(row);
0189     if (!entry.isFromConfig) {
0190         return;
0191     }
0192     if (!entry.enableIndex) {
0193         auto excluded = addTrailingSlashes(m_settings->excludedFolders());
0194         excluded.removeAll(entry.url);
0195         std::sort(std::begin(excluded), std::end(excluded));
0196         m_settings->setExcludedFolders(excluded);
0197     } else {
0198         auto included = addTrailingSlashes(m_settings->folders());
0199         included.removeAll(entry.url);
0200         std::sort(std::begin(included), std::end(included));
0201         m_settings->setFolders(included);
0202     }
0203     m_deletedSettings.append(entry.url);
0204 }
0205 
0206 void FilteredFolderModel::syncFolderConfig(const FolderInfo &entry)
0207 {
0208     auto excluded = addTrailingSlashes(m_settings->excludedFolders());
0209     auto included = addTrailingSlashes(m_settings->folders());
0210     if (entry.enableIndex) {
0211         included.append(entry.url);
0212         std::sort(std::begin(included), std::end(included));
0213         if (excluded.removeAll(entry.url)) {
0214             std::sort(std::begin(excluded), std::end(excluded));
0215             m_settings->setExcludedFolders(excluded);
0216         }
0217         m_settings->setFolders(included);
0218     } else {
0219         excluded.append(entry.url);
0220         std::sort(std::begin(excluded), std::end(excluded));
0221         if (included.removeAll(entry.url)) {
0222             std::sort(std::begin(included), std::end(included));
0223             m_settings->setFolders(included);
0224         }
0225         m_settings->setExcludedFolders(excluded);
0226     }
0227 }
0228 
0229 QHash<int, QByteArray> FilteredFolderModel::roleNames() const
0230 {
0231     return {{Url, "url"}, //
0232             {Folder, "folder"},
0233             {EnableIndex, "enableIndex"},
0234             {Deletable, "deletable"},
0235             {Qt::DecorationRole, "decoration"}};
0236 }