Commit 268c5ced authored by Mikhail Zolotukhin's avatar Mikhail Zolotukhin
Browse files

[Application Style] Add GTK Application Style Page

Summary:
The GTK KCM is now moved to Application Style KCM as a sub page. The
functionality is identical to the one, found in GTK KCM, as well for the
bugs :)

The page is only accessible, if the `gtkconfig` kded module is loaded,
because only with the module the configuration can be changed.

Depends on D26261

Test Plan:
# Apply D26261
# **Restart kded5**
# Open System Settings, go to Application Style KCM
# The button for GTK Configuration should be present
# Go to Startup and Shutdown > Background Services, disable `Plasma GTKd` {F7877157}
# Return to the App Style KCM, the button should disappear
# Return to the Kded KCM and enable `Plasma GTKd` back
# Return to the App Style KCM - button is present now
# Go to subpage
# Happy (I hope) testing!

{F7877153}

{F7877154}

Reviewers: #vdg, broulik, #plasma, ngraham, mart

Reviewed By: #vdg, #plasma, ngraham, mart

Subscribers: davidedmundson, ngraham, plasma-devel

Tags: #plasma

Maniphest Tasks: T10611

Differential Revision: https://phabricator.kde.org/D26583
parent e236b520
......@@ -3,7 +3,15 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kcm_style\")
########### next target ###############
set(kcm_style_PART_SRCS ../krdb/krdb.cpp styleconfdialog.cpp kcmstyle.cpp stylesmodel.cpp previewitem.cpp)
set(kcm_style_PART_SRCS
../krdb/krdb.cpp
styleconfdialog.cpp
kcmstyle.cpp
stylesmodel.cpp
gtkthemesmodel.cpp
gtkpage.cpp
previewitem.cpp
)
set(klauncher_xml ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml)
qt5_add_dbus_interface(kcm_style_PART_SRCS ${klauncher_xml} klauncher_iface)
......@@ -26,11 +34,14 @@ target_link_libraries(kcm_style
KF5::GuiAddons
KF5::QuickAddons
KF5::WindowSystem
KF5::Archive
KF5::NewStuff
)
kcoreaddons_desktop_to_json(kcm_style "kcm_style.desktop")
install(FILES stylesettings.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR})
install(FILES cgctheme.knsrc cgcgtk3.knsrc DESTINATION ${KDE_INSTALL_KNSRCDIR})
install(FILES kcm_style.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR})
install(TARGETS kcm_style DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms)
......
[KNewStuff3]
Name=GTK 3.x Themes
Name[ar]=سمات جتك الثّالثة
Name[ast]=Estilos pa GTK 3.x
Name[ca]=Temes GTK 3.x
Name[ca@valencia]=Temes GTK 3.x
Name[cs]=Motivy GTK 3.x
Name[da]=GTK 3.x-temaer
Name[de]=GTK 3.x-Designs
Name[el]=Θέματα GTK 3.x
Name[en_GB]=GTK 3.x Themes
Name[es]=Temas de GTK 3.x
Name[et]=GTK 3.x teemad
Name[eu]=GTK 3.x gaiak
Name[fi]=GTK 3.x -teemat
Name[fr]=Thèmes GTK 3.x
Name[gl]=Temas de GTK 3.x
Name[he]=ערכות נושא של GTK 3.x
Name[hu]=GTK 3.x témák
Name[id]=Tema GTK 3.x
Name[it]=Temi GTK 3.x
Name[ko]=GTK 3.x 테마
Name[lt]=GTK 3.x apipavidalinimai
Name[nl]=GTK 3.x thema's
Name[nn]=GTK 3.x-tema
Name[pl]=Wygląd GTK 3.x
Name[pt]=Temas do GTK 3.x
Name[pt_BR]=Temas GTK 3.x
Name[ru]=Темы GTK 3.x
Name[sk]=Témy GTK 3.x
Name[sl]=Teme GTK 3.x
Name[sr]=ГТК 3.x теме
Name[sr@ijekavian]=ГТК 3.x теме
Name[sr@ijekavianlatin]=GTK 3.x teme
Name[sr@latin]=GTK 3.x teme
Name[sv]=GTK 3.x-teman
Name[tg]=Мавзӯъҳои GTK 3.x
Name[tr]=GTK 3.x Temaları
Name[uk]=Теми GTK 3.x
Name[x-test]=xxGTK 3.x Themesxx
Name[zh_CN]=GTK 3.x 主题
Name[zh_TW]=GTK 3.x 主題
Categories=GTK 3.x Theme/Style
Uncompress=always
InstallPath=.themes
[KNewStuff3]
Name=GTK 2.x Themes
Name[ar]=سمات جتك الثّانية
Name[ast]=Estilos pa GTK 2.x
Name[ca]=Temes GTK 2.x
Name[ca@valencia]=Temes GTK 2.x
Name[cs]=Motivy GTK 2.x
Name[da]=GTK 2.x-temaer
Name[de]=GTK 2.x-Designs
Name[el]=Θέματα GTK 2.x
Name[en_GB]=GTK 2.x Themes
Name[es]=Temas de GTK 2.x
Name[et]=GTK 2.x teemad
Name[eu]=GTK 2.x gaiak
Name[fi]=GTK 2.x -teemat
Name[fr]=Thèmes GTK 2.x
Name[gl]=Temas de GTK 2.x
Name[he]=ערכות נושא של GTK 2.x
Name[hu]=GTK 2.x témák
Name[id]=Tema GTK 2.x
Name[it]=Temi GTK 2.x
Name[ko]=GTK 2.x 테마
Name[lt]=GTK 2.x apipavidalinimai
Name[nl]=GTK 2.x thema's
Name[nn]=GTK 2.x-tema
Name[pl]=Wygląd GTK 2.x
Name[pt]=Temas do GTK 2.x
Name[pt_BR]=Temas GTK 2.x
Name[ru]=Темы GTK 2.x
Name[sk]=Témy GTK 2.x
Name[sl]=Teme GTK 2.x
Name[sr]=ГТК 2.x теме
Name[sr@ijekavian]=ГТК 2.x теме
Name[sr@ijekavianlatin]=GTK 2.x teme
Name[sr@latin]=GTK 2.x teme
Name[sv]=GTK 2.x-teman
Name[tg]=Мавзӯъҳои GTK 2.x
Name[tr]=GTK 2.x Temaları
Name[uk]=Теми GTK 2.x
Name[x-test]=xxGTK 2.x Themesxx
Name[zh_CN]=GTK 2.x 主题
Name[zh_TW]=GTK 2.x 主題
Categories=GTK 2.x Theme/Style
Uncompress=always
InstallPath=.themes
/*
* Copyright 2020 Mikhail Zolotukhin <zomial@protonmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QDir>
#include <QUrl>
#include <QDBusInterface>
#include <QDBusReply>
#include <KTar>
#include <KLocalizedString>
#include <KNS3/DownloadDialog>
#include "gtkpage.h"
#include "gtkthemesmodel.h"
GtkPage::GtkPage(QObject *parent)
: QObject(parent)
, m_gtk2ThemesModel(new GtkThemesModel(this))
, m_gtk3ThemesModel(new GtkThemesModel(this))
, gtkConfigInterface(
QStringLiteral("org.kde.GtkConfig"),
QStringLiteral("/GtkConfig"),
QStringLiteral("org.kde.GtkConfig")
)
{
connect(m_gtk2ThemesModel, &GtkThemesModel::themeRemoved, this, &GtkPage::onThemeRemoved);
connect(m_gtk3ThemesModel, &GtkThemesModel::themeRemoved, this, &GtkPage::onThemeRemoved);
connect(m_gtk2ThemesModel, &GtkThemesModel::selectedThemeChanged, this, [this](){
Q_EMIT gtkThemeSettingsChanged();
});
connect(m_gtk3ThemesModel, &GtkThemesModel::selectedThemeChanged, this, [this](){
Q_EMIT gtkThemeSettingsChanged();
});
}
GtkPage::~GtkPage()
{
delete m_gtk2ThemesModel;
delete m_gtk3ThemesModel;
}
QString GtkPage::gtk2ThemeFromConfig()
{
QDBusReply<QString> dbusReply = gtkConfigInterface.call(QStringLiteral("gtk2Theme"));
return dbusReply.value();
}
QString GtkPage::gtk3ThemeFromConfig()
{
QDBusReply<QString> dbusReply = gtkConfigInterface.call(QStringLiteral("gtk3Theme"));
return dbusReply.value();
}
void GtkPage::showGtk2Preview()
{
gtkConfigInterface.call(QStringLiteral("showGtk2ThemePreview"), m_gtk2ThemesModel->selectedTheme());
}
void GtkPage::showGtk3Preview()
{
gtkConfigInterface.call(QStringLiteral("showGtk3ThemePreview"), m_gtk3ThemesModel->selectedTheme());
}
void GtkPage::installGtk2ThemeFromGHNS()
{
KNS3::DownloadDialog downloadDialog(QStringLiteral("cgctheme.knsrc"));
downloadDialog.setWindowTitle(i18n("Download New GNOME/GTK2 Application Styles"));
downloadDialog.setWindowModality(Qt::WindowModal);
if (downloadDialog.exec()) {
load();
}
}
void GtkPage::installGtk3ThemeFromGHNS()
{
KNS3::DownloadDialog downloadDialog(QStringLiteral("cgcgtk3.knsrc"));
downloadDialog.setWindowTitle(i18n("Download New GNOME/GTK3 Application Styles"));
downloadDialog.setWindowModality(Qt::WindowModal);
if (downloadDialog.exec()) {
load();
}
}
void GtkPage::onThemeRemoved()
{
load();
defaults();
save();
}
void GtkPage::installGtkThemeFromFile(const QUrl &fileUrl)
{
QString themesInstallDirectoryPath(QDir::homePath() + QStringLiteral("/.themes"));
QDir::home().mkpath(themesInstallDirectoryPath);
KTar themeArchive(fileUrl.path());
themeArchive.open(QIODevice::ReadOnly);
auto showError = [this, fileUrl]() {
Q_EMIT showErrorMessage(i18n("%1 is not a valid GTK Theme archive.", fileUrl.fileName()));
};
QString firstEntryName = themeArchive.directory()->entries().first();
const KArchiveEntry *possibleThemeDirectory = themeArchive.directory()->entry(firstEntryName);
if (possibleThemeDirectory->isDirectory()) {
const KArchiveDirectory *themeDirectory = static_cast<const KArchiveDirectory *>(possibleThemeDirectory);
QStringList archiveSubitems = themeDirectory->entries();
if (!archiveSubitems.contains(QStringLiteral("gtk-2.0")) && archiveSubitems.indexOf(QRegExp("gtk-3.*")) == -1) {
showError();
return;
}
} else {
showError();
return;
}
themeArchive.directory()->copyTo(themesInstallDirectoryPath);
load();
}
void GtkPage::save()
{
gtkConfigInterface.call(QStringLiteral("setGtk2Theme"), m_gtk2ThemesModel->selectedTheme());
gtkConfigInterface.call(QStringLiteral("setGtk3Theme"), m_gtk3ThemesModel->selectedTheme());
}
void GtkPage::defaults()
{
Q_EMIT selectGtk2ThemeInCombobox(QStringLiteral("Breeze"));
Q_EMIT selectGtk3ThemeInCombobox(QStringLiteral("Breeze"));
}
void GtkPage::load()
{
m_gtk2ThemesModel->loadGtk2();
m_gtk3ThemesModel->loadGtk3();
Q_EMIT selectGtk2ThemeInCombobox(gtk2ThemeFromConfig());
Q_EMIT selectGtk3ThemeInCombobox(gtk3ThemeFromConfig());
}
/*
* Copyright 2020 Mikhail Zolotukhin <zomial@protonmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QObject>
#include <QDBusInterface>
#include "gtkthemesmodel.h"
class GtkPage : public QObject
{
Q_OBJECT
Q_PROPERTY(GtkThemesModel *gtk2ThemesModel MEMBER m_gtk2ThemesModel NOTIFY gtk2ThemesModelChanged)
Q_PROPERTY(GtkThemesModel *gtk3ThemesModel MEMBER m_gtk3ThemesModel NOTIFY gtk3ThemesModelChanged)
public:
GtkPage(QObject *parent = nullptr);
~GtkPage() override;
void load();
void save();
void defaults();
public Q_SLOTS:
QString gtk2ThemeFromConfig();
QString gtk3ThemeFromConfig();
void showGtk2Preview();
void showGtk3Preview();
void installGtkThemeFromFile(const QUrl &fileUrl);
void installGtk2ThemeFromGHNS();
void installGtk3ThemeFromGHNS();
void onThemeRemoved();
Q_SIGNALS:
void gtk2ThemesModelChanged(GtkThemesModel *model);
void gtk3ThemesModelChanged(GtkThemesModel *model);
void showErrorMessage(const QString &message);
void selectGtk2ThemeInCombobox(const QString &themeName);
void selectGtk3ThemeInCombobox(const QString &themeName);
void gtkThemeSettingsChanged();
private:
GtkThemesModel *m_gtk2ThemesModel;
GtkThemesModel *m_gtk3ThemesModel;
QDBusInterface gtkConfigInterface;
};
/*
* Copyright 2020 Mikhail Zolotukhin <zomial@protonmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QStandardPaths>
#include <QDir>
#include <QUrl>
#include <QDebug>
#include <KIO/DeleteJob>
#include "gtkthemesmodel.h"
GtkThemesModel::GtkThemesModel(QObject* parent)
: QAbstractListModel(parent)
, m_selectedTheme(QStringLiteral("Breeze"))
, m_themesList()
{
}
void GtkThemesModel::loadGtk2()
{
QMap<QString, QString> gtk2ThemesNames;
for (const QString &possibleThemePath : possiblePathsToThemes()) {
// If the directory has a gtk-2.0 directory inside, it is the GTK2 theme for sure
QDir possibleThemeDirectory(possibleThemePath);
bool hasGtk2DirectoryInside = possibleThemeDirectory.exists(QStringLiteral("gtk-2.0"));
if (hasGtk2DirectoryInside) {
gtk2ThemesNames.insert(possibleThemeDirectory.dirName(), possibleThemeDirectory.path());
}
}
setThemesList(gtk2ThemesNames);
}
void GtkThemesModel::loadGtk3()
{
QMap<QString, QString> gtk3ThemesNames;
static const QStringList gtk3SubdirPattern(QStringLiteral("gtk-3.*"));
for (const QString &possibleThemePath : possiblePathsToThemes()) {
// If the directory contains any of gtk-3.X folders, it is the GTK3 theme for sure
QDir possibleThemeDirectory(possibleThemePath);
if (!possibleThemeDirectory.entryList(gtk3SubdirPattern, QDir::Dirs).isEmpty()) {
gtk3ThemesNames.insert(possibleThemeDirectory.dirName(), possibleThemeDirectory.path());
}
}
setThemesList(gtk3ThemesNames);
}
QString GtkThemesModel::themePath(const QString &themeName) {
if (themeName.isEmpty()) {
return QString();
} else {
return m_themesList.find(themeName).value();
}
}
QVariant GtkThemesModel::data(const QModelIndex& index, int role) const
{
if (role == Qt::DisplayRole || role == Roles::ThemeNameRole) {
if (index.row() < 0 || index.row() > m_themesList.count()) {
return QVariant();
}
return m_themesList.keys().at(index.row());
} else if (role == Roles::ThemePathRole) {
if (index.row() < 0 || index.row() > m_themesList.count()) {
return QVariant();
}
return m_themesList.values().at(index.row());
} else {
return QVariant();
}
}
QHash<int, QByteArray> GtkThemesModel::roleNames() const
{
QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
roles[Roles::ThemeNameRole] = "theme-name";
roles[Roles::ThemePathRole] = "theme-path";
return roles;
}
int GtkThemesModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid()) {
return 0;
}
return m_themesList.count();
}
void GtkThemesModel::setThemesList(const QMap<QString, QString>& themes)
{
beginResetModel();
m_themesList = themes;
endResetModel();
}
QMap<QString, QString> GtkThemesModel::themesList() {
return m_themesList;
}
void GtkThemesModel::setSelectedTheme(const QString &themeName)
{
m_selectedTheme = themeName;
}
QString GtkThemesModel::selectedTheme()
{
return m_selectedTheme;
}
QStringList GtkThemesModel::possiblePathsToThemes()
{
QStringList possibleThemesPaths;
QStringList themesLocationsPaths = QStandardPaths::locateAll(
QStandardPaths::GenericDataLocation,
QStringLiteral("themes"),
QStandardPaths::LocateDirectory);
themesLocationsPaths << QDir::homePath() + QStringLiteral("/.themes");
for (const QString& themesLocationPath : themesLocationsPaths) {
QStringList possibleThemesDirectoriesNames = QDir(themesLocationPath).entryList(QDir::NoDotAndDotDot | QDir::AllDirs);
for (const QString &possibleThemeDirectoryName : possibleThemesDirectoriesNames) {
possibleThemesPaths += themesLocationPath + '/' + possibleThemeDirectoryName;
}
}
return possibleThemesPaths;
}
bool GtkThemesModel::selectedThemeRemovable()
{
return themePath(m_selectedTheme).contains(QDir::homePath());
}
void GtkThemesModel::removeSelectedTheme()
{
QString path = themePath(m_selectedTheme);
KIO::DeleteJob* deleteJob = KIO::del(QUrl::fromLocalFile(path), KIO::HideProgressInfo);
connect(deleteJob, &KJob::finished, this, [this](){
Q_EMIT themeRemoved();
});
}
int GtkThemesModel::findThemeIndex(const QString &themeName)
{
return static_cast<int>(std::distance(m_themesList.begin(), m_themesList.find(themeName)));
}
void GtkThemesModel::setSelectedThemeDirty()
{
Q_EMIT selectedThemeChanged(m_selectedTheme);
}
/*
* Copyright 2020 Mikhail Zolotukhin <zomial@protonmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QAbstractListModel>
#include <QMap>
class QStringList;
class QString;
class GtkThemesModel : public QAbstractListModel {
Q_OBJECT
Q_PROPERTY(QString selectedTheme READ selectedTheme WRITE setSelectedTheme NOTIFY selectedThemeChanged)
public:
GtkThemesModel(QObject *parent = nullptr);
~GtkThemesModel() override = default;
enum Roles {
ThemeNameRole = Qt::UserRole + 1,
ThemePathRole
};
void loadGtk2();
void loadGtk3();
void setThemesList(const QMap<QString, QString> &themes);
QMap<QString, QString> themesList();
void setSelectedTheme(const QString &themeName);
QString selectedTheme();
Q_SIGNAL void selectedThemeChanged(const QString &themeName);
Q_INVOKABLE bool selectedThemeRemovable();
Q_INVOKABLE void removeSelectedTheme();
Q_INVOKABLE int findThemeIndex(const QString &themeName);
Q_INVOKABLE void setSelectedThemeDirty();
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Roles::ThemeNameRole) const override;
QHash<int, QByteArray> roleNames() const override;
void requestThemesListUpdate();
Q_SIGNALS:
void themeRemoved();
private:
QStringList possiblePathsToThemes();
QString themePath(const QString &themeName);
QString m_selectedTheme;
QMap<QString, QString> m_themesList;
};
......@@ -37,6 +37,7 @@
#include <KPluginFactory>
#include <KPluginLoader>
#include <QDBusReply>
#include <QLibrary>
#include <QMetaEnum>
#include <QQuickItem>
......@@ -55,6 +56,7 @@
#include "stylesmodel.h"
#include "previewitem.h"
#include "stylesettings.h"
#include "gtkpage.h"
K_PLUGIN_FACTORY_WITH_JSON(KCMStyleFactory, "kcm_style.json", registerPlugin<KCMStyle>();)
......@@ -79,6 +81,7 @@ KCMStyle::KCMStyle(QObject *parent, const QVariantList &args)
: KQuickAddons::ManagedConfigModule(parent, args)