kwin/kcmkwin/kwincompositing/model.cpp

511 lines
17 KiB
C++
Raw Normal View History

2013-06-25 14:07:48 +00:00
/**************************************************************************
* KWin - the KDE window manager *
* This file is part of the KDE project. *
* *
* Copyright (C) 2013 Antonis Tsiapaliokas <kok3rs@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, see <http://www.gnu.org/licenses/>. *
**************************************************************************/
#include "model.h"
#include "effectconfig.h"
2013-08-17 08:28:09 +00:00
#include "compositing.h"
#include <kwin_effects_interface.h>
2013-08-17 08:28:09 +00:00
#include <KPluginInfo>
#include <KService>
#include <KServiceTypeTrader>
#include <KSharedConfig>
#include <KCModuleProxy>
2013-06-25 14:07:48 +00:00
#include <QAbstractItemModel>
#include <QDBusConnection>
2013-08-08 13:47:01 +00:00
#include <QDBusInterface>
#include <QDBusPendingCall>
#include <QDBusMessage>
2013-06-25 14:07:48 +00:00
#include <QHash>
#include <QVariant>
#include <QList>
#include <QString>
#include <QQmlEngine>
#include <QtQml>
#include <QQuickItem>
2013-06-25 14:07:48 +00:00
#include <QDebug>
namespace KWin {
namespace Compositing {
2013-06-25 14:07:48 +00:00
EffectModel::EffectModel(QObject *parent)
: QAbstractItemModel(parent) {
2013-06-25 14:07:48 +00:00
loadEffects();
}
QHash< int, QByteArray > EffectModel::roleNames() const
{
2013-06-25 14:07:48 +00:00
QHash<int, QByteArray> roleNames;
roleNames[NameRole] = "NameRole";
roleNames[DescriptionRole] = "DescriptionRole";
roleNames[AuthorNameRole] = "AuthorNameRole";
roleNames[AuthorEmailRole] = "AuthorEmailRole";
roleNames[LicenseRole] = "LicenseRole";
roleNames[VersionRole] = "VersionRole";
roleNames[CategoryRole] = "CategoryRole";
roleNames[ServiceNameRole] = "ServiceNameRole";
roleNames[EffectStatusRole] = "EffectStatusRole";
2013-12-03 06:56:21 +00:00
roleNames[VideoRole] = "VideoRole";
roleNames[SupportedRole] = "SupportedRole";
return roleNames;
2013-06-25 14:07:48 +00:00
}
2013-08-28 07:36:53 +00:00
QModelIndex EffectModel::index(int row, int column, const QModelIndex &parent) const
{
2013-08-09 13:34:50 +00:00
if (parent.isValid() || column > 0 || column < 0 || row < 0 || row >= m_effectsList.count()) {
return QModelIndex();
}
return createIndex(row, column);
}
2013-08-28 07:36:53 +00:00
QModelIndex EffectModel::parent(const QModelIndex &child) const
{
Q_UNUSED(child)
2013-08-09 13:34:50 +00:00
return QModelIndex();
}
2013-08-28 07:36:53 +00:00
int EffectModel::columnCount(const QModelIndex &parent) const
{
2013-08-27 10:03:37 +00:00
Q_UNUSED(parent)
return 1;
}
2013-08-28 07:36:53 +00:00
int EffectModel::rowCount(const QModelIndex &parent) const
{
2013-08-09 13:34:50 +00:00
if (parent.isValid()) {
return 0;
}
2013-06-25 14:07:48 +00:00
return m_effectsList.count();
}
2013-08-28 07:36:53 +00:00
QVariant EffectModel::data(const QModelIndex &index, int role) const
{
2013-06-25 14:07:48 +00:00
if (!index.isValid()) {
return QVariant();
}
EffectData currentEffect = m_effectsList.at(index.row());
2013-06-25 14:07:48 +00:00
switch (role) {
case Qt::DisplayRole:
case NameRole:
2013-06-25 14:07:48 +00:00
return m_effectsList.at(index.row()).name;
case DescriptionRole:
2013-06-25 14:07:48 +00:00
return m_effectsList.at(index.row()).description;
case AuthorNameRole:
2013-06-25 14:07:48 +00:00
return m_effectsList.at(index.row()).authorName;
case AuthorEmailRole:
2013-06-25 14:07:48 +00:00
return m_effectsList.at(index.row()).authorEmail;
case LicenseRole:
2013-06-25 14:07:48 +00:00
return m_effectsList.at(index.row()).license;
case VersionRole:
2013-06-25 14:07:48 +00:00
return m_effectsList.at(index.row()).version;
case CategoryRole:
2013-06-25 14:07:48 +00:00
return m_effectsList.at(index.row()).category;
case ServiceNameRole:
return m_effectsList.at(index.row()).serviceName;
case EffectStatusRole:
return m_effectsList.at(index.row()).effectStatus;
2013-12-03 06:56:21 +00:00
case VideoRole:
return m_effectsList.at(index.row()).video;
case SupportedRole:
return m_effectsList.at(index.row()).supported;
2013-06-25 14:07:48 +00:00
default:
return QVariant();
}
}
2013-08-28 07:36:53 +00:00
bool EffectModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (!index.isValid())
return QAbstractItemModel::setData(index, value, role);
if (role == EffectModel::EffectStatusRole) {
m_effectsList[index.row()].effectStatus = value.toBool();
const QString effectServiceName = m_effectsList[index.row()].serviceName;
if (effectServiceName == "kwin4_effect_slide") {
handleDesktopSwitching(index.row());
} else if (effectServiceName == "kwin4_effect_fadedesktop") {
handleDesktopSwitching(index.row());
} else if (effectServiceName == "kwin4_effect_cubeslide") {
handleDesktopSwitching(index.row());
}
emit dataChanged(index, index);
return true;
} else if (role == EffectModel::WindowManagementRole) {
bool enabled = value.toBool();
handleWindowManagement(index.row(), enabled);
emit dataChanged(index, index);
return true;
}
return QAbstractItemModel::setData(index, value, role);
}
2013-08-28 07:36:53 +00:00
void EffectModel::loadEffects()
{
EffectData effect;
KConfigGroup kwinConfig(KSharedConfig::openConfig("kwinrc"), "Plugins");
2013-06-25 14:07:48 +00:00
beginResetModel();
2013-12-02 14:40:16 +00:00
m_effectsChanged.clear();
m_effectsList.clear();
2013-07-31 07:39:06 +00:00
KService::List offers = KServiceTypeTrader::self()->query("KWin/Effect");
for(KService::Ptr service : offers) {
2013-08-21 10:15:24 +00:00
const QString effectPluginPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kde5/services/"+ service->entryPath(), QStandardPaths::LocateFile);
KPluginInfo plugin(effectPluginPath);
2013-06-25 14:07:48 +00:00
effect.name = plugin.name();
effect.description = plugin.comment();
effect.authorName = plugin.author();
effect.authorEmail = plugin.email();
effect.license = plugin.license();
effect.version = plugin.version();
effect.category = plugin.category();
2013-08-21 10:15:24 +00:00
effect.serviceName = plugin.pluginName();
2013-09-13 16:01:26 +00:00
effect.effectStatus = kwinConfig.readEntry(effect.serviceName + "Enabled", plugin.isPluginEnabledByDefault());
2013-12-02 14:59:31 +00:00
effect.enabledByDefault = plugin.isPluginEnabledByDefault();
2013-12-03 06:56:21 +00:00
effect.video = service->property(QStringLiteral("X-KWin-Video-Url"), QVariant::Url).toUrl();
effect.supported = true;
2013-06-25 14:07:48 +00:00
m_effectsList << effect;
}
2013-08-08 13:47:01 +00:00
qSort(m_effectsList.begin(), m_effectsList.end(), [](const EffectData &a, const EffectData &b) {
return a.category < b.category;
});
2013-07-31 07:39:06 +00:00
OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"),
QStringLiteral("/Effects"),
QDBusConnection::sessionBus());
if (interface.isValid()) {
QStringList effectNames;
std::for_each(m_effectsList.constBegin(), m_effectsList.constEnd(), [&effectNames](const EffectData &data) {
effectNames << data.serviceName;
});
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(interface.areEffectsSupported(effectNames), this);
watcher->setProperty("effectNames", effectNames);
connect(watcher, &QDBusPendingCallWatcher::finished, [this](QDBusPendingCallWatcher *self) {
const QStringList effectNames = self->property("effectNames").toStringList();
const QDBusPendingReply< QList< bool > > reply = *self;
QList< bool> supportValues;
if (reply.isValid()) {
supportValues.append(reply.value());
}
if (effectNames.size() == supportValues.size()) {
for (int i = 0; i < effectNames.size(); ++i) {
const bool supportedValue = supportValues.at(i);
const QString &effectName = effectNames.at(i);
auto it = std::find_if(m_effectsList.begin(), m_effectsList.end(), [effectName](const EffectData &data) {
return data.serviceName == effectName;
});
if (it != m_effectsList.end()) {
if ((*it).supported != supportedValue) {
(*it).supported = supportedValue;
QModelIndex i = index(findRowByServiceName(effectName), 0);
if (i.isValid()) {
emit dataChanged(i, i, QVector<int>() << SupportedRole);
}
}
}
}
}
self->deleteLater();
});
}
m_effectsChanged = m_effectsList;
endResetModel();
}
2013-08-28 07:36:53 +00:00
void EffectModel::handleDesktopSwitching(int row)
{
//Q: Why do we need the handleDesktopSwitching?
//A: Because of the setData, when we enable the effect
//and then we scroll, our model is being updated,
//so the setData is being called again, and as a result
//of that we have multiple effects enabled for the desktop switching.
const QString currentEffect = m_effectsList[row].serviceName;
for (int it = 0; it < m_effectsList.size(); it++) {
EffectData effect = m_effectsList.at(it);
if (effect.serviceName == "kwin4_effect_slide" && currentEffect != effect.serviceName && effect.effectStatus) {
m_effectsList[it].effectStatus = !m_effectsList[it].effectStatus;
} else if (effect.serviceName == "kwin4_effect_cubeslide" && currentEffect != effect.serviceName && effect.effectStatus) {
m_effectsList[it].effectStatus = !m_effectsList[it].effectStatus;
} else if (effect.serviceName == "kwin4_effect_fadedesktop" && currentEffect != effect.serviceName && effect.effectStatus) {
m_effectsList[it].effectStatus = !m_effectsList[it].effectStatus;
}
}
2013-06-25 14:07:48 +00:00
}
2013-08-28 07:36:53 +00:00
void EffectModel::handleWindowManagement(int row, bool enabled)
{
//Make sure that our row is valid
if (m_effectsList.size() > 0 && row <= m_effectsList.size())
m_effectsList[row].effectStatus = enabled;
}
2013-08-28 07:36:53 +00:00
int EffectModel::findRowByServiceName(const QString &serviceName)
{
for (int it = 0; it < m_effectsList.size(); it++) {
if (m_effectsList.at(it).serviceName == serviceName) {
return it;
}
}
return -1;
}
void EffectModel::syncEffectsToKWin()
{
OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"),
QStringLiteral("/Effects"),
QDBusConnection::sessionBus());
for (int it = 0; it < m_effectsList.size(); it++) {
if (m_effectsList.at(it).effectStatus != m_effectsChanged.at(it).effectStatus) {
if (m_effectsList.at(it).effectStatus) {
interface.loadEffect(m_effectsList.at(it).serviceName);
} else {
interface.unloadEffect(m_effectsList.at(it).serviceName);
}
}
}
m_effectsChanged = m_effectsList;
}
void EffectModel::updateEffectStatus(const QModelIndex &rowIndex, bool effectState)
2013-08-28 07:36:53 +00:00
{
setData(rowIndex, effectState, EffectModel::EffectStatusRole);
}
2013-08-28 07:36:53 +00:00
void EffectModel::syncConfig()
{
KConfigGroup kwinConfig(KSharedConfig::openConfig("kwinrc"), "Plugins");
for (auto it = m_effectsList.begin(); it != m_effectsList.end(); it++) {
EffectData effect = *(it);
bool effectConfigStatus = kwinConfig.readEntry(effect.serviceName + "Enabled", false);
if (effect.effectStatus) {
kwinConfig.writeEntry(effect.serviceName + "Enabled", effect.effectStatus);
} else if (effect.effectStatus != effectConfigStatus) {
kwinConfig.writeEntry(effect.serviceName + "Enabled", effect.effectStatus);
}
}
kwinConfig.sync();
syncEffectsToKWin();
}
2013-08-28 07:36:53 +00:00
void EffectModel::enableWidnowManagement(bool enabled)
{
//Make sure that our effects are being installed properly
if (m_effectsList.size() <= 0)
return;
int desktopGridRow = findRowByServiceName("kwin4_effect_desktopgrid");
const QModelIndex desktopGridIndex = createIndex(desktopGridRow, 0);
setData(desktopGridIndex, enabled, EffectModel::WindowManagementRole);
int dialogParentRow = findRowByServiceName("kwin4_effect_dialogparent");
const QModelIndex dialogParentIndex = createIndex(dialogParentRow, 0);
setData(dialogParentIndex, enabled, EffectModel::WindowManagementRole);
int presentWindowsRow = findRowByServiceName("kwin4_effect_presentwindows");
const QModelIndex presentWindowsIndex = createIndex(presentWindowsRow, 0);
setData(presentWindowsIndex, enabled, EffectModel::WindowManagementRole);
}
2013-12-02 14:59:31 +00:00
void EffectModel::defaults()
{
for (int i = 0; i < m_effectsList.count(); ++i) {
const auto &effect = m_effectsList.at(i);
if (effect.effectStatus != effect.enabledByDefault) {
updateEffectStatus(index(i, 0), effect.enabledByDefault);
}
}
}
EffectFilterModel::EffectFilterModel(QObject *parent)
: QSortFilterProxyModel(parent)
, m_effectModel(new EffectModel(this))
, m_supported(true)
{
setSourceModel(m_effectModel);
}
2013-08-28 07:36:53 +00:00
const QString &EffectFilterModel::filter() const
{
return m_filter;
}
2013-08-28 07:36:53 +00:00
void EffectFilterModel::setFilter(const QString &filter)
{
if (filter == m_filter) {
return;
}
m_filter = filter;
emit filterChanged();
invalidateFilter();
}
void EffectFilterModel::setFilterOnSupported(bool supported)
{
if (m_supported == supported) {
return;
}
m_supported = supported;
emit filterOnSupportedChanged();
invalidateFilter();
}
2013-08-28 07:36:53 +00:00
bool EffectFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if (!m_effectModel) {
return false;
}
QModelIndex index = m_effectModel->index(source_row, 0, source_parent);
if (!index.isValid()) {
return false;
}
if (m_supported) {
bool supported = index.data(EffectModel::SupportedRole).toBool();
if (!supported) {
return false;
}
}
if (m_filter.isEmpty()) {
return true;
}
QVariant data = index.data();
if (!data.isValid()) {
//An invalid QVariant is valid data
return true;
}
if (m_effectModel->data(index, EffectModel::NameRole).toString().contains(m_filter, Qt::CaseInsensitive)) {
return true;
} else if (m_effectModel->data(index, EffectModel::DescriptionRole).toString().contains(m_filter, Qt::CaseInsensitive)) {
return true;
}
return false;
}
void EffectFilterModel::updateEffectStatus(int rowIndex, bool effectState)
2013-08-28 07:36:53 +00:00
{
const QModelIndex sourceIndex = mapToSource(index(rowIndex, 0));
m_effectModel->updateEffectStatus(sourceIndex, effectState);
}
2013-08-28 07:36:53 +00:00
void EffectFilterModel::syncConfig()
{
m_effectModel->syncConfig();
}
2013-08-28 07:36:53 +00:00
void EffectFilterModel::enableWidnowManagement(bool enabled)
{
m_effectModel->enableWidnowManagement(enabled);
}
2013-12-02 14:40:16 +00:00
void EffectFilterModel::load()
{
m_effectModel->loadEffects();
}
2013-12-02 14:59:31 +00:00
void EffectFilterModel::defaults()
{
m_effectModel->defaults();
}
EffectView::EffectView(ViewType type, QWindow *parent)
2013-06-25 14:07:48 +00:00
: QQuickView(parent)
{
qmlRegisterType<EffectConfig>("org.kde.kwin.kwincompositing", 1, 0, "EffectConfig");
qmlRegisterType<EffectFilterModel>("org.kde.kwin.kwincompositing", 1, 0, "EffectFilterModel");
2013-08-17 08:28:09 +00:00
qmlRegisterType<Compositing>("org.kde.kwin.kwincompositing", 1, 0, "Compositing");
qmlRegisterType<CompositingType>("org.kde.kwin.kwincompositing", 1, 0, "CompositingType");
init(type);
2013-06-25 14:07:48 +00:00
}
void EffectView::init(ViewType type)
2013-08-28 07:36:53 +00:00
{
QString path;
switch (type) {
case CompositingSettingsView:
path = QStringLiteral("kwincompositing/qml/main-compositing.qml");
break;
case DesktopEffectsView:
path = QStringLiteral("kwincompositing/qml/main.qml");
break;
}
QString mainFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, path, QStandardPaths::LocateFile);
2013-06-25 14:07:48 +00:00
setResizeMode(QQuickView::SizeRootObjectToView);
2013-09-13 12:29:53 +00:00
rootContext()->setContextProperty("engine", this);
2013-06-25 14:07:48 +00:00
setSource(QUrl(mainFile));
connect(rootObject(), SIGNAL(changed()), this, SIGNAL(changed()));
2013-06-25 14:07:48 +00:00
}
void EffectView::save()
{
if (auto *model = rootObject()->findChild<EffectFilterModel*>(QStringLiteral("filterModel"))) {
model->syncConfig();
}
if (auto *compositing = rootObject()->findChild<Compositing*>(QStringLiteral("compositing"))) {
compositing->save();
}
}
2013-12-02 14:40:16 +00:00
void EffectView::load()
{
if (auto *model = rootObject()->findChild<EffectFilterModel*>(QStringLiteral("filterModel"))) {
model->load();
}
if (auto *compositing = rootObject()->findChild<Compositing*>(QStringLiteral("compositing"))) {
compositing->reset();
}
}
2013-12-02 14:59:31 +00:00
void EffectView::defaults()
{
if (auto *model = rootObject()->findChild<EffectFilterModel*>(QStringLiteral("filterModel"))) {
model->defaults();
}
if (auto *compositing = rootObject()->findChild<Compositing*>(QStringLiteral("compositing"))) {
compositing->defaults();
}
}
}//end namespace Compositing
}//end namespace KWin