1 | /* This file is part of the KDE libraries |
2 | SPDX-FileCopyrightText: 1999 Waldo Bastian <bastian@kde.org> |
3 | |
4 | SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
5 | */ |
6 | |
7 | // KDE color collection |
8 | |
9 | #include "kcolorcollection.h" |
10 | |
11 | #include <QDir> |
12 | #include <QFile> |
13 | #include <QSaveFile> |
14 | #include <QSharedData> |
15 | #include <QStandardPaths> |
16 | #include <QTextStream> |
17 | |
18 | // BEGIN KColorCollectionPrivate |
19 | class KColorCollectionPrivate : public QSharedData |
20 | { |
21 | public: |
22 | KColorCollectionPrivate(const QString &); |
23 | KColorCollectionPrivate(const KColorCollectionPrivate &) = default; |
24 | ~KColorCollectionPrivate() |
25 | { |
26 | } |
27 | struct ColorNode { |
28 | ColorNode(const QColor &c, const QString &n) |
29 | : color(c) |
30 | , name(n) |
31 | { |
32 | } |
33 | QColor color; |
34 | QString name; |
35 | }; |
36 | QList<ColorNode> colorList; |
37 | |
38 | QString name; |
39 | QString desc; |
40 | KColorCollection::Editable editable; |
41 | }; |
42 | |
43 | KColorCollectionPrivate::KColorCollectionPrivate(const QString &_name) |
44 | : name(_name) |
45 | { |
46 | if (name.isEmpty()) { |
47 | return; |
48 | } |
49 | |
50 | QString filename = QStandardPaths::locate(type: QStandardPaths::GenericConfigLocation, fileName: QLatin1String("colors/" ) + name); |
51 | if (filename.isEmpty()) { |
52 | return; |
53 | } |
54 | |
55 | QFile paletteFile(filename); |
56 | if (!paletteFile.exists()) { |
57 | return; |
58 | } |
59 | if (!paletteFile.open(flags: QIODevice::ReadOnly)) { |
60 | return; |
61 | } |
62 | |
63 | // Read first line |
64 | // Expected "GIMP Palette" |
65 | QString line = QString::fromLocal8Bit(ba: paletteFile.readLine()); |
66 | if (line.contains(s: QLatin1String(" Palette" ))) { |
67 | return; |
68 | } |
69 | |
70 | while (!paletteFile.atEnd()) { |
71 | line = QString::fromLocal8Bit(ba: paletteFile.readLine()); |
72 | if (line[0] == QLatin1Char('#')) { |
73 | // This is a comment line |
74 | line.remove(i: 0, len: 1); // Strip '#' |
75 | line = line.trimmed(); // Strip remaining white space.. |
76 | if (!line.isEmpty()) { |
77 | desc += line + QLatin1Char('\n'); // Add comment to description |
78 | } |
79 | } else { |
80 | // This is a color line, hopefully |
81 | line = line.trimmed(); |
82 | if (line.isEmpty()) { |
83 | continue; |
84 | } |
85 | int r; |
86 | int g; |
87 | int b; |
88 | int pos = 0; |
89 | if (sscanf(s: line.toLatin1().constData(), format: "%d %d %d%n" , &r, &g, &b, &pos) >= 3) { |
90 | r = qBound(min: 0, val: r, max: 255); |
91 | g = qBound(min: 0, val: g, max: 255); |
92 | b = qBound(min: 0, val: b, max: 255); |
93 | QString name = line.mid(position: pos).trimmed(); |
94 | colorList.append(t: ColorNode(QColor(r, g, b), name)); |
95 | } |
96 | } |
97 | } |
98 | } |
99 | // END KColorCollectionPrivate |
100 | |
101 | QStringList KColorCollection::installedCollections() |
102 | { |
103 | const QStringList paletteDirs = QStandardPaths::locateAll(type: QStandardPaths::GenericConfigLocation, QStringLiteral("colors" ), options: QStandardPaths::LocateDirectory); |
104 | |
105 | QStringList paletteList; |
106 | for (const QString &dir : paletteDirs) { |
107 | paletteList += QDir(dir).entryList(filters: QDir::Files | QDir::NoDotAndDotDot); |
108 | } |
109 | paletteList.removeDuplicates(); |
110 | |
111 | return paletteList; |
112 | } |
113 | |
114 | KColorCollection::KColorCollection(const QString &name) |
115 | : d(new KColorCollectionPrivate(name)) |
116 | { |
117 | } |
118 | |
119 | KColorCollection::KColorCollection(const KColorCollection &p) = default; |
120 | |
121 | // Need auto-save? |
122 | KColorCollection::~KColorCollection() = default; |
123 | |
124 | bool KColorCollection::save() |
125 | { |
126 | QString filename = QStandardPaths::writableLocation(type: QStandardPaths::GenericConfigLocation) + QLatin1String("/colors/" ) + d->name; |
127 | QSaveFile sf(filename); |
128 | if (!sf.open(flags: QIODevice::WriteOnly)) { |
129 | return false; |
130 | } |
131 | |
132 | QTextStream str(&sf); |
133 | |
134 | QString description = d->desc.trimmed(); |
135 | description = QLatin1Char('#') + description.split(sep: QLatin1Char('\n'), behavior: Qt::KeepEmptyParts).join(sep: QLatin1String("\n#" )); |
136 | |
137 | str << QLatin1String("KDE RGB Palette\n" ); |
138 | str << description << QLatin1Char('\n'); |
139 | for (const KColorCollectionPrivate::ColorNode &node : std::as_const(t&: d->colorList)) { |
140 | int r; |
141 | int g; |
142 | int b; |
143 | node.color.getRgb(r: &r, g: &g, b: &b); |
144 | str << r << " " << g << " " << b << " " << node.name << "\n" ; |
145 | } |
146 | |
147 | return sf.commit(); |
148 | } |
149 | |
150 | QString KColorCollection::description() const |
151 | { |
152 | return d->desc; |
153 | } |
154 | |
155 | void KColorCollection::setDescription(const QString &desc) |
156 | { |
157 | d->desc = desc; |
158 | } |
159 | |
160 | QString KColorCollection::name() const |
161 | { |
162 | return d->name; |
163 | } |
164 | |
165 | void KColorCollection::setName(const QString &name) |
166 | { |
167 | d->name = name; |
168 | } |
169 | |
170 | KColorCollection::Editable KColorCollection::editable() const |
171 | { |
172 | return d->editable; |
173 | } |
174 | |
175 | void KColorCollection::setEditable(Editable editable) |
176 | { |
177 | d->editable = editable; |
178 | } |
179 | |
180 | int KColorCollection::count() const |
181 | { |
182 | return d->colorList.count(); |
183 | } |
184 | |
185 | KColorCollection &KColorCollection::operator=(const KColorCollection &p) = default; |
186 | |
187 | QColor KColorCollection::color(int index) const |
188 | { |
189 | if ((index < 0) || (index >= count())) { |
190 | return QColor(); |
191 | } |
192 | |
193 | return d->colorList[index].color; |
194 | } |
195 | |
196 | int KColorCollection::findColor(const QColor &color) const |
197 | { |
198 | for (int i = 0; i < d->colorList.size(); ++i) { |
199 | if (d->colorList[i].color == color) { |
200 | return i; |
201 | } |
202 | } |
203 | return -1; |
204 | } |
205 | |
206 | QString KColorCollection::name(int index) const |
207 | { |
208 | if ((index < 0) || (index >= count())) { |
209 | return QString(); |
210 | } |
211 | |
212 | return d->colorList[index].name; |
213 | } |
214 | |
215 | QString KColorCollection::name(const QColor &color) const |
216 | { |
217 | return name(index: findColor(color)); |
218 | } |
219 | |
220 | int KColorCollection::addColor(const QColor &newColor, const QString &newColorName) |
221 | { |
222 | d->colorList.append(t: KColorCollectionPrivate::ColorNode(newColor, newColorName)); |
223 | return count() - 1; |
224 | } |
225 | |
226 | int KColorCollection::changeColor(int index, const QColor &newColor, const QString &newColorName) |
227 | { |
228 | if ((index < 0) || (index >= count())) { |
229 | return -1; |
230 | } |
231 | |
232 | KColorCollectionPrivate::ColorNode &node = d->colorList[index]; |
233 | node.color = newColor; |
234 | node.name = newColorName; |
235 | |
236 | return index; |
237 | } |
238 | |
239 | int KColorCollection::changeColor(const QColor &oldColor, const QColor &newColor, const QString &newColorName) |
240 | { |
241 | return changeColor(index: findColor(color: oldColor), newColor, newColorName); |
242 | } |
243 | |