Warning, file /plasma/plasma-desktop/solid-device-automounter/kcm/DeviceModel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2009-2010 Trever Fischer <tdfischer@fedoraproject.org> 0003 SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de> 0004 SPDX-FileCopyrightText: 2021 Ismael Asensio <isma.af@gmail.com> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "DeviceModel.h" 0010 0011 #include <QIcon> 0012 #include <QTimer> 0013 0014 #include <KLocalizedString> 0015 #include <Solid/Device> 0016 #include <Solid/DeviceNotifier> 0017 #include <Solid/StorageAccess> 0018 #include <Solid/StorageVolume> 0019 0020 #include "AutomounterSettings.h" 0021 0022 DeviceModel::DeviceModel(AutomounterSettings *m_settings, QObject *parent) 0023 : QAbstractItemModel(parent) 0024 , m_settings(m_settings) 0025 { 0026 reload(); 0027 0028 connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceAdded, this, &DeviceModel::deviceAttached); 0029 connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceRemoved, this, &DeviceModel::deviceRemoved); 0030 } 0031 0032 void DeviceModel::forgetDevice(const QString &udi) 0033 { 0034 if (m_disconnected.contains(udi)) { 0035 const int deviceIndex = m_disconnected.indexOf(udi); 0036 beginRemoveRows(index(RowDetached, 0), deviceIndex, deviceIndex); 0037 m_disconnected.removeOne(udi); 0038 endRemoveRows(); 0039 } else if (m_attached.contains(udi)) { 0040 const int deviceIndex = m_attached.indexOf(udi); 0041 beginRemoveRows(index(RowAttached, 0), deviceIndex, deviceIndex); 0042 m_attached.removeOne(udi); 0043 endRemoveRows(); 0044 } 0045 } 0046 0047 QVariant DeviceModel::headerData(int section, Qt::Orientation orientation, int role) const 0048 { 0049 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { 0050 switch (section) { 0051 case 0: 0052 return i18n("Automount Device"); 0053 case 1: 0054 return i18nc("As in automount on login", "On Login"); 0055 case 2: 0056 return i18nc("As in automount on attach", "On Attach"); 0057 } 0058 } 0059 return QVariant(); 0060 } 0061 0062 void DeviceModel::deviceAttached(const QString &udi) 0063 { 0064 const Solid::Device device(udi); 0065 auto volume = device.as<Solid::StorageVolume>(); 0066 0067 if (volume && !volume->isIgnored()) { 0068 if (m_disconnected.contains(udi)) { 0069 const int deviceIndex = m_disconnected.indexOf(udi); 0070 beginRemoveRows(index(RowDetached, 0), deviceIndex, deviceIndex); 0071 m_disconnected.removeOne(udi); 0072 endRemoveRows(); 0073 } 0074 0075 addNewDevice(udi); 0076 } 0077 } 0078 0079 void DeviceModel::deviceRemoved(const QString &udi) 0080 { 0081 if (m_attached.contains(udi)) { 0082 const int deviceIndex = m_attached.indexOf(udi); 0083 0084 beginRemoveRows(index(RowAttached, 0), deviceIndex, deviceIndex); 0085 m_attached.removeOne(udi); 0086 endRemoveRows(); 0087 0088 // We move the device to the "Disconnected" section only if it 0089 // is a known device, meaning we have some setting for this device. 0090 // Otherwise the device is not moved to the "Disconnected" section 0091 // because we need to check whether the device that just got detached is ignored 0092 // (don't show partition tables and other garbage) but this information 0093 // is no longer available once the device is gone 0094 if (m_settings->knownDevices().contains(udi)) { 0095 beginInsertRows(index(RowDetached, 0), m_disconnected.size(), m_disconnected.size()); 0096 m_disconnected << udi; 0097 endInsertRows(); 0098 } 0099 } 0100 } 0101 0102 void DeviceModel::addNewDevice(const QString &udi) 0103 { 0104 m_settings->load(); 0105 0106 // The kded module might not have updated the settings yet with the new device. 0107 // Let's try again for a limited number of times 0108 static int loadTryouts = 0; 0109 if (!m_settings->hasDeviceInfo(udi)) { 0110 if (loadTryouts < 5) { 0111 loadTryouts++; 0112 QTimer::singleShot(100, this, [this, udi]() { 0113 addNewDevice(udi); 0114 }); 0115 } 0116 return; 0117 } 0118 loadTryouts = 0; 0119 0120 const Solid::Device dev(udi); 0121 if (dev.isValid()) { 0122 if (dev.is<Solid::StorageAccess>()) { 0123 const Solid::StorageAccess *access = dev.as<Solid::StorageAccess>(); 0124 if (!access->isIgnored() || !access->isAccessible()) { 0125 beginInsertRows(index(RowAttached, 0), m_attached.size(), m_attached.size()); 0126 m_attached << udi; 0127 endInsertRows(); 0128 } 0129 } 0130 } else { 0131 beginInsertRows(index(RowDetached, 0), m_disconnected.size(), m_disconnected.size()); 0132 m_disconnected << udi; 0133 endInsertRows(); 0134 } 0135 } 0136 0137 void DeviceModel::reload() 0138 { 0139 beginResetModel(); 0140 m_attached.clear(); 0141 m_disconnected.clear(); 0142 0143 const auto knownDevices = m_settings->knownDevices(); 0144 for (const QString &dev : knownDevices) { 0145 addNewDevice(dev); 0146 } 0147 endResetModel(); 0148 } 0149 0150 QModelIndex DeviceModel::index(int row, int column, const QModelIndex &parent) const 0151 { 0152 if (column < 0 || column >= columnCount()) { 0153 return QModelIndex(); 0154 } 0155 if (parent.isValid()) { 0156 if (parent.column() > 0 || parent.row() == RowAll) { 0157 return QModelIndex(); 0158 } 0159 0160 const int deviceCount = (parent.row() == RowAttached) ? m_attached.size() : m_disconnected.size(); 0161 if (row < deviceCount) { 0162 return createIndex(row, column, parent.row()); 0163 } 0164 } else { 0165 if (row < rowCount()) { 0166 return createIndex(row, column, 3); 0167 } 0168 } 0169 return QModelIndex(); 0170 } 0171 0172 QModelIndex DeviceModel::parent(const QModelIndex &index) const 0173 { 0174 if (index.isValid()) { 0175 if (index.internalId() == 3) 0176 return QModelIndex(); 0177 return createIndex(index.internalId(), 0, 3); 0178 } 0179 return QModelIndex(); 0180 } 0181 0182 Qt::ItemFlags DeviceModel::flags(const QModelIndex &index) const 0183 { 0184 if (!index.isValid()) { 0185 return Qt::NoItemFlags; 0186 } 0187 0188 if (!index.parent().isValid()) { 0189 if (index.row() == RowAll) { 0190 return Qt::ItemIsEnabled | (index.column() > 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags); 0191 } 0192 return (m_settings->automountOnLogin() && m_settings->automountOnPlugin()) ? Qt::NoItemFlags : Qt::ItemIsEnabled; 0193 } 0194 0195 // Select only detached devices to be removed 0196 Qt::ItemFlag selectableFlag = index.parent().row() == RowDetached ? Qt::ItemIsSelectable : Qt::NoItemFlags; 0197 0198 switch (index.column()) { 0199 case 0: 0200 if (m_settings->automountOnLogin() && m_settings->automountOnPlugin()) { 0201 return Qt::NoItemFlags; 0202 } 0203 return selectableFlag | Qt::ItemIsEnabled; 0204 case 1: 0205 return Qt::ItemIsUserCheckable | selectableFlag | (m_settings->automountOnLogin() ? Qt::NoItemFlags : Qt::ItemIsEnabled); 0206 case 2: 0207 return Qt::ItemIsUserCheckable | selectableFlag | (m_settings->automountOnPlugin() ? Qt::NoItemFlags : Qt::ItemIsEnabled); 0208 default: 0209 Q_UNREACHABLE(); 0210 } 0211 } 0212 0213 bool DeviceModel::setData(const QModelIndex &index, const QVariant &value, int role) 0214 { 0215 if (!index.isValid() || role != Qt::CheckStateRole || index.column() == 0) { 0216 return false; 0217 } 0218 0219 if (!index.parent().isValid() && index.row() == RowAll) { 0220 switch (index.column()) { 0221 case 1: 0222 setAutomaticMountOnLogin(value.toInt() == Qt::Checked); 0223 break; 0224 case 2: 0225 setAutomaticMountOnPlugin(value.toInt() == Qt::Checked); 0226 break; 0227 } 0228 Q_EMIT dataChanged(index, index); 0229 return true; 0230 } 0231 0232 const QString &udi = index.data(Qt::UserRole).toString(); 0233 Q_ASSERT(m_settings->hasDeviceInfo(udi)); 0234 0235 switch (index.column()) { 0236 case 1: 0237 m_settings->deviceSettings(udi)->setMountOnLogin(value.toInt() == Qt::Checked); 0238 break; 0239 case 2: 0240 m_settings->deviceSettings(udi)->setMountOnAttach(value.toInt() == Qt::Checked); 0241 break; 0242 } 0243 0244 Q_EMIT dataChanged(index, index); 0245 return true; 0246 } 0247 0248 QVariant DeviceModel::data(const QModelIndex &index, int role) const 0249 { 0250 if (!index.isValid()) { 0251 return QVariant(); 0252 } 0253 0254 if (!index.parent().isValid()) { 0255 if (role == Qt::DisplayRole && index.column() == 0) { 0256 switch (index.row()) { 0257 case RowAll: 0258 return m_settings->automountUnknownDevices() ? i18n("All Devices") : i18n("All Known Devices"); 0259 case RowAttached: 0260 return i18n("Attached Devices"); 0261 case RowDetached: 0262 return i18n("Disconnected Devices"); 0263 } 0264 } 0265 if (role == Qt::CheckStateRole && index.row() == RowAll) { 0266 if (index.column() == 1) { 0267 return m_settings->automountOnLogin() ? Qt::Checked : Qt::Unchecked; 0268 } else if (index.column() == 2) { 0269 return m_settings->automountOnPlugin() ? Qt::Checked : Qt::Unchecked; 0270 } 0271 } 0272 return QVariant(); 0273 } 0274 0275 if (index.parent().row() > RowDetached || index.column() >= columnCount() || index.row() >= rowCount(index.parent())) { 0276 return QVariant(); 0277 } 0278 0279 const bool isAttached = index.parent().row() == RowAttached; 0280 if (role == TypeRole) { 0281 return isAttached ? Attached : Detached; 0282 } 0283 0284 const QString &udi = isAttached ? m_attached.at(index.row()) : m_disconnected.at(index.row()); 0285 if (role == Qt::UserRole) { 0286 return udi; 0287 } 0288 0289 Q_ASSERT(m_settings->hasDeviceInfo(udi)); 0290 0291 if (index.column() == 0) { 0292 if (isAttached) { 0293 Solid::Device dev(udi); 0294 switch (role) { 0295 case Qt::DisplayRole: 0296 return dev.description(); 0297 case Qt::ToolTipRole: 0298 return i18n("UDI: %1", udi); 0299 case Qt::DecorationRole: 0300 return QIcon::fromTheme(dev.icon()); 0301 } 0302 } else { 0303 switch (role) { 0304 case Qt::DisplayRole: 0305 return m_settings->deviceSettings(udi)->name(); 0306 case Qt::ToolTipRole: 0307 return i18n("UDI: %1", udi); 0308 case Qt::DecorationRole: 0309 return QIcon::fromTheme(m_settings->deviceSettings(udi)->icon()); 0310 } 0311 } 0312 } else if (index.column() == 1) { 0313 const bool automount = m_settings->shouldAutomountDevice(udi, AutomounterSettings::Login); 0314 switch (role) { 0315 case Qt::CheckStateRole: 0316 return automount ? Qt::Checked : Qt::Unchecked; 0317 case Qt::ToolTipRole: 0318 return automount ? i18n("This device will be automatically mounted at login.") : i18n("This device will not be automatically mounted at login."); 0319 } 0320 } else if (index.column() == 2) { 0321 const bool automount = m_settings->shouldAutomountDevice(udi, AutomounterSettings::Attach); 0322 switch (role) { 0323 case Qt::CheckStateRole: 0324 return automount ? Qt::Checked : Qt::Unchecked; 0325 case Qt::ToolTipRole: 0326 return automount ? i18n("This device will be automatically mounted when attached.") 0327 : i18n("This device will not be automatically mounted when attached."); 0328 } 0329 } 0330 return QVariant(); 0331 } 0332 0333 int DeviceModel::rowCount(const QModelIndex &parent) const 0334 { 0335 if (!parent.isValid()) { 0336 return 3; 0337 } 0338 if (parent.internalId() < 3 || parent.column() > 0) { 0339 return 0; 0340 } 0341 0342 switch (parent.row()) { 0343 case RowAll: 0344 return 0; 0345 case RowAttached: 0346 return m_attached.size(); 0347 case RowDetached: 0348 return m_disconnected.size(); 0349 } 0350 0351 return 0; 0352 } 0353 0354 int DeviceModel::columnCount(const QModelIndex &parent) const 0355 { 0356 Q_UNUSED(parent) 0357 return 3; 0358 } 0359 0360 void DeviceModel::setAutomaticMountOnLogin(bool automaticLogin) 0361 { 0362 if (m_settings->automountOnLogin() == automaticLogin) { 0363 return; 0364 } 0365 0366 m_settings->setAutomountOnLogin(automaticLogin); 0367 updateCheckedColumns(1); 0368 } 0369 0370 void DeviceModel::setAutomaticMountOnPlugin(bool automaticAttached) 0371 { 0372 if (m_settings->automountOnPlugin() == automaticAttached) { 0373 return; 0374 } 0375 0376 m_settings->setAutomountOnPlugin(automaticAttached); 0377 updateCheckedColumns(2); 0378 } 0379 0380 void DeviceModel::setAutomaticUnknown(bool automaticUnknown) 0381 { 0382 if (m_settings->automountUnknownDevices() == automaticUnknown) { 0383 return; 0384 } 0385 0386 m_settings->setAutomountUnknownDevices(automaticUnknown); 0387 Q_EMIT dataChanged(index(0, 0), index(0, 0), {Qt::DisplayRole}); 0388 updateCheckedColumns(); 0389 } 0390 0391 void DeviceModel::updateCheckedColumns(int column) 0392 { 0393 for (int parent = RowAttached; parent < rowCount(); parent++) { 0394 const auto parentIndex = index(parent, 0); 0395 Q_EMIT dataChanged(index(0, (column > 0 ? column : 1), parentIndex), 0396 index(rowCount(parentIndex), column > 0 ? column : 2, parentIndex), 0397 {Qt::CheckStateRole, Qt::ToolTipRole}); 0398 } 0399 }