File indexing completed on 2024-04-21 16:17:10

0001 /*
0002  * Copyright 2018  Michail Vourlakos <mvourlakos@gmail.com>
0003  *
0004  * This file is part of Latte-Dock
0005  *
0006  * Latte-Dock is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU General Public License as
0008  * published by the Free Software Foundation; either version 2 of
0009  * the License, or (at your option) any later version.
0010  *
0011  * Latte-Dock is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License
0017  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0018  *
0019  */
0020 
0021 #include "theme.h"
0022 
0023 // local
0024 #include "lattecorona.h"
0025 #include "../../layouts/importer.h"
0026 #include "../../view/panelshadows_p.h"
0027 #include "../../wm/schemecolors.h"
0028 #include "../../../liblatte2/commontools.h"
0029 
0030 // Qt
0031 #include <QDebug>
0032 #include <QDir>
0033 #include <QProcess>
0034 
0035 // KDE
0036 #include <KDirWatch>
0037 #include <KConfigGroup>
0038 #include <KSharedConfig>
0039 
0040 // X11
0041 #include <KWindowSystem>
0042 
0043 #define DEFAULTCOLORSCHEME "default.colors"
0044 #define REVERSEDCOLORSCHEME "reversed.colors"
0045 
0046 namespace Latte {
0047 namespace PlasmaExtended {
0048 
0049 Theme::Theme(KSharedConfig::Ptr config, QObject *parent) :
0050     QObject(parent),
0051     m_themeGroup(KConfigGroup(config, QStringLiteral("PlasmaThemeExtended")))
0052 {
0053     m_corona = qobject_cast<Latte::Corona *>(parent);
0054 
0055     //! compositing tracking
0056     if (KWindowSystem::isPlatformWayland()) {
0057         //! TODO: Wayland compositing active
0058         m_compositing = true;
0059     } else {
0060         connect(KWindowSystem::self(), &KWindowSystem::compositingChanged
0061                 , this, [&](bool enabled) {
0062             if (m_compositing == enabled)
0063                 return;
0064 
0065             m_compositing = enabled;
0066             emit compositingChanged();
0067         });
0068 
0069         m_compositing = KWindowSystem::compositingActive();
0070     }
0071     //!
0072 
0073     loadConfig();
0074 
0075     connect(this, &Theme::compositingChanged, this, &Theme::roundnessChanged);
0076     connect(this, &Theme::outlineWidthChanged, this, &Theme::saveConfig);
0077 
0078     connect(&m_theme, &Plasma::Theme::themeChanged, this, &Theme::hasShadowChanged);
0079     connect(&m_theme, &Plasma::Theme::themeChanged, this, &Theme::load);
0080     connect(&m_theme, &Plasma::Theme::themeChanged, this, &Theme::themeChanged);
0081 }
0082 
0083 void Theme::load()
0084 {
0085     loadThemePaths();
0086     loadRoundness();
0087 }
0088 
0089 Theme::~Theme()
0090 {
0091     saveConfig();
0092 
0093     m_defaultScheme->deleteLater();
0094     m_reversedScheme->deleteLater();
0095 }
0096 
0097 bool Theme::hasShadow() const
0098 {
0099     return PanelShadows::self()->hasShadows();
0100 }
0101 
0102 bool Theme::isLightTheme() const
0103 {
0104     return m_isLightTheme;
0105 }
0106 
0107 bool Theme::isDarkTheme() const
0108 {
0109     return !m_isLightTheme;
0110 }
0111 
0112 int Theme::bottomEdgeRoundness() const
0113 {
0114     return m_bottomEdgeRoundness;
0115 }
0116 
0117 int Theme::leftEdgeRoundness() const
0118 {
0119     return m_leftEdgeRoundness;
0120 }
0121 
0122 int Theme::topEdgeRoundness() const
0123 {
0124     return m_topEdgeRoundness;
0125 }
0126 
0127 int Theme::rightEdgeRoundness() const
0128 {
0129     return m_rightEdgeRoundness;
0130 }
0131 
0132 int Theme::outlineWidth() const
0133 {
0134     return m_outlineWidth;
0135 }
0136 
0137 void Theme::setOutlineWidth(int width)
0138 {
0139     if (m_outlineWidth == width) {
0140         return;
0141     }
0142 
0143     m_outlineWidth = width;
0144     emit outlineWidthChanged();
0145 }
0146 
0147 float Theme::bottomEdgeMaxOpacity() const
0148 {
0149     return m_bottomEdgeMaxOpacity;
0150 }
0151 
0152 float Theme::leftEdgeMaxOpacity() const
0153 {
0154     return m_leftEdgeMaxOpacity;
0155 }
0156 
0157 float Theme::topEdgeMaxOpacity() const
0158 {
0159     return m_topEdgeMaxOpacity;
0160 }
0161 
0162 float Theme::rightEdgeMaxOpacity() const
0163 {
0164     return m_rightEdgeMaxOpacity;
0165 }
0166 
0167 WindowSystem::SchemeColors *Theme::defaultTheme() const
0168 {
0169     return m_defaultScheme;
0170 }
0171 
0172 WindowSystem::SchemeColors *Theme::lightTheme() const
0173 {
0174     return m_isLightTheme ? m_defaultScheme : m_reversedScheme;
0175 }
0176 
0177 WindowSystem::SchemeColors *Theme::darkTheme() const
0178 {
0179     return !m_isLightTheme ? m_defaultScheme : m_reversedScheme;
0180 }
0181 
0182 
0183 void Theme::setOriginalSchemeFile(const QString &file)
0184 {
0185     if (m_originalSchemePath == file) {
0186         return;
0187     }
0188 
0189     m_originalSchemePath = file;
0190 
0191     qDebug() << "plasma theme original colors ::: " << m_originalSchemePath;
0192 
0193     updateDefaultScheme();
0194     updateReversedScheme();
0195 
0196     loadThemeLightness();
0197 
0198     emit themeChanged();
0199 }
0200 
0201 //! WM records need to be updated based on the colors that
0202 //! plasma will use in order to be consistent. Such an example
0203 //! are the Breeze color schemes that have different values for
0204 //! WM and the plasma theme records
0205 void Theme::updateDefaultScheme()
0206 {
0207     QString defaultFilePath = m_extendedThemeDir.path() + "/" + DEFAULTCOLORSCHEME;
0208     if (QFileInfo(defaultFilePath).exists()) {
0209         QFile(defaultFilePath).remove();
0210     }
0211 
0212     QFile(m_originalSchemePath).copy(defaultFilePath);
0213     m_defaultSchemePath = defaultFilePath;
0214 
0215     updateDefaultSchemeValues();
0216 
0217     if (m_defaultScheme) {
0218         disconnect(m_defaultScheme, &WindowSystem::SchemeColors::colorsChanged, this, &Theme::loadThemeLightness);
0219         m_defaultScheme->deleteLater();
0220     }
0221 
0222     m_defaultScheme = new WindowSystem::SchemeColors(this, m_defaultSchemePath, true);
0223     connect(m_defaultScheme, &WindowSystem::SchemeColors::colorsChanged, this, &Theme::loadThemeLightness);
0224 
0225     qDebug() << "plasma theme default colors ::: " << m_defaultSchemePath;
0226 }
0227 
0228 void Theme::updateDefaultSchemeValues()
0229 {
0230     //! update WM values based on original scheme
0231     KSharedConfigPtr originalPtr = KSharedConfig::openConfig(m_originalSchemePath);
0232     KSharedConfigPtr defaultPtr = KSharedConfig::openConfig(m_defaultSchemePath);
0233 
0234     if (originalPtr && defaultPtr) {
0235         KConfigGroup normalWindowGroup(originalPtr, "Colors:Window");
0236         KConfigGroup defaultWMGroup(defaultPtr, "WM");
0237 
0238         defaultWMGroup.writeEntry("activeBackground", normalWindowGroup.readEntry("BackgroundNormal", QColor()));
0239         defaultWMGroup.writeEntry("activeForeground", normalWindowGroup.readEntry("ForegroundNormal", QColor()));
0240 
0241         defaultWMGroup.sync();
0242     }
0243 }
0244 
0245 void Theme::updateReversedScheme()
0246 {
0247     QString reversedFilePath = m_extendedThemeDir.path() + "/" + REVERSEDCOLORSCHEME;
0248 
0249     if (QFileInfo(reversedFilePath).exists()) {
0250         QFile(reversedFilePath).remove();
0251     }
0252 
0253     QFile(m_originalSchemePath).copy(reversedFilePath);
0254     m_reversedSchemePath = reversedFilePath;
0255 
0256     updateReversedSchemeValues();
0257 
0258     if (m_reversedScheme) {
0259         m_reversedScheme->deleteLater();
0260     }
0261 
0262     m_reversedScheme = new WindowSystem::SchemeColors(this, m_reversedSchemePath, true);
0263 
0264     qDebug() << "plasma theme reversed colors ::: " << m_reversedSchemePath;
0265 }
0266 
0267 void Theme::updateReversedSchemeValues()
0268 {
0269     //! reverse values based on original scheme
0270     KSharedConfigPtr originalPtr = KSharedConfig::openConfig(m_originalSchemePath);
0271     KSharedConfigPtr reversedPtr = KSharedConfig::openConfig(m_reversedSchemePath);
0272 
0273     if (originalPtr && reversedPtr) {
0274         for (const auto &groupName : reversedPtr->groupList()) {
0275             if (groupName != "Colors:Button" && groupName != "Colors:Selection") {
0276                 KConfigGroup reversedGroup(reversedPtr, groupName);
0277 
0278                 if (reversedGroup.keyList().contains("BackgroundNormal")
0279                         && reversedGroup.keyList().contains("ForegroundNormal")) {
0280                     //! reverse usual text/background values
0281                     KConfigGroup originalGroup(originalPtr, groupName);
0282 
0283                     reversedGroup.writeEntry("BackgroundNormal", originalGroup.readEntry("ForegroundNormal", QColor()));
0284                     reversedGroup.writeEntry("ForegroundNormal", originalGroup.readEntry("BackgroundNormal", QColor()));
0285 
0286                     reversedGroup.sync();
0287                 }
0288             }
0289         }
0290 
0291         //! update WM group
0292         KConfigGroup reversedWMGroup(reversedPtr, "WM");
0293         KConfigGroup normalWindowGroup(originalPtr, "Colors:Window");
0294 
0295         if (reversedWMGroup.keyList().contains("activeBackground")
0296                 && reversedWMGroup.keyList().contains("activeForeground")
0297                 && reversedWMGroup.keyList().contains("inactiveBackground")
0298                 && reversedWMGroup.keyList().contains("inactiveForeground")) {
0299             //! reverse usual wm titlebar values
0300             KConfigGroup originalGroup(originalPtr, "WM");
0301             reversedWMGroup.writeEntry("activeBackground", normalWindowGroup.readEntry("ForegroundNormal", QColor()));
0302             reversedWMGroup.writeEntry("activeForeground", normalWindowGroup.readEntry("BackgroundNormal", QColor()));
0303             reversedWMGroup.writeEntry("inactiveBackground", originalGroup.readEntry("inactiveForeground", QColor()));
0304             reversedWMGroup.writeEntry("inactiveForeground", originalGroup.readEntry("inactiveBackground", QColor()));
0305             reversedWMGroup.sync();
0306         }
0307 
0308         if (reversedWMGroup.keyList().contains("activeBlend")
0309                 && reversedWMGroup.keyList().contains("inactiveBlend")) {
0310             KConfigGroup originalGroup(originalPtr, "WM");
0311             reversedWMGroup.writeEntry("activeBlend", originalGroup.readEntry("inactiveBlend", QColor()));
0312             reversedWMGroup.writeEntry("inactiveBlend", originalGroup.readEntry("activeBlend", QColor()));
0313             reversedWMGroup.sync();
0314         }
0315 
0316         //! update scheme name
0317         QString originalSchemeName = WindowSystem::SchemeColors::schemeName(m_originalSchemePath);
0318         KConfigGroup generalGroup(reversedPtr, "General");
0319         generalGroup.writeEntry("Name", originalSchemeName + "_reversed");
0320         generalGroup.sync();
0321     }
0322 }
0323 
0324 int Theme::roundness(const QImage &svgImage, Plasma::Types::Location edge)
0325 {
0326     int discovRow = (edge == Plasma::Types::TopEdge ? svgImage.height()-1 : 0);
0327     int discovCol = (edge == Plasma::Types::LeftEdge ? svgImage.width()-1 : 0);
0328 
0329     int round{0};
0330 
0331     int maxOpacity = qMin(qAlpha(svgImage.pixel(49,0)), 200);
0332 
0333     if (edge == Plasma::Types::BottomEdge) {
0334         m_bottomEdgeMaxOpacity = (float)maxOpacity / (float)255;
0335     } else if (edge == Plasma::Types::LeftEdge) {
0336         m_leftEdgeMaxOpacity = (float)maxOpacity / (float)255;
0337     } else if (edge == Plasma::Types::TopEdge) {
0338         m_topEdgeMaxOpacity = (float)maxOpacity / (float)255;
0339     } else if (edge == Plasma::Types::RightEdge) {
0340         m_rightEdgeMaxOpacity = (float)maxOpacity / (float)255;
0341     }
0342 
0343     if (edge == Plasma::Types::BottomEdge || edge == Plasma::Types::RightEdge || edge == Plasma::Types::TopEdge) {
0344         //! TOPLEFT corner
0345         //! first LEFT pixel found
0346         QRgb *line = (QRgb *)svgImage.scanLine(discovRow);
0347 
0348         for (int col=0; col<50; ++col) {
0349             QRgb pixelData = line[col];
0350 
0351             if (qAlpha(pixelData) < maxOpacity) {
0352                 discovCol++;
0353                 round++;
0354             } else {
0355                 break;
0356             }
0357         }
0358     } else if (edge == Plasma::Types::LeftEdge) {
0359         //! it should be TOPRIGHT corner in that case
0360         //! first RIGHT pixel found
0361         QRgb *line = (QRgb *)svgImage.scanLine(discovRow);
0362         for (int col=99; col>50; --col) {
0363             QRgb pixelData = line[col];
0364 
0365             if (qAlpha(pixelData) < maxOpacity) {
0366                 discovCol--;
0367                 round++;
0368             } else {
0369                 break;
0370             }
0371         }
0372     }
0373 
0374     //! this needs investigation (the x2) I don't know if it is really needed
0375     //! but it gives me the impression that returns better results
0376     return round; ///**2*/;
0377 }
0378 
0379 void Theme::loadCompositingRoundness()
0380 {
0381     Plasma::FrameSvg *svg = new Plasma::FrameSvg(this);
0382     svg->setImagePath(QStringLiteral("widgets/panel-background"));
0383     svg->setEnabledBorders(Plasma::FrameSvg::AllBorders);
0384     svg->resizeFrame(QSize(100,100));
0385 
0386     //! New approach
0387     QPixmap pxm = svg->framePixmap();
0388 
0389     //! bottom roundness
0390     if (svg->hasElementPrefix("south")) {
0391         svg->setElementPrefix("south");
0392         pxm = svg->framePixmap();
0393     } else {
0394         svg->setElementPrefix("");
0395         pxm = svg->framePixmap();
0396     }
0397     m_bottomEdgeRoundness = roundness(pxm.toImage(), Plasma::Types::BottomEdge);
0398 
0399     //! left roundness
0400     if (svg->hasElementPrefix("west")) {
0401         svg->setElementPrefix("west");
0402         pxm = svg->framePixmap();
0403     } else {
0404         svg->setElementPrefix("");
0405         pxm = svg->framePixmap();
0406     }
0407     m_leftEdgeRoundness = roundness(pxm.toImage(), Plasma::Types::LeftEdge);
0408 
0409     //! top roundness
0410     if (svg->hasElementPrefix("north")) {
0411         svg->setElementPrefix("north");
0412         pxm = svg->framePixmap();
0413     } else {
0414         svg->setElementPrefix("");
0415         pxm = svg->framePixmap();
0416     }
0417     m_topEdgeRoundness = roundness(pxm.toImage(), Plasma::Types::TopEdge);
0418 
0419     //! right roundness
0420     if (svg->hasElementPrefix("east")) {
0421         svg->setElementPrefix("east");
0422         pxm = svg->framePixmap();
0423     } else {
0424         svg->setElementPrefix("");
0425         pxm = svg->framePixmap();
0426     }
0427     m_rightEdgeRoundness = roundness(pxm.toImage(), Plasma::Types::RightEdge);
0428 
0429   /*  qDebug() << " COMPOSITING MASK ::: " << svg->mask();
0430     qDebug() << " COMPOSITING MASK BOUNDING RECT ::: " << svg->mask().boundingRect();*/
0431     qDebug() << " COMPOSITING ROUNDNESS ::: " << m_bottomEdgeRoundness << " _ " << m_leftEdgeRoundness << " _ " << m_topEdgeRoundness << " _ " << m_rightEdgeRoundness;
0432 
0433     svg->deleteLater();
0434 }
0435 
0436 void Theme::loadRoundness()
0437 {
0438     loadCompositingRoundness();
0439 
0440     emit maxOpacityChanged();
0441     emit roundnessChanged();
0442 }
0443 
0444 void Theme::loadThemePaths()
0445 {
0446     m_themePath = Layouts::Importer::standardPath("plasma/desktoptheme/" + m_theme.themeName());
0447 
0448     if (QDir(m_themePath+"/widgets").exists()) {
0449         m_themeWidgetsPath = m_themePath + "/widgets";
0450     } else {
0451         m_themeWidgetsPath = Layouts::Importer::standardPath("plasma/desktoptheme/default/widgets");
0452     }
0453 
0454     qDebug() << "current plasma theme ::: " << m_theme.themeName();
0455     qDebug() << "theme path ::: " << m_themePath;
0456     qDebug() << "theme widgets path ::: " << m_themeWidgetsPath;
0457 
0458     //! clear kde connections
0459     for (auto &c : m_kdeConnections) {
0460         disconnect(c);
0461     }
0462 
0463     //! assign color schemes
0464     QString themeColorScheme = m_themePath + "/colors";
0465 
0466     if (QFileInfo(themeColorScheme).exists()) {
0467         setOriginalSchemeFile(themeColorScheme);
0468     } else {
0469         //! when plasma theme uses the kde colors
0470         //! we track when kde color scheme is changing
0471         QString kdeSettingsFile = QDir::homePath() + "/.config/kdeglobals";
0472 
0473         KDirWatch::self()->addFile(kdeSettingsFile);
0474 
0475         m_kdeConnections[0] = connect(KDirWatch::self(), &KDirWatch::dirty, this, [ &, kdeSettingsFile](const QString & path) {
0476             if (path == kdeSettingsFile) {
0477                 this->setOriginalSchemeFile(WindowSystem::SchemeColors::possibleSchemeFile("kdeglobals"));
0478             }
0479         });
0480 
0481         m_kdeConnections[1] = connect(KDirWatch::self(), &KDirWatch::created, this, [ &, kdeSettingsFile](const QString & path) {
0482             if (path == kdeSettingsFile) {
0483                 this->setOriginalSchemeFile(WindowSystem::SchemeColors::possibleSchemeFile("kdeglobals"));
0484             }
0485         });
0486 
0487         setOriginalSchemeFile(WindowSystem::SchemeColors::possibleSchemeFile("kdeglobals"));
0488     }
0489 
0490     //! this is probably not needed at all in order to provide full transparency for all
0491     //! plasma themes, so we disable it in order to confirm from user testing
0492     //! that it is not needed at all
0493     //parseThemeSvgFiles();
0494 }
0495 
0496 void Theme::parseThemeSvgFiles()
0497 {
0498     QString origBackgroundSvgFile;
0499     QString curBackgroundSvgFile = m_extendedThemeDir.path()+"/widgets/panel-background.svg";
0500 
0501     if (QFileInfo(curBackgroundSvgFile).exists()) {
0502         QDir(m_extendedThemeDir.path()+"/widgets").remove("panel-background.svg");
0503     }
0504 
0505     if (!QDir(m_extendedThemeDir.path()+"/widgets").exists()) {
0506         QDir(m_extendedThemeDir.path()).mkdir("widgets");
0507     }
0508 
0509     if (QFileInfo(m_themeWidgetsPath+"/panel-background.svg").exists()) {
0510         origBackgroundSvgFile = m_themeWidgetsPath+"/panel-background.svg";
0511         QFile(origBackgroundSvgFile).copy(curBackgroundSvgFile);
0512     } else if (QFileInfo(m_themeWidgetsPath+"/panel-background.svgz").exists()) {
0513         origBackgroundSvgFile = m_themeWidgetsPath+"/panel-background.svgz";
0514         QString tempBackFile = m_extendedThemeDir.path()+"/widgets/panel-background.svg.gz";
0515         QFile(origBackgroundSvgFile).copy(tempBackFile);
0516 
0517         //! Identify Plasma Desktop version
0518         QProcess process;
0519         process.start("gzip -d " + tempBackFile);
0520         process.waitForFinished();
0521         QString output(process.readAllStandardOutput());
0522 
0523         qDebug() << "plasma theme, background extraction output ::: " << output;
0524         qDebug() << "plasma theme, original background svg file was decompressed...";
0525     }
0526 
0527     if (QFileInfo(curBackgroundSvgFile).exists()) {
0528         qDebug() << "plasma theme, panel background ::: " << curBackgroundSvgFile;
0529     } else {
0530         qDebug() << "plasma theme, panel background ::: was not found...";
0531     }
0532 
0533     //! Find panel-background transparency
0534     QFile svgFile(curBackgroundSvgFile);
0535     QString styleSvgStr;
0536 
0537     if (svgFile.open(QIODevice::ReadOnly)) {
0538         QTextStream in(&svgFile);
0539         bool centerIdFound{false};
0540         bool styleFound{false};
0541 
0542         while (!in.atEnd() && !styleFound) {
0543             QString line = in.readLine();
0544 
0545             //! each time a rect starts then style can be reset
0546             if (line.contains("<rect")) {
0547                 styleSvgStr = "";
0548             }
0549 
0550             //! identify the id "center
0551             if (line.contains("id=\"center\"")) {
0552                 centerIdFound = true;
0553             }
0554 
0555             //! if valid style for center exists we can break
0556             if (centerIdFound && !styleSvgStr.isEmpty()) {
0557                 break;
0558             }
0559 
0560             if (centerIdFound && line.contains("style=\"") ) {
0561                 styleSvgStr = line;
0562             }
0563 
0564             //! when end of "center" you can break
0565             if (centerIdFound && line.contains("/rect>")) {
0566                 break;
0567             }
0568         }
0569         svgFile.close();
0570     }
0571 
0572     if (!styleSvgStr.isEmpty()) {
0573         int styleInd = styleSvgStr.indexOf("style=");
0574         QString cleanedStr = styleSvgStr.remove(0, styleInd+7);
0575         int endInd = cleanedStr.indexOf("\"");
0576         styleSvgStr = cleanedStr.mid(0,endInd);
0577 
0578         QStringList styleValues = styleSvgStr.split(";");
0579         // qDebug() << "plasma theme, discovered svg style ::: " << styleValues;
0580 
0581         float opacity{1};
0582         float fillOpacity{1};
0583 
0584         for (QString &value : styleValues) {
0585             if (value.startsWith("opacity:")) {
0586                 opacity = value.remove(0,8).toFloat();
0587             }
0588             if (value.startsWith("fill-opacity:")) {
0589                 fillOpacity = value.remove(0,13).toFloat();
0590             }
0591         }
0592 
0593       //  m_backgroundMaxOpacity = opacity * fillOpacity;
0594 
0595       //  qDebug() << "plasma theme opacity :: " << m_backgroundMaxOpacity << " from : " << opacity << " * " << fillOpacity;
0596     }
0597 
0598  //   emit backgroundMaxOpacityChanged();
0599 }
0600 
0601 void Theme::loadThemeLightness()
0602 {
0603     float textColorLum = Latte::colorLumina(m_defaultScheme->textColor());
0604     float backColorLum = Latte::colorLumina(m_defaultScheme->backgroundColor());
0605 
0606     if (backColorLum > textColorLum) {
0607         m_isLightTheme = true;
0608     } else {
0609         m_isLightTheme = false;
0610     }
0611 
0612     if (m_isLightTheme) {
0613         qDebug() << "Plasma theme is light...";
0614     } else {
0615         qDebug() << "Plasma theme is dark...";
0616     }
0617 }
0618 
0619 void Theme::loadConfig()
0620 {
0621     setOutlineWidth(m_themeGroup.readEntry("outlineWidth", 1));
0622 }
0623 
0624 void Theme::saveConfig()
0625 {
0626     m_themeGroup.writeEntry("outlineWidth", m_outlineWidth);
0627 
0628     m_themeGroup.sync();
0629 }
0630 
0631 }
0632 }