[OpenDocString] kdeconnect-kde (cpp)
devicesmodel.cpp
static QString createId()
{
    return QCoreApplication::instance()->applicationName() + QString::number(QCoreApplication::applicationPid());
}
Returns the id of the application.
DevicesModel::DevicesModel(QObject *parent)
    : QAbstractListModel(parent)
    , m_dbusInterface(new DaemonDbusInterface(this))
    , m_displayFilter(StatusFilterFlag::NoFilter)
{
    connect(this, &QAbstractItemModel::rowsRemoved, this, &DevicesModel::rowsChanged);
    connect(this, &QAbstractItemModel::rowsInserted, this, &DevicesModel::rowsChanged);

    connect(m_dbusInterface, SIGNAL(deviceAdded(QString)), this, SLOT(deviceAdded(QString)));
    connect(m_dbusInterface, &OrgKdeKdeconnectDaemonInterface::deviceVisibilityChanged, this, &DevicesModel::deviceUpdated);
    connect(m_dbusInterface, &OrgKdeKdeconnectDaemonInterface::deviceRemoved, this, &DevicesModel::deviceRemoved);

    QDBusServiceWatcher *watcher =
        new QDBusServiceWatcher(DaemonDbusInterface::activatedService(), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this);
    connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, &DevicesModel::refreshDeviceList);
    connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, &DevicesModel::clearDevices);

    // refresh the view, acquireDiscoveryMode if necessary
    setDisplayFilter(NoFilter);
}
This constructor builds a devices model object from an existing parent object. It takes the parent object, creates connections to the internal DBus Interface, and sets up signal/slot connections for device added and device removed events. It also connects the DBus service watcher to the service and refresh the device list if necessary.
QHash DevicesModel::roleNames() const
{
    QHash names = QAbstractItemModel::roleNames();
    names.insert(NameModelRole, "name");
    names.insert(IdModelRole, "deviceId");
    names.insert(IconNameRole, "iconName");
    names.insert(DeviceRole, "device");
    names.insert(StatusModelRole, "status");
    return names;
}
Returns a hash of the role names. The first key is the name, the second key is the device id, the third key is the icon name, and the third key is the device id, the third key is the device id, the third key is the device icon name, and the third key is the status.
DevicesModel::~DevicesModel()
{
    m_dbusInterface->releaseDiscoveryMode(*s_keyId);
}
This removes the discovery mode for the given key id.
int DevicesModel::rowForDevice(const QString &id) const
{
    for (int i = 0, c = m_deviceList.size(); i < c; ++i) {
        if (m_deviceList[i]->id() == id) {
            return i;
        }
    }
    return -1;
}
Returns the row number of the first device of the given id.
void DevicesModel::deviceAdded(const QString &id)
{
    if (rowForDevice(id) >= 0) {
        Q_ASSERT_X(false, "deviceAdded", "Trying to add a device twice");
        return;
    }

    DeviceDbusInterface *dev = new DeviceDbusInterface(id, this);
    Q_ASSERT(dev->isValid());

    if (!passesFilter(dev)) {
        delete dev;
        return;
    }

    beginInsertRows(QModelIndex(), m_deviceList.size(), m_deviceList.size());
    appendDevice(dev);
    endInsertRows();
}
This adds the device object to the device list. It checks that the device is not already in the list. Then it checks that the device is valid. Then it checks that the filter is true. Then it inserts the device into the device list.
void DevicesModel::deviceRemoved(const QString &id)
{
    int row = rowForDevice(id);
    if (row >= 0) {
        beginRemoveRows(QModelIndex(), row, row);
        delete m_deviceList.takeAt(row);
        endRemoveRows();
    }
}
This removes the device of the given id, from the device list.
void DevicesModel::deviceUpdated(const QString &id, bool isVisible)
{
    Q_UNUSED(isVisible);
    int row = rowForDevice(id);

    if (row < 0) {
        // FIXME: when m_dbusInterface is not valid refreshDeviceList() does
        // nothing and we can miss some devices.
        // Someone can reproduce this problem by restarting kdeconnectd while
        // kdeconnect's plasmoid is still running.
        // Another reason for this branch is that we removed the device previously
        // because of the filter settings.
        qCDebug(KDECONNECT_INTERFACES) << "Adding missing or previously removed device" << id;
        deviceAdded(id);
    } else {
        DeviceDbusInterface *dev = getDevice(row);
        if (!passesFilter(dev)) {
            beginRemoveRows(QModelIndex(), row, row);
            delete m_deviceList.takeAt(row);
            endRemoveRows();
            qCDebug(KDECONNECT_INTERFACES) << "Removed changed device " << id;
        } else {
            const QModelIndex idx = index(row);
            Q_EMIT dataChanged(idx, idx);
        }
    }
}
This removes the device with the given id, if it doesn't exist, it gets added to the device list, and if it passes the filter, it removes the device from the device list, and emits the signal 'dataChanged'.
int DevicesModel::displayFilter() const
{
    return m_displayFilter;
}
Returns the display filter value.
void DevicesModel::setDisplayFilter(int flags)
{
    m_displayFilter = (StatusFilterFlag)flags;

    const bool reachableNeeded = (m_displayFilter & StatusFilterFlag::Reachable);
    if (reachableNeeded)
        m_dbusInterface->acquireDiscoveryMode(*s_keyId);
    else
        m_dbusInterface->releaseDiscoveryMode(*s_keyId);

    refreshDeviceList();
}
Sets the display filter on the devices model. It acquires and releases the discovery mode if the filter is reachable. Then it refreshes the device list.
void DevicesModel::refreshDeviceList()
{
    if (!m_dbusInterface->isValid()) {
        clearDevices();
        qCWarning(KDECONNECT_INTERFACES) << "dbus interface not valid";
        return;
    }

    bool onlyPaired = (m_displayFilter & StatusFilterFlag::Paired);
    bool onlyReachable = (m_displayFilter & StatusFilterFlag::Reachable);

    QDBusPendingReply pendingDeviceIds = m_dbusInterface->devices(onlyReachable, onlyPaired);
    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingDeviceIds, this);

    QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &DevicesModel::receivedDeviceList);
}
This refreshes the device list from the dbus interface. It checks that the dbus interface is valid, and if it is paired, it will only refresh the devices if it is paired, and reachable. If the filter is true, it will only refresh the devices if the display filter is paired, and reachable. If the filter is false, it will clear the devices if the dbus interface is not valid, and return early.
void DevicesModel::receivedDeviceList(QDBusPendingCallWatcher *watcher)
{
    watcher->deleteLater();
    clearDevices();
    QDBusPendingReply pendingDeviceIds = *watcher;
    if (pendingDeviceIds.isError()) {
        qCWarning(KDECONNECT_INTERFACES) << "error while refreshing device list" << pendingDeviceIds.error().message();
        return;
    }

    Q_ASSERT(m_deviceList.isEmpty());
    const QStringList deviceIds = pendingDeviceIds.value();

    if (deviceIds.isEmpty())
        return;

    beginInsertRows(QModelIndex(), 0, deviceIds.count() - 1);
    for (const QString &id : deviceIds) {
        appendDevice(new DeviceDbusInterface(id, this));
    }
    endInsertRows();
}
This removes the devices that were sent to the watch list and inserts them into the device list.
void DevicesModel::appendDevice(DeviceDbusInterface *dev)
{
    m_deviceList.append(dev);
    connect(dev, &OrgKdeKdeconnectDeviceInterface::nameChanged, this, &DevicesModel::nameChanged);
}
This adds the device object to the list of devices, and connects the signal 'NameChanged'.
void DevicesModel::nameChanged(const QString &newName)
{
    Q_UNUSED(newName);
    DeviceDbusInterface *device = static_cast(sender());

    Q_ASSERT(rowForDevice(device->id()) >= 0);

    deviceUpdated(device->id(), true);
}
This sends a signal to tell that the device s name has changed. The function first unuses the device object, and checks that the row for the device is non-zero. Then it updates the deviceUpdated signal with true.
void DevicesModel::clearDevices()
{
    if (!m_deviceList.isEmpty()) {
        beginRemoveRows(QModelIndex(), 0, m_deviceList.size() - 1);
        qDeleteAll(m_deviceList);
        m_deviceList.clear();
        endRemoveRows();
    }
}
This removes all devices from the device list.
QVariant DevicesModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || index.row() < 0 || index.row() >= m_deviceList.size()) {
        return QVariant();
    }

    Q_ASSERT(m_dbusInterface->isValid());

    DeviceDbusInterface *device = m_deviceList[index.row()];
    Q_ASSERT(device->isValid());

    // This function gets called lots of times, producing lots of dbus calls. Add a cache?
    switch (role) {
    case Qt::SizeHintRole:
        return QSize(0, 32);
    case IconModelRole: {
        QString icon = data(index, IconNameRole).toString();
        return QIcon::fromTheme(icon);
    }
    case IdModelRole:
        return device->id();
    case NameModelRole:
        return device->name();
    case Qt::ToolTipRole: {
        bool trusted = device->isTrusted();
        bool reachable = device->isReachable();
        QString status = reachable ? (trusted ? i18n("Device trusted and connected") : i18n("Device not trusted")) : i18n("Device disconnected");
        return status;
    }
    case StatusModelRole: {
        int status = StatusFilterFlag::NoFilter;
        if (device->isReachable()) {
            status |= StatusFilterFlag::Reachable;
        }
        if (device->isTrusted()) {
            status |= StatusFilterFlag::Paired;
        }
        return status;
    }
    case IconNameRole:
        return device->statusIconName();
    case DeviceRole:
        return QVariant::fromValue(device);
    default:
        return QVariant();
    }
}
Returns a QVariant object that represents the data of the given role on the device at the given index. The function first checks that the index is valid, and that the device is valid. Then it checks that the device is valid. Then it checks that the device is reachable and paired. If the device is trusted and connected, it returns a QString with the status as a QString. If the device is not trusted, it returns a QString with the status as a QString.
DeviceDbusInterface *DevicesModel::getDevice(int row) const
{
    if (row < 0 || row >= m_deviceList.size()) {
        return nullptr;
    }

    return m_deviceList[row];
}
Returns a pointer to the device object of the given row. If the row is out of range, it returns nullptr.
int DevicesModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid()) {
        // Return size 0 if we are a child because this is not a tree
        return 0;
    }

    return m_deviceList.size();
}
Returns the row count for the given parent index. If the parent is not a tree, it returns 0.
bool DevicesModel::passesFilter(DeviceDbusInterface *dev) const
{
    bool onlyPaired = (m_displayFilter & StatusFilterFlag::Paired);
    bool onlyReachable = (m_displayFilter & StatusFilterFlag::Reachable);

    return !((onlyReachable && !dev->isReachable()) || (onlyPaired && !dev->isTrusted()));
}
This implements checking if the device passes the filter. It is checked if the filter is paired and reachable but not trusted.