diff --git a/kcmkwin/kwindesktop/CMakeLists.txt b/kcmkwin/kwindesktop/CMakeLists.txt --- a/kcmkwin/kwindesktop/CMakeLists.txt +++ b/kcmkwin/kwindesktop/CMakeLists.txt @@ -1,33 +1,27 @@ +include(ECMQMLModules) +ecm_find_qmlmodule(org.kde.plasma.core 2.0) -########### next target ############### -# KI18N Translation Domain for this library -add_definitions(-DTRANSLATION_DOMAIN=\"kcm_kwindesktop\") +# KI18N Translation Domain for this library. +add_definitions(-DTRANSLATION_DOMAIN=\"kcm_kwin_virtualdesktops\") -include_directories(${KWIN_SOURCE_DIR}/effects) +########### next target ############### -set(kcm_kwindesktop_PART_SRCS main.cpp desktopnameswidget.cpp) -ki18n_wrap_ui(kcm_kwindesktop_PART_SRCS main.ui) -qt5_add_dbus_interface( kcm_kwindesktop_PART_SRCS - ${KWIN_SOURCE_DIR}/org.kde.kwin.Effects.xml kwin_effects_interface) +set(kcm_kwin_virtualdesktops_PART_SRCS virtualdesktops.cpp desktopsmodel.cpp ../../virtualdesktopsdbustypes.cpp) -add_library(kcm_kwindesktop MODULE ${kcm_kwindesktop_PART_SRCS}) +add_library(kcm_kwin_virtualdesktops MODULE ${kcm_kwin_virtualdesktops_PART_SRCS}) -target_link_libraries(kcm_kwindesktop - Qt5::X11Extras - KF5::KCMUtils - KF5::Completion - KF5::GlobalAccel +target_link_libraries(kcm_kwin_virtualdesktops + Qt5::DBus KF5::I18n - KF5::Package - KF5::WindowSystem - KF5::XmlGui - ${X11_LIBRARIES} - kwin4_effect_builtins + KF5::KCMUtils + KF5::QuickAddons ) -install(TARGETS kcm_kwindesktop DESTINATION ${PLUGIN_INSTALL_DIR} ) - +kcoreaddons_desktop_to_json(kcm_kwin_virtualdesktops "kcm_kwin_virtualdesktops.desktop") ########### install files ############### -install( FILES desktop.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + +install(TARGETS kcm_kwin_virtualdesktops DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms) +install(FILES kcm_kwin_virtualdesktops.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) +kpackage_install_package(package kcm_kwin_virtualdesktops kcms) diff --git a/kcmkwin/kwindesktop/Messages.sh b/kcmkwin/kwindesktop/Messages.sh --- a/kcmkwin/kwindesktop/Messages.sh +++ b/kcmkwin/kwindesktop/Messages.sh @@ -1,4 +1,2 @@ #! /usr/bin/env bash -$EXTRACTRC `find . -name \*.ui` >> rc.cpp || exit 11 -$XGETTEXT *.cpp -o $podir/kcm_kwindesktop.pot -rm -f rc.cpp +$XGETTEXT `find . -name \*.cpp -o -name \*.qml` -o $podir/kcm_kwin_virtualdesktops.pot diff --git a/kcmkwin/kwindesktop/desktopnameswidget.h b/kcmkwin/kwindesktop/desktopnameswidget.h deleted file mode 100644 --- a/kcmkwin/kwindesktop/desktopnameswidget.h +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************** -KWin - the KDE window manager -This file is part of the KDE project. - -Copyright (C) 2009 Martin Gräßlin - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*********************************************************************/ - -#ifndef DESKTOPNAMESWIDGET_H -#define DESKTOPNAMESWIDGET_H - -#include -#include - -class KLineEdit; -class QLabel; -class QGridLayout; - -namespace KWin -{ -class KWinDesktopConfig; - -class DesktopNamesWidget : public QWidget -{ - Q_OBJECT -public: - explicit DesktopNamesWidget(QWidget *parent); - ~DesktopNamesWidget(); - QString name(int desktop); - void setName(int desktop, QString desktopName); - void setDefaultName(int desktop); - void setMaxDesktops(int maxDesktops); - void setDesktopConfig(KWinDesktopConfig *desktopConfig); - -Q_SIGNALS: - void changed(); - -public Q_SLOTS: - void numberChanged(int number); - -private: - QList< QLabel* > m_nameLabels; - QList< KLineEdit* > m_nameInputs; - QGridLayout* m_namesLayout; - int m_maxDesktops; - KWinDesktopConfig *m_desktopConfig; -}; - -} // namespace - -#endif // DESKTOPNAMESWIDGET_H diff --git a/kcmkwin/kwindesktop/desktopnameswidget.cpp b/kcmkwin/kwindesktop/desktopnameswidget.cpp deleted file mode 100644 --- a/kcmkwin/kwindesktop/desktopnameswidget.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/******************************************************************** -KWin - the KDE window manager -This file is part of the KDE project. - -Copyright (C) 2009 Martin Gräßlin - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*********************************************************************/ - -#include "desktopnameswidget.h" -#include "main.h" - -#include -#include - -#include -#include - -namespace KWin -{ - -DesktopNamesWidget::DesktopNamesWidget(QWidget *parent) - : QWidget(parent) - , m_maxDesktops(0) - , m_desktopConfig(0) -{ - m_namesLayout = new QGridLayout; - m_namesLayout->setMargin(0); - - setLayout(m_namesLayout); -} - -DesktopNamesWidget::~DesktopNamesWidget() -{ -} - -void DesktopNamesWidget::numberChanged(int number) -{ - if ((number < 1) || (number > m_maxDesktops)) - return; - if (m_nameInputs.size() != number) { - if (number < m_nameInputs.size()) { - // remove widgets - while (number != m_nameInputs.size()) { - KLineEdit* edit = m_nameInputs.last(); - m_nameInputs.removeLast(); - delete edit; - QLabel* label = m_nameLabels.last(); - m_nameLabels.removeLast(); - delete label; - } - } else { - // add widgets - while (number != m_nameInputs.size()) { - int desktop = m_nameInputs.size(); - QLabel* label = new QLabel(i18n("Desktop %1:", desktop + 1), this); - KLineEdit* edit = new KLineEdit(this); - label->setWhatsThis(i18n("Here you can enter the name for desktop %1", desktop + 1)); - edit->setWhatsThis(i18n("Here you can enter the name for desktop %1", desktop + 1)); - - m_namesLayout->addWidget(label, desktop % 10, 0 + 2 *(desktop >= 10), 1, 1); - m_namesLayout->addWidget(edit, desktop % 10, 1 + 2 *(desktop >= 10), 1, 1); - m_nameInputs << edit; - m_nameLabels << label; - - setDefaultName(desktop + 1); - if (desktop > 1) { - setTabOrder(m_nameInputs[desktop - 1], m_nameInputs[desktop]); - } - connect(edit, SIGNAL(textChanged(QString)), SIGNAL(changed())); - } - } - } -} - -QString DesktopNamesWidget::name(int desktop) -{ - if ((desktop < 1) || (desktop > m_maxDesktops) || (desktop > m_nameInputs.size())) - return QString(); - return m_nameInputs[ desktop -1 ]->text(); -} - - -void DesktopNamesWidget::setName(int desktop, QString desktopName) -{ - if ((desktop < 1) || (desktop > m_maxDesktops) || (desktop > m_nameInputs.size())) - return; - m_nameInputs[ desktop-1 ]->setText(desktopName); -} - -void DesktopNamesWidget::setDefaultName(int desktop) -{ - if ((desktop < 1) || (desktop > m_maxDesktops)) - return; - QString name = m_desktopConfig->cachedDesktopName(desktop); - if (name.isEmpty()) - name = i18n("Desktop %1", desktop); - m_nameInputs[ desktop -1 ]->setText(name); -} - - -void DesktopNamesWidget::setMaxDesktops(int maxDesktops) -{ - m_maxDesktops = maxDesktops; -} - -void DesktopNamesWidget::setDesktopConfig(KWinDesktopConfig* desktopConfig) -{ - m_desktopConfig = desktopConfig; -} - -} // namespace - diff --git a/kcmkwin/kwindesktop/desktopsmodel.h b/kcmkwin/kwindesktop/desktopsmodel.h new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindesktop/desktopsmodel.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2018 Eike Hein + * Copyright (C) 2018 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef DESKTOPSMODEL_H +#define DESKTOPSMODEL_H + +#include + +#include "../virtualdesktopsdbustypes.h" + +class QDBusArgument; +class QDBusMessage; +class QDBusServiceWatcher; + + +namespace KWin +{ + +/** + * @short An item model around KWin's D-Bus API for virtual desktops. + * + * The model initially gets the state from KWin and populates. + * + * As long as the user makes no changes, KWin-side changes are directly + * exposed in the model. + * + * If the user makes changes (see the `userModified` property), it stops + * exposing KWin-side changes live, but it keeps track of the KWin-side + * changes, so it can figure out and apply the delta when `syncWithServer` + * is called. + * + * When KWin-side changes happen while the model is user-modified, the + * model signals this via the `serverModified` property. A call to + * `syncWithServer` will overwrite the KWin-side changes. + * + * After synchronization, the model tracks Kwin-side changes again, + * until the user makes further changes. + * + * @author Eike Hein + **/ + +class DesktopsModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(bool ready READ ready NOTIFY readyChanged) + Q_PROPERTY(QString error READ error NOTIFY errorChanged) + Q_PROPERTY(bool userModified READ userModified NOTIFY userModifiedChanged) + Q_PROPERTY(bool serverModified READ serverModified NOTIFY serverModifiedChanged) + Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged) + +public: + enum AdditionalRoles { + Id = Qt::UserRole + 1, + DesktopRow + }; + Q_ENUM(AdditionalRoles) + + explicit DesktopsModel(QObject *parent = nullptr); + ~DesktopsModel() override; + + QHash roleNames() const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex &parent = {}) const override; + + bool ready() const; + QString error() const; + + bool userModified() const; + bool serverModified() const; + + int rows() const; + void setRows(int rows); + + Q_INVOKABLE void createDesktop(const QString &name); + Q_INVOKABLE void removeDesktop(const QString &id); + Q_INVOKABLE void setDesktopName(const QString &id, const QString &name); + + Q_INVOKABLE void syncWithServer(); + +Q_SIGNALS: + void readyChanged() const; + void errorChanged() const; + void userModifiedChanged() const; + void serverModifiedChanged() const; + void rowsChanged() const; + +protected Q_SLOTS: + void reset(); + void getAllAndConnect(const QDBusMessage &msg); + void desktopCreated(const QString &id, const KWin::DBusDesktopDataStruct &data); + void desktopRemoved(const QString &id); + void desktopDataChanged(const QString &id, const KWin::DBusDesktopDataStruct &data); + void desktopRowsChanged(uint rows); + void updateModifiedState(bool server = false); + void handleCallError(); + +private: + QDBusServiceWatcher *m_serviceWatcher; + QString m_error; + bool m_userModified; + bool m_serverModified; + QStringList m_serverSideDesktops; + QHash m_serverSideNames; + int m_serverSideRows; + QStringList m_desktops; + QHash m_names; + int m_rows; + bool m_synchronizing; +}; + +} + +#endif diff --git a/kcmkwin/kwindesktop/desktopsmodel.cpp b/kcmkwin/kwindesktop/desktopsmodel.cpp new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindesktop/desktopsmodel.cpp @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2018 Eike Hein + * Copyright (C) 2018 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "desktopsmodel.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace KWin +{ + +static const QString s_serviceName(QStringLiteral("org.kde.KWin")); +static const QString s_virtualDesktopsInterface(QStringLiteral("org.kde.KWin.VirtualDesktopManager")); +static const QString s_virtDesktopsPath(QStringLiteral("/VirtualDesktopManager")); +static const QString s_fdoPropertiesInterface(QStringLiteral("org.freedesktop.DBus.Properties")); + +DesktopsModel::DesktopsModel(QObject *parent) + : QAbstractListModel(parent) + , m_userModified(false) + , m_serverModified(false) + , m_serverSideRows(-1) + , m_rows(-1) + , m_synchronizing(false) +{ + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + + m_serviceWatcher = new QDBusServiceWatcher(s_serviceName, + QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange); + + QObject::connect(m_serviceWatcher, &QDBusServiceWatcher::serviceRegistered, + this, [this]() { reset(); }); + + QObject::connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, + this, [this]() { + QDBusConnection::sessionBus().disconnect( + s_serviceName, + s_virtDesktopsPath, + s_virtualDesktopsInterface, + QStringLiteral("desktopCreated"), + this, + SLOT(desktopCreated(QString,KWin::DBusDesktopDataStruct))); + + QDBusConnection::sessionBus().disconnect( + s_serviceName, + s_virtDesktopsPath, + s_virtualDesktopsInterface, + QStringLiteral("desktopRemoved"), + this, + SLOT(desktopRemoved(QString))); + + QDBusConnection::sessionBus().disconnect( + s_serviceName, + s_virtDesktopsPath, + s_virtualDesktopsInterface, + QStringLiteral("desktopDataChanged"), + this, + SLOT(desktopDataChanged(QString,KWin::DBusDesktopDataStruct))); + + + QDBusConnection::sessionBus().disconnect( + s_serviceName, + s_virtDesktopsPath, + s_virtualDesktopsInterface, + QStringLiteral("rowsChanged"), + this, + SLOT(desktopRowsChanged(uint))); + } + ); + + reset(); +} + +DesktopsModel::~DesktopsModel() +{ +} + +QHash DesktopsModel::roleNames() const +{ + QHash roles = QAbstractItemModel::roleNames(); + + QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("AdditionalRoles")); + + for (int i = 0; i < e.keyCount(); ++i) { + roles.insert(e.value(i), e.key(i)); + } + + return roles; +} + +QVariant DesktopsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() > (m_desktops.count() - 1)) { + return QVariant(); + } + + if (role == Qt::DisplayRole) { + return m_names.value(m_desktops.at(index.row())); + } else if (role == Id) { + return m_desktops.at(index.row()); + } else if (role == DesktopRow) { + const int rows = std::max(m_rows, 1); + const int perRow = std::ceil((qreal)m_desktops.count() / (qreal)rows); + + return (index.row() / perRow) + 1; + + } + + return QVariant(); +} + +int DesktopsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return 0; + } + + return m_desktops.count(); +} + +bool DesktopsModel::ready() const +{ + return !m_desktops.isEmpty(); +} + +QString DesktopsModel::error() const +{ + return m_error; +} + +bool DesktopsModel::userModified() const +{ + return m_userModified; +} + +bool DesktopsModel::serverModified() const +{ + return m_serverModified; +} + +int DesktopsModel::rows() const +{ + return m_rows; +} + +void DesktopsModel::setRows(int rows) +{ + if (!ready()) { + return; + } + + if (m_rows != rows) { + m_rows = rows; + + emit rowsChanged(); + emit dataChanged(index(0, 0), index(m_desktops.count() - 1, 0), QVector{DesktopRow}); + + updateModifiedState(); + } +} + +void DesktopsModel::createDesktop(const QString &name) +{ + if (!ready()) { + return; + } + + beginInsertRows(QModelIndex(), m_desktops.count(), m_desktops.count()); + + const QString &dummyId = QUuid::createUuid().toString(QUuid::WithoutBraces); + + m_desktops.append(dummyId); + m_names[dummyId] = name; + + endInsertRows(); + + updateModifiedState(); +} + +void DesktopsModel::removeDesktop(const QString &id) +{ + if (!ready() || !m_desktops.contains(id)) { + return; + } + + const int desktopIndex = m_desktops.indexOf(id); + + beginRemoveRows(QModelIndex(), desktopIndex, desktopIndex); + + m_desktops.removeAt(desktopIndex); + m_names.remove(id); + + endRemoveRows(); + + updateModifiedState(); +} + +void DesktopsModel::setDesktopName(const QString &id, const QString &name) +{ + if (!ready() || !m_desktops.contains(id)) { + return; + } + + m_names[id] = name; + + const QModelIndex &idx = index(m_desktops.indexOf(id), 0); + + dataChanged(idx, idx, QVector{Qt::DisplayRole}); + + updateModifiedState(); +} + +void DesktopsModel::syncWithServer() +{ + m_synchronizing = true; + + auto callFinished = [this](QDBusPendingCallWatcher *call) { + QDBusPendingReply reply = *call; + + if (reply.isError()) { + handleCallError(); + } + + call->deleteLater(); + }; + + if (m_desktops.count() > m_serverSideDesktops.count()) { + auto call = QDBusMessage::createMethodCall( + s_serviceName, + s_virtDesktopsPath, + s_virtualDesktopsInterface, + QStringLiteral("createDesktop")); + + const int newIndex = m_serverSideDesktops.count(); + + call.setArguments({(uint)newIndex, m_names.value(m_desktops.at(newIndex))}); + + QDBusPendingCall pending = QDBusConnection::sessionBus().asyncCall(call); + + const auto *watcher = new QDBusPendingCallWatcher(pending, this); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, callFinished); + + return; // The change-handling slot will call syncWithServer() again, + // until everything is in sync. + } + + if (m_desktops.count() < m_serverSideDesktops.count()) { + QStringListIterator i(m_serverSideDesktops); + + i.toBack(); + + while (i.hasPrevious()) { + const QString &previous = i.previous(); + + if (!m_desktops.contains(previous)) { + auto call = QDBusMessage::createMethodCall( + s_serviceName, + s_virtDesktopsPath, + s_virtualDesktopsInterface, + QStringLiteral("removeDesktop")); + + call.setArguments({previous}); + + QDBusPendingCall pending = QDBusConnection::sessionBus().asyncCall(call); + + const QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, callFinished); + + return; // The change-handling slot will call syncWithServer() again, + // until everything is in sync. + } + } + } + + // Sync ids. Replace dummy ids in the process. + for (int i = 0; i < m_serverSideDesktops.count(); ++i) { + const QString oldId = m_desktops.at(i); + const QString &newId = m_serverSideDesktops.at(i); + m_desktops[i] = newId; + m_names[newId] = m_names.take(oldId); + } + + emit dataChanged(index(0, 0), index(rowCount() - 1, 0), QVector{Qt::DisplayRole}); + + // Sync names. + if (m_names != m_serverSideNames) { + QHashIterator i(m_names); + + while (i.hasNext()) { + i.next(); + + if (i.value() != m_serverSideNames.value(i.key())) { + auto call = QDBusMessage::createMethodCall( + s_serviceName, + s_virtDesktopsPath, + s_virtualDesktopsInterface, + QStringLiteral("setDesktopName")); + + call.setArguments({i.key(), i.value()}); + + QDBusPendingCall pending = QDBusConnection::sessionBus().asyncCall(call); + + const QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, callFinished); + + break; + } + } + + return; // The change-handling slot will call syncWithServer() again, + // until everything is in sync.. + } + + // Sync rows. + if (m_rows != m_serverSideRows) { + auto call = QDBusMessage::createMethodCall( + s_serviceName, + s_virtDesktopsPath, + s_fdoPropertiesInterface, + QStringLiteral("Set")); + + call.setArguments({s_virtualDesktopsInterface, + QStringLiteral("rows"), QVariant::fromValue(QDBusVariant(QVariant((uint)m_rows)))}); + + QDBusPendingCall pending = QDBusConnection::sessionBus().asyncCall(call); + + const QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, callFinished); + } +} + +void DesktopsModel::reset() +{ + m_synchronizing = false; // Sanity. + + auto getAllAndConnectCall = QDBusMessage::createMethodCall( + s_serviceName, + s_virtDesktopsPath, + s_fdoPropertiesInterface, + QStringLiteral("GetAll")); + + getAllAndConnectCall.setArguments({s_virtualDesktopsInterface}); + + QDBusConnection::sessionBus().callWithCallback( + getAllAndConnectCall, + this, + SLOT(getAllAndConnect(QDBusMessage)), + SLOT(handleCallError())); +} + +void DesktopsModel::getAllAndConnect(const QDBusMessage &msg) +{ + const QVariantMap &data = qdbus_cast(msg.arguments().at(0).value()); + + const KWin::DBusDesktopDataVector &desktops = qdbus_cast( + data.value(QStringLiteral("desktops")).value() + ); + + const int newServerSideRows = data.value(QStringLiteral("rows")).toUInt(); + QStringList newServerSideDesktops; + QHash newServerSideNames; + + for (const KWin::DBusDesktopDataStruct &d : desktops) { + newServerSideDesktops.append(d.id); + newServerSideNames[d.id] = d.name; + } + + // If the server-side state changed during a KWin restart, and the + // user had made notifications, the model should notify about the + // change. + if (m_serverSideDesktops != newServerSideDesktops + || m_serverSideNames != newServerSideNames + || m_serverSideRows != newServerSideRows) { + if (!m_serverSideDesktops.isEmpty() || m_userModified) { + m_serverModified = true; + emit serverModifiedChanged(); + } + + m_serverSideDesktops = newServerSideDesktops; + m_serverSideNames = newServerSideNames; + m_serverSideRows = newServerSideRows; + } + + // For the case KWin restarts while the KCM was open: If the user had + // made no modifications, just reset to the server data. E.g. perhaps + // the user intentionally nuked the KWin config while it was down, so + // we should follow. + if (!m_userModified || m_desktops.empty()) { + beginResetModel(); + m_desktops = m_serverSideDesktops; + m_names = m_serverSideNames; + m_rows = m_serverSideRows; + endResetModel(); + } + + emit readyChanged(); + + auto handleConnectionError = [this]() { + m_error = i18n("There was an error connecting to the compositor."); + emit errorChanged(); + }; + + bool connected = QDBusConnection::sessionBus().connect( + s_serviceName, + s_virtDesktopsPath, + s_virtualDesktopsInterface, + QStringLiteral("desktopCreated"), + this, + SLOT(desktopCreated(QString,KWin::DBusDesktopDataStruct))); + + if (!connected) { + handleConnectionError(); + + return; + } + + connected = QDBusConnection::sessionBus().connect( + s_serviceName, + s_virtDesktopsPath, + s_virtualDesktopsInterface, + QStringLiteral("desktopRemoved"), + this, + SLOT(desktopRemoved(QString))); + + if (!connected) { + handleConnectionError(); + + return; + } + + connected = QDBusConnection::sessionBus().connect( + s_serviceName, + s_virtDesktopsPath, + s_virtualDesktopsInterface, + QStringLiteral("desktopDataChanged"), + this, + SLOT(desktopDataChanged(QString,KWin::DBusDesktopDataStruct))); + + if (!connected) { + handleConnectionError(); + + return; + } + + connected = QDBusConnection::sessionBus().connect( + s_serviceName, + s_virtDesktopsPath, + s_virtualDesktopsInterface, + QStringLiteral("rowsChanged"), + this, + SLOT(desktopRowsChanged(uint))); + + if (!connected) { + handleConnectionError(); + + return; + } +} + +void DesktopsModel::desktopCreated(const QString &id, const KWin::DBusDesktopDataStruct &data) +{ + m_serverSideDesktops.insert(data.position, id); + m_serverSideNames[data.id] = data.name; + + // If the user didn't make any changes, we can just stay in sync. + if (!m_userModified) { + beginInsertRows(QModelIndex(), data.position, data.position); + + m_desktops = m_serverSideDesktops; + m_names = m_serverSideNames; + + endInsertRows(); + } else { + // Remove dummy data. + const QString dummyId = m_desktops.at(data.position); + m_desktops[data.position] = id; + m_names.remove(dummyId); + m_names[id] = data.name; + const QModelIndex &idx = index(data.position, 0); + emit dataChanged(idx, idx, QVector{Id}); + + updateModifiedState(/* server */ true); + } +} + +void DesktopsModel::desktopRemoved(const QString &id) +{ + const int desktopIndex = m_serverSideDesktops.indexOf(id); + + m_serverSideDesktops.removeAt(desktopIndex); + m_serverSideNames.remove(id); + + // If the user didn't make any changes, we can just stay in sync. + if (!m_userModified) { + beginRemoveRows(QModelIndex(), desktopIndex, desktopIndex); + + m_desktops = m_serverSideDesktops; + m_names = m_serverSideNames; + + endRemoveRows(); + } else { + updateModifiedState(/* server */ true); + } +} + +void DesktopsModel::desktopDataChanged(const QString &id, const KWin::DBusDesktopDataStruct &data) +{ + const int desktopIndex = m_serverSideDesktops.indexOf(id); + + m_serverSideDesktops[desktopIndex] = id; + m_serverSideNames[id] = data.name; + + // If the user didn't make any changes, we can just stay in sync. + if (!m_userModified) { + m_desktops = m_serverSideDesktops; + m_names = m_serverSideNames; + + const QModelIndex &idx = index(desktopIndex, 0); + + dataChanged(idx, idx, QVector{Qt::DisplayRole}); + } else { + updateModifiedState(/* server */ true); + } +} + +void DesktopsModel::desktopRowsChanged(uint rows) +{ + // Unfortunately we sometimes get this signal from the server with an unchanged value. + if ((int)rows == m_serverSideRows) { + return; + } + + m_serverSideRows = rows; + + // If the user didn't make any changes, we can just stay in sync. + if (!m_userModified) { + m_rows = m_serverSideRows; + + emit rowsChanged(); + emit dataChanged(index(0, 0), index(m_desktops.count() - 1, 0), QVector{DesktopRow}); + } else { + updateModifiedState(/* server */ true); + } +} + +void DesktopsModel::updateModifiedState(bool server) +{ + // Count is the same but contents are not: The user may have + // removed and created new desktops in the UI, but there were + // no changes to send to the server because number and names + // have remained the same. In that case we can just clean + // that up here. + if (m_desktops.count() == m_serverSideDesktops.count() + && m_desktops != m_serverSideDesktops) { + + for (int i = 0; i < m_serverSideDesktops.count(); ++i) { + const QString oldId = m_desktops.at(i); + const QString &newId = m_serverSideDesktops.at(i); + m_desktops[i] = newId; + m_names[newId] = m_names.take(oldId); + } + + emit dataChanged(index(0, 0), index(rowCount() - 1, 0), QVector{Qt::DisplayRole}); + } + + if (m_desktops == m_serverSideDesktops + && m_names == m_serverSideNames + && m_rows == m_serverSideRows) { + + m_userModified = false; + emit userModifiedChanged(); + + m_serverModified = false; + emit serverModifiedChanged(); + + m_synchronizing = false; + } else { + if (m_synchronizing) { + m_serverModified = false; + emit serverModifiedChanged(); + + syncWithServer(); + } else if (server) { + m_serverModified = true; + emit serverModifiedChanged(); + } else { + m_userModified = true; + emit userModifiedChanged(); + } + } +} + +void DesktopsModel::handleCallError() +{ + if (m_synchronizing) { + m_synchronizing = false; + + m_serverModified = false; + emit serverModifiedChanged(); + + m_error = i18n("There was an error saving the settings to the compositor."); + emit errorChanged(); + } else { + m_error = i18n("There was an error requesting information from the compositor."); + emit errorChanged(); + } +} + +} diff --git a/kcmkwin/kwindesktop/desktop.desktop b/kcmkwin/kwindesktop/kcm_kwin_virtualdesktops.desktop rename from kcmkwin/kwindesktop/desktop.desktop rename to kcmkwin/kwindesktop/kcm_kwin_virtualdesktops.desktop --- a/kcmkwin/kwindesktop/desktop.desktop +++ b/kcmkwin/kwindesktop/kcm_kwin_virtualdesktops.desktop @@ -1,11 +1,11 @@ [Desktop Entry] +Exec=kcmshell5 kcm_kwin_virtualdesktops +Icon=preferences-desktop Type=Service X-KDE-ServiceTypes=KCModule -X-DocPath=kcontrol/desktop/index.html -Icon=preferences-desktop -Exec=kcmshell5 desktop +X-DocPath=kcontrol/kwin_virtualdesktops/index.html -X-KDE-Library=kcm_kwindesktop +X-KDE-Library=kcm_kwin_virtualdesktops X-KDE-ParentApp=kcontrol X-KDE-System-Settings-Parent-Category=desktopbehavior @@ -143,7 +143,7 @@ X-KDE-Keywords[nl]=bureaublad,bureaubladen,aantal,virtueel bureaublad,meervoudige bureaubladen,pager,pager-widget,pager-applet,pagerinstellingen X-KDE-Keywords[nn]=skrivebord,mengd,tal,virtuelt skrivebord,fleire skrivebord,vekslar,vekslarelement,vekslarelement,vekslerinnstillinger,vekslaroppsett X-KDE-Keywords[pa]=ਡੈਸਕਟਾਪ,ਗਿਣਤੀ,ਨੰਬਰ,ਅੰਕ,ਵਰਚੁਅਲ ਡੈਸਕਟਾਪ,ਕਈ ਡੈਸਕਟਾਪ,ਪੇਜ਼ਰ,ਪੇਜ਼ਰ ਵਿਜੈਟ,ਪੇਜ਼ਰ ਐਪਲਿਟ,ਪੇਜ਼ਰ ਸੈਟਿੰਗਾਂ -X-KDE-Keywords[pl]=pulpit,pulpity,liczba,pulpity wirtualne,wiele pulpitów +X-KDE-Keywords[pl]=pulpit,pulpity,liczba,pulpity wirtualne,wiele pulpitów X-KDE-Keywords[pt]=ecrã,ecrãs,número,ecrã virtual,múltiplos ecrãs,paginador,elemento paginador,'applet' do paginador,configuração do paginador X-KDE-Keywords[pt_BR]=área de trabalho,áreas de trabalho,desktop,desktops,número,área de trabalho virtual,múltiplas áreas de trabalho,paginador,elemento paginador,miniaplicativo do paginador,configurações do paginador X-KDE-Keywords[ru]=desktop,desktops,number,virtual desktop,multiple desktops,pager,pager widget,pager applet,pager settings,рабочий стол,рабочие столы,число,виртуальный рабочий стол,несколько рабочих столов,переключатель,переключение,виджет переключения,аплет переключения,параметры переключения,настройки переключения @@ -159,3 +159,6 @@ X-KDE-Keywords[x-test]=xxdesktopxx,xxdesktopsxx,xxnumberxx,xxvirtual desktopxx,xxmultiple desktopsxx,xxpagerxx,xxpager widgetxx,xxpager appletxx,xxpager settingsxx X-KDE-Keywords[zh_CN]=desktop,desktops,number,virtual desktop,multiple desktops,pager,pager widget,pager applet,pager settings,桌面,虚拟桌面,多桌面,分页,分页器,分页器组件,分页器设置 X-KDE-Keywords[zh_TW]=desktop,desktops,number,virtual desktop,multiple desktops,pager,pager widget,pager applet,pager settings + + +Categories=Qt;KDE;X-KDE-settings-translations; diff --git a/kcmkwin/kwindesktop/main.h b/kcmkwin/kwindesktop/main.h deleted file mode 100644 --- a/kcmkwin/kwindesktop/main.h +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************************** -KWin - the KDE window manager -This file is part of the KDE project. - -Copyright (C) 2009 Martin Gräßlin - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*********************************************************************/ -#ifndef __MAIN_H__ -#define __MAIN_H__ - -#include -#include - -#include "ui_main.h" - -class KActionCollection; -class KConfigGroup; -class KShortcutsEditor; - -namespace KWin -{ -// if you change this, update also the number of keyboard shortcuts in kwin/kwinbindings.cpp -static const int maxDesktops = 20; -static const int defaultDesktops = 4; - -class KWinDesktopConfigForm : public QWidget, public Ui::KWinDesktopConfigForm -{ - Q_OBJECT - -public: - explicit KWinDesktopConfigForm(QWidget* parent); -}; - -class KWinDesktopConfig : public KCModule -{ - Q_OBJECT - -public: - explicit KWinDesktopConfig(QWidget* parent, const QVariantList& args); - ~KWinDesktopConfig(); - QString cachedDesktopName(int desktop); - - // undo all changes - void undo(); - -public Q_SLOTS: - virtual void save(); - virtual void load(); - virtual void defaults(); - - -private Q_SLOTS: - void slotChangeShortcuts(int number); - void slotShowAllShortcuts(); - void slotEffectSelectionChanged(int index); - void slotAboutEffectClicked(); - void slotConfigureEffectClicked(); - -private: - void init(); - void addAction(const QString &name, const QString &label); - bool effectEnabled(const QString& effect, const KConfigGroup& cfg) const; - QString extrapolatedShortcut(int desktop) const; - -private: - KWinDesktopConfigForm* m_ui; - KSharedConfigPtr m_config; - // cache for desktop names given by NETRootInfo - // needed as the widget only stores the names for actual number of desktops - QStringList m_desktopNames; - // Collection for switching desktops like ctrl+f1 - KActionCollection* m_actionCollection; - // Collection for next, previous, up, down desktop - KActionCollection* m_switchDesktopCollection; - KShortcutsEditor* m_editor; -}; - -} // namespace - -#endif diff --git a/kcmkwin/kwindesktop/main.cpp b/kcmkwin/kwindesktop/main.cpp deleted file mode 100644 --- a/kcmkwin/kwindesktop/main.cpp +++ /dev/null @@ -1,671 +0,0 @@ -/******************************************************************** -KWin - the KDE window manager -This file is part of the KDE project. - -Copyright (C) 2009 Martin Gräßlin - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*********************************************************************/ - -#include "main.h" -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -K_PLUGIN_FACTORY(KWinDesktopConfigFactory, registerPlugin();) - -namespace KWin -{ - -KWinDesktopConfigForm::KWinDesktopConfigForm(QWidget* parent) - : QWidget(parent) -{ - setupUi(this); -} - -KWinDesktopConfig::KWinDesktopConfig(QWidget* parent, const QVariantList& args) - : KCModule(KAboutData::pluginData(QStringLiteral("kcm_kwindesktop")), parent, args) - , m_config(KSharedConfig::openConfig("kwinrc")) - , m_actionCollection(nullptr) - , m_switchDesktopCollection(nullptr) -{ - init(); -} - -void KWinDesktopConfig::init() -{ - m_ui = new KWinDesktopConfigForm(this); - // TODO: there has to be a way to add the shortcuts editor to the ui file - m_editor = new KShortcutsEditor(m_ui, KShortcutsEditor::GlobalAction); - m_ui->editorFrame->setLayout(new QVBoxLayout()); - m_ui->editorFrame->layout()->setMargin(0); - m_ui->editorFrame->layout()->addWidget(m_editor); - - m_ui->desktopNames->setDesktopConfig(this); - m_ui->desktopNames->setMaxDesktops(maxDesktops); - m_ui->desktopNames->numberChanged(defaultDesktops); - - QVBoxLayout* layout = new QVBoxLayout(this); - layout->addWidget(m_ui); - - setQuickHelp(i18n("

Multiple Desktops

In this module, you can configure how many virtual desktops you want and how these should be labeled.")); - - // Shortcut config. The shortcut belongs to the component "kwin"! - m_actionCollection = new KActionCollection(this, QStringLiteral("kwin")); - m_actionCollection->setComponentDisplayName(i18n("KWin")); - m_actionCollection->setConfigGroup("Desktop Switching"); - m_actionCollection->setConfigGlobal(true); - - m_switchDesktopCollection = new KActionCollection(this, QStringLiteral("kwin")); - m_switchDesktopCollection->setComponentDisplayName(i18n("KWin")); - m_switchDesktopCollection->setConfigGroup("Desktop Switching"); - m_switchDesktopCollection->setConfigGlobal(true); - - // actions for switch desktop collection - other action is filled dynamically - addAction("Switch to Next Desktop", i18n("Switch to Next Desktop")); - addAction("Switch to Previous Desktop", i18n("Switch to Previous Desktop")); - addAction("Switch One Desktop to the Right", i18n("Switch One Desktop to the Right")); - addAction("Switch One Desktop to the Left", i18n("Switch One Desktop to the Left")); - addAction("Switch One Desktop Up", i18n("Switch One Desktop Up")); - addAction("Switch One Desktop Down", i18n("Switch One Desktop Down")); - addAction("Walk Through Desktops", i18n("Walk Through Desktops")); - addAction("Walk Through Desktops (Reverse)", i18n("Walk Through Desktops (Reverse)")); - addAction("Walk Through Desktop List", i18n("Walk Through Desktop List")); - addAction("Walk Through Desktop List (Reverse)", i18n("Walk Through Desktop List (Reverse)")); - - m_editor->addCollection(m_switchDesktopCollection, i18n("Desktop Switching")); - - // get number of desktops - int n = 1; - if (QX11Info::isPlatformX11()) { - NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops | NET::DesktopNames); - n = info.numberOfDesktops(); - } - - auto addSwitchTo = [this](int i, const QKeySequence &sequence) { - QAction* a = m_actionCollection->addAction(QString("Switch to Desktop %1").arg(i)); - a->setProperty("isConfigurationAction", true); - a->setText(i18n("Switch to Desktop %1", i)); - KGlobalAccel::setGlobalShortcut(a, sequence); - }; - if (n >= 2) { - addSwitchTo(1, Qt::CTRL + Qt::Key_F1); - addSwitchTo(2, Qt::CTRL + Qt::Key_F2); - } - if (n >= 3) { - addSwitchTo(3, Qt::CTRL + Qt::Key_F3); - } - if (n >= 4) { - addSwitchTo(4, Qt::CTRL + Qt::Key_F4); - } - for (int i = 5; i <= n; ++i) { - addSwitchTo(i, QKeySequence()); - } - - // This should be after the "Switch to Desktop %1" loop. It HAS to be - // there after numberSpinBox is connected to slotChangeShortcuts. We would - // overwrite the users settings if not, - m_ui->numberSpinBox->setValue(n); - - m_editor->addCollection(m_actionCollection, i18n("Desktop Switching")); - - // search the effect names - // TODO: way to recognize if a effect is not found - KServiceTypeTrader* trader = KServiceTypeTrader::self(); - QString fadedesktop; - KService::List services = trader->query("KWin/Effect", "[X-KDE-PluginInfo-Name] == 'kwin4_effect_fadedesktop'"); - if (!services.isEmpty()) - fadedesktop = services.first()->name(); - - m_ui->effectComboBox->addItem(i18n("No Animation")); - m_ui->effectComboBox->addItem(BuiltInEffects::effectData(BuiltInEffect::Slide).displayName); - m_ui->effectComboBox->addItem(BuiltInEffects::effectData(BuiltInEffect::CubeSlide).displayName); - m_ui->effectComboBox->addItem(fadedesktop); - - // effect config and info button - m_ui->effectInfoButton->setIcon(QIcon::fromTheme("dialog-information")); - m_ui->effectConfigButton->setIcon(QIcon::fromTheme("configure")); - - connect(m_ui->rowsSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); - connect(m_ui->numberSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); - connect(m_ui->numberSpinBox, SIGNAL(valueChanged(int)), SLOT(slotChangeShortcuts(int))); - connect(m_ui->desktopNames, SIGNAL(changed()), SLOT(changed())); - connect(m_ui->popupInfoCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); - connect(m_ui->popupHideSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); - connect(m_ui->desktopLayoutIndicatorCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); - connect(m_ui->wrapAroundBox, SIGNAL(stateChanged(int)), SLOT(changed())); - connect(m_editor, SIGNAL(keyChange()), SLOT(changed())); - connect(m_ui->allShortcutsCheckBox, SIGNAL(stateChanged(int)), SLOT(slotShowAllShortcuts())); - connect(m_ui->effectComboBox, SIGNAL(currentIndexChanged(int)), SLOT(changed())); - connect(m_ui->effectComboBox, SIGNAL(currentIndexChanged(int)), SLOT(slotEffectSelectionChanged(int))); - connect(m_ui->effectInfoButton, SIGNAL(clicked()), SLOT(slotAboutEffectClicked())); - connect(m_ui->effectConfigButton, SIGNAL(clicked()), SLOT(slotConfigureEffectClicked())); - - // Begin check for immutable - taken from old desktops kcm - int kwin_screen_number = QX11Info::appScreen(); - - m_config = KSharedConfig::openConfig("kwinrc"); - - QByteArray groupname; - if (kwin_screen_number == 0) - groupname = "Desktops"; - else - groupname = "Desktops-screen-" + QByteArray::number(kwin_screen_number); - - if (m_config->isGroupImmutable(groupname)) { - m_ui->nameGroup->setEnabled(false); - //number of desktops widgets - m_ui->numberLabel->setEnabled(false); - m_ui->numberSpinBox->setEnabled(false); - m_ui->rowsSpinBox->setEnabled(false); - } else { - KConfigGroup cfgGroup(m_config.data(), groupname.constData()); - if (cfgGroup.isEntryImmutable("Number")) { - //number of desktops widgets - m_ui->numberLabel->setEnabled(false); - m_ui->numberSpinBox->setEnabled(false); - m_ui->rowsSpinBox->setEnabled(false); - } - } - // End check for immutable -} - -KWinDesktopConfig::~KWinDesktopConfig() -{ - undo(); -} - -void KWinDesktopConfig::addAction(const QString &name, const QString &label) -{ - QAction* a = m_switchDesktopCollection->addAction(name); - a->setProperty("isConfigurationAction", true); - a->setText(label); - KGlobalAccel::setGlobalShortcut(a, QKeySequence()); -} - -void KWinDesktopConfig::defaults() -{ - // TODO: plasma stuff - m_ui->numberSpinBox->setValue(defaultDesktops); - m_ui->desktopNames->numberChanged(defaultDesktops); - for (int i = 1; i <= maxDesktops; i++) { - m_desktopNames[i-1] = i18n("Desktop %1", i); - if (i <= defaultDesktops) - m_ui->desktopNames->setDefaultName(i); - } - - // popup info - m_ui->popupInfoCheckBox->setChecked(false); - m_ui->popupHideSpinBox->setValue(1000); - m_ui->desktopLayoutIndicatorCheckBox->setChecked(true); - - m_ui->effectComboBox->setCurrentIndex(1); - - m_ui->wrapAroundBox->setChecked(true); - - m_ui->rowsSpinBox->setValue(2); - - m_editor->allDefault(); - - emit changed(true); -} - - -void KWinDesktopConfig::load() -{ - // This method is called on reset(). So undo all changes. - undo(); - - if (QX11Info::isPlatformX11()) { - // get number of desktops - NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops | NET::DesktopNames, NET::WM2DesktopLayout); - - for (int i = 1; i <= maxDesktops; i++) { - QString name = QString::fromUtf8(info.desktopName(i)); - m_desktopNames << name; - m_ui->desktopNames->setName(i, name); - } - m_ui->rowsSpinBox->setValue(info.desktopLayoutColumnsRows().height()); - } else { - // TODO: proper implementation - m_ui->rowsSpinBox->setValue(1); - } - - // Popup info - KConfigGroup effectconfig(m_config, "Plugins"); - KConfigGroup popupInfo(m_config, "Script-desktopchangeosd"); - m_ui->popupInfoCheckBox->setChecked(effectconfig.readEntry("desktopchangeosdEnabled", false)); - m_ui->popupHideSpinBox->setValue(popupInfo.readEntry("PopupHideDelay", 1000)); - m_ui->desktopLayoutIndicatorCheckBox->setChecked(!popupInfo.readEntry("TextOnly", false)); - - // Wrap Around on screen edge - KConfigGroup windowConfig(m_config, "Windows"); - m_ui->wrapAroundBox->setChecked(windowConfig.readEntry("RollOverDesktops", true)); - - // Effect for desktop switching - // Set current option to "none" if no plugin is activated. - m_ui->effectComboBox->setCurrentIndex(0); - auto enableBuiltInEffect = [&effectconfig,this](BuiltInEffect effect, int index) { - const QString key = BuiltInEffects::nameForEffect(effect) + QStringLiteral("Enabled"); - if (effectconfig.readEntry(key, BuiltInEffects::enabledByDefault(effect))) { - m_ui->effectComboBox->setCurrentIndex(index); - } - }; - enableBuiltInEffect(BuiltInEffect::Slide, 1); - enableBuiltInEffect(BuiltInEffect::CubeSlide, 2); - if (effectEnabled("fadedesktop", effectconfig)) - m_ui->effectComboBox->setCurrentIndex(3); - slotEffectSelectionChanged(m_ui->effectComboBox->currentIndex()); - // TODO: plasma stuff - - emit changed(false); -} - -void KWinDesktopConfig::save() -{ - // TODO: plasma stuff - - const int numberDesktops = m_ui->numberSpinBox->value(); - int rows = m_ui->rowsSpinBox->value(); - rows = qBound(1, rows, numberDesktops); - // avoid weird cases like having 3 rows for 4 desktops, where the last row is unused - int columns = numberDesktops / rows; - if (numberDesktops % rows > 0) { - columns++; - } - - if (QX11Info::isPlatformX11()) { - NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops | NET::DesktopNames, NET::WM2DesktopLayout); - // set desktop names - for (int i = 1; i <= maxDesktops; i++) { - QString desktopName = m_desktopNames[ i -1 ]; - if (i <= m_ui->numberSpinBox->value()) - desktopName = m_ui->desktopNames->name(i); - info.setDesktopName(i, desktopName.toUtf8()); - info.activate(); - } - // set number of desktops - info.setNumberOfDesktops(numberDesktops); - info.activate(); - info.setDesktopLayout(NET::OrientationHorizontal, columns, rows, NET::DesktopLayoutCornerTopLeft); - info.activate(); - - XSync(QX11Info::display(), false); - } - - // save the desktops - QString groupname; - const int screenNumber = QX11Info::appScreen(); - if (screenNumber == 0) - groupname = "Desktops"; - else - groupname.sprintf("Desktops-screen-%d", screenNumber); - KConfigGroup group(m_config, groupname); - group.writeEntry("Rows", rows); - - // Popup info - KConfigGroup effectconfig(m_config, "Plugins"); - KConfigGroup popupInfo(m_config, "Script-desktopchangeosd"); - effectconfig.writeEntry("desktopchangeosdEnabled", m_ui->popupInfoCheckBox->isChecked()); - popupInfo.writeEntry("PopupHideDelay", m_ui->popupHideSpinBox->value()); - popupInfo.writeEntry("TextOnly", !m_ui->desktopLayoutIndicatorCheckBox->isChecked()); - - // Wrap Around on screen edge - KConfigGroup windowConfig(m_config, "Windows"); - windowConfig.writeEntry("RollOverDesktops", m_ui->wrapAroundBox->isChecked()); - - // Effect desktop switching - int desktopSwitcher = m_ui->effectComboBox->currentIndex(); - bool slideEnabled = false; - bool cubeSlideEnabled = false; - bool fadeEnabled = false; - switch(desktopSwitcher) { - case 1: - // slide - slideEnabled = true; - break; - case 2: - // cube - cubeSlideEnabled = true; - break; - case 3: - // fadedesktop - fadeEnabled = true; - break; - } - - effectconfig.writeEntry("slideEnabled", slideEnabled); - effectconfig.writeEntry("cubeslideEnabled", cubeSlideEnabled); - effectconfig.writeEntry("kwin4_effect_fadedesktopEnabled", fadeEnabled); - - m_editor->save(); - - m_config->sync(); - // Send signal to all kwin instances - QDBusMessage message = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig"); - QDBusConnection::sessionBus().send(message); - // and reconfigure the effects - OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), - QStringLiteral("/Effects"), - QDBusConnection::sessionBus()); - if (slideEnabled) { - interface.loadEffect(BuiltInEffects::nameForEffect(BuiltInEffect::Slide)); - } else { - interface.unloadEffect(BuiltInEffects::nameForEffect(BuiltInEffect::Slide)); - } - if (cubeSlideEnabled) { - interface.loadEffect(BuiltInEffects::nameForEffect(BuiltInEffect::CubeSlide)); - } else { - interface.unloadEffect(BuiltInEffects::nameForEffect(BuiltInEffect::CubeSlide)); - } - if (fadeEnabled) { - interface.loadEffect(QStringLiteral("kwin4_effect_fadedesktop")); - } else { - interface.unloadEffect(QStringLiteral("kwin4_effect_fadedesktop")); - } - - emit changed(false); -} - - -void KWinDesktopConfig::undo() -{ - // The global shortcuts editor makes changes active immediately. In case - // of undo we have to undo them manually - m_editor->undoChanges(); -} - -QString KWinDesktopConfig::cachedDesktopName(int desktop) -{ - if (desktop > m_desktopNames.size()) - return QString(); - return m_desktopNames[ desktop -1 ]; -} - -QString KWinDesktopConfig::extrapolatedShortcut(int desktop) const -{ - - if (!desktop || desktop > m_actionCollection->count()) - return QString(); - if (desktop == 1) - return QString("Ctrl+F1"); - - QAction *beforeAction = m_actionCollection->actions().at(qMin(9, desktop - 2)); - auto shortcuts = KGlobalAccel::self()->shortcut(beforeAction); - if (shortcuts.isEmpty()) { - shortcuts = KGlobalAccel::self()->defaultShortcut(beforeAction); - } - QString before; - if (!shortcuts.isEmpty()) { - before = shortcuts.first().toString(QKeySequence::PortableText); - } - - QString seq; - if (before.contains(QRegExp("F[0-9]{1,2}"))) { - if (desktop < 13) // 10? - seq = QString("F%1").arg(desktop); - else if (!before.contains("Shift")) - seq = "Shift+" + QString("F%1").arg(desktop - 10); - } else if (before.contains(QRegExp("[0-9]"))) { - if (desktop == 10) - seq = '0'; - else if (desktop > 10) { - if (!before.contains("Shift")) - seq = "Shift+" + QString::number(desktop == 20 ? 0 : (desktop - 10)); - } else - seq = QString::number(desktop); - } - - if (!seq.isEmpty()) { - if (before.contains("Ctrl")) - seq.prepend("Ctrl+"); - if (before.contains("Alt")) - seq.prepend("Alt+"); - if (before.contains("Shift")) - seq.prepend("Shift+"); - if (before.contains("Meta")) - seq.prepend("Meta+"); - } - return seq; -} - -void KWinDesktopConfig::slotChangeShortcuts(int number) -{ - if ((number < 1) || (number > maxDesktops)) - return; - - if (m_ui->allShortcutsCheckBox->isChecked()) - number = maxDesktops; - - while (number != m_actionCollection->count()) { - if (number < m_actionCollection->count()) { - // Remove the action from the action collection. The action itself - // will still exist because that's the way kwin currently works. - // No need to remove/forget it. See kwinbindings. - QAction *a = m_actionCollection->takeAction(m_actionCollection->actions().last()); - // Remove any associated global shortcut. Set it to "" - KGlobalAccel::self()->setShortcut(a, QList(), KGlobalAccel::NoAutoloading); - m_ui->messageLabel->hide(); - delete a; - } else { - // add desktop - int desktop = m_actionCollection->count() + 1; - QAction* action = m_actionCollection->addAction(QString("Switch to Desktop %1").arg(desktop)); - action->setProperty("isConfigurationAction", true); - action->setText(i18n("Switch to Desktop %1", desktop)); - KGlobalAccel::self()->setShortcut(action, QList()); - QString shortcutString = extrapolatedShortcut(desktop); - if (shortcutString.isEmpty()) { - m_ui->messageLabel->setText(i18n("No suitable Shortcut for Desktop %1 found", desktop)); - m_ui->messageLabel->show(); - } else { - QKeySequence shortcut(shortcutString); - if (!shortcut.isEmpty() && KGlobalAccel::self()->isGlobalShortcutAvailable(shortcut)) { - KGlobalAccel::self()->setShortcut(action, QList() << shortcut, KGlobalAccel::NoAutoloading); - m_ui->messageLabel->setText(i18n("Assigned global Shortcut \"%1\" to Desktop %2", shortcutString, desktop)); - m_ui->messageLabel->show(); - } else { - m_ui->messageLabel->setText(i18n("Shortcut conflict: Could not set Shortcut %1 for Desktop %2", shortcutString, desktop)); - m_ui->messageLabel->show(); - } - } - } - } - m_editor->clearCollections(); - m_editor->addCollection(m_switchDesktopCollection, i18n("Desktop Switching")); - m_editor->addCollection(m_actionCollection, i18n("Desktop Switching")); -} - -void KWinDesktopConfig::slotShowAllShortcuts() -{ - slotChangeShortcuts(m_ui->numberSpinBox->value()); -} - -void KWinDesktopConfig::slotEffectSelectionChanged(int index) -{ - bool enabled = false; - if (index != 0) - enabled = true; - m_ui->effectInfoButton->setEnabled(enabled); - - switch (index) { - case 1: // Slide - case 2: // Cube Slide - enabled = true; - break; - default: - enabled = false; - break; - } - m_ui->effectConfigButton->setEnabled(enabled); -} - - -bool KWinDesktopConfig::effectEnabled(const QString& effect, const KConfigGroup& cfg) const -{ - KService::List services = KServiceTypeTrader::self()->query( - "KWin/Effect", "[X-KDE-PluginInfo-Name] == 'kwin4_effect_" + effect + '\''); - if (services.isEmpty()) - return false; - QVariant v = services.first()->property("X-KDE-PluginInfo-EnabledByDefault"); - return cfg.readEntry("kwin4_effect_" + effect + "Enabled", v.toBool()); -} - -void KWinDesktopConfig::slotAboutEffectClicked() -{ - QString effect; - bool fromKService = false; - BuiltInEffect builtIn = BuiltInEffect::Invalid; - switch(m_ui->effectComboBox->currentIndex()) { - case 1: - builtIn = BuiltInEffect::Slide; - break; - case 2: - builtIn = BuiltInEffect::CubeSlide; - break; - case 3: - effect = "fadedesktop"; - fromKService = true; - break; - default: - return; - } - auto showDialog = [this](const KAboutData &aboutData) { - QPointer aboutPlugin = new KAboutApplicationDialog(aboutData, this); - aboutPlugin->exec(); - delete aboutPlugin; - }; - if (fromKService) { - const QString pluginId = QStringLiteral("kwin4_effect_%1").arg(effect); - const auto effectsMetaData = KPackage::PackageLoader::self()->findPackages( - QStringLiteral("KWin/Effect"), - QStringLiteral("kwin/effects/"), - [&pluginId](const KPluginMetaData &meta) { - return meta.pluginId() == pluginId; - }); - if (effectsMetaData.isEmpty()) { - return; - } - KPluginInfo pluginInfo(effectsMetaData.first()); - - const QString name = pluginInfo.name(); - const QString comment = pluginInfo.comment(); - const QString author = pluginInfo.author(); - const QString email = pluginInfo.email(); - const QString website = pluginInfo.website(); - const QString version = pluginInfo.version(); - const QString license = pluginInfo.license(); - const QString icon = pluginInfo.icon(); - - KAboutData aboutData(name, name, version, comment, KAboutLicense::byKeyword(license).key(), QString(), QString(), website.toLatin1()); - aboutData.setProgramLogo(icon); - const QStringList authors = author.split(','); - const QStringList emails = email.split(','); - int i = 0; - if (authors.count() == emails.count()) { - foreach (const QString & author, authors) { - if (!author.isEmpty()) { - aboutData.addAuthor(i18n(author.toUtf8()), QString(), emails[i]); - } - i++; - } - } - showDialog(aboutData); - } else { - const BuiltInEffects::EffectData &data = BuiltInEffects::effectData(builtIn); - KAboutData aboutData(data.name, - data.displayName, - QStringLiteral(KWIN_VERSION_STRING), - data.comment, - KAboutLicense::GPL_V2); - aboutData.setProgramLogo(QIcon::fromTheme(QStringLiteral("preferences-system-windows"))); - aboutData.addAuthor(i18n("KWin development team")); - showDialog(aboutData); - } -} - -void KWinDesktopConfig::slotConfigureEffectClicked() -{ - QString effect; - switch(m_ui->effectComboBox->currentIndex()) { - case 1: - effect = BuiltInEffects::nameForEffect(BuiltInEffect::Slide); - break; - case 2: - effect = BuiltInEffects::nameForEffect(BuiltInEffect::CubeSlide); - break; - default: - return; - } - - QPointer configDialog = new QDialog(this); - KCModule *kcm = KPluginTrader::createInstanceFromQuery(QStringLiteral("kwin/effects/configs/"), QString(), - QStringLiteral("'%1' in [X-KDE-ParentComponents]").arg(effect), - configDialog); - if (!kcm) { - delete configDialog; - return; - } - configDialog->setWindowTitle(m_ui->effectComboBox->currentText()); - configDialog->setLayout(new QVBoxLayout); - QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::RestoreDefaults, configDialog); - connect(buttons, SIGNAL(accepted()), configDialog, SLOT(accept())); - connect(buttons, SIGNAL(rejected()), configDialog, SLOT(reject())); - connect(buttons->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked(bool)), kcm, SLOT(defaults())); - - QWidget *showWidget = new QWidget(configDialog); - QVBoxLayout *layout = new QVBoxLayout; - showWidget->setLayout(layout); - layout->addWidget(kcm); - configDialog->layout()->addWidget(showWidget); - configDialog->layout()->addWidget(buttons); - - if (configDialog->exec() == QDialog::Accepted) { - kcm->save(); - } else { - kcm->load(); - } - delete configDialog; -} - -} // namespace - -#include "main.moc" diff --git a/kcmkwin/kwindesktop/main.ui b/kcmkwin/kwindesktop/main.ui deleted file mode 100644 --- a/kcmkwin/kwindesktop/main.ui +++ /dev/null @@ -1,326 +0,0 @@ - - - KWinDesktopConfigForm - - - - 0 - 0 - 572 - 310 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - Desktops - - - - - - Layout - - - - QFormLayout::AllNonFixedFieldsGrow - - - 0 - - - - - Here you can set how many virtual desktops you want on your KDE desktop. - - - &Number of desktops: - - - numberSpinBox - - - - - - - Here you can set how many virtual desktops you want on your KDE desktop. - - - 1 - - - 20 - - - 4 - - - - - - - true - - - N&umber of rows: - - - rowsSpinBox - - - - - - - true - - - 1 - - - 20 - - - - - - - - - - Desktop Names - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - Switching - - - - - - Enable this option if you want keyboard or active desktop border navigation beyond the edge of a desktop to take you to the opposite edge of the new desktop. - - - Desktop navigation wraps around - - - - - - - Desktop Effect Animation - - - - QFormLayout::ExpandingFieldsGrow - - - 0 - - - - - Animation: - - - effectComboBox - - - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - - - - - - - - - - - - Desktop Switch On-Screen Display - - - true - - - false - - - - QFormLayout::AllNonFixedFieldsGrow - - - 0 - - - - - Duration: - - - popupHideSpinBox - - - - - - - msec - - - 5000 - - - 50 - - - - - - - Enabling this option will show a small preview of the desktop layout indicating the selected desktop. - - - Show desktop layout indicators - - - - - - - - - - Shortcuts - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - - - - Show shortcuts for all possible desktops - - - - - - - - - - - - - - - KWin::DesktopNamesWidget - QWidget -
desktopnameswidget.h
- 1 - - numberChanged(int) - -
-
- - - - numberSpinBox - valueChanged(int) - desktopNames - numberChanged(int) - - - 327 - 144 - - - 326 - 209 - - - - -
diff --git a/kcmkwin/kwindesktop/package/contents/ui/main.qml b/kcmkwin/kwindesktop/package/contents/ui/main.qml new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindesktop/package/contents/ui/main.qml @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2018 Eike Hein + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +import QtQuick 2.1 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.4 as QtControls +import org.kde.kirigami 2.5 as Kirigami +import org.kde.plasma.core 2.1 as PlasmaCore +import org.kde.kcm 1.2 + +ScrollViewKCM { + id: root + + ConfigModule.quickHelp: i18n("Virtual Desktops") + + Connections { + target: kcm.desktopsModel + + onReadyChanged: { + rowsSpinBox.value = kcm.desktopsModel.rows; + } + + onRowsChanged: { + rowsSpinBox.value = kcm.desktopsModel.rows; + } + } + + Component { + id: desktopsListItemComponent + + Kirigami.SwipeListItem { + id: listItem + + contentItem: RowLayout { + QtControls.TextField { + id: nameField + + background: null + leftPadding: Kirigami.Units.largeSpacing + topPadding: 0 + bottomPadding: 0 + + Layout.fillWidth: true + + Layout.alignment: Qt.AlignVCenter + + text: model.display + + readOnly: true + + onEditingFinished: { + readOnly = true; + Qt.callLater(kcm.desktopsModel.setDesktopName, model.Id, text); + } + } + } + + actions: [ + Kirigami.Action { + enabled: !model.IsMissing + iconName: "edit-rename" + tooltip: i18nc("@info:tooltip", "Rename") + onTriggered: { + nameField.readOnly = false; + nameField.selectAll(); + nameField.forceActiveFocus(); + } + }, + Kirigami.Action { + enabled: !model.IsMissing + iconName: "list-remove" + tooltip: i18nc("@info:tooltip", "Remove") + onTriggered: kcm.desktopsModel.removeDesktop(model.Id) + }] + } + } + + header: ColumnLayout { + id: messagesLayout + + spacing: Kirigami.Units.largeSpacing + + Kirigami.InlineMessage { + Layout.fillWidth: true + + type: Kirigami.MessageType.Error + + text: kcm.desktopsModel.error + + visible: kcm.desktopsModel.error != "" + } + + Kirigami.InlineMessage { + Layout.fillWidth: true + + type: Kirigami.MessageType.Information + + text: i18n("Virtual desktops have been changed outside this settings application. Saving now will overwrite the changes.") + + visible: kcm.desktopsModel.serverModified + } + + RowLayout { + QtControls.Label { + text: i18n("Rows:") + } + + QtControls.SpinBox { + id: rowsSpinBox + + from: 1 + to: 20 + + onValueModified: kcm.desktopsModel.rows = value + } + + Item { // Spacer + Layout.fillWidth: true + } + + QtControls.Button { + Layout.alignment: Qt.AlignRight + + text: i18nc("@action:button", "Add") + icon.name: "list-add" + + onClicked: kcm.desktopsModel.createDesktop(i18n("New Desktop")) + } + } + } + + view: ListView { + id: desktopsList + + model: kcm.desktopsModel.ready ? kcm.desktopsModel : null + + section.property: "DesktopRow" + section.delegate: Kirigami.AbstractListItem { + width: desktopsList.width + + backgroundColor: Kirigami.Theme.backgroundColor + + hoverEnabled: false + supportsMouseEvents: false + + Kirigami.Theme.inherit: false + Kirigami.Theme.colorSet: Kirigami.Theme.Window + + QtControls.Label { + text: i18n("Row %1", section) + } + } + + delegate: Kirigami.DelegateRecycler { + width: desktopsList.width + + sourceComponent: desktopsListItemComponent + } + } + + footer: ColumnLayout { + Kirigami.FormLayout { + anchors.horizontalCenter: parent.horizontalCenter + + Connections { + target: kcm + + onNavWrapsChanged: navWraps.checked = kcm.navWraps + + onOsdEnabledChanged: osdEnabled.checked = kcm.osdEnabled + onOsdDurationChanged: osdDuration.value = kcm.osdDuration + onOsdTextOnlyChanged: osdTextOnly.checked = !kcm.osdTextOnly + } + + QtControls.CheckBox { + id: navWraps + + Kirigami.FormData.label: i18n("Options:") + + text: i18n("Navigation wraps around") + + checked: kcm.navWraps + + onCheckedChanged: kcm.navWraps = checked + } + + RowLayout { + Layout.fillWidth: true + + QtControls.CheckBox { + id: osdEnabled + + text: i18n("Show on-screen display when switching:") + + checked: kcm.osdEnabled + + onCheckedChanged: kcm.osdEnabled = checked + } + + QtControls.SpinBox { + id: osdDuration + + from: 0 + to: 10000 + stepSize: 100 + + textFromValue: function(value, locale) { return i18n("%1 ms", value)} + + value: kcm.osdDuration + + onValueChanged: kcm.osdDuration = value + } + } + + RowLayout { + Layout.fillWidth: true + + Item { + width: units.largeSpacing + } + + QtControls.CheckBox { + id: osdTextOnly + + enabled: osdEnabled.checked + + text: i18n("Show desktop layout indicators") + + checked: kcm.osdTextOnly + + onCheckedChanged: kcm.osdTextOnly = !checked + } + } + } + } +} + diff --git a/kcmkwin/kwindesktop/package/metadata.desktop b/kcmkwin/kwindesktop/package/metadata.desktop new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindesktop/package/metadata.desktop @@ -0,0 +1,116 @@ +[Desktop Entry] +Name=Virtual Desktops +Name[ar]=أسطح المكتب الافتراضية +Name[bg]=Виртуални работни плотове +Name[bs]=Virtuelne površi +Name[ca]=Escriptoris virtuals +Name[ca@valencia]=Escriptoris virtuals +Name[cs]=Virtuální plochy +Name[da]=Virtuelle skriveborde +Name[de]=Virtuelle Arbeitsflächen +Name[el]=Εικονικές επιφάνειες εργασίες +Name[en_GB]=Virtual Desktops +Name[es]=Escritorios virtuales +Name[et]=Virtuaalsed töölauad +Name[eu]=Alegiazko mahaigaina +Name[fi]=Virtuaalityöpöydät +Name[fr]=Bureaux virtuels +Name[ga]=Deasca Fíorúla +Name[gl]=Escritorios virtuais +Name[gu]=વર્ચ્યુઅલ ડેસ્કટોપો +Name[he]=שולחנות עבודה וירטואליים +Name[hi]=आभासी डेस्कटॉप +Name[hr]=Virtualne radne površine +Name[hu]=Virtuális asztalok +Name[ia]=Scriptorios virtual +Name[id]=Desktop Virtual +Name[is]=Sýndarskjáborð +Name[it]=Desktop virtuali +Name[ja]=仮想デスクトップ +Name[kk]=Виртуалды Үстелдер +Name[km]=ផ្ទៃតុ​និម្មិត +Name[kn]=ವಾಸ್ತವಪ್ರಾಯ ಗಣಕತೆರೆಗಳು +Name[ko]=가상 데스크톱 +Name[lt]=Virtualūs darbalaukiai +Name[lv]=Virtuālās darbvirsmas +Name[mr]=आभासी डेस्कटॉप +Name[nb]=Virtuelle skrivebord +Name[nds]=Mehr Schriefdischen +Name[nl]=Virtuele bureaubladen +Name[nn]=Virtuelle skrivebord +Name[pa]=ਵਰਚੁਅਲ ਡੈਸਕਟਾਪ +Name[pl]=Pulpity wirtualne +Name[pt]=Ecrãs Virtuais +Name[pt_BR]=Áreas de trabalho virtuais +Name[ro]=Birouri virtuale +Name[ru]=Рабочие столы +Name[si]=අත්ථ්‍ය වැඩතල +Name[sk]=Virtuálne pracovné plochy +Name[sl]=Navidezna namizja +Name[sr]=Виртуелне површи +Name[sr@ijekavian]=Виртуелне површи +Name[sr@ijekavianlatin]=Virtuelne površi +Name[sr@latin]=Virtuelne površi +Name[sv]=Virtuella skrivbord +Name[tg]=Мизҳои кории виртуалӣ +Name[th]=พื้นที่ทำงานเสมือน +Name[tr]=Sanal Masaüstleri +Name[ug]=مەۋھۇم ئۈستەلئۈستى +Name[uk]=Віртуальні стільниці +Name[wa]=Forveyous scribannes +Name[x-test]=xxVirtual Desktopsxx +Name[zh_CN]=虚拟桌面 +Name[zh_TW]=虛擬桌面 + +Comment=Navigation, Number and Layout of Virtual Desktops +Comment[bs]=Navigacija, broj i izgled virtualnih desktopa +Comment[ca]=Navegació, nombre i disposició dels escriptoris virtuals +Comment[ca@valencia]=Navegació, nombre i disposició dels escriptoris virtuals +Comment[cs]=Navigace, počet a rozvržení virtuálních ploch +Comment[da]=Navigation, antal og layout af virtuelle skriveborde +Comment[de]=Navigation, Anzahl und Layout virtueller Arbeitsflächen +Comment[el]=Περιήγηση, αριθμός και διάταξη εικονικών επιφανειών εργασίας +Comment[en_GB]=Navigation, Number and Layout of Virtual Desktops +Comment[es]=Navegación, número y disposición de los escritorios virtuales +Comment[et]=Virtuaalsete töölaudade vahel liikumine, nende arv ja paigutus +Comment[eu]=Nabigazioa, alegiazko mahaigainen kopurua eta antolamendua +Comment[fi]=Virtuaalityöpöytien vaihtaminen, määrä ja asettelu +Comment[fr]=Navigation, nombre et disposition des bureaux virtuels +Comment[gl]=Navegación, cantidade e disposición dos escritorios virtuais +Comment[he]=ניווט, פריסה ומספר שולחנות עבודה וירטואלים +Comment[hu]=Navigáció, a virtuális asztalok száma és elrendezése +Comment[id]=Navigasi, Jumlah dan Tata Letak Desktop Virtual +Comment[it]=Navigazione, numero e disposizione dei desktop virtuali +Comment[ko]=가상 데스크톱 탐색, 개수, 레이아웃 +Comment[lt]=Naršymas, Skaičius ir išdėstymas virtualių darbalaukių +Comment[nb]=Navigering, antall og utlegg av virtuelle skrivebord +Comment[nds]=Tall, Anornen un dat Anstüern vun de virtuellen Schriefdischen fastleggen +Comment[nl]=Navigatie door, aantal en indeling van virtuele bureaubladen +Comment[nn]=Navigering, nummer og vising av virtuelle skrivebord +Comment[pa]=ਵਰਚੁਅਲ ਡੈਸਕਟਾਪਾਂ ਲਈ ਨੇਵੀਗੇਸ਼ਨ, ਗਿਣਤੀ ਅਤੇ ਢਾਂਚਾ +Comment[pl]=Poruszanie się, liczba i układ wirtualnych pulpitów +Comment[pt]=Navegação, Número e Disposição dos Ecrãs Virtuais +Comment[pt_BR]=Navegação, quantidade e layout das áreas de trabalho virtuais +Comment[ru]=Число, расположение и способ переключения рабочих столов +Comment[sk]=Navigácia, počet a rozloženie virtuálnych plôch +Comment[sl]=Krmarjenje med, število in razporeditev navideznih namizij +Comment[sr]=Кретање, број и распоред виртуелних површи +Comment[sr@ijekavian]=Кретање, број и распоред виртуелних површи +Comment[sr@ijekavianlatin]=Kretanje, broj i raspored virtuelnih površi +Comment[sr@latin]=Kretanje, broj i raspored virtuelnih površi +Comment[sv]=Navigering, antal och layout av virtuella skrivbord +Comment[tr]=Gezinti, Sanal Masaüstlerinin Sayısı ve Yerleşimi +Comment[uk]=Навігація, кількість та компонування віртуальних стільниць +Comment[vi]=Số lượng, bố trí và điều hướng của màn hình ảo +Comment[x-test]=xxNavigation, Number and Layout of Virtual Desktopsxx +Comment[zh_CN]=虚拟桌面的切换,数量和布局 +Comment[zh_TW]=虛擬桌面的導覽、數字與佈局 + +Icon=preferences-desktop +Type=Service +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-Name=kcm_kwin_virtualdesktops +X-KDE-ServiceTypes=Plasma/Generic +X-Plasma-API=declarativeappletscript + +X-Plasma-MainScript=ui/main.qml diff --git a/kcmkwin/kwindesktop/virtualdesktops.h b/kcmkwin/kwindesktop/virtualdesktops.h new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindesktop/virtualdesktops.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 Eike Hein + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef VIRTUALDESKTOPS_H +#define VIRTUALDESKTOPS_H + +#include +#include + +namespace KWin +{ + +class DesktopsModel; + +class VirtualDesktops : public KQuickAddons::ConfigModule +{ + Q_OBJECT + + Q_PROPERTY(QAbstractItemModel* desktopsModel READ desktopsModel CONSTANT) + Q_PROPERTY(bool navWraps READ navWraps WRITE setNavWraps NOTIFY navWrapsChanged) + Q_PROPERTY(bool osdEnabled READ osdEnabled WRITE setOsdEnabled NOTIFY osdEnabledChanged) + Q_PROPERTY(int osdDuration READ osdDuration WRITE setOsdDuration NOTIFY osdDurationChanged) + Q_PROPERTY(bool osdTextOnly READ osdTextOnly WRITE setOsdTextOnly NOTIFY osdTextOnlyChanged) + +public: + explicit VirtualDesktops(QObject *parent = nullptr, const QVariantList &list = QVariantList()); + ~VirtualDesktops() override; + + QAbstractItemModel *desktopsModel() const; + + bool navWraps() const; + void setNavWraps(bool wraps); + + bool osdEnabled() const; + void setOsdEnabled(bool enabled); + + int osdDuration() const; + void setOsdDuration(int duration); + + int osdTextOnly() const; + void setOsdTextOnly(bool textOnly); + +Q_SIGNALS: + void navWrapsChanged() const; + void osdEnabledChanged() const; + void osdDurationChanged() const; + void osdTextOnlyChanged() const; + +public Q_SLOTS: + void load() override; + void save() override; + void defaults() override; + +private Q_SLOTS: + void updateNeedsSave(); + +private: + KSharedConfigPtr m_kwinConfig; + DesktopsModel *m_desktopsModel; + bool m_navWraps; + bool m_osdEnabled; + int m_osdDuration; + bool m_osdTextOnly; +}; + +} + +#endif diff --git a/kcmkwin/kwindesktop/virtualdesktops.cpp b/kcmkwin/kwindesktop/virtualdesktops.cpp new file mode 100644 --- /dev/null +++ b/kcmkwin/kwindesktop/virtualdesktops.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2018 Eike Hein + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "virtualdesktops.h" +#include "desktopsmodel.h" + +#include +#include +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON(VirtualDesktopsFactory, "kcm_kwin_virtualdesktops.json", registerPlugin();) + +namespace KWin +{ + +VirtualDesktops::VirtualDesktops(QObject *parent, const QVariantList &args) + : KQuickAddons::ConfigModule(parent, args) + , m_kwinConfig(KSharedConfig::openConfig("kwinrc")) + , m_desktopsModel(new KWin::DesktopsModel(this)) + , m_navWraps(true) + , m_osdEnabled(false) + , m_osdDuration(1000) + , m_osdTextOnly(false) +{ + KAboutData *about = new KAboutData(QStringLiteral("kcm_kwin_virtualdesktops"), + i18n("Configure Virtual Desktops"), + QStringLiteral("2.0"), QString(), KAboutLicense::GPL); + setAboutData(about); + + setButtons(Apply | Default); + + QObject::connect(m_desktopsModel, &KWin::DesktopsModel::userModifiedChanged, + this, &VirtualDesktops::updateNeedsSave); +} + +VirtualDesktops::~VirtualDesktops() +{ +} + +QAbstractItemModel *VirtualDesktops::desktopsModel() const +{ + return m_desktopsModel; +} + +bool VirtualDesktops::navWraps() const +{ + return m_navWraps; +} + +void VirtualDesktops::setNavWraps(bool wraps) +{ + if (m_navWraps != wraps) { + m_navWraps = wraps; + + emit navWrapsChanged(); + + updateNeedsSave(); + } +} + +bool VirtualDesktops::osdEnabled() const +{ + return m_osdEnabled; +} + +void VirtualDesktops::setOsdEnabled(bool enabled) +{ + if (m_osdEnabled != enabled) { + m_osdEnabled = enabled; + + emit osdEnabledChanged(); + + updateNeedsSave(); + } +} + +int VirtualDesktops::osdDuration() const +{ + return m_osdDuration; +} + +void VirtualDesktops::setOsdDuration(int duration) +{ + if (m_osdDuration != duration) { + m_osdDuration = duration; + + emit osdDurationChanged(); + + updateNeedsSave(); + } +} + +int VirtualDesktops::osdTextOnly() const +{ + return m_osdTextOnly; +} + +void VirtualDesktops::setOsdTextOnly(bool textOnly) +{ + if (m_osdTextOnly != textOnly) { + m_osdTextOnly = textOnly; + + emit osdTextOnlyChanged(); + + updateNeedsSave(); + } +} + +void VirtualDesktops::load() +{ + KConfigGroup navConfig(m_kwinConfig, "Windows"); + setNavWraps(navConfig.readEntry("RollOverDesktops", true)); + + KConfigGroup osdConfig(m_kwinConfig, "Plugins"); + setOsdEnabled(osdConfig.readEntry("desktopchangeosdEnabled", false)); + + KConfigGroup osdSettings(m_kwinConfig, "Script-desktopchangeosd"); + setOsdDuration(osdSettings.readEntry("PopupHideDelay", 1000)); + setOsdTextOnly(osdSettings.readEntry("TextOnly", false)); +} + +void VirtualDesktops::save() +{ + m_desktopsModel->syncWithServer(); + + KConfigGroup navConfig(m_kwinConfig, "Windows"); + navConfig.writeEntry("RollOverDesktops", m_navWraps); + + KConfigGroup osdConfig(m_kwinConfig, "Plugins"); + osdConfig.writeEntry("desktopchangeosdEnabled", m_osdEnabled); + + KConfigGroup osdSettings(m_kwinConfig, "Script-desktopchangeosd"); + osdSettings.writeEntry("PopupHideDelay", m_osdDuration); + osdSettings.writeEntry("TextOnly", m_osdTextOnly); + + m_kwinConfig->sync(); + + QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), + QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig")); + QDBusConnection::sessionBus().send(message); + + setNeedsSave(false); +} + +void VirtualDesktops::defaults() +{ + m_desktopsModel->setRows(1); + + setNavWraps(true); + setOsdEnabled(false); + setOsdDuration(1000); + setOsdTextOnly(false); +} + +void VirtualDesktops::updateNeedsSave() +{ + bool needsSave = false; + + if (m_desktopsModel->userModified()) { + needsSave = true; + } + + KConfigGroup navConfig(m_kwinConfig, "Windows"); + + if (m_navWraps != navConfig.readEntry("RollOverDesktops", true)) { + needsSave = true; + } + + KConfigGroup osdConfig(m_kwinConfig, "Plugins"); + + if (m_osdEnabled != osdConfig.readEntry("desktopchangeosdEnabled", false)) { + needsSave = true; + } + + KConfigGroup osdSettings(m_kwinConfig, "Script-desktopchangeosd"); + + if (m_osdDuration != osdSettings.readEntry("PopupHideDelay", 1000)) { + needsSave = true; + } + + if (m_osdTextOnly != osdSettings.readEntry("TextOnly", false)) { + needsSave = true; + } + + setNeedsSave(needsSave); +} + +} + +#include "virtualdesktops.moc"