File indexing completed on 2025-01-19 03:53:53
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2008-04-07 0007 * Description : Solid hardware list dialog 0008 * 0009 * SPDX-FileCopyrightText: 2008-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "solidhardwaredlg.h" 0016 0017 // Qt includes 0018 0019 #include <QStringList> 0020 #include <QDateTime> 0021 #include <QString> 0022 #include <QLabel> 0023 #include <QLayout> 0024 #include <QGridLayout> 0025 #include <QTreeWidget> 0026 #include <QHeaderView> 0027 #include <QMimeData> 0028 #include <QClipboard> 0029 #include <QApplication> 0030 #include <QPushButton> 0031 #include <QDialogButtonBox> 0032 0033 // KDE includes 0034 0035 #include <klocalizedstring.h> 0036 0037 #if defined(Q_CC_CLANG) 0038 # pragma clang diagnostic push 0039 # pragma clang diagnostic ignored "-Wnonportable-include-path" 0040 #endif 0041 0042 #include <solid_version.h> 0043 #include <solid/solidnamespace.h> 0044 #include <solid/camera.h> 0045 #include <solid/device.h> 0046 #include <solid/deviceinterface.h> 0047 #include <solid/devicenotifier.h> 0048 #include <solid/genericinterface.h> 0049 0050 #if defined(Q_CC_CLANG) 0051 # pragma clang diagnostic pop 0052 #endif 0053 0054 // Local includes 0055 0056 #include "digikam_config.h" 0057 0058 using namespace std; 0059 0060 namespace Digikam 0061 { 0062 0063 class Q_DECL_HIDDEN SolidHardwareDlg::Private 0064 { 0065 public: 0066 0067 explicit Private() 0068 : header (nullptr), 0069 searchBar (nullptr), 0070 eventsList(nullptr) 0071 { 0072 } 0073 0074 QLabel* header; 0075 SearchTextBar* searchBar; 0076 QTreeWidget* eventsList; 0077 }; 0078 0079 SolidHardwareDlg::SolidHardwareDlg(QWidget* const parent) 0080 : InfoDlg(parent), 0081 d (new Private) 0082 { 0083 setWindowTitle(i18nc("@title:window", "List of Detected Hardware")); 0084 0085 d->header = new QLabel(this); 0086 d->header->setText(i18nc("@label", "%1 uses Solid framework version %2\n" 0087 "to detect and manage devices from your computer.\n" 0088 "Press \"Refresh\" to update list if you plug a removable device.", 0089 QApplication::applicationName(), 0090 QLatin1String(SOLID_VERSION_STRING))); 0091 d->searchBar = new SearchTextBar(this, QLatin1String("SolidHardwareDlgSearchBar")); 0092 0093 listView()->setHeaderLabels(QStringList() << QLatin1String("Properties") << QLatin1String("Value")); // Hidden header -> no i18n 0094 listView()->setSortingEnabled(true); 0095 listView()->setRootIsDecorated(true); 0096 listView()->setSelectionMode(QAbstractItemView::SingleSelection); 0097 listView()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0098 listView()->setAllColumnsShowFocus(true); 0099 listView()->setColumnCount(2); 0100 listView()->header()->setSectionResizeMode(QHeaderView::Stretch); 0101 listView()->header()->hide(); 0102 0103 // -------------------------------------------------------- 0104 0105 d->eventsList = new QTreeWidget(this); 0106 d->eventsList->setSortingEnabled(true); 0107 d->eventsList->setRootIsDecorated(true); 0108 d->eventsList->setSelectionMode(QAbstractItemView::SingleSelection); 0109 d->eventsList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0110 d->eventsList->setAllColumnsShowFocus(true); 0111 d->eventsList->setColumnCount(2); 0112 d->eventsList->sortItems(0, Qt::AscendingOrder); 0113 d->eventsList->header()->setSectionResizeMode(QHeaderView::Stretch); 0114 d->eventsList->header()->hide(); 0115 0116 tabView()->addTab(d->eventsList, i18nc("@title", "Hotplug Device Events")); 0117 tabView()->setTabText(0, i18nc("@title", "Devices List")); 0118 0119 Solid::DeviceNotifier* const notifier = Solid::DeviceNotifier::instance(); 0120 0121 connect(notifier, SIGNAL(deviceAdded(QString)), 0122 this, SLOT(slotDeviceAdded(QString))); 0123 0124 connect(notifier, SIGNAL(deviceRemoved(QString)), 0125 this, SLOT(slotDeviceRemoved(QString))); 0126 0127 // -------------------------------------------------------- 0128 0129 buttonBox()->addButton(QDialogButtonBox::Reset); 0130 buttonBox()->button(QDialogButtonBox::Reset)->setText(i18nc("@action", "Refresh")); 0131 0132 // -------------------------------------------------------- 0133 0134 QGridLayout* const grid = dynamic_cast<QGridLayout*>(mainWidget()->layout()); 0135 grid->addWidget(d->header, 1, 0, 1, -1); 0136 grid->addWidget(d->searchBar, 3, 0, 1, -1); 0137 0138 // -------------------------------------------------------- 0139 0140 connect(d->searchBar, SIGNAL(signalSearchTextSettings(SearchTextSettings)), 0141 this, SLOT(slotSearchTextChanged(SearchTextSettings))); 0142 0143 connect(buttonBox()->button(QDialogButtonBox::Reset), SIGNAL(clicked()), 0144 this, SLOT(slotPopulateDevices())); 0145 0146 // -------------------------------------------------------- 0147 0148 slotPopulateDevices(); 0149 } 0150 0151 SolidHardwareDlg::~SolidHardwareDlg() 0152 { 0153 delete d; 0154 } 0155 0156 void SolidHardwareDlg::slotPopulateDevices() 0157 { 0158 listView()->clear(); 0159 0160 const QList<Solid::Device> all = Solid::Device::allDevices(); 0161 0162 for (const Solid::Device& device : all) 0163 { 0164 QString typeStr; 0165 QString typeDesc; 0166 0167 if (device.isDeviceInterface(Solid::DeviceInterface::StorageDrive)) 0168 { 0169 typeStr = Solid::DeviceInterface::typeToString(Solid::DeviceInterface::StorageDrive); 0170 typeDesc = Solid::DeviceInterface::typeDescription(Solid::DeviceInterface::StorageDrive); 0171 } 0172 else if (device.isDeviceInterface(Solid::DeviceInterface::StorageAccess)) 0173 { 0174 typeStr = Solid::DeviceInterface::typeToString(Solid::DeviceInterface::StorageAccess); 0175 typeDesc = Solid::DeviceInterface::typeDescription(Solid::DeviceInterface::StorageAccess); 0176 } 0177 else if (device.isDeviceInterface(Solid::DeviceInterface::StorageVolume)) 0178 { 0179 typeStr = Solid::DeviceInterface::typeToString(Solid::DeviceInterface::StorageVolume); 0180 typeDesc = Solid::DeviceInterface::typeDescription(Solid::DeviceInterface::StorageVolume); 0181 } 0182 else if (device.isDeviceInterface(Solid::DeviceInterface::OpticalDrive)) 0183 { 0184 typeStr = Solid::DeviceInterface::typeToString(Solid::DeviceInterface::OpticalDrive); 0185 typeDesc = Solid::DeviceInterface::typeDescription(Solid::DeviceInterface::OpticalDrive); 0186 } 0187 else if (device.isDeviceInterface(Solid::DeviceInterface::OpticalDisc)) 0188 { 0189 typeStr = Solid::DeviceInterface::typeToString(Solid::DeviceInterface::OpticalDisc); 0190 typeDesc = Solid::DeviceInterface::typeDescription(Solid::DeviceInterface::OpticalDisc); 0191 } 0192 else if (device.isDeviceInterface(Solid::DeviceInterface::Camera)) 0193 { 0194 typeStr = Solid::DeviceInterface::typeToString(Solid::DeviceInterface::Camera); 0195 typeDesc = Solid::DeviceInterface::typeDescription(Solid::DeviceInterface::Camera); 0196 } 0197 else if (device.isDeviceInterface(Solid::DeviceInterface::Processor)) 0198 { 0199 typeStr = Solid::DeviceInterface::typeToString(Solid::DeviceInterface::Processor); 0200 typeDesc = Solid::DeviceInterface::typeDescription(Solid::DeviceInterface::Processor); 0201 } 0202 else if (device.isDeviceInterface(Solid::DeviceInterface::Block)) 0203 { 0204 typeStr = Solid::DeviceInterface::typeToString(Solid::DeviceInterface::Block); 0205 typeDesc = Solid::DeviceInterface::typeDescription(Solid::DeviceInterface::Block); 0206 } 0207 else if (device.isDeviceInterface(Solid::DeviceInterface::PortableMediaPlayer)) 0208 { 0209 typeStr = Solid::DeviceInterface::typeToString(Solid::DeviceInterface::PortableMediaPlayer); 0210 typeDesc = Solid::DeviceInterface::typeDescription(Solid::DeviceInterface::PortableMediaPlayer); 0211 } 0212 else if (device.isDeviceInterface(Solid::DeviceInterface::NetworkShare)) 0213 { 0214 typeStr = Solid::DeviceInterface::typeToString(Solid::DeviceInterface::NetworkShare); 0215 typeDesc = Solid::DeviceInterface::typeDescription(Solid::DeviceInterface::NetworkShare); 0216 } 0217 else if (device.isDeviceInterface(Solid::DeviceInterface::Unknown)) 0218 { 0219 typeStr = Solid::DeviceInterface::typeToString(Solid::DeviceInterface::Unknown); 0220 typeDesc = Solid::DeviceInterface::typeDescription(Solid::DeviceInterface::Unknown); 0221 } 0222 0223 if (!typeStr.isEmpty()) 0224 { 0225 // NOTE: Data UserRole in column 0 is used to store index level of tree widget item. 0226 // This value is used later in copy to clipboard to render hierarchy as text. 0227 0228 QList<QTreeWidgetItem*> lst = listView()->findItems(typeStr, Qt::MatchExactly); 0229 QTreeWidgetItem* hitem = nullptr; 0230 0231 if (!lst.isEmpty()) 0232 { 0233 hitem = lst[0]; 0234 } 0235 else 0236 { 0237 hitem = new QTreeWidgetItem(listView(), QStringList() << typeStr << typeDesc); 0238 hitem->setData(0, Qt::UserRole, 0); 0239 listView()->addTopLevelItem(hitem); 0240 } 0241 0242 QTreeWidgetItem* vitem = nullptr; 0243 QString title; 0244 0245 #if (SOLID_VERSION >= QT_VERSION_CHECK(5, 71, 0)) 0246 0247 title = device.displayName(); 0248 0249 #endif 0250 0251 if (title.isEmpty()) 0252 { 0253 if (!device.vendor().isEmpty()) 0254 { 0255 title = device.vendor(); 0256 } 0257 0258 if (!device.product().isEmpty()) 0259 { 0260 title += QLatin1String(" - ") + device.product(); 0261 } 0262 } 0263 0264 if (title.isEmpty()) 0265 { 0266 title = device.udi().section(QLatin1Char('/'), -1); 0267 } 0268 0269 QTreeWidgetItem* const titem = new QTreeWidgetItem(hitem, QStringList() << title); 0270 titem->setData(0, Qt::UserRole, 1); 0271 0272 vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item: universal device ID", "Udi") << (device.udi().isEmpty() ? i18nc("@item: device property", "empty") : device.udi())); 0273 vitem->setData(0, Qt::UserRole, 2); 0274 0275 vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item: parent device ID", "Parent Udi") << (device.parentUdi().isEmpty() ? i18nc("@item: device property", "none") : device.parentUdi())); 0276 vitem->setData(0, Qt::UserRole, 2); 0277 0278 vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item; device constructor", "Vendor") << (device.vendor().isEmpty() ? i18nc("@item: device property", "empty") : device.vendor())); 0279 vitem->setData(0, Qt::UserRole, 2); 0280 0281 vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item: device name", "Product") << (device.product().isEmpty() ? i18nc("@item: device property", "empty") : device.product())); 0282 vitem->setData(0, Qt::UserRole, 2); 0283 0284 vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item: device description", "Description") << (device.description().isEmpty() ? i18nc("@item: device property", "empty") : device.description())); 0285 vitem->setData(0, Qt::UserRole, 2); 0286 0287 vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item: device status", "States") << (device.emblems().isEmpty() ? i18nc("@item: device property", "none") : device.emblems().join(QLatin1String(", ")))); 0288 vitem->setData(0, Qt::UserRole, 2); 0289 0290 #ifndef Q_OS_WIN 0291 0292 if (device.is<Solid::GenericInterface>()) 0293 { 0294 QTreeWidgetItem* const vitem2 = new QTreeWidgetItem(titem, QStringList() << i18nc("@item", "Properties") << i18nc("@item", "Non-portable info")); 0295 vitem2->setData(0, Qt::UserRole, 2); 0296 0297 QMap<QString, QVariant> properties = device.as<Solid::GenericInterface>()->allProperties(); 0298 0299 for (auto it = properties.constBegin() ; it != properties.constEnd() ; ++it) 0300 { 0301 QTreeWidgetItem* const pitem = new QTreeWidgetItem(vitem2, QStringList() << it.key() << it.value().toString()); 0302 pitem->setData(0, Qt::UserRole, 3); 0303 } 0304 } 0305 0306 #endif 0307 0308 } 0309 } 0310 } 0311 0312 QTreeWidget* SolidHardwareDlg::currentTreeView() const 0313 { 0314 if (tabView()->currentIndex() == 0) 0315 { 0316 return (listView()); 0317 } 0318 0319 return (d->eventsList); 0320 } 0321 0322 void SolidHardwareDlg::slotSearchTextChanged(const SearchTextSettings& settings) 0323 { 0324 bool query = false; 0325 int results = 0; 0326 QString search = settings.text.toLower(); 0327 0328 QTreeWidgetItemIterator it(currentTreeView()); 0329 0330 while (*it) 0331 { 0332 QTreeWidgetItem* const item = *it; 0333 0334 if (item->text(0).toLower().contains(search, settings.caseSensitive) || 0335 item->text(1).toLower().contains(search, settings.caseSensitive)) 0336 { 0337 ++results; 0338 query = true; 0339 item->setHidden(false); 0340 } 0341 else 0342 { 0343 item->setHidden(true); 0344 } 0345 0346 ++it; 0347 } 0348 0349 d->searchBar->slotSearchResult(query); 0350 } 0351 0352 void SolidHardwareDlg::slotCopy2ClipBoard() 0353 { 0354 QString textInfo; 0355 0356 textInfo.append(QApplication::applicationName()); 0357 textInfo.append(QLatin1String(" version ")); 0358 textInfo.append(QApplication::applicationVersion()); 0359 textInfo.append(QLatin1Char('\n')); 0360 textInfo.append(QLatin1String("Solid framework version ")); 0361 textInfo.append(QLatin1String(SOLID_VERSION_STRING)); 0362 textInfo.append(QLatin1Char('\n')); 0363 0364 QTreeWidgetItemIterator it(currentTreeView()); 0365 0366 while (*it) 0367 { 0368 int id = (*it)->data(0, Qt::UserRole).toInt(); 0369 textInfo.append(QString().fill(QLatin1Char(' '), id*3)); 0370 textInfo.append((*it)->text(0)); 0371 textInfo.append(QLatin1String(": ")); 0372 textInfo.append((*it)->text(1)); 0373 textInfo.append(QLatin1Char('\n')); 0374 ++it; 0375 } 0376 0377 QMimeData* const mimeData = new QMimeData(); 0378 mimeData->setText(textInfo); 0379 QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); 0380 } 0381 0382 void SolidHardwareDlg::slotDeviceAdded(const QString& udi) 0383 { 0384 QTreeWidgetItem* const titem = new QTreeWidgetItem(d->eventsList, QStringList() 0385 << QDateTime::currentDateTime().toString(Qt::ISODate) 0386 << i18nc("@item", "New Device Added")); 0387 titem->setData(0, Qt::UserRole, 0); 0388 0389 const Solid::Device device(udi); 0390 0391 QTreeWidgetItem* vitem = nullptr; 0392 0393 vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item: universal device ID", "Udi") << udi); 0394 vitem->setData(0, Qt::UserRole, 1); 0395 0396 vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item: parent device ID", "Parent Udi") << (device.parentUdi().isEmpty() ? i18nc("@info: device property", "none") : device.parentUdi())); 0397 vitem->setData(0, Qt::UserRole, 1); 0398 0399 vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item: device constructor", "Vendor") << (device.vendor().isEmpty() ? i18nc("@info: device property", "empty") : device.vendor())); 0400 vitem->setData(0, Qt::UserRole, 1); 0401 0402 vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item: device name", "Product") << (device.product().isEmpty() ? i18nc("@info: device property", "empty") : device.product())); 0403 vitem->setData(0, Qt::UserRole, 1); 0404 0405 vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item: device description", "Description") << (device.description().isEmpty() ? i18nc("@info: device property", "empty") : device.description())); 0406 vitem->setData(0, Qt::UserRole, 1); 0407 0408 vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item: device status", "States") << (device.emblems().isEmpty() ? i18nc("@info: device property", "none") : device.emblems().join(QLatin1String(", ")))); 0409 vitem->setData(0, Qt::UserRole, 1); 0410 0411 titem->setExpanded(true); 0412 } 0413 0414 void SolidHardwareDlg::slotDeviceRemoved(const QString& udi) 0415 { 0416 QTreeWidgetItem* const titem = new QTreeWidgetItem(d->eventsList, QStringList() 0417 << QDateTime::currentDateTime().toString(Qt::ISODate) 0418 << i18nc("@item", "Device Removed")); 0419 titem->setData(0, Qt::UserRole, 0); 0420 0421 QTreeWidgetItem* const vitem = new QTreeWidgetItem(titem, QStringList() << i18nc("@item", "Udi") << udi); 0422 vitem->setData(0, Qt::UserRole, 1); 0423 0424 titem->setExpanded(true); 0425 } 0426 0427 } // namespace Digikam 0428 0429 #include "moc_solidhardwaredlg.cpp"