GrantleeTheme

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

KDE's Doxygen guidelines are available online.