File indexing completed on 2024-04-14 03:41:14
0001 /* 0002 This file is a part of digiKam project 0003 https://www.digikam.org 0004 0005 SPDX-FileCopyrightText: 2006-2018 Gilles Caulier <caulier dot gilles at gmail dot com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "thememanager.h" 0011 0012 #include "kstars.h" 0013 #include "kstars_debug.h" 0014 #include "Options.h" 0015 #include "schememanager.h" 0016 #include "widgets/dmsbox.h" 0017 0018 #include <KLocalizedString> 0019 #include <KActionCollection> 0020 #include <KConfigGroup> 0021 #include <KXmlGuiWindow> 0022 0023 #include <QTabBar> 0024 #include <QPixmapCache> 0025 #include <QStringList> 0026 #include <QStyleFactory> 0027 #include <QFileInfo> 0028 #include <QPalette> 0029 #include <QColor> 0030 #include <QActionGroup> 0031 #include <QBitmap> 0032 #include <QPainter> 0033 #include <QPixmap> 0034 #include <QApplication> 0035 #include <QAction> 0036 #include <QStandardPaths> 0037 #include <QDirIterator> 0038 #include <QMenu> 0039 #include <QStyle> 0040 #include <QResource> 0041 0042 namespace KSTheme 0043 { 0044 0045 class ThemeManagerCreator 0046 { 0047 public: 0048 0049 Manager object; 0050 }; 0051 0052 Q_GLOBAL_STATIC(ThemeManagerCreator, creator) 0053 0054 // --------------------------------------------------------------- 0055 0056 0057 class Manager::Private 0058 { 0059 public: 0060 0061 Private() 0062 : defaultThemeName(i18nc("default theme name", "Default")), 0063 themeMenuActionGroup(0), 0064 themeMenuAction(0) 0065 { 0066 } 0067 0068 const QString defaultThemeName; 0069 QMap<QString, QString> themeMap; // map<theme name, theme config path> 0070 0071 QActionGroup* themeMenuActionGroup; 0072 QMenu* themeMenuAction; 0073 }; 0074 0075 Manager::Manager() 0076 : d(new Private) 0077 { 0078 } 0079 0080 Manager::~Manager() 0081 { 0082 delete d; 0083 } 0084 0085 Manager* Manager::instance() 0086 { 0087 return &creator->object; 0088 } 0089 0090 QString Manager::defaultThemeName() const 0091 { 0092 return d->defaultThemeName; 0093 } 0094 0095 QString Manager::currentThemeName() const 0096 { 0097 if (!d->themeMenuAction || !d->themeMenuActionGroup) 0098 { 0099 return defaultThemeName(); 0100 } 0101 0102 QAction* const action = d->themeMenuActionGroup->checkedAction(); 0103 return (!action ? defaultThemeName() 0104 : action->text().remove(QLatin1Char('&'))); 0105 } 0106 0107 void Manager::setCurrentTheme(const QString &name) 0108 { 0109 if (!d->themeMenuAction || !d->themeMenuActionGroup) 0110 { 0111 return; 0112 } 0113 0114 QList<QAction*> list = d->themeMenuActionGroup->actions(); 0115 0116 foreach(QAction* const action, list) 0117 { 0118 if (action->text().remove(QLatin1Char('&')) == name) 0119 { 0120 action->setChecked(true); 0121 slotChangePalette(); 0122 } 0123 } 0124 } 0125 0126 void Manager::slotChangePalette() 0127 { 0128 updateCurrentDesktopDefaultThemePreview(); 0129 0130 QString theme(currentThemeName()); 0131 0132 /*if (theme == defaultThemeName() || theme.isEmpty()) 0133 { 0134 theme = currentDesktopdefaultTheme(); 0135 }*/ 0136 0137 //QString themeIconName("breeze-dark"); 0138 IconTheme themeIconType = BREEZE_DARK_THEME; 0139 0140 if (theme == "Macintosh" || theme == "White Balance" || theme == "High Key" || (theme == "Default" 0141 && currentDesktopdefaultTheme().contains("Dark") == false)) 0142 themeIconType = BREEZE_THEME; 0143 0144 setIconTheme(themeIconType); 0145 0146 QString filename = d->themeMap.value(theme); 0147 KSharedConfigPtr config = KSharedConfig::openConfig(filename); 0148 // hint for the style to synchronize the color scheme with the window manager/compositor 0149 qApp->setProperty("KDE_COLOR_SCHEME_PATH", filename); 0150 QPalette palette = SchemeManager::createApplicationPalette(config); 0151 0152 qApp->setPalette(palette); 0153 0154 QList<QWidget *> widgets = qApp->allWidgets(); 0155 foreach(QWidget *w, widgets) 0156 { 0157 dmsBox *box = qobject_cast<dmsBox *>(w); 0158 if(box) 0159 box->setPalette(palette); 0160 QTabBar *bar = qobject_cast<QTabBar *>(w); 0161 if(bar) 0162 bar->setPalette(palette); 0163 } 0164 0165 if(theme == "Macintosh") 0166 qApp->setStyle(QStyleFactory::create("macintosh")); 0167 else 0168 { 0169 if (qgetenv("XDG_CURRENT_DESKTOP") != "KDE") 0170 qApp->setStyle(QStyleFactory::create("Fusion")); 0171 } 0172 0173 // Special case. For Night Vision theme, we also change the Sky Color Scheme 0174 if (theme == "Default" && Options::colorSchemeFile() == "night.colors") 0175 KStars::Instance()->actionCollection()->action("cs_moonless-night")->setChecked(true); 0176 else if (theme == "Night Vision") 0177 KStars::Instance()->actionCollection()->action("cs_night")->setChecked(true); 0178 0179 QPixmapCache::clear(); 0180 0181 qApp->style()->polish(qApp); 0182 0183 emit signalThemeChanged(); 0184 } 0185 0186 void Manager::setThemeMenuAction(QMenu* const action) 0187 { 0188 d->themeMenuAction = action; 0189 0190 action->setStyleSheet("QMenu::icon:checked {background: gray;border: 1px inset gray;position: absolute;" 0191 "top: 1px;right: 1px;bottom: 1px;left: 1px;}"); 0192 0193 populateThemeMenu(); 0194 } 0195 0196 void Manager::registerThemeActions(KXmlGuiWindow* const win) 0197 { 0198 if (!win) 0199 { 0200 return; 0201 } 0202 0203 if (!d->themeMenuAction) 0204 { 0205 qCDebug(KSTARS) << "Cannot register theme actions to " << win->windowTitle(); 0206 return; 0207 } 0208 0209 win->actionCollection()->addAction(QLatin1String("themes"), d->themeMenuAction->menuAction()); 0210 } 0211 0212 void Manager::populateThemeQListWidget(QListWidget *themeWidget) 0213 { 0214 themeWidget->clear(); 0215 0216 QList<QAction*> list = d->themeMenuActionGroup->actions(); 0217 0218 foreach(QAction* const action, list) 0219 { 0220 QListWidgetItem *item = new QListWidgetItem(); 0221 item->setText( action->text().remove('&') ); 0222 item->setIcon( action->icon() ); 0223 themeWidget->addItem(item); 0224 } 0225 themeWidget->sortItems(); 0226 } 0227 0228 void Manager::populateThemeMenu() 0229 { 0230 if (!d->themeMenuAction) 0231 { 0232 return; 0233 } 0234 0235 //QString theme(currentThemeName()); 0236 0237 d->themeMenuAction->clear(); 0238 delete d->themeMenuActionGroup; 0239 0240 d->themeMenuActionGroup = new QActionGroup(d->themeMenuAction); 0241 0242 connect(d->themeMenuActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotChangePalette())); 0243 0244 QAction* const action = new QAction(defaultThemeName(), d->themeMenuActionGroup); 0245 action->setCheckable(true); 0246 d->themeMenuAction->addAction(action); 0247 0248 QStringList schemeFiles; 0249 QStringList dirs; 0250 0251 // kstars themes 0252 dirs << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, 0253 QString::fromLatin1("kstars/themes"), 0254 QStandardPaths::LocateDirectory); 0255 0256 qCDebug(KSTARS) << "Paths to color scheme : " << dirs; 0257 0258 Q_FOREACH (const QString &dir, dirs) 0259 { 0260 QDirIterator it(dir, QStringList() << QLatin1String("*.colors")); 0261 0262 while (it.hasNext()) 0263 { 0264 schemeFiles.append(it.next()); 0265 } 0266 } 0267 0268 QMap<QString, QAction*> actionMap; 0269 0270 for (int i = 0; i < schemeFiles.size(); ++i) 0271 { 0272 const QString filename = schemeFiles.at(i); 0273 const QFileInfo info(filename); 0274 KSharedConfigPtr config = KSharedConfig::openConfig(filename); 0275 QIcon icon = createSchemePreviewIcon(config); 0276 KConfigGroup group(config, "General"); 0277 const QString name = group.readEntry("Name", info.baseName()); 0278 QAction* const ac = new QAction(name, d->themeMenuActionGroup); 0279 d->themeMap.insert(name, filename); 0280 ac->setIcon(icon); 0281 ac->setCheckable(true); 0282 actionMap.insert(name, ac); 0283 } 0284 0285 #ifdef Q_OS_MAC 0286 QAction* const macAction = new QAction(QLatin1String("Macintosh"), d->themeMenuActionGroup); 0287 macAction->setCheckable(true); 0288 //TODO Set appropriate icon 0289 //macAction->setAction(..) 0290 actionMap.insert(QLatin1String("Macintosh"), macAction); 0291 #endif 0292 0293 // sort the list 0294 QStringList actionMapKeys = actionMap.keys(); 0295 actionMapKeys.sort(); 0296 0297 foreach(const QString &name, actionMapKeys) 0298 { 0299 d->themeMenuAction->addAction(actionMap.value(name)); 0300 } 0301 0302 updateCurrentDesktopDefaultThemePreview(); 0303 //setCurrentTheme(theme); 0304 } 0305 0306 void Manager::updateCurrentDesktopDefaultThemePreview() 0307 { 0308 QList<QAction*> list = d->themeMenuActionGroup->actions(); 0309 0310 foreach(QAction* const action, list) 0311 { 0312 if (action->text().remove(QLatin1Char('&')) == defaultThemeName()) 0313 { 0314 KSharedConfigPtr config = KSharedConfig::openConfig(d->themeMap.value(currentDesktopdefaultTheme())); 0315 QIcon icon = createSchemePreviewIcon(config); 0316 action->setIcon(icon); 0317 } 0318 } 0319 } 0320 0321 QPixmap Manager::createSchemePreviewIcon(const KSharedConfigPtr &config) const 0322 { 0323 const uchar bits1[] = { 0xff, 0xff, 0xff, 0x2c, 0x16, 0x0b }; 0324 const uchar bits2[] = { 0x68, 0x34, 0x1a, 0xff, 0xff, 0xff }; 0325 const QSize bitsSize(24, 2); 0326 const QBitmap b1 = QBitmap::fromData(bitsSize, bits1); 0327 const QBitmap b2 = QBitmap::fromData(bitsSize, bits2); 0328 0329 QPixmap pixmap(23, 16); 0330 pixmap.fill(Qt::black); // FIXME use some color other than black for borders? 0331 0332 KConfigGroup group(config, QLatin1String("WM")); 0333 QPainter p(&pixmap); 0334 SchemeManager windowScheme(QPalette::Active, SchemeManager::Window, config); 0335 p.fillRect(1, 1, 7, 7, windowScheme.background()); 0336 p.fillRect(2, 2, 5, 2, QBrush(windowScheme.foreground().color(), b1)); 0337 0338 SchemeManager buttonScheme(QPalette::Active, SchemeManager::Button, config); 0339 p.fillRect(8, 1, 7, 7, buttonScheme.background()); 0340 p.fillRect(9, 2, 5, 2, QBrush(buttonScheme.foreground().color(), b1)); 0341 0342 p.fillRect(15, 1, 7, 7, group.readEntry(QLatin1String("activeBackground"), QColor(96, 148, 207))); 0343 p.fillRect(16, 2, 5, 2, QBrush(group.readEntry(QLatin1String("activeForeground"), QColor(255, 255, 255)), b1)); 0344 0345 SchemeManager viewScheme(QPalette::Active, SchemeManager::View, config); 0346 p.fillRect(1, 8, 7, 7, viewScheme.background()); 0347 p.fillRect(2, 12, 5, 2, QBrush(viewScheme.foreground().color(), b2)); 0348 0349 SchemeManager selectionScheme(QPalette::Active, SchemeManager::Selection, config); 0350 p.fillRect(8, 8, 7, 7, selectionScheme.background()); 0351 p.fillRect(9, 12, 5, 2, QBrush(selectionScheme.foreground().color(), b2)); 0352 0353 p.fillRect(15, 8, 7, 7, group.readEntry(QLatin1String("inactiveBackground"), QColor(224, 223, 222))); 0354 p.fillRect(16, 12, 5, 2, QBrush(group.readEntry(QLatin1String("inactiveForeground"), QColor(20, 19, 18)), b2)); 0355 0356 p.end(); 0357 return pixmap; 0358 } 0359 0360 QString Manager::currentDesktopdefaultTheme() const 0361 { 0362 KSharedConfigPtr config = KSharedConfig::openConfig(QLatin1String("kdeglobals")); 0363 KConfigGroup group(config, "General"); 0364 return group.readEntry("ColorScheme"); 0365 } 0366 0367 void Manager::slotSettingsChanged() 0368 { 0369 populateThemeMenu(); 0370 slotChangePalette(); 0371 } 0372 0373 void Manager::setIconTheme(IconTheme theme) 0374 { 0375 QString rccFile("breeze-icons.rcc"); 0376 QString iconTheme("breeze"); 0377 0378 #if QT_VERSION < 0x051200 0379 qCWarning(KSTARS) << "Current icon theme is" << QIcon::themeName(); 0380 #else 0381 qCWarning(KSTARS) << "Current icon theme is" << QIcon::themeName() << "(fallback" << QIcon::fallbackThemeName() << ")"; 0382 #endif 0383 0384 if (theme == BREEZE_DARK_THEME) 0385 { 0386 rccFile = "breeze-icons-dark.rcc"; 0387 iconTheme = "breeze-dark"; 0388 } 0389 0390 QStringList themeSearchPaths = (QStringList() << QIcon::themeSearchPaths()); 0391 #ifdef Q_OS_OSX 0392 themeSearchPaths = themeSearchPaths << QDir(QCoreApplication::applicationDirPath() + "/../Resources/icons").absolutePath(); 0393 QString resourcePath = QDir(QCoreApplication::applicationDirPath() + "/../Resources/icons/" + rccFile).absolutePath(); 0394 QResource::registerResource(resourcePath, "/icons/" + iconTheme); 0395 #elif defined(Q_OS_WIN) 0396 themeSearchPaths = themeSearchPaths << QStandardPaths::locate(QStandardPaths::GenericDataLocation, "icons", 0397 QStandardPaths::LocateDirectory); 0398 QString resourcePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "icons", 0399 QStandardPaths::LocateDirectory) + QDir::separator() + iconTheme + QDir::separator() + rccFile; 0400 QResource::registerResource(resourcePath, "/icons/" + iconTheme); 0401 #else 0402 // On Linux on non-KDE Distros, find out if the themes are installed or not and perhaps warn the user 0403 // The important point is that the current theme must be left as is to avoid empty icons 0404 { 0405 bool missing = true; 0406 foreach (auto &path, themeSearchPaths) 0407 if (QFile(path + '/' + iconTheme + "/index.theme").exists()) 0408 missing = false; 0409 0410 if (missing) 0411 { 0412 qCWarning(KSTARS) << "Warning: icon theme" << iconTheme << "not found, keeping original" << QIcon::themeName() << "."; 0413 iconTheme = QIcon::themeName(); 0414 } 0415 } 0416 #endif 0417 0418 QIcon::setThemeSearchPaths(themeSearchPaths); 0419 //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. 0420 QIcon::setThemeName(iconTheme); 0421 } 0422 0423 }