File indexing completed on 2024-05-05 16:08:23

0001 /*  This file is part of the KDE project
0002     Copyright (C) 2006 Michael Larouche <michael.larouche@kdemail.net>
0003                   2007 Kevin Ottens <ervin@kde.org>
0004 
0005     This library is free software; you can redistribute it and/or
0006     modify it under the terms of the GNU Library General Public
0007     License version 2 as published by the Free Software Foundation.
0008 
0009     This library is distributed in the hope that it will be useful,
0010     but WITHOUT ANY WARRANTY; without even the implied warranty of
0011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012     Library General Public License for more details.
0013 
0014     You should have received a copy of the GNU Library General Public License
0015     along with this library; see the file COPYING.LIB.  If not, write to
0016     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017     Boston, MA 02110-1301, USA.
0018 
0019 */
0020 #include "kdevicelistmodel.h"
0021 #include "kdevicelistitem_p.h"
0022 
0023 #include <solid/devicenotifier.h>
0024 #include <solid/deviceinterface.h>
0025 
0026 #include <QTimer>
0027 #include <QIcon>
0028 
0029 #include <klocalizedstring.h>
0030 
0031 class Q_DECL_HIDDEN KDeviceListModel::Private
0032 {
0033 public:
0034     Private(KDeviceListModel *self) : q(self), rootItem(new KDeviceListItem()) {}
0035     ~Private()
0036     {
0037         delete rootItem;
0038     }
0039 
0040     KDeviceListModel *q;
0041 
0042     KDeviceListItem *rootItem;
0043     QMap<QString, KDeviceListItem *> deviceItems;
0044     Solid::Predicate predicate;
0045 
0046     void initialize(const Solid::Predicate &p);
0047     QModelIndex indexForItem(KDeviceListItem *item) const;
0048     void addDevice(const Solid::Device &device);
0049     void removeBranch(const QString &udi);
0050 
0051     // Private slots
0052     void _k_initDeviceList();
0053     void _k_deviceAdded(const QString &udi);
0054     void _k_deviceRemoved(const QString &udi);
0055 };
0056 
0057 KDeviceListModel::KDeviceListModel(QObject *parent)
0058     : QAbstractItemModel(parent), d(new Private(this))
0059 {
0060     d->deviceItems[QString()] = d->rootItem;
0061     d->initialize(Solid::Predicate());
0062 }
0063 
0064 KDeviceListModel::KDeviceListModel(const QString &predicate, QObject *parent)
0065     : QAbstractItemModel(parent), d(new Private(this))
0066 {
0067     d->initialize(Solid::Predicate::fromString(predicate));
0068 }
0069 
0070 KDeviceListModel::KDeviceListModel(const Solid::Predicate &predicate, QObject *parent)
0071     : QAbstractItemModel(parent), d(new Private(this))
0072 {
0073     d->initialize(predicate);
0074 }
0075 
0076 KDeviceListModel::~KDeviceListModel()
0077 {
0078     delete d;
0079 }
0080 
0081 void KDeviceListModel::Private::initialize(const Solid::Predicate &p)
0082 {
0083     predicate = p;
0084 
0085     // Delay load of hardware list when the event loop start
0086     QTimer::singleShot(0, q, SLOT(_k_initDeviceList()));
0087 }
0088 
0089 QVariant KDeviceListModel::data(const QModelIndex &index, int role) const
0090 {
0091     if (!index.isValid()) {
0092         return QVariant();
0093     }
0094 
0095     KDeviceListItem *deviceItem = static_cast<KDeviceListItem *>(index.internalPointer());
0096     Solid::Device device = deviceItem->device();
0097 
0098     QVariant returnData;
0099     if (role == Qt::DisplayRole) {
0100         returnData = device.product();
0101     }
0102     // Only display icons in the first column
0103     else if (role == Qt::DecorationRole && index.column() == 0) {
0104         returnData = QIcon::fromTheme(device.icon());
0105     }
0106 
0107     return returnData;
0108 }
0109 
0110 QVariant KDeviceListModel::headerData(int section, Qt::Orientation orientation, int role) const
0111 {
0112     Q_UNUSED(section)
0113 
0114     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
0115         return i18n("Device name");
0116     }
0117 
0118     return QVariant();
0119 }
0120 
0121 QModelIndex KDeviceListModel::index(int row, int column, const QModelIndex &parent) const
0122 {
0123     if (row < 0 || column != 0) {
0124         return QModelIndex();
0125     }
0126 
0127     KDeviceListItem *parentItem;
0128     if (parent.isValid()) {
0129         parentItem = static_cast<KDeviceListItem *>(parent.internalPointer());
0130     } else {
0131         parentItem = d->rootItem;
0132     }
0133 
0134     KDeviceListItem *childItem = parentItem->child(row);
0135 
0136     if (childItem) {
0137         return createIndex(row, column, childItem);
0138     } else {
0139         return QModelIndex();
0140     }
0141 }
0142 
0143 QModelIndex KDeviceListModel::rootIndex() const
0144 {
0145     return d->indexForItem(d->rootItem);
0146 }
0147 
0148 QModelIndex KDeviceListModel::parent(const QModelIndex &child) const
0149 {
0150     if (!child.isValid()) {
0151         return QModelIndex();
0152     }
0153 
0154     KDeviceListItem *childItem = static_cast<KDeviceListItem *>(child.internalPointer());
0155     KDeviceListItem *parentItem = childItem->parent();
0156 
0157     if (!parentItem) {
0158         return QModelIndex();
0159     } else {
0160         return d->indexForItem(parentItem);
0161     }
0162 }
0163 
0164 int KDeviceListModel::rowCount(const QModelIndex &parent) const
0165 {
0166     if (!parent.isValid()) {
0167         return d->rootItem->childCount();
0168     }
0169 
0170     KDeviceListItem *item = static_cast<KDeviceListItem *>(parent.internalPointer());
0171 
0172     return item->childCount();
0173 }
0174 
0175 int KDeviceListModel::columnCount(const QModelIndex &parent) const
0176 {
0177     Q_UNUSED(parent);
0178     // We only know 1 information for a particualiar device.
0179     return 1;
0180 }
0181 
0182 Solid::Device KDeviceListModel::deviceForIndex(const QModelIndex &index) const
0183 {
0184     KDeviceListItem *deviceItem = static_cast<KDeviceListItem *>(index.internalPointer());
0185     return deviceItem->device();
0186 }
0187 
0188 void KDeviceListModel::Private::_k_initDeviceList()
0189 {
0190     Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance();
0191 
0192     connect(notifier, SIGNAL(deviceAdded(QString)),
0193             q, SLOT(_k_deviceAdded(QString)));
0194     connect(notifier, SIGNAL(deviceRemoved(QString)),
0195             q, SLOT(_k_deviceRemoved(QString)));
0196 
0197     // Use allDevices() from the manager if the predicate is not valid
0198     // otherwise the returned list is empty
0199     const QList<Solid::Device> &deviceList = predicate.isValid() ?
0200             Solid::Device::listFromQuery(predicate)
0201             : Solid::Device::allDevices();
0202 
0203     foreach (const Solid::Device &device, deviceList) {
0204         addDevice(device);
0205     }
0206 
0207     emit q->modelInitialized();
0208 }
0209 
0210 void KDeviceListModel::Private::addDevice(const Solid::Device &device)
0211 {
0212     // Don't insert invalid devices
0213     if (!device.isValid()) {
0214         return;
0215     }
0216 
0217     // Don't insert devices that doesn't match the predicate set
0218     // (except for the root)
0219     if (!device.parentUdi().isEmpty()
0220             && predicate.isValid() && !predicate.matches(device)) {
0221         return;
0222     }
0223 
0224     KDeviceListItem *item;
0225     if (deviceItems.contains(device.udi())) { // It was already inserted as a parent
0226         item = deviceItems[device.udi()];
0227     } else {
0228         item = new KDeviceListItem();
0229         deviceItems[device.udi()] = item;
0230     }
0231     item->setDevice(device);
0232 
0233     KDeviceListItem *parent = rootItem;
0234 
0235     if (!deviceItems.contains(device.parentUdi())) { // The parent was not present, try to insert it in the model
0236         addDevice(Solid::Device(device.parentUdi()));
0237     }
0238 
0239     if (deviceItems.contains(device.parentUdi())) { // Update the parent if the device is now present
0240         parent = deviceItems[device.parentUdi()];
0241     }
0242 
0243     if (item->parent() != parent) { // If it's already our parent no need to signal the new row
0244         q->beginInsertRows(indexForItem(parent), parent->childCount(), parent->childCount());
0245         item->setParent(parent);
0246         q->endInsertRows();
0247     }
0248 }
0249 
0250 void KDeviceListModel::Private::removeBranch(const QString &udi)
0251 {
0252     if (!deviceItems.contains(udi)) {
0253         return;
0254     }
0255 
0256     KDeviceListItem *item = deviceItems[udi];
0257     KDeviceListItem *parent = item->parent();
0258 
0259     QList<KDeviceListItem *> children = item->children();
0260 
0261     foreach (KDeviceListItem *child, children) {
0262         removeBranch(child->device().udi());
0263     }
0264 
0265     q->beginRemoveRows(indexForItem(parent),
0266                        item->row(), item->row());
0267 
0268     item->setParent(nullptr);
0269     deviceItems.remove(udi);
0270     delete item;
0271 
0272     q->endRemoveRows();
0273 }
0274 
0275 void KDeviceListModel::Private::_k_deviceAdded(const QString &udi)
0276 {
0277     Solid::Device device(udi);
0278     addDevice(device);
0279 }
0280 
0281 void KDeviceListModel::Private::_k_deviceRemoved(const QString &udi)
0282 {
0283     removeBranch(udi);
0284 }
0285 
0286 QModelIndex KDeviceListModel::Private::indexForItem(KDeviceListItem *item) const
0287 {
0288     if (item == rootItem) {
0289         return QModelIndex();
0290     } else {
0291         return q->createIndex(item->row(), 0, item);
0292     }
0293 }
0294 
0295 #include "moc_kdevicelistmodel.cpp"