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"