Changeset View
Changeset View
Standalone View
Standalone View
kcms/colors/colors.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net> | ||||
3 | * Copyright (C) 2007 Jeremy Whiting <jpwhiting@kde.org> | ||||
4 | * Copyright (C) 2016 Olivier Churlaud <olivier@churlaud.com> | ||||
5 | * Copyright (C) 2019 Kai Uwe Broulik <kde@privat.broulik.de> | ||||
6 | * | ||||
7 | * This program is free software; you can redistribute it and/or | ||||
8 | * modify it under the terms of the GNU General Public License as | ||||
9 | * published by the Free Software Foundation; either version 2 of | ||||
10 | * the License or (at your option) version 3 or any later version | ||||
11 | * accepted by the membership of KDE e.V. (or its successor approved | ||||
12 | * by the membership of KDE e.V.), which shall act as a proxy | ||||
13 | * defined in Section 14 of version 3 of the license. | ||||
14 | * | ||||
15 | * This program is distributed in the hope that it will be useful, | ||||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
18 | * GNU General Public License for more details. | ||||
19 | * | ||||
20 | * You should have received a copy of the GNU General Public License | ||||
21 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
22 | */ | ||||
23 | | ||||
24 | #include "colors.h" | ||||
25 | | ||||
26 | #include <QDBusConnection> | ||||
27 | #include <QDBusMessage> | ||||
28 | #include <QFileInfo> | ||||
29 | #include <QGuiApplication> | ||||
30 | #include <QProcess> | ||||
31 | #include <QStandardItemModel> | ||||
32 | #include <QStandardPaths> | ||||
33 | #include <QQuickItem> | ||||
34 | #include <QQuickRenderControl> | ||||
35 | #include <QQuickWindow> | ||||
36 | | ||||
37 | #include <KAboutData> | ||||
38 | #include <KColorScheme> | ||||
39 | #include <KConfigGroup> | ||||
40 | #include <KLocalizedString> | ||||
41 | #include <KPluginFactory> | ||||
42 | #include <KWindowSystem> | ||||
43 | | ||||
44 | #include <KIO/FileCopyJob> | ||||
45 | #include <KIO/DeleteJob> | ||||
46 | #include <KIO/JobUiDelegate> | ||||
47 | | ||||
48 | #include <algorithm> | ||||
49 | | ||||
50 | #include "../krdb/krdb.h" | ||||
51 | | ||||
52 | static const QString s_defaultColorSchemeName = QStringLiteral("Breeze"); | ||||
53 | | ||||
54 | K_PLUGIN_FACTORY_WITH_JSON(KCMColorsFactory, "kcm_colors.json", registerPlugin<KCMColors>();) | ||||
55 | | ||||
56 | KCMColors::KCMColors(QObject *parent, const QVariantList &args) | ||||
57 | : KQuickAddons::ConfigModule(parent, args) | ||||
58 | , m_config(KSharedConfig::openConfig(QStringLiteral("kdeglobals"))) | ||||
59 | { | ||||
GB_2: Better: "Choose the color scheme" | |||||
60 | qmlRegisterType<QStandardItemModel>(); | ||||
61 | | ||||
62 | KAboutData *about = new KAboutData(QStringLiteral("kcm_colors"), i18n("Choose the color scheme"), | ||||
63 | QStringLiteral("2.0"), QString(), KAboutLicense::GPL); | ||||
64 | about->addAuthor(i18n("Kai Uwe Broulik"), QString(), QStringLiteral("kde@privat.broulik.de")); | ||||
65 | setAboutData(about); | ||||
66 | | ||||
67 | m_model = new QStandardItemModel(this); | ||||
68 | m_model->setItemRoleNames({ | ||||
69 | {Qt::DisplayRole, QByteArrayLiteral("display")}, | ||||
70 | {SchemeNameRole, QByteArrayLiteral("schemeName")}, | ||||
71 | {PaletteRole, QByteArrayLiteral("palette")}, | ||||
72 | {RemovableRole, QByteArrayLiteral("removable")}, | ||||
73 | {PendingDeletionRole, QByteArrayLiteral("pendingDeletion")} | ||||
74 | }); | ||||
75 | } | ||||
76 | | ||||
77 | KCMColors::~KCMColors() | ||||
78 | { | ||||
79 | m_config->markAsClean(); | ||||
80 | } | ||||
81 | | ||||
82 | QStandardItemModel *KCMColors::colorsModel() const | ||||
83 | { | ||||
84 | return m_model; | ||||
85 | } | ||||
86 | | ||||
87 | QString KCMColors::selectedScheme() const | ||||
88 | { | ||||
89 | return m_selectedScheme; | ||||
90 | } | ||||
91 | | ||||
92 | void KCMColors::setSelectedScheme(const QString &scheme) | ||||
93 | { | ||||
94 | if (m_selectedScheme == scheme) { | ||||
95 | return; | ||||
96 | } | ||||
97 | | ||||
98 | const bool firstTime = m_selectedScheme.isNull(); | ||||
99 | m_selectedScheme = scheme; | ||||
100 | emit selectedSchemeChanged(); | ||||
101 | emit selectedSchemeIndexChanged(); | ||||
102 | | ||||
103 | if (!firstTime) { | ||||
104 | setNeedsSave(true); | ||||
105 | m_selectedSchemeDirty = true; | ||||
106 | } | ||||
107 | } | ||||
108 | | ||||
109 | int KCMColors::selectedSchemeIndex() const | ||||
110 | { | ||||
111 | return indexOfScheme(m_selectedScheme); | ||||
112 | } | ||||
113 | | ||||
114 | int KCMColors::indexOfScheme(const QString &schemeName) const | ||||
115 | { | ||||
116 | const auto results = m_model->match(m_model->index(0, 0), SchemeNameRole, schemeName); | ||||
117 | if (results.count() == 1) { | ||||
118 | return results.first().row(); | ||||
119 | } | ||||
120 | | ||||
121 | return -1; | ||||
122 | } | ||||
123 | | ||||
124 | bool KCMColors::downloadingFile() const | ||||
125 | { | ||||
126 | return m_tempCopyJob; | ||||
127 | } | ||||
128 | | ||||
129 | void KCMColors::setPendingDeletion(int index, bool pending) | ||||
130 | { | ||||
131 | QModelIndex idx = m_model->index(index, 0); | ||||
132 | | ||||
133 | m_model->setData(idx, pending, PendingDeletionRole); | ||||
134 | | ||||
135 | if (pending && selectedSchemeIndex() == index) { | ||||
136 | // move to the next non-pending theme | ||||
137 | const auto nonPending = m_model->match(idx, PendingDeletionRole, false); | ||||
138 | setSelectedScheme(nonPending.first().data(SchemeNameRole).toString()); | ||||
139 | } | ||||
140 | | ||||
141 | setNeedsSave(true); | ||||
142 | } | ||||
143 | | ||||
144 | void KCMColors::loadModel() | ||||
145 | { | ||||
146 | m_model->clear(); | ||||
147 | | ||||
148 | QStringList schemeFiles; | ||||
149 | | ||||
150 | const QStringList schemeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory); | ||||
151 | for (const QString &dir : schemeDirs) { | ||||
152 | const QStringList fileNames = QDir(dir).entryList(QStringList{QStringLiteral("*.colors")}); | ||||
153 | for (const QString &file : fileNames) { | ||||
154 | const QString suffixedFileName = QStringLiteral("color-schemes/") + file; | ||||
155 | // can't use QSet because of the transform below (passing const QString as this argument discards qualifiers) | ||||
156 | if (!schemeFiles.contains(suffixedFileName)) { | ||||
157 | schemeFiles.append(suffixedFileName); | ||||
158 | } | ||||
159 | } | ||||
160 | } | ||||
161 | | ||||
162 | std::transform(schemeFiles.begin(), schemeFiles.end(), schemeFiles.begin(), [](const QString &item) { | ||||
163 | return QStandardPaths::locate(QStandardPaths::GenericDataLocation, item); | ||||
164 | }); | ||||
165 | | ||||
166 | for (const QString &schemeFile : schemeFiles) { | ||||
167 | const QFileInfo fi(schemeFile); | ||||
168 | const QString baseName = fi.baseName(); | ||||
169 | | ||||
170 | KSharedConfigPtr config = KSharedConfig::openConfig(schemeFile, KConfig::SimpleConfig); | ||||
171 | KConfigGroup group(config, "General"); | ||||
172 | const QString name = group.readEntry("Name", baseName); | ||||
173 | | ||||
174 | QStandardItem *item = new QStandardItem(name); | ||||
175 | item->setData(baseName, SchemeNameRole); | ||||
176 | item->setData(fi.isWritable(), RemovableRole); | ||||
177 | item->setData(false, PendingDeletionRole); | ||||
178 | | ||||
179 | item->setData(KColorScheme::createApplicationPalette(config), PaletteRole); | ||||
180 | | ||||
181 | m_model->appendRow(item); | ||||
182 | } | ||||
183 | | ||||
184 | m_model->sort(0 /*column*/); | ||||
185 | } | ||||
186 | | ||||
187 | void KCMColors::getNewStuff(QQuickItem *ctx) | ||||
188 | { | ||||
189 | if (!m_newStuffDialog) { | ||||
190 | m_newStuffDialog = new KNS3::DownloadDialog(QStringLiteral("colorschemes.knsrc")); | ||||
191 | m_newStuffDialog.data()->setWindowTitle(i18n("Download New Color Schemes")); | ||||
192 | m_newStuffDialog->setWindowModality(Qt::WindowModal); | ||||
193 | m_newStuffDialog->winId(); // so it creates the windowHandle(); | ||||
194 | | ||||
195 | connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, [this] { | ||||
196 | loadModel(); | ||||
197 | | ||||
198 | const auto newEntries = m_newStuffDialog->installedEntries(); | ||||
199 | // If one new theme was installed, select the first color file in it | ||||
200 | if (newEntries.count() == 1) { | ||||
201 | QStringList installedThemes; | ||||
202 | | ||||
203 | const QString suffix = QStringLiteral(".colors"); | ||||
204 | | ||||
205 | for (const QString &path : newEntries.first().installedFiles()) { | ||||
206 | const QString fileName = path.section(QLatin1Char('/'), -1, -1); | ||||
207 | | ||||
208 | const int suffixPos = fileName.indexOf(suffix); | ||||
209 | if (suffixPos != fileName.length() - suffix.length()) { | ||||
210 | continue; | ||||
211 | } | ||||
212 | | ||||
213 | installedThemes.append(fileName.left(suffixPos)); | ||||
214 | } | ||||
215 | | ||||
216 | if (!installedThemes.isEmpty()) { | ||||
217 | // The list is sorted by (potentially translated) name | ||||
218 | // but that would require us parse every file, so this should be close enough | ||||
219 | std::sort(installedThemes.begin(), installedThemes.end()); | ||||
220 | | ||||
221 | setSelectedScheme(installedThemes.constFirst()); | ||||
222 | } | ||||
223 | } | ||||
224 | }); | ||||
225 | } | ||||
226 | | ||||
227 | if (ctx && ctx->window()) { | ||||
228 | m_newStuffDialog->windowHandle()->setTransientParent(ctx->window()); | ||||
229 | } | ||||
what would happen if this is called twice before the file_copy completes? davidedmundson: what would happen if this is called twice before the file_copy completes? | |||||
230 | | ||||
231 | m_newStuffDialog.data()->show(); | ||||
232 | } | ||||
233 | | ||||
234 | void KCMColors::installSchemeFromFile(const QUrl &url) | ||||
235 | { | ||||
236 | if (url.isLocalFile()) { | ||||
237 | installSchemeFile(url.toLocalFile()); | ||||
238 | return; | ||||
239 | } | ||||
240 | | ||||
241 | if (m_tempCopyJob) { | ||||
242 | return; | ||||
243 | } | ||||
244 | | ||||
245 | m_tempInstallFile.reset(new QTemporaryFile()); | ||||
246 | if (!m_tempInstallFile->open()) { | ||||
247 | emit showErrorMessage(i18n("Unable to create a temporary file.")); | ||||
248 | m_tempInstallFile.reset(); | ||||
249 | return; | ||||
250 | } | ||||
251 | | ||||
252 | // Ideally we copied the file into the proper location right away but | ||||
253 | // (for some reason) we determine the file name from the "Name" inside the file | ||||
254 | m_tempCopyJob = KIO::file_copy(url, QUrl::fromLocalFile(m_tempInstallFile->fileName()), | ||||
255 | -1, KIO::Overwrite); | ||||
256 | m_tempCopyJob->uiDelegate()->setAutoErrorHandlingEnabled(true); | ||||
257 | emit downloadingFileChanged(); | ||||
258 | | ||||
259 | connect(m_tempCopyJob, &KIO::FileCopyJob::result, this, [this, url](KJob *job) { | ||||
260 | if (job->error() != KJob::NoError) { | ||||
261 | emit showErrorMessage(i18n("Unable to download the color scheme: %1", job->errorText())); | ||||
262 | return; | ||||
263 | } | ||||
264 | | ||||
265 | installSchemeFile(m_tempInstallFile->fileName()); | ||||
266 | m_tempInstallFile.reset(); | ||||
267 | }); | ||||
268 | connect(m_tempCopyJob, &QObject::destroyed, this, &KCMColors::downloadingFileChanged); | ||||
269 | } | ||||
270 | | ||||
271 | void KCMColors::installSchemeFile(const QString &path) | ||||
272 | { | ||||
273 | KSharedConfigPtr config = KSharedConfig::openConfig(path, KConfig::SimpleConfig); | ||||
274 | | ||||
275 | KConfigGroup group(config, "General"); | ||||
276 | const QString name = group.readEntry("Name"); | ||||
277 | | ||||
278 | if (name.isEmpty()) { | ||||
279 | emit showErrorMessage(i18n("This file is not a color scheme file.")); | ||||
280 | return; | ||||
281 | } | ||||
282 | | ||||
283 | // Do not overwrite another scheme | ||||
284 | int increment = 0; | ||||
285 | QString newName = name; | ||||
286 | QString testpath; | ||||
287 | do { | ||||
288 | if (increment) { | ||||
289 | newName = name + QString::number(increment); | ||||
290 | } | ||||
291 | testpath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, | ||||
292 | QStringLiteral("color-schemes/%1.colors").arg(newName)); | ||||
293 | increment++; | ||||
294 | } while (!testpath.isEmpty()); | ||||
295 | | ||||
296 | QString newPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/color-schemes/"); | ||||
297 | | ||||
298 | if (!QDir().mkpath(newPath)) { | ||||
299 | emit showErrorMessage(i18n("Failed to create 'color-scheme' data folder.")); | ||||
300 | return; | ||||
301 | } | ||||
302 | | ||||
303 | newPath += newName + QLatin1String(".colors"); | ||||
304 | | ||||
305 | if (!QFile::copy(path, newPath)) { | ||||
306 | emit showErrorMessage(i18n("Failed to copy color scheme into 'color-scheme' data folder.")); | ||||
307 | return; | ||||
308 | } | ||||
309 | | ||||
310 | // Update name | ||||
311 | KSharedConfigPtr config2 = KSharedConfig::openConfig(newPath, KConfig::SimpleConfig); | ||||
312 | KConfigGroup group2(config2, "General"); | ||||
313 | group2.writeEntry("Name", newName); | ||||
314 | config2->sync(); | ||||
Can you use an initializer_list here and elsewhere? See https://www.angrycane.com.br/en/2018/06/19/speeding-up-cornercases/ for a comparison nicolasfella: Can you use an initializer_list here and elsewhere?
Also QStringLiteral?
See https://www. | |||||
315 | | ||||
316 | loadModel(); | ||||
317 | | ||||
318 | const auto results = m_model->match(m_model->index(0, 0), SchemeNameRole, newName); | ||||
319 | if (!results.isEmpty()) { | ||||
320 | setSelectedScheme(newName); | ||||
321 | } | ||||
322 | | ||||
323 | emit showSuccessMessage(i18n("Color scheme installed successfully.")); | ||||
324 | } | ||||
325 | | ||||
326 | void KCMColors::editScheme(int index, QQuickItem *ctx) | ||||
327 | { | ||||
328 | if (m_editDialogProcess) { | ||||
329 | return; | ||||
330 | } | ||||
331 | | ||||
332 | QModelIndex idx = m_model->index(index, 0); | ||||
333 | | ||||
334 | m_editDialogProcess = new QProcess(this); | ||||
335 | connect(m_editDialogProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, | ||||
336 | [this](int exitCode, QProcess::ExitStatus exitStatus) { | ||||
337 | Q_UNUSED(exitCode); | ||||
338 | Q_UNUSED(exitStatus); | ||||
339 | | ||||
340 | const auto savedThemes = QString::fromUtf8(m_editDialogProcess->readAllStandardOutput()).split(QLatin1Char('\n'), QString::SkipEmptyParts); | ||||
341 | | ||||
342 | if (!savedThemes.isEmpty()) { | ||||
343 | loadModel(); // would be cool to just reload/add the changed/new ones | ||||
344 | | ||||
345 | setSelectedScheme(savedThemes.last()); | ||||
346 | } | ||||
347 | | ||||
348 | m_editDialogProcess->deleteLater(); | ||||
349 | m_editDialogProcess = nullptr; | ||||
350 | }); | ||||
351 | | ||||
352 | QStringList args; | ||||
353 | args << idx.data(KCMColors::SchemeNameRole).toString(); | ||||
354 | if (idx.data(KCMColors::RemovableRole).toBool()) { | ||||
355 | args << QStringLiteral("--overwrite"); | ||||
356 | } | ||||
357 | | ||||
358 | if (ctx && ctx->window()) { | ||||
359 | // QQuickWidget, used for embedding QML KCMs, renders everything into an offscreen window | ||||
360 | // Qt is able to resolve this on its own when setting transient parents in-process. | ||||
361 | // However, since we pass the ID to an external process which has no idea of this | ||||
362 | // we need to resolve the actual window we end up showing in. | ||||
363 | if (QWindow *actualWindow = QQuickRenderControl::renderWindowFor(ctx->window())) { | ||||
364 | if (KWindowSystem::isPlatformX11()) { | ||||
365 | // TODO wayland: once we have foreign surface support | ||||
366 | args << QStringLiteral("--attach") << (QStringLiteral("x11:") + QString::number(actualWindow->winId())); | ||||
367 | } | ||||
368 | } | ||||
369 | } | ||||
370 | | ||||
371 | m_editDialogProcess->start(QStringLiteral("kcolorschemeeditor"), args); | ||||
372 | } | ||||
373 | | ||||
374 | void KCMColors::load() | ||||
375 | { | ||||
376 | loadModel(); | ||||
377 | | ||||
378 | m_config->markAsClean(); | ||||
379 | m_config->reparseConfiguration(); | ||||
380 | | ||||
381 | KConfigGroup group(m_config, "General"); | ||||
382 | const QString schemeName = group.readEntry("ColorScheme", s_defaultColorSchemeName); | ||||
383 | | ||||
384 | // If the scheme named in kdeglobals doesn't exist, show a warning and use default scheme | ||||
385 | if (indexOfScheme(schemeName) == -1) { | ||||
386 | setSelectedScheme(s_defaultColorSchemeName); | ||||
387 | emit showSchemeNotInstalledWarning(schemeName); | ||||
388 | } else { | ||||
389 | setSelectedScheme(schemeName); | ||||
390 | } | ||||
391 | | ||||
392 | { | ||||
393 | KConfig cfg(QStringLiteral("kcmdisplayrc"), KConfig::NoGlobals); | ||||
394 | group = KConfigGroup(&cfg, "X11"); | ||||
395 | m_applyToAlien = group.readEntry("exportKDEColors", true); | ||||
396 | } | ||||
397 | } | ||||
398 | | ||||
399 | void KCMColors::save() | ||||
400 | { | ||||
401 | if (m_selectedSchemeDirty) { | ||||
402 | saveColors(); | ||||
403 | } | ||||
404 | | ||||
405 | processPendingDeletions(); | ||||
406 | | ||||
407 | setNeedsSave(false); | ||||
408 | } | ||||
409 | | ||||
410 | void KCMColors::saveColors() | ||||
411 | { | ||||
412 | KConfigGroup grp(m_config, "General"); | ||||
413 | grp.writeEntry("ColorScheme", m_selectedScheme); | ||||
414 | | ||||
415 | const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, | ||||
416 | QStringLiteral("color-schemes/%1.colors").arg(m_selectedScheme)); | ||||
417 | | ||||
418 | KSharedConfigPtr config = KSharedConfig::openConfig(path); | ||||
419 | | ||||
420 | const QStringList colorSetGroupList{ | ||||
421 | QStringLiteral("Colors:View"), | ||||
422 | QStringLiteral("Colors:Window"), | ||||
423 | QStringLiteral("Colors:Button"), | ||||
424 | QStringLiteral("Colors:Selection"), | ||||
425 | QStringLiteral("Colors:Tooltip"), | ||||
426 | QStringLiteral("Colors:Complementary") | ||||
427 | }; | ||||
428 | | ||||
429 | const QList<KColorScheme> colorSchemes{ | ||||
430 | KColorScheme(QPalette::Active, KColorScheme::View, config), | ||||
431 | KColorScheme(QPalette::Active, KColorScheme::Window, config), | ||||
432 | KColorScheme(QPalette::Active, KColorScheme::Button, config), | ||||
433 | KColorScheme(QPalette::Active, KColorScheme::Selection, config), | ||||
434 | KColorScheme(QPalette::Active, KColorScheme::Tooltip, config), | ||||
435 | KColorScheme(QPalette::Active, KColorScheme::Complementary, config) | ||||
436 | }; | ||||
437 | | ||||
438 | for (int i = 0; i < colorSchemes.length(); ++i) { | ||||
439 | KConfigGroup group(m_config, colorSetGroupList.value(i)); | ||||
440 | group.writeEntry("BackgroundNormal", colorSchemes[i].background(KColorScheme::NormalBackground).color()); | ||||
441 | group.writeEntry("BackgroundAlternate", colorSchemes[i].background(KColorScheme::AlternateBackground).color()); | ||||
442 | group.writeEntry("ForegroundNormal", colorSchemes[i].foreground(KColorScheme::NormalText).color()); | ||||
443 | group.writeEntry("ForegroundInactive", colorSchemes[i].foreground(KColorScheme::InactiveText).color()); | ||||
444 | group.writeEntry("ForegroundActive", colorSchemes[i].foreground(KColorScheme::ActiveText).color()); | ||||
445 | group.writeEntry("ForegroundLink", colorSchemes[i].foreground(KColorScheme::LinkText).color()); | ||||
446 | group.writeEntry("ForegroundVisited", colorSchemes[i].foreground(KColorScheme::VisitedText).color()); | ||||
447 | group.writeEntry("ForegroundNegative", colorSchemes[i].foreground(KColorScheme::NegativeText).color()); | ||||
448 | group.writeEntry("ForegroundNeutral", colorSchemes[i].foreground(KColorScheme::NeutralText).color()); | ||||
449 | group.writeEntry("ForegroundPositive", colorSchemes[i].foreground(KColorScheme::PositiveText).color()); | ||||
450 | group.writeEntry("DecorationFocus", colorSchemes[i].decoration(KColorScheme::FocusColor).color()); | ||||
451 | group.writeEntry("DecorationHover", colorSchemes[i].decoration(KColorScheme::HoverColor).color()); | ||||
452 | } | ||||
453 | | ||||
454 | KConfigGroup groupWMTheme(config, "WM"); | ||||
455 | KConfigGroup groupWMOut(m_config, "WM"); | ||||
456 | | ||||
457 | const QStringList colorItemListWM{ | ||||
458 | QStringLiteral("activeBackground"), | ||||
459 | QStringLiteral("activeForeground"), | ||||
460 | QStringLiteral("inactiveBackground"), | ||||
461 | QStringLiteral("inactiveForeground"), | ||||
462 | QStringLiteral("activeBlend"), | ||||
463 | QStringLiteral("inactiveBlend") | ||||
464 | }; | ||||
465 | | ||||
466 | const QVector<QColor> defaultWMColors{ | ||||
467 | QColor(71,80,87), | ||||
468 | QColor(239,240,241), | ||||
469 | QColor(239,240,241), | ||||
470 | QColor(189,195,199), | ||||
471 | QColor(255,255,255), | ||||
472 | QColor(75,71,67) | ||||
473 | }; | ||||
474 | | ||||
475 | int i = 0; | ||||
476 | for (const QString &coloritem : colorItemListWM) { | ||||
477 | groupWMOut.writeEntry(coloritem, groupWMTheme.readEntry(coloritem, defaultWMColors.value(i))); | ||||
478 | ++i; | ||||
479 | } | ||||
480 | | ||||
481 | const QStringList groupNameList{ | ||||
482 | QStringLiteral("ColorEffects:Inactive"), | ||||
483 | QStringLiteral("ColorEffects:Disabled") | ||||
484 | }; | ||||
485 | | ||||
486 | const QStringList effectList{ | ||||
487 | QStringLiteral("Enable"), | ||||
488 | QStringLiteral("ChangeSelectionColor"), | ||||
489 | QStringLiteral("IntensityEffect"), | ||||
490 | QStringLiteral("IntensityAmount"), | ||||
491 | QStringLiteral("ColorEffect"), | ||||
492 | QStringLiteral("ColorAmount"), | ||||
493 | QStringLiteral("Color"), | ||||
494 | QStringLiteral("ContrastEffect"), | ||||
495 | QStringLiteral("ContrastAmount") | ||||
496 | }; | ||||
497 | | ||||
498 | for (const QString &groupName : groupNameList) { | ||||
499 | KConfigGroup groupEffectOut(m_config, groupName); | ||||
500 | KConfigGroup groupEffectTheme(config, groupName); | ||||
501 | | ||||
502 | for (const QString &effect : effectList) { | ||||
503 | groupEffectOut.writeEntry(effect, groupEffectTheme.readEntry(effect)); | ||||
504 | } | ||||
505 | } | ||||
506 | | ||||
507 | m_config->sync(); | ||||
508 | | ||||
509 | runRdb(KRdbExportQtColors | KRdbExportGtkTheme | (m_applyToAlien ? KRdbExportColors : 0)); | ||||
510 | | ||||
511 | QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KGlobalSettings"), | ||||
512 | QStringLiteral("org.kde.KGlobalSettings"), | ||||
513 | QStringLiteral("notifyChange")); | ||||
514 | message.setArguments({ | ||||
515 | 0, //previous KGlobalSettings::PaletteChanged. This is now private API in khintsettings | ||||
516 | 0 //unused in palette changed but needed for the DBus signature | ||||
517 | }); | ||||
518 | QDBusConnection::sessionBus().send(message); | ||||
519 | | ||||
520 | if (KWindowSystem::isPlatformX11()) { | ||||
521 | // Send signal to all kwin instances | ||||
522 | QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig")); | ||||
523 | QDBusConnection::sessionBus().send(message); | ||||
524 | } | ||||
525 | | ||||
526 | m_selectedSchemeDirty = false; | ||||
527 | } | ||||
528 | | ||||
529 | void KCMColors::processPendingDeletions() | ||||
530 | { | ||||
531 | const auto pendingDeletions = m_model->match(m_model->index(0, 0), PendingDeletionRole, true, -1 /*all*/); | ||||
532 | QVector<QPersistentModelIndex> persistentPendingDeletions; | ||||
533 | // turn into persistent model index so we can delete as we go | ||||
534 | std::transform(pendingDeletions.begin(), pendingDeletions.end(), | ||||
535 | std::back_inserter(persistentPendingDeletions), [](const QModelIndex &idx) { | ||||
536 | return QPersistentModelIndex(idx); | ||||
537 | }); | ||||
538 | | ||||
539 | for (const QPersistentModelIndex &idx : persistentPendingDeletions) { | ||||
540 | const QString schemeName = idx.data(SchemeNameRole).toString(); | ||||
541 | | ||||
542 | Q_ASSERT(schemeName != m_selectedScheme); | ||||
543 | | ||||
544 | const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, | ||||
545 | QStringLiteral("color-schemes/%1.colors").arg(schemeName)); | ||||
546 | | ||||
547 | auto *job = KIO::del(QUrl::fromLocalFile(path), KIO::HideProgressInfo); | ||||
548 | // needs to block for it to work on "OK" where the dialog (kcmshell) closes | ||||
549 | job->exec(); | ||||
550 | } | ||||
551 | | ||||
552 | // remove them in a separate loop after all the delete jobs for a smoother animation | ||||
553 | for (const QPersistentModelIndex &idx : persistentPendingDeletions) { | ||||
554 | m_model->removeRow(idx.row()); | ||||
555 | } | ||||
556 | } | ||||
557 | | ||||
558 | void KCMColors::defaults() | ||||
559 | { | ||||
560 | setSelectedScheme(s_defaultColorSchemeName); | ||||
561 | | ||||
562 | setNeedsSave(true); | ||||
563 | } | ||||
564 | | ||||
565 | #include "colors.moc" |
Better: "Choose the color scheme"