2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
4 * Copyright (C) 2019, 2021 Prince Gupta <jagannatharjun11@gmail.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21 * In addition, as a special exception, the copyright holders give permission to
22 * link this program with the OpenSSL project's "OpenSSL" library (or with
23 * modified versions of it that use the same license as the "OpenSSL" library),
24 * and distribute the linked executables. You must obey the GNU General Public
25 * License in all respects for all of the code used other than "OpenSSL". If
26 * you modify file(s), you may extend this exception to your version of the
27 * file(s), but you are not obligated to do so. If you do not wish to do so,
28 * delete this exception statement from your version.
31 #include "uithemesource.h"
33 #include <QJsonDocument>
34 #include <QJsonObject>
36 #include "base/global.h"
37 #include "base/logger.h"
38 #include "base/profile.h"
39 #include "base/utils/io.h"
43 const qint64 FILE_MAX_SIZE
= 1024 * 1024;
45 QJsonObject
parseThemeConfig(const QByteArray
&data
)
50 QJsonParseError jsonError
;
51 const QJsonDocument configJsonDoc
= QJsonDocument::fromJson(data
, &jsonError
);
52 if (jsonError
.error
!= QJsonParseError::NoError
)
54 LogMsg(UIThemeSource::tr("Couldn't parse UI Theme configuration file. Reason: %1")
55 .arg(jsonError
.errorString()), Log::WARNING
);
59 if (!configJsonDoc
.isObject())
61 LogMsg(UIThemeSource::tr("UI Theme configuration file has invalid format. Reason: %1")
62 .arg(UIThemeSource::tr("Root JSON value is not an object")), Log::WARNING
);
66 return configJsonDoc
.object();
69 QHash
<QString
, QColor
> colorsFromJSON(const QJsonObject
&jsonObj
)
71 QHash
<QString
, QColor
> colors
;
72 for (auto colorNode
= jsonObj
.constBegin(); colorNode
!= jsonObj
.constEnd(); ++colorNode
)
74 const QColor color
{colorNode
.value().toString()};
77 LogMsg(UIThemeSource::tr("Invalid color for ID \"%1\" is provided by theme")
78 .arg(colorNode
.key()), Log::WARNING
);
82 colors
.insert(colorNode
.key(), color
);
88 Path
findIcon(const QString
&iconId
, const Path
&dir
)
90 const Path pathSvg
= dir
/ Path(iconId
+ u
".svg");
94 const Path pathPng
= dir
/ Path(iconId
+ u
".png");
102 DefaultThemeSource::DefaultThemeSource()
103 : m_defaultPath
{u
":"_s
}
104 , m_userPath
{specialFolderLocation(SpecialFolder::Config
) / Path(u
"themes/default"_s
)}
105 , m_colors
{defaultUIThemeColors()}
110 QByteArray
DefaultThemeSource::readStyleSheet()
115 QColor
DefaultThemeSource::getColor(const QString
&colorId
, const ColorMode colorMode
) const
117 return (colorMode
== ColorMode::Light
)
118 ? m_colors
.value(colorId
).light
: m_colors
.value(colorId
).dark
;
121 Path
DefaultThemeSource::getIconPath(const QString
&iconId
, const ColorMode colorMode
) const
123 const Path iconsPath
{u
"icons"_s
};
124 const Path lightModeIconsPath
= iconsPath
/ Path(u
"light"_s
);
125 const Path darkModeIconsPath
= iconsPath
/ Path(u
"dark"_s
);
127 if (colorMode
== ColorMode::Dark
)
129 if (const Path iconPath
= findIcon(iconId
, (m_userPath
/ darkModeIconsPath
))
130 ; !iconPath
.isEmpty())
135 if (const Path iconPath
= findIcon(iconId
, (m_defaultPath
/ darkModeIconsPath
))
136 ; !iconPath
.isEmpty())
143 if (const Path iconPath
= findIcon(iconId
, (m_userPath
/ lightModeIconsPath
))
144 ; !iconPath
.isEmpty())
150 return findIcon(iconId
, (m_defaultPath
/ iconsPath
));
153 void DefaultThemeSource::loadColors()
155 const auto readResult
= Utils::IO::readFile((m_userPath
/ Path(CONFIG_FILE_NAME
)), FILE_MAX_SIZE
, QIODevice::Text
);
158 if (readResult
.error().status
!= Utils::IO::ReadError::NotExist
)
159 LogMsg(tr("Failed to load default theme colors. %1").arg(readResult
.error().message
), Log::WARNING
);
164 const QByteArray
&configData
= readResult
.value();
165 if (configData
.isEmpty())
168 const QJsonObject config
= parseThemeConfig(configData
);
170 const QHash
<QString
, QColor
> lightModeColorOverrides
= colorsFromJSON(config
.value(KEY_COLORS_LIGHT
).toObject());
171 for (auto overridesIt
= lightModeColorOverrides
.cbegin(); overridesIt
!= lightModeColorOverrides
.cend(); ++overridesIt
)
173 auto it
= m_colors
.find(overridesIt
.key());
174 if (it
!= m_colors
.end())
175 it
.value().light
= overridesIt
.value();
178 const QHash
<QString
, QColor
> darkModeColorOverrides
= colorsFromJSON(config
.value(KEY_COLORS_DARK
).toObject());
179 for (auto overridesIt
= darkModeColorOverrides
.cbegin(); overridesIt
!= darkModeColorOverrides
.cend(); ++overridesIt
)
181 auto it
= m_colors
.find(overridesIt
.key());
182 if (it
!= m_colors
.end())
183 it
.value().dark
= overridesIt
.value();
187 CustomThemeSource::CustomThemeSource(const Path
&themeRootPath
)
188 : m_themeRootPath
{themeRootPath
}
193 QColor
CustomThemeSource::getColor(const QString
&colorId
, const ColorMode colorMode
) const
195 if (colorMode
== ColorMode::Dark
)
197 if (const QColor color
= m_darkModeColors
.value(colorId
)
204 if (const QColor color
= m_colors
.value(colorId
)
210 return defaultThemeSource()->getColor(colorId
, colorMode
);
213 Path
CustomThemeSource::getIconPath(const QString
&iconId
, const ColorMode colorMode
) const
215 const Path iconsPath
{u
"icons"_s
};
216 const Path darkModeIconsPath
= iconsPath
/ Path(u
"dark"_s
);
218 if (colorMode
== ColorMode::Dark
)
220 if (const Path iconPath
= findIcon(iconId
, (themeRootPath() / darkModeIconsPath
))
221 ; !iconPath
.isEmpty())
227 if (const Path iconPath
= findIcon(iconId
, (themeRootPath() / iconsPath
))
228 ; !iconPath
.isEmpty())
233 return defaultThemeSource()->getIconPath(iconId
, colorMode
);
236 QByteArray
CustomThemeSource::readStyleSheet()
238 const auto readResult
= Utils::IO::readFile((themeRootPath() / Path(STYLESHEET_FILE_NAME
)), FILE_MAX_SIZE
, QIODevice::Text
);
241 if (readResult
.error().status
!= Utils::IO::ReadError::NotExist
)
242 LogMsg(tr("Failed to load custom theme style sheet. %1").arg(readResult
.error().message
), Log::WARNING
);
247 return readResult
.value();
250 DefaultThemeSource
*CustomThemeSource::defaultThemeSource() const
252 return m_defaultThemeSource
.get();
255 Path
CustomThemeSource::themeRootPath() const
257 return m_themeRootPath
;
260 void CustomThemeSource::loadColors()
262 const auto readResult
= Utils::IO::readFile((themeRootPath() / Path(CONFIG_FILE_NAME
)), FILE_MAX_SIZE
, QIODevice::Text
);
265 if (readResult
.error().status
!= Utils::IO::ReadError::NotExist
)
266 LogMsg(tr("Failed to load custom theme colors. %1").arg(readResult
.error().message
), Log::WARNING
);
271 const QByteArray
&configData
= readResult
.value();
272 if (configData
.isEmpty())
275 const QJsonObject config
= parseThemeConfig(configData
);
277 m_colors
.insert(colorsFromJSON(config
.value(KEY_COLORS
).toObject()));
278 m_darkModeColors
.insert(colorsFromJSON(config
.value(KEY_COLORS_DARK
).toObject()));
281 FolderThemeSource::FolderThemeSource(const Path
&folderPath
)
282 : CustomThemeSource(folderPath
)
283 , m_folder
{folderPath
}
287 QByteArray
FolderThemeSource::readStyleSheet()
289 // Directory used by stylesheet to reference internal resources
290 // for example `icon: url(:/uitheme/file.svg)` will be expected to
291 // point to a file `file.svg` in root directory of CONFIG_FILE_NAME
292 const QString stylesheetResourcesDir
= u
":/uitheme"_s
;
294 QByteArray styleSheetData
= CustomThemeSource::readStyleSheet();
295 return styleSheetData
.replace(stylesheetResourcesDir
.toUtf8(), m_folder
.data().toUtf8());
298 QRCThemeSource::QRCThemeSource()
299 : CustomThemeSource(Path(u
":/uitheme"_s
))