File indexing completed on 2024-05-12 05:35:38

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