Kstars

thememanager.cpp
1 /*
2  This file is a part of digiKam project
3  https://www.digikam.org
4 
5  SPDX-FileCopyrightText: 2006-2018 Gilles Caulier <caulier dot gilles at gmail dot com>
6 
7  SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #include "thememanager.h"
11 
12 #include "kstars.h"
13 #include "kstars_debug.h"
14 #include "Options.h"
15 #include "schememanager.h"
16 #include "widgets/dmsbox.h"
17 
18 #include <KLocalizedString>
19 #include <KActionCollection>
20 #include <KConfigGroup>
21 #include <KXmlGuiWindow>
22 
23 #include <QTabBar>
24 #include <QPixmapCache>
25 #include <QStringList>
26 #include <QStyleFactory>
27 #include <QFileInfo>
28 #include <QPalette>
29 #include <QColor>
30 #include <QActionGroup>
31 #include <QBitmap>
32 #include <QPainter>
33 #include <QPixmap>
34 #include <QApplication>
35 #include <QAction>
36 #include <QStandardPaths>
37 #include <QDirIterator>
38 #include <QMenu>
39 #include <QStyle>
40 #include <QResource>
41 
42 namespace KSTheme
43 {
44 
45 class ThemeManagerCreator
46 {
47  public:
48 
49  Manager object;
50 };
51 
52 Q_GLOBAL_STATIC(ThemeManagerCreator, creator)
53 
54 // ---------------------------------------------------------------
55 
56 
57 class Manager::Private
58 {
59  public:
60 
61  Private()
62  : defaultThemeName(i18nc("default theme name", "Default")),
63  themeMenuActionGroup(0),
64  themeMenuAction(0)
65  {
66  }
67 
68  const QString defaultThemeName;
69  QMap<QString, QString> themeMap; // map<theme name, theme config path>
70 
71  QActionGroup* themeMenuActionGroup;
72  QMenu* themeMenuAction;
73 };
74 
75 Manager::Manager()
76  : d(new Private)
77 {
78 }
79 
80 Manager::~Manager()
81 {
82  delete d;
83 }
84 
85 Manager* Manager::instance()
86 {
87  return &creator->object;
88 }
89 
90 QString Manager::defaultThemeName() const
91 {
92  return d->defaultThemeName;
93 }
94 
95 QString Manager::currentThemeName() const
96 {
97  if (!d->themeMenuAction || !d->themeMenuActionGroup)
98  {
99  return defaultThemeName();
100  }
101 
102  QAction* const action = d->themeMenuActionGroup->checkedAction();
103  return (!action ? defaultThemeName()
104  : action->text().remove(QLatin1Char('&')));
105 }
106 
107 void Manager::setCurrentTheme(const QString &name)
108 {
109  if (!d->themeMenuAction || !d->themeMenuActionGroup)
110  {
111  return;
112  }
113 
114  QList<QAction*> list = d->themeMenuActionGroup->actions();
115 
116  foreach(QAction* const action, list)
117  {
118  if (action->text().remove(QLatin1Char('&')) == name)
119  {
120  action->setChecked(true);
121  slotChangePalette();
122  }
123  }
124 }
125 
126 void Manager::slotChangePalette()
127 {
128  updateCurrentDesktopDefaultThemePreview();
129 
130  QString theme(currentThemeName());
131 
132  /*if (theme == defaultThemeName() || theme.isEmpty())
133  {
134  theme = currentDesktopdefaultTheme();
135  }*/
136 
137  //QString themeIconName("breeze-dark");
138  IconTheme themeIconType = BREEZE_DARK_THEME;
139 
140  if (theme == "Macintosh" || theme == "White Balance" || theme == "High Key" || (theme == "Default"
141  && currentDesktopdefaultTheme().contains("Dark") == false))
142  themeIconType = BREEZE_THEME;
143 
144  setIconTheme(themeIconType);
145 
146  QString filename = d->themeMap.value(theme);
147  KSharedConfigPtr config = KSharedConfig::openConfig(filename);
148  // hint for the style to synchronize the color scheme with the window manager/compositor
149  qApp->setProperty("KDE_COLOR_SCHEME_PATH", filename);
151 
152  qApp->setPalette(palette);
153 
154  QList<QWidget *> widgets = qApp->allWidgets();
155  foreach(QWidget *w, widgets)
156  {
157  dmsBox *box = qobject_cast<dmsBox *>(w);
158  if(box)
159  box->setPalette(palette);
160  QTabBar *bar = qobject_cast<QTabBar *>(w);
161  if(bar)
162  bar->setPalette(palette);
163  }
164 
165  if(theme == "Macintosh")
166  qApp->setStyle(QStyleFactory::create("macintosh"));
167  else
168  {
169  if (qgetenv("XDG_CURRENT_DESKTOP") != "KDE")
170  qApp->setStyle(QStyleFactory::create("Fusion"));
171  }
172 
173  // Special case. For Night Vision theme, we also change the Sky Color Scheme
174  if (theme == "Default" && Options::colorSchemeFile() == "night.colors")
175  KStars::Instance()->actionCollection()->action("cs_moonless-night")->setChecked(true);
176  else if (theme == "Night Vision")
177  KStars::Instance()->actionCollection()->action("cs_night")->setChecked(true);
178 
180 
181  qApp->style()->polish(qApp);
182 
183  emit signalThemeChanged();
184 }
185 
186 void Manager::setThemeMenuAction(QMenu* const action)
187 {
188  d->themeMenuAction = action;
189 
190  action->setStyleSheet("QMenu::icon:checked {background: gray;border: 1px inset gray;position: absolute;"
191  "top: 1px;right: 1px;bottom: 1px;left: 1px;}");
192 
193  populateThemeMenu();
194 }
195 
196 void Manager::registerThemeActions(KXmlGuiWindow* const win)
197 {
198  if (!win)
199  {
200  return;
201  }
202 
203  if (!d->themeMenuAction)
204  {
205  qCDebug(KSTARS) << "Cannot register theme actions to " << win->windowTitle();
206  return;
207  }
208 
209  win->actionCollection()->addAction(QLatin1String("themes"), d->themeMenuAction->menuAction());
210 }
211 
212 void Manager::populateThemeQListWidget(QListWidget *themeWidget)
213 {
214  themeWidget->clear();
215 
216  QList<QAction*> list = d->themeMenuActionGroup->actions();
217 
218  foreach(QAction* const action, list)
219  {
220  QListWidgetItem *item = new QListWidgetItem();
221  item->setText( action->text().remove('&') );
222  item->setIcon( action->icon() );
223  themeWidget->addItem(item);
224  }
225  themeWidget->sortItems();
226 }
227 
228 void Manager::populateThemeMenu()
229 {
230  if (!d->themeMenuAction)
231  {
232  return;
233  }
234 
235  //QString theme(currentThemeName());
236 
237  d->themeMenuAction->clear();
238  delete d->themeMenuActionGroup;
239 
240  d->themeMenuActionGroup = new QActionGroup(d->themeMenuAction);
241 
242  connect(d->themeMenuActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotChangePalette()));
243 
244  QAction* const action = new QAction(defaultThemeName(), d->themeMenuActionGroup);
245  action->setCheckable(true);
246  d->themeMenuAction->addAction(action);
247 
248  QStringList schemeFiles;
250 
251  // kstars themes
253  QString::fromLatin1("kstars/themes"),
255 
256  qCDebug(KSTARS) << "Paths to color scheme : " << dirs;
257 
258  Q_FOREACH (const QString &dir, dirs)
259  {
260  QDirIterator it(dir, QStringList() << QLatin1String("*.colors"));
261 
262  while (it.hasNext())
263  {
264  schemeFiles.append(it.next());
265  }
266  }
267 
268  QMap<QString, QAction*> actionMap;
269 
270  for (int i = 0; i < schemeFiles.size(); ++i)
271  {
272  const QString filename = schemeFiles.at(i);
273  const QFileInfo info(filename);
274  KSharedConfigPtr config = KSharedConfig::openConfig(filename);
275  QIcon icon = createSchemePreviewIcon(config);
276  KConfigGroup group(config, "General");
277  const QString name = group.readEntry("Name", info.baseName());
278  QAction* const ac = new QAction(name, d->themeMenuActionGroup);
279  d->themeMap.insert(name, filename);
280  ac->setIcon(icon);
281  ac->setCheckable(true);
282  actionMap.insert(name, ac);
283  }
284 
285 #ifdef Q_OS_MAC
286  QAction* const macAction = new QAction(QLatin1String("Macintosh"), d->themeMenuActionGroup);
287  macAction->setCheckable(true);
288  //TODO Set appropriate icon
289  //macAction->setAction(..)
290  actionMap.insert(QLatin1String("Macintosh"), macAction);
291 #endif
292 
293  // sort the list
294  QStringList actionMapKeys = actionMap.keys();
295  actionMapKeys.sort();
296 
297  foreach(const QString &name, actionMapKeys)
298  {
299  d->themeMenuAction->addAction(actionMap.value(name));
300  }
301 
302  updateCurrentDesktopDefaultThemePreview();
303  //setCurrentTheme(theme);
304 }
305 
306 void Manager::updateCurrentDesktopDefaultThemePreview()
307 {
308  QList<QAction*> list = d->themeMenuActionGroup->actions();
309 
310  foreach(QAction* const action, list)
311  {
312  if (action->text().remove(QLatin1Char('&')) == defaultThemeName())
313  {
314  KSharedConfigPtr config = KSharedConfig::openConfig(d->themeMap.value(currentDesktopdefaultTheme()));
315  QIcon icon = createSchemePreviewIcon(config);
316  action->setIcon(icon);
317  }
318  }
319 }
320 
321 QPixmap Manager::createSchemePreviewIcon(const KSharedConfigPtr &config) const
322 {
323  const uchar bits1[] = { 0xff, 0xff, 0xff, 0x2c, 0x16, 0x0b };
324  const uchar bits2[] = { 0x68, 0x34, 0x1a, 0xff, 0xff, 0xff };
325  const QSize bitsSize(24, 2);
326  const QBitmap b1 = QBitmap::fromData(bitsSize, bits1);
327  const QBitmap b2 = QBitmap::fromData(bitsSize, bits2);
328 
329  QPixmap pixmap(23, 16);
330  pixmap.fill(Qt::black); // FIXME use some color other than black for borders?
331 
332  KConfigGroup group(config, QLatin1String("WM"));
333  QPainter p(&pixmap);
335  p.fillRect(1, 1, 7, 7, windowScheme.background());
336  p.fillRect(2, 2, 5, 2, QBrush(windowScheme.foreground().color(), b1));
337 
339  p.fillRect(8, 1, 7, 7, buttonScheme.background());
340  p.fillRect(9, 2, 5, 2, QBrush(buttonScheme.foreground().color(), b1));
341 
342  p.fillRect(15, 1, 7, 7, group.readEntry(QLatin1String("activeBackground"), QColor(96, 148, 207)));
343  p.fillRect(16, 2, 5, 2, QBrush(group.readEntry(QLatin1String("activeForeground"), QColor(255, 255, 255)), b1));
344 
346  p.fillRect(1, 8, 7, 7, viewScheme.background());
347  p.fillRect(2, 12, 5, 2, QBrush(viewScheme.foreground().color(), b2));
348 
349  SchemeManager selectionScheme(QPalette::Active, SchemeManager::Selection, config);
350  p.fillRect(8, 8, 7, 7, selectionScheme.background());
351  p.fillRect(9, 12, 5, 2, QBrush(selectionScheme.foreground().color(), b2));
352 
353  p.fillRect(15, 8, 7, 7, group.readEntry(QLatin1String("inactiveBackground"), QColor(224, 223, 222)));
354  p.fillRect(16, 12, 5, 2, QBrush(group.readEntry(QLatin1String("inactiveForeground"), QColor(20, 19, 18)), b2));
355 
356  p.end();
357  return pixmap;
358 }
359 
360 QString Manager::currentDesktopdefaultTheme() const
361 {
362  KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("kdeglobals"));
363  KConfigGroup group(config, "General");
364  return group.readEntry("ColorScheme");
365 }
366 
367 void Manager::slotSettingsChanged()
368 {
369  populateThemeMenu();
370  slotChangePalette();
371 }
372 
373 void Manager::setIconTheme(IconTheme theme)
374 {
375  QString rccFile("breeze-icons.rcc");
376  QString iconTheme("breeze");
377 
378 #if QT_VERSION < 0x051200
379  qCWarning(KSTARS) << "Current icon theme is" << QIcon::themeName();
380 #else
381  qCWarning(KSTARS) << "Current icon theme is" << QIcon::themeName() << "(fallback" << QIcon::fallbackThemeName() << ")";
382 #endif
383 
384  if (theme == BREEZE_DARK_THEME)
385  {
386  rccFile = "breeze-icons-dark.rcc";
387  iconTheme = "breeze-dark";
388  }
389 
390  QStringList themeSearchPaths = (QStringList() << QIcon::themeSearchPaths());
391 #ifdef Q_OS_OSX
392  themeSearchPaths = themeSearchPaths << QDir(QCoreApplication::applicationDirPath() + "/../Resources/icons").absolutePath();
393  QString resourcePath = QDir(QCoreApplication::applicationDirPath() + "/../Resources/icons/" + rccFile).absolutePath();
394  QResource::registerResource(resourcePath, "/icons/" + iconTheme);
395 #elif defined(Q_OS_WIN)
396  themeSearchPaths = themeSearchPaths << QStandardPaths::locate(QStandardPaths::GenericDataLocation, "icons",
399  QStandardPaths::LocateDirectory) + QDir::separator() + iconTheme + QDir::separator() + rccFile;
400  QResource::registerResource(resourcePath, "/icons/" + iconTheme);
401 #else
402  // On Linux on non-KDE Distros, find out if the themes are installed or not and perhaps warn the user
403  // The important point is that the current theme must be left as is to avoid empty icons
404  {
405  bool missing = true;
406  foreach (auto &path, themeSearchPaths)
407  if (QFile(path + '/' + iconTheme + "/index.theme").exists())
408  missing = false;
409 
410  if (missing)
411  {
412  qCWarning(KSTARS) << "Warning: icon theme" << iconTheme << "not found, keeping original" << QIcon::themeName() << ".";
413  iconTheme = QIcon::themeName();
414  }
415  }
416 #endif
417 
418  QIcon::setThemeSearchPaths(themeSearchPaths);
419  //Note: in order to get it to actually load breeze from resources on mac, we had to add the index.theme, and just one icon from breeze into the qrc. Not sure why this was needed, but it works.
420  QIcon::setThemeName(iconTheme);
421 }
422 
423 }
void append(const T &value)
bool registerResource(const QString &rccFileName, const QString &mapRoot)
QAction * addAction(const QString &name, const QObject *receiver=nullptr, const char *member=nullptr)
QAction * action(const QString &name) const
@ Button
Buttons and button-like controls.
Definition: schememanager.h:84
void setPalette(const QPalette &)
const T value(const Key &key, const T &defaultValue) const const
QChar separator()
QString applicationDirPath()
QString fallbackThemeName()
void setStyleSheet(const QString &styleSheet)
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
Q_GLOBAL_STATIC(Internal::StaticControl, s_instance) class ControlPrivate
static KStars * Instance()
Definition: kstars.h:125
void clear()
QMap::iterator insert(const Key &key, const T &value)
void setThemeSearchPaths(const QStringList &paths)
int size() const const
void setIcon(const QIcon &icon)
void setThemeName(const QString &name)
@ View
Views; for example, frames, input fields, etc.
Definition: schememanager.h:70
QString themeName()
const T & at(int i) const const
void sortItems(Qt::SortOrder order)
KSharedConfigPtr config()
QString absolutePath() const const
QBitmap fromData(const QSize &size, const uchar *bits, QImage::Format monoFormat)
void setCheckable(bool)
@ Window
Non-editable window elements; for example, menus.
Definition: schememanager.h:76
static QPalette createApplicationPalette(const KSharedConfigPtr &config)
Used to obtain the QPalette that will be used to set the application palette from KDE Platform theme.
Primary class to handle all Ekos modules. The Ekos Manager class manages startup and shutdown of INDI...
Definition: manager.h:52
@ Selection
Selected items in views.
Definition: schememanager.h:92
virtual KActionCollection * actionCollection() const
QList< Key > keys() const const
QStyle * create(const QString &key)
QString fromLatin1(const char *str, int size)
QString name(StandardShortcut id)
KStandardDirs * dirs()
void addItem(const QString &label)
QStringList themeSearchPaths()
QStringList locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
Definition: dmsbox.h:33
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setText(const QString &text)
void setChecked(bool)
A set of methods used to work with colors.
Definition: schememanager.h:52
void sort(Qt::CaseSensitivity cs)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Mon Aug 8 2022 04:13:26 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.