File indexing completed on 2024-05-12 16:01:59
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2004-08-02 0007 * Description : theme manager 0008 * 0009 * SPDX-FileCopyrightText: 2006-2011 Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "thememanager.h" 0016 // Qt includes 0017 0018 #include <QStringList> 0019 #include <QFileInfo> 0020 #include <QFile> 0021 #include <QApplication> 0022 #include <QPalette> 0023 #include <QColor> 0024 #include <QActionGroup> 0025 #include <QBitmap> 0026 #include <QPainter> 0027 #include <QPixmap> 0028 #include <QDate> 0029 #include <QDesktopWidget> 0030 #include <QMenu> 0031 #include <QMenuBar> 0032 #include <QStatusBar> 0033 #include <QDebug> 0034 #include <QAction> 0035 #include <QMessageBox> 0036 0037 // KDE includes 0038 0039 #include <klocalizedstring.h> 0040 #include <kcolorscheme.h> 0041 #include <kactioncollection.h> 0042 #include <KoResourcePaths.h> 0043 #include <kactionmenu.h> 0044 #include <kconfig.h> 0045 #include <kconfiggroup.h> 0046 0047 // Calligra 0048 #include <kis_icon.h> 0049 0050 #ifdef __APPLE__ 0051 #include <QStyle> 0052 #endif 0053 0054 namespace Digikam 0055 { 0056 0057 // --------------------------------------------------------------- 0058 0059 0060 class ThemeManager::ThemeManagerPriv 0061 { 0062 public: 0063 0064 ThemeManagerPriv() 0065 : themeMenuActionGroup(0) 0066 , themeMenuAction(0) 0067 { 0068 } 0069 0070 QString currentThemeName; 0071 QMap<QString, QString> themeMap; // map<theme name, theme config path> 0072 0073 QActionGroup* themeMenuActionGroup; 0074 KActionMenu* themeMenuAction; 0075 }; 0076 0077 ThemeManager::ThemeManager(const QString &theme, QObject *parent) 0078 : QObject(parent) 0079 , d(new ThemeManagerPriv) 0080 { 0081 //qDebug() << "Creating theme manager with theme" << theme; 0082 d->currentThemeName = theme; 0083 populateThemeMap(); 0084 } 0085 0086 ThemeManager::~ThemeManager() 0087 { 0088 delete d; 0089 } 0090 0091 QString ThemeManager::currentThemeName() const 0092 { 0093 //qDebug() << "getting current themename"; 0094 QString themeName; 0095 if (d->themeMenuAction && d->themeMenuActionGroup) { 0096 0097 QAction* action = d->themeMenuActionGroup->checkedAction(); 0098 if (action) { 0099 themeName = action->text().remove('&'); 0100 } 0101 0102 //qDebug() << "\tthemename from action" << themeName; 0103 } 0104 else if (!d->currentThemeName.isEmpty()) { 0105 0106 //qDebug() << "\tcurrent themename" << d->currentThemeName; 0107 themeName = d->currentThemeName; 0108 } 0109 if (themeName.isEmpty()) { 0110 //qDebug() << "\tfallback"; 0111 themeName = "Krita dark"; 0112 } 0113 //qDebug() << "\tresult" << themeName; 0114 return themeName; 0115 } 0116 0117 void ThemeManager::setCurrentTheme(const QString& name) 0118 { 0119 //qDebug() << "setCurrentTheme();" << d->currentThemeName << "to" << name; 0120 d->currentThemeName = name; 0121 0122 if (d->themeMenuAction && d->themeMenuActionGroup) { 0123 QList<QAction*> list = d->themeMenuActionGroup->actions(); 0124 Q_FOREACH (QAction* action, list) { 0125 if (action->text().remove('&') == name) { 0126 action->setChecked(true); 0127 } 0128 } 0129 } 0130 slotChangePalette(); 0131 } 0132 0133 void ThemeManager::slotChangePalette() 0134 { 0135 //qDebug() << "slotChangePalette" << sender(); 0136 0137 // We must clear the icon cache before the palette is changed. That way 0138 // The widgets can change the icons properly when they receive the 0139 // PaletteChange event if needed. 0140 KisIconUtils::clearIconCache(); 0141 0142 QString theme(currentThemeName()); 0143 QString filename = d->themeMap.value(theme); 0144 KSharedConfigPtr config = KSharedConfig::openConfig(filename); 0145 0146 QPalette palette = qApp->palette(); 0147 QPalette::ColorGroup states[3] = { QPalette::Active, QPalette::Inactive, QPalette::Disabled }; 0148 // TT thinks tooltips shouldn't use active, so we use our active colors for all states 0149 KColorScheme schemeTooltip(QPalette::Active, KColorScheme::Tooltip, config); 0150 0151 for ( int i = 0; i < 3 ; ++i ) { 0152 0153 QPalette::ColorGroup state = states[i]; 0154 KColorScheme schemeView(state, KColorScheme::View, config); 0155 KColorScheme schemeWindow(state, KColorScheme::Window, config); 0156 KColorScheme schemeButton(state, KColorScheme::Button, config); 0157 KColorScheme schemeSelection(state, KColorScheme::Selection, config); 0158 0159 palette.setBrush(state, QPalette::WindowText, schemeWindow.foreground()); 0160 palette.setBrush(state, QPalette::Window, schemeWindow.background()); 0161 palette.setBrush(state, QPalette::Base, schemeView.background()); 0162 palette.setBrush(state, QPalette::Text, schemeView.foreground()); 0163 palette.setBrush(state, QPalette::Button, schemeButton.background()); 0164 palette.setBrush(state, QPalette::ButtonText, schemeButton.foreground()); 0165 palette.setBrush(state, QPalette::Highlight, schemeSelection.background()); 0166 palette.setBrush(state, QPalette::HighlightedText, schemeSelection.foreground()); 0167 palette.setBrush(state, QPalette::ToolTipBase, schemeTooltip.background()); 0168 palette.setBrush(state, QPalette::ToolTipText, schemeTooltip.foreground()); 0169 0170 palette.setColor(state, QPalette::Light, schemeWindow.shade(KColorScheme::LightShade)); 0171 palette.setColor(state, QPalette::Midlight, schemeWindow.shade(KColorScheme::MidlightShade)); 0172 palette.setColor(state, QPalette::Mid, schemeWindow.shade(KColorScheme::MidShade)); 0173 palette.setColor(state, QPalette::Dark, schemeWindow.shade(KColorScheme::DarkShade)); 0174 palette.setColor(state, QPalette::Shadow, schemeWindow.shade(KColorScheme::ShadowShade)); 0175 0176 palette.setBrush(state, QPalette::AlternateBase, schemeView.background(KColorScheme::AlternateBackground)); 0177 palette.setBrush(state, QPalette::Link, schemeView.foreground(KColorScheme::LinkText)); 0178 palette.setBrush(state, QPalette::LinkVisited, schemeView.foreground(KColorScheme::VisitedText)); 0179 } 0180 0181 //qDebug() << ">>>>>>>>>>>>>>>>>> going to set palette on app" << theme; 0182 // hint for the style to synchronize the color scheme with the window manager/compositor 0183 qApp->setProperty("KDE_COLOR_SCHEME_PATH", filename); 0184 qApp->setPalette(palette); 0185 0186 #ifdef Q_OS_MACOS 0187 if (theme == "Krita bright" || theme.isEmpty()) { 0188 qApp->setStyle("Macintosh"); 0189 qApp->style()->polish(qApp); 0190 } else { 0191 qApp->setStyle("Fusion"); 0192 qApp->style()->polish(qApp); 0193 } 0194 #endif 0195 0196 emit signalThemeChanged(); 0197 } 0198 0199 void ThemeManager::setThemeMenuAction(KActionMenu* const action) 0200 { 0201 d->themeMenuAction = action; 0202 populateThemeMenu(); 0203 } 0204 0205 void ThemeManager::registerThemeActions(KisKActionCollection *actionCollection) 0206 { 0207 if (!d->themeMenuAction) return; 0208 actionCollection->addAction("theme_menu", d->themeMenuAction); 0209 } 0210 0211 void ThemeManager::populateThemeMenu() 0212 { 0213 if (!d->themeMenuAction) return; 0214 0215 d->themeMenuAction->menu()->clear(); 0216 delete d->themeMenuActionGroup; 0217 0218 d->themeMenuActionGroup = new QActionGroup(d->themeMenuAction); 0219 connect(d->themeMenuActionGroup, SIGNAL(triggered(QAction*)), 0220 this, SLOT(slotChangePalette())); 0221 0222 QAction * action; 0223 QStringList schemeFiles = KoResourcePaths::findAllAssets("data", "color-schemes/*.colors"); 0224 schemeFiles += KoResourcePaths::findAllAssets("genericdata", "color-schemes/*.colors"); 0225 0226 QMap<QString, QAction*> actionMap; 0227 for (int i = 0; i < schemeFiles.size(); ++i) { 0228 const QString filename = schemeFiles.at(i); 0229 const QFileInfo info(filename); 0230 KSharedConfigPtr config = KSharedConfig::openConfig(filename); 0231 QIcon icon = createSchemePreviewIcon(config); 0232 KConfigGroup group(config, "General"); 0233 const QString name = group.readEntry("Name", info.completeBaseName()); 0234 action = new QAction(name, d->themeMenuActionGroup); 0235 action->setIcon(icon); 0236 action->setCheckable(true); 0237 actionMap.insert(name, action); 0238 } 0239 0240 // sort the list 0241 QStringList actionMapKeys = actionMap.keys(); 0242 actionMapKeys.sort(); 0243 0244 Q_FOREACH (const QString& name, actionMapKeys) { 0245 if ( name == currentThemeName()) { 0246 actionMap.value(name)->setChecked(true); 0247 } 0248 d->themeMenuAction->addAction(actionMap.value(name)); 0249 } 0250 } 0251 0252 QPixmap ThemeManager::createSchemePreviewIcon(const KSharedConfigPtr& config) 0253 { 0254 // code taken from kdebase/workspace/kcontrol/colors/colorscm.cpp 0255 const uchar bits1[] = { 0xff, 0xff, 0xff, 0x2c, 0x16, 0x0b }; 0256 const uchar bits2[] = { 0x68, 0x34, 0x1a, 0xff, 0xff, 0xff }; 0257 const QSize bitsSize(24, 2); 0258 const QBitmap b1 = QBitmap::fromData(bitsSize, bits1); 0259 const QBitmap b2 = QBitmap::fromData(bitsSize, bits2); 0260 0261 QPixmap pixmap(23, 16); 0262 pixmap.fill(Qt::black); // FIXME use some color other than black for borders? 0263 0264 KConfigGroup group(config, "WM"); 0265 QPainter p(&pixmap); 0266 KColorScheme windowScheme(QPalette::Active, KColorScheme::Window, config); 0267 p.fillRect(1, 1, 7, 7, windowScheme.background()); 0268 p.fillRect(2, 2, 5, 2, QBrush(windowScheme.foreground().color(), b1)); 0269 0270 KColorScheme buttonScheme(QPalette::Active, KColorScheme::Button, config); 0271 p.fillRect(8, 1, 7, 7, buttonScheme.background()); 0272 p.fillRect(9, 2, 5, 2, QBrush(buttonScheme.foreground().color(), b1)); 0273 0274 p.fillRect(15, 1, 7, 7, group.readEntry("activeBackground", QColor(96, 148, 207))); 0275 p.fillRect(16, 2, 5, 2, QBrush(group.readEntry("activeForeground", QColor(255, 255, 255)), b1)); 0276 0277 KColorScheme viewScheme(QPalette::Active, KColorScheme::View, config); 0278 p.fillRect(1, 8, 7, 7, viewScheme.background()); 0279 p.fillRect(2, 12, 5, 2, QBrush(viewScheme.foreground().color(), b2)); 0280 0281 KColorScheme selectionScheme(QPalette::Active, KColorScheme::Selection, config); 0282 p.fillRect(8, 8, 7, 7, selectionScheme.background()); 0283 p.fillRect(9, 12, 5, 2, QBrush(selectionScheme.foreground().color(), b2)); 0284 0285 p.fillRect(15, 8, 7, 7, group.readEntry("inactiveBackground", QColor(224, 223, 222))); 0286 p.fillRect(16, 12, 5, 2, QBrush(group.readEntry("inactiveForeground", QColor(20, 19, 18)), b2)); 0287 0288 p.end(); 0289 return pixmap; 0290 } 0291 0292 void ThemeManager::populateThemeMap() 0293 { 0294 QStringList schemeFiles = KoResourcePaths::findAllAssets("data", "color-schemes/*.colors"); 0295 schemeFiles += KoResourcePaths::findAllAssets("genericdata", "color-schemes/*.colors"); 0296 0297 for (int i = 0; i < schemeFiles.size(); ++i) { 0298 const QString filename = schemeFiles.at(i); 0299 const QFileInfo info(filename); 0300 KSharedConfigPtr config = KSharedConfig::openConfig(filename); 0301 KConfigGroup group(config, "General"); 0302 const QString name = group.readEntry("Name", info.completeBaseName()); 0303 d->themeMap.insert(name, filename); 0304 } 0305 0306 0307 } 0308 0309 } // namespace Digikam