File indexing completed on 2023-12-10 12:47:36
0001 /* 0002 * SPDX-FileCopyrightText: 2017, 2018, 2019 Ivan Cukic <ivan.cukic (at) kde.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "vaultsmodel.h" 0008 #include "vaultsmodel_p.h" 0009 #include <QFileInfo> 0010 0011 using namespace PlasmaVault; 0012 0013 VaultsModel::Private::Private(VaultsModel *parent) 0014 : service("org.kde.kded6", "/modules/plasmavault", QDBusConnection::sessionBus()) 0015 , serviceWatcher("org.kde.kded6", QDBusConnection::sessionBus()) 0016 , q(parent) 0017 { 0018 connect(&service, &org::kde::plasmavault::vaultAdded, this, &Private::onVaultAdded); 0019 connect(&service, &org::kde::plasmavault::vaultChanged, this, &Private::onVaultChanged); 0020 connect(&service, &org::kde::plasmavault::vaultRemoved, this, &Private::onVaultRemoved); 0021 0022 connect(&serviceWatcher, &QDBusServiceWatcher::serviceOwnerChanged, this, [this](const QString &service, const QString &oldOwner, const QString &newOwner) { 0023 Q_UNUSED(oldOwner); 0024 0025 if (service != "org.kde.kded6") { 0026 return; 0027 } 0028 0029 // If kded is not running, just clear all vault info, 0030 // otherwise load the data 0031 if (newOwner.isEmpty()) { 0032 clearData(); 0033 } else { 0034 loadData(); 0035 } 0036 }); 0037 0038 // Try to load the data. This should start kded if it is not running 0039 // for some reason 0040 loadData(); 0041 } 0042 0043 void VaultsModel::Private::loadData() 0044 { 0045 // Before loading the new data, lets forget everything 0046 clearData(); 0047 0048 // Asynchronously load the devices 0049 QDBusPendingReply<VaultInfoList> reply = service.availableDevices(); 0050 auto watcher = new QDBusPendingCallWatcher(reply); 0051 connect(watcher, &QDBusPendingCallWatcher::finished, q, [this, reply, watcher] { 0052 watcher->deleteLater(); 0053 if (reply.isError()) { 0054 return; 0055 } 0056 const VaultInfoList &vaultList = reply.value(); 0057 const int oldSize = vaultKeys.size(); 0058 q->beginResetModel(); 0059 0060 vaults.clear(); 0061 vaultKeys.clear(); 0062 busyVaults.clear(); 0063 errorVaults.clear(); 0064 0065 for (const auto &vault : vaultList) { 0066 vaults[vault.device] = vault; 0067 vaultKeys << vault.device; 0068 0069 if (vault.isBusy()) { 0070 busyVaults << vault.device; 0071 } 0072 0073 if (!vault.message.isEmpty()) { 0074 errorVaults << vault.device; 0075 } 0076 } 0077 0078 q->endResetModel(); 0079 0080 if (vaultKeys.size() != oldSize) { 0081 Q_EMIT q->rowCountChanged(vaultKeys.size()); 0082 } 0083 0084 Q_EMIT q->isBusyChanged(busyVaults.count() != 0); 0085 Q_EMIT q->hasErrorChanged(errorVaults.count() != 0); 0086 }); 0087 } 0088 0089 void VaultsModel::Private::clearData() 0090 { 0091 q->beginResetModel(); 0092 vaultKeys.clear(); 0093 vaults.clear(); 0094 q->endResetModel(); 0095 } 0096 0097 void VaultsModel::Private::onVaultAdded(const PlasmaVault::VaultInfo &vaultInfo) 0098 { 0099 const auto device = vaultInfo.device; 0100 0101 if (vaults.contains(device)) 0102 return; 0103 0104 q->beginInsertRows(QModelIndex(), vaultKeys.size(), vaultKeys.size()); 0105 vaults[device] = vaultInfo; 0106 vaultKeys << device; 0107 q->endInsertRows(); 0108 Q_EMIT q->rowCountChanged(vaultKeys.size()); 0109 } 0110 0111 void VaultsModel::Private::onVaultRemoved(const QString &device) 0112 { 0113 if (!vaults.contains(device)) 0114 return; 0115 0116 const auto row = vaultKeys.indexOf(device); 0117 0118 q->beginRemoveRows(QModelIndex(), row, row); 0119 vaultKeys.removeAt(row); 0120 vaults.remove(device); 0121 q->endRemoveRows(); 0122 Q_EMIT q->rowCountChanged(vaultKeys.size()); 0123 } 0124 0125 void VaultsModel::Private::onVaultChanged(const PlasmaVault::VaultInfo &vaultInfo) 0126 { 0127 const auto device = vaultInfo.device; 0128 0129 if (!vaultKeys.contains(device)) 0130 return; 0131 0132 const auto row = vaultKeys.indexOf(device); 0133 0134 // Lets see whether this warrants updates to the busy flag 0135 if (vaultInfo.isBusy() && !busyVaults.contains(device)) { 0136 busyVaults << device; 0137 if (busyVaults.count() == 1) { 0138 Q_EMIT q->isBusyChanged(true); 0139 } 0140 } 0141 0142 if (!vaultInfo.isBusy() && busyVaults.contains(device)) { 0143 busyVaults.remove(device); 0144 if (busyVaults.count() == 0) { 0145 Q_EMIT q->isBusyChanged(false); 0146 } 0147 } 0148 0149 // Lets see whether this warrants updates to the error flag 0150 if (!vaultInfo.message.isEmpty() && !errorVaults.contains(device)) { 0151 errorVaults << device; 0152 if (errorVaults.count() == 1) { 0153 Q_EMIT q->hasErrorChanged(true); 0154 } 0155 } 0156 0157 if (vaultInfo.message.isEmpty() && errorVaults.contains(device)) { 0158 errorVaults.remove(device); 0159 if (errorVaults.count() == 0) { 0160 Q_EMIT q->hasErrorChanged(false); 0161 } 0162 } 0163 0164 vaults[device] = vaultInfo; 0165 q->dataChanged(q->index(row), q->index(row)); 0166 } 0167 0168 VaultsModel::VaultsModel(QObject *parent) 0169 : QAbstractListModel(parent) 0170 , d(new Private(this)) 0171 { 0172 } 0173 0174 VaultsModel::~VaultsModel() 0175 { 0176 } 0177 0178 int VaultsModel::rowCount(const QModelIndex &parent) const 0179 { 0180 Q_UNUSED(parent); 0181 return d->vaultKeys.size(); 0182 } 0183 0184 QVariant VaultsModel::data(const QModelIndex &index, int role) const 0185 { 0186 if (!index.isValid()) { 0187 return {}; 0188 } 0189 0190 const int row = index.row(); 0191 0192 if (row >= d->vaultKeys.count()) { 0193 return {}; 0194 } 0195 0196 const auto device = d->vaultKeys[row]; 0197 const auto &vault = d->vaults[device]; 0198 0199 switch (role) { 0200 case VaultDevice: 0201 return vault.device; 0202 0203 case VaultMountPoint: 0204 return vault.mountPoint; 0205 0206 case VaultName: 0207 return vault.name.isEmpty() ? vault.device : vault.name; 0208 0209 case VaultIcon: { 0210 switch (vault.status) { 0211 case VaultInfo::Error: 0212 return "document-close"; 0213 0214 case VaultInfo::DeviceMissing: 0215 return "document-close"; 0216 0217 case VaultInfo::NotInitialized: 0218 return "folder-gray"; 0219 0220 case VaultInfo::Closed: 0221 return "folder-encrypted"; 0222 0223 case VaultInfo::Opened: 0224 return "folder-decrypted"; 0225 0226 default: 0227 return ""; 0228 } 0229 } 0230 0231 case VaultIsBusy: 0232 return vault.isBusy(); 0233 0234 case VaultIsOpened: 0235 return vault.isOpened(); 0236 0237 case VaultActivities: 0238 return vault.activities; 0239 0240 case VaultIsOfflineOnly: 0241 return vault.isOfflineOnly; 0242 0243 case VaultMessage: 0244 return vault.message; 0245 0246 case VaultIsEnabled: 0247 return !(vault.status == VaultInfo::Error) // 0248 && !(vault.status == VaultInfo::DeviceMissing); 0249 } 0250 0251 return {}; 0252 } 0253 0254 QHash<int, QByteArray> VaultsModel::roleNames() const 0255 { 0256 return { 0257 {VaultName, "name"}, 0258 {VaultIcon, "icon"}, 0259 {VaultDevice, "device"}, 0260 {VaultMountPoint, "mountPoint"}, 0261 {VaultIsBusy, "isBusy"}, 0262 {VaultIsOpened, "isOpened"}, 0263 {VaultActivities, "activities"}, 0264 {VaultIsOfflineOnly, "isOfflineOnly"}, 0265 {VaultStatus, "status"}, 0266 {VaultMessage, "message"}, 0267 {VaultIsEnabled, "isEnabled"}, 0268 }; 0269 } 0270 0271 void VaultsModel::refresh() 0272 { 0273 d->loadData(); 0274 } 0275 0276 void VaultsModel::reloadDevices() 0277 { 0278 d->service.updateStatus(); 0279 } 0280 0281 void VaultsModel::open(const QString &device) 0282 { 0283 if (!d->vaults.contains(device)) 0284 return; 0285 0286 d->service.openVault(device); 0287 } 0288 0289 void VaultsModel::close(const QString &device) 0290 { 0291 if (!d->vaults.contains(device)) 0292 return; 0293 0294 d->service.closeVault(device); 0295 } 0296 0297 void VaultsModel::toggle(const QString &device) 0298 { 0299 if (!d->vaults.contains(device)) 0300 return; 0301 0302 const auto &vault = d->vaults[device]; 0303 if (vault.status == VaultInfo::Opened) { 0304 close(device); 0305 } else if (vault.status == VaultInfo::Closed) { 0306 open(device); 0307 } 0308 } 0309 0310 void VaultsModel::forceClose(const QString &device) 0311 { 0312 if (!d->vaults.contains(device)) 0313 return; 0314 0315 d->service.forceCloseVault(device); 0316 } 0317 0318 void VaultsModel::configure(const QString &device) 0319 { 0320 if (!d->vaults.contains(device)) 0321 return; 0322 0323 d->service.configureVault(device); 0324 } 0325 0326 void VaultsModel::openInFileManager(const QString &device) 0327 { 0328 if (!d->vaults.contains(device)) 0329 return; 0330 0331 d->service.openVaultInFileManager(device); 0332 } 0333 0334 void VaultsModel::requestNewVault() 0335 { 0336 d->service.requestNewVault(); 0337 } 0338 0339 bool VaultsModel::isBusy() const 0340 { 0341 return !d->busyVaults.isEmpty(); 0342 } 0343 0344 bool VaultsModel::hasError() const 0345 { 0346 return !d->errorVaults.isEmpty(); 0347 } 0348 0349 SortedVaultsModelProxy::SortedVaultsModelProxy(QObject *parent) 0350 : QSortFilterProxyModel(parent) 0351 , m_source(new VaultsModel(this)) 0352 , m_kamd(new KActivities::Consumer(this)) 0353 { 0354 setSourceModel(m_source); 0355 0356 connect(m_kamd, &KActivities::Consumer::currentActivityChanged, this, &SortedVaultsModelProxy::invalidate); 0357 connect(m_kamd, &KActivities::Consumer::serviceStatusChanged, this, &SortedVaultsModelProxy::invalidate); 0358 } 0359 0360 bool SortedVaultsModelProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const 0361 { 0362 const auto leftData = sourceModel()->data(left, VaultsModel::VaultName); 0363 const auto rightData = sourceModel()->data(right, VaultsModel::VaultName); 0364 return QPartialOrdering::Less == QVariant::compare(leftData, rightData); 0365 } 0366 0367 bool SortedVaultsModelProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const 0368 { 0369 Q_UNUSED(sourceParent); 0370 0371 const auto activities = m_source->index(sourceRow).data(VaultsModel::VaultActivities).toStringList(); 0372 0373 const auto isOpened = m_source->index(sourceRow).data(VaultsModel::VaultIsOpened).toBool(); 0374 0375 return activities.size() == 0 || isOpened || activities.contains(m_kamd->currentActivity()); 0376 } 0377 0378 QObject *SortedVaultsModelProxy::actionsModel() const 0379 { 0380 return sourceModel(); 0381 } 0382 0383 void SortedVaultsModelProxy::reloadDevices() 0384 { 0385 static_cast<VaultsModel *>(sourceModel())->reloadDevices(); 0386 } 0387 0388 #include "moc_vaultsmodel_p.cpp" 0389 0390 #include "moc_vaultsmodel.cpp"