KWinRules KCM Redesign
Summary: Replacement KCM to configure kwin rules, using a QML-based UI. After some work on the task T12729, it is almost feature-par with the previous module, and adapted to the recent move to KConfigXT. Test Plan: {F8208046} {F8208047} Reviewers: #plasma, #kwin, #vdg, ngraham, davidedmundson, zzag Reviewed By: #plasma, #kwin, #vdg, ngraham, davidedmundson, zzag Subscribers: ngraham, davidedmundson, hchain, broulik, zzag, kwin Tags: #kwin, #vdg Differential Revision: https://phabricator.kde.org/D28152
This commit is contained in:
parent
19bfa7c065
commit
a04b40dadb
35 changed files with 3370 additions and 5265 deletions
|
@ -3,20 +3,19 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kcmkwinrules\")
|
|||
add_definitions(-DKCMRULES)
|
||||
|
||||
include_directories(../../)
|
||||
set(kwinrules_SRCS ../../rulebooksettings.cpp
|
||||
../../cursor.cpp
|
||||
../../plugins/platforms/x11/standalone/x11cursor.cpp
|
||||
../../rules.cpp
|
||||
../../placement.cpp
|
||||
../../utils.cpp
|
||||
yesnobox.cpp
|
||||
ruleswidget.cpp
|
||||
ruleslist.cpp
|
||||
kwinsrc.cpp
|
||||
detectwidget.cpp
|
||||
)
|
||||
|
||||
ki18n_wrap_ui(kwinrules_SRCS ruleslist.ui detectwidget.ui editshortcut.ui ruleswidgetbase.ui)
|
||||
set(kwinrules_SRCS
|
||||
../../rulebooksettings.cpp
|
||||
../../cursor.cpp
|
||||
../../plugins/platforms/x11/standalone/x11cursor.cpp
|
||||
../../rules.cpp
|
||||
../../placement.cpp
|
||||
../../utils.cpp
|
||||
kwinsrc.cpp
|
||||
optionsmodel.cpp
|
||||
ruleitem.cpp
|
||||
rulesmodel.cpp
|
||||
)
|
||||
|
||||
kconfig_add_kcfg_files(kwinrules_SRCS ../../rulesettings.kcfgc)
|
||||
kconfig_add_kcfg_files(kwinrules_SRCS ../../rulebooksettingsbase.kcfgc)
|
||||
|
@ -30,13 +29,11 @@ set(kwin_kcm_rules_XCB_LIBS
|
|||
)
|
||||
|
||||
set(kcm_libs
|
||||
Qt5::Concurrent
|
||||
Qt5::X11Extras
|
||||
Qt5::Quick
|
||||
Qt5::QuickWidgets
|
||||
|
||||
KF5::Completion
|
||||
KF5::ConfigWidgets
|
||||
KF5::I18n
|
||||
KF5::Service
|
||||
KF5::QuickAddons
|
||||
KF5::WindowSystem
|
||||
KF5::XmlGui
|
||||
)
|
||||
|
@ -46,14 +43,14 @@ if (KWIN_BUILD_ACTIVITIES)
|
|||
endif()
|
||||
target_link_libraries(KWinRulesObjects ${kcm_libs} ${kwin_kcm_rules_XCB_LIBS})
|
||||
|
||||
add_executable(kwin_rules_dialog main.cpp)
|
||||
add_executable(kwin_rules_dialog main.cpp rulesdialog.cpp)
|
||||
target_link_libraries(kwin_rules_dialog KWinRulesObjects)
|
||||
install(TARGETS kwin_rules_dialog DESTINATION ${LIBEXEC_INSTALL_DIR})
|
||||
|
||||
add_library(kcm_kwinrules MODULE kcm.cpp)
|
||||
add_library(kcm_kwinrules MODULE kcmrules.cpp rulebookmodel.cpp)
|
||||
target_link_libraries(kcm_kwinrules KWinRulesObjects)
|
||||
install(TARGETS kcm_kwinrules DESTINATION ${PLUGIN_INSTALL_DIR})
|
||||
kcoreaddons_desktop_to_json(kcm_kwinrules "kcm_kwinrules.desktop" SERVICE_TYPES kcmodule.desktop)
|
||||
|
||||
########### install files ###############
|
||||
|
||||
install(FILES kwinrules.desktop DESTINATION ${SERVICES_INSTALL_DIR})
|
||||
install(TARGETS kcm_kwinrules DESTINATION ${PLUGIN_INSTALL_DIR}/kcms)
|
||||
install(FILES kcm_kwinrules.desktop DESTINATION ${SERVICES_INSTALL_DIR})
|
||||
kpackage_install_package(package kcm_kwinrules kcms)
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "detectwidget.h"
|
||||
#include "../../plugins/platforms/x11/standalone/x11cursor.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <QDebug>
|
||||
#include <kwindowsystem.h>
|
||||
#include <QLabel>
|
||||
#include <QRadioButton>
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QMouseEvent>
|
||||
#include <QEvent>
|
||||
#include <QByteArray>
|
||||
#include <QTimer>
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QDBusPendingReply>
|
||||
|
||||
Q_DECLARE_METATYPE(NET::WindowType)
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
DetectWidget::DetectWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
}
|
||||
|
||||
DetectDialog::DetectDialog(QWidget* parent, const char* name)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setObjectName(name);
|
||||
setModal(true);
|
||||
setLayout(new QVBoxLayout);
|
||||
|
||||
widget = new DetectWidget(this);
|
||||
layout()->addWidget(widget);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, this);
|
||||
layout()->addWidget(buttons);
|
||||
|
||||
connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
}
|
||||
|
||||
void DetectDialog::detect(int secs)
|
||||
{
|
||||
QTimer::singleShot(secs*1000, this, SLOT(selectWindow()));
|
||||
}
|
||||
|
||||
void DetectDialog::executeDialog()
|
||||
{
|
||||
static const char* const types[] = {
|
||||
I18N_NOOP("Normal Window"),
|
||||
I18N_NOOP("Desktop"),
|
||||
I18N_NOOP("Dock (panel)"),
|
||||
I18N_NOOP("Toolbar"),
|
||||
I18N_NOOP("Torn-Off Menu"),
|
||||
I18N_NOOP("Dialog Window"),
|
||||
I18N_NOOP("Override Type"),
|
||||
I18N_NOOP("Standalone Menubar"),
|
||||
I18N_NOOP("Utility Window"),
|
||||
I18N_NOOP("Splash Screen")
|
||||
};
|
||||
widget->class_label->setText(wmclass_class + QLatin1String(" (") + wmclass_name + ' ' + wmclass_class + ')');
|
||||
widget->role_label->setText(role);
|
||||
widget->match_role->setEnabled(!role.isEmpty());
|
||||
if (type == NET::Unknown)
|
||||
widget->type_label->setText(i18n("Unknown - will be treated as Normal Window"));
|
||||
else
|
||||
widget->type_label->setText(i18n(types[ type ]));
|
||||
widget->title_label->setText(title);
|
||||
widget->machine_label->setText(machine);
|
||||
widget->adjustSize();
|
||||
adjustSize();
|
||||
if (width() < 4*height()/3)
|
||||
resize(4*height()/3, height());
|
||||
emit detectionDone(exec() == QDialog::Accepted);
|
||||
}
|
||||
|
||||
QByteArray DetectDialog::selectedClass() const
|
||||
{
|
||||
if (widget->match_whole_class->isChecked())
|
||||
return wmclass_name + ' ' + wmclass_class;
|
||||
return wmclass_class;
|
||||
}
|
||||
|
||||
bool DetectDialog::selectedWholeClass() const
|
||||
{
|
||||
return widget->match_whole_class->isChecked();
|
||||
}
|
||||
|
||||
QByteArray DetectDialog::selectedRole() const
|
||||
{
|
||||
if (widget->match_role->isChecked())
|
||||
return role;
|
||||
return "";
|
||||
}
|
||||
|
||||
QString DetectDialog::selectedTitle() const
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
Rules::StringMatch DetectDialog::titleMatch() const
|
||||
{
|
||||
return widget->match_title->isChecked() ? Rules::ExactMatch : Rules::UnimportantMatch;
|
||||
}
|
||||
|
||||
bool DetectDialog::selectedWholeApp() const
|
||||
{
|
||||
return !widget->match_type->isChecked();
|
||||
}
|
||||
|
||||
NET::WindowType DetectDialog::selectedType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
QByteArray DetectDialog::selectedMachine() const
|
||||
{
|
||||
return machine;
|
||||
}
|
||||
|
||||
void DetectDialog::selectWindow()
|
||||
{
|
||||
QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"),
|
||||
QStringLiteral("/KWin"),
|
||||
QStringLiteral("org.kde.KWin"),
|
||||
QStringLiteral("queryWindowInfo"));
|
||||
QDBusPendingReply<QVariantMap> async = QDBusConnection::sessionBus().asyncCall(message);
|
||||
|
||||
QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this);
|
||||
connect(callWatcher, &QDBusPendingCallWatcher::finished, this,
|
||||
[this](QDBusPendingCallWatcher *self) {
|
||||
QDBusPendingReply<QVariantMap> reply = *self;
|
||||
self->deleteLater();
|
||||
if (!reply.isValid()) {
|
||||
emit detectionDone(false);
|
||||
return;
|
||||
}
|
||||
m_windowInfo = reply.value();
|
||||
wmclass_class = m_windowInfo.value("resourceClass").toByteArray();
|
||||
wmclass_name = m_windowInfo.value("resourceName").toByteArray();
|
||||
role = m_windowInfo.value("role").toByteArray();
|
||||
type = m_windowInfo.value("type").value<NET::WindowType>();
|
||||
title = m_windowInfo.value("caption").toString();
|
||||
machine = m_windowInfo.value("clientMachine").toByteArray();
|
||||
executeDialog();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __DETECTWIDGET_H__
|
||||
#define __DETECTWIDGET_H__
|
||||
|
||||
#include <QDialog>
|
||||
#include <kwindowsystem.h>
|
||||
|
||||
#include "../../rules.h"
|
||||
//Added by qt3to4:
|
||||
#include <QEvent>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "ui_detectwidget.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class DetectWidget
|
||||
: public QWidget, public Ui_DetectWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DetectWidget(QWidget* parent = nullptr);
|
||||
};
|
||||
|
||||
class DetectDialog
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DetectDialog(QWidget* parent = nullptr, const char* name = nullptr);
|
||||
void detect(int secs = 0);
|
||||
QByteArray selectedClass() const;
|
||||
bool selectedWholeClass() const;
|
||||
QByteArray selectedRole() const;
|
||||
bool selectedWholeApp() const;
|
||||
NET::WindowType selectedType() const;
|
||||
QString selectedTitle() const;
|
||||
Rules::StringMatch titleMatch() const;
|
||||
QByteArray selectedMachine() const;
|
||||
|
||||
const QVariantMap &windowInfo() const {
|
||||
return m_windowInfo;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void detectionDone(bool);
|
||||
private Q_SLOTS:
|
||||
void selectWindow();
|
||||
private:
|
||||
void executeDialog();
|
||||
QByteArray wmclass_class;
|
||||
QByteArray wmclass_name;
|
||||
QByteArray role;
|
||||
NET::WindowType type;
|
||||
QString title;
|
||||
QByteArray extrarole;
|
||||
QByteArray machine;
|
||||
DetectWidget* widget;
|
||||
QVariantMap m_windowInfo;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
|
@ -1,229 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>KWin::DetectWidget</class>
|
||||
<widget class="QWidget" name="KWin::DetectWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>450</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Information About Selected Window</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="verticalSpacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="textLabel1">
|
||||
<property name="text">
|
||||
<string>Class:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="class_label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="textLabel3">
|
||||
<property name="text">
|
||||
<string>Role:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="role_label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="textLabel4">
|
||||
<property name="text">
|
||||
<string>Type:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="type_label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="textLabel8">
|
||||
<property name="text">
|
||||
<string>Title:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="title_label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="textLabel13">
|
||||
<property name="text">
|
||||
<string>Machine:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="machine_label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Match by primary class name and</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="match_whole_class">
|
||||
<property name="text">
|
||||
<string>Secondary class name (resulting in term in brackets)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="match_role">
|
||||
<property name="text">
|
||||
<string>Window role (can be used to select windows by function)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="match_type">
|
||||
<property name="text">
|
||||
<string>Window type (eg. all dialogs, but not the main windows)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="match_title">
|
||||
<property name="text">
|
||||
<string>Window title (very specific, can fail due to content changes or translation)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -1,161 +0,0 @@
|
|||
<ui version="4.0" >
|
||||
<class>EditShortcut</class>
|
||||
<widget class="QWidget" name="EditShortcut" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1194</width>
|
||||
<height>447</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="textLabel2" >
|
||||
<property name="text" >
|
||||
<string>A single shortcut can be easily assigned or cleared using the two buttons. Only shortcuts with modifiers can be used.<p>
|
||||
It is possible to have several possible shortcuts, and the first available shortcut will be used. The shortcuts are specified using shortcut sets separated by " - ". One set is specified as <i>base</i>+(<i>list</i>), where base are modifiers and list is a list of keys.<br>
|
||||
For example "<b>Shift+Alt+(123) Shift+Ctrl+(ABC)</b>" will first try <b>Shift+Alt+1</b>, then others with <b>Shift+Ctrl+C</b> as the last one.</string>
|
||||
</property>
|
||||
<property name="textFormat" >
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line1" >
|
||||
<property name="frameShape" >
|
||||
<enum>QFrame::HLine</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KLineEdit" native="1" name="shortcut" />
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" >
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType" >
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton1" >
|
||||
<property name="text" >
|
||||
<string>&Single Shortcut</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType" >
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton2" >
|
||||
<property name="text" >
|
||||
<string>C&lear</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer>
|
||||
<property name="orientation" >
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType" >
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" >
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line2" >
|
||||
<property name="frameShape" >
|
||||
<enum>QFrame::HLine</enum>
|
||||
</property>
|
||||
<property name="frameShadow" >
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>KLineEdit</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>klineedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>pushButton1</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>EditShortcut</receiver>
|
||||
<slot>editShortcut()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>pushButton2</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>EditShortcut</receiver>
|
||||
<slot>clearShortcut()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "kcm.h"
|
||||
|
||||
#include <QLayout>
|
||||
//Added by qt3to4:
|
||||
#include <QVBoxLayout>
|
||||
#include <KLocalizedString>
|
||||
#include <kaboutdata.h>
|
||||
#include <QtDBus>
|
||||
#include <QX11Info>
|
||||
|
||||
#include "ruleslist.h"
|
||||
#include <KPluginFactory>
|
||||
#include <KPluginLoader>
|
||||
|
||||
K_PLUGIN_FACTORY(KCMRulesFactory,
|
||||
registerPlugin<KWin::KCMRules>();
|
||||
)
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
KCMRules::KCMRules(QWidget *parent, const QVariantList &)
|
||||
: KCModule(parent)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
widget = new KCMRulesList(this);
|
||||
layout->addWidget(widget);
|
||||
connect(widget, SIGNAL(changed(bool)), SLOT(moduleChanged(bool)));
|
||||
KAboutData *about = new KAboutData(QStringLiteral("kcmkwinrules"),
|
||||
i18n("Window-Specific Settings Configuration Module"),
|
||||
QString(), QString(), KAboutLicense::GPL, i18n("(c) 2004 KWin and KControl Authors"));
|
||||
about->addAuthor(i18n("Lubos Lunak"), QString(), "l.lunak@kde.org");
|
||||
setAboutData(about);
|
||||
setButtons(KCModule::Help | KCModule::Apply);
|
||||
}
|
||||
|
||||
void KCMRules::load()
|
||||
{
|
||||
widget->load();
|
||||
emit KCModule::changed(false);
|
||||
}
|
||||
|
||||
void KCMRules::save()
|
||||
{
|
||||
widget->save();
|
||||
emit KCModule::changed(false);
|
||||
// Send signal to all kwin instances
|
||||
QDBusMessage message =
|
||||
QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
|
||||
QDBusConnection::sessionBus().send(message);
|
||||
|
||||
}
|
||||
|
||||
QString KCMRules::quickHelp() const
|
||||
{
|
||||
return i18n("<p><h1>Window-specific Settings</h1> Here you can customize window settings specifically only"
|
||||
" for some windows.</p>"
|
||||
" <p>Please note that this configuration will not take effect if you do not use"
|
||||
" KWin as your window manager. If you do use a different window manager, please refer to its documentation"
|
||||
" for how to customize window behavior.</p>");
|
||||
}
|
||||
|
||||
void KCMRules::moduleChanged(bool state)
|
||||
{
|
||||
emit KCModule::changed(state);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// i18n freeze :-/
|
||||
#if 0
|
||||
I18N_NOOP("Remember settings separately for every window")
|
||||
I18N_NOOP("Show internal settings for remembering")
|
||||
I18N_NOOP("Internal setting for remembering")
|
||||
#endif
|
||||
|
||||
|
||||
#include "kcm.moc"
|
|
@ -1,16 +1,17 @@
|
|||
[Desktop Entry]
|
||||
Exec=kcmshell5 kwinrules
|
||||
Icon=preferences-system-windows-actions
|
||||
Categories=Qt;KDE;X-KDE-settings-looknfeel;
|
||||
Type=Service
|
||||
X-KDE-ServiceTypes=KCModule
|
||||
X-DocPath=kcontrol/windowspecific/index.html
|
||||
|
||||
X-KDE-ServiceTypes=KCModule
|
||||
X-KDE-Library=kcm_kwinrules
|
||||
X-KDE-ParentApp=kcontrol
|
||||
|
||||
X-KDE-System-Settings-Parent-Category=windowmanagement
|
||||
X-KDE-Weight=120
|
||||
|
||||
X-KDE-FormFactors=desktop,tablet
|
||||
|
||||
Name=Window Rules
|
||||
Name[ar]=قواعد النوافذ
|
||||
Name[bg]=Правила за прозорци
|
254
kcmkwin/kwinrules/kcmrules.cpp
Normal file
254
kcmkwin/kwinrules/kcmrules.cpp
Normal file
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "kcmrules.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMessage>
|
||||
|
||||
#include <KAboutData>
|
||||
#include <KConfig>
|
||||
#include <KLocalizedString>
|
||||
#include <KPluginFactory>
|
||||
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
KCMKWinRules::KCMKWinRules(QObject *parent, const QVariantList &arguments)
|
||||
: KQuickAddons::ConfigModule(parent, arguments)
|
||||
, m_ruleBookModel(new RuleBookModel(this))
|
||||
, m_rulesModel(new RulesModel(this))
|
||||
{
|
||||
auto about = new KAboutData(QStringLiteral("kcm_kwinrules"),
|
||||
i18n("Window Rules"),
|
||||
QStringLiteral("1.0"),
|
||||
QString(),
|
||||
KAboutLicense::GPL);
|
||||
about->addAuthor(i18n("Ismael Asensio"),
|
||||
i18n("Author"),
|
||||
QStringLiteral("isma.af@gmail.com"));
|
||||
setAboutData(about);
|
||||
|
||||
setQuickHelp(i18n("<p><h1>Window-specific Settings</h1> Here you can customize window settings specifically only"
|
||||
" for some windows.</p>"
|
||||
" <p>Please note that this configuration will not take effect if you do not use"
|
||||
" KWin as your window manager. If you do use a different window manager, please refer to its documentation"
|
||||
" for how to customize window behavior.</p>"));
|
||||
|
||||
connect(m_rulesModel, &RulesModel::descriptionChanged, this, [this]{
|
||||
if (m_editIndex.isValid()) {
|
||||
m_ruleBookModel->setDescriptionAt(m_editIndex.row(), m_rulesModel->description());
|
||||
}
|
||||
} );
|
||||
connect(m_rulesModel, &RulesModel::dataChanged, this, &KCMKWinRules::updateNeedsSave);
|
||||
connect(m_ruleBookModel, &RulesModel::dataChanged, this, &KCMKWinRules::updateNeedsSave);
|
||||
}
|
||||
|
||||
void KCMKWinRules::load()
|
||||
{
|
||||
m_ruleBookModel->load();
|
||||
|
||||
m_editIndex = QModelIndex();
|
||||
emit editIndexChanged();
|
||||
|
||||
setNeedsSave(false);
|
||||
}
|
||||
|
||||
void KCMKWinRules::save()
|
||||
{
|
||||
saveCurrentRule();
|
||||
|
||||
m_ruleBookModel->save();
|
||||
|
||||
// Notify kwin to reload configuration
|
||||
QDBusMessage message = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
|
||||
QDBusConnection::sessionBus().send(message);
|
||||
}
|
||||
|
||||
void KCMKWinRules::updateNeedsSave()
|
||||
{
|
||||
setNeedsSave(true);
|
||||
emit needsSaveChanged();
|
||||
}
|
||||
|
||||
void KCMKWinRules::saveCurrentRule()
|
||||
{
|
||||
if (m_editIndex.isValid() && needsSave()) {
|
||||
m_ruleBookModel->setRuleAt(m_editIndex.row(), m_rulesModel->exportToRules());
|
||||
}
|
||||
}
|
||||
|
||||
int KCMKWinRules::editIndex() const
|
||||
{
|
||||
if (!m_editIndex.isValid()) {
|
||||
return -1;
|
||||
}
|
||||
return m_editIndex.row();
|
||||
}
|
||||
|
||||
|
||||
void KCMKWinRules::setRuleDescription(int index, const QString &description)
|
||||
{
|
||||
if (index < 0 || index >= m_ruleBookModel->rowCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_editIndex.row() == index) {
|
||||
m_rulesModel->setDescription(description);
|
||||
return;
|
||||
}
|
||||
m_ruleBookModel->setDescriptionAt(index, description);
|
||||
|
||||
updateNeedsSave();
|
||||
}
|
||||
|
||||
|
||||
void KCMKWinRules::editRule(int index)
|
||||
{
|
||||
if (index < 0 || index >= m_ruleBookModel->rowCount()) {
|
||||
return;
|
||||
}
|
||||
saveCurrentRule();
|
||||
|
||||
m_editIndex = m_ruleBookModel->index(index);
|
||||
emit editIndexChanged();
|
||||
|
||||
m_rulesModel->importFromRules(m_ruleBookModel->ruleAt(m_editIndex.row()));
|
||||
|
||||
// Set the active page to rules editor (0:RulesList, 1:RulesEditor)
|
||||
setCurrentIndex(1);
|
||||
}
|
||||
|
||||
void KCMKWinRules::createRule()
|
||||
{
|
||||
const int newIndex = m_ruleBookModel->rowCount();
|
||||
m_ruleBookModel->insertRow(newIndex);
|
||||
|
||||
updateNeedsSave();
|
||||
|
||||
editRule(newIndex);
|
||||
}
|
||||
|
||||
void KCMKWinRules::removeRule(int index)
|
||||
{
|
||||
if (index < 0 || index >= m_ruleBookModel->rowCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_ruleBookModel->removeRow(index);
|
||||
|
||||
emit editIndexChanged();
|
||||
updateNeedsSave();
|
||||
}
|
||||
|
||||
void KCMKWinRules::moveRule(int sourceIndex, int destIndex)
|
||||
{
|
||||
const int lastIndex = m_ruleBookModel->rowCount() - 1;
|
||||
if (sourceIndex == destIndex
|
||||
|| (sourceIndex < 0 || sourceIndex > lastIndex)
|
||||
|| (destIndex < 0 || destIndex > lastIndex)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_ruleBookModel->moveRow(QModelIndex(), sourceIndex, QModelIndex(), destIndex);
|
||||
|
||||
emit editIndexChanged();
|
||||
updateNeedsSave();
|
||||
}
|
||||
|
||||
void KCMKWinRules::exportToFile(const QUrl &path, const QList<int> &indexes)
|
||||
{
|
||||
if (indexes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
saveCurrentRule();
|
||||
|
||||
const auto config = KSharedConfig::openConfig(path.toLocalFile(), KConfig::SimpleConfig);
|
||||
|
||||
for (const QString &groupName : config->groupList()) {
|
||||
config->deleteGroup(groupName);
|
||||
}
|
||||
|
||||
for (int index : indexes) {
|
||||
if (index < 0 || index > m_ruleBookModel->rowCount()) {
|
||||
continue;
|
||||
}
|
||||
const Rules *rule = m_ruleBookModel->ruleAt(index);
|
||||
RuleSettings settings(config, rule->description);
|
||||
settings.setDefaults();
|
||||
rule->write(&settings);
|
||||
settings.save();
|
||||
}
|
||||
}
|
||||
|
||||
void KCMKWinRules::importFromFile(const QUrl &path)
|
||||
{
|
||||
const auto config = KSharedConfig::openConfig(path.toLocalFile(), KConfig::SimpleConfig);
|
||||
const QStringList groups = config->groupList();
|
||||
if (groups.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const QString &groupName : groups) {
|
||||
RuleSettings settings(config, groupName);
|
||||
|
||||
const bool remove = settings.deleteRule();
|
||||
const QString importDescription = settings.description();
|
||||
if (importDescription.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to find a rule with the same description to replace
|
||||
int newIndex = -2;
|
||||
for (int index = 0; index < m_ruleBookModel->rowCount(); index++) {
|
||||
if (m_ruleBookModel->descriptionAt(index) == importDescription) {
|
||||
newIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
m_ruleBookModel->removeRow(newIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newIndex < 0) {
|
||||
newIndex = m_ruleBookModel->rowCount();
|
||||
m_ruleBookModel->insertRow(newIndex);
|
||||
}
|
||||
|
||||
m_ruleBookModel->setRuleAt(newIndex, new Rules(&settings));
|
||||
|
||||
// Reset rule editor if the current rule changed when importing
|
||||
if (m_editIndex.row() == newIndex) {
|
||||
m_rulesModel->importFromRules(m_ruleBookModel->ruleAt(m_editIndex.row()));
|
||||
}
|
||||
}
|
||||
|
||||
updateNeedsSave();
|
||||
}
|
||||
|
||||
K_PLUGIN_CLASS_WITH_JSON(KCMKWinRules, "kcm_kwinrules.json");
|
||||
|
||||
} // namespace
|
||||
|
||||
#include "kcmrules.moc"
|
75
kcmkwin/kwinrules/kcmrules.h
Normal file
75
kcmkwin/kwinrules/kcmrules.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <rules.h>
|
||||
#include "rulebookmodel.h"
|
||||
#include "rulesmodel.h"
|
||||
|
||||
#include <KQuickAddons/ConfigModule>
|
||||
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class KCMKWinRules : public KQuickAddons::ConfigModule
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(RuleBookModel *ruleBookModel MEMBER m_ruleBookModel CONSTANT)
|
||||
Q_PROPERTY(RulesModel *rulesModel MEMBER m_rulesModel CONSTANT)
|
||||
Q_PROPERTY(int editIndex READ editIndex NOTIFY editIndexChanged)
|
||||
|
||||
public:
|
||||
explicit KCMKWinRules(QObject *parent, const QVariantList &arguments);
|
||||
|
||||
Q_INVOKABLE void setRuleDescription(int index, const QString &description);
|
||||
Q_INVOKABLE void editRule(int index);
|
||||
|
||||
Q_INVOKABLE void createRule();
|
||||
Q_INVOKABLE void removeRule(int index);
|
||||
Q_INVOKABLE void moveRule(int sourceIndex, int destIndex);
|
||||
|
||||
Q_INVOKABLE void exportToFile(const QUrl &path, const QList<int> &indexes);
|
||||
Q_INVOKABLE void importFromFile(const QUrl &path);
|
||||
|
||||
public slots:
|
||||
void load() override;
|
||||
void save() override;
|
||||
|
||||
signals:
|
||||
void editIndexChanged();
|
||||
|
||||
private slots:
|
||||
void updateNeedsSave();
|
||||
|
||||
private:
|
||||
int editIndex() const;
|
||||
void saveCurrentRule();
|
||||
|
||||
private:
|
||||
RuleBookModel *m_ruleBookModel;
|
||||
RulesModel* m_rulesModel;
|
||||
|
||||
QPersistentModelIndex m_editIndex;
|
||||
};
|
||||
|
||||
} // namespace
|
|
@ -22,8 +22,8 @@
|
|||
#include <KLocalizedString>
|
||||
#include <kwindowsystem.h>
|
||||
|
||||
#include "ruleswidget.h"
|
||||
#include "rulebooksettings.h"
|
||||
#include "rulesdialog.h"
|
||||
#include "../../rules.h"
|
||||
#include <QByteArray>
|
||||
|
||||
|
|
210
kcmkwin/kwinrules/optionsmodel.cpp
Normal file
210
kcmkwin/kwinrules/optionsmodel.cpp
Normal file
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "optionsmodel.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
QHash<int, QByteArray> OptionsModel::roleNames() const
|
||||
{
|
||||
return {
|
||||
{Qt::DisplayRole, QByteArrayLiteral("display")},
|
||||
{Qt::DecorationRole, QByteArrayLiteral("decoration")},
|
||||
{Qt::ToolTipRole, QByteArrayLiteral("tooltip")},
|
||||
{Qt::UserRole, QByteArrayLiteral("value")},
|
||||
{Qt::UserRole + 1, QByteArrayLiteral("iconName")},
|
||||
};
|
||||
}
|
||||
|
||||
int OptionsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
QVariant OptionsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
const Data data = m_data.at(index.row());
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return data.text;
|
||||
case Qt::UserRole:
|
||||
return data.value;
|
||||
case Qt::DecorationRole:
|
||||
return data.icon;
|
||||
case Qt::UserRole + 1:
|
||||
return data.icon.name();
|
||||
case Qt::ToolTipRole:
|
||||
return data.description;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
int OptionsModel::selectedIndex() const
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
int OptionsModel::indexOf(QVariant value) const
|
||||
{
|
||||
for (int index = 0; index < m_data.count(); index++) {
|
||||
if (m_data.at(index).value == value) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString OptionsModel::textOfValue(QVariant value) const
|
||||
{
|
||||
int index = indexOf(value);
|
||||
if (index < 0 || index >= m_data.count()) {
|
||||
return QString();
|
||||
}
|
||||
return m_data.at(index).text;
|
||||
}
|
||||
|
||||
QVariant OptionsModel::value() const
|
||||
{
|
||||
if (m_data.isEmpty()) {
|
||||
return QVariant();
|
||||
}
|
||||
return m_data.at(m_index).value;
|
||||
}
|
||||
|
||||
void OptionsModel::setValue(QVariant value)
|
||||
{
|
||||
if (this->value() == value) {
|
||||
return;
|
||||
}
|
||||
int index = indexOf(value);
|
||||
if (index >= 0 && index != m_index) {
|
||||
m_index = index;
|
||||
emit selectedIndexChanged(index);
|
||||
}
|
||||
}
|
||||
|
||||
void OptionsModel::resetValue()
|
||||
{
|
||||
m_index = 0;
|
||||
emit selectedIndexChanged(m_index);
|
||||
}
|
||||
|
||||
void OptionsModel::updateModelData(const QList<Data> &data) {
|
||||
beginResetModel();
|
||||
m_data = data;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
|
||||
RulePolicy::Type RulePolicy::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
int RulePolicy::value() const
|
||||
{
|
||||
if (m_type == RulePolicy::NoPolicy) {
|
||||
return Rules::Apply; // To simplify external checks when rule has no policy
|
||||
}
|
||||
return OptionsModel::value().toInt();
|
||||
}
|
||||
|
||||
QString RulePolicy::policyKey(const QString &key) const
|
||||
{
|
||||
switch (m_type) {
|
||||
case NoPolicy:
|
||||
return QString();
|
||||
case StringMatch:
|
||||
return QStringLiteral("%1match").arg(key);
|
||||
case SetRule:
|
||||
case ForceRule:
|
||||
return QStringLiteral("%1rule").arg(key);
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QList<RulePolicy::Data> RulePolicy::policyOptions(RulePolicy::Type type)
|
||||
{
|
||||
static const auto stringMatchOptions = QList<RulePolicy::Data> {
|
||||
{Rules::UnimportantMatch, i18n("Unimportant")},
|
||||
{Rules::ExactMatch, i18n("Exact Match")},
|
||||
{Rules::SubstringMatch, i18n("Substring Match")},
|
||||
{Rules::RegExpMatch, i18n("Regular Expression")}
|
||||
};
|
||||
|
||||
static const auto setRuleOptions = QList<RulePolicy::Data> {
|
||||
{Rules::DontAffect,
|
||||
i18n("Do Not Affect"),
|
||||
i18n("The window property will not be affected and therefore the default handling for it will be used."
|
||||
"\nSpecifying this will block more generic window settings from taking effect.")},
|
||||
{Rules::Apply,
|
||||
i18n("Apply Initially"),
|
||||
i18n("The window property will be only set to the given value after the window is created."
|
||||
"\nNo further changes will be affected.")},
|
||||
{Rules::Remember,
|
||||
i18n("Remember"),
|
||||
i18n("The value of the window property will be remembered and, every time the window"
|
||||
" is created, the last remembered value will be applied.")},
|
||||
{Rules::Force,
|
||||
i18n("Force"),
|
||||
i18n("The window property will be always forced to the given value.")},
|
||||
{Rules::ApplyNow,
|
||||
i18n("Apply Now"),
|
||||
i18n("The window property will be set to the given value immediately and will not be affected later"
|
||||
"\n(this action will be deleted afterwards).")},
|
||||
{Rules::ForceTemporarily,
|
||||
i18n("Force Temporarily"),
|
||||
i18n("The window property will be forced to the given value until it is hidden"
|
||||
"\n(this action will be deleted after the window is hidden).")}
|
||||
};
|
||||
|
||||
static auto forceRuleOptions = QList<RulePolicy::Data> {
|
||||
setRuleOptions.at(0), // Rules::DontAffect
|
||||
setRuleOptions.at(3), // Rules::Force
|
||||
setRuleOptions.at(5), // Rules::ForceTemporarily
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case NoPolicy:
|
||||
return {};
|
||||
case StringMatch:
|
||||
return stringMatchOptions;
|
||||
case SetRule:
|
||||
return setRuleOptions;
|
||||
case ForceRule:
|
||||
return forceRuleOptions;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} //namespace
|
115
kcmkwin/kwinrules/optionsmodel.h
Normal file
115
kcmkwin/kwinrules/optionsmodel.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KWIN_OPTIONS_MODEL_H
|
||||
#define KWIN_OPTIONS_MODEL_H
|
||||
|
||||
#include <rules.h>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QIcon>
|
||||
#include <QVariant>
|
||||
|
||||
|
||||
namespace KWin {
|
||||
|
||||
class OptionsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int selectedIndex READ selectedIndex NOTIFY selectedIndexChanged)
|
||||
|
||||
public:
|
||||
struct Data {
|
||||
Data(const QVariant &value, const QString &text, const QIcon &icon = {}, const QString &description = {})
|
||||
: value(value)
|
||||
, text(text)
|
||||
, icon(icon)
|
||||
, description(description)
|
||||
{}
|
||||
Data(const QVariant &value, const QString &text, const QString &description)
|
||||
: value(value)
|
||||
, text(text)
|
||||
, description(description)
|
||||
{}
|
||||
|
||||
QVariant value;
|
||||
QString text;
|
||||
QIcon icon;
|
||||
QString description;
|
||||
};
|
||||
|
||||
public:
|
||||
OptionsModel() : QAbstractListModel(), m_data(), m_index(0) {};
|
||||
OptionsModel(const QList<Data> &data) : QAbstractListModel(), m_data(data), m_index(0) {};
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
QVariant value() const;
|
||||
void setValue(QVariant value);
|
||||
void resetValue();
|
||||
|
||||
void updateModelData(const QList<Data> &data);
|
||||
|
||||
Q_INVOKABLE int indexOf(QVariant value) const;
|
||||
Q_INVOKABLE QString textOfValue(QVariant value) const;
|
||||
int selectedIndex() const;
|
||||
|
||||
signals:
|
||||
void selectedIndexChanged(int index);
|
||||
|
||||
public:
|
||||
QList<Data> m_data;
|
||||
|
||||
protected:
|
||||
int m_index = 0;
|
||||
};
|
||||
|
||||
class RulePolicy : public OptionsModel
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
NoPolicy,
|
||||
StringMatch,
|
||||
SetRule,
|
||||
ForceRule
|
||||
};
|
||||
|
||||
public:
|
||||
RulePolicy(Type type)
|
||||
: OptionsModel(policyOptions(type))
|
||||
, m_type(type)
|
||||
{};
|
||||
|
||||
Type type() const;
|
||||
int value() const;
|
||||
QString policyKey(const QString &key) const;
|
||||
|
||||
private:
|
||||
static QList<Data> policyOptions(RulePolicy::Type type);
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
||||
#endif //KWIN_OPTIONS_MODEL_H
|
59
kcmkwin/kwinrules/package/contents/ui/FileDialogLoader.qml
Normal file
59
kcmkwin/kwinrules/package/contents/ui/FileDialogLoader.qml
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.14
|
||||
import QtQuick.Dialogs 1.0 as QtDialogs
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
active: false
|
||||
|
||||
property string title : i18n("Select File");
|
||||
property string lastFolder : ""
|
||||
property bool isSaveDialog : false
|
||||
|
||||
signal fileSelected(string path)
|
||||
|
||||
sourceComponent: QtDialogs.FileDialog {
|
||||
id: fileDialog
|
||||
|
||||
title: root.title
|
||||
selectExisting: !root.isSaveDialog
|
||||
folder: (root.lastFolder == "") ? shortcuts.home : root.lastFolder
|
||||
nameFilters: [ i18n("KWin Rules (*.kwinrule)") ]
|
||||
defaultSuffix: "*.kwinrule"
|
||||
|
||||
Component.onCompleted: {
|
||||
open();
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
root.lastFolder = folder;
|
||||
if (fileUrl != "") {
|
||||
root.fileSelected(fileUrl);
|
||||
}
|
||||
root.active = false;
|
||||
}
|
||||
|
||||
onRejected: {
|
||||
root.active = false;
|
||||
}
|
||||
}
|
||||
}
|
116
kcmkwin/kwinrules/package/contents/ui/OptionsComboBox.qml
Normal file
116
kcmkwin/kwinrules/package/contents/ui/OptionsComboBox.qml
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
import QtQuick.Controls 2.14 as QQC2
|
||||
|
||||
import org.kde.kirigami 2.10 as Kirigami
|
||||
|
||||
|
||||
QQC2.ComboBox {
|
||||
id: optionsCombo
|
||||
|
||||
textRole: "display"
|
||||
//TODO: After KF5 (qqc2-desktop-style) depends on Qt 5.15 this can be simplified using newer API
|
||||
// (https://bugs.kde.org/show_bug.cgi?id=419521)
|
||||
// valueRole: "value"
|
||||
property var currentValue
|
||||
|
||||
onActivated: (index) => {
|
||||
var modelIndex = model.index(index, 0);
|
||||
currentValue = model.data(modelIndex, Qt.UserRole);
|
||||
}
|
||||
|
||||
property bool multipleChoice: false
|
||||
property int selectionMask: 0
|
||||
|
||||
currentIndex: multipleChoice ? -1 : model.selectedIndex
|
||||
|
||||
displayText: {
|
||||
if (!multipleChoice) {
|
||||
return currentText;
|
||||
}
|
||||
var selectionCount = selectionMask.toString(2).replace(/0/g, '').length;
|
||||
switch (selectionCount) {
|
||||
case 0:
|
||||
return i18n("None selected");
|
||||
case 1:
|
||||
var selectedValue = selectionMask.toString(2).length - 1;
|
||||
return model.textOfValue(selectedValue);
|
||||
case count:
|
||||
return i18n("All selected");
|
||||
}
|
||||
return i18np("%1 selected", "%1 selected", selectionCount);
|
||||
}
|
||||
|
||||
delegate: QQC2.ItemDelegate {
|
||||
highlighted: optionsCombo.highlightedIndex == index
|
||||
width: parent.width
|
||||
|
||||
contentItem: RowLayout {
|
||||
QQC2.CheckBox {
|
||||
id: itemSelection
|
||||
visible: multipleChoice
|
||||
checked: (selectionMask & (1 << value))
|
||||
onToggled: {
|
||||
selectionMask = (selectionMask & ~(1 << value)) | (checked << value);
|
||||
activated(index);
|
||||
}
|
||||
}
|
||||
Kirigami.Icon {
|
||||
source: model.decoration
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.small
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.small
|
||||
}
|
||||
QQC2.Label {
|
||||
text: model.display
|
||||
color: highlighted ? Kirigami.Theme.highlightedTextColor : Kirigami.Theme.textColor
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: contentItem
|
||||
enabled: multipleChoice
|
||||
onClicked: {
|
||||
itemSelection.toggle();
|
||||
itemSelection.toggled();
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ToolTip {
|
||||
text: model.tooltip
|
||||
visible: hovered && (model.tooltip != "")
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
//FIXME: work around bug https://bugs.kde.org/show_bug.cgi?id=403153
|
||||
optionsCombo.popup.width = Math.max(implicitWidth, optionsCombo.width, optionsCombo.popup.width);
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (!activeFocus) {
|
||||
popup.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
117
kcmkwin/kwinrules/package/contents/ui/RuleItemDelegate.qml
Normal file
117
kcmkwin/kwinrules/package/contents/ui/RuleItemDelegate.qml
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
import QtQuick.Controls 2.14 as QQC2
|
||||
import org.kde.kirigami 2.10 as Kirigami
|
||||
|
||||
Kirigami.AbstractListItem {
|
||||
id: ruleDelegate
|
||||
|
||||
property bool ruleEnabled: model.enabled
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
|
||||
width: ListView.view.width
|
||||
highlighted: false
|
||||
hoverEnabled: false
|
||||
|
||||
RowLayout {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: Kirigami.Units.smallSpacing
|
||||
}
|
||||
|
||||
Kirigami.Icon {
|
||||
id: itemIcon
|
||||
source: model.icon
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
|
||||
Layout.rightMargin: Kirigami.Units.smallSpacing
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
text: model.name
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideRight
|
||||
Layout.preferredWidth: 10 * Kirigami.Units.gridUnit
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
// This layout keeps the width constant between delegates, independent of items visibility
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 20 * Kirigami.Units.gridUnit
|
||||
Layout.minimumWidth: 13 * Kirigami.Units.gridUnit
|
||||
|
||||
OptionsComboBox {
|
||||
id: policyCombo
|
||||
Layout.preferredWidth: 50 // 50%
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
flat: true
|
||||
|
||||
visible: count > 0
|
||||
enabled: ruleEnabled
|
||||
|
||||
model: policyModel
|
||||
onActivated: {
|
||||
policy = currentValue;
|
||||
}
|
||||
}
|
||||
|
||||
ValueEditor {
|
||||
id: valueEditor
|
||||
Layout.preferredWidth: 50 // 50%
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
|
||||
enabled: model.enabled
|
||||
|
||||
ruleValue: model.value
|
||||
ruleOptions: model.options
|
||||
controlType: model.type
|
||||
|
||||
onValueEdited: (value) => {
|
||||
model.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
id: itemEnabled
|
||||
icon.name: "edit-delete"
|
||||
visible: model.selectable
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
onClicked: {
|
||||
model.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ToolTip {
|
||||
text: model.description
|
||||
visible: hovered && (text != "")
|
||||
}
|
||||
}
|
||||
}
|
270
kcmkwin/kwinrules/package/contents/ui/RulesEditor.qml
Normal file
270
kcmkwin/kwinrules/package/contents/ui/RulesEditor.qml
Normal file
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
import QtQuick.Controls 2.14 as QQC2
|
||||
import org.kde.kirigami 2.10 as Kirigami
|
||||
import org.kde.kcm 1.2
|
||||
import org.kde.kitemmodels 1.0
|
||||
import org.kde.kcms.kwinrules 1.0
|
||||
|
||||
|
||||
ScrollViewKCM {
|
||||
id: rulesEditor
|
||||
|
||||
property var rulesModel: kcm.rulesModel
|
||||
|
||||
title: rulesModel.description
|
||||
|
||||
view: ListView {
|
||||
id: rulesView
|
||||
clip: true
|
||||
|
||||
model: enabledRulesModel
|
||||
delegate: RuleItemDelegate {}
|
||||
section {
|
||||
property: "section"
|
||||
delegate: Kirigami.ListSectionHeader { label: section }
|
||||
}
|
||||
|
||||
Item {
|
||||
id: hintArea
|
||||
visible: rulesView.count <= 4
|
||||
anchors {
|
||||
top: parent.contentItem.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
QQC2.Button {
|
||||
anchors.centerIn: parent
|
||||
text: i18n("Add Properties...")
|
||||
icon.name: "list-add-symbolic"
|
||||
onClicked: {
|
||||
propertySheet.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: InlineMessage.qml:241:13: QML Label: Binding loop detected for property "verticalAlignment"
|
||||
header: Kirigami.InlineMessage {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
text: rulesModel.warningMessage
|
||||
visible: text != ""
|
||||
}
|
||||
|
||||
footer: RowLayout {
|
||||
QQC2.Button {
|
||||
text: checked ? i18n("Close") : i18n("Add Properties...")
|
||||
icon.name: checked ? "dialog-close" : "list-add-symbolic"
|
||||
checkable: true
|
||||
checked: propertySheet.sheetOpen
|
||||
visible: !hintArea.visible || checked
|
||||
onToggled: {
|
||||
propertySheet.sheetOpen = checked;
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.Button {
|
||||
text: i18n("Detect Window Properties")
|
||||
icon.name: "edit-find"
|
||||
onClicked: {
|
||||
overlayModel.onlySuggestions = true;
|
||||
rulesModel.detectWindowProperties(delaySpin.value);
|
||||
}
|
||||
}
|
||||
QQC2.SpinBox {
|
||||
id: delaySpin
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 8
|
||||
from: 0
|
||||
to: 30
|
||||
textFromValue: (value, locale) => {
|
||||
return (value == 0) ? i18n("Instantly")
|
||||
: i18np("After %1 second", "After %1 seconds", value)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: rulesModel
|
||||
onSuggestionsChanged: {
|
||||
propertySheet.sheetOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.OverlaySheet {
|
||||
id: propertySheet
|
||||
|
||||
parent: view
|
||||
|
||||
header: Kirigami.Heading {
|
||||
text: i18n("Select properties")
|
||||
}
|
||||
footer: Kirigami.SearchField {
|
||||
id: searchField
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: overlayView
|
||||
model: overlayModel
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 28
|
||||
|
||||
section {
|
||||
property: "section"
|
||||
delegate: Kirigami.ListSectionHeader { label: section }
|
||||
}
|
||||
|
||||
delegate: Kirigami.AbstractListItem {
|
||||
id: propertyDelegate
|
||||
highlighted: false
|
||||
width: ListView.view.width
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Kirigami.Icon {
|
||||
source: model.icon
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
QQC2.Label {
|
||||
id: itemNameLabel
|
||||
text: model.name
|
||||
horizontalAlignment: Qt.AlignLeft
|
||||
Layout.preferredWidth: implicitWidth
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
QQC2.ToolTip {
|
||||
text: model.description
|
||||
visible: hovered && (model.description != "")
|
||||
}
|
||||
}
|
||||
QQC2.Label {
|
||||
id: suggestedLabel
|
||||
text: formatValue(model.suggested, model.type, model.options)
|
||||
horizontalAlignment: Text.AlignRight
|
||||
elide: Text.ElideRight
|
||||
opacity: 0.7
|
||||
Layout.maximumWidth: propertyDelegate.width - itemNameLabel.implicitWidth - Kirigami.Units.gridUnit * 6
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
QQC2.ToolTip {
|
||||
text: suggestedLabel.text
|
||||
visible: hovered && suggestedLabel.truncated
|
||||
}
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
icon.name: (model.enabled) ? "dialog-ok-apply" : "list-add-symbolic"
|
||||
opacity: propertyDelegate.hovered ? 1 : 0
|
||||
onClicked: propertyDelegate.clicked()
|
||||
Layout.preferredWidth: implicitWidth
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
model.enabled = true;
|
||||
if (model.suggested != null) {
|
||||
model.value = model.suggested;
|
||||
model.suggested = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onSheetOpenChanged: {
|
||||
searchField.text = "";
|
||||
if (sheetOpen) {
|
||||
searchField.forceActiveFocus();
|
||||
} else {
|
||||
overlayModel.onlySuggestions = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function formatValue(value, type, options) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
switch (type) {
|
||||
case RuleItem.Boolean:
|
||||
return value ? i18n("Yes") : i18n("No");
|
||||
case RuleItem.Percentage:
|
||||
return i18n("%1 %", value);
|
||||
case RuleItem.Coordinate:
|
||||
var point = value.split(',');
|
||||
return i18nc("Coordinates (x, y)", "(%1, %2)", point[0], point[1]);
|
||||
case RuleItem.Option:
|
||||
return options.textOfValue(value);
|
||||
case RuleItem.FlagsOption:
|
||||
var selectedValue = value.toString(2).length - 1;
|
||||
return options.textOfValue(selectedValue);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
KSortFilterProxyModel {
|
||||
id: enabledRulesModel
|
||||
sourceModel: rulesModel
|
||||
filterRowCallback: (source_row, source_parent) => {
|
||||
var index = sourceModel.index(source_row, 0, source_parent);
|
||||
return sourceModel.data(index, RulesModel.EnabledRole);
|
||||
}
|
||||
}
|
||||
|
||||
KSortFilterProxyModel {
|
||||
id: overlayModel
|
||||
sourceModel: rulesModel
|
||||
|
||||
property bool onlySuggestions: false
|
||||
onOnlySuggestionsChanged: {
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
filterString: searchField.text.trim().toLowerCase()
|
||||
filterRowCallback: (source_row, source_parent) => {
|
||||
var index = sourceModel.index(source_row, 0, source_parent);
|
||||
|
||||
var hasSuggestion = sourceModel.data(index, RulesModel.SuggestedValueRole) != null;
|
||||
var isOptional = sourceModel.data(index, RulesModel.SelectableRole);
|
||||
var isEnabled = sourceModel.data(index, RulesModel.EnabledRole);
|
||||
|
||||
var showItem = hasSuggestion || (!onlySuggestions && isOptional && !isEnabled);
|
||||
|
||||
if (!showItem) {
|
||||
return false;
|
||||
}
|
||||
if (filterString != "") {
|
||||
return sourceModel.data(index, RulesModel.NameRole).toLowerCase().includes(filterString)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
252
kcmkwin/kwinrules/package/contents/ui/RulesList.qml
Normal file
252
kcmkwin/kwinrules/package/contents/ui/RulesList.qml
Normal file
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
import QtQuick.Controls 2.14 as QQC2
|
||||
import QtQml.Models 2.14
|
||||
import org.kde.kcm 1.2
|
||||
import org.kde.kirigami 2.5 as Kirigami
|
||||
|
||||
ScrollViewKCM {
|
||||
id: rulesListKCM
|
||||
|
||||
// FIXME: ScrollViewKCM.qml:73:13: QML Control: Binding loop detected for property "implicitHeight"
|
||||
implicitWidth: Kirigami.Units.gridUnit * 35
|
||||
implicitHeight: Kirigami.Units.gridUnit * 25
|
||||
|
||||
ConfigModule.columnWidth: Kirigami.Units.gridUnit * 23
|
||||
ConfigModule.buttons: ConfigModule.Apply
|
||||
|
||||
property var selectedIndexes: []
|
||||
|
||||
// Manage KCM pages
|
||||
Connections {
|
||||
target: kcm
|
||||
onEditIndexChanged: {
|
||||
if (kcm.editIndex < 0) {
|
||||
// If no rule is being edited, hide RulesEdidor page
|
||||
kcm.pop();
|
||||
} else if (kcm.depth < 2) {
|
||||
// Add the RulesEditor page if it wasn't already
|
||||
kcm.push("RulesEditor.qml");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
view: ListView {
|
||||
id: ruleBookView
|
||||
clip: true
|
||||
|
||||
model: kcm.ruleBookModel
|
||||
currentIndex: kcm.editIndex
|
||||
delegate: Kirigami.DelegateRecycler {
|
||||
width: ruleBookView.width
|
||||
sourceComponent: ruleBookDelegate
|
||||
}
|
||||
|
||||
displaced: Transition {
|
||||
NumberAnimation { properties: "y"; duration: Kirigami.Units.longDuration }
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
level: 3
|
||||
enabled: false
|
||||
visible: ruleBookView.count == 0
|
||||
anchors.fill: parent
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
wrapMode: Text.WordWrap
|
||||
text: i18n("No rules for specific windows are currently set");
|
||||
}
|
||||
}
|
||||
|
||||
header: Kirigami.InlineMessage {
|
||||
id: exportInfo
|
||||
icon.source: "document-export"
|
||||
showCloseButton: true
|
||||
text: i18n("Select the rules to export")
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
iconName: "document-save"
|
||||
text: i18n("Save Rules")
|
||||
// FIXME: It does not update on selection changes
|
||||
// enabled: selectedIndexes.length > 0
|
||||
onTriggered: {
|
||||
exportDialog.active = true;
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
footer: RowLayout {
|
||||
QQC2.Button {
|
||||
text: i18n("Add New...")
|
||||
icon.name: "list-add-symbolic"
|
||||
enabled: !exportInfo.visible
|
||||
onClicked: {
|
||||
kcm.createRule();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.Button {
|
||||
text: i18n("Import...")
|
||||
icon.name: "document-import"
|
||||
enabled: !exportInfo.visible
|
||||
onClicked: {
|
||||
importDialog.active = true;
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
text: i18n("Export...")
|
||||
icon.name: "document-export"
|
||||
enabled: ruleBookView.count > 0
|
||||
checkable: true
|
||||
checked: exportInfo.visible
|
||||
onToggled: {
|
||||
selectedIndexes = []
|
||||
exportInfo.visible = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: ruleBookDelegate
|
||||
Kirigami.AbstractListItem {
|
||||
id: ruleBookItem
|
||||
|
||||
RowLayout {
|
||||
//FIXME: If not used within DelegateRecycler, item goes on top of the first item when clicked
|
||||
Kirigami.ListItemDragHandle {
|
||||
visible: !exportInfo.visible
|
||||
listItem: ruleBookItem
|
||||
listView: ruleBookView
|
||||
onMoveRequested: {
|
||||
kcm.moveRule(oldIndex, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.TextField {
|
||||
id: descriptionField
|
||||
Layout.minimumWidth: Kirigami.Units.gridUnit * 2
|
||||
Layout.fillWidth: true
|
||||
background: Item {}
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: display
|
||||
onEditingFinished: {
|
||||
kcm.setRuleDescription(index, text);
|
||||
}
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
// On <Esc> key reset to model data before losing focus
|
||||
text = display;
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Tab:
|
||||
ruleBookItem.forceActiveFocus();
|
||||
event.accepted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: exportInfo.visible
|
||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.IBeamCursor
|
||||
onClicked: {
|
||||
itemSelectionCheck.toggle();
|
||||
itemSelectionCheck.toggled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.ActionToolBar {
|
||||
Layout.preferredWidth: maximumContentWidth + Kirigami.Units.smallSpacing
|
||||
alignment: Qt.AlignRight
|
||||
display: QQC2.Button.IconOnly
|
||||
visible: !exportInfo.visible
|
||||
opacity: ruleBookItem.hovered ? 1 : 0
|
||||
focus: false
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
text: i18n("Edit")
|
||||
iconName: "edit-entry"
|
||||
onTriggered: {
|
||||
kcm.editRule(index);
|
||||
}
|
||||
}
|
||||
,
|
||||
Kirigami.Action {
|
||||
text: i18n("Delete")
|
||||
iconName: "entry-delete"
|
||||
onTriggered: {
|
||||
kcm.removeRule(index);
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
QQC2.CheckBox {
|
||||
id: itemSelectionCheck
|
||||
visible: exportInfo.visible
|
||||
checked: selectedIndexes.includes(index)
|
||||
onToggled: {
|
||||
var position = selectedIndexes.indexOf(index);
|
||||
if (checked) {
|
||||
if (position < 0) { selectedIndexes.push(index); }
|
||||
} else {
|
||||
if (position >= 0) { selectedIndexes.splice(position, 1); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileDialogLoader {
|
||||
id: importDialog
|
||||
title: i18n("Import Rules")
|
||||
isSaveDialog: false
|
||||
onLastFolderChanged: {
|
||||
exportDialog.lastFolder = lastFolder;
|
||||
}
|
||||
onFileSelected: {
|
||||
kcm.importFromFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
FileDialogLoader {
|
||||
id: exportDialog
|
||||
title: i18n("Export Rules")
|
||||
isSaveDialog: true
|
||||
onLastFolderChanged: {
|
||||
importDialog.lastFolder = lastFolder;
|
||||
}
|
||||
onFileSelected: {
|
||||
selectedIndexes.sort();
|
||||
kcm.exportToFile(path, selectedIndexes);
|
||||
exportInfo.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
200
kcmkwin/kwinrules/package/contents/ui/ValueEditor.qml
Normal file
200
kcmkwin/kwinrules/package/contents/ui/ValueEditor.qml
Normal file
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
import QtQuick.Controls 2.14 as QQC2
|
||||
|
||||
import org.kde.kirigami 2.10 as Kirigami
|
||||
import org.kde.kquickcontrols 2.0 as KQC
|
||||
import org.kde.kcms.kwinrules 1.0
|
||||
|
||||
|
||||
Loader {
|
||||
id: valueEditor
|
||||
focus: true
|
||||
|
||||
property var ruleValue
|
||||
property var ruleOptions
|
||||
property int controlType
|
||||
|
||||
signal valueEdited(var value)
|
||||
|
||||
sourceComponent: {
|
||||
switch (controlType) {
|
||||
case RuleItem.Boolean: return booleanEditor
|
||||
case RuleItem.String: return stringEditor
|
||||
case RuleItem.Integer: return integerEditor
|
||||
case RuleItem.Option: return optionEditor
|
||||
case RuleItem.FlagsOption: return flagsEditor
|
||||
case RuleItem.Percentage: return percentageEditor
|
||||
case RuleItem.Coordinate: return coordinateEditor
|
||||
case RuleItem.Shortcut: return shortcutEditor
|
||||
default: return emptyEditor
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: emptyEditor
|
||||
Item {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: booleanEditor
|
||||
RowLayout {
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.RadioButton {
|
||||
text: i18n("Yes")
|
||||
checked: ruleValue
|
||||
Layout.margins: Kirigami.Units.smallSpacing
|
||||
onToggled: valueEditor.valueEdited(checked)
|
||||
}
|
||||
QQC2.RadioButton {
|
||||
text: i18n("No")
|
||||
checked: !ruleValue
|
||||
Layout.margins: Kirigami.Units.smallSpacing
|
||||
onToggled: valueEditor.valueEdited(!checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: stringEditor
|
||||
QQC2.TextField {
|
||||
property bool isTextEdited: false
|
||||
text: ruleValue
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
onTextEdited: { isTextEdited = true; }
|
||||
onEditingFinished: {
|
||||
if (isTextEdited) { valueEditor.valueEdited(text); }
|
||||
isTextEdited = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: integerEditor
|
||||
QQC2.SpinBox {
|
||||
editable: true
|
||||
value: ruleValue
|
||||
onValueModified: valueEditor.valueEdited(value)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: optionEditor
|
||||
OptionsComboBox {
|
||||
flat: true
|
||||
model: ruleOptions
|
||||
onActivated: (index) => {
|
||||
valueEditor.valueEdited(currentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: flagsEditor
|
||||
OptionsComboBox {
|
||||
flat: true
|
||||
model: ruleOptions
|
||||
multipleChoice: true
|
||||
selectionMask: ruleValue
|
||||
onActivated: {
|
||||
valueEditor.valueEdited(selectionMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: percentageEditor
|
||||
RowLayout {
|
||||
QQC2.Slider {
|
||||
id: slider
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 100
|
||||
value: ruleValue
|
||||
onMoved: valueEditor.valueEdited(Math.round(slider.value))
|
||||
}
|
||||
QQC2.Label {
|
||||
text: i18n("%1 %", Math.round(slider.value))
|
||||
horizontalAlignment: Qt.AlignRight
|
||||
Layout.minimumWidth: maxPercentage.width + Kirigami.Units.smallSpacing
|
||||
Layout.margins: Kirigami.Units.smallSpacing
|
||||
}
|
||||
TextMetrics {
|
||||
id: maxPercentage
|
||||
text: i18n("%1 %", 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: coordinateEditor
|
||||
RowLayout {
|
||||
id: coordItem
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
property var coords: ruleValue ? ruleValue.split(',') : [0, 0]
|
||||
|
||||
QQC2.SpinBox {
|
||||
id: coordX
|
||||
editable: true
|
||||
Layout.preferredWidth: 50 // 50%
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 4098
|
||||
value: coords[0]
|
||||
onValueModified: valueEditor.valueEdited(coordX.value + "," + coordY.value)
|
||||
}
|
||||
QQC2.Label {
|
||||
id: coordSeparator
|
||||
Layout.preferredWidth: implicitWidth
|
||||
text: i18nc("(x, y) coordinates separator in size/position","x")
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
QQC2.SpinBox {
|
||||
id: coordY
|
||||
editable: true
|
||||
from: 0
|
||||
to: 4098
|
||||
Layout.preferredWidth: 50 // 50%
|
||||
Layout.fillWidth: true
|
||||
value: coords[1]
|
||||
onValueModified: valueEditor.valueEdited(coordX.value + "," + coordY.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: shortcutEditor
|
||||
RowLayout {
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
KQC.KeySequenceItem {
|
||||
keySequence: ruleValue
|
||||
onCaptureFinished: valueEditor.valueEdited(keySequence)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
kcmkwin/kwinrules/package/metadata.desktop
Normal file
19
kcmkwin/kwinrules/package/metadata.desktop
Normal file
|
@ -0,0 +1,19 @@
|
|||
[Desktop Entry]
|
||||
Icon=preferences-system-windows-actions
|
||||
Type=Service
|
||||
Keywords=
|
||||
X-KDE-ParentApp=
|
||||
X-KDE-System-Settings-Parent-Category=applicationstyle
|
||||
X-KDE-PluginInfo-Author=Ismael Asensio
|
||||
X-KDE-PluginInfo-Email=isma.af@gmail.com
|
||||
X-KDE-PluginInfo-License=GPL-2.0+
|
||||
X-KDE-PluginInfo-Name=kcm_kwinrules
|
||||
X-KDE-PluginInfo-Version=
|
||||
X-KDE-PluginInfo-Website=https://www.kde.org/plasma-desktop
|
||||
X-KDE-ServiceTypes=Plasma/Generic
|
||||
X-Plasma-API=declarativeappletscript
|
||||
X-Plasma-MainScript=ui/RulesList.qml
|
||||
X-KDE-FormFactors=desktop,tablet
|
||||
|
||||
Name=Window Rules
|
||||
Comment=Individual Window Behavior
|
187
kcmkwin/kwinrules/rulebookmodel.cpp
Normal file
187
kcmkwin/kwinrules/rulebookmodel.cpp
Normal file
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rulebookmodel.h"
|
||||
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
RuleBookModel::RuleBookModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_ruleBook(new RuleBookSettings(this))
|
||||
{
|
||||
}
|
||||
|
||||
RuleBookModel::~RuleBookModel()
|
||||
{
|
||||
qDeleteAll(m_rules);
|
||||
}
|
||||
|
||||
int RuleBookModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_rules.count();
|
||||
}
|
||||
|
||||
QVariant RuleBookModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return descriptionAt(index.row());
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
bool RuleBookModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
setDescriptionAt(index.row(), value.toString());
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RuleBookModel::insertRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
if (row < 0 || row > rowCount() || parent.isValid()) {
|
||||
return false;
|
||||
}
|
||||
beginInsertRows(parent, row, row + count - 1);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
Rules *newRule = new Rules();
|
||||
// HACK: Improve integration with RuleSettings and use directly its defaults
|
||||
newRule->wmclassmatch = Rules::ExactMatch;
|
||||
m_rules.insert(row + i, newRule);
|
||||
}
|
||||
|
||||
m_ruleBook->setCount(m_rules.count());
|
||||
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuleBookModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
if (row < 0 || row > rowCount() || parent.isValid()) {
|
||||
return false;
|
||||
}
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
delete m_rules.at(row + i);
|
||||
}
|
||||
m_rules.remove(row, count);
|
||||
m_ruleBook->setCount(m_rules.count());
|
||||
|
||||
endRemoveRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuleBookModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
|
||||
const QModelIndex &destinationParent, int destinationChild)
|
||||
{
|
||||
if (sourceParent != destinationParent || sourceParent != QModelIndex()){
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool isMoveDown = destinationChild > sourceRow;
|
||||
// QAbstractItemModel::beginMoveRows(): when moving rows down in the same parent,
|
||||
// the rows will be placed before the destinationChild index.
|
||||
if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1,
|
||||
destinationParent, isMoveDown ? destinationChild + 1 : destinationChild)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
m_rules.insert(destinationChild + i,
|
||||
m_rules.takeAt(isMoveDown ? sourceRow : sourceRow + i));
|
||||
}
|
||||
|
||||
endMoveRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
QString RuleBookModel::descriptionAt(int row) const
|
||||
{
|
||||
Q_ASSERT (row >= 0 && row < m_rules.count());
|
||||
return m_rules.at(row)->description;
|
||||
}
|
||||
|
||||
Rules *RuleBookModel::ruleAt(int row) const
|
||||
{
|
||||
Q_ASSERT (row >= 0 && row < m_rules.count());
|
||||
return m_rules.at(row);
|
||||
}
|
||||
|
||||
void RuleBookModel::setDescriptionAt(int row, const QString &description)
|
||||
{
|
||||
Q_ASSERT (row >= 0 && row < m_rules.count());
|
||||
if (description == m_rules.at(row)->description) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_rules.at(row)->description = description;
|
||||
|
||||
emit dataChanged(index(row), index(row), QVector<int>{Qt::DisplayRole});
|
||||
}
|
||||
|
||||
void RuleBookModel::setRuleAt(int row, Rules *rule)
|
||||
{
|
||||
Q_ASSERT (row >= 0 && row < m_rules.count());
|
||||
|
||||
delete m_rules.at(row);
|
||||
m_rules[row] = rule;
|
||||
|
||||
emit dataChanged(index(row), index(row), QVector<int>{Qt::DisplayRole});
|
||||
}
|
||||
|
||||
|
||||
void RuleBookModel::load()
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
m_ruleBook->load();
|
||||
qDeleteAll(m_rules);
|
||||
m_rules = m_ruleBook->rules();
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void RuleBookModel::save()
|
||||
{
|
||||
m_ruleBook->setRules(m_rules);
|
||||
m_ruleBook->save();
|
||||
}
|
||||
|
||||
} // namespace
|
64
kcmkwin/kwinrules/rulebookmodel.h
Normal file
64
kcmkwin/kwinrules/rulebookmodel.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rulebooksettings.h"
|
||||
#include <rules.h>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class RuleBookModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RuleBookModel(QObject *parent = nullptr);
|
||||
~RuleBookModel();
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
|
||||
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
|
||||
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
|
||||
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
|
||||
const QModelIndex &destinationParent, int destinationChild) override;
|
||||
|
||||
QString descriptionAt(int row) const;
|
||||
void setDescriptionAt(int row, const QString &description);
|
||||
|
||||
Rules *ruleAt(int row) const;
|
||||
void setRuleAt(int row, Rules *rule);
|
||||
|
||||
void load();
|
||||
void save();
|
||||
|
||||
private:
|
||||
RuleBookSettings *m_ruleBook;
|
||||
QVector<Rules *> m_rules;
|
||||
};
|
||||
|
||||
} // namespace
|
225
kcmkwin/kwinrules/ruleitem.cpp
Normal file
225
kcmkwin/kwinrules/ruleitem.cpp
Normal file
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ruleitem.h"
|
||||
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
RuleItem::RuleItem(const QString &key,
|
||||
const RulePolicy::Type policyType,
|
||||
const RuleItem::Type type,
|
||||
const QString &name,
|
||||
const QString §ion,
|
||||
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 m_policy;
|
||||
delete m_options;
|
||||
}
|
||||
|
||||
void RuleItem::reset()
|
||||
{
|
||||
m_enabled = hasFlag(AlwaysEnabled) | hasFlag(StartEnabled);
|
||||
m_value = typedValue(QVariant(), m_type);
|
||||
m_suggestedValue = QVariant();
|
||||
m_policy->resetValue();
|
||||
if (m_options) {
|
||||
m_options->resetValue();
|
||||
}
|
||||
}
|
||||
|
||||
QString RuleItem::key() const
|
||||
{
|
||||
return m_key;
|
||||
}
|
||||
|
||||
QString RuleItem::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
QString RuleItem::section() const
|
||||
{
|
||||
return m_section;
|
||||
}
|
||||
|
||||
QString RuleItem::iconName() const
|
||||
{
|
||||
return m_icon.name();
|
||||
}
|
||||
|
||||
QIcon RuleItem::icon() const
|
||||
{
|
||||
return m_icon;
|
||||
}
|
||||
|
||||
QString RuleItem::description() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
bool RuleItem::isEnabled() const
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
void RuleItem::setEnabled(bool enabled)
|
||||
{
|
||||
m_enabled = enabled | hasFlag(AlwaysEnabled);
|
||||
}
|
||||
|
||||
bool RuleItem::hasFlag(RuleItem::Flags flag) const
|
||||
{
|
||||
return m_flags.testFlag(flag);
|
||||
}
|
||||
|
||||
void RuleItem::setFlag(RuleItem::Flags flag, bool active)
|
||||
{
|
||||
m_flags.setFlag(flag, active);
|
||||
}
|
||||
|
||||
RuleItem::Type RuleItem::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
QVariant RuleItem::value() const
|
||||
{
|
||||
if (m_type == Option) {
|
||||
return m_options->value();
|
||||
}
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void RuleItem::setValue(QVariant value)
|
||||
{
|
||||
if (m_type == Option) {
|
||||
m_options->setValue(value);
|
||||
}
|
||||
m_value = typedValue(value, m_type);
|
||||
}
|
||||
|
||||
QVariant RuleItem::suggestedValue() const
|
||||
{
|
||||
return m_suggestedValue;
|
||||
}
|
||||
|
||||
void RuleItem::setSuggestedValue(QVariant value, bool forceValue)
|
||||
{
|
||||
if (forceValue) {
|
||||
setValue(value);
|
||||
}
|
||||
m_suggestedValue = value.isNull() ? QVariant() : typedValue(value, m_type);
|
||||
}
|
||||
|
||||
QVariant RuleItem::options() const
|
||||
{
|
||||
if (!m_options) {
|
||||
return QVariant();
|
||||
}
|
||||
return QVariant::fromValue(m_options);
|
||||
}
|
||||
|
||||
void RuleItem::setOptionsData(const QList<OptionsModel::Data> &data)
|
||||
{
|
||||
if (!m_options) {
|
||||
if (m_type != Option && m_type != FlagsOption) {
|
||||
return;
|
||||
}
|
||||
m_options = new OptionsModel();
|
||||
}
|
||||
m_options->updateModelData(data);
|
||||
m_options->setValue(m_value);
|
||||
}
|
||||
|
||||
int RuleItem::policy() const
|
||||
{
|
||||
return m_policy->value();
|
||||
}
|
||||
|
||||
void RuleItem::setPolicy(int policy)
|
||||
{
|
||||
m_policy->setValue(policy);
|
||||
}
|
||||
|
||||
RulePolicy::Type RuleItem::policyType() const
|
||||
{
|
||||
return m_policy->type();
|
||||
}
|
||||
|
||||
QVariant RuleItem::policyModel() const
|
||||
{
|
||||
return QVariant::fromValue(m_policy);
|
||||
}
|
||||
|
||||
QString RuleItem::policyKey() const
|
||||
{
|
||||
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:
|
||||
return value.toString().trimmed();
|
||||
case Shortcut:
|
||||
return value.toString();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
} //namespace
|
||||
|
127
kcmkwin/kwinrules/ruleitem.h
Normal file
127
kcmkwin/kwinrules/ruleitem.h
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KWIN_RULEITEM_H
|
||||
#define KWIN_RULEITEM_H
|
||||
|
||||
#include "optionsmodel.h"
|
||||
|
||||
#include <QFlag>
|
||||
#include <QIcon>
|
||||
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
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 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);
|
||||
|
||||
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 suggestedValue() const;
|
||||
void setSuggestedValue(QVariant value, bool forceValue = false);
|
||||
|
||||
QVariant options() const;
|
||||
void setOptionsData(const QList<OptionsModel::Data> &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:
|
||||
QString m_key;
|
||||
RuleItem::Type m_type;
|
||||
QString m_name;
|
||||
QString m_section;
|
||||
QIcon m_icon;
|
||||
QString m_description;
|
||||
QFlags<Flags> m_flags;
|
||||
|
||||
bool m_enabled;
|
||||
|
||||
QVariant m_value;
|
||||
QVariant m_suggestedValue;
|
||||
|
||||
RulePolicy *m_policy;
|
||||
OptionsModel *m_options;
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
||||
#endif //KWIN_RULEITEM_H
|
86
kcmkwin/kwinrules/rulesdialog.cpp
Normal file
86
kcmkwin/kwinrules/rulesdialog.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "rulesdialog.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickView>
|
||||
#include <QLayout>
|
||||
#include <QPushButton>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
|
||||
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, SIGNAL(accepted()), SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), SLOT(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->setWindowProperties(info, true);
|
||||
}
|
||||
|
||||
exec();
|
||||
|
||||
return m_rules;
|
||||
}
|
||||
|
||||
void RulesDialog::accept()
|
||||
{
|
||||
m_rules = m_rulesModel->exportToRules();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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
|
||||
|
@ -16,35 +17,37 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KWIN_RULESDIALOG_H
|
||||
#define KWIN_RULESDIALOG_H
|
||||
|
||||
#ifndef __KCM_H__
|
||||
#define __KCM_H__
|
||||
#include "rulesmodel.h"
|
||||
#include "../../rules.h"
|
||||
|
||||
#include <kcmodule.h>
|
||||
#include <kconfig.h>
|
||||
|
||||
class KConfig;
|
||||
#include <QDialog>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class KCMRulesList;
|
||||
class Rules;
|
||||
|
||||
class KCMRules
|
||||
: public KCModule
|
||||
class RulesDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
KCMRules(QWidget *parent, const QVariantList &args);
|
||||
void load() override;
|
||||
void save() override;
|
||||
QString quickHelp() const override;
|
||||
protected Q_SLOTS:
|
||||
void moduleChanged(bool state);
|
||||
explicit RulesDialog(QWidget* parent = nullptr, const char* name = nullptr);
|
||||
|
||||
Rules* edit(Rules* r, const QVariantMap& info, bool show_hints);
|
||||
|
||||
protected:
|
||||
void accept() override;
|
||||
|
||||
private:
|
||||
KCMRulesList* widget;
|
||||
RulesModel* m_rulesModel;
|
||||
QWidget *m_quickWidget;
|
||||
Rules* m_rules;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
#endif // KWIN_RULESDIALOG_H
|
|
@ -1,233 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ruleslist.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <kconfig.h>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "../../rules.h"
|
||||
#include "rulesettings.h"
|
||||
#include "ruleswidget.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
KCMRulesList::KCMRulesList(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
// connect both current/selected, so that current==selected (stupid QListBox :( )
|
||||
connect(rules_listbox, SIGNAL(itemChanged(QListWidgetItem*)),
|
||||
SLOT(activeChanged()));
|
||||
connect(rules_listbox, SIGNAL(itemSelectionChanged()),
|
||||
SLOT(activeChanged()));
|
||||
connect(new_button, SIGNAL(clicked()),
|
||||
SLOT(newClicked()));
|
||||
connect(modify_button, SIGNAL(clicked()),
|
||||
SLOT(modifyClicked()));
|
||||
connect(delete_button, SIGNAL(clicked()),
|
||||
SLOT(deleteClicked()));
|
||||
connect(moveup_button, SIGNAL(clicked()),
|
||||
SLOT(moveupClicked()));
|
||||
connect(movedown_button, SIGNAL(clicked()),
|
||||
SLOT(movedownClicked()));
|
||||
connect(export_button, SIGNAL(clicked()),
|
||||
SLOT(exportClicked()));
|
||||
connect(import_button, SIGNAL(clicked()),
|
||||
SLOT(importClicked()));
|
||||
connect(rules_listbox, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
|
||||
SLOT(modifyClicked()));
|
||||
load();
|
||||
}
|
||||
|
||||
KCMRulesList::~KCMRulesList()
|
||||
{
|
||||
qDeleteAll(m_rules);
|
||||
m_rules.clear();
|
||||
}
|
||||
|
||||
void KCMRulesList::activeChanged()
|
||||
{
|
||||
QListWidgetItem *item = rules_listbox->currentItem();
|
||||
int itemRow = rules_listbox->row(item);
|
||||
|
||||
if (item != nullptr) // make current==selected
|
||||
rules_listbox->setCurrentItem(item, QItemSelectionModel::ClearAndSelect);
|
||||
modify_button->setEnabled(item != nullptr);
|
||||
delete_button->setEnabled(item != nullptr);
|
||||
export_button->setEnabled(item != nullptr);
|
||||
moveup_button->setEnabled(item != nullptr && itemRow > 0);
|
||||
movedown_button->setEnabled(item != nullptr && itemRow < (rules_listbox->count() - 1));
|
||||
}
|
||||
|
||||
void KCMRulesList::newClicked()
|
||||
{
|
||||
RulesDialog dlg(this);
|
||||
Rules* rule = dlg.edit(nullptr, {}, false);
|
||||
if (rule == nullptr)
|
||||
return;
|
||||
int pos = rules_listbox->currentRow() + 1;
|
||||
rules_listbox->insertItem(pos , rule->description);
|
||||
rules_listbox->setCurrentRow(pos, QItemSelectionModel::ClearAndSelect);
|
||||
m_rules.insert(m_rules.begin() + pos, rule);
|
||||
emit changed(true);
|
||||
}
|
||||
|
||||
void KCMRulesList::modifyClicked()
|
||||
{
|
||||
int pos = rules_listbox->currentRow();
|
||||
if (pos == -1)
|
||||
return;
|
||||
RulesDialog dlg(this);
|
||||
Rules *rule = dlg.edit(m_rules[pos], {}, false);
|
||||
if (rule == m_rules[pos])
|
||||
return;
|
||||
delete m_rules[pos];
|
||||
m_rules[pos] = rule;
|
||||
rules_listbox->item(pos)->setText(rule->description);
|
||||
emit changed(true);
|
||||
}
|
||||
|
||||
void KCMRulesList::deleteClicked()
|
||||
{
|
||||
int pos = rules_listbox->currentRow();
|
||||
Q_ASSERT(pos != -1);
|
||||
delete rules_listbox->takeItem(pos);
|
||||
m_rules.erase(m_rules.begin() + pos);
|
||||
emit changed(true);
|
||||
}
|
||||
|
||||
void KCMRulesList::moveupClicked()
|
||||
{
|
||||
int pos = rules_listbox->currentRow();
|
||||
Q_ASSERT(pos != -1);
|
||||
if (pos > 0) {
|
||||
QListWidgetItem * item = rules_listbox->takeItem(pos);
|
||||
rules_listbox->insertItem(pos - 1 , item);
|
||||
rules_listbox->setCurrentItem(item, QItemSelectionModel::ClearAndSelect);
|
||||
Rules *rule = m_rules[pos];
|
||||
m_rules[pos] = m_rules[pos - 1];
|
||||
m_rules[pos - 1] = rule;
|
||||
}
|
||||
emit changed(true);
|
||||
}
|
||||
|
||||
void KCMRulesList::movedownClicked()
|
||||
{
|
||||
int pos = rules_listbox->currentRow();
|
||||
Q_ASSERT(pos != -1);
|
||||
if (pos < int(rules_listbox->count()) - 1) {
|
||||
QListWidgetItem * item = rules_listbox->takeItem(pos);
|
||||
rules_listbox->insertItem(pos + 1 , item);
|
||||
rules_listbox->setCurrentItem(item, QItemSelectionModel::ClearAndSelect);
|
||||
Rules *rule = m_rules[pos];
|
||||
m_rules[pos] = m_rules[pos + 1];
|
||||
m_rules[pos + 1] = rule;
|
||||
}
|
||||
emit changed(true);
|
||||
}
|
||||
|
||||
void KCMRulesList::exportClicked()
|
||||
{
|
||||
int pos = rules_listbox->currentRow();
|
||||
Q_ASSERT(pos != -1);
|
||||
QString path = QFileDialog::getSaveFileName(this, i18n("Export Rules"), QDir::home().absolutePath(),
|
||||
i18n("KWin Rules (*.kwinrule)"));
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
const auto cfg = KSharedConfig::openConfig(path, KConfig::SimpleConfig);
|
||||
RuleSettings settings(cfg, m_rules[pos]->description);
|
||||
settings.setDefaults();
|
||||
m_rules[pos]->write(&settings);
|
||||
settings.save();
|
||||
}
|
||||
|
||||
void KCMRulesList::importClicked()
|
||||
{
|
||||
QString path = QFileDialog::getOpenFileName(this, i18n("Import Rules"), QDir::home().absolutePath(),
|
||||
i18n("KWin Rules (*.kwinrule)"));
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
const auto config = KSharedConfig::openConfig(path, KConfig::SimpleConfig);
|
||||
QStringList groups = config->groupList();
|
||||
if (groups.isEmpty())
|
||||
return;
|
||||
|
||||
int pos = qMax(0, rules_listbox->currentRow());
|
||||
for (const QString &group : groups) {
|
||||
RuleSettings settings(config, group);
|
||||
const bool remove = settings.deleteRule();
|
||||
Rules *new_rule = new Rules(&settings);
|
||||
|
||||
// try to replace existing rule first
|
||||
for (int i = 0; i < m_rules.count(); ++i) {
|
||||
if (m_rules[i]->description == new_rule->description) {
|
||||
delete m_rules[i];
|
||||
if (remove) {
|
||||
m_rules.remove(i);
|
||||
delete rules_listbox->takeItem(i);
|
||||
delete new_rule;
|
||||
pos = qMax(0, rules_listbox->currentRow()); // might have changed!
|
||||
} else
|
||||
m_rules[i] = new_rule;
|
||||
new_rule = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// don't add "to be deleted" if not present
|
||||
if (remove) {
|
||||
delete new_rule;
|
||||
new_rule = nullptr;
|
||||
}
|
||||
|
||||
// plain insertion
|
||||
if (new_rule) {
|
||||
m_rules.insert(pos, new_rule);
|
||||
rules_listbox->insertItem(pos++, new_rule->description);
|
||||
}
|
||||
}
|
||||
emit changed(true);
|
||||
}
|
||||
|
||||
void KCMRulesList::load()
|
||||
{
|
||||
rules_listbox->clear();
|
||||
m_settings.load();
|
||||
m_rules = m_settings.rules();
|
||||
for (const auto rule : qAsConst(m_rules)) {
|
||||
rules_listbox->addItem(rule->description);
|
||||
}
|
||||
|
||||
if (m_rules.count() > 0)
|
||||
rules_listbox->setCurrentItem(rules_listbox->item(0));
|
||||
else
|
||||
rules_listbox->setCurrentItem(nullptr);
|
||||
activeChanged();
|
||||
}
|
||||
|
||||
void KCMRulesList::save()
|
||||
{
|
||||
m_settings.setRules(m_rules);
|
||||
m_settings.save();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __RULESLIST_H__
|
||||
#define __RULESLIST_H__
|
||||
|
||||
#include "ui_ruleslist.h"
|
||||
#include "rulebooksettings.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Rules;
|
||||
|
||||
class KCMRulesList
|
||||
: public QWidget, Ui_KCMRulesList
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit KCMRulesList(QWidget* parent = nullptr);
|
||||
~KCMRulesList() override;
|
||||
void load();
|
||||
void save();
|
||||
Q_SIGNALS:
|
||||
void changed(bool);
|
||||
private Q_SLOTS:
|
||||
void newClicked();
|
||||
void modifyClicked();
|
||||
void deleteClicked();
|
||||
void moveupClicked();
|
||||
void movedownClicked();
|
||||
void exportClicked();
|
||||
void importClicked();
|
||||
void activeChanged();
|
||||
private:
|
||||
QVector<Rules *> m_rules;
|
||||
RuleBookSettings m_settings;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
|
@ -1,122 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>KWin::KCMRulesList</class>
|
||||
<widget class="QWidget" name="KWin::KCMRulesList">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>480</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0" rowspan="14">
|
||||
<widget class="QListWidget" name="rules_listbox"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="new_button">
|
||||
<property name="text">
|
||||
<string>&New...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="modify_button">
|
||||
<property name="text">
|
||||
<string>&Modify...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QPushButton" name="delete_button">
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QPushButton" name="moveup_button">
|
||||
<property name="text">
|
||||
<string>Move &Up</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QPushButton" name="movedown_button">
|
||||
<property name="text">
|
||||
<string>Move &Down</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<spacer name="spacer1">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>294</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="import_button">
|
||||
<property name="text">
|
||||
<string>&Import</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QPushButton" name="export_button">
|
||||
<property name="text">
|
||||
<string>&Export</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
821
kcmkwin/kwinrules/rulesmodel.cpp
Normal file
821
kcmkwin/kwinrules/rulesmodel.cpp
Normal file
|
@ -0,0 +1,821 @@
|
|||
/*
|
||||
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rulesmodel.h"
|
||||
#include <rules.h>
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QIcon>
|
||||
#include <QQmlEngine>
|
||||
#include <QTemporaryFile>
|
||||
#include <QtDBus>
|
||||
|
||||
#include <KColorSchemeManager>
|
||||
#include <KConfig>
|
||||
#include <KLocalizedString>
|
||||
#include <KWindowSystem>
|
||||
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
RulesModel::RulesModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
qmlRegisterUncreatableType<RuleItem>("org.kde.kcms.kwinrules", 1, 0, "RuleItem",
|
||||
QStringLiteral("Do not create objects of type RuleItem"));
|
||||
qmlRegisterUncreatableType<RulesModel>("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")},
|
||||
{SuggestedValueRole, QByteArrayLiteral("suggested")},
|
||||
};
|
||||
}
|
||||
|
||||
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();
|
||||
case SuggestedValueRole:
|
||||
return rule->suggestedValue();
|
||||
}
|
||||
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;
|
||||
case SuggestedValueRole:
|
||||
if (value == rule->suggestedValue()) {
|
||||
return true;
|
||||
}
|
||||
rule->setSuggestedValue(value);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
emit dataChanged(index, index, QVector<int>{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();
|
||||
}
|
||||
|
||||
void RulesModel::setDescription(const QString &description)
|
||||
{
|
||||
setData(index(0, 0), description, RulesModel::ValueRole);
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
rule->reset();
|
||||
|
||||
if (!configItem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool isEnabled = configPolicyItem ? configPolicyItem->property() != Rules::Unused
|
||||
: !configItem->property().toString().isEmpty();
|
||||
rule->setEnabled(isEnabled);
|
||||
|
||||
const QVariant value = configItem->property();
|
||||
rule->setValue(value);
|
||||
|
||||
if (configPolicyItem) {
|
||||
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
|
||||
auto description = addRule(new RuleItem(QLatin1String("description"),
|
||||
RulePolicy::NoPolicy, RuleItem::String,
|
||||
i18n("Description"), i18n("Window matching"),
|
||||
QIcon::fromTheme("entry-edit")));
|
||||
description->setFlag(RuleItem::AlwaysEnabled);
|
||||
description->setFlag(RuleItem::AffectsDescription);
|
||||
|
||||
// Window matching
|
||||
auto wmclass = addRule(new RuleItem(QLatin1String("wmclass"),
|
||||
RulePolicy::StringMatch, RuleItem::String,
|
||||
i18n("Window class (application)"), i18n("Window matching"),
|
||||
QIcon::fromTheme("window")));
|
||||
wmclass->setFlag(RuleItem::AlwaysEnabled);
|
||||
wmclass->setFlag(RuleItem::AffectsDescription);
|
||||
wmclass->setFlag(RuleItem::AffectsWarning);
|
||||
|
||||
auto wmclasscomplete = addRule(new RuleItem(QLatin1String("wmclasscomplete"),
|
||||
RulePolicy::NoPolicy, RuleItem::Boolean,
|
||||
i18n("Match whole window class"), i18n("Window matching"),
|
||||
QIcon::fromTheme("window")));
|
||||
wmclasscomplete->setFlag(RuleItem::AlwaysEnabled);
|
||||
|
||||
auto types = addRule(new RuleItem(QLatin1String("types"),
|
||||
RulePolicy::NoPolicy, RuleItem::FlagsOption,
|
||||
i18n("Window types"), i18n("Window matching"),
|
||||
QIcon::fromTheme("window-duplicate")));
|
||||
types->setOptionsData(windowTypesModelData());
|
||||
types->setFlag(RuleItem::AlwaysEnabled);
|
||||
types->setFlag(RuleItem::AffectsWarning);
|
||||
|
||||
addRule(new RuleItem(QLatin1String("windowrole"),
|
||||
RulePolicy::NoPolicy, RuleItem::String,
|
||||
i18n("Window role"), i18n("Window matching"),
|
||||
QIcon::fromTheme("dialog-object-properties")));
|
||||
|
||||
auto title = addRule(new RuleItem(QLatin1String("title"),
|
||||
RulePolicy::StringMatch, RuleItem::String,
|
||||
i18n("Window title"), i18n("Window matching"),
|
||||
QIcon::fromTheme("edit-comment")));
|
||||
title->setFlag(RuleItem::AffectsDescription);
|
||||
|
||||
addRule(new RuleItem(QLatin1String("clientmachine"),
|
||||
RulePolicy::StringMatch, RuleItem::String,
|
||||
i18n("Machine (hostname)"), i18n("Window matching"),
|
||||
QIcon::fromTheme("computer")));
|
||||
|
||||
// Size & Position
|
||||
addRule(new RuleItem(QLatin1String("position"),
|
||||
RulePolicy::SetRule, RuleItem::Coordinate,
|
||||
i18n("Position"), i18n("Size & Position"),
|
||||
QIcon::fromTheme("transform-move")));
|
||||
|
||||
addRule(new RuleItem(QLatin1String("size"),
|
||||
RulePolicy::SetRule, RuleItem::Coordinate,
|
||||
i18n("Size"), i18n("Size & Position"),
|
||||
QIcon::fromTheme("image-resize-symbolic")));
|
||||
|
||||
addRule(new RuleItem(QLatin1String("maximizehoriz"),
|
||||
RulePolicy::SetRule, RuleItem::Boolean,
|
||||
i18n("Maximized horizontally"), i18n("Size & Position"),
|
||||
QIcon::fromTheme("resizecol")));
|
||||
|
||||
addRule(new RuleItem(QLatin1String("maximizevert"),
|
||||
RulePolicy::SetRule, RuleItem::Boolean,
|
||||
i18n("Maximized vertically"), i18n("Size & Position"),
|
||||
QIcon::fromTheme("resizerow")));
|
||||
|
||||
auto desktop = addRule(new RuleItem(QLatin1String("desktop"),
|
||||
RulePolicy::SetRule, RuleItem::Option,
|
||||
i18n("Virtual Desktop"), i18n("Size & Position"),
|
||||
QIcon::fromTheme("virtual-desktops")));
|
||||
desktop->setOptionsData(virtualDesktopsModelData());
|
||||
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
m_activities = new KActivities::Consumer(this);
|
||||
|
||||
auto activity = addRule(new RuleItem(QLatin1String("activity"),
|
||||
RulePolicy::SetRule, RuleItem::Option,
|
||||
i18n("Activity"), i18n("Size & Position"),
|
||||
QIcon::fromTheme("activities")));
|
||||
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"),
|
||||
QIcon::fromTheme("osd-shutd-screen")));
|
||||
|
||||
addRule(new RuleItem(QLatin1String("fullscreen"),
|
||||
RulePolicy::SetRule, RuleItem::Boolean,
|
||||
i18n("Fullscreen"), i18n("Size & Position"),
|
||||
QIcon::fromTheme("view-fullscreen")));
|
||||
|
||||
addRule(new RuleItem(QLatin1String("minimize"),
|
||||
RulePolicy::SetRule, RuleItem::Boolean,
|
||||
i18n("Minimized"), i18n("Size & Position"),
|
||||
QIcon::fromTheme("window-minimize")));
|
||||
|
||||
addRule(new RuleItem(QLatin1String("shade"),
|
||||
RulePolicy::SetRule, RuleItem::Boolean,
|
||||
i18n("Shaded"), i18n("Size & Position"),
|
||||
QIcon::fromTheme("window-shade")));
|
||||
|
||||
auto placement = addRule(new RuleItem(QLatin1String("placement"),
|
||||
RulePolicy::ForceRule, RuleItem::Option,
|
||||
i18n("Initial placement"), i18n("Size & Position"),
|
||||
QIcon::fromTheme("region")));
|
||||
placement->setOptionsData(placementModelData());
|
||||
|
||||
addRule(new RuleItem(QLatin1String("ignoregeometry"),
|
||||
RulePolicy::SetRule, RuleItem::Boolean,
|
||||
i18n("Ignore requested geometry"), i18n("Size & Position"),
|
||||
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"),
|
||||
QIcon::fromTheme("image-resize-symbolic")));
|
||||
|
||||
addRule(new RuleItem(QLatin1String("maxsize"),
|
||||
RulePolicy::ForceRule, RuleItem::Coordinate,
|
||||
i18n("Maximum Size"), i18n("Size & Position"),
|
||||
QIcon::fromTheme("image-resize-symbolic")));
|
||||
|
||||
addRule(new RuleItem(QLatin1String("strictgeometry"),
|
||||
RulePolicy::ForceRule, RuleItem::Boolean,
|
||||
i18n("Obey geometry restrictions"), i18n("Size & Position"),
|
||||
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"),
|
||||
QIcon::fromTheme("window-keep-above")));
|
||||
|
||||
addRule(new RuleItem(QLatin1String("below"),
|
||||
RulePolicy::SetRule, RuleItem::Boolean,
|
||||
i18n("Keep below"), i18n("Arrangement & Access"),
|
||||
QIcon::fromTheme("window-keep-below")));
|
||||
|
||||
addRule(new RuleItem(QLatin1String("skiptaskbar"),
|
||||
RulePolicy::SetRule, RuleItem::Boolean,
|
||||
i18n("Skip taskbar"), i18n("Arrangement & Access"),
|
||||
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"),
|
||||
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"),
|
||||
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"),
|
||||
QIcon::fromTheme("configure-shortcuts")));
|
||||
|
||||
// Appearance & Fixes
|
||||
addRule(new RuleItem(QLatin1String("noborder"),
|
||||
RulePolicy::SetRule, RuleItem::Boolean,
|
||||
i18n("No titlebar and frame"), i18n("Appearance & Fixes"),
|
||||
QIcon::fromTheme("dialog-cancel")));
|
||||
|
||||
auto decocolor = addRule(new RuleItem(QLatin1String("decocolor"),
|
||||
RulePolicy::ForceRule, RuleItem::Option,
|
||||
i18n("Titlebar color scheme"), i18n("Appearance & Fixes"),
|
||||
QIcon::fromTheme("preferences-desktop-theme")));
|
||||
decocolor->setOptionsData(colorSchemesModelData());
|
||||
|
||||
addRule(new RuleItem(QLatin1String("opacityactive"),
|
||||
RulePolicy::ForceRule, RuleItem::Percentage,
|
||||
i18n("Active opacity"), i18n("Appearance & Fixes"),
|
||||
QIcon::fromTheme("edit-opacity")));
|
||||
|
||||
addRule(new RuleItem(QLatin1String("opacityinactive"),
|
||||
RulePolicy::ForceRule, RuleItem::Percentage,
|
||||
i18n("Inactive opacity"), i18n("Appearance & Fixes"),
|
||||
QIcon::fromTheme("edit-opacity")));
|
||||
|
||||
auto fsplevel = addRule(new RuleItem(QLatin1String("fsplevel"),
|
||||
RulePolicy::ForceRule, RuleItem::Option,
|
||||
i18n("Focus stealing prevention"), i18n("Appearance & Fixes"),
|
||||
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.")));
|
||||
fsplevel->setOptionsData(focusModelData());
|
||||
|
||||
auto fpplevel = addRule(new RuleItem(QLatin1String("fpplevel"),
|
||||
RulePolicy::ForceRule, RuleItem::Option,
|
||||
i18n("Focus protection"), i18n("Appearance & Fixes"),
|
||||
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.")));
|
||||
fpplevel->setOptionsData(focusModelData());
|
||||
|
||||
addRule(new RuleItem(QLatin1String("acceptfocus"),
|
||||
RulePolicy::ForceRule, RuleItem::Boolean,
|
||||
i18n("Accept focus"), i18n("Appearance & Fixes"),
|
||||
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"),
|
||||
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"),
|
||||
QIcon::fromTheme("dialog-close")));
|
||||
|
||||
auto type = addRule(new RuleItem(QLatin1String("type"),
|
||||
RulePolicy::ForceRule, RuleItem::Option,
|
||||
i18n("Set window type"), i18n("Appearance & Fixes"),
|
||||
QIcon::fromTheme("window-duplicate")));
|
||||
type->setOptionsData(windowTypesModelData());
|
||||
|
||||
addRule(new RuleItem(QLatin1String("desktopfile"),
|
||||
RulePolicy::SetRule, RuleItem::String,
|
||||
i18n("Desktop file name"), i18n("Appearance & Fixes"),
|
||||
QIcon::fromTheme("application-x-desktop")));
|
||||
|
||||
addRule(new RuleItem(QLatin1String("blockcompositing"),
|
||||
RulePolicy::ForceRule, RuleItem::Boolean,
|
||||
i18n("Block compositing"), i18n("Appearance & Fixes"),
|
||||
QIcon::fromTheme("composite-track-on")));
|
||||
}
|
||||
|
||||
|
||||
const QHash<QString, QString> RulesModel::x11PropertyHash()
|
||||
{
|
||||
static const auto propertyToRule = QHash<QString, QString> {
|
||||
/* 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::setWindowProperties(const QVariantMap &info, bool forceValue)
|
||||
{
|
||||
// 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());
|
||||
|
||||
m_rules["position"]->setSuggestedValue(position, forceValue);
|
||||
m_rules["size"]->setSuggestedValue(size, forceValue);
|
||||
m_rules["minsize"]->setSuggestedValue(size, forceValue);
|
||||
m_rules["maxsize"]->setSuggestedValue(size, forceValue);
|
||||
|
||||
NET::WindowType window_type = static_cast<NET::WindowType>(info.value("type", 0).toInt());
|
||||
if (window_type == NET::Unknown) {
|
||||
window_type = NET::Normal;
|
||||
}
|
||||
m_rules["types"]->setSuggestedValue(1 << window_type, forceValue);
|
||||
|
||||
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));
|
||||
|
||||
m_rules[ruleKey]->setSuggestedValue(info.value(property), forceValue);
|
||||
}
|
||||
|
||||
emit dataChanged(index(0), index(rowCount()-1), {RulesModel::SuggestedValueRole});
|
||||
if (!forceValue) {
|
||||
emit suggestionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QList<OptionsModel::Data> RulesModel::windowTypesModelData() const
|
||||
{
|
||||
static const auto modelData = QList<OptionsModel::Data> {
|
||||
//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<OptionsModel::Data> RulesModel::virtualDesktopsModelData() const
|
||||
{
|
||||
QList<OptionsModel::Data> 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<OptionsModel::Data> RulesModel::activitiesModelData() const
|
||||
{
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
QList<OptionsModel::Data> 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<OptionsModel::Data> RulesModel::placementModelData() const
|
||||
{
|
||||
// From "placement.h" : Placement rule is stored as a string, not the enum value
|
||||
static const auto modelData = QList<OptionsModel::Data> {
|
||||
{ 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<OptionsModel::Data> RulesModel::focusModelData() const
|
||||
{
|
||||
static const auto modelData = QList<OptionsModel::Data> {
|
||||
{ 0, i18n("None") },
|
||||
{ 1, i18n("Low") },
|
||||
{ 2, i18n("Normal") },
|
||||
{ 3, i18n("High") },
|
||||
{ 4, i18n("Extreme") }
|
||||
};
|
||||
return modelData;
|
||||
}
|
||||
|
||||
QList<OptionsModel::Data> RulesModel::colorSchemesModelData() const
|
||||
{
|
||||
QList<OptionsModel::Data> 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<QIcon>()
|
||||
};
|
||||
}
|
||||
|
||||
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<QVariantMap> async = QDBusConnection::sessionBus().asyncCall(message);
|
||||
|
||||
QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this);
|
||||
connect(callWatcher, &QDBusPendingCallWatcher::finished, this,
|
||||
[this](QDBusPendingCallWatcher *self) {
|
||||
QDBusPendingReply<QVariantMap> reply = *self;
|
||||
self->deleteLater();
|
||||
if (!reply.isValid()) {
|
||||
return;
|
||||
}
|
||||
const QVariantMap windowInfo = reply.value();
|
||||
setWindowProperties(windowInfo);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
} //namespace
|
127
kcmkwin/kwinrules/rulesmodel.h
Normal file
127
kcmkwin/kwinrules/rulesmodel.h
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Ismael Asensio <isma.af@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KWIN_RULES_MODEL_H
|
||||
#define KWIN_RULES_MODEL_H
|
||||
|
||||
#include "ruleitem.h"
|
||||
#include "rulesettings.h"
|
||||
#include <rules.h>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QObject>
|
||||
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
#include <KActivities/Consumer>
|
||||
#endif
|
||||
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class RulesModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged)
|
||||
Q_PROPERTY(QString warningMessage READ warningMessage NOTIFY warningMessageChanged)
|
||||
|
||||
public:
|
||||
enum RulesRole {
|
||||
NameRole = Qt::DisplayRole,
|
||||
DescriptionRole = Qt::ToolTipRole,
|
||||
IconRole = Qt::DecorationRole,
|
||||
IconNameRole = Qt::UserRole + 1,
|
||||
KeyRole,
|
||||
SectionRole,
|
||||
EnabledRole,
|
||||
SelectableRole,
|
||||
ValueRole,
|
||||
TypeRole,
|
||||
PolicyRole,
|
||||
PolicyModelRole,
|
||||
OptionsModelRole,
|
||||
SuggestedValueRole
|
||||
};
|
||||
Q_ENUM(RulesRole)
|
||||
|
||||
public:
|
||||
explicit RulesModel(QObject *parent = nullptr);
|
||||
~RulesModel();
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex & index, const QVariant & value, int role) override;
|
||||
|
||||
bool hasRule(const QString &key) const;
|
||||
RuleItem *ruleItem(const QString &key) const;
|
||||
|
||||
void readFromSettings(RuleSettings *settings);
|
||||
void writeToSettings(RuleSettings *settings) const;
|
||||
|
||||
void importFromRules(Rules *rules);
|
||||
Rules *exportToRules() const;
|
||||
|
||||
void setWindowProperties(const QVariantMap &info, bool forceValue = false);
|
||||
|
||||
QString description() const;
|
||||
void setDescription(const QString &description);
|
||||
QString warningMessage() const;
|
||||
|
||||
|
||||
public slots:
|
||||
void detectWindowProperties(int secs);
|
||||
|
||||
signals:
|
||||
void descriptionChanged();
|
||||
void warningMessageChanged();
|
||||
void suggestionsChanged();
|
||||
|
||||
private:
|
||||
void populateRuleList();
|
||||
bool wmclassWarning() const;
|
||||
RuleItem *addRule(RuleItem *rule);
|
||||
QString defaultDescription() const;
|
||||
|
||||
static const QHash<QString, QString> x11PropertyHash();
|
||||
|
||||
QList<OptionsModel::Data> windowTypesModelData() const;
|
||||
QList<OptionsModel::Data> virtualDesktopsModelData() const;
|
||||
QList<OptionsModel::Data> activitiesModelData() const;
|
||||
QList<OptionsModel::Data> placementModelData() const;
|
||||
QList<OptionsModel::Data> focusModelData() const;
|
||||
QList<OptionsModel::Data> colorSchemesModelData() const;
|
||||
|
||||
private slots:
|
||||
void selectX11Window();
|
||||
|
||||
private:
|
||||
QList<RuleItem *> m_ruleList;
|
||||
QHash<QString, RuleItem *> m_rules;
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
KActivities::Consumer *m_activities;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,970 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ruleswidget.h"
|
||||
|
||||
#include <klineedit.h>
|
||||
#include <kcombobox.h>
|
||||
#include <kcolorschememanager.h>
|
||||
#include <QCheckBox>
|
||||
#include <QFileInfo>
|
||||
#include <QLabel>
|
||||
#include <KLocalizedString>
|
||||
#include <QRegExp>
|
||||
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
#include <kactivities/consumer.h>
|
||||
#endif
|
||||
|
||||
#include <kmessagebox.h>
|
||||
#include <QTabWidget>
|
||||
#include <QTimer>
|
||||
|
||||
#include "../../rules.h"
|
||||
|
||||
#include "detectwidget.h"
|
||||
|
||||
Q_DECLARE_METATYPE(NET::WindowType)
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
#define SETUP( var, type ) \
|
||||
connect( enable_##var, SIGNAL(toggled(bool)), rule_##var, SLOT(setEnabled(bool))); \
|
||||
connect( enable_##var, SIGNAL(toggled(bool)), this, SLOT(updateEnable##var())); \
|
||||
connect( rule_##var, SIGNAL(activated(int)), this, SLOT(updateEnable##var())); \
|
||||
enable_##var->setWhatsThis( enableDesc ); \
|
||||
rule_##var->setWhatsThis( type##RuleDesc );
|
||||
|
||||
RulesWidget::RulesWidget(QWidget* parent)
|
||||
: detect_dlg(nullptr)
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
setupUi(this);
|
||||
QRegularExpressionValidator* validator = new QRegularExpressionValidator(QRegularExpression("[0-9\\-+,xX:]*"), this);
|
||||
maxsize->setValidator(validator);
|
||||
minsize->setValidator(validator);
|
||||
position->setValidator(validator);
|
||||
Ui::RulesWidgetBase::size->setValidator(validator);
|
||||
|
||||
QString enableDesc =
|
||||
i18n("Enable this checkbox to alter this window property for the specified window(s).");
|
||||
QString setRuleDesc =
|
||||
i18n("Specify how the window property should be affected:<ul>"
|
||||
"<li><em>Do Not Affect:</em> The window property will not be affected and therefore"
|
||||
" the default handling for it will be used. Specifying this will block more generic"
|
||||
" window settings from taking effect.</li>"
|
||||
"<li><em>Apply Initially:</em> The window property will be only set to the given value"
|
||||
" after the window is created. No further changes will be affected.</li>"
|
||||
"<li><em>Remember:</em> The value of the window property will be remembered and every"
|
||||
" time the window is created, the last remembered value will be applied.</li>"
|
||||
"<li><em>Force:</em> The window property will be always forced to the given value.</li>"
|
||||
"<li><em>Apply Now:</em> The window property will be set to the given value immediately"
|
||||
" and will not be affected later (this action will be deleted afterwards).</li>"
|
||||
"<li><em>Force temporarily:</em> The window property will be forced to the given value"
|
||||
" until it is hidden (this action will be deleted after the window is hidden).</li>"
|
||||
"</ul>");
|
||||
QString forceRuleDesc =
|
||||
i18n("Specify how the window property should be affected:<ul>"
|
||||
"<li><em>Do Not Affect:</em> The window property will not be affected and therefore"
|
||||
" the default handling for it will be used. Specifying this will block more generic"
|
||||
" window settings from taking effect.</li>"
|
||||
"<li><em>Force:</em> The window property will be always forced to the given value.</li>"
|
||||
"<li><em>Force temporarily:</em> The window property will be forced to the given value"
|
||||
" until it is hidden (this action will be deleted after the window is hidden).</li>"
|
||||
"</ul>");
|
||||
// window tabs have enable signals done in designer
|
||||
// geometry tab
|
||||
SETUP(position, set);
|
||||
SETUP(size, set);
|
||||
SETUP(desktop, set);
|
||||
SETUP(screen, set);
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
SETUP(activity, set);
|
||||
#endif
|
||||
SETUP(maximizehoriz, set);
|
||||
SETUP(maximizevert, set);
|
||||
SETUP(minimize, set);
|
||||
SETUP(shade, set);
|
||||
SETUP(fullscreen, set);
|
||||
SETUP(placement, force);
|
||||
// preferences tab
|
||||
SETUP(above, set);
|
||||
SETUP(below, set);
|
||||
SETUP(noborder, set);
|
||||
SETUP(decocolor, force);
|
||||
SETUP(skiptaskbar, set);
|
||||
SETUP(skippager, set);
|
||||
SETUP(skipswitcher, set);
|
||||
SETUP(acceptfocus, force);
|
||||
SETUP(closeable, force);
|
||||
SETUP(autogroup, force);
|
||||
SETUP(autogroupfg, force);
|
||||
SETUP(autogroupid, force);
|
||||
SETUP(opacityactive, force);
|
||||
SETUP(opacityinactive, force);
|
||||
SETUP(shortcut, force);
|
||||
// workarounds tab
|
||||
SETUP(fsplevel, force);
|
||||
SETUP(fpplevel, force);
|
||||
SETUP(type, force);
|
||||
SETUP(desktopfile, set);
|
||||
SETUP(ignoregeometry, set);
|
||||
SETUP(minsize, force);
|
||||
SETUP(maxsize, force);
|
||||
SETUP(strictgeometry, force);
|
||||
SETUP(disableglobalshortcuts, force);
|
||||
SETUP(blockcompositing, force);
|
||||
|
||||
connect (shortcut_edit, SIGNAL(clicked()), SLOT(shortcutEditClicked()));
|
||||
|
||||
edit_reg_wmclass->hide();
|
||||
edit_reg_role->hide();
|
||||
edit_reg_title->hide();
|
||||
edit_reg_machine->hide();
|
||||
|
||||
#ifndef KWIN_BUILD_ACTIVITIES
|
||||
rule_activity->hide();
|
||||
enable_activity->hide();
|
||||
activity->hide();
|
||||
#endif
|
||||
int i;
|
||||
for (i = 1;
|
||||
i <= KWindowSystem::numberOfDesktops();
|
||||
++i)
|
||||
desktop->addItem(QString::number(i).rightJustified(2) + ':' + KWindowSystem::desktopName(i));
|
||||
desktop->addItem(i18n("All Desktops"));
|
||||
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
m_activities = new KActivities::Consumer(this);
|
||||
connect(m_activities, &KActivities::Consumer::activitiesChanged,
|
||||
this, [this] { updateActivitiesList(); });
|
||||
connect(m_activities, &KActivities::Consumer::serviceStatusChanged,
|
||||
this, [this] { updateActivitiesList(); });
|
||||
updateActivitiesList();
|
||||
#endif
|
||||
|
||||
KColorSchemeManager *schemes = new KColorSchemeManager(this);
|
||||
decocolor->setModel(schemes->model());
|
||||
|
||||
// hide autogrouping as it's currently not supported
|
||||
// BUG 370301
|
||||
line_11->hide();
|
||||
enable_autogroup->hide();
|
||||
autogroup->hide();
|
||||
rule_autogroup->hide();
|
||||
enable_autogroupid->hide();
|
||||
autogroupid->hide();
|
||||
rule_autogroupid->hide();
|
||||
enable_autogroupfg->hide();
|
||||
autogroupfg->hide();
|
||||
rule_autogroupfg->hide();
|
||||
}
|
||||
|
||||
#undef SETUP
|
||||
|
||||
#define UPDATE_ENABLE_SLOT(var) \
|
||||
void RulesWidget::updateEnable##var() \
|
||||
{ \
|
||||
/* leave the label readable label_##var->setEnabled( enable_##var->isChecked() && rule_##var->currentIndex() != 0 );*/ \
|
||||
Ui::RulesWidgetBase::var->setEnabled( enable_##var->isChecked() && rule_##var->currentIndex() != 0 ); \
|
||||
}
|
||||
|
||||
// geometry tab
|
||||
UPDATE_ENABLE_SLOT(position)
|
||||
UPDATE_ENABLE_SLOT(size)
|
||||
UPDATE_ENABLE_SLOT(desktop)
|
||||
UPDATE_ENABLE_SLOT(screen)
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
UPDATE_ENABLE_SLOT(activity)
|
||||
#endif
|
||||
UPDATE_ENABLE_SLOT(maximizehoriz)
|
||||
UPDATE_ENABLE_SLOT(maximizevert)
|
||||
UPDATE_ENABLE_SLOT(minimize)
|
||||
UPDATE_ENABLE_SLOT(shade)
|
||||
UPDATE_ENABLE_SLOT(fullscreen)
|
||||
UPDATE_ENABLE_SLOT(placement)
|
||||
// preferences tab
|
||||
UPDATE_ENABLE_SLOT(above)
|
||||
UPDATE_ENABLE_SLOT(below)
|
||||
UPDATE_ENABLE_SLOT(noborder)
|
||||
UPDATE_ENABLE_SLOT(decocolor)
|
||||
UPDATE_ENABLE_SLOT(skiptaskbar)
|
||||
UPDATE_ENABLE_SLOT(skippager)
|
||||
UPDATE_ENABLE_SLOT(skipswitcher)
|
||||
UPDATE_ENABLE_SLOT(acceptfocus)
|
||||
UPDATE_ENABLE_SLOT(closeable)
|
||||
UPDATE_ENABLE_SLOT(autogroup)
|
||||
UPDATE_ENABLE_SLOT(autogroupfg)
|
||||
UPDATE_ENABLE_SLOT(autogroupid)
|
||||
UPDATE_ENABLE_SLOT(opacityactive)
|
||||
UPDATE_ENABLE_SLOT(opacityinactive)
|
||||
void RulesWidget::updateEnableshortcut()
|
||||
{
|
||||
shortcut->setEnabled(enable_shortcut->isChecked() && rule_shortcut->currentIndex() != 0);
|
||||
shortcut_edit->setEnabled(enable_shortcut->isChecked() && rule_shortcut->currentIndex() != 0);
|
||||
}
|
||||
// workarounds tab
|
||||
UPDATE_ENABLE_SLOT(fsplevel)
|
||||
UPDATE_ENABLE_SLOT(fpplevel)
|
||||
UPDATE_ENABLE_SLOT(type)
|
||||
UPDATE_ENABLE_SLOT(ignoregeometry)
|
||||
UPDATE_ENABLE_SLOT(minsize)
|
||||
UPDATE_ENABLE_SLOT(maxsize)
|
||||
UPDATE_ENABLE_SLOT(strictgeometry)
|
||||
UPDATE_ENABLE_SLOT(disableglobalshortcuts)
|
||||
UPDATE_ENABLE_SLOT(blockcompositing)
|
||||
UPDATE_ENABLE_SLOT(desktopfile)
|
||||
|
||||
#undef UPDATE_ENABLE_SLOT
|
||||
|
||||
static const int set_rule_to_combo[] = {
|
||||
0, // Unused
|
||||
0, // Don't Affect
|
||||
3, // Force
|
||||
1, // Apply
|
||||
2, // Remember
|
||||
4, // ApplyNow
|
||||
5 // ForceTemporarily
|
||||
};
|
||||
|
||||
static const Rules::SetRule combo_to_set_rule[] = {
|
||||
(Rules::SetRule)Rules::DontAffect,
|
||||
(Rules::SetRule)Rules::Apply,
|
||||
(Rules::SetRule)Rules::Remember,
|
||||
(Rules::SetRule)Rules::Force,
|
||||
(Rules::SetRule)Rules::ApplyNow,
|
||||
(Rules::SetRule)Rules::ForceTemporarily
|
||||
};
|
||||
|
||||
static const int force_rule_to_combo[] = {
|
||||
0, // Unused
|
||||
0, // Don't Affect
|
||||
1, // Force
|
||||
0, // Apply
|
||||
0, // Remember
|
||||
0, // ApplyNow
|
||||
2 // ForceTemporarily
|
||||
};
|
||||
|
||||
static const Rules::ForceRule combo_to_force_rule[] = {
|
||||
(Rules::ForceRule)Rules::DontAffect,
|
||||
(Rules::ForceRule)Rules::Force,
|
||||
(Rules::ForceRule)Rules::ForceTemporarily
|
||||
};
|
||||
|
||||
static QString positionToStr(const QPoint& p)
|
||||
{
|
||||
if (p == invalidPoint)
|
||||
return QString();
|
||||
return QString::number(p.x()) + ',' + QString::number(p.y());
|
||||
}
|
||||
|
||||
static QPoint strToPosition(const QString& str)
|
||||
{
|
||||
// two numbers, with + or -, separated by any of , x X :
|
||||
QRegExp reg("\\s*([+-]?[0-9]*)\\s*[,xX:]\\s*([+-]?[0-9]*)\\s*");
|
||||
if (!reg.exactMatch(str))
|
||||
return invalidPoint;
|
||||
return QPoint(reg.cap(1).toInt(), reg.cap(2).toInt());
|
||||
}
|
||||
|
||||
static QString sizeToStr(const QSize& s)
|
||||
{
|
||||
if (!s.isValid())
|
||||
return QString();
|
||||
return QString::number(s.width()) + ',' + QString::number(s.height());
|
||||
}
|
||||
|
||||
static QSize strToSize(const QString& str)
|
||||
{
|
||||
// two numbers, with + or -, separated by any of , x X :
|
||||
QRegExp reg("\\s*([+-]?[0-9]*)\\s*[,xX:]\\s*([+-]?[0-9]*)\\s*");
|
||||
if (!reg.exactMatch(str))
|
||||
return QSize();
|
||||
return QSize(reg.cap(1).toInt(), reg.cap(2).toInt());
|
||||
}
|
||||
|
||||
int RulesWidget::desktopToCombo(int d) const
|
||||
{
|
||||
if (d >= 1 && d < desktop->count())
|
||||
return d - 1;
|
||||
return desktop->count() - 1; // on all desktops
|
||||
}
|
||||
|
||||
int RulesWidget::comboToDesktop(int val) const
|
||||
{
|
||||
if (val == desktop->count() - 1)
|
||||
return NET::OnAllDesktops;
|
||||
return val + 1;
|
||||
}
|
||||
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
int RulesWidget::activityToCombo(const QString &d) const
|
||||
{
|
||||
// TODO: ivan - do a multiselection list
|
||||
for (int i = 0; i < activity->count(); i++) {
|
||||
if (activity->itemData(i).toString() == d) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return activity->count() - 1; // on all activities
|
||||
}
|
||||
|
||||
QString RulesWidget::comboToActivity(int val) const
|
||||
{
|
||||
// TODO: ivan - do a multiselection list
|
||||
if (val < 0 || val >= activity->count())
|
||||
return QString();
|
||||
|
||||
return activity->itemData(val).toString();
|
||||
}
|
||||
|
||||
void RulesWidget::updateActivitiesList()
|
||||
{
|
||||
activity->clear();
|
||||
|
||||
// cloned from kactivities/src/lib/core/consumer.cpp
|
||||
#define NULL_UUID "00000000-0000-0000-0000-000000000000"
|
||||
activity->addItem(i18n("All Activities"), QString::fromLatin1(NULL_UUID));
|
||||
#undef NULL_UUID
|
||||
|
||||
if (m_activities->serviceStatus() == KActivities::Consumer::Running) {
|
||||
foreach (const QString & activityId, m_activities->activities(KActivities::Info::Running)) {
|
||||
const KActivities::Info info(activityId);
|
||||
activity->addItem(info.name(), activityId);
|
||||
}
|
||||
}
|
||||
|
||||
auto rules = this->rules();
|
||||
if (rules->activityrule == Rules::UnusedSetRule) {
|
||||
enable_activity->setChecked(false);
|
||||
Ui::RulesWidgetBase::activity->setCurrentIndex(0);
|
||||
} else {
|
||||
enable_activity->setChecked(true);
|
||||
Ui::RulesWidgetBase::activity->setCurrentIndex(activityToCombo(m_selectedActivityId));
|
||||
}
|
||||
updateEnableactivity();
|
||||
}
|
||||
#endif
|
||||
|
||||
static int placementToCombo(Placement::Policy placement)
|
||||
{
|
||||
static const int conv[] = {
|
||||
1, // NoPlacement
|
||||
0, // Default
|
||||
0, // Unknown
|
||||
6, // Random
|
||||
2, // Smart
|
||||
4, // Cascade
|
||||
5, // Centered
|
||||
7, // ZeroCornered
|
||||
8, // UnderMouse
|
||||
9, // OnMainWindow
|
||||
3 // Maximizing
|
||||
};
|
||||
return conv[ placement ];
|
||||
}
|
||||
|
||||
static Placement::Policy comboToPlacement(int val)
|
||||
{
|
||||
static const Placement::Policy conv[] = {
|
||||
Placement::Default,
|
||||
Placement::NoPlacement,
|
||||
Placement::Smart,
|
||||
Placement::Maximizing,
|
||||
Placement::Cascade,
|
||||
Placement::Centered,
|
||||
Placement::Random,
|
||||
Placement::ZeroCornered,
|
||||
Placement::UnderMouse,
|
||||
Placement::OnMainWindow
|
||||
// no Placement::Unknown
|
||||
};
|
||||
return conv[ val ];
|
||||
}
|
||||
|
||||
static int typeToCombo(NET::WindowType type)
|
||||
{
|
||||
if (type < NET::Normal || type > NET::Splash ||
|
||||
type == NET::Override) // The user must NOT set a window to be unmanaged.
|
||||
// This case is not handled in KWin and will lead to segfaults.
|
||||
// Even iff it was supported, it would mean to allow the user to shoot himself
|
||||
// since an unmanaged window has to manage itself, what is probably not the case when the hint is not set.
|
||||
// Rule opportunity might be a relict from the Motif Hint window times of KDE1
|
||||
return 0; // Normal
|
||||
static const int conv[] = {
|
||||
0, // Normal
|
||||
7, // Desktop
|
||||
3, // Dock
|
||||
4, // Toolbar
|
||||
5, // Menu
|
||||
1, // Dialog
|
||||
8, // Override - ignored.
|
||||
9, // TopMenu
|
||||
2, // Utility
|
||||
6 // Splash
|
||||
};
|
||||
return conv[ type ];
|
||||
}
|
||||
|
||||
static NET::WindowType comboToType(int val)
|
||||
{
|
||||
static const NET::WindowType conv[] = {
|
||||
NET::Normal,
|
||||
NET::Dialog,
|
||||
NET::Utility,
|
||||
NET::Dock,
|
||||
NET::Toolbar,
|
||||
NET::Menu,
|
||||
NET::Splash,
|
||||
NET::Desktop,
|
||||
NET::TopMenu
|
||||
};
|
||||
return conv[ val ];
|
||||
}
|
||||
|
||||
#define GENERIC_RULE( var, func, Type, type, uimethod, uimethod0 ) \
|
||||
if ( rules->var##rule == Rules::Unused##Type##Rule ) \
|
||||
{ \
|
||||
enable_##var->setChecked( false ); \
|
||||
rule_##var->setCurrentIndex( 0 ); \
|
||||
Ui::RulesWidgetBase::var->uimethod0; \
|
||||
updateEnable##var(); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
enable_##var->setChecked( true ); \
|
||||
rule_##var->setCurrentIndex( type##_rule_to_combo[ rules->var##rule ] ); \
|
||||
Ui::RulesWidgetBase::var->uimethod( func( rules->var )); \
|
||||
updateEnable##var(); \
|
||||
}
|
||||
|
||||
#define CHECKBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setChecked, setChecked( false ))
|
||||
#define LINEEDIT_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setText, setText( QString() ))
|
||||
#define COMBOBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setCurrentIndex, setCurrentIndex( 0 ))
|
||||
#define SPINBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setValue, setValue(0))
|
||||
#define CHECKBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setChecked, setChecked( false ))
|
||||
#define LINEEDIT_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setText, setText( QString() ))
|
||||
#define COMBOBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setCurrentIndex, setCurrentIndex( 0 ))
|
||||
#define SPINBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setValue, setValue(0))
|
||||
|
||||
void RulesWidget::setRules(Rules* rules)
|
||||
{
|
||||
Rules tmp;
|
||||
if (rules == nullptr)
|
||||
rules = &tmp; // empty
|
||||
description->setText(rules->description);
|
||||
wmclass->setText(rules->wmclass);
|
||||
whole_wmclass->setChecked(rules->wmclasscomplete);
|
||||
wmclass_match->setCurrentIndex(rules->wmclassmatch);
|
||||
wmclassMatchChanged();
|
||||
role->setText(rules->windowrole);
|
||||
role_match->setCurrentIndex(rules->windowrolematch);
|
||||
roleMatchChanged();
|
||||
types->item(0)->setSelected(rules->types & NET::NormalMask);
|
||||
types->item(1)->setSelected(rules->types & NET::DialogMask);
|
||||
types->item(2)->setSelected(rules->types & NET::UtilityMask);
|
||||
types->item(3)->setSelected(rules->types & NET::DockMask);
|
||||
types->item(4)->setSelected(rules->types & NET::ToolbarMask);
|
||||
types->item(5)->setSelected(rules->types & NET::MenuMask);
|
||||
types->item(6)->setSelected(rules->types & NET::SplashMask);
|
||||
types->item(7)->setSelected(rules->types & NET::DesktopMask);
|
||||
types->item(8)->setSelected(rules->types & NET::OverrideMask);
|
||||
types->item(9)->setSelected(rules->types & NET::TopMenuMask);
|
||||
title->setText(rules->title);
|
||||
title_match->setCurrentIndex(rules->titlematch);
|
||||
titleMatchChanged();
|
||||
machine->setText(rules->clientmachine);
|
||||
machine_match->setCurrentIndex(rules->clientmachinematch);
|
||||
machineMatchChanged();
|
||||
LINEEDIT_SET_RULE(position, positionToStr);
|
||||
LINEEDIT_SET_RULE(size, sizeToStr);
|
||||
COMBOBOX_SET_RULE(desktop, desktopToCombo);
|
||||
SPINBOX_SET_RULE(screen, inc);
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
m_selectedActivityId = rules->activity;
|
||||
COMBOBOX_SET_RULE(activity, activityToCombo);
|
||||
#endif
|
||||
CHECKBOX_SET_RULE(maximizehoriz,);
|
||||
CHECKBOX_SET_RULE(maximizevert,);
|
||||
CHECKBOX_SET_RULE(minimize,);
|
||||
CHECKBOX_SET_RULE(shade,);
|
||||
CHECKBOX_SET_RULE(fullscreen,);
|
||||
COMBOBOX_FORCE_RULE(placement, placementToCombo);
|
||||
CHECKBOX_SET_RULE(above,);
|
||||
CHECKBOX_SET_RULE(below,);
|
||||
CHECKBOX_SET_RULE(noborder,);
|
||||
auto decoColorToCombo = [this](const QString &value) {
|
||||
for (int i = 0; i < decocolor->count(); ++i) {
|
||||
if (decocolor->itemData(i).toString() == value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// search for Breeze
|
||||
for (int i = 0; i < decocolor->count(); ++i) {
|
||||
if (QFileInfo(decocolor->itemData(i).toString()).baseName() == QStringLiteral("Breeze")) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
COMBOBOX_FORCE_RULE(decocolor, decoColorToCombo);
|
||||
CHECKBOX_SET_RULE(skiptaskbar,);
|
||||
CHECKBOX_SET_RULE(skippager,);
|
||||
CHECKBOX_SET_RULE(skipswitcher,);
|
||||
CHECKBOX_FORCE_RULE(acceptfocus,);
|
||||
CHECKBOX_FORCE_RULE(closeable,);
|
||||
CHECKBOX_FORCE_RULE(autogroup,);
|
||||
CHECKBOX_FORCE_RULE(autogroupfg,);
|
||||
LINEEDIT_FORCE_RULE(autogroupid,);
|
||||
SPINBOX_FORCE_RULE(opacityactive,);
|
||||
SPINBOX_FORCE_RULE(opacityinactive,);
|
||||
LINEEDIT_SET_RULE(shortcut,);
|
||||
COMBOBOX_FORCE_RULE(fsplevel,);
|
||||
COMBOBOX_FORCE_RULE(fpplevel,);
|
||||
COMBOBOX_FORCE_RULE(type, typeToCombo);
|
||||
CHECKBOX_SET_RULE(ignoregeometry,);
|
||||
LINEEDIT_FORCE_RULE(minsize, sizeToStr);
|
||||
LINEEDIT_FORCE_RULE(maxsize, sizeToStr);
|
||||
CHECKBOX_FORCE_RULE(strictgeometry,);
|
||||
CHECKBOX_FORCE_RULE(disableglobalshortcuts,);
|
||||
CHECKBOX_FORCE_RULE(blockcompositing,);
|
||||
LINEEDIT_SET_RULE(desktopfile,)
|
||||
}
|
||||
|
||||
#undef GENERIC_RULE
|
||||
#undef CHECKBOX_SET_RULE
|
||||
#undef LINEEDIT_SET_RULE
|
||||
#undef COMBOBOX_SET_RULE
|
||||
#undef SPINBOX_SET_RULE
|
||||
#undef CHECKBOX_FORCE_RULE
|
||||
#undef LINEEDIT_FORCE_RULE
|
||||
#undef COMBOBOX_FORCE_RULE
|
||||
#undef SPINBOX_FORCE_RULE
|
||||
|
||||
#define GENERIC_RULE( var, func, Type, type, uimethod ) \
|
||||
if ( enable_##var->isChecked() && rule_##var->currentIndex() >= 0) \
|
||||
{ \
|
||||
rules->var##rule = combo_to_##type##_rule[ rule_##var->currentIndex() ]; \
|
||||
rules->var = func( Ui::RulesWidgetBase::var->uimethod()); \
|
||||
} \
|
||||
else \
|
||||
rules->var##rule = Rules::Unused##Type##Rule;
|
||||
|
||||
#define CHECKBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, isChecked )
|
||||
#define LINEEDIT_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, text )
|
||||
#define COMBOBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, currentIndex )
|
||||
#define SPINBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, value)
|
||||
#define CHECKBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, isChecked )
|
||||
#define LINEEDIT_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, text )
|
||||
#define COMBOBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, currentIndex )
|
||||
#define SPINBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, value)
|
||||
|
||||
Rules* RulesWidget::rules() const
|
||||
{
|
||||
Rules* rules = new Rules();
|
||||
rules->description = description->text();
|
||||
rules->wmclass = wmclass->text().toUtf8();
|
||||
rules->wmclasscomplete = whole_wmclass->isChecked();
|
||||
rules->wmclassmatch = static_cast< Rules::StringMatch >(wmclass_match->currentIndex());
|
||||
rules->windowrole = role->text().toUtf8();
|
||||
rules->windowrolematch = static_cast< Rules::StringMatch >(role_match->currentIndex());
|
||||
rules->types = {};
|
||||
bool all_types = true;
|
||||
for (int i = 0;
|
||||
i < types->count();
|
||||
++i)
|
||||
if (!types->item(i)->isSelected())
|
||||
all_types = false;
|
||||
if (all_types) // if all types are selected, use AllTypesMask (for future expansion)
|
||||
rules->types = NET::AllTypesMask;
|
||||
else {
|
||||
rules->types |= types->item(0)->isSelected() ? NET::NormalMask : NET::WindowTypeMask(0);
|
||||
rules->types |= types->item(1)->isSelected() ? NET::DialogMask : NET::WindowTypeMask(0);
|
||||
rules->types |= types->item(2)->isSelected() ? NET::UtilityMask : NET::WindowTypeMask(0);
|
||||
rules->types |= types->item(3)->isSelected() ? NET::DockMask : NET::WindowTypeMask(0);
|
||||
rules->types |= types->item(4)->isSelected() ? NET::ToolbarMask : NET::WindowTypeMask(0);
|
||||
rules->types |= types->item(5)->isSelected() ? NET::MenuMask : NET::WindowTypeMask(0);
|
||||
rules->types |= types->item(6)->isSelected() ? NET::SplashMask : NET::WindowTypeMask(0);
|
||||
rules->types |= types->item(7)->isSelected() ? NET::DesktopMask : NET::WindowTypeMask(0);
|
||||
rules->types |= types->item(8)->isSelected() ? NET::OverrideMask : NET::WindowTypeMask(0);
|
||||
rules->types |= types->item(9)->isSelected() ? NET::TopMenuMask : NET::WindowTypeMask(0);
|
||||
}
|
||||
rules->title = title->text();
|
||||
rules->titlematch = static_cast< Rules::StringMatch >(title_match->currentIndex());
|
||||
rules->clientmachine = machine->text().toUtf8();
|
||||
rules->clientmachinematch = static_cast< Rules::StringMatch >(machine_match->currentIndex());
|
||||
LINEEDIT_SET_RULE(position, strToPosition);
|
||||
LINEEDIT_SET_RULE(size, strToSize);
|
||||
COMBOBOX_SET_RULE(desktop, comboToDesktop);
|
||||
SPINBOX_SET_RULE(screen, dec);
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
COMBOBOX_SET_RULE(activity, comboToActivity);
|
||||
#endif
|
||||
CHECKBOX_SET_RULE(maximizehoriz,);
|
||||
CHECKBOX_SET_RULE(maximizevert,);
|
||||
CHECKBOX_SET_RULE(minimize,);
|
||||
CHECKBOX_SET_RULE(shade,);
|
||||
CHECKBOX_SET_RULE(fullscreen,);
|
||||
COMBOBOX_FORCE_RULE(placement, comboToPlacement);
|
||||
CHECKBOX_SET_RULE(above,);
|
||||
CHECKBOX_SET_RULE(below,);
|
||||
CHECKBOX_SET_RULE(noborder,);
|
||||
auto comboToDecocolor = [this](int index) -> QString {
|
||||
return decocolor->itemData(index).toString();
|
||||
};
|
||||
COMBOBOX_FORCE_RULE(decocolor, comboToDecocolor);
|
||||
CHECKBOX_SET_RULE(skiptaskbar,);
|
||||
CHECKBOX_SET_RULE(skippager,);
|
||||
CHECKBOX_SET_RULE(skipswitcher,);
|
||||
CHECKBOX_FORCE_RULE(acceptfocus,);
|
||||
CHECKBOX_FORCE_RULE(closeable,);
|
||||
CHECKBOX_FORCE_RULE(autogroup,);
|
||||
CHECKBOX_FORCE_RULE(autogroupfg,);
|
||||
LINEEDIT_FORCE_RULE(autogroupid,);
|
||||
SPINBOX_FORCE_RULE(opacityactive,);
|
||||
SPINBOX_FORCE_RULE(opacityinactive,);
|
||||
LINEEDIT_SET_RULE(shortcut,);
|
||||
COMBOBOX_FORCE_RULE(fsplevel,);
|
||||
COMBOBOX_FORCE_RULE(fpplevel,);
|
||||
COMBOBOX_FORCE_RULE(type, comboToType);
|
||||
CHECKBOX_SET_RULE(ignoregeometry,);
|
||||
LINEEDIT_FORCE_RULE(minsize, strToSize);
|
||||
LINEEDIT_FORCE_RULE(maxsize, strToSize);
|
||||
CHECKBOX_FORCE_RULE(strictgeometry,);
|
||||
CHECKBOX_FORCE_RULE(disableglobalshortcuts,);
|
||||
CHECKBOX_FORCE_RULE(blockcompositing,);
|
||||
LINEEDIT_SET_RULE(desktopfile,);
|
||||
return rules;
|
||||
}
|
||||
|
||||
#undef GENERIC_RULE
|
||||
#undef CHECKBOX_SET_RULE
|
||||
#undef LINEEDIT_SET_RULE
|
||||
#undef COMBOBOX_SET_RULE
|
||||
#undef SPINBOX_SET_RULE
|
||||
#undef CHECKBOX_FORCE_RULE
|
||||
#undef LINEEDIT_FORCE_RULE
|
||||
#undef COMBOBOX_FORCE_RULE
|
||||
#undef SPINBOX_FORCE_RULE
|
||||
|
||||
#define STRING_MATCH_COMBO( type ) \
|
||||
void RulesWidget::type##MatchChanged() \
|
||||
{ \
|
||||
edit_reg_##type->setEnabled( type##_match->currentIndex() == Rules::RegExpMatch ); \
|
||||
type->setEnabled( type##_match->currentIndex() != Rules::UnimportantMatch ); \
|
||||
}
|
||||
|
||||
STRING_MATCH_COMBO(wmclass)
|
||||
STRING_MATCH_COMBO(role)
|
||||
STRING_MATCH_COMBO(title)
|
||||
STRING_MATCH_COMBO(machine)
|
||||
|
||||
#undef STRING_MATCH_COMBO
|
||||
|
||||
void RulesWidget::detectClicked()
|
||||
{
|
||||
Q_ASSERT(detect_dlg == nullptr);
|
||||
detect_dlg = new DetectDialog;
|
||||
connect(detect_dlg, SIGNAL(detectionDone(bool)), this, SLOT(detected(bool)));
|
||||
detect_dlg->detect(Ui::RulesWidgetBase::detection_delay->value());
|
||||
Ui::RulesWidgetBase::detect->setEnabled(false);
|
||||
}
|
||||
|
||||
void RulesWidget::detected(bool ok)
|
||||
{
|
||||
if (ok) {
|
||||
wmclass->setText(detect_dlg->selectedClass());
|
||||
wmclass_match->setCurrentIndex(Rules::ExactMatch);
|
||||
wmclassMatchChanged(); // grrr
|
||||
whole_wmclass->setChecked(detect_dlg->selectedWholeClass());
|
||||
role->setText(detect_dlg->selectedRole());
|
||||
role_match->setCurrentIndex(detect_dlg->selectedRole().isEmpty()
|
||||
? Rules::UnimportantMatch : Rules::ExactMatch);
|
||||
roleMatchChanged();
|
||||
if (detect_dlg->selectedWholeApp()) {
|
||||
for (int i = 0;
|
||||
i < types->count();
|
||||
++i)
|
||||
types->item(i)->setSelected(true);
|
||||
} else {
|
||||
NET::WindowType type = detect_dlg->selectedType();
|
||||
for (int i = 0;
|
||||
i < types->count();
|
||||
++i)
|
||||
types->item(i)->setSelected(false);
|
||||
types->item(typeToCombo(type))->setSelected(true);
|
||||
}
|
||||
title->setText(detect_dlg->selectedTitle());
|
||||
title_match->setCurrentIndex(detect_dlg->titleMatch());
|
||||
titleMatchChanged();
|
||||
machine->setText(detect_dlg->selectedMachine());
|
||||
machine_match->setCurrentIndex(Rules::UnimportantMatch);
|
||||
machineMatchChanged();
|
||||
// prefill values from to window to settings which already set
|
||||
prefillUnusedValues(detect_dlg->windowInfo());
|
||||
}
|
||||
delete detect_dlg;
|
||||
detect_dlg = nullptr;
|
||||
detect_dlg_ok = ok;
|
||||
Ui::RulesWidgetBase::detect->setEnabled(true);
|
||||
}
|
||||
|
||||
#define GENERIC_PREFILL( var, func, info, uimethod ) \
|
||||
if ( !enable_##var->isChecked()) \
|
||||
{ \
|
||||
Ui::RulesWidgetBase::var->uimethod( func( info )); \
|
||||
}
|
||||
|
||||
#define CHECKBOX_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setChecked )
|
||||
#define LINEEDIT_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setText )
|
||||
#define COMBOBOX_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setCurrentIndex )
|
||||
#define SPINBOX_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setValue )
|
||||
|
||||
void RulesWidget::prefillUnusedValues(const QVariantMap& info)
|
||||
{
|
||||
const QSize windowSize{info.value("width").toInt(), info.value("height").toInt()};
|
||||
LINEEDIT_PREFILL(position, positionToStr, QPoint(info.value("x").toInt(), info.value("y").toInt()));
|
||||
LINEEDIT_PREFILL(size, sizeToStr, windowSize);
|
||||
COMBOBOX_PREFILL(desktop, desktopToCombo, info.value("x11DesktopNumber").toInt());
|
||||
// COMBOBOX_PREFILL(activity, activityToCombo, info.activity()); // TODO: ivan
|
||||
CHECKBOX_PREFILL(maximizehoriz, , info.value("maximizeHorizontal").toBool());
|
||||
CHECKBOX_PREFILL(maximizevert, , info.value("maximizeVertical").toBool());
|
||||
CHECKBOX_PREFILL(minimize, , info.value("minimized").toBool());
|
||||
CHECKBOX_PREFILL(shade, , info.value("shaded").toBool());
|
||||
CHECKBOX_PREFILL(fullscreen, , info.value("fullscreen").toBool());
|
||||
//COMBOBOX_PREFILL( placement, placementToCombo );
|
||||
CHECKBOX_PREFILL(above, , info.value("keepAbove").toBool());
|
||||
CHECKBOX_PREFILL(below, , info.value("keepBelow").toBool());
|
||||
CHECKBOX_PREFILL(noborder, , info.value("noBorder").toBool());
|
||||
CHECKBOX_PREFILL(skiptaskbar, , info.value("skipTaskbar").toBool());
|
||||
CHECKBOX_PREFILL(skippager, , info.value("skipPager").toBool());
|
||||
CHECKBOX_PREFILL(skipswitcher, , info.value("skipSwitcher").toBool());
|
||||
//CHECKBOX_PREFILL( acceptfocus, );
|
||||
//CHECKBOX_PREFILL( closeable, );
|
||||
//CHECKBOX_PREFILL( autogroup, );
|
||||
//CHECKBOX_PREFILL( autogroupfg, );
|
||||
//LINEEDIT_PREFILL( autogroupid, );
|
||||
SPINBOX_PREFILL(opacityactive, , 100 /*get the actual opacity somehow*/);
|
||||
SPINBOX_PREFILL(opacityinactive, , 100 /*get the actual opacity somehow*/);
|
||||
//LINEEDIT_PREFILL( shortcut, );
|
||||
//COMBOBOX_PREFILL( fsplevel, );
|
||||
//COMBOBOX_PREFILL( fpplevel, );
|
||||
COMBOBOX_PREFILL(type, typeToCombo, info.value("type").value<NET::WindowType>());
|
||||
//CHECKBOX_PREFILL( ignoregeometry, );
|
||||
LINEEDIT_PREFILL(minsize, sizeToStr, windowSize);
|
||||
LINEEDIT_PREFILL(maxsize, sizeToStr, windowSize);
|
||||
//CHECKBOX_PREFILL( strictgeometry, );
|
||||
//CHECKBOX_PREFILL( disableglobalshortcuts, );
|
||||
//CHECKBOX_PREFILL( blockcompositing, );
|
||||
LINEEDIT_PREFILL(desktopfile, , info.value("desktopFile").toString());
|
||||
}
|
||||
|
||||
#undef GENERIC_PREFILL
|
||||
#undef CHECKBOX_PREFILL
|
||||
#undef LINEEDIT_PREFILL
|
||||
#undef COMBOBOX_PREFILL
|
||||
#undef SPINBOX_PREFILL
|
||||
|
||||
bool RulesWidget::finalCheck()
|
||||
{
|
||||
if (description->text().isEmpty()) {
|
||||
if (!wmclass->text().isEmpty())
|
||||
description->setText(i18n("Settings for %1", wmclass->text()));
|
||||
else
|
||||
description->setText(i18n("Unnamed entry"));
|
||||
}
|
||||
bool all_types = true;
|
||||
for (int i = 0;
|
||||
i < types->count();
|
||||
++i)
|
||||
if (!types->item(i)->isSelected())
|
||||
all_types = false;
|
||||
if (wmclass_match->currentIndex() == Rules::UnimportantMatch && all_types) {
|
||||
if (KMessageBox::warningContinueCancel(window(),
|
||||
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.")) != KMessageBox::Continue)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RulesWidget::prepareWindowSpecific(const QVariantMap &info)
|
||||
{
|
||||
tabs->setCurrentIndex(1); // geometry tab, skip tab for window identification
|
||||
prefillUnusedValues(info);
|
||||
}
|
||||
|
||||
void RulesWidget::shortcutEditClicked()
|
||||
{
|
||||
QPointer<EditShortcutDialog> dlg = new EditShortcutDialog(window());
|
||||
dlg->setShortcut(shortcut->text());
|
||||
if (dlg->exec() == QDialog::Accepted)
|
||||
shortcut->setText(dlg->shortcut());
|
||||
delete dlg;
|
||||
}
|
||||
|
||||
RulesDialog::RulesDialog(QWidget* parent, const char* name)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setObjectName(name);
|
||||
setModal(true);
|
||||
setWindowTitle(i18n("Edit Window-Specific Settings"));
|
||||
setWindowIcon(QIcon::fromTheme("preferences-system-windows-actions"));
|
||||
|
||||
setLayout(new QVBoxLayout);
|
||||
widget = new RulesWidget(this);
|
||||
layout()->addWidget(widget);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), SLOT(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)
|
||||
{
|
||||
rules = r;
|
||||
widget->setRules(rules);
|
||||
if (!info.isEmpty())
|
||||
{
|
||||
widget->prepareWindowSpecific(info);
|
||||
}
|
||||
if (show_hints)
|
||||
QTimer::singleShot(0, this, SLOT(displayHints()));
|
||||
exec();
|
||||
return rules;
|
||||
}
|
||||
|
||||
void RulesDialog::displayHints()
|
||||
{
|
||||
QString str = "<qt><p>";
|
||||
str += i18n("This configuration dialog allows altering settings only for the selected window"
|
||||
" or application. Find the setting you want to affect, enable the setting using the checkbox,"
|
||||
" select in what way the setting should be affected and to which value.");
|
||||
#if 0 // maybe later
|
||||
str += "</p><p>" + i18n("Consult the documentation for more details.");
|
||||
#endif
|
||||
str += "</p></qt>";
|
||||
KMessageBox::information(this, str, QString(), "displayhints");
|
||||
}
|
||||
|
||||
void RulesDialog::accept()
|
||||
{
|
||||
if (!widget->finalCheck())
|
||||
return;
|
||||
rules = widget->rules();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
EditShortcut::EditShortcut(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
}
|
||||
|
||||
void EditShortcut::editShortcut()
|
||||
{
|
||||
QPointer< ShortcutDialog > dlg = new ShortcutDialog(QKeySequence(shortcut->text()), window());
|
||||
if (dlg->exec() == QDialog::Accepted)
|
||||
shortcut->setText(dlg->shortcut().toString());
|
||||
delete dlg;
|
||||
}
|
||||
|
||||
void EditShortcut::clearShortcut()
|
||||
{
|
||||
shortcut->clear();
|
||||
}
|
||||
|
||||
EditShortcutDialog::EditShortcutDialog(QWidget* parent, const char* name)
|
||||
: QDialog(parent)
|
||||
, widget(new EditShortcut(this))
|
||||
{
|
||||
setObjectName(name);
|
||||
setModal(true);
|
||||
setWindowTitle(i18n("Edit Shortcut"));
|
||||
|
||||
setLayout(new QVBoxLayout);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
|
||||
layout()->addWidget(widget);
|
||||
layout()->addWidget(buttons);
|
||||
}
|
||||
|
||||
void EditShortcutDialog::setShortcut(const QString& cut)
|
||||
{
|
||||
widget->shortcut->setText(cut);
|
||||
}
|
||||
|
||||
QString EditShortcutDialog::shortcut() const
|
||||
{
|
||||
return widget->shortcut->text();
|
||||
}
|
||||
|
||||
ShortcutDialog::ShortcutDialog(const QKeySequence& cut, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, widget(new KKeySequenceWidget(this))
|
||||
{
|
||||
widget->setKeySequence(cut);
|
||||
// It's a global shortcut so don't allow multikey shortcuts
|
||||
widget->setMultiKeyShortcutsAllowed(false);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
|
||||
setLayout(new QVBoxLayout);
|
||||
layout()->addWidget(widget);
|
||||
layout()->addWidget(buttons);
|
||||
}
|
||||
|
||||
void ShortcutDialog::accept()
|
||||
{
|
||||
QKeySequence seq = shortcut();
|
||||
if (!seq.isEmpty()) {
|
||||
if (seq[0] == Qt::Key_Escape) {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
if (seq[0] == Qt::Key_Space
|
||||
|| (seq[0] & Qt::KeyboardModifierMask) == 0) {
|
||||
// clear
|
||||
widget->clearKeySequence();
|
||||
QDialog::accept();
|
||||
return;
|
||||
}
|
||||
}
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
QKeySequence ShortcutDialog::shortcut() const
|
||||
{
|
||||
return widget->keySequence();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __RULESWIDGET_H__
|
||||
#define __RULESWIDGET_H__
|
||||
|
||||
#include <config-kwin.h>
|
||||
|
||||
#include <QDialog>
|
||||
#include <kkeysequencewidget.h>
|
||||
|
||||
#include "ui_ruleswidgetbase.h"
|
||||
#include "ui_editshortcut.h"
|
||||
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
namespace KActivities {
|
||||
class Consumer;
|
||||
} // namespace KActivities
|
||||
#endif
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Rules;
|
||||
class DetectDialog;
|
||||
|
||||
class RulesWidget
|
||||
: public QWidget, public Ui::RulesWidgetBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RulesWidget(QWidget* parent = nullptr);
|
||||
void setRules(Rules* r);
|
||||
Rules* rules() const;
|
||||
bool finalCheck();
|
||||
void prepareWindowSpecific(const QVariantMap &info);
|
||||
Q_SIGNALS:
|
||||
void changed(bool state);
|
||||
protected Q_SLOTS:
|
||||
void detectClicked();
|
||||
void wmclassMatchChanged();
|
||||
void roleMatchChanged();
|
||||
void titleMatchChanged();
|
||||
void machineMatchChanged();
|
||||
void shortcutEditClicked();
|
||||
private Q_SLOTS:
|
||||
// geometry tab
|
||||
void updateEnableposition();
|
||||
void updateEnablesize();
|
||||
void updateEnabledesktop();
|
||||
void updateEnablescreen();
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
void updateEnableactivity();
|
||||
#endif
|
||||
void updateEnablemaximizehoriz();
|
||||
void updateEnablemaximizevert();
|
||||
void updateEnableminimize();
|
||||
void updateEnableshade();
|
||||
void updateEnablefullscreen();
|
||||
void updateEnableplacement();
|
||||
// preferences tab
|
||||
void updateEnableabove();
|
||||
void updateEnablebelow();
|
||||
void updateEnablenoborder();
|
||||
void updateEnabledecocolor();
|
||||
void updateEnableskiptaskbar();
|
||||
void updateEnableskippager();
|
||||
void updateEnableskipswitcher();
|
||||
void updateEnableacceptfocus();
|
||||
void updateEnablecloseable();
|
||||
void updateEnableautogroup();
|
||||
void updateEnableautogroupfg();
|
||||
void updateEnableautogroupid();
|
||||
void updateEnableopacityactive();
|
||||
void updateEnableopacityinactive();
|
||||
// workarounds tab
|
||||
void updateEnablefsplevel();
|
||||
void updateEnablefpplevel();
|
||||
void updateEnabletype();
|
||||
void updateEnableignoregeometry();
|
||||
void updateEnableminsize();
|
||||
void updateEnablemaxsize();
|
||||
void updateEnablestrictgeometry();
|
||||
void updateEnableshortcut();
|
||||
void updateEnabledisableglobalshortcuts();
|
||||
void updateEnableblockcompositing();
|
||||
void updateEnabledesktopfile();
|
||||
// internal
|
||||
void detected(bool);
|
||||
private:
|
||||
int desktopToCombo(int d) const;
|
||||
int comboToDesktop(int val) const;
|
||||
#ifdef KWIN_BUILD_ACTIVITIES
|
||||
int activityToCombo(const QString &d) const;
|
||||
QString comboToActivity(int val) const;
|
||||
void updateActivitiesList();
|
||||
KActivities::Consumer *m_activities;
|
||||
QString m_selectedActivityId; // we need this for async activity loading
|
||||
#endif
|
||||
int comboToTiling(int val) const;
|
||||
int inc(int i) const { return i+1; }
|
||||
int dec(int i) const { return i-1; }
|
||||
void prefillUnusedValues(const QVariantMap& info);
|
||||
DetectDialog* detect_dlg;
|
||||
bool detect_dlg_ok;
|
||||
};
|
||||
|
||||
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 Q_SLOTS:
|
||||
void displayHints();
|
||||
private:
|
||||
RulesWidget* widget;
|
||||
Rules* rules;
|
||||
};
|
||||
|
||||
class EditShortcut
|
||||
: public QWidget, public Ui_EditShortcut
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EditShortcut(QWidget* parent = nullptr);
|
||||
protected Q_SLOTS:
|
||||
void editShortcut();
|
||||
void clearShortcut();
|
||||
};
|
||||
|
||||
class EditShortcutDialog
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit EditShortcutDialog(QWidget* parent = nullptr, const char* name = nullptr);
|
||||
void setShortcut(const QString& cut);
|
||||
QString shortcut() const;
|
||||
private:
|
||||
EditShortcut* widget;
|
||||
};
|
||||
|
||||
// slightly duped from utils.cpp
|
||||
class ShortcutDialog
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ShortcutDialog(const QKeySequence& cut, QWidget* parent = nullptr);
|
||||
void accept() override;
|
||||
QKeySequence shortcut() const;
|
||||
private:
|
||||
KKeySequenceWidget* widget;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2011 Thomas Lübking <thomas.luebking@web.de>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "yesnobox.h"
|
||||
|
||||
YesNoBox::YesNoBox( QWidget *parent )
|
||||
: QWidget(parent)
|
||||
{
|
||||
QHBoxLayout *l = new QHBoxLayout(this);
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
l->addWidget(yes = new QRadioButton(i18n("Yes"), this));
|
||||
l->addWidget(no = new QRadioButton(i18n("No"), this));
|
||||
l->addStretch(100);
|
||||
no->setChecked(true);
|
||||
connect(yes, SIGNAL(clicked(bool)), this, SIGNAL(clicked(bool)));
|
||||
connect(yes, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool)));
|
||||
connect(no, SIGNAL(clicked(bool)), this, SLOT(noClicked(bool)));
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2011 Thomas Lübking <thomas.luebking@web.de>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef YESNOBOX_H
|
||||
#define YESNOBOX_H
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QRadioButton>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
class YesNoBox : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit YesNoBox( QWidget *parent );
|
||||
bool isChecked() { return yes->isChecked(); }
|
||||
public Q_SLOTS:
|
||||
void setChecked(bool b) { yes->setChecked(b); }
|
||||
void toggle() { yes->toggle(); }
|
||||
|
||||
Q_SIGNALS:
|
||||
void clicked(bool checked = false);
|
||||
void toggled(bool checked);
|
||||
private Q_SLOTS:
|
||||
void noClicked(bool checked) { emit clicked(!checked); }
|
||||
private:
|
||||
QRadioButton *yes, *no;
|
||||
};
|
||||
|
||||
#endif // YESNOBOX_H
|
Loading…
Reference in a new issue