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")