File indexing completed on 2024-05-19 05:38:25

0001 /*
0002     SPDX-FileCopyrightText: 2020 Mikhail Zolotukhin <zomial@protonmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include <QDebug>
0008 #include <QDir>
0009 #include <QStandardPaths>
0010 #include <QUrl>
0011 
0012 #include <KIO/DeleteJob>
0013 
0014 #include "gtkthemesmodel.h"
0015 
0016 GtkThemesModel::GtkThemesModel(QObject *parent)
0017     : QAbstractListModel(parent)
0018     , m_selectedTheme(QStringLiteral("Breeze"))
0019     , m_themes()
0020 {
0021 }
0022 
0023 void GtkThemesModel::load()
0024 {
0025     QMap<QString, QString> gtk3ThemesNames;
0026 
0027     static const QStringList gtk3SubdirPattern(QStringLiteral("gtk-3.*"));
0028     for (const QString &possibleThemePath : possiblePathsToThemes()) {
0029         // If the directory contains any of gtk-3.X folders, it is the GTK3 theme for sure
0030         QDir possibleThemeDirectory(possibleThemePath);
0031         if (!possibleThemeDirectory.entryList(gtk3SubdirPattern, QDir::Dirs).isEmpty()) {
0032             // Do not show dark Breeze GTK variant, since the colors of it
0033             // are coming from the color scheme and selecting them here
0034             // is redundant and does not work
0035             if (possibleThemeDirectory.dirName() == QStringLiteral("Breeze-Dark")) {
0036                 continue;
0037             }
0038 
0039             gtk3ThemesNames.insert(possibleThemeDirectory.dirName(), possibleThemeDirectory.path());
0040         }
0041     }
0042 
0043     setThemesList(gtk3ThemesNames);
0044 }
0045 
0046 QString GtkThemesModel::themePath(const QString &themeName)
0047 {
0048     if (themeName.isEmpty()) {
0049         return QString();
0050     } else {
0051         return m_themes.constFind(themeName).value();
0052     }
0053 }
0054 
0055 QVariant GtkThemesModel::data(const QModelIndex &index, int role) const
0056 {
0057     if (!checkIndex(index)) {
0058         return QVariant();
0059     }
0060 
0061     auto it = m_themes.constBegin();
0062     std::advance(it, index.row());
0063 
0064     switch (role) {
0065     case Qt::DisplayRole:
0066     case Roles::ThemeNameRole:
0067         return it.key();
0068     case Roles::ThemePathRole:
0069         return it.value();
0070     default:
0071         return QVariant();
0072     }
0073 }
0074 
0075 QHash<int, QByteArray> GtkThemesModel::roleNames() const
0076 {
0077     QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
0078     roles[Roles::ThemeNameRole] = "theme-name";
0079     roles[Roles::ThemePathRole] = "theme-path";
0080 
0081     return roles;
0082 }
0083 
0084 int GtkThemesModel::rowCount(const QModelIndex &parent) const
0085 {
0086     if (parent.isValid()) {
0087         return 0;
0088     }
0089     return m_themes.count();
0090 }
0091 
0092 void GtkThemesModel::setThemesList(const QMap<QString, QString> &themes)
0093 {
0094     beginResetModel();
0095     m_themes = themes;
0096     endResetModel();
0097 }
0098 
0099 QMap<QString, QString> GtkThemesModel::themesList()
0100 {
0101     return m_themes;
0102 }
0103 
0104 void GtkThemesModel::setSelectedTheme(const QString &themeName)
0105 {
0106     m_selectedTheme = themeName;
0107     Q_EMIT selectedThemeChanged(themeName);
0108 }
0109 
0110 QString GtkThemesModel::selectedTheme()
0111 {
0112     return m_selectedTheme;
0113 }
0114 
0115 QStringList GtkThemesModel::possiblePathsToThemes()
0116 {
0117     QStringList possibleThemesPaths;
0118 
0119     QStringList themesLocationsPaths =
0120         QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("themes"), QStandardPaths::LocateDirectory);
0121     // TODO: Remove. See https://gitlab.gnome.org/GNOME/gtk/-/issues/6219 for when GTK plans to, and when we should,
0122     // remove it as well.
0123     themesLocationsPaths << QDir::homePath() + QStringLiteral("/.themes");
0124 
0125     for (const QString &themesLocationPath : std::as_const(themesLocationsPaths)) {
0126         const QStringList possibleThemesDirectoriesNames = QDir(themesLocationPath).entryList(QDir::NoDotAndDotDot | QDir::AllDirs);
0127         for (const QString &possibleThemeDirectoryName : possibleThemesDirectoriesNames) {
0128             possibleThemesPaths += themesLocationPath + '/' + possibleThemeDirectoryName;
0129         }
0130     }
0131 
0132     return possibleThemesPaths;
0133 }
0134 
0135 bool GtkThemesModel::selectedThemeRemovable()
0136 {
0137     return themePath(m_selectedTheme).contains(QDir::homePath());
0138 }
0139 
0140 void GtkThemesModel::removeSelectedTheme()
0141 {
0142     QString path = themePath(m_selectedTheme);
0143     KIO::DeleteJob *deleteJob = KIO::del(QUrl::fromLocalFile(path), KIO::HideProgressInfo);
0144     connect(deleteJob, &KJob::finished, this, [this]() {
0145         Q_EMIT themeRemoved();
0146     });
0147 }
0148 
0149 int GtkThemesModel::findThemeIndex(const QString &themeName)
0150 {
0151     return static_cast<int>(std::distance(m_themes.constBegin(), m_themes.constFind(themeName)));
0152 }
0153 
0154 void GtkThemesModel::setSelectedThemeDirty()
0155 {
0156     Q_EMIT selectedThemeChanged(m_selectedTheme);
0157 }