9#include "rulesettings.h" 
   11#include <QDBusConnection> 
   12#include <QDBusMessage> 
   13#include <QDBusPendingCallWatcher> 
   14#include <QDBusPendingReply> 
   17#include <KLocalizedString> 
   18#include <KPluginFactory> 
   24    : KQuickConfigModule(parent, metaData)
 
   29    for (
const QVariant &arg : arguments) {
 
   30        argList << arg.toString();
 
   32    parseArguments(argList);
 
   35        if (m_editIndex.isValid()) {
 
   36            m_ruleBookModel->setDescriptionAt(m_editIndex.row(), m_rulesModel->description());
 
   39    connect(m_rulesModel, &RulesModel::dataChanged, 
this, [
this] {
 
   40        Q_EMIT m_ruleBookModel->dataChanged(m_editIndex, m_editIndex, {});
 
   42    connect(m_ruleBookModel, &RuleBookModel::dataChanged, 
this, &KCMKWinRules::updateNeedsSave);
 
 
   45void KCMKWinRules::parseArguments(
const QStringList &args)
 
   48    bool nextArgIsUuid = 
false;
 
   52    for (
const QString &arg : args) {
 
   53        if (arg == QLatin1String(
"uuid")) {
 
   55        } 
else if (nextArgIsUuid) {
 
   57            nextArgIsUuid = 
false;
 
   58        } 
else if (arg.startsWith(
"uuid=")) {
 
   59            uuid = QUuid(arg.mid(strlen(
"uuid=")));
 
   60        } 
else if (arg == QLatin1String(
"whole-app")) {
 
   66        qDebug() << 
"Invalid window uuid.";
 
   71    QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral(
"org.kde.KWin"),
 
   72                                                          QStringLiteral(
"/KWin"),
 
   73                                                          QStringLiteral(
"org.kde.KWin"),
 
   74                                                          QStringLiteral(
"getWindowInfo"));
 
   75    message.setArguments({uuid.toString()});
 
   76    QDBusPendingReply<QVariantMap> async = QDBusConnection::sessionBus().asyncCall(message);
 
   78    QDBusPendingCallWatcher *callWatcher = 
new QDBusPendingCallWatcher(async, 
this);
 
   79    connect(callWatcher, &QDBusPendingCallWatcher::finished, 
this, [
this, uuid](QDBusPendingCallWatcher *self) {
 
   80        QDBusPendingReply<QVariantMap> reply = *self;
 
   82        if (!reply.isValid() || reply.value().isEmpty()) {
 
   83            qDebug() << 
"Error retrieving properties for window" << uuid;
 
   86        qDebug() << 
"Retrieved properties for window" << uuid;
 
   87        m_winProperties = reply.value();
 
   89        if (m_alreadyLoaded) {
 
   90            createRuleFromProperties();
 
   97    m_ruleBookModel->
load();
 
   99    if (!m_winProperties.isEmpty() && !m_alreadyLoaded) {
 
  100        createRuleFromProperties();
 
  102        m_editIndex = QModelIndex();
 
  106    m_alreadyLoaded = 
true;
 
 
  113    m_ruleBookModel->
save();
 
  116    QDBusMessage message = QDBusMessage::createSignal(
"/KWin", 
"org.kde.KWin", 
"reloadConfig");
 
  117    QDBusConnection::sessionBus().send(message);
 
 
  120void KCMKWinRules::updateNeedsSave()
 
  123    Q_EMIT needsSaveChanged();
 
  126void KCMKWinRules::createRuleFromProperties()
 
  128    if (m_winProperties.isEmpty()) {
 
  132    QModelIndex matchedIndex = findRuleWithProperties(m_winProperties, m_wholeApp);
 
  133    if (!matchedIndex.isValid()) {
 
  134        m_ruleBookModel->insertRow(0);
 
  135        fillSettingsFromProperties(m_ruleBookModel->
ruleSettingsAt(0), m_winProperties, m_wholeApp);
 
  136        matchedIndex = m_ruleBookModel->index(0);
 
  143    m_winProperties.clear();
 
  148    if (!m_editIndex.isValid()) {
 
  151    return m_editIndex.row();
 
  156    if (index < 0 || index >= m_ruleBookModel->
rowCount()) {
 
  160    if (m_editIndex.row() == index) {
 
 
  171    if (index < 0 || index >= m_ruleBookModel->
rowCount()) {
 
  175    m_editIndex = m_ruleBookModel->index(index);
 
 
  186    const int newIndex = m_ruleBookModel->
rowCount();
 
  187    m_ruleBookModel->insertRow(newIndex);
 
 
  196    if (index < 0 || index >= m_ruleBookModel->
rowCount()) {
 
  200    m_ruleBookModel->removeRow(index);
 
 
  208    const int lastIndex = m_ruleBookModel->
rowCount() - 1;
 
  209    if (sourceIndex == destIndex
 
  210        || (sourceIndex < 0 || sourceIndex > lastIndex)
 
  211        || (destIndex < 0 || destIndex > lastIndex)) {
 
  215    m_ruleBookModel->moveRow(QModelIndex(), sourceIndex, QModelIndex(), destIndex);
 
 
  223    if (index < 0 || index >= m_ruleBookModel->
rowCount()) {
 
  227    const int newIndex = index + 1;
 
  228    const QString newDescription = i18n(
"Copy of %1", m_ruleBookModel->
descriptionAt(index));
 
  230    m_ruleBookModel->insertRow(newIndex);
 
 
  239    if (indexes.isEmpty()) {
 
  243    const auto config = KSharedConfig::openConfig(path.toLocalFile(), KConfig::SimpleConfig);
 
  245    const QStringList groups = config->groupList();
 
  246    for (
const QString &groupName : groups) {
 
  247        config->deleteGroup(groupName);
 
  250    for (
int index : indexes) {
 
  251        if (index < 0 || index > m_ruleBookModel->
rowCount()) {
 
  254        const RuleSettings *origin = m_ruleBookModel->
ruleSettingsAt(index);
 
  255        RuleSettings exported(config, origin->description());
 
 
  264    const auto config = KSharedConfig::openConfig(path.toLocalFile(), KConfig::SimpleConfig);
 
  265    const QStringList groups = config->groupList();
 
  266    if (groups.isEmpty()) {
 
  270    for (
const QString &groupName : groups) {
 
  271        RuleSettings settings(config, groupName);
 
  273        const bool remove = settings.deleteRule();
 
  274        const QString importDescription = settings.description();
 
  275        if (importDescription.isEmpty()) {
 
  281        for (
int index = 0; index < m_ruleBookModel->
rowCount(); index++) {
 
  282            if (m_ruleBookModel->
descriptionAt(index) == importDescription) {
 
  289            m_ruleBookModel->removeRow(newIndex);
 
  294            newIndex = m_ruleBookModel->
rowCount();
 
  295            m_ruleBookModel->insertRow(newIndex);
 
  301        if (m_editIndex.row() == newIndex) {
 
 
  310QModelIndex KCMKWinRules::findRuleWithProperties(
const QVariantMap &info, 
bool wholeApp)
 const 
  312    const QString wmclass_class = info.value(
"resourceClass").toString();
 
  313    const QString wmclass_name = info.value(
"resourceName").toString();
 
  314    const QString role = info.value(
"role").toString();
 
  315    const NET::WindowType 
type = 
static_cast<NET::WindowType
>(info.value(
"type").toInt());
 
  316    const QString title = info.value(
"caption").toString();
 
  317    const QString machine = info.value(
"clientMachine").toString();
 
  318    const bool isLocalHost = info.value(
"localhost").toBool();
 
  320    int bestMatchRow = -1;
 
  321    int bestMatchScore = 0;
 
  323    for (
int row = 0; row < m_ruleBookModel->
rowCount(); row++) {
 
  324        const RuleSettings *settings = m_ruleBookModel->
ruleSettingsAt(row);
 
  329        if (!rule.matchWMClass(wmclass_class, wmclass_name)
 
  330                || !rule.matchType(
type)
 
  331                || !rule.matchRole(role)
 
  332                || !rule.matchTitle(title)
 
  333                || !rule.matchClientMachine(machine, isLocalHost)) {
 
  348        if (settings->wmclasscomplete()) {
 
  361            if (settings->types() != NET::AllTypesMask) {
 
  364                for (
unsigned int bit = 1; bit < 1U << 31; bit <<= 1) {
 
  365                    if (settings->types() & bit) {
 
  377            if (settings->types() == NET::AllTypesMask) {
 
  382        if (score > bestMatchScore) {
 
  384            bestMatchScore = score;
 
  388    if (bestMatchRow < 0) {
 
  389        return QModelIndex();
 
  391    return m_ruleBookModel->index(bestMatchRow);
 
  395void KCMKWinRules::fillSettingsFromProperties(RuleSettings *settings, 
const QVariantMap &info, 
bool wholeApp)
 const 
  397    const QString wmclass_class = info.value(
"resourceClass").toString();
 
  398    const QString wmclass_name = info.value(
"resourceName").toString();
 
  399    const QString role = info.value(
"role").toString();
 
  400    const NET::WindowType 
type = 
static_cast<NET::WindowType
>(info.value(
"type").toInt());
 
  401    const QString title = info.value(
"caption").toString();
 
  402    const QString machine = info.value(
"clientMachine").toString();
 
  404    settings->setDefaults();
 
  407        if (!wmclass_class.isEmpty()) {
 
  408            settings->setDescription(i18n(
"Application settings for %1", wmclass_class));
 
  411        settings->setTypes(NET::AllTypesMask);
 
  413        settings->setClientmachine(machine); 
 
  416        if (wmclass_name == wmclass_class) {
 
  417            settings->setWmclasscomplete(
false);
 
  418            settings->setWmclass(wmclass_class);
 
  422            settings->setWmclasscomplete(
true);
 
  423            settings->setWmclass(QStringLiteral(
"%1 %2").arg(wmclass_name, wmclass_class));
 
  429    if (!wmclass_class.isEmpty()) {
 
  430        settings->setDescription(i18n(
"Window settings for %1", wmclass_class));
 
  432    if (
type == NET::Unknown) {
 
  433        settings->setTypes(NET::NormalMask);
 
  435        settings->setTypes(NET::WindowTypeMask(1 << 
type)); 
 
  437    settings->setTitle(title); 
 
  439    settings->setClientmachine(machine); 
 
  441    if (!role.isEmpty() && role != 
"unknown" && role != 
"unnamed") { 
 
  442        settings->setWindowrole(role);
 
  444        if (wmclass_name == wmclass_class) {
 
  445            settings->setWmclasscomplete(
false);
 
  446            settings->setWmclass(wmclass_class);
 
  450            settings->setWmclasscomplete(
true);
 
  451            settings->setWmclass(QStringLiteral(
"%1 %2").arg(wmclass_name, wmclass_class));
 
  455        if (wmclass_name != wmclass_class) {
 
  457            settings->setWmclasscomplete(
true);
 
  458            settings->setWmclass(QStringLiteral(
"%1 %2").arg(wmclass_name, wmclass_class));
 
  470            settings->setWmclasscomplete(
false);
 
  471            settings->setWmclass(wmclass_class);
 
  481#include "kcmrules.moc" 
  483#include "moc_kcmrules.cpp" 
Q_INVOKABLE void setRuleDescription(int index, const QString &description)
Q_INVOKABLE void moveRule(int sourceIndex, int destIndex)
Q_INVOKABLE void importFromFile(const QUrl &path)
Q_INVOKABLE void createRule()
Q_INVOKABLE void removeRule(int index)
Q_INVOKABLE void exportToFile(const QUrl &path, const QList< int > &indexes)
Q_INVOKABLE void duplicateRule(int index)
KCMKWinRules(QObject *parent, const KPluginMetaData &metaData, const QVariantList &arguments)
Q_INVOKABLE void editRule(int index)
RuleSettings * ruleSettingsAt(int row) const
void setDescriptionAt(int row, const QString &description)
static void copySettingsTo(RuleSettings *dest, const RuleSettings &source)
QString descriptionAt(int row) const
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void setRuleSettingsAt(int row, const RuleSettings &settings)
void setDescription(const QString &description)
void descriptionChanged()
void setSuggestedProperties(const QVariantMap &info)
void setSettings(RuleSettings *settings)
K_PLUGIN_CLASS_WITH_JSON(KCMKWinRules, "kcm_kwinrules.json")