Changeset View
Standalone View
kcms/icons/main.cpp
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | * main.cpp | 2 | * main.cpp | ||
3 | * | 3 | * | ||
4 | * Copyright (c) 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> | 4 | * Copyright (c) 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> | ||
5 | * Copyright (c) 2000 Antonio Larrosa <larrosa@kde.org> | 5 | * Copyright (c) 2000 Antonio Larrosa <larrosa@kde.org> | ||
6 | * Copyright (C) 2000 Geert Jansen <jansen@kde.org> | 6 | * Copyright (C) 2000 Geert Jansen <jansen@kde.org> | ||
7 | * KDE Frameworks 5 port Copyright (C) 2013 Jonathan Riddell <jr@jriddell.org> | 7 | * KDE Frameworks 5 port Copyright (C) 2013 Jonathan Riddell <jr@jriddell.org> | ||
8 | * Copyright (C) 2018 Kai Uwe Broulik <kde@privat.broulik.de> | 8 | * Copyright (C) 2018 Kai Uwe Broulik <kde@privat.broulik.de> | ||
9 | * Copyright (C) 2019 Benjamin Port <benjamin.port@enioka.com> | ||||
9 | * | 10 | * | ||
10 | * Requires the Qt widget libraries, available at no cost at | 11 | * Requires the Qt widget libraries, available at no cost at | ||
11 | * https://www.qt.io/ | 12 | * https://www.qt.io/ | ||
12 | * | 13 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | 14 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | 15 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | 16 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | 17 | * (at your option) any later version. | ||
Show All 33 Lines | |||||
50 | #include <KTar> | 51 | #include <KTar> | ||
51 | 52 | | |||
52 | #include <KIO/DeleteJob> | 53 | #include <KIO/DeleteJob> | ||
53 | #include <KIO/FileCopyJob> | 54 | #include <KIO/FileCopyJob> | ||
54 | 55 | | |||
55 | #include <algorithm> | 56 | #include <algorithm> | ||
56 | #include <unistd.h> // for unlink | 57 | #include <unistd.h> // for unlink | ||
57 | 58 | | |||
59 | #include "iconssettings.h" | ||||
58 | #include "iconsmodel.h" | 60 | #include "iconsmodel.h" | ||
61 | #include "iconsizecategorymodel.h" | ||||
59 | 62 | | |||
60 | #include "config.h" // for CMAKE_INSTALL_FULL_LIBEXECDIR | 63 | #include "config.h" // for CMAKE_INSTALL_FULL_LIBEXECDIR | ||
61 | 64 | | |||
62 | static const QVector<int> s_defaultIconSizes = { 32, 22, 22, 16, 48, 32 }; | | |||
63 | // we try to use KIconTheme::defaultThemeName() but that could be "hicolor" which isn't a "real" theme | | |||
64 | static const QString s_defaultThemeName = QStringLiteral("breeze"); | | |||
65 | | ||||
66 | K_PLUGIN_FACTORY_WITH_JSON(IconsFactory, "kcm_icons.json", registerPlugin<IconModule>();) | 65 | K_PLUGIN_FACTORY_WITH_JSON(IconsFactory, "kcm_icons.json", registerPlugin<IconModule>();) | ||
67 | 66 | | |||
68 | IconModule::IconModule(QObject *parent, const QVariantList &args) | 67 | IconModule::IconModule(QObject *parent, const QVariantList &args) | ||
69 | : KQuickAddons::ConfigModule(parent, args) | 68 | : KQuickAddons::ManagedConfigModule(parent, args) | ||
70 | , m_model(new IconsModel(this)) | 69 | , m_settings(new IconsSettings(this)) | ||
71 | , m_iconGroups{ | 70 | , m_model(new IconsModel(m_settings, this)) | ||
72 | QStringLiteral("Desktop"), | 71 | , m_iconSizeCategoryModel(new IconSizeCategoryModel(this)) | ||
73 | QStringLiteral("Toolbar"), | | |||
74 | QStringLiteral("MainToolbar"), | | |||
75 | QStringLiteral("Small"), | | |||
76 | QStringLiteral("Panel"), | | |||
77 | QStringLiteral("Dialog") | | |||
78 | } | | |||
79 | { | 72 | { | ||
73 | qmlRegisterType<IconsSettings>(); | ||||
80 | qmlRegisterType<IconsModel>(); | 74 | qmlRegisterType<IconsModel>(); | ||
75 | qmlRegisterType<IconSizeCategoryModel>(); | ||||
81 | 76 | | |||
82 | // to be able to access its enums | 77 | // to be able to access its enums | ||
83 | qmlRegisterUncreatableType<KIconLoader>("org.kde.private.kcms.icons", 1, 0, "KIconLoader", QString()); | 78 | qmlRegisterUncreatableType<KIconLoader>("org.kde.private.kcms.icons", 1, 0, "KIconLoader", QString()); | ||
84 | 79 | | |||
85 | KAboutData* about = new KAboutData(QStringLiteral("kcm5_icons"), i18n("Icons"), QStringLiteral("1.0"), | 80 | KAboutData* about = new KAboutData(QStringLiteral("kcm5_icons"), i18n("Icons"), QStringLiteral("1.0"), | ||
86 | i18n("Icons Control Panel Module"), KAboutLicense::GPL, | 81 | i18n("Icons Control Panel Module"), KAboutLicense::GPL, | ||
87 | i18n("(c) 2000-2003 Geert Jansen")); | 82 | i18n("(c) 2000-2003 Geert Jansen")); | ||
88 | about->addAuthor(i18n("Geert Jansen"), QString(), QStringLiteral("jansen@kde.org")); | 83 | about->addAuthor(i18n("Geert Jansen"), QString(), QStringLiteral("jansen@kde.org")); | ||
89 | about->addAuthor(i18n("Antonio Larrosa Jimenez"), QString(), QStringLiteral("larrosa@kde.org")); | 84 | about->addAuthor(i18n("Antonio Larrosa Jimenez"), QString(), QStringLiteral("larrosa@kde.org")); | ||
90 | about->addCredit(i18n("Torsten Rahn"), QString(), QStringLiteral("torsten@kde.org")); | 85 | about->addCredit(i18n("Torsten Rahn"), QString(), QStringLiteral("torsten@kde.org")); | ||
91 | about->addAuthor(i18n("Jonathan Riddell"), QString(), QStringLiteral("jr@jriddell.org")); | 86 | about->addAuthor(i18n("Jonathan Riddell"), QString(), QStringLiteral("jr@jriddell.org")); | ||
92 | about->addAuthor(i18n("Kai Uwe Broulik"), QString(), QStringLiteral("kde@privat.broulik.de>")); | 87 | about->addAuthor(i18n("Kai Uwe Broulik"), QString(), QStringLiteral("kde@privat.broulik.de>")); | ||
93 | setAboutData(about); | 88 | setAboutData(about); | ||
94 | 89 | | |||
95 | setButtons(Apply | Default); | 90 | setButtons(Apply | Default); | ||
96 | 91 | | |||
97 | connect(m_model, &IconsModel::selectedThemeChanged, this, [this] { | 92 | connect(m_model, &IconsModel::pendingDeletionsChanged, this, &IconModule::settingsChanged); | ||
ervin: Shouldn't you be able to remove that connect? I'd expect this signal trickling down to changing… | |||||
98 | m_selectedThemeDirty = true; | | |||
99 | setNeedsSave(true); | | |||
100 | }); | | |||
101 | connect(m_model, &IconsModel::pendingDeletionsChanged, this, [this] { | | |||
102 | setNeedsSave(true); | | |||
103 | }); | | |||
104 | 93 | | |||
Now that my ManagedConfigModule change landed, you should use a proper compile time check connect to settingsChanged here. ervin: Now that my ManagedConfigModule change landed, you should use a proper compile time check… | |||||
ervin: I still think those connects might not be necessary. | |||||
105 | // When user has a lot of themes installed, preview pixmaps might get evicted prematurely | 94 | // When user has a lot of themes installed, preview pixmaps might get evicted prematurely | ||
106 | QPixmapCache::setCacheLimit(50 * 1024); // 50 MiB | 95 | QPixmapCache::setCacheLimit(50 * 1024); // 50 MiB | ||
107 | } | 96 | } | ||
108 | 97 | | |||
109 | IconModule::~IconModule() | 98 | IconModule::~IconModule() | ||
110 | { | 99 | { | ||
100 | } | ||||
Don't do foreach. Instead write: for (auto theme : qAsConst(m_iconThemeCache)) { Or even better, just use qDeleteAll: qDeleteAll(m_iconThemeCache) Or even better yet : try to use std::unique_ptr, std::shared_ptr or QScopedPointer as values in your associative container (I let you check which one fits best). ervin: Don't do foreach. Instead write:
```
for (auto theme : qAsConst(m_iconThemeCache)) {
```
Or… | |||||
ervin: Still not addressed... mind your for loops... | |||||
111 | 101 | | |||
102 | IconsSettings *IconModule::iconsSettings() const | ||||
103 | { | ||||
104 | return m_settings; | ||||
112 | } | 105 | } | ||
113 | 106 | | |||
114 | IconsModel *IconModule::iconsModel() const | 107 | IconsModel *IconModule::iconsModel() const | ||
115 | { | 108 | { | ||
116 | return m_model; | 109 | return m_model; | ||
117 | } | 110 | } | ||
118 | 111 | | |||
119 | QStringList IconModule::iconGroups() const | 112 | IconSizeCategoryModel *IconModule::iconSizeCategoryModel() const | ||
120 | { | 113 | { | ||
121 | return m_iconGroups; | 114 | return m_iconSizeCategoryModel; | ||
122 | } | 115 | } | ||
123 | 116 | | |||
124 | bool IconModule::downloadingFile() const | 117 | bool IconModule::downloadingFile() const | ||
125 | { | 118 | { | ||
126 | return m_tempCopyJob; | 119 | return m_tempCopyJob; | ||
127 | } | 120 | } | ||
128 | 121 | | |||
129 | int IconModule::iconSize(int group) const | | |||
130 | { | | |||
131 | return m_iconSizes[group]; | | |||
132 | } | | |||
133 | | ||||
134 | void IconModule::setIconSize(int group, int size) | | |||
135 | { | | |||
136 | if (iconSize(group) == size) { | | |||
137 | return; | | |||
138 | } | | |||
139 | | ||||
140 | m_iconSizes[group] = size; | | |||
141 | setNeedsSave(true); | | |||
142 | m_iconSizesDirty = true; | | |||
143 | emit iconSizesChanged(); | | |||
144 | } | | |||
145 | | ||||
146 | QList<int> IconModule::availableIconSizes(int group) const | 122 | QList<int> IconModule::availableIconSizes(int group) const | ||
nitpick, I find = more readable in such a context (and less prone to the most vexing parse since you don't use curly braces init). I'd write: const auto themeName = m_model->selectedTheme(); ervin: nitpick, I find = more readable in such a context (and less prone to the most vexing parse… | |||||
Couldn't you fill the cache as soon as the selected theme changes instead? This way you wouldn't need to modify your cache here. ervin: Couldn't you fill the cache as soon as the selected theme changes instead? This way you… | |||||
nitpick, I find = more readable in such a context (and less prone to the most vexing parse since you don't use curly braces init). I'd write: const auto themeName = m_model->selectedTheme(); ervin: nitpick, I find = more readable in such a context (and less prone to the most vexing parse… | |||||
147 | { | 123 | { | ||
148 | return KIconLoader::global()->theme()->querySizes(static_cast<KIconLoader::Group>(group)); | 124 | const auto themeName = m_settings->theme(); | ||
125 | if (!m_kiconThemeCache.contains(m_settings->theme())) { | ||||
126 | m_kiconThemeCache.insert(themeName, new KIconTheme(themeName)); | ||||
127 | } | ||||
128 | return m_kiconThemeCache[themeName]->querySizes(static_cast<KIconLoader::Group>(group)); | ||||
Does this KIconTheme instance need caching? I recall creating those parses a tonne of files and directories and is quite slow broulik: Does this `KIconTheme` instance need caching? I recall creating those parses a tonne of files… | |||||
Agreed, it'll hit the disk quite a bit, this might be a problem in a function which takes part in bindings (more than one which will change around the same time). ervin: Agreed, it'll hit the disk quite a bit, this might be a problem in a function which takes part… | |||||
149 | } | 129 | } | ||
150 | 130 | | |||
151 | void IconModule::load() | 131 | void IconModule::load() | ||
152 | { | 132 | { | ||
133 | ManagedConfigModule::load(); | ||||
153 | m_model->load(); | 134 | m_model->load(); | ||
154 | loadIconSizes(); | 135 | // Model has been cleared so pretend the theme name changed to force view update | ||
155 | m_model->setSelectedTheme(KIconTheme::current()); | 136 | emit m_settings->ThemeChanged(); | ||
This one likely requires a comment, since normally you wouldn't need such an emit. ervin: This one likely requires a comment, since normally you wouldn't need such an emit. | |||||
156 | setNeedsSave(false); | | |||
157 | m_selectedThemeDirty = false; | | |||
158 | m_iconSizesDirty = false; | | |||
159 | } | 137 | } | ||
160 | 138 | | |||
161 | void IconModule::save() | 139 | void IconModule::save() | ||
162 | { | 140 | { | ||
163 | if (m_selectedThemeDirty) { | 141 | bool needToExportToKDE4 = m_settings->isSaveNeeded(); | ||
164 | QProcess::startDetached(CMAKE_INSTALL_FULL_LIBEXECDIR "/plasma-changeicons", {m_model->selectedTheme()}); | | |||
165 | } | | |||
166 | 142 | | |||
167 | if (m_iconSizesDirty) { | 143 | ManagedConfigModule::save(); | ||
168 | auto cfg = KSharedConfig::openConfig(); | | |||
169 | for (int i = 0; i < m_iconGroups.count(); ++i) { | | |||
170 | const QString &group = m_iconGroups.at(i); | | |||
171 | KConfigGroup cg(cfg, group + QLatin1String("Icons")); | | |||
172 | cg.writeEntry("Size", m_iconSizes.at(i), KConfig::Normal | KConfig::Global); | | |||
173 | } | | |||
174 | cfg->sync(); | | |||
175 | } | | |||
176 | 144 | | |||
177 | if (m_selectedThemeDirty || m_iconSizesDirty) { | 145 | if (needToExportToKDE4) { | ||
178 | exportToKDE4(); | 146 | exportToKDE4(); | ||
179 | } | 147 | } | ||
180 | 148 | | |||
181 | processPendingDeletions(); | 149 | processPendingDeletions(); | ||
182 | 150 | | |||
183 | KIconLoader::global()->newIconLoader(); | 151 | KIconLoader::global()->newIconLoader(); | ||
152 | } | ||||
184 | 153 | | |||
185 | setNeedsSave(false); | 154 | bool IconModule::isSaveNeeded() const | ||
186 | m_selectedThemeDirty = false; | 155 | { | ||
187 | m_iconSizesDirty = false; | 156 | return !m_model->pendingDeletions().isEmpty(); | ||
188 | } | 157 | } | ||
189 | 158 | | |||
190 | void IconModule::processPendingDeletions() | 159 | void IconModule::processPendingDeletions() | ||
ervin: Indentation looks wrong here. | |||||
191 | { | 160 | { | ||
192 | const QStringList pendingDeletions = m_model->pendingDeletions(); | 161 | const QStringList pendingDeletions = m_model->pendingDeletions(); | ||
193 | 162 | | |||
194 | for (const QString &themeName : pendingDeletions) { | 163 | for (const QString &themeName : pendingDeletions) { | ||
195 | Q_ASSERT(themeName != m_model->selectedTheme()); | 164 | Q_ASSERT(themeName != m_settings->theme()); | ||
196 | 165 | | |||
197 | KIconTheme theme(themeName); | 166 | KIconTheme theme(themeName); | ||
198 | auto *job = KIO::del(QUrl::fromLocalFile(theme.dir()), KIO::HideProgressInfo); | 167 | auto *job = KIO::del(QUrl::fromLocalFile(theme.dir()), KIO::HideProgressInfo); | ||
199 | // needs to block for it to work on "OK" where the dialog (kcmshell) closes | 168 | // needs to block for it to work on "OK" where the dialog (kcmshell) closes | ||
200 | job->exec(); | 169 | job->exec(); | ||
201 | } | 170 | } | ||
202 | 171 | | |||
203 | m_model->removeItemsPendingDeletion(); | 172 | m_model->removeItemsPendingDeletion(); | ||
204 | } | 173 | } | ||
205 | 174 | | |||
206 | void IconModule::defaults() | | |||
207 | { | | |||
208 | if (m_iconSizes != s_defaultIconSizes) { | | |||
209 | m_iconSizes = s_defaultIconSizes; | | |||
210 | emit iconSizesChanged(); | | |||
211 | } | | |||
212 | | ||||
213 | auto setThemeIfAvailable = [this](const QString &themeName) { | | |||
214 | const auto results = m_model->match(m_model->index(0, 0), ThemeNameRole, themeName); | | |||
215 | if (results.isEmpty()) { | | |||
216 | return false; | | |||
217 | } | | |||
218 | | ||||
219 | m_model->setSelectedTheme(themeName); | | |||
220 | return true; | | |||
221 | }; | | |||
222 | | ||||
223 | if (!setThemeIfAvailable(KIconTheme::defaultThemeName())) { | | |||
224 | setThemeIfAvailable(QStringLiteral("breeze")); | | |||
225 | } | | |||
226 | | ||||
227 | setNeedsSave(true); | | |||
228 | } | | |||
229 | | ||||
230 | void IconModule::loadIconSizes() | | |||
231 | { | | |||
232 | auto cfg = KSharedConfig::openConfig(); | | |||
233 | | ||||
234 | QVector<int> iconSizes(6, 0); // why doesn't KIconLoader::LastGroup - 1 work here?! | | |||
235 | | ||||
236 | int i = KIconLoader::FirstGroup; | | |||
237 | for (const QString &group : qAsConst(m_iconGroups)) { | | |||
238 | int size = KIconLoader::global()->theme()->defaultSize(static_cast<KIconLoader::Group>(i)); | | |||
239 | | ||||
240 | KConfigGroup iconGroup(cfg, group + QLatin1String("Icons")); | | |||
241 | size = iconGroup.readEntry("Size", size); | | |||
242 | | ||||
243 | iconSizes[i] = size; | | |||
244 | | ||||
245 | ++i; | | |||
246 | } | | |||
247 | | ||||
248 | if (m_iconSizes != iconSizes) { | | |||
249 | m_iconSizes = iconSizes; | | |||
250 | emit iconSizesChanged(); | | |||
251 | } | | |||
252 | } | | |||
253 | | ||||
254 | void IconModule::getNewStuff(QQuickItem *ctx) | 175 | void IconModule::getNewStuff(QQuickItem *ctx) | ||
255 | { | 176 | { | ||
256 | if (!m_newStuffDialog) { | 177 | if (!m_newStuffDialog) { | ||
257 | m_newStuffDialog = new KNS3::DownloadDialog(QStringLiteral("icons.knsrc")); | 178 | m_newStuffDialog = new KNS3::DownloadDialog(QStringLiteral("icons.knsrc")); | ||
258 | m_newStuffDialog->setWindowTitle(i18n("Download New Icon Themes")); | 179 | m_newStuffDialog->setWindowTitle(i18n("Download New Icon Themes")); | ||
259 | m_newStuffDialog->setWindowModality(Qt::WindowModal); | 180 | m_newStuffDialog->setWindowModality(Qt::WindowModal); | ||
260 | m_newStuffDialog->winId(); // so it creates the windowHandle(); | 181 | m_newStuffDialog->winId(); // so it creates the windowHandle(); | ||
261 | // TODO would be lovely to scroll to and select the newly installed scheme, if any | 182 | // TODO would be lovely to scroll to and select the newly installed scheme, if any | ||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Line(s) | 257 | { | |||
342 | } | 263 | } | ||
343 | 264 | | |||
344 | configFilePath += QLatin1String("kdeglobals"); | 265 | configFilePath += QLatin1String("kdeglobals"); | ||
345 | 266 | | |||
346 | KSharedConfigPtr kglobalcfg = KSharedConfig::openConfig(QStringLiteral("kdeglobals")); | 267 | KSharedConfigPtr kglobalcfg = KSharedConfig::openConfig(QStringLiteral("kdeglobals")); | ||
347 | KConfig kde4config(configFilePath, KConfig::SimpleConfig); | 268 | KConfig kde4config(configFilePath, KConfig::SimpleConfig); | ||
348 | 269 | | |||
349 | KConfigGroup kde4IconGroup(&kde4config, "Icons"); | 270 | KConfigGroup kde4IconGroup(&kde4config, "Icons"); | ||
350 | kde4IconGroup.writeEntry("Theme", m_model->selectedTheme()); | 271 | kde4IconGroup.writeEntry("Theme", m_settings->theme()); | ||
351 | 272 | | |||
352 | //Synchronize icon effects | 273 | //Synchronize icon effects | ||
353 | for (const QString &group : qAsConst(m_iconGroups)) { | 274 | for (int row = 0; row < m_iconSizeCategoryModel->rowCount(); row++) { | ||
275 | QModelIndex idx(m_iconSizeCategoryModel->index(row, 0)); | ||||
276 | QString group = m_iconSizeCategoryModel->data(idx, IconSizeCategoryModel::ConfigSectionRole).toString(); | ||||
354 | const QString groupName = group + QLatin1String("Icons"); | 277 | const QString groupName = group + QLatin1String("Icons"); | ||
355 | KConfigGroup cg(kglobalcfg, groupName); | 278 | KConfigGroup cg(kglobalcfg, groupName); | ||
356 | KConfigGroup kde4Cg(&kde4config, groupName); | 279 | KConfigGroup kde4Cg(&kde4config, groupName); | ||
357 | 280 | | |||
358 | // HACK copyTo only copies keys, it doesn't replace the entire group | 281 | // HACK copyTo only copies keys, it doesn't replace the entire group | ||
359 | // which means if we removed the effects in our config it won't remove | 282 | // which means if we removed the effects in our config it won't remove | ||
360 | // them from the kde4 config, hence revert all of them prior to copying | 283 | // them from the kde4 config, hence revert all of them prior to copying | ||
361 | const QStringList keys = cg.keyList() + kde4Cg.keyList(); | 284 | const QStringList keys = cg.keyList() + kde4Cg.keyList(); | ||
▲ Show 20 Lines • Show All 206 Lines • ▼ Show 20 Line(s) | 464 | for (const QString &iconName : iconNames) { | |||
568 | renderer.render(&p); | 491 | renderer.render(&p); | ||
569 | return pixmap; | 492 | return pixmap; | ||
570 | } | 493 | } | ||
571 | } | 494 | } | ||
572 | 495 | | |||
573 | return QPixmap(); | 496 | return QPixmap(); | ||
574 | } | 497 | } | ||
575 | 498 | | |||
499 | int IconModule::pluginIndex(const QString &themeName) const | ||||
500 | { | ||||
501 | const auto results = m_model->match(m_model->index(0, 0), ThemeNameRole, themeName); | ||||
502 | if (results.count() == 1) { | ||||
503 | return results.first().row(); | ||||
504 | } | ||||
505 | return -1; | ||||
506 | } | ||||
507 | | ||||
576 | #include "main.moc" | 508 | #include "main.moc" |
Shouldn't you be able to remove that connect? I'd expect this signal trickling down to changing the settings object, wouldn't it?