File indexing completed on 2024-04-28 08:49:05

0001 /**
0002  * SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "kcm.h"
0008 
0009 #include <KAboutData>
0010 #include <KColorSchemeManager>
0011 #include <KConfigGroup>
0012 #include <KLocalizedString>
0013 #include <KPluginFactory>
0014 #include <KPluginMetaData>
0015 #include <kcmutils_version.h>
0016 
0017 #include "dbushelpers.h"
0018 #include "dbusinterfaces.h"
0019 #include "devicesmodel.h"
0020 #include "devicessortproxymodel.h"
0021 #include "kdeconnect-version.h"
0022 
0023 K_PLUGIN_CLASS_WITH_JSON(KdeConnectKcm, "kcm_kdeconnect.json")
0024 
0025 KdeConnectKcm::KdeConnectKcm(QObject *parent, const QVariantList &args)
0026     : KCModule(qobject_cast<QWidget *>(parent))
0027     , daemon(new DaemonDbusInterface(this))
0028     , devicesModel(new DevicesModel(this))
0029     , currentDevice(nullptr)
0030 {
0031 #ifdef Q_OS_WIN
0032     KColorSchemeManager manager;
0033     QApplication::setStyle(QStringLiteral("breeze"));
0034 #endif
0035 
0036     kcmUi.setupUi(widget());
0037 
0038     sortProxyModel = new DevicesSortProxyModel(devicesModel);
0039 
0040     kcmUi.deviceList->setModel(sortProxyModel);
0041 
0042     kcmUi.deviceInfo->setVisible(false);
0043     kcmUi.progressBar->setVisible(false);
0044     kcmUi.messages->setVisible(false);
0045 
0046     // Workaround: If we set this directly (or if we set it in the .ui file), the layout breaks
0047     kcmUi.noDeviceLinks->setWordWrap(false);
0048     QTimer::singleShot(0, this, [this] {
0049         kcmUi.noDeviceLinks->setWordWrap(true);
0050     });
0051 
0052     setWhenAvailable(
0053         daemon->announcedName(),
0054         [this](const QString &announcedName) {
0055             kcmUi.rename_label->setText(announcedName);
0056             kcmUi.rename_edit->setText(announcedName);
0057         },
0058         this);
0059     connect(daemon, &DaemonDbusInterface::announcedNameChanged, kcmUi.rename_edit, &QLineEdit::setText);
0060     connect(daemon, &DaemonDbusInterface::announcedNameChanged, kcmUi.rename_label, &QLabel::setText);
0061     setRenameMode(false);
0062 
0063     setButtons(KCModule::Help | KCModule::NoAdditionalButton);
0064 
0065     connect(devicesModel, &QAbstractItemModel::dataChanged, this, &KdeConnectKcm::resetSelection);
0066     connect(kcmUi.deviceList->selectionModel(), &QItemSelectionModel::currentChanged, this, &KdeConnectKcm::deviceSelected);
0067     connect(kcmUi.accept_button, &QAbstractButton::clicked, this, &KdeConnectKcm::acceptPairing);
0068     connect(kcmUi.reject_button, &QAbstractButton::clicked, this, &KdeConnectKcm::cancelPairing);
0069     connect(kcmUi.cancel_button, &QAbstractButton::clicked, this, &KdeConnectKcm::cancelPairing);
0070     connect(kcmUi.pair_button, &QAbstractButton::clicked, this, &KdeConnectKcm::requestPairing);
0071     connect(kcmUi.unpair_button, &QAbstractButton::clicked, this, &KdeConnectKcm::unpair);
0072     connect(kcmUi.ping_button, &QAbstractButton::clicked, this, &KdeConnectKcm::sendPing);
0073     connect(kcmUi.refresh_button, &QAbstractButton::clicked, this, &KdeConnectKcm::refresh);
0074     connect(kcmUi.rename_edit, &QLineEdit::returnPressed, this, &KdeConnectKcm::renameDone);
0075     connect(kcmUi.renameDone_button, &QAbstractButton::clicked, this, &KdeConnectKcm::renameDone);
0076     connect(kcmUi.renameShow_button, &QAbstractButton::clicked, this, &KdeConnectKcm::renameShow);
0077     connect(kcmUi.pluginSelector, &KPluginWidget::changed, this, &KdeConnectKcm::pluginsConfigChanged);
0078 
0079     if (!args.isEmpty() && args.first().type() == QVariant::String) {
0080         const QString input = args.first().toString();
0081         const auto colonIdx = input.indexOf(QLatin1Char(':'));
0082         const QString deviceId = input.left(colonIdx);
0083         const QString pluginCM = colonIdx < 0 ? QString() : input.mid(colonIdx + 1);
0084 
0085         connect(devicesModel, &DevicesModel::rowsInserted, this, [this, deviceId, pluginCM]() {
0086             auto row = devicesModel->rowForDevice(deviceId);
0087             if (row >= 0) {
0088                 const QModelIndex idx = sortProxyModel->mapFromSource(devicesModel->index(row));
0089                 kcmUi.deviceList->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
0090             }
0091             if (!pluginCM.isEmpty()) {
0092                 kcmUi.pluginSelector->showConfiguration(pluginCM);
0093             }
0094             disconnect(devicesModel, &DevicesModel::rowsInserted, this, nullptr);
0095         });
0096     }
0097 }
0098 
0099 void KdeConnectKcm::renameShow()
0100 {
0101     setRenameMode(true);
0102 }
0103 
0104 void KdeConnectKcm::renameDone()
0105 {
0106     QString newName = kcmUi.rename_edit->text();
0107     if (newName.isEmpty()) {
0108         // Rollback changes
0109         kcmUi.rename_edit->setText(kcmUi.rename_label->text());
0110     } else {
0111         kcmUi.rename_label->setText(newName);
0112         daemon->setAnnouncedName(newName);
0113     }
0114     setRenameMode(false);
0115 }
0116 
0117 void KdeConnectKcm::setRenameMode(bool b)
0118 {
0119     kcmUi.renameDone_button->setVisible(b);
0120     kcmUi.rename_edit->setVisible(b);
0121     kcmUi.renameShow_button->setVisible(!b);
0122     kcmUi.rename_label->setVisible(!b);
0123 }
0124 
0125 KdeConnectKcm::~KdeConnectKcm()
0126 {
0127 }
0128 
0129 void KdeConnectKcm::refresh()
0130 {
0131     daemon->forceOnNetworkChange();
0132 }
0133 
0134 void KdeConnectKcm::resetSelection()
0135 {
0136     if (!currentDevice) {
0137         return;
0138     }
0139     kcmUi.deviceList->selectionModel()->setCurrentIndex(sortProxyModel->mapFromSource(currentIndex), QItemSelectionModel::ClearAndSelect);
0140 }
0141 
0142 void KdeConnectKcm::deviceSelected(const QModelIndex &current)
0143 {
0144     if (currentDevice) {
0145         disconnect(currentDevice, nullptr, this, nullptr);
0146     }
0147 
0148     if (!current.isValid()) {
0149         currentDevice = nullptr;
0150         kcmUi.deviceInfo->setVisible(false);
0151         return;
0152     }
0153 
0154     currentIndex = sortProxyModel->mapToSource(current);
0155     currentDevice = devicesModel->getDevice(currentIndex.row());
0156 
0157     kcmUi.noDevicePlaceholder->setVisible(false);
0158     bool valid = (currentDevice != nullptr && currentDevice->isValid());
0159     kcmUi.deviceInfo->setVisible(valid);
0160     if (!valid) {
0161         return;
0162     }
0163 
0164     kcmUi.messages->setVisible(false);
0165     resetDeviceView();
0166 
0167     connect(currentDevice, &DeviceDbusInterface::pluginsChanged, this, &KdeConnectKcm::resetCurrentDevice);
0168     connect(currentDevice, &DeviceDbusInterface::pairingFailed, this, &KdeConnectKcm::pairingFailed);
0169     connect(currentDevice, &DeviceDbusInterface::pairStateChanged, this, &KdeConnectKcm::setCurrentDevicePairState);
0170 }
0171 
0172 void KdeConnectKcm::resetCurrentDevice()
0173 {
0174     const QStringList supportedPluginNames = currentDevice->supportedPlugins();
0175 
0176     if (m_oldSupportedPluginNames != supportedPluginNames) {
0177         resetDeviceView();
0178     }
0179 }
0180 
0181 void KdeConnectKcm::resetDeviceView()
0182 {
0183     kcmUi.verificationKey->setText(i18n("Key: %1", QString::fromUtf8(currentDevice->verificationKey())));
0184 
0185     kcmUi.name_label->setText(currentDevice->name());
0186     setWhenAvailable(
0187         currentDevice->pairStateAsInt(),
0188         [this](int pairStateAsInt) {
0189             setCurrentDevicePairState(pairStateAsInt);
0190         },
0191         this);
0192 
0193     const QVector<KPluginMetaData> pluginInfo = KPluginMetaData::findPlugins(QStringLiteral("kdeconnect"));
0194     QVector<KPluginMetaData> availablePluginInfo;
0195 
0196     m_oldSupportedPluginNames = currentDevice->supportedPlugins();
0197     for (auto it = pluginInfo.cbegin(), itEnd = pluginInfo.cend(); it != itEnd; ++it) {
0198         if (m_oldSupportedPluginNames.contains(it->pluginId())) {
0199             availablePluginInfo.append(*it);
0200         }
0201     }
0202 
0203     KSharedConfigPtr deviceConfig = KSharedConfig::openConfig(currentDevice->pluginsConfigFile());
0204     kcmUi.pluginSelector->clear();
0205     kcmUi.pluginSelector->setConfigurationArguments({currentDevice->id()});
0206     kcmUi.pluginSelector->addPlugins(availablePluginInfo, i18n("Available plugins"));
0207     kcmUi.pluginSelector->setConfig(deviceConfig->group(QStringLiteral("Plugins")));
0208 }
0209 
0210 void KdeConnectKcm::requestPairing()
0211 {
0212     if (!currentDevice) {
0213         return;
0214     }
0215 
0216     kcmUi.messages->hide();
0217 
0218     currentDevice->requestPairing();
0219 }
0220 
0221 void KdeConnectKcm::unpair()
0222 {
0223     if (!currentDevice) {
0224         return;
0225     }
0226 
0227     currentDevice->unpair();
0228 }
0229 
0230 void KdeConnectKcm::acceptPairing()
0231 {
0232     if (!currentDevice) {
0233         return;
0234     }
0235 
0236     currentDevice->acceptPairing();
0237 }
0238 
0239 void KdeConnectKcm::cancelPairing()
0240 {
0241     if (!currentDevice) {
0242         return;
0243     }
0244 
0245     currentDevice->cancelPairing();
0246 }
0247 
0248 void KdeConnectKcm::pairingFailed(const QString &error)
0249 {
0250     if (sender() != currentDevice)
0251         return;
0252 
0253     kcmUi.messages->setText(i18n("Error trying to pair: %1", error));
0254     kcmUi.messages->animatedShow();
0255 }
0256 
0257 void KdeConnectKcm::setCurrentDevicePairState(int pairStateAsInt)
0258 {
0259     PairState state = (PairState)pairStateAsInt; // Hack because qdbus doesn't like enums
0260     kcmUi.accept_button->setVisible(state == PairState::RequestedByPeer);
0261     kcmUi.reject_button->setVisible(state == PairState::RequestedByPeer);
0262     kcmUi.cancel_button->setVisible(state == PairState::Requested);
0263     kcmUi.pair_button->setVisible(state == PairState::NotPaired);
0264     kcmUi.unpair_button->setVisible(state == PairState::Paired);
0265     kcmUi.progressBar->setVisible(state == PairState::Requested);
0266     kcmUi.ping_button->setVisible(state == PairState::Paired);
0267     switch (state) {
0268     case PairState::Paired:
0269         kcmUi.status_label->setText(i18n("(paired)"));
0270         break;
0271     case PairState::NotPaired:
0272         kcmUi.status_label->setText(i18n("(not paired)"));
0273         break;
0274     case PairState::RequestedByPeer:
0275         kcmUi.status_label->setText(i18n("(incoming pair request)"));
0276         break;
0277     case PairState::Requested:
0278         kcmUi.status_label->setText(i18n("(pairing requested)"));
0279         break;
0280     }
0281 }
0282 
0283 void KdeConnectKcm::pluginsConfigChanged(bool changed)
0284 {
0285     if (!changed)
0286         return;
0287 
0288     if (!currentDevice)
0289         return;
0290 
0291     kcmUi.pluginSelector->save();
0292     currentDevice->reloadPlugins();
0293 }
0294 
0295 void KdeConnectKcm::save()
0296 {
0297     KCModule::save();
0298 }
0299 
0300 void KdeConnectKcm::sendPing()
0301 {
0302     if (!currentDevice)
0303         return;
0304     currentDevice->pluginCall(QStringLiteral("ping"), QStringLiteral("sendPing"));
0305 }
0306 
0307 #include "kcm.moc"
0308 #include "moc_kcm.cpp"