diff --git a/kcmkwin/kwinrules/ruleitem.cpp b/kcmkwin/kwinrules/ruleitem.cpp index b89b59057..053d31772 100644 --- a/kcmkwin/kwinrules/ruleitem.cpp +++ b/kcmkwin/kwinrules/ruleitem.cpp @@ -1,248 +1,211 @@ /* * 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 { -class RuleItemPrivate -{ -public: - RuleItemPrivate(const QString &key, const QString &name, const QString §ion, const QString &iconName) - : m_key(key) - , m_name(name) - , m_section(section) - , m_icon(QIcon::fromTheme(iconName)) - , m_enabled(false) - , m_flags(0) - {}; - -public: - QString m_key; - QString m_name; - QString m_section; - QIcon m_icon; - - bool m_enabled; - - QString m_description; - uint m_flags; - - RuleItem::Type m_type; - QVariant m_value; -}; - - RuleItem::RuleItem(const QString &key, const RulePolicy::Type policyType, const RuleItem::Type type, const QString &name, const QString §ion, - const QString &iconName, - const QList &options - ) - : d(new RuleItemPrivate(key, name, section, iconName)) - , p(new RulePolicy(policyType)) - , o(nullptr) -{ - d->m_type = type; - if (type == Option || type == FlagsOption) { - o = new OptionsModel(options); - } - + 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 d; - delete p; - delete o; + delete m_policy; + delete m_options; } void RuleItem::reset() { - d->m_enabled = hasFlag(AlwaysEnabled) | hasFlag(StartEnabled); - d->m_value = typedValue(QVariant(), d->m_type); - p->resetValue(); - if (o) { - o->resetValue(); + m_enabled = hasFlag(AlwaysEnabled) | hasFlag(StartEnabled); + m_value = typedValue(QVariant(), m_type); + m_policy->resetValue(); + if (m_options) { + m_options->resetValue(); } } QString RuleItem::key() const { - return d->m_key; + return m_key; } QString RuleItem::name() const { - return d->m_name; + return m_name; } QString RuleItem::section() const { - return d->m_section; + return m_section; } QString RuleItem::iconName() const { - return d->m_icon.name(); + return m_icon.name(); } QIcon RuleItem::icon() const { - return d->m_icon; + return m_icon; } QString RuleItem::description() const { - return d->m_description; -} - -void RuleItem::setDescription(const QString& description) -{ - d->m_description = description; + return m_description; } bool RuleItem::isEnabled() const { - return d->m_enabled; + return m_enabled; } void RuleItem::setEnabled(bool enabled) { - d->m_enabled = enabled | hasFlag(AlwaysEnabled); + m_enabled = enabled | hasFlag(AlwaysEnabled); } -bool RuleItem::hasFlag(uint flag) const +bool RuleItem::hasFlag(RuleItem::Flags flag) const { - return (d->m_flags & flag) == flag; + return m_flags.testFlag(flag); } -void RuleItem::setFlags(uint flags, bool active) +void RuleItem::setFlag(RuleItem::Flags flag, bool active) { - if (active) { - d->m_flags |= flags; - } else { - d->m_flags &= (AllFlags ^ flags); - } + m_flags.setFlag(flag, active); } RuleItem::Type RuleItem::type() const { - return d->m_type; + return m_type; } QVariant RuleItem::value() const { - if (d->m_type == Option) { - return o->value(); + if (m_type == Option) { + return m_options->value(); } - return d->m_value; + return m_value; } void RuleItem::setValue(QVariant value) { - if (d->m_type == Option) { - o->setValue(value); + if (m_type == Option) { + m_options->setValue(value); } - d->m_value = typedValue(value, d->m_type); + m_value = typedValue(value, m_type); } QVariant RuleItem::options() const { - if (!o) { + if (!m_options) { return QVariant(); } - return QVariant::fromValue(o); + return QVariant::fromValue(m_options); } void RuleItem::setOptionsData(const QList &data) { - if (!o) { - if (d->m_type != Option && d->m_type != FlagsOption) { + if (!m_options) { + if (m_type != Option && m_type != FlagsOption) { return; } - o = new OptionsModel(); + m_options = new OptionsModel(); } - o->updateModelData(data); - o->setValue(d->m_value); + m_options->updateModelData(data); + m_options->setValue(m_value); } int RuleItem::policy() const { - return p->value(); + return m_policy->value(); } void RuleItem::setPolicy(int policy) { - p->setValue(policy); + m_policy->setValue(policy); } RulePolicy::Type RuleItem::policyType() const { - return p->type(); + return m_policy->type(); } QVariant RuleItem::policyModel() const { - return QVariant::fromValue(p); + return QVariant::fromValue(m_policy); } QString RuleItem::policyKey() const { - return p->policyKey(d->m_key); + return m_policy->policyKey(m_key); } QVariant RuleItem::typedValue(const QVariant &value, const RuleItem::Type type) { 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/ruleitem.h b/kcmkwin/kwinrules/ruleitem.h index 44477ba7d..104760f81 100644 --- a/kcmkwin/kwinrules/ruleitem.h +++ b/kcmkwin/kwinrules/ruleitem.h @@ -1,114 +1,122 @@ /* * 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 RuleItemPrivate; - 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 QString &iconName = QStringLiteral("window"), - const QList &options = {} + 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); - QString description() const; - void setDescription(const QString &description); - - bool hasFlag(uint flag) const; - void setFlags(uint flags, bool active=true); + 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 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: - RuleItemPrivate *d; - RulePolicy *p; - OptionsModel *o; + 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; + + RulePolicy *m_policy; + OptionsModel *m_options; }; } //namespace #endif //KWIN_RULEITEM_H diff --git a/kcmkwin/kwinrules/rulesmodel.cpp b/kcmkwin/kwinrules/rulesmodel.cpp index 630654e4d..4e2273acb 100644 --- a/kcmkwin/kwinrules/rulesmodel.cpp +++ b/kcmkwin/kwinrules/rulesmodel.cpp @@ -1,819 +1,823 @@ /* * 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")}, }; } 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(); } 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; 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(); } 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 || (configPolicyItem && configPolicyItem->property() == Rules::Unused) || (configItem->property().toString().isEmpty())) { rule->reset(); continue; } rule->setEnabled(true); const QVariant value = configItem->property(); rule->setValue(value); if (rule->policyType() != RulePolicy::NoPolicy) { 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 addRule(new RuleItem(QLatin1String("description"), RulePolicy::NoPolicy, RuleItem::String, i18n("Description"), i18n("Window matching"), - QStringLiteral("entry-edit"))); - m_rules["description"]->setFlags(RuleItem::AlwaysEnabled | RuleItem::AffectsDescription); + QIcon::fromTheme("entry-edit"))); + m_rules["description"]->setFlag(RuleItem::AlwaysEnabled); + m_rules["description"]->setFlag(RuleItem::AffectsDescription); // Window matching addRule(new RuleItem(QLatin1String("wmclass"), RulePolicy::StringMatch, RuleItem::String, i18n("Window class (application)"), i18n("Window matching"), - QStringLiteral("window"))); - m_rules["wmclass"]->setFlags(RuleItem::AlwaysEnabled | RuleItem::AffectsWarning | RuleItem::AffectsDescription); + QIcon::fromTheme("window"))); + m_rules["wmclass"]->setFlag(RuleItem::AlwaysEnabled); + m_rules["wmclass"]->setFlag(RuleItem::AffectsDescription); + m_rules["wmclass"]->setFlag(RuleItem::AffectsWarning); addRule(new RuleItem(QLatin1String("wmclasscomplete"), RulePolicy::NoPolicy, RuleItem::Boolean, i18n("Match whole window class"), i18n("Window matching"), - QStringLiteral("window"))); - m_rules["wmclasscomplete"]->setFlags(RuleItem::AlwaysEnabled); + QIcon::fromTheme("window"))); + m_rules["wmclasscomplete"]->setFlag(RuleItem::AlwaysEnabled); addRule(new RuleItem(QLatin1String("types"), RulePolicy::NoPolicy, RuleItem::FlagsOption, i18n("Window types"), i18n("Window matching"), - QStringLiteral("window-duplicate"), - windowTypesModelData())); - m_rules["types"]->setFlags(RuleItem::AlwaysEnabled | RuleItem::AffectsWarning ); + QIcon::fromTheme("window-duplicate"))); + m_rules["types"]->setOptionsData(windowTypesModelData()); + m_rules["types"]->setFlag(RuleItem::AlwaysEnabled); + m_rules["types"]->setFlag(RuleItem::AffectsWarning); addRule(new RuleItem(QLatin1String("windowrole"), RulePolicy::NoPolicy, RuleItem::String, i18n("Window role"), i18n("Window matching"), - QStringLiteral("dialog-object-properties"))); + QIcon::fromTheme("dialog-object-properties"))); addRule(new RuleItem(QLatin1String("title"), RulePolicy::StringMatch, RuleItem::String, i18n("Window title"), i18n("Window matching"), - QStringLiteral("edit-comment"))); - m_rules["title"]->setFlags(RuleItem::AffectsDescription); + QIcon::fromTheme("edit-comment"))); + m_rules["title"]->setFlag(RuleItem::AffectsDescription); addRule(new RuleItem(QLatin1String("clientmachine"), RulePolicy::StringMatch, RuleItem::String, i18n("Machine (hostname)"), i18n("Window matching"), - QStringLiteral("computer"))); + QIcon::fromTheme("computer"))); // Size & Position addRule(new RuleItem(QLatin1String("position"), RulePolicy::SetRule, RuleItem::Coordinate, i18n("Position"), i18n("Size & Position"), - QStringLiteral("transform-move"))); + QIcon::fromTheme("transform-move"))); addRule(new RuleItem(QLatin1String("size"), RulePolicy::SetRule, RuleItem::Coordinate, i18n("Size"), i18n("Size & Position"), - QStringLiteral("image-resize-symbolic"))); + QIcon::fromTheme("image-resize-symbolic"))); addRule(new RuleItem(QLatin1String("maximizehoriz"), RulePolicy::SetRule, RuleItem::Boolean, i18n("Maximized horizontally"), i18n("Size & Position"), - QStringLiteral("resizecol"))); + QIcon::fromTheme("resizecol"))); addRule(new RuleItem(QLatin1String("maximizevert"), RulePolicy::SetRule, RuleItem::Boolean, i18n("Maximized vertically"), i18n("Size & Position"), - QStringLiteral("resizerow"))); + QIcon::fromTheme("resizerow"))); addRule(new RuleItem(QLatin1String("desktop"), RulePolicy::SetRule, RuleItem::Option, i18n("Virtual Desktop"), i18n("Size & Position"), - QStringLiteral("virtual-desktops"), - virtualDesktopsModelData())); + QIcon::fromTheme("virtual-desktops"))); + m_rules["desktop"]->setOptionsData(virtualDesktopsModelData()); #ifdef KWIN_BUILD_ACTIVITIES m_activities = new KActivities::Consumer(this); addRule(new RuleItem(QLatin1String("activity"), RulePolicy::SetRule, RuleItem::Option, i18n("Activity"), i18n("Size & Position"), - QStringLiteral("activities"), - activitiesModelData())); + QIcon::fromTheme("activities"))); + m_rules["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"), - QStringLiteral("osd-shutd-screen"))); + QIcon::fromTheme("osd-shutd-screen"))); addRule(new RuleItem(QLatin1String("fullscreen"), RulePolicy::SetRule, RuleItem::Boolean, i18n("Fullscreen"), i18n("Size & Position"), - QStringLiteral("view-fullscreen"))); + QIcon::fromTheme("view-fullscreen"))); addRule(new RuleItem(QLatin1String("minimize"), RulePolicy::SetRule, RuleItem::Boolean, i18n("Minimized"), i18n("Size & Position"), - QStringLiteral("window-minimize"))); + QIcon::fromTheme("window-minimize"))); addRule(new RuleItem(QLatin1String("shade"), RulePolicy::SetRule, RuleItem::Boolean, i18n("Shaded"), i18n("Size & Position"), - QStringLiteral("window-shade"))); + QIcon::fromTheme("window-shade"))); addRule(new RuleItem(QLatin1String("placement"), RulePolicy::ForceRule, RuleItem::Option, i18n("Initial placement"), i18n("Size & Position"), - QStringLiteral("region"), - placementModelData())); + QIcon::fromTheme("region"))); + m_rules["placement"]->setOptionsData(placementModelData()); addRule(new RuleItem(QLatin1String("ignoregeometry"), RulePolicy::SetRule, RuleItem::Boolean, i18n("Ignore requested geometry"), i18n("Size & Position"), - QStringLiteral("view-time-schedule-baselined-remove"))); - m_rules["ignoregeometry"]->setDescription(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.")); + 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"), - QStringLiteral("image-resize-symbolic"))); + QIcon::fromTheme("image-resize-symbolic"))); addRule(new RuleItem(QLatin1String("maxsize"), RulePolicy::ForceRule, RuleItem::Coordinate, i18n("Maximum Size"), i18n("Size & Position"), - QStringLiteral("image-resize-symbolic"))); + QIcon::fromTheme("image-resize-symbolic"))); addRule(new RuleItem(QLatin1String("strictgeometry"), RulePolicy::ForceRule, RuleItem::Boolean, i18n("Obey geometry restrictions"), i18n("Size & Position"), - QStringLiteral("transform-crop-and-resize"))); - m_rules["strictgeometry"]->setDescription(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.")); + 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"), - QStringLiteral("window-keep-above"))); + QIcon::fromTheme("window-keep-above"))); addRule(new RuleItem(QLatin1String("below"), RulePolicy::SetRule, RuleItem::Boolean, i18n("Keep below"), i18n("Arrangement & Access"), - QStringLiteral("window-keep-below"))); + QIcon::fromTheme("window-keep-below"))); addRule(new RuleItem(QLatin1String("skiptaskbar"), RulePolicy::SetRule, RuleItem::Boolean, i18n("Skip taskbar"), i18n("Arrangement & Access"), - QStringLiteral("kt-show-statusbar"))); - m_rules["skiptaskbar"]->setDescription(i18n("Window shall (not) appear in the taskbar.")); + 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"), - QStringLiteral("org.kde.plasma.pager"))); - m_rules["skippager"]->setDescription(i18n("Window shall (not) appear in the manager for virtual desktops")); + 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"), - QStringLiteral("preferences-system-windows-effect-flipswitch"))); - m_rules["skipswitcher"]->setDescription(i18n("Window shall (not) appear in the Alt+Tab list")); + 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"), - QStringLiteral("configure-shortcuts"))); + QIcon::fromTheme("configure-shortcuts"))); // Appearance & Fixes addRule(new RuleItem(QLatin1String("noborder"), RulePolicy::SetRule, RuleItem::Boolean, i18n("No titlebar and frame"), i18n("Appearance & Fixes"), - QStringLiteral("dialog-cancel"))); + QIcon::fromTheme("dialog-cancel"))); addRule(new RuleItem(QLatin1String("decocolor"), RulePolicy::ForceRule, RuleItem::Option, i18n("Titlebar color scheme"), i18n("Appearance & Fixes"), - QStringLiteral("preferences-desktop-theme"), - colorSchemesModelData())); + QIcon::fromTheme("preferences-desktop-theme"))); + m_rules["decocolor"]->setOptionsData(colorSchemesModelData()); addRule(new RuleItem(QLatin1String("opacityactive"), RulePolicy::ForceRule, RuleItem::Percentage, i18n("Active opacity"), i18n("Appearance & Fixes"), - QStringLiteral("edit-opacity"))); + QIcon::fromTheme("edit-opacity"))); addRule(new RuleItem(QLatin1String("opacityinactive"), RulePolicy::ForceRule, RuleItem::Percentage, i18n("Inactive opacity"), i18n("Appearance & Fixes"), - QStringLiteral("edit-opacity"))); + QIcon::fromTheme("edit-opacity"))); addRule(new RuleItem(QLatin1String("fsplevel"), RulePolicy::ForceRule, RuleItem::Option, i18n("Focus stealing prevention"), i18n("Appearance & Fixes"), - QStringLiteral("preferences-system-windows-effect-glide"), - focusModelData())); - m_rules["fsplevel"]->setDescription(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.")); + 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."))); + m_rules["fsplevel"]->setOptionsData(focusModelData()); addRule(new RuleItem(QLatin1String("fpplevel"), RulePolicy::ForceRule, RuleItem::Option, i18n("Focus protection"), i18n("Appearance & Fixes"), - QStringLiteral("preferences-system-windows-effect-minimize"), - focusModelData())); - m_rules["fpplevel"]->setDescription(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.")); + 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."))); + m_rules["fpplevel"]->setOptionsData(focusModelData()); addRule(new RuleItem(QLatin1String("acceptfocus"), RulePolicy::ForceRule, RuleItem::Boolean, i18n("Accept focus"), i18n("Appearance & Fixes"), - QStringLiteral("preferences-desktop-cursors"))); - m_rules["acceptfocus"]->setDescription(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.")); + 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"), - QStringLiteral("input-keyboard-virtual-off"))); - m_rules["disableglobalshortcuts"]->setDescription(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!")); + 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"), - QStringLiteral("dialog-close"))); + QIcon::fromTheme("dialog-close"))); addRule(new RuleItem(QLatin1String("type"), RulePolicy::ForceRule, RuleItem::Option, i18n("Set window type"), i18n("Appearance & Fixes"), - QStringLiteral("window-duplicate"), - windowTypesModelData())); + QIcon::fromTheme("window-duplicate"))); + m_rules["type"]->setOptionsData(windowTypesModelData()); addRule(new RuleItem(QLatin1String("desktopfile"), RulePolicy::SetRule, RuleItem::String, i18n("Desktop file name"), i18n("Appearance & Fixes"), - QStringLiteral("application-x-desktop"))); + QIcon::fromTheme("application-x-desktop"))); addRule(new RuleItem(QLatin1String("blockcompositing"), RulePolicy::ForceRule, RuleItem::Boolean, i18n("Block compositing"), i18n("Appearance & Fixes"), - QStringLiteral("composite-track-on"))); + 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::prefillProperties(const QVariantMap &info) { beginResetModel(); // 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()); if (!m_rules["position"]->isEnabled()) { m_rules["position"]->setValue(position); } if (!m_rules["size"]->isEnabled()) { m_rules["size"]->setValue(size); } if (!m_rules["minsize"]->isEnabled()) { m_rules["minsize"]->setValue(size); } if (!m_rules["maxsize"]->isEnabled()) { m_rules["maxsize"]->setValue(size); } NET::WindowType window_type = static_cast(info.value("type", 0).toInt()); if (!m_rules["types"]->isEnabled() || m_rules["types"]->value() == 0) { if (window_type == NET::Unknown) { window_type = NET::Normal; } m_rules["types"]->setValue(1 << window_type); } 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)); // Only prefill disabled or empty properties if (!m_rules[ruleKey]->isEnabled() || m_rules[ruleKey]->value().toString().isEmpty()) { m_rules[ruleKey]->setValue(info.value(property)); } } endResetModel(); emit descriptionChanged(); emit warningMessageChanged(); } 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 prefillProperties(windowInfo); } ); } } //namespace