File indexing completed on 2024-07-21 07:58:05

0001 /*
0002     SPDX-FileCopyrightText: 2014 Lukas Tinkl <ltinkl@redhat.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "teamwidget.h"
0008 #include "connectioneditordialog.h"
0009 #include "plasma_nm_editor.h"
0010 #include "ui_team.h"
0011 
0012 #include <QDBusPendingReply>
0013 #include <QDesktopServices>
0014 #include <QFileDialog>
0015 #include <QStandardPaths>
0016 
0017 #include <NetworkManagerQt/Connection>
0018 #include <NetworkManagerQt/ConnectionSettings>
0019 #include <NetworkManagerQt/GenericTypes>
0020 #include <NetworkManagerQt/Settings>
0021 
0022 #include <KLocalizedString>
0023 #include <KMessageBox>
0024 #include <kwidgetsaddons_version.h>
0025 
0026 TeamWidget::TeamWidget(const QString &masterUuid, const QString &masterId, const NetworkManager::Setting::Ptr &setting, QWidget *parent, Qt::WindowFlags f)
0027     : SettingWidget(setting, parent, f)
0028     , m_uuid(masterUuid)
0029     , m_id(masterId)
0030     , m_ui(new Ui::TeamWidget)
0031     , m_menu(new QMenu(this))
0032 {
0033     m_ui->setupUi(this);
0034 
0035     // Action buttons and menu
0036     auto action = new QAction(i18n("Ethernet"), this);
0037     action->setData(NetworkManager::ConnectionSettings::Wired);
0038     m_menu->addAction(action);
0039     action = new QAction(i18n("Infiniband"), this);
0040     action->setData(NetworkManager::ConnectionSettings::Infiniband);
0041     m_menu->addAction(action);
0042     action = new QAction(i18n("Wi-Fi"), this);
0043     action->setData(NetworkManager::ConnectionSettings::Wireless);
0044     m_menu->addAction(action);
0045     action = new QAction(i18n("VLAN"), this);
0046     action->setData(NetworkManager::ConnectionSettings::Vlan);
0047     m_menu->addAction(action);
0048     m_ui->btnAdd->setMenu(m_menu);
0049     connect(m_menu, &QMenu::triggered, this, &TeamWidget::addTeam);
0050     connect(m_ui->btnEdit, &QPushButton::clicked, this, &TeamWidget::editTeam);
0051     connect(m_ui->btnDelete, &QPushButton::clicked, this, &TeamWidget::deleteTeam);
0052 
0053     connect(m_ui->btnImport, &QPushButton::clicked, this, &TeamWidget::importConfig);
0054 
0055     // teams
0056     populateTeams();
0057     connect(m_ui->teams, &QListWidget::currentItemChanged, this, &TeamWidget::currentTeamChanged);
0058     connect(m_ui->teams, &QListWidget::itemDoubleClicked, this, &TeamWidget::editTeam);
0059 
0060     connect(m_ui->ifaceName, &KLineEdit::textChanged, this, &TeamWidget::slotWidgetChanged);
0061 
0062     // Connect for setting check
0063     watchChangedSetting();
0064 
0065     KAcceleratorManager::manage(this);
0066     KAcceleratorManager::manage(m_menu);
0067 
0068     if (setting) {
0069         loadConfig(setting);
0070     }
0071 }
0072 
0073 TeamWidget::~TeamWidget()
0074 {
0075     delete m_ui;
0076 }
0077 
0078 void TeamWidget::loadConfig(const NetworkManager::Setting::Ptr &setting)
0079 {
0080     NetworkManager::TeamSetting::Ptr teamSetting = setting.staticCast<NetworkManager::TeamSetting>();
0081 
0082     m_ui->ifaceName->setText(teamSetting->interfaceName());
0083     m_ui->config->setPlainText(teamSetting->config());
0084 }
0085 
0086 QVariantMap TeamWidget::setting() const
0087 {
0088     NetworkManager::TeamSetting setting;
0089     setting.setInterfaceName(m_ui->ifaceName->text());
0090     setting.setConfig(m_ui->config->toPlainText());
0091 
0092     return setting.toMap();
0093 }
0094 
0095 void TeamWidget::addTeam(QAction *action)
0096 {
0097     qCDebug(PLASMA_NM_EDITOR_LOG) << "Adding teamed connection:" << action->data();
0098     qCDebug(PLASMA_NM_EDITOR_LOG) << "Master UUID:" << m_uuid;
0099     qCDebug(PLASMA_NM_EDITOR_LOG) << "Slave type:" << type();
0100 
0101     NetworkManager::ConnectionSettings::ConnectionType connectionType = static_cast<NetworkManager::ConnectionSettings::ConnectionType>(action->data().toInt());
0102     NetworkManager::ConnectionSettings::Ptr connectionSettings =
0103         NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(connectionType));
0104     connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid());
0105     connectionSettings->setMaster(m_uuid);
0106     connectionSettings->setSlaveType(type());
0107     connectionSettings->setAutoconnect(false);
0108 
0109     QPointer<ConnectionEditorDialog> teamEditor = new ConnectionEditorDialog(connectionSettings);
0110     teamEditor->setAttribute(Qt::WA_DeleteOnClose);
0111     connect(teamEditor.data(), &ConnectionEditorDialog::accepted, [teamEditor, this]() {
0112         qCDebug(PLASMA_NM_EDITOR_LOG) << "Saving slave connection";
0113         // qCDebug(PLASMA_NM_EDITOR_LOG) << teamEditor->setting();
0114         QDBusPendingReply<QDBusObjectPath> reply = NetworkManager::addConnection(teamEditor->setting());
0115         auto watcher = new QDBusPendingCallWatcher(reply, this);
0116         connect(watcher, &QDBusPendingCallWatcher::finished, this, &TeamWidget::teamAddComplete);
0117     });
0118     teamEditor->setModal(true);
0119     teamEditor->show();
0120 }
0121 
0122 void TeamWidget::currentTeamChanged(QListWidgetItem *current, QListWidgetItem *previous)
0123 {
0124     Q_UNUSED(previous)
0125 
0126     m_ui->btnEdit->setEnabled(current);
0127     m_ui->btnDelete->setEnabled(current);
0128 }
0129 
0130 void TeamWidget::teamAddComplete(QDBusPendingCallWatcher *watcher)
0131 {
0132     QDBusPendingReply<QDBusObjectPath> reply = *watcher;
0133 
0134     if (reply.isValid()) {
0135         // find the slave connection with matching UUID
0136         NetworkManager::Connection::Ptr connection = NetworkManager::findConnection(reply.value().path());
0137         if (connection && connection->settings()->master() == m_uuid) {
0138             const QString label = QString("%1 (%2)").arg(connection->name(), connection->settings()->typeAsString(connection->settings()->connectionType()));
0139             auto slaveItem = new QListWidgetItem(label, m_ui->teams);
0140             slaveItem->setData(Qt::UserRole, connection->uuid());
0141             slotWidgetChanged();
0142         }
0143     } else {
0144         qCWarning(PLASMA_NM_EDITOR_LOG) << "Teamed connection not added:" << reply.error().message();
0145     }
0146 }
0147 
0148 void TeamWidget::editTeam()
0149 {
0150     QListWidgetItem *currentItem = m_ui->teams->currentItem();
0151     if (!currentItem)
0152         return;
0153 
0154     const QString uuid = currentItem->data(Qt::UserRole).toString();
0155     NetworkManager::Connection::Ptr connection = NetworkManager::findConnectionByUuid(uuid);
0156 
0157     if (connection) {
0158         qCDebug(PLASMA_NM_EDITOR_LOG) << "Editing teamed connection" << currentItem->text() << uuid;
0159         QPointer<ConnectionEditorDialog> teamEditor = new ConnectionEditorDialog(connection->settings());
0160         teamEditor->setAttribute(Qt::WA_DeleteOnClose);
0161         connect(teamEditor.data(), &ConnectionEditorDialog::accepted, [connection, teamEditor, this]() {
0162             connection->update(teamEditor->setting());
0163             connect(connection.data(), &NetworkManager::Connection::updated, this, &TeamWidget::populateTeams);
0164         });
0165         teamEditor->setModal(true);
0166         teamEditor->show();
0167     }
0168 }
0169 
0170 void TeamWidget::deleteTeam()
0171 {
0172     QListWidgetItem *currentItem = m_ui->teams->currentItem();
0173     if (!currentItem)
0174         return;
0175 
0176     const QString uuid = currentItem->data(Qt::UserRole).toString();
0177     NetworkManager::Connection::Ptr connection = NetworkManager::findConnectionByUuid(uuid);
0178 
0179     if (connection) {
0180         qCDebug(PLASMA_NM_EDITOR_LOG) << "About to delete teamed connection" << currentItem->text() << uuid;
0181         if (KMessageBox::questionTwoActions(this,
0182                                             i18n("Do you want to remove the connection '%1'?", connection->name()),
0183                                             i18n("Remove Connection"),
0184                                             KStandardGuiItem::remove(),
0185                                             KStandardGuiItem::cancel(),
0186                                             QString(),
0187                                             KMessageBox::Dangerous)
0188             == KMessageBox::ButtonCode::PrimaryAction) {
0189             connection->remove();
0190             delete currentItem;
0191             slotWidgetChanged();
0192         }
0193     }
0194 }
0195 
0196 void TeamWidget::populateTeams()
0197 {
0198     m_ui->teams->clear();
0199 
0200     for (const NetworkManager::Connection::Ptr &connection : NetworkManager::listConnections()) {
0201         NetworkManager::ConnectionSettings::Ptr settings = connection->settings();
0202         // The mapping from slave to master may be by uuid or name, try our best to
0203         // figure out if we are master to the slave.
0204         const QString master = settings->master();
0205         bool isSlave = ((master == m_uuid) || // by-uuid
0206                         (!m_id.isEmpty() && master == m_id)); // by-name
0207         if (isSlave && (settings->slaveType() == type())) {
0208             const QString label =
0209                 QStringLiteral("%1 (%2)").arg(connection->name(), connection->settings()->typeAsString(connection->settings()->connectionType()));
0210             auto slaveItem = new QListWidgetItem(label, m_ui->teams);
0211             slaveItem->setData(Qt::UserRole, connection->uuid());
0212         }
0213     }
0214 }
0215 
0216 void TeamWidget::importConfig()
0217 {
0218     const QString filename =
0219         QFileDialog::getOpenFileName(this, i18n("Select file to import"), QStandardPaths::writableLocation(QStandardPaths::HomeLocation), "text/plain");
0220     if (!filename.isEmpty()) {
0221         // qCDebug(PLASMA_NM_EDITOR_LOG) << "Importing" << filename;
0222         QFile file(filename);
0223         if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
0224             QTextStream stream(&file);
0225             m_ui->config->setPlainText(stream.readAll());
0226             file.close();
0227         }
0228     }
0229 }
0230 
0231 bool TeamWidget::isValid() const
0232 {
0233     return !m_ui->ifaceName->text().isEmpty() && m_ui->teams->count() > 0;
0234 }
0235 
0236 #include "moc_teamwidget.cpp"