File indexing completed on 2024-05-19 05:38:03
0001 /* 0002 SPDX-FileCopyrightText: 2007 Matthew Woehlke <mw_triad@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2007 Jeremy Whiting <jpwhiting@kde.org> 0004 SPDX-FileCopyrightText: 2016 Olivier Churlaud <olivier@churlaud.com> 0005 SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@privat.broulik.de> 0006 0007 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0008 */ 0009 0010 #include "colorsmodel.h" 0011 0012 #include <QCollator> 0013 #include <QDir> 0014 #include <QStandardPaths> 0015 0016 #include <KColorScheme> 0017 #include <KConfigGroup> 0018 #include <KSharedConfig> 0019 0020 #include <algorithm> 0021 0022 #include "colorsapplicator.h" 0023 0024 using namespace Qt::StringLiterals; 0025 0026 ColorsModel::ColorsModel(QObject *parent) 0027 : QAbstractListModel(parent) 0028 { 0029 } 0030 0031 ColorsModel::~ColorsModel() = default; 0032 0033 int ColorsModel::rowCount(const QModelIndex &parent) const 0034 { 0035 if (parent.isValid()) { 0036 return 0; 0037 } 0038 0039 return m_data.count(); 0040 } 0041 0042 QVariant ColorsModel::data(const QModelIndex &index, int role) const 0043 { 0044 if (!index.isValid() || index.row() >= m_data.count()) { 0045 return QVariant(); 0046 } 0047 0048 const auto &item = m_data.at(index.row()); 0049 0050 switch (role) { 0051 case Qt::DisplayRole: 0052 return item.display; 0053 case SchemeNameRole: 0054 return item.schemeName; 0055 case PaletteRole: 0056 return item.palette; 0057 case SelectedPaletteRole: 0058 return item.selectedPalette; 0059 case DisabledText: 0060 return item.palette.color(QPalette::Disabled, QPalette::Text); 0061 case ActiveTitleBarBackgroundRole: 0062 return item.activeTitleBarBackground; 0063 case ActiveTitleBarForegroundRole: 0064 return item.activeTitleBarForeground; 0065 case PendingDeletionRole: 0066 return item.pendingDeletion; 0067 case RemovableRole: 0068 return item.removable; 0069 case AccentActiveTitlebarRole: 0070 return item.accentActiveTitlebar; 0071 case Tints: 0072 return item.tints; 0073 case TintFactor: 0074 return item.tintFactor; 0075 } 0076 0077 return QVariant(); 0078 } 0079 0080 bool ColorsModel::setData(const QModelIndex &index, const QVariant &value, int role) 0081 { 0082 if (!index.isValid() || index.row() >= m_data.count()) { 0083 return false; 0084 } 0085 0086 if (role == PendingDeletionRole) { 0087 auto &item = m_data[index.row()]; 0088 0089 const bool pendingDeletion = value.toBool(); 0090 0091 if (item.pendingDeletion != pendingDeletion) { 0092 item.pendingDeletion = pendingDeletion; 0093 Q_EMIT dataChanged(index, index, {PendingDeletionRole}); 0094 0095 if (index.row() == selectedSchemeIndex() && pendingDeletion) { 0096 // move to the next non-pending theme 0097 const auto nonPending = match(index, PendingDeletionRole, false); 0098 if (!nonPending.isEmpty()) { 0099 setSelectedScheme(nonPending.first().data(SchemeNameRole).toString()); 0100 } 0101 } 0102 0103 Q_EMIT pendingDeletionsChanged(); 0104 return true; 0105 } 0106 } 0107 0108 return false; 0109 } 0110 0111 QHash<int, QByteArray> ColorsModel::roleNames() const 0112 { 0113 return { 0114 {Qt::DisplayRole, QByteArrayLiteral("display")}, 0115 {SchemeNameRole, QByteArrayLiteral("schemeName")}, 0116 {PaletteRole, QByteArrayLiteral("palette")}, 0117 {SelectedPaletteRole, QByteArrayLiteral("selectedPalette")}, 0118 {ActiveTitleBarBackgroundRole, QByteArrayLiteral("activeTitleBarBackground")}, 0119 {ActiveTitleBarForegroundRole, QByteArrayLiteral("activeTitleBarForeground")}, 0120 {DisabledText, QByteArrayLiteral("disabledText")}, 0121 {RemovableRole, QByteArrayLiteral("removable")}, 0122 {AccentActiveTitlebarRole, QByteArrayLiteral("accentActiveTitlebar")}, 0123 {PendingDeletionRole, QByteArrayLiteral("pendingDeletion")}, 0124 {Tints, QByteArrayLiteral("tints")}, 0125 {TintFactor, QByteArrayLiteral("tintFactor")}, 0126 }; 0127 } 0128 0129 QString ColorsModel::selectedScheme() const 0130 { 0131 return m_selectedScheme; 0132 } 0133 0134 void ColorsModel::setSelectedScheme(const QString &scheme) 0135 { 0136 if (m_selectedScheme == scheme) { 0137 return; 0138 } 0139 0140 m_selectedScheme = scheme; 0141 0142 Q_EMIT selectedSchemeChanged(scheme); 0143 Q_EMIT selectedSchemeIndexChanged(); 0144 } 0145 0146 int ColorsModel::indexOfScheme(const QString &scheme) const 0147 { 0148 auto it = std::find_if(m_data.begin(), m_data.end(), [&scheme](const ColorsModelData &item) { 0149 return item.schemeName == scheme; 0150 }); 0151 0152 if (it != m_data.end()) { 0153 return std::distance(m_data.begin(), it); 0154 } 0155 0156 return -1; 0157 } 0158 0159 int ColorsModel::selectedSchemeIndex() const 0160 { 0161 return indexOfScheme(m_selectedScheme); 0162 } 0163 0164 void ColorsModel::load() 0165 { 0166 beginResetModel(); 0167 0168 const int oldCount = m_data.count(); 0169 0170 m_data.clear(); 0171 0172 QStringList schemeFiles; 0173 0174 const QStringList schemeDirs = 0175 QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory); 0176 for (const QString &dir : schemeDirs) { 0177 const QStringList fileNames = QDir(dir).entryList(QStringList{QStringLiteral("*.colors")}); 0178 for (const QString &file : fileNames) { 0179 const QString suffixedFileName = QLatin1String("color-schemes/") + file; 0180 // can't use QSet because of the transform below (passing const QString as this argument discards qualifiers) 0181 if (!schemeFiles.contains(suffixedFileName)) { 0182 schemeFiles.append(suffixedFileName); 0183 } 0184 } 0185 } 0186 0187 std::transform(schemeFiles.begin(), schemeFiles.end(), schemeFiles.begin(), [](const QString &item) { 0188 return QStandardPaths::locate(QStandardPaths::GenericDataLocation, item); 0189 }); 0190 0191 for (const QString &schemeFile : schemeFiles) { 0192 const QFileInfo fi(schemeFile); 0193 const QString baseName = fi.baseName(); 0194 0195 KSharedConfigPtr config = KSharedConfig::openConfig(schemeFile, KConfig::SimpleConfig); 0196 KConfigGroup group(config, u"General"_s); 0197 const QString name = group.readEntry("Name", baseName); 0198 0199 const QPalette palette = KColorScheme::createApplicationPalette(config); 0200 0201 // QPalette has no "selected" state, so there's e.g. no "selected link" color. 0202 const KColorScheme selectedScheme = KColorScheme(QPalette::Normal, KColorScheme::Selection, config); 0203 QPalette selectedPalette = palette; 0204 selectedPalette.setBrush(QPalette::Base, selectedScheme.background()); 0205 selectedPalette.setBrush(QPalette::Text, selectedScheme.foreground()); 0206 selectedPalette.setBrush(QPalette::AlternateBase, selectedScheme.background(KColorScheme::AlternateBackground)); 0207 selectedPalette.setBrush(QPalette::Link, selectedScheme.foreground(KColorScheme::LinkText)); 0208 selectedPalette.setBrush(QPalette::LinkVisited, selectedScheme.foreground(KColorScheme::VisitedText)); 0209 0210 QColor activeTitleBarBackground, activeTitleBarForeground; 0211 if (KColorScheme::isColorSetSupported(config, KColorScheme::Header)) { 0212 KColorScheme headerColorScheme(QPalette::Active, KColorScheme::Header, config); 0213 activeTitleBarBackground = headerColorScheme.background().color(); 0214 activeTitleBarForeground = headerColorScheme.foreground().color(); 0215 } else { 0216 KConfigGroup wmConfig(config, u"WM"_s); 0217 activeTitleBarBackground = wmConfig.readEntry("activeBackground", palette.color(QPalette::Active, QPalette::Highlight)); 0218 activeTitleBarForeground = wmConfig.readEntry("activeForeground", palette.color(QPalette::Active, QPalette::HighlightedText)); 0219 } 0220 0221 const bool colorActiveTitleBar = group.readEntry("accentActiveTitlebar", false); 0222 0223 ColorsModelData item{ 0224 name, 0225 baseName, 0226 palette, 0227 selectedPalette, 0228 activeTitleBarBackground, 0229 activeTitleBarForeground, 0230 fi.isWritable(), 0231 colorActiveTitleBar, 0232 false, // pending deletion 0233 group.hasKey("TintFactor"), 0234 group.readEntry<qreal>("TintFactor", DefaultTintFactor), 0235 }; 0236 0237 m_data.append(item); 0238 } 0239 0240 // Sort case-insensitively 0241 QCollator collator; 0242 collator.setCaseSensitivity(Qt::CaseInsensitive); 0243 std::sort(m_data.begin(), m_data.end(), [&collator](const ColorsModelData &a, const ColorsModelData &b) { 0244 return collator.compare(a.display, b.display) < 0; 0245 }); 0246 0247 endResetModel(); 0248 0249 // an item might have been added before the currently selected one 0250 if (oldCount != m_data.count()) { 0251 Q_EMIT selectedSchemeIndexChanged(); 0252 } 0253 } 0254 0255 QStringList ColorsModel::pendingDeletions() const 0256 { 0257 QStringList pendingDeletions; 0258 0259 for (const auto &item : m_data) { 0260 if (item.pendingDeletion) { 0261 pendingDeletions.append(item.schemeName); 0262 } 0263 } 0264 0265 return pendingDeletions; 0266 } 0267 0268 void ColorsModel::removeItemsPendingDeletion() 0269 { 0270 for (int i = m_data.count() - 1; i >= 0; --i) { 0271 if (m_data.at(i).pendingDeletion) { 0272 beginRemoveRows(QModelIndex(), i, i); 0273 m_data.remove(i); 0274 endRemoveRows(); 0275 } 0276 } 0277 } 0278 0279 #include "moc_colorsmodel.cpp"