diff --git a/kcmkwin/CMakeLists.txt b/kcmkwin/CMakeLists.txt index 19e1ee6b1..152ba322d 100644 --- a/kcmkwin/CMakeLists.txt +++ b/kcmkwin/CMakeLists.txt @@ -1,13 +1,14 @@ remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_STRICT_ITERATORS -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_KEYWORDS) +add_subdirectory( common ) add_subdirectory( kwincompositing ) add_subdirectory( kwinoptions ) add_subdirectory( kwindecoration ) add_subdirectory( kwinrules ) add_subdirectory( kwinscreenedges ) add_subdirectory( kwinscripts ) add_subdirectory( kwindesktop ) if( KWIN_BUILD_TABBOX ) add_subdirectory( kwintabbox ) endif() diff --git a/kcmkwin/common/CMakeLists.txt b/kcmkwin/common/CMakeLists.txt new file mode 100644 index 000000000..c9eaf7107 --- /dev/null +++ b/kcmkwin/common/CMakeLists.txt @@ -0,0 +1,32 @@ +# KI18N Translation Domain for this library +add_definitions(-DTRANSLATION_DOMAIN=\"kcmkwincommon\") + +include_directories(${KWIN_SOURCE_DIR}/effects) + +set(kcmkwincommon_SRC + effectmodel.cpp +) + +qt5_add_dbus_interface(kcmkwincommon_SRC + ${KWIN_SOURCE_DIR}/org.kde.kwin.Effects.xml kwin_effects_interface +) + +add_library(kcmkwincommon SHARED ${kcmkwincommon_SRC}) + +target_link_libraries(kcmkwincommon + Qt5::Core + Qt5::DBus + KF5::CoreAddons + KF5::ConfigCore + KF5::I18n + KF5::Package + KF5::KCMUtils + kwin4_effect_builtins +) + +set_target_properties(kcmkwincommon PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} +) + +install(TARGETS kcmkwincommon ${INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP) diff --git a/kcmkwin/common/Messages.sh b/kcmkwin/common/Messages.sh new file mode 100644 index 000000000..b4e10c655 --- /dev/null +++ b/kcmkwin/common/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$XGETTEXT `find . -name \*.cpp` -o $podir/kcmkwincommon.pot diff --git a/kcmkwin/kwincompositing/model.cpp b/kcmkwin/common/effectmodel.cpp similarity index 52% copy from kcmkwin/kwincompositing/model.cpp copy to kcmkwin/common/effectmodel.cpp index 5d9afc9b3..a6fbbab90 100644 --- a/kcmkwin/kwincompositing/model.cpp +++ b/kcmkwin/common/effectmodel.cpp @@ -1,662 +1,528 @@ -/************************************************************************** -* KWin - the KDE window manager * -* This file is part of the KDE project. * -* * -* Copyright (C) 2013 Antonis Tsiapaliokas * -* * -* 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 . * -**************************************************************************/ - -#include "model.h" -#include "effectconfig.h" -#include "compositing.h" +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2013 Antonis Tsiapaliokas +Copyright (C) 2018 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#include "effectmodel.h" + #include -#include #include +#include +#include #include -#include -#include #include -#include -#include #include #include -#include +#include -#include #include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace KWin { -namespace Compositing { +#include + +namespace KWin +{ static QString translatedCategory(const QString &category) { - static const QVector knownCategories = { + static const QVector knownCategories = { QStringLiteral("Accessibility"), QStringLiteral("Appearance"), QStringLiteral("Candy"), QStringLiteral("Focus"), QStringLiteral("Show Desktop Animation"), QStringLiteral("Tools"), QStringLiteral("Virtual Desktop Switching Animation"), QStringLiteral("Window Management"), QStringLiteral("Window Open/Close Animation") }; static const QVector translatedCategories = { i18nc("Category of Desktop Effects, used as section header", "Accessibility"), i18nc("Category of Desktop Effects, used as section header", "Appearance"), i18nc("Category of Desktop Effects, used as section header", "Candy"), i18nc("Category of Desktop Effects, used as section header", "Focus"), i18nc("Category of Desktop Effects, used as section header", "Show Desktop Animation"), i18nc("Category of Desktop Effects, used as section header", "Tools"), i18nc("Category of Desktop Effects, used as section header", "Virtual Desktop Switching Animation"), i18nc("Category of Desktop Effects, used as section header", "Window Management"), i18nc("Category of Desktop Effects, used as section header", "Window Open/Close Animation") }; + const int index = knownCategories.indexOf(category); if (index == -1) { qDebug() << "Unknown category '" << category << "' and thus not translated"; return category; } + return translatedCategories[index]; } -static EffectStatus effectStatus(bool enabled) +static EffectModel::Status effectStatus(bool enabled) { - return enabled ? EffectStatus::Enabled : EffectStatus::Disabled; + return enabled ? EffectModel::Status::Enabled : EffectModel::Status::Disabled; } EffectModel::EffectModel(QObject *parent) - : QAbstractItemModel(parent) { + : QAbstractItemModel(parent) +{ } -QHash< int, QByteArray > EffectModel::roleNames() const +QHash EffectModel::roleNames() const { QHash 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[IconNameRole] = "IconNameRole"; roleNames[EffectStatusRole] = "EffectStatusRole"; roleNames[VideoRole] = "VideoRole"; + roleNames[WebsiteRole] = "WebsiteRole"; roleNames[SupportedRole] = "SupportedRole"; roleNames[ExclusiveRole] = "ExclusiveRole"; roleNames[ConfigurableRole] = "ConfigurableRole"; roleNames[ScriptedRole] = QByteArrayLiteral("ScriptedRole"); + roleNames[EnabledByDefaultRole] = "EnabledByDefaultRole"; return roleNames; } QModelIndex EffectModel::index(int row, int column, const QModelIndex &parent) const { -if (parent.isValid() || column > 0 || column < 0 || row < 0 || row >= m_effectsList.count()) { - return QModelIndex(); + if (parent.isValid() || column > 0 || column < 0 || row < 0 || row >= m_effectsList.count()) { + return {}; } return createIndex(row, column); } QModelIndex EffectModel::parent(const QModelIndex &child) const { Q_UNUSED(child) - - return QModelIndex(); + return {}; } int EffectModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 1; } int EffectModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return m_effectsList.count(); } QVariant EffectModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { - return QVariant(); + return {}; } - EffectData currentEffect = m_effectsList.at(index.row()); + const EffectData effect = m_effectsList.at(index.row()); switch (role) { - case Qt::DisplayRole: - case NameRole: - return m_effectsList.at(index.row()).name; - case DescriptionRole: - return m_effectsList.at(index.row()).description; - case AuthorNameRole: - return m_effectsList.at(index.row()).authorName; - case AuthorEmailRole: - return m_effectsList.at(index.row()).authorEmail; - case LicenseRole: - return m_effectsList.at(index.row()).license; - case VersionRole: - return m_effectsList.at(index.row()).version; - case CategoryRole: - return m_effectsList.at(index.row()).category; - case ServiceNameRole: - return m_effectsList.at(index.row()).serviceName; - case EffectStatusRole: - return (int)m_effectsList.at(index.row()).effectStatus; - case VideoRole: - return m_effectsList.at(index.row()).video; - case SupportedRole: - return m_effectsList.at(index.row()).supported; - case ExclusiveRole: - return m_effectsList.at(index.row()).exclusiveGroup; - case InternalRole: - return m_effectsList.at(index.row()).internal; - case ConfigurableRole: - return m_effectsList.at(index.row()).configurable; - case ScriptedRole: - return m_effectsList.at(index.row()).scripted; - default: - return QVariant(); + case Qt::DisplayRole: + case NameRole: + return effect.name; + case DescriptionRole: + return effect.description; + case AuthorNameRole: + return effect.authorName; + case AuthorEmailRole: + return effect.authorEmail; + case LicenseRole: + return effect.license; + case VersionRole: + return effect.version; + case CategoryRole: + return effect.category; + case ServiceNameRole: + return effect.serviceName; + case IconNameRole: + return effect.iconName; + case EffectStatusRole: + return static_cast(effect.effectStatus); + case VideoRole: + return effect.video; + case WebsiteRole: + return effect.website; + case SupportedRole: + return effect.supported; + case ExclusiveRole: + return effect.exclusiveGroup; + case InternalRole: + return effect.internal; + case ConfigurableRole: + return effect.configurable; + case ScriptedRole: + return effect.kind == Kind::Scripted; + case EnabledByDefaultRole: + return effect.enabledByDefault; + default: + return {}; } } -bool EffectModel::setData(const QModelIndex& index, const QVariant& value, int role) +bool EffectModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (!index.isValid()) + if (!index.isValid()) { return QAbstractItemModel::setData(index, value, role); + } if (role == EffectModel::EffectStatusRole) { // note: whenever the StatusRole is modified (even to the same value) the entry // gets marked as changed and will get saved to the config file. This means the // config file could get polluted EffectData &data = m_effectsList[index.row()]; - data.effectStatus = EffectStatus(value.toInt()); + data.effectStatus = Status(value.toInt()); data.changed = true; emit dataChanged(index, index); - if (data.effectStatus == EffectStatus::Enabled && !data.exclusiveGroup.isEmpty()) { + if (data.effectStatus == Status::Enabled && !data.exclusiveGroup.isEmpty()) { // need to disable all other exclusive effects in the same category for (int i = 0; i < m_effectsList.size(); ++i) { if (i == index.row()) { continue; } EffectData &otherData = m_effectsList[i]; if (otherData.exclusiveGroup == data.exclusiveGroup) { - otherData.effectStatus = EffectStatus::Disabled; + otherData.effectStatus = Status::Disabled; otherData.changed = true; emit dataChanged(this->index(i, 0), this->index(i, 0)); } } } return true; } return QAbstractItemModel::setData(index, value, role); } void EffectModel::loadBuiltInEffects(const KConfigGroup &kwinConfig, const KPluginInfo::List &configs) { const auto builtins = BuiltInEffects::availableEffects(); for (auto builtin : builtins) { const BuiltInEffects::EffectData &data = BuiltInEffects::effectData(builtin); EffectData effect; effect.name = data.displayName; effect.description = data.comment; effect.authorName = i18n("KWin development team"); effect.authorEmail = QString(); // not used at all effect.license = QStringLiteral("GPL"); effect.version = QStringLiteral(KWIN_VERSION_STRING); + effect.untranslatedCategory = data.category; effect.category = translatedCategory(data.category); effect.serviceName = data.name; + effect.iconName = QStringLiteral("preferences-system-windows"); effect.enabledByDefault = data.enabled; effect.enabledByDefaultFunction = (data.enabledFunction != nullptr); const QString enabledKey = QStringLiteral("%1Enabled").arg(effect.serviceName); if (kwinConfig.hasKey(enabledKey)) { effect.effectStatus = effectStatus(kwinConfig.readEntry(effect.serviceName + "Enabled", effect.enabledByDefault)); } else if (data.enabledFunction != nullptr) { - effect.effectStatus = EffectStatus::EnabledUndeterminded; + effect.effectStatus = Status::EnabledUndeterminded; } else { effect.effectStatus = effectStatus(effect.enabledByDefault); } effect.video = data.video; + effect.website = QUrl(); effect.supported = true; effect.exclusiveGroup = data.exclusiveCategory; effect.internal = data.internal; - effect.scripted = false; + effect.kind = Kind::BuiltIn; - auto it = std::find_if(configs.begin(), configs.end(), [data](const KPluginInfo &info) { - return info.property(QStringLiteral("X-KDE-ParentComponents")).toString() == data.name; - }); - effect.configurable = it != configs.end(); + effect.configurable = std::any_of(configs.constBegin(), configs.constEnd(), + [data](const KPluginInfo &info) { + return info.property(QStringLiteral("X-KDE-ParentComponents")).toString() == data.name; + } + ); - m_effectsList << effect; + if (shouldStore(effect)) { + m_effectsList << effect; + } } } void EffectModel::loadJavascriptEffects(const KConfigGroup &kwinConfig) { - const auto plugins = KPackage::PackageLoader::self()->listPackages(QStringLiteral("KWin/Effect"), QStringLiteral("kwin/effects")); + const auto plugins = KPackage::PackageLoader::self()->listPackages( + QStringLiteral("KWin/Effect"), + QStringLiteral("kwin/effects") + ); for (const KPluginMetaData &metaData : plugins) { KPluginInfo plugin(metaData); EffectData effect; 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.untranslatedCategory = plugin.category(); effect.category = translatedCategory(plugin.category()); effect.serviceName = plugin.pluginName(); + effect.iconName = plugin.icon(); effect.effectStatus = effectStatus(kwinConfig.readEntry(effect.serviceName + "Enabled", plugin.isPluginEnabledByDefault())); effect.enabledByDefault = plugin.isPluginEnabledByDefault(); effect.enabledByDefaultFunction = false; effect.video = plugin.property(QStringLiteral("X-KWin-Video-Url")).toUrl(); + effect.website = plugin.website(); effect.supported = true; effect.exclusiveGroup = plugin.property(QStringLiteral("X-KWin-Exclusive-Category")).toString(); effect.internal = plugin.property(QStringLiteral("X-KWin-Internal")).toBool(); - effect.scripted = true; + effect.kind = Kind::Scripted; const QString pluginKeyword = plugin.property(QStringLiteral("X-KDE-PluginKeyword")).toString(); if (!pluginKeyword.isEmpty()) { // scripted effects have their pluginName() as the keyword effect.configurable = plugin.property(QStringLiteral("X-KDE-ParentComponents")).toString() == pluginKeyword; } else { effect.configurable = false; } - m_effectsList << effect; + if (shouldStore(effect)) { + m_effectsList << effect; + } } } void EffectModel::loadPluginEffects(const KConfigGroup &kwinConfig, const KPluginInfo::List &configs) { - static const QString subDir(QStringLiteral("kwin/effects/plugins/")); - static const QString serviceType(QStringLiteral("KWin/Effect")); - const QVector pluginEffects = KPluginLoader::findPlugins(subDir, [] (const KPluginMetaData &data) { return data.serviceTypes().contains(serviceType); }); - for (KPluginMetaData pluginEffect : pluginEffects) { - if (!pluginEffect.isValid()) + const auto pluginEffects = KPluginLoader::findPlugins( + QStringLiteral("kwin/effects/plugins/"), + [](const KPluginMetaData &data) { + return data.serviceTypes().contains(QStringLiteral("KWin/Effect")); + } + ); + for (const KPluginMetaData &pluginEffect : pluginEffects) { + if (!pluginEffect.isValid()) { continue; + } EffectData effect; effect.name = pluginEffect.name(); effect.description = pluginEffect.description(); effect.license = pluginEffect.license(); effect.version = pluginEffect.version(); - effect.category = pluginEffect.category(); + effect.untranslatedCategory = pluginEffect.category(); + effect.category = translatedCategory(pluginEffect.category()); effect.serviceName = pluginEffect.pluginId(); + effect.iconName = pluginEffect.iconName(); effect.enabledByDefault = pluginEffect.isEnabledByDefault(); effect.supported = true; effect.enabledByDefaultFunction = false; effect.internal = false; - effect.scripted = false; + effect.kind = Kind::Binary; for (int i = 0; i < pluginEffect.authors().count(); ++i) { effect.authorName.append(pluginEffect.authors().at(i).name()); effect.authorEmail.append(pluginEffect.authors().at(i).emailAddress()); if (i+1 < pluginEffect.authors().count()) { effect.authorName.append(", "); effect.authorEmail.append(", "); } } if (pluginEffect.rawData().contains("org.kde.kwin.effect")) { const QJsonObject d(pluginEffect.rawData().value("org.kde.kwin.effect").toObject()); effect.exclusiveGroup = d.value("exclusiveGroup").toString(); effect.video = QUrl::fromUserInput(d.value("video").toString()); effect.enabledByDefaultFunction = d.value("enabledByDefaultMethod").toBool(); } + effect.website = pluginEffect.website(); + const QString enabledKey = QStringLiteral("%1Enabled").arg(effect.serviceName); if (kwinConfig.hasKey(enabledKey)) { effect.effectStatus = effectStatus(kwinConfig.readEntry(effect.serviceName + "Enabled", effect.enabledByDefault)); } else if (effect.enabledByDefaultFunction) { - effect.effectStatus = EffectStatus::EnabledUndeterminded; + effect.effectStatus = Status::EnabledUndeterminded; } else { effect.effectStatus = effectStatus(effect.enabledByDefault); } - auto it = std::find_if(configs.begin(), configs.end(), [pluginEffect](const KPluginInfo &info) { - return info.property(QStringLiteral("X-KDE-ParentComponents")).toString() == pluginEffect.pluginId(); - }); - effect.configurable = it != configs.end(); + effect.configurable = std::any_of(configs.constBegin(), configs.constEnd(), + [pluginEffect](const KPluginInfo &info) { + return info.property(QStringLiteral("X-KDE-ParentComponents")).toString() == pluginEffect.pluginId(); + } + ); - m_effectsList << effect; + if (shouldStore(effect)) { + m_effectsList << effect; + } } } -void EffectModel::loadEffects() +void EffectModel::load() { KConfigGroup kwinConfig(KSharedConfig::openConfig("kwinrc"), "Plugins"); beginResetModel(); m_effectsChanged.clear(); m_effectsList.clear(); const KPluginInfo::List configs = KPluginTrader::self()->query(QStringLiteral("kwin/effects/configs/")); loadBuiltInEffects(kwinConfig, configs); loadJavascriptEffects(kwinConfig); loadPluginEffects(kwinConfig, configs); qSort(m_effectsList.begin(), m_effectsList.end(), [](const EffectData &a, const EffectData &b) { if (a.category == b.category) { if (a.exclusiveGroup == b.exclusiveGroup) { return a.name < b.name; } return a.exclusiveGroup < b.exclusiveGroup; } return a.category < b.category; }); OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), - QStringLiteral("/Effects"), - QDBusConnection::sessionBus()); + 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; - }); + effectNames.reserve(m_effectsList.count()); + for (const EffectData &data : m_effectsList) { + effectNames.append(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; + const QDBusPendingReply > reply = *self; + QList 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() << SupportedRole); } } } } } self->deleteLater(); }); } m_effectsChanged = m_effectsList; endResetModel(); } 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()); + 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 != EffectStatus::Disabled) { - interface.loadEffect(m_effectsList.at(it).serviceName); - } else { - interface.unloadEffect(m_effectsList.at(it).serviceName); - } + if (m_effectsList.at(it).effectStatus == m_effectsChanged.at(it).effectStatus) { + continue; + } + if (m_effectsList.at(it).effectStatus != Status::Disabled) { + 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, EffectStatus effectState) +void EffectModel::updateEffectStatus(const QModelIndex &rowIndex, Status effectState) { - setData(rowIndex, (int)effectState, EffectModel::EffectStatusRole); + setData(rowIndex, static_cast(effectState), EffectModel::EffectStatusRole); } -void EffectModel::syncConfig() +void EffectModel::save() { KConfigGroup kwinConfig(KSharedConfig::openConfig("kwinrc"), "Plugins"); for (auto it = m_effectsList.begin(); it != m_effectsList.end(); it++) { EffectData &effect = *(it); if (!effect.changed) { continue; } effect.changed = false; const QString key = effect.serviceName + QStringLiteral("Enabled"); - const bool shouldEnable = (effect.effectStatus != EffectStatus::Disabled); + const bool shouldEnable = (effect.effectStatus != Status::Disabled); const bool restoreToDefault = effect.enabledByDefaultFunction - ? effect.effectStatus == EffectStatus::EnabledUndeterminded - : shouldEnable == effect.enabledByDefault; + ? effect.effectStatus == Status::EnabledUndeterminded + : shouldEnable == effect.enabledByDefault; if (restoreToDefault) { kwinConfig.deleteEntry(key); } else { kwinConfig.writeEntry(key, shouldEnable); } } kwinConfig.sync(); syncEffectsToKWin(); } void EffectModel::defaults() { for (int i = 0; i < m_effectsList.count(); ++i) { const auto &effect = m_effectsList.at(i); - if (effect.enabledByDefaultFunction && effect.effectStatus != EffectStatus::EnabledUndeterminded) { - updateEffectStatus(index(i, 0), EffectStatus::EnabledUndeterminded); + if (effect.enabledByDefaultFunction && effect.effectStatus != Status::EnabledUndeterminded) { + updateEffectStatus(index(i, 0), Status::EnabledUndeterminded); } else if ((bool)effect.effectStatus != effect.enabledByDefault) { - updateEffectStatus(index(i, 0), effect.enabledByDefault ? EffectStatus::Enabled : EffectStatus::Disabled); + updateEffectStatus(index(i, 0), effect.enabledByDefault ? Status::Enabled : Status::Disabled); } } } -EffectFilterModel::EffectFilterModel(QObject *parent) - : QSortFilterProxyModel(parent) - , m_effectModel(new EffectModel(this)) - , m_filterOutUnsupported(true) - , m_filterOutInternal(true) -{ - setSourceModel(m_effectModel); - connect(this, &EffectFilterModel::filterOutUnsupportedChanged, this, &EffectFilterModel::invalidateFilter); - connect(this, &EffectFilterModel::filterOutInternalChanged, this, &EffectFilterModel::invalidateFilter); -} - -const QString &EffectFilterModel::filter() const +bool EffectModel::shouldStore(const EffectData &data) const { - return m_filter; + Q_UNUSED(data) + return true; } -void EffectFilterModel::setFilter(const QString &filter) -{ - if (filter == m_filter) { - return; - } - - m_filter = filter; - emit filterChanged(); - invalidateFilter(); } - -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_filterOutUnsupported) { - if (!index.data(EffectModel::SupportedRole).toBool()) { - return false; - } - } - - if (m_filterOutInternal) { - if (index.data(EffectModel::InternalRole).toBool()) { - 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; - } - if (index.data(EffectModel::CategoryRole).toString().contains(m_filter, Qt::CaseInsensitive)) { - return true; - } - - return false; -} - -void EffectFilterModel::updateEffectStatus(int rowIndex, int effectState) -{ - const QModelIndex sourceIndex = mapToSource(index(rowIndex, 0)); - - m_effectModel->updateEffectStatus(sourceIndex, EffectStatus(effectState)); -} - -void EffectFilterModel::syncConfig() -{ - m_effectModel->syncConfig(); -} - -void EffectFilterModel::load() -{ - m_effectModel->loadEffects(); -} - -void EffectFilterModel::defaults() -{ - m_effectModel->defaults(); -} - -EffectView::EffectView(ViewType type, QWidget *parent) - : QQuickWidget(parent) -{ - qRegisterMetaType(); - qmlRegisterType("org.kde.kwin.kwincompositing", 1, 0, "EffectConfig"); - qmlRegisterType("org.kde.kwin.kwincompositing", 1, 0, "EffectFilterModel"); - qmlRegisterType("org.kde.kwin.kwincompositing", 1, 0, "Compositing"); - qmlRegisterType("org.kde.kwin.kwincompositing", 1, 0, "CompositingType"); - init(type); -} - -void EffectView::init(ViewType type) -{ - KDeclarative::KDeclarative kdeclarative; - kdeclarative.setDeclarativeEngine(engine()); - kdeclarative.setTranslationDomain(QStringLiteral(TRANSLATION_DOMAIN)); - kdeclarative.setupContext(); - kdeclarative.setupEngine(engine()); - 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); - setResizeMode(QQuickWidget::SizeRootObjectToView); - setSource(QUrl(mainFile)); - rootObject()->setProperty("color", - KColorScheme(QPalette::Active, KColorScheme::Window, KSharedConfigPtr(0)).background(KColorScheme::NormalBackground).color()); - connect(rootObject(), SIGNAL(changed()), this, SIGNAL(changed())); - setMinimumSize(initialSize()); - connect(rootObject(), SIGNAL(implicitWidthChanged()), this, SLOT(slotImplicitSizeChanged())); - connect(rootObject(), SIGNAL(implicitHeightChanged()), this, SLOT(slotImplicitSizeChanged())); -} - -void EffectView::save() -{ - if (auto *model = rootObject()->findChild(QStringLiteral("filterModel"))) { - model->syncConfig(); - } - if (auto *compositing = rootObject()->findChild(QStringLiteral("compositing"))) { - compositing->save(); - } -} - -void EffectView::load() -{ - if (auto *model = rootObject()->findChild(QStringLiteral("filterModel"))) { - model->load(); - } - if (auto *compositing = rootObject()->findChild(QStringLiteral("compositing"))) { - compositing->reset(); - } -} - -void EffectView::defaults() -{ - if (auto *model = rootObject()->findChild(QStringLiteral("filterModel"))) { - model->defaults(); - } - if (auto *compositing = rootObject()->findChild(QStringLiteral("compositing"))) { - compositing->defaults(); - } -} - -void EffectView::slotImplicitSizeChanged() -{ - setMinimumSize(QSize(rootObject()->property("implicitWidth").toInt(), - rootObject()->property("implicitHeight").toInt())); -} - -}//end namespace Compositing -}//end namespace KWin diff --git a/kcmkwin/common/effectmodel.h b/kcmkwin/common/effectmodel.h new file mode 100644 index 000000000..e950f24dd --- /dev/null +++ b/kcmkwin/common/effectmodel.h @@ -0,0 +1,232 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2013 Antonis Tsiapaliokas +Copyright (C) 2018 Vlad Zagorodniy + +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 . +*********************************************************************/ + +#pragma once + +#include + +#include +#include + +#include +#include +#include + +namespace KWin +{ + +class KWIN_EXPORT EffectModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + /** + * This enum type is used to specify data roles. + **/ + enum AdditionalRoles { + /** + * The user-friendly name of the effect. + **/ + NameRole = Qt::UserRole + 1, + /** + * The description of the effect. + **/ + DescriptionRole, + /** + * The name of the effect's author. If there are several authors, they + * will be comma separated. + **/ + AuthorNameRole, + /** + * The email of the effect's author. If there are several authors, the + * emails will be comma separated. + **/ + AuthorEmailRole, + /** + * The license of the effect. + **/ + LicenseRole, + /** + * The version of the effect. + **/ + VersionRole, + /** + * The category of the effect. + **/ + CategoryRole, + /** + * The service name(plugin name) of the effect. + **/ + ServiceNameRole, + /** + * The icon name of the effect. + **/ + IconNameRole, + /** + * Whether the effect is enabled or disabled. + **/ + EffectStatusRole, + /** + * Link to a video demonstration of the effect. + **/ + VideoRole, + /** + * Link to the home page of the effect. + **/ + WebsiteRole, + /** + * Whether the effect is supported. + **/ + SupportedRole, + /** + * The exclusive group of the effect. + **/ + ExclusiveRole, + /** + * Whether the effect is internal. + **/ + InternalRole, + /** + * Whether the effect has a KCM. + **/ + ConfigurableRole, + /** + * Whether this is a scripted effect. + **/ + ScriptedRole, + /** + * Whether the effect is enabled by default. + **/ + EnabledByDefaultRole + }; + + /** + * This enum type is used to specify the status of a given effect. + **/ + enum class Status { + /** + * The effect is disabled. + **/ + Disabled = Qt::Unchecked, + /** + * An enable function is used to determine whether the effect is enabled. + * For example, such function can be useful to disable the blur effect + * when running in a virtual machine. + **/ + EnabledUndeterminded = Qt::PartiallyChecked, + /** + * The effect is enabled. + **/ + Enabled = Qt::Checked + }; + + explicit EffectModel(QObject *parent = nullptr); + + // Reimplemented from QAbstractItemModel. + QHash roleNames() const override; + QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override; + QModelIndex parent(const QModelIndex &child) const override; + int rowCount(const QModelIndex &parent = {}) const override; + int columnCount(const QModelIndex &parent = {}) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + /** + * Changes the status of a given effect. + * + * @param rowIndex An effect represented by the given index. + * @param effectState The new state. + * @note In order to actually apply the change, you have to call save(). + **/ + void updateEffectStatus(const QModelIndex &rowIndex, Status effectState); + + /** + * Loads effects. + * + * You have to call this method in order to populate the model. + **/ + void load(); + + /** + * Saves status of each modified effect. + **/ + void save(); + + /** + * Resets the status of each effect to the default state. + * + * @note In order to actually apply the change, you have to call save(). + **/ + void defaults(); + +protected: + enum class Kind { + BuiltIn, + Binary, + Scripted + }; + + struct EffectData { + QString name; + QString description; + QString authorName; + QString authorEmail; + QString license; + QString version; + QString untranslatedCategory; + QString category; + QString serviceName; + QString iconName; + Status effectStatus; + bool enabledByDefault; + bool enabledByDefaultFunction; + QUrl video; + QUrl website; + bool supported; + QString exclusiveGroup; + bool internal; + bool configurable; + Kind kind; + bool changed = false; + }; + + /** + * Returns whether the given effect should be stored in the model. + * + * @param data The effect. + * @returns @c true if the effect should be stored, otherwise @c false. + **/ + virtual bool shouldStore(const EffectData &data) const; + +private: + void loadBuiltInEffects(const KConfigGroup &kwinConfig, const KPluginInfo::List &configs); + void loadJavascriptEffects(const KConfigGroup &kwinConfig); + void loadPluginEffects(const KConfigGroup &kwinConfig, const KPluginInfo::List &configs); + int findRowByServiceName(const QString &serviceName); + void syncEffectsToKWin(); + + QVector m_effectsList; + QVector m_effectsChanged; + + Q_DISABLE_COPY(EffectModel) +}; + +} diff --git a/kcmkwin/kwincompositing/CMakeLists.txt b/kcmkwin/kwincompositing/CMakeLists.txt index 7232abde1..540a41f1d 100644 --- a/kcmkwin/kwincompositing/CMakeLists.txt +++ b/kcmkwin/kwincompositing/CMakeLists.txt @@ -1,87 +1,85 @@ ######################################################################### # KI18N Translation Domain for this library add_definitions(-DTRANSLATION_DOMAIN=\"kcmkwincompositing\") add_definitions(-DQT_NO_URL_CAST_FROM_STRING) remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_STRICT_ITERATORS -DQT_NO_CAST_FROM_BYTEARRAY) include_directories(${KWIN_SOURCE_DIR}/effects) ################# configure checks and create the configured files ################# # now create config headers configure_file(config-prefix.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-prefix.h ) configure_file(config-compiler.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-compiler.h ) set(kwincomposing_SRC model.cpp main.cpp compositing.cpp effectconfig.cpp) qt5_add_dbus_interface( kwincomposing_SRC ${KWIN_SOURCE_DIR}/org.kde.kwin.Compositing.xml kwin_compositing_interface) qt5_add_dbus_interface( kwincomposing_SRC ${KWIN_SOURCE_DIR}/org.kde.kwin.Effects.xml kwin_effects_interface) ki18n_wrap_ui(kwincomposing_SRC compositing.ui) add_library(kwincompositing MODULE ${kwincomposing_SRC}) target_link_libraries(kwincompositing Qt5::Quick Qt5::QuickWidgets Qt5::DBus Qt5::Widgets KF5::CoreAddons KF5::ConfigCore KF5::Declarative KF5::I18n - KF5::Package KF5::KCMUtils KF5::NewStuff - kwin4_effect_builtins + kcmkwincommon ) if (BUILD_TESTING) include(ECMMarkAsTest) set(modelTest_SRC model.cpp effectconfig.cpp compositing.cpp test/effectmodeltest.cpp test/modeltest.cpp) qt5_add_dbus_interface(modelTest_SRC ${KWIN_SOURCE_DIR}/org.kde.kwin.Compositing.xml kwin_compositing_interface) qt5_add_dbus_interface(modelTest_SRC ${KWIN_SOURCE_DIR}/org.kde.kwin.Effects.xml kwin_effects_interface) add_executable(effectModelTest ${modelTest_SRC}) ecm_mark_as_test(effectModelTest) target_link_libraries(effectModelTest Qt5::Quick Qt5::QuickWidgets Qt5::DBus Qt5::Test Qt5::Widgets KF5::CoreAddons KF5::ConfigCore KF5::Declarative KF5::I18n - KF5::Package KF5::KCMUtils KF5::NewStuff kwineffects - kwin4_effect_builtins + kcmkwincommon ) endif() INSTALL(DIRECTORY qml DESTINATION ${DATA_INSTALL_DIR}/kwincompositing) INSTALL(TARGETS kwincompositing DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES kwincompositing.desktop kcmkwineffects.desktop DESTINATION ${SERVICES_INSTALL_DIR}) install(FILES kwineffect.knsrc DESTINATION ${CONFIG_INSTALL_DIR}) ################# list the subdirectories ################# diff --git a/kcmkwin/kwincompositing/model.cpp b/kcmkwin/kwincompositing/model.cpp index 5d9afc9b3..e4685188c 100644 --- a/kcmkwin/kwincompositing/model.cpp +++ b/kcmkwin/kwincompositing/model.cpp @@ -1,662 +1,212 @@ /************************************************************************** * KWin - the KDE window manager * * This file is part of the KDE project. * * * * Copyright (C) 2013 Antonis Tsiapaliokas * * * * 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 . * **************************************************************************/ #include "model.h" #include "effectconfig.h" +#include "effectmodel.h" #include "compositing.h" #include #include #include -#include #include #include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include #include #include #include #include #include namespace KWin { namespace Compositing { -static QString translatedCategory(const QString &category) -{ - static const QVector knownCategories = { - QStringLiteral("Accessibility"), - QStringLiteral("Appearance"), - QStringLiteral("Candy"), - QStringLiteral("Focus"), - QStringLiteral("Show Desktop Animation"), - QStringLiteral("Tools"), - QStringLiteral("Virtual Desktop Switching Animation"), - QStringLiteral("Window Management"), - QStringLiteral("Window Open/Close Animation") - }; - - static const QVector translatedCategories = { - i18nc("Category of Desktop Effects, used as section header", "Accessibility"), - i18nc("Category of Desktop Effects, used as section header", "Appearance"), - i18nc("Category of Desktop Effects, used as section header", "Candy"), - i18nc("Category of Desktop Effects, used as section header", "Focus"), - i18nc("Category of Desktop Effects, used as section header", "Show Desktop Animation"), - i18nc("Category of Desktop Effects, used as section header", "Tools"), - i18nc("Category of Desktop Effects, used as section header", "Virtual Desktop Switching Animation"), - i18nc("Category of Desktop Effects, used as section header", "Window Management"), - i18nc("Category of Desktop Effects, used as section header", "Window Open/Close Animation") - }; - const int index = knownCategories.indexOf(category); - if (index == -1) { - qDebug() << "Unknown category '" << category << "' and thus not translated"; - return category; - } - return translatedCategories[index]; -} - -static EffectStatus effectStatus(bool enabled) -{ - return enabled ? EffectStatus::Enabled : EffectStatus::Disabled; -} - -EffectModel::EffectModel(QObject *parent) - : QAbstractItemModel(parent) { -} - -QHash< int, QByteArray > EffectModel::roleNames() const -{ - QHash 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"; - roleNames[VideoRole] = "VideoRole"; - roleNames[SupportedRole] = "SupportedRole"; - roleNames[ExclusiveRole] = "ExclusiveRole"; - roleNames[ConfigurableRole] = "ConfigurableRole"; - roleNames[ScriptedRole] = QByteArrayLiteral("ScriptedRole"); - return roleNames; -} - -QModelIndex EffectModel::index(int row, int column, const QModelIndex &parent) const -{ -if (parent.isValid() || column > 0 || column < 0 || row < 0 || row >= m_effectsList.count()) { - return QModelIndex(); - } - - return createIndex(row, column); -} - -QModelIndex EffectModel::parent(const QModelIndex &child) const -{ - Q_UNUSED(child) - - return QModelIndex(); -} - -int EffectModel::columnCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent) - return 1; -} - -int EffectModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) { - return 0; - } - return m_effectsList.count(); -} - -QVariant EffectModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) { - return QVariant(); - } - - EffectData currentEffect = m_effectsList.at(index.row()); - switch (role) { - case Qt::DisplayRole: - case NameRole: - return m_effectsList.at(index.row()).name; - case DescriptionRole: - return m_effectsList.at(index.row()).description; - case AuthorNameRole: - return m_effectsList.at(index.row()).authorName; - case AuthorEmailRole: - return m_effectsList.at(index.row()).authorEmail; - case LicenseRole: - return m_effectsList.at(index.row()).license; - case VersionRole: - return m_effectsList.at(index.row()).version; - case CategoryRole: - return m_effectsList.at(index.row()).category; - case ServiceNameRole: - return m_effectsList.at(index.row()).serviceName; - case EffectStatusRole: - return (int)m_effectsList.at(index.row()).effectStatus; - case VideoRole: - return m_effectsList.at(index.row()).video; - case SupportedRole: - return m_effectsList.at(index.row()).supported; - case ExclusiveRole: - return m_effectsList.at(index.row()).exclusiveGroup; - case InternalRole: - return m_effectsList.at(index.row()).internal; - case ConfigurableRole: - return m_effectsList.at(index.row()).configurable; - case ScriptedRole: - return m_effectsList.at(index.row()).scripted; - default: - return QVariant(); - } -} - -bool EffectModel::setData(const QModelIndex& index, const QVariant& value, int role) -{ - if (!index.isValid()) - return QAbstractItemModel::setData(index, value, role); - - if (role == EffectModel::EffectStatusRole) { - // note: whenever the StatusRole is modified (even to the same value) the entry - // gets marked as changed and will get saved to the config file. This means the - // config file could get polluted - EffectData &data = m_effectsList[index.row()]; - data.effectStatus = EffectStatus(value.toInt()); - data.changed = true; - emit dataChanged(index, index); - - if (data.effectStatus == EffectStatus::Enabled && !data.exclusiveGroup.isEmpty()) { - // need to disable all other exclusive effects in the same category - for (int i = 0; i < m_effectsList.size(); ++i) { - if (i == index.row()) { - continue; - } - EffectData &otherData = m_effectsList[i]; - if (otherData.exclusiveGroup == data.exclusiveGroup) { - otherData.effectStatus = EffectStatus::Disabled; - otherData.changed = true; - emit dataChanged(this->index(i, 0), this->index(i, 0)); - } - } - } - - return true; - } - - return QAbstractItemModel::setData(index, value, role); -} - -void EffectModel::loadBuiltInEffects(const KConfigGroup &kwinConfig, const KPluginInfo::List &configs) -{ - const auto builtins = BuiltInEffects::availableEffects(); - for (auto builtin : builtins) { - const BuiltInEffects::EffectData &data = BuiltInEffects::effectData(builtin); - EffectData effect; - effect.name = data.displayName; - effect.description = data.comment; - effect.authorName = i18n("KWin development team"); - effect.authorEmail = QString(); // not used at all - effect.license = QStringLiteral("GPL"); - effect.version = QStringLiteral(KWIN_VERSION_STRING); - effect.category = translatedCategory(data.category); - effect.serviceName = data.name; - effect.enabledByDefault = data.enabled; - effect.enabledByDefaultFunction = (data.enabledFunction != nullptr); - const QString enabledKey = QStringLiteral("%1Enabled").arg(effect.serviceName); - if (kwinConfig.hasKey(enabledKey)) { - effect.effectStatus = effectStatus(kwinConfig.readEntry(effect.serviceName + "Enabled", effect.enabledByDefault)); - } else if (data.enabledFunction != nullptr) { - effect.effectStatus = EffectStatus::EnabledUndeterminded; - } else { - effect.effectStatus = effectStatus(effect.enabledByDefault); - } - effect.video = data.video; - effect.supported = true; - effect.exclusiveGroup = data.exclusiveCategory; - effect.internal = data.internal; - effect.scripted = false; - - auto it = std::find_if(configs.begin(), configs.end(), [data](const KPluginInfo &info) { - return info.property(QStringLiteral("X-KDE-ParentComponents")).toString() == data.name; - }); - effect.configurable = it != configs.end(); - - m_effectsList << effect; - } -} - -void EffectModel::loadJavascriptEffects(const KConfigGroup &kwinConfig) -{ - const auto plugins = KPackage::PackageLoader::self()->listPackages(QStringLiteral("KWin/Effect"), QStringLiteral("kwin/effects")); - for (const KPluginMetaData &metaData : plugins) { - KPluginInfo plugin(metaData); - EffectData effect; - - 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 = translatedCategory(plugin.category()); - effect.serviceName = plugin.pluginName(); - effect.effectStatus = effectStatus(kwinConfig.readEntry(effect.serviceName + "Enabled", plugin.isPluginEnabledByDefault())); - effect.enabledByDefault = plugin.isPluginEnabledByDefault(); - effect.enabledByDefaultFunction = false; - effect.video = plugin.property(QStringLiteral("X-KWin-Video-Url")).toUrl(); - effect.supported = true; - effect.exclusiveGroup = plugin.property(QStringLiteral("X-KWin-Exclusive-Category")).toString(); - effect.internal = plugin.property(QStringLiteral("X-KWin-Internal")).toBool(); - effect.scripted = true; - - const QString pluginKeyword = plugin.property(QStringLiteral("X-KDE-PluginKeyword")).toString(); - if (!pluginKeyword.isEmpty()) { - // scripted effects have their pluginName() as the keyword - effect.configurable = plugin.property(QStringLiteral("X-KDE-ParentComponents")).toString() == pluginKeyword; - } else { - effect.configurable = false; - } - - m_effectsList << effect; - } -} - -void EffectModel::loadPluginEffects(const KConfigGroup &kwinConfig, const KPluginInfo::List &configs) -{ - static const QString subDir(QStringLiteral("kwin/effects/plugins/")); - static const QString serviceType(QStringLiteral("KWin/Effect")); - const QVector pluginEffects = KPluginLoader::findPlugins(subDir, [] (const KPluginMetaData &data) { return data.serviceTypes().contains(serviceType); }); - for (KPluginMetaData pluginEffect : pluginEffects) { - if (!pluginEffect.isValid()) - continue; - EffectData effect; - effect.name = pluginEffect.name(); - effect.description = pluginEffect.description(); - effect.license = pluginEffect.license(); - effect.version = pluginEffect.version(); - effect.category = pluginEffect.category(); - effect.serviceName = pluginEffect.pluginId(); - effect.enabledByDefault = pluginEffect.isEnabledByDefault(); - effect.supported = true; - effect.enabledByDefaultFunction = false; - effect.internal = false; - effect.scripted = false; - - for (int i = 0; i < pluginEffect.authors().count(); ++i) { - effect.authorName.append(pluginEffect.authors().at(i).name()); - effect.authorEmail.append(pluginEffect.authors().at(i).emailAddress()); - if (i+1 < pluginEffect.authors().count()) { - effect.authorName.append(", "); - effect.authorEmail.append(", "); - } - } - - if (pluginEffect.rawData().contains("org.kde.kwin.effect")) { - const QJsonObject d(pluginEffect.rawData().value("org.kde.kwin.effect").toObject()); - effect.exclusiveGroup = d.value("exclusiveGroup").toString(); - effect.video = QUrl::fromUserInput(d.value("video").toString()); - effect.enabledByDefaultFunction = d.value("enabledByDefaultMethod").toBool(); - } - - const QString enabledKey = QStringLiteral("%1Enabled").arg(effect.serviceName); - if (kwinConfig.hasKey(enabledKey)) { - effect.effectStatus = effectStatus(kwinConfig.readEntry(effect.serviceName + "Enabled", effect.enabledByDefault)); - } else if (effect.enabledByDefaultFunction) { - effect.effectStatus = EffectStatus::EnabledUndeterminded; - } else { - effect.effectStatus = effectStatus(effect.enabledByDefault); - } - - auto it = std::find_if(configs.begin(), configs.end(), [pluginEffect](const KPluginInfo &info) { - return info.property(QStringLiteral("X-KDE-ParentComponents")).toString() == pluginEffect.pluginId(); - }); - effect.configurable = it != configs.end(); - - m_effectsList << effect; - } -} - -void EffectModel::loadEffects() -{ - KConfigGroup kwinConfig(KSharedConfig::openConfig("kwinrc"), "Plugins"); - - beginResetModel(); - m_effectsChanged.clear(); - m_effectsList.clear(); - const KPluginInfo::List configs = KPluginTrader::self()->query(QStringLiteral("kwin/effects/configs/")); - loadBuiltInEffects(kwinConfig, configs); - loadJavascriptEffects(kwinConfig); - loadPluginEffects(kwinConfig, configs); - - qSort(m_effectsList.begin(), m_effectsList.end(), [](const EffectData &a, const EffectData &b) { - if (a.category == b.category) { - if (a.exclusiveGroup == b.exclusiveGroup) { - return a.name < b.name; - } - return a.exclusiveGroup < b.exclusiveGroup; - } - return a.category < b.category; - }); - - 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() << SupportedRole); - } - } - } - } - } - self->deleteLater(); - }); - } - - m_effectsChanged = m_effectsList; - endResetModel(); -} - -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 != EffectStatus::Disabled) { - 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, EffectStatus effectState) -{ - setData(rowIndex, (int)effectState, EffectModel::EffectStatusRole); -} - -void EffectModel::syncConfig() -{ - KConfigGroup kwinConfig(KSharedConfig::openConfig("kwinrc"), "Plugins"); - - for (auto it = m_effectsList.begin(); it != m_effectsList.end(); it++) { - EffectData &effect = *(it); - if (!effect.changed) { - continue; - } - effect.changed = false; - - const QString key = effect.serviceName + QStringLiteral("Enabled"); - const bool shouldEnable = (effect.effectStatus != EffectStatus::Disabled); - const bool restoreToDefault = effect.enabledByDefaultFunction - ? effect.effectStatus == EffectStatus::EnabledUndeterminded - : shouldEnable == effect.enabledByDefault; - if (restoreToDefault) { - kwinConfig.deleteEntry(key); - } else { - kwinConfig.writeEntry(key, shouldEnable); - } - } - - kwinConfig.sync(); - syncEffectsToKWin(); -} - -void EffectModel::defaults() -{ - for (int i = 0; i < m_effectsList.count(); ++i) { - const auto &effect = m_effectsList.at(i); - if (effect.enabledByDefaultFunction && effect.effectStatus != EffectStatus::EnabledUndeterminded) { - updateEffectStatus(index(i, 0), EffectStatus::EnabledUndeterminded); - } else if ((bool)effect.effectStatus != effect.enabledByDefault) { - updateEffectStatus(index(i, 0), effect.enabledByDefault ? EffectStatus::Enabled : EffectStatus::Disabled); - } - } -} - EffectFilterModel::EffectFilterModel(QObject *parent) : QSortFilterProxyModel(parent) , m_effectModel(new EffectModel(this)) , m_filterOutUnsupported(true) , m_filterOutInternal(true) { setSourceModel(m_effectModel); connect(this, &EffectFilterModel::filterOutUnsupportedChanged, this, &EffectFilterModel::invalidateFilter); connect(this, &EffectFilterModel::filterOutInternalChanged, this, &EffectFilterModel::invalidateFilter); } const QString &EffectFilterModel::filter() const { return m_filter; } void EffectFilterModel::setFilter(const QString &filter) { if (filter == m_filter) { return; } m_filter = filter; emit filterChanged(); invalidateFilter(); } 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_filterOutUnsupported) { if (!index.data(EffectModel::SupportedRole).toBool()) { return false; } } if (m_filterOutInternal) { if (index.data(EffectModel::InternalRole).toBool()) { 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; } if (index.data(EffectModel::CategoryRole).toString().contains(m_filter, Qt::CaseInsensitive)) { return true; } return false; } void EffectFilterModel::updateEffectStatus(int rowIndex, int effectState) { const QModelIndex sourceIndex = mapToSource(index(rowIndex, 0)); - m_effectModel->updateEffectStatus(sourceIndex, EffectStatus(effectState)); + m_effectModel->updateEffectStatus(sourceIndex, EffectModel::Status(effectState)); } void EffectFilterModel::syncConfig() { - m_effectModel->syncConfig(); + m_effectModel->save(); } void EffectFilterModel::load() { - m_effectModel->loadEffects(); + m_effectModel->load(); } void EffectFilterModel::defaults() { m_effectModel->defaults(); } EffectView::EffectView(ViewType type, QWidget *parent) : QQuickWidget(parent) { qRegisterMetaType(); qmlRegisterType("org.kde.kwin.kwincompositing", 1, 0, "EffectConfig"); qmlRegisterType("org.kde.kwin.kwincompositing", 1, 0, "EffectFilterModel"); qmlRegisterType("org.kde.kwin.kwincompositing", 1, 0, "Compositing"); qmlRegisterType("org.kde.kwin.kwincompositing", 1, 0, "CompositingType"); init(type); } void EffectView::init(ViewType type) { KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); kdeclarative.setTranslationDomain(QStringLiteral(TRANSLATION_DOMAIN)); kdeclarative.setupContext(); kdeclarative.setupEngine(engine()); 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); setResizeMode(QQuickWidget::SizeRootObjectToView); setSource(QUrl(mainFile)); rootObject()->setProperty("color", KColorScheme(QPalette::Active, KColorScheme::Window, KSharedConfigPtr(0)).background(KColorScheme::NormalBackground).color()); connect(rootObject(), SIGNAL(changed()), this, SIGNAL(changed())); setMinimumSize(initialSize()); connect(rootObject(), SIGNAL(implicitWidthChanged()), this, SLOT(slotImplicitSizeChanged())); connect(rootObject(), SIGNAL(implicitHeightChanged()), this, SLOT(slotImplicitSizeChanged())); } void EffectView::save() { if (auto *model = rootObject()->findChild(QStringLiteral("filterModel"))) { model->syncConfig(); } if (auto *compositing = rootObject()->findChild(QStringLiteral("compositing"))) { compositing->save(); } } void EffectView::load() { if (auto *model = rootObject()->findChild(QStringLiteral("filterModel"))) { model->load(); } if (auto *compositing = rootObject()->findChild(QStringLiteral("compositing"))) { compositing->reset(); } } void EffectView::defaults() { if (auto *model = rootObject()->findChild(QStringLiteral("filterModel"))) { model->defaults(); } if (auto *compositing = rootObject()->findChild(QStringLiteral("compositing"))) { compositing->defaults(); } } void EffectView::slotImplicitSizeChanged() { setMinimumSize(QSize(rootObject()->property("implicitWidth").toInt(), rootObject()->property("implicitHeight").toInt())); } }//end namespace Compositing }//end namespace KWin diff --git a/kcmkwin/kwincompositing/model.h b/kcmkwin/kwincompositing/model.h index 8adde91dc..c0b4ad3d3 100644 --- a/kcmkwin/kwincompositing/model.h +++ b/kcmkwin/kwincompositing/model.h @@ -1,201 +1,121 @@ /************************************************************************** * KWin - the KDE window manager * * This file is part of the KDE project. * * * * Copyright (C) 2013 Antonis Tsiapaliokas * * * * 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 . * **************************************************************************/ #ifndef MODEL_H #define MODEL_H #include #include -#include -#include -#include #include #include #include -#include namespace KWin { -namespace Compositing { - -enum class EffectStatus { - Disabled = Qt::Unchecked, - EnabledUndeterminded = Qt::PartiallyChecked, - Enabled = Qt::Checked -}; - -struct EffectData { - QString name; - QString description; - QString authorName; - QString authorEmail; - QString license; - QString version; - QString category; - QString serviceName; - EffectStatus effectStatus; - bool enabledByDefault; - bool enabledByDefaultFunction; - QUrl video; - bool supported; - QString exclusiveGroup; - bool internal; - bool configurable; - bool scripted; - bool changed = false; -}; - -class EffectModel : public QAbstractItemModel -{ - - Q_OBJECT - -public: - enum EffectRoles { - NameRole = Qt::UserRole + 1, - DescriptionRole, - AuthorNameRole, - AuthorEmailRole, - LicenseRole, - VersionRole, - CategoryRole, - ServiceNameRole, - EffectStatusRole, - VideoRole, - SupportedRole, - ExclusiveRole, - InternalRole, - ConfigurableRole, - ScriptedRole - }; - - explicit EffectModel(QObject *parent = 0); - - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex &child) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(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 = Qt::EditRole) override; - QString serviceName(const QString &effectName); - - virtual QHash< int, QByteArray > roleNames() const override; - void updateEffectStatus(const QModelIndex &rowIndex, EffectStatus effectState); - void syncEffectsToKWin(); - void syncConfig(); - void loadEffects(); - void defaults(); - -private: - void loadBuiltInEffects(const KConfigGroup &kwinConfig, const KPluginInfo::List &configs); - void loadJavascriptEffects(const KConfigGroup &kwinConfig); - void loadPluginEffects(const KConfigGroup &kwinConfig, const KPluginInfo::List &configs); - int findRowByServiceName(const QString &serviceName); - QList m_effectsList; - QList m_effectsChanged; +class EffectModel; -}; +namespace Compositing { class EffectView : public QQuickWidget { Q_OBJECT public: enum ViewType { DesktopEffectsView, CompositingSettingsView }; EffectView(ViewType type, QWidget *parent = 0); void save(); void load(); void defaults(); Q_SIGNALS: void changed(); private Q_SLOTS: void slotImplicitSizeChanged(); private: void init(ViewType type); }; class EffectFilterModel : public QSortFilterProxyModel { Q_OBJECT Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged) /** * If @c true not supported effects are excluded, if @c false no restriction on supported. * Default value is @c true. **/ Q_PROPERTY(bool filterOutUnsupported MEMBER m_filterOutUnsupported NOTIFY filterOutUnsupportedChanged) /** * If @c true internal effects are excluded, if @c false no restriction on internal. * Default value is @c true. **/ Q_PROPERTY(bool filterOutInternal MEMBER m_filterOutInternal NOTIFY filterOutInternalChanged) Q_PROPERTY(QColor backgroundActiveColor READ backgroundActiveColor CONSTANT) Q_PROPERTY(QColor backgroundNormalColor READ backgroundNormalColor CONSTANT) Q_PROPERTY(QColor backgroundAlternateColor READ backgroundAlternateColor CONSTANT) Q_PROPERTY(QColor sectionColor READ sectionColor CONSTANT) public: EffectFilterModel(QObject *parent = 0); const QString &filter() const; Q_INVOKABLE void updateEffectStatus(int rowIndex, int effectState); Q_INVOKABLE void syncConfig(); Q_INVOKABLE void load(); QColor backgroundActiveColor() { return KColorScheme(QPalette::Active, KColorScheme::Selection, KSharedConfigPtr(0)).background(KColorScheme::LinkBackground).color(); }; QColor backgroundNormalColor() { return KColorScheme(QPalette::Active, KColorScheme::View, KSharedConfigPtr(0)).background(KColorScheme::NormalBackground).color(); }; QColor backgroundAlternateColor() { return KColorScheme(QPalette::Active, KColorScheme::View, KSharedConfigPtr(0)).background(KColorScheme::AlternateBackground).color(); }; QColor sectionColor() const { QColor color = KColorScheme(QPalette::Active).foreground().color(); color.setAlphaF(0.6); return color; } void defaults(); public Q_SLOTS: void setFilter(const QString &filter); protected: virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; Q_SIGNALS: void effectModelChanged(); void filterChanged(); void filterOutUnsupportedChanged(); void filterOutInternalChanged(); private: EffectModel *m_effectModel; QString m_filter; bool m_filterOutUnsupported; bool m_filterOutInternal; }; } } #endif diff --git a/kcmkwin/kwincompositing/test/effectmodeltest.cpp b/kcmkwin/kwincompositing/test/effectmodeltest.cpp index 3b49c047d..1b56bac57 100644 --- a/kcmkwin/kwincompositing/test/effectmodeltest.cpp +++ b/kcmkwin/kwincompositing/test/effectmodeltest.cpp @@ -1,43 +1,46 @@ /************************************************************************** * KWin - the KDE window manager * * This file is part of the KDE project. * * * * Copyright (C) 2013 Antonis Tsiapaliokas * * * * 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 . * **************************************************************************/ #include "modeltest.h" #include "../model.h" #include "effectmodeltest.h" + +#include "effectmodel.h" + #include EffectModelTest::EffectModelTest(QObject *parent) : QObject(parent) { } void EffectModelTest::testEffectModel() { - KWin::Compositing::EffectModel *effectModel = new KWin::Compositing::EffectModel(); + auto effectModel = new KWin::EffectModel(); new ModelTest(effectModel, this); } void EffectModelTest::testEffectFilterModel() { KWin::Compositing::EffectFilterModel *model = new KWin::Compositing::EffectFilterModel(); new ModelTest(model, this); } QTEST_MAIN(EffectModelTest)