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.
218 lines
6.0 KiB
218 lines
6.0 KiB
/* |
|
* Copyright (C) 2019 Kai Uwe Broulik <kde@broulik.de> |
|
* |
|
* 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 "stylesmodel.h" |
|
|
|
#include <QCollator> |
|
#include <QDir> |
|
#include <QStandardPaths> |
|
#include <QStyleFactory> |
|
|
|
#include <KConfig> |
|
#include <KConfigGroup> |
|
|
|
#include <algorithm> |
|
|
|
StylesModel::StylesModel(QObject *parent) |
|
: QAbstractListModel(parent) |
|
{ |
|
} |
|
|
|
StylesModel::~StylesModel() = default; |
|
|
|
int StylesModel::rowCount(const QModelIndex &parent) const |
|
{ |
|
if (parent.isValid()) { |
|
return 0; |
|
} |
|
|
|
return m_data.count(); |
|
} |
|
|
|
QVariant StylesModel::data(const QModelIndex &index, int role) const |
|
{ |
|
if (!checkIndex(index)) { |
|
return QVariant(); |
|
} |
|
|
|
const auto &item = m_data.at(index.row()); |
|
|
|
switch (role) { |
|
case Qt::DisplayRole: |
|
if (!item.display.isEmpty()) { |
|
return item.display; |
|
} |
|
return item.styleName; |
|
case StyleNameRole: |
|
return item.styleName; |
|
case DescriptionRole: |
|
return item.description; |
|
case ConfigurableRole: |
|
return !item.configPage.isEmpty(); |
|
} |
|
|
|
return QVariant(); |
|
} |
|
|
|
QHash<int, QByteArray> StylesModel::roleNames() const |
|
{ |
|
return { |
|
{Qt::DisplayRole, QByteArrayLiteral("display")}, |
|
{StyleNameRole, QByteArrayLiteral("styleName")}, |
|
{DescriptionRole, QByteArrayLiteral("description")}, |
|
{ConfigurableRole, QByteArrayLiteral("configurable")}, |
|
}; |
|
} |
|
|
|
QString StylesModel::selectedStyle() const |
|
{ |
|
return m_selectedStyle; |
|
} |
|
|
|
void StylesModel::setSelectedStyle(const QString &style) |
|
{ |
|
if (m_selectedStyle == style) { |
|
return; |
|
} |
|
|
|
const bool firstTime = m_selectedStyle.isNull(); |
|
m_selectedStyle = style; |
|
|
|
if (!firstTime) { |
|
emit selectedStyleChanged(style); |
|
} |
|
emit selectedStyleIndexChanged(); |
|
} |
|
|
|
int StylesModel::indexOfStyle(const QString &style) const |
|
{ |
|
auto it = std::find_if(m_data.begin(), m_data.end(), [&style](const StylesModelData &item) { |
|
return item.styleName == style; |
|
}); |
|
|
|
if (it != m_data.end()) { |
|
return std::distance(m_data.begin(), it); |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
int StylesModel::selectedStyleIndex() const |
|
{ |
|
return indexOfStyle(m_selectedStyle); |
|
} |
|
|
|
QString StylesModel::styleConfigPage(const QString &style) const |
|
{ |
|
const int idx = indexOfStyle(style); |
|
if (idx == -1) { |
|
return QString(); |
|
} |
|
|
|
return m_data.at(idx).configPage; |
|
} |
|
|
|
void StylesModel::load() |
|
{ |
|
beginResetModel(); |
|
|
|
const int oldCount = m_data.count(); |
|
|
|
m_data.clear(); |
|
|
|
// Combines the info we get from QStyleFactory and our themerc files |
|
QHash<QString, StylesModelData> styleData; |
|
|
|
const QStringList allStyles = QStyleFactory::keys(); |
|
for (const QString &styleName : allStyles) { |
|
auto &item = styleData[styleName]; |
|
item.styleName = styleName; |
|
} |
|
|
|
QStringList themeFiles; |
|
|
|
const QStringList themeDirs = |
|
QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kstyle/themes"), QStandardPaths::LocateDirectory); |
|
for (const QString &dir : themeDirs) { |
|
const QStringList fileNames = QDir(dir).entryList(QStringList{QStringLiteral("*.themerc")}); |
|
for (const QString &file : fileNames) { |
|
const QString suffixedFileName = QLatin1String("kstyle/themes/") + file; |
|
if (!themeFiles.contains(suffixedFileName)) { |
|
themeFiles.append(suffixedFileName); |
|
} |
|
} |
|
} |
|
|
|
std::transform(themeFiles.begin(), themeFiles.end(), themeFiles.begin(), [](const QString &item) { |
|
return QStandardPaths::locate(QStandardPaths::GenericDataLocation, item); |
|
}); |
|
|
|
for (const QString &file : themeFiles) { |
|
KConfig config(file, KConfig::SimpleConfig); |
|
if (!config.hasGroup("KDE") || !config.hasGroup("Misc")) { |
|
continue; |
|
} |
|
|
|
KConfigGroup kdeGroup = config.group("KDE"); |
|
|
|
const QString styleName = kdeGroup.readEntry("WidgetStyle", QString()); |
|
if (styleName.isEmpty()) { |
|
continue; |
|
} |
|
|
|
auto it = styleData.find(styleName); |
|
if (it == styleData.end()) { |
|
continue; |
|
} |
|
|
|
auto &item = *it; |
|
|
|
KConfigGroup desktopEntryGroup = config.group("Desktop Entry"); |
|
if (desktopEntryGroup.readEntry("Hidden", false)) { |
|
// Don't list hidden styles |
|
styleData.remove(styleName); |
|
continue; |
|
} |
|
|
|
KConfigGroup miscGroup = config.group("Misc"); |
|
|
|
item.display = miscGroup.readEntry("Name"); |
|
item.description = miscGroup.readEntry("Comment"); |
|
item.configPage = miscGroup.readEntry("ConfigPage"); |
|
} |
|
|
|
m_data = styleData.values().toVector(); |
|
|
|
// Sort case-insensitively |
|
QCollator collator; |
|
collator.setCaseSensitivity(Qt::CaseInsensitive); |
|
std::sort(m_data.begin(), m_data.end(), [&collator](const StylesModelData &a, const StylesModelData &b) { |
|
const QString aDisplay = !a.display.isEmpty() ? a.display : a.styleName; |
|
const QString bDisplay = !b.display.isEmpty() ? b.display : b.styleName; |
|
return collator.compare(aDisplay, bDisplay) < 0; |
|
}); |
|
|
|
endResetModel(); |
|
|
|
// an item might have been added before the currently selected one |
|
if (oldCount != m_data.count()) { |
|
emit selectedStyleIndexChanged(); |
|
} |
|
}
|
|
|