diff --git a/kcmkwin/kwinrules/CMakeLists.txt b/kcmkwin/kwinrules/CMakeLists.txt
--- a/kcmkwin/kwinrules/CMakeLists.txt
+++ b/kcmkwin/kwinrules/CMakeLists.txt
@@ -3,20 +3,19 @@
add_definitions(-DKCMRULES)
include_directories(../../)
-set(kwinrules_SRCS ../../rulebooksettings.cpp
- ../../cursor.cpp
- ../../plugins/platforms/x11/standalone/x11cursor.cpp
- ../../rules.cpp
- ../../placement.cpp
- ../../utils.cpp
- yesnobox.cpp
- ruleswidget.cpp
- ruleslist.cpp
- kwinsrc.cpp
- detectwidget.cpp
- )
-
-ki18n_wrap_ui(kwinrules_SRCS ruleslist.ui detectwidget.ui editshortcut.ui ruleswidgetbase.ui)
+
+set(kwinrules_SRCS
+ ../../rulebooksettings.cpp
+ ../../cursor.cpp
+ ../../plugins/platforms/x11/standalone/x11cursor.cpp
+ ../../rules.cpp
+ ../../placement.cpp
+ ../../utils.cpp
+ kwinsrc.cpp
+ optionsmodel.cpp
+ ruleitem.cpp
+ rulesmodel.cpp
+)
kconfig_add_kcfg_files(kwinrules_SRCS ../../rulesettings.kcfgc)
kconfig_add_kcfg_files(kwinrules_SRCS ../../rulebooksettingsbase.kcfgc)
@@ -30,13 +29,11 @@
)
set(kcm_libs
- Qt5::Concurrent
- Qt5::X11Extras
+ Qt5::Quick
+ Qt5::QuickWidgets
- KF5::Completion
- KF5::ConfigWidgets
KF5::I18n
- KF5::Service
+ KF5::QuickAddons
KF5::WindowSystem
KF5::XmlGui
)
@@ -46,14 +43,14 @@
endif()
target_link_libraries(KWinRulesObjects ${kcm_libs} ${kwin_kcm_rules_XCB_LIBS})
-add_executable(kwin_rules_dialog main.cpp)
+add_executable(kwin_rules_dialog main.cpp rulesdialog.cpp)
target_link_libraries(kwin_rules_dialog KWinRulesObjects)
install(TARGETS kwin_rules_dialog DESTINATION ${LIBEXEC_INSTALL_DIR})
-add_library(kcm_kwinrules MODULE kcm.cpp)
+add_library(kcm_kwinrules MODULE kcmrules.cpp rulebookmodel.cpp)
target_link_libraries(kcm_kwinrules KWinRulesObjects)
-install(TARGETS kcm_kwinrules DESTINATION ${PLUGIN_INSTALL_DIR})
-
-########### install files ###############
+kcoreaddons_desktop_to_json(kcm_kwinrules "kcm_kwinrules.desktop" SERVICE_TYPES kcmodule.desktop)
-install(FILES kwinrules.desktop DESTINATION ${SERVICES_INSTALL_DIR})
+install(TARGETS kcm_kwinrules DESTINATION ${PLUGIN_INSTALL_DIR}/kcms)
+install(FILES kcm_kwinrules.desktop DESTINATION ${SERVICES_INSTALL_DIR})
+kpackage_install_package(package kcm_kwinrules kcms)
diff --git a/kcmkwin/kwinrules/detectwidget.h b/kcmkwin/kwinrules/detectwidget.h
deleted file mode 100644
--- a/kcmkwin/kwinrules/detectwidget.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 2004 Lubos Lunak
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-
-#ifndef __DETECTWIDGET_H__
-#define __DETECTWIDGET_H__
-
-#include
-#include
-
-#include "../../rules.h"
-//Added by qt3to4:
-#include
-#include
-
-#include "ui_detectwidget.h"
-
-namespace KWin
-{
-
-class DetectWidget
- : public QWidget, public Ui_DetectWidget
-{
- Q_OBJECT
-public:
- explicit DetectWidget(QWidget* parent = nullptr);
-};
-
-class DetectDialog
- : public QDialog
-{
- Q_OBJECT
-public:
- explicit DetectDialog(QWidget* parent = nullptr, const char* name = nullptr);
- void detect(int secs = 0);
- QByteArray selectedClass() const;
- bool selectedWholeClass() const;
- QByteArray selectedRole() const;
- bool selectedWholeApp() const;
- NET::WindowType selectedType() const;
- QString selectedTitle() const;
- Rules::StringMatch titleMatch() const;
- QByteArray selectedMachine() const;
-
- const QVariantMap &windowInfo() const {
- return m_windowInfo;
- }
-
-Q_SIGNALS:
- void detectionDone(bool);
-private Q_SLOTS:
- void selectWindow();
-private:
- void executeDialog();
- QByteArray wmclass_class;
- QByteArray wmclass_name;
- QByteArray role;
- NET::WindowType type;
- QString title;
- QByteArray extrarole;
- QByteArray machine;
- DetectWidget* widget;
- QVariantMap m_windowInfo;
-};
-
-} // namespace
-
-#endif
diff --git a/kcmkwin/kwinrules/detectwidget.cpp b/kcmkwin/kwinrules/detectwidget.cpp
deleted file mode 100644
--- a/kcmkwin/kwinrules/detectwidget.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (c) 2004 Lubos Lunak
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "detectwidget.h"
-#include "../../plugins/platforms/x11/standalone/x11cursor.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-
-Q_DECLARE_METATYPE(NET::WindowType)
-
-namespace KWin
-{
-
-DetectWidget::DetectWidget(QWidget* parent)
- : QWidget(parent)
-{
- setupUi(this);
-}
-
-DetectDialog::DetectDialog(QWidget* parent, const char* name)
- : QDialog(parent)
-{
- setObjectName(name);
- setModal(true);
- setLayout(new QVBoxLayout);
-
- widget = new DetectWidget(this);
- layout()->addWidget(widget);
-
- QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, this);
- layout()->addWidget(buttons);
-
- connect(buttons, SIGNAL(accepted()), SLOT(accept()));
- connect(buttons, SIGNAL(rejected()), SLOT(reject()));
-}
-
-void DetectDialog::detect(int secs)
-{
- QTimer::singleShot(secs*1000, this, SLOT(selectWindow()));
-}
-
-void DetectDialog::executeDialog()
-{
- static const char* const types[] = {
- I18N_NOOP("Normal Window"),
- I18N_NOOP("Desktop"),
- I18N_NOOP("Dock (panel)"),
- I18N_NOOP("Toolbar"),
- I18N_NOOP("Torn-Off Menu"),
- I18N_NOOP("Dialog Window"),
- I18N_NOOP("Override Type"),
- I18N_NOOP("Standalone Menubar"),
- I18N_NOOP("Utility Window"),
- I18N_NOOP("Splash Screen")
- };
- widget->class_label->setText(wmclass_class + QLatin1String(" (") + wmclass_name + ' ' + wmclass_class + ')');
- widget->role_label->setText(role);
- widget->match_role->setEnabled(!role.isEmpty());
- if (type == NET::Unknown)
- widget->type_label->setText(i18n("Unknown - will be treated as Normal Window"));
- else
- widget->type_label->setText(i18n(types[ type ]));
- widget->title_label->setText(title);
- widget->machine_label->setText(machine);
- widget->adjustSize();
- adjustSize();
- if (width() < 4*height()/3)
- resize(4*height()/3, height());
- emit detectionDone(exec() == QDialog::Accepted);
-}
-
-QByteArray DetectDialog::selectedClass() const
-{
- if (widget->match_whole_class->isChecked())
- return wmclass_name + ' ' + wmclass_class;
- return wmclass_class;
-}
-
-bool DetectDialog::selectedWholeClass() const
-{
- return widget->match_whole_class->isChecked();
-}
-
-QByteArray DetectDialog::selectedRole() const
-{
- if (widget->match_role->isChecked())
- return role;
- return "";
-}
-
-QString DetectDialog::selectedTitle() const
-{
- return title;
-}
-
-Rules::StringMatch DetectDialog::titleMatch() const
-{
- return widget->match_title->isChecked() ? Rules::ExactMatch : Rules::UnimportantMatch;
-}
-
-bool DetectDialog::selectedWholeApp() const
-{
- return !widget->match_type->isChecked();
-}
-
-NET::WindowType DetectDialog::selectedType() const
-{
- return type;
-}
-
-QByteArray DetectDialog::selectedMachine() const
-{
- return machine;
-}
-
-void DetectDialog::selectWindow()
-{
- QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"),
- QStringLiteral("/KWin"),
- QStringLiteral("org.kde.KWin"),
- QStringLiteral("queryWindowInfo"));
- QDBusPendingReply async = QDBusConnection::sessionBus().asyncCall(message);
-
- QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this);
- connect(callWatcher, &QDBusPendingCallWatcher::finished, this,
- [this](QDBusPendingCallWatcher *self) {
- QDBusPendingReply reply = *self;
- self->deleteLater();
- if (!reply.isValid()) {
- emit detectionDone(false);
- return;
- }
- m_windowInfo = reply.value();
- wmclass_class = m_windowInfo.value("resourceClass").toByteArray();
- wmclass_name = m_windowInfo.value("resourceName").toByteArray();
- role = m_windowInfo.value("role").toByteArray();
- type = m_windowInfo.value("type").value();
- title = m_windowInfo.value("caption").toString();
- machine = m_windowInfo.value("clientMachine").toByteArray();
- executeDialog();
- }
- );
-}
-
-} // namespace
-
diff --git a/kcmkwin/kwinrules/detectwidget.ui b/kcmkwin/kwinrules/detectwidget.ui
deleted file mode 100644
--- a/kcmkwin/kwinrules/detectwidget.ui
+++ /dev/null
@@ -1,229 +0,0 @@
-
-
- KWin::DetectWidget
-
-
-
- 0
- 0
- 450
- 300
-
-
-
- -
-
-
- Information About Selected Window
-
-
- true
-
-
-
- 0
-
-
-
-
-
- Class:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- false
-
-
-
- -
-
-
-
- true
-
-
-
-
-
-
- false
-
-
-
- -
-
-
- Role:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- false
-
-
-
- -
-
-
-
- true
-
-
-
-
-
-
- false
-
-
-
- -
-
-
- Type:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- false
-
-
-
- -
-
-
-
- true
-
-
-
-
-
-
- false
-
-
-
- -
-
-
- Title:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- false
-
-
-
- -
-
-
-
- true
-
-
-
-
-
-
- false
-
-
-
- -
-
-
- Machine:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- false
-
-
-
- -
-
-
-
- true
-
-
-
-
-
-
- false
-
-
-
-
-
-
- -
-
-
- Match by primary class name and
-
-
- true
-
-
-
-
-
-
- Secondary class name (resulting in term in brackets)
-
-
-
- -
-
-
- Window role (can be used to select windows by function)
-
-
-
- -
-
-
- Window type (eg. all dialogs, but not the main windows)
-
-
-
- -
-
-
- Window title (very specific, can fail due to content changes or translation)
-
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
- QSizePolicy::Expanding
-
-
-
- 20
- 0
-
-
-
-
-
-
-
-
-
diff --git a/kcmkwin/kwinrules/editshortcut.ui b/kcmkwin/kwinrules/editshortcut.ui
deleted file mode 100644
--- a/kcmkwin/kwinrules/editshortcut.ui
+++ /dev/null
@@ -1,161 +0,0 @@
-
- EditShortcut
-
-
-
- 0
- 0
- 1194
- 447
-
-
-
- -
-
-
- A single shortcut can be easily assigned or cleared using the two buttons. Only shortcuts with modifiers can be used.<p>
-It is possible to have several possible shortcuts, and the first available shortcut will be used. The shortcuts are specified using shortcut sets separated by " - ". One set is specified as <i>base</i>+(<i>list</i>), where base are modifiers and list is a list of keys.<br>
-For example "<b>Shift+Alt+(123) Shift+Ctrl+(ABC)</b>" will first try <b>Shift+Alt+1</b>, then others with <b>Shift+Ctrl+C</b> as the last one.
-
-
- Qt::RichText
-
-
- true
-
-
-
- -
-
-
- QFrame::HLine
-
-
- QFrame::Sunken
-
-
-
- -
-
-
- -
-
-
-
-
-
- Qt::Vertical
-
-
- QSizePolicy::Expanding
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- &Single Shortcut
-
-
-
- -
-
-
- Qt::Vertical
-
-
- QSizePolicy::Expanding
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- C&lear
-
-
-
- -
-
-
- Qt::Vertical
-
-
- QSizePolicy::Expanding
-
-
-
- 40
- 20
-
-
-
-
-
-
- -
-
-
- QFrame::HLine
-
-
- QFrame::Sunken
-
-
-
-
-
-
-
- KLineEdit
- QWidget
-
-
-
-
-
-
- pushButton1
- clicked()
- EditShortcut
- editShortcut()
-
-
- 20
- 20
-
-
- 20
- 20
-
-
-
-
- pushButton2
- clicked()
- EditShortcut
- clearShortcut()
-
-
- 20
- 20
-
-
- 20
- 20
-
-
-
-
-
diff --git a/kcmkwin/kwinrules/kcm.cpp b/kcmkwin/kwinrules/kcm.cpp
deleted file mode 100644
--- a/kcmkwin/kwinrules/kcm.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2004 Lubos Lunak
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "kcm.h"
-
-#include
-//Added by qt3to4:
-#include
-#include
-#include
-#include
-#include
-
-#include "ruleslist.h"
-#include
-#include
-
-K_PLUGIN_FACTORY(KCMRulesFactory,
- registerPlugin();
- )
-
-namespace KWin
-{
-
-KCMRules::KCMRules(QWidget *parent, const QVariantList &)
- : KCModule(parent)
-{
- QVBoxLayout *layout = new QVBoxLayout(this);
- layout->setContentsMargins(0, 0, 0, 0);
-
- widget = new KCMRulesList(this);
- layout->addWidget(widget);
- connect(widget, SIGNAL(changed(bool)), SLOT(moduleChanged(bool)));
- KAboutData *about = new KAboutData(QStringLiteral("kcmkwinrules"),
- i18n("Window-Specific Settings Configuration Module"),
- QString(), QString(), KAboutLicense::GPL, i18n("(c) 2004 KWin and KControl Authors"));
- about->addAuthor(i18n("Lubos Lunak"), QString(), "l.lunak@kde.org");
- setAboutData(about);
- setButtons(KCModule::Help | KCModule::Apply);
-}
-
-void KCMRules::load()
-{
- widget->load();
- emit KCModule::changed(false);
-}
-
-void KCMRules::save()
-{
- widget->save();
- emit KCModule::changed(false);
- // Send signal to all kwin instances
- QDBusMessage message =
- QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
- QDBusConnection::sessionBus().send(message);
-
-}
-
-QString KCMRules::quickHelp() const
-{
- return i18n("Window-specific Settings
Here you can customize window settings specifically only"
- " for some windows.
"
- " Please note that this configuration will not take effect if you do not use"
- " KWin as your window manager. If you do use a different window manager, please refer to its documentation"
- " for how to customize window behavior.
");
-}
-
-void KCMRules::moduleChanged(bool state)
-{
- emit KCModule::changed(state);
-}
-
-}
-
-// i18n freeze :-/
-#if 0
-I18N_NOOP("Remember settings separately for every window")
-I18N_NOOP("Show internal settings for remembering")
-I18N_NOOP("Internal setting for remembering")
-#endif
-
-
-#include "kcm.moc"
diff --git a/kcmkwin/kwinrules/kwinrules.desktop b/kcmkwin/kwinrules/kcm_kwinrules.desktop
rename from kcmkwin/kwinrules/kwinrules.desktop
rename to kcmkwin/kwinrules/kcm_kwinrules.desktop
--- a/kcmkwin/kwinrules/kwinrules.desktop
+++ b/kcmkwin/kwinrules/kcm_kwinrules.desktop
@@ -1,16 +1,17 @@
[Desktop Entry]
Exec=kcmshell5 kwinrules
Icon=preferences-system-windows-actions
+Categories=Qt;KDE;X-KDE-settings-looknfeel;
Type=Service
-X-KDE-ServiceTypes=KCModule
-X-DocPath=kcontrol/windowspecific/index.html
+X-KDE-ServiceTypes=KCModule
X-KDE-Library=kcm_kwinrules
X-KDE-ParentApp=kcontrol
-
X-KDE-System-Settings-Parent-Category=windowmanagement
X-KDE-Weight=120
+X-KDE-FormFactors=desktop,tablet
+
Name=Window Rules
Name[ar]=قواعد النوافذ
Name[bg]=Правила за прозорци
diff --git a/kcmkwin/kwinrules/kcmrules.h b/kcmkwin/kwinrules/kcmrules.h
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/kcmrules.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include
+#include "rulebookmodel.h"
+#include "rulesmodel.h"
+
+#include
+
+
+namespace KWin
+{
+
+class KCMKWinRules : public KQuickAddons::ConfigModule
+{
+ Q_OBJECT
+
+ Q_PROPERTY(RuleBookModel *ruleBookModel MEMBER m_ruleBookModel CONSTANT)
+ Q_PROPERTY(RulesModel *rulesModel MEMBER m_rulesModel CONSTANT)
+ Q_PROPERTY(int editIndex READ editIndex NOTIFY editIndexChanged)
+
+public:
+ explicit KCMKWinRules(QObject *parent, const QVariantList &arguments);
+
+ Q_INVOKABLE void setRuleDescription(int index, const QString &description);
+ Q_INVOKABLE void editRule(int index);
+
+ Q_INVOKABLE void createRule();
+ Q_INVOKABLE void removeRule(int index);
+ Q_INVOKABLE void moveRule(int sourceIndex, int destIndex);
+
+ Q_INVOKABLE void exportToFile(const QUrl &path, const QList &indexes);
+ Q_INVOKABLE void importFromFile(const QUrl &path);
+
+public slots:
+ void load() override;
+ void save() override;
+
+signals:
+ void editIndexChanged();
+
+private slots:
+ void updateNeedsSave();
+
+private:
+ int editIndex() const;
+ void saveCurrentRule();
+
+private:
+ RuleBookModel *m_ruleBookModel;
+ RulesModel* m_rulesModel;
+
+ QPersistentModelIndex m_editIndex;
+};
+
+} // namespace
diff --git a/kcmkwin/kwinrules/kcmrules.cpp b/kcmkwin/kwinrules/kcmrules.cpp
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/kcmrules.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2004 Lubos Lunak
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 "kcmrules.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+
+namespace KWin
+{
+
+KCMKWinRules::KCMKWinRules(QObject *parent, const QVariantList &arguments)
+ : KQuickAddons::ConfigModule(parent, arguments)
+ , m_ruleBookModel(new RuleBookModel(this))
+ , m_rulesModel(new RulesModel(this))
+{
+ auto about = new KAboutData(QStringLiteral("kcm_kwinrules"),
+ i18n("Window Rules"),
+ QStringLiteral("1.0"),
+ QString(),
+ KAboutLicense::GPL);
+ about->addAuthor(i18n("Ismael Asensio"),
+ i18n("Author"),
+ QStringLiteral("isma.af@gmail.com"));
+ setAboutData(about);
+
+ setQuickHelp(i18n("Window-specific Settings
Here you can customize window settings specifically only"
+ " for some windows."
+ " Please note that this configuration will not take effect if you do not use"
+ " KWin as your window manager. If you do use a different window manager, please refer to its documentation"
+ " for how to customize window behavior.
"));
+
+ connect(m_rulesModel, &RulesModel::descriptionChanged, this, [this]{
+ if (m_editIndex.isValid()) {
+ m_ruleBookModel->setDescriptionAt(m_editIndex.row(), m_rulesModel->description());
+ }
+ } );
+ connect(m_rulesModel, &RulesModel::dataChanged, this, &KCMKWinRules::updateNeedsSave);
+ connect(m_ruleBookModel, &RulesModel::dataChanged, this, &KCMKWinRules::updateNeedsSave);
+}
+
+void KCMKWinRules::load()
+{
+ m_ruleBookModel->load();
+
+ m_editIndex = QModelIndex();
+ emit editIndexChanged();
+
+ setNeedsSave(false);
+}
+
+void KCMKWinRules::save()
+{
+ saveCurrentRule();
+
+ m_ruleBookModel->save();
+
+ // Notify kwin to reload configuration
+ QDBusMessage message = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
+ QDBusConnection::sessionBus().send(message);
+}
+
+void KCMKWinRules::updateNeedsSave()
+{
+ setNeedsSave(true);
+ emit needsSaveChanged();
+}
+
+void KCMKWinRules::saveCurrentRule()
+{
+ if (m_editIndex.isValid() && needsSave()) {
+ m_ruleBookModel->setRuleAt(m_editIndex.row(), m_rulesModel->exportToRules());
+ }
+}
+
+int KCMKWinRules::editIndex() const
+{
+ if (!m_editIndex.isValid()) {
+ return -1;
+ }
+ return m_editIndex.row();
+}
+
+
+void KCMKWinRules::setRuleDescription(int index, const QString &description)
+{
+ if (index < 0 || index >= m_ruleBookModel->rowCount()) {
+ return;
+ }
+
+ if (m_editIndex.row() == index) {
+ m_rulesModel->setDescription(description);
+ return;
+ }
+ m_ruleBookModel->setDescriptionAt(index, description);
+
+ updateNeedsSave();
+}
+
+
+void KCMKWinRules::editRule(int index)
+{
+ if (index < 0 || index >= m_ruleBookModel->rowCount()) {
+ return;
+ }
+ saveCurrentRule();
+
+ m_editIndex = m_ruleBookModel->index(index);
+ emit editIndexChanged();
+
+ m_rulesModel->importFromRules(m_ruleBookModel->ruleAt(m_editIndex.row()));
+
+ // Set the active page to rules editor (0:RulesList, 1:RulesEditor)
+ setCurrentIndex(1);
+}
+
+void KCMKWinRules::createRule()
+{
+ const int newIndex = m_ruleBookModel->rowCount();
+ m_ruleBookModel->insertRow(newIndex);
+
+ updateNeedsSave();
+
+ editRule(newIndex);
+}
+
+void KCMKWinRules::removeRule(int index)
+{
+ if (index < 0 || index >= m_ruleBookModel->rowCount()) {
+ return;
+ }
+
+ m_ruleBookModel->removeRow(index);
+
+ emit editIndexChanged();
+ updateNeedsSave();
+}
+
+void KCMKWinRules::moveRule(int sourceIndex, int destIndex)
+{
+ const int lastIndex = m_ruleBookModel->rowCount() - 1;
+ if (sourceIndex == destIndex
+ || (sourceIndex < 0 || sourceIndex > lastIndex)
+ || (destIndex < 0 || destIndex > lastIndex)) {
+ return;
+ }
+
+ m_ruleBookModel->moveRow(QModelIndex(), sourceIndex, QModelIndex(), destIndex);
+
+ emit editIndexChanged();
+ updateNeedsSave();
+}
+
+void KCMKWinRules::exportToFile(const QUrl &path, const QList &indexes)
+{
+ if (indexes.isEmpty()) {
+ return;
+ }
+ saveCurrentRule();
+
+ const auto config = KSharedConfig::openConfig(path.toLocalFile(), KConfig::SimpleConfig);
+
+ for (const QString &groupName : config->groupList()) {
+ config->deleteGroup(groupName);
+ }
+
+ for (int index : indexes) {
+ if (index < 0 || index > m_ruleBookModel->rowCount()) {
+ continue;
+ }
+ const Rules *rule = m_ruleBookModel->ruleAt(index);
+ RuleSettings settings(config, rule->description);
+ settings.setDefaults();
+ rule->write(&settings);
+ settings.save();
+ }
+}
+
+void KCMKWinRules::importFromFile(const QUrl &path)
+{
+ const auto config = KSharedConfig::openConfig(path.toLocalFile(), KConfig::SimpleConfig);
+ const QStringList groups = config->groupList();
+ if (groups.isEmpty()) {
+ return;
+ }
+
+ for (const QString &groupName : groups) {
+ RuleSettings settings(config, groupName);
+
+ const bool remove = settings.deleteRule();
+ const QString importDescription = settings.description();
+ if (importDescription.isEmpty()) {
+ continue;
+ }
+
+ // Try to find a rule with the same description to replace
+ int newIndex = -2;
+ for (int index = 0; index < m_ruleBookModel->rowCount(); index++) {
+ if (m_ruleBookModel->descriptionAt(index) == importDescription) {
+ newIndex = index;
+ break;
+ }
+ }
+
+ if (remove) {
+ m_ruleBookModel->removeRow(newIndex);
+ continue;
+ }
+
+ if (newIndex < 0) {
+ newIndex = m_ruleBookModel->rowCount();
+ m_ruleBookModel->insertRow(newIndex);
+ }
+
+ m_ruleBookModel->setRuleAt(newIndex, new Rules(&settings));
+
+ // Reset rule editor if the current rule changed when importing
+ if (m_editIndex.row() == newIndex) {
+ m_rulesModel->importFromRules(m_ruleBookModel->ruleAt(m_editIndex.row()));
+ }
+ }
+
+ updateNeedsSave();
+}
+
+K_PLUGIN_CLASS_WITH_JSON(KCMKWinRules, "kcm_kwinrules.json");
+
+} // namespace
+
+#include "kcmrules.moc"
diff --git a/kcmkwin/kwinrules/main.cpp b/kcmkwin/kwinrules/main.cpp
--- a/kcmkwin/kwinrules/main.cpp
+++ b/kcmkwin/kwinrules/main.cpp
@@ -22,8 +22,8 @@
#include
#include
-#include "ruleswidget.h"
#include "rulebooksettings.h"
+#include "rulesdialog.h"
#include "../../rules.h"
#include
diff --git a/kcmkwin/kwinrules/optionsmodel.h b/kcmkwin/kwinrules/optionsmodel.h
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/optionsmodel.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 KWIN_OPTIONS_MODEL_H
+#define KWIN_OPTIONS_MODEL_H
+
+#include
+
+#include
+#include
+#include
+
+
+namespace KWin {
+
+class OptionsModel : public QAbstractListModel
+{
+ Q_OBJECT
+ Q_PROPERTY(int selectedIndex READ selectedIndex NOTIFY selectedIndexChanged)
+
+public:
+ struct Data {
+ Data(const QVariant &value, const QString &text, const QIcon &icon = {}, const QString &description = {})
+ : value(value)
+ , text(text)
+ , icon(icon)
+ , description(description)
+ {}
+ Data(const QVariant &value, const QString &text, const QString &description)
+ : value(value)
+ , text(text)
+ , description(description)
+ {}
+
+ QVariant value;
+ QString text;
+ QIcon icon;
+ QString description;
+ };
+
+public:
+ OptionsModel() : QAbstractListModel(), m_data(), m_index(0) {};
+ OptionsModel(const QList &data) : QAbstractListModel(), m_data(data), m_index(0) {};
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QHash roleNames() const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ QVariant value() const;
+ void setValue(QVariant value);
+ void resetValue();
+
+ void updateModelData(const QList &data);
+
+ Q_INVOKABLE int indexOf(QVariant value) const;
+ Q_INVOKABLE QString textOfValue(QVariant value) const;
+ int selectedIndex() const;
+
+signals:
+ void selectedIndexChanged(int index);
+
+public:
+ QList m_data;
+
+protected:
+ int m_index = 0;
+};
+
+class RulePolicy : public OptionsModel
+{
+public:
+ enum Type {
+ NoPolicy,
+ StringMatch,
+ SetRule,
+ ForceRule
+ };
+
+public:
+ RulePolicy(Type type)
+ : OptionsModel(policyOptions(type))
+ , m_type(type)
+ {};
+
+ Type type() const;
+ int value() const;
+ QString policyKey(const QString &key) const;
+
+private:
+ static QList policyOptions(RulePolicy::Type type);
+
+private:
+ Type m_type;
+};
+
+} //namespace
+
+#endif //KWIN_OPTIONS_MODEL_H
diff --git a/kcmkwin/kwinrules/optionsmodel.cpp b/kcmkwin/kwinrules/optionsmodel.cpp
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/optionsmodel.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 "optionsmodel.h"
+
+#include
+
+
+namespace KWin
+{
+
+QHash OptionsModel::roleNames() const
+{
+ return {
+ {Qt::DisplayRole, QByteArrayLiteral("display")},
+ {Qt::DecorationRole, QByteArrayLiteral("decoration")},
+ {Qt::ToolTipRole, QByteArrayLiteral("tooltip")},
+ {Qt::UserRole, QByteArrayLiteral("value")},
+ {Qt::UserRole + 1, QByteArrayLiteral("iconName")},
+ };
+}
+
+int OptionsModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid()) {
+ return 0;
+ }
+ return m_data.size();
+}
+
+QVariant OptionsModel::data(const QModelIndex &index, int role) const
+{
+ if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) {
+ return QVariant();
+ }
+
+ const Data data = m_data.at(index.row());
+
+ switch (role) {
+ case Qt::DisplayRole:
+ return data.text;
+ case Qt::UserRole:
+ return data.value;
+ case Qt::DecorationRole:
+ return data.icon;
+ case Qt::UserRole + 1:
+ return data.icon.name();
+ case Qt::ToolTipRole:
+ return data.description;
+ }
+ return QVariant();
+}
+
+int OptionsModel::selectedIndex() const
+{
+ return m_index;
+}
+
+int OptionsModel::indexOf(QVariant value) const
+{
+ for (int index = 0; index < m_data.count(); index++) {
+ if (m_data.at(index).value == value) {
+ return index;
+ }
+ }
+ return -1;
+}
+
+QString OptionsModel::textOfValue(QVariant value) const
+{
+ int index = indexOf(value);
+ if (index < 0 || index >= m_data.count()) {
+ return QString();
+ }
+ return m_data.at(index).text;
+}
+
+QVariant OptionsModel::value() const
+{
+ if (m_data.isEmpty()) {
+ return QVariant();
+ }
+ return m_data.at(m_index).value;
+}
+
+void OptionsModel::setValue(QVariant value)
+{
+ if (this->value() == value) {
+ return;
+ }
+ int index = indexOf(value);
+ if (index >= 0 && index != m_index) {
+ m_index = index;
+ emit selectedIndexChanged(index);
+ }
+}
+
+void OptionsModel::resetValue()
+{
+ m_index = 0;
+ emit selectedIndexChanged(m_index);
+}
+
+void OptionsModel::updateModelData(const QList &data) {
+ beginResetModel();
+ m_data = data;
+ endResetModel();
+}
+
+
+RulePolicy::Type RulePolicy::type() const
+{
+ return m_type;
+}
+
+int RulePolicy::value() const
+{
+ if (m_type == RulePolicy::NoPolicy) {
+ return Rules::Apply; // To simplify external checks when rule has no policy
+ }
+ return OptionsModel::value().toInt();
+}
+
+QString RulePolicy::policyKey(const QString &key) const
+{
+ switch (m_type) {
+ case NoPolicy:
+ return QString();
+ case StringMatch:
+ return QStringLiteral("%1match").arg(key);
+ case SetRule:
+ case ForceRule:
+ return QStringLiteral("%1rule").arg(key);
+ }
+
+ return QString();
+}
+
+QList RulePolicy::policyOptions(RulePolicy::Type type)
+{
+ static const auto stringMatchOptions = QList {
+ {Rules::UnimportantMatch, i18n("Unimportant")},
+ {Rules::ExactMatch, i18n("Exact Match")},
+ {Rules::SubstringMatch, i18n("Substring Match")},
+ {Rules::RegExpMatch, i18n("Regular Expression")}
+ };
+
+ static const auto setRuleOptions = QList {
+ {Rules::DontAffect,
+ i18n("Do Not Affect"),
+ i18n("The window property will not be affected and therefore the default handling for it will be used."
+ "\nSpecifying this will block more generic window settings from taking effect.")},
+ {Rules::Apply,
+ i18n("Apply Initially"),
+ i18n("The window property will be only set to the given value after the window is created."
+ "\nNo further changes will be affected.")},
+ {Rules::Remember,
+ i18n("Remember"),
+ i18n("The value of the window property will be remembered and, every time the window"
+ " is created, the last remembered value will be applied.")},
+ {Rules::Force,
+ i18n("Force"),
+ i18n("The window property will be always forced to the given value.")},
+ {Rules::ApplyNow,
+ i18n("Apply Now"),
+ i18n("The window property will be set to the given value immediately and will not be affected later"
+ "\n(this action will be deleted afterwards).")},
+ {Rules::ForceTemporarily,
+ i18n("Force Temporarily"),
+ i18n("The window property will be forced to the given value until it is hidden"
+ "\n(this action will be deleted after the window is hidden).")}
+ };
+
+ static auto forceRuleOptions = QList {
+ setRuleOptions.at(0), // Rules::DontAffect
+ setRuleOptions.at(3), // Rules::Force
+ setRuleOptions.at(5), // Rules::ForceTemporarily
+ };
+
+ switch (type) {
+ case NoPolicy:
+ return {};
+ case StringMatch:
+ return stringMatchOptions;
+ case SetRule:
+ return setRuleOptions;
+ case ForceRule:
+ return forceRuleOptions;
+ }
+ return {};
+}
+
+} //namespace
diff --git a/kcmkwin/kwinrules/package/contents/ui/FileDialogLoader.qml b/kcmkwin/kwinrules/package/contents/ui/FileDialogLoader.qml
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/package/contents/ui/FileDialogLoader.qml
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 .
+ */
+
+import QtQuick 2.14
+import QtQuick.Dialogs 1.0 as QtDialogs
+
+Loader {
+ id: root
+ active: false
+
+ property string title : i18n("Select File");
+ property string lastFolder : ""
+ property bool isSaveDialog : false
+
+ signal fileSelected(string path)
+
+ sourceComponent: QtDialogs.FileDialog {
+ id: fileDialog
+
+ title: root.title
+ selectExisting: !root.isSaveDialog
+ folder: (root.lastFolder == "") ? shortcuts.home : root.lastFolder
+ nameFilters: [ i18n("KWin Rules (*.kwinrule)") ]
+ defaultSuffix: "*.kwinrule"
+
+ Component.onCompleted: {
+ open();
+ }
+
+ onAccepted: {
+ root.lastFolder = folder;
+ if (fileUrl != "") {
+ root.fileSelected(fileUrl);
+ }
+ root.active = false;
+ }
+
+ onRejected: {
+ root.active = false;
+ }
+ }
+}
diff --git a/kcmkwin/kwinrules/package/contents/ui/OptionsComboBox.qml b/kcmkwin/kwinrules/package/contents/ui/OptionsComboBox.qml
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/package/contents/ui/OptionsComboBox.qml
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 .
+ */
+
+import QtQuick 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Controls 2.14 as QQC2
+
+import org.kde.kirigami 2.10 as Kirigami
+
+
+QQC2.ComboBox {
+ id: optionsCombo
+
+ textRole: "display"
+ //TODO: After KF5 (qqc2-desktop-style) depends on Qt 5.15 this can be simplified using newer API
+ // (https://bugs.kde.org/show_bug.cgi?id=419521)
+ // valueRole: "value"
+ property var currentValue
+
+ onActivated: (index) => {
+ var modelIndex = model.index(index, 0);
+ currentValue = model.data(modelIndex, Qt.UserRole);
+ }
+
+ property bool multipleChoice: false
+ property int selectionMask: 0
+
+ currentIndex: multipleChoice ? -1 : model.selectedIndex
+
+ displayText: {
+ if (!multipleChoice) {
+ return currentText;
+ }
+ var selectionCount = selectionMask.toString(2).replace(/0/g, '').length;
+ switch (selectionCount) {
+ case 0:
+ return i18n("None selected");
+ case 1:
+ var selectedValue = selectionMask.toString(2).length - 1;
+ return model.textOfValue(selectedValue);
+ case count:
+ return i18n("All selected");
+ }
+ return i18np("1 selected", "%1 selected", selectionCount);
+ }
+
+ delegate: QQC2.ItemDelegate {
+ highlighted: optionsCombo.highlightedIndex == index
+ width: parent.width
+
+ LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
+ LayoutMirroring.childrenInherit: true
+
+ contentItem: RowLayout {
+ QQC2.CheckBox {
+ id: itemSelection
+ visible: multipleChoice
+ checked: (selectionMask & (1 << value))
+ onToggled: {
+ selectionMask = (selectionMask & ~(1 << value)) | (checked << value);
+ activated(index);
+ }
+ }
+ Kirigami.Icon {
+ source: model.decoration
+ Layout.preferredHeight: Kirigami.Units.iconSizes.small
+ Layout.preferredWidth: Kirigami.Units.iconSizes.small
+ }
+ QQC2.Label {
+ text: model.display
+ color: highlighted ? Kirigami.Theme.highlightedTextColor : Kirigami.Theme.textColor
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignLeft
+ }
+ }
+
+ MouseArea {
+ anchors.fill: contentItem
+ enabled: multipleChoice
+ onClicked: {
+ itemSelection.toggle();
+ itemSelection.toggled();
+ }
+ }
+
+ QQC2.ToolTip {
+ text: model.tooltip
+ visible: hovered && (model.tooltip != "")
+ }
+
+ Component.onCompleted: {
+ optionsCombo.popup.width = Math.max(implicitWidth, optionsCombo.width, optionsCombo.popup.width);
+ }
+
+ onFocusChanged: {
+ if (!focus) popup.close();
+ }
+ }
+}
diff --git a/kcmkwin/kwinrules/package/contents/ui/RuleItemDelegate.qml b/kcmkwin/kwinrules/package/contents/ui/RuleItemDelegate.qml
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/package/contents/ui/RuleItemDelegate.qml
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 .
+ */
+
+import QtQuick 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Controls 2.14 as QQC2
+import org.kde.kirigami 2.10 as Kirigami
+
+Kirigami.AbstractListItem {
+ id: ruleDelegate
+
+ property bool ruleEnabled: model.enabled
+
+ Kirigami.Theme.colorSet: Kirigami.Theme.View
+
+ highlighted: false
+
+ RowLayout {
+ anchors {
+ left: parent.left
+ right: parent.right
+ margins: Kirigami.Units.smallSpacing
+ }
+
+ Kirigami.Icon {
+ id: itemIcon
+ source: model.icon
+ Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
+ Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
+ Layout.rightMargin: Kirigami.Units.smallSpacing
+ QQC2.ToolTip {
+ text: model.description
+ visible: hovered && (model.description != "")
+ }
+ }
+
+ QQC2.Label {
+ text: model.name
+ Layout.minimumWidth: 12 * Kirigami.Units.gridUnit
+ horizontalAlignment: Text.AlignLeft
+ elide: Text.ElideRight
+ }
+
+ QQC2.ToolButton {
+ id: itemEnabled
+ icon.name: "list-remove"
+ opacity: (ruleDelegate.hovered && model.selectable) ? 1 : 0
+ onClicked: {
+ model.enabled = false;
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ OptionsComboBox {
+ id: policyCombo
+ Layout.minimumWidth: 6 * Kirigami.Units.gridUnit
+ Layout.maximumWidth: 12 * Kirigami.Units.gridUnit
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter
+ flat: true
+
+ visible: count > 0
+ enabled: ruleEnabled
+
+ model: policyModel
+ onActivated: {
+ policy = currentValue;
+ }
+ }
+
+ ValueEditor {
+ id: valueEditor
+ Layout.minimumWidth: 9 * Kirigami.Units.gridUnit
+ Layout.maximumWidth: policyCombo.visible ? 20 * Kirigami.Units.gridUnit
+ : 32 * Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+
+ enabled: model.enabled
+
+ ruleValue: model.value
+ ruleOptions: model.options
+ controlType: model.type
+
+ onValueEdited: (value) => {
+ model.value = value;
+ }
+ }
+ }
+}
diff --git a/kcmkwin/kwinrules/package/contents/ui/RulesEditor.qml b/kcmkwin/kwinrules/package/contents/ui/RulesEditor.qml
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/package/contents/ui/RulesEditor.qml
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 .
+ */
+
+import QtQuick 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Controls 2.14 as QQC2
+import org.kde.kirigami 2.10 as Kirigami
+import org.kde.kcm 1.2
+import org.kde.kitemmodels 1.0
+import org.kde.kcms.kwinrules 1.0
+
+
+ScrollViewKCM {
+ id: rulesEditor
+
+ property var rulesModel: kcm.rulesModel
+
+ title: rulesModel.description
+
+ view: ListView {
+ id: rulesView
+ clip: true
+
+ model: enabledRulesModel
+ delegate: RuleItemDelegate {}
+ section {
+ property: "section"
+ delegate: Kirigami.ListSectionHeader { label: section }
+ }
+
+ Kirigami.Heading {
+ level: 3
+ enabled: false
+ visible: rulesView.count <= 4
+ anchors {
+ top: parent.contentItem.bottom
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+ horizontalAlignment: Qt.AlignHCenter
+ verticalAlignment: Qt.AlignVCenter
+ wrapMode: Text.WordWrap
+ text: i18n("Add specific properties to the rule")
+ }
+ }
+
+ // FIXME: InlineMessage.qml:241:13: QML Label: Binding loop detected for property "verticalAlignment"
+ header: Kirigami.InlineMessage {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ text: rulesModel.warningMessage
+ visible: text != ""
+ }
+
+ footer: RowLayout {
+ QQC2.Button {
+ text: i18n("Detect window properties")
+ icon.name: "edit-find"
+ onClicked: {
+ overlayModel.onlySuggestions = true;
+ rulesModel.detectWindowProperties(delaySpin.value);
+ }
+ }
+ QQC2.SpinBox {
+ id: delaySpin
+ Layout.minimumWidth: Kirigami.Units.gridUnit * 6
+ from: 0
+ to: 30
+ textFromValue: (value, locale) => {
+ return (value == 0) ? i18n("Instantly")
+ : i18np("After 1 second", "After %1 seconds", value)
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ }
+ QQC2.Button {
+ text: i18n("Add properties")
+ icon.name: "list-add-symbolic"
+ checkable: true
+ checked: propertySheet.sheetOpen
+ onToggled: {
+ propertySheet.sheetOpen = checked;
+ }
+ }
+ }
+
+ Connections {
+ target: rulesModel
+ onSuggestionsChanged: {
+ propertySheet.sheetOpen = true;
+ }
+ }
+
+ Kirigami.OverlaySheet {
+ id: propertySheet
+
+ parent: view
+
+ header: Kirigami.Heading {
+ text: i18n("Select properties")
+ }
+ footer: Kirigami.SearchField {
+ id: searchField
+ horizontalAlignment: Text.AlignLeft
+ }
+
+ ListView {
+ model: overlayModel
+
+ clip: true
+ Layout.preferredWidth: Kirigami.Units.gridUnit * 28
+
+ section {
+ property: "section"
+ delegate: Kirigami.ListSectionHeader { label: section }
+ }
+
+ delegate: Kirigami.AbstractListItem {
+ id: propertyDelegate
+ highlighted: false
+
+ RowLayout {
+ anchors.fill: parent
+
+ Kirigami.Icon {
+ source: model.icon
+ Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
+ Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
+ Layout.leftMargin: Kirigami.Units.smallSpacing
+ }
+ QQC2.Label {
+ text: model.name
+ horizontalAlignment: Qt.AlignLeft
+ Layout.minimumWidth: implicitWidth
+ Layout.rightMargin: Kirigami.Units.largeSpacing
+ Layout.fillWidth: true
+ QQC2.ToolTip {
+ text: model.description
+ visible: hovered && (model.description != "")
+ }
+ }
+ QQC2.Label {
+ id: suggestedLabel
+ text: formatValue(model.suggested, model.type, model.options)
+ horizontalAlignment: Qt.AlignRight
+ opacity: 0.7
+ Layout.fillWidth: true
+ elide: Text.ElideRight
+ QQC2.ToolTip {
+ text: suggestedLabel.text
+ visible: hovered && suggestedLabel.truncated
+ }
+ }
+ QQC2.ToolButton {
+ icon.name: (model.enabled) ? "dialog-ok-apply" : "list-add-symbolic"
+ opacity: propertyDelegate.hovered ? 1 : 0
+ onClicked: propertyDelegate.clicked()
+ Layout.maximumWidth: implicitWidth
+ }
+ }
+
+ onClicked: {
+ model.enabled = true;
+ if (model.suggested != null) {
+ model.value = model.suggested;
+ model.suggested = null;
+ }
+ }
+ }
+ }
+
+ onSheetOpenChanged: {
+ searchField.text = "";
+ if (sheetOpen) {
+ searchField.focus = true; // FIXME: It doesn't set the focus to the search field
+ } else {
+ overlayModel.onlySuggestions = false;
+ }
+ }
+ }
+
+ function formatValue(value, type, options) {
+ if (value == null) {
+ return "";
+ }
+ switch (type) {
+ case RuleItem.Boolean:
+ return value ? i18n("Yes") : i18n("No");
+ case RuleItem.Percentage:
+ return i18n("%1 %", value);
+ case RuleItem.Option:
+ return options.textOfValue(value);
+ case RuleItem.FlagsOption:
+ var selectedValue = value.toString(2).length - 1;
+ return options.textOfValue(selectedValue);
+ }
+ return value;
+ }
+
+ KSortFilterProxyModel {
+ id: enabledRulesModel
+ sourceModel: rulesModel
+ filterRowCallback: (source_row, source_parent) => {
+ var index = sourceModel.index(source_row, 0, source_parent);
+ return sourceModel.data(index, RulesModel.EnabledRole);
+ }
+ }
+
+ KSortFilterProxyModel {
+ id: overlayModel
+ sourceModel: rulesModel
+
+ property bool onlySuggestions: false
+ onOnlySuggestionsChanged: {
+ invalidateFilter();
+ }
+
+ filterString: searchField.text.trim().toLowerCase()
+ filterRowCallback: (source_row, source_parent) => {
+ var index = sourceModel.index(source_row, 0, source_parent);
+
+ var hasSuggestion = sourceModel.data(index, RulesModel.SuggestedValueRole) != null;
+ var isOptional = sourceModel.data(index, RulesModel.SelectableRole);
+ var isEnabled = sourceModel.data(index, RulesModel.EnabledRole);
+
+ var showItem = hasSuggestion || (!onlySuggestions && isOptional && !isEnabled);
+
+ if (!showItem) {
+ return false;
+ }
+ if (filterString != "") {
+ return sourceModel.data(index, RulesModel.NameRole).toLowerCase().includes(filterString)
+ }
+ return true;
+ }
+ }
+
+}
diff --git a/kcmkwin/kwinrules/package/contents/ui/RulesList.qml b/kcmkwin/kwinrules/package/contents/ui/RulesList.qml
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/package/contents/ui/RulesList.qml
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 .
+ */
+
+import QtQuick 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Controls 2.14 as QQC2
+import QtQml.Models 2.14
+import org.kde.kcm 1.2
+import org.kde.kirigami 2.5 as Kirigami
+
+ScrollViewKCM {
+ id: rulesListKCM
+
+ // FIXME: ScrollViewKCM.qml:73:13: QML Control: Binding loop detected for property "implicitHeight"
+ implicitWidth: Kirigami.Units.gridUnit * 35
+ implicitHeight: Kirigami.Units.gridUnit * 25
+
+ ConfigModule.columnWidth: Kirigami.Units.gridUnit * 23
+ ConfigModule.buttons: ConfigModule.Apply
+
+ property var selectedIndexes: []
+
+ // Manage KCM pages
+ Connections {
+ target: kcm
+ onEditIndexChanged: {
+ if (kcm.editIndex < 0) {
+ // If no rule is being edited, hide RulesEdidor page
+ kcm.pop();
+ } else if (kcm.depth < 2) {
+ // Add the RulesEditor page if it wasn't already
+ kcm.push("RulesEditor.qml");
+ }
+ }
+ }
+
+ view: ListView {
+ id: ruleBookView
+ clip: true
+
+ model: kcm.ruleBookModel
+ currentIndex: kcm.editIndex
+ delegate: Kirigami.DelegateRecycler {
+ width: ruleBookView.width
+ sourceComponent: ruleBookDelegate
+ }
+
+ displaced: Transition {
+ NumberAnimation { properties: "y"; duration: Kirigami.Units.longDuration }
+ }
+
+ Kirigami.Heading {
+ level: 3
+ enabled: false
+ visible: ruleBookView.count == 0
+ anchors.fill: parent
+ horizontalAlignment: Qt.AlignHCenter
+ verticalAlignment: Qt.AlignVCenter
+ wrapMode: Text.WordWrap
+ text: i18n("No rules for specific windows are currently set");
+ }
+ }
+
+ header: Kirigami.InlineMessage {
+ id: exportInfo
+ icon.source: "document-export"
+ showCloseButton: true
+ text: i18n("Select the rules to export")
+ actions: [
+ Kirigami.Action {
+ iconName: "document-save"
+ text: i18n("Save Rules")
+ // FIXME: It does not update on selection changes
+ // enabled: selectedIndexes.length > 0
+ onTriggered: {
+ exportDialog.active = true;
+ }
+ }
+ ]
+ }
+
+ footer: Kirigami.ActionToolBar {
+ Layout.fillWidth: true
+ alignment: Qt.AlignRight
+ flat: false
+
+ actions: [
+ Kirigami.Action {
+ text: i18n("Import...")
+ iconName: "document-import"
+ enabled: !exportInfo.visible
+ onTriggered: {
+ importDialog.active = true;
+ }
+ }
+ ,
+ Kirigami.Action {
+ text: i18n("Export...")
+ iconName: "document-export"
+ enabled: ruleBookView.count > 0
+ checkable: true
+ checked: exportInfo.visible
+ onToggled: {
+ selectedIndexes = []
+ exportInfo.visible = checked;
+ }
+ }
+ ,
+ Kirigami.Action {
+ text: i18n("Add New...")
+ iconName: "list-add-symbolic"
+ enabled: !exportInfo.visible
+ onTriggered: {
+ kcm.createRule();
+ }
+ }
+ ]
+ }
+
+ Component {
+ id: ruleBookDelegate
+ Kirigami.AbstractListItem {
+ id: ruleBookItem
+
+ RowLayout {
+ //FIXME: If not used within DelegateRecycler, item goes on top of the first item when clicked
+ Kirigami.ListItemDragHandle {
+ visible: !exportInfo.visible
+ listItem: ruleBookItem
+ listView: ruleBookView
+ onMoveRequested: {
+ kcm.moveRule(oldIndex, newIndex);
+ }
+ }
+
+ QQC2.TextField {
+ id: descriptionField
+ Layout.minimumWidth: Kirigami.Units.gridUnit * 2
+ Layout.fillWidth: true
+ background: Item {}
+ horizontalAlignment: Text.AlignLeft
+ text: display
+ onEditingFinished: {
+ kcm.setRuleDescription(index, text);
+ }
+ Keys.onPressed: {
+ switch (event.key) {
+ case Qt.Key_Escape:
+ // On key reset to model data before losing focus
+ text = display;
+ case Qt.Key_Enter:
+ case Qt.Key_Return:
+ ruleBookItem.focus = true;
+ event.accepted = true;
+ break;
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ enabled: exportInfo.visible
+ cursorShape: enabled ? Qt.PointingHandCursor : Qt.IBeamCursor
+ onClicked: {
+ itemSelectionCheck.toggle();
+ itemSelectionCheck.toggled();
+ }
+ }
+ }
+
+ Kirigami.ActionToolBar {
+ Layout.preferredWidth: maximumContentWidth + Kirigami.Units.smallSpacing
+ alignment: Qt.AlignRight
+ display: QQC2.Button.IconOnly
+ visible: !exportInfo.visible
+ opacity: ruleBookItem.hovered ? 1 : 0
+ focus: false
+
+ actions: [
+ Kirigami.Action {
+ text: i18n("Edit")
+ iconName: "edit-entry"
+ onTriggered: {
+ kcm.editRule(index);
+ }
+ }
+ ,
+ Kirigami.Action {
+ text: i18n("Delete")
+ iconName: "entry-delete"
+ onTriggered: {
+ kcm.removeRule(index);
+ }
+ }
+ ]
+ }
+
+ QQC2.CheckBox {
+ id: itemSelectionCheck
+ visible: exportInfo.visible
+ checked: selectedIndexes.includes(index)
+ onToggled: {
+ var position = selectedIndexes.indexOf(index);
+ if (checked) {
+ if (position < 0) { selectedIndexes.push(index); }
+ } else {
+ if (position >= 0) { selectedIndexes.splice(position, 1); }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ FileDialogLoader {
+ id: importDialog
+ title: i18n("Import Rules")
+ isSaveDialog: false
+ onLastFolderChanged: {
+ exportDialog.lastFolder = lastFolder;
+ }
+ onFileSelected: {
+ kcm.importFromFile(path);
+ }
+ }
+
+ FileDialogLoader {
+ id: exportDialog
+ title: i18n("Export Rules")
+ isSaveDialog: true
+ onLastFolderChanged: {
+ importDialog.lastFolder = lastFolder;
+ }
+ onFileSelected: {
+ selectedIndexes.sort();
+ kcm.exportToFile(path, selectedIndexes);
+ exportInfo.visible = false;
+ }
+ }
+}
diff --git a/kcmkwin/kwinrules/package/contents/ui/ValueEditor.qml b/kcmkwin/kwinrules/package/contents/ui/ValueEditor.qml
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/package/contents/ui/ValueEditor.qml
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 .
+ */
+
+import QtQuick 2.14
+import QtQuick.Layouts 1.14
+import QtQuick.Controls 2.14 as QQC2
+
+import org.kde.kirigami 2.10 as Kirigami
+import org.kde.kquickcontrols 2.0 as KQC
+import org.kde.kcms.kwinrules 1.0
+
+
+Loader {
+ id: valueEditor
+ focus: true
+
+ property var ruleValue
+ property var ruleOptions
+ property int controlType
+
+ signal valueEdited(var value)
+
+ sourceComponent: {
+ switch (controlType) {
+ case RuleItem.Boolean: return booleanEditor
+ case RuleItem.String: return stringEditor
+ case RuleItem.Integer: return integerEditor
+ case RuleItem.Option: return optionEditor
+ case RuleItem.FlagsOption: return flagsEditor
+ case RuleItem.Percentage: return percentageEditor
+ case RuleItem.Coordinate: return coordinateEditor
+ case RuleItem.Shortcut: return shortcutEditor
+ default: return emptyEditor
+ }
+ }
+
+ Component {
+ id: emptyEditor
+ Item {}
+ }
+
+ Component {
+ id: booleanEditor
+ RowLayout {
+ Item {
+ Layout.fillWidth: true
+ }
+ QQC2.RadioButton {
+ text: i18n("Yes")
+ checked: ruleValue
+ onToggled: valueEditor.valueEdited(checked)
+ }
+ QQC2.RadioButton {
+ text: i18n("No")
+ checked: !ruleValue
+ onToggled: valueEditor.valueEdited(!checked)
+ }
+ }
+ }
+
+ Component {
+ id: stringEditor
+ QQC2.TextField {
+ property bool isTextEdited: false
+ text: ruleValue
+ horizontalAlignment: Text.AlignLeft
+ onTextEdited: { isTextEdited = true; }
+ onEditingFinished: {
+ if (isTextEdited) { valueEditor.valueEdited(text); }
+ isTextEdited = false;
+ }
+ }
+ }
+
+ Component {
+ id: integerEditor
+ QQC2.SpinBox {
+ editable: true
+ value: ruleValue
+ onValueModified: valueEditor.valueEdited(value)
+ }
+ }
+
+ Component {
+ id: optionEditor
+ OptionsComboBox {
+ flat: true
+ model: ruleOptions
+ onActivated: (index) => {
+ valueEditor.valueEdited(currentValue);
+ }
+ }
+ }
+
+ Component {
+ id: flagsEditor
+ OptionsComboBox {
+ flat: true
+ model: ruleOptions
+ multipleChoice: true
+ selectionMask: ruleValue
+ onActivated: {
+ valueEditor.valueEdited(selectionMask);
+ }
+ }
+ }
+
+ Component {
+ id: percentageEditor
+ RowLayout {
+ QQC2.Slider {
+ id: slider
+ Layout.fillWidth: true
+ from: 0
+ to: 100
+ value: ruleValue
+ onMoved: valueEditor.valueEdited(Math.round(slider.value))
+ }
+ QQC2.Label {
+ text: i18n("%1 %", Math.round(slider.value))
+ horizontalAlignment: Qt.AlignRight
+ Layout.minimumWidth: maxPercentage.width + Kirigami.Units.smallSpacing
+ }
+ TextMetrics {
+ id: maxPercentage
+ text: i18n("%1 %", 100)
+ }
+ }
+ }
+
+ Component {
+ id: coordinateEditor
+ RowLayout {
+ id: coordItem
+ spacing: Kirigami.Units.smallSpacing
+
+ property var coords: ruleValue ? ruleValue.split(',') : [0, 0]
+ property int coordWidth: (coordItem.width - coordSeparator.width) / 2 - coordItem.spacing
+
+ QQC2.SpinBox {
+ id: coordX
+ editable: true
+ implicitWidth: coordWidth
+ from: 0
+ to: 4098
+ value: coords[0]
+ onValueModified: valueEditor.valueEdited(coordX.value + "," + coordY.value)
+ }
+ QQC2.Label {
+ id: coordSeparator
+ text: i18nc("(x, y) coordinates separator in size/position","x")
+ horizontalAlignment: Text.AlignHCenter
+ }
+ QQC2.SpinBox {
+ id: coordY
+ editable: true
+ from: 0
+ to: 4098
+ implicitWidth: coordWidth
+ value: coords[1]
+ onValueModified: valueEditor.valueEdited(coordX.value + "," + coordY.value)
+ }
+ }
+ }
+
+ Component {
+ id: shortcutEditor
+ RowLayout {
+ Item {
+ Layout.fillWidth: true
+ }
+ KQC.KeySequenceItem {
+ keySequence: ruleValue
+ onCaptureFinished: valueEditor.valueEdited(keySequence)
+ }
+ }
+ }
+}
diff --git a/kcmkwin/kwinrules/package/metadata.desktop b/kcmkwin/kwinrules/package/metadata.desktop
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/package/metadata.desktop
@@ -0,0 +1,19 @@
+[Desktop Entry]
+Icon=preferences-system-windows-actions
+Type=Service
+Keywords=
+X-KDE-ParentApp=
+X-KDE-System-Settings-Parent-Category=applicationstyle
+X-KDE-PluginInfo-Author=Ismael Asensio
+X-KDE-PluginInfo-Email=isma.af@gmail.com
+X-KDE-PluginInfo-License=GPL-2.0+
+X-KDE-PluginInfo-Name=kcm_kwinrules
+X-KDE-PluginInfo-Version=
+X-KDE-PluginInfo-Website=https://www.kde.org/plasma-desktop
+X-KDE-ServiceTypes=Plasma/Generic
+X-Plasma-API=declarativeappletscript
+X-Plasma-MainScript=ui/RulesList.qml
+X-KDE-FormFactors=desktop,tablet
+
+Name=Window Rules
+Comment=Individual Window Behavior
diff --git a/kcmkwin/kwinrules/rulebookmodel.h b/kcmkwin/kwinrules/rulebookmodel.h
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/rulebookmodel.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "rulebooksettings.h"
+#include
+
+#include
+
+
+namespace KWin
+{
+
+class RuleBookModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ explicit RuleBookModel(QObject *parent = nullptr);
+ ~RuleBookModel();
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role) override;
+
+ bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
+ bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
+ const QModelIndex &destinationParent, int destinationChild) override;
+
+ QString descriptionAt(int row) const;
+ void setDescriptionAt(int row, const QString &description);
+
+ Rules *ruleAt(int row) const;
+ void setRuleAt(int row, Rules *rule);
+
+ void load();
+ void save();
+
+private:
+ RuleBookSettings *m_ruleBook;
+ QVector m_rules;
+};
+
+} // namespace
diff --git a/kcmkwin/kwinrules/rulebookmodel.cpp b/kcmkwin/kwinrules/rulebookmodel.cpp
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/rulebookmodel.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 "rulebookmodel.h"
+
+
+namespace KWin
+{
+
+RuleBookModel::RuleBookModel(QObject *parent)
+ : QAbstractListModel(parent)
+ , m_ruleBook(new RuleBookSettings(this))
+{
+}
+
+RuleBookModel::~RuleBookModel()
+{
+ qDeleteAll(m_rules);
+}
+
+int RuleBookModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return m_rules.count();
+}
+
+QVariant RuleBookModel::data(const QModelIndex &index, int role) const
+{
+ if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) {
+ return QVariant();
+ }
+
+ switch (role) {
+ case Qt::DisplayRole:
+ return descriptionAt(index.row());
+ default:
+ return QVariant();
+ }
+}
+
+bool RuleBookModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) {
+ return false;
+ }
+
+ switch (role) {
+ case Qt::DisplayRole:
+ setDescriptionAt(index.row(), value.toString());
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool RuleBookModel::insertRows(int row, int count, const QModelIndex &parent)
+{
+ if (row < 0 || row > rowCount() || parent.isValid()) {
+ return false;
+ }
+ beginInsertRows(parent, row, row + count - 1);
+
+ for (int i = 0; i < count; i++) {
+ Rules *newRule = new Rules();
+ // HACK: Improve integration with RuleSettings and use directly its defaults
+ newRule->wmclassmatch = Rules::ExactMatch;
+ m_rules.insert(row + i, newRule);
+ }
+
+ m_ruleBook->setCount(m_rules.count());
+
+ endInsertRows();
+ return true;
+}
+
+bool RuleBookModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (row < 0 || row > rowCount() || parent.isValid()) {
+ return false;
+ }
+ beginRemoveRows(parent, row, row + count - 1);
+
+ for (int i = 0; i < count; i++) {
+ delete m_rules.at(row + i);
+ }
+ m_rules.remove(row, count);
+ m_ruleBook->setCount(m_rules.count());
+
+ endRemoveRows();
+ return true;
+}
+
+bool RuleBookModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
+ const QModelIndex &destinationParent, int destinationChild)
+{
+ if (sourceParent != destinationParent || sourceParent != QModelIndex()){
+ return false;
+ }
+
+ const bool isMoveDown = destinationChild > sourceRow;
+ // QAbstractItemModel::beginMoveRows(): when moving rows down in the same parent,
+ // the rows will be placed before the destinationChild index.
+ if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1,
+ destinationParent, isMoveDown ? destinationChild + 1 : destinationChild)) {
+ return false;
+ }
+
+ for (int i = 0; i < count; i++) {
+ m_rules.insert(destinationChild + i,
+ m_rules.takeAt(isMoveDown ? sourceRow : sourceRow + i));
+ }
+
+ endMoveRows();
+ return true;
+}
+
+
+QString RuleBookModel::descriptionAt(int row) const
+{
+ Q_ASSERT (row >= 0 && row < m_rules.count());
+ return m_rules.at(row)->description;
+}
+
+Rules *RuleBookModel::ruleAt(int row) const
+{
+ Q_ASSERT (row >= 0 && row < m_rules.count());
+ return m_rules.at(row);
+}
+
+void RuleBookModel::setDescriptionAt(int row, const QString &description)
+{
+ Q_ASSERT (row >= 0 && row < m_rules.count());
+ if (description == m_rules.at(row)->description) {
+ return;
+ }
+
+ m_rules.at(row)->description = description;
+
+ emit dataChanged(index(row), index(row), QVector{Qt::DisplayRole});
+}
+
+void RuleBookModel::setRuleAt(int row, Rules *rule)
+{
+ Q_ASSERT (row >= 0 && row < m_rules.count());
+
+ delete m_rules.at(row);
+ m_rules[row] = rule;
+
+ emit dataChanged(index(row), index(row), QVector{Qt::DisplayRole});
+}
+
+
+void RuleBookModel::load()
+{
+ beginResetModel();
+
+ m_ruleBook->load();
+ qDeleteAll(m_rules);
+ m_rules = m_ruleBook->rules();
+
+ endResetModel();
+}
+
+void RuleBookModel::save()
+{
+ m_ruleBook->setRules(m_rules);
+ m_ruleBook->save();
+}
+
+} // namespace
diff --git a/kcmkwin/kwinrules/ruleitem.h b/kcmkwin/kwinrules/ruleitem.h
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/ruleitem.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 KWIN_RULEITEM_H
+#define KWIN_RULEITEM_H
+
+#include "optionsmodel.h"
+
+#include
+#include
+
+
+namespace KWin
+{
+
+class RuleItem : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Type {
+ Undefined,
+ Boolean,
+ String,
+ Integer,
+ Option,
+ FlagsOption,
+ Percentage,
+ Coordinate,
+ Shortcut
+ };
+ Q_ENUM(Type)
+
+ enum Flags {
+ NoFlags = 0,
+ AlwaysEnabled = 1u << 0,
+ StartEnabled = 1u << 1,
+ AffectsWarning = 1u << 2,
+ AffectsDescription = 1u << 3,
+ AllFlags = 0b1111
+ };
+
+public:
+ RuleItem() {};
+ RuleItem(const QString &key,
+ const RulePolicy::Type policyType,
+ const Type type,
+ const QString &name,
+ const QString §ion,
+ const QIcon &icon = QIcon::fromTheme("window"),
+ const QString &description = QString("")
+ );
+ ~RuleItem();
+
+ QString key() const;
+ QString name() const;
+ QString section() const;
+ QIcon icon() const;
+ QString iconName() const;
+ QString description() const;
+
+ bool isEnabled() const;
+ void setEnabled(bool enabled);
+
+ bool hasFlag(RuleItem::Flags flag) const;
+ void setFlag(RuleItem::Flags flag, bool active=true);
+
+ Type type() const;
+ QVariant value() const;
+ void setValue(QVariant value);
+ QVariant suggestedValue() const;
+ void setSuggestedValue(QVariant value, bool forceValue = false);
+
+ QVariant options() const;
+ void setOptionsData(const QList &data);
+
+ RulePolicy::Type policyType() const;
+ int policy() const; // int belongs to anonymous enum in Rules::
+ void setPolicy(int policy); // int belongs to anonymous enum in Rules::
+ QVariant policyModel() const;
+ QString policyKey() const;
+
+
+
+ void reset();
+
+private:
+ static QVariant typedValue(const QVariant &value, const Type type);
+
+private:
+ QString m_key;
+ RuleItem::Type m_type;
+ QString m_name;
+ QString m_section;
+ QIcon m_icon;
+ QString m_description;
+ QFlags m_flags;
+
+ bool m_enabled;
+
+ QVariant m_value;
+ QVariant m_suggestedValue;
+
+ RulePolicy *m_policy;
+ OptionsModel *m_options;
+};
+
+} //namespace
+
+#endif //KWIN_RULEITEM_H
diff --git a/kcmkwin/kwinrules/ruleitem.cpp b/kcmkwin/kwinrules/ruleitem.cpp
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/ruleitem.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 "ruleitem.h"
+
+
+namespace KWin
+{
+
+RuleItem::RuleItem(const QString &key,
+ const RulePolicy::Type policyType,
+ const RuleItem::Type type,
+ const QString &name,
+ const QString §ion,
+ const QIcon &icon,
+ const QString &description)
+ : m_key(key)
+ , m_type(type)
+ , m_name(name)
+ , m_section(section)
+ , m_icon(icon)
+ , m_description(description)
+ , m_flags(NoFlags)
+ , m_enabled(false)
+ , m_policy(new RulePolicy(policyType))
+ , m_options(nullptr)
+{
+ reset();
+}
+
+RuleItem::~RuleItem()
+{
+ delete m_policy;
+ delete m_options;
+}
+
+void RuleItem::reset()
+{
+ m_enabled = hasFlag(AlwaysEnabled) | hasFlag(StartEnabled);
+ m_value = typedValue(QVariant(), m_type);
+ m_suggestedValue = QVariant();
+ m_policy->resetValue();
+ if (m_options) {
+ m_options->resetValue();
+ }
+}
+
+QString RuleItem::key() const
+{
+ return m_key;
+}
+
+QString RuleItem::name() const
+{
+ return m_name;
+}
+
+QString RuleItem::section() const
+{
+ return m_section;
+}
+
+QString RuleItem::iconName() const
+{
+ return m_icon.name();
+}
+
+QIcon RuleItem::icon() const
+{
+ return m_icon;
+}
+
+QString RuleItem::description() const
+{
+ return m_description;
+}
+
+bool RuleItem::isEnabled() const
+{
+ return m_enabled;
+}
+
+void RuleItem::setEnabled(bool enabled)
+{
+ m_enabled = enabled | hasFlag(AlwaysEnabled);
+}
+
+bool RuleItem::hasFlag(RuleItem::Flags flag) const
+{
+ return m_flags.testFlag(flag);
+}
+
+void RuleItem::setFlag(RuleItem::Flags flag, bool active)
+{
+ m_flags.setFlag(flag, active);
+}
+
+RuleItem::Type RuleItem::type() const
+{
+ return m_type;
+}
+
+QVariant RuleItem::value() const
+{
+ if (m_type == Option) {
+ return m_options->value();
+ }
+ return m_value;
+}
+
+void RuleItem::setValue(QVariant value)
+{
+ if (m_type == Option) {
+ m_options->setValue(value);
+ }
+ m_value = typedValue(value, m_type);
+}
+
+QVariant RuleItem::suggestedValue() const
+{
+ return m_suggestedValue;
+}
+
+void RuleItem::setSuggestedValue(QVariant value, bool forceValue)
+{
+ if (forceValue) {
+ setValue(value);
+ }
+ m_suggestedValue = typedValue(value, m_type);
+}
+
+QVariant RuleItem::options() const
+{
+ if (!m_options) {
+ return QVariant();
+ }
+ return QVariant::fromValue(m_options);
+}
+
+void RuleItem::setOptionsData(const QList &data)
+{
+ if (!m_options) {
+ if (m_type != Option && m_type != FlagsOption) {
+ return;
+ }
+ m_options = new OptionsModel();
+ }
+ m_options->updateModelData(data);
+ m_options->setValue(m_value);
+}
+
+int RuleItem::policy() const
+{
+ return m_policy->value();
+}
+
+void RuleItem::setPolicy(int policy)
+{
+ m_policy->setValue(policy);
+}
+
+RulePolicy::Type RuleItem::policyType() const
+{
+ return m_policy->type();
+}
+
+QVariant RuleItem::policyModel() const
+{
+ return QVariant::fromValue(m_policy);
+}
+
+QString RuleItem::policyKey() const
+{
+ return m_policy->policyKey(m_key);
+}
+
+QVariant RuleItem::typedValue(const QVariant &value, const RuleItem::Type type)
+{
+ if (value.isNull()) {
+ return QVariant();
+ }
+ switch (type) {
+ case Undefined:
+ case Option:
+ return value;
+ case Boolean:
+ return value.toBool();
+ case Integer:
+ case Percentage:
+ return value.toInt();
+ case FlagsOption:
+ // HACK: Currently, the only user of this is "types" property
+ if (value.toInt() == -1) { //NET:AllTypesMask
+ return 0x3FF - 0x040; //All possible flags minus NET::Override (deprecated)
+ }
+ return value.toInt();
+ case Coordinate:
+ if (value.toString().isEmpty()) {
+ return QStringLiteral("0,0");
+ }
+ return value.toString();
+ case String:
+ case Shortcut:
+ return value.toString();
+ }
+ return value;
+}
+
+} //namespace
+
diff --git a/kcmkwin/kwinrules/kcm.h b/kcmkwin/kwinrules/rulesdialog.h
rename from kcmkwin/kwinrules/kcm.h
rename to kcmkwin/kwinrules/rulesdialog.h
--- a/kcmkwin/kwinrules/kcm.h
+++ b/kcmkwin/kwinrules/rulesdialog.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2004 Lubos Lunak
+ * Copyright (c) 2020 Ismael Asensio
*
* 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
@@ -16,35 +17,37 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#ifndef KWIN_RULESDIALOG_H
+#define KWIN_RULESDIALOG_H
-#ifndef __KCM_H__
-#define __KCM_H__
+#include "rulesmodel.h"
+#include "../../rules.h"
-#include
-#include
-
-class KConfig;
+#include
namespace KWin
{
-class KCMRulesList;
+class Rules;
-class KCMRules
- : public KCModule
+class RulesDialog : public QDialog
{
Q_OBJECT
+
public:
- KCMRules(QWidget *parent, const QVariantList &args);
- void load() override;
- void save() override;
- QString quickHelp() const override;
-protected Q_SLOTS:
- void moduleChanged(bool state);
+ explicit RulesDialog(QWidget* parent = nullptr, const char* name = nullptr);
+
+ Rules* edit(Rules* r, const QVariantMap& info, bool show_hints);
+
+protected:
+ void accept() override;
+
private:
- KCMRulesList* widget;
+ RulesModel* m_rulesModel;
+ QWidget *m_quickWidget;
+ Rules* m_rules;
};
} // namespace
-#endif
+#endif // KWIN_RULESDIALOG_H
diff --git a/kcmkwin/kwinrules/rulesdialog.cpp b/kcmkwin/kwinrules/rulesdialog.cpp
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/rulesdialog.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2004 Lubos Lunak
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "rulesdialog.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+
+namespace KWin
+{
+
+RulesDialog::RulesDialog(QWidget* parent, const char* name)
+ : QDialog(parent)
+ , m_rulesModel(new RulesModel(this))
+{
+ setObjectName(name);
+ setModal(true);
+ setWindowTitle(i18n("Edit Window-Specific Settings"));
+ setWindowIcon(QIcon::fromTheme("preferences-system-windows-actions"));
+ setLayout(new QVBoxLayout);
+
+ // Init RuleEditor QML QuickView
+ QQuickView *quickView = new QQuickView();
+ quickView->setSource(QUrl::fromLocalFile(QStandardPaths::locate(
+ QStandardPaths::GenericDataLocation,
+ QStringLiteral("kpackage/kcms/kcm_kwinrules/contents/ui/RulesEditor.qml"))));
+ quickView->setResizeMode(QQuickView::SizeRootObjectToView);
+ quickView->rootObject()->setProperty("rulesModel", QVariant::fromValue(m_rulesModel));
+
+ m_quickWidget = QWidget::createWindowContainer(quickView, this);
+ m_quickWidget->setMinimumSize(QSize(650, 575));
+ layout()->addWidget(m_quickWidget);
+
+ QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
+ connect(buttons, SIGNAL(accepted()), SLOT(accept()));
+ connect(buttons, SIGNAL(rejected()), SLOT(reject()));
+ layout()->addWidget(buttons);
+}
+
+// window is set only for Alt+F3/Window-specific settings, because the dialog
+// is then related to one specific window
+Rules* RulesDialog::edit(Rules* r, const QVariantMap& info, bool show_hints)
+{
+ Q_UNUSED(show_hints);
+
+ m_rules = r;
+
+ m_rulesModel->importFromRules(m_rules);
+ if (!info.isEmpty()) {
+ m_rulesModel->setWindowProperties(info, true);
+ }
+
+ exec();
+
+ return m_rules;
+}
+
+void RulesDialog::accept()
+{
+ m_rules = m_rulesModel->exportToRules();
+ QDialog::accept();
+}
+
+}
diff --git a/kcmkwin/kwinrules/ruleslist.h b/kcmkwin/kwinrules/ruleslist.h
deleted file mode 100644
--- a/kcmkwin/kwinrules/ruleslist.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2004 Lubos Lunak
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-
-#ifndef __RULESLIST_H__
-#define __RULESLIST_H__
-
-#include "ui_ruleslist.h"
-#include "rulebooksettings.h"
-
-namespace KWin
-{
-class Rules;
-
-class KCMRulesList
- : public QWidget, Ui_KCMRulesList
-{
- Q_OBJECT
-public:
- explicit KCMRulesList(QWidget* parent = nullptr);
- ~KCMRulesList() override;
- void load();
- void save();
-Q_SIGNALS:
- void changed(bool);
-private Q_SLOTS:
- void newClicked();
- void modifyClicked();
- void deleteClicked();
- void moveupClicked();
- void movedownClicked();
- void exportClicked();
- void importClicked();
- void activeChanged();
-private:
- QVector m_rules;
- RuleBookSettings m_settings;
-};
-
-} // namespace
-
-#endif
diff --git a/kcmkwin/kwinrules/ruleslist.cpp b/kcmkwin/kwinrules/ruleslist.cpp
deleted file mode 100644
--- a/kcmkwin/kwinrules/ruleslist.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (c) 2004 Lubos Lunak
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "ruleslist.h"
-
-#include
-#include
-#include
-
-#include "../../rules.h"
-#include "rulesettings.h"
-#include "ruleswidget.h"
-
-namespace KWin
-{
-
-KCMRulesList::KCMRulesList(QWidget* parent)
- : QWidget(parent)
-{
- setupUi(this);
- // connect both current/selected, so that current==selected (stupid QListBox :( )
- connect(rules_listbox, SIGNAL(itemChanged(QListWidgetItem*)),
- SLOT(activeChanged()));
- connect(rules_listbox, SIGNAL(itemSelectionChanged()),
- SLOT(activeChanged()));
- connect(new_button, SIGNAL(clicked()),
- SLOT(newClicked()));
- connect(modify_button, SIGNAL(clicked()),
- SLOT(modifyClicked()));
- connect(delete_button, SIGNAL(clicked()),
- SLOT(deleteClicked()));
- connect(moveup_button, SIGNAL(clicked()),
- SLOT(moveupClicked()));
- connect(movedown_button, SIGNAL(clicked()),
- SLOT(movedownClicked()));
- connect(export_button, SIGNAL(clicked()),
- SLOT(exportClicked()));
- connect(import_button, SIGNAL(clicked()),
- SLOT(importClicked()));
- connect(rules_listbox, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
- SLOT(modifyClicked()));
- load();
-}
-
-KCMRulesList::~KCMRulesList()
-{
- qDeleteAll(m_rules);
- m_rules.clear();
-}
-
-void KCMRulesList::activeChanged()
-{
- QListWidgetItem *item = rules_listbox->currentItem();
- int itemRow = rules_listbox->row(item);
-
- if (item != nullptr) // make current==selected
- rules_listbox->setCurrentItem(item, QItemSelectionModel::ClearAndSelect);
- modify_button->setEnabled(item != nullptr);
- delete_button->setEnabled(item != nullptr);
- export_button->setEnabled(item != nullptr);
- moveup_button->setEnabled(item != nullptr && itemRow > 0);
- movedown_button->setEnabled(item != nullptr && itemRow < (rules_listbox->count() - 1));
-}
-
-void KCMRulesList::newClicked()
-{
- RulesDialog dlg(this);
- Rules* rule = dlg.edit(nullptr, {}, false);
- if (rule == nullptr)
- return;
- int pos = rules_listbox->currentRow() + 1;
- rules_listbox->insertItem(pos , rule->description);
- rules_listbox->setCurrentRow(pos, QItemSelectionModel::ClearAndSelect);
- m_rules.insert(m_rules.begin() + pos, rule);
- emit changed(true);
-}
-
-void KCMRulesList::modifyClicked()
-{
- int pos = rules_listbox->currentRow();
- if (pos == -1)
- return;
- RulesDialog dlg(this);
- Rules *rule = dlg.edit(m_rules[pos], {}, false);
- if (rule == m_rules[pos])
- return;
- delete m_rules[pos];
- m_rules[pos] = rule;
- rules_listbox->item(pos)->setText(rule->description);
- emit changed(true);
-}
-
-void KCMRulesList::deleteClicked()
-{
- int pos = rules_listbox->currentRow();
- Q_ASSERT(pos != -1);
- delete rules_listbox->takeItem(pos);
- m_rules.erase(m_rules.begin() + pos);
- emit changed(true);
-}
-
-void KCMRulesList::moveupClicked()
-{
- int pos = rules_listbox->currentRow();
- Q_ASSERT(pos != -1);
- if (pos > 0) {
- QListWidgetItem * item = rules_listbox->takeItem(pos);
- rules_listbox->insertItem(pos - 1 , item);
- rules_listbox->setCurrentItem(item, QItemSelectionModel::ClearAndSelect);
- Rules *rule = m_rules[pos];
- m_rules[pos] = m_rules[pos - 1];
- m_rules[pos - 1] = rule;
- }
- emit changed(true);
-}
-
-void KCMRulesList::movedownClicked()
-{
- int pos = rules_listbox->currentRow();
- Q_ASSERT(pos != -1);
- if (pos < int(rules_listbox->count()) - 1) {
- QListWidgetItem * item = rules_listbox->takeItem(pos);
- rules_listbox->insertItem(pos + 1 , item);
- rules_listbox->setCurrentItem(item, QItemSelectionModel::ClearAndSelect);
- Rules *rule = m_rules[pos];
- m_rules[pos] = m_rules[pos + 1];
- m_rules[pos + 1] = rule;
- }
- emit changed(true);
-}
-
-void KCMRulesList::exportClicked()
-{
- int pos = rules_listbox->currentRow();
- Q_ASSERT(pos != -1);
- QString path = QFileDialog::getSaveFileName(this, i18n("Export Rules"), QDir::home().absolutePath(),
- i18n("KWin Rules (*.kwinrule)"));
- if (path.isEmpty())
- return;
- const auto cfg = KSharedConfig::openConfig(path, KConfig::SimpleConfig);
- RuleSettings settings(cfg, m_rules[pos]->description);
- settings.setDefaults();
- m_rules[pos]->write(&settings);
- settings.save();
-}
-
-void KCMRulesList::importClicked()
-{
- QString path = QFileDialog::getOpenFileName(this, i18n("Import Rules"), QDir::home().absolutePath(),
- i18n("KWin Rules (*.kwinrule)"));
- if (path.isEmpty())
- return;
- const auto config = KSharedConfig::openConfig(path, KConfig::SimpleConfig);
- QStringList groups = config->groupList();
- if (groups.isEmpty())
- return;
-
- int pos = qMax(0, rules_listbox->currentRow());
- for (const QString &group : groups) {
- RuleSettings settings(config, group);
- const bool remove = settings.deleteRule();
- Rules *new_rule = new Rules(&settings);
-
- // try to replace existing rule first
- for (int i = 0; i < m_rules.count(); ++i) {
- if (m_rules[i]->description == new_rule->description) {
- delete m_rules[i];
- if (remove) {
- m_rules.remove(i);
- delete rules_listbox->takeItem(i);
- delete new_rule;
- pos = qMax(0, rules_listbox->currentRow()); // might have changed!
- } else
- m_rules[i] = new_rule;
- new_rule = nullptr;
- break;
- }
- }
-
- // don't add "to be deleted" if not present
- if (remove) {
- delete new_rule;
- new_rule = nullptr;
- }
-
- // plain insertion
- if (new_rule) {
- m_rules.insert(pos, new_rule);
- rules_listbox->insertItem(pos++, new_rule->description);
- }
- }
- emit changed(true);
-}
-
-void KCMRulesList::load()
-{
- rules_listbox->clear();
- m_settings.load();
- m_rules = m_settings.rules();
- for (const auto rule : qAsConst(m_rules)) {
- rules_listbox->addItem(rule->description);
- }
-
- if (m_rules.count() > 0)
- rules_listbox->setCurrentItem(rules_listbox->item(0));
- else
- rules_listbox->setCurrentItem(nullptr);
- activeChanged();
-}
-
-void KCMRulesList::save()
-{
- m_settings.setRules(m_rules);
- m_settings.save();
-}
-
-} // namespace
-
diff --git a/kcmkwin/kwinrules/ruleslist.ui b/kcmkwin/kwinrules/ruleslist.ui
deleted file mode 100644
--- a/kcmkwin/kwinrules/ruleslist.ui
+++ /dev/null
@@ -1,122 +0,0 @@
-
-
- KWin::KCMRulesList
-
-
-
- 0
- 0
- 600
- 480
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
- -
-
-
- &New...
-
-
-
- -
-
-
- &Modify...
-
-
-
- -
-
-
- Delete
-
-
-
-
-
-
- -
-
-
- Move &Up
-
-
-
- -
-
-
- Move &Down
-
-
-
- -
-
-
- Qt::Vertical
-
-
- QSizePolicy::Expanding
-
-
-
- 20
- 294
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- &Import
-
-
-
- -
-
-
- &Export
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
-
-
-
-
-
diff --git a/kcmkwin/kwinrules/rulesmodel.h b/kcmkwin/kwinrules/rulesmodel.h
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/rulesmodel.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 KWIN_RULES_MODEL_H
+#define KWIN_RULES_MODEL_H
+
+#include "ruleitem.h"
+#include "rulesettings.h"
+#include
+
+#include
+#include
+#include
+
+#ifdef KWIN_BUILD_ACTIVITIES
+#include
+#endif
+
+
+namespace KWin
+{
+
+class RulesModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged)
+ Q_PROPERTY(QString warningMessage READ warningMessage NOTIFY warningMessageChanged)
+
+public:
+ enum RulesRole {
+ NameRole = Qt::DisplayRole,
+ DescriptionRole = Qt::ToolTipRole,
+ IconRole = Qt::DecorationRole,
+ IconNameRole = Qt::UserRole + 1,
+ KeyRole,
+ SectionRole,
+ EnabledRole,
+ SelectableRole,
+ ValueRole,
+ TypeRole,
+ PolicyRole,
+ PolicyModelRole,
+ OptionsModelRole,
+ SuggestedValueRole
+ };
+ Q_ENUM(RulesRole)
+
+public:
+ explicit RulesModel(QObject *parent = nullptr);
+ ~RulesModel();
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QHash roleNames() const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ bool setData(const QModelIndex & index, const QVariant & value, int role) override;
+
+ bool hasRule(const QString &key) const;
+ RuleItem *ruleItem(const QString &key) const;
+
+ void readFromSettings(RuleSettings *settings);
+ void writeToSettings(RuleSettings *settings) const;
+
+ void importFromRules(Rules *rules);
+ Rules *exportToRules() const;
+
+ void setWindowProperties(const QVariantMap &info, bool forceValue = false);
+
+ QString description() const;
+ void setDescription(const QString &description);
+ QString warningMessage() const;
+
+
+public slots:
+ void detectWindowProperties(int secs);
+
+signals:
+ void descriptionChanged();
+ void warningMessageChanged();
+ void suggestionsChanged();
+
+private:
+ void populateRuleList();
+ bool wmclassWarning() const;
+ RuleItem *addRule(RuleItem *rule);
+ QString defaultDescription() const;
+
+ static const QHash x11PropertyHash();
+
+ QList windowTypesModelData() const;
+ QList virtualDesktopsModelData() const;
+ QList activitiesModelData() const;
+ QList placementModelData() const;
+ QList focusModelData() const;
+ QList colorSchemesModelData() const;
+
+private slots:
+ void selectX11Window();
+
+private:
+ QList m_ruleList;
+ QHash m_rules;
+#ifdef KWIN_BUILD_ACTIVITIES
+ KActivities::Consumer *m_activities;
+#endif
+};
+
+}
+
+#endif
diff --git a/kcmkwin/kwinrules/rulesmodel.cpp b/kcmkwin/kwinrules/rulesmodel.cpp
new file mode 100644
--- /dev/null
+++ b/kcmkwin/kwinrules/rulesmodel.cpp
@@ -0,0 +1,822 @@
+/*
+ * Copyright (c) 2004 Lubos Lunak
+ * Copyright (c) 2020 Ismael Asensio
+ *
+ * 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) 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 14 of version 3 of the license.
+ *
+ * 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 "rulesmodel.h"
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+
+namespace KWin
+{
+
+RulesModel::RulesModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+ qmlRegisterUncreatableType("org.kde.kcms.kwinrules", 1, 0, "RuleItem",
+ QStringLiteral("Do not create objects of type RuleItem"));
+ qmlRegisterUncreatableType("org.kde.kcms.kwinrules", 1, 0, "RulesModel",
+ QStringLiteral("Do not create objects of type RulesModel"));
+
+ populateRuleList();
+}
+
+RulesModel::~RulesModel()
+{
+}
+
+QHash< int, QByteArray > RulesModel::roleNames() const
+{
+ return {
+ {KeyRole, QByteArrayLiteral("key")},
+ {NameRole, QByteArrayLiteral("name")},
+ {IconRole, QByteArrayLiteral("icon")},
+ {IconNameRole, QByteArrayLiteral("iconName")},
+ {SectionRole, QByteArrayLiteral("section")},
+ {DescriptionRole, QByteArrayLiteral("description")},
+ {EnabledRole, QByteArrayLiteral("enabled")},
+ {SelectableRole, QByteArrayLiteral("selectable")},
+ {ValueRole, QByteArrayLiteral("value")},
+ {TypeRole, QByteArrayLiteral("type")},
+ {PolicyRole, QByteArrayLiteral("policy")},
+ {PolicyModelRole, QByteArrayLiteral("policyModel")},
+ {OptionsModelRole, QByteArrayLiteral("options")},
+ {SuggestedValueRole, QByteArrayLiteral("suggested")},
+ };
+}
+
+int RulesModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid()) {
+ return 0;
+ }
+ return m_ruleList.size();
+}
+
+QVariant RulesModel::data(const QModelIndex &index, int role) const
+{
+ if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) {
+ return QVariant();
+ }
+
+ const RuleItem *rule = m_ruleList.at(index.row());
+
+ switch (role) {
+ case KeyRole:
+ return rule->key();
+ case NameRole:
+ return rule->name();
+ case IconRole:
+ return rule->icon();
+ case IconNameRole:
+ return rule->iconName();
+ case DescriptionRole:
+ return rule->description();
+ case SectionRole:
+ return rule->section();
+ case EnabledRole:
+ return rule->isEnabled();
+ case SelectableRole:
+ return !rule->hasFlag(RuleItem::AlwaysEnabled);
+ case ValueRole:
+ return rule->value();
+ case TypeRole:
+ return rule->type();
+ case PolicyRole:
+ return rule->policy();
+ case PolicyModelRole:
+ return rule->policyModel();
+ case OptionsModelRole:
+ return rule->options();
+ case SuggestedValueRole:
+ return rule->suggestedValue();
+ }
+ return QVariant();
+}
+
+bool RulesModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) {
+ return false;
+ }
+
+ RuleItem *rule = m_ruleList.at(index.row());
+
+ switch (role) {
+ case EnabledRole:
+ if (value.toBool() == rule->isEnabled()) {
+ return true;
+ }
+ rule->setEnabled(value.toBool());
+ break;
+ case ValueRole:
+ if (value == rule->value()) {
+ return true;
+ }
+ rule->setValue(value);
+ break;
+ case PolicyRole:
+ if (value.toInt() == rule->policy()) {
+ return true;
+ }
+ rule->setPolicy(value.toInt());
+ break;
+ case SuggestedValueRole:
+ if (value == rule->suggestedValue()) {
+ return true;
+ }
+ rule->setSuggestedValue(value);
+ break;
+ default:
+ return false;
+ }
+
+ emit dataChanged(index, index, QVector{role});
+
+ if (rule->hasFlag(RuleItem::AffectsDescription)) {
+ emit descriptionChanged();
+ }
+ if (rule->hasFlag(RuleItem::AffectsWarning)) {
+ emit warningMessageChanged();
+ }
+
+ return true;
+}
+
+RuleItem *RulesModel::addRule(RuleItem *rule)
+{
+ m_ruleList << rule;
+ m_rules.insert(rule->key(), rule);
+
+ return rule;
+}
+
+bool RulesModel::hasRule(const QString& key) const
+{
+ return m_rules.contains(key);
+}
+
+
+RuleItem *RulesModel::ruleItem(const QString& key) const
+{
+ return m_rules.value(key);
+}
+
+QString RulesModel::description() const
+{
+ const QString desc = m_rules["description"]->value().toString();
+ if (!desc.isEmpty()) {
+ return desc;
+ }
+ return defaultDescription();
+}
+
+void RulesModel::setDescription(const QString &description)
+{
+ setData(index(0, 0), description, RulesModel::ValueRole);
+}
+
+QString RulesModel::defaultDescription() const
+{
+ const QString wmclass = m_rules["wmclass"]->value().toString();
+ const QString title = m_rules["title"]->isEnabled() ? m_rules["title"]->value().toString() : QString();
+
+ if (!title.isEmpty()) {
+ return i18n("Window settings for %1", title);
+ }
+ if (!wmclass.isEmpty()) {
+ return i18n("Settings for %1", wmclass);
+ }
+
+ return i18n("New window settings");
+}
+
+QString RulesModel::warningMessage() const
+{
+ if (wmclassWarning()) {
+ return i18n("You have specified the window class as unimportant.\n"
+ "This means the settings will possibly apply to windows from all applications."
+ " If you really want to create a generic setting, it is recommended"
+ " you at least limit the window types to avoid special window types.");
+ }
+
+ return QString();
+}
+
+
+bool RulesModel::wmclassWarning() const
+{
+ const bool no_wmclass = !m_rules["wmclass"]->isEnabled()
+ || m_rules["wmclass"]->policy() == Rules::UnimportantMatch;
+ const bool alltypes = !m_rules["types"]->isEnabled()
+ || (m_rules["types"]->value() == 0)
+ || (m_rules["types"]->value() == NET::AllTypesMask)
+ || ((m_rules["types"]->value().toInt() | (1 << NET::Override)) == 0x3FF);
+
+ return (no_wmclass && alltypes);
+}
+
+
+void RulesModel::readFromSettings(RuleSettings *settings)
+{
+ beginResetModel();
+
+ for (RuleItem *rule : qAsConst(m_ruleList)) {
+ const KConfigSkeletonItem *configItem = settings->findItem(rule->key());
+ const KConfigSkeletonItem *configPolicyItem = settings->findItem(rule->policyKey());
+
+ if (!configItem) {
+ rule->reset();
+ continue;
+ }
+
+ const bool isEnabled = configPolicyItem ? configPolicyItem->property() != Rules::Unused
+ : !configItem->property().toString().isEmpty();
+ rule->setEnabled(isEnabled);
+
+ const QVariant value = configItem->property();
+ rule->setValue(value);
+
+ if (configPolicyItem) {
+ const int policy = configPolicyItem->property().toInt();
+ rule->setPolicy(policy);
+ }
+ }
+
+ endResetModel();
+
+ emit descriptionChanged();
+ emit warningMessageChanged();
+}
+
+void RulesModel::writeToSettings(RuleSettings *settings) const
+{
+ const QString description = m_rules["description"]->value().toString();
+ if (description.isEmpty()) {
+ m_rules["description"]->setValue(defaultDescription());
+ }
+
+ for (const RuleItem *rule : qAsConst(m_ruleList)) {
+ KConfigSkeletonItem *configItem = settings->findItem(rule->key());
+ KConfigSkeletonItem *configPolicyItem = settings->findItem(rule->policyKey());
+
+ Q_ASSERT (configItem);
+
+ if (rule->isEnabled()) {
+ configItem->setProperty(rule->value());
+ if (configPolicyItem) {
+ configPolicyItem->setProperty(rule->policy());
+ }
+ } else {
+ if (configPolicyItem) {
+ configPolicyItem->setProperty(Rules::Unused);
+ } else {
+ // Rules without policy gets deactivated by an empty string
+ configItem->setProperty(QString());
+ }
+ }
+ }
+}
+
+void RulesModel::importFromRules(Rules* rules)
+{
+ QTemporaryFile tempFile;
+ if (!tempFile.open()) {
+ return;
+ }
+ const auto cfg = KSharedConfig::openConfig(tempFile.fileName(), KConfig::SimpleConfig);
+ RuleSettings *settings = new RuleSettings(cfg, QStringLiteral("tempSettings"));
+
+ settings->setDefaults();
+ if (rules) {
+ rules->write(settings);
+ }
+ readFromSettings(settings);
+
+ delete(settings);
+}
+
+Rules *RulesModel::exportToRules() const
+{
+ QTemporaryFile tempFile;
+ if (!tempFile.open()) {
+ return nullptr;
+ }
+ const auto cfg = KSharedConfig::openConfig(tempFile.fileName(), KConfig::SimpleConfig);
+
+ RuleSettings *settings = new RuleSettings(cfg, QStringLiteral("tempSettings"));
+
+ writeToSettings(settings);
+ Rules *rules = new Rules(settings);
+
+ delete(settings);
+ return rules;
+}
+
+
+void RulesModel::populateRuleList()
+{
+ qDeleteAll(m_ruleList);
+ m_ruleList.clear();
+
+ //Rule description
+ auto description = addRule(new RuleItem(QLatin1String("description"),
+ RulePolicy::NoPolicy, RuleItem::String,
+ i18n("Description"), i18n("Window matching"),
+ QIcon::fromTheme("entry-edit")));
+ description->setFlag(RuleItem::AlwaysEnabled);
+ description->setFlag(RuleItem::AffectsDescription);
+
+ // Window matching
+ auto wmclass = addRule(new RuleItem(QLatin1String("wmclass"),
+ RulePolicy::StringMatch, RuleItem::String,
+ i18n("Window class (application)"), i18n("Window matching"),
+ QIcon::fromTheme("window")));
+ wmclass->setFlag(RuleItem::AlwaysEnabled);
+ wmclass->setFlag(RuleItem::AffectsDescription);
+ wmclass->setFlag(RuleItem::AffectsWarning);
+
+ auto wmclasscomplete = addRule(new RuleItem(QLatin1String("wmclasscomplete"),
+ RulePolicy::NoPolicy, RuleItem::Boolean,
+ i18n("Match whole window class"), i18n("Window matching"),
+ QIcon::fromTheme("window")));
+ wmclasscomplete->setFlag(RuleItem::AlwaysEnabled);
+
+ auto types = addRule(new RuleItem(QLatin1String("types"),
+ RulePolicy::NoPolicy, RuleItem::FlagsOption,
+ i18n("Window types"), i18n("Window matching"),
+ QIcon::fromTheme("window-duplicate")));
+ types->setOptionsData(windowTypesModelData());
+ types->setFlag(RuleItem::AlwaysEnabled);
+ types->setFlag(RuleItem::AffectsWarning);
+
+ addRule(new RuleItem(QLatin1String("windowrole"),
+ RulePolicy::NoPolicy, RuleItem::String,
+ i18n("Window role"), i18n("Window matching"),
+ QIcon::fromTheme("dialog-object-properties")));
+
+ auto title = addRule(new RuleItem(QLatin1String("title"),
+ RulePolicy::StringMatch, RuleItem::String,
+ i18n("Window title"), i18n("Window matching"),
+ QIcon::fromTheme("edit-comment")));
+ title->setFlag(RuleItem::AffectsDescription);
+
+ addRule(new RuleItem(QLatin1String("clientmachine"),
+ RulePolicy::StringMatch, RuleItem::String,
+ i18n("Machine (hostname)"), i18n("Window matching"),
+ QIcon::fromTheme("computer")));
+
+ // Size & Position
+ addRule(new RuleItem(QLatin1String("position"),
+ RulePolicy::SetRule, RuleItem::Coordinate,
+ i18n("Position"), i18n("Size & Position"),
+ QIcon::fromTheme("transform-move")));
+
+ addRule(new RuleItem(QLatin1String("size"),
+ RulePolicy::SetRule, RuleItem::Coordinate,
+ i18n("Size"), i18n("Size & Position"),
+ QIcon::fromTheme("image-resize-symbolic")));
+
+ addRule(new RuleItem(QLatin1String("maximizehoriz"),
+ RulePolicy::SetRule, RuleItem::Boolean,
+ i18n("Maximized horizontally"), i18n("Size & Position"),
+ QIcon::fromTheme("resizecol")));
+
+ addRule(new RuleItem(QLatin1String("maximizevert"),
+ RulePolicy::SetRule, RuleItem::Boolean,
+ i18n("Maximized vertically"), i18n("Size & Position"),
+ QIcon::fromTheme("resizerow")));
+
+ auto desktop = addRule(new RuleItem(QLatin1String("desktop"),
+ RulePolicy::SetRule, RuleItem::Option,
+ i18n("Virtual Desktop"), i18n("Size & Position"),
+ QIcon::fromTheme("virtual-desktops")));
+ desktop->setOptionsData(virtualDesktopsModelData());
+
+#ifdef KWIN_BUILD_ACTIVITIES
+ m_activities = new KActivities::Consumer(this);
+
+ auto activity = addRule(new RuleItem(QLatin1String("activity"),
+ RulePolicy::SetRule, RuleItem::Option,
+ i18n("Activity"), i18n("Size & Position"),
+ QIcon::fromTheme("activities")));
+ activity->setOptionsData(activitiesModelData());
+
+ // Activites consumer may update the available activities later
+ connect(m_activities, &KActivities::Consumer::activitiesChanged,
+ this, [this] { m_rules["activity"]->setOptionsData(activitiesModelData()); });
+ connect(m_activities, &KActivities::Consumer::serviceStatusChanged,
+ this, [this] { m_rules["activity"]->setOptionsData(activitiesModelData()); });
+
+#endif
+
+ addRule(new RuleItem(QLatin1String("screen"),
+ RulePolicy::SetRule, RuleItem::Integer,
+ i18n("Screen"), i18n("Size & Position"),
+ QIcon::fromTheme("osd-shutd-screen")));
+
+ addRule(new RuleItem(QLatin1String("fullscreen"),
+ RulePolicy::SetRule, RuleItem::Boolean,
+ i18n("Fullscreen"), i18n("Size & Position"),
+ QIcon::fromTheme("view-fullscreen")));
+
+ addRule(new RuleItem(QLatin1String("minimize"),
+ RulePolicy::SetRule, RuleItem::Boolean,
+ i18n("Minimized"), i18n("Size & Position"),
+ QIcon::fromTheme("window-minimize")));
+
+ addRule(new RuleItem(QLatin1String("shade"),
+ RulePolicy::SetRule, RuleItem::Boolean,
+ i18n("Shaded"), i18n("Size & Position"),
+ QIcon::fromTheme("window-shade")));
+
+ auto placement = addRule(new RuleItem(QLatin1String("placement"),
+ RulePolicy::ForceRule, RuleItem::Option,
+ i18n("Initial placement"), i18n("Size & Position"),
+ QIcon::fromTheme("region")));
+ placement->setOptionsData(placementModelData());
+
+ addRule(new RuleItem(QLatin1String("ignoregeometry"),
+ RulePolicy::SetRule, RuleItem::Boolean,
+ i18n("Ignore requested geometry"), i18n("Size & Position"),
+ QIcon::fromTheme("view-time-schedule-baselined-remove"),
+ i18n("Windows can ask to appear in a certain position.\n"
+ "By default this overrides the placement strategy\n"
+ "what might be nasty if the client abuses the feature\n"
+ "to unconditionally popup in the middle of your screen.")));
+
+ addRule(new RuleItem(QLatin1String("minsize"),
+ RulePolicy::ForceRule, RuleItem::Coordinate,
+ i18n("Minimum Size"), i18n("Size & Position"),
+ QIcon::fromTheme("image-resize-symbolic")));
+
+ addRule(new RuleItem(QLatin1String("maxsize"),
+ RulePolicy::ForceRule, RuleItem::Coordinate,
+ i18n("Maximum Size"), i18n("Size & Position"),
+ QIcon::fromTheme("image-resize-symbolic")));
+
+ addRule(new RuleItem(QLatin1String("strictgeometry"),
+ RulePolicy::ForceRule, RuleItem::Boolean,
+ i18n("Obey geometry restrictions"), i18n("Size & Position"),
+ QIcon::fromTheme("transform-crop-and-resize"),
+ i18n("Eg. terminals or video players can ask to keep a certain aspect ratio\n"
+ "or only grow by values larger than one\n"
+ "(eg. by the dimensions of one character).\n"
+ "This may be pointless and the restriction prevents arbitrary dimensions\n"
+ "like your complete screen area.")));
+
+ // Arrangement & Access
+ addRule(new RuleItem(QLatin1String("above"),
+ RulePolicy::SetRule, RuleItem::Boolean,
+ i18n("Keep above"), i18n("Arrangement & Access"),
+ QIcon::fromTheme("window-keep-above")));
+
+ addRule(new RuleItem(QLatin1String("below"),
+ RulePolicy::SetRule, RuleItem::Boolean,
+ i18n("Keep below"), i18n("Arrangement & Access"),
+ QIcon::fromTheme("window-keep-below")));
+
+ addRule(new RuleItem(QLatin1String("skiptaskbar"),
+ RulePolicy::SetRule, RuleItem::Boolean,
+ i18n("Skip taskbar"), i18n("Arrangement & Access"),
+ QIcon::fromTheme("kt-show-statusbar"),
+ i18n("Window shall (not) appear in the taskbar.")));
+
+ addRule(new RuleItem(QLatin1String("skippager"),
+ RulePolicy::SetRule, RuleItem::Boolean,
+ i18n("Skip pager"), i18n("Arrangement & Access"),
+ QIcon::fromTheme("org.kde.plasma.pager"),
+ i18n("Window shall (not) appear in the manager for virtual desktops")));
+
+ addRule(new RuleItem(QLatin1String("skipswitcher"),
+ RulePolicy::SetRule, RuleItem::Boolean,
+ i18n("Skip switcher"), i18n("Arrangement & Access"),
+ QIcon::fromTheme("preferences-system-windows-effect-flipswitch"),
+ i18n("Window shall (not) appear in the Alt+Tab list")));
+
+ addRule(new RuleItem(QLatin1String("shortcut"),
+ RulePolicy::SetRule, RuleItem::Shortcut,
+ i18n("Shortcut"), i18n("Arrangement & Access"),
+ QIcon::fromTheme("configure-shortcuts")));
+
+ // Appearance & Fixes
+ addRule(new RuleItem(QLatin1String("noborder"),
+ RulePolicy::SetRule, RuleItem::Boolean,
+ i18n("No titlebar and frame"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("dialog-cancel")));
+
+ auto decocolor = addRule(new RuleItem(QLatin1String("decocolor"),
+ RulePolicy::ForceRule, RuleItem::Option,
+ i18n("Titlebar color scheme"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("preferences-desktop-theme")));
+ decocolor->setOptionsData(colorSchemesModelData());
+
+ addRule(new RuleItem(QLatin1String("opacityactive"),
+ RulePolicy::ForceRule, RuleItem::Percentage,
+ i18n("Active opacity"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("edit-opacity")));
+
+ addRule(new RuleItem(QLatin1String("opacityinactive"),
+ RulePolicy::ForceRule, RuleItem::Percentage,
+ i18n("Inactive opacity"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("edit-opacity")));
+
+ auto fsplevel = addRule(new RuleItem(QLatin1String("fsplevel"),
+ RulePolicy::ForceRule, RuleItem::Option,
+ i18n("Focus stealing prevention"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("preferences-system-windows-effect-glide"),
+ i18n("KWin tries to prevent windows from taking the focus\n"
+ "(\"activate\") while you're working in another window,\n"
+ "but this may sometimes fail or superact.\n"
+ "\"None\" will unconditionally allow this window to get the focus while\n"
+ "\"Extreme\" will completely prevent it from taking the focus.")));
+ fsplevel->setOptionsData(focusModelData());
+
+ auto fpplevel = addRule(new RuleItem(QLatin1String("fpplevel"),
+ RulePolicy::ForceRule, RuleItem::Option,
+ i18n("Focus protection"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("preferences-system-windows-effect-minimize"),
+ i18n("This controls the focus protection of the currently active window.\n"
+ "None will always give the focus away,\n"
+ "Extreme will keep it.\n"
+ "Otherwise it's interleaved with the stealing prevention\n"
+ "assigned to the window that wants the focus.")));
+ fpplevel->setOptionsData(focusModelData());
+
+ addRule(new RuleItem(QLatin1String("acceptfocus"),
+ RulePolicy::ForceRule, RuleItem::Boolean,
+ i18n("Accept focus"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("preferences-desktop-cursors"),
+ i18n("Windows may prevent to get the focus (activate) when being clicked.\n"
+ "On the other hand you might wish to prevent a window\n"
+ "from getting focused on a mouse click.")));
+
+ addRule(new RuleItem(QLatin1String("disableglobalshortcuts"),
+ RulePolicy::ForceRule, RuleItem::Boolean,
+ i18n("Ignore global shortcuts"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("input-keyboard-virtual-off"),
+ i18n("When used, a window will receive\n"
+ "all keyboard inputs while it is active, including Alt+Tab etc.\n"
+ "This is especially interesting for emulators or virtual machines.\n"
+ "\n"
+ "Be warned:\n"
+ "you won't be able to Alt+Tab out of the window\n"
+ "nor use any other global shortcut (such as Alt+F2 to show KRunner)\n"
+ "while it's active!")));
+
+ addRule(new RuleItem(QLatin1String("closeable"),
+ RulePolicy::ForceRule, RuleItem::Boolean,
+ i18n("Closeable"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("dialog-close")));
+
+ auto type = addRule(new RuleItem(QLatin1String("type"),
+ RulePolicy::ForceRule, RuleItem::Option,
+ i18n("Set window type"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("window-duplicate")));
+ type->setOptionsData(windowTypesModelData());
+
+ addRule(new RuleItem(QLatin1String("desktopfile"),
+ RulePolicy::SetRule, RuleItem::String,
+ i18n("Desktop file name"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("application-x-desktop")));
+
+ addRule(new RuleItem(QLatin1String("blockcompositing"),
+ RulePolicy::ForceRule, RuleItem::Boolean,
+ i18n("Block compositing"), i18n("Appearance & Fixes"),
+ QIcon::fromTheme("composite-track-on")));
+}
+
+
+const QHash RulesModel::x11PropertyHash()
+{
+ static const auto propertyToRule = QHash {
+ /* The original detection dialog allows to choose depending on "Match complete window class":
+ * if Match Complete == false: wmclass = "resourceClass"
+ * if Match Complete == true: wmclass = "resourceName" + " " + "resourceClass"
+ */
+ { "resourceName", "wmclass" },
+ { "caption", "title" },
+ { "role", "windowrole" },
+ { "clientMachine", "clientmachine" },
+ { "x11DesktopNumber", "desktop" },
+ { "maximizeHorizontal", "maximizehoriz" },
+ { "maximizeVertical", "maximizevert" },
+ { "minimized", "minimize" },
+ { "shaded", "shade" },
+ { "fullscreen", "fullscreen" },
+ { "keepAbove", "above" },
+ { "keepBelow", "below" },
+ { "noBorder", "noborder" },
+ { "skipTaskbar", "skiptaskbar" },
+ { "skipPager", "skippager" },
+ { "skipSwitcher", "skipswitcher" },
+ { "type", "type" },
+ { "desktopFile", "desktopfile" }
+ };
+ return propertyToRule;
+};
+
+void RulesModel::setWindowProperties(const QVariantMap &info, bool forceValue)
+{
+ // Properties that cannot be directly applied via x11PropertyHash
+ const QString position = QStringLiteral("%1,%2").arg(info.value("x").toInt())
+ .arg(info.value("y").toInt());
+ const QString size = QStringLiteral("%1,%2").arg(info.value("width").toInt())
+ .arg(info.value("height").toInt());
+
+ m_rules["position"]->setSuggestedValue(position, forceValue);
+ m_rules["size"]->setSuggestedValue(size, forceValue);
+ m_rules["minsize"]->setSuggestedValue(size, forceValue);
+ m_rules["maxsize"]->setSuggestedValue(size, forceValue);
+
+ NET::WindowType window_type = static_cast(info.value("type", 0).toInt());
+ if (window_type == NET::Unknown) {
+ window_type = NET::Normal;
+ }
+ m_rules["types"]->setSuggestedValue(1 << window_type, forceValue);
+
+ const auto ruleForProperty = x11PropertyHash();
+ for (QString &property : info.keys()) {
+ if (!ruleForProperty.contains(property)) {
+ continue;
+ }
+ const QString ruleKey = ruleForProperty.value(property, QString());
+ Q_ASSERT(hasRule(ruleKey));
+
+ m_rules[ruleKey]->setSuggestedValue(info.value(property), forceValue);
+ }
+
+ emit dataChanged(index(0), index(rowCount()-1), {RulesModel::SuggestedValueRole});
+ if (!forceValue) {
+ emit suggestionsChanged();
+ }
+}
+
+
+QList RulesModel::windowTypesModelData() const
+{
+ static const auto modelData = QList {
+ //TODO: Find/create better icons
+ { NET::Normal, i18n("Normal Window") , QIcon::fromTheme("window") },
+ { NET::Dialog, i18n("Dialog Window") , QIcon::fromTheme("window-duplicate") },
+ { NET::Utility, i18n("Utility Window") , QIcon::fromTheme("dialog-object-properties") },
+ { NET::Dock, i18n("Dock (panel)") , QIcon::fromTheme("list-remove") },
+ { NET::Toolbar, i18n("Toolbar") , QIcon::fromTheme("tools") },
+ { NET::Menu, i18n("Torn-Off Menu") , QIcon::fromTheme("overflow-menu-left") },
+ { NET::Splash, i18n("Splash Screen") , QIcon::fromTheme("embosstool") },
+ { NET::Desktop, i18n("Desktop") , QIcon::fromTheme("desktop") },
+ // { NET::Override, i18n("Unmanaged Window") }, deprecated
+ { NET::TopMenu, i18n("Standalone Menubar"), QIcon::fromTheme("open-menu-symbolic") }
+ };
+ return modelData;
+}
+
+QList RulesModel::virtualDesktopsModelData() const
+{
+ QList modelData;
+ for (int desktopId = 1; desktopId <= KWindowSystem::numberOfDesktops(); ++desktopId) {
+ modelData << OptionsModel::Data{
+ desktopId,
+ QString::number(desktopId).rightJustified(2) + QStringLiteral(": ") + KWindowSystem::desktopName(desktopId),
+ QIcon::fromTheme("virtual-desktops")
+ };
+ }
+ modelData << OptionsModel::Data{ NET::OnAllDesktops, i18n("All Desktops"), QIcon::fromTheme("window-pin") };
+ return modelData;
+}
+
+
+QList RulesModel::activitiesModelData() const
+{
+#ifdef KWIN_BUILD_ACTIVITIES
+ QList modelData;
+
+ // NULL_ID from kactivities/src/lib/core/consumer.cpp
+ modelData << OptionsModel::Data{
+ QString::fromLatin1("00000000-0000-0000-0000-000000000000"),
+ i18n("All Activities"),
+ QIcon::fromTheme("activities")
+ };
+
+ const auto activities = m_activities->activities(KActivities::Info::Running);
+ if (m_activities->serviceStatus() == KActivities::Consumer::Running) {
+ for (const QString &activityId : activities) {
+ const KActivities::Info info(activityId);
+ modelData << OptionsModel::Data{ activityId, info.name(), QIcon::fromTheme(info.icon()) };
+ }
+ }
+
+ return modelData;
+#else
+ return {};
+#endif
+}
+
+QList RulesModel::placementModelData() const
+{
+ // From "placement.h" : Placement rule is stored as a string, not the enum value
+ static const auto modelData = QList {
+ { Placement::policyToString(Placement::Default), i18n("Default") },
+ { Placement::policyToString(Placement::NoPlacement), i18n("No Placement") },
+ { Placement::policyToString(Placement::Smart), i18n("Minimal Overlapping") },
+ { Placement::policyToString(Placement::Maximizing), i18n("Maximized") },
+ { Placement::policyToString(Placement::Cascade), i18n("Cascaded") },
+ { Placement::policyToString(Placement::Centered), i18n("Centered") },
+ { Placement::policyToString(Placement::Random), i18n("Random") },
+ { Placement::policyToString(Placement::ZeroCornered), i18n("In Top-Left Corner") },
+ { Placement::policyToString(Placement::UnderMouse), i18n("Under Mouse") },
+ { Placement::policyToString(Placement::OnMainWindow), i18n("On Main Window") }
+ };
+ return modelData;
+}
+
+QList RulesModel::focusModelData() const
+{
+ static const auto modelData = QList {
+ { 0, i18n("None") },
+ { 1, i18n("Low") },
+ { 2, i18n("Normal") },
+ { 3, i18n("High") },
+ { 4, i18n("Extreme") }
+ };
+ return modelData;
+}
+
+QList RulesModel::colorSchemesModelData() const
+{
+ QList modelData;
+
+ KColorSchemeManager schemes;
+ QAbstractItemModel *schemesModel = schemes.model();
+
+ // Skip row 0, which is Default scheme
+ for (int r = 1; r < schemesModel->rowCount(); r++) {
+ const QModelIndex index = schemesModel->index(r, 0);
+ modelData << OptionsModel::Data{
+ QFileInfo(index.data(Qt::UserRole).toString()).baseName(),
+ index.data(Qt::DisplayRole).toString(),
+ index.data(Qt::DecorationRole).value()
+ };
+ }
+
+ return modelData;
+}
+
+void RulesModel::detectWindowProperties(int secs)
+{
+ QTimer::singleShot(secs*1000, this, &RulesModel::selectX11Window);
+}
+
+void RulesModel::selectX11Window()
+{
+ QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"),
+ QStringLiteral("/KWin"),
+ QStringLiteral("org.kde.KWin"),
+ QStringLiteral("queryWindowInfo"));
+
+ QDBusPendingReply async = QDBusConnection::sessionBus().asyncCall(message);
+
+ QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this);
+ connect(callWatcher, &QDBusPendingCallWatcher::finished, this,
+ [this](QDBusPendingCallWatcher *self) {
+ QDBusPendingReply reply = *self;
+ self->deleteLater();
+ if (!reply.isValid()) {
+ return;
+ }
+ const QVariantMap windowInfo = reply.value();
+ //TODO: Improve UI to suggest/select the detected properties.
+ // For now, just prefill unused rules
+ setWindowProperties(windowInfo);
+ }
+ );
+}
+
+} //namespace
diff --git a/kcmkwin/kwinrules/ruleswidget.h b/kcmkwin/kwinrules/ruleswidget.h
deleted file mode 100644
--- a/kcmkwin/kwinrules/ruleswidget.h
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (c) 2004 Lubos Lunak
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-
-#ifndef __RULESWIDGET_H__
-#define __RULESWIDGET_H__
-
-#include
-
-#include
-#include
-
-#include "ui_ruleswidgetbase.h"
-#include "ui_editshortcut.h"
-
-#ifdef KWIN_BUILD_ACTIVITIES
-namespace KActivities {
- class Consumer;
-} // namespace KActivities
-#endif
-
-namespace KWin
-{
-
-class Rules;
-class DetectDialog;
-
-class RulesWidget
- : public QWidget, public Ui::RulesWidgetBase
-{
- Q_OBJECT
-public:
- explicit RulesWidget(QWidget* parent = nullptr);
- void setRules(Rules* r);
- Rules* rules() const;
- bool finalCheck();
- void prepareWindowSpecific(const QVariantMap &info);
-Q_SIGNALS:
- void changed(bool state);
-protected Q_SLOTS:
- void detectClicked();
- void wmclassMatchChanged();
- void roleMatchChanged();
- void titleMatchChanged();
- void machineMatchChanged();
- void shortcutEditClicked();
-private Q_SLOTS:
- // geometry tab
- void updateEnableposition();
- void updateEnablesize();
- void updateEnabledesktop();
- void updateEnablescreen();
-#ifdef KWIN_BUILD_ACTIVITIES
- void updateEnableactivity();
-#endif
- void updateEnablemaximizehoriz();
- void updateEnablemaximizevert();
- void updateEnableminimize();
- void updateEnableshade();
- void updateEnablefullscreen();
- void updateEnableplacement();
- // preferences tab
- void updateEnableabove();
- void updateEnablebelow();
- void updateEnablenoborder();
- void updateEnabledecocolor();
- void updateEnableskiptaskbar();
- void updateEnableskippager();
- void updateEnableskipswitcher();
- void updateEnableacceptfocus();
- void updateEnablecloseable();
- void updateEnableautogroup();
- void updateEnableautogroupfg();
- void updateEnableautogroupid();
- void updateEnableopacityactive();
- void updateEnableopacityinactive();
- // workarounds tab
- void updateEnablefsplevel();
- void updateEnablefpplevel();
- void updateEnabletype();
- void updateEnableignoregeometry();
- void updateEnableminsize();
- void updateEnablemaxsize();
- void updateEnablestrictgeometry();
- void updateEnableshortcut();
- void updateEnabledisableglobalshortcuts();
- void updateEnableblockcompositing();
- void updateEnabledesktopfile();
- // internal
- void detected(bool);
-private:
- int desktopToCombo(int d) const;
- int comboToDesktop(int val) const;
-#ifdef KWIN_BUILD_ACTIVITIES
- int activityToCombo(const QString &d) const;
- QString comboToActivity(int val) const;
- void updateActivitiesList();
- KActivities::Consumer *m_activities;
- QString m_selectedActivityId; // we need this for async activity loading
-#endif
- int comboToTiling(int val) const;
- int inc(int i) const { return i+1; }
- int dec(int i) const { return i-1; }
- void prefillUnusedValues(const QVariantMap& info);
- DetectDialog* detect_dlg;
- bool detect_dlg_ok;
-};
-
-class RulesDialog
- : public QDialog
-{
- Q_OBJECT
-public:
- explicit RulesDialog(QWidget* parent = nullptr, const char* name = nullptr);
- Rules* edit(Rules* r, const QVariantMap& info, bool show_hints);
-protected:
- void accept() override;
-private Q_SLOTS:
- void displayHints();
-private:
- RulesWidget* widget;
- Rules* rules;
-};
-
-class EditShortcut
- : public QWidget, public Ui_EditShortcut
-{
- Q_OBJECT
-public:
- explicit EditShortcut(QWidget* parent = nullptr);
-protected Q_SLOTS:
- void editShortcut();
- void clearShortcut();
-};
-
-class EditShortcutDialog
- : public QDialog
-{
- Q_OBJECT
-public:
- explicit EditShortcutDialog(QWidget* parent = nullptr, const char* name = nullptr);
- void setShortcut(const QString& cut);
- QString shortcut() const;
-private:
- EditShortcut* widget;
-};
-
-// slightly duped from utils.cpp
-class ShortcutDialog
- : public QDialog
-{
- Q_OBJECT
-public:
- explicit ShortcutDialog(const QKeySequence& cut, QWidget* parent = nullptr);
- void accept() override;
- QKeySequence shortcut() const;
-private:
- KKeySequenceWidget* widget;
-};
-
-} // namespace
-
-#endif
diff --git a/kcmkwin/kwinrules/ruleswidget.cpp b/kcmkwin/kwinrules/ruleswidget.cpp
deleted file mode 100644
--- a/kcmkwin/kwinrules/ruleswidget.cpp
+++ /dev/null
@@ -1,970 +0,0 @@
-/*
- * Copyright (c) 2004 Lubos Lunak
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "ruleswidget.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifdef KWIN_BUILD_ACTIVITIES
-#include
-#endif
-
-#include
-#include
-#include
-
-#include "../../rules.h"
-
-#include "detectwidget.h"
-
-Q_DECLARE_METATYPE(NET::WindowType)
-
-namespace KWin
-{
-
-#define SETUP( var, type ) \
- connect( enable_##var, SIGNAL(toggled(bool)), rule_##var, SLOT(setEnabled(bool))); \
- connect( enable_##var, SIGNAL(toggled(bool)), this, SLOT(updateEnable##var())); \
- connect( rule_##var, SIGNAL(activated(int)), this, SLOT(updateEnable##var())); \
- enable_##var->setWhatsThis( enableDesc ); \
- rule_##var->setWhatsThis( type##RuleDesc );
-
-RulesWidget::RulesWidget(QWidget* parent)
- : detect_dlg(nullptr)
-{
- Q_UNUSED(parent);
- setupUi(this);
- QRegularExpressionValidator* validator = new QRegularExpressionValidator(QRegularExpression("[0-9\\-+,xX:]*"), this);
- maxsize->setValidator(validator);
- minsize->setValidator(validator);
- position->setValidator(validator);
- Ui::RulesWidgetBase::size->setValidator(validator);
-
- QString enableDesc =
- i18n("Enable this checkbox to alter this window property for the specified window(s).");
- QString setRuleDesc =
- i18n("Specify how the window property should be affected:"
- "- Do Not Affect: The window property will not be affected and therefore"
- " the default handling for it will be used. Specifying this will block more generic"
- " window settings from taking effect.
"
- "- Apply Initially: The window property will be only set to the given value"
- " after the window is created. No further changes will be affected.
"
- "- Remember: The value of the window property will be remembered and every"
- " time the window is created, the last remembered value will be applied.
"
- "- Force: The window property will be always forced to the given value.
"
- "- Apply Now: The window property will be set to the given value immediately"
- " and will not be affected later (this action will be deleted afterwards).
"
- "- Force temporarily: The window property will be forced to the given value"
- " until it is hidden (this action will be deleted after the window is hidden).
"
- "
");
- QString forceRuleDesc =
- i18n("Specify how the window property should be affected:"
- "- Do Not Affect: The window property will not be affected and therefore"
- " the default handling for it will be used. Specifying this will block more generic"
- " window settings from taking effect.
"
- "- Force: The window property will be always forced to the given value.
"
- "- Force temporarily: The window property will be forced to the given value"
- " until it is hidden (this action will be deleted after the window is hidden).
"
- "
");
- // window tabs have enable signals done in designer
- // geometry tab
- SETUP(position, set);
- SETUP(size, set);
- SETUP(desktop, set);
- SETUP(screen, set);
-#ifdef KWIN_BUILD_ACTIVITIES
- SETUP(activity, set);
-#endif
- SETUP(maximizehoriz, set);
- SETUP(maximizevert, set);
- SETUP(minimize, set);
- SETUP(shade, set);
- SETUP(fullscreen, set);
- SETUP(placement, force);
- // preferences tab
- SETUP(above, set);
- SETUP(below, set);
- SETUP(noborder, set);
- SETUP(decocolor, force);
- SETUP(skiptaskbar, set);
- SETUP(skippager, set);
- SETUP(skipswitcher, set);
- SETUP(acceptfocus, force);
- SETUP(closeable, force);
- SETUP(autogroup, force);
- SETUP(autogroupfg, force);
- SETUP(autogroupid, force);
- SETUP(opacityactive, force);
- SETUP(opacityinactive, force);
- SETUP(shortcut, force);
- // workarounds tab
- SETUP(fsplevel, force);
- SETUP(fpplevel, force);
- SETUP(type, force);
- SETUP(desktopfile, set);
- SETUP(ignoregeometry, set);
- SETUP(minsize, force);
- SETUP(maxsize, force);
- SETUP(strictgeometry, force);
- SETUP(disableglobalshortcuts, force);
- SETUP(blockcompositing, force);
-
- connect (shortcut_edit, SIGNAL(clicked()), SLOT(shortcutEditClicked()));
-
- edit_reg_wmclass->hide();
- edit_reg_role->hide();
- edit_reg_title->hide();
- edit_reg_machine->hide();
-
-#ifndef KWIN_BUILD_ACTIVITIES
- rule_activity->hide();
- enable_activity->hide();
- activity->hide();
-#endif
- int i;
- for (i = 1;
- i <= KWindowSystem::numberOfDesktops();
- ++i)
- desktop->addItem(QString::number(i).rightJustified(2) + ':' + KWindowSystem::desktopName(i));
- desktop->addItem(i18n("All Desktops"));
-
-#ifdef KWIN_BUILD_ACTIVITIES
- m_activities = new KActivities::Consumer(this);
- connect(m_activities, &KActivities::Consumer::activitiesChanged,
- this, [this] { updateActivitiesList(); });
- connect(m_activities, &KActivities::Consumer::serviceStatusChanged,
- this, [this] { updateActivitiesList(); });
- updateActivitiesList();
-#endif
-
- KColorSchemeManager *schemes = new KColorSchemeManager(this);
- decocolor->setModel(schemes->model());
-
- // hide autogrouping as it's currently not supported
- // BUG 370301
- line_11->hide();
- enable_autogroup->hide();
- autogroup->hide();
- rule_autogroup->hide();
- enable_autogroupid->hide();
- autogroupid->hide();
- rule_autogroupid->hide();
- enable_autogroupfg->hide();
- autogroupfg->hide();
- rule_autogroupfg->hide();
-}
-
-#undef SETUP
-
-#define UPDATE_ENABLE_SLOT(var) \
- void RulesWidget::updateEnable##var() \
- { \
- /* leave the label readable label_##var->setEnabled( enable_##var->isChecked() && rule_##var->currentIndex() != 0 );*/ \
- Ui::RulesWidgetBase::var->setEnabled( enable_##var->isChecked() && rule_##var->currentIndex() != 0 ); \
- }
-
-// geometry tab
-UPDATE_ENABLE_SLOT(position)
-UPDATE_ENABLE_SLOT(size)
-UPDATE_ENABLE_SLOT(desktop)
-UPDATE_ENABLE_SLOT(screen)
-#ifdef KWIN_BUILD_ACTIVITIES
-UPDATE_ENABLE_SLOT(activity)
-#endif
-UPDATE_ENABLE_SLOT(maximizehoriz)
-UPDATE_ENABLE_SLOT(maximizevert)
-UPDATE_ENABLE_SLOT(minimize)
-UPDATE_ENABLE_SLOT(shade)
-UPDATE_ENABLE_SLOT(fullscreen)
-UPDATE_ENABLE_SLOT(placement)
-// preferences tab
-UPDATE_ENABLE_SLOT(above)
-UPDATE_ENABLE_SLOT(below)
-UPDATE_ENABLE_SLOT(noborder)
-UPDATE_ENABLE_SLOT(decocolor)
-UPDATE_ENABLE_SLOT(skiptaskbar)
-UPDATE_ENABLE_SLOT(skippager)
-UPDATE_ENABLE_SLOT(skipswitcher)
-UPDATE_ENABLE_SLOT(acceptfocus)
-UPDATE_ENABLE_SLOT(closeable)
-UPDATE_ENABLE_SLOT(autogroup)
-UPDATE_ENABLE_SLOT(autogroupfg)
-UPDATE_ENABLE_SLOT(autogroupid)
-UPDATE_ENABLE_SLOT(opacityactive)
-UPDATE_ENABLE_SLOT(opacityinactive)
-void RulesWidget::updateEnableshortcut()
-{
- shortcut->setEnabled(enable_shortcut->isChecked() && rule_shortcut->currentIndex() != 0);
- shortcut_edit->setEnabled(enable_shortcut->isChecked() && rule_shortcut->currentIndex() != 0);
-}
-// workarounds tab
-UPDATE_ENABLE_SLOT(fsplevel)
-UPDATE_ENABLE_SLOT(fpplevel)
-UPDATE_ENABLE_SLOT(type)
-UPDATE_ENABLE_SLOT(ignoregeometry)
-UPDATE_ENABLE_SLOT(minsize)
-UPDATE_ENABLE_SLOT(maxsize)
-UPDATE_ENABLE_SLOT(strictgeometry)
-UPDATE_ENABLE_SLOT(disableglobalshortcuts)
-UPDATE_ENABLE_SLOT(blockcompositing)
-UPDATE_ENABLE_SLOT(desktopfile)
-
-#undef UPDATE_ENABLE_SLOT
-
-static const int set_rule_to_combo[] = {
- 0, // Unused
- 0, // Don't Affect
- 3, // Force
- 1, // Apply
- 2, // Remember
- 4, // ApplyNow
- 5 // ForceTemporarily
-};
-
-static const Rules::SetRule combo_to_set_rule[] = {
- (Rules::SetRule)Rules::DontAffect,
- (Rules::SetRule)Rules::Apply,
- (Rules::SetRule)Rules::Remember,
- (Rules::SetRule)Rules::Force,
- (Rules::SetRule)Rules::ApplyNow,
- (Rules::SetRule)Rules::ForceTemporarily
-};
-
-static const int force_rule_to_combo[] = {
- 0, // Unused
- 0, // Don't Affect
- 1, // Force
- 0, // Apply
- 0, // Remember
- 0, // ApplyNow
- 2 // ForceTemporarily
-};
-
-static const Rules::ForceRule combo_to_force_rule[] = {
- (Rules::ForceRule)Rules::DontAffect,
- (Rules::ForceRule)Rules::Force,
- (Rules::ForceRule)Rules::ForceTemporarily
-};
-
-static QString positionToStr(const QPoint& p)
-{
- if (p == invalidPoint)
- return QString();
- return QString::number(p.x()) + ',' + QString::number(p.y());
-}
-
-static QPoint strToPosition(const QString& str)
-{
- // two numbers, with + or -, separated by any of , x X :
- QRegExp reg("\\s*([+-]?[0-9]*)\\s*[,xX:]\\s*([+-]?[0-9]*)\\s*");
- if (!reg.exactMatch(str))
- return invalidPoint;
- return QPoint(reg.cap(1).toInt(), reg.cap(2).toInt());
-}
-
-static QString sizeToStr(const QSize& s)
-{
- if (!s.isValid())
- return QString();
- return QString::number(s.width()) + ',' + QString::number(s.height());
-}
-
-static QSize strToSize(const QString& str)
-{
- // two numbers, with + or -, separated by any of , x X :
- QRegExp reg("\\s*([+-]?[0-9]*)\\s*[,xX:]\\s*([+-]?[0-9]*)\\s*");
- if (!reg.exactMatch(str))
- return QSize();
- return QSize(reg.cap(1).toInt(), reg.cap(2).toInt());
-}
-
-int RulesWidget::desktopToCombo(int d) const
-{
- if (d >= 1 && d < desktop->count())
- return d - 1;
- return desktop->count() - 1; // on all desktops
-}
-
-int RulesWidget::comboToDesktop(int val) const
-{
- if (val == desktop->count() - 1)
- return NET::OnAllDesktops;
- return val + 1;
-}
-
-#ifdef KWIN_BUILD_ACTIVITIES
-int RulesWidget::activityToCombo(const QString &d) const
-{
- // TODO: ivan - do a multiselection list
- for (int i = 0; i < activity->count(); i++) {
- if (activity->itemData(i).toString() == d) {
- return i;
- }
- }
-
- return activity->count() - 1; // on all activities
-}
-
-QString RulesWidget::comboToActivity(int val) const
-{
- // TODO: ivan - do a multiselection list
- if (val < 0 || val >= activity->count())
- return QString();
-
- return activity->itemData(val).toString();
-}
-
-void RulesWidget::updateActivitiesList()
-{
- activity->clear();
-
- // cloned from kactivities/src/lib/core/consumer.cpp
- #define NULL_UUID "00000000-0000-0000-0000-000000000000"
- activity->addItem(i18n("All Activities"), QString::fromLatin1(NULL_UUID));
- #undef NULL_UUID
-
- if (m_activities->serviceStatus() == KActivities::Consumer::Running) {
- foreach (const QString & activityId, m_activities->activities(KActivities::Info::Running)) {
- const KActivities::Info info(activityId);
- activity->addItem(info.name(), activityId);
- }
- }
-
- auto rules = this->rules();
- if (rules->activityrule == Rules::UnusedSetRule) {
- enable_activity->setChecked(false);
- Ui::RulesWidgetBase::activity->setCurrentIndex(0);
- } else {
- enable_activity->setChecked(true);
- Ui::RulesWidgetBase::activity->setCurrentIndex(activityToCombo(m_selectedActivityId));
- }
- updateEnableactivity();
-}
-#endif
-
-static int placementToCombo(Placement::Policy placement)
-{
- static const int conv[] = {
- 1, // NoPlacement
- 0, // Default
- 0, // Unknown
- 6, // Random
- 2, // Smart
- 4, // Cascade
- 5, // Centered
- 7, // ZeroCornered
- 8, // UnderMouse
- 9, // OnMainWindow
- 3 // Maximizing
- };
- return conv[ placement ];
-}
-
-static Placement::Policy comboToPlacement(int val)
-{
- static const Placement::Policy conv[] = {
- Placement::Default,
- Placement::NoPlacement,
- Placement::Smart,
- Placement::Maximizing,
- Placement::Cascade,
- Placement::Centered,
- Placement::Random,
- Placement::ZeroCornered,
- Placement::UnderMouse,
- Placement::OnMainWindow
- // no Placement::Unknown
- };
- return conv[ val ];
-}
-
-static int typeToCombo(NET::WindowType type)
-{
- if (type < NET::Normal || type > NET::Splash ||
- type == NET::Override) // The user must NOT set a window to be unmanaged.
- // This case is not handled in KWin and will lead to segfaults.
- // Even iff it was supported, it would mean to allow the user to shoot himself
- // since an unmanaged window has to manage itself, what is probably not the case when the hint is not set.
- // Rule opportunity might be a relict from the Motif Hint window times of KDE1
- return 0; // Normal
- static const int conv[] = {
- 0, // Normal
- 7, // Desktop
- 3, // Dock
- 4, // Toolbar
- 5, // Menu
- 1, // Dialog
- 8, // Override - ignored.
- 9, // TopMenu
- 2, // Utility
- 6 // Splash
- };
- return conv[ type ];
-}
-
-static NET::WindowType comboToType(int val)
-{
- static const NET::WindowType conv[] = {
- NET::Normal,
- NET::Dialog,
- NET::Utility,
- NET::Dock,
- NET::Toolbar,
- NET::Menu,
- NET::Splash,
- NET::Desktop,
- NET::TopMenu
- };
- return conv[ val ];
-}
-
-#define GENERIC_RULE( var, func, Type, type, uimethod, uimethod0 ) \
- if ( rules->var##rule == Rules::Unused##Type##Rule ) \
- { \
- enable_##var->setChecked( false ); \
- rule_##var->setCurrentIndex( 0 ); \
- Ui::RulesWidgetBase::var->uimethod0; \
- updateEnable##var(); \
- } \
- else \
- { \
- enable_##var->setChecked( true ); \
- rule_##var->setCurrentIndex( type##_rule_to_combo[ rules->var##rule ] ); \
- Ui::RulesWidgetBase::var->uimethod( func( rules->var )); \
- updateEnable##var(); \
- }
-
-#define CHECKBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setChecked, setChecked( false ))
-#define LINEEDIT_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setText, setText( QString() ))
-#define COMBOBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setCurrentIndex, setCurrentIndex( 0 ))
-#define SPINBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setValue, setValue(0))
-#define CHECKBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setChecked, setChecked( false ))
-#define LINEEDIT_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setText, setText( QString() ))
-#define COMBOBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setCurrentIndex, setCurrentIndex( 0 ))
-#define SPINBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setValue, setValue(0))
-
-void RulesWidget::setRules(Rules* rules)
-{
- Rules tmp;
- if (rules == nullptr)
- rules = &tmp; // empty
- description->setText(rules->description);
- wmclass->setText(rules->wmclass);
- whole_wmclass->setChecked(rules->wmclasscomplete);
- wmclass_match->setCurrentIndex(rules->wmclassmatch);
- wmclassMatchChanged();
- role->setText(rules->windowrole);
- role_match->setCurrentIndex(rules->windowrolematch);
- roleMatchChanged();
- types->item(0)->setSelected(rules->types & NET::NormalMask);
- types->item(1)->setSelected(rules->types & NET::DialogMask);
- types->item(2)->setSelected(rules->types & NET::UtilityMask);
- types->item(3)->setSelected(rules->types & NET::DockMask);
- types->item(4)->setSelected(rules->types & NET::ToolbarMask);
- types->item(5)->setSelected(rules->types & NET::MenuMask);
- types->item(6)->setSelected(rules->types & NET::SplashMask);
- types->item(7)->setSelected(rules->types & NET::DesktopMask);
- types->item(8)->setSelected(rules->types & NET::OverrideMask);
- types->item(9)->setSelected(rules->types & NET::TopMenuMask);
- title->setText(rules->title);
- title_match->setCurrentIndex(rules->titlematch);
- titleMatchChanged();
- machine->setText(rules->clientmachine);
- machine_match->setCurrentIndex(rules->clientmachinematch);
- machineMatchChanged();
- LINEEDIT_SET_RULE(position, positionToStr);
- LINEEDIT_SET_RULE(size, sizeToStr);
- COMBOBOX_SET_RULE(desktop, desktopToCombo);
- SPINBOX_SET_RULE(screen, inc);
-#ifdef KWIN_BUILD_ACTIVITIES
- m_selectedActivityId = rules->activity;
- COMBOBOX_SET_RULE(activity, activityToCombo);
-#endif
- CHECKBOX_SET_RULE(maximizehoriz,);
- CHECKBOX_SET_RULE(maximizevert,);
- CHECKBOX_SET_RULE(minimize,);
- CHECKBOX_SET_RULE(shade,);
- CHECKBOX_SET_RULE(fullscreen,);
- COMBOBOX_FORCE_RULE(placement, placementToCombo);
- CHECKBOX_SET_RULE(above,);
- CHECKBOX_SET_RULE(below,);
- CHECKBOX_SET_RULE(noborder,);
- auto decoColorToCombo = [this](const QString &value) {
- for (int i = 0; i < decocolor->count(); ++i) {
- if (decocolor->itemData(i).toString() == value) {
- return i;
- }
- }
- // search for Breeze
- for (int i = 0; i < decocolor->count(); ++i) {
- if (QFileInfo(decocolor->itemData(i).toString()).baseName() == QStringLiteral("Breeze")) {
- return i;
- }
- }
- return 0;
- };
- COMBOBOX_FORCE_RULE(decocolor, decoColorToCombo);
- CHECKBOX_SET_RULE(skiptaskbar,);
- CHECKBOX_SET_RULE(skippager,);
- CHECKBOX_SET_RULE(skipswitcher,);
- CHECKBOX_FORCE_RULE(acceptfocus,);
- CHECKBOX_FORCE_RULE(closeable,);
- CHECKBOX_FORCE_RULE(autogroup,);
- CHECKBOX_FORCE_RULE(autogroupfg,);
- LINEEDIT_FORCE_RULE(autogroupid,);
- SPINBOX_FORCE_RULE(opacityactive,);
- SPINBOX_FORCE_RULE(opacityinactive,);
- LINEEDIT_SET_RULE(shortcut,);
- COMBOBOX_FORCE_RULE(fsplevel,);
- COMBOBOX_FORCE_RULE(fpplevel,);
- COMBOBOX_FORCE_RULE(type, typeToCombo);
- CHECKBOX_SET_RULE(ignoregeometry,);
- LINEEDIT_FORCE_RULE(minsize, sizeToStr);
- LINEEDIT_FORCE_RULE(maxsize, sizeToStr);
- CHECKBOX_FORCE_RULE(strictgeometry,);
- CHECKBOX_FORCE_RULE(disableglobalshortcuts,);
- CHECKBOX_FORCE_RULE(blockcompositing,);
- LINEEDIT_SET_RULE(desktopfile,)
-}
-
-#undef GENERIC_RULE
-#undef CHECKBOX_SET_RULE
-#undef LINEEDIT_SET_RULE
-#undef COMBOBOX_SET_RULE
-#undef SPINBOX_SET_RULE
-#undef CHECKBOX_FORCE_RULE
-#undef LINEEDIT_FORCE_RULE
-#undef COMBOBOX_FORCE_RULE
-#undef SPINBOX_FORCE_RULE
-
-#define GENERIC_RULE( var, func, Type, type, uimethod ) \
- if ( enable_##var->isChecked() && rule_##var->currentIndex() >= 0) \
- { \
- rules->var##rule = combo_to_##type##_rule[ rule_##var->currentIndex() ]; \
- rules->var = func( Ui::RulesWidgetBase::var->uimethod()); \
- } \
- else \
- rules->var##rule = Rules::Unused##Type##Rule;
-
-#define CHECKBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, isChecked )
-#define LINEEDIT_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, text )
-#define COMBOBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, currentIndex )
-#define SPINBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, value)
-#define CHECKBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, isChecked )
-#define LINEEDIT_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, text )
-#define COMBOBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, currentIndex )
-#define SPINBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, value)
-
-Rules* RulesWidget::rules() const
-{
- Rules* rules = new Rules();
- rules->description = description->text();
- rules->wmclass = wmclass->text().toUtf8();
- rules->wmclasscomplete = whole_wmclass->isChecked();
- rules->wmclassmatch = static_cast< Rules::StringMatch >(wmclass_match->currentIndex());
- rules->windowrole = role->text().toUtf8();
- rules->windowrolematch = static_cast< Rules::StringMatch >(role_match->currentIndex());
- rules->types = {};
- bool all_types = true;
- for (int i = 0;
- i < types->count();
- ++i)
- if (!types->item(i)->isSelected())
- all_types = false;
- if (all_types) // if all types are selected, use AllTypesMask (for future expansion)
- rules->types = NET::AllTypesMask;
- else {
- rules->types |= types->item(0)->isSelected() ? NET::NormalMask : NET::WindowTypeMask(0);
- rules->types |= types->item(1)->isSelected() ? NET::DialogMask : NET::WindowTypeMask(0);
- rules->types |= types->item(2)->isSelected() ? NET::UtilityMask : NET::WindowTypeMask(0);
- rules->types |= types->item(3)->isSelected() ? NET::DockMask : NET::WindowTypeMask(0);
- rules->types |= types->item(4)->isSelected() ? NET::ToolbarMask : NET::WindowTypeMask(0);
- rules->types |= types->item(5)->isSelected() ? NET::MenuMask : NET::WindowTypeMask(0);
- rules->types |= types->item(6)->isSelected() ? NET::SplashMask : NET::WindowTypeMask(0);
- rules->types |= types->item(7)->isSelected() ? NET::DesktopMask : NET::WindowTypeMask(0);
- rules->types |= types->item(8)->isSelected() ? NET::OverrideMask : NET::WindowTypeMask(0);
- rules->types |= types->item(9)->isSelected() ? NET::TopMenuMask : NET::WindowTypeMask(0);
- }
- rules->title = title->text();
- rules->titlematch = static_cast< Rules::StringMatch >(title_match->currentIndex());
- rules->clientmachine = machine->text().toUtf8();
- rules->clientmachinematch = static_cast< Rules::StringMatch >(machine_match->currentIndex());
- LINEEDIT_SET_RULE(position, strToPosition);
- LINEEDIT_SET_RULE(size, strToSize);
- COMBOBOX_SET_RULE(desktop, comboToDesktop);
- SPINBOX_SET_RULE(screen, dec);
-#ifdef KWIN_BUILD_ACTIVITIES
- COMBOBOX_SET_RULE(activity, comboToActivity);
-#endif
- CHECKBOX_SET_RULE(maximizehoriz,);
- CHECKBOX_SET_RULE(maximizevert,);
- CHECKBOX_SET_RULE(minimize,);
- CHECKBOX_SET_RULE(shade,);
- CHECKBOX_SET_RULE(fullscreen,);
- COMBOBOX_FORCE_RULE(placement, comboToPlacement);
- CHECKBOX_SET_RULE(above,);
- CHECKBOX_SET_RULE(below,);
- CHECKBOX_SET_RULE(noborder,);
- auto comboToDecocolor = [this](int index) -> QString {
- return decocolor->itemData(index).toString();
- };
- COMBOBOX_FORCE_RULE(decocolor, comboToDecocolor);
- CHECKBOX_SET_RULE(skiptaskbar,);
- CHECKBOX_SET_RULE(skippager,);
- CHECKBOX_SET_RULE(skipswitcher,);
- CHECKBOX_FORCE_RULE(acceptfocus,);
- CHECKBOX_FORCE_RULE(closeable,);
- CHECKBOX_FORCE_RULE(autogroup,);
- CHECKBOX_FORCE_RULE(autogroupfg,);
- LINEEDIT_FORCE_RULE(autogroupid,);
- SPINBOX_FORCE_RULE(opacityactive,);
- SPINBOX_FORCE_RULE(opacityinactive,);
- LINEEDIT_SET_RULE(shortcut,);
- COMBOBOX_FORCE_RULE(fsplevel,);
- COMBOBOX_FORCE_RULE(fpplevel,);
- COMBOBOX_FORCE_RULE(type, comboToType);
- CHECKBOX_SET_RULE(ignoregeometry,);
- LINEEDIT_FORCE_RULE(minsize, strToSize);
- LINEEDIT_FORCE_RULE(maxsize, strToSize);
- CHECKBOX_FORCE_RULE(strictgeometry,);
- CHECKBOX_FORCE_RULE(disableglobalshortcuts,);
- CHECKBOX_FORCE_RULE(blockcompositing,);
- LINEEDIT_SET_RULE(desktopfile,);
- return rules;
-}
-
-#undef GENERIC_RULE
-#undef CHECKBOX_SET_RULE
-#undef LINEEDIT_SET_RULE
-#undef COMBOBOX_SET_RULE
-#undef SPINBOX_SET_RULE
-#undef CHECKBOX_FORCE_RULE
-#undef LINEEDIT_FORCE_RULE
-#undef COMBOBOX_FORCE_RULE
-#undef SPINBOX_FORCE_RULE
-
-#define STRING_MATCH_COMBO( type ) \
- void RulesWidget::type##MatchChanged() \
- { \
- edit_reg_##type->setEnabled( type##_match->currentIndex() == Rules::RegExpMatch ); \
- type->setEnabled( type##_match->currentIndex() != Rules::UnimportantMatch ); \
- }
-
-STRING_MATCH_COMBO(wmclass)
-STRING_MATCH_COMBO(role)
-STRING_MATCH_COMBO(title)
-STRING_MATCH_COMBO(machine)
-
-#undef STRING_MATCH_COMBO
-
-void RulesWidget::detectClicked()
-{
- Q_ASSERT(detect_dlg == nullptr);
- detect_dlg = new DetectDialog;
- connect(detect_dlg, SIGNAL(detectionDone(bool)), this, SLOT(detected(bool)));
- detect_dlg->detect(Ui::RulesWidgetBase::detection_delay->value());
- Ui::RulesWidgetBase::detect->setEnabled(false);
-}
-
-void RulesWidget::detected(bool ok)
-{
- if (ok) {
- wmclass->setText(detect_dlg->selectedClass());
- wmclass_match->setCurrentIndex(Rules::ExactMatch);
- wmclassMatchChanged(); // grrr
- whole_wmclass->setChecked(detect_dlg->selectedWholeClass());
- role->setText(detect_dlg->selectedRole());
- role_match->setCurrentIndex(detect_dlg->selectedRole().isEmpty()
- ? Rules::UnimportantMatch : Rules::ExactMatch);
- roleMatchChanged();
- if (detect_dlg->selectedWholeApp()) {
- for (int i = 0;
- i < types->count();
- ++i)
- types->item(i)->setSelected(true);
- } else {
- NET::WindowType type = detect_dlg->selectedType();
- for (int i = 0;
- i < types->count();
- ++i)
- types->item(i)->setSelected(false);
- types->item(typeToCombo(type))->setSelected(true);
- }
- title->setText(detect_dlg->selectedTitle());
- title_match->setCurrentIndex(detect_dlg->titleMatch());
- titleMatchChanged();
- machine->setText(detect_dlg->selectedMachine());
- machine_match->setCurrentIndex(Rules::UnimportantMatch);
- machineMatchChanged();
- // prefill values from to window to settings which already set
- prefillUnusedValues(detect_dlg->windowInfo());
- }
- delete detect_dlg;
- detect_dlg = nullptr;
- detect_dlg_ok = ok;
- Ui::RulesWidgetBase::detect->setEnabled(true);
-}
-
-#define GENERIC_PREFILL( var, func, info, uimethod ) \
- if ( !enable_##var->isChecked()) \
- { \
- Ui::RulesWidgetBase::var->uimethod( func( info )); \
- }
-
-#define CHECKBOX_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setChecked )
-#define LINEEDIT_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setText )
-#define COMBOBOX_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setCurrentIndex )
-#define SPINBOX_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setValue )
-
-void RulesWidget::prefillUnusedValues(const QVariantMap& info)
-{
- const QSize windowSize{info.value("width").toInt(), info.value("height").toInt()};
- LINEEDIT_PREFILL(position, positionToStr, QPoint(info.value("x").toInt(), info.value("y").toInt()));
- LINEEDIT_PREFILL(size, sizeToStr, windowSize);
- COMBOBOX_PREFILL(desktop, desktopToCombo, info.value("x11DesktopNumber").toInt());
- // COMBOBOX_PREFILL(activity, activityToCombo, info.activity()); // TODO: ivan
- CHECKBOX_PREFILL(maximizehoriz, , info.value("maximizeHorizontal").toBool());
- CHECKBOX_PREFILL(maximizevert, , info.value("maximizeVertical").toBool());
- CHECKBOX_PREFILL(minimize, , info.value("minimized").toBool());
- CHECKBOX_PREFILL(shade, , info.value("shaded").toBool());
- CHECKBOX_PREFILL(fullscreen, , info.value("fullscreen").toBool());
- //COMBOBOX_PREFILL( placement, placementToCombo );
- CHECKBOX_PREFILL(above, , info.value("keepAbove").toBool());
- CHECKBOX_PREFILL(below, , info.value("keepBelow").toBool());
- CHECKBOX_PREFILL(noborder, , info.value("noBorder").toBool());
- CHECKBOX_PREFILL(skiptaskbar, , info.value("skipTaskbar").toBool());
- CHECKBOX_PREFILL(skippager, , info.value("skipPager").toBool());
- CHECKBOX_PREFILL(skipswitcher, , info.value("skipSwitcher").toBool());
- //CHECKBOX_PREFILL( acceptfocus, );
- //CHECKBOX_PREFILL( closeable, );
- //CHECKBOX_PREFILL( autogroup, );
- //CHECKBOX_PREFILL( autogroupfg, );
- //LINEEDIT_PREFILL( autogroupid, );
- SPINBOX_PREFILL(opacityactive, , 100 /*get the actual opacity somehow*/);
- SPINBOX_PREFILL(opacityinactive, , 100 /*get the actual opacity somehow*/);
- //LINEEDIT_PREFILL( shortcut, );
- //COMBOBOX_PREFILL( fsplevel, );
- //COMBOBOX_PREFILL( fpplevel, );
- COMBOBOX_PREFILL(type, typeToCombo, info.value("type").value());
- //CHECKBOX_PREFILL( ignoregeometry, );
- LINEEDIT_PREFILL(minsize, sizeToStr, windowSize);
- LINEEDIT_PREFILL(maxsize, sizeToStr, windowSize);
- //CHECKBOX_PREFILL( strictgeometry, );
- //CHECKBOX_PREFILL( disableglobalshortcuts, );
- //CHECKBOX_PREFILL( blockcompositing, );
- LINEEDIT_PREFILL(desktopfile, , info.value("desktopFile").toString());
-}
-
-#undef GENERIC_PREFILL
-#undef CHECKBOX_PREFILL
-#undef LINEEDIT_PREFILL
-#undef COMBOBOX_PREFILL
-#undef SPINBOX_PREFILL
-
-bool RulesWidget::finalCheck()
-{
- if (description->text().isEmpty()) {
- if (!wmclass->text().isEmpty())
- description->setText(i18n("Settings for %1", wmclass->text()));
- else
- description->setText(i18n("Unnamed entry"));
- }
- bool all_types = true;
- for (int i = 0;
- i < types->count();
- ++i)
- if (!types->item(i)->isSelected())
- all_types = false;
- if (wmclass_match->currentIndex() == Rules::UnimportantMatch && all_types) {
- if (KMessageBox::warningContinueCancel(window(),
- i18n("You have specified the window class as unimportant.\n"
- "This means the settings will possibly apply to windows from all applications. "
- "If you really want to create a generic setting, it is recommended you at least "
- "limit the window types to avoid special window types.")) != KMessageBox::Continue)
- return false;
- }
- return true;
-}
-
-void RulesWidget::prepareWindowSpecific(const QVariantMap &info)
-{
- tabs->setCurrentIndex(1); // geometry tab, skip tab for window identification
- prefillUnusedValues(info);
-}
-
-void RulesWidget::shortcutEditClicked()
-{
- QPointer dlg = new EditShortcutDialog(window());
- dlg->setShortcut(shortcut->text());
- if (dlg->exec() == QDialog::Accepted)
- shortcut->setText(dlg->shortcut());
- delete dlg;
-}
-
-RulesDialog::RulesDialog(QWidget* parent, const char* name)
- : QDialog(parent)
-{
- setObjectName(name);
- setModal(true);
- setWindowTitle(i18n("Edit Window-Specific Settings"));
- setWindowIcon(QIcon::fromTheme("preferences-system-windows-actions"));
-
- setLayout(new QVBoxLayout);
- widget = new RulesWidget(this);
- layout()->addWidget(widget);
-
- QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
- connect(buttons, SIGNAL(accepted()), SLOT(accept()));
- connect(buttons, SIGNAL(rejected()), SLOT(reject()));
- layout()->addWidget(buttons);
-}
-
-// window is set only for Alt+F3/Window-specific settings, because the dialog
-// is then related to one specific window
-Rules* RulesDialog::edit(Rules* r, const QVariantMap& info, bool show_hints)
-{
- rules = r;
- widget->setRules(rules);
- if (!info.isEmpty())
- {
- widget->prepareWindowSpecific(info);
- }
- if (show_hints)
- QTimer::singleShot(0, this, SLOT(displayHints()));
- exec();
- return rules;
-}
-
-void RulesDialog::displayHints()
-{
- QString str = "";
- str += i18n("This configuration dialog allows altering settings only for the selected window"
- " or application. Find the setting you want to affect, enable the setting using the checkbox,"
- " select in what way the setting should be affected and to which value.");
-#if 0 // maybe later
- str += "
" + i18n("Consult the documentation for more details.");
-#endif
- str += "
";
- KMessageBox::information(this, str, QString(), "displayhints");
-}
-
-void RulesDialog::accept()
-{
- if (!widget->finalCheck())
- return;
- rules = widget->rules();
- QDialog::accept();
-}
-
-EditShortcut::EditShortcut(QWidget* parent)
- : QWidget(parent)
-{
- setupUi(this);
-}
-
-void EditShortcut::editShortcut()
-{
- QPointer< ShortcutDialog > dlg = new ShortcutDialog(QKeySequence(shortcut->text()), window());
- if (dlg->exec() == QDialog::Accepted)
- shortcut->setText(dlg->shortcut().toString());
- delete dlg;
-}
-
-void EditShortcut::clearShortcut()
-{
- shortcut->clear();
-}
-
-EditShortcutDialog::EditShortcutDialog(QWidget* parent, const char* name)
- : QDialog(parent)
- , widget(new EditShortcut(this))
-{
- setObjectName(name);
- setModal(true);
- setWindowTitle(i18n("Edit Shortcut"));
-
- setLayout(new QVBoxLayout);
-
- QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
- connect(buttons, SIGNAL(accepted()), SLOT(accept()));
- connect(buttons, SIGNAL(rejected()), SLOT(reject()));
-
- layout()->addWidget(widget);
- layout()->addWidget(buttons);
-}
-
-void EditShortcutDialog::setShortcut(const QString& cut)
-{
- widget->shortcut->setText(cut);
-}
-
-QString EditShortcutDialog::shortcut() const
-{
- return widget->shortcut->text();
-}
-
-ShortcutDialog::ShortcutDialog(const QKeySequence& cut, QWidget* parent)
- : QDialog(parent)
- , widget(new KKeySequenceWidget(this))
-{
- widget->setKeySequence(cut);
- // It's a global shortcut so don't allow multikey shortcuts
- widget->setMultiKeyShortcutsAllowed(false);
-
- QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
- connect(buttons, SIGNAL(accepted()), SLOT(accept()));
- connect(buttons, SIGNAL(rejected()), SLOT(reject()));
-
- setLayout(new QVBoxLayout);
- layout()->addWidget(widget);
- layout()->addWidget(buttons);
-}
-
-void ShortcutDialog::accept()
-{
- QKeySequence seq = shortcut();
- if (!seq.isEmpty()) {
- if (seq[0] == Qt::Key_Escape) {
- reject();
- return;
- }
- if (seq[0] == Qt::Key_Space
- || (seq[0] & Qt::KeyboardModifierMask) == 0) {
- // clear
- widget->clearKeySequence();
- QDialog::accept();
- return;
- }
- }
- QDialog::accept();
-}
-
-QKeySequence ShortcutDialog::shortcut() const
-{
- return widget->keySequence();
-}
-
-} // namespace
-
diff --git a/kcmkwin/kwinrules/ruleswidgetbase.ui b/kcmkwin/kwinrules/ruleswidgetbase.ui
deleted file mode 100644
--- a/kcmkwin/kwinrules/ruleswidgetbase.ui
+++ /dev/null
@@ -1,2834 +0,0 @@
-
-
- KWin::RulesWidgetBase
-
-
-
- 0
- 0
- 592
- 588
-
-
-
- -
-
-
- 0
-
-
-
- &Window matching
-
-
-
-
-
-
- &Description:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- false
-
-
- description
-
-
-
- -
-
-
- -
-
-
- Window &class (application):
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- false
-
-
- wmclass
-
-
-
- -
-
-
-
-
- Unimportant
-
-
- -
-
- Exact Match
-
-
- -
-
- Substring Match
-
-
- -
-
- Regular Expression
-
-
-
-
- -
-
-
- -
-
-
- false
-
-
- Edit
-
-
-
-
-
-
- -
-
-
- Match w&hole window class
-
-
-
- -
-
-
- Window ro&le:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- false
-
-
- role
-
-
-
- -
-
-
-
-
- Unimportant
-
-
- -
-
- Exact Match
-
-
- -
-
- Substring Match
-
-
- -
-
- Regular Expression
-
-
-
-
- -
-
-
- -
-
-
- false
-
-
- Edit
-
-
-
- -
-
-
- Window &types:
-
-
- Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing
-
-
- false
-
-
- types
-
-
-
- -
-
-
- Window t&itle:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- false
-
-
- title
-
-
-
- -
-
-
-
-
- Unimportant
-
-
- -
-
- Exact Match
-
-
- -
-
- Substring Match
-
-
- -
-
- Regular Expression
-
-
-
-
- -
-
-
- false
-
-
- Edit
-
-
-
-
-
-
- -
-
-
- &Machine (hostname):
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- false
-
-
- machine
-
-
-
- -
-
-
-
-
- Unimportant
-
-
- -
-
- Exact Match
-
-
- -
-
- Substring Match
-
-
- -
-
- Regular Expression
-
-
-
-
- -
-
-
- false
-
-
- Edit
-
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- &Detect Window Properties
-
-
-
- -
-
-
- s delay
-
-
- 30
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- false
-
-
- Qt::ScrollBarAsNeeded
-
-
- Qt::ScrollBarAsNeeded
-
-
- true
-
-
- false
-
-
- QAbstractItemView::NoDragDrop
-
-
- false
-
-
- QAbstractItemView::ExtendedSelection
-
-
- QListView::Static
-
-
- QListView::TopToBottom
-
-
- true
-
-
- QListView::Adjust
-
-
- 0
-
-
- QListView::ListMode
-
-
- true
-
-
-
-
- Normal Window
-
-
- -
-
- Dialog Window
-
-
- -
-
- Utility Window
-
-
- -
-
- Dock (panel)
-
-
- -
-
- Toolbar
-
-
- -
-
- Torn-Off Menu
-
-
- -
-
- Splash Screen
-
-
- -
-
- Desktop
-
-
- -
-
- Unmanaged Window
-
-
- -
-
- Standalone Menubar
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
-
-
-
-
- &Size && Position
-
-
- -
-
-
- &Position
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
- x,y
-
-
- 0123456789-+,xX:
-
-
-
- -
-
-
- &Size
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
- width,height
-
-
- 0123456789-+,xX:
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- Maximized &horizontally
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Maximized &vertically
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- &Desktop
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Activit&y
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- false
-
-
- 1
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- &Fullscreen
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- M&inimized
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Sh&aded
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
-
- Default
-
-
- -
-
- No Placement
-
-
- -
-
- Minimal Overlapping
-
-
- -
-
- Maximized
-
-
- -
-
- Cascaded
-
-
- -
-
- Centered
-
-
- -
-
- Random
-
-
- -
-
- In Top-Left Corner
-
-
- -
-
- Under Mouse
-
-
- -
-
- On Main Window
-
-
-
-
- -
-
-
-
-
-
- Initial p&lacement
-
-
-
- -
-
-
- Windows can ask to appear in a certain position.
-By default this overrides the placement strategy
-what might be nasty if the client abuses the feature
-to unconditionally popup in the middle of your screen.
-
-
- Ignore requested &geometry
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- M&inimum size
-
-
-
- -
-
-
- false
-
-
- width,height
-
-
- 0123456789-+,xX:
-
-
-
- -
-
-
- M&aximum size
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
- width,height
-
-
- 0123456789-+,xX:
-
-
-
- -
-
-
- Eg. terminals or video players can ask to keep a certain aspect ratio
-or only grow by values larger than one
-(eg. by the dimensions of one character).
-This may be pointless and the restriction prevents arbitrary dimensions
-like your complete screen area.
-
-
- Qt::LeftToRight
-
-
- Obey geometry restrictions
-
-
-
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Qt::Vertical
-
-
- QSizePolicy::Expanding
-
-
-
- 20
- 16
-
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- Screen
-
-
-
-
-
-
-
- &Arrangement && Access
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- Window shall (not) appear in the manager for virtual desktops
-
-
- Skip pa&ger
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Window shall (not) appear in the taskbar.
-
-
- Skip &taskbar
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Window shall (not) appear in the Alt+Tab list
-
-
- Skip &switcher
-
-
-
- -
-
-
- false
-
-
- Edit...
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- Qt::Vertical
-
-
- QSizePolicy::Fixed
-
-
-
- 20
- 8
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Shortcut
-
-
-
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Keep &above
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Autog&roup in foreground
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- Keep &below
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- Autogroup by I&D
-
-
-
- -
-
-
- Autogroup with &identical
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
-
-
-
-
- Appearance && &Fixes
-
-
- -
-
-
- &No titlebar and frame
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Titlebar color &scheme
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- A&ctive opacity
-
-
-
- -
-
-
- false
-
-
- %
-
-
- 100
-
-
- 100
-
-
-
- -
-
-
- I&nactive opacity
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
- %
-
-
- 100
-
-
- 100
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- Qt::Vertical
-
-
- QSizePolicy::Fixed
-
-
-
- 20
- 8
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
- QSizePolicy::Fixed
-
-
-
- 20
- 8
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- KWin tries to prevent windows from taking the focus
-("activate") while you're working in another window,
-but this may sometimes fail or superact.
-"None" will unconditionally allow this window to get the focus while
-"Extreme" will completely prevent it from taking the focus.
-
-
- &Focus stealing prevention
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
-
- None
-
-
- -
-
- Low
-
-
- -
-
- Normal
-
-
- -
-
- High
-
-
- -
-
- Extreme
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- This controls the focus protection of the currently active window.
-None will always give the focus away,
-Extreme will keep it.
-Otherwise it's interleaved with the stealing prevention
-assigned to the window that wants the focus.
-
-
- Focus protection
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
-
- None
-
-
- -
-
- Low
-
-
- -
-
- Normal
-
-
- -
-
- High
-
-
- -
-
- Extreme
-
-
-
-
- -
-
-
- Windows may prevent to get the focus (activate) when being clicked.
-On the other hand you might wish to prevent a window
-from getting focused on a mouse click.
-
-
- Accept &focus
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- When used, a window will receive
-all keyboard inputs while it is active, including Alt+Tab etc.
-This is especially interesting for emulators or virtual machines.
-
-Be warned:
-you won't be able to Alt+Tab out of the window
-nor use any other global shortcut (such as Alt+F2 to show KRunner)
-while it's active!
-
-
- Ignore global shortcuts
-
-
-
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- &Closeable
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Window &type
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
-
- Normal Window
-
-
- -
-
- Dialog Window
-
-
- -
-
- Utility Window
-
-
- -
-
- Dock (panel)
-
-
- -
-
- Toolbar
-
-
- -
-
- Torn-Off Menu
-
-
- -
-
- Splash Screen
-
-
- -
-
- Desktop
-
-
- -
-
- Standalone Menubar
-
-
-
-
- -
-
-
- Desktop file name
-
-
-
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Apply Initially
-
-
- -
-
- Remember
-
-
- -
-
- Force
-
-
- -
-
- Apply Now
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
- org.kde.kwin
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- Block compositing
-
-
-
- -
-
-
- false
-
-
-
-
- Do Not Affect
-
-
- -
-
- Force
-
-
- -
-
- Force Temporarily
-
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
-
-
-
-
-
-
-
- KLineEdit
- QLineEdit
-
-
-
- KComboBox
- QComboBox
-
-
-
- YesNoBox
- QWidget
-
- 1
-
-
-
- description
- detect
- detection_delay
- wmclass_match
- wmclass
- edit_reg_wmclass
- whole_wmclass
- role_match
- role
- edit_reg_role
- types
- title_match
- title
- edit_reg_title
- machine_match
- machine
- edit_reg_machine
- enable_position
- rule_position
- position
- enable_size
- rule_size
- size
- enable_maximizehoriz
- rule_maximizehoriz
- enable_maximizevert
- rule_maximizevert
- enable_desktop
- rule_desktop
- desktop
- enable_activity
- rule_activity
- activity
- enable_screen
- rule_screen
- screen
- enable_fullscreen
- rule_fullscreen
- enable_minimize
- rule_minimize
- enable_shade
- rule_shade
- enable_placement
- rule_placement
- placement
- enable_ignoregeometry
- rule_ignoregeometry
- enable_minsize
- rule_minsize
- minsize
- enable_maxsize
- rule_maxsize
- maxsize
- enable_strictgeometry
- rule_strictgeometry
- enable_above
- rule_above
- enable_below
- rule_below
- enable_autogroup
- rule_autogroup
- enable_autogroupfg
- rule_autogroupfg
- enable_autogroupid
- rule_autogroupid
- autogroupid
- enable_skiptaskbar
- rule_skiptaskbar
- enable_skippager
- rule_skippager
- enable_skipswitcher
- rule_skipswitcher
- enable_shortcut
- rule_shortcut
- shortcut
- shortcut_edit
- enable_noborder
- rule_noborder
- enable_decocolor
- rule_decocolor
- decocolor
- enable_opacityactive
- rule_opacityactive
- opacityactive
- enable_opacityinactive
- rule_opacityinactive
- opacityinactive
- enable_fsplevel
- rule_fsplevel
- fsplevel
- enable_fpplevel
- rule_fpplevel
- fpplevel
- enable_acceptfocus
- rule_acceptfocus
- enable_disableglobalshortcuts
- rule_disableglobalshortcuts
- enable_closeable
- rule_closeable
- enable_type
- rule_type
- type
- enable_desktopfile
- rule_desktopfile
- desktopfile
- enable_blockcompositing
- rule_blockcompositing
- tabs
-
-
-
-
- detect
- clicked()
- KWin::RulesWidgetBase
- detectClicked()
-
-
- 285
- 124
-
-
- 20
- 20
-
-
-
-
- wmclass_match
- activated(int)
- KWin::RulesWidgetBase
- wmclassMatchChanged()
-
-
- 297
- 196
-
-
- 20
- 20
-
-
-
-
- role_match
- activated(int)
- KWin::RulesWidgetBase
- roleMatchChanged()
-
-
- 297
- 254
-
-
- 20
- 20
-
-
-
-
- title_match
- activated(int)
- KWin::RulesWidgetBase
- titleMatchChanged()
-
-
- 231
- 482
-
-
- 242
- 293
-
-
-
-
- machine_match
- activated(int)
- KWin::RulesWidgetBase
- machineMatchChanged()
-
-
- 194
- 509
-
-
- 242
- 293
-
-
-
-
-
diff --git a/kcmkwin/kwinrules/yesnobox.h b/kcmkwin/kwinrules/yesnobox.h
deleted file mode 100644
--- a/kcmkwin/kwinrules/yesnobox.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2011 Thomas Lübking
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-
-#ifndef YESNOBOX_H
-#define YESNOBOX_H
-
-#include
-#include
-
-#include
-
-class YesNoBox : public QWidget {
- Q_OBJECT
-public:
- explicit YesNoBox( QWidget *parent );
- bool isChecked() { return yes->isChecked(); }
-public Q_SLOTS:
- void setChecked(bool b) { yes->setChecked(b); }
- void toggle() { yes->toggle(); }
-
-Q_SIGNALS:
- void clicked(bool checked = false);
- void toggled(bool checked);
-private Q_SLOTS:
- void noClicked(bool checked) { emit clicked(!checked); }
-private:
- QRadioButton *yes, *no;
-};
-
-#endif // YESNOBOX_H
diff --git a/kcmkwin/kwinrules/yesnobox.cpp b/kcmkwin/kwinrules/yesnobox.cpp
deleted file mode 100644
--- a/kcmkwin/kwinrules/yesnobox.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2011 Thomas Lübking
- *
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "yesnobox.h"
-
-YesNoBox::YesNoBox( QWidget *parent )
- : QWidget(parent)
-{
- QHBoxLayout *l = new QHBoxLayout(this);
- l->setContentsMargins(0, 0, 0, 0);
- l->addWidget(yes = new QRadioButton(i18n("Yes"), this));
- l->addWidget(no = new QRadioButton(i18n("No"), this));
- l->addStretch(100);
- no->setChecked(true);
- connect(yes, SIGNAL(clicked(bool)), this, SIGNAL(clicked(bool)));
- connect(yes, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool)));
- connect(no, SIGNAL(clicked(bool)), this, SLOT(noClicked(bool)));
-}