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 }