You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
370 lines
15 KiB
370 lines
15 KiB
/******************************************************************** |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 2009 Martin Gräßlin <mgraesslin@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, see <http://www.gnu.org/licenses/>. |
|
*********************************************************************/ |
|
#include "decorationmodel.h" |
|
#include "preview.h" |
|
// kwin |
|
#include <kdecorationfactory.h> |
|
// Qt |
|
#include <QDir> |
|
#include <QFileInfo> |
|
#include <QApplication> |
|
#include <QStandardPaths> |
|
// KDE |
|
#include <KConfigGroup> |
|
#include <KDesktopFile> |
|
#include <KLocalizedString> |
|
#include <KServiceTypeTrader> |
|
#include <KPluginInfo> |
|
#include <KPluginTrader> |
|
#include "kwindecoration.h" |
|
|
|
/* WARNING ------------------------------------------------------------------------- |
|
* it is *ABSOLUTELY* mandatory to manage loadPlugin() and destroyPreviousPlugin() |
|
* using disablePreview() |
|
* |
|
* loadPlugin() moves the present factory pointer to "old_fact" which is then deleted |
|
* by the succeeding destroyPreviousPlugin() |
|
* |
|
* So if you loaded a new plugin and that changed the current factory, call disablePreview() |
|
* BEFORE the following destroyPreviousPlugin() destroys the factory for the current m_preview->deco->factory |
|
* (which is invoked on deco deconstruction) |
|
* WARNING ------------------------------------------------------------------------ */ |
|
|
|
static const QString s_auroraePluginName = QStringLiteral("aurorae"); |
|
|
|
namespace KWin |
|
{ |
|
|
|
DecorationModel::DecorationModel(KSharedConfigPtr config, QObject* parent) |
|
: QAbstractListModel(parent) |
|
, m_plugins(new KDecorationPreviewPlugins(config)) |
|
, m_preview(new KDecorationPreview()) |
|
, m_customButtons(false) |
|
, m_options(new KDecorationPreviewOptions) |
|
{ |
|
QHash<int, QByteArray> roleNames; |
|
roleNames[Qt::DisplayRole] = "display"; |
|
roleNames[TypeRole] = "type"; |
|
roleNames[AuroraeNameRole] = "auroraeThemeName"; |
|
roleNames[QmlMainScriptRole] = "mainScript"; |
|
roleNames[BorderSizeRole] = "borderSize"; |
|
roleNames[ButtonSizeRole] = "buttonSize"; |
|
roleNames[LibraryNameRole] = "library"; |
|
setRoleNames(roleNames); |
|
m_config = KSharedConfig::openConfig("auroraerc"); |
|
findDecorations(); |
|
} |
|
|
|
DecorationModel::~DecorationModel() |
|
{ |
|
delete m_preview; |
|
delete m_plugins; |
|
} |
|
|
|
void DecorationModel::reload() |
|
{ |
|
m_decorations.clear(); |
|
findDecorations(); |
|
} |
|
|
|
// Find all theme desktop files in all 'data' dirs owned by kwin. |
|
// And insert these into a DecorationInfo structure |
|
void DecorationModel::findDecorations() |
|
{ |
|
beginResetModel(); |
|
const auto decorations = KPluginTrader::self()->query(QStringLiteral("kwin/kdecorations")); |
|
for (const KPluginInfo &plugin : decorations) { |
|
if (plugin.pluginName() == s_auroraePluginName) { |
|
// read the Aurorae themes |
|
findAuroraeThemes(); |
|
continue; |
|
} |
|
DecorationModelData data; |
|
data.name = plugin.name(); |
|
data.pluginName = plugin.pluginName(); |
|
data.type = DecorationModelData::NativeDecoration; |
|
data.borderSize = KDecorationDefines::BorderNormal; |
|
data.closeDblClick = false; |
|
data.comment = plugin.comment(); |
|
data.author = plugin.author(); |
|
data.email = plugin.email(); |
|
data.version = plugin.version(); |
|
data.license = plugin.license(); |
|
data.website = plugin.website(); |
|
m_decorations.append(data); |
|
} |
|
|
|
KService::List offers = KServiceTypeTrader::self()->query("KWin/Decoration"); |
|
foreach (KService::Ptr service, offers) { |
|
DecorationModelData data; |
|
data.name = service->name(); |
|
data.pluginName = s_auroraePluginName; |
|
data.type = DecorationModelData::QmlDecoration; |
|
data.auroraeName = service->property("X-KDE-PluginInfo-Name").toString(); |
|
QString scriptName = service->property("X-Plasma-MainScript").toString(); |
|
data.configTranslationDomain = service->property(QStringLiteral("X-KWin-Config-TranslationDomain")).toString(); |
|
data.qmlPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kwin/decorations/" + data.auroraeName + "/contents/" + scriptName); |
|
if (data.qmlPath.isEmpty()) { |
|
// not a valid QML theme |
|
continue; |
|
} |
|
KConfigGroup config(m_config, data.auroraeName); |
|
data.borderSize = (KDecorationDefines::BorderSize)config.readEntry< int >("BorderSize", KDecorationDefines::BorderNormal); |
|
data.buttonSize = (KDecorationDefines::BorderSize)config.readEntry< int >("ButtonSize", KDecorationDefines::BorderNormal); |
|
data.closeDblClick = config.readEntry< bool >("CloseOnDoubleClickMenuButton", false); |
|
data.comment = service->comment(); |
|
KPluginInfo info(service); |
|
data.author = info.author(); |
|
data.email= info.email(); |
|
data.version = info.version(); |
|
data.license = info.license(); |
|
data.website = info.website(); |
|
m_decorations.append(data); |
|
} |
|
qSort(m_decorations.begin(), m_decorations.end(), DecorationModelData::less); |
|
endResetModel(); |
|
} |
|
|
|
void DecorationModel::findAuroraeThemes() |
|
{ |
|
// get all destop themes |
|
QStringList themes; |
|
const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "aurorae/themes/", QStandardPaths::LocateDirectory); |
|
QStringList themeDirectories; |
|
for (const QString & dir : dirs) { |
|
QDir directory = QDir(dir); |
|
for (const QString &themeDir : directory.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { |
|
themeDirectories << dir + themeDir; |
|
} |
|
} |
|
for (const QString &dir : themeDirectories) { |
|
for (const QString & file : QDir(dir).entryList(QStringList() << QStringLiteral("metadata.desktop"))) { |
|
themes.append(dir + '/' + file); |
|
} |
|
} |
|
foreach (const QString & theme, themes) { |
|
int themeSepIndex = theme.lastIndexOf('/', -1); |
|
QString themeRoot = theme.left(themeSepIndex); |
|
int themeNameSepIndex = themeRoot.lastIndexOf('/', -1); |
|
QString packageName = themeRoot.right(themeRoot.length() - themeNameSepIndex - 1); |
|
|
|
KDesktopFile df(theme); |
|
QString name = df.readName(); |
|
if (name.isEmpty()) { |
|
name = packageName; |
|
} |
|
|
|
DecorationModelData data; |
|
data.name = name; |
|
data.pluginName = s_auroraePluginName; |
|
data.type = DecorationModelData::AuroraeDecoration; |
|
data.auroraeName = packageName; |
|
KConfigGroup config(m_config, data.auroraeName); |
|
data.borderSize = (KDecorationDefines::BorderSize)config.readEntry< int >("BorderSize", KDecorationDefines::BorderNormal); |
|
data.buttonSize = (KDecorationDefines::BorderSize)config.readEntry< int >("ButtonSize", KDecorationDefines::BorderNormal); |
|
data.closeDblClick = config.readEntry< bool >("CloseOnDoubleClickMenuButton", false); |
|
metaData(data, df); |
|
m_decorations.append(data); |
|
} |
|
} |
|
|
|
void DecorationModel::metaData(DecorationModelData& data, const KDesktopFile& df) |
|
{ |
|
data.comment = df.readComment(); |
|
data.author = df.desktopGroup().readEntry("X-KDE-PluginInfo-Author", QString()); |
|
data.email = df.desktopGroup().readEntry("X-KDE-PluginInfo-Email", QString()); |
|
data.version = df.desktopGroup().readEntry("X-KDE-PluginInfo-Version", QString()); |
|
data.license = df.desktopGroup().readEntry("X-KDE-PluginInfo-License", QString()); |
|
data.website = df.desktopGroup().readEntry("X-KDE-PluginInfo-Website", QString()); |
|
} |
|
|
|
int DecorationModel::rowCount(const QModelIndex& parent) const |
|
{ |
|
Q_UNUSED(parent) |
|
return m_decorations.count(); |
|
} |
|
|
|
QVariant DecorationModel::data(const QModelIndex& index, int role) const |
|
{ |
|
if (!index.isValid()) |
|
return QVariant(); |
|
|
|
switch(role) { |
|
case Qt::DisplayRole: |
|
case NameRole: |
|
return m_decorations[ index.row()].name; |
|
case LibraryNameRole: |
|
return m_decorations[ index.row()].pluginName; |
|
case TypeRole: |
|
return m_decorations[ index.row()].type; |
|
case AuroraeNameRole: |
|
return m_decorations[ index.row()].auroraeName; |
|
case PackageDescriptionRole: |
|
return m_decorations[ index.row()].comment; |
|
case PackageAuthorRole: |
|
return m_decorations[ index.row()].author; |
|
case PackageEmailRole: |
|
return m_decorations[ index.row()].email; |
|
case PackageWebsiteRole: |
|
return m_decorations[ index.row()].website; |
|
case PackageVersionRole: |
|
return m_decorations[ index.row()].version; |
|
case PackageLicenseRole: |
|
return m_decorations[ index.row()].license; |
|
case BorderSizeRole: |
|
return static_cast< int >(m_decorations[ index.row()].borderSize); |
|
case BorderSizesRole: { |
|
QList< QVariant > sizes; |
|
const bool mustDisablePreview = m_plugins->factory() && m_plugins->factory() == m_preview->factory(); |
|
if (m_plugins->loadPlugin(m_decorations[index.row()].pluginName) && m_plugins->factory()) { |
|
foreach (KDecorationDefines::BorderSize size, m_plugins->factory()->borderSizes()) // krazy:exclude=foreach |
|
sizes << int(size); |
|
if (mustDisablePreview) // it's nuked with destroyPreviousPlugin() |
|
m_preview->disablePreview(); // so we need to get rid of m_preview->deco first |
|
m_plugins->destroyPreviousPlugin(); |
|
} |
|
return sizes; |
|
} |
|
case ButtonSizeRole: |
|
if (m_decorations[ index.row()].type == DecorationModelData::AuroraeDecoration || |
|
m_decorations[ index.row()].type == DecorationModelData::QmlDecoration) |
|
return static_cast< int >(m_decorations[ index.row()].buttonSize); |
|
else |
|
return QVariant(); |
|
case QmlMainScriptRole: |
|
return m_decorations[ index.row()].qmlPath; |
|
case CloseOnDblClickRole: |
|
return m_decorations[index.row()].closeDblClick; |
|
case ConfigTranslationDomain: |
|
return m_decorations[index.row()].configTranslationDomain; |
|
default: |
|
return QVariant(); |
|
} |
|
} |
|
|
|
bool DecorationModel::setData(const QModelIndex& index, const QVariant& value, int role) |
|
{ |
|
if (!index.isValid() || (role != BorderSizeRole && role != ButtonSizeRole && role != CloseOnDblClickRole)) |
|
return QAbstractItemModel::setData(index, value, role); |
|
|
|
const DecorationModelData::DecorationType type = m_decorations[ index.row()].type; |
|
|
|
if (role == BorderSizeRole) { |
|
m_decorations[ index.row()].borderSize = (KDecorationDefines::BorderSize)value.toInt(); |
|
if (type == DecorationModelData::AuroraeDecoration || type == DecorationModelData::QmlDecoration) { |
|
KConfigGroup config(m_config, m_decorations[ index.row()].auroraeName); |
|
config.writeEntry("BorderSize", value.toInt()); |
|
config.sync(); |
|
} |
|
emit dataChanged(index, index); |
|
emit configChanged(m_decorations[ index.row()].auroraeName); |
|
return true; |
|
} |
|
if (role == ButtonSizeRole && (type == DecorationModelData::AuroraeDecoration || type == DecorationModelData::QmlDecoration)) { |
|
m_decorations[ index.row()].buttonSize = (KDecorationDefines::BorderSize)value.toInt(); |
|
KConfigGroup config(m_config, m_decorations[ index.row()].auroraeName); |
|
config.writeEntry("ButtonSize", value.toInt()); |
|
config.sync(); |
|
emit dataChanged(index, index); |
|
emit configChanged(m_decorations[ index.row()].auroraeName); |
|
return true; |
|
} |
|
if (role == CloseOnDblClickRole && (type == DecorationModelData::AuroraeDecoration || type == DecorationModelData::QmlDecoration)) { |
|
if (m_decorations[ index.row()].closeDblClick == value.toBool()) { |
|
return false; |
|
} |
|
m_decorations[ index.row()].closeDblClick = value.toBool(); |
|
KConfigGroup config(m_config, m_decorations[ index.row()].auroraeName); |
|
config.writeEntry("CloseOnDoubleClickMenuButton", value.toBool()); |
|
config.sync(); |
|
emit dataChanged(index, index); |
|
emit configChanged(m_decorations[ index.row()].auroraeName); |
|
return true; |
|
} |
|
return QAbstractItemModel::setData(index, value, role); |
|
} |
|
|
|
|
|
void DecorationModel::changeButtons(const KWin::DecorationButtons *buttons) |
|
{ |
|
m_customButtons = buttons->customPositions(); |
|
m_options->setCustomTitleButtonsEnabled(m_customButtons); |
|
m_options->setCustomTitleButtons(buttons->leftButtons(), buttons->rightButtons()); |
|
} |
|
|
|
void DecorationModel::setButtons(bool custom, const QList<KDecorationDefines::DecorationButton> &left, const QList<KDecorationDefines::DecorationButton> &right) |
|
{ |
|
m_customButtons = custom; |
|
m_options->setCustomTitleButtons(left, right); |
|
} |
|
|
|
QModelIndex DecorationModel::indexOfLibrary(const QString& libraryName) const |
|
{ |
|
for (int i = 0; i < m_decorations.count(); i++) { |
|
if (m_decorations.at(i).pluginName.compare(libraryName) == 0) |
|
return index(i); |
|
} |
|
return QModelIndex(); |
|
} |
|
|
|
QModelIndex DecorationModel::indexOfName(const QString& decoName) const |
|
{ |
|
for (int i = 0; i < m_decorations.count(); i++) { |
|
if (m_decorations.at(i).name.compare(decoName) == 0) |
|
return index(i); |
|
} |
|
return QModelIndex(); |
|
} |
|
|
|
QModelIndex DecorationModel::indexOfAuroraeName(const QString &auroraeName, const QString &type) const |
|
{ |
|
for (int i = 0; i < m_decorations.count(); i++) { |
|
const DecorationModelData& data = m_decorations.at(i); |
|
if (type == "aurorae" && data.type == DecorationModelData::AuroraeDecoration && |
|
data.auroraeName.compare(auroraeName) == 0) |
|
return index(i); |
|
if (type == "qml" && data.type == DecorationModelData::QmlDecoration && |
|
data.auroraeName.compare(auroraeName) == 0) |
|
return index(i); |
|
} |
|
return QModelIndex(); |
|
} |
|
|
|
void DecorationModel::setBorderSize(const QModelIndex& index, KDecorationDefines::BorderSize size) |
|
{ |
|
if (!index.isValid() || m_decorations[ index.row()].type == DecorationModelData::AuroraeDecoration || m_decorations[ index.row()].type == DecorationModelData::QmlDecoration) |
|
return; |
|
m_decorations[ index.row()].borderSize = size; |
|
} |
|
|
|
QVariant DecorationModel::readConfig(const QString &themeName, const QString &key, const QVariant &defaultValue) |
|
{ |
|
return m_config->group(themeName).readEntry(key, defaultValue); |
|
} |
|
|
|
void DecorationModel::notifyConfigChanged(const QModelIndex &index) |
|
{ |
|
if (!index.isValid()) { |
|
return; |
|
} |
|
emit configChanged(m_decorations[index.row()].auroraeName); |
|
} |
|
|
|
} // namespace KWin
|
|
|