diff --git a/src/kcmkwin/kwinrules/CMakeLists.txt b/src/kcmkwin/kwinrules/CMakeLists.txt
index c68f7bdca5..64b19cec58 100644
--- a/src/kcmkwin/kwinrules/CMakeLists.txt
+++ b/src/kcmkwin/kwinrules/CMakeLists.txt
@@ -16,6 +16,7 @@ set(kwinrules_SRCS
optionsmodel.cpp
ruleitem.cpp
rulesmodel.cpp
+ rulebookmodel.cpp
)
kconfig_add_kcfg_files(kwinrules_SRCS ../../rulesettings.kcfgc)
@@ -33,6 +34,7 @@ set(kcm_libs
Qt::Quick
Qt::QuickWidgets
+ KF5::KCMUtils
KF5::I18n
KF5::QuickAddons
KF5::WindowSystem
@@ -44,11 +46,11 @@ if (KWIN_BUILD_ACTIVITIES)
endif()
target_link_libraries(KWinRulesObjects ${kcm_libs} ${kwin_kcm_rules_XCB_LIBS})
-add_executable(kwin_rules_dialog main.cpp rulesdialog.cpp)
+add_executable(kwin_rules_dialog main.cpp)
target_link_libraries(kwin_rules_dialog KWinRulesObjects)
install(TARGETS kwin_rules_dialog DESTINATION ${KDE_INSTALL_LIBEXECDIR})
-add_library(kcm_kwinrules MODULE kcmrules.cpp rulebookmodel.cpp)
+add_library(kcm_kwinrules MODULE kcmrules.cpp)
target_link_libraries(kcm_kwinrules KWinRulesObjects)
kcoreaddons_desktop_to_json(kcm_kwinrules "kcm_kwinrules.desktop" SERVICE_TYPES kcmodule.desktop)
diff --git a/src/kcmkwin/kwinrules/kcmrules.cpp b/src/kcmkwin/kwinrules/kcmrules.cpp
index 8eab547d80..bc47099687 100644
--- a/src/kcmkwin/kwinrules/kcmrules.cpp
+++ b/src/kcmkwin/kwinrules/kcmrules.cpp
@@ -40,6 +40,12 @@ KCMKWinRules::KCMKWinRules(QObject *parent, const QVariantList &arguments)
" KWin as your window manager. If you do use a different window manager, please refer to its documentation"
" for how to customize window behavior.
"));
+ QStringList argList;
+ for (const QVariant &arg : arguments) {
+ argList << arg.toString();
+ }
+ parseArguments(argList);
+
connect(m_rulesModel, &RulesModel::descriptionChanged, this, [this]{
if (m_editIndex.isValid()) {
m_ruleBookModel->setDescriptionAt(m_editIndex.row(), m_rulesModel->description());
@@ -49,14 +55,69 @@ KCMKWinRules::KCMKWinRules(QObject *parent, const QVariantList &arguments)
connect(m_ruleBookModel, &RulesModel::dataChanged, this, &KCMKWinRules::updateNeedsSave);
}
+void KCMKWinRules::parseArguments(const QStringList &args)
+{
+ // When called from window menu, "uuid" and "whole-app" are set in arguments list
+ bool nextArgIsUuid = false;
+ QUuid uuid = QUuid();
+
+ // TODO: Use a better argument parser
+ for (const QString &arg : args) {
+ if (arg == QLatin1String("uuid")) {
+ nextArgIsUuid = true;
+ } else if (nextArgIsUuid) {
+ uuid = QUuid(arg);
+ nextArgIsUuid = false;
+ } else if (arg.startsWith("uuid=")) {
+ uuid = arg.mid(strlen("uuid="));
+ } else if (arg == QLatin1String("whole-app")) {
+ m_wholeApp = true;
+ }
+ }
+
+ if (uuid.isNull()) {
+ qDebug() << "Invalid window uuid.";
+ return;
+ }
+
+ // Get the Window properties
+ QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"),
+ QStringLiteral("/KWin"),
+ QStringLiteral("org.kde.KWin"),
+ QStringLiteral("getWindowInfo"));
+ message.setArguments({uuid.toString()});
+ QDBusPendingReply async = QDBusConnection::sessionBus().asyncCall(message);
+
+ QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this);
+ connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this, uuid](QDBusPendingCallWatcher *self) {
+ QDBusPendingReply reply = *self;
+ self->deleteLater();
+ if (!reply.isValid() || reply.value().isEmpty()) {
+ qDebug() << "Error retrieving properties for window" << uuid;
+ return;
+ }
+ qDebug() << "Retrieved properties for window" << uuid;
+ m_winProperties = reply.value();
+
+ if (m_alreadyLoaded) {
+ createRuleFromProperties();
+ }
+ });
+}
+
void KCMKWinRules::load()
{
m_ruleBookModel->load();
-
- m_editIndex = QModelIndex();
- emit editIndexChanged();
-
setNeedsSave(false);
+
+ if (!m_winProperties.isEmpty() && !m_alreadyLoaded) {
+ createRuleFromProperties();
+ } else {
+ m_editIndex = QModelIndex();
+ emit editIndexChanged();
+ }
+
+ m_alreadyLoaded = true;
}
void KCMKWinRules::save()
@@ -76,6 +137,26 @@ void KCMKWinRules::updateNeedsSave()
emit needsSaveChanged();
}
+void KCMKWinRules::createRuleFromProperties()
+{
+ if (m_winProperties.isEmpty()) {
+ return;
+ }
+
+ QModelIndex matchedIndex = m_ruleBookModel->findRuleWithProperties(m_winProperties, m_wholeApp);
+ if (!matchedIndex.isValid()) {
+ m_ruleBookModel->insertRow(0);
+ m_ruleBookModel->setRuleAt(0, ruleForProperties(m_winProperties, m_wholeApp));
+ matchedIndex = m_ruleBookModel->index(0);
+ updateNeedsSave();
+ }
+
+ editRule(matchedIndex.row());
+ m_rulesModel->setSuggestedProperties(m_winProperties);
+
+ m_winProperties.clear();
+}
+
void KCMKWinRules::saveCurrentRule()
{
if (m_editIndex.isValid() && needsSave()) {
@@ -249,6 +330,84 @@ void KCMKWinRules::importFromFile(const QUrl &path)
updateNeedsSave();
}
+// Code adapted from original `findRule()` method in `kwin_rules_dialog::main.cpp`
+Rules *KCMKWinRules::ruleForProperties(const QVariantMap &windowProperties, bool wholeApp) const
+{
+ const QByteArray wmclass_class = windowProperties.value("resourceClass").toByteArray().toLower();
+ const QByteArray wmclass_name = windowProperties.value("resourceName").toByteArray().toLower();
+ const QByteArray role = windowProperties.value("role").toByteArray().toLower();
+ const NET::WindowType type = static_cast(windowProperties.value("type").toInt());
+ const QString title = windowProperties.value("caption").toString();
+ const QByteArray machine = windowProperties.value("clientMachine").toByteArray();
+
+ Rules *rule = new Rules();
+
+ if (wholeApp) {
+ rule->description = i18n("Application settings for %1", QString::fromLatin1(wmclass_class));
+ // TODO maybe exclude some types? If yes, then also exclude them when searching.
+ rule->types = NET::AllTypesMask;
+ rule->titlematch = Rules::UnimportantMatch;
+ rule->clientmachine = machine; // set, but make unimportant
+ rule->clientmachinematch = Rules::UnimportantMatch;
+ rule->windowrolematch = Rules::UnimportantMatch;
+ if (wmclass_name == wmclass_class) {
+ rule->wmclasscomplete = false;
+ rule->wmclass = wmclass_class;
+ rule->wmclassmatch = Rules::ExactMatch;
+ } else {
+ // WM_CLASS components differ - perhaps the app got -name argument
+ rule->wmclasscomplete = true;
+ rule->wmclass = wmclass_name + ' ' + wmclass_class;
+ rule->wmclassmatch = Rules::ExactMatch;
+ }
+ return rule;
+ }
+
+ rule->description = i18n("Window settings for %1", QString::fromLatin1(wmclass_class));
+ if (type == NET::Unknown) {
+ rule->types = NET::NormalMask;
+ } else {
+ rule->types = NET::WindowTypeMask(1 << type); // convert type to its mask
+ }
+ rule->title = title; // set, but make unimportant
+ rule->titlematch = Rules::UnimportantMatch;
+ rule->clientmachine = machine; // set, but make unimportant
+ rule->clientmachinematch = Rules::UnimportantMatch;
+ if (!role.isEmpty() && role != "unknown" && role != "unnamed") { // Qt sets this if not specified
+ rule->windowrole = role;
+ rule->windowrolematch = Rules::ExactMatch;
+ if (wmclass_name == wmclass_class) {
+ rule->wmclasscomplete = false;
+ rule->wmclass = wmclass_class;
+ rule->wmclassmatch = Rules::ExactMatch;
+ } else {
+ // WM_CLASS components differ - perhaps the app got -name argument
+ rule->wmclasscomplete = true;
+ rule->wmclass = wmclass_name + ' ' + wmclass_class;
+ rule->wmclassmatch = Rules::ExactMatch;
+ }
+ } else { // no role set
+ if (wmclass_name != wmclass_class) {
+ rule->wmclasscomplete = true;
+ rule->wmclass = wmclass_name + ' ' + wmclass_class;
+ rule->wmclassmatch = Rules::ExactMatch;
+ } else {
+ // This is a window that has no role set, and both components of WM_CLASS
+ // match (possibly only differing in case), which most likely means either
+ // the application doesn't give a damn about distinguishing its various
+ // windows, or it's an app that uses role for that, but this window
+ // lacks it for some reason. Use non-complete WM_CLASS matching, also
+ // include window title in the matching, and pray it causes many more positive
+ // matches than negative matches.
+ rule->titlematch = Rules::ExactMatch;
+ rule->wmclasscomplete = false;
+ rule->wmclass = wmclass_class;
+ rule->wmclassmatch = Rules::ExactMatch;
+ }
+ }
+ return rule;
+}
+
K_PLUGIN_CLASS_WITH_JSON(KCMKWinRules, "kcm_kwinrules.json");
} // namespace
diff --git a/src/kcmkwin/kwinrules/kcmrules.h b/src/kcmkwin/kwinrules/kcmrules.h
index b41c36cede..6f96eea8c2 100644
--- a/src/kcmkwin/kwinrules/kcmrules.h
+++ b/src/kcmkwin/kwinrules/kcmrules.h
@@ -51,12 +51,19 @@ private slots:
private:
int editIndex() const;
void saveCurrentRule();
+ void parseArguments(const QStringList &args);
+ void createRuleFromProperties();
+ Rules *ruleForProperties(const QVariantMap &windowProperties, bool wholeApp) const;
private:
RuleBookModel *m_ruleBookModel;
RulesModel* m_rulesModel;
QPersistentModelIndex m_editIndex;
+
+ bool m_alreadyLoaded = false;
+ QVariantMap m_winProperties;
+ bool m_wholeApp = false;
};
} // namespace
diff --git a/src/kcmkwin/kwinrules/main.cpp b/src/kcmkwin/kwinrules/main.cpp
index 75bea63992..70929bbfc9 100644
--- a/src/kcmkwin/kwinrules/main.cpp
+++ b/src/kcmkwin/kwinrules/main.cpp
@@ -1,191 +1,18 @@
/*
SPDX-FileCopyrightText: 2004 Lubos Lunak
+ SPDX-FileCopyrightText: 2018 Nicolas Fella
+ SPDX-FileCopyrightText: 2020 Ismael Asensio
- SPDX-License-Identifier: GPL-2.0-or-later
+ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
-#include
#include
-#include
-#include
-#include
-
-#include "rulebooksettings.h"
-#include "rulesdialog.h"
-#include "../../rules.h"
-#include
-
-#include
-#include
-#include
-#include
+#include
+#include
#include
-Q_DECLARE_METATYPE(NET::WindowType)
-
-namespace KWin
-{
-
-static Rules *findRule(const QVector &rules, const QVariantMap &data, bool whole_app)
-{
- QByteArray wmclass_class = data.value("resourceClass").toByteArray().toLower();
- QByteArray wmclass_name = data.value("resourceName").toByteArray().toLower();
- QByteArray role = data.value("role").toByteArray().toLower();
- NET::WindowType type = data.value("type").value();
- QString title = data.value("caption").toString();
- QByteArray machine = data.value("clientMachine").toByteArray();
- Rules* best_match = nullptr;
- int match_quality = 0;
- for (const auto rule : rules) {
- // try to find an exact match, i.e. not a generic rule
- int quality = 0;
- bool generic = true;
- if (rule->wmclassmatch != Rules::ExactMatch)
- continue; // too generic
- if (!rule->matchWMClass(wmclass_class, wmclass_name))
- continue;
- // from now on, it matches the app - now try to match for a specific window
- if (rule->wmclasscomplete) {
- quality += 1;
- generic = false; // this can be considered specific enough (old X apps)
- }
- if (!whole_app) {
- if (rule->windowrolematch != Rules::UnimportantMatch) {
- quality += rule->windowrolematch == Rules::ExactMatch ? 5 : 1;
- generic = false;
- }
- if (rule->titlematch != Rules::UnimportantMatch) {
- quality += rule->titlematch == Rules::ExactMatch ? 3 : 1;
- generic = false;
- }
- if (rule->types != NET::AllTypesMask) {
- int bits = 0;
- for (unsigned int bit = 1;
- bit < 1U << 31;
- bit <<= 1)
- if (rule->types & bit)
- ++bits;
- if (bits == 1)
- quality += 2;
- }
- if (generic) // ignore generic rules, use only the ones that are for this window
- continue;
- } else {
- if (rule->types == NET::AllTypesMask)
- quality += 2;
- }
- if (!rule->matchType(type)
- || !rule->matchRole(role)
- || !rule->matchTitle(title)
- || !rule->matchClientMachine(machine, data.value("localhost").toBool()))
- continue;
- if (quality > match_quality) {
- best_match = rule;
- match_quality = quality;
- }
- }
- if (best_match != nullptr)
- return best_match;
- Rules* ret = new Rules;
- if (whole_app) {
- ret->description = i18n("Application settings for %1", QString::fromLatin1(wmclass_class));
- // TODO maybe exclude some types? If yes, then also exclude them above
- // when searching.
- ret->types = NET::AllTypesMask;
- ret->titlematch = Rules::UnimportantMatch;
- ret->clientmachine = machine; // set, but make unimportant
- ret->clientmachinematch = Rules::UnimportantMatch;
- ret->windowrolematch = Rules::UnimportantMatch;
- if (wmclass_name == wmclass_class) {
- ret->wmclasscomplete = false;
- ret->wmclass = wmclass_class;
- ret->wmclassmatch = Rules::ExactMatch;
- } else {
- // WM_CLASS components differ - perhaps the app got -name argument
- ret->wmclasscomplete = true;
- ret->wmclass = wmclass_name + ' ' + wmclass_class;
- ret->wmclassmatch = Rules::ExactMatch;
- }
- return ret;
- }
- ret->description = i18n("Window settings for %1", QString::fromLatin1(wmclass_class));
- if (type == NET::Unknown)
- ret->types = NET::NormalMask;
- else
- ret->types = NET::WindowTypeMask( 1 << type); // convert type to its mask
- ret->title = title; // set, but make unimportant
- ret->titlematch = Rules::UnimportantMatch;
- ret->clientmachine = machine; // set, but make unimportant
- ret->clientmachinematch = Rules::UnimportantMatch;
- if (!role.isEmpty()
- && role != "unknown" && role != "unnamed") { // Qt sets this if not specified
- ret->windowrole = role;
- ret->windowrolematch = Rules::ExactMatch;
- if (wmclass_name == wmclass_class) {
- ret->wmclasscomplete = false;
- ret->wmclass = wmclass_class;
- ret->wmclassmatch = Rules::ExactMatch;
- } else {
- // WM_CLASS components differ - perhaps the app got -name argument
- ret->wmclasscomplete = true;
- ret->wmclass = wmclass_name + ' ' + wmclass_class;
- ret->wmclassmatch = Rules::ExactMatch;
- }
- } else { // no role set
- if (wmclass_name != wmclass_class) {
- ret->wmclasscomplete = true;
- ret->wmclass = wmclass_name + ' ' + wmclass_class;
- ret->wmclassmatch = Rules::ExactMatch;
- } else {
- // This is a window that has no role set, and both components of WM_CLASS
- // match (possibly only differing in case), which most likely means either
- // the application doesn't give a damn about distinguishing its various
- // windows, or it's an app that uses role for that, but this window
- // lacks it for some reason. Use non-complete WM_CLASS matching, also
- // include window title in the matching, and pray it causes many more positive
- // matches than negative matches.
- ret->titlematch = Rules::ExactMatch;
- ret->wmclasscomplete = false;
- ret->wmclass = wmclass_class;
- ret->wmclassmatch = Rules::ExactMatch;
- }
- }
- return ret;
-}
-
-static void edit(const QVariantMap &data, bool whole_app)
-{
- RuleBookSettings settings(KConfig::NoGlobals);
- QVector rules = settings.rules();
- Rules *orig_rule = findRule(rules, data, whole_app);
- RulesDialog dlg;
- if (whole_app)
- dlg.setWindowTitle(i18nc("Window caption for the application wide rules dialog", "Edit Application-Specific Settings"));
- // dlg.edit() creates new Rules instance if edited
- Rules* edited_rule = dlg.edit(orig_rule, data, true);
- if (edited_rule == nullptr || edited_rule->isEmpty()) {
- rules.removeAll(orig_rule);
- delete orig_rule;
- if (orig_rule != edited_rule)
- delete edited_rule;
- } else if (edited_rule != orig_rule) {
- int pos = rules.indexOf(orig_rule);
- if (pos != -1)
- rules[ pos ] = edited_rule;
- else
- rules.prepend(edited_rule);
- delete orig_rule;
- }
- settings.setRules(rules);
- settings.save();
- // Send signal to all kwin instances
- QDBusMessage message =
- QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
- QDBusConnection::sessionBus().send(message);
- qApp->quit();
-}
-
-} // namespace
+#include
+#include
int main(int argc, char* argv[])
{
@@ -194,48 +21,39 @@ int main(int argc, char* argv[])
KLocalizedString::setApplicationDomain("kcm_kwinrules");
app.setAttribute(Qt::AA_UseHighDpiPixmaps, true);
- app.setApplicationDisplayName(i18n("KWin"));
app.setApplicationName("kwin_rules_dialog");
- app.setApplicationVersion("1.0");
- bool whole_app = false;
- QUuid uuid;
- {
- QCommandLineParser parser;
- parser.setApplicationDescription(i18n("KWin helper utility"));
- parser.addOption(QCommandLineOption("uuid", i18n("KWin id of the window for special window settings."), "uuid"));
- parser.addOption(QCommandLineOption("whole-app", i18n("Whether the settings should affect all windows of the application.")));
- parser.process(app);
+ app.setWindowIcon(QIcon::fromTheme("preferences-system-windows-actions"));
+ app.setApplicationVersion("2.0");
- uuid = QUuid::fromString(parser.value("uuid"));
- whole_app = parser.isSet("whole-app");
- }
+ QCommandLineParser parser;
+ parser.setApplicationDescription(i18n("KWinRules KCM launcher"));
+ parser.addOption(QCommandLineOption("uuid", i18n("KWin id of the window for special window settings."), "uuid"));
+ parser.addOption(QCommandLineOption("whole-app", i18n("Whether the settings should affect all windows of the application.")));
+ parser.process(app);
+ const QUuid uuid = QUuid::fromString(parser.value("uuid"));
+ const bool whole_app = parser.isSet("whole-app");
if (uuid.isNull()) {
printf("%s\n", qPrintable(i18n("This helper utility is not supposed to be called directly.")));
return 1;
}
- QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"),
- QStringLiteral("/KWin"),
- QStringLiteral("org.kde.KWin"),
- QStringLiteral("getWindowInfo"));
- message.setArguments({uuid.toString()});
- QDBusPendingReply async = QDBusConnection::sessionBus().asyncCall(message);
- QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, &app);
- QObject::connect(callWatcher, &QDBusPendingCallWatcher::finished, &app,
- [&whole_app] (QDBusPendingCallWatcher *self) {
- QDBusPendingReply reply = *self;
- self->deleteLater();
- if (!reply.isValid() || reply.value().isEmpty()) {
- qApp->quit();
- return;
- }
- KWin::edit(reply.value(), whole_app);
- }
- );
+ app.setApplicationDisplayName((whole_app) ? i18nc("Window caption for the application wide rules dialog", "Edit Application-Specific Settings")
+ : i18n("Edit Window-Specific Settings"));
+ QStringList kcm_args;
+ kcm_args << QStringLiteral("uuid=%1").arg(uuid.toString());
+ if (whole_app) {
+ kcm_args << QStringLiteral("whole-app");
+ }
+ KCMultiDialog *dialog = new KCMultiDialog;
+ dialog->addModule(QStringLiteral("kcm_kwinrules"), kcm_args);
+ dialog->setAttribute(Qt::WA_DeleteOnClose);
+ dialog->show();
+
+ app.setQuitOnLastWindowClosed(true);
return app.exec();
}
diff --git a/src/kcmkwin/kwinrules/package/contents/ui/RulesEditor.qml b/src/kcmkwin/kwinrules/package/contents/ui/RulesEditor.qml
index 97b4d236be..9521e0a86f 100644
--- a/src/kcmkwin/kwinrules/package/contents/ui/RulesEditor.qml
+++ b/src/kcmkwin/kwinrules/package/contents/ui/RulesEditor.qml
@@ -16,9 +16,7 @@ import org.kde.kcms.kwinrules 1.0
ScrollViewKCM {
id: rulesEditor
- property var rulesModel: kcm.rulesModel
-
- title: rulesModel.description
+ title: kcm.rulesModel.description
view: ListView {
id: rulesView
@@ -73,7 +71,7 @@ ScrollViewKCM {
visible: warningList.count > 0
Repeater {
id: warningList
- model: rulesModel.warningMessages
+ model: kcm.rulesModel.warningMessages
delegate: Kirigami.InlineMessage {
text: modelData
@@ -104,8 +102,8 @@ ScrollViewKCM {
enabled: !propertySheet.sheetOpen && !errorSheet.sheetOpen
onClicked: {
overlayModel.onlySuggestions = true;
- rulesModel.detectWindowProperties(Math.max(delaySpin.value * 1000,
- Kirigami.Units.shortDuration));
+ kcm.rulesModel.detectWindowProperties(Math.max(delaySpin.value * 1000,
+ Kirigami.Units.shortDuration));
}
}
QQC2.SpinBox {
@@ -123,7 +121,7 @@ ScrollViewKCM {
}
Connections {
- target: rulesModel
+ target: kcm.rulesModel
function onShowSuggestions() {
overlayModel.onlySuggestions = true;
propertySheet.sheetOpen = true;
@@ -268,7 +266,7 @@ ScrollViewKCM {
KSortFilterProxyModel {
id: enabledRulesModel
- sourceModel: rulesModel
+ sourceModel: kcm.rulesModel
filterRowCallback: (source_row, source_parent) => {
var index = sourceModel.index(source_row, 0, source_parent);
return sourceModel.data(index, RulesModel.EnabledRole);
@@ -277,7 +275,7 @@ ScrollViewKCM {
KSortFilterProxyModel {
id: overlayModel
- sourceModel: rulesModel
+ sourceModel: kcm.rulesModel
property bool onlySuggestions: false
onOnlySuggestionsChanged: {
diff --git a/src/kcmkwin/kwinrules/rulebookmodel.cpp b/src/kcmkwin/kwinrules/rulebookmodel.cpp
index 167a7d29e6..54e5534721 100644
--- a/src/kcmkwin/kwinrules/rulebookmodel.cpp
+++ b/src/kcmkwin/kwinrules/rulebookmodel.cpp
@@ -1,4 +1,5 @@
/*
+ SPDX-FileCopyrightText: 2004 Lubos Lunak
SPDX-FileCopyrightText: 2020 Ismael Asensio
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
@@ -6,6 +7,7 @@
#include "rulebookmodel.h"
+#include
namespace KWin
{
@@ -170,4 +172,88 @@ void RuleBookModel::save()
m_ruleBook->save();
}
+// Code adapted from original `findRule()` method in `kwin_rules_dialog::main.cpp`
+QModelIndex RuleBookModel::findRuleWithProperties(const QVariantMap &info, bool wholeApp) const
+{
+ const QByteArray wmclass_class = info.value("resourceClass").toByteArray().toLower();
+ const QByteArray wmclass_name = info.value("resourceName").toByteArray().toLower();
+ const QByteArray role = info.value("role").toByteArray().toLower();
+ const NET::WindowType type = static_cast(info.value("type").toInt());
+ const QString title = info.value("caption").toString();
+ const QByteArray machine = info.value("clientMachine").toByteArray();
+ const bool isLocalHost = info.value("localhost").toBool();
+
+ int bestMatchRow = -1;
+ int match_quality = 0;
+
+ for (int row = 0; row < m_rules.count(); row++) {
+ Rules *rule = m_rules.at(row);
+
+ /* clang-format off */
+ // If the rule doesn't match try the next one
+ if (!rule->matchWMClass(wmclass_class, wmclass_name)
+ || !rule->matchType(type)
+ || !rule->matchRole(role)
+ || !rule->matchTitle(title)
+ || !rule->matchClientMachine(machine, isLocalHost)) {
+ continue;
+ }
+ /* clang-format on */
+
+ if (rule->wmclassmatch != Rules::ExactMatch) {
+ continue; // too generic
+ }
+
+ // Now that the rule matches the window, check the quality of the match
+ // It stablishes a quality depending on the match policy of the rule
+ int quality = 0;
+ bool generic = true;
+
+ // from now on, it matches the app - now try to match for a specific window
+ if (rule->wmclasscomplete) {
+ quality += 1;
+ generic = false; // this can be considered specific enough (old X apps)
+ }
+ if (!wholeApp) {
+ if (rule->windowrolematch != Rules::UnimportantMatch) {
+ quality += rule->windowrolematch == Rules::ExactMatch ? 5 : 1;
+ generic = false;
+ }
+ if (rule->titlematch != Rules::UnimportantMatch) {
+ quality += rule->titlematch == Rules::ExactMatch ? 3 : 1;
+ generic = false;
+ }
+ if (rule->types != NET::AllTypesMask) {
+ // Checks that type fits the mask, and only one of the types
+ int bits = 0;
+ for (unsigned int bit = 1; bit < 1U << 31; bit <<= 1) {
+ if (rule->types & bit) {
+ ++bits;
+ }
+ }
+ if (bits == 1) {
+ quality += 2;
+ }
+ }
+ if (generic) { // ignore generic rules, use only the ones that are for this window
+ continue;
+ }
+ } else {
+ if (rule->types == NET::AllTypesMask) {
+ quality += 2;
+ }
+ }
+
+ if (quality > match_quality) {
+ bestMatchRow = row;
+ match_quality = quality;
+ }
+ }
+
+ if (bestMatchRow < 0) {
+ return QModelIndex();
+ }
+ return index(bestMatchRow);
+}
+
} // namespace
diff --git a/src/kcmkwin/kwinrules/rulebookmodel.h b/src/kcmkwin/kwinrules/rulebookmodel.h
index aaf39378d2..1dde70db4a 100644
--- a/src/kcmkwin/kwinrules/rulebookmodel.h
+++ b/src/kcmkwin/kwinrules/rulebookmodel.h
@@ -42,6 +42,8 @@ public:
void load();
void save();
+ QModelIndex findRuleWithProperties(const QVariantMap &info, bool wholeApp) const;
+
private:
RuleBookSettings *m_ruleBook;
QVector m_rules;
diff --git a/src/kcmkwin/kwinrules/rulesdialog.cpp b/src/kcmkwin/kwinrules/rulesdialog.cpp
deleted file mode 100644
index c59a473411..0000000000
--- a/src/kcmkwin/kwinrules/rulesdialog.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- SPDX-FileCopyrightText: 2004 Lubos Lunak
- SPDX-FileCopyrightText: 2020 Ismael Asensio
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#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, &QDialogButtonBox::accepted, this, &QDialog::accept);
- connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::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->setSuggestedProperties(info);
- }
-
- exec();
-
- return m_rules;
-}
-
-void RulesDialog::accept()
-{
- m_rules = m_rulesModel->exportToRules();
- QDialog::accept();
-}
-
-}
diff --git a/src/kcmkwin/kwinrules/rulesdialog.h b/src/kcmkwin/kwinrules/rulesdialog.h
deleted file mode 100644
index 78c4447055..0000000000
--- a/src/kcmkwin/kwinrules/rulesdialog.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- SPDX-FileCopyrightText: 2004 Lubos Lunak
- SPDX-FileCopyrightText: 2020 Ismael Asensio
-
- SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#ifndef KWIN_RULESDIALOG_H
-#define KWIN_RULESDIALOG_H
-
-#include "rulesmodel.h"
-#include "../../rules.h"
-
-#include
-
-namespace KWin
-{
-
-class Rules;
-
-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:
- RulesModel* m_rulesModel;
- QWidget *m_quickWidget;
- Rules* m_rules;
-};
-
-} // namespace
-
-#endif // KWIN_RULESDIALOG_H