diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c176a9b..d1102b4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,127 +1,128 @@ cmake_minimum_required(VERSION 3.0) project(plasma-networkmanagement) set(PROJECT_VERSION "5.16.80") set(PROJECT_VERSION_MAJOR 5) set(QT_MIN_VERSION "5.12.0") set(KF5_MIN_VERSION "5.58.0") ################# set KDE specific information ################# find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMQMLModules) include(FeatureSummary) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core DBus Network Quick + QuickWidgets Widgets ) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED ConfigWidgets Completion CoreAddons Declarative DBusAddons KIO I18n IconThemes NetworkManagerQt Notifications Plasma Service Solid Wallet WidgetsAddons WindowSystem ) find_package(KF5ModemManagerQt ${KF5_MIN_VERSION}) set_package_properties(KF5ModemManagerQt PROPERTIES TYPE OPTIONAL) ecm_find_qmlmodule(org.kde.prison 1.0) if (BUILD_MOBILE) find_package(KF5Kirigami2 ${KF5_MIN_VERSION} CONFIG) set_package_properties(KF5Kirigami2 PROPERTIES DESCRIPTION "A QtQuick based components set" PURPOSE "Required at runtime by the mobile KCMs" TYPE RUNTIME ) endif() # Required only for getting information about NetworkManager version in CMake find_package(NetworkManager 1.4.0) set_package_properties(NetworkManager PROPERTIES TYPE REQUIRED) find_package(MobileBroadbandProviderInfo) set_package_properties(MobileBroadbandProviderInfo PROPERTIES DESCRIPTION "Database of mobile broadband service providers" URL "http://live.gnome.org/NetworkManager/MobileBroadband/ServiceProviders" TYPE OPTIONAL) find_package(Qca-qt5 2.1.0) set_package_properties(Qca-qt5 PROPERTIES DESCRIPTION "Support for encryption" URL "http://download.kde.org/stable/qca-qt5/" TYPE REQUIRED) find_package(KF5Prison ${KF5_MIN_VERSION}) set_package_properties(KF5Prison PROPERTIES DESCRIPTION "Prison library" URL "http://projects.kde.org/prison" TYPE RUNTIME PURPOSE "Needed to create mobile barcodes for WiFi networks" ) if (DISABLE_MODEMMANAGER_SUPPORT) message(STATUS "Disabling ModemManager support") set(WITH_MODEMMANAGER_SUPPORT 0) else() if (KF5ModemManagerQt_FOUND) message(STATUS "Enabling ModemManager support") set(WITH_MODEMMANAGER_SUPPORT 1) else() message(STATUS "ModemManager or ModemManagerQt not found") set(WITH_MODEMMANAGER_SUPPORT 0) endif() endif() add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0) add_definitions(-DQT_USE_FAST_OPERATOR_PLUS) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_FROM_BYTEARRAY) add_definitions(-DWITH_MODEMMANAGER_SUPPORT=${WITH_MODEMMANAGER_SUPPORT}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs ${CMAKE_CURRENT_SOURCE_DIR}/libs/editor/) add_subdirectory(applet) add_subdirectory(kded) add_subdirectory(kcm) add_subdirectory(libs) add_subdirectory(vpn) if (BUILD_MOBILE) add_subdirectory(mobile) endif() # Enable unit testing if (BUILD_TESTING) add_subdirectory(tests) endif() feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/kcm/CMakeLists.txt b/kcm/CMakeLists.txt index b89b0d4e..37e81bb9 100755 --- a/kcm/CMakeLists.txt +++ b/kcm/CMakeLists.txt @@ -1,38 +1,39 @@ include_directories(${CMAKE_SOURCE_DIR}/libs/editor ${CMAKE_SOURCE_DIR}/libs/editor/widgets) #KI18N Translation Domain for this library add_definitions(-DTRANSLATION_DOMAIN=\"plasmanetworkmanagement-kcm\") set(kcm_networkmanagement_PART_SRCS ../libs/debug.cpp kcm.cpp ) ki18n_wrap_ui(kcm_networkmanagement_PART_SRCS kcm.ui ) add_library(kcm_networkmanagement MODULE ${kcm_networkmanagement_PART_SRCS}) target_link_libraries(kcm_networkmanagement plasmanm_internal plasmanm_editor KF5::ConfigWidgets KF5::Declarative KF5::I18n KF5::Service Qt5::Quick + Qt5::QuickWidgets ) install(TARGETS kcm_networkmanagement DESTINATION ${KDE_INSTALL_PLUGINDIR} ) ########### install files ############### install( FILES kcm_networkmanagement.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) install( FILES qml/ConnectionItem.qml qml/Dialog.qml qml/Header.qml qml/ListItem.qml qml/main.qml DESTINATION ${KDE_INSTALL_DATADIR}/kcm_networkmanagement/qml) diff --git a/kcm/kcm.cpp b/kcm/kcm.cpp index 134981e3..881c0855 100755 --- a/kcm/kcm.cpp +++ b/kcm/kcm.cpp @@ -1,576 +1,571 @@ /* Copyright 2016 Jan Grulich This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "kcm.h" #include "debug.h" #include "connectioneditordialog.h" #include "mobileconnectionwizard.h" #include "uiutils.h" #include "vpnuiplugin.h" #include "settings/wireguardinterfacewidget.h" // KDE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Qt #include #include #include #include #include #include #include #include +#include K_PLUGIN_FACTORY(KCMNetworkConfigurationFactory, registerPlugin();) KCMNetworkmanagement::KCMNetworkmanagement(QWidget *parent, const QVariantList &args) : KCModule(parent, args) , m_handler(new Handler(this)) , m_tabWidget(nullptr) , m_ui(new Ui::KCMForm) - , m_quickView(nullptr) { QWidget *mainWidget = new QWidget(this); m_ui->setupUi(mainWidget); - m_quickView = new QQuickView(nullptr); KDeclarative::KDeclarative kdeclarative; - kdeclarative.setDeclarativeEngine(m_quickView->engine()); + kdeclarative.setDeclarativeEngine(m_ui->connectionView->engine()); kdeclarative.setTranslationDomain(QStringLiteral(TRANSLATION_DOMAIN)); - kdeclarative.setupEngine(m_quickView->engine()); + kdeclarative.setupEngine(m_ui->connectionView->engine()); kdeclarative.setupContext(); - QWidget *widget = QWidget::createWindowContainer(m_quickView, this); - widget->setMinimumWidth(300); - QVBoxLayout *layout = new QVBoxLayout(m_ui->connectionView); - layout->addWidget(widget); - - m_quickView->rootContext()->setContextProperty("alternateBaseColor", mainWidget->palette().color(QPalette::Active, QPalette::AlternateBase)); - m_quickView->rootContext()->setContextProperty("backgroundColor", mainWidget->palette().color(QPalette::Active, QPalette::Window)); - m_quickView->rootContext()->setContextProperty("baseColor", mainWidget->palette().color(QPalette::Active, QPalette::Base)); - m_quickView->rootContext()->setContextProperty("highlightColor", mainWidget->palette().color(QPalette::Active, QPalette::Highlight)); - m_quickView->rootContext()->setContextProperty("textColor", mainWidget->palette().color(QPalette::Active, QPalette::Text)); - m_quickView->rootContext()->setContextProperty("connectionModified", false); - m_quickView->setResizeMode(QQuickView::SizeRootObjectToView); - m_quickView->setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kcm_networkmanagement/qml/main.qml")))); - - QObject *rootItem = m_quickView->rootObject(); + m_ui->connectionView->setMinimumWidth(300); + m_ui->connectionView->rootContext()->setContextProperty("alternateBaseColor", mainWidget->palette().color(QPalette::Active, QPalette::AlternateBase)); + m_ui->connectionView->rootContext()->setContextProperty("backgroundColor", mainWidget->palette().color(QPalette::Active, QPalette::Window)); + m_ui->connectionView->rootContext()->setContextProperty("baseColor", mainWidget->palette().color(QPalette::Active, QPalette::Base)); + m_ui->connectionView->rootContext()->setContextProperty("highlightColor", mainWidget->palette().color(QPalette::Active, QPalette::Highlight)); + m_ui->connectionView->rootContext()->setContextProperty("textColor", mainWidget->palette().color(QPalette::Active, QPalette::Text)); + m_ui->connectionView->rootContext()->setContextProperty("connectionModified", false); + m_ui->connectionView->setClearColor(Qt::transparent); + m_ui->connectionView->setResizeMode(QQuickWidget::SizeRootObjectToView); + m_ui->connectionView->setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kcm_networkmanagement/qml/main.qml")))); + + QObject *rootItem = m_ui->connectionView->rootObject(); connect(rootItem, SIGNAL(selectedConnectionChanged(QString)), this, SLOT(onSelectedConnectionChanged(QString))); connect(rootItem, SIGNAL(requestCreateConnection(int,QString,QString,bool)), this, SLOT(onRequestCreateConnection(int,QString,QString,bool))); connect(rootItem, SIGNAL(requestExportConnection(QString)), this, SLOT(onRequestExportConnection(QString))); connect(rootItem, SIGNAL(requestToChangeConnection(QString,QString)), this, SLOT(onRequestToChangeConnection(QString,QString))); QVBoxLayout *l = new QVBoxLayout(this); l->addWidget(mainWidget); setButtons(Button::Apply); NetworkManager::Connection::Ptr selectedConnection; // Look in the arguments for a connection ID to preselect static const QLatin1Literal uuidArgumentMarker { "Uuid=" }; for (QVariant arg : args) { if (arg.canConvert(QMetaType::QString)) { QString uuid = arg.toString(); if (uuid.startsWith(uuidArgumentMarker)) { uuid = uuid.replace(uuidArgumentMarker, QString()); selectedConnection = NetworkManager::findConnectionByUuid(uuid); qDebug() << "Selecting user connection:" << uuid; break; } } } // Pre-select the currently active primary connection if (!selectedConnection || !selectedConnection->isValid()) { NetworkManager::ActiveConnection::Ptr activeConnection = NetworkManager::primaryConnection(); if (activeConnection && activeConnection->isValid()) { selectedConnection = activeConnection->connection(); qDebug() << "Selecting active connection:" << selectedConnection->uuid(); } } // Select the very first connection as a fallback if (!selectedConnection || !selectedConnection->isValid()) { NetworkManager::Connection::List connectionList = NetworkManager::listConnections(); std::sort(connectionList.begin(), connectionList.end(), [] (const NetworkManager::Connection::Ptr &left, const NetworkManager::Connection::Ptr &right) { const QString leftName = left->settings()->id(); const UiUtils::SortedConnectionType leftType = UiUtils::connectionTypeToSortedType(left->settings()->connectionType()); const QDateTime leftDate = left->settings()->timestamp(); const QString rightName = right->settings()->id(); const UiUtils::SortedConnectionType rightType = UiUtils::connectionTypeToSortedType(right->settings()->connectionType()); const QDateTime rightDate = right->settings()->timestamp(); if (leftType < rightType) { return true; } else if (leftType > rightType) { return false; } if (leftDate > rightDate) { return true; } else if (leftDate < rightDate) { return false; } if (QString::localeAwareCompare(leftName, rightName) > 0) { return true; } else { return false; } }); for (const NetworkManager::Connection::Ptr &connection : connectionList) { const NetworkManager::ConnectionSettings::ConnectionType type = connection->settings()->connectionType(); if (UiUtils::isConnectionTypeSupported(type)) { selectedConnection = connection; qDebug() << "Selecting first connection:" << connection->uuid(); break; } } } if (selectedConnection && selectedConnection->isValid()) { const NetworkManager::ConnectionSettings::Ptr settings = selectedConnection->settings(); if (UiUtils::isConnectionTypeSupported(settings->connectionType())) { QMetaObject::invokeMethod(rootItem, "selectConnection", Q_ARG(QVariant, settings->id()), Q_ARG(QVariant, selectedConnection->path())); } } else { qDebug() << "Cannot preselect a connection"; } connect(NetworkManager::settingsNotifier(), &NetworkManager::SettingsNotifier::connectionAdded, this, &KCMNetworkmanagement::onConnectionAdded, Qt::UniqueConnection); // Initialize first scan and then scan every 15 seconds m_handler->requestScan(); m_timer = new QTimer(this); m_timer->setInterval(15000); connect(m_timer, &QTimer::timeout, [this] () { m_handler->requestScan(); }); m_timer->start(); } KCMNetworkmanagement::~KCMNetworkmanagement() { delete m_handler; if (m_tabWidget) { delete m_tabWidget; } - delete m_quickView; delete m_ui; } void KCMNetworkmanagement::defaults() { KCModule::defaults(); } void KCMNetworkmanagement::load() { // If there is no loaded connection do nothing if (m_currentConnectionPath.isEmpty()) { return; } NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(m_currentConnectionPath); if (connection) { NetworkManager::ConnectionSettings::Ptr connectionSettings = connection->settings(); // Re-load the connection again to load stored values if (m_tabWidget) { m_tabWidget->setConnection(connectionSettings); } } KCModule::load(); } void KCMNetworkmanagement::save() { NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(m_currentConnectionPath); if (connection) { m_handler->updateConnection(connection, m_tabWidget->setting()); } kcmChanged(false); KCModule::save(); } void KCMNetworkmanagement::onConnectionAdded(const QString &connection) { if (m_createdConnectionUuid.isEmpty()) { return; } NetworkManager::Connection::Ptr newConnection = NetworkManager::findConnection(connection); if (newConnection) { NetworkManager::ConnectionSettings::Ptr connectionSettings = newConnection->settings(); if (connectionSettings && connectionSettings->uuid() == m_createdConnectionUuid) { - QObject *rootItem = m_quickView->rootObject(); + QObject *rootItem = m_ui->connectionView->rootObject(); loadConnectionSettings(connectionSettings); QMetaObject::invokeMethod(rootItem, "selectConnection", Q_ARG(QVariant, connectionSettings->id()), Q_ARG(QVariant, newConnection->path())); m_createdConnectionUuid.clear(); } } } void KCMNetworkmanagement::onRequestCreateConnection(int connectionType, const QString &vpnType, const QString &specificType, bool shared) { NetworkManager::ConnectionSettings::ConnectionType type = static_cast(connectionType); if (type == NetworkManager::ConnectionSettings::Vpn && vpnType == "imported") { importVpn(); } else if (type == NetworkManager::ConnectionSettings::Gsm) { // launch the mobile broadband wizard, both gsm/cdma #if WITH_MODEMMANAGER_SUPPORT QPointer wizard = new MobileConnectionWizard(NetworkManager::ConnectionSettings::Unknown, this); connect(wizard.data(), &MobileConnectionWizard::accepted, [wizard, this] () { if (wizard->getError() == MobileProviders::Success) { qCDebug(PLASMA_NM) << "Mobile broadband wizard finished:" << wizard->type() << wizard->args(); if (wizard->args().count() == 2) { QVariantMap tmp = qdbus_cast(wizard->args().value(1)); NetworkManager::ConnectionSettings::Ptr connectionSettings; connectionSettings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(wizard->type())); connectionSettings->setId(wizard->args().value(0).toString()); if (wizard->type() == NetworkManager::ConnectionSettings::Gsm) { NetworkManager::GsmSetting::Ptr gsmSetting = connectionSettings->setting(NetworkManager::Setting::Gsm).staticCast(); gsmSetting->fromMap(tmp); gsmSetting->setPasswordFlags(NetworkManager::Setting::NotRequired); gsmSetting->setPinFlags(NetworkManager::Setting::NotRequired); } else if (wizard->type() == NetworkManager::ConnectionSettings::Cdma) { connectionSettings->setting(NetworkManager::Setting::Cdma)->fromMap(tmp); } else { qCWarning(PLASMA_NM) << Q_FUNC_INFO << "Unhandled setting type"; } // Generate new UUID connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); addConnection(connectionSettings); } else { qCWarning(PLASMA_NM) << Q_FUNC_INFO << "Unexpected number of args to parse"; } } }); connect(wizard.data(), &MobileConnectionWizard::finished, [wizard] () { if (wizard) { wizard->deleteLater(); } }); wizard->setModal(true); wizard->show(); #endif } else { NetworkManager::ConnectionSettings::Ptr connectionSettings; connectionSettings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(type)); if (type == NetworkManager::ConnectionSettings::Vpn) { NetworkManager::VpnSetting::Ptr vpnSetting = connectionSettings->setting(NetworkManager::Setting::Vpn).dynamicCast(); vpnSetting->setServiceType(vpnType); // Set VPN subtype in case of Openconnect to add support for juniper if (vpnType == QLatin1String("org.freedesktop.NetworkManager.openconnect")) { NMStringMap data = vpnSetting->data(); data.insert(QLatin1String("protocol"), specificType); vpnSetting->setData(data); } } if (type == NetworkManager::ConnectionSettings::Wired || type == NetworkManager::ConnectionSettings::Wireless) { // Set auto-negotiate to true, NM sets it to false by default, but we used to have this before and also // I don't think it's wise to request users to specify speed and duplex as most of them don't know what is that // and what to set if (type == NetworkManager::ConnectionSettings::Wired) { NetworkManager::WiredSetting::Ptr wiredSetting = connectionSettings->setting(NetworkManager::Setting::Wired).dynamicCast(); wiredSetting->setAutoNegotiate(true); } if (shared) { if (type == NetworkManager::ConnectionSettings::Wireless) { NetworkManager::WirelessSetting::Ptr wifiSetting = connectionSettings->setting(NetworkManager::Setting::Wireless).dynamicCast(); wifiSetting->setMode(NetworkManager::WirelessSetting::Adhoc); wifiSetting->setSsid(i18n("my_shared_connection").toUtf8()); for (const NetworkManager::Device::Ptr & device : NetworkManager::networkInterfaces()) { if (device->type() == NetworkManager::Device::Wifi) { NetworkManager::WirelessDevice::Ptr wifiDev = device.objectCast(); if (wifiDev) { if (wifiDev->wirelessCapabilities().testFlag(NetworkManager::WirelessDevice::ApCap)) { wifiSetting->setMode(NetworkManager::WirelessSetting::Ap); wifiSetting->setMacAddress(NetworkManager::macAddressFromString(wifiDev->permanentHardwareAddress())); } } } } } NetworkManager::Ipv4Setting::Ptr ipv4Setting = connectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast(); ipv4Setting->setMethod(NetworkManager::Ipv4Setting::Shared); connectionSettings->setAutoconnect(false); } } if (type == NetworkManager::ConnectionSettings::WireGuard) { NetworkManager::WireGuardSetting::Ptr wireguardSetting = connectionSettings->setting(NetworkManager::Setting::WireGuard).dynamicCast(); NetworkManager::Ipv4Setting::Ptr ipv4Setting = connectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast(); NetworkManager::Ipv6Setting::Ptr ipv6Setting = connectionSettings->setting(NetworkManager::Setting::Ipv6).dynamicCast(); connectionSettings->setAutoconnect(false); ipv4Setting->setMethod(NetworkManager::Ipv4Setting::Disabled); ipv6Setting->setMethod(NetworkManager::Ipv6Setting::Ignored); } // Generate new UUID connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); addConnection(connectionSettings); } } void KCMNetworkmanagement::onRequestExportConnection(const QString &connectionPath) { NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(connectionPath); if (!connection) { return; } NetworkManager::ConnectionSettings::Ptr connSettings = connection->settings(); if (connSettings->connectionType() != NetworkManager::ConnectionSettings::Vpn) return; NetworkManager::VpnSetting::Ptr vpnSetting = connSettings->setting(NetworkManager::Setting::Vpn).dynamicCast(); qCDebug(PLASMA_NM) << "Exporting VPN connection" << connection->name() << "type:" << vpnSetting->serviceType(); QString error; VpnUiPlugin * vpnPlugin = KServiceTypeTrader::createInstanceFromQuery(QStringLiteral("PlasmaNetworkManagement/VpnUiPlugin"), QStringLiteral("[X-NetworkManager-Services]=='%1'").arg(vpnSetting->serviceType()), this, QVariantList(), &error); if (vpnPlugin) { if (vpnPlugin->suggestedFileName(connSettings).isEmpty()) { // this VPN doesn't support export qCWarning(PLASMA_NM) << "This VPN doesn't support export"; return; } const QString url = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + QDir::separator() + vpnPlugin->suggestedFileName(connSettings); const QString filename = QFileDialog::getSaveFileName(this, i18n("Export VPN Connection"), url, vpnPlugin->supportedFileExtensions()); if (!filename.isEmpty()) { if (!vpnPlugin->exportConnectionSettings(connSettings, filename)) { // TODO display failure qCWarning(PLASMA_NM) << "Failed to export VPN connection"; } else { // TODO display success } } delete vpnPlugin; } else { qCWarning(PLASMA_NM) << "Error getting VpnUiPlugin for export:" << error; } } void KCMNetworkmanagement::onRequestToChangeConnection( const QString &connectionName, const QString &connectionPath) { NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(m_currentConnectionPath); if (connection) { if (KMessageBox::questionYesNo(this, i18n("Do you want to save changes made to the connection '%1'?", connection->name()), i18nc("@title:window", "Save Changes"), KStandardGuiItem::save(), KStandardGuiItem::discard(), QString(), KMessageBox::Notify) == KMessageBox::Yes) { save(); } } - QObject *rootItem = m_quickView->rootObject(); + QObject *rootItem = m_ui->connectionView->rootObject(); QMetaObject::invokeMethod(rootItem, "selectConnection", Q_ARG(QVariant, connectionName), Q_ARG(QVariant, connectionPath)); } void KCMNetworkmanagement::onSelectedConnectionChanged(const QString &connectionPath) { if (connectionPath.isEmpty()) { resetSelection(); return; } m_currentConnectionPath = connectionPath; NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(m_currentConnectionPath); if (connection) { NetworkManager::ConnectionSettings::Ptr connectionSettings = connection->settings(); loadConnectionSettings(connectionSettings); } } void KCMNetworkmanagement::addConnection(const NetworkManager::ConnectionSettings::Ptr &connectionSettings) { QPointer editor = new ConnectionEditorDialog(connectionSettings); connect(editor.data(), &ConnectionEditorDialog::accepted, [connectionSettings, editor, this] () { // We got confirmation so watch this connection and select it once it is created m_createdConnectionUuid = connectionSettings->uuid(); m_handler->addConnection(editor->setting()); }); connect(editor.data(), &ConnectionEditorDialog::finished, [editor] () { if (editor) { editor->deleteLater(); } }); editor->setModal(true); editor->show(); } void KCMNetworkmanagement::kcmChanged(bool kcmChanged) { - m_quickView->rootContext()->setContextProperty("connectionModified", kcmChanged); + m_ui->connectionView->rootContext()->setContextProperty("connectionModified", kcmChanged); Q_EMIT changed(kcmChanged); } void KCMNetworkmanagement::loadConnectionSettings(const NetworkManager::ConnectionSettings::Ptr& connectionSettings) { if (m_tabWidget) { m_tabWidget->setConnection(connectionSettings); } else { m_tabWidget = new ConnectionEditorTabWidget(connectionSettings); connect(m_tabWidget, &ConnectionEditorTabWidget::settingChanged, [this] () { if (m_tabWidget->isInitialized() && m_tabWidget->isValid()) { kcmChanged(true); } }); connect(m_tabWidget, &ConnectionEditorTabWidget::validityChanged, [this] (bool valid) { if (m_tabWidget->isInitialized() && m_tabWidget->isValid() != valid) { kcmChanged(valid); } }); QVBoxLayout *layout = new QVBoxLayout(m_ui->connectionConfiguration); layout->addWidget(m_tabWidget); } kcmChanged(false); } void KCMNetworkmanagement::importVpn() { // get the list of supported extensions const KService::List services = KServiceTypeTrader::self()->query("PlasmaNetworkManagement/VpnUiPlugin"); QString extensions; for (const KService::Ptr &service : services) { VpnUiPlugin * vpnPlugin = service->createInstance(this); if (vpnPlugin) { extensions += vpnPlugin->supportedFileExtensions() % QStringLiteral(" "); delete vpnPlugin; } } const QString &filename = QFileDialog::getOpenFileName(this, i18n("Import VPN Connection"), QDir::homePath(), extensions.simplified()); if (!filename.isEmpty()) { const KService::List services = KServiceTypeTrader::self()->query("PlasmaNetworkManagement/VpnUiPlugin"); QFileInfo fi(filename); const QString ext = QStringLiteral("*.") % fi.suffix(); qCDebug(PLASMA_NM) << "Importing VPN connection " << filename << "extension:" << ext; // Handle WireGuard separately because it is different than all the other VPNs if (WireGuardInterfaceWidget::supportedFileExtensions().contains(ext)) { NMVariantMapMap connection = WireGuardInterfaceWidget::importConnectionSettings(filename); NetworkManager::ConnectionSettings connectionSettings; connectionSettings.fromMap(connection); connectionSettings.setUuid(NetworkManager::ConnectionSettings::createNewUuid()); // qCDebug(PLASMA_NM) << "Converted connection:" << connectionSettings; m_handler->addConnection(connectionSettings.toMap()); // qCDebug(PLASMA_NM) << "Adding imported connection under id:" << conId; if (!connection.isEmpty()) { return; // get out if the import produced at least some output } } for (const KService::Ptr &service : services) { VpnUiPlugin * vpnPlugin = service->createInstance(this); if (vpnPlugin && vpnPlugin->supportedFileExtensions().contains(ext)) { qCDebug(PLASMA_NM) << "Found VPN plugin" << service->name() << ", type:" << service->property("X-NetworkManager-Services", QVariant::String).toString(); NMVariantMapMap connection = vpnPlugin->importConnectionSettings(filename); // qCDebug(PLASMA_NM) << "Raw connection:" << connection; NetworkManager::ConnectionSettings connectionSettings; connectionSettings.fromMap(connection); connectionSettings.setUuid(NetworkManager::ConnectionSettings::createNewUuid()); // qCDebug(PLASMA_NM) << "Converted connection:" << connectionSettings; m_handler->addConnection(connectionSettings.toMap()); // qCDebug(PLASMA_NM) << "Adding imported connection under id:" << conId; if (connection.isEmpty()) { // the "positive" part will arrive with connectionAdded // TODO display success } else { delete vpnPlugin; break; // stop iterating over the plugins if the import produced at least some output } delete vpnPlugin; } } } } void KCMNetworkmanagement::resetSelection() { // Reset selected connections m_currentConnectionPath.clear(); - QObject *rootItem = m_quickView->rootObject(); + QObject *rootItem = m_ui->connectionView->rootObject(); QMetaObject::invokeMethod(rootItem, "deselectConnections"); if (m_tabWidget) { delete m_ui->connectionConfiguration->layout(); delete m_tabWidget; m_tabWidget = nullptr; } Q_EMIT changed(false); } #include "kcm.moc" diff --git a/kcm/kcm.h b/kcm/kcm.h index a232fa35..1e45c893 100755 --- a/kcm/kcm.h +++ b/kcm/kcm.h @@ -1,67 +1,66 @@ /* Copyright 2016 Jan Grulich This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef PLASMA_NM_KCM_H #define PLASMA_NM_KCM_H #include "connectioneditortabwidget.h" #include "handler.h" #include #include class QQuickView; class KCMNetworkmanagement : public KCModule { Q_OBJECT public: explicit KCMNetworkmanagement(QWidget *parent = nullptr, const QVariantList &args = QVariantList()); ~KCMNetworkmanagement() override; public Q_SLOTS: void defaults() override; void load() override; void save() override; private Q_SLOTS: void onConnectionAdded(const QString &connection); void onSelectedConnectionChanged(const QString &connectionPath); void onRequestCreateConnection(int connectionType, const QString &vpnType, const QString &specificType, bool shared); void onRequestExportConnection(const QString &connectionPath); void onRequestToChangeConnection(const QString &connectionName, const QString &connectionPath); private: void addConnection(const NetworkManager::ConnectionSettings::Ptr &connectionSettings); void importVpn(); void kcmChanged(bool kcmChanged); void loadConnectionSettings(const NetworkManager::ConnectionSettings::Ptr &connectionSettings); void resetSelection(); QString m_currentConnectionPath; QString m_createdConnectionUuid; Handler *m_handler; ConnectionEditorTabWidget *m_tabWidget; QTimer *m_timer; Ui::KCMForm *m_ui; - QQuickView *m_quickView; }; #endif diff --git a/kcm/kcm.ui b/kcm/kcm.ui index 94f99227..5e866bea 100755 --- a/kcm/kcm.ui +++ b/kcm/kcm.ui @@ -1,40 +1,47 @@ KCMForm 0 0 800 500 0 0 800 500 - + - - - Qt::LeftToRight + + + QQuickWidget::SizeRootObjectToView + + + QQuickWidget + QWidget +
QtQuickWidgets/QQuickWidget
+
+
diff --git a/kcm/qml/main.qml b/kcm/qml/main.qml index 7dc0eb31..d38a2834 100755 --- a/kcm/qml/main.qml +++ b/kcm/qml/main.qml @@ -1,214 +1,213 @@ /* Copyright 2016 Jan Grulich This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ import QtQuick 2.1 import QtQuick.Dialogs 1.1 import QtQuick.Controls 1.2 as QtControls import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.plasma.networkmanagement 0.2 as PlasmaNM Item { id: root - anchors.fill: parent focus: true signal selectedConnectionChanged(string connection) signal requestCreateConnection(int type, string vpnType, string specificType, bool shared) signal requestExportConnection(string connection) signal requestToChangeConnection(string name, string path) Rectangle { id: background anchors.fill: parent focus: true color: baseColor } PlasmaNM.Handler { id: handler } PlasmaNM.KcmIdentityModel { id: connectionModel } PlasmaNM.EditorProxyModel { id: editorProxyModel sourceModel: connectionModel } QtControls.TextField { id: searchField anchors { left: parent.left right: parent.right top: parent.top } placeholderText: i18n("Search...") onTextChanged: { editorProxyModel.setFilterRegExp(text) } } QtControls.ScrollView { id: scrollView anchors { bottom: buttonRow.top bottomMargin: Math.round(units.gridUnit / 3) left: parent.left right: parent.right top: searchField.bottom } ListView { id: connectionView property bool currentConnectionExportable: false property string currentConnectionName property string currentConnectionPath anchors.fill: parent clip: true model: editorProxyModel currentIndex: -1 boundsBehavior: Flickable.StopAtBounds section.property: "KcmConnectionType" section.delegate: Header { text: section } delegate: ConnectionItem { onAboutToChangeConnection: { // Shouldn't be problem to set this in advance connectionView.currentConnectionExportable = exportable if (connectionModified) { requestToChangeConnection(name, path) } else { connectionView.currentConnectionName = name connectionView.currentConnectionPath = path } } onAboutToExportConnection: { requestExportConnection(path) } onAboutToRemoveConnection: { deleteConfirmationDialog.connectionName = name deleteConfirmationDialog.connectionPath = path deleteConfirmationDialog.open() } } onCurrentConnectionPathChanged: { root.selectedConnectionChanged(currentConnectionPath) } } } Row { id: buttonRow anchors { bottom: parent.bottom right: parent.right margins: Math.round(units.gridUnit / 3) } spacing: Math.round(units.gridUnit / 2) QtControls.ToolButton { id: addConnectionButton iconName: "list-add" tooltip: i18n("Add new connection") onClicked: { addNewConnectionDialog.open() } } QtControls.ToolButton { id: removeConnectionButton enabled: connectionView.currentConnectionPath && connectionView.currentConnectionPath.length iconName: "list-remove" tooltip: i18n("Remove selected connection") onClicked: { deleteConfirmationDialog.connectionName = connectionView.currentConnectionName deleteConfirmationDialog.connectionPath = connectionView.currentConnectionPath deleteConfirmationDialog.open() } } QtControls.ToolButton { id: exportConnectionButton enabled: connectionView.currentConnectionExportable iconName: "document-export" tooltip: i18n("Export selected connection") onClicked: { root.requestExportConnection(connectionView.currentConnectionPath) } } } MessageDialog { id: deleteConfirmationDialog property string connectionName property string connectionPath icon: StandardIcon.Question standardButtons: StandardButton.Ok | StandardButton.Cancel title: i18nc("@title:window", "Remove Connection") text: i18n("Do you want to remove the connection '%1'?", connectionName) onAccepted: { if (connectionPath == connectionView.currentConnectionPath) { // Deselect now non-existing connection deselectConnections() } handler.removeConnection(connectionPath) } } Dialog { id: addNewConnectionDialog onRequestCreateConnection: { root.requestCreateConnection(type, vpnType, specificType, shared) } } function deselectConnections() { connectionView.currentConnectionPath = "" } function selectConnection(connectionName, connectionPath) { connectionView.currentConnectionName = connectionName connectionView.currentConnectionPath = connectionPath } }