Changeset View
Standalone View
src/kcm.cpp
Show All 23 Lines | |||||
24 | #include <KPluginFactory> | 24 | #include <KPluginFactory> | ||
25 | #include <KPluginLoader> | 25 | #include <KPluginLoader> | ||
26 | #include <KAboutData> | 26 | #include <KAboutData> | ||
27 | #include <KConfigGroup> | 27 | #include <KConfigGroup> | ||
28 | #include <KSharedConfig> | 28 | #include <KSharedConfig> | ||
29 | #include <QDebug> | 29 | #include <QDebug> | ||
30 | #include <QStandardPaths> | 30 | #include <QStandardPaths> | ||
31 | #include <QProcess> | 31 | #include <QProcess> | ||
32 | #include <QQuickView> | | |||
33 | 32 | | |||
34 | #include <QtQml> | | |||
35 | #include <QQmlEngine> | 33 | #include <QQmlEngine> | ||
36 | #include <QQmlContext> | 34 | #include <QQuickItem> | ||
35 | #include <QQuickWindow> | ||||
37 | #include <QStandardItemModel> | 36 | #include <QStandardItemModel> | ||
38 | 37 | | |||
39 | #include <KLocalizedString> | 38 | #include <KLocalizedString> | ||
40 | #include <KMessageBox> | | |||
41 | 39 | | |||
42 | #include <kauthaction.h> | 40 | #include <kauthaction.h> | ||
43 | #include <kauthexecutejob.h> | 41 | #include <kauthexecutejob.h> | ||
44 | 42 | | |||
45 | #include <KNewStuff3/KNSCore/Engine> | 43 | #include <KNewStuff3/KNSCore/Engine> | ||
46 | #include <KNewStuff3/KNSCore/EntryInternal> | 44 | #include <KNewStuff3/KNSCore/EntryInternal> | ||
47 | 45 | | |||
48 | #include <kio/job.h> | 46 | #include <kio/job.h> | ||
49 | #include <KIO/CopyJob> | 47 | #include <KIO/CopyJob> | ||
50 | 48 | | |||
51 | K_PLUGIN_FACTORY_WITH_JSON(KCMPlymouthFactory, "kcm_plymouth.json", registerPlugin<KCMPlymouth>();) | 49 | K_PLUGIN_FACTORY_WITH_JSON(KCMPlymouthFactory, "kcm_plymouth.json", registerPlugin<KCMPlymouth>();) | ||
52 | 50 | | |||
53 | KCMPlymouth::KCMPlymouth(QObject* parent, const QVariantList& args) | 51 | KCMPlymouth::KCMPlymouth(QObject* parent, const QVariantList& args) | ||
54 | : KQuickAddons::ConfigModule(parent, args) | 52 | : KQuickAddons::ConfigModule(parent, args) | ||
55 | { | 53 | { | ||
56 | //This flag seems to be needed in order for QQuickWidget to work | | |||
57 | //see https://bugreports.qt-project.org/browse/QTBUG-40765 | | |||
58 | //also, it seems to work only if set in the kcm, not in the systemsettings' main | | |||
59 | qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); | | |||
60 | qmlRegisterType<QStandardItemModel>(); | 54 | qmlRegisterType<QStandardItemModel>(); | ||
61 | qmlRegisterType<KCMPlymouth>(); | 55 | qmlRegisterType<KCMPlymouth>(); | ||
62 | KAboutData* about = new KAboutData(QStringLiteral("kcm_plymouth"), i18n("Configure Plymouth Splash Screen"), | 56 | KAboutData* about = new KAboutData(QStringLiteral("kcm_plymouth"), i18n("Boot Splash Screen"), | ||
63 | QStringLiteral(PLYMOUTH_KCM_VERSION), QString(), KAboutLicense::LGPL); | 57 | QStringLiteral(PLYMOUTH_KCM_VERSION), QString(), KAboutLicense::LGPL); | ||
64 | about->addAuthor(i18n("Marco Martin"), QString(), QStringLiteral("mart@kde.org")); | 58 | about->addAuthor(i18n("Marco Martin"), QString(), QStringLiteral("mart@kde.org")); | ||
65 | setAboutData(about); | 59 | setAboutData(about); | ||
66 | setButtons(Apply | Default); | 60 | setButtons(Apply); | ||
67 | 61 | | |||
68 | m_model = new QStandardItemModel(this); | 62 | m_model = new QStandardItemModel(this); | ||
69 | QHash<int, QByteArray> roles = m_model->roleNames(); | 63 | | ||
70 | roles[PluginNameRole] = "pluginName"; | 64 | m_model->setItemRoleNames({ | ||
71 | roles[ScreenhotRole] = "screenshot"; | 65 | {Qt::DisplayRole, QByteArrayLiteral("display")}, | ||
72 | roles[UninstallableRole] = "uninstallable"; | 66 | {DescriptionRole, QByteArrayLiteral("description")}, | ||
73 | m_model->setItemRoleNames(roles); | 67 | {PluginNameRole, QByteArrayLiteral("pluginName")}, | ||
68 | {ScreenhotRole, QByteArrayLiteral("screenshot")}, | ||||
69 | {UninstallableRole, QByteArrayLiteral("uninstallable")} | ||||
70 | }); | ||||
74 | 71 | | |||
75 | //setAuthActionName("org.kde.kcontrol.kcmplymouth.save"); | 72 | //setAuthActionName("org.kde.kcontrol.kcmplymouth.save"); | ||
76 | //setNeedsAuthorization(true); | 73 | //setNeedsAuthorization(true); | ||
77 | } | 74 | } | ||
78 | 75 | | |||
79 | KCMPlymouth::~KCMPlymouth() | 76 | KCMPlymouth::~KCMPlymouth() | ||
80 | { | 77 | { | ||
81 | delete m_newStuffDialog.data(); | 78 | delete m_newStuffDialog.data(); | ||
82 | } | 79 | } | ||
83 | 80 | | |||
84 | void KCMPlymouth::getNewStuff() | 81 | void KCMPlymouth::reloadModel() | ||
82 | { | ||||
83 | m_model->clear(); | ||||
84 | | ||||
85 | QDir dir(QStringLiteral(PLYMOUTH_THEMES_DIR)); | ||||
86 | if (!dir.exists()) { | ||||
87 | return; | ||||
88 | } | ||||
89 | | ||||
90 | KConfigGroup installedCg(KSharedConfig::openConfig(QStringLiteral("kplymouththemeinstallerrc")), "DownloadedThemes"); | ||||
91 | | ||||
92 | dir.setFilter(QDir::NoDotAndDotDot|QDir::Dirs); | ||||
93 | | ||||
94 | const auto list = dir.entryInfoList(); | ||||
95 | for (const QFileInfo &fileInfo : list) { | ||||
96 | const QString pluginName = fileInfo.fileName(); | ||||
97 | QDir themeDir(fileInfo.filePath()); | ||||
98 | | ||||
99 | KConfig file(themeDir.filePath(pluginName + QLatin1String(".plymouth")), KConfig::SimpleConfig); | ||||
100 | KConfigGroup grp = file.group("Plymouth Theme"); | ||||
101 | | ||||
102 | QString displayName = grp.readEntry("Name", QString()); | ||||
103 | if (displayName.isEmpty()) { | ||||
104 | displayName = pluginName; | ||||
105 | } | ||||
106 | | ||||
107 | QStandardItem *row = new QStandardItem(displayName); | ||||
108 | row->setData(pluginName, PluginNameRole); | ||||
109 | row->setData(grp.readEntry("Description", QString()), DescriptionRole); | ||||
110 | row->setData(installedCg.entryMap().contains(fileInfo.fileName()), UninstallableRole); | ||||
111 | | ||||
112 | //the theme has a preview | ||||
113 | if (QFile::exists(themeDir.path() + QStringLiteral("/preview.png"))) { | ||||
114 | row->setData(QString(themeDir.path() + QStringLiteral("/preview.png")), ScreenhotRole); | ||||
115 | //fetch it downloaded from kns | ||||
116 | } else { | ||||
117 | const QString fileName = installedCg.readEntry(fileInfo.fileName(), QString()); | ||||
118 | if (fileName.isEmpty()) { | ||||
119 | row->setData(QString(), ScreenhotRole); | ||||
120 | } else { | ||||
121 | row->setData(fileName + QStringLiteral(".png"), ScreenhotRole); | ||||
122 | } | ||||
123 | } | ||||
124 | | ||||
125 | m_model->appendRow(row); | ||||
126 | } | ||||
127 | | ||||
128 | emit selectedPluginIndexChanged(); | ||||
129 | } | ||||
130 | | ||||
131 | void KCMPlymouth::getNewStuff(QQuickItem *ctx) | ||||
85 | { | 132 | { | ||
86 | if (!m_newStuffDialog) { | 133 | if (!m_newStuffDialog) { | ||
87 | m_newStuffDialog = new KNS3::DownloadDialog( QLatin1String("plymouth.knsrc") ); | 134 | m_newStuffDialog = new KNS3::DownloadDialog( QLatin1String("plymouth.knsrc") ); | ||
88 | m_newStuffDialog.data()->setWindowTitle(i18n("Download New Splash Screens")); | 135 | m_newStuffDialog->setWindowTitle(i18n("Download New Boot Splash Screens")); | ||
89 | connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, &KCMPlymouth::load); | 136 | m_newStuffDialog->setWindowModality(Qt::WindowModal); | ||
137 | m_newStuffDialog->winId(); // so it creates the windowHandle(); | ||||
138 | connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, &KCMPlymouth::reloadModel); | ||||
broulik: Ideally here we would reload the model and then set the newly installed theme as current | |||||
I think it should not select the newest installed theme instead of still having the current one selected, because in the "Get New..." dialog you can install multiple themes and it can't know which of them you actually want. GB_2: I think it should not select the newest installed theme instead of still having the current one… | |||||
90 | connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::finished, m_newStuffDialog.data(), &KNS3::DownloadDialog::deleteLater); | 139 | connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::finished, m_newStuffDialog.data(), &KNS3::DownloadDialog::deleteLater); | ||
91 | 140 | | |||
92 | connect(m_newStuffDialog->engine(), &KNSCore::Engine::signalEntryChanged, this, [=](const KNSCore::EntryInternal &entry){ | 141 | connect(m_newStuffDialog->engine(), &KNSCore::Engine::signalEntryChanged, this, [=](const KNSCore::EntryInternal &entry){ | ||
93 | if (!entry.isValid() || entry.status() != KNS3::Entry::Installed) { | 142 | if (!entry.isValid() || entry.status() != KNS3::Entry::Installed) { | ||
94 | return; | 143 | return; | ||
95 | } | 144 | } | ||
96 | 145 | | |||
97 | KIO::file_copy(QUrl(entry.previewUrl(KNSCore::EntryInternal::PreviewBig1)), QUrl::fromLocalFile(QString(entry.installedFiles().first() + QStringLiteral(".png"))), -1, KIO::Overwrite | KIO::HideProgressInfo); | 146 | KIO::file_copy(QUrl(entry.previewUrl(KNSCore::EntryInternal::PreviewBig1)), QUrl::fromLocalFile(QString(entry.installedFiles().first() + QStringLiteral(".png"))), -1, KIO::Overwrite | KIO::HideProgressInfo); | ||
98 | }); | 147 | }); | ||
99 | } | 148 | } | ||
100 | m_newStuffDialog.data()->show(); | 149 | | ||
150 | if (ctx && ctx->window()) { | ||||
151 | m_newStuffDialog->windowHandle()->setTransientParent(ctx->window()); | ||||
152 | } | ||||
153 | | ||||
154 | m_newStuffDialog->show(); | ||||
101 | } | 155 | } | ||
102 | 156 | | |||
103 | QStandardItemModel *KCMPlymouth::themesModel() | 157 | QStandardItemModel *KCMPlymouth::themesModel() | ||
104 | { | 158 | { | ||
105 | return m_model; | 159 | return m_model; | ||
106 | } | 160 | } | ||
107 | 161 | | |||
108 | QString KCMPlymouth::selectedPlugin() const | 162 | QString KCMPlymouth::selectedPlugin() const | ||
109 | { | 163 | { | ||
110 | return m_selectedPlugin; | 164 | return m_selectedPlugin; | ||
111 | } | 165 | } | ||
112 | 166 | | |||
113 | void KCMPlymouth::setSelectedPlugin(const QString &plugin) | 167 | void KCMPlymouth::setSelectedPlugin(const QString &plugin) | ||
114 | { | 168 | { | ||
115 | if (m_selectedPlugin == plugin) { | 169 | if (m_selectedPlugin == plugin) { | ||
116 | return; | 170 | return; | ||
117 | } | 171 | } | ||
118 | 172 | | |||
119 | const bool firstTime = m_selectedPlugin.isNull(); | | |||
120 | m_selectedPlugin = plugin; | 173 | m_selectedPlugin = plugin; | ||
121 | emit selectedPluginChanged(); | 174 | emit selectedPluginChanged(); | ||
175 | emit selectedPluginIndexChanged(); | ||||
122 | 176 | | |||
123 | if (!firstTime) { | | |||
124 | setNeedsSave(true); | 177 | setNeedsSave(true); | ||
125 | } | 178 | } | ||
179 | | ||||
180 | bool KCMPlymouth::busy() const | ||||
181 | { | ||||
182 | return m_busy; | ||||
183 | } | ||||
184 | | ||||
185 | void KCMPlymouth::setBusy(const bool &busy) | ||||
186 | { | ||||
187 | if (m_busy == busy) { | ||||
188 | return; | ||||
189 | } | ||||
190 | | ||||
191 | m_busy = busy; | ||||
192 | emit busyChanged(); | ||||
126 | } | 193 | } | ||
127 | 194 | | |||
128 | int KCMPlymouth::selectedPluginIndex() const | 195 | int KCMPlymouth::selectedPluginIndex() const | ||
129 | { | 196 | { | ||
130 | for (int i = 0; i < m_model->rowCount(); ++i) { | 197 | for (int i = 0; i < m_model->rowCount(); ++i) { | ||
131 | if (m_model->data(m_model->index(i, 0), PluginNameRole).toString() == m_selectedPlugin) { | 198 | if (m_model->data(m_model->index(i, 0), PluginNameRole).toString() == m_selectedPlugin) { | ||
132 | return i; | 199 | return i; | ||
133 | } | 200 | } | ||
134 | } | 201 | } | ||
135 | return -1; | 202 | return -1; | ||
136 | } | 203 | } | ||
137 | 204 | | |||
138 | void KCMPlymouth::load() | 205 | void KCMPlymouth::load() | ||
139 | { | 206 | { | ||
140 | m_model->clear(); | 207 | reloadModel(); | ||
141 | QDir dir(QStringLiteral(PLYMOUTH_THEMES_DIR)); | | |||
142 | if (!dir.exists()) { | | |||
143 | return; | | |||
144 | } | | |||
145 | 208 | | |||
146 | KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral(PLYMOUTH_CONFIG_PATH)), "Daemon"); | 209 | KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral(PLYMOUTH_CONFIG_PATH)), "Daemon"); | ||
147 | m_selectedPlugin = cg.readEntry("Theme"); | | |||
148 | KConfigGroup installedCg(KSharedConfig::openConfig(QStringLiteral("kplymouththemeinstallerrc")), "DownloadedThemes"); | | |||
149 | 210 | | |||
150 | dir.setFilter(QDir::NoDotAndDotDot|QDir::Dirs); | 211 | setSelectedPlugin(cg.readEntry("Theme")); | ||
151 | QFileInfoList list = dir.entryInfoList(); | | |||
152 | for (int i = 0; i < list.size(); ++i) { | | |||
153 | QFileInfo fileInfo = list.at(i); | | |||
154 | QStandardItem* row = new QStandardItem(fileInfo.fileName()); | | |||
155 | row->setData(fileInfo.fileName(), PluginNameRole); | | |||
156 | row->setData(installedCg.entryMap().contains(fileInfo.fileName()), UninstallableRole); | | |||
157 | QDir themeDir(fileInfo.filePath()); | | |||
158 | //the theme has a preview | | |||
159 | if (QFile::exists(themeDir.path() + QStringLiteral("/preview.png"))) { | | |||
160 | row->setData(QString(themeDir.path() + QStringLiteral("/preview.png")), ScreenhotRole); | | |||
161 | //fetch it downloaded from kns | | |||
162 | } else { | | |||
163 | const QString fileName = installedCg.readEntry(fileInfo.fileName(), QString()); | | |||
164 | if (fileName.isEmpty()) { | | |||
165 | row->setData(QString(), ScreenhotRole); | | |||
166 | } else { | | |||
167 | row->setData(fileName + QStringLiteral(".png"), ScreenhotRole); | | |||
168 | } | | |||
169 | } | | |||
170 | 212 | | |||
I think the model population must be split from the KCM load() so you can reload the model without marking it as non-dirty. Right now when installing a new theme, the Apply button doesn't enable and no theme is selected. Ideally it would select the newly installed theme. Also needs a emit selectedPluginIndexChanged(); after reloading the model as a model reset will cause ListView to forget its currentIndex broulik: I think the model population must be split from the KCM `load()` so you can reload the model… | |||||
171 | m_model->appendRow(row); | | |||
172 | } | | |||
173 | setNeedsSave(false); | 213 | setNeedsSave(false); | ||
174 | } | 214 | } | ||
175 | 215 | | |||
176 | 216 | | |||
177 | void KCMPlymouth::save() | 217 | void KCMPlymouth::save() | ||
178 | { | 218 | { | ||
219 | setBusy(true); | ||||
179 | QVariantMap helperargs; | 220 | QVariantMap helperargs; | ||
180 | helperargs[QStringLiteral("theme")] = m_selectedPlugin; | 221 | helperargs[QStringLiteral("theme")] = m_selectedPlugin; | ||
181 | 222 | | |||
182 | //KAuth::Action action(authActionName()); | 223 | //KAuth::Action action(authActionName()); | ||
183 | KAuth::Action action(QStringLiteral("org.kde.kcontrol.kcmplymouth.save")); | 224 | KAuth::Action action(QStringLiteral("org.kde.kcontrol.kcmplymouth.save")); | ||
184 | action.setHelperId(QStringLiteral("org.kde.kcontrol.kcmplymouth")); | 225 | action.setHelperId(QStringLiteral("org.kde.kcontrol.kcmplymouth")); | ||
185 | action.setArguments(helperargs); | 226 | action.setArguments(helperargs); | ||
186 | action.setTimeout(60000); | 227 | action.setTimeout(60000); | ||
187 | 228 | | |||
188 | KAuth::ExecuteJob *job = action.execute(); | 229 | KAuth::ExecuteJob *job = action.execute(); | ||
189 | bool rc = job->exec(); | 230 | bool rc = job->exec(); | ||
190 | if (!rc) { | 231 | if (!rc) { | ||
191 | KMessageBox::error(nullptr, i18n("Unable to authenticate/execute the action: %1, %2", job->error(), job->errorString())); | 232 | if (job->error() == KAuth::ActionReply::UserCancelledError) { | ||
233 | emit showErrorMessage(i18n("Unable to authenticate/execute the action: %1 (%2)", job->error(), job->errorString())); | ||||
This isn't a signal, also why do you need to reload when saving? It's not like any themes could magically appear here? broulik: This isn't a signal, also why do you need to reload when saving? It's not like any themes could… | |||||
It resets it if you cancel the action, so the apply button is enabled again and the selection is reset. GB_2: It resets it if you cancel the action, so the apply button is enabled again and the selection… | |||||
234 | } | ||||
235 | load(); | ||||
In error handling we should probably check for error() == KAuth::ActionReply::UserCancelledError to not show a pointless error message when user canceled broulik: In error handling we should probably check for `error() == KAuth::ActionReply… | |||||
192 | } | 236 | } | ||
237 | setBusy(false); | ||||
193 | } | 238 | } | ||
194 | 239 | | |||
195 | void KCMPlymouth::uninstall(const QString &plugin) | 240 | void KCMPlymouth::uninstall(const QString &plugin) | ||
196 | { | 241 | { | ||
197 | QVariantMap helperargs; | 242 | QVariantMap helperargs; | ||
198 | helperargs[QStringLiteral("theme")] = plugin; | 243 | helperargs[QStringLiteral("theme")] = plugin; | ||
199 | 244 | | |||
200 | //KAuth::Action action(authActionName()); | 245 | //KAuth::Action action(authActionName()); | ||
201 | KAuth::Action action(QStringLiteral("org.kde.kcontrol.kcmplymouth.uninstall")); | 246 | KAuth::Action action(QStringLiteral("org.kde.kcontrol.kcmplymouth.uninstall")); | ||
202 | action.setHelperId(QStringLiteral("org.kde.kcontrol.kcmplymouth")); | 247 | action.setHelperId(QStringLiteral("org.kde.kcontrol.kcmplymouth")); | ||
203 | action.setArguments(helperargs); | 248 | action.setArguments(helperargs); | ||
204 | 249 | | |||
205 | KAuth::ExecuteJob *job = action.execute(); | 250 | KAuth::ExecuteJob *job = action.execute(); | ||
206 | bool rc = job->exec(); | 251 | bool rc = job->exec(); | ||
207 | if (!rc) { | 252 | if (!rc) { | ||
208 | KMessageBox::error(nullptr, i18n("Unable to authenticate/execute the action: %1, %2", job->error(), job->errorString())); | 253 | emit showErrorMessage(i18n("Unable to authenticate/execute the action: %1 (%2)", job->error(), job->errorString())); | ||
209 | } else { | 254 | } else { | ||
210 | KConfigGroup installedCg(KSharedConfig::openConfig(QStringLiteral("kplymouththemeinstallerrc")), "DownloadedThemes"); | 255 | KConfigGroup installedCg(KSharedConfig::openConfig(QStringLiteral("kplymouththemeinstallerrc")), "DownloadedThemes"); | ||
211 | installedCg.deleteEntry(plugin); | 256 | installedCg.deleteEntry(plugin); | ||
257 | emit showSuccessMessage(i18n("Theme uninstalled successfully.")); | ||||
212 | load(); | 258 | load(); | ||
213 | } | 259 | } | ||
214 | } | 260 | } | ||
215 | 261 | | |||
216 | void KCMPlymouth::defaults() | 262 | void KCMPlymouth::defaults() | ||
217 | {/*TODO | 263 | {/*TODO | ||
218 | if (!) { | 264 | if (!) { | ||
219 | return; | 265 | return; | ||
220 | } | 266 | } | ||
221 | */ | 267 | */ | ||
222 | 268 | | |||
223 | } | 269 | } | ||
224 | 270 | | |||
225 | #include "kcm.moc" | 271 | #include "kcm.moc" |
Ideally here we would reload the model and then set the newly installed theme as current