File indexing completed on 2024-03-24 05:49:22
0001 // SPDX-FileCopyrightText: 2003 Scott Wheeler <wheeler@kde.org> 0002 // SPDX-FileCopyrightText: 2004 Max Howell <max.howell@methylblue.com> 0003 // SPDX-FileCopyrightText: 2004 Mark Kretschmann <markey@web.de> 0004 // SPDX-FileCopyrightText: 2008 Seb Ruiz <ruiz@kde.org> 0005 // SPDX-FileCopyrightText: 2008 Sebastian Trueg <trueg@kde.org> 0006 // SPDX-FileCopyrightText: 2020 Simon Persson <simon.persson@mykolab.com> 0007 // 0008 // SPDX-License-Identifier: GPL-2.0-only 0009 0010 #include "folderselectionmodel.h" 0011 #include "kuputils.h" 0012 0013 #include <QBrush> 0014 #include <QColor> 0015 #include <QDir> 0016 #include <QFileInfo> 0017 #include <QIcon> 0018 #include <QPalette> 0019 0020 #include <KLocalizedString> 0021 0022 namespace { 0023 0024 bool setContainsSubdir(const QSet<QString> &pSet, const QString &pParentDir) { 0025 // we need the trailing slash to be able to use the startsWith() function to check for parent dirs. 0026 QString lPathWithSlash = pParentDir; 0027 ensureTrailingSlash(lPathWithSlash); 0028 foreach(QString lTestedPath, pSet) { 0029 if(lTestedPath.startsWith(lPathWithSlash)) { 0030 return true; 0031 } 0032 } 0033 return false; 0034 } 0035 0036 } 0037 0038 0039 FolderSelectionModel::FolderSelectionModel(bool pHiddenFoldersVisible, QObject *pParent) 0040 : QFileSystemModel(pParent) 0041 { 0042 setHiddenFoldersVisible(pHiddenFoldersVisible); 0043 } 0044 0045 Qt::ItemFlags FolderSelectionModel::flags(const QModelIndex &pIndex) const { 0046 Qt::ItemFlags lFlags = QFileSystemModel::flags(pIndex); 0047 lFlags |= Qt::ItemIsUserCheckable; 0048 return lFlags; 0049 } 0050 0051 QVariant FolderSelectionModel::data(const QModelIndex& pIndex, int pRole) const { 0052 if(!pIndex.isValid() || pIndex.column() != 0) { 0053 return QFileSystemModel::data(pIndex, pRole); 0054 } 0055 const QString lPath = filePath(pIndex); 0056 const InclusionState lState = inclusionState(lPath); 0057 switch(pRole) { 0058 case Qt::CheckStateRole: { 0059 switch(lState) { 0060 case StateIncluded: 0061 case StateIncludeInherited: 0062 if(setContainsSubdir(mExcludedPaths, lPath)) { 0063 return Qt::PartiallyChecked; 0064 } 0065 return Qt::Checked; 0066 default: 0067 return Qt::Unchecked; 0068 } 0069 } 0070 case IncludeStateRole: 0071 return inclusionState(pIndex); 0072 case Qt::ForegroundRole: { 0073 switch(lState) { 0074 case StateIncluded: 0075 case StateIncludeInherited: 0076 return QVariant::fromValue(QPalette().brush(QPalette::Active, QPalette::Text)); 0077 default: 0078 if(setContainsSubdir(mIncludedPaths, lPath)) { 0079 return QVariant::fromValue(QPalette().brush( QPalette::Active, QPalette::Text)); 0080 } 0081 return QVariant::fromValue(QPalette().brush( QPalette::Disabled, QPalette::Text)); 0082 } 0083 } 0084 case Qt::ToolTipRole: { 0085 switch(lState) { 0086 case StateIncluded: 0087 case StateIncludeInherited: 0088 if(setContainsSubdir(mExcludedPaths, lPath)) { 0089 return xi18nc("@info:tooltip %1 is the path of the folder in a listview", 0090 "<filename>%1</filename><nl/>will be included in the backup, except " 0091 "for unchecked subfolders", filePath(pIndex)); 0092 } 0093 return xi18nc("@info:tooltip %1 is the path of the folder in a listview", 0094 "<filename>%1</filename><nl/>will be included in the backup", filePath(pIndex)); 0095 default: 0096 if(setContainsSubdir(mIncludedPaths, lPath)) { 0097 return xi18nc("@info:tooltip %1 is the path of the folder in a listview", 0098 "<filename>%1</filename><nl/> will <emphasis>not</emphasis> be included " 0099 "in the backup but contains folders that will", filePath(pIndex)); 0100 } 0101 return xi18nc("@info:tooltip %1 is the path of the folder in a listview", 0102 "<filename>%1</filename><nl/> will <emphasis>not</emphasis> be included " 0103 "in the backup", filePath(pIndex)); 0104 } 0105 } 0106 case Qt::DecorationRole: 0107 if(lPath == QDir::homePath()) { 0108 return QIcon::fromTheme(QStringLiteral("user-home")); 0109 } 0110 break; 0111 } 0112 0113 return QFileSystemModel::data(pIndex, pRole); 0114 } 0115 0116 bool FolderSelectionModel::setData(const QModelIndex& pIndex, const QVariant& pValue, int pRole) { 0117 if(!pIndex.isValid() || pIndex.column() != 0 || pRole != Qt::CheckStateRole) { 0118 return QFileSystemModel::setData(pIndex, pValue, pRole); 0119 } 0120 0121 // here we ignore the check value, we treat it as a toggle 0122 // This is due to our using the Qt checking system in a virtual way 0123 const QString lPath = filePath(pIndex); 0124 const InclusionState lState = inclusionState(lPath); 0125 switch(lState) { 0126 case StateIncluded: 0127 case StateIncludeInherited: 0128 excludePath(lPath); 0129 break; 0130 default: 0131 includePath(lPath); 0132 } 0133 QModelIndex lRecurseIndex = pIndex; 0134 while(lRecurseIndex.isValid()) { 0135 emit dataChanged(lRecurseIndex, lRecurseIndex); 0136 lRecurseIndex = lRecurseIndex.parent(); 0137 } 0138 return true; 0139 } 0140 0141 void FolderSelectionModel::includePath(const QString &pPath) { 0142 const InclusionState lState = inclusionState(pPath); 0143 if(lState == StateIncluded) { 0144 return; 0145 } 0146 removeSubDirs(pPath); 0147 if(lState == StateNone || lState == StateExcludeInherited) { 0148 mIncludedPaths.insert(pPath); 0149 emit includedPathAdded(pPath); 0150 } 0151 emit dataChanged(index(pPath), findLastLeaf(index(pPath))); 0152 } 0153 0154 void FolderSelectionModel::excludePath(const QString& pPath) { 0155 const InclusionState lState = inclusionState(pPath); 0156 if(lState == StateExcluded) { 0157 return; 0158 } 0159 removeSubDirs(pPath); 0160 if(lState == StateIncludeInherited) { 0161 mExcludedPaths.insert(pPath); 0162 emit excludedPathAdded(pPath); 0163 } 0164 emit dataChanged(index(pPath), findLastLeaf(index(pPath))); 0165 } 0166 0167 void FolderSelectionModel::setIncludedPaths(const QSet<QString> &pIncludedPaths) { 0168 QSet<QString> lRemoved = mIncludedPaths - pIncludedPaths; 0169 QSet<QString> lAdded = pIncludedPaths - mIncludedPaths; 0170 if(lRemoved.count() + lAdded.count() == 0) return; 0171 0172 beginResetModel(); 0173 mIncludedPaths = pIncludedPaths; 0174 foreach(const QString &lRemovedPath, lRemoved) { 0175 emit includedPathRemoved(lRemovedPath); 0176 } 0177 foreach(const QString &lAddedPath, lAdded) { 0178 emit includedPathAdded(lAddedPath); 0179 } 0180 endResetModel(); 0181 } 0182 0183 void FolderSelectionModel::setExcludedPaths(const QSet<QString> &pExcludedPaths) { 0184 QSet<QString> lRemoved = mExcludedPaths - pExcludedPaths; 0185 QSet<QString> lAdded = pExcludedPaths - mExcludedPaths; 0186 if(lRemoved.count() + lAdded.count() == 0)return; 0187 0188 beginResetModel(); 0189 mExcludedPaths = pExcludedPaths; 0190 foreach(const QString &lRemovedPath, lRemoved) { 0191 emit excludedPathRemoved(lRemovedPath); 0192 } 0193 foreach(const QString &lAddedPath, lAdded) { 0194 emit excludedPathAdded(lAddedPath); 0195 } 0196 endResetModel(); 0197 } 0198 0199 QSet<QString> FolderSelectionModel::includedPaths() const { 0200 return mIncludedPaths; 0201 } 0202 0203 QSet<QString> FolderSelectionModel::excludedPaths() const { 0204 return mExcludedPaths; 0205 } 0206 0207 FolderSelectionModel::InclusionState FolderSelectionModel::inclusionState(const QModelIndex& pIndex) const { 0208 return inclusionState(filePath(pIndex)); 0209 } 0210 0211 FolderSelectionModel::InclusionState FolderSelectionModel::inclusionState(const QString& pPath) const { 0212 if(mIncludedPaths.contains(pPath)) { 0213 return StateIncluded; 0214 } 0215 if(mExcludedPaths.contains(pPath)) { 0216 return StateExcluded; 0217 } 0218 QString lParent = pPath.section(QDir::separator(), 0, -2, QString::SectionSkipEmpty|QString::SectionIncludeLeadingSep); 0219 if(lParent.isEmpty()) { 0220 return StateNone; 0221 } 0222 InclusionState state = inclusionState(lParent); 0223 if(state == StateNone) { 0224 return StateNone; 0225 } 0226 if(state == StateIncluded || state == StateIncludeInherited) { 0227 return StateIncludeInherited; 0228 } 0229 return StateExcludeInherited; 0230 } 0231 0232 bool FolderSelectionModel::hiddenFoldersVisible() const { 0233 return filter() & QDir::Hidden; 0234 } 0235 0236 void FolderSelectionModel::setHiddenFoldersVisible(bool pVisible){ 0237 if(pVisible) { 0238 setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden); 0239 } else { 0240 setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); 0241 } 0242 } 0243 0244 QModelIndex FolderSelectionModel::findLastLeaf(const QModelIndex &pIndex) { 0245 QModelIndex lIndex = pIndex; 0246 forever { 0247 int lRowCount = rowCount(lIndex); 0248 if(lRowCount > 0) { 0249 lIndex = index(lRowCount-1, 0, lIndex); 0250 } else { 0251 return lIndex; 0252 } 0253 } 0254 } 0255 0256 void FolderSelectionModel::removeSubDirs(const QString& pPath) { 0257 QSet<QString>::iterator it = mExcludedPaths.begin(); 0258 QString lPath = pPath + QStringLiteral("/"); 0259 while(it != mExcludedPaths.end()) { 0260 if(*it == pPath || it->startsWith(lPath)) { 0261 QString lPathCopy = *it; 0262 it = mExcludedPaths.erase(it); 0263 emit excludedPathRemoved(lPathCopy); 0264 } else { 0265 ++it; 0266 } 0267 } 0268 it = mIncludedPaths.begin(); 0269 while(it != mIncludedPaths.end()) { 0270 if(*it == pPath || it->startsWith(lPath)) { 0271 QString lPathCopy = *it; 0272 it = mIncludedPaths.erase(it); 0273 emit includedPathRemoved(lPathCopy); 0274 } else { 0275 ++it; 0276 } 0277 } 0278 } 0279