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