5#include <QCommandLineOption>
6#include <QCommandLineParser>
7#include <QCoreApplication>
10#include <QDirIterator>
12#include <QMimeDatabase>
13#include <QRegularExpression>
14#include <QXmlStreamReader>
15#include <QXmlStreamWriter>
17using namespace Qt::StringLiterals;
23 static const auto ellipsis =
"…"_L1;
24 static constexpr auto limit = 100000;
25 if (
string.size() > limit) {
26 return string.first(qBound(0, limit - ellipsis.size(),
string.size())) + ellipsis;
35 stylesheet.
remove(
QRE(u
"\\s"_s, patternOptions));
39 QRE regex(u
"\\.ColorScheme-(\\S+?){color:(#[0-9a-fA-F]+);}"_s, patternOptions);
40 auto matchIt = regex.globalMatch(stylesheet);
41 while (matchIt.hasNext()) {
42 auto match = matchIt.next();
43 auto classString =
match.captured(1);
44 auto colorString =
match.captured(2);
45 if (classString ==
"Text"_L1) {
46 colorString = u
"#fcfcfc"_s;
47 }
else if (classString ==
"Background"_L1) {
48 colorString = u
"#2a2e32"_s;
50 classColorMap.
insert(
match.captured(1), colorString);
54 for (
auto it = classColorMap.
cbegin(); it != classColorMap.
cend(); ++it) {
55 output +=
".ColorScheme-%1 { color: %2; } "_L1.
arg(it.key(), it.value());
60int main(
int argc,
char **argv)
65 commandLineParser.
setApplicationDescription(u
"Takes light theme icons and makes modified copies of them with dark theme stylesheets."_s);
66 commandLineParser.
addPositionalArgument(u
"inputs"_s, u
"Input folders (separated by spaces)"_s, u
"inputs..."_s);
67 commandLineParser.
addPositionalArgument(u
"output"_s, u
"Output folder (will be created if not existing)"_s, u
"output"_s);
74 if (positionalArguments.isEmpty()) {
75 qWarning() <<
"The arguments are missing.";
79 QFileInfo outputDirInfo(positionalArguments.last());
80 if (outputDirInfo.exists() && !outputDirInfo.isDir()) {
81 qWarning() << positionalArguments.last() <<
"is not a folder.";
87 for (
int i = 0; i < positionalArguments.size() - 1; ++i) {
88 QFileInfo inputDirInfo(positionalArguments[i]);
89 if (!inputDirInfo.isDir()) {
90 ignoredArgs << positionalArguments[i];
93 inputDirs << inputDirInfo.absoluteFilePath();
97 qWarning() <<
"None of the input arguments could be used.";
103 qWarning() <<
"The following input arguments were ignored:";
104 qWarning().noquote() << elideString(ignoredArgs.
join(
"\n"_L1));
107 bool wasAnyFileWritten =
false;
112 for (
auto &inputDir : std::as_const(inputDirs)) {
114 while (dirIt.hasNext()) {
115 auto inputFileInfo = dirIt.nextFileInfo();
116 const auto inputFilePath = inputFileInfo.absoluteFilePath();
119 if (!inputFileInfo.isFile() || inputFileInfo.isSymLink() || !inputFilePath.endsWith(
".svg"_L1)
124 QFile inputFile(inputFilePath);
126 unreadFiles.
append(
"\""_L1 + inputFile.fileName() +
"\": "_L1 + inputFile.errorString());
129 const auto inputData = inputFile.readAll();
133 if (!inputData.contains(
"current-color-scheme")) {
139 QFileInfo outputFileInfo(outputFilePath);
140 outputDir = outputFileInfo.dir();
141 if (!outputDir.
exists()) {
144 QFile outputFile(outputFilePath);
146 unwrittenFiles.
append(
"\""_L1 + outputFile.fileName() +
"\": "_L1 + outputFile.errorString());
151 reader.setNamespaceProcessing(
false);
154 writer.setAutoFormatting(
true);
156 while (!reader.atEnd() && !reader.hasError() && !writer.hasError()) {
158 writer.writeCurrentToken(reader);
159 if (!reader.isStartElement() || reader.qualifiedName() !=
"style"_L1 || reader.attributes().value(
"id"_L1) !=
"current-color-scheme"_L1) {
163 if (!reader.isCharacters()) {
164 writer.writeCurrentToken(reader);
167 writer.writeCharacters(convertStylesheet(reader.text().toString()));
170 if (reader.hasError()) {
171 xmlReadErrorFiles.
append(
"\""_L1 + inputFile.fileName() +
"\": "_L1 + reader.errorString());
173 if (writer.hasError()) {
174 xmlWriteErrorFiles.
append(
"\""_L1 + outputFile.fileName() +
"\""_L1);
177 auto bytesWritten = outputFile.write(outputData);
179 wasAnyFileWritten |= bytesWritten > 0;
183 if (!unreadFiles.
empty()) {
184 qWarning() <<
"Input file open errors:";
185 qWarning().noquote() << elideString(unreadFiles.
join(
"\n"_L1));
187 if (!unwrittenFiles.
empty()) {
188 qWarning() <<
"Output file open errors:";
189 qWarning().noquote() << elideString(unwrittenFiles.
join(
"\n"_L1));
191 if (!xmlReadErrorFiles.
empty()) {
192 qWarning() <<
"Input XML read errors:";
193 qWarning().noquote() << elideString(xmlReadErrorFiles.
join(
"\n"_L1));
195 if (!xmlWriteErrorFiles.
empty()) {
196 qWarning() <<
"Output XML write errors:";
197 qWarning().noquote() << elideString(xmlWriteErrorFiles.
join(
"\n"_L1));
200 return wasAnyFileWritten ? 0 : 1;
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
QCommandLineOption addHelpOption()
void addPositionalArgument(const QString &name, const QString &description, const QString &syntax)
QCommandLineOption addVersionOption()
QStringList positionalArguments() const const
void process(const QCoreApplication &app)
void setApplicationDescription(const QString &description)
QString absoluteFilePath(const QString &fileName) const const
QString absolutePath() const const
bool exists() const const
bool mkpath(const QString &dirPath) const const
bool exists() const const
void append(QList< T > &&value)
bool isEmpty() const const
const_iterator cbegin() const const
const_iterator cend() const const
iterator insert(const Key &key, const T &value)
QString arg(Args &&... args) const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString join(QChar separator) const const