Warning, file /plasma/plasma-workspace/kcms/style/stylesmodel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@broulik.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "stylesmodel.h" 0008 0009 #include <QCollator> 0010 #include <QDir> 0011 #include <QStandardPaths> 0012 #include <QStyleFactory> 0013 0014 #include <KConfig> 0015 #include <KConfigGroup> 0016 0017 #include <algorithm> 0018 0019 StylesModel::StylesModel(QObject *parent) 0020 : QAbstractListModel(parent) 0021 { 0022 } 0023 0024 StylesModel::~StylesModel() = default; 0025 0026 int StylesModel::rowCount(const QModelIndex &parent) const 0027 { 0028 if (parent.isValid()) { 0029 return 0; 0030 } 0031 0032 return m_data.count(); 0033 } 0034 0035 QVariant StylesModel::data(const QModelIndex &index, int role) const 0036 { 0037 if (!checkIndex(index)) { 0038 return QVariant(); 0039 } 0040 0041 const auto &item = m_data.at(index.row()); 0042 0043 switch (role) { 0044 case Qt::DisplayRole: 0045 if (!item.display.isEmpty()) { 0046 return item.display; 0047 } 0048 return item.styleName; 0049 case StyleNameRole: 0050 return item.styleName; 0051 case DescriptionRole: 0052 return item.description; 0053 case ConfigurableRole: 0054 return !item.configPage.isEmpty(); 0055 } 0056 0057 return QVariant(); 0058 } 0059 0060 QHash<int, QByteArray> StylesModel::roleNames() const 0061 { 0062 return { 0063 {Qt::DisplayRole, QByteArrayLiteral("display")}, 0064 {StyleNameRole, QByteArrayLiteral("styleName")}, 0065 {DescriptionRole, QByteArrayLiteral("description")}, 0066 {ConfigurableRole, QByteArrayLiteral("configurable")}, 0067 }; 0068 } 0069 0070 QString StylesModel::selectedStyle() const 0071 { 0072 return m_selectedStyle; 0073 } 0074 0075 void StylesModel::setSelectedStyle(const QString &style) 0076 { 0077 if (m_selectedStyle == style) { 0078 return; 0079 } 0080 0081 const bool firstTime = m_selectedStyle.isNull(); 0082 m_selectedStyle = style; 0083 0084 if (!firstTime) { 0085 Q_EMIT selectedStyleChanged(style); 0086 } 0087 Q_EMIT selectedStyleIndexChanged(); 0088 } 0089 0090 int StylesModel::indexOfStyle(const QString &style) const 0091 { 0092 auto it = std::find_if(m_data.begin(), m_data.end(), [&style](const StylesModelData &item) { 0093 return item.styleName == style; 0094 }); 0095 0096 if (it != m_data.end()) { 0097 return std::distance(m_data.begin(), it); 0098 } 0099 0100 return -1; 0101 } 0102 0103 int StylesModel::selectedStyleIndex() const 0104 { 0105 return indexOfStyle(m_selectedStyle); 0106 } 0107 0108 QString StylesModel::styleConfigPage(const QString &style) const 0109 { 0110 const int idx = indexOfStyle(style); 0111 if (idx == -1) { 0112 return QString(); 0113 } 0114 0115 return m_data.at(idx).configPage; 0116 } 0117 0118 void StylesModel::load() 0119 { 0120 beginResetModel(); 0121 0122 const int oldCount = m_data.count(); 0123 0124 m_data.clear(); 0125 0126 // Combines the info we get from QStyleFactory and our themerc files 0127 QHash<QString, StylesModelData> styleData; 0128 0129 const QStringList allStyles = QStyleFactory::keys(); 0130 for (const QString &styleName : allStyles) { 0131 auto &item = styleData[styleName]; 0132 item.styleName = styleName; 0133 } 0134 0135 QStringList themeFiles; 0136 0137 const QStringList themeDirs = 0138 QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kstyle/themes"), QStandardPaths::LocateDirectory); 0139 for (const QString &dir : themeDirs) { 0140 const QStringList fileNames = QDir(dir).entryList(QStringList{QStringLiteral("*.themerc")}); 0141 for (const QString &file : fileNames) { 0142 const QString suffixedFileName = QLatin1String("kstyle/themes/") + file; 0143 if (!themeFiles.contains(suffixedFileName)) { 0144 themeFiles.append(suffixedFileName); 0145 } 0146 } 0147 } 0148 0149 std::transform(themeFiles.begin(), themeFiles.end(), themeFiles.begin(), [](const QString &item) { 0150 return QStandardPaths::locate(QStandardPaths::GenericDataLocation, item); 0151 }); 0152 0153 for (const QString &file : themeFiles) { 0154 KConfig config(file, KConfig::SimpleConfig); 0155 if (!config.hasGroup("KDE") || !config.hasGroup("Misc")) { 0156 continue; 0157 } 0158 0159 KConfigGroup kdeGroup = config.group("KDE"); 0160 0161 const QString styleName = kdeGroup.readEntry("WidgetStyle", QString()); 0162 if (styleName.isEmpty()) { 0163 continue; 0164 } 0165 0166 auto it = styleData.find(styleName); 0167 if (it == styleData.end()) { 0168 continue; 0169 } 0170 0171 auto &item = *it; 0172 0173 KConfigGroup desktopEntryGroup = config.group("Desktop Entry"); 0174 if (desktopEntryGroup.readEntry("Hidden", false)) { 0175 // Don't list hidden styles 0176 styleData.remove(styleName); 0177 continue; 0178 } 0179 0180 KConfigGroup miscGroup = config.group("Misc"); 0181 0182 item.display = miscGroup.readEntry("Name"); 0183 item.description = miscGroup.readEntry("Comment"); 0184 item.configPage = miscGroup.readEntry("ConfigPage"); 0185 } 0186 0187 m_data = styleData.values().toVector(); 0188 0189 // Sort case-insensitively 0190 QCollator collator; 0191 collator.setCaseSensitivity(Qt::CaseInsensitive); 0192 std::sort(m_data.begin(), m_data.end(), [&collator](const StylesModelData &a, const StylesModelData &b) { 0193 const QString aDisplay = !a.display.isEmpty() ? a.display : a.styleName; 0194 const QString bDisplay = !b.display.isEmpty() ? b.display : b.styleName; 0195 return collator.compare(aDisplay, bDisplay) < 0; 0196 }); 0197 0198 endResetModel(); 0199 0200 // an item might have been added before the currently selected one 0201 if (oldCount != m_data.count()) { 0202 Q_EMIT selectedStyleIndexChanged(); 0203 } 0204 }