Warning, file /plasma/plasma-workspace/kcms/colors/colorsmodel.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: 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 ColorsModel::ColorsModel(QObject *parent)
0025     : QAbstractListModel(parent)
0026 {
0027 }
0028 
0029 ColorsModel::~ColorsModel() = default;
0030 
0031 int ColorsModel::rowCount(const QModelIndex &parent) const
0032 {
0033     if (parent.isValid()) {
0034         return 0;
0035     }
0036 
0037     return m_data.count();
0038 }
0039 
0040 QVariant ColorsModel::data(const QModelIndex &index, int role) const
0041 {
0042     if (!index.isValid() || index.row() >= m_data.count()) {
0043         return QVariant();
0044     }
0045 
0046     const auto &item = m_data.at(index.row());
0047 
0048     switch (role) {
0049     case Qt::DisplayRole:
0050         return item.display;
0051     case SchemeNameRole:
0052         return item.schemeName;
0053     case PaletteRole:
0054         return item.palette;
0055     case DisabledText:
0056         return item.palette.color(QPalette::Disabled, QPalette::Text);
0057     case ActiveTitleBarBackgroundRole:
0058         return item.activeTitleBarBackground;
0059     case ActiveTitleBarForegroundRole:
0060         return item.activeTitleBarForeground;
0061     case PendingDeletionRole:
0062         return item.pendingDeletion;
0063     case RemovableRole:
0064         return item.removable;
0065     case AccentActiveTitlebarRole:
0066         return item.accentActiveTitlebar;
0067     case Tints:
0068         return item.tints;
0069     case TintFactor:
0070         return item.tintFactor;
0071     }
0072 
0073     return QVariant();
0074 }
0075 
0076 bool ColorsModel::setData(const QModelIndex &index, const QVariant &value, int role)
0077 {
0078     if (!index.isValid() || index.row() >= m_data.count()) {
0079         return false;
0080     }
0081 
0082     if (role == PendingDeletionRole) {
0083         auto &item = m_data[index.row()];
0084 
0085         const bool pendingDeletion = value.toBool();
0086 
0087         if (item.pendingDeletion != pendingDeletion) {
0088             item.pendingDeletion = pendingDeletion;
0089             Q_EMIT dataChanged(index, index, {PendingDeletionRole});
0090 
0091             if (index.row() == selectedSchemeIndex() && pendingDeletion) {
0092                 // move to the next non-pending theme
0093                 const auto nonPending = match(index, PendingDeletionRole, false);
0094                 if (!nonPending.isEmpty()) {
0095                     setSelectedScheme(nonPending.first().data(SchemeNameRole).toString());
0096                 }
0097             }
0098 
0099             Q_EMIT pendingDeletionsChanged();
0100             return true;
0101         }
0102     }
0103 
0104     return false;
0105 }
0106 
0107 QHash<int, QByteArray> ColorsModel::roleNames() const
0108 {
0109     return {
0110         {Qt::DisplayRole, QByteArrayLiteral("display")},
0111         {SchemeNameRole, QByteArrayLiteral("schemeName")},
0112         {PaletteRole, QByteArrayLiteral("palette")},
0113         {ActiveTitleBarBackgroundRole, QByteArrayLiteral("activeTitleBarBackground")},
0114         {ActiveTitleBarForegroundRole, QByteArrayLiteral("activeTitleBarForeground")},
0115         {DisabledText, QByteArrayLiteral("disabledText")},
0116         {RemovableRole, QByteArrayLiteral("removable")},
0117         {AccentActiveTitlebarRole, QByteArrayLiteral("accentActiveTitlebar")},
0118         {PendingDeletionRole, QByteArrayLiteral("pendingDeletion")},
0119         {Tints, QByteArrayLiteral("tints")},
0120         {TintFactor, QByteArrayLiteral("tintFactor")},
0121     };
0122 }
0123 
0124 QString ColorsModel::selectedScheme() const
0125 {
0126     return m_selectedScheme;
0127 }
0128 
0129 void ColorsModel::setSelectedScheme(const QString &scheme)
0130 {
0131     if (m_selectedScheme == scheme) {
0132         return;
0133     }
0134 
0135     m_selectedScheme = scheme;
0136 
0137     Q_EMIT selectedSchemeChanged(scheme);
0138     Q_EMIT selectedSchemeIndexChanged();
0139 }
0140 
0141 int ColorsModel::indexOfScheme(const QString &scheme) const
0142 {
0143     auto it = std::find_if(m_data.begin(), m_data.end(), [&scheme](const ColorsModelData &item) {
0144         return item.schemeName == scheme;
0145     });
0146 
0147     if (it != m_data.end()) {
0148         return std::distance(m_data.begin(), it);
0149     }
0150 
0151     return -1;
0152 }
0153 
0154 int ColorsModel::selectedSchemeIndex() const
0155 {
0156     return indexOfScheme(m_selectedScheme);
0157 }
0158 
0159 void ColorsModel::load()
0160 {
0161     beginResetModel();
0162 
0163     const int oldCount = m_data.count();
0164 
0165     m_data.clear();
0166 
0167     QStringList schemeFiles;
0168 
0169     const QStringList schemeDirs =
0170         QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory);
0171     for (const QString &dir : schemeDirs) {
0172         const QStringList fileNames = QDir(dir).entryList(QStringList{QStringLiteral("*.colors")});
0173         for (const QString &file : fileNames) {
0174             const QString suffixedFileName = QLatin1String("color-schemes/") + file;
0175             // can't use QSet because of the transform below (passing const QString as this argument discards qualifiers)
0176             if (!schemeFiles.contains(suffixedFileName)) {
0177                 schemeFiles.append(suffixedFileName);
0178             }
0179         }
0180     }
0181 
0182     std::transform(schemeFiles.begin(), schemeFiles.end(), schemeFiles.begin(), [](const QString &item) {
0183         return QStandardPaths::locate(QStandardPaths::GenericDataLocation, item);
0184     });
0185 
0186     for (const QString &schemeFile : schemeFiles) {
0187         const QFileInfo fi(schemeFile);
0188         const QString baseName = fi.baseName();
0189 
0190         KSharedConfigPtr config = KSharedConfig::openConfig(schemeFile, KConfig::SimpleConfig);
0191         KConfigGroup group(config, "General");
0192         const QString name = group.readEntry("Name", baseName);
0193 
0194         const QPalette palette = KColorScheme::createApplicationPalette(config);
0195 
0196         QColor activeTitleBarBackground, activeTitleBarForeground;
0197         if (KColorScheme::isColorSetSupported(config, KColorScheme::Header)) {
0198             KColorScheme headerColorScheme(QPalette::Active, KColorScheme::Header, config);
0199             activeTitleBarBackground = headerColorScheme.background().color();
0200             activeTitleBarForeground = headerColorScheme.foreground().color();
0201         } else {
0202             KConfigGroup wmConfig(config, QStringLiteral("WM"));
0203             activeTitleBarBackground = wmConfig.readEntry("activeBackground", palette.color(QPalette::Active, QPalette::Highlight));
0204             activeTitleBarForeground = wmConfig.readEntry("activeForeground", palette.color(QPalette::Active, QPalette::HighlightedText));
0205         }
0206 
0207         const bool colorActiveTitleBar = group.readEntry("accentActiveTitlebar", false);
0208 
0209         ColorsModelData item{
0210             name,
0211             baseName,
0212             palette,
0213             activeTitleBarBackground,
0214             activeTitleBarForeground,
0215             fi.isWritable(),
0216             colorActiveTitleBar,
0217             false, // pending deletion
0218             group.hasKey("TintFactor"),
0219             group.readEntry<qreal>("TintFactor", DefaultTintFactor),
0220         };
0221 
0222         m_data.append(item);
0223     }
0224 
0225     // Sort case-insensitively
0226     QCollator collator;
0227     collator.setCaseSensitivity(Qt::CaseInsensitive);
0228     std::sort(m_data.begin(), m_data.end(), [&collator](const ColorsModelData &a, const ColorsModelData &b) {
0229         return collator.compare(a.display, b.display) < 0;
0230     });
0231 
0232     endResetModel();
0233 
0234     // an item might have been added before the currently selected one
0235     if (oldCount != m_data.count()) {
0236         Q_EMIT selectedSchemeIndexChanged();
0237     }
0238 }
0239 
0240 QStringList ColorsModel::pendingDeletions() const
0241 {
0242     QStringList pendingDeletions;
0243 
0244     for (const auto &item : m_data) {
0245         if (item.pendingDeletion) {
0246             pendingDeletions.append(item.schemeName);
0247         }
0248     }
0249 
0250     return pendingDeletions;
0251 }
0252 
0253 void ColorsModel::removeItemsPendingDeletion()
0254 {
0255     for (int i = m_data.count() - 1; i >= 0; --i) {
0256         if (m_data.at(i).pendingDeletion) {
0257             beginRemoveRows(QModelIndex(), i, i);
0258             m_data.remove(i);
0259             endRemoveRows();
0260         }
0261     }
0262 }