GrantleeTheme

grantleethememanager.cpp
1 /*
2  SPDX-FileCopyrightText: 2013-2022 Laurent Montel <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-or-later
5 */
6 
7 #include "grantleethememanager.h"
8 #include "grantleetheme_p.h"
9 
10 #include <KActionCollection>
11 #include <KActionMenu>
12 #include <KAuthorized>
13 #include <KConfigGroup>
14 #include <KDirWatch>
15 #include <KLocalizedString>
16 #include <KNSWidgets/Action>
17 #include <KSharedConfig>
18 #include <KToggleAction>
19 #include <QAction>
20 #include <QIcon>
21 #include <QPointer>
22 
23 #include <QActionGroup>
24 #include <QDir>
25 #include <QDirIterator>
26 #include <QStandardPaths>
27 
28 namespace GrantleeTheme
29 {
30 class ThemeManagerPrivate
31 {
32 public:
33  ThemeManagerPrivate(const QString &type, const QString &desktopFileName, KActionCollection *ac, const QString &relativePath, ThemeManager *qq)
34  : applicationType(type)
35  , defaultDesktopFileName(desktopFileName)
36  , actionCollection(ac)
37  , q(qq)
38  {
39  watch = new KDirWatch(q);
40  initThemesDirectories(relativePath);
41  if (KAuthorized::authorize(QStringLiteral("ghns"))) {
42  downloadThemesAction = new KNSWidgets::Action(i18n("Download new Templates..."), QString(), q);
43  if (actionCollection) {
44  actionCollection->addAction(QStringLiteral("download_header_themes"), downloadThemesAction);
45  }
46  separatorAction = new QAction(q);
47  separatorAction->setSeparator(true);
48  }
49 
50  q->connect(watch, &KDirWatch::dirty, q, [this]() {
51  directoryChanged();
52  });
53  updateThemesPath(true);
54 
55  // Migrate the old configuration format that only support mail and addressbook
56  // theming to the new generic format
57  KSharedConfig::Ptr config = KSharedConfig::openConfig();
58  if (config->hasGroup(QStringLiteral("GrantleeTheme"))) {
59  const KConfigGroup group = config->group(QStringLiteral("GrantleeTheme"));
60  const QString mailTheme = group.readEntry(QStringLiteral("grantleeMailThemeName"));
61  const QString addressbookTheme = group.readEntry(QStringLiteral("grantleeAddressBookThemeName"));
62 
63  config->group(QStringLiteral("mail")).writeEntry(QStringLiteral("themeName"), mailTheme);
64  config->group(QStringLiteral("addressbook")).writeEntry(QStringLiteral("themeName"), addressbookTheme);
65 
66  config->deleteGroup(QStringLiteral("GrantleeTheme"));
67  }
68  }
69 
70  ~ThemeManagerPrivate()
71  {
72  removeActions();
73  themes.clear();
74  }
75 
76  void directoryChanged()
77  {
78  updateThemesPath();
79  updateActionList();
80  Q_EMIT q->updateThemes();
81  }
82 
83  void updateThemesPath(bool init = false)
84  {
85  if (!init) {
86  if (!themesDirectories.isEmpty()) {
87  for (const QString &directory : std::as_const(themesDirectories)) {
88  watch->removeDir(directory);
89  }
90  } else {
91  return;
92  }
93  }
94 
95  // clear all previous theme information
96  themes.clear();
97 
98  for (const QString &directory : std::as_const(themesDirectories)) {
100  QStringList alreadyLoadedThemeName;
101  while (dirIt.hasNext()) {
102  dirIt.next();
103  const QString dirName = dirIt.fileName();
104  GrantleeTheme::Theme theme = q->loadTheme(dirIt.filePath(), dirName, defaultDesktopFileName);
105  if (theme.isValid()) {
106  QString themeName = theme.name();
107  if (alreadyLoadedThemeName.contains(themeName)) {
108  int i = 2;
109  const QString originalName(theme.name());
110  while (alreadyLoadedThemeName.contains(themeName)) {
111  themeName = originalName + QStringLiteral(" (%1)").arg(i);
112  ++i;
113  }
114  theme.d->name = themeName;
115  }
116  alreadyLoadedThemeName << themeName;
117  auto it = themes.find(dirName);
118  if (it != themes.end()) {
119  (*it).addThemePath(dirIt.filePath());
120  } else {
121  themes.insert(dirName, theme);
122  }
123  }
124  }
125  watch->addDir(directory);
126  }
127 
128  Q_EMIT q->themesChanged();
129  watch->startScan();
130  }
131 
132  void removeActions()
133  {
134  if (!actionGroup || !menu) {
135  return;
136  }
137  for (KToggleAction *action : std::as_const(themesActionList)) {
138  actionGroup->removeAction(action);
139  menu->removeAction(action);
140  if (actionCollection) {
141  actionCollection->removeAction(action);
142  }
143  }
144  if (separatorAction) {
145  menu->removeAction(separatorAction);
146  if (downloadThemesAction) {
147  menu->removeAction(downloadThemesAction);
148  }
149  }
150  themesActionList.clear();
151  }
152 
153  void updateActionList()
154  {
155  if (!actionGroup || !menu) {
156  return;
157  }
158  QString themeActivated;
159 
160  QAction *selectedAction = actionGroup->checkedAction();
161  if (selectedAction) {
162  themeActivated = selectedAction->data().toString();
163  }
164 
165  removeActions();
166 
167  bool themeActivatedFound = false;
169  while (i.hasNext()) {
170  i.next();
171  GrantleeTheme::Theme theme = i.value();
172  auto act = new KToggleAction(theme.name(), q);
173  act->setToolTip(theme.description());
174  act->setData(theme.dirName());
175  if (theme.dirName() == themeActivated) {
176  act->setChecked(true);
177  themeActivatedFound = true;
178  }
179  themesActionList.append(act);
180  actionGroup->addAction(act);
181  menu->addAction(act);
182  q->connect(act, &KToggleAction::triggered, q, [this]() {
183  slotThemeSelected();
184  });
185  }
186  if (!themeActivatedFound) {
187  if (!themesActionList.isEmpty() && !themeActivated.isEmpty()) {
188  // Activate first item if we removed theme.
189  KToggleAction *act = themesActionList.at(0);
190  act->setChecked(true);
191  selectTheme(act);
192  }
193  }
194  if (separatorAction) {
195  menu->addAction(separatorAction);
196  if (downloadThemesAction) {
197  menu->addAction(downloadThemesAction);
198  }
199  }
200  }
201 
202  void selectTheme(KToggleAction *act)
203  {
204  if (act) {
205  KSharedConfig::Ptr config = KSharedConfig::openConfig();
206  KConfigGroup group = config->group(applicationType);
207  group.writeEntry(QStringLiteral("themeName"), act->data().toString());
208  config->sync();
209  }
210  }
211 
212  void slotThemeSelected()
213  {
214  if (q->sender()) {
215  auto act = qobject_cast<KToggleAction *>(q->sender());
216  selectTheme(act);
217  Q_EMIT q->grantleeThemeSelected();
218  }
219  }
220 
221  KToggleAction *actionForTheme()
222  {
223  const KSharedConfig::Ptr config = KSharedConfig::openConfig();
224  const KConfigGroup group = config->group(applicationType);
225  const QString themeName = group.readEntry(QStringLiteral("themeName"), QStringLiteral("default"));
226 
227  if (themeName.isEmpty()) {
228  return nullptr;
229  }
230  for (KToggleAction *act : std::as_const(themesActionList)) {
231  if (act->data().toString() == themeName) {
232  return static_cast<KToggleAction *>(act);
233  }
234  }
235  return nullptr;
236  }
237 
238  void initThemesDirectories(const QString &themesRelativePath)
239  {
240  if (!themesRelativePath.isEmpty()) {
242  const QString localDirectory = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + themesRelativePath;
243  themesDirectories.append(localDirectory);
244  }
245  }
246 
247  QString applicationType;
248  QString defaultDesktopFileName;
249  QStringList themesDirectories;
251  QVector<KToggleAction *> themesActionList;
252  KDirWatch *watch = nullptr;
253  QActionGroup *actionGroup = nullptr;
254  KActionMenu *menu = nullptr;
255  KActionCollection *const actionCollection;
256  QAction *separatorAction = nullptr;
257  KNSWidgets::Action *downloadThemesAction = nullptr;
258  ThemeManager *const q;
259 };
260 }
261 
262 using namespace GrantleeTheme;
263 
264 ThemeManager::ThemeManager(const QString &applicationType,
265  const QString &defaultDesktopFileName,
266  KActionCollection *actionCollection,
267  const QString &path,
268  QObject *parent)
269  : QObject(parent)
270  , d(new ThemeManagerPrivate(applicationType, defaultDesktopFileName, actionCollection, path, this))
271 {
272 }
273 
274 ThemeManager::~ThemeManager() = default;
275 
276 QMap<QString, GrantleeTheme::Theme> ThemeManager::themes() const
277 {
278  return d->themes;
279 }
280 
281 void ThemeManager::setActionGroup(QActionGroup *actionGroup)
282 {
283  if (d->actionGroup != actionGroup) {
284  d->removeActions();
285  d->actionGroup = actionGroup;
286  d->updateActionList();
287  }
288 }
289 
290 KToggleAction *ThemeManager::actionForTheme()
291 {
292  return d->actionForTheme();
293 }
294 
295 void ThemeManager::setThemeMenu(KActionMenu *menu)
296 {
297  if (d->menu != menu) {
298  d->menu = menu;
299  d->updateActionList();
300  }
301 }
302 
303 QStringList ThemeManager::displayExtraVariables(const QString &themename) const
304 {
306  while (i.hasNext()) {
307  i.next();
308  if (i.value().dirName() == themename) {
309  return i.value().displayExtraVariables();
310  }
311  }
312  return {};
313 }
314 
315 GrantleeTheme::Theme ThemeManager::theme(const QString &themeName)
316 {
317  return d->themes.value(themeName);
318 }
319 
320 void ThemeManager::setDownloadNewStuffConfigFile(const QString &configFileName)
321 {
322  d->downloadThemesAction->setConfigFile(configFileName);
323 }
324 
325 QString ThemeManager::pathFromThemes(const QString &themesRelativePath, const QString &themeName, const QString &defaultDesktopFileName)
326 {
327  QStringList themesDirectories;
328  if (!themesRelativePath.isEmpty()) {
330  if (themesDirectories.count() < 2) {
331  // Make sure to add local directory
332  const QString localDirectory = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + themesRelativePath;
333  if (!themesDirectories.contains(localDirectory)) {
334  themesDirectories.append(localDirectory);
335  }
336  }
337  for (const QString &directory : std::as_const(themesDirectories)) {
339  while (dirIt.hasNext()) {
340  dirIt.next();
341  const QString dirName = dirIt.fileName();
342  GrantleeTheme::Theme theme = loadTheme(dirIt.filePath(), dirName, defaultDesktopFileName);
343  if (theme.isValid()) {
344  if (dirName == themeName) {
345  return theme.absolutePath();
346  }
347  }
348  }
349  }
350  }
351  return {};
352 }
353 
354 GrantleeTheme::Theme ThemeManager::loadTheme(const QString &themePath, const QString &dirName, const QString &defaultDesktopFileName)
355 {
356  const GrantleeTheme::Theme theme(themePath, dirName, defaultDesktopFileName);
357  return theme;
358 }
359 
360 QString ThemeManager::configuredThemeName() const
361 {
362  return configuredThemeName(d->applicationType);
363 }
364 
365 QString ThemeManager::configuredThemeName(const QString &themeType)
366 {
367  const KSharedConfig::Ptr config = KSharedConfig::openConfig();
368  const KConfigGroup grp = config->group(themeType);
369  return grp.readEntry(QStringLiteral("themeName"));
370 }
371 
372 #include "moc_grantleethememanager.cpp"
void append(const T &value)
QString readEntry(const char *key, const char *aDefault=nullptr) const
Type type(const QSqlDatabase &db)
int count(const T &value) const const
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
void clear()
QString writableLocation(QStandardPaths::StandardLocation type)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QString i18n(const char *text, const TYPE &arg...)
bool isEmpty() const const
The Theme class.
Definition: grantleetheme.h:26
KSharedConfigPtr config()
KCONFIGCORE_EXPORT bool authorize(const QString &action)
QVariant data() const const
void triggered(bool checked)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString & insert(int position, QChar ch)
void dirty(const QString &path)
QStringList locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
void setChecked(bool)
QString & append(QChar ch)
The ThemeManager class.
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Wed Sep 28 2022 04:06:59 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.