diff --git a/kcms/colors/colors.cpp b/kcms/colors/colors.cpp index 01f771973..333f5bf3d 100644 --- a/kcms/colors/colors.cpp +++ b/kcms/colors/colors.cpp @@ -1,476 +1,480 @@ /* * Copyright (C) 2007 Matthew Woehlke * Copyright (C) 2007 Jeremy Whiting * Copyright (C) 2016 Olivier Churlaud * Copyright (C) 2019 Kai Uwe Broulik * Copyright (c) 2019 Cyril Rossi * * 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 . */ #include "colors.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../krdb/krdb.h" #include "colorsmodel.h" #include "filterproxymodel.h" #include "colorssettings.h" K_PLUGIN_FACTORY_WITH_JSON(KCMColorsFactory, "kcm_colors.json", registerPlugin();) KCMColors::KCMColors(QObject *parent, const QVariantList &args) : KQuickAddons::ManagedConfigModule(parent, args) , m_model(new ColorsModel(this)) , m_filteredModel(new FilterProxyModel(this)) , m_settings(new ColorsSettings(this)) , m_config(KSharedConfig::openConfig(QStringLiteral("kdeglobals"))) { qmlRegisterUncreatableType("org.kde.private.kcms.colors", 1, 0, "KCM", QStringLiteral("Cannot create instances of KCM")); qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); KAboutData *about = new KAboutData(QStringLiteral("kcm_colors"), i18n("Colors"), QStringLiteral("2.0"), QString(), KAboutLicense::GPL); about->addAuthor(i18n("Kai Uwe Broulik"), QString(), QStringLiteral("kde@privat.broulik.de")); setAboutData(about); - connect(m_model, &ColorsModel::pendingDeletionsChanged, this, [this] { - setNeedsSave(true); - }); + connect(m_model, &ColorsModel::pendingDeletionsChanged, this, &KCMColors::settingsChanged); connect(m_model, &ColorsModel::selectedSchemeChanged, this, [this](const QString &scheme) { m_selectedSchemeDirty = true; m_settings->setColorScheme(scheme); }); connect(m_settings, &ColorsSettings::colorSchemeChanged, this, [this] { m_model->setSelectedScheme(m_settings->colorScheme()); }); connect(m_model, &ColorsModel::selectedSchemeChanged, m_filteredModel, &FilterProxyModel::setSelectedScheme); m_filteredModel->setSourceModel(m_model); } KCMColors::~KCMColors() { m_config->markAsClean(); } ColorsModel *KCMColors::model() const { return m_model; } FilterProxyModel *KCMColors::filteredModel() const { return m_filteredModel; } ColorsSettings *KCMColors::colorsSettings() const { return m_settings; } bool KCMColors::downloadingFile() const { return m_tempCopyJob; } void KCMColors::getNewStuff(QQuickItem *ctx) { if (!m_newStuffDialog) { m_newStuffDialog = new KNS3::DownloadDialog(QStringLiteral("colorschemes.knsrc")); m_newStuffDialog.data()->setWindowTitle(i18n("Download New Color Schemes")); m_newStuffDialog->setWindowModality(Qt::WindowModal); m_newStuffDialog->winId(); // so it creates the windowHandle(); connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, [this] { m_model->load(); const auto newEntries = m_newStuffDialog->installedEntries(); // If one new theme was installed, select the first color file in it if (newEntries.count() == 1) { QStringList installedThemes; const QString suffix = QStringLiteral(".colors"); for (const QString &path : newEntries.first().installedFiles()) { const QString fileName = path.section(QLatin1Char('/'), -1, -1); const int suffixPos = fileName.indexOf(suffix); if (suffixPos != fileName.length() - suffix.length()) { continue; } installedThemes.append(fileName.left(suffixPos)); } if (!installedThemes.isEmpty()) { // The list is sorted by (potentially translated) name // but that would require us parse every file, so this should be close enough std::sort(installedThemes.begin(), installedThemes.end()); m_model->setSelectedScheme(installedThemes.constFirst()); } } }); } if (ctx && ctx->window()) { m_newStuffDialog->windowHandle()->setTransientParent(ctx->window()); } m_newStuffDialog.data()->show(); } void KCMColors::installSchemeFromFile(const QUrl &url) { if (url.isLocalFile()) { installSchemeFile(url.toLocalFile()); return; } if (m_tempCopyJob) { return; } m_tempInstallFile.reset(new QTemporaryFile()); if (!m_tempInstallFile->open()) { emit showErrorMessage(i18n("Unable to create a temporary file.")); m_tempInstallFile.reset(); return; } // Ideally we copied the file into the proper location right away but // (for some reason) we determine the file name from the "Name" inside the file m_tempCopyJob = KIO::file_copy(url, QUrl::fromLocalFile(m_tempInstallFile->fileName()), -1, KIO::Overwrite); m_tempCopyJob->uiDelegate()->setAutoErrorHandlingEnabled(true); emit downloadingFileChanged(); connect(m_tempCopyJob, &KIO::FileCopyJob::result, this, [this, url](KJob *job) { if (job->error() != KJob::NoError) { emit showErrorMessage(i18n("Unable to download the color scheme: %1", job->errorText())); return; } installSchemeFile(m_tempInstallFile->fileName()); m_tempInstallFile.reset(); }); connect(m_tempCopyJob, &QObject::destroyed, this, &KCMColors::downloadingFileChanged); } void KCMColors::installSchemeFile(const QString &path) { KSharedConfigPtr config = KSharedConfig::openConfig(path, KConfig::SimpleConfig); KConfigGroup group(config, "General"); const QString name = group.readEntry("Name"); if (name.isEmpty()) { emit showErrorMessage(i18n("This file is not a color scheme file.")); return; } // Do not overwrite another scheme int increment = 0; QString newName = name; QString testpath; do { if (increment) { newName = name + QString::number(increment); } testpath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes/%1.colors").arg(newName)); increment++; } while (!testpath.isEmpty()); QString newPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/color-schemes/"); if (!QDir().mkpath(newPath)) { emit showErrorMessage(i18n("Failed to create 'color-scheme' data folder.")); return; } newPath += newName + QLatin1String(".colors"); if (!QFile::copy(path, newPath)) { emit showErrorMessage(i18n("Failed to copy color scheme into 'color-scheme' data folder.")); return; } // Update name KSharedConfigPtr config2 = KSharedConfig::openConfig(newPath, KConfig::SimpleConfig); KConfigGroup group2(config2, "General"); group2.writeEntry("Name", newName); config2->sync(); m_model->load(); const auto results = m_model->match(m_model->index(0, 0), SchemeNameRole, newName); if (!results.isEmpty()) { m_model->setSelectedScheme(newName); } emit showSuccessMessage(i18n("Color scheme installed successfully.")); } void KCMColors::editScheme(const QString &schemeName, QQuickItem *ctx) { if (m_editDialogProcess) { return; } QModelIndex idx = m_model->index(m_model->indexOfScheme(schemeName), 0); m_editDialogProcess = new QProcess(this); connect(m_editDialogProcess, QOverload::of(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitCode); Q_UNUSED(exitStatus); const auto savedThemes = QString::fromUtf8(m_editDialogProcess->readAllStandardOutput()).split(QLatin1Char('\n'), QString::SkipEmptyParts); if (!savedThemes.isEmpty()) { m_model->load(); // would be cool to just reload/add the changed/new ones m_model->setSelectedScheme(savedThemes.last()); } m_editDialogProcess->deleteLater(); m_editDialogProcess = nullptr; }); QStringList args; args << idx.data(KCMColors::SchemeNameRole).toString(); if (idx.data(KCMColors::RemovableRole).toBool()) { args << QStringLiteral("--overwrite"); } if (ctx && ctx->window()) { // QQuickWidget, used for embedding QML KCMs, renders everything into an offscreen window // Qt is able to resolve this on its own when setting transient parents in-process. // However, since we pass the ID to an external process which has no idea of this // we need to resolve the actual window we end up showing in. if (QWindow *actualWindow = QQuickRenderControl::renderWindowFor(ctx->window())) { if (KWindowSystem::isPlatformX11()) { // TODO wayland: once we have foreign surface support args << QStringLiteral("--attach") << (QStringLiteral("x11:") + QString::number(actualWindow->winId())); } } } m_editDialogProcess->start(QStringLiteral("kcolorschemeeditor"), args); } +bool KCMColors::isSaveNeeded() const +{ + return !m_model->match(m_model->index(0, 0), ColorsModel::PendingDeletionRole, true).isEmpty(); +} + + void KCMColors::load() { ManagedConfigModule::load(); m_model->load(); m_config->markAsClean(); m_config->reparseConfiguration(); const QString schemeName = m_settings->colorScheme(); // If the scheme named in kdeglobals doesn't exist, show a warning and use default scheme if (m_model->indexOfScheme(schemeName) == -1) { m_model->setSelectedScheme(m_settings->defaultColorSchemeValue()); // These are normally synced but initially the model doesn't emit a change to avoid the // Apply button from being enabled without any user interaction. Sync manually here. m_filteredModel->setSelectedScheme(m_settings->defaultColorSchemeValue()); emit showSchemeNotInstalledWarning(schemeName); } else { m_model->setSelectedScheme(schemeName); m_filteredModel->setSelectedScheme(schemeName); } { KConfig cfg(QStringLiteral("kcmdisplayrc"), KConfig::NoGlobals); KConfigGroup group(m_config, "General"); group = KConfigGroup(&cfg, "X11"); m_applyToAlien = group.readEntry("exportKDEColors", true); } } void KCMColors::save() { ManagedConfigModule::save(); if (m_selectedSchemeDirty) { saveColors(); } processPendingDeletions(); } void KCMColors::saveColors() { const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes/%1.colors").arg(m_model->selectedScheme())); KSharedConfigPtr config = KSharedConfig::openConfig(path); const QStringList colorSetGroupList{ QStringLiteral("Colors:View"), QStringLiteral("Colors:Window"), QStringLiteral("Colors:Button"), QStringLiteral("Colors:Selection"), QStringLiteral("Colors:Tooltip"), QStringLiteral("Colors:Complementary") }; const QList colorSchemes{ KColorScheme(QPalette::Active, KColorScheme::View, config), KColorScheme(QPalette::Active, KColorScheme::Window, config), KColorScheme(QPalette::Active, KColorScheme::Button, config), KColorScheme(QPalette::Active, KColorScheme::Selection, config), KColorScheme(QPalette::Active, KColorScheme::Tooltip, config), KColorScheme(QPalette::Active, KColorScheme::Complementary, config) }; for (int i = 0; i < colorSchemes.length(); ++i) { KConfigGroup group(m_config, colorSetGroupList.value(i)); group.writeEntry("BackgroundNormal", colorSchemes[i].background(KColorScheme::NormalBackground).color()); group.writeEntry("BackgroundAlternate", colorSchemes[i].background(KColorScheme::AlternateBackground).color()); group.writeEntry("ForegroundNormal", colorSchemes[i].foreground(KColorScheme::NormalText).color()); group.writeEntry("ForegroundInactive", colorSchemes[i].foreground(KColorScheme::InactiveText).color()); group.writeEntry("ForegroundActive", colorSchemes[i].foreground(KColorScheme::ActiveText).color()); group.writeEntry("ForegroundLink", colorSchemes[i].foreground(KColorScheme::LinkText).color()); group.writeEntry("ForegroundVisited", colorSchemes[i].foreground(KColorScheme::VisitedText).color()); group.writeEntry("ForegroundNegative", colorSchemes[i].foreground(KColorScheme::NegativeText).color()); group.writeEntry("ForegroundNeutral", colorSchemes[i].foreground(KColorScheme::NeutralText).color()); group.writeEntry("ForegroundPositive", colorSchemes[i].foreground(KColorScheme::PositiveText).color()); group.writeEntry("DecorationFocus", colorSchemes[i].decoration(KColorScheme::FocusColor).color()); group.writeEntry("DecorationHover", colorSchemes[i].decoration(KColorScheme::HoverColor).color()); } KConfigGroup groupWMTheme(config, "WM"); KConfigGroup groupWMOut(m_config, "WM"); const QStringList colorItemListWM{ QStringLiteral("activeBackground"), QStringLiteral("activeForeground"), QStringLiteral("inactiveBackground"), QStringLiteral("inactiveForeground"), QStringLiteral("activeBlend"), QStringLiteral("inactiveBlend") }; const QVector defaultWMColors{ QColor(71,80,87), QColor(239,240,241), QColor(239,240,241), QColor(189,195,199), QColor(255,255,255), QColor(75,71,67) }; int i = 0; for (const QString &coloritem : colorItemListWM) { groupWMOut.writeEntry(coloritem, groupWMTheme.readEntry(coloritem, defaultWMColors.value(i))); ++i; } const QStringList groupNameList{ QStringLiteral("ColorEffects:Inactive"), QStringLiteral("ColorEffects:Disabled") }; const QStringList effectList{ QStringLiteral("Enable"), QStringLiteral("ChangeSelectionColor"), QStringLiteral("IntensityEffect"), QStringLiteral("IntensityAmount"), QStringLiteral("ColorEffect"), QStringLiteral("ColorAmount"), QStringLiteral("Color"), QStringLiteral("ContrastEffect"), QStringLiteral("ContrastAmount") }; for (const QString &groupName : groupNameList) { KConfigGroup groupEffectOut(m_config, groupName); KConfigGroup groupEffectTheme(config, groupName); for (const QString &effect : effectList) { groupEffectOut.writeEntry(effect, groupEffectTheme.readEntry(effect)); } } m_config->sync(); runRdb(KRdbExportQtColors | KRdbExportGtkTheme | KRdbExportGtkColors | (m_applyToAlien ? KRdbExportColors : 0)); saveGtkColors(config); QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KGlobalSettings"), QStringLiteral("org.kde.KGlobalSettings"), QStringLiteral("notifyChange")); message.setArguments({ 0, //previous KGlobalSettings::PaletteChanged. This is now private API in khintsettings 0 //unused in palette changed but needed for the DBus signature }); QDBusConnection::sessionBus().send(message); m_selectedSchemeDirty = false; } void KCMColors::processPendingDeletions() { const QStringList pendingDeletions = m_model->pendingDeletions(); for (const QString &schemeName : pendingDeletions) { Q_ASSERT(schemeName != m_model->selectedScheme()); const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes/%1.colors").arg(schemeName)); auto *job = KIO::del(QUrl::fromLocalFile(path), KIO::HideProgressInfo); // needs to block for it to work on "OK" where the dialog (kcmshell) closes job->exec(); } m_model->removeItemsPendingDeletion(); } #include "colors.moc" diff --git a/kcms/colors/colors.h b/kcms/colors/colors.h index ecbb782ff..8916f10b5 100644 --- a/kcms/colors/colors.h +++ b/kcms/colors/colors.h @@ -1,117 +1,119 @@ /* * Copyright (c) 2019 Kai Uwe Broulik * Copyright (c) 2019 Cyril Rossi * * 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 . */ #pragma once #include #include #include #include #include class QProcess; class QTemporaryFile; namespace KIO { class FileCopyJob; } class ColorsModel; class FilterProxyModel; class ColorsSettings; class KCMColors : public KQuickAddons::ManagedConfigModule { Q_OBJECT Q_PROPERTY(ColorsModel *model READ model CONSTANT) Q_PROPERTY(FilterProxyModel *filteredModel READ filteredModel CONSTANT) Q_PROPERTY(ColorsSettings *colorsSettings READ colorsSettings CONSTANT) Q_PROPERTY(bool downloadingFile READ downloadingFile NOTIFY downloadingFileChanged) public: KCMColors(QObject *parent, const QVariantList &args); ~KCMColors() override; enum Roles { SchemeNameRole = Qt::UserRole + 1, PaletteRole, RemovableRole, PendingDeletionRole }; enum SchemeFilter { AllSchemes, LightSchemes, DarkSchemes }; Q_ENUM(SchemeFilter) ColorsModel *model() const; FilterProxyModel *filteredModel() const; ColorsSettings *colorsSettings() const; bool downloadingFile() const; Q_INVOKABLE void getNewStuff(QQuickItem *ctx); Q_INVOKABLE void installSchemeFromFile(const QUrl &url); Q_INVOKABLE void editScheme(const QString &schemeName, QQuickItem *ctx); public Q_SLOTS: void load() override; void save() override; Q_SIGNALS: void downloadingFileChanged(); void showSuccessMessage(const QString &message); void showErrorMessage(const QString &message); void showSchemeNotInstalledWarning(const QString &schemeName); private: + bool isSaveNeeded() const override; + void saveColors(); void processPendingDeletions(); void installSchemeFile(const QString &path); ColorsModel *m_model; FilterProxyModel *m_filteredModel; ColorsSettings *m_settings; bool m_selectedSchemeDirty = false; bool m_applyToAlien = true; QPointer m_newStuffDialog; QProcess *m_editDialogProcess = nullptr; KSharedConfigPtr m_config; QScopedPointer m_tempInstallFile; QPointer m_tempCopyJob; }; diff --git a/kcms/colors/colorsmodel.cpp b/kcms/colors/colorsmodel.cpp index 836ca8c16..27e4396f0 100644 --- a/kcms/colors/colorsmodel.cpp +++ b/kcms/colors/colorsmodel.cpp @@ -1,242 +1,239 @@ /* * Copyright (C) 2007 Matthew Woehlke * Copyright (C) 2007 Jeremy Whiting * Copyright (C) 2016 Olivier Churlaud * Copyright (C) 2019 Kai Uwe Broulik * * 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 . */ #include "colorsmodel.h" #include #include #include #include #include #include #include ColorsModel::ColorsModel(QObject *parent) : QAbstractListModel(parent) { } ColorsModel::~ColorsModel() = default; int ColorsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return m_data.count(); } QVariant ColorsModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_data.count()) { return QVariant(); } const auto &item = m_data.at(index.row()); switch (role) { case Qt::DisplayRole: return item.display; case SchemeNameRole: return item.schemeName; case PaletteRole: return item.palette; case ActiveTitleBarBackgroundRole: return item.activeTitleBarBackground; case ActiveTitleBarForegroundRole: return item.activeTitleBarForeground; case PendingDeletionRole: return item.pendingDeletion; case RemovableRole: return item.removable; } return QVariant(); } bool ColorsModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || index.row() >= m_data.count()) { return false; } if (role == PendingDeletionRole) { auto &item = m_data[index.row()]; const bool pendingDeletion = value.toBool(); if (item.pendingDeletion != pendingDeletion) { item.pendingDeletion = pendingDeletion; emit dataChanged(index, index, {PendingDeletionRole}); // move to the next non-pending theme const auto nonPending = match(index, PendingDeletionRole, false); if (!nonPending.isEmpty()) { setSelectedScheme(nonPending.first().data(SchemeNameRole).toString()); } emit pendingDeletionsChanged(); return true; } } return false; } QHash ColorsModel::roleNames() const { return { {Qt::DisplayRole, QByteArrayLiteral("display")}, {SchemeNameRole, QByteArrayLiteral("schemeName")}, {PaletteRole, QByteArrayLiteral("palette")}, {ActiveTitleBarBackgroundRole, QByteArrayLiteral("activeTitleBarBackground")}, {ActiveTitleBarForegroundRole, QByteArrayLiteral("activeTitleBarForeground")}, {RemovableRole, QByteArrayLiteral("removable")}, {PendingDeletionRole, QByteArrayLiteral("pendingDeletion")} }; } QString ColorsModel::selectedScheme() const { return m_selectedScheme; } void ColorsModel::setSelectedScheme(const QString &scheme) { if (m_selectedScheme == scheme) { return; } - const bool firstTime = m_selectedScheme.isNull(); m_selectedScheme = scheme; - if (!firstTime) { - emit selectedSchemeChanged(scheme); - } + emit selectedSchemeChanged(scheme); emit selectedSchemeIndexChanged(); } int ColorsModel::indexOfScheme(const QString &scheme) const { auto it = std::find_if(m_data.begin(), m_data.end(), [ &scheme](const ColorsModelData &item) { return item.schemeName == scheme; }); if (it != m_data.end()) { return std::distance(m_data.begin(), it); } return -1; } int ColorsModel::selectedSchemeIndex() const { return indexOfScheme(m_selectedScheme); } void ColorsModel::load() { beginResetModel(); const int oldCount = m_data.count(); m_data.clear(); QStringList schemeFiles; const QStringList schemeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory); for (const QString &dir : schemeDirs) { const QStringList fileNames = QDir(dir).entryList(QStringList{QStringLiteral("*.colors")}); for (const QString &file : fileNames) { const QString suffixedFileName = QLatin1String("color-schemes/") + file; // can't use QSet because of the transform below (passing const QString as this argument discards qualifiers) if (!schemeFiles.contains(suffixedFileName)) { schemeFiles.append(suffixedFileName); } } } std::transform(schemeFiles.begin(), schemeFiles.end(), schemeFiles.begin(), [](const QString &item) { return QStandardPaths::locate(QStandardPaths::GenericDataLocation, item); }); for (const QString &schemeFile : schemeFiles) { const QFileInfo fi(schemeFile); const QString baseName = fi.baseName(); KSharedConfigPtr config = KSharedConfig::openConfig(schemeFile, KConfig::SimpleConfig); KConfigGroup group(config, "General"); const QString name = group.readEntry("Name", baseName); const QPalette palette = KColorScheme::createApplicationPalette(config); // from kwin/decorations/decorationpalette.cpp KConfigGroup wmConfig(config, QStringLiteral("WM")); const QColor activeTitleBarBackground = wmConfig.readEntry("activeBackground", palette.color(QPalette::Active, QPalette::Highlight)); const QColor activeTitleBarForeground = wmConfig.readEntry("activeForeground", palette.color(QPalette::Active, QPalette::HighlightedText)); ColorsModelData item{ name, baseName, palette, activeTitleBarBackground, activeTitleBarForeground, fi.isWritable(), false, // pending deletion }; m_data.append(item); } QCollator collator; std::sort(m_data.begin(), m_data.end(), [&collator](const ColorsModelData &a, const ColorsModelData &b) { return collator.compare(a.display, b.display) < 0; }); endResetModel(); // an item might have been added before the currently selected one if (oldCount != m_data.count()) { emit selectedSchemeIndexChanged(); } } QStringList ColorsModel::pendingDeletions() const { QStringList pendingDeletions; for (const auto &item : m_data) { if (item.pendingDeletion) { pendingDeletions.append(item.schemeName); } } return pendingDeletions; } void ColorsModel::removeItemsPendingDeletion() { for (int i = m_data.count() - 1; i >= 0; --i) { if (m_data.at(i).pendingDeletion) { beginRemoveRows(QModelIndex(), i, i); m_data.remove(i); endRemoveRows(); } } } diff --git a/kcms/colors/filterproxymodel.cpp b/kcms/colors/filterproxymodel.cpp index d08d2a1e5..fcad109b3 100644 --- a/kcms/colors/filterproxymodel.cpp +++ b/kcms/colors/filterproxymodel.cpp @@ -1,133 +1,130 @@ /* * Copyright (C) 2019 Kai Uwe Broulik * * 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 . */ #include "filterproxymodel.h" #include "colorsmodel.h" FilterProxyModel::FilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { } FilterProxyModel::~FilterProxyModel() = default; QString FilterProxyModel::selectedScheme() const { return m_selectedScheme; } void FilterProxyModel::setSelectedScheme(const QString &scheme) { if (m_selectedScheme == scheme) { return; } - const bool firstTime = m_selectedScheme.isNull(); m_selectedScheme = scheme; - if (!firstTime) { - emit selectedSchemeChanged(); - } + emit selectedSchemeChanged(); emit selectedSchemeIndexChanged(); } int FilterProxyModel::selectedSchemeIndex() const { // We must search in the source model and then map the index to our proxy model. const auto results = sourceModel()->match(sourceModel()->index(0, 0), ColorsModel::SchemeNameRole, m_selectedScheme); if (results.count() == 1) { const QModelIndex result = mapFromSource(results.first()); if (result.isValid()) { return result.row(); } } return -1; } QString FilterProxyModel::query() const { return m_query; } void FilterProxyModel::setQuery(const QString &query) { if (m_query != query) { const int oldIndex = selectedSchemeIndex(); m_query = query; invalidateFilter(); emit queryChanged(); if (selectedSchemeIndex() != oldIndex) { emit selectedSchemeIndexChanged(); } } } KCMColors::SchemeFilter FilterProxyModel::filter() const { return m_filter; } void FilterProxyModel::setFilter(KCMColors::SchemeFilter filter) { if (m_filter != filter) { const int oldIndex = selectedSchemeIndex(); m_filter = filter; invalidateFilter(); emit filterChanged(); if (selectedSchemeIndex() != oldIndex) { emit selectedSchemeIndexChanged(); } } } bool FilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); if (!m_query.isEmpty()) { if (!idx.data(Qt::DisplayRole).toString().contains(m_query, Qt::CaseInsensitive) && !idx.data(KCMColors::SchemeNameRole).toString().contains(m_query, Qt::CaseInsensitive)) { return false; } } if (m_filter != KCMColors::AllSchemes) { const QPalette palette = idx.data(KCMColors::PaletteRole).value(); const int windowBackgroundGray = qGray(palette.window().color().rgb()); if (m_filter == KCMColors::DarkSchemes) { return windowBackgroundGray < 192; } else if (m_filter == KCMColors::LightSchemes) { return windowBackgroundGray >= 192; } } return true; }